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