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