File: win32\vid_dll.c
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 { "Mode 10: 2048x1536", 2048, 1536, 10 }
497 };
498
499 qboolean VID_GetModeInfo( int *width, int *height, int mode )
500 {
501 if ( mode < 0 || mode >= VID_NUM_MODES )
502 return false;
503
504 *width = vid_modes[mode].width;
505 *height = vid_modes[mode].height;
506
507 return true;
508 }
509
510 /*
511 ** VID_UpdateWindowPosAndSize
512 */
513 void VID_UpdateWindowPosAndSize( int x, int y )
514 {
515 RECT r;
516 int style;
517 int w, h;
518
519 r.left = 0;
520 r.top = 0;
521 r.right = viddef.width;
522 r.bottom = viddef.height;
523
524 style = GetWindowLong( cl_hwnd, GWL_STYLE );
525 AdjustWindowRect( &r, style, FALSE );
526
527 w = r.right - r.left;
528 h = r.bottom - r.top;
529
530 MoveWindow( cl_hwnd, vid_xpos->value, vid_ypos->value, w, h, TRUE );
531 }
532
533 /*
534 ** VID_NewWindow
535 */
536 void VID_NewWindow ( int width, int height)
537 {
538 viddef.width = width;
539 viddef.height = height;
540
541 cl.force_refdef = true; // can't use a paused refdef
542 }
543
544 void VID_FreeReflib (void)
545 {
546 if ( !FreeLibrary( reflib_library ) )
547 Com_Error( ERR_FATAL, "Reflib FreeLibrary failed" );
548 memset (&re, 0, sizeof(re));
549 reflib_library = NULL;
550 reflib_active = false;
551 }
552
553 /*
554 ==============
555 VID_LoadRefresh
556 ==============
557 */
558 qboolean VID_LoadRefresh( char *name )
559 {
560 refimport_t ri;
561 GetRefAPI_t GetRefAPI;
562
563 if ( reflib_active )
564 {
565 re.Shutdown();
566 VID_FreeReflib ();
567 }
568
569 Com_Printf( "------- Loading %s -------\n", name );
570
571 if ( ( reflib_library = LoadLibrary( name ) ) == 0 )
572 {
573 Com_Printf( "LoadLibrary(\"%s\") failed\n", name );
574
575 return false;
576 }
577
578 ri.Cmd_AddCommand = Cmd_AddCommand;
579 ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
580 ri.Cmd_Argc = Cmd_Argc;
581 ri.Cmd_Argv = Cmd_Argv;
582 ri.Cmd_ExecuteText = Cbuf_ExecuteText;
583 ri.Con_Printf = VID_Printf;
584 ri.Sys_Error = VID_Error;
585 ri.FS_LoadFile = FS_LoadFile;
586 ri.FS_FreeFile = FS_FreeFile;
587 ri.FS_Gamedir = FS_Gamedir;
588 ri.Cvar_Get = Cvar_Get;
589 ri.Cvar_Set = Cvar_Set;
590 ri.Cvar_SetValue = Cvar_SetValue;
591 ri.Vid_GetModeInfo = VID_GetModeInfo;
592 ri.Vid_MenuInit = VID_MenuInit;
593 ri.Vid_NewWindow = VID_NewWindow;
594
595 if ( ( GetRefAPI = (void *) GetProcAddress( reflib_library, "GetRefAPI" ) ) == 0 )
596 Com_Error( ERR_FATAL, "GetProcAddress failed on %s", name );
597
598 re = GetRefAPI( ri );
599
600 if (re.api_version != API_VERSION)
601 {
602 VID_FreeReflib ();
603 Com_Error (ERR_FATAL, "%s has incompatible api_version", name);
604 }
605
606 if ( re.Init( global_hInstance, MainWndProc ) == -1 )
607 {
608 re.Shutdown();
609 VID_FreeReflib ();
610 return false;
611 }
612
613 Com_Printf( "------------------------------------\n");
614 reflib_active = true;
615
616 //======
617 //PGM
618 vidref_val = VIDREF_OTHER;
619 if(vid_ref)
620 {
621 if(!strcmp (vid_ref->string, "gl"))
622 vidref_val = VIDREF_GL;
623 else if(!strcmp(vid_ref->string, "soft"))
624 vidref_val = VIDREF_SOFT;
625 }
626 //PGM
627 //======
628
629 return true;
630 }
631
632 /*
633 ============
634 VID_CheckChanges
635
636 This function gets called once just before drawing each frame, and it's sole purpose in life
637 is to check to see if any of the video mode parameters have changed, and if they have to
638 update the rendering DLL and/or video mode to match.
639 ============
640 */
641 void VID_CheckChanges (void)
642 {
643 char name[100];
644
645 if ( win_noalttab->modified )
646 {
647 if ( win_noalttab->value )
648 {
649 WIN_DisableAltTab();
650 }
651 else
652 {
653 WIN_EnableAltTab();
654 }
655 win_noalttab->modified = false;
656 }
657
658 if ( vid_ref->modified )
659 {
660 cl.force_refdef = true; // can't use a paused refdef
661 S_StopAllSounds();
662 }
663 while (vid_ref->modified)
664 {
665 /*
666 ** refresh has changed
667 */
668 vid_ref->modified = false;
669 vid_fullscreen->modified = true;
670 cl.refresh_prepped = false;
671 cls.disable_screen = true;
672
673 Com_sprintf( name, sizeof(name), "ref_%s.dll", vid_ref->string );
674 if ( !VID_LoadRefresh( name ) )
675 {
676 if ( strcmp (vid_ref->string, "soft") == 0 )
677 Com_Error (ERR_FATAL, "Couldn't fall back to software refresh!");
678 Cvar_Set( "vid_ref", "soft" );
679
680 /*
681 ** drop the console if we fail to load a refresh
682 */
683 if ( cls.key_dest != key_console )
684 {
685 Con_ToggleConsole_f();
686 }
687 }
688 cls.disable_screen = false;
689 }
690
691 /*
692 ** update our window position
693 */
694 if ( vid_xpos->modified || vid_ypos->modified )
695 {
696 if (!vid_fullscreen->value)
697 VID_UpdateWindowPosAndSize( vid_xpos->value, vid_ypos->value );
698
699 vid_xpos->modified = false;
700 vid_ypos->modified = false;
701 }
702 }
703
704 /*
705 ============
706 VID_Init
707 ============
708 */
709 void VID_Init (void)
710 {
711 /* Create the video variables so we know how to start the graphics drivers */
712 vid_ref = Cvar_Get ("vid_ref", "soft", CVAR_ARCHIVE);
713 vid_xpos = Cvar_Get ("vid_xpos", "3", CVAR_ARCHIVE);
714 vid_ypos = Cvar_Get ("vid_ypos", "22", CVAR_ARCHIVE);
715 vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ARCHIVE);
716 vid_gamma = Cvar_Get( "vid_gamma", "1", CVAR_ARCHIVE );
717 win_noalttab = Cvar_Get( "win_noalttab", "0", CVAR_ARCHIVE );
718
719 /* Add some console commands that we want to handle */
720 Cmd_AddCommand ("vid_restart", VID_Restart_f);
721 Cmd_AddCommand ("vid_front", VID_Front_f);
722
723 /*
724 ** this is a gross hack but necessary to clamp the mode for 3Dfx
725 */
726 #if 0
738 #endif
739
740 /* Disable the 3Dfx splash screen */
741 putenv("FX_GLIDE_NO_SPLASH=0");
742
743 /* Start the graphics mode and load refresh DLL */
744 VID_CheckChanges();
745 }
746
747 /*
748 ============
749 VID_Shutdown
750 ============
751 */
752 void VID_Shutdown (void)
753 {
754 if ( reflib_active )
755 {
756 re.Shutdown ();
757 VID_FreeReflib ();
758 }
759 }
760
761
762