macView.mm
Engine/source/windowManager/mac/macView.mm
Public Defines
define
WHEEL_DELTA() ( 120 * 0.1 )
Public Variables
Public Functions
NSModifiersToTorqueModifiers(NSUInteger mods)
Detailed Description
Public Defines
WHEEL_DELTA() ( 120 * 0.1 )
Public Variables
bool smApplicationInactive
Public Functions
convertModifierBits(const U32 in)
NSModifiersToTorqueModifiers(NSUInteger mods)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "windowManager/mac/macView.h" 25#include "platform/event.h" 26#include "platform/platformInput.h" 27#include "console/console.h" 28#include "sim/actionMap.h" 29#include "app/mainLoop.h" 30 31// For left/right side definitions. 32#include <IOKit/hidsystem/IOLLEvent.h> 33 34 35#define WHEEL_DELTA ( 120 * 0.1 ) 36 37static bool smApplicationInactive = false; 38 39 40extern InputModifiers convertModifierBits( const U32 in ); 41 42 43inline U32 NSModifiersToTorqueModifiers( NSUInteger mods ) 44{ 45 U32 torqueMods = 0; 46 47 if( mods & NX_DEVICELSHIFTKEYMASK ) 48 torqueMods |= IM_LSHIFT; 49 if( mods & NX_DEVICERSHIFTKEYMASK ) 50 torqueMods |= IM_RSHIFT; 51 if( mods & NX_DEVICELALTKEYMASK ) 52 torqueMods |= IM_LOPT; 53 if( mods & NX_DEVICERALTKEYMASK ) 54 torqueMods |= IM_ROPT; 55 if( mods & NX_DEVICELCTLKEYMASK ) 56 torqueMods |= IM_LCTRL; 57 if( mods & NX_DEVICERCTLKEYMASK ) 58 torqueMods |= IM_RCTRL; 59 if( mods & NX_DEVICELCMDKEYMASK ) 60 torqueMods |= IM_LALT; 61 if( mods & NX_DEVICERCMDKEYMASK ) 62 torqueMods |= IM_RALT; 63 64 Input::setModifierKeys( convertModifierBits( torqueMods ) ); 65 66 return torqueMods; 67} 68 69 70@implementation GGMacView 71- (void)setTorqueWindow:(MacWindow*)theWindow 72{ 73 mTorqueWindow = theWindow; 74 mLastMods = 0; 75} 76 77- (MacWindow*)torqueWindow 78{ 79 return mTorqueWindow; 80} 81 82-(void)trackModState:(U32)torqueKey withMacMods:(U32)macMods event:(NSEvent*)theEvent 83{ 84 // track state: 85 // translate the torque key code to the torque flag that changed 86 // xor with existing mods for new mod state 87 // clear anything that the mac says both siblings are not down ( to help stay in sync, a little bit ) 88 89 // ?set left sibling of anything that the mac says some sibling is down, but that we don't see as down? 90 91 U32 torqueMod = 0; 92 switch(torqueKey) 93 { 94 case KEY_LSHIFT: torqueMod = IM_LSHIFT; break; 95 case KEY_RSHIFT: torqueMod = IM_RSHIFT; break; 96 case KEY_LCONTROL: torqueMod = IM_LCTRL; break; 97 case KEY_RCONTROL: torqueMod = IM_RCTRL; break; 98 case KEY_MAC_LOPT: torqueMod = IM_LOPT; break; 99 case KEY_MAC_ROPT: torqueMod = IM_ROPT; break; 100 case KEY_LALT: torqueMod = IM_LALT; break; 101 case KEY_RALT: torqueMod = IM_RALT; break; 102 }; 103 104 // flip the mod that we got an event for 105 U32 newMods = mLastMods ^ torqueMod; 106 107 // clear left and right if mac thinks both are up. 108 if( !(macMods & NSShiftKeyMask)) newMods &= ~<a href="/coding/file/journaledsignal_8h/#journaledsignal_8h_1a8f8764965f19908a2da26b3326621ca6a520fd88aabf8a7f707cd7de89e119811">IM_LSHIFT</a>, newMods &= ~<a href="/coding/file/journaledsignal_8h/#journaledsignal_8h_1a8f8764965f19908a2da26b3326621ca6abb57e804bbd6c09202d7f7b2fa6d3907">IM_RSHIFT</a>; 109 if( !(macMods & NSControlKeyMask)) newMods &= ~<a href="/coding/file/journaledsignal_8h/#journaledsignal_8h_1a8f8764965f19908a2da26b3326621ca6aa435588bf2e758d8b2b24073e88006f8">IM_LCTRL</a>, newMods &= ~<a href="/coding/file/journaledsignal_8h/#journaledsignal_8h_1a8f8764965f19908a2da26b3326621ca6ae647ce795d744ba06b5edc4693956b84">IM_RCTRL</a>; 110 if( !(macMods & NSAlternateKeyMask)) newMods &= ~<a href="/coding/file/journaledsignal_8h/#journaledsignal_8h_1a8f8764965f19908a2da26b3326621ca6a8df3b9f0051fbe5d47f2988fe5afd7be">IM_LOPT</a>, newMods &= ~<a href="/coding/file/journaledsignal_8h/#journaledsignal_8h_1a8f8764965f19908a2da26b3326621ca6a04ac1714e5ae4e0fc9587321de606780">IM_ROPT</a>; 111 if( !(macMods & NSCommandKeyMask)) newMods &= ~<a href="/coding/file/journaledsignal_8h/#journaledsignal_8h_1a8f8764965f19908a2da26b3326621ca6aabf78abc3c51309683fedac36b710bcb">IM_LALT</a>, newMods &= ~<a href="/coding/file/journaledsignal_8h/#journaledsignal_8h_1a8f8764965f19908a2da26b3326621ca6adf1deda38598beea0eff101875f6f4a9">IM_RALT</a>; 112 113 // Generate keyUp/Down event (allows us to use modifier keys for actions) 114 mLastMods = 0; 115 [self rawKeyUpDown:theEvent keyDown:(newMods & torqueMod)]; 116 117 mLastMods = newMods; 118 119 Input::setModifierKeys( convertModifierBits( mLastMods ) ); 120} 121 122- (Point2I)viewCoordsToTorqueCoords:(NSPoint)mousePoint 123{ 124 if(mTorqueWindow->isFullscreen()) 125 { 126 CGRect bounds = mTorqueWindow->getDisplayBounds(); 127 CGRect mainbounds = mTorqueWindow->getMainDisplayBounds(); 128 F32 offsetY = mainbounds.size.height - (bounds.size.height + bounds.origin.y); 129 Point2I pt(mousePoint.x - bounds.origin.x, bounds.size.height + offsetY - mousePoint.y); 130 return pt; 131 } 132 return Point2I(mousePoint.x, mTorqueWindow->getClientExtent().y - mousePoint.y); 133} 134 135- (void)signalGainFocus 136{ 137 if(smApplicationInactive) 138 smApplicationInactive = false; 139 140 bool gainFocus = static_cast<MacWindowManager*>(WindowManager)->canWindowGainFocus(mTorqueWindow); 141 if(gainFocus) 142 mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), GainFocus); 143} 144 145#pragma mark - 146#pragma mark Mouse Input 147// We're funnelling all the standard cocoa event handlers to -mouseUpDown and -mouseMotion. 148- (void)mouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; } 149- (void)rightMouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; } 150- (void)otherMouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; } 151- (void)mouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; } 152- (void)rightMouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; } 153- (void)otherMouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; } 154- (void)mouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; } 155- (void)rightMouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; } 156- (void)otherMouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; } 157- (void)mouseMoved:(NSEvent *)theEvent { [self mouseMotion:theEvent]; } 158 159- (void)mouseUpDown:(NSEvent *)theEvent mouseDown:(BOOL)isMouseDown 160{ 161 Point2I eventLocation = [self viewCoordsToTorqueCoords: [theEvent locationInWindow]]; 162 U16 buttonNumber = [theEvent buttonNumber]; 163 U32 action = isMouseDown ? SI_MAKE : SI_BREAK; 164 165 // If the event location is negative then it occurred in the structure region (e.g. title bar, resize corner), and we don't want 166 // to lock the mouse/drop into fullscreen for that. 167 if(WindowManager->getFocusedWindow() != mTorqueWindow && eventLocation.x > 0 && eventLocation.y > 0) 168 [self signalGainFocus]; 169 170 mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ); 171 172 mTorqueWindow->buttonEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, action, buttonNumber); 173} 174 175- (void)mouseMotion:(NSEvent *)theEvent 176{ 177 mTorqueWindow->_doMouseLockNow(); 178 179 // when moving the mouse to the center of the window for mouse locking, we need 180 // to skip the next mouse delta event 181 if(mTorqueWindow->isMouseLocked() && mTorqueWindow->_skipNextMouseEvent()) 182 { 183 mTorqueWindow->_skippedMouseEvent(); 184 return; 185 } 186 187 Point2I eventLocation; 188 if(mTorqueWindow->isMouseLocked()) 189 { 190 eventLocation.x = [theEvent deltaX]; 191 eventLocation.y = [theEvent deltaY]; 192 } 193 else 194 { 195 eventLocation = [self viewCoordsToTorqueCoords:[theEvent locationInWindow]]; 196 } 197 198 mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ); 199 200 mTorqueWindow->mouseEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, eventLocation.x, eventLocation.y, mTorqueWindow->isMouseLocked()); 201} 202 203- (void)scrollWheel:(NSEvent *)theEvent 204{ 205 float deltaX = [ theEvent deltaX ]; 206 float deltaY = [ theEvent deltaY ]; 207 208 if( mIsZero( deltaX ) && mIsZero( deltaY ) ) 209 return; 210 211 mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ); 212 213 mTorqueWindow->wheelEvent.trigger( mTorqueWindow->getWindowId(), mLastMods, S32( deltaX * WHEEL_DELTA ), S32( deltaY * WHEEL_DELTA ) ); 214} 215 216#pragma mark - 217#pragma mark Keyboard Input 218- (BOOL)performKeyEquivalent:(NSEvent *)theEvent 219{ 220 // Pass it off to the main menu. If the main menu handled it, we're done. 221 if([[NSApp mainMenu] performKeyEquivalent:theEvent]) 222 return YES; 223 224 // cmd-q will quit. End of story. 225 if(([theEvent modifierFlags] & NSCommandKeyMask && !([theEvent modifierFlags] & NSAlternateKeyMask) && !([theEvent modifierFlags] & NSControlKeyMask)) && [theEvent keyCode] == 0x0C) 226 { 227 StandardMainLoop::shutdown(); 228 [NSApp terminate:self]; 229 return YES; 230 } 231 232 // In fullscreen we grab ALL keyboard events, including ones which would normally be handled by the system, 233 // like cmd-tab. Thus, we need to specifically check for cmd-tab and bail out of fullscreen and hide the app 234 // to switch to the next application. 235 // 0x30 is tab, so this grabs command-tab and command-shift-tab 236 if([theEvent keyCode] == 0x30 && mTorqueWindow->isFullscreen()) 237 { 238 // Bail! 239 mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus); 240 [NSApp hide:nil]; 241 return YES; 242 } 243 244 // All other events are uninteresting to us and can be handled by Torque. 245 if([theEvent type] == NSKeyDown) 246 [self keyDown:theEvent]; 247 else if([theEvent type] == NSKeyUp) 248 [self keyUp:theEvent]; 249 250 // Don't let the default handler continue processing these events, it does things we don't like. 251 return YES; 252} 253 254- (void)flagsChanged:(NSEvent *)theEvent 255{ 256 U32 torqueKeyCode = TranslateOSKeyCode([theEvent keyCode]); 257 U32 macMods = [theEvent modifierFlags]; 258 [self trackModState:torqueKeyCode withMacMods:macMods event:theEvent]; 259} 260 261- (void)keyDown:(NSEvent *)theEvent 262{ 263 // If keyboard translation is on and the key isn't bound in the 264 // global action map, first try turning this into one or more 265 // character events. 266 267 if( mTorqueWindow->getKeyboardTranslation() 268 && !mTorqueWindow->shouldNotTranslate( 269 convertModifierBits( NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ) ), 270 ( InputObjectInstances ) TranslateOSKeyCode( [ theEvent keyCode ] ) ) ) 271 { 272 mHandledAsCharEvent = false; 273 [ self interpretKeyEvents: [ NSArray arrayWithObject: theEvent ] ]; 274 275 if( mHandledAsCharEvent ) 276 return; 277 } 278 279 // Fire as raw keyboard event. 280 281 [ self rawKeyUpDown: theEvent keyDown: YES ]; 282} 283 284- (void)keyUp:(NSEvent *)theEvent 285{ 286 [self rawKeyUpDown:theEvent keyDown:NO]; 287} 288 289- (void)rawKeyUpDown:(NSEvent *)theEvent keyDown:(BOOL)isKeyDown 290{ 291 U32 action; 292 if([theEvent type] != NSFlagsChanged) 293 action = isKeyDown ? ([theEvent isARepeat] ? SI_REPEAT : SI_MAKE) : SI_BREAK; 294 else 295 action = isKeyDown ? SI_MAKE : SI_BREAK; 296 297 U32 torqueKeyCode = TranslateOSKeyCode([theEvent keyCode]); 298 mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ); 299 300 mTorqueWindow->keyEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, action, torqueKeyCode); 301} 302 303- (void)insertText:(id)_inString 304{ 305 // input string may be an NSString or an NSAttributedString 306 NSString *text = [_inString isKindOfClass:[NSAttributedString class]] ? [_inString string] : _inString; 307 for(int i = 0; i < [text length]; i++) 308 { 309 mTorqueWindow->charEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, [text characterAtIndex:i]); 310 mHandledAsCharEvent = true; 311 } 312} 313 314#pragma mark - 315#pragma mark Application Delegate 316- (void)applicationDidResignActive:(NSNotification *)aNotification 317{ 318 smApplicationInactive = true; 319 mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus); 320 [NSApp setDelegate:nil]; 321} 322 323- (void)applicationDidHide:(NSNotification *)aNotification 324{ 325 mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus); 326} 327 328- (void)applicationDidUnhide:(NSNotification *)aNotification 329{ 330 mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), GainFocus); 331} 332 333#ifndef TORQUE_SHARED 334 335- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender 336{ 337 Platform::postQuitMessage(0); 338 return NSTerminateCancel; 339} 340 341#endif 342 343#pragma mark - 344#pragma mark Window Delegate 345- (BOOL)windowShouldClose:(NSWindow *)sender 346{ 347 // We close the window ourselves 348 mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), WindowDestroy); 349 return NO; 350} 351 352- (void)windowWillClose:(NSNotification *) notification 353{ 354 mTorqueWindow->_disassociateCocoaWindow(); 355} 356 357- (void)windowDidBecomeKey:(NSNotification *)notification 358{ 359 // when our window is the key window, we become the app delegate. 360 PlatformWindow* focusWindow = WindowManager->getFocusedWindow(); 361 if(focusWindow && focusWindow != mTorqueWindow) 362 focusWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus); 363 [NSApp setDelegate:self]; 364 [self signalGainFocus]; 365} 366 367- (void)windowDidResignKey:(NSNotification*)notification 368{ 369 mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseScreen); 370 mTorqueWindow->_associateMouse(); 371 mTorqueWindow->setCursorVisible(true); 372 [NSApp setDelegate:nil]; 373} 374 375- (void)windowDidChangeScreen:(NSNotification*)notification 376{ 377 NSWindow* wnd = [notification object]; 378 // TODO: Add a category to NSScreen to deal with this 379 CGDirectDisplayID disp = (CGDirectDisplayID)[[[[wnd screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue]; 380 mTorqueWindow->setDisplay(disp); 381} 382 383- (void)windowDidResize:(NSNotification*)notification 384{ 385 Point2I clientExtent = mTorqueWindow->getClientExtent(); 386 mTorqueWindow->resizeEvent.trigger(mTorqueWindow->getWindowId(), clientExtent.x, clientExtent.y); 387} 388 389#pragma mark - 390#pragma mark responder status 391- (BOOL)acceptsFirstResponder { return YES; } 392- (BOOL)becomeFirstResponder { return YES; } 393- (BOOL)resignFirstResponder { return YES; } 394 395// Basic implementation because NSResponder's default implementation can cause infinite loops when our keyDown: method calls interpretKeyEvents: 396- (void)doCommandBySelector:(SEL)aSelector 397{ 398 if([self respondsToSelector:aSelector]) 399 [self performSelector:aSelector withObject:nil]; 400} 401 402@end 403