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