macView.mm

Engine/source/windowManager/mac/macView.mm

More...

Public Defines

define
WHEEL_DELTA() ( 120 * 0.1 )

Public Variables

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