vid_dll.c (16730B)
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 // Main windowed and fullscreen graphics interface module. This module 21 // is used for both the software and OpenGL rendering versions of the 22 // Quake refresh engine. 23 #include <assert.h> 24 #include <float.h> 25 26 #include "..\client\client.h" 27 #include "winquake.h" 28 //#include "zmouse.h" 29 30 // Structure containing functions exported from refresh DLL 31 refexport_t re; 32 33 cvar_t *win_noalttab; 34 35 #ifndef WM_MOUSEWHEEL 36 #define WM_MOUSEWHEEL (WM_MOUSELAST+1) // message that will be supported by the OS 37 #endif 38 39 static UINT MSH_MOUSEWHEEL; 40 41 // Console variables that we need to access from this module 42 cvar_t *vid_gamma; 43 cvar_t *vid_ref; // Name of Refresh DLL loaded 44 cvar_t *vid_xpos; // X coordinate of window position 45 cvar_t *vid_ypos; // Y coordinate of window position 46 cvar_t *vid_fullscreen; 47 48 // Global variables used internally by this module 49 viddef_t viddef; // global video state; used by other modules 50 HINSTANCE reflib_library; // Handle to refresh DLL 51 qboolean reflib_active = 0; 52 53 HWND cl_hwnd; // Main window handle for life of program 54 55 #define VID_NUM_MODES ( sizeof( vid_modes ) / sizeof( vid_modes[0] ) ) 56 57 LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); 58 59 static qboolean s_alttab_disabled; 60 61 extern unsigned sys_msg_time; 62 63 /* 64 ** WIN32 helper functions 65 */ 66 extern qboolean s_win95; 67 68 static void WIN_DisableAltTab( void ) 69 { 70 if ( s_alttab_disabled ) 71 return; 72 73 if ( s_win95 ) 74 { 75 BOOL old; 76 77 SystemParametersInfo( SPI_SCREENSAVERRUNNING, 1, &old, 0 ); 78 } 79 else 80 { 81 RegisterHotKey( 0, 0, MOD_ALT, VK_TAB ); 82 RegisterHotKey( 0, 1, MOD_ALT, VK_RETURN ); 83 } 84 s_alttab_disabled = true; 85 } 86 87 static void WIN_EnableAltTab( void ) 88 { 89 if ( s_alttab_disabled ) 90 { 91 if ( s_win95 ) 92 { 93 BOOL old; 94 95 SystemParametersInfo( SPI_SCREENSAVERRUNNING, 0, &old, 0 ); 96 } 97 else 98 { 99 UnregisterHotKey( 0, 0 ); 100 UnregisterHotKey( 0, 1 ); 101 } 102 103 s_alttab_disabled = false; 104 } 105 } 106 107 /* 108 ========================================================================== 109 110 DLL GLUE 111 112 ========================================================================== 113 */ 114 115 #define MAXPRINTMSG 4096 116 void VID_Printf (int print_level, char *fmt, ...) 117 { 118 va_list argptr; 119 char msg[MAXPRINTMSG]; 120 static qboolean inupdate; 121 122 va_start (argptr,fmt); 123 vsprintf (msg,fmt,argptr); 124 va_end (argptr); 125 126 if (print_level == PRINT_ALL) 127 { 128 Com_Printf ("%s", msg); 129 } 130 else if ( print_level == PRINT_DEVELOPER ) 131 { 132 Com_DPrintf ("%s", msg); 133 } 134 else if ( print_level == PRINT_ALERT ) 135 { 136 MessageBox( 0, msg, "PRINT_ALERT", MB_ICONWARNING ); 137 OutputDebugString( msg ); 138 } 139 } 140 141 void VID_Error (int err_level, char *fmt, ...) 142 { 143 va_list argptr; 144 char msg[MAXPRINTMSG]; 145 static qboolean inupdate; 146 147 va_start (argptr,fmt); 148 vsprintf (msg,fmt,argptr); 149 va_end (argptr); 150 151 Com_Error (err_level,"%s", msg); 152 } 153 154 //========================================================================== 155 156 byte scantokey[128] = 157 { 158 // 0 1 2 3 4 5 6 7 159 // 8 9 A B C D E F 160 0 , 27, '1', '2', '3', '4', '5', '6', 161 '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 162 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 163 'o', 'p', '[', ']', 13 , K_CTRL,'a', 's', // 1 164 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 165 '\'' , '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 166 'b', 'n', 'm', ',', '.', '/', K_SHIFT,'*', 167 K_ALT,' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 168 K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, 169 K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW, K_KP_PLUS,K_END, //4 170 K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, 0, K_F11, 171 K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 172 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, 173 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 174 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, 175 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 176 }; 177 178 /* 179 ======= 180 MapKey 181 182 Map from windows to quake keynums 183 ======= 184 */ 185 int MapKey (int key) 186 { 187 int result; 188 int modified = ( key >> 16 ) & 255; 189 qboolean is_extended = false; 190 191 if ( modified > 127) 192 return 0; 193 194 if ( key & ( 1 << 24 ) ) 195 is_extended = true; 196 197 result = scantokey[modified]; 198 199 if ( !is_extended ) 200 { 201 switch ( result ) 202 { 203 case K_HOME: 204 return K_KP_HOME; 205 case K_UPARROW: 206 return K_KP_UPARROW; 207 case K_PGUP: 208 return K_KP_PGUP; 209 case K_LEFTARROW: 210 return K_KP_LEFTARROW; 211 case K_RIGHTARROW: 212 return K_KP_RIGHTARROW; 213 case K_END: 214 return K_KP_END; 215 case K_DOWNARROW: 216 return K_KP_DOWNARROW; 217 case K_PGDN: 218 return K_KP_PGDN; 219 case K_INS: 220 return K_KP_INS; 221 case K_DEL: 222 return K_KP_DEL; 223 default: 224 return result; 225 } 226 } 227 else 228 { 229 switch ( result ) 230 { 231 case 0x0D: 232 return K_KP_ENTER; 233 case 0x2F: 234 return K_KP_SLASH; 235 case 0xAF: 236 return K_KP_PLUS; 237 } 238 return result; 239 } 240 } 241 242 void AppActivate(BOOL fActive, BOOL minimize) 243 { 244 Minimized = minimize; 245 246 Key_ClearStates(); 247 248 // we don't want to act like we're active if we're minimized 249 if (fActive && !Minimized) 250 ActiveApp = true; 251 else 252 ActiveApp = false; 253 254 // minimize/restore mouse-capture on demand 255 if (!ActiveApp) 256 { 257 IN_Activate (false); 258 CDAudio_Activate (false); 259 S_Activate (false); 260 261 if ( win_noalttab->value ) 262 { 263 WIN_EnableAltTab(); 264 } 265 } 266 else 267 { 268 IN_Activate (true); 269 CDAudio_Activate (true); 270 S_Activate (true); 271 if ( win_noalttab->value ) 272 { 273 WIN_DisableAltTab(); 274 } 275 } 276 } 277 278 /* 279 ==================== 280 MainWndProc 281 282 main window procedure 283 ==================== 284 */ 285 LONG WINAPI MainWndProc ( 286 HWND hWnd, 287 UINT uMsg, 288 WPARAM wParam, 289 LPARAM lParam) 290 { 291 LONG lRet = 0; 292 293 if ( uMsg == MSH_MOUSEWHEEL ) 294 { 295 if ( ( ( int ) wParam ) > 0 ) 296 { 297 Key_Event( K_MWHEELUP, true, sys_msg_time ); 298 Key_Event( K_MWHEELUP, false, sys_msg_time ); 299 } 300 else 301 { 302 Key_Event( K_MWHEELDOWN, true, sys_msg_time ); 303 Key_Event( K_MWHEELDOWN, false, sys_msg_time ); 304 } 305 return DefWindowProc (hWnd, uMsg, wParam, lParam); 306 } 307 308 switch (uMsg) 309 { 310 case WM_MOUSEWHEEL: 311 /* 312 ** this chunk of code theoretically only works under NT4 and Win98 313 ** since this message doesn't exist under Win95 314 */ 315 if ( ( short ) HIWORD( wParam ) > 0 ) 316 { 317 Key_Event( K_MWHEELUP, true, sys_msg_time ); 318 Key_Event( K_MWHEELUP, false, sys_msg_time ); 319 } 320 else 321 { 322 Key_Event( K_MWHEELDOWN, true, sys_msg_time ); 323 Key_Event( K_MWHEELDOWN, false, sys_msg_time ); 324 } 325 break; 326 327 case WM_HOTKEY: 328 return 0; 329 330 case WM_CREATE: 331 cl_hwnd = hWnd; 332 333 MSH_MOUSEWHEEL = RegisterWindowMessage("MSWHEEL_ROLLMSG"); 334 return DefWindowProc (hWnd, uMsg, wParam, lParam); 335 336 case WM_PAINT: 337 SCR_DirtyScreen (); // force entire screen to update next frame 338 return DefWindowProc (hWnd, uMsg, wParam, lParam); 339 340 case WM_DESTROY: 341 // let sound and input know about this? 342 cl_hwnd = NULL; 343 return DefWindowProc (hWnd, uMsg, wParam, lParam); 344 345 case WM_ACTIVATE: 346 { 347 int fActive, fMinimized; 348 349 // KJB: Watch this for problems in fullscreen modes with Alt-tabbing. 350 fActive = LOWORD(wParam); 351 fMinimized = (BOOL) HIWORD(wParam); 352 353 AppActivate( fActive != WA_INACTIVE, fMinimized); 354 355 if ( reflib_active ) 356 re.AppActivate( !( fActive == WA_INACTIVE ) ); 357 } 358 return DefWindowProc (hWnd, uMsg, wParam, lParam); 359 360 case WM_MOVE: 361 { 362 int xPos, yPos; 363 RECT r; 364 int style; 365 366 if (!vid_fullscreen->value) 367 { 368 xPos = (short) LOWORD(lParam); // horizontal position 369 yPos = (short) HIWORD(lParam); // vertical position 370 371 r.left = 0; 372 r.top = 0; 373 r.right = 1; 374 r.bottom = 1; 375 376 style = GetWindowLong( hWnd, GWL_STYLE ); 377 AdjustWindowRect( &r, style, FALSE ); 378 379 Cvar_SetValue( "vid_xpos", xPos + r.left); 380 Cvar_SetValue( "vid_ypos", yPos + r.top); 381 vid_xpos->modified = false; 382 vid_ypos->modified = false; 383 if (ActiveApp) 384 IN_Activate (true); 385 } 386 } 387 return DefWindowProc (hWnd, uMsg, wParam, lParam); 388 389 // this is complicated because Win32 seems to pack multiple mouse events into 390 // one update sometimes, so we always check all states and look for events 391 case WM_LBUTTONDOWN: 392 case WM_LBUTTONUP: 393 case WM_RBUTTONDOWN: 394 case WM_RBUTTONUP: 395 case WM_MBUTTONDOWN: 396 case WM_MBUTTONUP: 397 case WM_MOUSEMOVE: 398 { 399 int temp; 400 401 temp = 0; 402 403 if (wParam & MK_LBUTTON) 404 temp |= 1; 405 406 if (wParam & MK_RBUTTON) 407 temp |= 2; 408 409 if (wParam & MK_MBUTTON) 410 temp |= 4; 411 412 IN_MouseEvent (temp); 413 } 414 break; 415 416 case WM_SYSCOMMAND: 417 if ( wParam == SC_SCREENSAVE ) 418 return 0; 419 return DefWindowProc (hWnd, uMsg, wParam, lParam); 420 case WM_SYSKEYDOWN: 421 if ( wParam == 13 ) 422 { 423 if ( vid_fullscreen ) 424 { 425 Cvar_SetValue( "vid_fullscreen", !vid_fullscreen->value ); 426 } 427 return 0; 428 } 429 // fall through 430 case WM_KEYDOWN: 431 Key_Event( MapKey( lParam ), true, sys_msg_time); 432 break; 433 434 case WM_SYSKEYUP: 435 case WM_KEYUP: 436 Key_Event( MapKey( lParam ), false, sys_msg_time); 437 break; 438 439 case MM_MCINOTIFY: 440 { 441 LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 442 lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); 443 } 444 break; 445 446 default: // pass all unhandled messages to DefWindowProc 447 return DefWindowProc (hWnd, uMsg, wParam, lParam); 448 } 449 450 /* return 0 if handled message, 1 if not */ 451 return DefWindowProc( hWnd, uMsg, wParam, lParam ); 452 } 453 454 /* 455 ============ 456 VID_Restart_f 457 458 Console command to re-start the video mode and refresh DLL. We do this 459 simply by setting the modified flag for the vid_ref variable, which will 460 cause the entire video mode and refresh DLL to be reset on the next frame. 461 ============ 462 */ 463 void VID_Restart_f (void) 464 { 465 vid_ref->modified = true; 466 } 467 468 void VID_Front_f( void ) 469 { 470 SetWindowLong( cl_hwnd, GWL_EXSTYLE, WS_EX_TOPMOST ); 471 SetForegroundWindow( cl_hwnd ); 472 } 473 474 /* 475 ** VID_GetModeInfo 476 */ 477 typedef struct vidmode_s 478 { 479 const char *description; 480 int width, height; 481 int mode; 482 } vidmode_t; 483 484 vidmode_t vid_modes[] = 485 { 486 { "Mode 0: 320x240", 320, 240, 0 }, 487 { "Mode 1: 400x300", 400, 300, 1 }, 488 { "Mode 2: 512x384", 512, 384, 2 }, 489 { "Mode 3: 640x480", 640, 480, 3 }, 490 { "Mode 4: 800x600", 800, 600, 4 }, 491 { "Mode 5: 960x720", 960, 720, 5 }, 492 { "Mode 6: 1024x768", 1024, 768, 6 }, 493 { "Mode 7: 1152x864", 1152, 864, 7 }, 494 { "Mode 8: 1280x960", 1280, 960, 8 }, 495 { "Mode 9: 1600x1200", 1600, 1200, 9 } 496 }; 497 498 qboolean VID_GetModeInfo( int *width, int *height, int mode ) 499 { 500 if ( mode < 0 || mode >= VID_NUM_MODES ) 501 return false; 502 503 *width = vid_modes[mode].width; 504 *height = vid_modes[mode].height; 505 506 return true; 507 } 508 509 /* 510 ** VID_UpdateWindowPosAndSize 511 */ 512 void VID_UpdateWindowPosAndSize( int x, int y ) 513 { 514 RECT r; 515 int style; 516 int w, h; 517 518 r.left = 0; 519 r.top = 0; 520 r.right = viddef.width; 521 r.bottom = viddef.height; 522 523 style = GetWindowLong( cl_hwnd, GWL_STYLE ); 524 AdjustWindowRect( &r, style, FALSE ); 525 526 w = r.right - r.left; 527 h = r.bottom - r.top; 528 529 MoveWindow( cl_hwnd, vid_xpos->value, vid_ypos->value, w, h, TRUE ); 530 } 531 532 /* 533 ** VID_NewWindow 534 */ 535 void VID_NewWindow ( int width, int height) 536 { 537 viddef.width = width; 538 viddef.height = height; 539 540 cl.force_refdef = true; // can't use a paused refdef 541 } 542 543 void VID_FreeReflib (void) 544 { 545 if ( !FreeLibrary( reflib_library ) ) 546 Com_Error( ERR_FATAL, "Reflib FreeLibrary failed" ); 547 memset (&re, 0, sizeof(re)); 548 reflib_library = NULL; 549 reflib_active = false; 550 } 551 552 /* 553 ============== 554 VID_LoadRefresh 555 ============== 556 */ 557 qboolean VID_LoadRefresh( char *name ) 558 { 559 refimport_t ri; 560 GetRefAPI_t GetRefAPI; 561 562 if ( reflib_active ) 563 { 564 re.Shutdown(); 565 VID_FreeReflib (); 566 } 567 568 Com_Printf( "------- Loading %s -------\n", name ); 569 570 if ( ( reflib_library = LoadLibrary( name ) ) == 0 ) 571 { 572 Com_Printf( "LoadLibrary(\"%s\") failed\n", name ); 573 574 return false; 575 } 576 577 ri.Cmd_AddCommand = Cmd_AddCommand; 578 ri.Cmd_RemoveCommand = Cmd_RemoveCommand; 579 ri.Cmd_Argc = Cmd_Argc; 580 ri.Cmd_Argv = Cmd_Argv; 581 ri.Cmd_ExecuteText = Cbuf_ExecuteText; 582 ri.Con_Printf = VID_Printf; 583 ri.Sys_Error = VID_Error; 584 ri.FS_LoadFile = FS_LoadFile; 585 ri.FS_FreeFile = FS_FreeFile; 586 ri.FS_Gamedir = FS_Gamedir; 587 ri.Cvar_Get = Cvar_Get; 588 ri.Cvar_Set = Cvar_Set; 589 ri.Cvar_SetValue = Cvar_SetValue; 590 ri.Vid_GetModeInfo = VID_GetModeInfo; 591 ri.Vid_MenuInit = VID_MenuInit; 592 ri.Vid_NewWindow = VID_NewWindow; 593 594 if ( ( GetRefAPI = (void *) GetProcAddress( reflib_library, "GetRefAPI" ) ) == 0 ) 595 Com_Error( ERR_FATAL, "GetProcAddress failed on %s", name ); 596 597 re = GetRefAPI( ri ); 598 599 if (re.api_version != API_VERSION) 600 { 601 VID_FreeReflib (); 602 Com_Error (ERR_FATAL, "%s has incompatible api_version", name); 603 } 604 605 if ( re.Init( global_hInstance, MainWndProc ) == -1 ) 606 { 607 re.Shutdown(); 608 VID_FreeReflib (); 609 return false; 610 } 611 612 Com_Printf( "------------------------------------\n"); 613 reflib_active = true; 614 615 //====== 616 //PGM 617 vidref_val = VIDREF_OTHER; 618 if(vid_ref) 619 { 620 if(!strcmp (vid_ref->string, "gl")) 621 vidref_val = VIDREF_GL; 622 else if(!strcmp(vid_ref->string, "soft")) 623 vidref_val = VIDREF_SOFT; 624 } 625 //PGM 626 //====== 627 628 return true; 629 } 630 631 /* 632 ============ 633 VID_CheckChanges 634 635 This function gets called once just before drawing each frame, and it's sole purpose in life 636 is to check to see if any of the video mode parameters have changed, and if they have to 637 update the rendering DLL and/or video mode to match. 638 ============ 639 */ 640 void VID_CheckChanges (void) 641 { 642 char name[100]; 643 644 if ( win_noalttab->modified ) 645 { 646 if ( win_noalttab->value ) 647 { 648 WIN_DisableAltTab(); 649 } 650 else 651 { 652 WIN_EnableAltTab(); 653 } 654 win_noalttab->modified = false; 655 } 656 657 if ( vid_ref->modified ) 658 { 659 cl.force_refdef = true; // can't use a paused refdef 660 S_StopAllSounds(); 661 } 662 while (vid_ref->modified) 663 { 664 /* 665 ** refresh has changed 666 */ 667 vid_ref->modified = false; 668 vid_fullscreen->modified = true; 669 cl.refresh_prepped = false; 670 cls.disable_screen = true; 671 672 Com_sprintf( name, sizeof(name), "ref_%s.dll", vid_ref->string ); 673 if ( !VID_LoadRefresh( name ) ) 674 { 675 if ( strcmp (vid_ref->string, "soft") == 0 ) 676 Com_Error (ERR_FATAL, "Couldn't fall back to software refresh!"); 677 Cvar_Set( "vid_ref", "soft" ); 678 679 /* 680 ** drop the console if we fail to load a refresh 681 */ 682 if ( cls.key_dest != key_console ) 683 { 684 Con_ToggleConsole_f(); 685 } 686 } 687 cls.disable_screen = false; 688 } 689 690 /* 691 ** update our window position 692 */ 693 if ( vid_xpos->modified || vid_ypos->modified ) 694 { 695 if (!vid_fullscreen->value) 696 VID_UpdateWindowPosAndSize( vid_xpos->value, vid_ypos->value ); 697 698 vid_xpos->modified = false; 699 vid_ypos->modified = false; 700 } 701 } 702 703 /* 704 ============ 705 VID_Init 706 ============ 707 */ 708 void VID_Init (void) 709 { 710 /* Create the video variables so we know how to start the graphics drivers */ 711 vid_ref = Cvar_Get ("vid_ref", "soft", CVAR_ARCHIVE); 712 vid_xpos = Cvar_Get ("vid_xpos", "3", CVAR_ARCHIVE); 713 vid_ypos = Cvar_Get ("vid_ypos", "22", CVAR_ARCHIVE); 714 vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE); 715 vid_gamma = Cvar_Get( "vid_gamma", "1", CVAR_ARCHIVE ); 716 win_noalttab = Cvar_Get( "win_noalttab", "0", CVAR_ARCHIVE ); 717 718 /* Add some console commands that we want to handle */ 719 Cmd_AddCommand ("vid_restart", VID_Restart_f); 720 Cmd_AddCommand ("vid_front", VID_Front_f); 721 722 /* 723 ** this is a gross hack but necessary to clamp the mode for 3Dfx 724 */ 725 #if 0 726 { 727 cvar_t *gl_driver = Cvar_Get( "gl_driver", "opengl32", 0 ); 728 cvar_t *gl_mode = Cvar_Get( "gl_mode", "3", 0 ); 729 730 if ( stricmp( gl_driver->string, "3dfxgl" ) == 0 ) 731 { 732 Cvar_SetValue( "gl_mode", 3 ); 733 viddef.width = 640; 734 viddef.height = 480; 735 } 736 } 737 #endif 738 739 /* Disable the 3Dfx splash screen */ 740 putenv("FX_GLIDE_NO_SPLASH=0"); 741 742 /* Start the graphics mode and load refresh DLL */ 743 VID_CheckChanges(); 744 } 745 746 /* 747 ============ 748 VID_Shutdown 749 ============ 750 */ 751 void VID_Shutdown (void) 752 { 753 if ( reflib_active ) 754 { 755 re.Shutdown (); 756 VID_FreeReflib (); 757 } 758 } 759 760