src/video/x11/SDL_x11gl.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 08 Dec 2005 14:21:17 +0000
changeset 1196 b81f54c3963f
parent 1191 2bd4cec0de63
child 1246 ca51a76a7328
permissions -rw-r--r--
Fixed compile warnings with gcc 4
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 #include <stdlib.h>	/* For getenv() prototype */
    29 #include <string.h>
    30 
    31 #include "SDL_events_c.h"
    32 #include "SDL_error.h"
    33 #include "SDL_x11video.h"
    34 #include "SDL_x11dga_c.h"
    35 #include "SDL_x11gl_c.h"
    36 
    37 #if defined(sgi)
    38 /* IRIX doesn't have a GL library versioning system */
    39 #define DEFAULT_OPENGL	"libGL.so"
    40 #elif defined(MACOSX)
    41 #define DEFAULT_OPENGL	"/usr/X11R6/lib/libGL.1.dylib"
    42 #else
    43 #define DEFAULT_OPENGL	"libGL.so.1"
    44 #endif
    45 
    46 #ifndef GLX_ARB_multisample
    47 #define GLX_ARB_multisample
    48 #define GLX_SAMPLE_BUFFERS_ARB             100000
    49 #define GLX_SAMPLES_ARB                    100001
    50 #endif
    51 
    52 /* return the preferred visual to use for openGL graphics */
    53 XVisualInfo *X11_GL_GetVisual(_THIS)
    54 {
    55 #ifdef HAVE_OPENGL_X11
    56 	/* 64 seems nice. */
    57 	int attribs[64];
    58 	int i;
    59 
    60 	/* load the gl driver from a default path */
    61 	if ( ! this->gl_config.driver_loaded ) {
    62 	        /* no driver has been loaded, use default (ourselves) */
    63 	        if ( X11_GL_LoadLibrary(this, NULL) < 0 ) {
    64 		        return NULL;
    65 		}
    66 	}
    67 
    68 	/* See if we already have a window which we must use */
    69 	if ( SDL_windowid ) {
    70 		XWindowAttributes a;
    71 		XVisualInfo vi_in;
    72 		int out_count;
    73 
    74 		pXGetWindowAttributes(SDL_Display, SDL_Window, &a);
    75 		vi_in.screen = SDL_Screen;
    76 		vi_in.visualid = pXVisualIDFromVisual(a.visual);
    77 		glx_visualinfo = pXGetVisualInfo(SDL_Display,
    78 	                     VisualScreenMask|VisualIDMask, &vi_in, &out_count);
    79 		return glx_visualinfo;
    80 	}
    81 
    82         /* Setup our GLX attributes according to the gl_config. */
    83 	i = 0;
    84 	attribs[i++] = GLX_RGBA;
    85 	attribs[i++] = GLX_RED_SIZE;
    86 	attribs[i++] = this->gl_config.red_size;
    87 	attribs[i++] = GLX_GREEN_SIZE;
    88 	attribs[i++] = this->gl_config.green_size;
    89 	attribs[i++] = GLX_BLUE_SIZE;
    90 	attribs[i++] = this->gl_config.blue_size;
    91 
    92 	if( this->gl_config.alpha_size ) {
    93 		attribs[i++] = GLX_ALPHA_SIZE;
    94 		attribs[i++] = this->gl_config.alpha_size;
    95 	}
    96 
    97 	if( this->gl_config.buffer_size ) {
    98 		attribs[i++] = GLX_BUFFER_SIZE;
    99 		attribs[i++] = this->gl_config.buffer_size;
   100 	}
   101 
   102 	if( this->gl_config.double_buffer ) {
   103 		attribs[i++] = GLX_DOUBLEBUFFER;
   104 	}
   105 
   106 	attribs[i++] = GLX_DEPTH_SIZE;
   107 	attribs[i++] = this->gl_config.depth_size;
   108 
   109 	if( this->gl_config.stencil_size ) {
   110 		attribs[i++] = GLX_STENCIL_SIZE;
   111 		attribs[i++] = this->gl_config.stencil_size;
   112 	}
   113 
   114 	if( this->gl_config.accum_red_size ) {
   115 		attribs[i++] = GLX_ACCUM_RED_SIZE;
   116 		attribs[i++] = this->gl_config.accum_red_size;
   117 	}
   118 
   119 	if( this->gl_config.accum_green_size ) {
   120 		attribs[i++] = GLX_ACCUM_GREEN_SIZE;
   121 		attribs[i++] = this->gl_config.accum_green_size;
   122 	}
   123 
   124 	if( this->gl_config.accum_blue_size ) {
   125 		attribs[i++] = GLX_ACCUM_BLUE_SIZE;
   126 		attribs[i++] = this->gl_config.accum_blue_size;
   127 	}
   128 
   129 	if( this->gl_config.accum_alpha_size ) {
   130 		attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
   131 		attribs[i++] = this->gl_config.accum_alpha_size;
   132 	}
   133 
   134 	if( this->gl_config.stereo ) {
   135 		attribs[i++] = GLX_STEREO;
   136 		attribs[i++] = this->gl_config.stereo;
   137 	}
   138 	
   139 	if( this->gl_config.multisamplebuffers ) {
   140 		attribs[i++] = GLX_SAMPLE_BUFFERS_ARB;
   141 		attribs[i++] = this->gl_config.multisamplebuffers;
   142 	}
   143 	
   144 	if( this->gl_config.multisamplesamples ) {
   145 		attribs[i++] = GLX_SAMPLES_ARB;
   146 		attribs[i++] = this->gl_config.multisamplesamples;
   147 	}
   148 
   149 #ifdef GLX_DIRECT_COLOR /* Try for a DirectColor visual for gamma support */
   150 	if ( !getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) {
   151 		attribs[i++] = GLX_X_VISUAL_TYPE;
   152 		attribs[i++] = GLX_DIRECT_COLOR;
   153 	}
   154 #endif
   155 	attribs[i++] = None;
   156 
   157  	glx_visualinfo = this->gl_data->glXChooseVisual(GFX_Display, 
   158 						  SDL_Screen, attribs);
   159 #ifdef GLX_DIRECT_COLOR
   160 	if( !glx_visualinfo && !getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) { /* No DirectColor visual?  Try again.. */
   161 		attribs[i-3] = None;
   162  		glx_visualinfo = this->gl_data->glXChooseVisual(GFX_Display, 
   163 						  SDL_Screen, attribs);
   164 	}
   165 #endif
   166 	if( !glx_visualinfo ) {
   167 		SDL_SetError( "Couldn't find matching GLX visual");
   168 		return NULL;
   169 	}
   170 	return glx_visualinfo;
   171 #else
   172 	SDL_SetError("X11 driver not configured with OpenGL");
   173 	return NULL;
   174 #endif
   175 }
   176 
   177 int X11_GL_CreateWindow(_THIS, int w, int h)
   178 {
   179 	int retval;
   180 #ifdef HAVE_OPENGL_X11
   181 	XSetWindowAttributes attributes;
   182 	unsigned long mask;
   183 	unsigned long black;
   184 
   185 	black = (glx_visualinfo->visual == DefaultVisual(SDL_Display,
   186 						 	SDL_Screen))
   187 	       	? BlackPixel(SDL_Display, SDL_Screen) : 0;
   188 	attributes.background_pixel = black;
   189 	attributes.border_pixel = black;
   190 	attributes.colormap = SDL_XColorMap;
   191 	mask = CWBackPixel | CWBorderPixel | CWColormap;
   192 
   193 	SDL_Window = pXCreateWindow(SDL_Display, WMwindow,
   194 			0, 0, w, h, 0, glx_visualinfo->depth,
   195 			InputOutput, glx_visualinfo->visual,
   196 			mask, &attributes);
   197 	if ( !SDL_Window ) {
   198 		SDL_SetError("Could not create window");
   199 		return -1;
   200 	}
   201 	retval = 0;
   202 #else
   203 	SDL_SetError("X11 driver not configured with OpenGL");
   204 	retval = -1;
   205 #endif
   206 	return(retval);
   207 }
   208 
   209 int X11_GL_CreateContext(_THIS)
   210 {
   211 	int retval;
   212 #ifdef HAVE_OPENGL_X11
   213 	/* We do this to create a clean separation between X and GLX errors. */
   214 	pXSync( SDL_Display, False );
   215 	glx_context = this->gl_data->glXCreateContext(GFX_Display, 
   216 				     glx_visualinfo, NULL, True);
   217 	pXSync( GFX_Display, False );
   218 
   219 	if (glx_context == NULL) {
   220 		SDL_SetError("Could not create GL context");
   221 		return -1;
   222 	}
   223 
   224 	gl_active = 1;
   225 #else
   226 	SDL_SetError("X11 driver not configured with OpenGL");
   227 #endif
   228 	if ( gl_active ) {
   229 		retval = 0;
   230 	} else {
   231 		retval = -1;
   232 	}
   233 	return(retval);
   234 }
   235 
   236 void X11_GL_Shutdown(_THIS)
   237 {
   238 #ifdef HAVE_OPENGL_X11
   239 	/* Clean up OpenGL */
   240 	if( glx_context ) {
   241 		this->gl_data->glXMakeCurrent(GFX_Display, None, NULL);
   242 
   243 		if (glx_context != NULL)
   244 			this->gl_data->glXDestroyContext(GFX_Display, glx_context);
   245 
   246 		if( this->gl_data->glXReleaseBuffersMESA ) {
   247 		    this->gl_data->glXReleaseBuffersMESA(GFX_Display,SDL_Window);
   248 		}
   249 		glx_context = NULL;
   250 	}
   251 	gl_active = 0;
   252 #endif /* HAVE_OPENGL_X11 */
   253 }
   254 
   255 #ifdef HAVE_OPENGL_X11
   256 
   257 static int ExtensionSupported(const char *extension)
   258 {
   259 	const GLubyte *extensions = NULL;
   260 	const GLubyte *start;
   261 	GLubyte *where, *terminator;
   262 
   263 	/* Extension names should not have spaces. */
   264 	where = (GLubyte *) strchr(extension, ' ');
   265 	if (where || *extension == '\0')
   266 	      return 0;
   267 	
   268 	extensions = current_video->glGetString(GL_EXTENSIONS);
   269 	/* It takes a bit of care to be fool-proof about parsing the
   270 	 *      OpenGL extensions string. Don't be fooled by sub-strings,
   271 	 *           etc. */
   272 	
   273 	start = extensions;
   274 	
   275 	for (;;)
   276 	{
   277 		where = (GLubyte *) strstr((const char *) start, extension);
   278 		if (!where) break;
   279 		
   280 		terminator = where + strlen(extension);
   281 		if (where == start || *(where - 1) == ' ')
   282 	        if (*terminator == ' ' || *terminator == '\0') return 1;
   283 						  
   284 		start = terminator;
   285 	}
   286 	
   287 	return 0;
   288 }
   289 
   290 /* Make the current context active */
   291 int X11_GL_MakeCurrent(_THIS)
   292 {
   293 	int retval;
   294 	
   295 	retval = 0;
   296 	if ( ! this->gl_data->glXMakeCurrent(GFX_Display,
   297 	                                     SDL_Window, glx_context) ) {
   298 		SDL_SetError("Unable to make GL context current");
   299 		retval = -1;
   300 	}
   301 	pXSync( GFX_Display, False );
   302 
   303 	/* 
   304 	 * The context is now current, check for glXReleaseBuffersMESA() 
   305 	 * extension. If extension is _not_ supported, destroy the pointer 
   306 	 * (to make sure it will not be called in X11_GL_Shutdown() ).
   307 	 * 
   308 	 * DRI/Mesa drivers include glXReleaseBuffersMESA() in the libGL.so, 
   309 	 * but there's no need to call it (is is only needed for some old 
   310 	 * non-DRI drivers).
   311 	 * 
   312 	 * When using for example glew (http://glew.sf.net), dlsym() for
   313 	 * glXReleaseBuffersMESA() returns the pointer from the glew library
   314 	 * (namespace conflict).
   315 	 *
   316 	 * The glXReleaseBuffersMESA() pointer in the glew is NULL, if the 
   317 	 * driver doesn't support this extension. So blindly calling it will
   318 	 * cause segfault with DRI/Mesa drivers!
   319 	 * 
   320 	 */
   321 	
   322 	if ( ! ExtensionSupported("glXReleaseBuffersMESA") ) {
   323 		this->gl_data->glXReleaseBuffersMESA = NULL;
   324 	}
   325 
   326 	/* More Voodoo X server workarounds... Grr... */
   327 	SDL_Lock_EventThread();
   328 	X11_CheckDGAMouse(this);
   329 	SDL_Unlock_EventThread();
   330 
   331 	return(retval);
   332 }
   333 
   334 /* Get attribute data from glX. */
   335 int X11_GL_GetAttribute(_THIS, SDL_GLattr attrib, int* value)
   336 {
   337 	int retval;
   338 	int glx_attrib = None;
   339 
   340 	switch( attrib ) {
   341 	    case SDL_GL_RED_SIZE:
   342 		glx_attrib = GLX_RED_SIZE;
   343 		break;
   344 	    case SDL_GL_GREEN_SIZE:
   345 		glx_attrib = GLX_GREEN_SIZE;
   346 		break;
   347 	    case SDL_GL_BLUE_SIZE:
   348 		glx_attrib = GLX_BLUE_SIZE;
   349 		break;
   350 	    case SDL_GL_ALPHA_SIZE:
   351 		glx_attrib = GLX_ALPHA_SIZE;
   352 		break;
   353 	    case SDL_GL_DOUBLEBUFFER:
   354 		glx_attrib = GLX_DOUBLEBUFFER;
   355 		break;
   356 	    case SDL_GL_BUFFER_SIZE:
   357 		glx_attrib = GLX_BUFFER_SIZE;
   358 		break;
   359 	    case SDL_GL_DEPTH_SIZE:
   360 		glx_attrib = GLX_DEPTH_SIZE;
   361 		break;
   362 	    case SDL_GL_STENCIL_SIZE:
   363 		glx_attrib = GLX_STENCIL_SIZE;
   364 		break;
   365 	    case SDL_GL_ACCUM_RED_SIZE:
   366 		glx_attrib = GLX_ACCUM_RED_SIZE;
   367 		break;
   368 	    case SDL_GL_ACCUM_GREEN_SIZE:
   369 		glx_attrib = GLX_ACCUM_GREEN_SIZE;
   370 		break;
   371 	    case SDL_GL_ACCUM_BLUE_SIZE:
   372 		glx_attrib = GLX_ACCUM_BLUE_SIZE;
   373 		break;
   374 	    case SDL_GL_ACCUM_ALPHA_SIZE:
   375 		glx_attrib = GLX_ACCUM_ALPHA_SIZE;
   376 		break;
   377 	    case SDL_GL_STEREO:
   378 		glx_attrib = GLX_STEREO;
   379 		break;
   380  	    case SDL_GL_MULTISAMPLEBUFFERS:
   381  		glx_attrib = GLX_SAMPLE_BUFFERS_ARB;
   382  		break;
   383  	    case SDL_GL_MULTISAMPLESAMPLES:
   384  		glx_attrib = GLX_SAMPLES_ARB;
   385  		break;
   386 	    default:
   387 		return(-1);
   388 	}
   389 
   390 	retval = this->gl_data->glXGetConfig(GFX_Display, glx_visualinfo, glx_attrib, value);
   391 
   392 	return retval;
   393 }
   394 
   395 void X11_GL_SwapBuffers(_THIS)
   396 {
   397 	this->gl_data->glXSwapBuffers(GFX_Display, SDL_Window);
   398 }
   399 
   400 #endif /* HAVE_OPENGL_X11 */
   401 
   402 void X11_GL_UnloadLibrary(_THIS)
   403 {
   404 #ifdef HAVE_OPENGL_X11
   405 	if ( this->gl_config.driver_loaded ) {
   406 
   407 		/* !!! FIXME: Can we just use SDL_UnloadObject() everywhere? */
   408 		#ifdef USE_DLOPEN
   409 		dlclose(this->gl_config.dll_handle);
   410 		#else
   411 		SDL_UnloadObject(this->gl_config.dll_handle);
   412 		#endif
   413 
   414 		this->gl_data->glXGetProcAddress = NULL;
   415 		this->gl_data->glXChooseVisual = NULL;
   416 		this->gl_data->glXCreateContext = NULL;
   417 		this->gl_data->glXDestroyContext = NULL;
   418 		this->gl_data->glXMakeCurrent = NULL;
   419 		this->gl_data->glXSwapBuffers = NULL;
   420 
   421 		this->gl_config.dll_handle = NULL;
   422 		this->gl_config.driver_loaded = 0;
   423 	}
   424 #endif
   425 }
   426 
   427 #ifdef HAVE_OPENGL_X11
   428 
   429 static void *do_dlsym(void *handle, const char *name)
   430 {
   431 	/* !!! FIXME: Can we just use SDL_LoadFunction() everywhere? */
   432 #ifdef USE_DLOPEN
   433 	return dlsym(handle, name);
   434 #else
   435 	return SDL_LoadFunction(handle, name);
   436 #endif
   437 }
   438 
   439 #if defined(__OpenBSD__) && !defined(__ELF__)
   440 #define do_dlsym(x,y) do_dlsym(x, "_" y)
   441 #endif
   442 
   443 /* Passing a NULL path means load pointers from the application */
   444 int X11_GL_LoadLibrary(_THIS, const char* path) 
   445 {
   446 	void* handle = NULL;
   447 
   448 	if ( gl_active ) {
   449 		SDL_SetError("OpenGL context already created");
   450 		return -1;
   451 	}
   452 
   453 	if ( path == NULL ) {
   454 		path = getenv("SDL_VIDEO_GL_DRIVER");
   455 		if ( path == NULL ) {
   456 			path = DEFAULT_OPENGL;
   457 		}
   458 	}
   459 
   460 	/* !!! FIXME: Can we just use SDL_LoadObject() everywhere? */
   461 	#ifdef USE_DLOPEN
   462 	{
   463 		#ifdef RTLD_GLOBAL
   464 			int dlopen_flags = RTLD_LAZY | RTLD_GLOBAL;
   465 		#else
   466 			int dlopen_flags = RTLD_LAZY;
   467 		#endif
   468 		handle = dlopen(path, dlopen_flags);
   469 	}
   470 	#else
   471 		handle = SDL_LoadObject(path);
   472 	#endif
   473 
   474 	if ( handle == NULL ) {
   475 		SDL_SetError("Could not load OpenGL library");
   476 		return -1;
   477 	}
   478 
   479 	/* Unload the old driver and reset the pointers */
   480 	X11_GL_UnloadLibrary(this);
   481 
   482 	/* Load new function pointers */
   483 	this->gl_data->glXGetProcAddress =
   484 		(void *(*)(const GLubyte *)) do_dlsym(handle, "glXGetProcAddressARB");
   485 	this->gl_data->glXChooseVisual =
   486 		(XVisualInfo *(*)(Display *, int, int *)) do_dlsym(handle, "glXChooseVisual");
   487 	this->gl_data->glXCreateContext =
   488 		(GLXContext (*)(Display *, XVisualInfo *, GLXContext, int)) do_dlsym(handle, "glXCreateContext");
   489 	this->gl_data->glXDestroyContext =
   490 		(void (*)(Display *, GLXContext)) do_dlsym(handle, "glXDestroyContext");
   491 	this->gl_data->glXMakeCurrent =
   492 		(int (*)(Display *, GLXDrawable, GLXContext)) do_dlsym(handle, "glXMakeCurrent");
   493 	this->gl_data->glXSwapBuffers =
   494 		(void (*)(Display *, GLXDrawable)) do_dlsym(handle, "glXSwapBuffers");
   495 	this->gl_data->glXGetConfig =
   496 		(int (*)(Display *, XVisualInfo *, int, int *)) do_dlsym(handle, "glXGetConfig");
   497 	this->gl_data->glXQueryExtensionsString =
   498 		(const char *(*)(Display *, int)) do_dlsym(handle, "glXQueryExtensionsString");
   499 	
   500 	/* We don't compare below for this in case we're not using Mesa. */
   501 	this->gl_data->glXReleaseBuffersMESA =
   502 		(void (*)(Display *, GLXDrawable)) do_dlsym( handle, "glXReleaseBuffersMESA" );
   503 	
   504 	
   505 	if ( (this->gl_data->glXChooseVisual == NULL) || 
   506 	     (this->gl_data->glXCreateContext == NULL) ||
   507 	     (this->gl_data->glXDestroyContext == NULL) ||
   508 	     (this->gl_data->glXMakeCurrent == NULL) ||
   509 	     (this->gl_data->glXSwapBuffers == NULL) ||
   510 	     (this->gl_data->glXGetConfig == NULL) ||
   511 	     (this->gl_data->glXQueryExtensionsString == NULL)) {
   512 		SDL_SetError("Could not retrieve OpenGL functions");
   513 		return -1;
   514 	}
   515 
   516 	this->gl_config.dll_handle = handle;
   517 	this->gl_config.driver_loaded = 1;
   518 	if ( path ) {
   519 		strncpy(this->gl_config.driver_path, path,
   520 			sizeof(this->gl_config.driver_path)-1);
   521 	} else {
   522 		strcpy(this->gl_config.driver_path, "");
   523 	}
   524 	return 0;
   525 }
   526 
   527 void *X11_GL_GetProcAddress(_THIS, const char* proc)
   528 {
   529 	static char procname[1024];
   530 	void* handle;
   531 	void* retval;
   532 	
   533 	handle = this->gl_config.dll_handle;
   534 	if ( this->gl_data->glXGetProcAddress ) {
   535 		return this->gl_data->glXGetProcAddress((const GLubyte *)proc);
   536 	}
   537 #if defined(__OpenBSD__) && !defined(__ELF__)
   538 #undef do_dlsym
   539 #endif
   540 	retval = do_dlsym(handle, proc);
   541 	if (!retval && strlen(proc) <= 1022) {
   542 		procname[0] = '_';
   543 		strcpy(procname + 1, proc);
   544 		retval = do_dlsym(handle, procname);
   545 	}
   546 	return retval;
   547 }
   548 
   549 #endif /* HAVE_OPENGL_X11 */