/**************************************************************************** ** Copyright (c) 2006 - 2011, the LibQxt project. ** See the Qxt AUTHORS file for a list of authors and copyright holders. ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** * Neither the name of the LibQxt project nor the ** names of its contributors may be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ** DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** ** *****************************************************************************/ #include #include "qxtglobalshortcut_p.h" #include #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) { Q_UNUSED(nextHandler); Q_UNUSED(data); if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) { EventHotKeyID keyID; GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(keyID), nullptr, &keyID); Identifier id = keyIDs.key(keyID.id); QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first); } return noErr; } quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) { quint32 native = 0; if (modifiers & Qt::ShiftModifier) native |= shiftKey; 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 switch (key) { 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_Control: return kVK_Command; case Qt::Key_Shift: return kVK_Shift; case Qt::Key_CapsLock: return kVK_CapsLock; case Qt::Key_Option: return kVK_Option; case Qt::Key_Meta: return kVK_Control; 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: ; } 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; CFDataRef currentLayoutData; TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); if (currentKeyboard == nullptr) return 0; currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); CFRelease(currentKeyboard); if (currentLayoutData == nullptr) return 0; UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData); UCKeyboardTypeHeader* table = header->keyboardTypeList; uint8_t *data = (uint8_t*)header; // God, would a little documentation for this shit kill you... 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, nullptr, nullptr); } 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); } 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); }