1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_VIDEO_DRIVER_X11
24 
25 #include "SDL_x11video.h"
26 #include "SDL_assert.h"
27 #include "SDL_hints.h"
28 
29 /* GLX implementation of SDL OpenGL support */
30 
31 #if SDL_VIDEO_OPENGL_GLX
32 #include "SDL_loadso.h"
33 #include "SDL_x11opengles.h"
34 
35 #if defined(__IRIX__) || defined(__NetBSD__) || defined(__OpenBSD__)
36 /*
37  * IRIX doesn't have a GL library versioning system.
38  * NetBSD and OpenBSD have different GL library versions depending on how
39  * the library was installed.
40  */
41 #define DEFAULT_OPENGL  "libGL.so"
42 #elif defined(__MACOSX__)
43 #define DEFAULT_OPENGL  "/opt/X11/lib/libGL.1.dylib"
44 #elif defined(__QNXNTO__)
45 #define DEFAULT_OPENGL  "libGL.so.3"
46 #else
47 #define DEFAULT_OPENGL  "libGL.so.1"
48 #endif
49 
50 #ifndef GLX_NONE_EXT
51 #define GLX_NONE_EXT                       0x8000
52 #endif
53 
54 #ifndef GLX_ARB_multisample
55 #define GLX_ARB_multisample
56 #define GLX_SAMPLE_BUFFERS_ARB             100000
57 #define GLX_SAMPLES_ARB                    100001
58 #endif
59 
60 #ifndef GLX_EXT_visual_rating
61 #define GLX_EXT_visual_rating
62 #define GLX_VISUAL_CAVEAT_EXT              0x20
63 #define GLX_NONE_EXT                       0x8000
64 #define GLX_SLOW_VISUAL_EXT                0x8001
65 #define GLX_NON_CONFORMANT_VISUAL_EXT      0x800D
66 #endif
67 
68 #ifndef GLX_EXT_visual_info
69 #define GLX_EXT_visual_info
70 #define GLX_X_VISUAL_TYPE_EXT              0x22
71 #define GLX_DIRECT_COLOR_EXT               0x8003
72 #endif
73 
74 #ifndef GLX_ARB_create_context
75 #define GLX_ARB_create_context
76 #define GLX_CONTEXT_MAJOR_VERSION_ARB      0x2091
77 #define GLX_CONTEXT_MINOR_VERSION_ARB      0x2092
78 #define GLX_CONTEXT_FLAGS_ARB              0x2094
79 #define GLX_CONTEXT_DEBUG_BIT_ARB          0x0001
80 #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
81 
82 /* Typedef for the GL 3.0 context creation function */
83 typedef GLXContext(*PFNGLXCREATECONTEXTATTRIBSARBPROC) (Display * dpy,
84                                                         GLXFBConfig config,
85                                                         GLXContext
86                                                         share_context,
87                                                         Bool direct,
88                                                         const int
89                                                         *attrib_list);
90 #endif
91 
92 #ifndef GLX_ARB_create_context_profile
93 #define GLX_ARB_create_context_profile
94 #define GLX_CONTEXT_PROFILE_MASK_ARB       0x9126
95 #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB   0x00000001
96 #define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
97 #endif
98 
99 #ifndef GLX_ARB_create_context_robustness
100 #define GLX_ARB_create_context_robustness
101 #define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB  0x00000004
102 #define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB     0x8256
103 #define GLX_NO_RESET_NOTIFICATION_ARB                   0x8261
104 #define GLX_LOSE_CONTEXT_ON_RESET_ARB                   0x8252
105 #endif
106 
107 #ifndef GLX_EXT_create_context_es2_profile
108 #define GLX_EXT_create_context_es2_profile
109 #ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT
110 #define GLX_CONTEXT_ES2_PROFILE_BIT_EXT    0x00000002
111 #endif
112 #endif
113 
114 #ifndef GLX_ARB_framebuffer_sRGB
115 #define GLX_ARB_framebuffer_sRGB
116 #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB
117 #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB                0x20B2
118 #endif
119 #endif
120 
121 #ifndef GLX_ARB_create_context_no_error
122 #define GLX_ARB_create_context_no_error
123 #ifndef GLX_CONTEXT_OPENGL_NO_ERROR_ARB
124 #define GLX_CONTEXT_OPENGL_NO_ERROR_ARB                 0x31B3
125 #endif
126 #endif
127 
128 #ifndef GLX_EXT_swap_control
129 #define GLX_SWAP_INTERVAL_EXT              0x20F1
130 #define GLX_MAX_SWAP_INTERVAL_EXT          0x20F2
131 #endif
132 
133 #ifndef GLX_EXT_swap_control_tear
134 #define GLX_LATE_SWAPS_TEAR_EXT 0x20F3
135 #endif
136 
137 #ifndef GLX_ARB_context_flush_control
138 #define GLX_ARB_context_flush_control
139 #define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB   0x2097
140 #define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB           0x0000
141 #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB          0x2098
142 #endif
143 
144 #define OPENGL_REQUIRES_DLOPEN
145 #if defined(OPENGL_REQUIRES_DLOPEN) && defined(SDL_LOADSO_DLOPEN)
146 #include <dlfcn.h>
147 #define GL_LoadObject(X)    dlopen(X, (RTLD_NOW|RTLD_GLOBAL))
148 #define GL_LoadFunction     dlsym
149 #define GL_UnloadObject     dlclose
150 #else
151 #define GL_LoadObject   SDL_LoadObject
152 #define GL_LoadFunction SDL_LoadFunction
153 #define GL_UnloadObject SDL_UnloadObject
154 #endif
155 
156 static void X11_GL_InitExtensions(_THIS);
157 
158 int
X11_GL_LoadLibrary(_THIS,const char * path)159 X11_GL_LoadLibrary(_THIS, const char *path)
160 {
161     Display *display;
162     void *handle;
163 
164     if (_this->gl_data) {
165         return SDL_SetError("OpenGL context already created");
166     }
167 
168     /* Load the OpenGL library */
169     if (path == NULL) {
170         path = SDL_getenv("SDL_OPENGL_LIBRARY");
171     }
172     if (path == NULL) {
173         path = DEFAULT_OPENGL;
174     }
175     _this->gl_config.dll_handle = GL_LoadObject(path);
176     if (!_this->gl_config.dll_handle) {
177 #if defined(OPENGL_REQUIRES_DLOPEN) && defined(SDL_LOADSO_DLOPEN)
178         SDL_SetError("Failed loading %s: %s", path, dlerror());
179 #endif
180         return -1;
181     }
182     SDL_strlcpy(_this->gl_config.driver_path, path,
183                 SDL_arraysize(_this->gl_config.driver_path));
184 
185     /* Allocate OpenGL memory */
186     _this->gl_data =
187         (struct SDL_GLDriverData *) SDL_calloc(1,
188                                                sizeof(struct
189                                                       SDL_GLDriverData));
190     if (!_this->gl_data) {
191         return SDL_OutOfMemory();
192     }
193 
194     /* Load function pointers */
195     handle = _this->gl_config.dll_handle;
196     _this->gl_data->glXQueryExtension =
197         (Bool (*)(Display *, int *, int *))
198             GL_LoadFunction(handle, "glXQueryExtension");
199     _this->gl_data->glXGetProcAddress =
200         (void *(*)(const GLubyte *))
201             GL_LoadFunction(handle, "glXGetProcAddressARB");
202     _this->gl_data->glXChooseVisual =
203         (XVisualInfo * (*)(Display *, int, int *))
204             X11_GL_GetProcAddress(_this, "glXChooseVisual");
205     _this->gl_data->glXCreateContext =
206         (GLXContext(*)(Display *, XVisualInfo *, GLXContext, int))
207             X11_GL_GetProcAddress(_this, "glXCreateContext");
208     _this->gl_data->glXDestroyContext =
209         (void (*)(Display *, GLXContext))
210             X11_GL_GetProcAddress(_this, "glXDestroyContext");
211     _this->gl_data->glXMakeCurrent =
212         (int (*)(Display *, GLXDrawable, GLXContext))
213             X11_GL_GetProcAddress(_this, "glXMakeCurrent");
214     _this->gl_data->glXSwapBuffers =
215         (void (*)(Display *, GLXDrawable))
216             X11_GL_GetProcAddress(_this, "glXSwapBuffers");
217     _this->gl_data->glXQueryDrawable =
218         (void (*)(Display*,GLXDrawable,int,unsigned int*))
219             X11_GL_GetProcAddress(_this, "glXQueryDrawable");
220 
221     if (!_this->gl_data->glXQueryExtension ||
222         !_this->gl_data->glXChooseVisual ||
223         !_this->gl_data->glXCreateContext ||
224         !_this->gl_data->glXDestroyContext ||
225         !_this->gl_data->glXMakeCurrent ||
226         !_this->gl_data->glXSwapBuffers) {
227         return SDL_SetError("Could not retrieve OpenGL functions");
228     }
229 
230     display = ((SDL_VideoData *) _this->driverdata)->display;
231     if (!_this->gl_data->glXQueryExtension(display, &_this->gl_data->errorBase, &_this->gl_data->eventBase)) {
232         return SDL_SetError("GLX is not supported");
233     }
234 
235     /* Initialize extensions */
236     /* See lengthy comment about the inc/dec in
237        ../windows/SDL_windowsopengl.c. */
238     ++_this->gl_config.driver_loaded;
239     X11_GL_InitExtensions(_this);
240     --_this->gl_config.driver_loaded;
241 
242     /* If we need a GL ES context and there's no
243      * GLX_EXT_create_context_es2_profile extension, switch over to X11_GLES functions
244      */
245     if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
246          SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_FORCE_EGL, SDL_FALSE)) &&
247         X11_GL_UseEGL(_this) ) {
248 #if SDL_VIDEO_OPENGL_EGL
249         X11_GL_UnloadLibrary(_this);
250         /* Better avoid conflicts! */
251         if (_this->gl_config.dll_handle != NULL ) {
252             GL_UnloadObject(_this->gl_config.dll_handle);
253             _this->gl_config.dll_handle = NULL;
254         }
255         _this->GL_LoadLibrary = X11_GLES_LoadLibrary;
256         _this->GL_GetProcAddress = X11_GLES_GetProcAddress;
257         _this->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
258         _this->GL_CreateContext = X11_GLES_CreateContext;
259         _this->GL_MakeCurrent = X11_GLES_MakeCurrent;
260         _this->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
261         _this->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
262         _this->GL_SwapWindow = X11_GLES_SwapWindow;
263         _this->GL_DeleteContext = X11_GLES_DeleteContext;
264         return X11_GLES_LoadLibrary(_this, NULL);
265 #else
266         return SDL_SetError("SDL not configured with EGL support");
267 #endif
268     }
269 
270     return 0;
271 }
272 
273 void *
X11_GL_GetProcAddress(_THIS,const char * proc)274 X11_GL_GetProcAddress(_THIS, const char *proc)
275 {
276     if (_this->gl_data->glXGetProcAddress) {
277         return _this->gl_data->glXGetProcAddress((const GLubyte *) proc);
278     }
279     return GL_LoadFunction(_this->gl_config.dll_handle, proc);
280 }
281 
282 void
X11_GL_UnloadLibrary(_THIS)283 X11_GL_UnloadLibrary(_THIS)
284 {
285     /* Don't actually unload the library, since it may have registered
286      * X11 shutdown hooks, per the notes at:
287      * http://dri.sourceforge.net/doc/DRIuserguide.html
288      */
289 #if 0
290     GL_UnloadObject(_this->gl_config.dll_handle);
291     _this->gl_config.dll_handle = NULL;
292 #endif
293 
294     /* Free OpenGL memory */
295     SDL_free(_this->gl_data);
296     _this->gl_data = NULL;
297 }
298 
299 static SDL_bool
HasExtension(const char * extension,const char * extensions)300 HasExtension(const char *extension, const char *extensions)
301 {
302     const char *start;
303     const char *where, *terminator;
304 
305     if (!extensions)
306         return SDL_FALSE;
307 
308     /* Extension names should not have spaces. */
309     where = SDL_strchr(extension, ' ');
310     if (where || *extension == '\0')
311         return SDL_FALSE;
312 
313     /* It takes a bit of care to be fool-proof about parsing the
314      * OpenGL extensions string. Don't be fooled by sub-strings,
315      * etc. */
316 
317     start = extensions;
318 
319     for (;;) {
320         where = SDL_strstr(start, extension);
321         if (!where)
322             break;
323 
324         terminator = where + SDL_strlen(extension);
325         if (where == start || *(where - 1) == ' ')
326             if (*terminator == ' ' || *terminator == '\0')
327                 return SDL_TRUE;
328 
329         start = terminator;
330     }
331     return SDL_FALSE;
332 }
333 
334 static void
X11_GL_InitExtensions(_THIS)335 X11_GL_InitExtensions(_THIS)
336 {
337     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
338     const int screen = DefaultScreen(display);
339     XVisualInfo *vinfo = NULL;
340     Window w = 0;
341     GLXContext prev_ctx = 0;
342     GLXDrawable prev_drawable = 0;
343     GLXContext context = 0;
344     const char *(*glXQueryExtensionsStringFunc) (Display *, int);
345     const char *extensions;
346 
347     vinfo = X11_GL_GetVisual(_this, display, screen);
348     if (vinfo) {
349         GLXContext (*glXGetCurrentContextFunc) (void) =
350             (GLXContext(*)(void))
351                 X11_GL_GetProcAddress(_this, "glXGetCurrentContext");
352 
353         GLXDrawable (*glXGetCurrentDrawableFunc) (void) =
354             (GLXDrawable(*)(void))
355                 X11_GL_GetProcAddress(_this, "glXGetCurrentDrawable");
356 
357         if (glXGetCurrentContextFunc && glXGetCurrentDrawableFunc) {
358             XSetWindowAttributes xattr;
359             prev_ctx = glXGetCurrentContextFunc();
360             prev_drawable = glXGetCurrentDrawableFunc();
361 
362             xattr.background_pixel = 0;
363             xattr.border_pixel = 0;
364             xattr.colormap =
365                 X11_XCreateColormap(display, RootWindow(display, screen),
366                                     vinfo->visual, AllocNone);
367             w = X11_XCreateWindow(display, RootWindow(display, screen), 0, 0,
368                         32, 32, 0, vinfo->depth, InputOutput, vinfo->visual,
369                         (CWBackPixel | CWBorderPixel | CWColormap), &xattr);
370 
371             context = _this->gl_data->glXCreateContext(display, vinfo,
372                                                         NULL, True);
373             if (context) {
374                 _this->gl_data->glXMakeCurrent(display, w, context);
375             }
376         }
377 
378         X11_XFree(vinfo);
379     }
380 
381     glXQueryExtensionsStringFunc =
382         (const char *(*)(Display *, int)) X11_GL_GetProcAddress(_this,
383                                                                 "glXQueryExtensionsString");
384     if (glXQueryExtensionsStringFunc) {
385         extensions = glXQueryExtensionsStringFunc(display, screen);
386     } else {
387         extensions = NULL;
388     }
389 
390     /* Check for GLX_EXT_swap_control(_tear) */
391     _this->gl_data->HAS_GLX_EXT_swap_control_tear = SDL_FALSE;
392     if (HasExtension("GLX_EXT_swap_control", extensions)) {
393         _this->gl_data->glXSwapIntervalEXT =
394             (void (*)(Display*,GLXDrawable,int))
395                 X11_GL_GetProcAddress(_this, "glXSwapIntervalEXT");
396         if (HasExtension("GLX_EXT_swap_control_tear", extensions)) {
397             _this->gl_data->HAS_GLX_EXT_swap_control_tear = SDL_TRUE;
398         }
399     }
400 
401     /* Check for GLX_MESA_swap_control */
402     if (HasExtension("GLX_MESA_swap_control", extensions)) {
403         _this->gl_data->glXSwapIntervalMESA =
404             (int(*)(int)) X11_GL_GetProcAddress(_this, "glXSwapIntervalMESA");
405         _this->gl_data->glXGetSwapIntervalMESA =
406             (int(*)(void)) X11_GL_GetProcAddress(_this,
407                                                    "glXGetSwapIntervalMESA");
408     }
409 
410     /* Check for GLX_SGI_swap_control */
411     if (HasExtension("GLX_SGI_swap_control", extensions)) {
412         _this->gl_data->glXSwapIntervalSGI =
413             (int (*)(int)) X11_GL_GetProcAddress(_this, "glXSwapIntervalSGI");
414     }
415 
416     /* Check for GLX_ARB_create_context */
417     if (HasExtension("GLX_ARB_create_context", extensions)) {
418         _this->gl_data->glXCreateContextAttribsARB =
419             (GLXContext (*)(Display*,GLXFBConfig,GLXContext,Bool,const int *))
420                 X11_GL_GetProcAddress(_this, "glXCreateContextAttribsARB");
421         _this->gl_data->glXChooseFBConfig =
422             (GLXFBConfig *(*)(Display *, int, const int *, int *))
423                 X11_GL_GetProcAddress(_this, "glXChooseFBConfig");
424     }
425 
426     /* Check for GLX_EXT_visual_rating */
427     if (HasExtension("GLX_EXT_visual_rating", extensions)) {
428         _this->gl_data->HAS_GLX_EXT_visual_rating = SDL_TRUE;
429     }
430 
431     /* Check for GLX_EXT_visual_info */
432     if (HasExtension("GLX_EXT_visual_info", extensions)) {
433         _this->gl_data->HAS_GLX_EXT_visual_info = SDL_TRUE;
434     }
435 
436     /* Check for GLX_EXT_create_context_es2_profile */
437     if (HasExtension("GLX_EXT_create_context_es2_profile", extensions)) {
438         /* this wants to call glGetString(), so it needs a context. */
439         /* !!! FIXME: it would be nice not to make a context here though! */
440         if (context) {
441             SDL_GL_DeduceMaxSupportedESProfile(
442                 &_this->gl_data->es_profile_max_supported_version.major,
443                 &_this->gl_data->es_profile_max_supported_version.minor
444             );
445         }
446     }
447 
448     /* Check for GLX_ARB_context_flush_control */
449     if (HasExtension("GLX_ARB_context_flush_control", extensions)) {
450         _this->gl_data->HAS_GLX_ARB_context_flush_control = SDL_TRUE;
451     }
452 
453     /* Check for GLX_ARB_create_context_robustness */
454     if (HasExtension("GLX_ARB_create_context_robustness", extensions)) {
455         _this->gl_data->HAS_GLX_ARB_create_context_robustness = SDL_TRUE;
456     }
457 
458     /* Check for GLX_ARB_create_context_no_error */
459     if (HasExtension("GLX_ARB_create_context_no_error", extensions)) {
460         _this->gl_data->HAS_GLX_ARB_create_context_no_error = SDL_TRUE;
461     }
462 
463     if (context) {
464         _this->gl_data->glXMakeCurrent(display, None, NULL);
465         _this->gl_data->glXDestroyContext(display, context);
466         if (prev_ctx && prev_drawable) {
467             _this->gl_data->glXMakeCurrent(display, prev_drawable, prev_ctx);
468         }
469     }
470 
471     if (w) {
472         X11_XDestroyWindow(display, w);
473     }
474     X11_PumpEvents(_this);
475 }
476 
477 /* glXChooseVisual and glXChooseFBConfig have some small differences in
478  * the attribute encoding, it can be chosen with the for_FBConfig parameter.
479  * Some targets fail if you use GLX_X_VISUAL_TYPE_EXT/GLX_DIRECT_COLOR_EXT,
480  *  so it gets specified last if used and is pointed to by *_pvistypeattr.
481  *  In case of failure, if that pointer is not NULL, set that pointer to None
482  *  and try again.
483  */
484 static int
X11_GL_GetAttributes(_THIS,Display * display,int screen,int * attribs,int size,Bool for_FBConfig,int ** _pvistypeattr)485 X11_GL_GetAttributes(_THIS, Display * display, int screen, int * attribs, int size, Bool for_FBConfig, int **_pvistypeattr)
486 {
487     int i = 0;
488     const int MAX_ATTRIBUTES = 64;
489     int *pvistypeattr = NULL;
490 
491     /* assert buffer is large enough to hold all SDL attributes. */
492     SDL_assert(size >= MAX_ATTRIBUTES);
493 
494     /* Setup our GLX attributes according to the gl_config. */
495     if( for_FBConfig ) {
496         attribs[i++] = GLX_RENDER_TYPE;
497         attribs[i++] = GLX_RGBA_BIT;
498     } else {
499         attribs[i++] = GLX_RGBA;
500     }
501     attribs[i++] = GLX_RED_SIZE;
502     attribs[i++] = _this->gl_config.red_size;
503     attribs[i++] = GLX_GREEN_SIZE;
504     attribs[i++] = _this->gl_config.green_size;
505     attribs[i++] = GLX_BLUE_SIZE;
506     attribs[i++] = _this->gl_config.blue_size;
507 
508     if (_this->gl_config.alpha_size) {
509         attribs[i++] = GLX_ALPHA_SIZE;
510         attribs[i++] = _this->gl_config.alpha_size;
511     }
512 
513     if (_this->gl_config.double_buffer) {
514         attribs[i++] = GLX_DOUBLEBUFFER;
515         if( for_FBConfig ) {
516             attribs[i++] = True;
517         }
518     }
519 
520     attribs[i++] = GLX_DEPTH_SIZE;
521     attribs[i++] = _this->gl_config.depth_size;
522 
523     if (_this->gl_config.stencil_size) {
524         attribs[i++] = GLX_STENCIL_SIZE;
525         attribs[i++] = _this->gl_config.stencil_size;
526     }
527 
528     if (_this->gl_config.accum_red_size) {
529         attribs[i++] = GLX_ACCUM_RED_SIZE;
530         attribs[i++] = _this->gl_config.accum_red_size;
531     }
532 
533     if (_this->gl_config.accum_green_size) {
534         attribs[i++] = GLX_ACCUM_GREEN_SIZE;
535         attribs[i++] = _this->gl_config.accum_green_size;
536     }
537 
538     if (_this->gl_config.accum_blue_size) {
539         attribs[i++] = GLX_ACCUM_BLUE_SIZE;
540         attribs[i++] = _this->gl_config.accum_blue_size;
541     }
542 
543     if (_this->gl_config.accum_alpha_size) {
544         attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
545         attribs[i++] = _this->gl_config.accum_alpha_size;
546     }
547 
548     if (_this->gl_config.stereo) {
549         attribs[i++] = GLX_STEREO;
550         if( for_FBConfig ) {
551             attribs[i++] = True;
552         }
553     }
554 
555     if (_this->gl_config.multisamplebuffers) {
556         attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
557         attribs[i++] = _this->gl_config.multisamplebuffers;
558     }
559 
560     if (_this->gl_config.multisamplesamples) {
561         attribs[i++] = GLX_SAMPLES_ARB;
562         attribs[i++] = _this->gl_config.multisamplesamples;
563     }
564 
565     if (_this->gl_config.framebuffer_srgb_capable) {
566         attribs[i++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;
567         attribs[i++] = True;  /* always needed, for_FBConfig or not! */
568     }
569 
570     if (_this->gl_config.accelerated >= 0 &&
571         _this->gl_data->HAS_GLX_EXT_visual_rating) {
572         attribs[i++] = GLX_VISUAL_CAVEAT_EXT;
573         attribs[i++] = _this->gl_config.accelerated ? GLX_NONE_EXT :
574                                                       GLX_SLOW_VISUAL_EXT;
575     }
576 
577     /* If we're supposed to use DirectColor visuals, and we've got the
578        EXT_visual_info extension, then add GLX_X_VISUAL_TYPE_EXT. */
579     if (X11_UseDirectColorVisuals() &&
580         _this->gl_data->HAS_GLX_EXT_visual_info) {
581         pvistypeattr = &attribs[i];
582         attribs[i++] = GLX_X_VISUAL_TYPE_EXT;
583         attribs[i++] = GLX_DIRECT_COLOR_EXT;
584     }
585 
586     attribs[i++] = None;
587 
588     SDL_assert(i <= MAX_ATTRIBUTES);
589 
590     if (_pvistypeattr) {
591         *_pvistypeattr = pvistypeattr;
592     }
593 
594     return i;
595 }
596 
597 XVisualInfo *
X11_GL_GetVisual(_THIS,Display * display,int screen)598 X11_GL_GetVisual(_THIS, Display * display, int screen)
599 {
600     /* 64 seems nice. */
601     int attribs[64];
602     XVisualInfo *vinfo;
603     int *pvistypeattr = NULL;
604 
605     if (!_this->gl_data) {
606         /* The OpenGL library wasn't loaded, SDL_GetError() should have info */
607         return NULL;
608     }
609 
610     X11_GL_GetAttributes(_this, display, screen, attribs, 64, SDL_FALSE, &pvistypeattr);
611     vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
612 
613     if (!vinfo && (pvistypeattr != NULL)) {
614         *pvistypeattr = None;
615         vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs);
616     }
617 
618     if (!vinfo) {
619         SDL_SetError("Couldn't find matching GLX visual");
620     }
621     return vinfo;
622 }
623 
624 static int (*handler) (Display *, XErrorEvent *) = NULL;
625 static const char *errorHandlerOperation = NULL;
626 static int errorBase = 0;
627 static int errorCode = 0;
628 static int
X11_GL_ErrorHandler(Display * d,XErrorEvent * e)629 X11_GL_ErrorHandler(Display * d, XErrorEvent * e)
630 {
631     char *x11_error = NULL;
632     char x11_error_locale[256];
633 
634     errorCode = e->error_code;
635     if (X11_XGetErrorText(d, errorCode, x11_error_locale, sizeof(x11_error_locale)) == Success)
636     {
637         x11_error = SDL_iconv_string("UTF-8", "", x11_error_locale, SDL_strlen(x11_error_locale)+1);
638     }
639 
640     if (x11_error)
641     {
642         SDL_SetError("Could not %s: %s", errorHandlerOperation, x11_error);
643         SDL_free(x11_error);
644     }
645     else
646     {
647         SDL_SetError("Could not %s: %i (Base %i)", errorHandlerOperation, errorCode, errorBase);
648     }
649 
650     return (0);
651 }
652 
653 SDL_bool
X11_GL_UseEGL(_THIS)654 X11_GL_UseEGL(_THIS)
655 {
656     SDL_assert(_this->gl_data != NULL);
657     if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_FORCE_EGL, SDL_FALSE))
658     {
659         /* use of EGL has been requested, even for desktop GL */
660         return SDL_TRUE;
661     }
662 
663     SDL_assert(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES);
664     return (SDL_GetHintBoolean(SDL_HINT_OPENGL_ES_DRIVER, SDL_FALSE)
665             || _this->gl_config.major_version == 1 /* No GLX extension for OpenGL ES 1.x profiles. */
666             || _this->gl_config.major_version > _this->gl_data->es_profile_max_supported_version.major
667             || (_this->gl_config.major_version == _this->gl_data->es_profile_max_supported_version.major
668                 && _this->gl_config.minor_version > _this->gl_data->es_profile_max_supported_version.minor));
669 }
670 
671 SDL_GLContext
X11_GL_CreateContext(_THIS,SDL_Window * window)672 X11_GL_CreateContext(_THIS, SDL_Window * window)
673 {
674     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
675     Display *display = data->videodata->display;
676     int screen =
677         ((SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata)->screen;
678     XWindowAttributes xattr;
679     XVisualInfo v, *vinfo;
680     int n;
681     GLXContext context = NULL, share_context;
682 
683     if (_this->gl_config.share_with_current_context) {
684         share_context = (GLXContext)SDL_GL_GetCurrentContext();
685     } else {
686         share_context = NULL;
687     }
688 
689     /* We do this to create a clean separation between X and GLX errors. */
690     X11_XSync(display, False);
691     errorHandlerOperation = "create GL context";
692     errorBase = _this->gl_data->errorBase;
693     errorCode = Success;
694     handler = X11_XSetErrorHandler(X11_GL_ErrorHandler);
695     X11_XGetWindowAttributes(display, data->xwindow, &xattr);
696     v.screen = screen;
697     v.visualid = X11_XVisualIDFromVisual(xattr.visual);
698     vinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &v, &n);
699     if (vinfo) {
700         if (_this->gl_config.major_version < 3 &&
701             _this->gl_config.profile_mask == 0 &&
702             _this->gl_config.flags == 0) {
703             /* Create legacy context */
704             context =
705                 _this->gl_data->glXCreateContext(display, vinfo, share_context, True);
706         } else {
707             /* max 14 attributes plus terminator */
708             int attribs[15] = {
709                 GLX_CONTEXT_MAJOR_VERSION_ARB,
710                 _this->gl_config.major_version,
711                 GLX_CONTEXT_MINOR_VERSION_ARB,
712                 _this->gl_config.minor_version,
713                 0
714             };
715             int iattr = 4;
716 
717             /* SDL profile bits match GLX profile bits */
718             if( _this->gl_config.profile_mask != 0 ) {
719                 attribs[iattr++] = GLX_CONTEXT_PROFILE_MASK_ARB;
720                 attribs[iattr++] = _this->gl_config.profile_mask;
721             }
722 
723             /* SDL flags match GLX flags */
724             if( _this->gl_config.flags != 0 ) {
725                 attribs[iattr++] = GLX_CONTEXT_FLAGS_ARB;
726                 attribs[iattr++] = _this->gl_config.flags;
727             }
728 
729             /* only set if glx extension is available */
730             if( _this->gl_data->HAS_GLX_ARB_context_flush_control ) {
731                 attribs[iattr++] = GLX_CONTEXT_RELEASE_BEHAVIOR_ARB;
732                 attribs[iattr++] =
733                     _this->gl_config.release_behavior ?
734                     GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB :
735                     GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB;
736             }
737 
738             /* only set if glx extension is available */
739             if( _this->gl_data->HAS_GLX_ARB_create_context_robustness ) {
740                 attribs[iattr++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;
741                 attribs[iattr++] =
742                     _this->gl_config.reset_notification ?
743                     GLX_LOSE_CONTEXT_ON_RESET_ARB :
744                     GLX_NO_RESET_NOTIFICATION_ARB;
745             }
746 
747             /* only set if glx extension is available */
748             if( _this->gl_data->HAS_GLX_ARB_create_context_no_error ) {
749                 attribs[iattr++] = GLX_CONTEXT_OPENGL_NO_ERROR_ARB;
750                 attribs[iattr++] = _this->gl_config.no_error;
751             }
752 
753             attribs[iattr++] = 0;
754 
755             /* Get a pointer to the context creation function for GL 3.0 */
756             if (!_this->gl_data->glXCreateContextAttribsARB) {
757                 SDL_SetError("OpenGL 3.0 and later are not supported by this system");
758             } else {
759                 int glxAttribs[64];
760 
761                 /* Create a GL 3.x context */
762                 GLXFBConfig *framebuffer_config = NULL;
763                 int fbcount = 0;
764                 int *pvistypeattr = NULL;
765 
766                 X11_GL_GetAttributes(_this,display,screen,glxAttribs,64,SDL_TRUE,&pvistypeattr);
767 
768                 if (_this->gl_data->glXChooseFBConfig) {
769                     framebuffer_config = _this->gl_data->glXChooseFBConfig(display,
770                                           DefaultScreen(display), glxAttribs,
771                                           &fbcount);
772 
773                     if (!framebuffer_config && (pvistypeattr != NULL)) {
774                         *pvistypeattr = None;
775                         framebuffer_config = _this->gl_data->glXChooseFBConfig(display,
776                                           DefaultScreen(display), glxAttribs,
777                                           &fbcount);
778                     }
779 
780                     if (framebuffer_config) {
781                         context = _this->gl_data->glXCreateContextAttribsARB(display,
782                                                         framebuffer_config[0],
783                                                         share_context, True, attribs);
784                         X11_XFree(framebuffer_config);
785                     }
786                 }
787             }
788         }
789         X11_XFree(vinfo);
790     }
791     X11_XSync(display, False);
792     X11_XSetErrorHandler(handler);
793 
794     if (!context) {
795         if (errorCode == Success) {
796             SDL_SetError("Could not create GL context");
797         }
798         return NULL;
799     }
800 
801     if (X11_GL_MakeCurrent(_this, window, context) < 0) {
802         X11_GL_DeleteContext(_this, context);
803         return NULL;
804     }
805 
806     return context;
807 }
808 
809 int
X11_GL_MakeCurrent(_THIS,SDL_Window * window,SDL_GLContext context)810 X11_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context)
811 {
812     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
813     Window drawable =
814         (context ? ((SDL_WindowData *) window->driverdata)->xwindow : None);
815     GLXContext glx_context = (GLXContext) context;
816     int rc;
817 
818     if (!_this->gl_data) {
819         return SDL_SetError("OpenGL not initialized");
820     }
821 
822     /* We do this to create a clean separation between X and GLX errors. */
823     X11_XSync(display, False);
824     errorHandlerOperation = "make GL context current";
825     errorBase = _this->gl_data->errorBase;
826     errorCode = Success;
827     handler = X11_XSetErrorHandler(X11_GL_ErrorHandler);
828     rc = _this->gl_data->glXMakeCurrent(display, drawable, glx_context);
829     X11_XSetErrorHandler(handler);
830 
831     if (errorCode != Success) {   /* uhoh, an X error was thrown! */
832         return -1;  /* the error handler called SDL_SetError() already. */
833     } else if (!rc) {  /* glXMakeCurrent() failed without throwing an X error */
834         return SDL_SetError("Unable to make GL context current");
835     }
836 
837     return 0;
838 }
839 
840 /*
841    0 is a valid argument to glXSwapInterval(MESA|EXT) and setting it to 0
842    will undo the effect of a previous call with a value that is greater
843    than zero (or at least that is what the docs say). OTOH, 0 is an invalid
844    argument to glXSwapIntervalSGI and it returns an error if you call it
845    with 0 as an argument.
846 */
847 
848 static int swapinterval = 0;
849 int
X11_GL_SetSwapInterval(_THIS,int interval)850 X11_GL_SetSwapInterval(_THIS, int interval)
851 {
852     int status = -1;
853 
854     if ((interval < 0) && (!_this->gl_data->HAS_GLX_EXT_swap_control_tear)) {
855         SDL_SetError("Negative swap interval unsupported in this GL");
856     } else if (_this->gl_data->glXSwapIntervalEXT) {
857         Display *display = ((SDL_VideoData *) _this->driverdata)->display;
858         const SDL_WindowData *windowdata = (SDL_WindowData *)
859             SDL_GL_GetCurrentWindow()->driverdata;
860 
861         Window drawable = windowdata->xwindow;
862 
863         /*
864          * This is a workaround for a bug in NVIDIA drivers. Bug has been reported
865          * and will be fixed in a future release (probably 319.xx).
866          *
867          * There's a bug where glXSetSwapIntervalEXT ignores updates because
868          * it has the wrong value cached. To work around it, we just run a no-op
869          * update to the current value.
870          */
871         int currentInterval = X11_GL_GetSwapInterval(_this);
872         _this->gl_data->glXSwapIntervalEXT(display, drawable, currentInterval);
873         _this->gl_data->glXSwapIntervalEXT(display, drawable, interval);
874 
875         status = 0;
876         swapinterval = interval;
877     } else if (_this->gl_data->glXSwapIntervalMESA) {
878         status = _this->gl_data->glXSwapIntervalMESA(interval);
879         if (status != 0) {
880             SDL_SetError("glXSwapIntervalMESA failed");
881         } else {
882             swapinterval = interval;
883         }
884     } else if (_this->gl_data->glXSwapIntervalSGI) {
885         status = _this->gl_data->glXSwapIntervalSGI(interval);
886         if (status != 0) {
887             SDL_SetError("glXSwapIntervalSGI failed");
888         } else {
889             swapinterval = interval;
890         }
891     } else {
892         SDL_Unsupported();
893     }
894     return status;
895 }
896 
897 int
X11_GL_GetSwapInterval(_THIS)898 X11_GL_GetSwapInterval(_THIS)
899 {
900     if (_this->gl_data->glXSwapIntervalEXT) {
901         Display *display = ((SDL_VideoData *) _this->driverdata)->display;
902         const SDL_WindowData *windowdata = (SDL_WindowData *)
903             SDL_GL_GetCurrentWindow()->driverdata;
904         Window drawable = windowdata->xwindow;
905         unsigned int allow_late_swap_tearing = 0;
906         unsigned int interval = 0;
907 
908         if (_this->gl_data->HAS_GLX_EXT_swap_control_tear) {
909             _this->gl_data->glXQueryDrawable(display, drawable,
910                                             GLX_LATE_SWAPS_TEAR_EXT,
911                                             &allow_late_swap_tearing);
912         }
913 
914         _this->gl_data->glXQueryDrawable(display, drawable,
915                                          GLX_SWAP_INTERVAL_EXT, &interval);
916 
917         if ((allow_late_swap_tearing) && (interval > 0)) {
918             return -((int) interval);
919         }
920 
921         return (int) interval;
922     } else if (_this->gl_data->glXGetSwapIntervalMESA) {
923         return _this->gl_data->glXGetSwapIntervalMESA();
924     } else {
925         return swapinterval;
926     }
927 }
928 
929 int
X11_GL_SwapWindow(_THIS,SDL_Window * window)930 X11_GL_SwapWindow(_THIS, SDL_Window * window)
931 {
932     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
933     Display *display = data->videodata->display;
934 
935     _this->gl_data->glXSwapBuffers(display, data->xwindow);
936     return 0;
937 }
938 
939 void
X11_GL_DeleteContext(_THIS,SDL_GLContext context)940 X11_GL_DeleteContext(_THIS, SDL_GLContext context)
941 {
942     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
943     GLXContext glx_context = (GLXContext) context;
944 
945     if (!_this->gl_data) {
946         return;
947     }
948     _this->gl_data->glXDestroyContext(display, glx_context);
949     X11_XSync(display, False);
950 }
951 
952 #endif /* SDL_VIDEO_OPENGL_GLX */
953 
954 #endif /* SDL_VIDEO_DRIVER_X11 */
955 
956 /* vi: set ts=4 sw=4 expandtab: */
957