Created
April 2, 2025 20:50
-
-
Save DanielGibson/25d1ce26a18e6e7cbc0acd4dd1d633bd to your computer and use it in GitHub Desktop.
Minimal OpenGL on X11 example, to reproduce OpenGL driver bug
This file contains hidden or 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
// 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