src/video/x11/SDL_x11gl.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 28 Jun 2003 17:27:33 +0000
changeset 638 b0108e9dea53
parent 566 d6e7d7006062
child 643 564716cfb502
permissions -rw-r--r--
Date: Sun, 11 May 2003 19:59:06 +0300
From: Pasi K?rkk?inen
Subject: [PATCH] fix SDL OpenGL segfault with DRI/Mesa drivers and Glew

Hello!

The attached patch fixes a bug in SDL which causes SDL to crash in
X11_GL_Shutdown() if you are using DRI/Mesa drivers AND glew
(http://glew.sf.net).

The bug is caused by a namespace collision affecting dlsym() to fetch wrong
pointer for glXReleaseBuffersMESA() (uninitialized pointer from glew because
the extension is NOT supported by the driver) and then SDL calling it in
X11_GL_Shutdown().

SDL should check if the glXReleaseBuffersMESA() is really supported by the
driver (from the extensions string) before calling it.

Attached patch adds extension string parsing to check if
glXReleaseBuffersMESA() is really supported (and this way
prevents the segfault).

Availability of the extensions should be _always_ checked from the
extensions string rather than using dlsym()!

Please add it to the next version of SDL.

Thanks to gltron and author of glew to help fixing this.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  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 #define DEFAULT_OPENGL	"libGL.so.1"
    38 
    39 /* return the preferred visual to use for openGL graphics */
    40 XVisualInfo *X11_GL_GetVisual(_THIS)
    41 {
    42 #ifdef HAVE_OPENGL
    43 	/* 64 seems nice. */
    44 	int attribs[64];
    45 	int i;
    46 
    47 	/* load the gl driver from a default path */
    48 	if ( ! this->gl_config.driver_loaded ) {
    49 	        /* no driver has been loaded, use default (ourselves) */
    50 	        if ( X11_GL_LoadLibrary(this, NULL) < 0 ) {
    51 		        return NULL;
    52 		}
    53 	}
    54 
    55 	/* See if we already have a window which we must use */
    56 	if ( SDL_windowid ) {
    57 		XWindowAttributes a;
    58 		XVisualInfo vi_in;
    59 		int out_count;
    60 
    61 		XGetWindowAttributes(SDL_Display, SDL_Window, &a);
    62 		vi_in.screen = SDL_Screen;
    63 		vi_in.visualid = XVisualIDFromVisual(a.visual);
    64 		glx_visualinfo = XGetVisualInfo(SDL_Display,
    65 	                     VisualScreenMask|VisualIDMask, &vi_in, &out_count);
    66 		return glx_visualinfo;
    67 	}
    68 
    69         /* Setup our GLX attributes according to the gl_config. */
    70 	i = 0;
    71 	attribs[i++] = GLX_RGBA;
    72 	attribs[i++] = GLX_RED_SIZE;
    73 	attribs[i++] = this->gl_config.red_size;
    74 	attribs[i++] = GLX_GREEN_SIZE;
    75 	attribs[i++] = this->gl_config.green_size;
    76 	attribs[i++] = GLX_BLUE_SIZE;
    77 	attribs[i++] = this->gl_config.blue_size;
    78 
    79 	if( this->gl_config.alpha_size ) {
    80 		attribs[i++] = GLX_ALPHA_SIZE;
    81 		attribs[i++] = this->gl_config.alpha_size;
    82 	}
    83 
    84 	if( this->gl_config.buffer_size ) {
    85 		attribs[i++] = GLX_BUFFER_SIZE;
    86 		attribs[i++] = this->gl_config.buffer_size;
    87 	}
    88 
    89 	if( this->gl_config.double_buffer ) {
    90 		attribs[i++] = GLX_DOUBLEBUFFER;
    91 	}
    92 
    93 	attribs[i++] = GLX_DEPTH_SIZE;
    94 	attribs[i++] = this->gl_config.depth_size;
    95 
    96 	if( this->gl_config.stencil_size ) {
    97 		attribs[i++] = GLX_STENCIL_SIZE;
    98 		attribs[i++] = this->gl_config.stencil_size;
    99 	}
   100 
   101 	if( this->gl_config.accum_red_size ) {
   102 		attribs[i++] = GLX_ACCUM_RED_SIZE;
   103 		attribs[i++] = this->gl_config.accum_red_size;
   104 	}
   105 
   106 	if( this->gl_config.accum_green_size ) {
   107 		attribs[i++] = GLX_ACCUM_GREEN_SIZE;
   108 		attribs[i++] = this->gl_config.accum_green_size;
   109 	}
   110 
   111 	if( this->gl_config.accum_blue_size ) {
   112 		attribs[i++] = GLX_ACCUM_BLUE_SIZE;
   113 		attribs[i++] = this->gl_config.accum_blue_size;
   114 	}
   115 
   116 	if( this->gl_config.accum_alpha_size ) {
   117 		attribs[i++] = GLX_ACCUM_ALPHA_SIZE;
   118 		attribs[i++] = this->gl_config.accum_alpha_size;
   119 	}
   120 
   121 	if( this->gl_config.stereo ) {
   122 		attribs[i++] = GLX_STEREO;
   123 		attribs[i++] = this->gl_config.stereo;
   124 	}
   125 
   126 #ifdef GLX_DIRECT_COLOR /* Try for a DirectColor visual for gamma support */
   127 	attribs[i++] = GLX_X_VISUAL_TYPE;
   128 	attribs[i++] = GLX_DIRECT_COLOR;
   129 #endif
   130 	attribs[i++] = None;
   131 
   132  	glx_visualinfo = this->gl_data->glXChooseVisual(GFX_Display, 
   133 						  SDL_Screen, attribs);
   134 #ifdef GLX_DIRECT_COLOR
   135 	if( !glx_visualinfo ) { /* No DirectColor visual?  Try again.. */
   136 		attribs[i-3] = None;
   137  		glx_visualinfo = this->gl_data->glXChooseVisual(GFX_Display, 
   138 						  SDL_Screen, attribs);
   139 	}
   140 #endif
   141 	if( !glx_visualinfo ) {
   142 		SDL_SetError( "Couldn't find matching GLX visual");
   143 		return NULL;
   144 	}
   145 	return glx_visualinfo;
   146 #else
   147 	SDL_SetError("X11 driver not configured with OpenGL");
   148 	return NULL;
   149 #endif
   150 }
   151 
   152 int X11_GL_CreateWindow(_THIS, int w, int h)
   153 {
   154 	int retval;
   155 #ifdef HAVE_OPENGL
   156 	XSetWindowAttributes attributes;
   157 	unsigned long mask;
   158 	unsigned long black;
   159 
   160 	black = (glx_visualinfo->visual == DefaultVisual(SDL_Display,
   161 						 	SDL_Screen))
   162 	       	? BlackPixel(SDL_Display, SDL_Screen) : 0;
   163 	attributes.background_pixel = black;
   164 	attributes.border_pixel = black;
   165 	attributes.colormap = SDL_XColorMap;
   166 	mask = CWBackPixel | CWBorderPixel | CWColormap;
   167 
   168 	SDL_Window = XCreateWindow(SDL_Display, WMwindow,
   169 			0, 0, w, h, 0, glx_visualinfo->depth,
   170 			InputOutput, glx_visualinfo->visual,
   171 			mask, &attributes);
   172 	if ( !SDL_Window ) {
   173 		SDL_SetError("Could not create window");
   174 		return -1;
   175 	}
   176 	retval = 0;
   177 #else
   178 	SDL_SetError("X11 driver not configured with OpenGL");
   179 	retval = -1;
   180 #endif
   181 	return(retval);
   182 }
   183 
   184 int X11_GL_CreateContext(_THIS)
   185 {
   186 	int retval;
   187 #ifdef HAVE_OPENGL
   188 	/* We do this to create a clean separation between X and GLX errors. */
   189 	XSync( SDL_Display, False );
   190 	glx_context = this->gl_data->glXCreateContext(GFX_Display, 
   191 				     glx_visualinfo, NULL, True);
   192 	XSync( GFX_Display, False );
   193 
   194 	if (glx_context == NULL) {
   195 		SDL_SetError("Could not create GL context");
   196 		return -1;
   197 	}
   198 
   199 	gl_active = 1;
   200 #else
   201 	SDL_SetError("X11 driver not configured with OpenGL");
   202 #endif
   203 	if ( gl_active ) {
   204 		retval = 0;
   205 	} else {
   206 		retval = -1;
   207 	}
   208 	return(retval);
   209 }
   210 
   211 void X11_GL_Shutdown(_THIS)
   212 {
   213 #ifdef HAVE_OPENGL
   214 	/* Clean up OpenGL */
   215 	if( glx_context ) {
   216 		this->gl_data->glXMakeCurrent(GFX_Display, None, NULL);
   217 
   218 		if (glx_context != NULL)
   219 			this->gl_data->glXDestroyContext(GFX_Display, glx_context);
   220 
   221 		if( this->gl_data->glXReleaseBuffersMESA ) {
   222 		    this->gl_data->glXReleaseBuffersMESA(GFX_Display,SDL_Window);
   223 		}
   224 		glx_context = NULL;
   225 	}
   226 	gl_active = 0;
   227 #endif /* HAVE_OPENGL */
   228 }
   229 
   230 #ifdef HAVE_OPENGL
   231 
   232 static int ExtensionSupported(const char *extension, const char *all_extensions)
   233 {
   234 	const GLubyte *extensions = NULL;
   235 	const GLubyte *start;
   236 	GLubyte *where, *terminator;
   237 
   238 	/* Extension names should not have spaces. */
   239 	where = (GLubyte *) strchr(extension, ' ');
   240 	if (where || *extension == '\0')
   241 	      return 0;
   242 	
   243 	extensions = glGetString(GL_EXTENSIONS);
   244 	/* It takes a bit of care to be fool-proof about parsing the
   245 	 *      OpenGL extensions string. Don't be fooled by sub-strings,
   246 	 *           etc. */
   247 	
   248 	start = extensions;
   249 	
   250 	for (;;)
   251 	{
   252 		where = (GLubyte *) strstr((const char *) start, extension);
   253 		if (!where) break;
   254 		
   255 		terminator = where + strlen(extension);
   256 		if (where == start || *(where - 1) == ' ')
   257 	        if (*terminator == ' ' || *terminator == '\0') return 1;
   258 						  
   259 		start = terminator;
   260 	}
   261 	
   262 	return 0;
   263 }
   264 
   265 /* Make the current context active */
   266 int X11_GL_MakeCurrent(_THIS)
   267 {
   268 	int retval;
   269 	const char *glx_extensions;
   270 	
   271 	retval = 0;
   272 	if ( ! this->gl_data->glXMakeCurrent(GFX_Display,
   273 	                                     SDL_Window, glx_context) ) {
   274 		SDL_SetError("Unable to make GL context current");
   275 		retval = -1;
   276 	}
   277 	XSync( GFX_Display, False );
   278 
   279 	
   280 	/* 
   281 	 * The context is now current, check for glXReleaseBuffersMESA() 
   282 	 * extension. If extension is _not_ supported, destroy the pointer 
   283 	 * (to make sure it will not be called in X11_GL_Shutdown() ).
   284 	 * 
   285 	 * DRI/Mesa drivers include glXReleaseBuffersMESA() in the libGL.so, 
   286 	 * but there's no need to call it (is is only needed for some old 
   287 	 * non-DRI drivers).
   288 	 * 
   289 	 * When using for example glew (http://glew.sf.net), dlsym() for
   290 	 * glXReleaseBuffersMESA() returns the pointer from the glew library
   291 	 * (namespace conflict).
   292 	 *
   293 	 * The glXReleaseBuffersMESA() pointer in the glew is NULL, if the 
   294 	 * driver doesn't support this extension. So blindly calling it will
   295 	 * cause segfault with DRI/Mesa drivers!
   296 	 * 
   297 	 */
   298 	
   299 	glx_extensions = this->gl_data->glXQueryExtensionsString(GFX_Display, SDL_Screen);
   300 	if (!ExtensionSupported("glXReleaseBuffersMESA", glx_extensions)) this->gl_data->glXReleaseBuffersMESA = NULL;
   301 	
   302 	
   303 	/* More Voodoo X server workarounds... Grr... */
   304 	SDL_Lock_EventThread();
   305 	X11_CheckDGAMouse(this);
   306 	SDL_Unlock_EventThread();
   307 
   308 	return(retval);
   309 }
   310 
   311 /* Get attribute data from glX. */
   312 int X11_GL_GetAttribute(_THIS, SDL_GLattr attrib, int* value)
   313 {
   314 	int retval;
   315 	int glx_attrib = None;
   316 
   317 	switch( attrib ) {
   318 	    case SDL_GL_RED_SIZE:
   319 		glx_attrib = GLX_RED_SIZE;
   320 		break;
   321 	    case SDL_GL_GREEN_SIZE:
   322 		glx_attrib = GLX_GREEN_SIZE;
   323 		break;
   324 	    case SDL_GL_BLUE_SIZE:
   325 		glx_attrib = GLX_BLUE_SIZE;
   326 		break;
   327 	    case SDL_GL_ALPHA_SIZE:
   328 		glx_attrib = GLX_ALPHA_SIZE;
   329 		break;
   330 	    case SDL_GL_DOUBLEBUFFER:
   331 		glx_attrib = GLX_DOUBLEBUFFER;
   332 		break;
   333 	    case SDL_GL_BUFFER_SIZE:
   334 		glx_attrib = GLX_BUFFER_SIZE;
   335 		break;
   336 	    case SDL_GL_DEPTH_SIZE:
   337 		glx_attrib = GLX_DEPTH_SIZE;
   338 		break;
   339 	    case SDL_GL_STENCIL_SIZE:
   340 		glx_attrib = GLX_STENCIL_SIZE;
   341 		break;
   342 	    case SDL_GL_ACCUM_RED_SIZE:
   343 		glx_attrib = GLX_ACCUM_RED_SIZE;
   344 		break;
   345 	    case SDL_GL_ACCUM_GREEN_SIZE:
   346 		glx_attrib = GLX_ACCUM_GREEN_SIZE;
   347 		break;
   348 	    case SDL_GL_ACCUM_BLUE_SIZE:
   349 		glx_attrib = GLX_ACCUM_BLUE_SIZE;
   350 		break;
   351 	    case SDL_GL_ACCUM_ALPHA_SIZE:
   352 		glx_attrib = GLX_ACCUM_ALPHA_SIZE;
   353 		break;
   354 	    case SDL_GL_STEREO:
   355 		glx_attrib = GLX_STEREO;
   356 		break;
   357 	    default:
   358 		return(-1);
   359 	}
   360 
   361 	retval = this->gl_data->glXGetConfig(GFX_Display, glx_visualinfo, glx_attrib, value);
   362 
   363 	return retval;
   364 }
   365 
   366 void X11_GL_SwapBuffers(_THIS)
   367 {
   368 	this->gl_data->glXSwapBuffers(GFX_Display, SDL_Window);
   369 }
   370 
   371 #endif /* HAVE_OPENGL */
   372 
   373 void X11_GL_UnloadLibrary(_THIS)
   374 {
   375 #ifdef HAVE_OPENGL
   376 	if ( this->gl_config.driver_loaded ) {
   377 		dlclose(this->gl_config.dll_handle);
   378 
   379 		this->gl_data->glXGetProcAddress = NULL;
   380 		this->gl_data->glXChooseVisual = NULL;
   381 		this->gl_data->glXCreateContext = NULL;
   382 		this->gl_data->glXDestroyContext = NULL;
   383 		this->gl_data->glXMakeCurrent = NULL;
   384 		this->gl_data->glXSwapBuffers = NULL;
   385 
   386 		this->gl_config.dll_handle = NULL;
   387 		this->gl_config.driver_loaded = 0;
   388 	}
   389 #endif
   390 }
   391 
   392 #ifdef HAVE_OPENGL
   393 
   394 /* Passing a NULL path means load pointers from the application */
   395 int X11_GL_LoadLibrary(_THIS, const char* path) 
   396 {
   397 	void* handle;
   398 	int dlopen_flags;
   399 
   400  	if ( gl_active ) {
   401  		SDL_SetError("OpenGL context already created");
   402  		return -1;
   403  	}
   404 
   405 #ifdef RTLD_GLOBAL
   406 	dlopen_flags = RTLD_LAZY | RTLD_GLOBAL;
   407 #else
   408 	dlopen_flags = RTLD_LAZY;
   409 #endif
   410 	handle = dlopen(path, dlopen_flags);
   411 	/* Catch the case where the application isn't linked with GL */
   412 	if ( (dlsym(handle, "glXChooseVisual") == NULL) && (path == NULL) ) {
   413 		dlclose(handle);
   414 		path = getenv("SDL_VIDEO_GL_DRIVER");
   415 		if ( path == NULL ) {
   416 			path = DEFAULT_OPENGL;
   417 		}
   418 		handle = dlopen(path, dlopen_flags);
   419 	}
   420 	if ( handle == NULL ) {
   421 		SDL_SetError("Could not load OpenGL library");
   422 		return -1;
   423 	}
   424 
   425 	/* Unload the old driver and reset the pointers */
   426 	X11_GL_UnloadLibrary(this);
   427 
   428 	/* Load new function pointers */
   429 	this->gl_data->glXGetProcAddress =
   430 		(void *(*)(const GLubyte *)) dlsym(handle, "glXGetProcAddressARB");
   431 	this->gl_data->glXChooseVisual =
   432 		(XVisualInfo *(*)(Display *, int, int *)) dlsym(handle, "glXChooseVisual");
   433 	this->gl_data->glXCreateContext =
   434 		(GLXContext (*)(Display *, XVisualInfo *, GLXContext, int)) dlsym(handle, "glXCreateContext");
   435 	this->gl_data->glXDestroyContext =
   436 		(void (*)(Display *, GLXContext)) dlsym(handle, "glXDestroyContext");
   437 	this->gl_data->glXMakeCurrent =
   438 		(int (*)(Display *, GLXDrawable, GLXContext)) dlsym(handle, "glXMakeCurrent");
   439 	this->gl_data->glXSwapBuffers =
   440 		(void (*)(Display *, GLXDrawable)) dlsym(handle, "glXSwapBuffers");
   441 	this->gl_data->glXGetConfig =
   442 		(int (*)(Display *, XVisualInfo *, int, int *)) dlsym(handle, "glXGetConfig");
   443 	this->gl_data->glXQueryExtensionsString =
   444 		(const char (*)(Display *, int)) dlsym(handle, "glXQueryExtensionsString");
   445 	
   446 	/* We don't compare below for this in case we're not using Mesa. */
   447 	this->gl_data->glXReleaseBuffersMESA =
   448 		(void (*)(Display *, GLXDrawable)) dlsym( handle, "glXReleaseBuffersMESA" );
   449 	
   450 	
   451 	if ( (this->gl_data->glXChooseVisual == NULL) || 
   452 	     (this->gl_data->glXCreateContext == NULL) ||
   453 	     (this->gl_data->glXDestroyContext == NULL) ||
   454 	     (this->gl_data->glXMakeCurrent == NULL) ||
   455 	     (this->gl_data->glXSwapBuffers == NULL) ||
   456 	     (this->gl_data->glXGetConfig == NULL) ||
   457 	     (this->gl_data->glXQueryExtensionsString == NULL)) {
   458 		SDL_SetError("Could not retrieve OpenGL functions");
   459 		return -1;
   460 	}
   461 
   462 	this->gl_config.dll_handle = handle;
   463 	this->gl_config.driver_loaded = 1;
   464 	if ( path ) {
   465 		strncpy(this->gl_config.driver_path, path,
   466 			sizeof(this->gl_config.driver_path)-1);
   467 	} else {
   468 		strcpy(this->gl_config.driver_path, "");
   469 	}
   470 	return 0;
   471 }
   472 
   473 void *X11_GL_GetProcAddress(_THIS, const char* proc)
   474 {
   475 	static char procname[1024];
   476 	void* handle;
   477 	void* retval;
   478 	
   479 	handle = this->gl_config.dll_handle;
   480 	if ( this->gl_data->glXGetProcAddress ) {
   481 		return this->gl_data->glXGetProcAddress(proc);
   482 	}
   483 #if defined(__OpenBSD__) && !defined(__ELF__)
   484 #undef dlsym(x,y);
   485 #endif
   486 	retval = dlsym(handle, proc);
   487 	if (!retval && strlen(proc) <= 1022) {
   488 		procname[0] = '_';
   489 		strcpy(procname + 1, proc);
   490 		retval = dlsym(handle, procname);
   491 	}
   492 	return retval;
   493 }
   494 
   495 #endif /* HAVE_OPENGL */