/**************************************************************************** ** ** Copyright (C) Qxt Foundation. Some rights reserved. ** ** This file is part of the QxtGui module of the Qxt library. ** ** This library is free software; you can redistribute it and/or modify it ** under the terms of the Common Public License, version 1.0, as published ** by IBM, and/or under the terms of the GNU Lesser General Public License, ** version 2.1, as published by the Free Software Foundation. ** ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR ** FITNESS FOR A PARTICULAR PURPOSE. ** ** You should have received a copy of the CPL and the LGPL along with this ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files ** included with the source distribution for more information. ** If you did not receive a copy of the licenses, contact the Qxt Foundation. ** ** ** ****************************************************************************/ #include #include "qxtglobalshortcut_p.h" #include #include #include #include typedef QPair Identifier; static QMap keyRefs; static QHash keyIDs; static quint32 hotKeySerial = 0; static bool qxt_mac_handler_installed = false; OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data) { // pass event to the app event filter Q_UNUSED(data); qApp->macEventFilter(nextHandler, event); return noErr; } bool QxtGlobalShortcutPrivate::eventFilter(void* message) //bool QxtGlobalShortcutPrivate::macEventFilter(EventHandlerCallRef caller, EventRef event) { EventRef event = (EventRef) message; if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) { EventHotKeyID keyID; GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID); Identifier id = keyIDs.key(keyID.id); activateShortcut(id.second, id.first); } return false; } quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { quint32 native = 0; if (modifiers & Qt::ShiftModifier) native |= shiftKeyBit; if (modifiers & Qt::ControlModifier) native |= cmdKey; if (modifiers & Qt::AltModifier) native |= optionKey; if (modifiers & Qt::MetaModifier) native |= controlKey; if (modifiers & Qt::KeypadModifier) native |= kEventKeyModifierNumLockMask; return native; } quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) { UTF16Char ch; // Constants found in NSEvent.h from AppKit.framework if (key == Qt::Key_Up) ch = 0xF700; else if (key == Qt::Key_Down) ch = 0xF701; else if (key == Qt::Key_Left) ch = 0xF702; else if (key == Qt::Key_Right) ch = 0xF703; else if (key >= Qt::Key_F1 && key <= Qt::Key_F35) ch = key - Qt::Key_F1 + 0xF704; else if (key == Qt::Key_Insert) ch = 0xF727; else if (key == Qt::Key_Delete) ch = 0xF728; else if (key == Qt::Key_Home) ch = 0xF729; else if (key == Qt::Key_End) ch = 0xF72B; else if (key == Qt::Key_PageUp) ch = 0xF72C; else if (key == Qt::Key_PageDown) ch = 0xF72D; else if (key == Qt::Key_Print) ch = 0xF72E; else if (key == Qt::Key_ScrollLock) ch = 0xF72F; else if (key == Qt::Key_Pause) ch = 0xF730; else if (key == Qt::Key_SysReq) ch = 0xF731; else if (key == Qt::Key_Stop) ch = 0xF734; else if (key == Qt::Key_Menu) ch = 0xF735; else if (key == Qt::Key_Select) ch = 0xF741; else if (key == Qt::Key_Execute) ch = 0xF742; else if (key == Qt::Key_Help) ch = 0xF746; else if (key == Qt::Key_Mode_switch) ch = 0xF747; else if (key == Qt::Key_Escape) ch = 27; else if (key == Qt::Key_Return) ch = 13; else if (key == Qt::Key_Enter) ch = 3; else if (key == Qt::Key_Tab) ch = 9; else ch = key; KeyboardLayoutRef layout; KeyboardLayoutKind layoutKind; KLGetCurrentKeyboardLayout(&layout); KLGetKeyboardLayoutProperty(layout, kKLKind, const_cast(reinterpret_cast(&layoutKind))); if (layoutKind == kKLKCHRKind) { // no Unicode available if (ch > 255) return 0; char* data; KLGetKeyboardLayoutProperty(layout, kKLKCHRData, const_cast(reinterpret_cast(&data))); int ct = *reinterpret_cast(data + 258); for (int i = 0; i < ct; i++) { char* keyTable = data + 260 + 128 * i; for (int j = 0; j < 128; j++) { if (keyTable[j] == ch) return j; } } return 0; } char* data; KLGetKeyboardLayoutProperty(layout, kKLuchrData, const_cast(reinterpret_cast(&data))); UCKeyboardLayout* header = reinterpret_cast(data); UCKeyboardTypeHeader* table = header->keyboardTypeList; 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) return k; } } else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE) { if (keyToChar[k] == ch) return k; } } // for k } // for j } // for i return 0; } bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) { if (!qxt_mac_handler_installed) { EventTypeSpec t; t.eventClass = kEventClassKeyboard; t.eventKind = kEventHotKeyPressed; InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL); } EventHotKeyID keyID; keyID.signature = 'cute'; keyID.id = ++hotKeySerial; EventHotKeyRef ref = 0; bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref); if (rv) { keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id); keyRefs.insert(keyID.id, ref); } qDebug() << ref; return rv; } bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) { Identifier id(nativeMods, nativeKey); if (!keyIDs.contains(id)) return false; EventHotKeyRef ref = keyRefs.take(keyIDs[id]); keyIDs.remove(id); return !UnregisterEventHotKey(ref); }