src/video/x11/SDL_x11gl.c
author Ryan C. Gordon
Tue, 01 Jul 2003 16:35:54 +0000
changeset 643 564716cfb502
parent 638 b0108e9dea53
child 646 0f2029a70548
permissions -rw-r--r--
Removed direct dependency on OpenGL (call current_video->glGetString() instead
of glGetString() directly)...otherwise we'd have to explicitly link to a
libGL. --ryan.
     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 = current_video->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 */