Home | History | Annotate | Download | only in client
      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.h
     21 
     22 #include "quakedef.h"
     23 #include "winquake.h"
     24 #include "resource.h"
     25 #include "errno.h"
     26 #include "fcntl.h"
     27 #include <limits.h>
     28 
     29 #define MINIMUM_WIN_MEMORY	0x0c00000
     30 #define MAXIMUM_WIN_MEMORY	0x1000000
     31 
     32 #define PAUSE_SLEEP		50				// sleep time on pause or minimization
     33 #define NOT_FOCUS_SLEEP	20				// sleep time when not focus
     34 
     35 int		starttime;
     36 qboolean ActiveApp, Minimized;
     37 qboolean	WinNT;
     38 
     39 HWND	hwnd_dialog;		// startup dialog box
     40 
     41 static double		pfreq;
     42 static double		curtime = 0.0;
     43 static double		lastcurtime = 0.0;
     44 static int			lowshift;
     45 static HANDLE		hinput, houtput;
     46 
     47 HANDLE		qwclsemaphore;
     48 
     49 static HANDLE	tevent;
     50 
     51 void Sys_InitFloatTime (void);
     52 
     53 void MaskExceptions (void);
     54 void Sys_PopFPCW (void);
     55 void Sys_PushFPCW_SetHigh (void);
     56 
     57 void Sys_DebugLog(char *file, char *fmt, ...)
     58 {
     59     va_list argptr;
     60     static char data[1024];
     61     int fd;
     62 
     63     va_start(argptr, fmt);
     64     vsprintf(data, fmt, argptr);
     65     va_end(argptr);
     66     fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);
     67     write(fd, data, strlen(data));
     68     close(fd);
     69 };
     70 
     71 /*
     72 ===============================================================================
     73 
     74 FILE IO
     75 
     76 ===============================================================================
     77 */
     78 
     79 /*
     80 ================
     81 filelength
     82 ================
     83 */
     84 int filelength (FILE *f)
     85 {
     86 	int		pos;
     87 	int		end;
     88 
     89 	pos = ftell (f);
     90 	fseek (f, 0, SEEK_END);
     91 	end = ftell (f);
     92 	fseek (f, pos, SEEK_SET);
     93 
     94 	return end;
     95 }
     96 
     97 
     98 int	Sys_FileTime (char *path)
     99 {
    100 	FILE	*f;
    101 	int		t, retval;
    102 
    103 	t = VID_ForceUnlockedAndReturnState ();
    104 
    105 	f = fopen(path, "rb");
    106 
    107 	if (f)
    108 	{
    109 		fclose(f);
    110 		retval = 1;
    111 	}
    112 	else
    113 	{
    114 		retval = -1;
    115 	}
    116 
    117 	VID_ForceLockState (t);
    118 	return retval;
    119 }
    120 
    121 void Sys_mkdir (char *path)
    122 {
    123 	_mkdir (path);
    124 }
    125 
    126 
    127 /*
    128 ===============================================================================
    129 
    130 SYSTEM IO
    131 
    132 ===============================================================================
    133 */
    134 
    135 /*
    136 ================
    137 Sys_MakeCodeWriteable
    138 ================
    139 */
    140 void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length)
    141 {
    142 	DWORD  flOldProtect;
    143 
    144 //@@@ copy on write or just read-write?
    145 	if (!VirtualProtect((LPVOID)startaddr, length, PAGE_READWRITE, &flOldProtect))
    146    		Sys_Error("Protection change failed\n");
    147 }
    148 
    149 
    150 /*
    151 ================
    152 Sys_Init
    153 ================
    154 */
    155 void Sys_Init (void)
    156 {
    157 	LARGE_INTEGER	PerformanceFreq;
    158 	unsigned int	lowpart, highpart;
    159 	OSVERSIONINFO	vinfo;
    160 
    161 #ifndef SERVERONLY
    162 	// allocate a named semaphore on the client so the
    163 	// front end can tell if it is alive
    164 
    165 	// mutex will fail if semephore allready exists
    166     qwclsemaphore = CreateMutex(
    167         NULL,         /* Security attributes */
    168         0,            /* owner       */
    169         "qwcl"); /* Semaphore name      */
    170 	if (!qwclsemaphore)
    171 		Sys_Error ("QWCL is already running on this system");
    172 	CloseHandle (qwclsemaphore);
    173 
    174     qwclsemaphore = CreateSemaphore(
    175         NULL,         /* Security attributes */
    176         0,            /* Initial count       */
    177         1,            /* Maximum count       */
    178         "qwcl"); /* Semaphore name      */
    179 #endif
    180 
    181 	MaskExceptions ();
    182 	Sys_SetFPCW ();
    183 
    184 #if 0
    185 	if (!QueryPerformanceFrequency (&PerformanceFreq))
    186 		Sys_Error ("No hardware timer available");
    187 
    188 // get 32 out of the 64 time bits such that we have around
    189 // 1 microsecond resolution
    190 	lowpart = (unsigned int)PerformanceFreq.LowPart;
    191 	highpart = (unsigned int)PerformanceFreq.HighPart;
    192 	lowshift = 0;
    193 
    194 	while (highpart || (lowpart > 2000000.0))
    195 	{
    196 		lowshift++;
    197 		lowpart >>= 1;
    198 		lowpart |= (highpart & 1) << 31;
    199 		highpart >>= 1;
    200 	}
    201 
    202 	pfreq = 1.0 / (double)lowpart;
    203 
    204 	Sys_InitFloatTime ();
    205 #endif
    206 
    207 	// make sure the timer is high precision, otherwise
    208 	// NT gets 18ms resolution
    209 	timeBeginPeriod( 1 );
    210 
    211 	vinfo.dwOSVersionInfoSize = sizeof(vinfo);
    212 
    213 	if (!GetVersionEx (&vinfo))
    214 		Sys_Error ("Couldn't get OS info");
    215 
    216 	if ((vinfo.dwMajorVersion < 4) ||
    217 		(vinfo.dwPlatformId == VER_PLATFORM_WIN32s))
    218 	{
    219 		Sys_Error ("QuakeWorld requires at least Win95 or NT 4.0");
    220 	}
    221 
    222 	if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
    223 		WinNT = true;
    224 	else
    225 		WinNT = false;
    226 }
    227 
    228 
    229 void Sys_Error (char *error, ...)
    230 {
    231 	va_list		argptr;
    232 	char		text[1024], text2[1024];
    233 	DWORD		dummy;
    234 
    235 	Host_Shutdown ();
    236 
    237 	va_start (argptr, error);
    238 	vsprintf (text, error, argptr);
    239 	va_end (argptr);
    240 
    241 	MessageBox(NULL, text, "Error", 0 /* MB_OK */ );
    242 
    243 #ifndef SERVERONLY
    244 	CloseHandle (qwclsemaphore);
    245 #endif
    246 
    247 	exit (1);
    248 }
    249 
    250 void Sys_Printf (char *fmt, ...)
    251 {
    252 	va_list		argptr;
    253 	char		text[1024];
    254 	DWORD		dummy;
    255 
    256 	va_start (argptr,fmt);
    257 	vprintf (fmt, argptr);
    258 	va_end (argptr);
    259 }
    260 
    261 void Sys_Quit (void)
    262 {
    263 	VID_ForceUnlockedAndReturnState ();
    264 
    265 	Host_Shutdown();
    266 #ifndef SERVERONLY
    267 	if (tevent)
    268 		CloseHandle (tevent);
    269 
    270 	if (qwclsemaphore)
    271 		CloseHandle (qwclsemaphore);
    272 #endif
    273 
    274 	exit (0);
    275 }
    276 
    277 
    278 #if 0
    279 /*
    280 ================
    281 Sys_DoubleTime
    282 ================
    283 */
    284 double Sys_DoubleTime (void)
    285 {
    286 	static int			sametimecount;
    287 	static unsigned int	oldtime;
    288 	static int			first = 1;
    289 	LARGE_INTEGER		PerformanceCount;
    290 	unsigned int		temp, t2;
    291 	double				time;
    292 
    293 	Sys_PushFPCW_SetHigh ();
    294 
    295 	QueryPerformanceCounter (&PerformanceCount);
    296 
    297 	temp = ((unsigned int)PerformanceCount.LowPart >> lowshift) |
    298 		   ((unsigned int)PerformanceCount.HighPart << (32 - lowshift));
    299 
    300 	if (first)
    301 	{
    302 		oldtime = temp;
    303 		first = 0;
    304 	}
    305 	else
    306 	{
    307 	// check for turnover or backward time
    308 		if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000))
    309 		{
    310 			oldtime = temp;	// so we can't get stuck
    311 		}
    312 		else
    313 		{
    314 			t2 = temp - oldtime;
    315 
    316 			time = (double)t2 * pfreq;
    317 			oldtime = temp;
    318 
    319 			curtime += time;
    320 
    321 			if (curtime == lastcurtime)
    322 			{
    323 				sametimecount++;
    324 
    325 				if (sametimecount > 100000)
    326 				{
    327 					curtime += 1.0;
    328 					sametimecount = 0;
    329 				}
    330 			}
    331 			else
    332 			{
    333 				sametimecount = 0;
    334 			}
    335 
    336 			lastcurtime = curtime;
    337 		}
    338 	}
    339 
    340 	Sys_PopFPCW ();
    341 
    342     return curtime;
    343 }
    344 
    345 /*
    346 ================
    347 Sys_InitFloatTime
    348 ================
    349 */
    350 void Sys_InitFloatTime (void)
    351 {
    352 	int		j;
    353 
    354 	Sys_DoubleTime ();
    355 
    356 	j = COM_CheckParm("-starttime");
    357 
    358 	if (j)
    359 	{
    360 		curtime = (double) (Q_atof(com_argv[j+1]));
    361 	}
    362 	else
    363 	{
    364 		curtime = 0.0;
    365 	}
    366 
    367 	lastcurtime = curtime;
    368 }
    369 
    370 #endif
    371 
    372 double Sys_DoubleTime (void)
    373 {
    374 	static DWORD starttime;
    375 	static qboolean first = true;
    376 	DWORD now;
    377 	double t;
    378 
    379 	now = timeGetTime();
    380 
    381 	if (first) {
    382 		first = false;
    383 		starttime = now;
    384 		return 0.0;
    385 	}
    386 
    387 	if (now < starttime) // wrapped?
    388 		return (now / 1000.0) + (LONG_MAX - starttime / 1000.0);
    389 
    390 	if (now - starttime == 0)
    391 		return 0.0;
    392 
    393 	return (now - starttime) / 1000.0;
    394 }
    395 
    396 char *Sys_ConsoleInput (void)
    397 {
    398 	static char	text[256];
    399 	static int		len;
    400 	INPUT_RECORD	recs[1024];
    401 	int		count;
    402 	int		i, dummy;
    403 	int		ch, numread, numevents;
    404 	HANDLE	th;
    405 	char	*clipText, *textCopied;
    406 
    407 	for ( ;; )
    408 	{
    409 		if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
    410 			Sys_Error ("Error getting # of console events");
    411 
    412 		if (numevents <= 0)
    413 			break;
    414 
    415 		if (!ReadConsoleInput(hinput, recs, 1, &numread))
    416 			Sys_Error ("Error reading console input");
    417 
    418 		if (numread != 1)
    419 			Sys_Error ("Couldn't read console input");
    420 
    421 		if (recs[0].EventType == KEY_EVENT)
    422 		{
    423 			if (!recs[0].Event.KeyEvent.bKeyDown)
    424 			{
    425 				ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
    426 
    427 				switch (ch)
    428 				{
    429 					case '\r':
    430 						WriteFile(houtput, "\r\n", 2, &dummy, NULL);
    431 
    432 						if (len)
    433 						{
    434 							text[len] = 0;
    435 							len = 0;
    436 							return text;
    437 						}
    438 						break;
    439 
    440 					case '\b':
    441 						WriteFile(houtput, "\b \b", 3, &dummy, NULL);
    442 						if (len)
    443 						{
    444 							len--;
    445 							putch('\b');
    446 						}
    447 						break;
    448 
    449 					default:
    450 						Con_Printf("Stupid: %d\n", recs[0].Event.KeyEvent.dwControlKeyState);
    451 						if (((ch=='V' || ch=='v') && (recs[0].Event.KeyEvent.dwControlKeyState &
    452 							(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))) || ((recs[0].Event.KeyEvent.dwControlKeyState
    453 							& SHIFT_PRESSED) && (recs[0].Event.KeyEvent.wVirtualKeyCode
    454 							==VK_INSERT))) {
    455 							if (OpenClipboard(NULL)) {
    456 								th = GetClipboardData(CF_TEXT);
    457 								if (th) {
    458 									clipText = GlobalLock(th);
    459 									if (clipText) {
    460 										textCopied = malloc(GlobalSize(th)+1);
    461 										strcpy(textCopied, clipText);
    462 /* Substitutes a NULL for every token */strtok(textCopied, "\n\r\b");
    463 										i = strlen(textCopied);
    464 										if (i+len>=256)
    465 											i=256-len;
    466 										if (i>0) {
    467 											textCopied[i]=0;
    468 											text[len]=0;
    469 											strcat(text, textCopied);
    470 											len+=dummy;
    471 											WriteFile(houtput, textCopied, i, &dummy, NULL);
    472 										}
    473 										free(textCopied);
    474 									}
    475 									GlobalUnlock(th);
    476 								}
    477 								CloseClipboard();
    478 							}
    479 						} else if (ch >= ' ')
    480 						{
    481 							WriteFile(houtput, &ch, 1, &dummy, NULL);
    482 							text[len] = ch;
    483 							len = (len + 1) & 0xff;
    484 						}
    485 
    486 						break;
    487 
    488 				}
    489 			}
    490 		}
    491 	}
    492 
    493 	return NULL;
    494 }
    495 
    496 void Sys_Sleep (void)
    497 {
    498 }
    499 
    500 
    501 void Sys_SendKeyEvents (void)
    502 {
    503     MSG        msg;
    504 
    505 	while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
    506 	{
    507 	// we always update if there are any event, even if we're paused
    508 		scr_skipupdate = 0;
    509 
    510 		if (!GetMessage (&msg, NULL, 0, 0))
    511 			Sys_Quit ();
    512       	TranslateMessage (&msg);
    513       	DispatchMessage (&msg);
    514 	}
    515 }
    516 
    517 
    518 
    519 /*
    520 ==============================================================================
    521 
    522  WINDOWS CRAP
    523 
    524 ==============================================================================
    525 */
    526 
    527 /*
    528 ==================
    529 WinMain
    530 ==================
    531 */
    532 void SleepUntilInput (int time)
    533 {
    534 
    535 	MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);
    536 }
    537 
    538 
    539 
    540 /*
    541 ==================
    542 WinMain
    543 ==================
    544 */
    545 HINSTANCE	global_hInstance;
    546 int			global_nCmdShow;
    547 char		*argv[MAX_NUM_ARGVS];
    548 static char	*empty_string = "";
    549 HWND		hwnd_dialog;
    550 
    551 
    552 int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    553 {
    554     MSG				msg;
    555 	quakeparms_t	parms;
    556 	double			time, oldtime, newtime;
    557 	MEMORYSTATUS	lpBuffer;
    558 	static	char	cwd[1024];
    559 	int				t;
    560 	RECT			rect;
    561 
    562     /* previous instances do not exist in Win32 */
    563     if (hPrevInstance)
    564         return 0;
    565 
    566 	global_hInstance = hInstance;
    567 	global_nCmdShow = nCmdShow;
    568 
    569 	lpBuffer.dwLength = sizeof(MEMORYSTATUS);
    570 	GlobalMemoryStatus (&lpBuffer);
    571 
    572 	if (!GetCurrentDirectory (sizeof(cwd), cwd))
    573 		Sys_Error ("Couldn't determine current directory");
    574 
    575 	if (cwd[Q_strlen(cwd)-1] == '/')
    576 		cwd[Q_strlen(cwd)-1] = 0;
    577 
    578 	parms.basedir = cwd;
    579 	parms.cachedir = NULL;
    580 
    581 	parms.argc = 1;
    582 	argv[0] = empty_string;
    583 
    584 	while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS))
    585 	{
    586 		while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
    587 			lpCmdLine++;
    588 
    589 		if (*lpCmdLine)
    590 		{
    591 			argv[parms.argc] = lpCmdLine;
    592 			parms.argc++;
    593 
    594 			while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
    595 				lpCmdLine++;
    596 
    597 			if (*lpCmdLine)
    598 			{
    599 				*lpCmdLine = 0;
    600 				lpCmdLine++;
    601 			}
    602 
    603 		}
    604 	}
    605 
    606 	parms.argv = argv;
    607 
    608 	COM_InitArgv (parms.argc, parms.argv);
    609 
    610 	parms.argc = com_argc;
    611 	parms.argv = com_argv;
    612 
    613 	hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL);
    614 
    615 	if (hwnd_dialog)
    616 	{
    617 		if (GetWindowRect (hwnd_dialog, &rect))
    618 		{
    619 			if (rect.left > (rect.top * 2))
    620 			{
    621 				SetWindowPos (hwnd_dialog, 0,
    622 					(rect.left / 2) - ((rect.right - rect.left) / 2),
    623 					rect.top, 0, 0,
    624 					SWP_NOZORDER | SWP_NOSIZE);
    625 			}
    626 		}
    627 
    628 		ShowWindow (hwnd_dialog, SW_SHOWDEFAULT);
    629 		UpdateWindow (hwnd_dialog);
    630 		SetForegroundWindow (hwnd_dialog);
    631 	}
    632 
    633 // take the greater of all the available memory or half the total memory,
    634 // but at least 8 Mb and no more than 16 Mb, unless they explicitly
    635 // request otherwise
    636 	parms.memsize = lpBuffer.dwAvailPhys;
    637 
    638 	if (parms.memsize < MINIMUM_WIN_MEMORY)
    639 		parms.memsize = MINIMUM_WIN_MEMORY;
    640 
    641 	if (parms.memsize < (lpBuffer.dwTotalPhys >> 1))
    642 		parms.memsize = lpBuffer.dwTotalPhys >> 1;
    643 
    644 	if (parms.memsize > MAXIMUM_WIN_MEMORY)
    645 		parms.memsize = MAXIMUM_WIN_MEMORY;
    646 
    647 	if (COM_CheckParm ("-heapsize"))
    648 	{
    649 		t = COM_CheckParm("-heapsize") + 1;
    650 
    651 		if (t < com_argc)
    652 			parms.memsize = Q_atoi (com_argv[t]) * 1024;
    653 	}
    654 
    655 	parms.membase = malloc (parms.memsize);
    656 
    657 	if (!parms.membase)
    658 		Sys_Error ("Not enough memory free; check disk space\n");
    659 
    660 	tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
    661 
    662 	if (!tevent)
    663 		Sys_Error ("Couldn't create event");
    664 
    665 	Sys_Init ();
    666 
    667 // because sound is off until we become active
    668 	S_BlockSound ();
    669 
    670 	Sys_Printf ("Host_Init\n");
    671 	Host_Init (&parms);
    672 
    673 	oldtime = Sys_DoubleTime ();
    674 
    675     /* main window message loop */
    676 	while (1)
    677 	{
    678 	// yield the CPU for a little while when paused, minimized, or not the focus
    679 		if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized || block_drawing)
    680 		{
    681 			SleepUntilInput (PAUSE_SLEEP);
    682 			scr_skipupdate = 1;		// no point in bothering to draw
    683 		}
    684 		else if (!ActiveApp && !DDActive)
    685 		{
    686 			SleepUntilInput (NOT_FOCUS_SLEEP);
    687 		}
    688 
    689 		newtime = Sys_DoubleTime ();
    690 		time = newtime - oldtime;
    691 		Host_Frame (time);
    692 		oldtime = newtime;
    693 	}
    694 
    695     /* return success of application */
    696     return TRUE;
    697 }
    698 
    699