292 lines
7.9 KiB
C++
292 lines
7.9 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, _NATIVE_EVENT_RESULT *result) 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<QHotkey::NativeShortcut, EventHotKeyRef> hotkeyRefs;
|
||
|
};
|
||
|
NATIVE_INSTANCE(QHotkeyPrivateMac)
|
||
|
|
||
|
bool QHotkeyPrivate::isPlatformSupported()
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false;
|
||
|
QHash<QHotkey::NativeShortcut, EventHotKeyRef> QHotkeyPrivateMac::hotkeyRefs;
|
||
|
|
||
|
bool QHotkeyPrivateMac::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *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 = TISCopyCurrentASCIICapableKeyboardLayoutInputSource();
|
||
|
|
||
|
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 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;
|
||
|
}
|