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