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