1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Sam Lantinga 20 slouken (at) libsdl.org 21 */ 22 #include "SDL_config.h" 23 24 #include "SDL_x11video.h" 25 #include "../../events/SDL_events_c.h" 26 #include "SDL_x11dga_c.h" 27 #include "SDL_x11gl_c.h" 28 29 #if defined(__IRIX__) 30 /* IRIX doesn't have a GL library versioning system */ 31 #define DEFAULT_OPENGL "libGL.so" 32 #elif defined(__MACOSX__) 33 #define DEFAULT_OPENGL "/usr/X11R6/lib/libGL.1.dylib" 34 #elif defined(__QNXNTO__) 35 #define DEFAULT_OPENGL "libGL.so.3" 36 #elif defined(__OpenBSD__) 37 #define DEFAULT_OPENGL "libGL.so.4.0" 38 #else 39 #define DEFAULT_OPENGL "libGL.so.1" 40 #endif 41 42 #ifndef GLX_ARB_multisample 43 #define GLX_ARB_multisample 44 #define GLX_SAMPLE_BUFFERS_ARB 100000 45 #define GLX_SAMPLES_ARB 100001 46 #endif 47 48 /* GLX_EXT_visual_rating stuff that might not be in the system headers... */ 49 #ifndef GLX_VISUAL_CAVEAT_EXT 50 #define GLX_VISUAL_CAVEAT_EXT 0x20 51 #endif 52 #ifndef GLX_NONE_EXT 53 #define GLX_NONE_EXT 0x8000 54 #endif 55 #ifndef GLX_SLOW_VISUAL_EXT 56 #define GLX_SLOW_VISUAL_EXT 0x8001 57 #endif 58 #ifndef GLX_NON_CONFORMANT_VISUAL_EXT 59 #define GLX_NON_CONFORMANT_VISUAL_EXT 0x800D 60 #endif 61 62 63 #if SDL_VIDEO_OPENGL_GLX 64 static int glXExtensionSupported(_THIS, const char *extension) 65 { 66 const char *extensions; 67 const char *start; 68 const char *where, *terminator; 69 70 /* Extension names should not have spaces. */ 71 where = SDL_strchr(extension, ' '); 72 if ( where || *extension == '\0' ) { 73 return 0; 74 } 75 76 extensions = this->gl_data->glXQueryExtensionsString(GFX_Display,SDL_Screen); 77 /* It takes a bit of care to be fool-proof about parsing the 78 * OpenGL extensions string. Don't be fooled by sub-strings, etc. 79 */ 80 81 /* http://bugs.debian.org/537487 */ 82 if (extensions == NULL) { 83 return 0; 84 } 85 86 start = extensions; 87 88 for (;;) { 89 where = SDL_strstr(start, extension); 90 if (!where) break; 91 92 terminator = where + strlen(extension); 93 if (where == start || *(where - 1) == ' ') 94 if (*terminator == ' ' || *terminator == '\0') return 1; 95 96 start = terminator; 97 } 98 return 0; 99 } 100 #endif /* SDL_VIDEO_OPENGL_GLX */ 101 102 XVisualInfo *X11_GL_GetVisual(_THIS) 103 { 104 #if SDL_VIDEO_OPENGL_GLX 105 /* 64 seems nice. */ 106 int attribs[64]; 107 int i; 108 109 /* load the gl driver from a default path */ 110 if ( ! this->gl_config.driver_loaded ) { 111 /* no driver has been loaded, use default (ourselves) */ 112 if ( X11_GL_LoadLibrary(this, NULL) < 0 ) { 113 return NULL; 114 } 115 } 116 117 /* See if we already have a window which we must use */ 118 if ( SDL_windowid ) { 119 XWindowAttributes a; 120 XVisualInfo vi_in; 121 int out_count; 122 123 XGetWindowAttributes(SDL_Display, SDL_Window, &a); 124 vi_in.screen = SDL_Screen; 125 vi_in.visualid = XVisualIDFromVisual(a.visual); 126 glx_visualinfo = XGetVisualInfo(SDL_Display, 127 VisualScreenMask|VisualIDMask, &vi_in, &out_count); 128 return glx_visualinfo; 129 } 130 131 /* Setup our GLX attributes according to the gl_config. */ 132 i = 0; 133 attribs[i++] = GLX_RGBA; 134 attribs[i++] = GLX_RED_SIZE; 135 attribs[i++] = this->gl_config.red_size; 136 attribs[i++] = GLX_GREEN_SIZE; 137 attribs[i++] = this->gl_config.green_size; 138 attribs[i++] = GLX_BLUE_SIZE; 139 attribs[i++] = this->gl_config.blue_size; 140 141 if( this->gl_config.alpha_size ) { 142 attribs[i++] = GLX_ALPHA_SIZE; 143 attribs[i++] = this->gl_config.alpha_size; 144 } 145 146 if( this->gl_config.double_buffer ) { 147 attribs[i++] = GLX_DOUBLEBUFFER; 148 } 149 150 attribs[i++] = GLX_DEPTH_SIZE; 151 attribs[i++] = this->gl_config.depth_size; 152 153 if( this->gl_config.stencil_size ) { 154 attribs[i++] = GLX_STENCIL_SIZE; 155 attribs[i++] = this->gl_config.stencil_size; 156 } 157 158 if( this->gl_config.accum_red_size ) { 159 attribs[i++] = GLX_ACCUM_RED_SIZE; 160 attribs[i++] = this->gl_config.accum_red_size; 161 } 162 163 if( this->gl_config.accum_green_size ) { 164 attribs[i++] = GLX_ACCUM_GREEN_SIZE; 165 attribs[i++] = this->gl_config.accum_green_size; 166 } 167 168 if( this->gl_config.accum_blue_size ) { 169 attribs[i++] = GLX_ACCUM_BLUE_SIZE; 170 attribs[i++] = this->gl_config.accum_blue_size; 171 } 172 173 if( this->gl_config.accum_alpha_size ) { 174 attribs[i++] = GLX_ACCUM_ALPHA_SIZE; 175 attribs[i++] = this->gl_config.accum_alpha_size; 176 } 177 178 if( this->gl_config.stereo ) { 179 attribs[i++] = GLX_STEREO; 180 } 181 182 if( this->gl_config.multisamplebuffers ) { 183 attribs[i++] = GLX_SAMPLE_BUFFERS_ARB; 184 attribs[i++] = this->gl_config.multisamplebuffers; 185 } 186 187 if( this->gl_config.multisamplesamples ) { 188 attribs[i++] = GLX_SAMPLES_ARB; 189 attribs[i++] = this->gl_config.multisamplesamples; 190 } 191 192 if( this->gl_config.accelerated >= 0 && 193 glXExtensionSupported(this, "GLX_EXT_visual_rating") ) { 194 attribs[i++] = GLX_VISUAL_CAVEAT_EXT; 195 attribs[i++] = GLX_NONE_EXT; 196 } 197 198 #ifdef GLX_DIRECT_COLOR /* Try for a DirectColor visual for gamma support */ 199 if ( !SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) { 200 attribs[i++] = GLX_X_VISUAL_TYPE; 201 attribs[i++] = GLX_DIRECT_COLOR; 202 } 203 #endif 204 attribs[i++] = None; 205 206 glx_visualinfo = this->gl_data->glXChooseVisual(GFX_Display, 207 SDL_Screen, attribs); 208 #ifdef GLX_DIRECT_COLOR 209 if( !glx_visualinfo && !SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) { /* No DirectColor visual? Try again.. */ 210 attribs[i-3] = None; 211 glx_visualinfo = this->gl_data->glXChooseVisual(GFX_Display, 212 SDL_Screen, attribs); 213 } 214 #endif 215 if( !glx_visualinfo ) { 216 SDL_SetError( "Couldn't find matching GLX visual"); 217 return NULL; 218 } 219 /* 220 printf("Found GLX visual 0x%x\n", glx_visualinfo->visualid); 221 */ 222 return glx_visualinfo; 223 #else 224 SDL_SetError("X11 driver not configured with OpenGL"); 225 return NULL; 226 #endif 227 } 228 229 int X11_GL_CreateWindow(_THIS, int w, int h) 230 { 231 int retval; 232 #if SDL_VIDEO_OPENGL_GLX 233 XSetWindowAttributes attributes; 234 unsigned long mask; 235 unsigned long black; 236 237 black = (glx_visualinfo->visual == DefaultVisual(SDL_Display, 238 SDL_Screen)) 239 ? BlackPixel(SDL_Display, SDL_Screen) : 0; 240 attributes.background_pixel = black; 241 attributes.border_pixel = black; 242 attributes.colormap = SDL_XColorMap; 243 mask = CWBackPixel | CWBorderPixel | CWColormap; 244 245 SDL_Window = XCreateWindow(SDL_Display, WMwindow, 246 0, 0, w, h, 0, glx_visualinfo->depth, 247 InputOutput, glx_visualinfo->visual, 248 mask, &attributes); 249 if ( !SDL_Window ) { 250 SDL_SetError("Could not create window"); 251 return -1; 252 } 253 retval = 0; 254 #else 255 SDL_SetError("X11 driver not configured with OpenGL"); 256 retval = -1; 257 #endif 258 return(retval); 259 } 260 261 int X11_GL_CreateContext(_THIS) 262 { 263 int retval; 264 #if SDL_VIDEO_OPENGL_GLX 265 266 /* We do this to create a clean separation between X and GLX errors. */ 267 XSync( SDL_Display, False ); 268 glx_context = this->gl_data->glXCreateContext(GFX_Display, 269 glx_visualinfo, NULL, True); 270 XSync( GFX_Display, False ); 271 272 if ( glx_context == NULL ) { 273 SDL_SetError("Could not create GL context"); 274 return(-1); 275 } 276 if ( X11_GL_MakeCurrent(this) < 0 ) { 277 return(-1); 278 } 279 gl_active = 1; 280 281 if ( !glXExtensionSupported(this, "GLX_SGI_swap_control") ) { 282 this->gl_data->glXSwapIntervalSGI = NULL; 283 } 284 if ( !glXExtensionSupported(this, "GLX_MESA_swap_control") ) { 285 this->gl_data->glXSwapIntervalMESA = NULL; 286 } 287 if ( !glXExtensionSupported(this, "GLX_EXT_swap_control") ) { 288 this->gl_data->glXSwapIntervalEXT = NULL; 289 } 290 291 if ( this->gl_config.swap_control >= 0 ) { 292 int rc = -1; 293 if ( this->gl_data->glXSwapIntervalEXT ) { 294 rc = this->gl_data->glXSwapIntervalEXT(GFX_Display, SDL_Window, 295 this->gl_config.swap_control); 296 } else if ( this->gl_data->glXSwapIntervalMESA ) { 297 rc = this->gl_data->glXSwapIntervalMESA(this->gl_config.swap_control); 298 } else if ( this->gl_data->glXSwapIntervalSGI ) { 299 rc = this->gl_data->glXSwapIntervalSGI(this->gl_config.swap_control); 300 } 301 if (rc == 0) { 302 this->gl_data->swap_interval = this->gl_config.swap_control; 303 } 304 } 305 #else 306 SDL_SetError("X11 driver not configured with OpenGL"); 307 #endif 308 if ( gl_active ) { 309 retval = 0; 310 } else { 311 retval = -1; 312 } 313 return(retval); 314 } 315 316 void X11_GL_Shutdown(_THIS) 317 { 318 #if SDL_VIDEO_OPENGL_GLX 319 /* Clean up OpenGL */ 320 if( glx_context ) { 321 this->gl_data->glXMakeCurrent(GFX_Display, None, NULL); 322 323 if (glx_context != NULL) 324 this->gl_data->glXDestroyContext(GFX_Display, glx_context); 325 326 glx_context = NULL; 327 } 328 gl_active = 0; 329 #endif /* SDL_VIDEO_OPENGL_GLX */ 330 } 331 332 #if SDL_VIDEO_OPENGL_GLX 333 334 /* Make the current context active */ 335 int X11_GL_MakeCurrent(_THIS) 336 { 337 int retval; 338 339 retval = 0; 340 if ( ! this->gl_data->glXMakeCurrent(GFX_Display, 341 SDL_Window, glx_context) ) { 342 SDL_SetError("Unable to make GL context current"); 343 retval = -1; 344 } 345 XSync( GFX_Display, False ); 346 347 /* More Voodoo X server workarounds... Grr... */ 348 SDL_Lock_EventThread(); 349 X11_CheckDGAMouse(this); 350 SDL_Unlock_EventThread(); 351 352 return(retval); 353 } 354 355 /* Get attribute data from glX. */ 356 int X11_GL_GetAttribute(_THIS, SDL_GLattr attrib, int* value) 357 { 358 int retval = -1; 359 int unsupported = 0; 360 int glx_attrib = None; 361 362 switch( attrib ) { 363 case SDL_GL_RED_SIZE: 364 glx_attrib = GLX_RED_SIZE; 365 break; 366 case SDL_GL_GREEN_SIZE: 367 glx_attrib = GLX_GREEN_SIZE; 368 break; 369 case SDL_GL_BLUE_SIZE: 370 glx_attrib = GLX_BLUE_SIZE; 371 break; 372 case SDL_GL_ALPHA_SIZE: 373 glx_attrib = GLX_ALPHA_SIZE; 374 break; 375 case SDL_GL_DOUBLEBUFFER: 376 glx_attrib = GLX_DOUBLEBUFFER; 377 break; 378 case SDL_GL_BUFFER_SIZE: 379 glx_attrib = GLX_BUFFER_SIZE; 380 break; 381 case SDL_GL_DEPTH_SIZE: 382 glx_attrib = GLX_DEPTH_SIZE; 383 break; 384 case SDL_GL_STENCIL_SIZE: 385 glx_attrib = GLX_STENCIL_SIZE; 386 break; 387 case SDL_GL_ACCUM_RED_SIZE: 388 glx_attrib = GLX_ACCUM_RED_SIZE; 389 break; 390 case SDL_GL_ACCUM_GREEN_SIZE: 391 glx_attrib = GLX_ACCUM_GREEN_SIZE; 392 break; 393 case SDL_GL_ACCUM_BLUE_SIZE: 394 glx_attrib = GLX_ACCUM_BLUE_SIZE; 395 break; 396 case SDL_GL_ACCUM_ALPHA_SIZE: 397 glx_attrib = GLX_ACCUM_ALPHA_SIZE; 398 break; 399 case SDL_GL_STEREO: 400 glx_attrib = GLX_STEREO; 401 break; 402 case SDL_GL_MULTISAMPLEBUFFERS: 403 glx_attrib = GLX_SAMPLE_BUFFERS_ARB; 404 break; 405 case SDL_GL_MULTISAMPLESAMPLES: 406 glx_attrib = GLX_SAMPLES_ARB; 407 break; 408 case SDL_GL_ACCELERATED_VISUAL: 409 if ( glXExtensionSupported(this, "GLX_EXT_visual_rating") ) { 410 glx_attrib = GLX_VISUAL_CAVEAT_EXT; 411 retval = this->gl_data->glXGetConfig(GFX_Display, glx_visualinfo, glx_attrib, value); 412 if ( *value == GLX_SLOW_VISUAL_EXT ) { 413 *value = SDL_FALSE; 414 } else { 415 *value = SDL_TRUE; 416 } 417 return retval; 418 } else { 419 unsupported = 1; 420 } 421 break; 422 case SDL_GL_SWAP_CONTROL: 423 if ( ( this->gl_data->glXSwapIntervalEXT ) || 424 ( this->gl_data->glXSwapIntervalMESA ) || 425 ( this->gl_data->glXSwapIntervalSGI ) ) { 426 *value = this->gl_data->swap_interval; 427 return 0; 428 } else { 429 unsupported = 1; 430 } 431 break; 432 default: 433 unsupported = 1; 434 break; 435 } 436 437 if (unsupported) { 438 SDL_SetError("OpenGL attribute is unsupported on this system"); 439 } else { 440 retval = this->gl_data->glXGetConfig(GFX_Display, glx_visualinfo, glx_attrib, value); 441 } 442 return retval; 443 } 444 445 void X11_GL_SwapBuffers(_THIS) 446 { 447 this->gl_data->glXSwapBuffers(GFX_Display, SDL_Window); 448 } 449 450 #endif /* SDL_VIDEO_OPENGL_GLX */ 451 452 #define OPENGL_REQUIRS_DLOPEN 453 #if defined(OPENGL_REQUIRS_DLOPEN) && defined(SDL_LOADSO_DLOPEN) 454 #include <dlfcn.h> 455 #define GL_LoadObject(X) dlopen(X, (RTLD_NOW|RTLD_GLOBAL)) 456 #define GL_LoadFunction dlsym 457 #define GL_UnloadObject dlclose 458 #else 459 #define GL_LoadObject SDL_LoadObject 460 #define GL_LoadFunction SDL_LoadFunction 461 #define GL_UnloadObject SDL_UnloadObject 462 #endif 463 464 void X11_GL_UnloadLibrary(_THIS) 465 { 466 #if SDL_VIDEO_OPENGL_GLX 467 if ( this->gl_config.driver_loaded ) { 468 469 GL_UnloadObject(this->gl_config.dll_handle); 470 471 this->gl_data->glXGetProcAddress = NULL; 472 this->gl_data->glXChooseVisual = NULL; 473 this->gl_data->glXCreateContext = NULL; 474 this->gl_data->glXDestroyContext = NULL; 475 this->gl_data->glXMakeCurrent = NULL; 476 this->gl_data->glXSwapBuffers = NULL; 477 this->gl_data->glXSwapIntervalSGI = NULL; 478 this->gl_data->glXSwapIntervalMESA = NULL; 479 this->gl_data->glXSwapIntervalEXT = NULL; 480 481 this->gl_config.dll_handle = NULL; 482 this->gl_config.driver_loaded = 0; 483 } 484 #endif 485 } 486 487 #if SDL_VIDEO_OPENGL_GLX 488 489 /* Passing a NULL path means load pointers from the application */ 490 int X11_GL_LoadLibrary(_THIS, const char* path) 491 { 492 void* handle = NULL; 493 494 if ( gl_active ) { 495 SDL_SetError("OpenGL context already created"); 496 return -1; 497 } 498 499 if ( path == NULL ) { 500 path = SDL_getenv("SDL_VIDEO_GL_DRIVER"); 501 if ( path == NULL ) { 502 path = DEFAULT_OPENGL; 503 } 504 } 505 506 handle = GL_LoadObject(path); 507 if ( handle == NULL ) { 508 #if defined(OPENGL_REQUIRS_DLOPEN) && defined(SDL_LOADSO_DLOPEN) 509 SDL_SetError("Failed loading %s", path); 510 #else 511 /* SDL_LoadObject() will call SDL_SetError() for us. */ 512 #endif 513 return -1; 514 } 515 516 /* Unload the old driver and reset the pointers */ 517 X11_GL_UnloadLibrary(this); 518 519 /* Save the handle for X11_GL_GetProcAddress() */ 520 this->gl_config.dll_handle = handle; 521 522 /* Load new function pointers */ 523 this->gl_data->glXGetProcAddress = 524 (void *(*)(const GLubyte *)) GL_LoadFunction(handle, "glXGetProcAddressARB"); 525 this->gl_data->glXChooseVisual = 526 (XVisualInfo *(*)(Display *, int, int *)) X11_GL_GetProcAddress(this, "glXChooseVisual"); 527 this->gl_data->glXCreateContext = 528 (GLXContext (*)(Display *, XVisualInfo *, GLXContext, int)) X11_GL_GetProcAddress(this, "glXCreateContext"); 529 this->gl_data->glXDestroyContext = 530 (void (*)(Display *, GLXContext)) X11_GL_GetProcAddress(this, "glXDestroyContext"); 531 this->gl_data->glXMakeCurrent = 532 (int (*)(Display *, GLXDrawable, GLXContext)) X11_GL_GetProcAddress(this, "glXMakeCurrent"); 533 this->gl_data->glXSwapBuffers = 534 (void (*)(Display *, GLXDrawable)) X11_GL_GetProcAddress(this, "glXSwapBuffers"); 535 this->gl_data->glXGetConfig = 536 (int (*)(Display *, XVisualInfo *, int, int *)) X11_GL_GetProcAddress(this, "glXGetConfig"); 537 this->gl_data->glXQueryExtensionsString = 538 (const char *(*)(Display *, int)) X11_GL_GetProcAddress(this, "glXQueryExtensionsString"); 539 this->gl_data->glXSwapIntervalSGI = 540 (int (*)(int)) X11_GL_GetProcAddress(this, "glXSwapIntervalSGI"); 541 this->gl_data->glXSwapIntervalMESA = 542 (GLint (*)(unsigned)) X11_GL_GetProcAddress(this, "glXSwapIntervalMESA"); 543 this->gl_data->glXSwapIntervalEXT = 544 (int (*)(Display*,GLXDrawable,int)) X11_GL_GetProcAddress(this, "glXSwapIntervalEXT"); 545 546 if ( (this->gl_data->glXChooseVisual == NULL) || 547 (this->gl_data->glXCreateContext == NULL) || 548 (this->gl_data->glXDestroyContext == NULL) || 549 (this->gl_data->glXMakeCurrent == NULL) || 550 (this->gl_data->glXSwapBuffers == NULL) || 551 (this->gl_data->glXGetConfig == NULL) || 552 (this->gl_data->glXQueryExtensionsString == NULL)) { 553 GL_UnloadObject(this->gl_config.dll_handle); 554 this->gl_config.dll_handle = NULL; 555 SDL_SetError("Could not retrieve OpenGL functions"); 556 return -1; 557 } 558 559 this->gl_config.driver_loaded = 1; 560 if ( path ) { 561 SDL_strlcpy(this->gl_config.driver_path, path, 562 SDL_arraysize(this->gl_config.driver_path)); 563 } else { 564 *this->gl_config.driver_path = '\0'; 565 } 566 return 0; 567 } 568 569 void *X11_GL_GetProcAddress(_THIS, const char* proc) 570 { 571 if ( this->gl_data->glXGetProcAddress ) { 572 return this->gl_data->glXGetProcAddress((const GLubyte *)proc); 573 } 574 return GL_LoadFunction(this->gl_config.dll_handle, proc); 575 } 576 577 #endif /* SDL_VIDEO_OPENGL_GLX */ 578