#include "qhotkey.h" #include "qhotkey_p.h" #include #include class QHotkeyPrivateMac : public QHotkeyPrivate { public: // QAbstractNativeEventFilter interface bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) Q_DECL_OVERRIDE; static OSStatus hotkeyPressEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* data); static OSStatus hotkeyReleaseEventHandler(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 hotkeyRefs; }; NATIVE_INSTANCE(QHotkeyPrivateMac) bool QHotkeyPrivate::isPlatformSupported() { return true; } bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false; QHash 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( data + table[i].keyStateRecordsIndexOffset); if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0; } UCKeyToCharTableIndex* charTable = reinterpret_cast( data + table[i].keyToCharTableIndexOffset); if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue; for (quint32 j = 0; j < charTable->keyToCharTableCount; j++) { UCKeyOutput* keyToChar = reinterpret_cast( 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( 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 pressEventSpec; pressEventSpec.eventClass = kEventClassKeyboard; pressEventSpec.eventKind = kEventHotKeyPressed; InstallApplicationEventHandler( &QHotkeyPrivateMac::hotkeyPressEventHandler, 1, &pressEventSpec, NULL, NULL); EventTypeSpec releaseEventSpec; releaseEventSpec.eventClass = kEventClassKeyboard; releaseEventSpec.eventKind = kEventHotKeyReleased; InstallApplicationEventHandler( &QHotkeyPrivateMac::hotkeyReleaseEventHandler, 1, &releaseEventSpec, 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) { error = QString::number(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) { error = QString::number(status); return false; } else { this->hotkeyRefs.remove(shortcut); return true; } } OSStatus QHotkeyPrivateMac::hotkeyPressEventHandler( 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({ hkeyID.signature, hkeyID.id }); } return noErr; } OSStatus QHotkeyPrivateMac::hotkeyReleaseEventHandler( EventHandlerCallRef nextHandler, EventRef event, void* data) { Q_UNUSED(nextHandler); Q_UNUSED(data); if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyReleased) { EventHotKeyID hkeyID; GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(EventHotKeyID), NULL, &hkeyID); hotkeyPrivate->releaseShortcut({ hkeyID.signature, hkeyID.id }); } return noErr; }