1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: sys_win.cpp 4328 2010-09-07 20:05:38Z dj_jl $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #define INITGUID
29 #include "winlocal.h"
30 #include <objbase.h>
31 #include <mmsystem.h>
32 #include "gamedefs.h"
33 #include <signal.h>
34 #include <fcntl.h>
35 #include <io.h>
36 #include <direct.h>
37 #include <sys/stat.h>
38 
39 //	Generate all GUIDs
40 #define DIRECTINPUT_VERSION		0x0800
41 #define DIRECTSOUND_VERSION		0x0900
42 #include <dinput.h>
43 #include <ddraw.h>
44 #include <dsound.h>
45 #include "eax.h"
46 
47 // MACROS ------------------------------------------------------------------
48 
49 #define R_OK	4
50 
51 #define PAUSE_SLEEP		50				// sleep time on pause or minimization
52 #define NOT_FOCUS_SLEEP	20				// sleep time when not focus
53 
54 // TYPES -------------------------------------------------------------------
55 
56 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
57 
58 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
59 
60 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
61 
62 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
63 
64 // PUBLIC DATA DEFINITIONS -------------------------------------------------
65 
66 HWND				hwnd;	//	Needed for all DirectX interfaces
67 HINSTANCE			hInst;	//	Needed for DirectInput
68 VWinMessageHandler*	GCDMsgHandler;
69 
70 // PRIVATE DATA DEFINITIONS ------------------------------------------------
71 
72 static HANDLE			dir_handle;
73 static WIN32_FIND_DATA	dir_buf;
74 static bool				dir_already_got;
75 
76 static double		pfreq;
77 static double		curtime = 0.0;
78 static double		lastcurtime = 0.0;
79 static vuint32		oldtime;
80 static int			lowshift;
81 
82 static HANDLE		tevent;
83 
84 static bool			ActiveApp, Minimized;
85 
86 static VCvarI		win_priority("win_priority", "0", CVAR_Archive);
87 static VCvarI		win_sys_keys("win_sys_keys", "1", CVAR_Archive);
88 
89 // CODE --------------------------------------------------------------------
90 
91 //==========================================================================
92 //
93 //	Sys_FileExists
94 //
95 //==========================================================================
96 
Sys_FileExists(const VStr & filename)97 int Sys_FileExists(const VStr& filename)
98 {
99 #ifdef WIN32
100 	return !_access(*filename, R_OK);
101 #else
102 	return !access(*filename, R_OK);
103 #endif
104 }
105 
106 //==========================================================================
107 //
108 //	Sys_FileTime
109 //
110 //	Returns -1 if not present
111 //
112 //==========================================================================
113 
Sys_FileTime(const VStr & path)114 int	Sys_FileTime(const VStr& path)
115 {
116 	struct	stat	buf;
117 
118 	if (stat(*path, &buf) == -1)
119 		return -1;
120 
121 	return buf.st_mtime;
122 }
123 
124 //==========================================================================
125 //
126 //	Sys_CreateDirectory
127 //
128 //==========================================================================
129 
Sys_CreateDirectory(const VStr & path)130 int Sys_CreateDirectory(const VStr& path)
131 {
132 #ifdef WIN32
133 	return _mkdir(*path);
134 #else
135 	return mkdir(*path);
136 #endif
137 }
138 
139 //==========================================================================
140 //
141 //	Sys_OpenDir
142 //
143 //==========================================================================
144 
Sys_OpenDir(const VStr & dirname)145 int Sys_OpenDir(const VStr& dirname)
146 {
147 	dir_handle = FindFirstFile(va("%s/*.*", *dirname), &dir_buf);
148 	if (dir_handle == INVALID_HANDLE_VALUE)
149 	{
150 		return false;
151 	}
152 	dir_already_got = true;
153 	return true;
154 }
155 
156 //==========================================================================
157 //
158 //	Sys_ReadDir
159 //
160 //==========================================================================
161 
Sys_ReadDir()162 VStr Sys_ReadDir()
163 {
164 	if (!dir_already_got)
165 	{
166 		if (FindNextFile(dir_handle, &dir_buf) != TRUE)
167 		{
168 			return VStr();
169 		}
170 	}
171 	dir_already_got = false;
172 	return dir_buf.cFileName;
173 }
174 
175 //==========================================================================
176 //
177 //	Sys_CloseDir
178 //
179 //==========================================================================
180 
Sys_CloseDir()181 void Sys_CloseDir()
182 {
183     FindClose(dir_handle);
184 }
185 
186 //==========================================================================
187 //
188 //	Sys_DirExists
189 //
190 //==========================================================================
191 
Sys_DirExists(const VStr & path)192 bool Sys_DirExists(const VStr& path)
193 {
194 	struct stat s;
195 
196 	if (stat(*path, &s) == -1)
197 		return false;
198 
199 	return !!(s.st_mode & S_IFDIR);
200 }
201 
202 //**************************************************************************
203 //**
204 //**	TIME
205 //**
206 //**************************************************************************
207 
208 //==========================================================================
209 //
210 //  Sys_Time
211 //
212 //==========================================================================
213 
Sys_Time()214 double Sys_Time()
215 {
216 	static int			sametimecount;
217 	LARGE_INTEGER		PerformanceCount;
218 	vuint32				temp, t2;
219 	double				time;
220 
221 	QueryPerformanceCounter(&PerformanceCount);
222 
223 	temp = ((unsigned int)PerformanceCount.u.LowPart >> lowshift) |
224 		   ((unsigned int)PerformanceCount.u.HighPart << (32 - lowshift));
225 
226 	// check for turnover or backward time
227 	if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000))
228 	{
229 		oldtime = temp;	// so we can't get stuck
230 	}
231 	else
232 	{
233 		t2 = temp - oldtime;
234 
235 		time = (double)t2 * pfreq;
236 		oldtime = temp;
237 
238 		curtime += time;
239 
240 		if (curtime == lastcurtime)
241 		{
242 			sametimecount++;
243 
244 			if (sametimecount > 100000)
245 			{
246 				curtime += 1.0;
247 				sametimecount = 0;
248 			}
249 		}
250 		else
251 		{
252 			sametimecount = 0;
253 		}
254 
255 		lastcurtime = curtime;
256 	}
257 
258 	return curtime;
259 }
260 
261 //==========================================================================
262 //
263 //	Sys_Sleep
264 //
265 //==========================================================================
266 
Sys_Sleep()267 void Sys_Sleep()
268 {
269 	Sleep(1);
270 }
271 
272 //==========================================================================
273 //
274 //  Sys_InitTime
275 //
276 //==========================================================================
277 
Sys_InitTime()278 static void Sys_InitTime()
279 {
280 	LARGE_INTEGER	PerformanceFreq;
281 	LARGE_INTEGER	PerformanceCount;
282 	vuint32			lowpart, highpart;
283 
284 	if (!QueryPerformanceFrequency(&PerformanceFreq))
285 		Sys_Error("No hardware timer available");
286 
287 	// get 32 out of the 64 time bits such that we have around
288 	// 1 microsecond resolution
289 	lowpart = (vuint32)PerformanceFreq.u.LowPart;
290 	highpart = (vuint32)PerformanceFreq.u.HighPart;
291 	lowshift = 0;
292 
293 	while (highpart || (lowpart > 2000000.0))
294 	{
295 		lowshift++;
296 		lowpart >>= 1;
297 		lowpart |= (highpart & 1) << 31;
298 		highpart >>= 1;
299 	}
300 
301 	pfreq = 1.0 / (double)lowpart;
302 
303 	//	Read current time and set old time.
304 	QueryPerformanceCounter(&PerformanceCount);
305 
306 	oldtime = ((vuint32)PerformanceCount.u.LowPart >> lowshift) |
307 			  ((vuint32)PerformanceCount.u.HighPart << (32 - lowshift));
308 
309 	//	Set start time
310 	const char* p = GArgs.CheckValue("-starttime");
311 
312 	if (p)
313 	{
314 		curtime = (double)atof(p);
315 	}
316 	else
317 	{
318 		curtime = 0.0;
319 	}
320 
321 	lastcurtime = curtime;
322 }
323 
324 //==========================================================================
325 //
326 //	Sys_Shutdown
327 //
328 //==========================================================================
329 
Sys_Shutdown()330 void Sys_Shutdown()
331 {
332 	CoUninitialize();
333 	ShowCursor(TRUE);
334 
335 	if (tevent)
336 	{
337 		CloseHandle(tevent);
338 	}
339 }
340 
341 //==========================================================================
342 //
343 // 	Sys_Quit
344 //
345 // 	Shuts down net game, saves defaults, prints the exit text message,
346 // goes to text mode, and exits.
347 //
348 //==========================================================================
349 
Sys_Quit(const char *)350 void Sys_Quit(const char*)
351 {
352 	dprintf("==========================================================================\n");
353 	dprintf("			Shuting down VAVOOM\n");
354 	dprintf("==========================================================================\n");
355 
356     // Shutdown system
357 	Host_Shutdown();
358 
359 	// Exit
360 	SendMessage(hwnd, WM_CLOSE, 0, 0);
361 	exit(0);
362 }
363 
364 //==========================================================================
365 //
366 // 	signal_handler
367 //
368 // 	Shuts down system, on error signal
369 //
370 //==========================================================================
371 
signal_handler(int s)372 static void signal_handler(int s)
373 {
374 	signal(s, SIG_IGN);  // Ignore future instances of this signal.
375 
376 	switch (s)
377 	{
378 	 case SIGINT:	throw VavoomError("Interrupted by User");
379 	 case SIGILL:	throw VavoomError("Illegal Instruction");
380 	 case SIGFPE:	throw VavoomError("Floating Point Exception");
381 	 case SIGSEGV:	throw VavoomError("Segmentation Violation");
382 	 case SIGTERM:	throw VavoomError("Software termination signal from kill");
383 	 case SIGBREAK:	throw VavoomError("Ctrl-Break sequence");
384 	 case SIGABRT:	throw VavoomError("Abnormal termination triggered by abort call");
385 	 default:		throw VavoomError("Terminated by signal");
386 	}
387 }
388 
389 //==========================================================================
390 //
391 //	Sys_ConsoleInput
392 //
393 //==========================================================================
394 
Sys_ConsoleInput()395 char *Sys_ConsoleInput()
396 {
397 //FIXME
398 	return NULL;
399 }
400 
401 //==========================================================================
402 //
403 //	WndProc
404 //
405 //==========================================================================
406 
WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)407 static LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
408 								WPARAM wParam, LPARAM lParam)
409 {
410 	switch (iMsg)
411 	{
412 	 case WM_KEYDOWN:
413 		if (!(lParam & 0x40000000) && wParam == VK_PAUSE)
414 		{
415 			GInput->KeyEvent(K_PAUSE, true);
416 		}
417 		return 0;
418 
419 	 case WM_KEYUP:
420 		if (wParam == VK_PAUSE)
421 		{
422 			GInput->KeyEvent(K_PAUSE, false);
423 		}
424 		return 0;
425 
426 	 case WM_DESTROY:
427 		PostQuitMessage(0);
428 		return 0;
429 
430 	 case MM_MCINOTIFY:
431 		if (GCDMsgHandler)
432 		{
433 			return GCDMsgHandler->OnMessage(hwnd, iMsg, wParam, lParam);
434 		}
435 		break;
436 
437 	case WM_SYSKEYDOWN:
438 		// Pressing Alt+Enter can toggle between fullscreen and windowed.
439 		if (wParam == VK_RETURN && !(lParam & 0x40000000))
440 		{
441 			// TODO: Add something here...
442 		}
443 		// Pressing Alt+F4 quits the program.
444 		if (wParam == VK_F4 && !(lParam & 0x40000000))
445 		{
446 			PostQuitMessage(0);
447 		}
448 		break;
449 
450 	case WM_SYSCOMMAND:
451 		// Check for maximize being hit
452 		switch (wParam & ~0x0F)
453 		{
454 		 case SC_SCREENSAVE:
455 		 case SC_MONITORPOWER:
456 		 case SC_KEYMENU:
457 		 case SC_HOTKEY:
458 			// don't call DefWindowProc() because we don't want to start
459 			// the screen saver fullscreen
460 			return 0;
461 		}
462 		break;
463 
464 	 case WM_ACTIVATE:
465 		ActiveApp = !(LOWORD(wParam) == WA_INACTIVE);
466 		if ((BOOL)HIWORD(wParam))
467 		{
468 			Minimized = true;
469 		}
470 		else
471 		{
472 			Minimized = false;
473 		}
474 		break;
475 
476 	 case WM_KILLFOCUS:
477 		SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
478 		break;
479 
480 	 case WM_SETFOCUS:
481 		if (win_priority)
482 		{
483 			SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
484 		}
485 		else
486 		{
487 			SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
488 		}
489 		break;
490 	}
491 	return DefWindowProc(hwnd, iMsg, wParam, lParam);
492 }
493 
494 //==========================================================================
495 //
496 //	SleepUntilInput
497 //
498 //==========================================================================
499 
SleepUntilInput(int time)500 void SleepUntilInput(int time)
501 {
502 	MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);
503 }
504 
505 //==========================================================================
506 //
507 //	_matherr
508 //
509 //	Borland floating point exception handling
510 //
511 //==========================================================================
512 
513 #ifdef __BORLANDC__
_matherr(struct _exception *)514 extern "C" int _matherr(struct _exception  *)
515 {
516 	return 1;	// Error has been handled.
517 }
518 #endif
519 
520 //==========================================================================
521 //
522 //	WinMain
523 //
524 // 	Main program
525 //
526 //==========================================================================
527 
WinMain(HINSTANCE hInstance,HINSTANCE,PSTR,int iCmdShow)528 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, int iCmdShow)
529 {
530 	WNDCLASSEX	wndclass;
531 	MSG 		msg;
532 	HACCEL		ghAccel;
533 
534 	try
535 	{
536 		GArgs.Init(__argc, __argv);
537 
538 		hInst = hInstance;
539 
540 		//	Create window class
541 		wndclass.cbSize        = sizeof(wndclass);
542 		wndclass.style         = CS_VREDRAW | CS_HREDRAW | CS_OWNDC;
543 		wndclass.lpfnWndProc   = WndProc;
544 		wndclass.cbClsExtra    = 0;
545 		wndclass.cbWndExtra    = 0;
546 		wndclass.hInstance     = hInst;
547 		wndclass.hIcon         = LoadIcon(hInstance, "APPICON");
548 		wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
549 		wndclass.hbrBackground = NULL;
550 		wndclass.lpszMenuName  = NULL;
551 		wndclass.lpszClassName = "VAVOOM";
552 		wndclass.hIconSm       = LoadIcon(hInstance, "APPICON");
553 
554 		if (!RegisterClassEx(&wndclass))
555 		{
556 			MessageBox(NULL, "Failed to register class", "Error", MB_OK);
557 			return 1;
558 		}
559 
560 		//	Create window
561 		hwnd = CreateWindowEx(WS_EX_APPWINDOW, "VAVOOM", "VAVOOM for Windows",
562 			WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 2, 2,
563 			NULL, NULL, hInst, NULL);
564 		if (!hwnd)
565 		{
566 			MessageBox(NULL, "Couldn't create window", "Error", MB_OK);
567 			return 1;
568 		}
569 
570 		// Make the window visible & update its client area
571 		ShowWindow(hwnd, iCmdShow);
572 		UpdateWindow(hwnd);
573 
574 		//	Initialise COM
575 		if (FAILED(CoInitialize(NULL)))
576 		{
577 			MessageBox(hwnd, "Failed to initialise COM", "Error", MB_OK);
578 			return 1;
579 		}
580 
581 		//	Create event
582 		tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
583 		if (!tevent)
584 		{
585 			CoUninitialize();
586 			MessageBox(hwnd, "Couldn't create event", "Error", MB_OK);
587 			return 1;
588 		}
589 
590 		ghAccel = LoadAccelerators(hInst, "AppAccel");
591 
592 		ShowCursor(FALSE);
593 
594 #ifndef _DEBUG
595 		//	Install signal handlers
596 		signal(SIGINT,  signal_handler);
597 		signal(SIGILL,  signal_handler);
598 		signal(SIGFPE,  signal_handler);
599 		signal(SIGSEGV, signal_handler);
600 		signal(SIGTERM, signal_handler);
601 		signal(SIGBREAK,signal_handler);
602 		signal(SIGABRT, signal_handler);
603 #endif
604 
605 		Sys_InitTime();
606 
607 		Host_Init();
608 		while (1)
609 		{
610 			while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
611 			{
612 				if (msg.message == WM_QUIT)
613 				{
614 					dprintf("Quit message\n");
615 					Sys_Quit(NULL);
616 				}
617 				else if (!win_sys_keys && (msg.message == WM_SYSKEYDOWN ||
618 					msg.message == WM_SYSKEYUP))
619 				{
620 				}
621 				else if (!TranslateAccelerator(msg.hwnd, ghAccel, &msg))
622 				{
623 					TranslateMessage(&msg);
624 					DispatchMessage(&msg);
625 				}
626 			}
627 
628 			if (Minimized || !ActiveApp)
629 			{
630 				SleepUntilInput(PAUSE_SLEEP);
631 				continue;
632 			}
633 
634 			Host_Frame();
635 		}
636 	}
637 	catch (VavoomError &e)
638 	{
639 		char *tmp_msg;
640 
641 		Host_Shutdown();
642 
643 		dprintf("\n\nERROR: %s\n", e.message);
644 		tmp_msg = new char[VStr::Length(e.message) +
645 			VStr::Length(Host_GetCoreDump()) + 4];
646 		sprintf(tmp_msg, "%s\n\n%s", e.message, Host_GetCoreDump());
647 		MessageBox(hwnd, tmp_msg, "Error", MB_OK);
648 		delete tmp_msg;
649 		tmp_msg = NULL;
650 
651 		SendMessage(hwnd, WM_CLOSE, 0, 0);
652 		return 1;
653 	}
654 #ifndef _DEBUG
655 	catch (...)
656 	{
657 		char *tmp_msg;
658 
659 		Host_Shutdown();
660 		dprintf("\n\nExiting due to external exception\n");
661 
662 		tmp_msg = new char[VStr::Length(Host_GetCoreDump()) + 32];
663 		sprintf(tmp_msg, "Received external exception\n\n%s", Host_GetCoreDump());
664 		MessageBox(hwnd, tmp_msg, "Error", MB_OK);
665 		delete tmp_msg;
666 		tmp_msg = NULL;
667 
668 //		throw;
669 		SendMessage(hwnd, WM_CLOSE, 0, 0);
670 		return 1;
671 	}
672 #endif
673 }
674