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