Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

glw_imp.c (14058B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 /*
     21 ** GLW_IMP.C
     22 **
     23 ** This file contains ALL Win32 specific stuff having to do with the
     24 ** OpenGL refresh.  When a port is being made the following functions
     25 ** must be implemented by the port:
     26 **
     27 ** GLimp_EndFrame
     28 ** GLimp_Init
     29 ** GLimp_Shutdown
     30 ** GLimp_SwitchFullscreen
     31 **
     32 */
     33 #include <assert.h>
     34 #include <windows.h>
     35 #include "../ref_gl/gl_local.h"
     36 #include "glw_win.h"
     37 #include "winquake.h"
     38 
     39 static qboolean GLimp_SwitchFullscreen( int width, int height );
     40 qboolean GLimp_InitGL (void);
     41 
     42 glwstate_t glw_state;
     43 
     44 extern cvar_t *vid_fullscreen;
     45 extern cvar_t *vid_ref;
     46 
     47 static qboolean VerifyDriver( void )
     48 {
     49 	char buffer[1024];
     50 
     51 	strcpy( buffer, qglGetString( GL_RENDERER ) );
     52 	strlwr( buffer );
     53 	if ( strcmp( buffer, "gdi generic" ) == 0 )
     54 		if ( !glw_state.mcd_accelerated )
     55 			return false;
     56 	return true;
     57 }
     58 
     59 /*
     60 ** VID_CreateWindow
     61 */
     62 #define	WINDOW_CLASS_NAME	"Quake 2"
     63 
     64 qboolean VID_CreateWindow( int width, int height, qboolean fullscreen )
     65 {
     66 	WNDCLASS		wc;
     67 	RECT			r;
     68 	cvar_t			*vid_xpos, *vid_ypos;
     69 	int				stylebits;
     70 	int				x, y, w, h;
     71 	int				exstyle;
     72 
     73 	/* Register the frame class */
     74     wc.style         = 0;
     75     wc.lpfnWndProc   = (WNDPROC)glw_state.wndproc;
     76     wc.cbClsExtra    = 0;
     77     wc.cbWndExtra    = 0;
     78     wc.hInstance     = glw_state.hInstance;
     79     wc.hIcon         = 0;
     80     wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
     81 	wc.hbrBackground = (void *)COLOR_GRAYTEXT;
     82     wc.lpszMenuName  = 0;
     83     wc.lpszClassName = WINDOW_CLASS_NAME;
     84 
     85     if (!RegisterClass (&wc) )
     86 		ri.Sys_Error (ERR_FATAL, "Couldn't register window class");
     87 
     88 	if (fullscreen)
     89 	{
     90 		exstyle = WS_EX_TOPMOST;
     91 		stylebits = WS_POPUP|WS_VISIBLE;
     92 	}
     93 	else
     94 	{
     95 		exstyle = 0;
     96 		stylebits = WINDOW_STYLE;
     97 	}
     98 
     99 	r.left = 0;
    100 	r.top = 0;
    101 	r.right  = width;
    102 	r.bottom = height;
    103 
    104 	AdjustWindowRect (&r, stylebits, FALSE);
    105 
    106 	w = r.right - r.left;
    107 	h = r.bottom - r.top;
    108 
    109 	if (fullscreen)
    110 	{
    111 		x = 0;
    112 		y = 0;
    113 	}
    114 	else
    115 	{
    116 		vid_xpos = ri.Cvar_Get ("vid_xpos", "0", 0);
    117 		vid_ypos = ri.Cvar_Get ("vid_ypos", "0", 0);
    118 		x = vid_xpos->value;
    119 		y = vid_ypos->value;
    120 	}
    121 
    122 	glw_state.hWnd = CreateWindowEx (
    123 		 exstyle, 
    124 		 WINDOW_CLASS_NAME,
    125 		 "Quake 2",
    126 		 stylebits,
    127 		 x, y, w, h,
    128 		 NULL,
    129 		 NULL,
    130 		 glw_state.hInstance,
    131 		 NULL);
    132 
    133 	if (!glw_state.hWnd)
    134 		ri.Sys_Error (ERR_FATAL, "Couldn't create window");
    135 	
    136 	ShowWindow( glw_state.hWnd, SW_SHOW );
    137 	UpdateWindow( glw_state.hWnd );
    138 
    139 	// init all the gl stuff for the window
    140 	if (!GLimp_InitGL ())
    141 	{
    142 		ri.Con_Printf( PRINT_ALL, "VID_CreateWindow() - GLimp_InitGL failed\n");
    143 		return false;
    144 	}
    145 
    146 	SetForegroundWindow( glw_state.hWnd );
    147 	SetFocus( glw_state.hWnd );
    148 
    149 	// let the sound and input subsystems know about the new window
    150 	ri.Vid_NewWindow (width, height);
    151 
    152 	return true;
    153 }
    154 
    155 
    156 /*
    157 ** GLimp_SetMode
    158 */
    159 rserr_t GLimp_SetMode( int *pwidth, int *pheight, int mode, qboolean fullscreen )
    160 {
    161 	int width, height;
    162 	const char *win_fs[] = { "W", "FS" };
    163 
    164 	ri.Con_Printf( PRINT_ALL, "Initializing OpenGL display\n");
    165 
    166 	ri.Con_Printf (PRINT_ALL, "...setting mode %d:", mode );
    167 
    168 	if ( !ri.Vid_GetModeInfo( &width, &height, mode ) )
    169 	{
    170 		ri.Con_Printf( PRINT_ALL, " invalid mode\n" );
    171 		return rserr_invalid_mode;
    172 	}
    173 
    174 	ri.Con_Printf( PRINT_ALL, " %d %d %s\n", width, height, win_fs[fullscreen] );
    175 
    176 	// destroy the existing window
    177 	if (glw_state.hWnd)
    178 	{
    179 		GLimp_Shutdown ();
    180 	}
    181 
    182 	// do a CDS if needed
    183 	if ( fullscreen )
    184 	{
    185 		DEVMODE dm;
    186 
    187 		ri.Con_Printf( PRINT_ALL, "...attempting fullscreen\n" );
    188 
    189 		memset( &dm, 0, sizeof( dm ) );
    190 
    191 		dm.dmSize = sizeof( dm );
    192 
    193 		dm.dmPelsWidth  = width;
    194 		dm.dmPelsHeight = height;
    195 		dm.dmFields     = DM_PELSWIDTH | DM_PELSHEIGHT;
    196 
    197 		if ( gl_bitdepth->value != 0 )
    198 		{
    199 			dm.dmBitsPerPel = gl_bitdepth->value;
    200 			dm.dmFields |= DM_BITSPERPEL;
    201 			ri.Con_Printf( PRINT_ALL, "...using gl_bitdepth of %d\n", ( int ) gl_bitdepth->value );
    202 		}
    203 		else
    204 		{
    205 			HDC hdc = GetDC( NULL );
    206 			int bitspixel = GetDeviceCaps( hdc, BITSPIXEL );
    207 
    208 			ri.Con_Printf( PRINT_ALL, "...using desktop display depth of %d\n", bitspixel );
    209 
    210 			ReleaseDC( 0, hdc );
    211 		}
    212 
    213 		ri.Con_Printf( PRINT_ALL, "...calling CDS: " );
    214 		if ( ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) == DISP_CHANGE_SUCCESSFUL )
    215 		{
    216 			*pwidth = width;
    217 			*pheight = height;
    218 
    219 			gl_state.fullscreen = true;
    220 
    221 			ri.Con_Printf( PRINT_ALL, "ok\n" );
    222 
    223 			if ( !VID_CreateWindow (width, height, true) )
    224 				return rserr_invalid_mode;
    225 
    226 			return rserr_ok;
    227 		}
    228 		else
    229 		{
    230 			*pwidth = width;
    231 			*pheight = height;
    232 
    233 			ri.Con_Printf( PRINT_ALL, "failed\n" );
    234 
    235 			ri.Con_Printf( PRINT_ALL, "...calling CDS assuming dual monitors:" );
    236 
    237 			dm.dmPelsWidth = width * 2;
    238 			dm.dmPelsHeight = height;
    239 			dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
    240 
    241 			if ( gl_bitdepth->value != 0 )
    242 			{
    243 				dm.dmBitsPerPel = gl_bitdepth->value;
    244 				dm.dmFields |= DM_BITSPERPEL;
    245 			}
    246 
    247 			/*
    248 			** our first CDS failed, so maybe we're running on some weird dual monitor
    249 			** system 
    250 			*/
    251 			if ( ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL )
    252 			{
    253 				ri.Con_Printf( PRINT_ALL, " failed\n" );
    254 
    255 				ri.Con_Printf( PRINT_ALL, "...setting windowed mode\n" );
    256 
    257 				ChangeDisplaySettings( 0, 0 );
    258 
    259 				*pwidth = width;
    260 				*pheight = height;
    261 				gl_state.fullscreen = false;
    262 				if ( !VID_CreateWindow (width, height, false) )
    263 					return rserr_invalid_mode;
    264 				return rserr_invalid_fullscreen;
    265 			}
    266 			else
    267 			{
    268 				ri.Con_Printf( PRINT_ALL, " ok\n" );
    269 				if ( !VID_CreateWindow (width, height, true) )
    270 					return rserr_invalid_mode;
    271 
    272 				gl_state.fullscreen = true;
    273 				return rserr_ok;
    274 			}
    275 		}
    276 	}
    277 	else
    278 	{
    279 		ri.Con_Printf( PRINT_ALL, "...setting windowed mode\n" );
    280 
    281 		ChangeDisplaySettings( 0, 0 );
    282 
    283 		*pwidth = width;
    284 		*pheight = height;
    285 		gl_state.fullscreen = false;
    286 		if ( !VID_CreateWindow (width, height, false) )
    287 			return rserr_invalid_mode;
    288 	}
    289 
    290 	return rserr_ok;
    291 }
    292 
    293 /*
    294 ** GLimp_Shutdown
    295 **
    296 ** This routine does all OS specific shutdown procedures for the OpenGL
    297 ** subsystem.  Under OpenGL this means NULLing out the current DC and
    298 ** HGLRC, deleting the rendering context, and releasing the DC acquired
    299 ** for the window.  The state structure is also nulled out.
    300 **
    301 */
    302 void GLimp_Shutdown( void )
    303 {
    304 	if ( qwglMakeCurrent && !qwglMakeCurrent( NULL, NULL ) )
    305 		ri.Con_Printf( PRINT_ALL, "ref_gl::R_Shutdown() - wglMakeCurrent failed\n");
    306 	if ( glw_state.hGLRC )
    307 	{
    308 		if (  qwglDeleteContext && !qwglDeleteContext( glw_state.hGLRC ) )
    309 			ri.Con_Printf( PRINT_ALL, "ref_gl::R_Shutdown() - wglDeleteContext failed\n");
    310 		glw_state.hGLRC = NULL;
    311 	}
    312 	if (glw_state.hDC)
    313 	{
    314 		if ( !ReleaseDC( glw_state.hWnd, glw_state.hDC ) )
    315 			ri.Con_Printf( PRINT_ALL, "ref_gl::R_Shutdown() - ReleaseDC failed\n" );
    316 		glw_state.hDC   = NULL;
    317 	}
    318 	if (glw_state.hWnd)
    319 	{
    320 		DestroyWindow (	glw_state.hWnd );
    321 		glw_state.hWnd = NULL;
    322 	}
    323 
    324 	if ( glw_state.log_fp )
    325 	{
    326 		fclose( glw_state.log_fp );
    327 		glw_state.log_fp = 0;
    328 	}
    329 
    330 	UnregisterClass (WINDOW_CLASS_NAME, glw_state.hInstance);
    331 
    332 	if ( gl_state.fullscreen )
    333 	{
    334 		ChangeDisplaySettings( 0, 0 );
    335 		gl_state.fullscreen = false;
    336 	}
    337 }
    338 
    339 
    340 /*
    341 ** GLimp_Init
    342 **
    343 ** This routine is responsible for initializing the OS specific portions
    344 ** of OpenGL.  Under Win32 this means dealing with the pixelformats and
    345 ** doing the wgl interface stuff.
    346 */
    347 qboolean GLimp_Init( void *hinstance, void *wndproc )
    348 {
    349 #define OSR2_BUILD_NUMBER 1111
    350 
    351 	OSVERSIONINFO	vinfo;
    352 
    353 	vinfo.dwOSVersionInfoSize = sizeof(vinfo);
    354 
    355 	glw_state.allowdisplaydepthchange = false;
    356 
    357 	if ( GetVersionEx( &vinfo) )
    358 	{
    359 		if ( vinfo.dwMajorVersion > 4 )
    360 		{
    361 			glw_state.allowdisplaydepthchange = true;
    362 		}
    363 		else if ( vinfo.dwMajorVersion == 4 )
    364 		{
    365 			if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
    366 			{
    367 				glw_state.allowdisplaydepthchange = true;
    368 			}
    369 			else if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
    370 			{
    371 				if ( LOWORD( vinfo.dwBuildNumber ) >= OSR2_BUILD_NUMBER )
    372 				{
    373 					glw_state.allowdisplaydepthchange = true;
    374 				}
    375 			}
    376 		}
    377 	}
    378 	else
    379 	{
    380 		ri.Con_Printf( PRINT_ALL, "GLimp_Init() - GetVersionEx failed\n" );
    381 		return false;
    382 	}
    383 
    384 	glw_state.hInstance = ( HINSTANCE ) hinstance;
    385 	glw_state.wndproc = wndproc;
    386 
    387 	return true;
    388 }
    389 
    390 qboolean GLimp_InitGL (void)
    391 {
    392     PIXELFORMATDESCRIPTOR pfd = 
    393 	{
    394 		sizeof(PIXELFORMATDESCRIPTOR),	// size of this pfd
    395 		1,								// version number
    396 		PFD_DRAW_TO_WINDOW |			// support window
    397 		PFD_SUPPORT_OPENGL |			// support OpenGL
    398 		PFD_DOUBLEBUFFER,				// double buffered
    399 		PFD_TYPE_RGBA,					// RGBA type
    400 		24,								// 24-bit color depth
    401 		0, 0, 0, 0, 0, 0,				// color bits ignored
    402 		0,								// no alpha buffer
    403 		0,								// shift bit ignored
    404 		0,								// no accumulation buffer
    405 		0, 0, 0, 0, 					// accum bits ignored
    406 		32,								// 32-bit z-buffer	
    407 		0,								// no stencil buffer
    408 		0,								// no auxiliary buffer
    409 		PFD_MAIN_PLANE,					// main layer
    410 		0,								// reserved
    411 		0, 0, 0							// layer masks ignored
    412     };
    413     int  pixelformat;
    414 	cvar_t *stereo;
    415 	
    416 	stereo = ri.Cvar_Get( "cl_stereo", "0", 0 );
    417 
    418 	/*
    419 	** set PFD_STEREO if necessary
    420 	*/
    421 	if ( stereo->value != 0 )
    422 	{
    423 		ri.Con_Printf( PRINT_ALL, "...attempting to use stereo\n" );
    424 		pfd.dwFlags |= PFD_STEREO;
    425 		gl_state.stereo_enabled = true;
    426 	}
    427 	else
    428 	{
    429 		gl_state.stereo_enabled = false;
    430 	}
    431 
    432 	/*
    433 	** figure out if we're running on a minidriver or not
    434 	*/
    435 	if ( strstr( gl_driver->string, "opengl32" ) != 0 )
    436 		glw_state.minidriver = false;
    437 	else
    438 		glw_state.minidriver = true;
    439 
    440 	/*
    441 	** Get a DC for the specified window
    442 	*/
    443 	if ( glw_state.hDC != NULL )
    444 		ri.Con_Printf( PRINT_ALL, "GLimp_Init() - non-NULL DC exists\n" );
    445 
    446     if ( ( glw_state.hDC = GetDC( glw_state.hWnd ) ) == NULL )
    447 	{
    448 		ri.Con_Printf( PRINT_ALL, "GLimp_Init() - GetDC failed\n" );
    449 		return false;
    450 	}
    451 
    452 	if ( glw_state.minidriver )
    453 	{
    454 		if ( (pixelformat = qwglChoosePixelFormat( glw_state.hDC, &pfd)) == 0 )
    455 		{
    456 			ri.Con_Printf (PRINT_ALL, "GLimp_Init() - qwglChoosePixelFormat failed\n");
    457 			return false;
    458 		}
    459 		if ( qwglSetPixelFormat( glw_state.hDC, pixelformat, &pfd) == FALSE )
    460 		{
    461 			ri.Con_Printf (PRINT_ALL, "GLimp_Init() - qwglSetPixelFormat failed\n");
    462 			return false;
    463 		}
    464 		qwglDescribePixelFormat( glw_state.hDC, pixelformat, sizeof( pfd ), &pfd );
    465 	}
    466 	else
    467 	{
    468 		if ( ( pixelformat = ChoosePixelFormat( glw_state.hDC, &pfd)) == 0 )
    469 		{
    470 			ri.Con_Printf (PRINT_ALL, "GLimp_Init() - ChoosePixelFormat failed\n");
    471 			return false;
    472 		}
    473 		if ( SetPixelFormat( glw_state.hDC, pixelformat, &pfd) == FALSE )
    474 		{
    475 			ri.Con_Printf (PRINT_ALL, "GLimp_Init() - SetPixelFormat failed\n");
    476 			return false;
    477 		}
    478 		DescribePixelFormat( glw_state.hDC, pixelformat, sizeof( pfd ), &pfd );
    479 
    480 		if ( !( pfd.dwFlags & PFD_GENERIC_ACCELERATED ) )
    481 		{
    482 			extern cvar_t *gl_allow_software;
    483 
    484 			if ( gl_allow_software->value )
    485 				glw_state.mcd_accelerated = true;
    486 			else
    487 				glw_state.mcd_accelerated = false;
    488 		}
    489 		else
    490 		{
    491 			glw_state.mcd_accelerated = true;
    492 		}
    493 	}
    494 
    495 	/*
    496 	** report if stereo is desired but unavailable
    497 	*/
    498 	if ( !( pfd.dwFlags & PFD_STEREO ) && ( stereo->value != 0 ) ) 
    499 	{
    500 		ri.Con_Printf( PRINT_ALL, "...failed to select stereo pixel format\n" );
    501 		ri.Cvar_SetValue( "cl_stereo", 0 );
    502 		gl_state.stereo_enabled = false;
    503 	}
    504 
    505 	/*
    506 	** startup the OpenGL subsystem by creating a context and making
    507 	** it current
    508 	*/
    509 	if ( ( glw_state.hGLRC = qwglCreateContext( glw_state.hDC ) ) == 0 )
    510 	{
    511 		ri.Con_Printf (PRINT_ALL, "GLimp_Init() - qwglCreateContext failed\n");
    512 
    513 		goto fail;
    514 	}
    515 
    516     if ( !qwglMakeCurrent( glw_state.hDC, glw_state.hGLRC ) )
    517 	{
    518 		ri.Con_Printf (PRINT_ALL, "GLimp_Init() - qwglMakeCurrent failed\n");
    519 
    520 		goto fail;
    521 	}
    522 
    523 	if ( !VerifyDriver() )
    524 	{
    525 		ri.Con_Printf( PRINT_ALL, "GLimp_Init() - no hardware acceleration detected\n" );
    526 		goto fail;
    527 	}
    528 
    529 	/*
    530 	** print out PFD specifics 
    531 	*/
    532 	ri.Con_Printf( PRINT_ALL, "GL PFD: color(%d-bits) Z(%d-bit)\n", ( int ) pfd.cColorBits, ( int ) pfd.cDepthBits );
    533 
    534 	return true;
    535 
    536 fail:
    537 	if ( glw_state.hGLRC )
    538 	{
    539 		qwglDeleteContext( glw_state.hGLRC );
    540 		glw_state.hGLRC = NULL;
    541 	}
    542 
    543 	if ( glw_state.hDC )
    544 	{
    545 		ReleaseDC( glw_state.hWnd, glw_state.hDC );
    546 		glw_state.hDC = NULL;
    547 	}
    548 	return false;
    549 }
    550 
    551 /*
    552 ** GLimp_BeginFrame
    553 */
    554 void GLimp_BeginFrame( float camera_separation )
    555 {
    556 	if ( gl_bitdepth->modified )
    557 	{
    558 		if ( gl_bitdepth->value != 0 && !glw_state.allowdisplaydepthchange )
    559 		{
    560 			ri.Cvar_SetValue( "gl_bitdepth", 0 );
    561 			ri.Con_Printf( PRINT_ALL, "gl_bitdepth requires Win95 OSR2.x or WinNT 4.x\n" );
    562 		}
    563 		gl_bitdepth->modified = false;
    564 	}
    565 
    566 	if ( camera_separation < 0 && gl_state.stereo_enabled )
    567 	{
    568 		qglDrawBuffer( GL_BACK_LEFT );
    569 	}
    570 	else if ( camera_separation > 0 && gl_state.stereo_enabled )
    571 	{
    572 		qglDrawBuffer( GL_BACK_RIGHT );
    573 	}
    574 	else
    575 	{
    576 		qglDrawBuffer( GL_BACK );
    577 	}
    578 }
    579 
    580 /*
    581 ** GLimp_EndFrame
    582 ** 
    583 ** Responsible for doing a swapbuffers and possibly for other stuff
    584 ** as yet to be determined.  Probably better not to make this a GLimp
    585 ** function and instead do a call to GLimp_SwapBuffers.
    586 */
    587 void GLimp_EndFrame (void)
    588 {
    589 	int		err;
    590 
    591 	err = qglGetError();
    592 	assert( err == GL_NO_ERROR );
    593 
    594 	if ( stricmp( gl_drawbuffer->string, "GL_BACK" ) == 0 )
    595 	{
    596 		if ( !qwglSwapBuffers( glw_state.hDC ) )
    597 			ri.Sys_Error( ERR_FATAL, "GLimp_EndFrame() - SwapBuffers() failed!\n" );
    598 	}
    599 }
    600 
    601 /*
    602 ** GLimp_AppActivate
    603 */
    604 void GLimp_AppActivate( qboolean active )
    605 {
    606 	if ( active )
    607 	{
    608 		SetForegroundWindow( glw_state.hWnd );
    609 		ShowWindow( glw_state.hWnd, SW_RESTORE );
    610 	}
    611 	else
    612 	{
    613 		if ( vid_fullscreen->value )
    614 			ShowWindow( glw_state.hWnd, SW_MINIMIZE );
    615 	}
    616 }