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 // win_vid.c
22 //
23 
24 #include "../client/cl_local.h"
25 #include "../renderer/r_local.h"
26 #include "win_local.h"
27 #include "win_glimp.h"
28 #include "resource.h"
29 
30 // console variables that we need to access from this module
31 cVar_t		*vid_xpos;			// x coordinate of window position
32 cVar_t		*vid_ypos;			// y coordinate of window position
33 cVar_t		*vid_fullscreen;
34 
35 cVar_t		*win_noalttab;
36 
37 static qBool	sys_altTabDisabled;
38 
39 static qBool	vid_isActive;
40 static qBool	vid_queueRestart;
41 
42 /*
43 ==============================================================================
44 
45 	CONSOLE FUNCTIONS
46 
47 ==============================================================================
48 */
49 
50 /*
51 ============
52 VID_Restart_f
53 ============
54 */
VID_Restart_f(void)55 static void VID_Restart_f (void)
56 {
57 	vid_queueRestart = qTrue;
58 }
59 
60 
61 /*
62 ============
63 VID_Front_f
64 ============
65 */
VID_Front_f(void)66 static void VID_Front_f (void)
67 {
68 	SetWindowLong (sys_winInfo.hWnd, GWL_EXSTYLE, WS_EX_TOPMOST);
69 	SetForegroundWindow (sys_winInfo.hWnd);
70 }
71 
72 /*
73 ==============================================================================
74 
75 	MESSAGE HANDLING
76 
77 ==============================================================================
78 */
79 
80 /*
81 =================
82 WIN_ToggleAltTab
83 =================
84 */
WIN_ToggleAltTab(qBool enable)85 static void WIN_ToggleAltTab (qBool enable)
86 {
87 	if (enable) {
88 		if (!sys_altTabDisabled)
89 			return;
90 
91 		if (sys_winInfo.isWin32) {
92 			BOOL	old;
93 			SystemParametersInfo (SPI_SCREENSAVERRUNNING, 0, &old, 0);
94 		}
95 		else {
96 			UnregisterHotKey (0, 0);
97 			UnregisterHotKey (0, 1);
98 		}
99 
100 		sys_altTabDisabled = qFalse;
101 	}
102 	else {
103 		if (sys_altTabDisabled)
104 			return;
105 
106 		if (sys_winInfo.isWin32) {
107 			BOOL	old;
108 			SystemParametersInfo (SPI_SCREENSAVERRUNNING, 1, &old, 0);
109 		}
110 		else {
111 			RegisterHotKey (0, 0, MOD_ALT, VK_TAB);
112 			RegisterHotKey (0, 1, MOD_ALT, VK_RETURN);
113 		}
114 
115 		sys_altTabDisabled = qTrue;
116 	}
117 }
118 
119 
120 /*
121 ====================
122 VID_AppActivate
123 ====================
124 */
VID_AppActivate(qBool fActive,qBool minimize)125 static void VID_AppActivate (qBool fActive, qBool minimize)
126 {
127 	sys_winInfo.appMinimized = minimize;
128 
129 	Key_ClearStates ();
130 
131 	// we don't want to act like we're active if we're minimized
132 	sys_winInfo.appActive = (fActive && !minimize) ? qTrue : qFalse;
133 
134 	// minimize/restore mouse-capture on demand
135 	IN_Activate (sys_winInfo.appActive);
136 	CDAudio_Activate (sys_winInfo.appActive);
137 	SndImp_Activate (sys_winInfo.appActive);
138 
139 	if (win_noalttab->intVal)
140 		WIN_ToggleAltTab (!sys_winInfo.appActive);
141 
142 	GLimp_AppActivate (sys_winInfo.appActive); // FIXME was fActive, should it be this instead?
143 }
144 
145 
146 /*
147 ============
148 VID_UpdateWindowPosAndSize
149 ============
150 */
VID_UpdateWindowPosAndSize(void)151 static void VID_UpdateWindowPosAndSize (void)
152 {
153 	if (!vid_xpos->modified && !vid_ypos->modified)
154 		return;
155 
156 	if (!ri.config.vidFullScreen) {
157 		RECT	rect;
158 		int		style;
159 		int		w, h;
160 
161 		rect.left	= 0;
162 		rect.top	= 0;
163 		rect.right	= ri.config.vidWidth;
164 		rect.bottom	= ri.config.vidHeight;
165 
166 		style = GetWindowLong (sys_winInfo.hWnd, GWL_STYLE);
167 		AdjustWindowRect (&rect, style, FALSE);
168 
169 		w = rect.right - rect.left;
170 		h = rect.bottom - rect.top;
171 
172 		MoveWindow (sys_winInfo.hWnd, vid_xpos->intVal, vid_ypos->intVal, w, h, qTrue);
173 
174 		ri.def.width = w;
175 		ri.def.height = h;
176 	}
177 
178 	vid_xpos->modified = qFalse;
179 	vid_ypos->modified = qFalse;
180 }
181 
182 
183 /*
184 ====================
185 MainWndProc
186 
187 Main window procedure
188 ====================
189 */
190 #ifndef WM_MOUSEWHEEL
191 # define WM_MOUSEWHEEL		(WM_MOUSELAST+1)	// message that will be supported by the OS
192 #endif // WM_MOUSEWHEEL
193 
194 #ifndef WM_MOUSEHWHEEL
195 # define WM_MOUSEHWHEEL		0x020E
196 #endif // WM_MOUSEHWHEEL
197 
198 #ifndef WM_MOUSEHWHEEL
199 # define WM_MOUSEHWHEEL		0x020E
200 #endif // WM_MOUSEHWHEEL
201 
202 #ifndef WM_XBUTTONDOWN
203 # define WM_XBUTTONDOWN		0x020B
204 # define WM_XBUTTONUP		0x020C
205 #endif // WM_XBUTTONDOWN
206 
207 #ifndef MK_XBUTTON1
208 # define MK_XBUTTON1		0x0020
209 # define MK_XBUTTON2		0x0040
210 #endif // MK_XBUTTON1
211 
212 LRESULT CALLBACK CDAudio_MessageHandler (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
MainWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)213 LRESULT CALLBACK MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
214 {
215 	static UINT MSH_MOUSEWHEEL;
216 	int		state;
217 	int		width;
218 	int		height;
219 
220 	if (uMsg == MSH_MOUSEWHEEL) {
221 		// for Win95
222 		if (((int) wParam) > 0) {
223 			Key_Event (K_MWHEELUP, qTrue, sys_winInfo.msgTime);
224 			Key_Event (K_MWHEELUP, qFalse, sys_winInfo.msgTime);
225 		}
226 		else {
227 			Key_Event (K_MWHEELDOWN, qTrue, sys_winInfo.msgTime);
228 			Key_Event (K_MWHEELDOWN, qFalse, sys_winInfo.msgTime);
229 		}
230 		goto end;
231 	}
232 
233 	switch (uMsg) {
234 	case WM_MOUSEWHEEL:
235 		// this chunk of code theoretically only works under NT4 and Win98
236 		// since this message doesn't exist under Win95
237 		if ((int16)HIWORD (wParam) > 0) {
238 			Key_Event (K_MWHEELUP, qTrue, sys_winInfo.msgTime);
239 			Key_Event (K_MWHEELUP, qFalse, sys_winInfo.msgTime);
240 		}
241 		else {
242 			Key_Event (K_MWHEELDOWN, qTrue, sys_winInfo.msgTime);
243 			Key_Event (K_MWHEELDOWN, qFalse, sys_winInfo.msgTime);
244 		}
245 		goto end;
246 
247 	case WM_MOUSEHWHEEL:
248 		if ((int16)HIWORD (wParam) > 0) {
249 			Key_Event (K_MWHEELRIGHT, qTrue, sys_winInfo.msgTime);
250 			Key_Event (K_MWHEELRIGHT, qFalse, sys_winInfo.msgTime);
251 		}
252 		else {
253 			Key_Event (K_MWHEELLEFT, qTrue, sys_winInfo.msgTime);
254 			Key_Event (K_MWHEELLEFT, qFalse, sys_winInfo.msgTime);
255 		}
256 		Com_Printf (0, "WM_MOUSE H WHEEL\n");
257 		goto end;
258 
259 	case WM_HOTKEY:
260 		return 0;
261 
262 	case WM_CREATE:
263 		MSH_MOUSEWHEEL = RegisterWindowMessage ("MSWHEEL_ROLLMSG");
264 		goto end;
265 
266 	case WM_PAINT:
267 		// force entire screen to update next frame
268 		SCR_UpdateScreen ();
269 		goto end;
270 
271 	case WM_DESTROY:
272 		// let sound and input know about this?
273 		sys_winInfo.hWnd = NULL;
274 		goto end;
275 
276 	case WM_ACTIVATE:
277 		// KJB: Watch this for problems in fullscreen modes with Alt-tabbing.
278 		VID_AppActivate ((qBool)(LOWORD(wParam) != WA_INACTIVE), (qBool)HIWORD (wParam));
279 		goto end;
280 
281 	case WM_MOVE:
282 		if (!ri.config.vidFullScreen) {
283 			int		xPos, yPos;
284 			RECT	r;
285 			int		style;
286 
287 			xPos = (int16) LOWORD (lParam);		// horizontal position
288 			yPos = (int16) HIWORD (lParam);		// vertical position
289 
290 			r.left		= 0;
291 			r.top		= 0;
292 			r.right		= 1;
293 			r.bottom	= 1;
294 
295 			style = GetWindowLong (hWnd, GWL_STYLE);
296 			AdjustWindowRect (&r, style, FALSE);
297 
298 			Cvar_VariableSetValue (vid_xpos, xPos + r.left, qTrue);
299 			Cvar_VariableSetValue (vid_ypos, yPos + r.top, qTrue);
300 			vid_xpos->modified = qFalse;
301 			vid_ypos->modified = qFalse;
302 
303 			if (sys_winInfo.appActive)
304 				IN_Activate (qTrue);
305 		}
306 		goto end;
307 
308 	// this is complicated because Win32 seems to pack multiple mouse events into
309 	// one update sometimes, so we always check all states and look for events
310 	case WM_LBUTTONDOWN:
311 	case WM_LBUTTONUP:
312 	case WM_RBUTTONDOWN:
313 	case WM_RBUTTONUP:
314 	case WM_MBUTTONDOWN:
315 	case WM_MBUTTONUP:
316 	case WM_MOUSEMOVE:
317 	case WM_XBUTTONDOWN:
318 	case WM_XBUTTONUP:
319 		state = 0;
320 
321 		if (wParam & MK_LBUTTON)	state |= 1;
322 		if (wParam & MK_RBUTTON)	state |= 2;
323 		if (wParam & MK_MBUTTON)	state |= 4;
324 		if (wParam & MK_XBUTTON1)	state |= 8;
325 		if (wParam & MK_XBUTTON2)	state |= 16;
326 
327 		IN_MouseEvent (state);
328 
329 		goto end;
330 
331 	case WM_SYSCOMMAND:
332 		switch (wParam) {
333 		case SC_MONITORPOWER:
334 		case SC_SCREENSAVE:
335 			return 0;
336 
337 		case SC_CLOSE:
338 			Cbuf_AddText ("quit\n");
339 			return 0;
340 		}
341 		goto end;
342 
343 	case WM_SYSKEYDOWN:
344 		if (wParam == 13) {
345 			Cvar_VariableSetValue (vid_fullscreen, !vid_fullscreen->intVal, qTrue);
346 			VID_Restart_f ();
347 			return 0;
348 		}
349 	// fall through
350 	case WM_KEYDOWN:
351 		Key_Event (In_MapKey (wParam, lParam), qTrue, sys_winInfo.msgTime);
352 		break;
353 
354 	case WM_SYSKEYUP:
355 	case WM_KEYUP:
356 		Key_Event (In_MapKey (wParam, lParam), qFalse, sys_winInfo.msgTime);
357 		break;
358 
359 	case WM_CLOSE:
360 		Cbuf_AddText ("quit\n");
361 		return 0;
362 
363 	case WM_SIZE:
364 		width = LOWORD (lParam);
365 		height = HIWORD (lParam);
366 
367 		if (width > 0 && height > 0) {
368 			// Force a window size update, so that the window dimensions cannot be float.
369 			ri.config.vidWidth  = width;
370 			ri.config.vidHeight = height;
371 
372 			vid_xpos->modified = qTrue;
373 			vid_ypos->modified = qTrue;
374 
375 			VID_UpdateWindowPosAndSize ();
376 
377 			// Update the subsystems
378 			CL_SetRefConfig ();
379 		}
380 		goto end;
381 
382 	case MM_MCINOTIFY:
383 		CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
384 		goto end;
385 	}
386 
387 	// FIXME: wtf?
388 	// - has something to do with window style and the minimize/maximize/exit
389 	// buttons I think, the key check is just fucking nasty though.
390 
391 	// pass all unhandled messages to DefWindowProc
392 	// return 0 if handled message, 1 if not
393 	state = In_MapKey (wParam, lParam);
394 	if (state == K_ALT || state == K_F10)
395 		return 0;
396 
397 end:
398 	return DefWindowProc (hWnd, uMsg, wParam, lParam);
399 }
400 
401 /*
402 ==============================================================================
403 
404 	WINDOW SETUP
405 
406 ==============================================================================
407 */
408 
409 /*
410 ============
411 VID_CheckChanges
412 
413 This function gets called once just before drawing each frame, and it's sole purpose in life
414 is to check to see if any of the video mode parameters have changed, and if they have to
415 update the video modes to match.
416 ============
417 */
VID_CheckChanges(refConfig_t * outConfig)418 void VID_CheckChanges (refConfig_t *outConfig)
419 {
420 	static HWND		oldhWnd = 0;
421 	int				errNum;
422 
423 	if (win_noalttab->modified) {
424 		if (win_noalttab->intVal)
425 			WIN_ToggleAltTab (qFalse);
426 		else
427 			WIN_ToggleAltTab (qTrue);
428 
429 		win_noalttab->modified = qFalse;
430 	}
431 
432 	while (vid_queueRestart) {
433 		qBool cgWasActive = cls.mapLoaded;
434 
435 		CL_MediaShutdown ();
436 
437 		// Refresh has changed
438 		vid_queueRestart = qFalse;
439 		cls.refreshPrepped = qFalse;
440 		cls.disableScreen = qTrue;
441 
442 		// Kill if already active
443 		if (vid_isActive) {
444 			R_Shutdown (qFalse);
445 			vid_isActive = qFalse;
446 		}
447 
448 		// Initialize renderer
449 		if ((errNum = R_Init ()) != R_INIT_SUCCESS) {
450 			R_Shutdown (qTrue);
451 			vid_isActive = qFalse;
452 
453 			switch (errNum) {
454 			case R_INIT_QGL_FAIL:
455 				Com_Error (ERR_FATAL, "Couldn't initialize OpenGL!\n" "QGL library failure!");
456 				break;
457 
458 			case R_INIT_OS_FAIL:
459 				Com_Error (ERR_FATAL, "Couldn't initialize OpenGL!\n" "Incorrect operating system!");
460 				break;
461 
462 			case R_INIT_MODE_FAIL:
463 				Com_Error (ERR_FATAL, "Couldn't initialize OpenGL!\n" "Couldn't set video mode!");
464 				break;
465 			}
466 		}
467 
468 		CL_SetRefConfig ();
469 		R_GetRefConfig (outConfig);
470 
471 		Snd_Init ();
472 		CL_MediaInit ();
473 
474 		// R1: Restart our input devices as the window handle most likely changed
475 		if (oldhWnd && sys_winInfo.hWnd != oldhWnd)
476 			IN_Restart_f ();
477 		oldhWnd = sys_winInfo.hWnd;
478 
479 		cls.disableScreen = qFalse;
480 
481 		CL_ConsoleClose ();
482 
483 		// This is to stop cgame from initializing on first load
484 		// and so it will load after a vid_restart while connected somewhere
485 		if (cgWasActive) {
486 			CL_CGModule_LoadMap ();
487 			Key_SetDest (KD_GAME);
488 		}
489 		else if (Com_ClientState() < CA_CONNECTED) {
490 			CL_CGModule_MainMenu ();
491 		}
492 
493 		vid_isActive = qTrue;
494 	}
495 
496 	// Update our window position
497 	VID_UpdateWindowPosAndSize ();
498 }
499 
500 /*
501 ==============================================================================
502 
503 	INIT / SHUTDOWN
504 
505 ==============================================================================
506 */
507 
508 /*
509 ============
510 VID_Init
511 ============
512 */
VID_Init(refConfig_t * outConfig)513 void VID_Init (refConfig_t *outConfig)
514 {
515 	// Create the video variables so we know how to start the graphics drivers
516 	vid_xpos		= Cvar_Register ("vid_xpos",			"3",	CVAR_ARCHIVE);
517 	vid_ypos		= Cvar_Register ("vid_ypos",			"22",	CVAR_ARCHIVE);
518 	vid_fullscreen	= Cvar_Register ("vid_fullscreen",		"0",	CVAR_ARCHIVE|CVAR_LATCH_VIDEO);
519 
520 	win_noalttab	= Cvar_Register ("win_noalttab",		"0",	CVAR_ARCHIVE);
521 
522 	// Add some console commands that we want to handle
523 	Cmd_AddCommand ("vid_restart",	VID_Restart_f,		"Restarts refresh and media");
524 	Cmd_AddCommand ("vid_front",	VID_Front_f,		"");
525 
526 	// Disable the 3Dfx splash screen
527 	putenv ("FX_GLIDE_NO_SPLASH=0");
528 
529 	// Start the graphics mode
530 	vid_queueRestart = qTrue;
531 	vid_isActive = qFalse;
532 	VID_CheckChanges (outConfig);
533 }
534 
535 
536 /*
537 ============
538 VID_Shutdown
539 ============
540 */
VID_Shutdown(void)541 void VID_Shutdown (void)
542 {
543 	if (vid_isActive) {
544 		R_Shutdown (qTrue);
545 		vid_isActive = qFalse;
546 	}
547 }
548