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

This commit is contained in:
rdb 2014-03-08 20:58:03 +00:00
parent c897b0ece1
commit 0b99086e8c
7 changed files with 267 additions and 50 deletions

View File

@ -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

View File

@ -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];

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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

View File

@ -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

View File

@ -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"