Files
libsst/libsst-wm/Xlib/GLXPrivate.c
2026-04-03 00:22:39 -05:00

375 lines
11 KiB
C

/*
GLXPrivate.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 1/11/2013
Purpose:
OpenGL on X Windows (GLX) utility code
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include <SST/SST_WMOpenGL.h>
#include "XlibPrivate.h"
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
/* GL constants */
#define GL_VERSION 0x1F02
/* GLX constants */
#define GLX_DOUBLEBUFFER 5
#define GLX_STEREO 6
#define GLX_AUX_BUFFERS 7
#define GLX_RED_SIZE 8
#define GLX_GREEN_SIZE 9
#define GLX_BLUE_SIZE 10
#define GLX_ALPHA_SIZE 11
#define GLX_DEPTH_SIZE 12
#define GLX_STENCIL_SIZE 13
#define GLX_DRAWABLE_TYPE 0x8010
#define GLX_RGBA_TYPE 0x8014
#define GLX_WINDOW_BIT 0x00000001
#define GLX_PIXMAP_BIT 0x00000002
#define GLX_PBUFFER_BIT 0x00000004
/* GLX_ARB_multisample */
#define GLX_SAMPLE_BUFFERS_ARB 100000
#define GLX_SAMPLES_ARB 100001
#define GLX_PBUFFER_HEIGHT 0x8040
#define GLX_PBUFFER_WIDTH 0x8041
/* GLX_ARB_create_context[_profile] */
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
#define GLX_CONTEXT_FLAGS_ARB 0x2094
#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
GLXFunctions glX = { 0 };
static void setupAttribs(int* glxAttr, const SST_OpenGLContextAttributes* attribs, Bool supportsMultisample);
static int* addAttr(int* ptr, int attr, int value)
{
ptr[0] = attr;
ptr[1] = value;
return ptr + 2;
}
/******************************************************************************/
int loadGLX()
{
const int flags = RTLD_LAZY | RTLD_GLOBAL;
void* libGL;
/* Set all values to null/0/false */
memset(&glX, 0, sizeof(GLXFunctions));
/* Load OpenGL library with proper DT_SONAME */
libGL = dlopen("libGL.so.1", flags);
if(libGL == NULL)
{
/* Failed...try generic name for it */
libGL = dlopen("libGL.so", flags);
if(libGL == NULL)
return 0;
}
/* Resolve symbols */
glX.QueryExtension = dlsym(libGL, "glXQueryExtension");
glX.QueryVersion = dlsym(libGL, "glXQueryVersion");
glX.QueryExtensionsString = dlsym(libGL, "glXQueryExtensionsString");
glX.ChooseFBConfig = dlsym(libGL, "glXChooseFBConfig");
glX.CreateNewContext = dlsym(libGL, "glXCreateNewContext");
glX.DestroyContext = dlsym(libGL, "glXDestroyContext");
glX.MakeContextCurrent = dlsym(libGL, "glXMakeContextCurrent");
glX.GetCurrentContext = dlsym(libGL, "glXGetCurrentContext");
glX.SwapBuffers = dlsym(libGL, "glXSwapBuffers");
glX.CreatePbuffer = dlsym(libGL, "glXCreatePbuffer");
glX.DestroyPbuffer = dlsym(libGL, "glXDestroyPbuffer");
/* On Linux, glXGetProcAddressARB() is statically exported from libGL.so as part of LSB. However,
on non-LSB compliant or other UNIX systems, the -ARB version may not exist. Try the unsuffixed
form if the -ARB variant does not exist. */
glX.GetProcAddressARB = dlsym(libGL, "glXGetProcAddressARB");
if(glX.GetProcAddressARB == NULL)
glX.GetProcAddressARB = dlsym(libGL, "glXGetProcAddress");
/* We dont resolve glXCreateContextAttribsARB yet; first check GLX extension strings. */
glX.CreateContextAttribsARB = NULL;
/* Save library handle */
glX.libGL = libGL;
return 1;
}
/******************************************************************************/
void unloadGLX()
{
dlclose(glX.libGL);
}
/******************************************************************************/
int isGLXLoaded()
{
return (glX.libGL != NULL);
}
/******************************************************************************/
int isGLXSupported(Display* display)
{
int major = 0, minor = 0;
int combined;
const char* ext;
/* Query if the X server supports GLX. If it doesn't, then nothing to do */
if(glX.QueryExtension(display, NULL, NULL) == False)
{
fprintf(stderr, "%s:GLX extension not supported on this connection.\n", "libsst-wm");
return 0;
}
/* Query the version; we need 1.3 or later */
if(glX.QueryVersion(display, &major, &minor) == False)
{
fprintf(stderr, "%s:Unable to query GLX version.\n", "libsst-wm");
return 0;
}
/* Fail on less than 1.3 */
combined = (major << 8) | minor;
if(combined < 0x13)
{
fprintf(stderr, "%s:GLX version 1.3 required, found %d.%d\n", "libsst-wm", major, minor);
return 0;
}
ext = glX.QueryExtensionsString(display, DefaultScreen(display));
/* "GLX_ARB_create_context" and "GLX_ARB_create_context_profile" might both match the first strstr(),
but "GLX_ARB_create_context_profile" requires the former, so it's all good.
TODO: write less stupid extension checker - one day may not be true */
if(combined >= 0x14) /* GLX 1.4 required for GLX_ARB_create_context */
{
glX.supportsCreateContextARB = (Bool)(strstr(ext, "GLX_ARB_create_context") != NULL);
glX.supportsProfiles = (Bool)(strstr(ext, "GLX_ARB_create_context_profile") != NULL);
/* Supposedly GLX_ARB_create_context is supported. Try to resolve the symbol. If we can't,
then backout. */
if(glX.supportsCreateContextARB) {
glX.CreateContextAttribsARB = glX.GetProcAddressARB("glXCreateContextAttribsARB");
if(glX.CreateContextAttribsARB == NULL) {
glX.supportsCreateContextARB = False;
}
}
}
else /* Don't have GLX 1.4, so can't possibly have these */
{
glX.supportsCreateContextARB = False;
glX.supportsProfiles = False;
}
glX.supportsMultisample = (Bool)(strstr(ext, "GLX_ARB_multisample") != NULL);
return 1;
}
/******************************************************************************/
SST_OpenGLContext GLXCreateOpenGLContext(SST_DisplayTarget_Xlib* displayTarget, SST_WMOpenGLType apiType, const SST_OpenGLContextAttributes* attribs, SST_OpenGLContextAttributes* selectedAttribsReturn)
{
int attr[MAX_GL_ATTRS];
Display* display = displayTarget->display;
GLXFBConfig* configs;
GLXFBConfig fbconfig;
GLXContext ctx;
int nrConfigs = 0;
SST_OpenGLContext_Xlib* GLctx;
uint8_t major, minor;
GLXPbuffer pb;
const int pbattr[5] = { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, None };
/* GLX cannot be used to do anything other than standard OpenGL */
if(apiType != SSTGL_OPENGL)
return NULL;
/* GLX doesn't support multisampling, but it was explicitly requested */
if(!glX.supportsMultisample && attribs->multisampleFactor > 1)
return NULL;
/* Copy SST attributes to GLX attributes */
setupAttribs(attr, attribs, glX.supportsMultisample);
/* Get a list of configs -- we're going to only use the first one though */
configs = glX.ChooseFBConfig(display, DefaultScreen(display), attr, &nrConfigs);
if(configs == NULL)
return NULL;
/* Copy first FBConfig entry, free array */
fbconfig = *configs;
X.Free(configs);
/* Use the new create context functions? */
if(glX.supportsCreateContextARB)
{
int attr[32];
int* ptr = &attr[0];
int ctxFlags = 0;
/* Set the major/minor version */
ptr = addAttr(ptr, GLX_CONTEXT_MAJOR_VERSION_ARB, attribs->contextVersionMajor);
ptr = addAttr(ptr, GLX_CONTEXT_MINOR_VERSION_ARB, attribs->contextVersionMinor);
/* If GLX_ARB_create_context_profile is supported, add a profile mask */
if(glX.supportsProfiles)
ptr = addAttr(ptr, GLX_CONTEXT_PROFILE_MASK_ARB, (attribs->legacyEnabled ? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB : GLX_CONTEXT_CORE_PROFILE_BIT_ARB));
/* Enable/disable forward compatibility */
if(!attribs->legacyEnabled)
ctxFlags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
/* Debug mode? */
if(attribs->debugEnabled)
ctxFlags |= GLX_CONTEXT_DEBUG_BIT_ARB;
ptr = addAttr(ptr, GLX_CONTEXT_FLAGS_ARB, ctxFlags);
*ptr = None;
ctx = glX.CreateContextAttribsARB(display, fbconfig, NULL, True, attr);
if(ctx == NULL)
return NULL;
}
else /* Use older GLX 1.3 glXCreateNewContext() function */
{
/* Attempt to create an OpenGL context */
ctx = glX.CreateNewContext(display, fbconfig, GLX_RGBA_TYPE, NULL, True);
if(ctx == NULL)
return NULL;
}
/* In order to check the version number, we're going to need to create
a temporary context. I'm using a Pbuffer to do so. */
pb = glX.CreatePbuffer(display, fbconfig, pbattr);
if(pb == None)
{
glX.DestroyContext(display, ctx);
return NULL;
}
/* Activate it */
if(glX.MakeContextCurrent(display, pb, pb, ctx))
{
const char* (*pglGetString)(unsigned int);
const char* version;
/* Check libGL for this */
pglGetString = dlsym(glX.libGL, "glGetString");
version = pglGetString(GL_VERSION);
major = (uint8_t)(version[0] - '0'); /* "X.Y" -> [0]:major, [2]:minor */
minor = (uint8_t)(version[2] - '0');
/* Save context version information */
if(selectedAttribsReturn != NULL)
{
selectedAttribsReturn->contextVersionMajor = (uint8_t)major;
selectedAttribsReturn->contextVersionMinor = (uint8_t)minor;
}
/* OK, we have the information we need. Unbind the context and destroy the Pbuffer */
glX.MakeContextCurrent(display, None, None, NULL);
glX.DestroyPbuffer(display, pb);
/* If the major version is too low or the major version is OK, but the minor version is lacking, then fail */
if(major < attribs->contextVersionMajor || (major == attribs->contextVersionMajor && minor < attribs->contextVersionMinor))
{
glX.DestroyContext(display, ctx);
return NULL;
}
}
else /* Failed to make current */
{
glX.DestroyContext(display, ctx);
glX.DestroyPbuffer(display, pb);
return NULL;
}
GLctx = (SST_OpenGLContext_Xlib*)malloc(sizeof(SST_OpenGLContext_Xlib));
if(GLctx == NULL)
{
glX.DestroyContext(display, ctx);
return NULL;
}
GLctx->glxcontext = ctx;
GLctx->displayTarget = displayTarget;
GLctx->ctxVersion[0] = major;
GLctx->ctxVersion[1] = minor;
return GLctx;
}
/******************************************************************************/
static void setupAttribs(int* glxAttr, const SST_OpenGLContextAttributes* attribs, Bool supportsMultisample)
{
int* ptr = glxAttr;
int r, b, g;
switch(attribs->colorBits)
{
/* Since these values are "at least", 5-5-5 and 5-6-5 configurations can use the same number */
case 15:
case 16:
r = g = b = 5;
break;
case 24:
r = g = b = 8;
}
ptr = addAttr(ptr, GLX_DOUBLEBUFFER, True);
ptr = addAttr(ptr, GLX_STEREO, (attribs-> stereoEnabled != 0));
ptr = addAttr(ptr, GLX_RED_SIZE, r);
ptr = addAttr(ptr, GLX_GREEN_SIZE, r);
ptr = addAttr(ptr, GLX_BLUE_SIZE, b);
ptr = addAttr(ptr, GLX_ALPHA_SIZE, ((int)attribs->alphaBits) & 0xFF);
ptr = addAttr(ptr, GLX_DEPTH_SIZE, ((int)attribs->depthBits) & 0xFF);
ptr = addAttr(ptr, GLX_STENCIL_SIZE, ((int)attribs->stencilBits) & 0xFF);
/* Add multisample attributes as appropriate */
if(attribs->multisampleFactor > 1 && supportsMultisample)
{
ptr = addAttr(ptr, GLX_SAMPLE_BUFFERS_ARB, 1);
ptr = addAttr(ptr, GLX_SAMPLES_ARB, ((int)attribs->multisampleFactor) & 0xFF);
}
/* End attribute list */
*ptr = None;
}