Skip to content

Instantly share code, notes, and snippets.

@tspspi

tspspi/Makefile Secret

Created June 12, 2021 22:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tspspi/2021392f3dd2497d7aa27d59d812e634 to your computer and use it in GitHub Desktop.
Save tspspi/2021392f3dd2497d7aa27d59d812e634 to your computer and use it in GitHub Desktop.
Simple OpenGL context creation using Xlib
#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;
}
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