Skip to content

Instantly share code, notes, and snippets.

@DanielGibson
Created April 2, 2025 20:50
Show Gist options
  • Save DanielGibson/25d1ce26a18e6e7cbc0acd4dd1d633bd to your computer and use it in GitHub Desktop.
Save DanielGibson/25d1ce26a18e6e7cbc0acd4dd1d633bd to your computer and use it in GitHub Desktop.
Minimal OpenGL on X11 example, to reproduce OpenGL driver bug
// minimal glx application, build with: gcc -o glxsimple glxsimple.c -lGL -lGLX -lX11
// based on http://andrewd.ces.clemson.edu/courses/graphics/examples/glx-simple/glxsimple.c
// with minimal changes to make it constantly draw frames, not just on input
// doesn't properly shut down when closing the window, but that doesn't matter here
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <GL/glx.h>
#include <GL/gl.h>
/* Prototypes */
int main(int argc, char **argv);
void redraw(void);
void fatalError(char *);
static int sngBuf[] = { GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DEPTH_SIZE, 12,
None };
static int dblBuf[] = { GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DEPTH_SIZE, 12,
GLX_DOUBLEBUFFER,
None };
Display *dpy;
Window win;
Bool doubleBuffer = True;
/* Initial 3d box orientation. */
GLfloat xAngle = 42.0, yAngle = 82.0, zAngle = 112.0;
int main(int argc, char **argv)
{
XVisualInfo *vi = NULL;
Colormap cmap;
XSetWindowAttributes swa;
GLXContext cx;
XEvent event;
//Bool needRedraw = False, recalcModelView = True;
int dummy;
/* Step 1. Open a connection to the X server. If an unexpected condition
occurs, fatalError will print an explanation and exit.
*/
if(!(dpy = XOpenDisplay(NULL)))
fatalError("could not open display");
/* Step 2. Make sure OpenGL's GLX extension is supported. The
glXQueryExtension also returns the GLX extension's error base and event
base. For almost all OpenGL programs, this information is irrelevant;
hence the use of dummy.
*/
if(!glXQueryExtension(dpy, &dummy, &dummy))
fatalError("X server has no OpenGL GLX extension");
/* Step 3. Find an appropriate OpenGL-capable visual. Look for double
buffering first; if it is not found, settle for a single buffered visual.
*/
if(!(vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf))) {
if(!(vi = glXChooseVisual(dpy, DefaultScreen(dpy), sngBuf)))
fatalError("no RGB visual with depth buffer");
doubleBuffer = False;
}
if(vi->class != TrueColor)
fatalError("TrueColor visual required for this program");
/* Step 4. Create an OpenGL rendering context. */
if(!(cx = glXCreateContext(dpy, vi,
None, /* no sharing of display lists */
True /* direct rendering if possible */
)))
fatalError("could not create rendering context");
/* Step 5. Create an X window with the selected visual. Since the visual
selected is likely not be the default, create an X colormap for use.
*/
cmap = XCreateColormap(dpy,RootWindow(dpy,vi->screen),vi->visual,AllocNone);
swa.colormap = cmap;
swa.border_pixel = 0;
swa.event_mask = ExposureMask | ButtonPressMask | StructureNotifyMask;
win = XCreateWindow(dpy,RootWindow(dpy,vi->screen),0,0,300,300,
0,vi->depth,InputOutput,vi->visual,
CWBorderPixel | CWColormap | CWEventMask,
&swa);
XSetStandardProperties(dpy,win,"glxsimple","glxsimple",None,argv,argc,NULL);
/* Step 6. Bind the rendering context to the window. */
glXMakeCurrent(dpy, win, cx);
/* Step 7. Request that the X window be displayed on the screen. */
XMapWindow(dpy, win);
/* Step 8. Configure the OpenGL context for rendering. */
glEnable(GL_DEPTH_TEST); /* enable depth buffering */
glMatrixMode(GL_PROJECTION); /* set up projection transform */
glLoadIdentity();
glFrustum(-1.0,1.0,-1.0,1.0,1.0,10.0);
/* Step 9. Dispatch X events. The program handles only three event types.
A mouse ButtonPress event will rotate the cube. Each button updates a
different axis of rotation and sets the recalcModelView flag. A
ConfigureNotify event informs the program when the window is resized.
In response, the program updates the OpenGL viewport to render to the
full size of the window. In an Expose event is received, indicating
that the window needs to be redrawn, this is noted with the needRedraw
flag. To minimize the work to be done, we reas as many events as are
ready to be received before acting on the events.
*/
int keepRunning = 1;
while(keepRunning) {
while(XPending(dpy)) {
XNextEvent(dpy, &event);
switch(event.type) {
case ButtonPress:
//recalcModelView = True;
switch(event.xbutton.button) {
case 1: xAngle += 10.0; break;
case 2: yAngle += 10.0; break;
case 3: zAngle += 10.0; break;
}
break;
case ConfigureNotify:
glViewport(0,0,event.xconfigure.width,event.xconfigure.height);
/* fall through */
case Expose:
//needRedraw = True;
break;
}
}
zAngle += 0.1; // rotate a bit each frame so one can see that frames are rendered
//if(recalcModelView)
{
glMatrixMode(GL_MODELVIEW); /* switch to model matrix stack */
glLoadIdentity(); /* reset modelview matrix to identity */
glTranslatef(0.0,0.0,-3.0); /* move camera back 3 units */
glRotatef(xAngle,0.1,0.0,0.0); /* rotate by X angle */
glRotatef(yAngle,0.0,0.1,0.0); /* rotate by Y angle */
glRotatef(zAngle,0.0,0.0,1.0); /* rotate by Z angle */
//recalcModelView = False;
//needRedraw = True;
}
redraw();
}
return 0;
}
void redraw(void)
{
static Bool displayListInited = False;
if(displayListInited)
glCallList(1); /* if list exists, execute it */
else {
glNewList(1,GL_COMPILE_AND_EXECUTE); /* else create and execute it */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_QUADS);
/* Front face */
glColor3f(0.0,0.7,0.1); /* green */
glVertex3f(-1.0, 1.0, 1.0);
glVertex3f( 1.0, 1.0, 1.0);
glVertex3f( 1.0,-1.0, 1.0);
glVertex3f(-1.0,-1.0, 1.0);
/* Back face */
glColor3f(0.9,1.0,0.0); /* yellow */
glVertex3f(-1.0, 1.0,-1.0);
glVertex3f( 1.0, 1.0,-1.0);
glVertex3f( 1.0,-1.0,-1.0);
glVertex3f(-1.0,-1.0,-1.0);
/* Top side face */
glColor3f(0.2,0.2,1.0); /* blue */
glVertex3f(-1.0, 1.0, 1.0);
glVertex3f( 1.0, 1.0, 1.0);
glVertex3f( 1.0, 1.0,-1.0);
glVertex3f(-1.0, 1.0,-1.0);
/* Bottom side face */
glColor3f(0.7,0.0,0.1); /* red */
glVertex3f(-1.0,-1.0, 1.0);
glVertex3f( 1.0,-1.0, 1.0);
glVertex3f( 1.0,-1.0,-1.0);
glVertex3f(-1.0,-1.0,-1.0);
glEnd();
glEndList();
displayListInited = True;
}
if(doubleBuffer)
glXSwapBuffers(dpy,win); /* buffer swap does implicit glFlush. */
else
glFlush(); /* explicit flush for single buf case */
}
void fatalError(char *estr)
{
fprintf(stderr, "%s", estr);
exit(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment