Fix the global shortcut grabber on Mac.

This commit is contained in:
John Maguire 2011-09-01 14:10:30 +01:00
parent 3aa1d75163
commit 85e101c8a7
8 changed files with 196 additions and 102 deletions

View File

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

View File

@ -1,6 +1,8 @@
#ifndef MAC_STARTUP_H
#define MAC_STARTUP_H
#include <QKeySequence>
class MacGlobalShortcutBackend;
class QObject;

View File

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

11
src/core/mac_utilities.h Normal file
View File

@ -0,0 +1,11 @@
// Only include this from Objective-C++ files
#include <QKeySequence>
@class NSEvent;
namespace mac {
QKeySequence KeySequenceFromNSEvent(NSEvent* event);
}

View File

@ -20,6 +20,7 @@
#include "config.h"
#include "globalshortcuts.h"
#include "mac_startup.h"
#import "mac_utilities.h"
#include <boost/noncopyable.hpp>
@ -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_;

View File

@ -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<QKeyEvent*>(e);
@ -65,7 +79,7 @@ bool GlobalShortcutGrabber::event(QEvent* e) {
else
ret_ = QKeySequence(ke->modifiers() | ke->key());
ui_->combo->setText("<b>" + ret_.toString(QKeySequence::NativeText) + "</b>");
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("<b>" + ret_.toString(QKeySequence::NativeText) + "</b>");
}

View File

@ -20,6 +20,8 @@
#include <QDialog>
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<int> modifier_keys_;
MacMonitorWrapper* wrapper_;
};
#endif // GLOBALSHORTCUTGRABBER_H

View File

@ -0,0 +1,48 @@
#include "globalshortcutgrabber.h"
#import <AppKit/NSEvent.h>
#import <AppKit/NSGraphics.h>
#import <AppKit/NSViewController.h>
#import <QuartzCore/CALayer.h>
#include <QKeySequence>
#include <boost/noncopyable.hpp>
#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_;
}