doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
macosx_event.mm
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 // -*- mode: objc -*-
30 #import "../../idlib/precompiled.h"
31 
32 #import "macosx_local.h"
33 #import "macosx_sys.h"
34 
35 #import <AppKit/NSCursor.h>
36 #import <AppKit/NSWindow.h>
37 #import <AppKit/NSScreen.h>
38 #import <AppKit/NSApplication.h>
39 #import <AppKit/NSGraphicsContext.h>
40 #import <AppKit/NSEvent.h>
41 
42 #import <Foundation/NSArray.h>
43 #import <Foundation/NSString.h>
44 #import <Foundation/NSRunLoop.h>
45 #import <Carbon/Carbon.h>
46 
47 #import <ApplicationServices/ApplicationServices.h>
48 
49 #import <sys/types.h>
50 #import <sys/time.h>
51 #import <unistd.h>
52 #include <pthread.h>
53 
54 static NSDate *distantPast = NULL;
55 static bool inputActive = false;
56 static bool mouseActive = false;
57 static bool inputRectValid = NO;
58 static CGRect inputRect;
59 static const void *sKLuchrData = NULL;
60 static const void *sKLKCHRData = NULL;
61 
62 int vkeyToDoom3Key[256] = {
63  /*0x00*/ 'a', 's', 'd', 'f', 'h', 'g', 'z', 'x',
64  /*0x08*/ 'c', 'v', '?', 'b', 'q', 'w', 'e', 'r',
65  /*0x10*/ 'y', 't', '1', '2', '3', '4', '6', '5',
66  /*0x18*/ '=', '9', '7', '-', '8', '0', ']', 'o',
67  /*0x20*/ 'u', '[', 'i', 'p', K_ENTER, 'l', 'j', '\'',
68  /*0x28*/ 'k', ';', '\\', ',', '/', 'n', 'm', '.',
69  /*0x30*/ K_TAB, K_SPACE, '`', K_BACKSPACE, '?', K_ESCAPE, '?', K_COMMAND,
70  /*0x38*/ K_SHIFT, K_CAPSLOCK, K_ALT, K_CTRL, '?', '?', '?', '?',
71  /*0x40*/ '?', K_KP_DEL, '?', K_KP_STAR, '?', K_KP_PLUS, '?', K_KP_NUMLOCK,
72  /*0x48*/ '?', '?', '?', K_KP_SLASH, K_KP_ENTER, '?', K_KP_MINUS, '?',
74  /*0x58*/ K_KP_RIGHTARROW, K_KP_HOME, '?', K_KP_UPARROW, K_KP_PGUP, '?', '?', '?',
75  /*0x60*/ K_F5, K_F6, K_F7, K_F3, K_F8, K_F9, '?', K_F11,
76  /*0x68*/ '?', K_PRINT_SCR, '?', K_F14, '?', K_F10, '?', K_F12,
77  /*0x70*/ '?', K_F15, K_INS, K_HOME, K_PGUP, K_DEL, K_F4, K_END,
79 };
80 
82  /*0x00*/ 'q', 's', 'd', 'f', 'h', 'g', 'w', 'x',
83  /*0x08*/ 'c', 'v', '?', 'b', 'a', 'z', 'e', 'r',
84  /*0x10*/ 'y', 't', '1', '2', '3', '4', '6', '5',
85  /*0x18*/ '-', '9', '7', ')', '8', '0', '$', 'o',
86  /*0x20*/ 'u', '^', 'i', 'p', K_ENTER, 'l', 'j', 'ù' /*0x28*/ 'k', 'm', 0x60, ';', '=', 'n', ',', ':', /*0x30*/ K_TAB, K_SPACE, '<', K_BACKSPACE, '?', K_ESCAPE, '?', K_COMMAND, /*0x38*/ K_SHIFT, K_CAPSLOCK, K_ALT, K_CTRL, '?', '?', '?', '?', /*0x40*/ '?', K_KP_DEL, '?', K_KP_STAR, '?', K_KP_PLUS, '?', K_KP_NUMLOCK, /*0x48*/ '?', '?', '?', K_KP_SLASH, K_KP_ENTER, '?', K_KP_MINUS, '?', /*0x50*/ '?', K_KP_EQUALS, K_KP_INS, K_KP_END, K_KP_DOWNARROW, K_KP_PGDN, K_KP_LEFTARROW, K_KP_5, /*0x58*/ K_KP_RIGHTARROW, K_KP_HOME, '?', K_KP_UPARROW, K_KP_PGUP, '?', '?', '?', /*0x60*/ K_F5, K_F6, K_F7, K_F3, K_F8, K_F9, '?', K_F11, /*0x68*/ '?', K_PRINT_SCR, '?', K_F14, '?', K_F10, '?', K_F12, /*0x70*/ '?', K_F15, K_INS, K_HOME, K_PGUP, K_DEL, K_F4, K_END, /*0x78*/ K_F2, K_PGDN, K_F1, K_LEFTARROW, K_RIGHTARROW, K_DOWNARROW, K_UPARROW, K_POWER }; int vkeyToDoom3Key_German[256] = { /*0x00*/ 'a', 's', 'd', 'f', 'h', 'g', 'y', 'x', /*0x08*/ 'c', 'v', '?', 'b', 'q', 'w', 'e', 'r', /*0x10*/ 'z', 't', '1', '2', '3', '4', '6', '5', /*0x18*/ '«', '9', '7', '-', '8', '0', '+', 'o', /*0x20*/ 'u', '[', 'i', 'p', K_ENTER, 'l', 'j', '\'', /*0x28*/ 'k', ';', '#', ',', '-', 'n', 'm', '.', /*0x30*/ K_TAB, K_SPACE, '`', K_BACKSPACE, '?', K_ESCAPE, '?', K_COMMAND, /*0x38*/ K_SHIFT, K_CAPSLOCK, K_ALT, K_CTRL, '?', '?', '?', '?', /*0x40*/ '?', K_KP_DEL, '?', K_KP_STAR, '?', K_KP_PLUS, '?', K_KP_NUMLOCK, /*0x48*/ '?', '?', '?', K_KP_SLASH, K_KP_ENTER, '?', K_KP_MINUS, '?', /*0x50*/ '?', K_KP_EQUALS, K_KP_INS, K_KP_END, K_KP_DOWNARROW, K_KP_PGDN, K_KP_LEFTARROW, K_KP_5, /*0x58*/ K_KP_RIGHTARROW, K_KP_HOME, '?', K_KP_UPARROW, K_KP_PGUP, '?', '?', '?', /*0x60*/ K_F5, K_F6, K_F7, K_F3, K_F8, K_F9, '?', K_F11, /*0x68*/ '?', K_PRINT_SCR, '?', K_F14, '?', K_F10, '?', K_F12, /*0x70*/ '?', K_F15, K_INS, K_HOME, K_PGUP, K_DEL, K_F4, K_END, /*0x78*/ K_F2, K_PGDN, K_F1, K_LEFTARROW, K_RIGHTARROW, K_DOWNARROW, K_UPARROW, K_POWER }; static const int *vkeyTable = vkeyToDoom3Key; /* =========== Sys_InitScanTable =========== */ void Sys_InitScanTable( void ) { KeyboardLayoutRef kbLayout; idStr lang = cvarSystem->GetCVarString( "sys_lang" ); if ( lang.Length() == 0 ) { lang = "english"; } if ( lang.Icmp( "english" ) == 0 ) { vkeyTable = vkeyToDoom3Key; } else if ( lang.Icmp( "french" ) == 0 ) { vkeyTable = vkeyToDoom3Key_French; } else if ( lang.Icmp( "german" ) == 0 ) { vkeyTable = vkeyToDoom3Key_German; } if ( KLGetCurrentKeyboardLayout( &kbLayout ) == 0 ) { if ( KLGetKeyboardLayoutProperty( kbLayout, kKLuchrData, &sKLuchrData ) ) { common->Warning("KLGetKeyboardLayoutProperty failed"); } if ( !sKLuchrData ) { if ( KLGetKeyboardLayoutProperty( kbLayout, kKLKCHRData, &sKLKCHRData ) ) { common->Warning("KLGetKeyboardLayoutProperty failed"); } } } if ( !sKLuchrData && !sKLKCHRData ) { common->Warning("Keyboard input initialziation failed"); } } void Sys_InitInput( void ) { common->Printf( "------- Input Initialization -------\n" ); if ( !distantPast ) { distantPast = [ [ NSDate distantPast ] retain ]; } IN_ActivateMouse(); inputActive = true; } void Sys_ShutdownInput( void ) { common->Printf( "------- Input Shutdown -------\n" ); if ( !inputActive ) { return; } inputActive = false; if ( mouseActive ) { IN_DeactivateMouse(); } common->Printf( "------------------------------\n" ); } void processMouseMovedEvent( NSEvent *mouseMovedEvent ) { CGMouseDelta dx, dy; if ( !mouseActive ) { return; } #if 0 #define ACT_LIKE_WINDOWS #ifdef ACT_LIKE_WINDOWS cvar_t *in_mouseLowEndSlope = Cvar_Get("in_mouseLowEndSlope", "3.5", CVAR_ARCHIVE); if (in_mouseLowEndSlope->value < 1) { Cvar_Set("in_mouseLowEndSlope", "1"); } #else cvar_t *in_mouseLowEndSlope = Cvar_Get("in_mouseLowEndSlope", "1", CVAR_ARCHIVE); if (in_mouseLowEndSlope->value < 1) { Cvar_Set("in_mouseLowEndSlope", "1"); } #endif cvar_t *in_mouseHighEndCutoff = Cvar_Get("in_mouseHighEndCutoff", "20", CVAR_ARCHIVE); if (in_mouseLowEndSlope->value < 1) { Cvar_Set("in_mouseHighEndCutoff", "1"); } #endif CGGetLastMouseDelta(&dx, &dy); if ( dx || dy ) { #if 0 // this is be handled by the mouse driver clean me out later CGMouseDelta distSqr; float m0, N; distSqr = dx * dx + dy * dy; //Com_Printf("distSqr = %d\n", distSqr); /* This code is here to help people that like the feel of the Logitech USB Gaming Mouse with the Win98 drivers. By empirical testing, the Windows drivers seem to be more heavily accelerated at the low end of the curve. */ //N = in_mouseHighEndCutoff->value; N = 1; if (distSqr < N*N) { float dist, accel, scale; //m0 = in_mouseLowEndSlope->value; m0 = 1; dist = sqrt(distSqr); accel = (((m0 - 1.0)/(N*N) * dist + (2.0 - 2.0*m0)/N) * dist + m0) * dist; scale = accel / dist; //Com_Printf("dx = %d, dy = %d, dist = %f, accel = %f, scale = %f\n", dx, dy, dist, accel, scale); dx *= scale; dy *= scale; } #endif Posix_QueEvent( SE_MOUSE, dx, dy, 0, NULL ); Posix_AddMousePollEvent( M_DELTAX, dx ); Posix_AddMousePollEvent( M_DELTAY, dy ); } } inline bool OSX_LookupCharacter(unsigned short vkey, unsigned int modifiers, bool keyDownFlag, unsigned char *outChar) { UInt32 translated; UInt32 deadKeyState = 0; UniChar unicodeString[16]; UniCharCount actualStringLength = 0; static UInt32 keyTranslateState = 0; // Only want character if Translate() returns a single character if ( sKLuchrData ) { UCKeyTranslate( (UCKeyboardLayout*)sKLuchrData, vkey, keyDownFlag ? kUCKeyActionDown : kUCKeyActionUp, modifiers, LMGetKbdType(), 0, &deadKeyState, 16, &actualStringLength, unicodeString ); if ( actualStringLength == 1 ) { *outChar = (unsigned char)unicodeString[0]; return true; } } else if ( sKLKCHRData ) { translated = KeyTranslate( sKLKCHRData, vkey, &keyTranslateState ); if ( ( translated & 0x00ff0000 ) == 0 ) { *outChar = translated & 0xff; return true; } } return false; } void OSX_ProcessKeyEvent( NSEvent *keyEvent, bool keyDownFlag ) { unsigned char character; unsigned int modifiers = 0; unsigned short vkey = [ keyEvent keyCode ]; if ( [ keyEvent modifierFlags ] & NSAlphaShiftKeyMask ) modifiers |= alphaLock; if ( [ keyEvent modifierFlags ] & NSShiftKeyMask ) modifiers |= shiftKey; if ( [ keyEvent modifierFlags ] & NSControlKeyMask ) modifiers |= controlKey; if ( [ keyEvent modifierFlags ] & NSAlternateKeyMask ) modifiers |= optionKey; if ( [ keyEvent modifierFlags ] & NSCommandKeyMask ) modifiers |= cmdKey; modifiers >>= 8; int doomKey = (unsigned char)vkeyTable[vkey]; Posix_QueEvent( SE_KEY, doomKey, keyDownFlag, 0, NULL ); if ( keyDownFlag ) { if ( OSX_LookupCharacter(vkey, modifiers, keyDownFlag, &character ) && character != Sys_GetConsoleKey( false ) && character != Sys_GetConsoleKey( true ) ) { Posix_QueEvent( SE_CHAR, character, 0, 0, NULL); } } Posix_AddKeyboardPollEvent( doomKey, keyDownFlag ); return; } void sendEventForMaskChangeInFlags( int quakeKey, unsigned int modifierMask, unsigned int oldModifierFlags, unsigned int newModifierFlags ) { bool oldHadModifier, newHasModifier; oldHadModifier = (oldModifierFlags & modifierMask) != 0; newHasModifier = (newModifierFlags & modifierMask) != 0; if (oldHadModifier != newHasModifier) { //NSLog(@"Key %d posted for modifier mask modifierMask", quakeKey); Posix_QueEvent( SE_KEY, quakeKey, newHasModifier, 0, NULL); Posix_AddKeyboardPollEvent( quakeKey, newHasModifier ); } } void processFlagsChangedEvent( NSEvent *flagsChangedEvent ) { static int oldModifierFlags; int newModifierFlags; newModifierFlags = [flagsChangedEvent modifierFlags]; sendEventForMaskChangeInFlags( K_ALT, NSAlternateKeyMask, oldModifierFlags, newModifierFlags ); sendEventForMaskChangeInFlags( K_CTRL, NSControlKeyMask, oldModifierFlags, newModifierFlags ); sendEventForMaskChangeInFlags( K_SHIFT, NSShiftKeyMask, oldModifierFlags, newModifierFlags ); oldModifierFlags = newModifierFlags; } void processSystemDefinedEvent( NSEvent *systemDefinedEvent ) { static int oldButtons = 0; int buttonsDelta; int buttons; int isDown; if ( [systemDefinedEvent subtype] == 7 ) { if ( !mouseActive ) { return; } buttons = [systemDefinedEvent data2]; buttonsDelta = oldButtons ^ buttons; //common->Printf( "uberbuttons: %08lx %08lx\n", buttonsDelta, buttons ); if (buttonsDelta & 1) { isDown = buttons & 1; Posix_QueEvent( SE_KEY, K_MOUSE1, isDown, 0, NULL); Posix_AddMousePollEvent( M_ACTION1, isDown ); } if (buttonsDelta & 2) { isDown = buttons & 2; Posix_QueEvent( SE_KEY, K_MOUSE2, isDown, 0, NULL); Posix_AddMousePollEvent( M_ACTION2, isDown ); } if (buttonsDelta & 4) { isDown = buttons & 4; Posix_QueEvent( SE_KEY, K_MOUSE3, isDown, 0, NULL); Posix_AddMousePollEvent( M_ACTION3, isDown ); } if (buttonsDelta & 8) { isDown = buttons & 8; Posix_QueEvent( SE_KEY, K_MOUSE4, isDown, 0, NULL); Posix_AddMousePollEvent( M_ACTION4, isDown ); } if (buttonsDelta & 16) { isDown = buttons & 16; Posix_QueEvent( SE_KEY, K_MOUSE5, isDown, 0, NULL); Posix_AddMousePollEvent( M_ACTION5, isDown ); } oldButtons = buttons; } } void processEvent( NSEvent *event ) { NSEventType eventType; if ( !inputActive ) { return; } eventType = [ event type ]; switch ( eventType ) { // These four 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. case NSLeftMouseDown: case NSLeftMouseUp: case NSRightMouseDown: case NSRightMouseUp: //NSLog( @"ignore simple mouse event %@", event ); return; case NSMouseMoved: case NSLeftMouseDragged: case NSRightMouseDragged: processMouseMovedEvent( event ); return; case NSKeyDown: // Send ALL command key-ups to Quake, but not command key-downs, otherwise if the user hits a key, presses command, and lets up on the key, the key-up won't register. if ( [ event modifierFlags ] & NSCommandKeyMask ) { NSLog( @"command key up ignored: %@", event ); break; } case NSKeyUp: OSX_ProcessKeyEvent( event, eventType == NSKeyDown ); return; case NSFlagsChanged: processFlagsChangedEvent( event ); return; case NSSystemDefined: processSystemDefinedEvent( event ); return; case NSScrollWheel: if ([event deltaY] < 0.0) { Posix_QueEvent( SE_KEY, K_MWHEELDOWN, true, 0, NULL ); Posix_QueEvent( SE_KEY, K_MWHEELDOWN, false, 0, NULL ); Posix_AddMousePollEvent( M_DELTAZ, -1 ); } else { Posix_QueEvent( SE_KEY, K_MWHEELUP, true, 0, NULL ); Posix_QueEvent( SE_KEY, K_MWHEELUP, false, 0, NULL ); Posix_AddMousePollEvent( M_DELTAZ, 1 ); } return; default: //NSLog( @"handle event %@", event ); break; } [NSApp sendEvent:event]; } void Posix_PollInput( void ) { NSEvent *event; unsigned int eventMask; eventMask = NSAnyEventMask; while ( ( event = [ NSApp nextEventMatchingMask: eventMask untilDate: distantPast inMode: NSDefaultRunLoopMode dequeue:YES ] ) ) { processEvent( event ); } } void Sys_PreventMouseMovement( CGPoint point ) { CGEventErr err; //common->Printf( "**** Calling CGAssociateMouseAndMouseCursorPosition(false)\n" ); err = CGAssociateMouseAndMouseCursorPosition( false ); if ( err != CGEventNoErr ) { common->Error( "Could not disable mouse movement, CGAssociateMouseAndMouseCursorPosition returned %d\n", err ); } // Put the mouse in the position we want to leave it at err = CGWarpMouseCursorPosition( point ); if ( err != CGEventNoErr ) { common->Error( "Could not disable mouse movement, CGWarpMouseCursorPosition returned %d\n", err ); } } void Sys_ReenableMouseMovement() { CGEventErr err; //common->Printf( "**** Calling CGAssociateMouseAndMouseCursorPosition(true)\n" ); err = CGAssociateMouseAndMouseCursorPosition( true ); if ( err != CGEventNoErr ) { common->Error( "Could not reenable mouse movement, CGAssociateMouseAndMouseCursorPosition returned %d\n", err ); } // Leave the mouse where it was -- don't warp here. } void Sys_LockMouseInInputRect(CGRect rect) { CGPoint center; center.x = rect.origin.x + rect.size.width / 2.0; center.y = rect.origin.y + rect.size.height / 2.0; // Now, put the mouse in the middle of the input rect (anywhere over it would do) // and don't allow it to move. This means that the user won't be able to accidentally // select another application. Sys_PreventMouseMovement(center); } void Sys_SetMouseInputRect(CGRect newRect) { inputRectValid = YES; inputRect = newRect; if ( mouseActive ) { Sys_LockMouseInInputRect( inputRect ); } } void IN_ActivateMouse( void ) { if ( mouseActive ) { return; } if ( inputRectValid ) { // Make sure that if window moved we don't hose the user... Sys_UpdateWindowMouseInputRect(); } Sys_LockMouseInInputRect( inputRect ); CGDisplayHideCursor( Sys_DisplayToUse() ); mouseActive = true; } void IN_DeactivateMouse( void ) { if ( !mouseActive ) { return; } Sys_ReenableMouseMovement(); CGDisplayShowCursor( Sys_DisplayToUse() ); mouseActive = false; } /* =============== Sys_MapCharForKey =============== */ unsigned char Sys_MapCharForKey( int key ) { return (unsigned char)key; } /* =============== Sys_GetConsoleKey =============== */ unsigned char Sys_GetConsoleKey( bool shifted ) { if ( vkeyTable == vkeyToDoom3Key_French ) { return shifted ? '>' : '<'; } else { return shifted ? '~' : '`'; } } ,
87  /*0x28*/ 'k', 'm', 0x60, ';', '=', 'n', ',', ':',
88  /*0x30*/ K_TAB, K_SPACE, '<', K_BACKSPACE, '?', K_ESCAPE, '?', K_COMMAND,
89  /*0x38*/ K_SHIFT, K_CAPSLOCK, K_ALT, K_CTRL, '?', '?', '?', '?',
90  /*0x40*/ '?', K_KP_DEL, '?', K_KP_STAR, '?', K_KP_PLUS, '?', K_KP_NUMLOCK,
91  /*0x48*/ '?', '?', '?', K_KP_SLASH, K_KP_ENTER, '?', K_KP_MINUS, '?',
93  /*0x58*/ K_KP_RIGHTARROW, K_KP_HOME, '?', K_KP_UPARROW, K_KP_PGUP, '?', '?', '?',
94  /*0x60*/ K_F5, K_F6, K_F7, K_F3, K_F8, K_F9, '?', K_F11,
95  /*0x68*/ '?', K_PRINT_SCR, '?', K_F14, '?', K_F10, '?', K_F12,
96  /*0x70*/ '?', K_F15, K_INS, K_HOME, K_PGUP, K_DEL, K_F4, K_END,
98 };
99 
101  /*0x00*/ 'a', 's', 'd', 'f', 'h', 'g', 'y', 'x',
102  /*0x08*/ 'c', 'v', '?', 'b', 'q', 'w', 'e', 'r',
103  /*0x10*/ 'z', 't', '1', '2', '3', '4', '6', '5',
104  /*0x18*/ '«', '9', '7', '-', '8', '0', '+', 'o',
105  /*0x20*/ 'u', '[', 'i', 'p', K_ENTER, 'l', 'j', '\'',
106  /*0x28*/ 'k', ';', '#', ',', '-', 'n', 'm', '.',
107  /*0x30*/ K_TAB, K_SPACE, '`', K_BACKSPACE, '?', K_ESCAPE, '?', K_COMMAND,
108  /*0x38*/ K_SHIFT, K_CAPSLOCK, K_ALT, K_CTRL, '?', '?', '?', '?',
109  /*0x40*/ '?', K_KP_DEL, '?', K_KP_STAR, '?', K_KP_PLUS, '?', K_KP_NUMLOCK,
110  /*0x48*/ '?', '?', '?', K_KP_SLASH, K_KP_ENTER, '?', K_KP_MINUS, '?',
112  /*0x58*/ K_KP_RIGHTARROW, K_KP_HOME, '?', K_KP_UPARROW, K_KP_PGUP, '?', '?', '?',
113  /*0x60*/ K_F5, K_F6, K_F7, K_F3, K_F8, K_F9, '?', K_F11,
114  /*0x68*/ '?', K_PRINT_SCR, '?', K_F14, '?', K_F10, '?', K_F12,
115  /*0x70*/ '?', K_F15, K_INS, K_HOME, K_PGUP, K_DEL, K_F4, K_END,
117 };
118 
119 static const int *vkeyTable = vkeyToDoom3Key;
120 
121 /*
122  ===========
123  Sys_InitScanTable
124  ===========
125  */
126 void Sys_InitScanTable( void ) {
127  KeyboardLayoutRef kbLayout;
128 
129  idStr lang = cvarSystem->GetCVarString( "sys_lang" );
130  if ( lang.Length() == 0 ) {
131  lang = "english";
132  }
133 
134  if ( lang.Icmp( "english" ) == 0 ) {
135  vkeyTable = vkeyToDoom3Key;
136  } else if ( lang.Icmp( "french" ) == 0 ) {
137  vkeyTable = vkeyToDoom3Key_French;
138  } else if ( lang.Icmp( "german" ) == 0 ) {
139  vkeyTable = vkeyToDoom3Key_German;
140  }
141 
142  if ( KLGetCurrentKeyboardLayout( &kbLayout ) == 0 ) {
143  if ( KLGetKeyboardLayoutProperty( kbLayout, kKLuchrData, &sKLuchrData ) ) {
144  common->Warning("KLGetKeyboardLayoutProperty failed");
145  }
146  if ( !sKLuchrData ) {
147  if ( KLGetKeyboardLayoutProperty( kbLayout, kKLKCHRData, &sKLKCHRData ) ) {
148  common->Warning("KLGetKeyboardLayoutProperty failed");
149  }
150  }
151  }
152  if ( !sKLuchrData && !sKLKCHRData ) {
153  common->Warning("Keyboard input initialziation failed");
154  }
155 }
156 
157 void Sys_InitInput( void ) {
158  common->Printf( "------- Input Initialization -------\n" );
159 
160  if ( !distantPast ) {
161  distantPast = [ [ NSDate distantPast ] retain ];
162  }
163 
165 
166  inputActive = true;
167 }
168 
169 void Sys_ShutdownInput( void ) {
170  common->Printf( "------- Input Shutdown -------\n" );
171 
172  if ( !inputActive ) {
173  return;
174  }
175  inputActive = false;
176  if ( mouseActive ) {
178  }
179 
180  common->Printf( "------------------------------\n" );
181 }
182 
183 void processMouseMovedEvent( NSEvent *mouseMovedEvent ) {
184  CGMouseDelta dx, dy;
185 
186  if ( !mouseActive ) {
187  return;
188  }
189 
190 #if 0
191 
192 #define ACT_LIKE_WINDOWS
193 #ifdef ACT_LIKE_WINDOWS
194  cvar_t *in_mouseLowEndSlope = Cvar_Get("in_mouseLowEndSlope", "3.5", CVAR_ARCHIVE);
195  if (in_mouseLowEndSlope->value < 1) {
196  Cvar_Set("in_mouseLowEndSlope", "1");
197  }
198 #else
199  cvar_t *in_mouseLowEndSlope = Cvar_Get("in_mouseLowEndSlope", "1", CVAR_ARCHIVE);
200  if (in_mouseLowEndSlope->value < 1) {
201  Cvar_Set("in_mouseLowEndSlope", "1");
202  }
203 #endif
204 
205  cvar_t *in_mouseHighEndCutoff = Cvar_Get("in_mouseHighEndCutoff", "20", CVAR_ARCHIVE);
206  if (in_mouseLowEndSlope->value < 1) {
207  Cvar_Set("in_mouseHighEndCutoff", "1");
208  }
209 
210 #endif
211 
212  CGGetLastMouseDelta(&dx, &dy);
213 
214  if ( dx || dy ) {
215  #if 0 // this is be handled by the mouse driver clean me out later
216  CGMouseDelta distSqr;
217  float m0, N;
218 
219  distSqr = dx * dx + dy * dy;
220  //Com_Printf("distSqr = %d\n", distSqr);
221 
222  /* This code is here to help people that like the feel of the Logitech USB Gaming Mouse with the Win98 drivers. By empirical testing, the Windows drivers seem to be more heavily accelerated at the low end of the curve. */
223  //N = in_mouseHighEndCutoff->value;
224  N = 1;
225 
226  if (distSqr < N*N) {
227  float dist, accel, scale;
228 
229  //m0 = in_mouseLowEndSlope->value;
230  m0 = 1;
231  dist = sqrt(distSqr);
232  accel = (((m0 - 1.0)/(N*N) * dist + (2.0 - 2.0*m0)/N) * dist + m0) * dist;
233 
234  scale = accel / dist;
235  //Com_Printf("dx = %d, dy = %d, dist = %f, accel = %f, scale = %f\n", dx, dy, dist, accel, scale);
236 
237  dx *= scale;
238  dy *= scale;
239  }
240 #endif
241  Posix_QueEvent( SE_MOUSE, dx, dy, 0, NULL );
244  }
245 }
246 
247 inline bool OSX_LookupCharacter(unsigned short vkey, unsigned int modifiers, bool keyDownFlag, unsigned char *outChar)
248 {
249  UInt32 translated;
250  UInt32 deadKeyState = 0;
251  UniChar unicodeString[16];
252  UniCharCount actualStringLength = 0;
253  static UInt32 keyTranslateState = 0;
254 
255  // Only want character if Translate() returns a single character
256  if ( sKLuchrData ) {
257  UCKeyTranslate( (UCKeyboardLayout*)sKLuchrData, vkey, keyDownFlag ? kUCKeyActionDown : kUCKeyActionUp, modifiers,
258  LMGetKbdType(), 0, &deadKeyState, 16, &actualStringLength, unicodeString );
259 
260  if ( actualStringLength == 1 ) {
261  *outChar = (unsigned char)unicodeString[0];
262  return true;
263  }
264  }
265  else if ( sKLKCHRData ) {
266  translated = KeyTranslate( sKLKCHRData, vkey, &keyTranslateState );
267  if ( ( translated & 0x00ff0000 ) == 0 ) {
268  *outChar = translated & 0xff;
269  return true;
270  }
271  }
272  return false;
273 }
274 
275 void OSX_ProcessKeyEvent( NSEvent *keyEvent, bool keyDownFlag ) {
276  unsigned char character;
277  unsigned int modifiers = 0;
278  unsigned short vkey = [ keyEvent keyCode ];
279 
280  if ( [ keyEvent modifierFlags ] & NSAlphaShiftKeyMask )
281  modifiers |= alphaLock;
282  if ( [ keyEvent modifierFlags ] & NSShiftKeyMask )
283  modifiers |= shiftKey;
284  if ( [ keyEvent modifierFlags ] & NSControlKeyMask )
285  modifiers |= controlKey;
286  if ( [ keyEvent modifierFlags ] & NSAlternateKeyMask )
287  modifiers |= optionKey;
288  if ( [ keyEvent modifierFlags ] & NSCommandKeyMask )
289  modifiers |= cmdKey;
290  modifiers >>= 8;
291 
292  int doomKey = (unsigned char)vkeyTable[vkey];
293  Posix_QueEvent( SE_KEY, doomKey, keyDownFlag, 0, NULL );
294  if ( keyDownFlag ) {
295  if ( OSX_LookupCharacter(vkey, modifiers, keyDownFlag, &character ) &&
296  character != Sys_GetConsoleKey( false ) && character != Sys_GetConsoleKey( true ) ) {
297  Posix_QueEvent( SE_CHAR, character, 0, 0, NULL);
298  }
299  }
300  Posix_AddKeyboardPollEvent( doomKey, keyDownFlag );
301 
302  return;
303 }
304 
305 void sendEventForMaskChangeInFlags( int quakeKey, unsigned int modifierMask, unsigned int oldModifierFlags, unsigned int newModifierFlags ) {
306  bool oldHadModifier, newHasModifier;
307 
308  oldHadModifier = (oldModifierFlags & modifierMask) != 0;
309  newHasModifier = (newModifierFlags & modifierMask) != 0;
310  if (oldHadModifier != newHasModifier) {
311  //NSLog(@"Key %d posted for modifier mask modifierMask", quakeKey);
312  Posix_QueEvent( SE_KEY, quakeKey, newHasModifier, 0, NULL);
313  Posix_AddKeyboardPollEvent( quakeKey, newHasModifier );
314  }
315 }
316 
317 void processFlagsChangedEvent( NSEvent *flagsChangedEvent ) {
318  static int oldModifierFlags;
319  int newModifierFlags;
320 
321  newModifierFlags = [flagsChangedEvent modifierFlags];
322  sendEventForMaskChangeInFlags( K_ALT, NSAlternateKeyMask, oldModifierFlags, newModifierFlags );
323  sendEventForMaskChangeInFlags( K_CTRL, NSControlKeyMask, oldModifierFlags, newModifierFlags );
324  sendEventForMaskChangeInFlags( K_SHIFT, NSShiftKeyMask, oldModifierFlags, newModifierFlags );
325  oldModifierFlags = newModifierFlags;
326 }
327 
328 void processSystemDefinedEvent( NSEvent *systemDefinedEvent ) {
329  static int oldButtons = 0;
330  int buttonsDelta;
331  int buttons;
332  int isDown;
333 
334  if ( [systemDefinedEvent subtype] == 7 ) {
335 
336  if ( !mouseActive ) {
337  return;
338  }
339 
340  buttons = [systemDefinedEvent data2];
341  buttonsDelta = oldButtons ^ buttons;
342 
343  //common->Printf( "uberbuttons: %08lx %08lx\n", buttonsDelta, buttons );
344 
345  if (buttonsDelta & 1) {
346  isDown = buttons & 1;
347  Posix_QueEvent( SE_KEY, K_MOUSE1, isDown, 0, NULL);
349  }
350 
351  if (buttonsDelta & 2) {
352  isDown = buttons & 2;
353  Posix_QueEvent( SE_KEY, K_MOUSE2, isDown, 0, NULL);
355  }
356 
357  if (buttonsDelta & 4) {
358  isDown = buttons & 4;
359  Posix_QueEvent( SE_KEY, K_MOUSE3, isDown, 0, NULL);
361  }
362 
363  if (buttonsDelta & 8) {
364  isDown = buttons & 8;
365  Posix_QueEvent( SE_KEY, K_MOUSE4, isDown, 0, NULL);
367  }
368 
369  if (buttonsDelta & 16) {
370  isDown = buttons & 16;
371  Posix_QueEvent( SE_KEY, K_MOUSE5, isDown, 0, NULL);
373  }
374 
375  oldButtons = buttons;
376  }
377 }
378 
379 void processEvent( NSEvent *event ) {
380  NSEventType eventType;
381 
382  if ( !inputActive ) {
383  return;
384  }
385 
386  eventType = [ event type ];
387 
388  switch ( eventType ) {
389  // These four 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.
390  case NSLeftMouseDown:
391  case NSLeftMouseUp:
392  case NSRightMouseDown:
393  case NSRightMouseUp:
394  //NSLog( @"ignore simple mouse event %@", event );
395  return;
396  case NSMouseMoved:
397  case NSLeftMouseDragged:
398  case NSRightMouseDragged:
399  processMouseMovedEvent( event );
400  return;
401  case NSKeyDown:
402  // Send ALL command key-ups to Quake, but not command key-downs, otherwise if the user hits a key, presses command, and lets up on the key, the key-up won't register.
403  if ( [ event modifierFlags ] & NSCommandKeyMask ) {
404  NSLog( @"command key up ignored: %@", event );
405  break;
406  }
407  case NSKeyUp:
408  OSX_ProcessKeyEvent( event, eventType == NSKeyDown );
409  return;
410  case NSFlagsChanged:
411  processFlagsChangedEvent( event );
412  return;
413  case NSSystemDefined:
414  processSystemDefinedEvent( event );
415  return;
416  case NSScrollWheel:
417  if ([event deltaY] < 0.0) {
418  Posix_QueEvent( SE_KEY, K_MWHEELDOWN, true, 0, NULL );
419  Posix_QueEvent( SE_KEY, K_MWHEELDOWN, false, 0, NULL );
421  } else {
422  Posix_QueEvent( SE_KEY, K_MWHEELUP, true, 0, NULL );
423  Posix_QueEvent( SE_KEY, K_MWHEELUP, false, 0, NULL );
425  }
426  return;
427  default:
428  //NSLog( @"handle event %@", event );
429  break;
430  }
431  [NSApp sendEvent:event];
432 }
433 
434 void Posix_PollInput( void ) {
435  NSEvent *event;
436  unsigned int eventMask;
437 
438  eventMask = NSAnyEventMask;
439 
440  while ( ( event = [ NSApp nextEventMatchingMask: eventMask
441  untilDate: distantPast
442  inMode: NSDefaultRunLoopMode
443  dequeue:YES ] ) ) {
444  processEvent( event );
445  }
446 }
447 
448 void Sys_PreventMouseMovement( CGPoint point ) {
449  CGEventErr err;
450 
451  //common->Printf( "**** Calling CGAssociateMouseAndMouseCursorPosition(false)\n" );
452  err = CGAssociateMouseAndMouseCursorPosition( false );
453  if ( err != CGEventNoErr ) {
454  common->Error( "Could not disable mouse movement, CGAssociateMouseAndMouseCursorPosition returned %d\n", err );
455  }
456 
457  // Put the mouse in the position we want to leave it at
458  err = CGWarpMouseCursorPosition( point );
459  if ( err != CGEventNoErr ) {
460  common->Error( "Could not disable mouse movement, CGWarpMouseCursorPosition returned %d\n", err );
461  }
462 }
463 
465  CGEventErr err;
466 
467  //common->Printf( "**** Calling CGAssociateMouseAndMouseCursorPosition(true)\n" );
468  err = CGAssociateMouseAndMouseCursorPosition( true );
469  if ( err != CGEventNoErr ) {
470  common->Error( "Could not reenable mouse movement, CGAssociateMouseAndMouseCursorPosition returned %d\n", err );
471  }
472 
473  // Leave the mouse where it was -- don't warp here.
474 }
475 
476 void Sys_LockMouseInInputRect(CGRect rect) {
477  CGPoint center;
478 
479  center.x = rect.origin.x + rect.size.width / 2.0;
480  center.y = rect.origin.y + rect.size.height / 2.0;
481 
482  // Now, put the mouse in the middle of the input rect (anywhere over it would do)
483  // and don't allow it to move. This means that the user won't be able to accidentally
484  // select another application.
485  Sys_PreventMouseMovement(center);
486 }
487 
488 void Sys_SetMouseInputRect(CGRect newRect) {
489  inputRectValid = YES;
490  inputRect = newRect;
491 
492  if ( mouseActive ) {
493  Sys_LockMouseInInputRect( inputRect );
494  }
495 }
496 
497 void IN_ActivateMouse( void ) {
498  if ( mouseActive ) {
499  return;
500  }
501  if ( inputRectValid ) {
502  // Make sure that if window moved we don't hose the user...
504  }
505  Sys_LockMouseInInputRect( inputRect );
506  CGDisplayHideCursor( Sys_DisplayToUse() );
507  mouseActive = true;
508 }
509 
510 void IN_DeactivateMouse( void ) {
511  if ( !mouseActive ) {
512  return;
513  }
515  CGDisplayShowCursor( Sys_DisplayToUse() );
516  mouseActive = false;
517 }
518 
519 /*
520 ===============
521 Sys_MapCharForKey
522 ===============
523 */
524 unsigned char Sys_MapCharForKey( int key ) {
525  return (unsigned char)key;
526 }
527 
528 /*
529  ===============
530  Sys_GetConsoleKey
531  ===============
532  */
533 unsigned char Sys_GetConsoleKey( bool shifted ) {
534  if ( vkeyTable == vkeyToDoom3Key_French ) {
535  return shifted ? '>' : '<';
536  }
537  else {
538  return shifted ? '~' : '`';
539  }
540 
541 }
542 
Definition: KeyInput.h:78
Definition: KeyInput.h:83
void Sys_InitInput(void)
Definition: KeyInput.h:82
bool Posix_AddKeyboardPollEvent(int key, bool state)
Definition: posix_input.cpp:55
idCVarSystem * cvarSystem
Definition: CVarSystem.cpp:487
Definition: KeyInput.h:88
Definition: KeyInput.h:86
int vkeyToDoom3Key_French[256]
Definition: macosx_event.mm:81
void Sys_LockMouseInInputRect(CGRect rect)
GLenum GLenum GLenum GLenum GLenum scale
Definition: glext.h:4804
Definition: KeyInput.h:74
int Length(void) const
Definition: Str.h:702
Definition: KeyInput.h:85
bool Posix_AddMousePollEvent(int action, int value)
Definition: posix_input.cpp:72
void Posix_PollInput(void)
Definition: KeyInput.h:81
CGDirectDisplayID Sys_DisplayToUse(void)
unsigned char Sys_GetConsoleKey(bool shifted)
Definition: eax4.h:1413
void Sys_ShutdownInput(void)
Definition: KeyInput.h:91
int vkeyToDoom3Key[256]
Definition: macosx_event.mm:62
int Icmp(const char *text) const
Definition: Str.h:667
bool OSX_LookupCharacter(unsigned short vkey, unsigned int modifiers, bool keyDownFlag, unsigned char *outChar)
void Sys_ReenableMouseMovement()
void processMouseMovedEvent(NSEvent *mouseMovedEvent)
void sendEventForMaskChangeInFlags(int quakeKey, unsigned int modifierMask, unsigned int oldModifierFlags, unsigned int newModifierFlags)
Definition: KeyInput.h:90
Definition: KeyInput.h:80
Definition: KeyInput.h:73
unsigned char Sys_MapCharForKey(int key)
void Sys_PreventMouseMovement(CGPoint point)
idCommon * common
Definition: Common.cpp:206
#define NULL
Definition: Lib.h:88
virtual const char * GetCVarString(const char *name) const =0
Definition: KeyInput.h:94
void IN_DeactivateMouse(void)
Definition: KeyInput.h:47
Definition: KeyInput.h:84
void OSX_ProcessKeyEvent(NSEvent *keyEvent, bool keyDownFlag)
virtual void Printf(const char *fmt,...) id_attribute((format(printf
Definition: KeyInput.h:87
void Sys_InitScanTable(void)
void Sys_SetMouseInputRect(CGRect newRect)
void IN_ActivateMouse(void)
static WindowRef ValidModeCallbackProc inCallback OSStatus err
void Posix_QueEvent(sysEventType_t type, int value, int value2, int ptrLength, void *ptr)
Definition: posix_main.cpp:275
Definition: KeyInput.h:70
Definition: Str.h:116
void Sys_UpdateWindowMouseInputRect(void)
void processEvent(NSEvent *event)
void processFlagsChangedEvent(NSEvent *flagsChangedEvent)
virtual void Error(const char *fmt,...) id_attribute((format(printf
int vkeyToDoom3Key_German[256]
virtual void virtual void Warning(const char *fmt,...) id_attribute((format(printf
Definition: KeyInput.h:89
Definition: KeyInput.h:95
void processSystemDefinedEvent(NSEvent *systemDefinedEvent)