1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, 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 35int starttime; 36qboolean ActiveApp, Minimized; 37qboolean WinNT; 38 39HWND hwnd_dialog; // startup dialog box 40 41static double pfreq; 42static double curtime = 0.0; 43static double lastcurtime = 0.0; 44static int lowshift; 45static HANDLE hinput, houtput; 46 47HANDLE qwclsemaphore; 48 49static HANDLE tevent; 50 51void Sys_InitFloatTime (void); 52 53void MaskExceptions (void); 54void Sys_PopFPCW (void); 55void Sys_PushFPCW_SetHigh (void); 56 57void 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 74FILE IO 75 76=============================================================================== 77*/ 78 79/* 80================ 81filelength 82================ 83*/ 84int 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 98int 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 121void Sys_mkdir (char *path) 122{ 123 _mkdir (path); 124} 125 126 127/* 128=============================================================================== 129 130SYSTEM IO 131 132=============================================================================== 133*/ 134 135/* 136================ 137Sys_MakeCodeWriteable 138================ 139*/ 140void 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================ 152Sys_Init 153================ 154*/ 155void 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 229void 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 250void 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 261void 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================ 281Sys_DoubleTime 282================ 283*/ 284double 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================ 347Sys_InitFloatTime 348================ 349*/ 350void 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 372double 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 396char *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 496void Sys_Sleep (void) 497{ 498} 499 500 501void 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================== 529WinMain 530================== 531*/ 532void SleepUntilInput (int time) 533{ 534 535 MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT); 536} 537 538 539 540/* 541================== 542WinMain 543================== 544*/ 545HINSTANCE global_hInstance; 546int global_nCmdShow; 547char *argv[MAX_NUM_ARGVS]; 548static char *empty_string = ""; 549HWND hwnd_dialog; 550 551 552int 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