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