-
-
Save tspspi/2021392f3dd2497d7aa27d59d812e634 to your computer and use it in GitHub Desktop.
Simple OpenGL context creation using Xlib
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <GL/gl.h> | |
#include <GL/glx.h> | |
#include <time.h> | |
#define DEBUG 1 | |
#ifndef __cplusplus | |
typedef unsigned long int bool; | |
#define false 0 | |
#define true 1 | |
#endif | |
typedef int (*lpfnErrorHandler)(Display* display, XErrorEvent* ev); | |
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); | |
static bool glCheckExtensionSupported( | |
const char* lpExtensionString, | |
const char* lpExtensionName | |
) { | |
unsigned long int dwCurrentStart = 0; | |
unsigned long int dwCurrentEnd = 0; | |
unsigned long int extStrLen; | |
if((lpExtensionName == NULL) || (lpExtensionString == NULL)) { | |
return false; | |
} | |
extStrLen = strlen(lpExtensionString); | |
if((lpExtensionString[0] == 0x00) || (extStrLen == 0)) { | |
return false; | |
} | |
for(; dwCurrentEnd <= extStrLen; dwCurrentEnd = dwCurrentEnd + 1) { | |
if((lpExtensionString[dwCurrentEnd] != ' ') && (lpExtensionString[dwCurrentEnd] != 0x00)) { | |
continue; | |
} | |
if(strncmp(lpExtensionName, &(lpExtensionString[dwCurrentStart]), dwCurrentEnd - dwCurrentStart) == 0) { | |
return true; | |
} | |
if(lpExtensionString[dwCurrentEnd] == 0x00) { | |
return false; | |
} | |
dwCurrentStart = dwCurrentEnd + 1; | |
} | |
return false; | |
} | |
/* | |
Simple error handler used during creation | |
*/ | |
static bool bGLnitErrorRaised = false; | |
static int glInitErrorHandler(Display* display, XErrorEvent* ev) { | |
bGLnitErrorRaised = true; | |
return 0; | |
} | |
static int visualAttributes[] = { | |
GLX_X_RENDERABLE, True, | |
GLX_RENDER_TYPE, GLX_RGBA_BIT, | |
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, | |
GLX_RED_SIZE, 8, | |
GLX_GREEN_SIZE, 8, | |
GLX_BLUE_SIZE, 8, | |
GLX_ALPHA_SIZE, 8, | |
GLX_DEPTH_SIZE, 24, | |
GLX_STENCIL_SIZE, 8, | |
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, | |
GLX_DOUBLEBUFFER, True, | |
None | |
}; | |
int main(int argc, char* argv[]) { | |
/* | |
Open X display | |
*/ | |
Display *display = XOpenDisplay(NULL); | |
if(!display) { | |
printf("Failed to open X display\n"); | |
return 1; | |
} | |
/* | |
Verify GLX version | |
*/ | |
{ | |
int dwGLXVersionMajor; | |
int dwGLXVersionMinor; | |
if(!glXQueryVersion(display, &dwGLXVersionMajor, &dwGLXVersionMinor)) { | |
printf("Failed to query GLX version\n"); | |
XCloseDisplay(display); | |
return 1; | |
} | |
if((dwGLXVersionMajor < 1) || (dwGLXVersionMinor < 3)) { | |
printf("Invalid GLX version %d.%d\n", dwGLXVersionMajor, dwGLXVersionMinor); | |
XCloseDisplay(display); | |
return 1; | |
} | |
} | |
/* | |
Get a framebuffer configuration | |
*/ | |
GLXFBConfig cfgChoosen; | |
{ | |
int dwConfigurationCount; | |
int i; | |
GLXFBConfig* fbConfig = glXChooseFBConfig( | |
display, | |
DefaultScreen(display), | |
visualAttributes, | |
&dwConfigurationCount | |
); | |
if((!fbConfig) || (dwConfigurationCount == 0)) { | |
printf("Failed to query framebuffer configuration\n"); | |
XCloseDisplay(display); | |
return 1; | |
} | |
#ifdef DEBUG | |
printf("Matching visuals:\n"); | |
#endif | |
int dwIndexBestConfig = 0; | |
int dwBestSamples = 0; | |
for(i=0; i < dwConfigurationCount; i++) { | |
XVisualInfo* vi = glXGetVisualFromFBConfig(display, fbConfig[i]); | |
if(vi) { | |
int dwSampleBuffers = 0; | |
int dwSamples = 0; | |
glXGetFBConfigAttrib(display, fbConfig[i], GLX_SAMPLE_BUFFERS, &dwSampleBuffers); | |
glXGetFBConfigAttrib(display, fbConfig[i], GLX_SAMPLES, &dwSamples ); | |
#ifdef DEBUG | |
printf("\t0x%02lx, Sample buffers: %d, Samples: %d\n", vi->visualid, dwSampleBuffers, dwSamples); | |
#endif | |
if((dwBestSamples < dwSamples) && (dwSampleBuffers > 0)) { | |
dwBestSamples = dwSamples; | |
dwIndexBestConfig = i; | |
} | |
} | |
} | |
memcpy(&cfgChoosen, &(fbConfig[dwIndexBestConfig]), sizeof(cfgChoosen)); | |
XFree(fbConfig); | |
} | |
/* | |
Create window | |
*/ | |
Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); | |
XVisualInfo* viChoosen = glXGetVisualFromFBConfig(display, cfgChoosen); | |
Colormap cmap; | |
cmap = XCreateColormap( | |
display, | |
RootWindow(display, viChoosen->screen), | |
viChoosen->visual, | |
AllocNone | |
); | |
XSetWindowAttributes swa; | |
swa.colormap = cmap; | |
swa.background_pixmap = None; | |
swa.border_pixel = 0; | |
swa.event_mask = StructureNotifyMask; | |
Window wndWindow = XCreateWindow( | |
display, | |
RootWindow(display, viChoosen->screen), | |
0, 0, | |
250, 250, | |
0, | |
viChoosen->depth, | |
InputOutput, | |
viChoosen->visual, | |
CWBorderPixel|CWColormap|CWEventMask, | |
&swa | |
); | |
if(!wndWindow) { | |
printf("Failed to create window\n"); | |
XFree(viChoosen); | |
XCloseDisplay(display); | |
return 1; | |
} | |
XFree(viChoosen); | |
/* | |
Map the window | |
*/ | |
XStoreName(display, wndWindow, "OpenGL context window"); | |
XMapWindow(display, wndWindow); | |
XSetWMProtocols(display, wndWindow, &wmDeleteMessage, 1); | |
XSync(display, False); | |
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; | |
{ | |
const char* lpGLExtensions = glXQueryExtensionsString(display, DefaultScreen(display)); | |
#ifdef DEBUG | |
printf("GL Extensions: %s\n", lpGLExtensions); | |
#endif | |
if(!glCheckExtensionSupported(lpGLExtensions, "GLX_ARB_create_context")) { | |
printf("GLX_ARB_create_context is not supported\n"); | |
XDestroyWindow(display, wndWindow); | |
XCloseDisplay(display); | |
return 1; | |
} | |
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); | |
if(!glXCreateContextAttribsARB) { | |
printf("Failed to query function pointer to glXCreateContextAttribsARB\n"); | |
XDestroyWindow(display, wndWindow); | |
XCloseDisplay(display); | |
return 1; | |
} | |
} | |
GLXContext ctx = 0; | |
{ | |
lpfnErrorHandler oldHandler = XSetErrorHandler(&glInitErrorHandler); | |
int contextAttributes[] = { | |
GLX_CONTEXT_MAJOR_VERSION_ARB, 3, | |
GLX_CONTEXT_MINOR_VERSION_ARB, 0, | |
None | |
}; | |
ctx = glXCreateContextAttribsARB( | |
display, | |
cfgChoosen, | |
0, | |
True, | |
contextAttributes | |
); | |
/* Flush */ | |
XSync(display, False); | |
if(bGLnitErrorRaised || (!ctx)) { | |
#ifdef DEBUG | |
printf("Failed to create 3.0 context\n"); | |
#endif | |
/* | |
Fallback context creation ... IF we want to support OpenGL < 3.0 | |
*/ | |
bGLnitErrorRaised = false; | |
/* | |
Use Major version 1, minor version 0. Will return the newest | |
possible context version ... | |
*/ | |
contextAttributes[1] = 1; | |
contextAttributes[3] = 0; | |
ctx = glXCreateContextAttribsARB( | |
display, | |
cfgChoosen, | |
0, | |
True, | |
contextAttributes | |
); | |
if(bGLnitErrorRaised || (!ctx)) { | |
printf("Failed to create any legacy context\n"); | |
XDestroyWindow(display, wndWindow); | |
XCloseDisplay(display); | |
return 1; | |
} | |
#ifdef DEBUG | |
printf("Created legacy context ...\n"); | |
#endif | |
} | |
/* Restore old error handler */ | |
XSetErrorHandler(oldHandler); | |
} | |
/* | |
In case we want to know if the context is direct or indirect we | |
may verify this too ... this might be required for some applications | |
to determine if they're capable of using OpenGL in a performant way. | |
This is especially important if you generate geometry from a geometry | |
shader and avoid roundtrips via the CPU to determine buffer sizes, etc. | |
*/ | |
#ifdef DEBUG | |
if(glXIsDirect(display, ctx)) { | |
printf("Context is direct\n"); | |
} else { | |
printf("Indirect context obtained\n"); | |
} | |
#endif | |
/* | |
Simple event loop just clearing the color buffer with alternating | |
colors every 250 ms (with a busy waiting loop - not the nicest approach) | |
*/ | |
glXMakeCurrent(display, wndWindow, ctx); | |
unsigned long int dwLastFrame = 0; | |
float fRed = 0.0f; | |
float fGreen = 0.0f; | |
float fBlue = 0.0f; | |
for(;;) { | |
if(XEventsQueued(display, QueuedAfterReading)) { | |
XEvent xev; | |
XNextEvent(display, &xev); | |
if(xev.type == ClientMessage) { | |
if(xev.xclient.data.l[0] == wmDeleteMessage) { | |
glXMakeCurrent(display, 0, 0); | |
XDestroyWindow(display, xev.xclient.window); | |
wndWindow = 0; | |
break; | |
} | |
} | |
/* HANDLE EVENTS HERE */ | |
} else { | |
/* | |
Here the time since the last frame is calculated in a | |
busy waiting mechanism. This is ugly but might be interesting | |
for some specific applications. Usually you should choose | |
either the approach of waiting for events using the | |
blocking XNextEvent (when programming applications that only | |
change on UI events) or use some kind of event notification | |
mechanism like kqueue/kevent - one can obtain the handle used | |
by Xlib by using ConnectionNumber(display) and use this directly | |
in kqueue / select. | |
*/ | |
unsigned long int dwTS; | |
unsigned long int dwDeltaTS; | |
struct timespec spec; | |
clock_gettime(CLOCK_REALTIME, &spec); | |
dwTS = spec.tv_sec * 1000 + spec.tv_nsec / 1000000; | |
if(dwTS > dwLastFrame) { | |
dwDeltaTS = dwTS - dwLastFrame; | |
} else { | |
dwDeltaTS = (~0) - dwTS + dwLastFrame; | |
} | |
if(dwDeltaTS > 250) { | |
/* Next frame ... */ | |
dwLastFrame = dwTS; | |
glClearColor(fRed, fGreen, fBlue, 1); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glXSwapBuffers(display, wndWindow); | |
fRed = fRed + 0.1; | |
if(fRed > 1) { | |
fRed = 0; | |
fGreen = fGreen + 0.1; | |
} | |
if(fGreen > 1.0) { | |
fGreen = 0; | |
fBlue = fBlue + 0.1; | |
} | |
if(fBlue > 1) { | |
fBlue = 0; | |
} | |
} | |
} | |
} | |
/* | |
Cleanup | |
*/ | |
if((ctx) && (wndWindow)) { | |
glXMakeCurrent(display, 0, 0); | |
glXDestroyContext(display, ctx); | |
} | |
if(wndWindow) { XDestroyWindow(display, wndWindow); wndWindow = 0; } | |
if(display) { XCloseDisplay(display); display = NULL; } | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
contextsample: contextsample.c | |
clang -Wall -ansi -std=c99 -L/usr/local/lib/ -I/usr/local/include/ -o context2 context2.c -lGL -lX11 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment