Home | History | Annotate | Download | only in x11
      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