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