macosx_input.m (31009B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 #import <AppKit/AppKit.h> 23 #import <Foundation/Foundation.h> 24 #include <ApplicationServices/ApplicationServices.h> 25 26 #import "../client/client.h" 27 #import "macosx_local.h" 28 #import "../renderer/tr_local.h" 29 30 #import "Q3Controller.h" 31 //#import "CGMouseDeltaFix.h" 32 #import "macosx_timers.h" 33 #import "macosx_display.h" // For Sys_SetScreenFade 34 35 #import <drivers/event_status_driver.h> 36 #import <sys/types.h> 37 #import <sys/time.h> 38 #import <unistd.h> 39 40 41 static qboolean inputActive; 42 43 static NSDate *distantPast; 44 45 static cvar_t *in_nomouse; 46 static cvar_t *in_showevents; 47 static cvar_t *in_mouseLowEndSlope; 48 static cvar_t *in_mouseHighEndCutoff; 49 static cvar_t *in_disableOSMouseScaling; 50 51 static void Sys_StartMouseInput(); 52 static void Sys_StopMouseInput(); 53 static qboolean mouseactive = qfalse; 54 static BOOL inputRectValid = NO; 55 static CGRect inputRect; 56 static NXMouseScaling originalScaling; 57 58 static unsigned int currentModifierFlags; 59 60 61 62 static void Sys_PreventMouseMovement(CGPoint point) 63 { 64 CGEventErr err; 65 66 //Com_Printf("**** Calling CGAssociateMouseAndMouseCursorPosition(false)\n"); 67 err = CGAssociateMouseAndMouseCursorPosition(false); 68 if (err != CGEventNoErr) { 69 Sys_Error("Could not disable mouse movement, CGAssociateMouseAndMouseCursorPosition returned %d\n", err); 70 } 71 72 // Put the mouse in the position we want to leave it at 73 err = CGWarpMouseCursorPosition(point); 74 if (err != CGEventNoErr) { 75 Sys_Error("Could not disable mouse movement, CGWarpMouseCursorPosition returned %d\n", err); 76 } 77 } 78 79 static void Sys_ReenableMouseMovement() 80 { 81 CGEventErr err; 82 83 //Com_Printf("**** Calling CGAssociateMouseAndMouseCursorPosition(true)\n"); 84 85 err = CGAssociateMouseAndMouseCursorPosition(true); 86 if (err != CGEventNoErr) { 87 Sys_Error("Could not reenable mouse movement, CGAssociateMouseAndMouseCursorPosition returned %d\n", err); 88 } 89 90 // Leave the mouse where it was -- don't warp here. 91 } 92 93 94 void Sys_InitInput(void) 95 { 96 // no input with dedicated servers 97 if ( com_dedicated->integer ) { 98 return; 99 } 100 101 // The Cvars don't seem to work really early. 102 [(Q3Controller *)[NSApp delegate] showBanner]; 103 104 Com_Printf( "------- Input Initialization -------\n" ); 105 106 if (!distantPast) 107 distantPast = [[NSDate distantPast] retain]; 108 109 // For hide support. If we don't do this, then the command key will get stuck on when we hide (since we won't get the flags changed event when it goes up). 110 currentModifierFlags = 0; 111 112 r_fullscreen = Cvar_Get( "r_fullscreen", "1", CVAR_ARCHIVE | CVAR_LATCH ); 113 in_nomouse = Cvar_Get( "in_nomouse", "0", 0 ); 114 in_showevents = Cvar_Get( "in_showevents", "0", 0 ); 115 116 // these defaults were arrived at via emprical testing between a Windows box and a Mac OS X box 117 #define ACT_LIKE_WINDOWS 118 #ifdef ACT_LIKE_WINDOWS 119 in_mouseLowEndSlope = Cvar_Get("in_mouseLowEndSlope", "3.5", CVAR_ARCHIVE); 120 if (in_mouseLowEndSlope->value < 1) { 121 Cvar_Set("in_mouseLowEndSlope", "1"); 122 } 123 #else 124 in_mouseLowEndSlope = Cvar_Get("in_mouseLowEndSlope", "1", CVAR_ARCHIVE); 125 if (in_mouseLowEndSlope->value < 1) { 126 Cvar_Set("in_mouseLowEndSlope", "1"); 127 } 128 #endif 129 130 in_mouseHighEndCutoff = Cvar_Get("in_mouseHighEndCutoff", "20", CVAR_ARCHIVE); 131 if (in_mouseLowEndSlope->value < 1) { 132 Cvar_Set("in_mouseHighEndCutoff", "1"); 133 } 134 in_disableOSMouseScaling = Cvar_Get("in_disableOSMouseScaling", "1", CVAR_ARCHIVE ); 135 136 glw_state.display = Sys_DisplayToUse(); 137 138 inputActive = qtrue; 139 140 if ( in_nomouse->integer == 0 ) 141 Sys_StartMouseInput(); 142 else 143 Com_Printf( " in_nomouse is set, skipping.\n" ); 144 145 Com_Printf( "------------------------------------\n" ); 146 } 147 148 void Sys_ShutdownInput(void) 149 { 150 // no input with dedicated servers 151 if ( !com_dedicated || com_dedicated->integer ) { 152 return; 153 } 154 155 Com_Printf( "------- Input Shutdown -------\n" ); 156 if ( !inputActive ) { 157 return; 158 } 159 inputActive = qfalse; 160 161 if (mouseactive) 162 Sys_StopMouseInput(); 163 164 Com_Printf( "------------------------------\n" ); 165 } 166 167 static void Sys_LockMouseInInputRect(CGRect rect) 168 { 169 CGPoint center; 170 171 center.x = rect.origin.x + rect.size.width / 2.0; 172 center.y = rect.origin.y + rect.size.height / 2.0; 173 174 // Now, put the mouse in the middle of the input rect (anywhere over it would do) 175 // and don't allow it to move. This means that the user won't be able to accidentally 176 // select another application. 177 Sys_PreventMouseMovement(center); 178 } 179 180 extern void Sys_UpdateWindowMouseInputRect(void); 181 182 static void Sys_StartMouseInput() 183 { 184 NXEventHandle eventStatus; 185 CGMouseDelta dx, dy; 186 187 if (mouseactive) { 188 //Com_Printf("**** Attempted to start mouse input while already started\n"); 189 return; 190 } 191 192 Com_Printf("Starting mouse input\n"); 193 194 mouseactive = qtrue; 195 if (inputRectValid && !glConfig.isFullscreen) 196 // Make sure that if window moved we don't hose the user... 197 Sys_UpdateWindowMouseInputRect(); 198 199 Sys_LockMouseInInputRect(inputRect); 200 201 // Grab any mouse delta information to reset the last delta buffer 202 CGGetLastMouseDelta(&dx, &dy); 203 204 // Turn off mouse scaling 205 if (in_disableOSMouseScaling->integer==0 && (eventStatus = NXOpenEventStatus())) { 206 NXMouseScaling newScaling; 207 208 NXGetMouseScaling(eventStatus, &originalScaling); 209 newScaling.numScaleLevels = 1; 210 newScaling.scaleThresholds[0] = 1; 211 newScaling.scaleFactors[0] = -1; 212 NXSetMouseScaling(eventStatus, &newScaling); 213 NXCloseEventStatus(eventStatus); 214 } 215 216 [NSCursor hide]; 217 } 218 219 static void Sys_StopMouseInput() 220 { 221 NXEventHandle eventStatus; 222 if (!mouseactive) { 223 //Com_Printf("**** Attempted to stop mouse input while already stopped\n"); 224 return; 225 } 226 227 Com_Printf("Stopping mouse input\n"); 228 229 // Restore mouse scaling 230 if (in_disableOSMouseScaling->integer == 0 && (eventStatus = NXOpenEventStatus())) { 231 NXSetMouseScaling(eventStatus, &originalScaling); 232 NXCloseEventStatus(eventStatus); 233 } 234 235 mouseactive = qfalse; 236 Sys_ReenableMouseMovement(); 237 238 [NSCursor unhide]; 239 } 240 241 //=========================================================================== 242 243 #include <sys/types.h> 244 #include <sys/time.h> 245 #include <unistd.h> 246 247 static char *Sys_ConsoleInput(void) 248 { 249 extern qboolean stdin_active; 250 static char text[256]; 251 int len; 252 fd_set fdset; 253 struct timeval timeout; 254 255 if (!com_dedicated || !com_dedicated->integer) 256 return NULL; 257 258 if (!stdin_active) 259 return NULL; 260 261 FD_ZERO(&fdset); 262 FD_SET(fileno(stdin), &fdset); 263 timeout.tv_sec = 0; 264 timeout.tv_usec = 0; 265 if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(fileno(stdin), &fdset)) 266 return NULL; 267 268 len = read (fileno(stdin), text, sizeof(text)); 269 if (len == 0) { // eof! 270 stdin_active = qfalse; 271 return NULL; 272 } 273 274 if (len < 1) 275 return NULL; 276 text[len-1] = 0; // rip off the /n and terminate 277 278 return text; 279 } 280 281 //=========================================================================== 282 // Mouse input 283 //=========================================================================== 284 285 #define MAX_DISPLAYS 128 286 287 CGDirectDisplayID Sys_DisplayToUse(void) 288 { 289 static BOOL gotDisplay = NO; 290 static CGDirectDisplayID displayToUse; 291 292 cvar_t *vid_screen; 293 CGDisplayErr err; 294 CGDirectDisplayID displays[MAX_DISPLAYS]; 295 CGDisplayCount displayCount; 296 int displayIndex; 297 298 if (gotDisplay) 299 return displayToUse; 300 gotDisplay = YES; 301 302 err = CGGetActiveDisplayList(MAX_DISPLAYS, displays, &displayCount); 303 if (err != CGDisplayNoErr) 304 Sys_Error("Cannot get display list -- CGGetActiveDisplayList returned %d.\n", err); 305 306 // -1, the default, means to use the main screen 307 if ((vid_screen = Cvar_Get("vid_screen", "-1", CVAR_ARCHIVE))) 308 displayIndex = vid_screen->integer; 309 else 310 displayIndex = -1; 311 312 if (displayIndex < 0 || displayIndex >= displayCount) 313 // This is documented (in CGDirectDisplay.h) to be the main display. We want to 314 // return this instead of kCGDirectMainDisplay since this will allow us to compare 315 // display IDs. 316 displayToUse = displays[0]; 317 else 318 displayToUse = displays[displayIndex]; 319 320 return displayToUse; 321 } 322 323 void Sys_SetMouseInputRect(CGRect newRect) 324 { 325 inputRectValid = YES; 326 inputRect = newRect; 327 //Com_Printf("**** inputRect = (%f, %f, %f, %f)\n", newRect.origin.x, newRect.origin.y, newRect.size.width, newRect.size.height); 328 329 if (mouseactive) 330 Sys_LockMouseInInputRect(inputRect); 331 } 332 333 334 static void Sys_ProcessMouseMovedEvent(NSEvent *mouseMovedEvent, int currentTime) 335 { 336 float dx, dy; 337 338 if (!mouseactive) 339 return; 340 341 dx = [mouseMovedEvent deltaX]; 342 dy = [mouseMovedEvent deltaY]; 343 344 if (in_showevents->integer) 345 Com_Printf("MOUSE MOVED: %d, %d\n", dx, dy); 346 347 Sys_QueEvent(currentTime, SE_MOUSE, dx, dy, 0, NULL ); 348 } 349 350 // If we are 'paused' (i.e., in any state that our normal key bindings aren't in effect), then interpret cmd-h and cmd-tab as hiding the application. 351 static qboolean maybeHide() 352 { 353 if ((currentModifierFlags & NSCommandKeyMask) == 0) 354 return qfalse; 355 356 return Sys_Hide(); 357 } 358 359 static inline void sendEventForCharacter(NSEvent *event, unichar character, qboolean keyDownFlag, int currentTime) 360 { 361 if (in_showevents->integer) 362 Com_Printf("CHARACTER: 0x%02x down=%d\n", character, keyDownFlag); 363 364 #ifdef OMNI_TIMER 365 if (character == NSF9FunctionKey && !keyDownFlag) { 366 // Log and reset the root timer. We should currently only have the root on the stack. 367 OTStackPopRoot(); 368 OTStackReportResults(NULL); 369 OTStackReset(); 370 OTStackPushRoot(rootNode); 371 } 372 #endif 373 374 switch (character) { 375 case 0x03: 376 Sys_QueEvent(currentTime, SE_KEY, K_KP_ENTER, keyDownFlag, 0, NULL); 377 break; 378 case '\b': 379 case '\177': 380 Sys_QueEvent(currentTime, SE_KEY, K_BACKSPACE, keyDownFlag, 0, NULL); 381 if (keyDownFlag) { 382 Sys_QueEvent(currentTime, SE_CHAR, '\b', 0, 0, NULL); 383 } 384 break; 385 case '\t': 386 if (maybeHide()) 387 return; 388 Sys_QueEvent(currentTime, SE_KEY, K_TAB, keyDownFlag, 0, NULL); 389 if (keyDownFlag) { 390 Sys_QueEvent(currentTime, SE_CHAR, '\t', 0, 0, NULL); 391 } 392 break; 393 case '\r': 394 case '\n': 395 Sys_QueEvent(currentTime, SE_KEY, K_ENTER, keyDownFlag, 0, NULL); 396 if (keyDownFlag) { 397 Sys_QueEvent(currentTime, SE_CHAR, '\r', 0, 0, NULL); 398 } 399 break; 400 case '\033': 401 Sys_QueEvent(currentTime, SE_KEY, K_ESCAPE, keyDownFlag, 0, NULL); 402 break; 403 case ' ': 404 Sys_QueEvent(currentTime, SE_KEY, K_SPACE, keyDownFlag, 0, NULL); 405 if (keyDownFlag) { 406 Sys_QueEvent(currentTime, SE_CHAR, ' ', 0, 0, NULL); 407 } 408 break; 409 case NSUpArrowFunctionKey: 410 Sys_QueEvent(currentTime, SE_KEY, K_UPARROW, keyDownFlag, 0, NULL); 411 break; 412 case NSDownArrowFunctionKey: 413 Sys_QueEvent(currentTime, SE_KEY, K_DOWNARROW, keyDownFlag, 0, NULL); 414 break; 415 case NSLeftArrowFunctionKey: 416 Sys_QueEvent(currentTime, SE_KEY, K_LEFTARROW, keyDownFlag, 0, NULL); 417 break; 418 case NSRightArrowFunctionKey: 419 Sys_QueEvent(currentTime, SE_KEY, K_RIGHTARROW, keyDownFlag, 0, NULL); 420 break; 421 case NSF1FunctionKey: 422 Sys_QueEvent(currentTime, SE_KEY, K_F1, keyDownFlag, 0, NULL); 423 break; 424 case NSF2FunctionKey: 425 Sys_QueEvent(currentTime, SE_KEY, K_F2, keyDownFlag, 0, NULL); 426 break; 427 case NSF3FunctionKey: 428 Sys_QueEvent(currentTime, SE_KEY, K_F3, keyDownFlag, 0, NULL); 429 break; 430 case NSF4FunctionKey: 431 Sys_QueEvent(currentTime, SE_KEY, K_F4, keyDownFlag, 0, NULL); 432 break; 433 case NSF5FunctionKey: 434 Sys_QueEvent(currentTime, SE_KEY, K_F5, keyDownFlag, 0, NULL); 435 break; 436 case NSF6FunctionKey: 437 Sys_QueEvent(currentTime, SE_KEY, K_F6, keyDownFlag, 0, NULL); 438 break; 439 case NSF7FunctionKey: 440 Sys_QueEvent(currentTime, SE_KEY, K_F7, keyDownFlag, 0, NULL); 441 break; 442 case NSF8FunctionKey: 443 Sys_QueEvent(currentTime, SE_KEY, K_F8, keyDownFlag, 0, NULL); 444 break; 445 case NSF9FunctionKey: 446 Sys_QueEvent(currentTime, SE_KEY, K_F9, keyDownFlag, 0, NULL); 447 break; 448 case NSF10FunctionKey: 449 Sys_QueEvent(currentTime, SE_KEY, K_F10, keyDownFlag, 0, NULL); 450 break; 451 case NSF11FunctionKey: 452 Sys_QueEvent(currentTime, SE_KEY, K_F11, keyDownFlag, 0, NULL); 453 break; 454 case NSF12FunctionKey: 455 Sys_QueEvent(currentTime, SE_KEY, K_F12, keyDownFlag, 0, NULL); 456 break; 457 case NSF13FunctionKey: 458 Sys_QueEvent(currentTime, SE_KEY, '`', keyDownFlag, 0, NULL); 459 if (keyDownFlag) { 460 Sys_QueEvent(currentTime, SE_CHAR, '`', 0, 0, NULL); 461 } 462 break; 463 case NSInsertFunctionKey: 464 Sys_QueEvent(currentTime, SE_KEY, K_INS, keyDownFlag, 0, NULL); 465 break; 466 case NSDeleteFunctionKey: 467 Sys_QueEvent(currentTime, SE_KEY, K_DEL, keyDownFlag, 0, NULL); 468 break; 469 case NSPageDownFunctionKey: 470 Sys_QueEvent(currentTime, SE_KEY, K_PGDN, keyDownFlag, 0, NULL); 471 break; 472 case NSPageUpFunctionKey: 473 Sys_QueEvent(currentTime, SE_KEY, K_PGUP, keyDownFlag, 0, NULL); 474 break; 475 case NSHomeFunctionKey: 476 Sys_QueEvent(currentTime, SE_KEY, K_HOME, keyDownFlag, 0, NULL); 477 break; 478 case NSEndFunctionKey: 479 Sys_QueEvent(currentTime, SE_KEY, K_END, keyDownFlag, 0, NULL); 480 break; 481 case NSPauseFunctionKey: 482 Sys_QueEvent(currentTime, SE_KEY, K_PAUSE, keyDownFlag, 0, NULL); 483 break; 484 default: 485 if ([event modifierFlags] & NSNumericPadKeyMask) { 486 switch (character) { 487 case '0': 488 Sys_QueEvent(currentTime, SE_KEY, K_KP_INS, keyDownFlag, 0, NULL); 489 break; 490 case '1': 491 Sys_QueEvent(currentTime, SE_KEY, K_KP_END, keyDownFlag, 0, NULL); 492 break; 493 case '2': 494 Sys_QueEvent(currentTime, SE_KEY, K_KP_DOWNARROW, keyDownFlag, 0, NULL); 495 break; 496 case '3': 497 Sys_QueEvent(currentTime, SE_KEY, K_KP_PGDN, keyDownFlag, 0, NULL); 498 break; 499 case '4': 500 Sys_QueEvent(currentTime, SE_KEY, K_KP_LEFTARROW, keyDownFlag, 0, NULL); 501 break; 502 case '5': 503 Sys_QueEvent(currentTime, SE_KEY, K_KP_5, keyDownFlag, 0, NULL); 504 break; 505 case '6': 506 Sys_QueEvent(currentTime, SE_KEY, K_KP_RIGHTARROW, keyDownFlag, 0, NULL); 507 break; 508 case '7': 509 Sys_QueEvent(currentTime, SE_KEY, K_KP_HOME, keyDownFlag, 0, NULL); 510 break; 511 case '8': 512 Sys_QueEvent(currentTime, SE_KEY, K_KP_UPARROW, keyDownFlag, 0, NULL); 513 break; 514 case '9': 515 Sys_QueEvent(currentTime, SE_KEY, K_KP_PGUP, keyDownFlag, 0, NULL); 516 break; 517 case '.': 518 case ',': 519 Sys_QueEvent(currentTime, SE_KEY, K_KP_DEL, keyDownFlag, 0, NULL); 520 break; 521 case '+': 522 Sys_QueEvent(currentTime, SE_KEY, K_KP_PLUS, keyDownFlag, 0, NULL); 523 break; 524 case '-': 525 Sys_QueEvent(currentTime, SE_KEY, K_KP_MINUS, keyDownFlag, 0, NULL); 526 break; 527 case '*': 528 Sys_QueEvent(currentTime, SE_KEY, K_KP_STAR, keyDownFlag, 0, NULL); 529 break; 530 case '/': 531 Sys_QueEvent(currentTime, SE_KEY, K_KP_SLASH, keyDownFlag, 0, NULL); 532 break; 533 case '=': 534 Sys_QueEvent(currentTime, SE_KEY, K_KP_EQUALS, keyDownFlag, 0, NULL); 535 break; 536 default: 537 //NSLog(@"TODO: Implement character %d", (int)character); 538 break; 539 } 540 } else if (character >= 'a' && character <= 'z') { 541 if (character == 'h') { 542 if (maybeHide()) 543 return; 544 } 545 Sys_QueEvent(currentTime, SE_KEY, character, keyDownFlag, 0, NULL); 546 if (keyDownFlag) { 547 Sys_QueEvent(currentTime, SE_CHAR, (char)character, 0, 0, NULL); 548 } 549 } else if (character >= 'A' && character <= 'Z') { 550 Sys_QueEvent(currentTime, SE_KEY, 'a' + (character - 'A'), keyDownFlag, 0, NULL); 551 if (keyDownFlag) { 552 Sys_QueEvent(currentTime, SE_CHAR, character, 0, 0, NULL); 553 } 554 } else if (character >= 32 && character < 127) { 555 Sys_QueEvent(currentTime, SE_KEY, character, keyDownFlag, 0, NULL); 556 if (keyDownFlag) { 557 Sys_QueEvent(currentTime, SE_CHAR, (char)character, 0, 0, NULL); 558 } 559 } else { 560 //NSLog(@"TODO: Implement character %d", (int)character); 561 } 562 break; 563 } 564 } 565 566 static inline void processKeyEvent(NSEvent *keyEvent, qboolean keyDownFlag, int currentTime) 567 { 568 NSEventType eventType; 569 NSString *characters; 570 unsigned int characterIndex, characterCount; 571 572 eventType = [keyEvent type]; 573 characters = [keyEvent charactersIgnoringModifiers]; 574 characterCount = [characters length]; 575 576 for (characterIndex = 0; characterIndex < characterCount; characterIndex++) { 577 sendEventForCharacter(keyEvent, [characters characterAtIndex:characterIndex], keyDownFlag, currentTime); 578 } 579 } 580 581 static inline void sendEventForMaskChangeInFlags(int quakeKey, unsigned int modifierMask, unsigned int newModifierFlags, int currentTime) 582 { 583 BOOL oldHadModifier, newHasModifier; 584 585 oldHadModifier = (currentModifierFlags & modifierMask) != 0; 586 newHasModifier = (newModifierFlags & modifierMask) != 0; 587 if (oldHadModifier != newHasModifier) { 588 // NSLog(@"Key %d posted for modifier mask modifierMask", quakeKey); 589 Sys_QueEvent(currentTime, SE_KEY, quakeKey, newHasModifier, 0, NULL); 590 } 591 } 592 593 static inline void processFlagsChangedEvent(NSEvent *flagsChangedEvent, int currentTime) 594 { 595 int newModifierFlags; 596 597 newModifierFlags = [flagsChangedEvent modifierFlags]; 598 sendEventForMaskChangeInFlags(K_COMMAND, NSCommandKeyMask, newModifierFlags, currentTime); 599 sendEventForMaskChangeInFlags(K_CAPSLOCK, NSAlphaShiftKeyMask, newModifierFlags, currentTime); 600 sendEventForMaskChangeInFlags(K_ALT, NSAlternateKeyMask, newModifierFlags, currentTime); 601 sendEventForMaskChangeInFlags(K_CTRL, NSControlKeyMask, newModifierFlags, currentTime); 602 sendEventForMaskChangeInFlags(K_SHIFT, NSShiftKeyMask, newModifierFlags, currentTime); 603 currentModifierFlags = newModifierFlags; 604 } 605 606 static inline void processSystemDefinedEvent(NSEvent *systemDefinedEvent, int currentTime) 607 { 608 static int oldButtons = 0; 609 int buttonsDelta; 610 int buttons; 611 int isDown; 612 613 if ([systemDefinedEvent subtype] == 7) { 614 615 if (!mouseactive) 616 return; 617 618 619 buttons = [systemDefinedEvent data2]; 620 buttonsDelta = oldButtons ^ buttons; 621 622 //Com_Printf("uberbuttons: %08lx %08lx\n",buttonsDelta,buttons); 623 624 625 if (buttonsDelta & 1) { 626 isDown = buttons & 1; 627 Sys_QueEvent(currentTime, SE_KEY, K_MOUSE1, isDown, 0, NULL); 628 if (in_showevents->integer) { 629 Com_Printf("MOUSE2: %s\n", isDown ? "down" : "up"); 630 } 631 } 632 633 if (buttonsDelta & 2) { 634 isDown = buttons & 2; 635 Sys_QueEvent(currentTime, SE_KEY, K_MOUSE2, isDown, 0, NULL); 636 if (in_showevents->integer) { 637 Com_Printf("MOUSE3: %s\n", isDown ? "down" : "up"); 638 } 639 } 640 641 if (buttonsDelta & 4) { 642 isDown = buttons & 4; 643 Sys_QueEvent(currentTime, SE_KEY, K_MOUSE3, isDown, 0, NULL); 644 if (in_showevents->integer) { 645 Com_Printf("MOUSE1: %s\n", isDown ? "down" : "up"); 646 } 647 } 648 649 if (buttonsDelta & 8) { 650 isDown = buttons & 8; 651 Sys_QueEvent(currentTime, SE_KEY, K_MOUSE4, isDown, 0, NULL); 652 if (in_showevents->integer) { 653 Com_Printf("MOUSE4: %s\n", isDown ? "down" : "up"); 654 } 655 } 656 657 if (buttonsDelta & 16) { 658 isDown = buttons & 16; 659 Sys_QueEvent(currentTime, SE_KEY, K_MOUSE5, isDown, 0, NULL); 660 if (in_showevents->integer) { 661 Com_Printf("MOUSE5: %s\n", isDown ? "down" : "up"); 662 } 663 } 664 665 oldButtons = buttons; 666 } 667 } 668 669 static inline void processEvent(NSEvent *event, int currentTime) 670 { 671 NSEventType eventType; 672 673 if (!inputActive) 674 return; 675 676 eventType = [event type]; 677 678 if (in_showevents->integer) 679 NSLog(@"event = %@", event); 680 681 switch (eventType) { 682 // These six event types are ignored since we do all of our mouse down/up process via the uber-mouse system defined event. We have to accept these events however since they get enqueued and the queue will fill up if we don't. 683 case NSLeftMouseDown: 684 //Sys_QueEvent(currentTime, SE_KEY, K_MOUSE1, qtrue, 0, NULL); 685 return; 686 case NSLeftMouseUp: 687 //Sys_QueEvent(currentTime, SE_KEY, K_MOUSE1, qfalse, 0, NULL); 688 return; 689 case NSRightMouseDown: 690 //Sys_QueEvent(currentTime, SE_KEY, K_MOUSE2, qtrue, 0, NULL); 691 return; 692 case NSRightMouseUp: 693 //Sys_QueEvent(currentTime, SE_KEY, K_MOUSE2, qfalse, 0, NULL); 694 return; 695 case 25: // other mouse down 696 return; 697 case 26: // other mouse up 698 return; 699 700 case NSMouseMoved: 701 case NSLeftMouseDragged: 702 case NSRightMouseDragged: 703 case 27: // other mouse dragged 704 Sys_ProcessMouseMovedEvent(event, currentTime); 705 return; 706 case NSKeyDown: 707 case NSKeyUp: 708 processKeyEvent(event, eventType == NSKeyDown, currentTime); 709 return; 710 case NSFlagsChanged: 711 processFlagsChangedEvent(event, currentTime); 712 return; 713 case NSSystemDefined: 714 processSystemDefinedEvent(event, currentTime); 715 return; 716 case NSScrollWheel: 717 if ([event deltaY] < 0.0) { 718 Sys_QueEvent(currentTime, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL ); 719 Sys_QueEvent(currentTime, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL ); 720 } else { 721 Sys_QueEvent(currentTime, SE_KEY, K_MWHEELUP, qtrue, 0, NULL ); 722 Sys_QueEvent(currentTime, SE_KEY, K_MWHEELUP, qfalse, 0, NULL ); 723 } 724 return; 725 default: 726 break; 727 } 728 [NSApp sendEvent:event]; 729 } 730 731 static void Sys_SendKeyEvents(int currentTime) 732 { 733 #ifndef DEDICATED 734 NSEvent *event; 735 NSDate *timeout; 736 extern float SNDDMA_GetBufferDuration(); 737 738 timeout = distantPast; 739 if (Sys_IsHidden) 740 timeout = [NSDate dateWithTimeIntervalSinceNow: 0.25 * SNDDMA_GetBufferDuration()]; 741 742 // This gets call regardless of whether inputActive is true or not. This is important since we need to be poking the event queue in order for the unhide event to make its way through the system. This means that when we hide, we can just shut down the input system and reeanbled it when we unhide. 743 while ((event = [NSApp nextEventMatchingMask: NSAnyEventMask 744 untilDate: timeout 745 inMode: NSDefaultRunLoopMode 746 dequeue:YES])) { 747 if (Sys_IsHidden) { 748 // Just let NSApp handle events so that we'll get the app activation event 749 [NSApp sendEvent: event]; 750 timeout = [NSDate dateWithTimeIntervalSinceNow: 0.1]; 751 } else { 752 static int lastEventTime = 0; 753 static BOOL lastEventTimeValid = NO; 754 755 // Mac OS X 10.0.3 has a bug where the if the monitor goes to sleep in fullscreen GL mode, the gamma won't be restored. We'll restore the gamma if there is a pause while in the game of more than 10 seconds. We don't do this on the 'Sys_IsHidden' branch since unhiding will restore the monitor gamma. 756 if ((currentTime - lastEventTime > 1 * 1000) && lastEventTimeValid) { 757 //Com_Printf("Restoring monitor gamma after being idle for %f seconds.\n", (currentTime - lastEventTime) / 1000.0); 758 [NSCursor hide]; 759 Sys_SetScreenFade(&glw_state.inGameTable, 1.0); 760 } 761 lastEventTime = [event timestamp] * 1000.0; //currentTime; 762 lastEventTimeValid = YES; 763 764 processEvent(event, lastEventTime); 765 } 766 } 767 #endif 768 } 769 770 /* 771 ======================================================================== 772 773 EVENT LOOP 774 775 ======================================================================== 776 */ 777 778 extern qboolean Sys_GetPacket ( netadr_t *net_from, msg_t *net_message ); 779 780 #define MAX_QUED_EVENTS 256 781 #define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 ) 782 783 static sysEvent_t eventQue[MAX_QUED_EVENTS]; 784 static int eventHead, eventTail; 785 static byte sys_packetReceived[MAX_MSGLEN]; 786 787 /* 788 ================ 789 Sys_QueEvent 790 791 A time of 0 will get the current time 792 Ptr should either be null, or point to a block of data that can 793 be freed by the game later. 794 ================ 795 */ 796 void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) { 797 sysEvent_t *ev; 798 int i,j; 799 #ifndef DEDICATED 800 if (in_showevents->integer) 801 NSLog(@"EVENT ENQUEUE: time=%d type=%d value=0x%08x value2=0x%08x\n", time, type, value, value2); 802 #endif 803 804 if ( eventHead - eventTail >= MAX_QUED_EVENTS ) { 805 Com_Printf("Sys_QueEvent: overflow\n"); 806 } 807 808 if ( !time ) { 809 time = Sys_Milliseconds(); 810 } 811 812 // insert it by time 813 for ( i = eventTail ; i < eventHead ; i++ ) { 814 ev = &eventQue[ i & MASK_QUED_EVENTS ]; 815 if ( ev->evTime > time ) { 816 break; 817 } 818 } 819 820 // insert before i 821 for ( j = eventHead ; j > i ; j-- ) { 822 eventQue[ j & MASK_QUED_EVENTS ] = eventQue[ (j-1) & MASK_QUED_EVENTS ]; 823 } 824 ev = &eventQue[ i & MASK_QUED_EVENTS ]; 825 826 eventHead++; 827 828 ev->evTime = time; 829 ev->evType = type; 830 ev->evValue = value; 831 ev->evValue2 = value2; 832 ev->evPtrLength = ptrLength; 833 ev->evPtr = ptr; 834 } 835 836 /* 837 ================ 838 Sys_GetEvent 839 840 ================ 841 */ 842 sysEvent_t Sys_GetEvent( void ) 843 { 844 sysEvent_t ev; 845 char *s; 846 msg_t netmsg; 847 netadr_t adr; 848 int currentTime; 849 850 // return if we have data 851 if (eventHead > eventTail) { 852 eventTail++; 853 return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; 854 } 855 856 // The queue must be empty. Check all of the event sources. If the events are 857 // already in the queue, we can't imply any real ordering, so we'll avoid extra 858 // system calls and give them all the same time. 859 currentTime = Sys_Milliseconds(); 860 861 // Check for mouse and keyboard events 862 Sys_SendKeyEvents(currentTime); 863 864 // check for console commands 865 s = Sys_ConsoleInput(); 866 if ( s ) { 867 char *b; 868 int len; 869 870 len = strlen( s ) + 1; 871 b = Z_Malloc( len ); 872 strcpy( b, s ); 873 Sys_QueEvent( currentTime, SE_CONSOLE, 0, 0, len, b ); 874 } 875 876 877 // During debugging it is sometimes usefull to be able to start/stop mouse input. 878 // Don't turn on the input when we've disabled it because we're hidden, however. 879 if (!com_dedicated->integer) { 880 if (in_nomouse->integer == mouseactive && !Sys_IsHidden) { 881 if (in_nomouse->integer) 882 Sys_StopMouseInput(); 883 else 884 Sys_StartMouseInput(); 885 } 886 } 887 888 // check for network packets 889 MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) ); 890 if ( Sys_GetPacket ( &adr, &netmsg ) ) { 891 netadr_t *buf; 892 int len; 893 894 // copy out to a seperate buffer for qeueing 895 len = sizeof( netadr_t ) + netmsg.cursize; 896 buf = Z_Malloc( len ); 897 *buf = adr; 898 memcpy( buf+1, netmsg.data, netmsg.cursize ); 899 Sys_QueEvent( currentTime, SE_PACKET, 0, 0, len, buf ); 900 } 901 902 // If we got an event, return it 903 if (eventHead > eventTail) { 904 eventTail++; 905 return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ]; 906 } 907 908 // Otherwise, return an empty event to indicate that there are no events pending. 909 memset( &ev, 0, sizeof( ev ) ); 910 ev.evTime = currentTime; 911 912 return ev; 913 } 914 915 916