Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

win_glimp.c (39965B)


      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 Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 /*
     23 ** WIN_GLIMP.C
     24 **
     25 ** This file contains ALL Win32 specific stuff having to do with the
     26 ** OpenGL refresh.  When a port is being made the following functions
     27 ** must be implemented by the port:
     28 **
     29 ** GLimp_EndFrame
     30 ** GLimp_Init
     31 ** GLimp_LogComment
     32 ** GLimp_Shutdown
     33 **
     34 ** Note that the GLW_xxx functions are Windows specific GL-subsystem
     35 ** related functions that are relevant ONLY to win_glimp.c
     36 */
     37 #include <assert.h>
     38 #include "../renderer/tr_local.h"
     39 #include "../qcommon/qcommon.h"
     40 #include "resource.h"
     41 #include "glw_win.h"
     42 #include "win_local.h"
     43 
     44 extern void WG_CheckHardwareGamma( void );
     45 extern void WG_RestoreGamma( void );
     46 
     47 typedef enum {
     48 	RSERR_OK,
     49 
     50 	RSERR_INVALID_FULLSCREEN,
     51 	RSERR_INVALID_MODE,
     52 
     53 	RSERR_UNKNOWN
     54 } rserr_t;
     55 
     56 #define TRY_PFD_SUCCESS		0
     57 #define TRY_PFD_FAIL_SOFT	1
     58 #define TRY_PFD_FAIL_HARD	2
     59 
     60 #define	WINDOW_CLASS_NAME	"Quake 3: Arena"
     61 
     62 static void		GLW_InitExtensions( void );
     63 static rserr_t	GLW_SetMode( const char *drivername, 
     64 							 int mode, 
     65 							 int colorbits, 
     66 							 qboolean cdsFullscreen );
     67 
     68 static qboolean s_classRegistered = qfalse;
     69 
     70 //
     71 // function declaration
     72 //
     73 void	 QGL_EnableLogging( qboolean enable );
     74 qboolean QGL_Init( const char *dllname );
     75 void     QGL_Shutdown( void );
     76 
     77 //
     78 // variable declarations
     79 //
     80 glwstate_t glw_state;
     81 
     82 cvar_t	*r_allowSoftwareGL;		// don't abort out if the pixelformat claims software
     83 cvar_t	*r_maskMinidriver;		// allow a different dll name to be treated as if it were opengl32.dll
     84 
     85 
     86 
     87 /*
     88 ** GLW_StartDriverAndSetMode
     89 */
     90 static qboolean GLW_StartDriverAndSetMode( const char *drivername, 
     91 										   int mode, 
     92 										   int colorbits,
     93 										   qboolean cdsFullscreen )
     94 {
     95 	rserr_t err;
     96 
     97 	err = GLW_SetMode( drivername, r_mode->integer, colorbits, cdsFullscreen );
     98 
     99 	switch ( err )
    100 	{
    101 	case RSERR_INVALID_FULLSCREEN:
    102 		ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
    103 		return qfalse;
    104 	case RSERR_INVALID_MODE:
    105 		ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
    106 		return qfalse;
    107 	default:
    108 		break;
    109 	}
    110 	return qtrue;
    111 }
    112 
    113 /*
    114 ** ChoosePFD
    115 **
    116 ** Helper function that replaces ChoosePixelFormat.
    117 */
    118 #define MAX_PFDS 256
    119 
    120 static int GLW_ChoosePFD( HDC hDC, PIXELFORMATDESCRIPTOR *pPFD )
    121 {
    122 	PIXELFORMATDESCRIPTOR pfds[MAX_PFDS+1];
    123 	int maxPFD = 0;
    124 	int i;
    125 	int bestMatch = 0;
    126 
    127 	ri.Printf( PRINT_ALL, "...GLW_ChoosePFD( %d, %d, %d )\n", ( int ) pPFD->cColorBits, ( int ) pPFD->cDepthBits, ( int ) pPFD->cStencilBits );
    128 
    129 	// count number of PFDs
    130 	if ( glConfig.driverType > GLDRV_ICD )
    131 	{
    132 		maxPFD = qwglDescribePixelFormat( hDC, 1, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[0] );
    133 	}
    134 	else
    135 	{
    136 		maxPFD = DescribePixelFormat( hDC, 1, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[0] );
    137 	}
    138 	if ( maxPFD > MAX_PFDS )
    139 	{
    140 		ri.Printf( PRINT_WARNING, "...numPFDs > MAX_PFDS (%d > %d)\n", maxPFD, MAX_PFDS );
    141 		maxPFD = MAX_PFDS;
    142 	}
    143 
    144 	ri.Printf( PRINT_ALL, "...%d PFDs found\n", maxPFD - 1 );
    145 
    146 	// grab information
    147 	for ( i = 1; i <= maxPFD; i++ )
    148 	{
    149 		if ( glConfig.driverType > GLDRV_ICD )
    150 		{
    151 			qwglDescribePixelFormat( hDC, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[i] );
    152 		}
    153 		else
    154 		{
    155 			DescribePixelFormat( hDC, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfds[i] );
    156 		}
    157 	}
    158 
    159 	// look for a best match
    160 	for ( i = 1; i <= maxPFD; i++ )
    161 	{
    162 		//
    163 		// make sure this has hardware acceleration
    164 		//
    165 		if ( ( pfds[i].dwFlags & PFD_GENERIC_FORMAT ) != 0 ) 
    166 		{
    167 			if ( !r_allowSoftwareGL->integer )
    168 			{
    169 				if ( r_verbose->integer )
    170 				{
    171 					ri.Printf( PRINT_ALL, "...PFD %d rejected, software acceleration\n", i );
    172 				}
    173 				continue;
    174 			}
    175 		}
    176 
    177 		// verify pixel type
    178 		if ( pfds[i].iPixelType != PFD_TYPE_RGBA )
    179 		{
    180 			if ( r_verbose->integer )
    181 			{
    182 				ri.Printf( PRINT_ALL, "...PFD %d rejected, not RGBA\n", i );
    183 			}
    184 			continue;
    185 		}
    186 
    187 		// verify proper flags
    188 		if ( ( ( pfds[i].dwFlags & pPFD->dwFlags ) & pPFD->dwFlags ) != pPFD->dwFlags ) 
    189 		{
    190 			if ( r_verbose->integer )
    191 			{
    192 				ri.Printf( PRINT_ALL, "...PFD %d rejected, improper flags (%x instead of %x)\n", i, pfds[i].dwFlags, pPFD->dwFlags );
    193 			}
    194 			continue;
    195 		}
    196 
    197 		// verify enough bits
    198 		if ( pfds[i].cDepthBits < 15 )
    199 		{
    200 			continue;
    201 		}
    202 		if ( ( pfds[i].cStencilBits < 4 ) && ( pPFD->cStencilBits > 0 ) )
    203 		{
    204 			continue;
    205 		}
    206 
    207 		//
    208 		// selection criteria (in order of priority):
    209 		// 
    210 		//  PFD_STEREO
    211 		//  colorBits
    212 		//  depthBits
    213 		//  stencilBits
    214 		//
    215 		if ( bestMatch )
    216 		{
    217 			// check stereo
    218 			if ( ( pfds[i].dwFlags & PFD_STEREO ) && ( !( pfds[bestMatch].dwFlags & PFD_STEREO ) ) && ( pPFD->dwFlags & PFD_STEREO ) )
    219 			{
    220 				bestMatch = i;
    221 				continue;
    222 			}
    223 			
    224 			if ( !( pfds[i].dwFlags & PFD_STEREO ) && ( pfds[bestMatch].dwFlags & PFD_STEREO ) && ( pPFD->dwFlags & PFD_STEREO ) )
    225 			{
    226 				bestMatch = i;
    227 				continue;
    228 			}
    229 
    230 			// check color
    231 			if ( pfds[bestMatch].cColorBits != pPFD->cColorBits )
    232 			{
    233 				// prefer perfect match
    234 				if ( pfds[i].cColorBits == pPFD->cColorBits )
    235 				{
    236 					bestMatch = i;
    237 					continue;
    238 				}
    239 				// otherwise if this PFD has more bits than our best, use it
    240 				else if ( pfds[i].cColorBits > pfds[bestMatch].cColorBits )
    241 				{
    242 					bestMatch = i;
    243 					continue;
    244 				}
    245 			}
    246 
    247 			// check depth
    248 			if ( pfds[bestMatch].cDepthBits != pPFD->cDepthBits )
    249 			{
    250 				// prefer perfect match
    251 				if ( pfds[i].cDepthBits == pPFD->cDepthBits )
    252 				{
    253 					bestMatch = i;
    254 					continue;
    255 				}
    256 				// otherwise if this PFD has more bits than our best, use it
    257 				else if ( pfds[i].cDepthBits > pfds[bestMatch].cDepthBits )
    258 				{
    259 					bestMatch = i;
    260 					continue;
    261 				}
    262 			}
    263 
    264 			// check stencil
    265 			if ( pfds[bestMatch].cStencilBits != pPFD->cStencilBits )
    266 			{
    267 				// prefer perfect match
    268 				if ( pfds[i].cStencilBits == pPFD->cStencilBits )
    269 				{
    270 					bestMatch = i;
    271 					continue;
    272 				}
    273 				// otherwise if this PFD has more bits than our best, use it
    274 				else if ( ( pfds[i].cStencilBits > pfds[bestMatch].cStencilBits ) && 
    275 					 ( pPFD->cStencilBits > 0 ) )
    276 				{
    277 					bestMatch = i;
    278 					continue;
    279 				}
    280 			}
    281 		}
    282 		else
    283 		{
    284 			bestMatch = i;
    285 		}
    286 	}
    287 	
    288 	if ( !bestMatch )
    289 		return 0;
    290 
    291 	if ( ( pfds[bestMatch].dwFlags & PFD_GENERIC_FORMAT ) != 0 )
    292 	{
    293 		if ( !r_allowSoftwareGL->integer )
    294 		{
    295 			ri.Printf( PRINT_ALL, "...no hardware acceleration found\n" );
    296 			return 0;
    297 		}
    298 		else
    299 		{
    300 			ri.Printf( PRINT_ALL, "...using software emulation\n" );
    301 		}
    302 	}
    303 	else if ( pfds[bestMatch].dwFlags & PFD_GENERIC_ACCELERATED )
    304 	{
    305 		ri.Printf( PRINT_ALL, "...MCD acceleration found\n" );
    306 	}
    307 	else
    308 	{
    309 		ri.Printf( PRINT_ALL, "...hardware acceleration found\n" );
    310 	}
    311 
    312 	*pPFD = pfds[bestMatch];
    313 
    314 	return bestMatch;
    315 }
    316 
    317 /*
    318 ** void GLW_CreatePFD
    319 **
    320 ** Helper function zeros out then fills in a PFD
    321 */
    322 static void GLW_CreatePFD( PIXELFORMATDESCRIPTOR *pPFD, int colorbits, int depthbits, int stencilbits, qboolean stereo )
    323 {
    324     PIXELFORMATDESCRIPTOR src = 
    325 	{
    326 		sizeof(PIXELFORMATDESCRIPTOR),	// size of this pfd
    327 		1,								// version number
    328 		PFD_DRAW_TO_WINDOW |			// support window
    329 		PFD_SUPPORT_OPENGL |			// support OpenGL
    330 		PFD_DOUBLEBUFFER,				// double buffered
    331 		PFD_TYPE_RGBA,					// RGBA type
    332 		24,								// 24-bit color depth
    333 		0, 0, 0, 0, 0, 0,				// color bits ignored
    334 		0,								// no alpha buffer
    335 		0,								// shift bit ignored
    336 		0,								// no accumulation buffer
    337 		0, 0, 0, 0, 					// accum bits ignored
    338 		24,								// 24-bit z-buffer	
    339 		8,								// 8-bit stencil buffer
    340 		0,								// no auxiliary buffer
    341 		PFD_MAIN_PLANE,					// main layer
    342 		0,								// reserved
    343 		0, 0, 0							// layer masks ignored
    344     };
    345 
    346 	src.cColorBits = colorbits;
    347 	src.cDepthBits = depthbits;
    348 	src.cStencilBits = stencilbits;
    349 
    350 	if ( stereo )
    351 	{
    352 		ri.Printf( PRINT_ALL, "...attempting to use stereo\n" );
    353 		src.dwFlags |= PFD_STEREO;
    354 		glConfig.stereoEnabled = qtrue;
    355 	}
    356 	else
    357 	{
    358 		glConfig.stereoEnabled = qfalse;
    359 	}
    360 
    361 	*pPFD = src;
    362 }
    363 
    364 /*
    365 ** GLW_MakeContext
    366 */
    367 static int GLW_MakeContext( PIXELFORMATDESCRIPTOR *pPFD )
    368 {
    369 	int pixelformat;
    370 
    371 	//
    372 	// don't putz around with pixelformat if it's already set (e.g. this is a soft
    373 	// reset of the graphics system)
    374 	//
    375 	if ( !glw_state.pixelFormatSet )
    376 	{
    377 		//
    378 		// choose, set, and describe our desired pixel format.  If we're
    379 		// using a minidriver then we need to bypass the GDI functions,
    380 		// otherwise use the GDI functions.
    381 		//
    382 		if ( ( pixelformat = GLW_ChoosePFD( glw_state.hDC, pPFD ) ) == 0 )
    383 		{
    384 			ri.Printf( PRINT_ALL, "...GLW_ChoosePFD failed\n");
    385 			return TRY_PFD_FAIL_SOFT;
    386 		}
    387 		ri.Printf( PRINT_ALL, "...PIXELFORMAT %d selected\n", pixelformat );
    388 
    389 		if ( glConfig.driverType > GLDRV_ICD )
    390 		{
    391 			qwglDescribePixelFormat( glw_state.hDC, pixelformat, sizeof( *pPFD ), pPFD );
    392 			if ( qwglSetPixelFormat( glw_state.hDC, pixelformat, pPFD ) == FALSE )
    393 			{
    394 				ri.Printf ( PRINT_ALL, "...qwglSetPixelFormat failed\n");
    395 				return TRY_PFD_FAIL_SOFT;
    396 			}
    397 		}
    398 		else
    399 		{
    400 			DescribePixelFormat( glw_state.hDC, pixelformat, sizeof( *pPFD ), pPFD );
    401 
    402 			if ( SetPixelFormat( glw_state.hDC, pixelformat, pPFD ) == FALSE )
    403 			{
    404 				ri.Printf (PRINT_ALL, "...SetPixelFormat failed\n", glw_state.hDC );
    405 				return TRY_PFD_FAIL_SOFT;
    406 			}
    407 		}
    408 
    409 		glw_state.pixelFormatSet = qtrue;
    410 	}
    411 
    412 	//
    413 	// startup the OpenGL subsystem by creating a context and making it current
    414 	//
    415 	if ( !glw_state.hGLRC )
    416 	{
    417 		ri.Printf( PRINT_ALL, "...creating GL context: " );
    418 		if ( ( glw_state.hGLRC = qwglCreateContext( glw_state.hDC ) ) == 0 )
    419 		{
    420 			ri.Printf (PRINT_ALL, "failed\n");
    421 
    422 			return TRY_PFD_FAIL_HARD;
    423 		}
    424 		ri.Printf( PRINT_ALL, "succeeded\n" );
    425 
    426 		ri.Printf( PRINT_ALL, "...making context current: " );
    427 		if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) )
    428 		{
    429 			qwglDeleteContext( glw_state.hGLRC );
    430 			glw_state.hGLRC = NULL;
    431 			ri.Printf (PRINT_ALL, "failed\n");
    432 			return TRY_PFD_FAIL_HARD;
    433 		}
    434 		ri.Printf( PRINT_ALL, "succeeded\n" );
    435 	}
    436 
    437 	return TRY_PFD_SUCCESS;
    438 }
    439 
    440 
    441 /*
    442 ** GLW_InitDriver
    443 **
    444 ** - get a DC if one doesn't exist
    445 ** - create an HGLRC if one doesn't exist
    446 */
    447 static qboolean GLW_InitDriver( const char *drivername, int colorbits )
    448 {
    449 	int		tpfd;
    450 	int		depthbits, stencilbits;
    451 	static PIXELFORMATDESCRIPTOR pfd;		// save between frames since 'tr' gets cleared
    452 
    453 	ri.Printf( PRINT_ALL, "Initializing OpenGL driver\n" );
    454 
    455 	//
    456 	// get a DC for our window if we don't already have one allocated
    457 	//
    458 	if ( glw_state.hDC == NULL )
    459 	{
    460 		ri.Printf( PRINT_ALL, "...getting DC: " );
    461 
    462 		if ( ( glw_state.hDC = GetDC( g_wv.hWnd ) ) == NULL )
    463 		{
    464 			ri.Printf( PRINT_ALL, "failed\n" );
    465 			return qfalse;
    466 		}
    467 		ri.Printf( PRINT_ALL, "succeeded\n" );
    468 	}
    469 
    470 	if ( colorbits == 0 )
    471 	{
    472 		colorbits = glw_state.desktopBitsPixel;
    473 	}
    474 
    475 	//
    476 	// implicitly assume Z-buffer depth == desktop color depth
    477 	//
    478 	if ( r_depthbits->integer == 0 ) {
    479 		if ( colorbits > 16 ) {
    480 			depthbits = 24;
    481 		} else {
    482 			depthbits = 16;
    483 		}
    484 	} else {
    485 		depthbits = r_depthbits->integer;
    486 	}
    487 
    488 	//
    489 	// do not allow stencil if Z-buffer depth likely won't contain it
    490 	//
    491 	stencilbits = r_stencilbits->integer;
    492 	if ( depthbits < 24 )
    493 	{
    494 		stencilbits = 0;
    495 	}
    496 
    497 	//
    498 	// make two attempts to set the PIXELFORMAT
    499 	//
    500 
    501 	//
    502 	// first attempt: r_colorbits, depthbits, and r_stencilbits
    503 	//
    504 	if ( !glw_state.pixelFormatSet )
    505 	{
    506 		GLW_CreatePFD( &pfd, colorbits, depthbits, stencilbits, r_stereo->integer );
    507 		if ( ( tpfd = GLW_MakeContext( &pfd ) ) != TRY_PFD_SUCCESS )
    508 		{
    509 			if ( tpfd == TRY_PFD_FAIL_HARD )
    510 			{
    511 				ri.Printf( PRINT_WARNING, "...failed hard\n" );
    512 				return qfalse;
    513 			}
    514 
    515 			//
    516 			// punt if we've already tried the desktop bit depth and no stencil bits
    517 			//
    518 			if ( ( r_colorbits->integer == glw_state.desktopBitsPixel ) &&
    519 				 ( stencilbits == 0 ) )
    520 			{
    521 				ReleaseDC( g_wv.hWnd, glw_state.hDC );
    522 				glw_state.hDC = NULL;
    523 
    524 				ri.Printf( PRINT_ALL, "...failed to find an appropriate PIXELFORMAT\n" );
    525 
    526 				return qfalse;
    527 			}
    528 
    529 			//
    530 			// second attempt: desktop's color bits and no stencil
    531 			//
    532 			if ( colorbits > glw_state.desktopBitsPixel )
    533 			{
    534 				colorbits = glw_state.desktopBitsPixel;
    535 			}
    536 			GLW_CreatePFD( &pfd, colorbits, depthbits, 0, r_stereo->integer );
    537 			if ( GLW_MakeContext( &pfd ) != TRY_PFD_SUCCESS )
    538 			{
    539 				if ( glw_state.hDC )
    540 				{
    541 					ReleaseDC( g_wv.hWnd, glw_state.hDC );
    542 					glw_state.hDC = NULL;
    543 				}
    544 
    545 				ri.Printf( PRINT_ALL, "...failed to find an appropriate PIXELFORMAT\n" );
    546 
    547 				return qfalse;
    548 			}
    549 		}
    550 
    551 		/*
    552 		** report if stereo is desired but unavailable
    553 		*/
    554 		if ( !( pfd.dwFlags & PFD_STEREO ) && ( r_stereo->integer != 0 ) ) 
    555 		{
    556 			ri.Printf( PRINT_ALL, "...failed to select stereo pixel format\n" );
    557 			glConfig.stereoEnabled = qfalse;
    558 		}
    559 	}
    560 
    561 	/*
    562 	** store PFD specifics 
    563 	*/
    564 	glConfig.colorBits = ( int ) pfd.cColorBits;
    565 	glConfig.depthBits = ( int ) pfd.cDepthBits;
    566 	glConfig.stencilBits = ( int ) pfd.cStencilBits;
    567 
    568 	return qtrue;
    569 }
    570 
    571 /*
    572 ** GLW_CreateWindow
    573 **
    574 ** Responsible for creating the Win32 window and initializing the OpenGL driver.
    575 */
    576 #define	WINDOW_STYLE	(WS_OVERLAPPED|WS_BORDER|WS_CAPTION|WS_VISIBLE)
    577 static qboolean GLW_CreateWindow( const char *drivername, int width, int height, int colorbits, qboolean cdsFullscreen )
    578 {
    579 	RECT			r;
    580 	cvar_t			*vid_xpos, *vid_ypos;
    581 	int				stylebits;
    582 	int				x, y, w, h;
    583 	int				exstyle;
    584 
    585 	//
    586 	// register the window class if necessary
    587 	//
    588 	if ( !s_classRegistered )
    589 	{
    590 		WNDCLASS wc;
    591 
    592 		memset( &wc, 0, sizeof( wc ) );
    593 
    594 		wc.style         = 0;
    595 		wc.lpfnWndProc   = (WNDPROC) glw_state.wndproc;
    596 		wc.cbClsExtra    = 0;
    597 		wc.cbWndExtra    = 0;
    598 		wc.hInstance     = g_wv.hInstance;
    599 		wc.hIcon         = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1));
    600 		wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
    601 		wc.hbrBackground = (void *)COLOR_GRAYTEXT;
    602 		wc.lpszMenuName  = 0;
    603 		wc.lpszClassName = WINDOW_CLASS_NAME;
    604 
    605 		if ( !RegisterClass( &wc ) )
    606 		{
    607 			ri.Error( ERR_FATAL, "GLW_CreateWindow: could not register window class" );
    608 		}
    609 		s_classRegistered = qtrue;
    610 		ri.Printf( PRINT_ALL, "...registered window class\n" );
    611 	}
    612 
    613 	//
    614 	// create the HWND if one does not already exist
    615 	//
    616 	if ( !g_wv.hWnd )
    617 	{
    618 		//
    619 		// compute width and height
    620 		//
    621 		r.left = 0;
    622 		r.top = 0;
    623 		r.right  = width;
    624 		r.bottom = height;
    625 
    626 		if ( cdsFullscreen || !Q_stricmp( _3DFX_DRIVER_NAME, drivername ) )
    627 		{
    628 			exstyle = WS_EX_TOPMOST;
    629 			stylebits = WS_POPUP|WS_VISIBLE|WS_SYSMENU;
    630 		}
    631 		else
    632 		{
    633 			exstyle = 0;
    634 			stylebits = WINDOW_STYLE|WS_SYSMENU;
    635 			AdjustWindowRect (&r, stylebits, FALSE);
    636 		}
    637 
    638 		w = r.right - r.left;
    639 		h = r.bottom - r.top;
    640 
    641 		if ( cdsFullscreen || !Q_stricmp( _3DFX_DRIVER_NAME, drivername ) )
    642 		{
    643 			x = 0;
    644 			y = 0;
    645 		}
    646 		else
    647 		{
    648 			vid_xpos = ri.Cvar_Get ("vid_xpos", "", 0);
    649 			vid_ypos = ri.Cvar_Get ("vid_ypos", "", 0);
    650 			x = vid_xpos->integer;
    651 			y = vid_ypos->integer;
    652 
    653 			// adjust window coordinates if necessary 
    654 			// so that the window is completely on screen
    655 			if ( x < 0 )
    656 				x = 0;
    657 			if ( y < 0 )
    658 				y = 0;
    659 
    660 			if ( w < glw_state.desktopWidth &&
    661 				 h < glw_state.desktopHeight )
    662 			{
    663 				if ( x + w > glw_state.desktopWidth )
    664 					x = ( glw_state.desktopWidth - w );
    665 				if ( y + h > glw_state.desktopHeight )
    666 					y = ( glw_state.desktopHeight - h );
    667 			}
    668 		}
    669 
    670 		g_wv.hWnd = CreateWindowEx (
    671 			 exstyle, 
    672 			 WINDOW_CLASS_NAME,
    673 			 "Quake 3: Arena",
    674 			 stylebits,
    675 			 x, y, w, h,
    676 			 NULL,
    677 			 NULL,
    678 			 g_wv.hInstance,
    679 			 NULL);
    680 
    681 		if ( !g_wv.hWnd )
    682 		{
    683 			ri.Error (ERR_FATAL, "GLW_CreateWindow() - Couldn't create window");
    684 		}
    685 	
    686 		ShowWindow( g_wv.hWnd, SW_SHOW );
    687 		UpdateWindow( g_wv.hWnd );
    688 		ri.Printf( PRINT_ALL, "...created window@%d,%d (%dx%d)\n", x, y, w, h );
    689 	}
    690 	else
    691 	{
    692 		ri.Printf( PRINT_ALL, "...window already present, CreateWindowEx skipped\n" );
    693 	}
    694 
    695 	if ( !GLW_InitDriver( drivername, colorbits ) )
    696 	{
    697 		ShowWindow( g_wv.hWnd, SW_HIDE );
    698 		DestroyWindow( g_wv.hWnd );
    699 		g_wv.hWnd = NULL;
    700 
    701 		return qfalse;
    702 	}
    703 
    704 	SetForegroundWindow( g_wv.hWnd );
    705 	SetFocus( g_wv.hWnd );
    706 
    707 	return qtrue;
    708 }
    709 
    710 static void PrintCDSError( int value )
    711 {
    712 	switch ( value )
    713 	{
    714 	case DISP_CHANGE_RESTART:
    715 		ri.Printf( PRINT_ALL, "restart required\n" );
    716 		break;
    717 	case DISP_CHANGE_BADPARAM:
    718 		ri.Printf( PRINT_ALL, "bad param\n" );
    719 		break;
    720 	case DISP_CHANGE_BADFLAGS:
    721 		ri.Printf( PRINT_ALL, "bad flags\n" );
    722 		break;
    723 	case DISP_CHANGE_FAILED:
    724 		ri.Printf( PRINT_ALL, "DISP_CHANGE_FAILED\n" );
    725 		break;
    726 	case DISP_CHANGE_BADMODE:
    727 		ri.Printf( PRINT_ALL, "bad mode\n" );
    728 		break;
    729 	case DISP_CHANGE_NOTUPDATED:
    730 		ri.Printf( PRINT_ALL, "not updated\n" );
    731 		break;
    732 	default:
    733 		ri.Printf( PRINT_ALL, "unknown error %d\n", value );
    734 		break;
    735 	}
    736 }
    737 
    738 /*
    739 ** GLW_SetMode
    740 */
    741 static rserr_t GLW_SetMode( const char *drivername, 
    742 						    int mode, 
    743 							int colorbits, 
    744 							qboolean cdsFullscreen )
    745 {
    746 	HDC hDC;
    747 	const char *win_fs[] = { "W", "FS" };
    748 	int		cdsRet;
    749 	DEVMODE dm;
    750 		
    751 	//
    752 	// print out informational messages
    753 	//
    754 	ri.Printf( PRINT_ALL, "...setting mode %d:", mode );
    755 	if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) )
    756 	{
    757 		ri.Printf( PRINT_ALL, " invalid mode\n" );
    758 		return RSERR_INVALID_MODE;
    759 	}
    760 	ri.Printf( PRINT_ALL, " %d %d %s\n", glConfig.vidWidth, glConfig.vidHeight, win_fs[cdsFullscreen] );
    761 
    762 	//
    763 	// check our desktop attributes
    764 	//
    765 	hDC = GetDC( GetDesktopWindow() );
    766 	glw_state.desktopBitsPixel = GetDeviceCaps( hDC, BITSPIXEL );
    767 	glw_state.desktopWidth = GetDeviceCaps( hDC, HORZRES );
    768 	glw_state.desktopHeight = GetDeviceCaps( hDC, VERTRES );
    769 	ReleaseDC( GetDesktopWindow(), hDC );
    770 
    771 	//
    772 	// verify desktop bit depth
    773 	//
    774 	if ( glConfig.driverType != GLDRV_VOODOO )
    775 	{
    776 		if ( glw_state.desktopBitsPixel < 15 || glw_state.desktopBitsPixel == 24 )
    777 		{
    778 			if ( colorbits == 0 || ( !cdsFullscreen && colorbits >= 15 ) )
    779 			{
    780 				if ( MessageBox( NULL,
    781 							"It is highly unlikely that a correct\n"
    782 							"windowed display can be initialized with\n"
    783 							"the current desktop display depth.  Select\n"
    784 							"'OK' to try anyway.  Press 'Cancel' if you\n"
    785 							"have a 3Dfx Voodoo, Voodoo-2, or Voodoo Rush\n"
    786 							"3D accelerator installed, or if you otherwise\n"
    787 							"wish to quit.",
    788 							"Low Desktop Color Depth",
    789 							MB_OKCANCEL | MB_ICONEXCLAMATION ) != IDOK )
    790 				{
    791 					return RSERR_INVALID_MODE;
    792 				}
    793 			}
    794 		}
    795 	}
    796 
    797 	// do a CDS if needed
    798 	if ( cdsFullscreen )
    799 	{
    800 		memset( &dm, 0, sizeof( dm ) );
    801 		
    802 		dm.dmSize = sizeof( dm );
    803 		
    804 		dm.dmPelsWidth  = glConfig.vidWidth;
    805 		dm.dmPelsHeight = glConfig.vidHeight;
    806 		dm.dmFields     = DM_PELSWIDTH | DM_PELSHEIGHT;
    807 
    808 		if ( r_displayRefresh->integer != 0 )
    809 		{
    810 			dm.dmDisplayFrequency = r_displayRefresh->integer;
    811 			dm.dmFields |= DM_DISPLAYFREQUENCY;
    812 		}
    813 		
    814 		// try to change color depth if possible
    815 		if ( colorbits != 0 )
    816 		{
    817 			if ( glw_state.allowdisplaydepthchange )
    818 			{
    819 				dm.dmBitsPerPel = colorbits;
    820 				dm.dmFields |= DM_BITSPERPEL;
    821 				ri.Printf( PRINT_ALL, "...using colorsbits of %d\n", colorbits );
    822 			}
    823 			else
    824 			{
    825 				ri.Printf( PRINT_ALL, "WARNING:...changing depth not supported on Win95 < pre-OSR 2.x\n" );
    826 			}
    827 		}
    828 		else
    829 		{
    830 			ri.Printf( PRINT_ALL, "...using desktop display depth of %d\n", glw_state.desktopBitsPixel );
    831 		}
    832 
    833 		//
    834 		// if we're already in fullscreen then just create the window
    835 		//
    836 		if ( glw_state.cdsFullscreen )
    837 		{
    838 			ri.Printf( PRINT_ALL, "...already fullscreen, avoiding redundant CDS\n" );
    839 
    840 			if ( !GLW_CreateWindow ( drivername, glConfig.vidWidth, glConfig.vidHeight, colorbits, qtrue ) )
    841 			{
    842 				ri.Printf( PRINT_ALL, "...restoring display settings\n" );
    843 				ChangeDisplaySettings( 0, 0 );
    844 				return RSERR_INVALID_MODE;
    845 			}
    846 		}
    847 		//
    848 		// need to call CDS
    849 		//
    850 		else
    851 		{
    852 			ri.Printf( PRINT_ALL, "...calling CDS: " );
    853 			
    854 			// try setting the exact mode requested, because some drivers don't report
    855 			// the low res modes in EnumDisplaySettings, but still work
    856 			if ( ( cdsRet = ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) ) == DISP_CHANGE_SUCCESSFUL )
    857 			{
    858 				ri.Printf( PRINT_ALL, "ok\n" );
    859 
    860 				if ( !GLW_CreateWindow ( drivername, glConfig.vidWidth, glConfig.vidHeight, colorbits, qtrue) )
    861 				{
    862 					ri.Printf( PRINT_ALL, "...restoring display settings\n" );
    863 					ChangeDisplaySettings( 0, 0 );
    864 					return RSERR_INVALID_MODE;
    865 				}
    866 				
    867 				glw_state.cdsFullscreen = qtrue;
    868 			}
    869 			else
    870 			{
    871 				//
    872 				// the exact mode failed, so scan EnumDisplaySettings for the next largest mode
    873 				//
    874 				DEVMODE		devmode;
    875 				int			modeNum;
    876 
    877 				ri.Printf( PRINT_ALL, "failed, " );
    878 				
    879 				PrintCDSError( cdsRet );
    880 			
    881 				ri.Printf( PRINT_ALL, "...trying next higher resolution:" );
    882 				
    883 				// we could do a better matching job here...
    884 				for ( modeNum = 0 ; ; modeNum++ ) {
    885 					if ( !EnumDisplaySettings( NULL, modeNum, &devmode ) ) {
    886 						modeNum = -1;
    887 						break;
    888 					}
    889 					if ( devmode.dmPelsWidth >= glConfig.vidWidth
    890 						&& devmode.dmPelsHeight >= glConfig.vidHeight
    891 						&& devmode.dmBitsPerPel >= 15 ) {
    892 						break;
    893 					}
    894 				}
    895 
    896 				if ( modeNum != -1 && ( cdsRet = ChangeDisplaySettings( &devmode, CDS_FULLSCREEN ) ) == DISP_CHANGE_SUCCESSFUL )
    897 				{
    898 					ri.Printf( PRINT_ALL, " ok\n" );
    899 					if ( !GLW_CreateWindow( drivername, glConfig.vidWidth, glConfig.vidHeight, colorbits, qtrue) )
    900 					{
    901 						ri.Printf( PRINT_ALL, "...restoring display settings\n" );
    902 						ChangeDisplaySettings( 0, 0 );
    903 						return RSERR_INVALID_MODE;
    904 					}
    905 					
    906 					glw_state.cdsFullscreen = qtrue;
    907 				}
    908 				else
    909 				{
    910 					ri.Printf( PRINT_ALL, " failed, " );
    911 					
    912 					PrintCDSError( cdsRet );
    913 					
    914 					ri.Printf( PRINT_ALL, "...restoring display settings\n" );
    915 					ChangeDisplaySettings( 0, 0 );
    916 					
    917 					glw_state.cdsFullscreen = qfalse;
    918 					glConfig.isFullscreen = qfalse;
    919 					if ( !GLW_CreateWindow( drivername, glConfig.vidWidth, glConfig.vidHeight, colorbits, qfalse) )
    920 					{
    921 						return RSERR_INVALID_MODE;
    922 					}
    923 					return RSERR_INVALID_FULLSCREEN;
    924 				}
    925 			}
    926 		}
    927 	}
    928 	else
    929 	{
    930 		if ( glw_state.cdsFullscreen )
    931 		{
    932 			ChangeDisplaySettings( 0, 0 );
    933 		}
    934 
    935 		glw_state.cdsFullscreen = qfalse;
    936 		if ( !GLW_CreateWindow( drivername, glConfig.vidWidth, glConfig.vidHeight, colorbits, qfalse ) )
    937 		{
    938 			return RSERR_INVALID_MODE;
    939 		}
    940 	}
    941 
    942 	//
    943 	// success, now check display frequency, although this won't be valid on Voodoo(2)
    944 	//
    945 	memset( &dm, 0, sizeof( dm ) );
    946 	dm.dmSize = sizeof( dm );
    947 	if ( EnumDisplaySettings( NULL, ENUM_CURRENT_SETTINGS, &dm ) )
    948 	{
    949 		glConfig.displayFrequency = dm.dmDisplayFrequency;
    950 	}
    951 
    952 	// NOTE: this is overridden later on standalone 3Dfx drivers
    953 	glConfig.isFullscreen = cdsFullscreen;
    954 
    955 	return RSERR_OK;
    956 }
    957 
    958 /*
    959 ** GLW_InitExtensions
    960 */
    961 static void GLW_InitExtensions( void )
    962 {
    963 	if ( !r_allowExtensions->integer )
    964 	{
    965 		ri.Printf( PRINT_ALL, "*** IGNORING OPENGL EXTENSIONS ***\n" );
    966 		return;
    967 	}
    968 
    969 	ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
    970 
    971 	// GL_S3_s3tc
    972 	glConfig.textureCompression = TC_NONE;
    973 	if ( strstr( glConfig.extensions_string, "GL_S3_s3tc" ) )
    974 	{
    975 		if ( r_ext_compressed_textures->integer )
    976 		{
    977 			glConfig.textureCompression = TC_S3TC;
    978 			ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
    979 		}
    980 		else
    981 		{
    982 			glConfig.textureCompression = TC_NONE;
    983 			ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
    984 		}
    985 	}
    986 	else
    987 	{
    988 		ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
    989 	}
    990 
    991 	// GL_EXT_texture_env_add
    992 	glConfig.textureEnvAddAvailable = qfalse;
    993 	if ( strstr( glConfig.extensions_string, "EXT_texture_env_add" ) )
    994 	{
    995 		if ( r_ext_texture_env_add->integer )
    996 		{
    997 			glConfig.textureEnvAddAvailable = qtrue;
    998 			ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
    999 		}
   1000 		else
   1001 		{
   1002 			glConfig.textureEnvAddAvailable = qfalse;
   1003 			ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
   1004 		}
   1005 	}
   1006 	else
   1007 	{
   1008 		ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
   1009 	}
   1010 
   1011 	// WGL_EXT_swap_control
   1012 	qwglSwapIntervalEXT = ( BOOL (WINAPI *)(int)) qwglGetProcAddress( "wglSwapIntervalEXT" );
   1013 	if ( qwglSwapIntervalEXT )
   1014 	{
   1015 		ri.Printf( PRINT_ALL, "...using WGL_EXT_swap_control\n" );
   1016 		r_swapInterval->modified = qtrue;	// force a set next frame
   1017 	}
   1018 	else
   1019 	{
   1020 		ri.Printf( PRINT_ALL, "...WGL_EXT_swap_control not found\n" );
   1021 	}
   1022 
   1023 	// GL_ARB_multitexture
   1024 	qglMultiTexCoord2fARB = NULL;
   1025 	qglActiveTextureARB = NULL;
   1026 	qglClientActiveTextureARB = NULL;
   1027 	if ( strstr( glConfig.extensions_string, "GL_ARB_multitexture" )  )
   1028 	{
   1029 		if ( r_ext_multitexture->integer )
   1030 		{
   1031 			qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) qwglGetProcAddress( "glMultiTexCoord2fARB" );
   1032 			qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) qwglGetProcAddress( "glActiveTextureARB" );
   1033 			qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) qwglGetProcAddress( "glClientActiveTextureARB" );
   1034 
   1035 			if ( qglActiveTextureARB )
   1036 			{
   1037 				qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, &glConfig.maxActiveTextures );
   1038 
   1039 				if ( glConfig.maxActiveTextures > 1 )
   1040 				{
   1041 					ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
   1042 				}
   1043 				else
   1044 				{
   1045 					qglMultiTexCoord2fARB = NULL;
   1046 					qglActiveTextureARB = NULL;
   1047 					qglClientActiveTextureARB = NULL;
   1048 					ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
   1049 				}
   1050 			}
   1051 		}
   1052 		else
   1053 		{
   1054 			ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
   1055 		}
   1056 	}
   1057 	else
   1058 	{
   1059 		ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
   1060 	}
   1061 
   1062 	// GL_EXT_compiled_vertex_array
   1063 	qglLockArraysEXT = NULL;
   1064 	qglUnlockArraysEXT = NULL;
   1065 	if ( strstr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) && ( glConfig.hardwareType != GLHW_RIVA128 ) )
   1066 	{
   1067 		if ( r_ext_compiled_vertex_array->integer )
   1068 		{
   1069 			ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
   1070 			qglLockArraysEXT = ( void ( APIENTRY * )( int, int ) ) qwglGetProcAddress( "glLockArraysEXT" );
   1071 			qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) qwglGetProcAddress( "glUnlockArraysEXT" );
   1072 			if (!qglLockArraysEXT || !qglUnlockArraysEXT) {
   1073 				ri.Error (ERR_FATAL, "bad getprocaddress");
   1074 			}
   1075 		}
   1076 		else
   1077 		{
   1078 			ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
   1079 		}
   1080 	}
   1081 	else
   1082 	{
   1083 		ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
   1084 	}
   1085 
   1086 	// WGL_3DFX_gamma_control
   1087 	qwglGetDeviceGammaRamp3DFX = NULL;
   1088 	qwglSetDeviceGammaRamp3DFX = NULL;
   1089 
   1090 	if ( strstr( glConfig.extensions_string, "WGL_3DFX_gamma_control" ) )
   1091 	{
   1092 		if ( !r_ignorehwgamma->integer && r_ext_gamma_control->integer )
   1093 		{
   1094 			qwglGetDeviceGammaRamp3DFX = ( BOOL ( WINAPI * )( HDC, LPVOID ) ) qwglGetProcAddress( "wglGetDeviceGammaRamp3DFX" );
   1095 			qwglSetDeviceGammaRamp3DFX = ( BOOL ( WINAPI * )( HDC, LPVOID ) ) qwglGetProcAddress( "wglSetDeviceGammaRamp3DFX" );
   1096 
   1097 			if ( qwglGetDeviceGammaRamp3DFX && qwglSetDeviceGammaRamp3DFX )
   1098 			{
   1099 				ri.Printf( PRINT_ALL, "...using WGL_3DFX_gamma_control\n" );
   1100 			}
   1101 			else
   1102 			{
   1103 				qwglGetDeviceGammaRamp3DFX = NULL;
   1104 				qwglSetDeviceGammaRamp3DFX = NULL;
   1105 			}
   1106 		}
   1107 		else
   1108 		{
   1109 			ri.Printf( PRINT_ALL, "...ignoring WGL_3DFX_gamma_control\n" );
   1110 		}
   1111 	}
   1112 	else
   1113 	{
   1114 		ri.Printf( PRINT_ALL, "...WGL_3DFX_gamma_control not found\n" );
   1115 	}
   1116 }
   1117 
   1118 /*
   1119 ** GLW_CheckOSVersion
   1120 */
   1121 static qboolean GLW_CheckOSVersion( void )
   1122 {
   1123 #define OSR2_BUILD_NUMBER 1111
   1124 
   1125 	OSVERSIONINFO	vinfo;
   1126 
   1127 	vinfo.dwOSVersionInfoSize = sizeof(vinfo);
   1128 
   1129 	glw_state.allowdisplaydepthchange = qfalse;
   1130 
   1131 	if ( GetVersionEx( &vinfo) )
   1132 	{
   1133 		if ( vinfo.dwMajorVersion > 4 )
   1134 		{
   1135 			glw_state.allowdisplaydepthchange = qtrue;
   1136 		}
   1137 		else if ( vinfo.dwMajorVersion == 4 )
   1138 		{
   1139 			if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
   1140 			{
   1141 				glw_state.allowdisplaydepthchange = qtrue;
   1142 			}
   1143 			else if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
   1144 			{
   1145 				if ( LOWORD( vinfo.dwBuildNumber ) >= OSR2_BUILD_NUMBER )
   1146 				{
   1147 					glw_state.allowdisplaydepthchange = qtrue;
   1148 				}
   1149 			}
   1150 		}
   1151 	}
   1152 	else
   1153 	{
   1154 		ri.Printf( PRINT_ALL, "GLW_CheckOSVersion() - GetVersionEx failed\n" );
   1155 		return qfalse;
   1156 	}
   1157 
   1158 	return qtrue;
   1159 }
   1160 
   1161 /*
   1162 ** GLW_LoadOpenGL
   1163 **
   1164 ** GLimp_win.c internal function that attempts to load and use 
   1165 ** a specific OpenGL DLL.
   1166 */
   1167 static qboolean GLW_LoadOpenGL( const char *drivername )
   1168 {
   1169 	char buffer[1024];
   1170 	qboolean cdsFullscreen;
   1171 
   1172 	Q_strncpyz( buffer, drivername, sizeof(buffer) );
   1173 	Q_strlwr(buffer);
   1174 
   1175 	//
   1176 	// determine if we're on a standalone driver
   1177 	//
   1178 	if ( strstr( buffer, "opengl32" ) != 0 || r_maskMinidriver->integer )
   1179 	{
   1180 		glConfig.driverType = GLDRV_ICD;
   1181 	}
   1182 	else
   1183 	{
   1184 		glConfig.driverType = GLDRV_STANDALONE;
   1185 
   1186 		ri.Printf( PRINT_ALL, "...assuming '%s' is a standalone driver\n", drivername );
   1187 
   1188 		if ( strstr( buffer, _3DFX_DRIVER_NAME ) )
   1189 		{
   1190 			glConfig.driverType = GLDRV_VOODOO;
   1191 		}
   1192 	}
   1193 
   1194 	// disable the 3Dfx splash screen
   1195 	_putenv("FX_GLIDE_NO_SPLASH=0");
   1196 
   1197 	//
   1198 	// load the driver and bind our function pointers to it
   1199 	// 
   1200 	if ( QGL_Init( buffer ) ) 
   1201 	{
   1202 		cdsFullscreen = r_fullscreen->integer;
   1203 
   1204 		// create the window and set up the context
   1205 		if ( !GLW_StartDriverAndSetMode( drivername, r_mode->integer, r_colorbits->integer, cdsFullscreen ) )
   1206 		{
   1207 			// if we're on a 24/32-bit desktop and we're going fullscreen on an ICD,
   1208 			// try it again but with a 16-bit desktop
   1209 			if ( glConfig.driverType == GLDRV_ICD )
   1210 			{
   1211 				if ( r_colorbits->integer != 16 ||
   1212 					 cdsFullscreen != qtrue ||
   1213 					 r_mode->integer != 3 )
   1214 				{
   1215 					if ( !GLW_StartDriverAndSetMode( drivername, 3, 16, qtrue ) )
   1216 					{
   1217 						goto fail;
   1218 					}
   1219 				}
   1220 			}
   1221 			else
   1222 			{
   1223 				goto fail;
   1224 			}
   1225 		}
   1226 
   1227 		if ( glConfig.driverType == GLDRV_VOODOO )
   1228 		{
   1229 			glConfig.isFullscreen = qtrue;
   1230 		}
   1231 
   1232 		return qtrue;
   1233 	}
   1234 fail:
   1235 
   1236 	QGL_Shutdown();
   1237 
   1238 	return qfalse;
   1239 }
   1240 
   1241 /*
   1242 ** GLimp_EndFrame
   1243 */
   1244 void GLimp_EndFrame (void)
   1245 {
   1246 	//
   1247 	// swapinterval stuff
   1248 	//
   1249 	if ( r_swapInterval->modified ) {
   1250 		r_swapInterval->modified = qfalse;
   1251 
   1252 		if ( !glConfig.stereoEnabled ) {	// why?
   1253 			if ( qwglSwapIntervalEXT ) {
   1254 				qwglSwapIntervalEXT( r_swapInterval->integer );
   1255 			}
   1256 		}
   1257 	}
   1258 
   1259 
   1260 	// don't flip if drawing to front buffer
   1261 	if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
   1262 	{
   1263 		if ( glConfig.driverType > GLDRV_ICD )
   1264 		{
   1265 			if ( !qwglSwapBuffers( glw_state.hDC ) )
   1266 			{
   1267 				ri.Error( ERR_FATAL, "GLimp_EndFrame() - SwapBuffers() failed!\n" );
   1268 			}
   1269 		}
   1270 		else
   1271 		{
   1272 			SwapBuffers( glw_state.hDC );
   1273 		}
   1274 	}
   1275 
   1276 	// check logging
   1277 	QGL_EnableLogging( r_logFile->integer );
   1278 }
   1279 
   1280 static void GLW_StartOpenGL( void )
   1281 {
   1282 	qboolean attemptedOpenGL32 = qfalse;
   1283 	qboolean attempted3Dfx = qfalse;
   1284 
   1285 	//
   1286 	// load and initialize the specific OpenGL driver
   1287 	//
   1288 	if ( !GLW_LoadOpenGL( r_glDriver->string ) )
   1289 	{
   1290 		if ( !Q_stricmp( r_glDriver->string, OPENGL_DRIVER_NAME ) )
   1291 		{
   1292 			attemptedOpenGL32 = qtrue;
   1293 		}
   1294 		else if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) )
   1295 		{
   1296 			attempted3Dfx = qtrue;
   1297 		}
   1298 
   1299 		if ( !attempted3Dfx )
   1300 		{
   1301 			attempted3Dfx = qtrue;
   1302 			if ( GLW_LoadOpenGL( _3DFX_DRIVER_NAME ) )
   1303 			{
   1304 				ri.Cvar_Set( "r_glDriver", _3DFX_DRIVER_NAME );
   1305 				r_glDriver->modified = qfalse;
   1306 			}
   1307 			else
   1308 			{
   1309 				if ( !attemptedOpenGL32 )
   1310 				{
   1311 					if ( !GLW_LoadOpenGL( OPENGL_DRIVER_NAME ) )
   1312 					{
   1313 						ri.Error( ERR_FATAL, "GLW_StartOpenGL() - could not load OpenGL subsystem\n" );
   1314 					}
   1315 					ri.Cvar_Set( "r_glDriver", OPENGL_DRIVER_NAME );
   1316 					r_glDriver->modified = qfalse;
   1317 				}
   1318 				else
   1319 				{
   1320 					ri.Error( ERR_FATAL, "GLW_StartOpenGL() - could not load OpenGL subsystem\n" );
   1321 				}
   1322 			}
   1323 		}
   1324 		else if ( !attemptedOpenGL32 )
   1325 		{
   1326 			attemptedOpenGL32 = qtrue;
   1327 			if ( GLW_LoadOpenGL( OPENGL_DRIVER_NAME ) )
   1328 			{
   1329 				ri.Cvar_Set( "r_glDriver", OPENGL_DRIVER_NAME );
   1330 				r_glDriver->modified = qfalse;
   1331 			}
   1332 			else
   1333 			{
   1334 				ri.Error( ERR_FATAL, "GLW_StartOpenGL() - could not load OpenGL subsystem\n" );
   1335 			}
   1336 		}
   1337 	}
   1338 }
   1339 
   1340 /*
   1341 ** GLimp_Init
   1342 **
   1343 ** This is the platform specific OpenGL initialization function.  It
   1344 ** is responsible for loading OpenGL, initializing it, setting
   1345 ** extensions, creating a window of the appropriate size, doing
   1346 ** fullscreen manipulations, etc.  Its overall responsibility is
   1347 ** to make sure that a functional OpenGL subsystem is operating
   1348 ** when it returns to the ref.
   1349 */
   1350 void GLimp_Init( void )
   1351 {
   1352 	char	buf[1024];
   1353 	cvar_t *lastValidRenderer = ri.Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE );
   1354 	cvar_t	*cv;
   1355 
   1356 	ri.Printf( PRINT_ALL, "Initializing OpenGL subsystem\n" );
   1357 
   1358 	//
   1359 	// check OS version to see if we can do fullscreen display changes
   1360 	//
   1361 	if ( !GLW_CheckOSVersion() )
   1362 	{
   1363 		ri.Error( ERR_FATAL, "GLimp_Init() - incorrect operating system\n" );
   1364 	}
   1365 
   1366 	// save off hInstance and wndproc
   1367 	cv = ri.Cvar_Get( "win_hinstance", "", 0 );
   1368 	sscanf( cv->string, "%i", (int *)&g_wv.hInstance );
   1369 
   1370 	cv = ri.Cvar_Get( "win_wndproc", "", 0 );
   1371 	sscanf( cv->string, "%i", (int *)&glw_state.wndproc );
   1372 
   1373 	r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
   1374 	r_maskMinidriver = ri.Cvar_Get( "r_maskMinidriver", "0", CVAR_LATCH );
   1375 
   1376 	// load appropriate DLL and initialize subsystem
   1377 	GLW_StartOpenGL();
   1378 
   1379 	// get our config strings
   1380 	Q_strncpyz( glConfig.vendor_string, qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
   1381 	Q_strncpyz( glConfig.renderer_string, qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
   1382 	Q_strncpyz( glConfig.version_string, qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
   1383 	Q_strncpyz( glConfig.extensions_string, qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
   1384 
   1385 	//
   1386 	// chipset specific configuration
   1387 	//
   1388 	Q_strncpyz( buf, glConfig.renderer_string, sizeof(buf) );
   1389 	Q_strlwr( buf );
   1390 
   1391 	//
   1392 	// NOTE: if changing cvars, do it within this block.  This allows them
   1393 	// to be overridden when testing driver fixes, etc. but only sets
   1394 	// them to their default state when the hardware is first installed/run.
   1395 	//
   1396 	if ( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) )
   1397 	{
   1398 		glConfig.hardwareType = GLHW_GENERIC;
   1399 
   1400 		ri.Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" );
   1401 
   1402 		// VOODOO GRAPHICS w/ 2MB
   1403 		if ( strstr( buf, "voodoo graphics/1 tmu/2 mb" ) )
   1404 		{
   1405 			ri.Cvar_Set( "r_picmip", "2" );
   1406 			ri.Cvar_Get( "r_picmip", "1", CVAR_ARCHIVE | CVAR_LATCH );
   1407 		}
   1408 		else
   1409 		{
   1410 			ri.Cvar_Set( "r_picmip", "1" );
   1411 
   1412 			if ( strstr( buf, "rage 128" ) || strstr( buf, "rage128" ) )
   1413 			{
   1414 				ri.Cvar_Set( "r_finish", "0" );
   1415 			}
   1416 			// Savage3D and Savage4 should always have trilinear enabled
   1417 			else if ( strstr( buf, "savage3d" ) || strstr( buf, "s3 savage4" ) )
   1418 			{
   1419 				ri.Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
   1420 			}
   1421 		}
   1422 	}
   1423 	
   1424 	//
   1425 	// this is where hardware specific workarounds that should be
   1426 	// detected/initialized every startup should go.
   1427 	//
   1428 	if ( strstr( buf, "banshee" ) || strstr( buf, "voodoo3" ) )
   1429 	{
   1430 		glConfig.hardwareType = GLHW_3DFX_2D3D;
   1431 	}
   1432 	// VOODOO GRAPHICS w/ 2MB
   1433 	else if ( strstr( buf, "voodoo graphics/1 tmu/2 mb" ) )
   1434 	{
   1435 	}
   1436 	else if ( strstr( buf, "glzicd" ) )
   1437 	{
   1438 	}
   1439 	else if ( strstr( buf, "rage pro" ) || strstr( buf, "Rage Pro" ) || strstr( buf, "ragepro" ) )
   1440 	{
   1441 		glConfig.hardwareType = GLHW_RAGEPRO;
   1442 	}
   1443 	else if ( strstr( buf, "rage 128" ) )
   1444 	{
   1445 	}
   1446 	else if ( strstr( buf, "permedia2" ) )
   1447 	{
   1448 		glConfig.hardwareType = GLHW_PERMEDIA2;
   1449 	}
   1450 	else if ( strstr( buf, "riva 128" ) )
   1451 	{
   1452 		glConfig.hardwareType = GLHW_RIVA128;
   1453 	}
   1454 	else if ( strstr( buf, "riva tnt " ) )
   1455 	{
   1456 	}
   1457 
   1458 	ri.Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string );
   1459 
   1460 	GLW_InitExtensions();
   1461 	WG_CheckHardwareGamma();
   1462 }
   1463 
   1464 /*
   1465 ** GLimp_Shutdown
   1466 **
   1467 ** This routine does all OS specific shutdown procedures for the OpenGL
   1468 ** subsystem.
   1469 */
   1470 void GLimp_Shutdown( void )
   1471 {
   1472 //	const char *strings[] = { "soft", "hard" };
   1473 	const char *success[] = { "failed", "success" };
   1474 	int retVal;
   1475 
   1476 	// FIXME: Brian, we need better fallbacks from partially initialized failures
   1477 	if ( !qwglMakeCurrent ) {
   1478 		return;
   1479 	}
   1480 
   1481 	ri.Printf( PRINT_ALL, "Shutting down OpenGL subsystem\n" );
   1482 
   1483 	// restore gamma.  We do this first because 3Dfx's extension needs a valid OGL subsystem
   1484 	WG_RestoreGamma();
   1485 
   1486 	// set current context to NULL
   1487 	if ( qwglMakeCurrent )
   1488 	{
   1489 		retVal = qwglMakeCurrent( NULL, NULL ) != 0;
   1490 
   1491 		ri.Printf( PRINT_ALL, "...wglMakeCurrent( NULL, NULL ): %s\n", success[retVal] );
   1492 	}
   1493 
   1494 	// delete HGLRC
   1495 	if ( glw_state.hGLRC )
   1496 	{
   1497 		retVal = qwglDeleteContext( glw_state.hGLRC ) != 0;
   1498 		ri.Printf( PRINT_ALL, "...deleting GL context: %s\n", success[retVal] );
   1499 		glw_state.hGLRC = NULL;
   1500 	}
   1501 
   1502 	// release DC
   1503 	if ( glw_state.hDC )
   1504 	{
   1505 		retVal = ReleaseDC( g_wv.hWnd, glw_state.hDC ) != 0;
   1506 		ri.Printf( PRINT_ALL, "...releasing DC: %s\n", success[retVal] );
   1507 		glw_state.hDC   = NULL;
   1508 	}
   1509 
   1510 	// destroy window
   1511 	if ( g_wv.hWnd )
   1512 	{
   1513 		ri.Printf( PRINT_ALL, "...destroying window\n" );
   1514 		ShowWindow( g_wv.hWnd, SW_HIDE );
   1515 		DestroyWindow( g_wv.hWnd );
   1516 		g_wv.hWnd = NULL;
   1517 		glw_state.pixelFormatSet = qfalse;
   1518 	}
   1519 
   1520 	// close the r_logFile
   1521 	if ( glw_state.log_fp )
   1522 	{
   1523 		fclose( glw_state.log_fp );
   1524 		glw_state.log_fp = 0;
   1525 	}
   1526 
   1527 	// reset display settings
   1528 	if ( glw_state.cdsFullscreen )
   1529 	{
   1530 		ri.Printf( PRINT_ALL, "...resetting display\n" );
   1531 		ChangeDisplaySettings( 0, 0 );
   1532 		glw_state.cdsFullscreen = qfalse;
   1533 	}
   1534 
   1535 	// shutdown QGL subsystem
   1536 	QGL_Shutdown();
   1537 
   1538 	memset( &glConfig, 0, sizeof( glConfig ) );
   1539 	memset( &glState, 0, sizeof( glState ) );
   1540 }
   1541 
   1542 /*
   1543 ** GLimp_LogComment
   1544 */
   1545 void GLimp_LogComment( char *comment ) 
   1546 {
   1547 	if ( glw_state.log_fp ) {
   1548 		fprintf( glw_state.log_fp, "%s", comment );
   1549 	}
   1550 }
   1551 
   1552 
   1553 /*
   1554 ===========================================================
   1555 
   1556 SMP acceleration
   1557 
   1558 ===========================================================
   1559 */
   1560 
   1561 HANDLE	renderCommandsEvent;
   1562 HANDLE	renderCompletedEvent;
   1563 HANDLE	renderActiveEvent;
   1564 
   1565 void (*glimpRenderThread)( void );
   1566 
   1567 void GLimp_RenderThreadWrapper( void ) {
   1568 	glimpRenderThread();
   1569 
   1570 	// unbind the context before we die
   1571 	qwglMakeCurrent( glw_state.hDC, NULL );
   1572 }
   1573 
   1574 /*
   1575 =======================
   1576 GLimp_SpawnRenderThread
   1577 =======================
   1578 */
   1579 HANDLE	renderThreadHandle;
   1580 int		renderThreadId;
   1581 qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) {
   1582 
   1583 	renderCommandsEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
   1584 	renderCompletedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
   1585 	renderActiveEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
   1586 
   1587 	glimpRenderThread = function;
   1588 
   1589 	renderThreadHandle = CreateThread(
   1590 	   NULL,	// LPSECURITY_ATTRIBUTES lpsa,
   1591 	   0,		// DWORD cbStack,
   1592 	   (LPTHREAD_START_ROUTINE)GLimp_RenderThreadWrapper,	// LPTHREAD_START_ROUTINE lpStartAddr,
   1593 	   0,			// LPVOID lpvThreadParm,
   1594 	   0,			//   DWORD fdwCreate,
   1595 	   &renderThreadId );
   1596 
   1597 	if ( !renderThreadHandle ) {
   1598 		return qfalse;
   1599 	}
   1600 
   1601 	return qtrue;
   1602 }
   1603 
   1604 static	void	*smpData;
   1605 static	int		wglErrors;
   1606 
   1607 void *GLimp_RendererSleep( void ) {
   1608 	void	*data;
   1609 
   1610 	if ( !qwglMakeCurrent( glw_state.hDC, NULL ) ) {
   1611 		wglErrors++;
   1612 	}
   1613 
   1614 	ResetEvent( renderActiveEvent );
   1615 
   1616 	// after this, the front end can exit GLimp_FrontEndSleep
   1617 	SetEvent( renderCompletedEvent );
   1618 
   1619 	WaitForSingleObject( renderCommandsEvent, INFINITE );
   1620 
   1621 	if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) ) {
   1622 		wglErrors++;
   1623 	}
   1624 
   1625 	ResetEvent( renderCompletedEvent );
   1626 	ResetEvent( renderCommandsEvent );
   1627 
   1628 	data = smpData;
   1629 
   1630 	// after this, the main thread can exit GLimp_WakeRenderer
   1631 	SetEvent( renderActiveEvent );
   1632 
   1633 	return data;
   1634 }
   1635 
   1636 
   1637 void GLimp_FrontEndSleep( void ) {
   1638 	WaitForSingleObject( renderCompletedEvent, INFINITE );
   1639 
   1640 	if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) ) {
   1641 		wglErrors++;
   1642 	}
   1643 }
   1644 
   1645 
   1646 void GLimp_WakeRenderer( void *data ) {
   1647 	smpData = data;
   1648 
   1649 	if ( !qwglMakeCurrent( glw_state.hDC, NULL ) ) {
   1650 		wglErrors++;
   1651 	}
   1652 
   1653 	// after this, the renderer can continue through GLimp_RendererSleep
   1654 	SetEvent( renderCommandsEvent );
   1655 
   1656 	WaitForSingleObject( renderActiveEvent, INFINITE );
   1657 }
   1658