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