From 85e101c8a7127481f746e5bb5202a0c59cadb208 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 1 Sep 2011 14:10:30 +0100 Subject: [PATCH] Fix the global shortcut grabber on Mac. --- src/CMakeLists.txt | 1 + src/core/mac_startup.h | 2 + src/core/mac_startup.mm | 102 ++++++++++++++++++++++++++ src/core/mac_utilities.h | 11 +++ src/core/macglobalshortcutbackend.mm | 103 +-------------------------- src/ui/globalshortcutgrabber.cpp | 20 +++++- src/ui/globalshortcutgrabber.h | 11 +++ src/ui/globalshortcutgrabber.mm | 48 +++++++++++++ 8 files changed, 196 insertions(+), 102 deletions(-) create mode 100644 src/core/mac_utilities.h create mode 100644 src/ui/globalshortcutgrabber.mm diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 33aa39a2c..38c8a5759 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -698,6 +698,7 @@ if(APPLE) list(APPEND HEADERS widgets/maclineedit.h) list(APPEND SOURCES core/macglobalshortcutbackend.mm) list(APPEND SOURCES devices/macdevicelister.mm) + list(APPEND SOURCES ui/globalshortcutgrabber.mm) list(APPEND SOURCES ui/macscreensaver.cpp) list(APPEND SOURCES ui/macsystemtrayicon.mm) list(APPEND SOURCES widgets/maclineedit.mm) diff --git a/src/core/mac_startup.h b/src/core/mac_startup.h index 08f482e6e..c4c603dd2 100644 --- a/src/core/mac_startup.h +++ b/src/core/mac_startup.h @@ -1,6 +1,8 @@ #ifndef MAC_STARTUP_H #define MAC_STARTUP_H +#include + class MacGlobalShortcutBackend; class QObject; diff --git a/src/core/mac_startup.mm b/src/core/mac_startup.mm index 3d59401cf..9023423f5 100644 --- a/src/core/mac_startup.mm +++ b/src/core/mac_startup.mm @@ -41,6 +41,7 @@ #include "globalshortcuts.h" #include "mac_delegate.h" #include "mac_startup.h" +#include "mac_utilities.h" #include "macglobalshortcutbackend.h" #include "utilities.h" #include "core/logging.h" @@ -343,4 +344,105 @@ bool MigrateLegacyConfigFiles() { return moved_dir; } +static int MapFunctionKey(int keycode) { + switch (keycode) { + // Function keys + case NSInsertFunctionKey: return Qt::Key_Insert; + case NSDeleteFunctionKey: return Qt::Key_Delete; + case NSPauseFunctionKey: return Qt::Key_Pause; + case NSPrintFunctionKey: return Qt::Key_Print; + case NSSysReqFunctionKey: return Qt::Key_SysReq; + case NSHomeFunctionKey: return Qt::Key_Home; + case NSEndFunctionKey: return Qt::Key_End; + case NSLeftArrowFunctionKey: return Qt::Key_Left; + case NSUpArrowFunctionKey: return Qt::Key_Up; + case NSRightArrowFunctionKey: return Qt::Key_Right; + case NSDownArrowFunctionKey: return Qt::Key_Down; + case NSPageUpFunctionKey: return Qt::Key_PageUp; + case NSPageDownFunctionKey: return Qt::Key_PageDown; + case NSScrollLockFunctionKey: return Qt::Key_ScrollLock; + case NSF1FunctionKey: return Qt::Key_F1; + case NSF2FunctionKey: return Qt::Key_F2; + case NSF3FunctionKey: return Qt::Key_F3; + case NSF4FunctionKey: return Qt::Key_F4; + case NSF5FunctionKey: return Qt::Key_F5; + case NSF6FunctionKey: return Qt::Key_F6; + case NSF7FunctionKey: return Qt::Key_F7; + case NSF8FunctionKey: return Qt::Key_F8; + case NSF9FunctionKey: return Qt::Key_F9; + case NSF10FunctionKey: return Qt::Key_F10; + case NSF11FunctionKey: return Qt::Key_F11; + case NSF12FunctionKey: return Qt::Key_F12; + case NSF13FunctionKey: return Qt::Key_F13; + case NSF14FunctionKey: return Qt::Key_F14; + case NSF15FunctionKey: return Qt::Key_F15; + case NSF16FunctionKey: return Qt::Key_F16; + case NSF17FunctionKey: return Qt::Key_F17; + case NSF18FunctionKey: return Qt::Key_F18; + case NSF19FunctionKey: return Qt::Key_F19; + case NSF20FunctionKey: return Qt::Key_F20; + case NSF21FunctionKey: return Qt::Key_F21; + case NSF22FunctionKey: return Qt::Key_F22; + case NSF23FunctionKey: return Qt::Key_F23; + case NSF24FunctionKey: return Qt::Key_F24; + case NSF25FunctionKey: return Qt::Key_F25; + case NSF26FunctionKey: return Qt::Key_F26; + case NSF27FunctionKey: return Qt::Key_F27; + case NSF28FunctionKey: return Qt::Key_F28; + case NSF29FunctionKey: return Qt::Key_F29; + case NSF30FunctionKey: return Qt::Key_F30; + case NSF31FunctionKey: return Qt::Key_F31; + case NSF32FunctionKey: return Qt::Key_F32; + case NSF33FunctionKey: return Qt::Key_F33; + case NSF34FunctionKey: return Qt::Key_F34; + case NSF35FunctionKey: return Qt::Key_F35; + case NSMenuFunctionKey: return Qt::Key_Menu; + case NSHelpFunctionKey: return Qt::Key_Help; + } + + return 0; +} + +QKeySequence KeySequenceFromNSEvent(NSEvent* event) { + NSString* str = [event charactersIgnoringModifiers]; + NSString* upper = [str uppercaseString]; + const char* chars = [upper UTF8String]; + NSUInteger modifiers = [event modifierFlags]; + int key = 0; + unsigned char c = chars[0]; + switch (c) { + case 0x1b: key = Qt::Key_Escape; break; + case 0x09: key = Qt::Key_Tab; break; + case 0x0d: key = Qt::Key_Return; break; + case 0x08: key = Qt::Key_Backspace; break; + case 0x03: key = Qt::Key_Enter; break; + } + + if (key == 0) { + if (c >= 0x20 && c <= 0x7e) { // ASCII from space to ~ + key = c; + } else { + key = MapFunctionKey([event keyCode]); + if (key == 0) { + return QKeySequence(); + } + } + } + + if (modifiers & NSShiftKeyMask) { + key += Qt::SHIFT; + } + if (modifiers & NSControlKeyMask) { + key += Qt::META; + } + if (modifiers & NSAlternateKeyMask) { + key += Qt::ALT; + } + if (modifiers & NSCommandKeyMask) { + key += Qt::CTRL; + } + + return QKeySequence(key); +} + } // namespace mac diff --git a/src/core/mac_utilities.h b/src/core/mac_utilities.h new file mode 100644 index 000000000..067b4296b --- /dev/null +++ b/src/core/mac_utilities.h @@ -0,0 +1,11 @@ +// Only include this from Objective-C++ files + +#include + +@class NSEvent; + +namespace mac { + +QKeySequence KeySequenceFromNSEvent(NSEvent* event); + +} diff --git a/src/core/macglobalshortcutbackend.mm b/src/core/macglobalshortcutbackend.mm index 528e99a27..96d173892 100644 --- a/src/core/macglobalshortcutbackend.mm +++ b/src/core/macglobalshortcutbackend.mm @@ -20,6 +20,7 @@ #include "config.h" #include "globalshortcuts.h" #include "mac_startup.h" +#import "mac_utilities.h" #include @@ -67,111 +68,11 @@ class MacGlobalShortcutBackendPrivate : boost::noncopyable { } private: - static QKeySequence GetSequence(NSEvent* event) { - NSString* str = [event charactersIgnoringModifiers]; - NSString* upper = [str uppercaseString]; - const char* chars = [upper UTF8String]; - NSUInteger modifiers = [event modifierFlags]; - int key = 0; - unsigned char c = chars[0]; - switch (c) { - case 0x1b: key = Qt::Key_Escape; break; - case 0x09: key = Qt::Key_Tab; break; - case 0x0d: key = Qt::Key_Return; break; - case 0x08: key = Qt::Key_Backspace; break; - case 0x03: key = Qt::Key_Enter; break; - } - - if (key == 0) { - if (c >= 0x20 && c <= 0x7e) { // ASCII from space to ~ - key = c; - } else { - key = MapFunctionKey([event keyCode]); - if (key == 0) { - return QKeySequence(); - } - } - } - - if (modifiers & NSShiftKeyMask) { - key += Qt::SHIFT; - } - if (modifiers & NSControlKeyMask) { - key += Qt::META; - } - if (modifiers & NSAlternateKeyMask) { - key += Qt::ALT; - } - if (modifiers & NSCommandKeyMask) { - key += Qt::CTRL; - } - - return QKeySequence(key); - } - bool HandleKeyEvent(NSEvent* event) { - QKeySequence sequence = GetSequence(event); + QKeySequence sequence = mac::KeySequenceFromNSEvent(event); return backend_->KeyPressed(sequence); } - static int MapFunctionKey(int keycode) { - switch (keycode) { - // Function keys - case NSInsertFunctionKey: return Qt::Key_Insert; - case NSDeleteFunctionKey: return Qt::Key_Delete; - case NSPauseFunctionKey: return Qt::Key_Pause; - case NSPrintFunctionKey: return Qt::Key_Print; - case NSSysReqFunctionKey: return Qt::Key_SysReq; - case NSHomeFunctionKey: return Qt::Key_Home; - case NSEndFunctionKey: return Qt::Key_End; - case NSLeftArrowFunctionKey: return Qt::Key_Left; - case NSUpArrowFunctionKey: return Qt::Key_Up; - case NSRightArrowFunctionKey: return Qt::Key_Right; - case NSDownArrowFunctionKey: return Qt::Key_Down; - case NSPageUpFunctionKey: return Qt::Key_PageUp; - case NSPageDownFunctionKey: return Qt::Key_PageDown; - case NSScrollLockFunctionKey: return Qt::Key_ScrollLock; - case NSF1FunctionKey: return Qt::Key_F1; - case NSF2FunctionKey: return Qt::Key_F2; - case NSF3FunctionKey: return Qt::Key_F3; - case NSF4FunctionKey: return Qt::Key_F4; - case NSF5FunctionKey: return Qt::Key_F5; - case NSF6FunctionKey: return Qt::Key_F6; - case NSF7FunctionKey: return Qt::Key_F7; - case NSF8FunctionKey: return Qt::Key_F8; - case NSF9FunctionKey: return Qt::Key_F9; - case NSF10FunctionKey: return Qt::Key_F10; - case NSF11FunctionKey: return Qt::Key_F11; - case NSF12FunctionKey: return Qt::Key_F12; - case NSF13FunctionKey: return Qt::Key_F13; - case NSF14FunctionKey: return Qt::Key_F14; - case NSF15FunctionKey: return Qt::Key_F15; - case NSF16FunctionKey: return Qt::Key_F16; - case NSF17FunctionKey: return Qt::Key_F17; - case NSF18FunctionKey: return Qt::Key_F18; - case NSF19FunctionKey: return Qt::Key_F19; - case NSF20FunctionKey: return Qt::Key_F20; - case NSF21FunctionKey: return Qt::Key_F21; - case NSF22FunctionKey: return Qt::Key_F22; - case NSF23FunctionKey: return Qt::Key_F23; - case NSF24FunctionKey: return Qt::Key_F24; - case NSF25FunctionKey: return Qt::Key_F25; - case NSF26FunctionKey: return Qt::Key_F26; - case NSF27FunctionKey: return Qt::Key_F27; - case NSF28FunctionKey: return Qt::Key_F28; - case NSF29FunctionKey: return Qt::Key_F29; - case NSF30FunctionKey: return Qt::Key_F30; - case NSF31FunctionKey: return Qt::Key_F31; - case NSF32FunctionKey: return Qt::Key_F32; - case NSF33FunctionKey: return Qt::Key_F33; - case NSF34FunctionKey: return Qt::Key_F34; - case NSF35FunctionKey: return Qt::Key_F35; - case NSMenuFunctionKey: return Qt::Key_Menu; - case NSHelpFunctionKey: return Qt::Key_Help; - } - - return 0; - } id global_monitor_; id local_monitor_; diff --git a/src/ui/globalshortcutgrabber.cpp b/src/ui/globalshortcutgrabber.cpp index bb242050d..8cba96390 100644 --- a/src/ui/globalshortcutgrabber.cpp +++ b/src/ui/globalshortcutgrabber.cpp @@ -56,6 +56,20 @@ void GlobalShortcutGrabber::hideEvent(QHideEvent* e) { QDialog::hideEvent(e); } +void GlobalShortcutGrabber::grabKeyboard() { +#ifdef Q_OS_DARWIN + SetupMacEventHandler(); +#endif + QDialog::grabKeyboard(); +} + +void GlobalShortcutGrabber::releaseKeyboard() { +#ifdef Q_OS_DARWIN + TeardownMacEventHandler(); +#endif + QDialog::releaseKeyboard(); +} + bool GlobalShortcutGrabber::event(QEvent* e) { if (e->type() == QEvent::ShortcutOverride) { QKeyEvent* ke = static_cast(e); @@ -65,7 +79,7 @@ bool GlobalShortcutGrabber::event(QEvent* e) { else ret_ = QKeySequence(ke->modifiers() | ke->key()); - ui_->combo->setText("" + ret_.toString(QKeySequence::NativeText) + ""); + UpdateText(); if (!modifier_keys_.contains(ke->key())) accept(); @@ -74,4 +88,8 @@ bool GlobalShortcutGrabber::event(QEvent* e) { return QDialog::event(e); } +void GlobalShortcutGrabber::UpdateText() { + ui_->combo->setText("" + ret_.toString(QKeySequence::NativeText) + ""); +} + diff --git a/src/ui/globalshortcutgrabber.h b/src/ui/globalshortcutgrabber.h index f67991ace..2859098bc 100644 --- a/src/ui/globalshortcutgrabber.h +++ b/src/ui/globalshortcutgrabber.h @@ -20,6 +20,8 @@ #include +class MacMonitorWrapper; +class NSEvent; class Ui_GlobalShortcutGrabber; class GlobalShortcutGrabber : public QDialog { @@ -35,12 +37,21 @@ class GlobalShortcutGrabber : public QDialog { bool event(QEvent *); void showEvent(QShowEvent *); void hideEvent(QHideEvent *); + void grabKeyboard(); + void releaseKeyboard(); private: + void UpdateText(); + void SetupMacEventHandler(); + void TeardownMacEventHandler(); + bool HandleMacEvent(NSEvent*); + Ui_GlobalShortcutGrabber* ui_; QKeySequence ret_; QList modifier_keys_; + + MacMonitorWrapper* wrapper_; }; #endif // GLOBALSHORTCUTGRABBER_H diff --git a/src/ui/globalshortcutgrabber.mm b/src/ui/globalshortcutgrabber.mm new file mode 100644 index 000000000..ae2fb0ec1 --- /dev/null +++ b/src/ui/globalshortcutgrabber.mm @@ -0,0 +1,48 @@ +#include "globalshortcutgrabber.h" + +#import +#import +#import +#import + +#include + +#include + +#import "core/mac_utilities.h" + +class MacMonitorWrapper : boost::noncopyable { + public: + explicit MacMonitorWrapper(id monitor) + : local_monitor_(monitor) { + } + + ~MacMonitorWrapper() { + [NSEvent removeMonitor: local_monitor_]; + } + + private: + id local_monitor_; +}; + +bool GlobalShortcutGrabber::HandleMacEvent(NSEvent* event) { + ret_ = mac::KeySequenceFromNSEvent(event); + UpdateText(); + if ([[event charactersIgnoringModifiers] length] != 0) { + accept(); + return true; + } + return ret_ == QKeySequence(Qt::Key_Escape); +} + +void GlobalShortcutGrabber::SetupMacEventHandler() { + id monitor = [NSEvent addLocalMonitorForEventsMatchingMask: NSKeyDownMask + handler:^(NSEvent* event) { + return HandleMacEvent(event) ? event : nil; + }]; + wrapper_ = new MacMonitorWrapper(monitor); +} + +void GlobalShortcutGrabber::TeardownMacEventHandler() { + delete wrapper_; +}