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 }