/** * compile with: gcc -o contextRetargetDrawable02 contextRetargetDrawable02.c -lX11 -lGL */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <X11/X.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <GL/glx.h> #include <GL/gl.h> typedef int bool; #define true 1 #define false 0 static PFNGLXSWAPINTERVALSGIPROC _glXSwapIntervalSGI = NULL; static void testRetarget(bool reverse); static const char * msg = "contextRetargetDrawable01"; static const useconds_t demodelay = 2 * 1000 * 1000; int main(int nargs, char **vargs) { _glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) glXGetProcAddressARB("glXSwapIntervalSGI"); if(NULL == _glXSwapIntervalSGI) { fprintf(stderr, "No glXSwapIntervalSGI avail, bail out\n"); return 1; } testRetarget(false); return 0; } static void createGLWin(Display *dpy, int width, int height, Window *rWin, GLXContext *rCtx); static void useGL(Display *dpy, Window win, GLXContext ctx, int width, int height, float c, int swapInterval); static void testRetarget(bool reverse) { int major, minor; Display *disp1; Window win1; GLXContext ctx1; Display *disp2; Window win2; GLXContext ctx2; fprintf(stderr, "%s: Create #1\n", msg); disp1 = XOpenDisplay(NULL); createGLWin(disp1, 200, 200, &win1, &ctx1); fprintf(stderr, "%s: Create #2\n", msg); disp2 = disp1; // disp2 = XOpenDisplay(NULL); createGLWin(disp2, 300, 300, &win2, &ctx2); fprintf(stderr, "%s: Use #1.1\n", msg); useGL(disp1, win1, ctx1, 200, 200, 0.0f, 1); // OK fprintf(stderr, "%s: Use #1.2\n", msg); useGL(disp2, win2, ctx2, 300, 300, 1.0f, 1); // OK usleep( demodelay ); fprintf(stderr, "%s: Retarget Drawable\n", msg); { GLXContext _ctx = ctx2; ctx2 = ctx1; ctx1 = _ctx; } /** if(reverse) { fprintf(stderr, "%s: Use #2.2\n", msg); useGL(disp2, win2, ctx2, 300, 300, 1.0f, 0); // no setSwapInterval - OK fprintf(stderr, "%s: Use #2.1\n", msg); useGL(disp1, win1, ctx1, 200, 200, 0.0f, 0); // no setSwapInterval - OK } else { fprintf(stderr, "%s: Use #2.1\n", msg); useGL(disp1, win1, ctx1, 200, 200, 0.0f, 0); // no setSwapInterval - OK fprintf(stderr, "%s: Use #2.2\n", msg); useGL(disp2, win2, ctx2, 300, 300, 1.0f, 0); // no setSwapInterval - OK } usleep( demodelay ); */ if(reverse) { fprintf(stderr, "%s: Use #3.2\n", msg); useGL(disp2, win2, ctx2, 300, 300, 0.9f, 1); // setSwapInterval - crash on Mesa 8.0.4 DRI2 fprintf(stderr, "%s: Use #3.1\n", msg); useGL(disp1, win1, ctx1, 200, 200, 0.1f, 1); // setSwapInterval - crash on Mesa 8.0.4 DRI2 } else { fprintf(stderr, "%s: Use #3.1\n", msg); useGL(disp1, win1, ctx1, 200, 200, 0.1f, 1); // setSwapInterval - crash on Mesa 8.0.4 DRI2 fprintf(stderr, "%s: Use #3.2\n", msg); useGL(disp2, win2, ctx2, 300, 300, 0.9f, 1); // setSwapInterval - crash on Mesa 8.0.4 DRI2 } fprintf(stderr, "%s: Success - no bug\n", msg); usleep( demodelay ); fprintf(stderr, "%s: Destroy #1.0\n", msg); glXMakeContextCurrent(disp1, 0, 0, 0); glXDestroyContext(disp1, ctx1); if( disp1 != disp2 ) { XCloseDisplay(disp1); } fprintf(stderr, "%s: Destroy #1.X\n", msg); fprintf(stderr, "%s: Destroy #2.0\n", msg); glXMakeContextCurrent(disp2, 0, 0, 0); glXDestroyContext(disp2, ctx2); XCloseDisplay(disp2); fprintf(stderr, "%s: Destroy #2.X\n", msg); fprintf(stderr, "%s: Exit - OK\n", msg); } static void useGL(Display *dpy, Window win, GLXContext ctx, int width, int height, float c, int swapInterval) { glXMakeContextCurrent(dpy, win, win, ctx); glViewport(0, 0, width, height); if(0 < swapInterval) { fprintf(stderr, "%s: glXSwapIntervalSGI(1)\n", msg); _glXSwapIntervalSGI(1); // offending op after retargeting drawable } fprintf(stderr, "GL_VENDOR: %s\n", glGetString(GL_VENDOR)); fprintf(stderr, "GL_VERSION: %s\n", glGetString(GL_VERSION)); fprintf(stderr, "GL_RENDERER: %s\n", glGetString(GL_RENDERER)); glClearColor(c, c, c, 0.0f); glClearDepth(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glXSwapBuffers(dpy, win); glXMakeContextCurrent(dpy, 0, 0, 0); } static volatile bool ctxErrorOccurred = false; static int ctxErrorHandler( Display *dpy, XErrorEvent *e ) { const char * errnoStr = strerror(errno); char errCodeStr[80]; char reqCodeStr[80]; snprintf(errCodeStr, sizeof(errCodeStr), "%d", e->request_code); XGetErrorDatabaseText(dpy, "XRequest", errCodeStr, "Unknown", reqCodeStr, sizeof(reqCodeStr)); XGetErrorText(dpy, e->error_code, errCodeStr, sizeof(errCodeStr)); fprintf(stderr, "X11 Error: %d - %s, dpy %p, id %x, # %d: %d:%d %s\n", e->error_code, errCodeStr, e->display, (int)e->resourceid, (int)e->serial, (int)e->request_code, (int)e->minor_code, reqCodeStr); fflush(stderr); ctxErrorOccurred = true; return 0; } /* attributes for a double buffered visual in RGBA format with at least * 8 bits per color and a 16 bit depth buffer */ static int visual_attribs[] = { GLX_X_RENDERABLE , True, GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, GLX_RENDER_TYPE , GLX_RGBA_BIT, GLX_RED_SIZE , 8, GLX_GREEN_SIZE , 8, GLX_BLUE_SIZE , 8, GLX_DEPTH_SIZE , 16, GLX_DOUBLEBUFFER , True, GLX_STEREO , False, GLX_TRANSPARENT_TYPE, GLX_NONE, //GLX_SAMPLE_BUFFERS , 1, //GLX_SAMPLES , 4, None }; static int context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 0, GLX_RENDER_TYPE , GLX_RGBA_TYPE, GLX_CONTEXT_FLAGS_ARB , 0, // GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, // GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, None }; static bool isExtensionSupported(const char *extList, const char *extension); static void createGLWin(Display *dpy, int width, int height, Window *rWin, GLXContext *rCtx) { int glx_major, glx_minor; // FBConfigs were added in GLX version 1.3. if ( !glXQueryVersion( dpy, &glx_major, &glx_minor ) || ( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) ) { printf( "Invalid GLX version" ); exit(1); } int fbcount; GLXFBConfig *fbc = glXChooseFBConfig( dpy, DefaultScreen( dpy ), visual_attribs, &fbcount ); if ( !fbc || 0 == fbcount ) { printf( "Failed to retrieve a framebuffer config\n" ); exit(1); } printf( "Found %d matching FB configs.\n", fbcount ); GLXFBConfig bestFbc = fbc[ 0 ]; int bestFbcID = 0; if( 0 != glXGetFBConfigAttrib( dpy, bestFbc, GLX_FBCONFIG_ID, &bestFbcID ) ) { printf( "Invalid FBConfigID\n" ); exit(1); } printf( "Chosen FBConfigID = 0x%x\n", bestFbcID); XVisualInfo *vi = glXGetVisualFromFBConfig( dpy, bestFbc ); printf( "Chosen visual ID = 0x%x\n", (int) vi->visualid ); XSetWindowAttributes swa; Colormap cmap; swa.colormap = cmap = XCreateColormap( dpy, RootWindow( dpy, vi->screen ), vi->visual, AllocNone ); swa.background_pixmap = None ; swa.border_pixel = 0; swa.event_mask = StructureNotifyMask; printf( "Creating window\n" ); Window win = XCreateWindow( dpy, RootWindow( dpy, vi->screen ), 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa ); if ( !win ) { printf( "Failed to create window.\n" ); exit(1); } // Done with the visual info data XFree( vi ); XStoreName( dpy, win, "GL Window" ); XMapWindow( dpy, win ); *rWin = win; GLXContext ctx0 = glXCreateNewContext( dpy, bestFbc, GLX_RGBA_TYPE, 0, True ); if( !ctx0 ) { printf( "Failed to create intermediate old OpenGL context\n" ); exit(1); } glXMakeContextCurrent(dpy, win, win, ctx0); // Get the default screen's GLX extension list const char *glxExts01 = glXQueryExtensionsString( dpy, DefaultScreen( dpy ) ); const char *glxExts02 = glXGetClientString( dpy, GLX_EXTENSIONS); const char *glxExts03 = glXQueryServerString( dpy, DefaultScreen( dpy ), GLX_EXTENSIONS); // NOTE: It is not necessary to create or make current to a context before // calling glXGetProcAddressARB PFNGLXCREATECONTEXTATTRIBSARBPROC _glXCreateContextAttribsARB = 0; _glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); // Check for the GLX_ARB_create_context extension string and the function. // If either is not present, use GLX 1.3 context creation method. bool isGLX_ARB_create_contextAvail = isExtensionSupported( glxExts01, "GLX_ARB_create_context" ) || isExtensionSupported( glxExts02, "GLX_ARB_create_context" ) || isExtensionSupported( glxExts03, "GLX_ARB_create_context" ); glXMakeContextCurrent(dpy, 0, 0, 0); GLXContext ctx = 0; // Install an X error handler so the application won't exit if GL 3.0 // context allocation fails. // // Note this error handler is global. All display connections in all threads // of a process use the same error handler, so be sure to guard against other // threads issuing X commands while this code is running. int (*oldHandler)(Display*, XErrorEvent*) = XSetErrorHandler(&ctxErrorHandler); if ( !isGLX_ARB_create_contextAvail || !_glXCreateContextAttribsARB ) { printf( "glXCreateContextAttribsARB() not found (ext %d, func %p)" " ... using old-style GLX context\n", isGLX_ARB_create_contextAvail, _glXCreateContextAttribsARB ); printf( "extensions 01: %s\n", glxExts01); printf( "extensions 02: %s\n", glxExts02); printf( "extensions 03: %s\n", glxExts03); ctx = ctx0; } // If it does, try to get a GL 3.0 context! else { printf( "Creating context\n" ); XSync( dpy, False ); ctxErrorOccurred = false; ctx = _glXCreateContextAttribsARB( dpy, bestFbc, 0, True, context_attribs ); XSync( dpy, False ); if ( !ctxErrorOccurred && ctx ) { printf( "Created GL 3.0 context\n" ); glXDestroyContext(dpy, ctx0); // get rid of old ctx } else { // Couldn't create GL 3.0 context. Fall back to old-style 2.x context. // When a context version below 3.0 is requested, implementations will // return the newest context version compatible with OpenGL versions less // than version 3.0. // GLX_CONTEXT_MAJOR_VERSION_ARB = 1 context_attribs[1] = 1; // GLX_CONTEXT_MINOR_VERSION_ARB = 0 context_attribs[3] = 0; printf( "Failed to create GL 3.0 context (err %d, ctx %p)" " ... using old-style GLX context\n", ctxErrorOccurred, (void*)ctx ); ctx = ctx0; ctxErrorOccurred = false; } } // Sync to ensure any errors generated are processed. XSync( dpy, False ); // Restore the original error handler XSetErrorHandler( oldHandler ); if ( ctxErrorOccurred || !ctx ) { printf( "Failed to create an OpenGL context\n" ); exit(1); } XFree( fbc ); *rCtx = ctx; } // Helper to check for extension string presence. Adapted from: // http://www.opengl.org/resources/features/OGLextensions/ static bool isExtensionSupported(const char *extList, const char *extension) { const char *start; const char *where, *terminator; /* Extension names should not have spaces. */ where = strchr(extension, ' '); if ( where || *extension == '\0' ) return false; /* It takes a bit of care to be fool-proof about parsing the OpenGL extensions string. Don't be fooled by sub-strings, etc. */ for ( start = extList; ; ) { where = strstr( start, extension ); if ( !where ) break; terminator = where + strlen( extension ); if ( where == start || *(where - 1) == ' ' ) if ( *terminator == ' ' || *terminator == '\0' ) return true; start = terminator; } return false; }