most of Q2's keyboard handling ported over - what this means: keypad is now separatel...
[divverent/darkplaces.git] / sys_win.c
1 /*
2 Copyright (C) 1996-1997 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 // sys_win.c -- Win32 system interface code
21
22 #include "quakedef.h"
23 #include "winquake.h"
24 #include "errno.h"
25 #include "resource.h"
26 #include "conproc.h"
27 #include "direct.h"
28
29 cvar_t sys_usetimegettime = {CVAR_SAVE, "sys_usetimegettime", "1"};
30
31 // # of seconds to wait on Sys_Error running dedicated before exiting
32 #define CONSOLE_ERROR_TIMEOUT   60.0
33 // sleep time on pause or minimization
34 #define PAUSE_SLEEP             50
35 // sleep time when not focus
36 #define NOT_FOCUS_SLEEP 20
37
38 int                     starttime;
39
40 static qboolean         sc_return_on_enter = false;
41 HANDLE                          hinput, houtput;
42
43 static HANDLE   tevent;
44 static HANDLE   hFile;
45 static HANDLE   heventParent;
46 static HANDLE   heventChild;
47
48
49 /*
50 ===============================================================================
51
52 DLL MANAGEMENT
53
54 ===============================================================================
55 */
56
57 dllhandle_t Sys_LoadLibrary (const char* name)
58 {
59         return LoadLibrary (name);
60 }
61
62 void Sys_UnloadLibrary (dllhandle_t handle)
63 {
64         FreeLibrary (handle);
65 }
66
67 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
68 {
69         return (void *)GetProcAddress (handle, name);
70 }
71
72
73 /*
74 ===============================================================================
75
76 SYSTEM IO
77
78 ===============================================================================
79 */
80
81 void SleepUntilInput (int time);
82
83 void Sys_Error (const char *error, ...)
84 {
85         va_list         argptr;
86         char            text[1024], text2[1024];
87         char            *text3 = "Press Enter to exit\n";
88         char            *text4 = "***********************************\n";
89         char            *text5 = "\n";
90         DWORD           dummy;
91         double          starttime;
92         static int      in_sys_error0 = 0;
93         static int      in_sys_error1 = 0;
94         static int      in_sys_error2 = 0;
95         static int      in_sys_error3 = 0;
96
97         if (!in_sys_error3)
98                 in_sys_error3 = 1;
99
100         va_start (argptr, error);
101         vsprintf (text, error, argptr);
102         va_end (argptr);
103
104         if (cls.state == ca_dedicated)
105         {
106                 va_start (argptr, error);
107                 vsprintf (text, error, argptr);
108                 va_end (argptr);
109
110                 sprintf (text2, "ERROR: %s\n", text);
111                 WriteFile (houtput, text5, strlen (text5), &dummy, NULL);
112                 WriteFile (houtput, text4, strlen (text4), &dummy, NULL);
113                 WriteFile (houtput, text2, strlen (text2), &dummy, NULL);
114                 WriteFile (houtput, text3, strlen (text3), &dummy, NULL);
115                 WriteFile (houtput, text4, strlen (text4), &dummy, NULL);
116
117
118                 starttime = Sys_DoubleTime ();
119                 sc_return_on_enter = true;      // so Enter will get us out of here
120
121                 while (!Sys_ConsoleInput () && ((Sys_DoubleTime () - starttime) < CONSOLE_ERROR_TIMEOUT))
122                         SleepUntilInput(1);
123         }
124         else
125         {
126         // switch to windowed so the message box is visible, unless we already
127         // tried that and failed
128                 if (!in_sys_error0)
129                 {
130                         in_sys_error0 = 1;
131                         VID_Shutdown();
132                         MessageBox(NULL, text, "Quake Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
133                 }
134                 else
135                         MessageBox(NULL, text, "Double Quake Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
136         }
137
138         if (!in_sys_error1)
139         {
140                 in_sys_error1 = 1;
141                 Host_Shutdown ();
142         }
143
144 // shut down QHOST hooks if necessary
145         if (!in_sys_error2)
146         {
147                 in_sys_error2 = 1;
148                 DeinitConProc ();
149         }
150
151         exit (1);
152 }
153
154 void Sys_Quit (void)
155 {
156         Host_Shutdown();
157
158         if (tevent)
159                 CloseHandle (tevent);
160
161         if (cls.state == ca_dedicated)
162                 FreeConsole ();
163
164 // shut down QHOST hooks if necessary
165         DeinitConProc ();
166
167         exit (0);
168 }
169
170
171 /*
172 ================
173 Sys_DoubleTime
174 ================
175 */
176 double Sys_DoubleTime (void)
177 {
178         static int first = true;
179         static double oldtime = 0.0, curtime = 0.0;
180         double newtime;
181         // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
182         if (sys_usetimegettime.integer)
183         {
184                 static int firsttimegettime = true;
185                 // timeGetTime
186                 // platform:
187                 // Windows 95/98/ME/NT/2000/XP
188                 // features:
189                 // reasonable accuracy (millisecond)
190                 // issues:
191                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
192
193                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
194                 if (firsttimegettime)
195                 {
196                         timeBeginPeriod (1);
197                         firsttimegettime = false;
198                 }
199
200                 newtime = (double) timeGetTime () / 1000.0;
201         }
202         else
203         {
204                 // QueryPerformanceCounter
205                 // platform:
206                 // Windows 95/98/ME/NT/2000/XP
207                 // features:
208                 // very accurate (CPU cycles)
209                 // known issues:
210                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
211                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
212                 double timescale;
213                 LARGE_INTEGER PerformanceFreq;
214                 LARGE_INTEGER PerformanceCount;
215
216                 if (!QueryPerformanceFrequency (&PerformanceFreq))
217                         Sys_Error ("No hardware timer available");
218                 QueryPerformanceCounter (&PerformanceCount);
219
220                 #ifdef __BORLANDC__
221                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
222                 newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
223                 #else
224                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
225                 newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
226                 #endif
227         }
228
229         if (first)
230         {
231                 first = false;
232                 oldtime = newtime;
233         }
234
235         if (newtime < oldtime)
236         {
237                 // warn if it's significant
238                 if (newtime - oldtime < -0.01)
239                         Con_Printf("Sys_DoubleTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
240         }
241         else
242                 curtime += newtime - oldtime;
243         oldtime = newtime;
244
245         return curtime;
246 }
247
248
249 char *Sys_ConsoleInput (void)
250 {
251         static char text[256];
252         static int len;
253         INPUT_RECORD recs[1024];
254         int ch;
255         DWORD numread, numevents, dummy;
256
257         if (cls.state != ca_dedicated)
258                 return NULL;
259
260
261         for ( ;; )
262         {
263                 if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
264                         Sys_Error ("Error getting # of console events");
265
266                 if (numevents <= 0)
267                         break;
268
269                 if (!ReadConsoleInput(hinput, recs, 1, &numread))
270                         Sys_Error ("Error reading console input");
271
272                 if (numread != 1)
273                         Sys_Error ("Couldn't read console input");
274
275                 if (recs[0].EventType == KEY_EVENT)
276                 {
277                         if (!recs[0].Event.KeyEvent.bKeyDown)
278                         {
279                                 ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
280
281                                 switch (ch)
282                                 {
283                                         case '\r':
284                                                 WriteFile(houtput, "\r\n", 2, &dummy, NULL);
285
286                                                 if (len)
287                                                 {
288                                                         text[len] = 0;
289                                                         len = 0;
290                                                         return text;
291                                                 }
292                                                 else if (sc_return_on_enter)
293                                                 {
294                                                 // special case to allow exiting from the error handler on Enter
295                                                         text[0] = '\r';
296                                                         len = 0;
297                                                         return text;
298                                                 }
299
300                                                 break;
301
302                                         case '\b':
303                                                 WriteFile(houtput, "\b \b", 3, &dummy, NULL);
304                                                 if (len)
305                                                 {
306                                                         len--;
307                                                 }
308                                                 break;
309
310                                         default:
311                                                 if (ch >= ' ')
312                                                 {
313                                                         WriteFile(houtput, &ch, 1, &dummy, NULL);
314                                                         text[len] = ch;
315                                                         len = (len + 1) & 0xff;
316                                                 }
317
318                                                 break;
319
320                                 }
321                         }
322                 }
323         }
324
325         return NULL;
326 }
327
328 void Sys_Sleep (void)
329 {
330         Sleep (1);
331 }
332
333
334 void Sys_SendKeyEvents (void)
335 {
336         MSG msg;
337
338         while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
339         {
340         // we always update if there are any event, even if we're paused
341                 scr_skipupdate = 0;
342
343                 if (!GetMessage (&msg, NULL, 0, 0))
344                         Sys_Quit ();
345
346                 TranslateMessage (&msg);
347                 DispatchMessage (&msg);
348         }
349 }
350
351
352 /*
353 ==============================================================================
354
355 WINDOWS CRAP
356
357 ==============================================================================
358 */
359
360
361 void SleepUntilInput (int time)
362 {
363         MsgWaitForMultipleObjects(1, &tevent, false, time, QS_ALLINPUT);
364 }
365
366
367 /*
368 ==================
369 WinMain
370 ==================
371 */
372 HINSTANCE       global_hInstance;
373 int                     global_nCmdShow;
374 const char      *argv[MAX_NUM_ARGVS];
375 char            program_name[MAX_OSPATH];
376
377 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
378 {
379         double frameoldtime, framenewtime;
380         MEMORYSTATUS lpBuffer;
381         int t;
382
383         /* previous instances do not exist in Win32 */
384         if (hPrevInstance)
385                 return 0;
386
387         global_hInstance = hInstance;
388         global_nCmdShow = nCmdShow;
389
390         lpBuffer.dwLength = sizeof(MEMORYSTATUS);
391         GlobalMemoryStatus (&lpBuffer);
392
393         com_argc = 1;
394         program_name[sizeof(program_name)-1] = 0;
395         GetModuleFileNameA(NULL, program_name, sizeof(program_name) - 1);
396         argv[0] = program_name;
397
398         while (*lpCmdLine && (com_argc < MAX_NUM_ARGVS))
399         {
400                 while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
401                         lpCmdLine++;
402
403                 if (*lpCmdLine)
404                 {
405                         argv[com_argc] = lpCmdLine;
406                         com_argc++;
407
408                         while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
409                                 lpCmdLine++;
410
411                         if (*lpCmdLine)
412                         {
413                                 *lpCmdLine = 0;
414                                 lpCmdLine++;
415                         }
416                 }
417         }
418         com_argv = argv;
419
420         Sys_Shared_EarlyInit();
421
422         Cvar_RegisterVariable(&sys_usetimegettime);
423
424         tevent = CreateEvent(NULL, false, false, NULL);
425
426         if (!tevent)
427                 Sys_Error ("Couldn't create event");
428
429         // LordHavoc: can't check cls.state because it hasn't been initialized yet
430         // if (cls.state == ca_dedicated)
431         if (COM_CheckParm("-dedicated"))
432         {
433                 if (!AllocConsole ())
434                         Sys_Error ("Couldn't create dedicated server console");
435
436                 hinput = GetStdHandle (STD_INPUT_HANDLE);
437                 houtput = GetStdHandle (STD_OUTPUT_HANDLE);
438
439         // give QHOST a chance to hook into the console
440                 if ((t = COM_CheckParm ("-HFILE")) > 0)
441                 {
442                         if (t < com_argc)
443                                 hFile = (HANDLE)atoi (com_argv[t+1]);
444                 }
445
446                 if ((t = COM_CheckParm ("-HPARENT")) > 0)
447                 {
448                         if (t < com_argc)
449                                 heventParent = (HANDLE)atoi (com_argv[t+1]);
450                 }
451
452                 if ((t = COM_CheckParm ("-HCHILD")) > 0)
453                 {
454                         if (t < com_argc)
455                                 heventChild = (HANDLE)atoi (com_argv[t+1]);
456                 }
457
458                 InitConProc (hFile, heventParent, heventChild);
459         }
460
461 // because sound is off until we become active
462         S_BlockSound ();
463
464         Host_Init ();
465
466         Sys_Shared_LateInit();
467
468         frameoldtime = Sys_DoubleTime ();
469
470         /* main window message loop */
471         while (1)
472         {
473                 if (cls.state != ca_dedicated)
474                 {
475                 // yield the CPU for a little while when paused, minimized, or not the focus
476                         if ((cl.paused && !vid_activewindow) || vid_hidden)
477                         {
478                                 SleepUntilInput (PAUSE_SLEEP);
479                                 scr_skipupdate = 1;             // no point in bothering to draw
480                         }
481                         else if (!vid_activewindow)
482                                 SleepUntilInput (NOT_FOCUS_SLEEP);
483                 }
484
485                 framenewtime = Sys_DoubleTime ();
486                 Host_Frame (framenewtime - frameoldtime);
487                 frameoldtime = framenewtime;
488         }
489
490         /* return success of application */
491         return true;
492 }
493