1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 #ifdef USE_LOCAL_HEADERS
24 #	include "SDL.h"
25 #else
26 #	include <SDL.h>
27 #endif
28 
29 #if !SDL_VERSION_ATLEAST(1, 2, 10)
30 #define SDL_GL_ACCELERATED_VISUAL 15
31 #define SDL_GL_SWAP_CONTROL 16
32 #elif MINSDL_PATCH >= 10
33 #error Code block no longer necessary, please remove
34 #endif
35 
36 #ifdef SMP
37 #	ifdef USE_LOCAL_HEADERS
38 #		include "SDL_thread.h"
39 #	else
40 #		include <SDL_thread.h>
41 #	endif
42 #endif
43 
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <math.h>
48 
49 #include "../renderer/tr_local.h"
50 #include "../client/client.h"
51 #include "../sys/sys_local.h"
52 #include "sdl_icon.h"
53 
54 /* Just hack it for now. */
55 #ifdef MACOS_X
56 #include <OpenGL/OpenGL.h>
57 typedef CGLContextObj QGLContext;
58 #define GLimp_GetCurrentContext() CGLGetCurrentContext()
59 #define GLimp_SetCurrentContext(ctx) CGLSetCurrentContext(ctx)
60 #else
61 typedef void *QGLContext;
62 #define GLimp_GetCurrentContext() (NULL)
63 #define GLimp_SetCurrentContext(ctx)
64 #endif
65 
66 static QGLContext opengl_context;
67 
68 typedef enum
69 {
70 	RSERR_OK,
71 
72 	RSERR_INVALID_FULLSCREEN,
73 	RSERR_INVALID_MODE,
74 
75 	RSERR_UNKNOWN
76 } rserr_t;
77 
78 static SDL_Surface *screen = NULL;
79 
80 cvar_t *r_allowSoftwareGL; // Don't abort out if a hardware visual can't be obtained
81 
82 void (APIENTRYP qglActiveTextureARB) (GLenum texture);
83 void (APIENTRYP qglClientActiveTextureARB) (GLenum texture);
84 void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t);
85 
86 void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count);
87 void (APIENTRYP qglUnlockArraysEXT) (void);
88 
89 /*
90 ===============
91 GLimp_Shutdown
92 ===============
93 */
GLimp_Shutdown(void)94 void GLimp_Shutdown( void )
95 {
96 	IN_Shutdown();
97 
98 	SDL_QuitSubSystem( SDL_INIT_VIDEO );
99 	screen = NULL;
100 
101 	Com_Memset( &glConfig, 0, sizeof( glConfig ) );
102 	Com_Memset( &glState, 0, sizeof( glState ) );
103 }
104 
105 /*
106 ===============
107 GLimp_LogComment
108 ===============
109 */
GLimp_LogComment(char * comment)110 void GLimp_LogComment( char *comment )
111 {
112 }
113 
114 /*
115 ===============
116 GLimp_CompareModes
117 ===============
118 */
GLimp_CompareModes(const void * a,const void * b)119 static int GLimp_CompareModes( const void *a, const void *b )
120 {
121 	const float ASPECT_EPSILON = 0.001f;
122 	SDL_Rect *modeA = *(SDL_Rect **)a;
123 	SDL_Rect *modeB = *(SDL_Rect **)b;
124 	float aspectDiffA = fabs( ( (float)modeA->w / (float)modeA->h ) - displayAspect );
125 	float aspectDiffB = fabs( ( (float)modeB->w / (float)modeB->h ) - displayAspect );
126 	float aspectDiffsDiff = aspectDiffA - aspectDiffB;
127 
128 	if( aspectDiffsDiff > ASPECT_EPSILON )
129 		return 1;
130 	else if( aspectDiffsDiff < -ASPECT_EPSILON )
131 		return -1;
132 	else
133 	{
134 		if( modeA->w == modeB->w )
135 			return modeA->h - modeB->h;
136 		else
137 			return modeA->w - modeB->w;
138 	}
139 }
140 
141 /*
142 ===============
143 GLimp_DetectAvailableModes
144 ===============
145 */
GLimp_DetectAvailableModes(void)146 static void GLimp_DetectAvailableModes(void)
147 {
148 	char buf[ MAX_STRING_CHARS ] = { 0 };
149 	SDL_Rect **modes;
150 	int numModes;
151 	int i;
152 
153 	modes = SDL_ListModes( NULL, SDL_OPENGL | SDL_FULLSCREEN );
154 
155 	if( !modes )
156 	{
157 		ri.Printf( PRINT_WARNING, "Can't get list of available modes\n" );
158 		return;
159 	}
160 
161 	if( modes == (SDL_Rect **)-1 )
162 	{
163 		ri.Printf( PRINT_ALL, "Display supports any resolution\n" );
164 		return; // can set any resolution
165 	}
166 
167 	for( numModes = 0; modes[ numModes ]; numModes++ );
168 
169 	if(numModes > 1)
170 		qsort( modes+1, numModes-1, sizeof( SDL_Rect* ), GLimp_CompareModes );
171 
172 	for( i = 0; i < numModes; i++ )
173 	{
174 		const char *newModeString = va( "%ux%u ", modes[ i ]->w, modes[ i ]->h );
175 
176 		if( strlen( newModeString ) < (int)sizeof( buf ) - strlen( buf ) )
177 			Q_strcat( buf, sizeof( buf ), newModeString );
178 		else
179 			ri.Printf( PRINT_WARNING, "Skipping mode %ux%x, buffer too small\n", modes[i]->w, modes[i]->h );
180 	}
181 
182 	if( *buf )
183 	{
184 		buf[ strlen( buf ) - 1 ] = 0;
185 		ri.Printf( PRINT_ALL, "Available modes: '%s'\n", buf );
186 		ri.Cvar_Set( "r_availableModes", buf );
187 	}
188 }
189 
190 /*
191 ===============
192 GLimp_SetMode
193 ===============
194 */
GLimp_SetMode(int mode,qboolean fullscreen)195 static int GLimp_SetMode( int mode, qboolean fullscreen )
196 {
197 	const char*   glstring;
198 	int sdlcolorbits;
199 	int colorbits, depthbits, stencilbits;
200 	int tcolorbits, tdepthbits, tstencilbits;
201 	int i = 0;
202 	SDL_Surface *vidscreen = NULL;
203 	Uint32 flags = SDL_OPENGL;
204 	const SDL_VideoInfo *videoInfo;
205 
206 	ri.Printf( PRINT_ALL, "Initializing OpenGL display\n");
207 
208 	if( displayAspect == 0.0f )
209 	{
210 #if !SDL_VERSION_ATLEAST(1, 2, 10)
211 		// 1.2.10 is needed to get the desktop resolution
212 		displayAspect = 4.0f / 3.0f;
213 #elif MINSDL_PATCH >= 10
214 #	error Ifdeffery no longer necessary, please remove
215 #else
216 		// Guess the display aspect ratio through the desktop resolution
217 		// by assuming (relatively safely) that it is set at or close to
218 		// the display's native aspect ratio
219 		videoInfo = SDL_GetVideoInfo( );
220 		displayAspect = (float)videoInfo->current_w / (float)videoInfo->current_h;
221 #endif
222 
223 		ri.Printf( PRINT_ALL, "Estimated display aspect: %.3f\n", displayAspect );
224 	}
225 
226 	ri.Printf (PRINT_ALL, "...setting mode %d:", mode );
227 
228 	if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) )
229 	{
230 		ri.Printf( PRINT_ALL, " invalid mode\n" );
231 		return RSERR_INVALID_MODE;
232 	}
233 	ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight);
234 
235 	if (fullscreen)
236 	{
237 		flags |= SDL_FULLSCREEN;
238 		glConfig.isFullscreen = qtrue;
239 	}
240 	else
241 		glConfig.isFullscreen = qfalse;
242 
243 	if (!r_colorbits->value)
244 		colorbits = 24;
245 	else
246 		colorbits = r_colorbits->value;
247 
248 	if (!r_depthbits->value)
249 		depthbits = 24;
250 	else
251 		depthbits = r_depthbits->value;
252 	stencilbits = r_stencilbits->value;
253 
254 	for (i = 0; i < 16; i++)
255 	{
256 		// 0 - default
257 		// 1 - minus colorbits
258 		// 2 - minus depthbits
259 		// 3 - minus stencil
260 		if ((i % 4) == 0 && i)
261 		{
262 			// one pass, reduce
263 			switch (i / 4)
264 			{
265 				case 2 :
266 					if (colorbits == 24)
267 						colorbits = 16;
268 					break;
269 				case 1 :
270 					if (depthbits == 24)
271 						depthbits = 16;
272 					else if (depthbits == 16)
273 						depthbits = 8;
274 				case 3 :
275 					if (stencilbits == 24)
276 						stencilbits = 16;
277 					else if (stencilbits == 16)
278 						stencilbits = 8;
279 			}
280 		}
281 
282 		tcolorbits = colorbits;
283 		tdepthbits = depthbits;
284 		tstencilbits = stencilbits;
285 
286 		if ((i % 4) == 3)
287 		{ // reduce colorbits
288 			if (tcolorbits == 24)
289 				tcolorbits = 16;
290 		}
291 
292 		if ((i % 4) == 2)
293 		{ // reduce depthbits
294 			if (tdepthbits == 24)
295 				tdepthbits = 16;
296 			else if (tdepthbits == 16)
297 				tdepthbits = 8;
298 		}
299 
300 		if ((i % 4) == 1)
301 		{ // reduce stencilbits
302 			if (tstencilbits == 24)
303 				tstencilbits = 16;
304 			else if (tstencilbits == 16)
305 				tstencilbits = 8;
306 			else
307 				tstencilbits = 0;
308 		}
309 
310 		sdlcolorbits = 4;
311 		if (tcolorbits == 24)
312 			sdlcolorbits = 8;
313 
314 		SDL_GL_SetAttribute( SDL_GL_RED_SIZE, sdlcolorbits );
315 		SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, sdlcolorbits );
316 		SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, sdlcolorbits );
317 		SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, tdepthbits );
318 		SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, tstencilbits );
319 
320 		if(r_stereoEnabled->integer)
321 		{
322 			glConfig.stereoEnabled = qtrue;
323 			SDL_GL_SetAttribute(SDL_GL_STEREO, 1);
324 		}
325 		else
326 		{
327 			glConfig.stereoEnabled = qfalse;
328 			SDL_GL_SetAttribute(SDL_GL_STEREO, 0);
329 		}
330 
331 		SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
332 
333 #if 0 // See http://bugzilla.icculus.org/show_bug.cgi?id=3526
334 		// If not allowing software GL, demand accelerated
335 		if( !r_allowSoftwareGL->integer )
336 		{
337 			if( SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 ) < 0 )
338 			{
339 				ri.Printf( PRINT_ALL, "Unable to guarantee accelerated "
340 						"visual with libSDL < 1.2.10\n" );
341 			}
342 		}
343 #endif
344 
345 		if( SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, r_swapInterval->integer ) < 0 )
346 			ri.Printf( PRINT_ALL, "r_swapInterval requires libSDL >= 1.2.10\n" );
347 
348 #ifdef USE_ICON
349 		{
350 			SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(
351 					(void *)CLIENT_WINDOW_ICON.pixel_data,
352 					CLIENT_WINDOW_ICON.width,
353 					CLIENT_WINDOW_ICON.height,
354 					CLIENT_WINDOW_ICON.bytes_per_pixel * 8,
355 					CLIENT_WINDOW_ICON.bytes_per_pixel * CLIENT_WINDOW_ICON.width,
356 #ifdef Q3_LITTLE_ENDIAN
357 					0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000
358 #else
359 					0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
360 #endif
361 					);
362 
363 			SDL_WM_SetIcon( icon, NULL );
364 			SDL_FreeSurface( icon );
365 		}
366 #endif
367 
368 		SDL_WM_SetCaption(CLIENT_WINDOW_TITLE, CLIENT_WINDOW_MIN_TITLE);
369 		SDL_ShowCursor(0);
370 
371 		if (!(vidscreen = SDL_SetVideoMode(glConfig.vidWidth, glConfig.vidHeight, colorbits, flags)))
372 		{
373 			ri.Printf( PRINT_DEVELOPER, "SDL_SetVideoMode failed: %s\n", SDL_GetError( ) );
374 			continue;
375 		}
376 
377 		opengl_context = GLimp_GetCurrentContext();
378 
379 		ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n",
380 				sdlcolorbits, sdlcolorbits, sdlcolorbits, tdepthbits, tstencilbits);
381 
382 		glConfig.colorBits = tcolorbits;
383 		glConfig.depthBits = tdepthbits;
384 		glConfig.stencilBits = tstencilbits;
385 		break;
386 	}
387 
388 	GLimp_DetectAvailableModes();
389 
390 	if (!vidscreen)
391 	{
392 		ri.Printf( PRINT_ALL, "Couldn't get a visual\n" );
393 		return RSERR_INVALID_MODE;
394 	}
395 
396 	screen = vidscreen;
397 
398 	glstring = (char *) qglGetString (GL_RENDERER);
399 	ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glstring );
400 
401 	return RSERR_OK;
402 }
403 
404 /*
405 ===============
406 GLimp_StartDriverAndSetMode
407 ===============
408 */
GLimp_StartDriverAndSetMode(int mode,qboolean fullscreen)409 static qboolean GLimp_StartDriverAndSetMode( int mode, qboolean fullscreen )
410 {
411 	rserr_t err;
412 
413 	if (!SDL_WasInit(SDL_INIT_VIDEO))
414 	{
415 		ri.Printf( PRINT_ALL, "SDL_Init( SDL_INIT_VIDEO )... ");
416 		if (SDL_Init(SDL_INIT_VIDEO) == -1)
417 		{
418 			ri.Printf( PRINT_ALL, "FAILED (%s)\n", SDL_GetError());
419 			return qfalse;
420 		}
421 		ri.Printf( PRINT_ALL, "OK\n");
422 	}
423 
424 	if (fullscreen && Cvar_VariableIntegerValue( "in_nograb" ) )
425 	{
426 		ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
427 		ri.Cvar_Set( "r_fullscreen", "0" );
428 		r_fullscreen->modified = qfalse;
429 		fullscreen = qfalse;
430 	}
431 
432 	err = GLimp_SetMode( mode, fullscreen );
433 
434 	switch ( err )
435 	{
436 		case RSERR_INVALID_FULLSCREEN:
437 			ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
438 			return qfalse;
439 		case RSERR_INVALID_MODE:
440 			ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
441 			return qfalse;
442 		default:
443 			break;
444 	}
445 
446 	return qtrue;
447 }
448 /*
449 ===============
450 GLimp_InitExtensions
451 ===============
452 */
GLimp_InitExtensions(void)453 static void GLimp_InitExtensions( void )
454 {
455 	if ( !r_allowExtensions->integer )
456 	{
457 		ri.Printf( PRINT_ALL, "* IGNORING OPENGL EXTENSIONS *\n" );
458 		return;
459 	}
460 
461 	ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
462 
463 	// GL_S3_s3tc
464 	if ( Q_stristr( glConfig.extensions_string, "GL_S3_s3tc" ) )
465 	{
466 		if ( r_ext_compressed_textures->value )
467 		{
468 			glConfig.textureCompression = TC_S3TC;
469 			ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
470 		}
471 		else
472 		{
473 			glConfig.textureCompression = TC_NONE;
474 			ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
475 		}
476 	}
477 	else
478 	{
479 		glConfig.textureCompression = TC_NONE;
480 		ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
481 	}
482 
483 	// GL_EXT_texture_env_add
484 	glConfig.textureEnvAddAvailable = qfalse;
485 	if ( Q_stristr( glConfig.extensions_string, "EXT_texture_env_add" ) )
486 	{
487 		if ( r_ext_texture_env_add->integer )
488 		{
489 			glConfig.textureEnvAddAvailable = qtrue;
490 			ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
491 		}
492 		else
493 		{
494 			glConfig.textureEnvAddAvailable = qfalse;
495 			ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
496 		}
497 	}
498 	else
499 	{
500 		ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
501 	}
502 
503 	// GL_ARB_multitexture
504 	qglMultiTexCoord2fARB = NULL;
505 	qglActiveTextureARB = NULL;
506 	qglClientActiveTextureARB = NULL;
507 	if ( Q_stristr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
508 	{
509 		if ( r_ext_multitexture->value )
510 		{
511 			qglMultiTexCoord2fARB = SDL_GL_GetProcAddress( "glMultiTexCoord2fARB" );
512 			qglActiveTextureARB = SDL_GL_GetProcAddress( "glActiveTextureARB" );
513 			qglClientActiveTextureARB = SDL_GL_GetProcAddress( "glClientActiveTextureARB" );
514 
515 			if ( qglActiveTextureARB )
516 			{
517 				GLint glint = 0;
518 				qglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glint );
519 				glConfig.numTextureUnits = (int) glint;
520 				if ( glConfig.numTextureUnits > 1 )
521 				{
522 					ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
523 				}
524 				else
525 				{
526 					qglMultiTexCoord2fARB = NULL;
527 					qglActiveTextureARB = NULL;
528 					qglClientActiveTextureARB = NULL;
529 					ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
530 				}
531 			}
532 		}
533 		else
534 		{
535 			ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
536 		}
537 	}
538 	else
539 	{
540 		ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
541 	}
542 
543 	// GL_EXT_compiled_vertex_array
544 	if ( Q_stristr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) )
545 	{
546 		if ( r_ext_compiled_vertex_array->value )
547 		{
548 			ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
549 			qglLockArraysEXT = ( void ( APIENTRY * )( GLint, GLint ) ) SDL_GL_GetProcAddress( "glLockArraysEXT" );
550 			qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) SDL_GL_GetProcAddress( "glUnlockArraysEXT" );
551 			if (!qglLockArraysEXT || !qglUnlockArraysEXT)
552 			{
553 				ri.Error (ERR_FATAL, "bad getprocaddress");
554 			}
555 		}
556 		else
557 		{
558 			ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
559 		}
560 	}
561 	else
562 	{
563 		ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
564 	}
565 
566 	textureFilterAnisotropic = qfalse;
567 	if ( strstr( glConfig.extensions_string, "GL_EXT_texture_filter_anisotropic" ) )
568 	{
569 		if ( r_ext_texture_filter_anisotropic->integer ) {
570 			qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, (GLint *)&maxAnisotropy );
571 			if ( maxAnisotropy <= 0 ) {
572 				ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" );
573 				maxAnisotropy = 0;
574 			}
575 			else
576 			{
577 				ri.Printf( PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy );
578 				textureFilterAnisotropic = qtrue;
579 			}
580 		}
581 		else
582 		{
583 			ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_filter_anisotropic\n" );
584 		}
585 	}
586 	else
587 	{
588 		ri.Printf( PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n" );
589 	}
590 }
591 
592 #define R_MODE_FALLBACK 3 // 640 * 480
593 
594 /*
595 ===============
596 GLimp_Init
597 
598 This routine is responsible for initializing the OS specific portions
599 of OpenGL
600 ===============
601 */
GLimp_Init(void)602 void GLimp_Init( void )
603 {
604 	qboolean success = qtrue;
605 
606 	r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
607 
608 	// create the window and set up the context
609 	if( !GLimp_StartDriverAndSetMode( r_mode->integer, r_fullscreen->integer ) )
610 	{
611 		if( r_mode->integer != R_MODE_FALLBACK )
612 		{
613 			ri.Printf( PRINT_ALL, "Setting r_mode %d failed, falling back on r_mode %d\n",
614 					r_mode->integer, R_MODE_FALLBACK );
615 			if( !GLimp_StartDriverAndSetMode( R_MODE_FALLBACK, r_fullscreen->integer ) )
616 				success = qfalse;
617 		}
618 		else
619 			success = qfalse;
620 	}
621 
622 	if( !success )
623 		ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" );
624 
625 	// This values force the UI to disable driver selection
626 	glConfig.driverType = GLDRV_ICD;
627 	glConfig.hardwareType = GLHW_GENERIC;
628 	glConfig.deviceSupportsGamma = !!( SDL_SetGamma( 1.0f, 1.0f, 1.0f ) >= 0 );
629 
630 	// get our config strings
631 	Q_strncpyz( glConfig.vendor_string, (char *) qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
632 	Q_strncpyz( glConfig.renderer_string, (char *) qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
633 	if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n')
634 		glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0;
635 	Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
636 	Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
637 
638 	// initialize extensions
639 	GLimp_InitExtensions( );
640 
641 	ri.Cvar_Get( "r_availableModes", "", CVAR_ROM );
642 
643 	// This depends on SDL_INIT_VIDEO, hence having it here
644 	IN_Init( );
645 
646 	return;
647 }
648 
649 
650 /*
651 ===============
652 GLimp_EndFrame
653 
654 Responsible for doing a swapbuffers
655 ===============
656 */
GLimp_EndFrame(void)657 void GLimp_EndFrame( void )
658 {
659 	// don't flip if drawing to front buffer
660 	if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
661 	{
662 		SDL_GL_SwapBuffers();
663 	}
664 
665 	if( r_fullscreen->modified )
666 	{
667 		qboolean    fullscreen;
668 		qboolean    sdlToggled = qfalse;
669 		SDL_Surface *s = SDL_GetVideoSurface( );
670 
671 		if( s )
672 		{
673 			// Find out the current state
674 			if( s->flags & SDL_FULLSCREEN )
675 				fullscreen = qtrue;
676 			else
677 				fullscreen = qfalse;
678 
679 			if (r_fullscreen->integer && Cvar_VariableIntegerValue( "in_nograb" ))
680 			{
681 				ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
682 				ri.Cvar_Set( "r_fullscreen", "0" );
683 				r_fullscreen->modified = qfalse;
684 			}
685 
686 			// Is the state we want different from the current state?
687 			if( !!r_fullscreen->integer != fullscreen )
688 				sdlToggled = SDL_WM_ToggleFullScreen( s );
689 			else
690 				sdlToggled = qtrue;
691 		}
692 
693 		// SDL_WM_ToggleFullScreen didn't work, so do it the slow way
694 		if( !sdlToggled )
695 			Cbuf_AddText( "vid_restart" );
696 
697 		r_fullscreen->modified = qfalse;
698 	}
699 }
700 
701 
702 
703 #ifdef SMP
704 /*
705 ===========================================================
706 
707 SMP acceleration
708 
709 ===========================================================
710 */
711 
712 /*
713  * I have no idea if this will even work...most platforms don't offer
714  * thread-safe OpenGL libraries, and it looks like the original Linux
715  * code counted on each thread claiming the GL context with glXMakeCurrent(),
716  * which you can't currently do in SDL. We'll just have to hope for the best.
717  */
718 
719 static SDL_mutex *smpMutex = NULL;
720 static SDL_cond *renderCommandsEvent = NULL;
721 static SDL_cond *renderCompletedEvent = NULL;
722 static void (*glimpRenderThread)( void ) = NULL;
723 static SDL_Thread *renderThread = NULL;
724 
725 /*
726 ===============
727 GLimp_ShutdownRenderThread
728 ===============
729 */
GLimp_ShutdownRenderThread(void)730 static void GLimp_ShutdownRenderThread(void)
731 {
732 	if (smpMutex != NULL)
733 	{
734 		SDL_DestroyMutex(smpMutex);
735 		smpMutex = NULL;
736 	}
737 
738 	if (renderCommandsEvent != NULL)
739 	{
740 		SDL_DestroyCond(renderCommandsEvent);
741 		renderCommandsEvent = NULL;
742 	}
743 
744 	if (renderCompletedEvent != NULL)
745 	{
746 		SDL_DestroyCond(renderCompletedEvent);
747 		renderCompletedEvent = NULL;
748 	}
749 
750 	glimpRenderThread = NULL;
751 }
752 
753 /*
754 ===============
755 GLimp_RenderThreadWrapper
756 ===============
757 */
GLimp_RenderThreadWrapper(void * arg)758 static int GLimp_RenderThreadWrapper( void *arg )
759 {
760 	Com_Printf( "Render thread starting\n" );
761 
762 	glimpRenderThread();
763 
764 	GLimp_SetCurrentContext(NULL);
765 
766 	Com_Printf( "Render thread terminating\n" );
767 
768 	return 0;
769 }
770 
771 /*
772 ===============
773 GLimp_SpawnRenderThread
774 ===============
775 */
GLimp_SpawnRenderThread(void (* function)(void))776 qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
777 {
778 	static qboolean warned = qfalse;
779 	if (!warned)
780 	{
781 		Com_Printf("WARNING: You enable r_smp at your own risk!\n");
782 		warned = qtrue;
783 	}
784 
785 #ifndef MACOS_X
786 	return qfalse;  /* better safe than sorry for now. */
787 #endif
788 
789 	if (renderThread != NULL)  /* hopefully just a zombie at this point... */
790 	{
791 		Com_Printf("Already a render thread? Trying to clean it up...\n");
792 		SDL_WaitThread(renderThread, NULL);
793 		renderThread = NULL;
794 		GLimp_ShutdownRenderThread();
795 	}
796 
797 	smpMutex = SDL_CreateMutex();
798 	if (smpMutex == NULL)
799 	{
800 		Com_Printf( "smpMutex creation failed: %s\n", SDL_GetError() );
801 		GLimp_ShutdownRenderThread();
802 		return qfalse;
803 	}
804 
805 	renderCommandsEvent = SDL_CreateCond();
806 	if (renderCommandsEvent == NULL)
807 	{
808 		Com_Printf( "renderCommandsEvent creation failed: %s\n", SDL_GetError() );
809 		GLimp_ShutdownRenderThread();
810 		return qfalse;
811 	}
812 
813 	renderCompletedEvent = SDL_CreateCond();
814 	if (renderCompletedEvent == NULL)
815 	{
816 		Com_Printf( "renderCompletedEvent creation failed: %s\n", SDL_GetError() );
817 		GLimp_ShutdownRenderThread();
818 		return qfalse;
819 	}
820 
821 	glimpRenderThread = function;
822 	renderThread = SDL_CreateThread(GLimp_RenderThreadWrapper, NULL);
823 	if ( renderThread == NULL )
824 	{
825 		ri.Printf( PRINT_ALL, "SDL_CreateThread() returned %s", SDL_GetError() );
826 		GLimp_ShutdownRenderThread();
827 		return qfalse;
828 	}
829 	else
830 	{
831 		// tma 01/09/07: don't think this is necessary anyway?
832 		//
833 		// !!! FIXME: No detach API available in SDL!
834 		//ret = pthread_detach( renderThread );
835 		//if ( ret ) {
836 		//ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) );
837 		//}
838 	}
839 
840 	return qtrue;
841 }
842 
843 static volatile void    *smpData = NULL;
844 static volatile qboolean smpDataReady;
845 
846 /*
847 ===============
848 GLimp_RendererSleep
849 ===============
850 */
GLimp_RendererSleep(void)851 void *GLimp_RendererSleep( void )
852 {
853 	void  *data = NULL;
854 
855 	GLimp_SetCurrentContext(NULL);
856 
857 	SDL_LockMutex(smpMutex);
858 	{
859 		smpData = NULL;
860 		smpDataReady = qfalse;
861 
862 		// after this, the front end can exit GLimp_FrontEndSleep
863 		SDL_CondSignal(renderCompletedEvent);
864 
865 		while ( !smpDataReady )
866 			SDL_CondWait(renderCommandsEvent, smpMutex);
867 
868 		data = (void *)smpData;
869 	}
870 	SDL_UnlockMutex(smpMutex);
871 
872 	GLimp_SetCurrentContext(opengl_context);
873 
874 	return data;
875 }
876 
877 /*
878 ===============
879 GLimp_FrontEndSleep
880 ===============
881 */
GLimp_FrontEndSleep(void)882 void GLimp_FrontEndSleep( void )
883 {
884 	SDL_LockMutex(smpMutex);
885 	{
886 		while ( smpData )
887 			SDL_CondWait(renderCompletedEvent, smpMutex);
888 	}
889 	SDL_UnlockMutex(smpMutex);
890 
891 	GLimp_SetCurrentContext(opengl_context);
892 }
893 
894 /*
895 ===============
896 GLimp_WakeRenderer
897 ===============
898 */
GLimp_WakeRenderer(void * data)899 void GLimp_WakeRenderer( void *data )
900 {
901 	GLimp_SetCurrentContext(NULL);
902 
903 	SDL_LockMutex(smpMutex);
904 	{
905 		assert( smpData == NULL );
906 		smpData = data;
907 		smpDataReady = qtrue;
908 
909 		// after this, the renderer can continue through GLimp_RendererSleep
910 		SDL_CondSignal(renderCommandsEvent);
911 	}
912 	SDL_UnlockMutex(smpMutex);
913 }
914 
915 #else
916 
917 // No SMP - stubs
GLimp_RenderThreadWrapper(void * arg)918 void GLimp_RenderThreadWrapper( void *arg )
919 {
920 }
921 
GLimp_SpawnRenderThread(void (* function)(void))922 qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
923 {
924 	ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n");
925 	return qfalse;
926 }
927 
GLimp_RendererSleep(void)928 void *GLimp_RendererSleep( void )
929 {
930 	return NULL;
931 }
932 
GLimp_FrontEndSleep(void)933 void GLimp_FrontEndSleep( void )
934 {
935 }
936 
GLimp_WakeRenderer(void * data)937 void GLimp_WakeRenderer( void *data )
938 {
939 }
940 
941 #endif
942