]> icculus.org git repositories - divverent/darkplaces.git/blob - sys_win.c
fixed Sys_Error popups in -dedicated console setup code so that they
[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 #ifdef SUPPORTDIRECTX
26 #include <dsound.h>
27 #endif
28 #include "errno.h"
29 #include "resource.h"
30 #include "conproc.h"
31 #include "direct.h"
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
112         if ((houtput != 0) && (houtput != INVALID_HANDLE_VALUE))
113                 WriteFile(houtput, text, (DWORD) strlen(text), &dummy, NULL);
114 }
115
116 /*
117 ================
118 Sys_DoubleTime
119 ================
120 */
121 double Sys_DoubleTime (void)
122 {
123         static int first = true;
124         static double oldtime = 0.0, curtime = 0.0;
125         double newtime;
126         // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
127         if (sys_usetimegettime.integer)
128         {
129                 static int firsttimegettime = true;
130                 // timeGetTime
131                 // platform:
132                 // Windows 95/98/ME/NT/2000/XP
133                 // features:
134                 // reasonable accuracy (millisecond)
135                 // issues:
136                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
137
138                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
139                 if (firsttimegettime)
140                 {
141                         timeBeginPeriod (1);
142                         firsttimegettime = false;
143                 }
144
145                 newtime = (double) timeGetTime () / 1000.0;
146         }
147         else
148         {
149                 // QueryPerformanceCounter
150                 // platform:
151                 // Windows 95/98/ME/NT/2000/XP
152                 // features:
153                 // very accurate (CPU cycles)
154                 // known issues:
155                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
156                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
157                 double timescale;
158                 LARGE_INTEGER PerformanceFreq;
159                 LARGE_INTEGER PerformanceCount;
160
161                 if (!QueryPerformanceFrequency (&PerformanceFreq))
162                 {
163                         Con_Printf ("No hardware timer available\n");
164                         // fall back to timeGetTime
165                         Cvar_SetValueQuick(&sys_usetimegettime, true);
166                         return Sys_DoubleTime();
167                 }
168                 QueryPerformanceCounter (&PerformanceCount);
169
170                 #ifdef __BORLANDC__
171                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
172                 newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
173                 #else
174                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
175                 newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
176                 #endif
177         }
178
179         if (first)
180         {
181                 first = false;
182                 oldtime = newtime;
183         }
184
185         if (newtime < oldtime)
186         {
187                 // warn if it's significant
188                 if (newtime - oldtime < -0.01)
189                         Con_Printf("Sys_DoubleTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
190         }
191         else
192                 curtime += newtime - oldtime;
193         oldtime = newtime;
194
195         return curtime;
196 }
197
198
199 char *Sys_ConsoleInput (void)
200 {
201         static char text[MAX_INPUTLINE];
202         static int len;
203         INPUT_RECORD recs[1024];
204         int ch;
205         DWORD numread, numevents, dummy;
206
207         if (cls.state != ca_dedicated)
208                 return NULL;
209
210         for ( ;; )
211         {
212                 if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
213                 {
214                         cls.state = ca_disconnected;
215                         Sys_Error ("Error getting # of console events (error code %x)", (unsigned int)GetLastError());
216                 }
217
218                 if (numevents <= 0)
219                         break;
220
221                 if (!ReadConsoleInput(hinput, recs, 1, &numread))
222                 {
223                         cls.state = ca_disconnected;
224                         Sys_Error ("Error reading console input (error code %x)", (unsigned int)GetLastError());
225                 }
226
227                 if (numread != 1)
228                 {
229                         cls.state = ca_disconnected;
230                         Sys_Error ("Couldn't read console input (error code %x)", (unsigned int)GetLastError());
231                 }
232
233                 if (recs[0].EventType == KEY_EVENT)
234                 {
235                         if (!recs[0].Event.KeyEvent.bKeyDown)
236                         {
237                                 ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
238
239                                 switch (ch)
240                                 {
241                                         case '\r':
242                                                 WriteFile(houtput, "\r\n", 2, &dummy, NULL);
243
244                                                 if (len)
245                                                 {
246                                                         text[len] = 0;
247                                                         len = 0;
248                                                         return text;
249                                                 }
250
251                                                 break;
252
253                                         case '\b':
254                                                 WriteFile(houtput, "\b \b", 3, &dummy, NULL);
255                                                 if (len)
256                                                 {
257                                                         len--;
258                                                 }
259                                                 break;
260
261                                         default:
262                                                 if (ch >= ' ')
263                                                 {
264                                                         WriteFile(houtput, &ch, 1, &dummy, NULL);
265                                                         text[len] = ch;
266                                                         len = (len + 1) & 0xff;
267                                                 }
268
269                                                 break;
270
271                                 }
272                         }
273                 }
274         }
275
276         return NULL;
277 }
278
279 void Sys_Sleep(int microseconds)
280 {
281         Sleep(microseconds / 1000);
282 }
283
284 char *Sys_GetClipboardData (void)
285 {
286         char *data = NULL;
287         char *cliptext;
288
289         if (OpenClipboard (NULL) != 0)
290         {
291                 HANDLE hClipboardData;
292
293                 if ((hClipboardData = GetClipboardData (CF_TEXT)) != 0)
294                 {
295                         if ((cliptext = GlobalLock (hClipboardData)) != 0)
296                         {
297                                 size_t allocsize;
298                                 allocsize = GlobalSize (hClipboardData) + 1;
299                                 data = Z_Malloc (allocsize);
300                                 strlcpy (data, cliptext, allocsize);
301                                 GlobalUnlock (hClipboardData);
302                         }
303                 }
304                 CloseClipboard ();
305         }
306         return data;
307 }
308
309 void Sys_InitConsole (void)
310 {
311         int t;
312
313         // initialize the windows dedicated server console if needed
314         tevent = CreateEvent(NULL, false, false, NULL);
315
316         if (!tevent)
317                 Sys_Error ("Couldn't create event");
318
319         houtput = GetStdHandle (STD_OUTPUT_HANDLE);
320         hinput = GetStdHandle (STD_INPUT_HANDLE);
321
322         // LordHavoc: can't check cls.state because it hasn't been initialized yet
323         // if (cls.state == ca_dedicated)
324         if (COM_CheckParm("-dedicated"))
325         {
326                 //if ((houtput == 0) || (houtput == INVALID_HANDLE_VALUE)) // LordHavoc: on Windows XP this is never 0 or invalid, but hinput is invalid
327                 {
328                         if (!AllocConsole ())
329                                 Sys_Error ("Couldn't create dedicated server console (error code %x)", (unsigned int)GetLastError());
330                         houtput = GetStdHandle (STD_OUTPUT_HANDLE);
331                         hinput = GetStdHandle (STD_INPUT_HANDLE);
332                 }
333                 if ((houtput == 0) || (houtput == INVALID_HANDLE_VALUE))
334                         Sys_Error ("Couldn't create dedicated server console");
335
336
337 #ifdef _WIN64
338 #define atoi _atoi64
339 #endif
340         // give QHOST a chance to hook into the console
341                 if ((t = COM_CheckParm ("-HFILE")) > 0)
342                 {
343                         if (t < com_argc)
344                                 hFile = (HANDLE)atoi (com_argv[t+1]);
345                 }
346
347                 if ((t = COM_CheckParm ("-HPARENT")) > 0)
348                 {
349                         if (t < com_argc)
350                                 heventParent = (HANDLE)atoi (com_argv[t+1]);
351                 }
352
353                 if ((t = COM_CheckParm ("-HCHILD")) > 0)
354                 {
355                         if (t < com_argc)
356                                 heventChild = (HANDLE)atoi (com_argv[t+1]);
357                 }
358
359                 InitConProc (hFile, heventParent, heventChild);
360         }
361
362 // because sound is off until we become active
363         S_BlockSound ();
364 }
365
366 void Sys_Init_Commands (void)
367 {
368         Cvar_RegisterVariable(&sys_usetimegettime);
369 }
370
371 /*
372 ==============================================================================
373
374 WINDOWS CRAP
375
376 ==============================================================================
377 */
378
379
380 /*
381 ==================
382 WinMain
383 ==================
384 */
385 HINSTANCE       global_hInstance;
386 const char      *argv[MAX_NUM_ARGVS];
387 char            program_name[MAX_OSPATH];
388
389 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
390 {
391         MEMORYSTATUS lpBuffer;
392
393         /* previous instances do not exist in Win32 */
394         if (hPrevInstance)
395                 return 0;
396
397         global_hInstance = hInstance;
398
399         lpBuffer.dwLength = sizeof(MEMORYSTATUS);
400         GlobalMemoryStatus (&lpBuffer);
401
402         program_name[sizeof(program_name)-1] = 0;
403         GetModuleFileNameA(NULL, program_name, sizeof(program_name) - 1);
404
405         com_argc = 1;
406         com_argv = argv;
407         argv[0] = program_name;
408
409         // FIXME: this tokenizer is rather redundent, call a more general one
410         while (*lpCmdLine && (com_argc < MAX_NUM_ARGVS))
411         {
412                 while (*lpCmdLine && *lpCmdLine <= ' ')
413                         lpCmdLine++;
414
415                 if (!*lpCmdLine)
416                         break;
417
418                 if (*lpCmdLine == '\"')
419                 {
420                         // quoted string
421                         lpCmdLine++;
422                         argv[com_argc] = lpCmdLine;
423                         com_argc++;
424                         while (*lpCmdLine && (*lpCmdLine != '\"'))
425                                 lpCmdLine++;
426                 }
427                 else
428                 {
429                         // unquoted word
430                         argv[com_argc] = lpCmdLine;
431                         com_argc++;
432                         while (*lpCmdLine && *lpCmdLine > ' ')
433                                 lpCmdLine++;
434                 }
435
436                 if (*lpCmdLine)
437                 {
438                         *lpCmdLine = 0;
439                         lpCmdLine++;
440                 }
441         }
442
443         Host_Main();
444
445         /* return success of application */
446         return true;
447 }
448
449 int main (int argc, const char* argv[])
450 {
451         MEMORYSTATUS lpBuffer;
452
453         global_hInstance = GetModuleHandle (0);
454
455         lpBuffer.dwLength = sizeof(MEMORYSTATUS);
456         GlobalMemoryStatus (&lpBuffer);
457
458         program_name[sizeof(program_name)-1] = 0;
459         GetModuleFileNameA(NULL, program_name, sizeof(program_name) - 1);
460
461         com_argc = argc;
462         com_argv = argv;
463
464         Host_Main();
465
466         return true;
467 }