Browse Source

Improve key events on Cocoa: add support for the brand new raw key event system, distinguish between left/right modifier keys, fix the lack of down event on help/insert key, fix the lack of up-event while command is pressed

rdb 11 years ago
parent
commit
0b99086e8c

+ 2 - 1
panda/src/cocoadisplay/Sources.pp

@@ -17,13 +17,14 @@
      cocoaGraphicsPipe.h cocoaGraphicsPipe.I \
      cocoaGraphicsWindow.h cocoaGraphicsWindow.I \
      cocoaGraphicsStateGuardian.h cocoaGraphicsStateGuardian.I \
-     cocoaPandaView.h cocoaPandaWindowDelegate.h
+     cocoaPandaApp.h cocoaPandaView.h cocoaPandaWindowDelegate.h
     
   #define INCLUDED_SOURCES \
     config_cocoadisplay.mm \
     cocoaGraphicsPipe.mm \
     cocoaGraphicsStateGuardian.mm \
     cocoaGraphicsWindow.mm \
+    cocoaPandaApp.mm \
     cocoaPandaView.mm \
     cocoaPandaWindow.mm \
     cocoaPandaWindowDelegate.mm

+ 2 - 1
panda/src/cocoadisplay/cocoaGraphicsPipe.mm

@@ -16,6 +16,7 @@
 //#include "cocoaGraphicsBuffer.h"
 #include "cocoaGraphicsWindow.h"
 #include "cocoaGraphicsStateGuardian.h"
+#include "cocoaPandaApp.h"
 #include "config_cocoadisplay.h"
 #include "frameBufferProperties.h"
 
