1 /**
2  * compile with: gcc -o contextRetargetDrawable02 contextRetargetDrawable02.c -lX11 -lGL
3  */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <X11/X.h>
11 #include <X11/Xlib.h>
12 #include <X11/Xutil.h>
13 #include <GL/glx.h>
14 #include <GL/gl.h>
15 
16 typedef int bool;
17 #define true 1
18 #define false 0
19 
20 static PFNGLXSWAPINTERVALSGIPROC _glXSwapIntervalSGI = NULL;
21 
22 static void testRetarget(bool reverse);
23 
24 static const char * msg = "contextRetargetDrawable01";
25 
26 static const useconds_t demodelay = 2 * 1000 * 1000;
27 
main(int nargs,char ** vargs)28 int main(int nargs, char **vargs) {
29     _glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) glXGetProcAddressARB("glXSwapIntervalSGI");
30     if(NULL == _glXSwapIntervalSGI) {
31         fprintf(stderr, "No glXSwapIntervalSGI avail, bail out\n");
32         return 1;
33     }
34     testRetarget(false);
35     return 0;
36 }
37 
38 static void createGLWin(Display *dpy, int width, int height, Window *rWin, GLXContext *rCtx);
39 static void useGL(Display *dpy, Window win, GLXContext ctx, int width, int height, float c, int swapInterval);
40 
testRetarget(bool reverse)41 static void testRetarget(bool reverse) {
42     int major, minor;
43     Display *disp1;
44     Window win1;
45     GLXContext ctx1;
46 
47     Display *disp2;
48     Window win2;
49     GLXContext ctx2;
50 
51     fprintf(stderr, "%s: Create #1\n", msg);
52     disp1 = XOpenDisplay(NULL);
53     createGLWin(disp1, 200, 200, &win1, &ctx1);
54 
55     fprintf(stderr, "%s: Create #2\n", msg);
56     disp2 = disp1;
57     // disp2 = XOpenDisplay(NULL);
58     createGLWin(disp2, 300, 300, &win2, &ctx2);
59 
60     fprintf(stderr, "%s: Use #1.1\n", msg);
61     useGL(disp1, win1, ctx1, 200, 200, 0.0f, 1); // OK
62 
63     fprintf(stderr, "%s: Use #1.2\n", msg);
64     useGL(disp2, win2, ctx2, 300, 300, 1.0f, 1); // OK
65 
66     usleep( demodelay );
67 
68     fprintf(stderr, "%s: Retarget Drawable\n", msg);
69     {
70         GLXContext _ctx = ctx2;
71         ctx2 = ctx1;
72         ctx1 = _ctx;
73     }
74 
75     /**
76     if(reverse) {
77         fprintf(stderr, "%s: Use #2.2\n", msg);
78         useGL(disp2, win2, ctx2, 300, 300, 1.0f, 0); // no setSwapInterval - OK
79 
80         fprintf(stderr, "%s: Use #2.1\n", msg);
81         useGL(disp1, win1, ctx1, 200, 200, 0.0f, 0); // no setSwapInterval - OK
82     } else {
83         fprintf(stderr, "%s: Use #2.1\n", msg);
84         useGL(disp1, win1, ctx1, 200, 200, 0.0f, 0); // no setSwapInterval - OK
85 
86         fprintf(stderr, "%s: Use #2.2\n", msg);
87         useGL(disp2, win2, ctx2, 300, 300, 1.0f, 0); // no setSwapInterval - OK
88     }
89     usleep( demodelay ); */
90 
91     if(reverse) {
92         fprintf(stderr, "%s: Use #3.2\n", msg);
93         useGL(disp2, win2, ctx2, 300, 300, 0.9f, 1); // setSwapInterval - crash on Mesa 8.0.4 DRI2
94 
95         fprintf(stderr, "%s: Use #3.1\n", msg);
96         useGL(disp1, win1, ctx1, 200, 200, 0.1f, 1); // setSwapInterval - crash on Mesa 8.0.4 DRI2
97     } else {
98         fprintf(stderr, "%s: Use #3.1\n", msg);
99         useGL(disp1, win1, ctx1, 200, 200, 0.1f, 1); // setSwapInterval - crash on Mesa 8.0.4 DRI2
100 
101         fprintf(stderr, "%s: Use #3.2\n", msg);
102         useGL(disp2, win2, ctx2, 300, 300, 0.9f, 1); // setSwapInterval - crash on Mesa 8.0.4 DRI2
103     }
104     fprintf(stderr, "%s: Success - no bug\n", msg);
105     usleep( demodelay );
106 
107     fprintf(stderr, "%s: Destroy #1.0\n", msg);
108     glXMakeContextCurrent(disp1, 0, 0, 0);
109     glXDestroyContext(disp1, ctx1);
110     if( disp1 != disp2 ) {
111         XCloseDisplay(disp1);
112     }
113     fprintf(stderr, "%s: Destroy #1.X\n", msg);
114 
115     fprintf(stderr, "%s: Destroy #2.0\n", msg);
116     glXMakeContextCurrent(disp2, 0, 0, 0);
117     glXDestroyContext(disp2, ctx2);
118     XCloseDisplay(disp2);
119     fprintf(stderr, "%s: Destroy #2.X\n", msg);
120 
121     fprintf(stderr, "%s: Exit - OK\n", msg);
122 }
123 
useGL(Display * dpy,Window win,GLXContext ctx,int width,int height,float c,int swapInterval)124 static void useGL(Display *dpy, Window win, GLXContext ctx, int width, int height, float c, int swapInterval)
125 {
126     glXMakeContextCurrent(dpy, win, win, ctx);
127     glViewport(0, 0, width, height);
128     if(0 < swapInterval) {
129         fprintf(stderr, "%s: glXSwapIntervalSGI(1)\n", msg);
130         _glXSwapIntervalSGI(1); // offending op after retargeting drawable
131     }
132     fprintf(stderr, "GL_VENDOR: %s\n", glGetString(GL_VENDOR));
133     fprintf(stderr, "GL_VERSION: %s\n", glGetString(GL_VERSION));
134     fprintf(stderr, "GL_RENDERER: %s\n", glGetString(GL_RENDERER));
135     glClearColor(c, c, c, 0.0f);
136     glClearDepth(1.0f);
137     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
138     glXSwapBuffers(dpy, win);
139     glXMakeContextCurrent(dpy, 0, 0, 0);
140 }
141 
142 static volatile bool ctxErrorOccurred = false;
ctxErrorHandler(Display * dpy,XErrorEvent * e)143 static int ctxErrorHandler( Display *dpy, XErrorEvent *e )
144 {
145     const char * errnoStr = strerror(errno);
146     char errCodeStr[80];
147     char reqCodeStr[80];
148 
149     snprintf(errCodeStr, sizeof(errCodeStr), "%d", e->request_code);
150     XGetErrorDatabaseText(dpy, "XRequest", errCodeStr, "Unknown", reqCodeStr, sizeof(reqCodeStr));
151     XGetErrorText(dpy, e->error_code, errCodeStr, sizeof(errCodeStr));
152 
153     fprintf(stderr, "X11 Error: %d - %s, dpy %p, id %x, # %d: %d:%d %s\n",
154         e->error_code, errCodeStr, e->display, (int)e->resourceid, (int)e->serial,
155         (int)e->request_code, (int)e->minor_code, reqCodeStr);
156     fflush(stderr);
157 
158     ctxErrorOccurred = true;
159     return 0;
160 }
161 
162 /* attributes for a double buffered visual in RGBA format with at least
163  * 8 bits per color and a 16 bit depth buffer */
164 static int visual_attribs[] = {
165   GLX_X_RENDERABLE    , True,
166   GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
167   GLX_RENDER_TYPE     , GLX_RGBA_BIT,
168   GLX_RED_SIZE        , 8,
169   GLX_GREEN_SIZE      , 8,
170   GLX_BLUE_SIZE       , 8,
171   GLX_DEPTH_SIZE      , 16,
172   GLX_DOUBLEBUFFER    , True,
173   GLX_STEREO          , False,
174   GLX_TRANSPARENT_TYPE, GLX_NONE,
175   //GLX_SAMPLE_BUFFERS  , 1,
176   //GLX_SAMPLES         , 4,
177   None };
178 
179 static int context_attribs[] = {
180     GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
181     GLX_CONTEXT_MINOR_VERSION_ARB, 0,
182     GLX_RENDER_TYPE              , GLX_RGBA_TYPE,
183     GLX_CONTEXT_FLAGS_ARB        , 0,
184     // GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
185     // GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
186     None };
187 
188 static bool isExtensionSupported(const char *extList, const char *extension);
189 
createGLWin(Display * dpy,int width,int height,Window * rWin,GLXContext * rCtx)190 static void createGLWin(Display *dpy, int width, int height, Window *rWin, GLXContext *rCtx)
191 {
192     int glx_major, glx_minor;
193 
194     // FBConfigs were added in GLX version 1.3.
195     if ( !glXQueryVersion( dpy, &glx_major, &glx_minor ) ||
196        ( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) )
197     {
198         printf( "Invalid GLX version" );
199         exit(1);
200     }
201 
202     int fbcount;
203     GLXFBConfig *fbc = glXChooseFBConfig( dpy, DefaultScreen( dpy ),
204                                         visual_attribs, &fbcount );
205     if ( !fbc || 0 == fbcount )
206     {
207         printf( "Failed to retrieve a framebuffer config\n" );
208         exit(1);
209     }
210     printf( "Found %d matching FB configs.\n", fbcount );
211 
212     GLXFBConfig bestFbc = fbc[ 0 ];
213     int bestFbcID = 0;
214     if( 0 != glXGetFBConfigAttrib( dpy, bestFbc, GLX_FBCONFIG_ID, &bestFbcID ) ) {
215         printf( "Invalid FBConfigID\n" );
216         exit(1);
217     }
218     printf( "Chosen FBConfigID = 0x%x\n", bestFbcID);
219 
220     XVisualInfo *vi = glXGetVisualFromFBConfig( dpy, bestFbc );
221     printf( "Chosen visual ID = 0x%x\n", (int) vi->visualid );
222 
223     XSetWindowAttributes swa;
224     Colormap cmap;
225     swa.colormap = cmap = XCreateColormap( dpy,
226                                          RootWindow( dpy, vi->screen ),
227                                          vi->visual, AllocNone );
228     swa.background_pixmap = None ;
229     swa.border_pixel      = 0;
230     swa.event_mask        = StructureNotifyMask;
231 
232     printf( "Creating window\n" );
233     Window win = XCreateWindow( dpy, RootWindow( dpy, vi->screen ),
234                                 0, 0, width, height, 0, vi->depth, InputOutput,
235                                 vi->visual,
236                                 CWBorderPixel|CWColormap|CWEventMask, &swa );
237     if ( !win )
238     {
239         printf( "Failed to create window.\n" );
240         exit(1);
241     }
242 
243     // Done with the visual info data
244     XFree( vi );
245 
246     XStoreName( dpy, win, "GL Window" );
247 
248     XMapWindow( dpy, win );
249 
250     *rWin = win;
251 
252     GLXContext ctx0 = glXCreateNewContext( dpy, bestFbc, GLX_RGBA_TYPE, 0, True );
253     if( !ctx0 ) {
254         printf( "Failed to create intermediate old OpenGL context\n" );
255         exit(1);
256     }
257     glXMakeContextCurrent(dpy, win, win, ctx0);
258 
259 
260     // Get the default screen's GLX extension list
261     const char *glxExts01 = glXQueryExtensionsString( dpy,
262                                                   DefaultScreen( dpy ) );
263     const char *glxExts02 = glXGetClientString( dpy, GLX_EXTENSIONS);
264     const char *glxExts03 = glXQueryServerString( dpy, DefaultScreen( dpy ), GLX_EXTENSIONS);
265 
266     // NOTE: It is not necessary to create or make current to a context before
267     // calling glXGetProcAddressARB
268     PFNGLXCREATECONTEXTATTRIBSARBPROC _glXCreateContextAttribsARB = 0;
269     _glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
270            glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );
271 
272     // Check for the GLX_ARB_create_context extension string and the function.
273     // If either is not present, use GLX 1.3 context creation method.
274     bool isGLX_ARB_create_contextAvail = isExtensionSupported( glxExts01, "GLX_ARB_create_context" ) ||
275                                          isExtensionSupported( glxExts02, "GLX_ARB_create_context" ) ||
276                                          isExtensionSupported( glxExts03, "GLX_ARB_create_context" );
277 
278     glXMakeContextCurrent(dpy, 0, 0, 0);
279 
280     GLXContext ctx = 0;
281 
282     // Install an X error handler so the application won't exit if GL 3.0
283     // context allocation fails.
284     //
285     // Note this error handler is global.  All display connections in all threads
286     // of a process use the same error handler, so be sure to guard against other
287     // threads issuing X commands while this code is running.
288     int (*oldHandler)(Display*, XErrorEvent*) =
289       XSetErrorHandler(&ctxErrorHandler);
290 
291     if ( !isGLX_ARB_create_contextAvail || !_glXCreateContextAttribsARB )
292     {
293         printf( "glXCreateContextAttribsARB() not found (ext %d, func %p)"
294                 " ... using old-style GLX context\n", isGLX_ARB_create_contextAvail, _glXCreateContextAttribsARB );
295         printf( "extensions 01: %s\n", glxExts01);
296         printf( "extensions 02: %s\n", glxExts02);
297         printf( "extensions 03: %s\n", glxExts03);
298         ctx = ctx0;
299     }
300 
301     // If it does, try to get a GL 3.0 context!
302     else
303     {
304         printf( "Creating context\n" );
305         XSync( dpy, False );
306         ctxErrorOccurred = false;
307         ctx = _glXCreateContextAttribsARB( dpy, bestFbc, 0, True, context_attribs );
308         XSync( dpy, False );
309 
310         if ( !ctxErrorOccurred && ctx ) {
311           printf( "Created GL 3.0 context\n" );
312           glXDestroyContext(dpy, ctx0); // get rid of old ctx
313         } else
314         {
315           // Couldn't create GL 3.0 context.  Fall back to old-style 2.x context.
316           // When a context version below 3.0 is requested, implementations will
317           // return the newest context version compatible with OpenGL versions less
318           // than version 3.0.
319           // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
320           context_attribs[1] = 1;
321           // GLX_CONTEXT_MINOR_VERSION_ARB = 0
322           context_attribs[3] = 0;
323 
324           printf( "Failed to create GL 3.0 context (err %d, ctx %p)"
325                   " ... using old-style GLX context\n", ctxErrorOccurred, (void*)ctx );
326           ctx = ctx0;
327 
328           ctxErrorOccurred = false;
329         }
330     }
331 
332     // Sync to ensure any errors generated are processed.
333     XSync( dpy, False );
334 
335     // Restore the original error handler
336     XSetErrorHandler( oldHandler );
337 
338     if ( ctxErrorOccurred || !ctx )
339     {
340         printf( "Failed to create an OpenGL context\n" );
341         exit(1);
342     }
343 
344     XFree( fbc );
345 
346     *rCtx = ctx;
347 }
348 
349 // Helper to check for extension string presence.  Adapted from:
350 //   http://www.opengl.org/resources/features/OGLextensions/
isExtensionSupported(const char * extList,const char * extension)351 static bool isExtensionSupported(const char *extList, const char *extension)
352 {
353 
354   const char *start;
355   const char *where, *terminator;
356 
357   /* Extension names should not have spaces. */
358   where = strchr(extension, ' ');
359   if ( where || *extension == '\0' )
360     return false;
361 
362   /* It takes a bit of care to be fool-proof about parsing the
363      OpenGL extensions string. Don't be fooled by sub-strings,
364      etc. */
365   for ( start = extList; ; ) {
366     where = strstr( start, extension );
367 
368     if ( !where )
369       break;
370 
371     terminator = where + strlen( extension );
372 
373     if ( where == start || *(where - 1) == ' ' )
374       if ( *terminator == ' ' || *terminator == '\0' )
375         return true;
376 
377     start = terminator;
378   }
379 
380   return false;
381 }
382 
383