| /*
|
| Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
| This program is free software; you can redistribute it and/or
|
| modify it under the terms of the GNU General Public License
|
| as published by the Free Software Foundation; either version 2
|
| of the License, or (at your option) any later version.
|
|
|
| This program is distributed in the hope that it will be useful,
|
| but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
| See the GNU General Public License for more details.
|
|
|
| You should have received a copy of the GNU General Public License
|
| along with this program; if not, write to the Free Software
|
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
| */
|
| // sys_win.h |
| |
| #include "quakedef.h" |
| #include "winquake.h" |
| #include "resource.h" |
| #include "errno.h" |
| #include "fcntl.h" |
| #include <limits.h> |
| |
| #define MINIMUM_WIN_MEMORY 0x0c00000 |
| #define MAXIMUM_WIN_MEMORY 0x1000000 |
| |
| #define PAUSE_SLEEP 50 // sleep time on pause or minimization |
| #define NOT_FOCUS_SLEEP 20 // sleep time when not focus |
| |
| int starttime; |
| qboolean ActiveApp, Minimized; |
| qboolean WinNT; |
| |
| HWND hwnd_dialog; // startup dialog box |
| |
| static double pfreq; |
| static double curtime = 0.0; |
| static double lastcurtime = 0.0; |
| static int lowshift; |
| static HANDLE hinput, houtput; |
| |
| HANDLE qwclsemaphore; |
| |
| static HANDLE tevent; |
| |
| void Sys_InitFloatTime (void); |
| |
| void MaskExceptions (void); |
| void Sys_PopFPCW (void); |
| void Sys_PushFPCW_SetHigh (void); |
| |
| void Sys_DebugLog(char *file, char *fmt, ...) |
| { |
| va_list argptr; |
| static char data[1024]; |
| int fd; |
| |
| va_start(argptr, fmt); |
| vsprintf(data, fmt, argptr); |
| va_end(argptr); |
| fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); |
| write(fd, data, strlen(data)); |
| close(fd); |
| }; |
| |
| /* |
| =============================================================================== |
| |
| FILE IO |
| |
| =============================================================================== |
| */ |
| |
| /* |
| ================ |
| filelength |
| ================ |
| */ |
| int filelength (FILE *f) |
| { |
| int pos; |
| int end; |
| |
| pos = ftell (f); |
| fseek (f, 0, SEEK_END); |
| end = ftell (f); |
| fseek (f, pos, SEEK_SET); |
| |
| return end; |
| } |
| |
| |
| int Sys_FileTime (char *path) |
| { |
| FILE *f; |
| int t, retval; |
| |
| t = VID_ForceUnlockedAndReturnState (); |
| |
| f = fopen(path, "rb"); |
| |
| if (f) |
| { |
| fclose(f); |
| retval = 1; |
| } |
| else |
| { |
| retval = -1; |
| } |
| |
| VID_ForceLockState (t); |
| return retval; |
| } |
| |
| void Sys_mkdir (char *path) |
| { |
| _mkdir (path); |
| } |
| |
| |
| /* |
| =============================================================================== |
| |
| SYSTEM IO |
| |
| =============================================================================== |
| */ |
| |
| /* |
| ================ |
| Sys_MakeCodeWriteable |
| ================ |
| */ |
| void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) |
| { |
| DWORD flOldProtect; |
| |
| //@@@ copy on write or just read-write? |
| if (!VirtualProtect((LPVOID)startaddr, length, PAGE_READWRITE, &flOldProtect)) |
| Sys_Error("Protection change failed\n"); |
| } |
| |
| |
| /* |
| ================ |
| Sys_Init |
| ================ |
| */ |
| void Sys_Init (void) |
| { |
| LARGE_INTEGER PerformanceFreq; |
| unsigned int lowpart, highpart; |
| OSVERSIONINFO vinfo; |
| |
| #ifndef SERVERONLY |
| // allocate a named semaphore on the client so the |
| // front end can tell if it is alive |
| |
| // mutex will fail if semephore allready exists |
| qwclsemaphore = CreateMutex( |
| NULL, /* Security attributes */ |
| 0, /* owner */ |
| "qwcl"); /* Semaphore name */ |
| if (!qwclsemaphore) |
| Sys_Error ("QWCL is already running on this system"); |
| CloseHandle (qwclsemaphore); |
| |
| qwclsemaphore = CreateSemaphore( |
| NULL, /* Security attributes */ |
| 0, /* Initial count */ |
| 1, /* Maximum count */ |
| "qwcl"); /* Semaphore name */ |
| #endif |
| |
| MaskExceptions (); |
| Sys_SetFPCW (); |
| |
| #if 0 |
| if (!QueryPerformanceFrequency (&PerformanceFreq)) |
| Sys_Error ("No hardware timer available"); |
| |
| // get 32 out of the 64 time bits such that we have around |
| // 1 microsecond resolution |
| lowpart = (unsigned int)PerformanceFreq.LowPart; |
| highpart = (unsigned int)PerformanceFreq.HighPart; |
| lowshift = 0; |
| |
| while (highpart || (lowpart > 2000000.0)) |
| { |
| lowshift++; |
| lowpart >>= 1; |
| lowpart |= (highpart & 1) << 31; |
| highpart >>= 1; |
| } |
| |
| pfreq = 1.0 / (double)lowpart; |
| |
| Sys_InitFloatTime (); |
| #endif |
| |
| // make sure the timer is high precision, otherwise |
| // NT gets 18ms resolution |
| timeBeginPeriod( 1 ); |
| |
| vinfo.dwOSVersionInfoSize = sizeof(vinfo); |
| |
| if (!GetVersionEx (&vinfo)) |
| Sys_Error ("Couldn't get OS info"); |
| |
| if ((vinfo.dwMajorVersion < 4) || |
| (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) |
| { |
| Sys_Error ("QuakeWorld requires at least Win95 or NT 4.0"); |
| } |
| |
| if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) |
| WinNT = true; |
| else |
| WinNT = false; |
| } |
| |
| |
| void Sys_Error (char *error, ...) |
| { |
| va_list argptr; |
| char text[1024], text2[1024]; |
| DWORD dummy; |
| |
| Host_Shutdown (); |
| |
| va_start (argptr, error); |
| vsprintf (text, error, argptr); |
| va_end (argptr); |
| |
| MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); |
| |
| #ifndef SERVERONLY |
| CloseHandle (qwclsemaphore); |
| #endif |
| |
| exit (1); |
| } |
| |
| void Sys_Printf (char *fmt, ...) |
| { |
| va_list argptr; |
| char text[1024]; |
| DWORD dummy; |
| |
| va_start (argptr,fmt); |
| vprintf (fmt, argptr); |
| va_end (argptr); |
| } |
| |
| void Sys_Quit (void) |
| { |
| VID_ForceUnlockedAndReturnState (); |
| |
| Host_Shutdown(); |
| #ifndef SERVERONLY |
| if (tevent) |
| CloseHandle (tevent); |
| |
| if (qwclsemaphore) |
| CloseHandle (qwclsemaphore); |
| #endif |
| |
| exit (0); |
| } |
| |
| |
| #if 0 |
| /* |
| ================ |
| Sys_DoubleTime |
| ================ |
| */ |
| double Sys_DoubleTime (void) |
| { |
| static int sametimecount; |
| static unsigned int oldtime; |
| static int first = 1; |
| LARGE_INTEGER PerformanceCount; |
| unsigned int temp, t2; |
| double time; |
| |
| Sys_PushFPCW_SetHigh (); |
| |
| QueryPerformanceCounter (&PerformanceCount); |
| |
| temp = ((unsigned int)PerformanceCount.LowPart >> lowshift) | |
| ((unsigned int)PerformanceCount.HighPart << (32 - lowshift)); |
| |
| if (first) |
| { |
| oldtime = temp; |
| first = 0; |
| } |
| else |
| { |
| // check for turnover or backward time |
| if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000)) |
| { |
| oldtime = temp; // so we can't get stuck |
| } |
| else |
| { |
| t2 = temp - oldtime; |
| |
| time = (double)t2 * pfreq; |
| oldtime = temp; |
| |
| curtime += time; |
| |
| if (curtime == lastcurtime) |
| { |
| sametimecount++; |
| |
| if (sametimecount > 100000) |
| { |
| curtime += 1.0; |
| sametimecount = 0; |
| } |
| } |
| else |
| { |
| sametimecount = 0; |
| } |
| |
| lastcurtime = curtime; |
| } |
| } |
| |
| Sys_PopFPCW (); |
| |
| return curtime; |
| } |
| |
| /* |
| ================ |
| Sys_InitFloatTime |
| ================ |
| */ |
| void Sys_InitFloatTime (void) |
| { |
| int j; |
| |
| Sys_DoubleTime (); |
| |
| j = COM_CheckParm("-starttime"); |
| |
| if (j) |
| { |
| curtime = (double) (Q_atof(com_argv[j+1])); |
| } |
| else |
| { |
| curtime = 0.0; |
| } |
| |
| lastcurtime = curtime; |
| } |
| |
| #endif |
| |
| double Sys_DoubleTime (void) |
| { |
| static DWORD starttime; |
| static qboolean first = true; |
| DWORD now; |
| double t; |
| |
| now = timeGetTime(); |
| |
| if (first) { |
| first = false; |
| starttime = now; |
| return 0.0; |
| } |
| |
| if (now < starttime) // wrapped? |
| return (now / 1000.0) + (LONG_MAX - starttime / 1000.0); |
| |
| if (now - starttime == 0) |
| return 0.0; |
| |
| return (now - starttime) / 1000.0; |
| } |
| |
| char *Sys_ConsoleInput (void) |
| { |
| static char text[256]; |
| static int len; |
| INPUT_RECORD recs[1024]; |
| int count; |
| int i, dummy; |
| int ch, numread, numevents; |
| HANDLE th; |
| char *clipText, *textCopied; |
| |
| for ( ;; ) |
| { |
| if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) |
| Sys_Error ("Error getting # of console events"); |
| |
| if (numevents <= 0) |
| break; |
| |
| if (!ReadConsoleInput(hinput, recs, 1, &numread)) |
| Sys_Error ("Error reading console input"); |
| |
| if (numread != 1) |
| Sys_Error ("Couldn't read console input"); |
| |
| if (recs[0].EventType == KEY_EVENT) |
| { |
| if (!recs[0].Event.KeyEvent.bKeyDown) |
| { |
| ch = recs[0].Event.KeyEvent.uChar.AsciiChar; |
| |
| switch (ch) |
| { |
| case '\r': |
| WriteFile(houtput, "\r\n", 2, &dummy, NULL); |
| |
| if (len) |
| { |
| text[len] = 0; |
| len = 0; |
| return text; |
| } |
| break; |
| |
| case '\b': |
| WriteFile(houtput, "\b \b", 3, &dummy, NULL); |
| if (len) |
| { |
| len--; |
| putch('\b'); |
| } |
| break; |
| |
| default: |
| Con_Printf("Stupid: %d\n", recs[0].Event.KeyEvent.dwControlKeyState); |
| if (((ch=='V' || ch=='v') && (recs[0].Event.KeyEvent.dwControlKeyState & |
| (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))) || ((recs[0].Event.KeyEvent.dwControlKeyState |
| & SHIFT_PRESSED) && (recs[0].Event.KeyEvent.wVirtualKeyCode |
| ==VK_INSERT))) { |
| if (OpenClipboard(NULL)) { |
| th = GetClipboardData(CF_TEXT); |
| if (th) { |
| clipText = GlobalLock(th); |
| if (clipText) { |
| textCopied = malloc(GlobalSize(th)+1); |
| strcpy(textCopied, clipText); |
| /* Substitutes a NULL for every token */strtok(textCopied, "\n\r\b"); |
| i = strlen(textCopied); |
| if (i+len>=256) |
| i=256-len; |
| if (i>0) { |
| textCopied[i]=0; |
| text[len]=0; |
| strcat(text, textCopied); |
| len+=dummy; |
| WriteFile(houtput, textCopied, i, &dummy, NULL); |
| } |
| free(textCopied); |
| } |
| GlobalUnlock(th); |
| } |
| CloseClipboard(); |
| } |
| } else if (ch >= ' ') |
| { |
| WriteFile(houtput, &ch, 1, &dummy, NULL); |
| text[len] = ch; |
| len = (len + 1) & 0xff; |
| } |
| |
| break; |
| |
| } |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| void Sys_Sleep (void) |
| { |
| } |
| |
| |
| void Sys_SendKeyEvents (void) |
| { |
| MSG msg; |
| |
| while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) |
| { |
| // we always update if there are any event, even if we're paused |
| scr_skipupdate = 0; |
| |
| if (!GetMessage (&msg, NULL, 0, 0)) |
| Sys_Quit (); |
| TranslateMessage (&msg); |
| DispatchMessage (&msg); |
| } |
| } |
| |
| |
| |
| /* |
| ============================================================================== |
| |
| WINDOWS CRAP |
| |
| ============================================================================== |
| */ |
| |
| /* |
| ================== |
| WinMain |
| ================== |
| */ |
| void SleepUntilInput (int time) |
| { |
| |
| MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT); |
| } |
| |
| |
| |
| /* |
| ================== |
| WinMain |
| ================== |
| */ |
| HINSTANCE global_hInstance; |
| int global_nCmdShow; |
| char *argv[MAX_NUM_ARGVS]; |
| static char *empty_string = ""; |
| HWND hwnd_dialog; |
| |
| |
| int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) |
| { |
| MSG msg; |
| quakeparms_t parms; |
| double time, oldtime, newtime; |
| MEMORYSTATUS lpBuffer; |
| static char cwd[1024]; |
| int t; |
| RECT rect; |
| |
| /* previous instances do not exist in Win32 */ |
| if (hPrevInstance) |
| return 0; |
| |
| global_hInstance = hInstance; |
| global_nCmdShow = nCmdShow; |
| |
| lpBuffer.dwLength = sizeof(MEMORYSTATUS); |
| GlobalMemoryStatus (&lpBuffer); |
| |
| if (!GetCurrentDirectory (sizeof(cwd), cwd)) |
| Sys_Error ("Couldn't determine current directory"); |
| |
| if (cwd[Q_strlen(cwd)-1] == '/') |
| cwd[Q_strlen(cwd)-1] = 0; |
| |
| parms.basedir = cwd; |
| parms.cachedir = NULL; |
| |
| parms.argc = 1; |
| argv[0] = empty_string; |
| |
| while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS)) |
| { |
| while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) |
| lpCmdLine++; |
| |
| if (*lpCmdLine) |
| { |
| argv[parms.argc] = lpCmdLine; |
| parms.argc++; |
| |
| while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) |
| lpCmdLine++; |
| |
| if (*lpCmdLine) |
| { |
| *lpCmdLine = 0; |
| lpCmdLine++; |
| } |
| |
| } |
| } |
| |
| parms.argv = argv; |
| |
| COM_InitArgv (parms.argc, parms.argv); |
| |
| parms.argc = com_argc; |
| parms.argv = com_argv; |
| |
| hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL); |
| |
| if (hwnd_dialog) |
| { |
| if (GetWindowRect (hwnd_dialog, &rect)) |
| { |
| if (rect.left > (rect.top * 2)) |
| { |
| SetWindowPos (hwnd_dialog, 0, |
| (rect.left / 2) - ((rect.right - rect.left) / 2), |
| rect.top, 0, 0, |
| SWP_NOZORDER | SWP_NOSIZE); |
| } |
| } |
| |
| ShowWindow (hwnd_dialog, SW_SHOWDEFAULT); |
| UpdateWindow (hwnd_dialog); |
| SetForegroundWindow (hwnd_dialog); |
| } |
| |
| // take the greater of all the available memory or half the total memory, |
| // but at least 8 Mb and no more than 16 Mb, unless they explicitly |
| // request otherwise |
| parms.memsize = lpBuffer.dwAvailPhys; |
| |
| if (parms.memsize < MINIMUM_WIN_MEMORY) |
| parms.memsize = MINIMUM_WIN_MEMORY; |
| |
| if (parms.memsize < (lpBuffer.dwTotalPhys >> 1)) |
| parms.memsize = lpBuffer.dwTotalPhys >> 1; |
| |
| if (parms.memsize > MAXIMUM_WIN_MEMORY) |
| parms.memsize = MAXIMUM_WIN_MEMORY; |
| |
| if (COM_CheckParm ("-heapsize")) |
| { |
| t = COM_CheckParm("-heapsize") + 1; |
| |
| if (t < com_argc) |
| parms.memsize = Q_atoi (com_argv[t]) * 1024; |
| } |
| |
| parms.membase = malloc (parms.memsize); |
| |
| if (!parms.membase) |
| Sys_Error ("Not enough memory free; check disk space\n"); |
| |
| tevent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| |
| if (!tevent) |
| Sys_Error ("Couldn't create event"); |
| |
| Sys_Init (); |
| |
| // because sound is off until we become active |
| S_BlockSound (); |
| |
| Sys_Printf ("Host_Init\n"); |
| Host_Init (&parms); |
| |
| oldtime = Sys_DoubleTime (); |
| |
| /* main window message loop */ |
| while (1) |
| { |
| // yield the CPU for a little while when paused, minimized, or not the focus |
| if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized || block_drawing) |
| { |
| SleepUntilInput (PAUSE_SLEEP); |
| scr_skipupdate = 1; // no point in bothering to draw |
| } |
| else if (!ActiveApp && !DDActive) |
| { |
| SleepUntilInput (NOT_FOCUS_SLEEP); |
| } |
| |
| newtime = Sys_DoubleTime (); |
| time = newtime - oldtime; |
| Host_Frame (time); |
| oldtime = newtime; |
| } |
| |
| /* return success of application */ |
| return TRUE; |
| } |
| |