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