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