@@ -32,7 +33,7 @@ TypeHandle CocoaGraphicsPipe::_type_handle;
 static void init_app() {
   if (NSApp == nil) {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    [NSApplication sharedApplication];
+    [CocoaPandaApp sharedApplication];
 
 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
     [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];

+ 3 - 1
panda/src/cocoadisplay/cocoaGraphicsWindow.h

@@ -80,7 +80,9 @@ protected:
 private:
   NSImage *load_image(const Filename &filename);
 
-  ButtonHandle map_key(unsigned short keycode);
+  void handle_modifier(NSUInteger modifierFlags, NSUInteger mask, ButtonHandle button);
+  ButtonHandle map_key(unsigned short c);
+  ButtonHandle map_raw_key(unsigned short keycode);
 
 private:
   NSWindow *_window;

+ 208 - 46
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -1495,54 +1495,73 @@ void CocoaGraphicsWindow::
 handle_key_event(NSEvent *event) {
   NSUInteger modifierFlags = [event modifierFlags];
 
-  if ((modifierFlags ^ _modifier_keys) & NSAlphaShiftKeyMask) {
-    if (modifierFlags & NSAlphaShiftKeyMask) {
-      _input_devices[0].button_down(KeyboardButton::caps_lock());
-    } else {
-      _input_devices[0].button_up(KeyboardButton::caps_lock());
-    }
-  }
-
-  if ((modifierFlags ^ _modifier_keys) & NSShiftKeyMask) {
-    if (modifierFlags & NSShiftKeyMask) {
-      _input_devices[0].button_down(KeyboardButton::shift());
-    } else {
-      _input_devices[0].button_up(KeyboardButton::shift());
-    }
-  }
-
-  if ((modifierFlags ^ _modifier_keys) & NSControlKeyMask) {
-    if (modifierFlags & NSControlKeyMask) {
-      _input_devices[0].button_down(KeyboardButton::control());
-    } else {
-      _input_devices[0].button_up(KeyboardButton::control());
-    }
-  }
+  //NB.  This is actually a on-off toggle, not up-down.
+  // Should we instead rapidly fire two successive up-down events?
+  handle_modifier(modifierFlags, NSAlphaShiftKeyMask, KeyboardButton::caps_lock());
+
+  // Check if any of the modifier keys have changed.
+  handle_modifier(modifierFlags, NSShiftKeyMask, KeyboardButton::shift());
+  handle_modifier(modifierFlags, NSControlKeyMask, KeyboardButton::control());
+  handle_modifier(modifierFlags, NSAlternateKeyMask, KeyboardButton::alt());
+  handle_modifier(modifierFlags, NSCommandKeyMask, KeyboardButton::meta());
+
+  // These are not documented, but they seem to be a reliable indicator
+  // of the status of the left/right modifier keys.
+  handle_modifier(modifierFlags, 0x0002, KeyboardButton::lshift());
+  handle_modifier(modifierFlags, 0x0004, KeyboardButton::rshift());
+  handle_modifier(modifierFlags, 0x0001, KeyboardButton::lcontrol());
+  handle_modifier(modifierFlags, 0x2000, KeyboardButton::rcontrol());
+  handle_modifier(modifierFlags, 0x0020, KeyboardButton::lalt());
+  handle_modifier(modifierFlags, 0x0040, KeyboardButton::ralt());
+  handle_modifier(modifierFlags, 0x0008, KeyboardButton::lmeta());
+  handle_modifier(modifierFlags, 0x0010, KeyboardButton::rmeta());
 
-  if ((modifierFlags ^ _modifier_keys) & NSAlternateKeyMask) {
-    if (modifierFlags & NSAlternateKeyMask) {
-      _input_devices[0].button_down(KeyboardButton::alt());
-    } else {
-      _input_devices[0].button_up(KeyboardButton::alt());
-    }
-  }
+  _modifier_keys = modifierFlags;
 
-  if ((modifierFlags ^ _modifier_keys) & NSCommandKeyMask) {
-    if (modifierFlags & NSCommandKeyMask) {
-      _input_devices[0].button_down(KeyboardButton::meta());
-    } else {
-      _input_devices[0].button_up(KeyboardButton::meta());
+  // Get the raw button and send it.
+  ButtonHandle raw_button = map_raw_key([event keyCode]);
+  if (raw_button != ButtonHandle::none()) {
+    // This is not perfect.  Eventually, this whole thing should
+    // probably be replaced with something that uses IOKit or so.
+    // In particular, the flaws are:
+    // - OS eats unmodified F11, F12, scroll lock, pause
+    // - no up events for caps lock
+    // - no robust way to distinguish up/down for modkeys
+    if ([event type] == NSKeyUp) {
+      _input_devices[0].raw_button_up(raw_button);
+
+    } else if ([event type] == NSFlagsChanged) {
+      bool down = false;
+      if (raw_button == KeyboardButton::lshift()) {
+        down = (modifierFlags & 0x0002);
+      } else if (raw_button == KeyboardButton::rshift()) {
+        down = (modifierFlags & 0x0004);
+      } else if (raw_button == KeyboardButton::lcontrol()) {
+        down = (modifierFlags & 0x0001);
+      } else if (raw_button == KeyboardButton::rcontrol()) {
+        down = (modifierFlags & 0x2000);
+      } else if (raw_button == KeyboardButton::lalt()) {
+        down = (modifierFlags & 0x0020);
+      } else if (raw_button == KeyboardButton::ralt()) {
+        down = (modifierFlags & 0x0040);
+      } else if (raw_button == KeyboardButton::lmeta()) {
+        down = (modifierFlags & 0x0008);
+      } else if (raw_button == KeyboardButton::rmeta()) {
+        down = (modifierFlags & 0x0010);
+      } else if (raw_button == KeyboardButton::caps_lock()) {
+        // Emulate down-up, annoying hack!
+        _input_devices[0].raw_button_down(raw_button);
+      }
+      if (down) {
+        _input_devices[0].raw_button_down(raw_button);
+      } else {
+        _input_devices[0].raw_button_up(raw_button);
+      }
+    } else if (![event isARepeat]) {
+      _input_devices[0].raw_button_down(raw_button);
     }
   }
 
-  // I'd add the help key too, but something else in Cocoa messes
-  // around with it.  The up event is registered fine below, but
-  // the down event isn't, and the modifier flag gets stuck after 1 press.
-  // More testing is needed, but I don't think it's worth it until
-  // we encounter someone who requires support for the help key.
-
-  _modifier_keys = modifierFlags;
-
   // FlagsChanged events only carry modifier key information.
   if ([event type] == NSFlagsChanged) {
     return;
@@ -1597,6 +1616,23 @@ handle_key_event(NSEvent *event) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CocoaGraphicsWindow::handle_modifier
+//       Access: Private
+//  Description: Called by handle_key_event to read the state of
+//               a modifier key.
+////////////////////////////////////////////////////////////////////
+void CocoaGraphicsWindow::
+handle_modifier(NSUInteger modifierFlags, NSUInteger mask, ButtonHandle button) {
+  if ((modifierFlags ^ _modifier_keys) & mask) {
+    if (modifierFlags & mask) {
+      _input_devices[0].button_down(button);
+    } else {
+      _input_devices[0].button_up(button);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CocoaGraphicsWindow::handle_mouse_button_event
 //       Access: Public
@@ -1697,11 +1733,11 @@ handle_wheel_event(double x, double y) {
 ////////////////////////////////////////////////////////////////////
 //     Function: CocoaGraphicsWindow::map_key
 //       Access: Private
-//  Description:
+//  Description: Maps a Cocoa key character to a ButtonHandle.
 ////////////////////////////////////////////////////////////////////
 ButtonHandle CocoaGraphicsWindow::
-map_key(unsigned short keycode) {
-  switch (keycode) {
+map_key(unsigned short c) {
+  switch (c) {
   case NSEnterCharacter:
     return KeyboardButton::enter();
   case NSBackspaceCharacter:
@@ -1713,6 +1749,11 @@ map_key(unsigned short keycode) {
     // BackTabCharacter is sent when shift-tab is used.
     return KeyboardButton::tab();
 
+  case 16:
+    // No idea where this constant comes from, but it
+    // is sent whenever the menu key is pressed.
+    return KeyboardButton::menu();
+
   case NSUpArrowFunctionKey:
     return KeyboardButton::up();
   case NSDownArrowFunctionKey:
@@ -1824,3 +1865,124 @@ map_key(unsigned short keycode) {
   }
   return ButtonHandle::none();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: CocoaGraphicsWindow::map_raw_key
+//       Access: Private
+//  Description: Maps a keycode to a ButtonHandle.
+////////////////////////////////////////////////////////////////////
+ButtonHandle CocoaGraphicsWindow::
+map_raw_key(unsigned short keycode) {
+  if (keycode > 0x7f) {
+    return ButtonHandle::none();
+  }
+  switch ((unsigned char) keycode) {
+  /* See HIToolBox/Events.h */
+  case 0x00: return KeyboardButton::ascii_key('a');
+  case 0x01: return KeyboardButton::ascii_key('s');
+  case 0x02: return KeyboardButton::ascii_key('d');
+  case 0x03: return KeyboardButton::ascii_key('f');
+  case 0x04: return KeyboardButton::ascii_key('h');
+  case 0x05: return KeyboardButton::ascii_key('g');
+  case 0x06: return KeyboardButton::ascii_key('z');
+  case 0x07: return KeyboardButton::ascii_key('x');
+  case 0x08: return KeyboardButton::ascii_key('c');
+  case 0x09: return KeyboardButton::ascii_key('v');
+  case 0x0B: return KeyboardButton::ascii_key('b');
+  case 0x0C: return KeyboardButton::ascii_key('q');
+  case 0x0D: return KeyboardButton::ascii_key('w');
+  case 0x0E: return KeyboardButton::ascii_key('e');
+  case 0x0F: return KeyboardButton::ascii_key('r');
+  case 0x10: return KeyboardButton::ascii_key('y');
+  case 0x11: return KeyboardButton::ascii_key('t');
+  case 0x12: return KeyboardButton::ascii_key('1');
+  case 0x13: return KeyboardButton::ascii_key('2');
+  case 0x14: return KeyboardButton::ascii_key('3');
+  case 0x15: return KeyboardButton::ascii_key('4');
+  case 0x16: return KeyboardButton::ascii_key('6');
+  case 0x17: return KeyboardButton::ascii_key('5');
+  case 0x18: return KeyboardButton::ascii_key('=');
+  case 0x19: return KeyboardButton::ascii_key('9');
+  case 0x1A: return KeyboardButton::ascii_key('7');
+  case 0x1B: return KeyboardButton::ascii_key('-');
+  case 0x1C: return KeyboardButton::ascii_key('8');
+  case 0x1D: return KeyboardButton::ascii_key('0');
+  case 0x1E: return KeyboardButton::ascii_key(']');
+  case 0x1F: return KeyboardButton::ascii_key('o');
+  case 0x20: return KeyboardButton::ascii_key('u');
+  case 0x21: return KeyboardButton::ascii_key('[');
+  case 0x22: return KeyboardButton::ascii_key('i');
+  case 0x23: return KeyboardButton::ascii_key('p');
+  case 0x24: return KeyboardButton::enter();
+  case 0x25: return KeyboardButton::ascii_key('l');
+  case 0x26: return KeyboardButton::ascii_key('j');
+  case 0x27: return KeyboardButton::ascii_key('\'');
+  case 0x28: return KeyboardButton::ascii_key('k');
+  case 0x29: return KeyboardButton::ascii_key(';');
+  case 0x2A: return KeyboardButton::ascii_key('\\');
+  case 0x2B: return KeyboardButton::ascii_key(',');
+  case 0x2C: return KeyboardButton::ascii_key('/');
+  case 0x2D: return KeyboardButton::ascii_key('n');
+  case 0x2E: return KeyboardButton::ascii_key('m');
+  case 0x2F: return KeyboardButton::ascii_key('.');
+  case 0x30: return KeyboardButton::tab();
+  case 0x31: return KeyboardButton::ascii_key(' ');
+  case 0x32: return KeyboardButton::ascii_key('`');
+  case 0x33: return KeyboardButton::backspace();
+  case 0x35: return KeyboardButton::escape();
+  case 0x36: return KeyboardButton::rmeta();
+  case 0x37: return KeyboardButton::lmeta();
+  case 0x38: return KeyboardButton::lshift();
+  case 0x39: return KeyboardButton::caps_lock();
+  case 0x3A: return KeyboardButton::lalt();
+  case 0x3B: return KeyboardButton::lcontrol();
+  case 0x3C: return KeyboardButton::rshift();
+  case 0x3D: return KeyboardButton::ralt();
+  case 0x3E: return KeyboardButton::rcontrol();
+  case 0x41: return KeyboardButton::ascii_key('.');
+  case 0x43: return KeyboardButton::ascii_key('*');
+  case 0x45: return KeyboardButton::ascii_key('+');
+  case 0x47: return KeyboardButton::num_lock();
+  case 0x4B: return KeyboardButton::ascii_key('/');
+  case 0x4C: return KeyboardButton::enter();
+  case 0x4E: return KeyboardButton::ascii_key('-');
+  case 0x51: return KeyboardButton::ascii_key('=');
+  case 0x52: return KeyboardButton::ascii_key('0');
+  case 0x53: return KeyboardButton::ascii_key('1');
+  case 0x54: return KeyboardButton::ascii_key('2');
+  case 0x55: return KeyboardButton::ascii_key('3');
+  case 0x56: return KeyboardButton::ascii_key('4');
+  case 0x57: return KeyboardButton::ascii_key('5');
+  case 0x58: return KeyboardButton::ascii_key('6');
+  case 0x59: return KeyboardButton::ascii_key('7');
+  case 0x5B: return KeyboardButton::ascii_key('8');
+  case 0x5C: return KeyboardButton::ascii_key('9');
+  case 0x60: return KeyboardButton::f5();
+  case 0x61: return KeyboardButton::f6();
+  case 0x62: return KeyboardButton::f7();
+  case 0x63: return KeyboardButton::f3();
+  case 0x64: return KeyboardButton::f8();
+  case 0x65: return KeyboardButton::f9();
+  case 0x67: return KeyboardButton::f11();
+  case 0x69: return KeyboardButton::print_screen();
+  case 0x6B: return KeyboardButton::scroll_lock();
+  case 0x6D: return KeyboardButton::f10();
+  case 0x6E: return KeyboardButton::menu();
+  case 0x6F: return KeyboardButton::f12();
+  case 0x71: return KeyboardButton::pause();
+  case 0x72: return KeyboardButton::insert();
+  case 0x73: return KeyboardButton::home();
+  case 0x74: return KeyboardButton::page_up();
+  case 0x75: return KeyboardButton::del();
+  case 0x76: return KeyboardButton::f4();
+  case 0x77: return KeyboardButton::end();
+  case 0x78: return KeyboardButton::f2();
+  case 0x79: return KeyboardButton::page_down();
+  case 0x7A: return KeyboardButton::f1();
+  case 0x7B: return KeyboardButton::left();
+  case 0x7C: return KeyboardButton::right();
+  case 0x7D: return KeyboardButton::down();
+  case 0x7E: return KeyboardButton::up();
+  default:   return ButtonHandle::none();
+  }
+}

+ 21 - 0
panda/src/cocoadisplay/cocoaPandaApp.h

@@ -0,0 +1,21 @@
+// Filename: cocoaPandaApp.h
+// Created by:  rdb (08Mar14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#import <AppKit/NSApplication.h>
+
+// This class solely exists so that we can override sendEvent in order
+// to prevent NSApplication from eating certain keyboard events.
+@interface CocoaPandaApp : NSApplication
+- (void) sendEvent: (NSEvent *) event;
+@end

+ 29 - 0
panda/src/cocoadisplay/cocoaPandaApp.mm

@@ -0,0 +1,29 @@
+// Filename: cocoaPandaApp.mm
+// Created by:  rdb (08Mar14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#import "cocoaPandaApp.h"
+
+@implementation CocoaPandaApp
+- (void) sendEvent: (NSEvent *) event {
+  // This is a hack that allows us to receive cmd-key-up events correctly.
+  // Also prevent it from eating the insert/help key.
+  if (([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask))
+    ||([event type] == NSKeyDown && [event keyCode] == 0x72)) {
+
+    [[self keyWindow] sendEvent: event];
+  } else {
+    [super sendEvent: event];
+  }
+}
+@end

+ 2 - 1
panda/src/cocoadisplay/p3cocoadisplay_composite1.mm

@@ -2,6 +2,7 @@
 #include "cocoaGraphicsPipe.mm"
 #include "cocoaGraphicsStateGuardian.mm"
 #include "cocoaGraphicsWindow.mm"
+#include "cocoaPandaApp.mm"
 #include "cocoaPandaView.mm"
 #include "cocoaPandaWindow.mm"
-#include "cocoaPandaWindowDelegate.mm"
+#include "cocoaPandaWindowDelegate.mm"