diff --git a/panda/src/cocoadisplay/Sources.pp b/panda/src/cocoadisplay/Sources.pp index 3aed55d405..0a3a5b4e44 100644 --- a/panda/src/cocoadisplay/Sources.pp +++ b/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 diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm index 8a66bfd87a..8c6d86ef17 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm +++ b/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]; diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.h b/panda/src/cocoadisplay/cocoaGraphicsWindow.h index 6fdc2a4497..b50edbf173 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.h +++ b/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; diff --git a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm b/panda/src/cocoadisplay/cocoaGraphicsWindow.mm index e314fbe4a0..a5c63f3d50 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsWindow.mm +++ b/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()); - } - } + //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()); - if ((modifierFlags ^ _modifier_keys) & NSShiftKeyMask) { - if (modifierFlags & NSShiftKeyMask) { - _input_devices[0].button_down(KeyboardButton::shift()); - } else { - _input_devices[0].button_up(KeyboardButton::shift()); - } - } + // 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()); - if ((modifierFlags ^ _modifier_keys) & NSControlKeyMask) { - if (modifierFlags & NSControlKeyMask) { - _input_devices[0].button_down(KeyboardButton::control()); - } else { - _input_devices[0].button_up(KeyboardButton::control()); - } - } - - if ((modifierFlags ^ _modifier_keys) & NSAlternateKeyMask) { - if (modifierFlags & NSAlternateKeyMask) { - _input_devices[0].button_down(KeyboardButton::alt()); - } else { - _input_devices[0].button_up(KeyboardButton::alt()); - } - } - - if ((modifierFlags ^ _modifier_keys) & NSCommandKeyMask) { - if (modifierFlags & NSCommandKeyMask) { - _input_devices[0].button_down(KeyboardButton::meta()); - } else { - _input_devices[0].button_up(KeyboardButton::meta()); - } - } - - // 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. + // 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()); _modifier_keys = modifierFlags; + // 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); + } + } + // 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(); + } +} diff --git a/panda/src/cocoadisplay/cocoaPandaApp.h b/panda/src/cocoadisplay/cocoaPandaApp.h new file mode 100644 index 0000000000..a15d77a543 --- /dev/null +++ b/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 + +// 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 diff --git a/panda/src/cocoadisplay/cocoaPandaApp.mm b/panda/src/cocoadisplay/cocoaPandaApp.mm new file mode 100644 index 0000000000..95efc8fe2d --- /dev/null +++ b/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 diff --git a/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm b/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm index fe8d7d03de..28755a66dd 100644 --- a/panda/src/cocoadisplay/p3cocoadisplay_composite1.mm +++ b/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" \ No newline at end of file +#include "cocoaPandaWindowDelegate.mm"