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