changed PF_WARNING to not do a return
[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 <windows.h>
24 #include <mmsystem.h>
25 #include <dsound.h>
26 #include "errno.h"
27 #include "resource.h"
28 #include "conproc.h"
29 #include "direct.h"
30
31 cvar_t sys_usetimegettime = {CVAR_SAVE, "sys_usetimegettime", "1", "use windows timeGetTime function (which has issues on some motherboards) for timing rather than QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power)"};
32
33 HANDLE                          hinput, houtput;
34
35 static HANDLE   tevent;
36 static HANDLE   hFile;
37 static HANDLE   heventParent;
38 static HANDLE   heventChild;
39
40
41 /*
42 ===============================================================================
43
44 SYSTEM IO
45
46 ===============================================================================
47 */
48
49 void Sys_Error (const char *error, ...)
50 {
51         va_list         argptr;
52         char            text[MAX_INPUTLINE];
53         static int      in_sys_error0 = 0;
54         static int      in_sys_error1 = 0;
55         static int      in_sys_error2 = 0;
56         static int      in_sys_error3 = 0;
57
58         va_start (argptr, error);
59         dpvsnprintf (text, sizeof (text), error, argptr);
60         va_end (argptr);
61
62         Con_Printf ("Quake Error: %s\n", text);
63
64         // close video so the message box is visible, unless we already tried that
65         if (!in_sys_error0 && cls.state != ca_dedicated)
66         {
67                 in_sys_error0 = 1;
68                 VID_Shutdown();
69         }
70
71         if (!in_sys_error3 && cls.state != ca_dedicated)
72         {
73                 in_sys_error3 = true;
74                 MessageBox(NULL, text, "Quake Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
75         }
76
77         if (!in_sys_error1)
78         {
79                 in_sys_error1 = 1;
80                 Host_Shutdown ();
81         }
82
83 // shut down QHOST hooks if necessary
84         if (!in_sys_error2)
85         {
86                 in_sys_error2 = 1;
87                 Sys_Shutdown ();
88         }
89
90         exit (1);
91 }
92
93 void Sys_Shutdown (void)
94 {
95         if (tevent)
96                 CloseHandle (tevent);
97
98         if (cls.state == ca_dedicated)
99                 FreeConsole ();
100
101 // shut down QHOST hooks if necessary
102         DeinitConProc ();
103 }
104
105 void Sys_PrintToTerminal(const char *text)
106 {
107         DWORD dummy;
108         extern HANDLE houtput;
109         if (cls.state == ca_dedicated)
110                 WriteFile(houtput, text, (DWORD) strlen(text), &dummy, NULL);
111 }
112
113 /*
114 ================
115 Sys_DoubleTime
116 ================
117 */
118 double Sys_DoubleTime (void)
119 {
120         static int first = true;
121         static double oldtime = 0.0, curtime = 0.0;
122         double newtime;
123         // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
124         if (sys_usetimegettime.integer)
125         {
126                 static int firsttimegettime = true;
127                 // timeGetTime
128                 // platform:
129                 // Windows 95/98/ME/NT/2000/XP
130                 // features:
131                 // reasonable accuracy (millisecond)
132                 // issues:
133                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
134
135                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
136                 if (firsttimegettime)
137                 {
138                         timeBeginPeriod (1);
139                         firsttimegettime = false;
140                 }
141
142                 newtime = (double) timeGetTime () / 1000.0;
143         }
144         else
145         {
146                 // QueryPerformanceCounter
147                 // platform:
148                 // Windows 95/98/ME/NT/2000/XP
149                 // features:
150                 // very accurate (CPU cycles)
151                 // known issues:
152                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
153                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
154                 double timescale;
155                 LARGE_INTEGER PerformanceFreq;
156                 LARGE_INTEGER PerformanceCount;
157
158                 if (!QueryPerformanceFrequency (&PerformanceFreq))
159                 {
160                         Con_Printf ("No hardware timer available\n");
161                         // fall back to timeGetTime
162                         Cvar_SetValueQuick(&sys_usetimegettime, true);
163                         return Sys_DoubleTime();
164                 }
165                 QueryPerformanceCounter (&PerformanceCount);
166
167                 #ifdef __BORLANDC__
168                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
169                 newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
170                 #else
171                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
172                 newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
173                 #endif
174         }
175
176         if (first)
177         {
178                 first = false;
179                 oldtime = newtime;
180         }
181
182         if (newtime < oldtime)
183         {
184                 // warn if it's significant
185                 if (newtime - oldtime < -0.01)
186                         Con_Printf("Sys_DoubleTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
187         }
188         else
189                 curtime += newtime - oldtime;
190         oldtime = newtime;
191
192         return curtime;
193 }
194
195
196 char *Sys_ConsoleInput (void)
197 {
198         static char text[MAX_INPUTLINE];
199         static int len;
200         INPUT_RECORD recs[1024];
201         int ch;
202         DWORD numread, numevents, dummy;
203
204         if (cls.state != ca_dedicated)
205                 return NULL;
206
207
208         for ( ;; )
209         {
210                 if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
211                         Sys_Error ("Error getting # of console events");
212
213                 if (numevents <= 0)
214                         break;
215
216                 if (!ReadConsoleInput(hinput, recs, 1, &numread))
217                         Sys_Error ("Error reading console input");
218
219                 if (numread != 1)
220                         Sys_Error ("Couldn't read console input");
221
222                 if (recs[0].EventType == KEY_EVENT)
223                 {
224                         if (!recs[0].Event.KeyEvent.bKeyDown)
225                         {
226                                 ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
227
228                                 switch (ch)
229                                 {
230                                         case '\r':
231                                                 WriteFile(houtput, "\r\n", 2, &dummy, NULL);
232
233                                                 if (len)
234                                                 {
235                                                         text[len] = 0;
236                                                         len = 0;
237                                                         return text;
238                                                 }
239
240                                                 break;
241
242                                         case '\b':
243                                                 WriteFile(houtput, "\b \b", 3, &dummy, NULL);
244                                                 if (len)
245                                                 {
246                                                         len--;
247                                                 }
248                                                 break;
249
250                                         default:
251                                                 if (ch >= ' ')
252                                                 {
253                                                         WriteFile(houtput, &ch, 1, &dummy, NULL);
254                                                         text[len] = ch;
255                                                         len = (len + 1) & 0xff;
256                                                 }
257
258                                                 break;
259
260                                 }
261                         }
262                 }
263         }
264
265         return NULL;
266 }
267
268 void Sys_Sleep(int milliseconds)
269 {
270         if (milliseconds < 1)
271                 milliseconds = 1;
272         Sleep(milliseconds);
273 }
274
275 char *Sys_GetClipboardData (void)
276 {
277         char *data = NULL;
278         char *cliptext;
279
280         if (OpenClipboard (NULL) != 0)
281         {
282                 HANDLE hClipboardData;
283
284                 if ((hClipboardData = GetClipboardData (CF_TEXT)) != 0)
285                 {
286                         if ((cliptext = GlobalLock (hClipboardData)) != 0)
287                         {
288                                 data = Z_Malloc (GlobalSize(hClipboardData)+1);
289                                 strcpy (data, cliptext);
290                                 GlobalUnlock (hClipboardData);
291                         }
292                 }
293                 CloseClipboard ();
294         }
295         return data;
296 }
297
298 void Sys_InitConsole (void)
299 {
300         int t;
301
302         // initialize the windows dedicated server console if needed
303         tevent = CreateEvent(NULL, false, false, NULL);
304
305         if (!tevent)
306                 Sys_Error ("Couldn't create event");
307
308         // LordHavoc: can't check cls.state because it hasn't been initialized yet
309         // if (cls.state == ca_dedicated)
310         if (COM_CheckParm("-dedicated"))
311         {
312                 if (!AllocConsole ())
313                         Sys_Error ("Couldn't create dedicated server console");
314
315                 hinput = GetStdHandle (STD_INPUT_HANDLE);
316                 houtput = GetStdHandle (STD_OUTPUT_HANDLE);
317
318 #ifdef _WIN64
319 #define atoi _atoi64
320 #endif
321         // give QHOST a chance to hook into the console
322                 if ((t = COM_CheckParm ("-HFILE")) > 0)
323                 {
324                         if (t < com_argc)
325                                 hFile = (HANDLE)atoi (com_argv[t+1]);
326                 }
327
328                 if ((t = COM_CheckParm ("-HPARENT")) > 0)
329                 {
330                         if (t < com_argc)
331                                 heventParent = (HANDLE)atoi (com_argv[t+1]);
332                 }
333
334                 if ((t = COM_CheckParm ("-HCHILD")) > 0)
335                 {
336                         if (t < com_argc)
337                                 heventChild = (HANDLE)atoi (com_argv[t+1]);
338                 }
339
340                 InitConProc (hFile, heventParent, heventChild);
341         }
342
343 // because sound is off until we become active
344         S_BlockSound ();
345 }
346
347 void Sys_Init_Commands (void)
348 {
349         Cvar_RegisterVariable(&sys_usetimegettime);
350 }
351
352 /*
353 ==============================================================================
354
355 WINDOWS CRAP
356
357 ==============================================================================
358 */
359
360
361 /*
362 ==================
363 WinMain
364 ==================
365 */
366 HINSTANCE       global_hInstance;
367 const char      *argv[MAX_NUM_ARGVS];
368 char            program_name[MAX_OSPATH];
369
370 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
371 {
372         MEMORYSTATUS lpBuffer;
373
374         /* previous instances do not exist in Win32 */
375         if (hPrevInstance)
376                 return 0;
377
378         global_hInstance = hInstance;
379
380         lpBuffer.dwLength = sizeof(MEMORYSTATUS);
381         GlobalMemoryStatus (&lpBuffer);
382
383         program_name[sizeof(program_name)-1] = 0;
384         GetModuleFileNameA(NULL, program_name, sizeof(program_name) - 1);
385
386         com_argc = 1;
387         com_argv = argv;
388         argv[0] = program_name;
389
390         // FIXME: this tokenizer is rather redundent, call a more general one
391         while (*lpCmdLine && (com_argc < MAX_NUM_ARGVS))
392         {
393                 while (*lpCmdLine && *lpCmdLine <= ' ')
394                         lpCmdLine++;
395
396                 if (!*lpCmdLine)
397                         break;
398
399                 if (*lpCmdLine == '\"')
400                 {
401                         // quoted string
402                         lpCmdLine++;
403                         argv[com_argc] = lpCmdLine;
404                         com_argc++;
405                         while (*lpCmdLine && (*lpCmdLine != '\"'))
406                                 lpCmdLine++;
407                 }
408                 else
409                 {
410                         // unquoted word
411                         argv[com_argc] = lpCmdLine;
412                         com_argc++;
413                         while (*lpCmdLine && *lpCmdLine > ' ')
414                                 lpCmdLine++;
415                 }
416
417                 if (*lpCmdLine)
418                 {
419                         *lpCmdLine = 0;
420                         lpCmdLine++;
421                 }
422         }
423
424         Host_Main();
425
426         /* return success of application */
427         return true;
428 }