qt_demoe/3rd_qhotkey/qhotkey_mac.cpp

269 lines
8.6 KiB
C++

#include "qhotkey.h"
#include "qhotkey_p.h"
#include <Carbon/Carbon.h>
#include <QDebug>
class QHotkeyPrivateMac : public QHotkeyPrivate
{
public:
// QAbstractNativeEventFilter interface
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE;
static OSStatus hotkeyEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *data);
protected:
// QHotkeyPrivate interface
quint32 nativeKeycode(Qt::Key keycode, bool &ok) Q_DECL_OVERRIDE;
quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok) Q_DECL_OVERRIDE;
bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
private:
static bool isHotkeyHandlerRegistered;
static QHash<QHotkey::NativeShortcut, EventHotKeyRef> hotkeyRefs;
};
NATIVE_INSTANCE(QHotkeyPrivateMac)
bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false;
QHash<QHotkey::NativeShortcut, EventHotKeyRef> QHotkeyPrivateMac::hotkeyRefs;
bool QHotkeyPrivateMac::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType);
Q_UNUSED(message);
Q_UNUSED(result);
return false;
}
quint32 QHotkeyPrivateMac::nativeKeycode(Qt::Key keycode, bool &ok)
{
// Constants found in NSEvent.h from AppKit.framework
ok = true;
switch (keycode) {
case Qt::Key_Return:
return kVK_Return;
case Qt::Key_Enter:
return kVK_ANSI_KeypadEnter;
case Qt::Key_Tab:
return kVK_Tab;
case Qt::Key_Space:
return kVK_Space;
case Qt::Key_Backspace:
return kVK_Delete;
case Qt::Key_Escape:
return kVK_Escape;
case Qt::Key_CapsLock:
return kVK_CapsLock;
case Qt::Key_Option:
return kVK_Option;
case Qt::Key_F17:
return kVK_F17;
case Qt::Key_VolumeUp:
return kVK_VolumeUp;
case Qt::Key_VolumeDown:
return kVK_VolumeDown;
case Qt::Key_F18:
return kVK_F18;
case Qt::Key_F19:
return kVK_F19;
case Qt::Key_F20:
return kVK_F20;
case Qt::Key_F5:
return kVK_F5;
case Qt::Key_F6:
return kVK_F6;
case Qt::Key_F7:
return kVK_F7;
case Qt::Key_F3:
return kVK_F3;
case Qt::Key_F8:
return kVK_F8;
case Qt::Key_F9:
return kVK_F9;
case Qt::Key_F11:
return kVK_F11;
case Qt::Key_F13:
return kVK_F13;
case Qt::Key_F16:
return kVK_F16;
case Qt::Key_F14:
return kVK_F14;
case Qt::Key_F10:
return kVK_F10;
case Qt::Key_F12:
return kVK_F12;
case Qt::Key_F15:
return kVK_F15;
case Qt::Key_Help:
return kVK_Help;
case Qt::Key_Home:
return kVK_Home;
case Qt::Key_PageUp:
return kVK_PageUp;
case Qt::Key_Delete:
return kVK_ForwardDelete;
case Qt::Key_F4:
return kVK_F4;
case Qt::Key_End:
return kVK_End;
case Qt::Key_F2:
return kVK_F2;
case Qt::Key_PageDown:
return kVK_PageDown;
case Qt::Key_F1:
return kVK_F1;
case Qt::Key_Left:
return kVK_LeftArrow;
case Qt::Key_Right:
return kVK_RightArrow;
case Qt::Key_Down:
return kVK_DownArrow;
case Qt::Key_Up:
return kVK_UpArrow;
default:
ok = false;
break;
}
UTF16Char ch = keycode;
CFDataRef currentLayoutData;
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
if (currentKeyboard == NULL) {
return 0;
}
currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
CFRelease(currentKeyboard);
if (currentLayoutData == NULL) {
return 0;
}
UCKeyboardLayout *header = (UCKeyboardLayout *)CFDataGetBytePtr(currentLayoutData);
UCKeyboardTypeHeader *table = header->keyboardTypeList;
uint8_t *data = (uint8_t *)header;
for (quint32 i = 0; i < header->keyboardTypeCount; i++) {
UCKeyStateRecordsIndex *stateRec = 0;
if (table[i].keyStateRecordsIndexOffset != 0) {
stateRec = reinterpret_cast<UCKeyStateRecordsIndex *>(data + table[i].keyStateRecordsIndexOffset);
if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) {
stateRec = 0;
}
}
UCKeyToCharTableIndex *charTable = reinterpret_cast<UCKeyToCharTableIndex *>(data + table[i].keyToCharTableIndexOffset);
if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) {
continue;
}
for (quint32 j = 0; j < charTable->keyToCharTableCount; j++) {
UCKeyOutput *keyToChar = reinterpret_cast<UCKeyOutput *>(data + charTable->keyToCharTableOffsets[j]);
for (quint32 k = 0; k < charTable->keyToCharTableSize; k++) {
if (keyToChar[k] & kUCKeyOutputTestForIndexMask) {
long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
if (stateRec && idx < stateRec->keyStateRecordCount) {
UCKeyStateRecord *rec = reinterpret_cast<UCKeyStateRecord *>(data + stateRec->keyStateRecordOffsets[idx]);
if (rec->stateZeroCharData == ch) {
ok = true;
return k;
}
}
} else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE) {
if (keyToChar[k] == ch) {
ok = true;
return k;
}
}
}
}
}
return 0;
}
quint32 QHotkeyPrivateMac::nativeModifiers(Qt::KeyboardModifiers modifiers, bool &ok)
{
quint32 nMods = 0;
if (modifiers & Qt::ShiftModifier) {
nMods |= shiftKey;
}
if (modifiers & Qt::ControlModifier) {
nMods |= cmdKey;
}
if (modifiers & Qt::AltModifier) {
nMods |= optionKey;
}
if (modifiers & Qt::MetaModifier) {
nMods |= controlKey;
}
if (modifiers & Qt::KeypadModifier) {
nMods |= kEventKeyModifierNumLockMask;
}
ok = true;
return nMods;
}
bool QHotkeyPrivateMac::registerShortcut(QHotkey::NativeShortcut shortcut)
{
if (!this->isHotkeyHandlerRegistered) {
EventTypeSpec eventSpec;
eventSpec.eventClass = kEventClassKeyboard;
eventSpec.eventKind = kEventHotKeyPressed;
InstallApplicationEventHandler(&QHotkeyPrivateMac::hotkeyEventHandler, 1, &eventSpec, NULL, NULL);
}
EventHotKeyID hkeyID;
hkeyID.signature = shortcut.key;
hkeyID.id = shortcut.modifier;
EventHotKeyRef eventRef = 0;
OSStatus status = RegisterEventHotKey(shortcut.key,
shortcut.modifier,
hkeyID,
GetApplicationEventTarget(),
0,
&eventRef);
if (status != noErr) {
qDebug() << "Failed to register hotkey. Error:" << status;
return false;
} else {
this->hotkeyRefs.insert(shortcut, eventRef);
return true;
}
}
bool QHotkeyPrivateMac::unregisterShortcut(QHotkey::NativeShortcut shortcut)
{
EventHotKeyRef eventRef = QHotkeyPrivateMac::hotkeyRefs.value(shortcut);
OSStatus status = UnregisterEventHotKey(eventRef);
if (status != noErr) {
qDebug() << "Failed to unregister hotkey. Error:" << status;
return false;
} else {
this->hotkeyRefs.remove(shortcut);
return true;
}
}
OSStatus QHotkeyPrivateMac::hotkeyEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *data)
{
Q_UNUSED(nextHandler);
Q_UNUSED(data);
if (GetEventClass(event) == kEventClassKeyboard &&
GetEventKind(event) == kEventHotKeyPressed) {
EventHotKeyID hkeyID;
GetEventParameter(event,
kEventParamDirectObject,
typeEventHotKeyID,
NULL,
sizeof(EventHotKeyID),
NULL,
&hkeyID);
hotkeyPrivate->activateShortcut(QHotkey::NativeShortcut(hkeyID.signature, hkeyID.id));
}
return noErr;
}