From d962de314b0e78250952b5ea493bbe47f4f3e4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Sun, 28 Jun 2020 13:03:11 +0200 Subject: macOS: Modernize QCocoaKeyMapper Paying off technical debt from Qt 4 times, and preparation for removing Carbon dependency. - Proper variable names (m_ prefix, titleCase, fullyWrittenOut) - Modern data structures for lookups - Removal of dead code/variables - Categorized logging - Built in constants instead of magic numbers - Typed variables instead of naked integers Change-Id: Ie14621e0da8ed61e2185fa05373047204dc4ea62 Reviewed-by: Volker Hilsheimer --- src/plugins/platforms/cocoa/qcocoakeymapper.h | 16 +- src/plugins/platforms/cocoa/qcocoakeymapper.mm | 584 ++++++++++++------------- 2 files changed, 284 insertions(+), 316 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.h b/src/plugins/platforms/cocoa/qcocoakeymapper.h index b4eacf7a76..bb39717b79 100644 --- a/src/plugins/platforms/cocoa/qcocoakeymapper.h +++ b/src/plugins/platforms/cocoa/qcocoakeymapper.h @@ -70,7 +70,6 @@ QT_BEGIN_NAMESPACE 16. Meta + Alt + Control + Shift */ struct KeyboardLayoutItem { - bool dirty; quint32 qtKey[16]; // Can by any Qt::Key_, or unicode character }; @@ -82,19 +81,20 @@ public: ~QCocoaKeyMapper(); static Qt::KeyboardModifiers queryKeyboardModifiers(); QList possibleKeys(const QKeyEvent *event) const; + +private: bool updateKeyboard(); void deleteLayouts(); void updateKeyMap(unsigned short macVirtualKey, QChar unicodeKey); void clearMappings(); -private: - QCFType currentInputSource = nullptr; + QCFType m_currentInputSource = nullptr; - enum { NullMode, UnicodeMode, OtherMode } keyboard_mode = NullMode; - const UCKeyboardLayout *keyboard_layout_format = nullptr; - KeyboardLayoutKind keyboard_kind = kKLKCHRuchrKind; - UInt32 keyboard_dead = 0; - KeyboardLayoutItem *keyLayout[256]; + enum { NullMode, UnicodeMode, OtherMode } m_keyboardMode = NullMode; + const UCKeyboardLayout *m_keyboardLayoutFormat = nullptr; + KeyboardLayoutKind m_keyboardKind = kKLKCHRuchrKind; + UInt32 m_deadKeyState = 0; // Maintains dead key state beween calls to UCKeyTranslate + KeyboardLayoutItem *m_keyLayout[256]; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm index 8d923bb9ec..a0004f6f8f 100644 --- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm +++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -41,308 +41,246 @@ #include "qcocoakeymapper.h" -#include +#include #include QT_BEGIN_NAMESPACE -// QCocoaKeyMapper debug facilities -//#define DEBUG_KEY_BINDINGS -//#define DEBUG_KEY_BINDINGS_MODIFIERS -//#define DEBUG_KEY_MAPS - -// Possible modifier states. -// NOTE: The order of these states match the order in updatePossibleKeyCodes()! -static const Qt::KeyboardModifiers ModsTbl[] = { - Qt::NoModifier, // 0 - Qt::ShiftModifier, // 1 - Qt::ControlModifier, // 2 - Qt::ControlModifier | Qt::ShiftModifier, // 3 - Qt::AltModifier, // 4 - Qt::AltModifier | Qt::ShiftModifier, // 5 - Qt::AltModifier | Qt::ControlModifier, // 6 - Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 - Qt::MetaModifier, // 8 - Qt::MetaModifier | Qt::ShiftModifier, // 9 - Qt::MetaModifier | Qt::ControlModifier, // 10 - Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier,// 11 - Qt::MetaModifier | Qt::AltModifier, // 12 - Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier, // 13 - Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier, // 14 - Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 15 +Q_LOGGING_CATEGORY(lcQpaKeyMapper, "qt.qpa.keymapper"); +Q_LOGGING_CATEGORY(lcQpaKeyMapperKeys, "qt.qpa.keymapper.keys"); +Q_LOGGING_CATEGORY(lcQpaKeyMapperModifiers, "qt.qpa.keymapper.modifiers"); + +static constexpr std::tuple carbonModifierMap[] = { + { shiftKey, Qt::ShiftModifier }, + { rightShiftKey, Qt::ShiftModifier }, + { controlKey, Qt::MetaModifier }, + { rightControlKey, Qt::MetaModifier }, + { cmdKey, Qt::ControlModifier }, + { optionKey, Qt::AltModifier }, + { rightOptionKey, Qt::AltModifier }, + { kEventKeyModifierNumLockMask, Qt::KeypadModifier } }; -bool qt_mac_eat_unicode_key = false; - - -/* key maps */ -struct qt_mac_enum_mapper -{ - int mac_code; - int qt_code; -#if defined(DEBUG_KEY_BINDINGS) -# define QT_MAC_MAP_ENUM(x) x, #x - const char *desc; -#else -# define QT_MAC_MAP_ENUM(x) x -#endif -}; +using CarbonModifiers = UInt32; // As opposed to EventModifiers which is UInt16 -//modifiers -static qt_mac_enum_mapper qt_mac_modifier_symbols[] = { - { shiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) }, - { rightShiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) }, - { controlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) }, - { rightControlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) }, - { cmdKey, QT_MAC_MAP_ENUM(Qt::ControlModifier) }, - { optionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) }, - { rightOptionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) }, - { kEventKeyModifierNumLockMask, QT_MAC_MAP_ENUM(Qt::KeypadModifier) }, - { 0, QT_MAC_MAP_ENUM(0) } -}; -Qt::KeyboardModifiers qt_mac_get_modifiers(int keys) +Qt::KeyboardModifiers fromCarbonModifiers(CarbonModifiers carbonModifiers) { -#ifdef DEBUG_KEY_BINDINGS_MODIFIERS - qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", keys, keys); -#endif - Qt::KeyboardModifiers ret = Qt::NoModifier; - for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) { - if (keys & qt_mac_modifier_symbols[i].mac_code) { -#ifdef DEBUG_KEY_BINDINGS_MODIFIERS - qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc); -#endif - ret |= Qt::KeyboardModifier(qt_mac_modifier_symbols[i].qt_code); + qCDebug(lcQpaKeyMapperModifiers, "Mapping carbon modifiers: %d (0x%04x)", + carbonModifiers, carbonModifiers); + + Qt::KeyboardModifiers qtModifiers = Qt::NoModifier; + for (const auto &[carbonModifier, qtModifier] : carbonModifierMap) { + if (carbonModifiers & carbonModifier) { + qCDebug(lcQpaKeyMapperModifiers) << "Got modifier" << qtModifier; + qtModifiers |= qtModifier; } } + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { - Qt::KeyboardModifiers oldModifiers = ret; - ret &= ~(Qt::MetaModifier | Qt::ControlModifier); + Qt::KeyboardModifiers oldModifiers = qtModifiers; + qtModifiers &= ~(Qt::MetaModifier | Qt::ControlModifier); if (oldModifiers & Qt::ControlModifier) - ret |= Qt::MetaModifier; + qtModifiers |= Qt::MetaModifier; if (oldModifiers & Qt::MetaModifier) - ret |= Qt::ControlModifier; + qtModifiers |= Qt::ControlModifier; } - return ret; + + return qtModifiers; } -static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys) + +static CarbonModifiers toCarbonModifiers(Qt::KeyboardModifiers qtModifiers) { -#ifdef DEBUG_KEY_BINDINGS_MODIFIERS - qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", (int)keys, (int)keys); -#endif - int ret = 0; - for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) { - if (keys & qt_mac_modifier_symbols[i].qt_code) { -#ifdef DEBUG_KEY_BINDINGS_MODIFIERS - qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc); -#endif - ret |= qt_mac_modifier_symbols[i].mac_code; + qCDebug(lcQpaKeyMapperModifiers).verbosity(1) << "Mapping" << qtModifiers; + + CarbonModifiers carbonModifiers = 0; + for (const auto &[carbonModifier, qtModifier] : carbonModifierMap) { + if (qtModifiers & qtModifier) { + qCDebug(lcQpaKeyMapperModifiers) << "Got carbon modifier" << carbonModifier; + carbonModifiers |= carbonModifier; } } if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { - int oldModifiers = ret; - ret &= ~(controlKeyBit | cmdKeyBit); + int oldModifiers = carbonModifiers; + carbonModifiers &= ~(controlKeyBit | cmdKeyBit); if (oldModifiers & controlKeyBit) - ret |= cmdKeyBit; + carbonModifiers |= cmdKeyBit; if (oldModifiers & cmdKeyBit) - ret |= controlKeyBit; + carbonModifiers |= controlKeyBit; } - return ret; + return carbonModifiers; } -//keyboard keys (non-modifiers) -static qt_mac_enum_mapper qt_mac_keyboard_symbols[] = { - { kHomeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Home) }, - { kEnterCharCode, QT_MAC_MAP_ENUM(Qt::Key_Enter) }, - { kEndCharCode, QT_MAC_MAP_ENUM(Qt::Key_End) }, - { kBackspaceCharCode, QT_MAC_MAP_ENUM(Qt::Key_Backspace) }, - { kTabCharCode, QT_MAC_MAP_ENUM(Qt::Key_Tab) }, - { kPageUpCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageUp) }, - { kPageDownCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageDown) }, - { kReturnCharCode, QT_MAC_MAP_ENUM(Qt::Key_Return) }, - { kEscapeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Escape) }, - { kLeftArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Left) }, - { kRightArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Right) }, - { kUpArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Up) }, - { kDownArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Down) }, - { kHelpCharCode, QT_MAC_MAP_ENUM(Qt::Key_Help) }, - { kDeleteCharCode, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, -//ascii maps, for debug - { ':', QT_MAC_MAP_ENUM(Qt::Key_Colon) }, - { ';', QT_MAC_MAP_ENUM(Qt::Key_Semicolon) }, - { '<', QT_MAC_MAP_ENUM(Qt::Key_Less) }, - { '=', QT_MAC_MAP_ENUM(Qt::Key_Equal) }, - { '>', QT_MAC_MAP_ENUM(Qt::Key_Greater) }, - { '?', QT_MAC_MAP_ENUM(Qt::Key_Question) }, - { '@', QT_MAC_MAP_ENUM(Qt::Key_At) }, - { ' ', QT_MAC_MAP_ENUM(Qt::Key_Space) }, - { '!', QT_MAC_MAP_ENUM(Qt::Key_Exclam) }, - { '"', QT_MAC_MAP_ENUM(Qt::Key_QuoteDbl) }, - { '#', QT_MAC_MAP_ENUM(Qt::Key_NumberSign) }, - { '$', QT_MAC_MAP_ENUM(Qt::Key_Dollar) }, - { '%', QT_MAC_MAP_ENUM(Qt::Key_Percent) }, - { '&', QT_MAC_MAP_ENUM(Qt::Key_Ampersand) }, - { '\'', QT_MAC_MAP_ENUM(Qt::Key_Apostrophe) }, - { '(', QT_MAC_MAP_ENUM(Qt::Key_ParenLeft) }, - { ')', QT_MAC_MAP_ENUM(Qt::Key_ParenRight) }, - { '*', QT_MAC_MAP_ENUM(Qt::Key_Asterisk) }, - { '+', QT_MAC_MAP_ENUM(Qt::Key_Plus) }, - { ',', QT_MAC_MAP_ENUM(Qt::Key_Comma) }, - { '-', QT_MAC_MAP_ENUM(Qt::Key_Minus) }, - { '.', QT_MAC_MAP_ENUM(Qt::Key_Period) }, - { '/', QT_MAC_MAP_ENUM(Qt::Key_Slash) }, - { '[', QT_MAC_MAP_ENUM(Qt::Key_BracketLeft) }, - { ']', QT_MAC_MAP_ENUM(Qt::Key_BracketRight) }, - { '\\', QT_MAC_MAP_ENUM(Qt::Key_Backslash) }, - { '_', QT_MAC_MAP_ENUM(Qt::Key_Underscore) }, - { '`', QT_MAC_MAP_ENUM(Qt::Key_QuoteLeft) }, - { '{', QT_MAC_MAP_ENUM(Qt::Key_BraceLeft) }, - { '}', QT_MAC_MAP_ENUM(Qt::Key_BraceRight) }, - { '|', QT_MAC_MAP_ENUM(Qt::Key_Bar) }, - { '~', QT_MAC_MAP_ENUM(Qt::Key_AsciiTilde) }, - { '^', QT_MAC_MAP_ENUM(Qt::Key_AsciiCircum) }, - { 0, QT_MAC_MAP_ENUM(0) } +// Keyboard keys (non-modifiers) +static QHash standardKeys = { + { kHomeCharCode, Qt::Key_Home }, + { kEnterCharCode, Qt::Key_Enter }, + { kEndCharCode, Qt::Key_End }, + { kBackspaceCharCode, Qt::Key_Backspace }, + { kTabCharCode, Qt::Key_Tab }, + { kPageUpCharCode, Qt::Key_PageUp }, + { kPageDownCharCode, Qt::Key_PageDown }, + { kReturnCharCode, Qt::Key_Return }, + { kEscapeCharCode, Qt::Key_Escape }, + { kLeftArrowCharCode, Qt::Key_Left }, + { kRightArrowCharCode, Qt::Key_Right }, + { kUpArrowCharCode, Qt::Key_Up }, + { kDownArrowCharCode, Qt::Key_Down }, + { kHelpCharCode, Qt::Key_Help }, + { kDeleteCharCode, Qt::Key_Delete }, + // ASCII maps, for debugging + { ':', Qt::Key_Colon }, + { ';', Qt::Key_Semicolon }, + { '<', Qt::Key_Less }, + { '=', Qt::Key_Equal }, + { '>', Qt::Key_Greater }, + { '?', Qt::Key_Question }, + { '@', Qt::Key_At }, + { ' ', Qt::Key_Space }, + { '!', Qt::Key_Exclam }, + { '"', Qt::Key_QuoteDbl }, + { '#', Qt::Key_NumberSign }, + { '$', Qt::Key_Dollar }, + { '%', Qt::Key_Percent }, + { '&', Qt::Key_Ampersand }, + { '\'', Qt::Key_Apostrophe }, + { '(', Qt::Key_ParenLeft }, + { ')', Qt::Key_ParenRight }, + { '*', Qt::Key_Asterisk }, + { '+', Qt::Key_Plus }, + { ',', Qt::Key_Comma }, + { '-', Qt::Key_Minus }, + { '.', Qt::Key_Period }, + { '/', Qt::Key_Slash }, + { '[', Qt::Key_BracketLeft }, + { ']', Qt::Key_BracketRight }, + { '\\', Qt::Key_Backslash }, + { '_', Qt::Key_Underscore }, + { '`', Qt::Key_QuoteLeft }, + { '{', Qt::Key_BraceLeft }, + { '}', Qt::Key_BraceRight }, + { '|', Qt::Key_Bar }, + { '~', Qt::Key_AsciiTilde }, + { '^', Qt::Key_AsciiCircum } }; -static qt_mac_enum_mapper qt_mac_keyvkey_symbols[] = { //real scan codes - { kVK_F1, QT_MAC_MAP_ENUM(Qt::Key_F1) }, - { kVK_F2, QT_MAC_MAP_ENUM(Qt::Key_F2) }, - { kVK_F3, QT_MAC_MAP_ENUM(Qt::Key_F3) }, - { kVK_F4, QT_MAC_MAP_ENUM(Qt::Key_F4) }, - { kVK_F5, QT_MAC_MAP_ENUM(Qt::Key_F5) }, - { kVK_F6, QT_MAC_MAP_ENUM(Qt::Key_F6) }, - { kVK_F7, QT_MAC_MAP_ENUM(Qt::Key_F7) }, - { kVK_F8, QT_MAC_MAP_ENUM(Qt::Key_F8) }, - { kVK_F9, QT_MAC_MAP_ENUM(Qt::Key_F9) }, - { kVK_F10, QT_MAC_MAP_ENUM(Qt::Key_F10) }, - { kVK_F11, QT_MAC_MAP_ENUM(Qt::Key_F11) }, - { kVK_F12, QT_MAC_MAP_ENUM(Qt::Key_F12) }, - { kVK_F13, QT_MAC_MAP_ENUM(Qt::Key_F13) }, - { kVK_F14, QT_MAC_MAP_ENUM(Qt::Key_F14) }, - { kVK_F15, QT_MAC_MAP_ENUM(Qt::Key_F15) }, - { kVK_F16, QT_MAC_MAP_ENUM(Qt::Key_F16) }, - { kVK_Return, QT_MAC_MAP_ENUM(Qt::Key_Return) }, - { kVK_Tab, QT_MAC_MAP_ENUM(Qt::Key_Tab) }, - { kVK_Escape, QT_MAC_MAP_ENUM(Qt::Key_Escape) }, - { kVK_Help, QT_MAC_MAP_ENUM(Qt::Key_Help) }, - { kVK_UpArrow, QT_MAC_MAP_ENUM(Qt::Key_Up) }, - { kVK_DownArrow, QT_MAC_MAP_ENUM(Qt::Key_Down) }, - { kVK_LeftArrow, QT_MAC_MAP_ENUM(Qt::Key_Left) }, - { kVK_RightArrow, QT_MAC_MAP_ENUM(Qt::Key_Right) }, - { kVK_PageUp, QT_MAC_MAP_ENUM(Qt::Key_PageUp) }, - { kVK_PageDown, QT_MAC_MAP_ENUM(Qt::Key_PageDown) }, - { 0, QT_MAC_MAP_ENUM(0) } +static QHash virtualKeys = { + { kVK_F1, Qt::Key_F1 }, + { kVK_F2, Qt::Key_F2 }, + { kVK_F3, Qt::Key_F3 }, + { kVK_F4, Qt::Key_F4 }, + { kVK_F5, Qt::Key_F5 }, + { kVK_F6, Qt::Key_F6 }, + { kVK_F7, Qt::Key_F7 }, + { kVK_F8, Qt::Key_F8 }, + { kVK_F9, Qt::Key_F9 }, + { kVK_F10, Qt::Key_F10 }, + { kVK_F11, Qt::Key_F11 }, + { kVK_F12, Qt::Key_F12 }, + { kVK_F13, Qt::Key_F13 }, + { kVK_F14, Qt::Key_F14 }, + { kVK_F15, Qt::Key_F15 }, + { kVK_F16, Qt::Key_F16 }, + { kVK_Return, Qt::Key_Return }, + { kVK_Tab, Qt::Key_Tab }, + { kVK_Escape, Qt::Key_Escape }, + { kVK_Help, Qt::Key_Help }, + { kVK_UpArrow, Qt::Key_Up }, + { kVK_DownArrow, Qt::Key_Down }, + { kVK_LeftArrow, Qt::Key_Left }, + { kVK_RightArrow, Qt::Key_Right }, + { kVK_PageUp, Qt::Key_PageUp }, + { kVK_PageDown, Qt::Key_PageDown } }; -static qt_mac_enum_mapper qt_mac_private_unicode[] = { - { 0xF700, QT_MAC_MAP_ENUM(Qt::Key_Up) }, //NSUpArrowFunctionKey - { 0xF701, QT_MAC_MAP_ENUM(Qt::Key_Down) }, //NSDownArrowFunctionKey - { 0xF702, QT_MAC_MAP_ENUM(Qt::Key_Left) }, //NSLeftArrowFunctionKey - { 0xF703, QT_MAC_MAP_ENUM(Qt::Key_Right) }, //NSRightArrowFunctionKey - { 0xF727, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertFunctionKey - { 0xF728, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteFunctionKey - { 0xF729, QT_MAC_MAP_ENUM(Qt::Key_Home) }, //NSHomeFunctionKey - { 0xF72B, QT_MAC_MAP_ENUM(Qt::Key_End) }, //NSEndFunctionKey - { 0xF72C, QT_MAC_MAP_ENUM(Qt::Key_PageUp) }, //NSPageUpFunctionKey - { 0xF72D, QT_MAC_MAP_ENUM(Qt::Key_PageDown) }, //NSPageDownFunctionKey - { 0xF72E, QT_MAC_MAP_ENUM(Qt::Key_Print) }, //NSPrintScreenFunctionKey - { 0xF72F, QT_MAC_MAP_ENUM(Qt::Key_ScrollLock) }, //NSScrollLockFunctionKey - { 0xF730, QT_MAC_MAP_ENUM(Qt::Key_Pause) }, //NSPauseFunctionKey - { 0xF731, QT_MAC_MAP_ENUM(Qt::Key_SysReq) }, //NSSysReqFunctionKey - { 0xF735, QT_MAC_MAP_ENUM(Qt::Key_Menu) }, //NSMenuFunctionKey - { 0xF738, QT_MAC_MAP_ENUM(Qt::Key_Printer) }, //NSPrintFunctionKey - { 0xF73A, QT_MAC_MAP_ENUM(Qt::Key_Clear) }, //NSClearDisplayFunctionKey - { 0xF73D, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertCharFunctionKey - { 0xF73E, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteCharFunctionKey - { 0xF741, QT_MAC_MAP_ENUM(Qt::Key_Select) }, //NSSelectFunctionKey - { 0xF742, QT_MAC_MAP_ENUM(Qt::Key_Execute) }, //NSExecuteFunctionKey - { 0xF743, QT_MAC_MAP_ENUM(Qt::Key_Undo) }, //NSUndoFunctionKey - { 0xF744, QT_MAC_MAP_ENUM(Qt::Key_Redo) }, //NSRedoFunctionKey - { 0xF745, QT_MAC_MAP_ENUM(Qt::Key_Find) }, //NSFindFunctionKey - { 0xF746, QT_MAC_MAP_ENUM(Qt::Key_Help) }, //NSHelpFunctionKey - { 0xF747, QT_MAC_MAP_ENUM(Qt::Key_Mode_switch) }, //NSModeSwitchFunctionKey - { 0, QT_MAC_MAP_ENUM(0) } +static QHash functionKeys = { + { NSUpArrowFunctionKey, Qt::Key_Up }, + { NSDownArrowFunctionKey, Qt::Key_Down }, + { NSLeftArrowFunctionKey, Qt::Key_Left }, + { NSRightArrowFunctionKey, Qt::Key_Right }, + // F1-35 function keys handled manually below + { NSInsertFunctionKey, Qt::Key_Insert }, + { NSDeleteFunctionKey, Qt::Key_Delete }, + { NSHomeFunctionKey, Qt::Key_Home }, + { NSEndFunctionKey, Qt::Key_End }, + { NSPageUpFunctionKey, Qt::Key_PageUp }, + { NSPageDownFunctionKey, Qt::Key_PageDown }, + { NSPrintScreenFunctionKey, Qt::Key_Print }, + { NSScrollLockFunctionKey, Qt::Key_ScrollLock }, + { NSPauseFunctionKey, Qt::Key_Pause }, + { NSSysReqFunctionKey, Qt::Key_SysReq }, + { NSMenuFunctionKey, Qt::Key_Menu }, + { NSPrintFunctionKey, Qt::Key_Printer }, + { NSClearDisplayFunctionKey, Qt::Key_Clear }, + { NSInsertCharFunctionKey, Qt::Key_Insert }, + { NSDeleteCharFunctionKey, Qt::Key_Delete }, + { NSSelectFunctionKey, Qt::Key_Select }, + { NSExecuteFunctionKey, Qt::Key_Execute }, + { NSUndoFunctionKey, Qt::Key_Undo }, + { NSRedoFunctionKey, Qt::Key_Redo }, + { NSFindFunctionKey, Qt::Key_Find }, + { NSHelpFunctionKey, Qt::Key_Help }, + { NSModeSwitchFunctionKey, Qt::Key_Mode_switch } }; -static int qt_mac_get_key(int modif, const QChar &key, int virtualKey) +static int toKeyCode(const QChar &key, int virtualKey, int modifiers) { -#ifdef DEBUG_KEY_BINDINGS - qDebug("**Mapping key: %d (0x%04x) - %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey); -#endif + qCDebug(lcQpaKeyMapperKeys, "Mapping key: %d (0x%04x) / vk %d (0x%04x)", + key.unicode(), key.unicode(), virtualKey, virtualKey); if (key == kClearCharCode && virtualKey == 0x47) return Qt::Key_Clear; if (key.isDigit()) { -#ifdef DEBUG_KEY_BINDINGS - qDebug("%d: got key: %d", __LINE__, key.digitValue()); -#endif + qCDebug(lcQpaKeyMapperKeys, "Got digit key: %d", key.digitValue()); return key.digitValue() + Qt::Key_0; } if (key.isLetter()) { -#ifdef DEBUG_KEY_BINDINGS - qDebug("%d: got key: %d", __LINE__, (key.toUpper().unicode() - 'A')); -#endif + qCDebug(lcQpaKeyMapperKeys, "Got letter key: %d", (key.toUpper().unicode() - 'A')); return (key.toUpper().unicode() - 'A') + Qt::Key_A; } if (key.isSymbol()) { -#ifdef DEBUG_KEY_BINDINGS - qDebug("%d: got key: %d", __LINE__, (key.unicode())); -#endif + qCDebug(lcQpaKeyMapperKeys, "Got symbol key: %d", (key.unicode())); return key.unicode(); } - for (int i = 0; qt_mac_keyboard_symbols[i].qt_code; i++) { - if (qt_mac_keyboard_symbols[i].mac_code == key) { - /* To work like Qt for X11 we issue Backtab when Shift + Tab are pressed */ - if (qt_mac_keyboard_symbols[i].qt_code == Qt::Key_Tab && (modif & Qt::ShiftModifier)) { -#ifdef DEBUG_KEY_BINDINGS - qDebug("%d: got key: Qt::Key_Backtab", __LINE__); -#endif - return Qt::Key_Backtab; - } - -#ifdef DEBUG_KEY_BINDINGS - qDebug("%d: got key: %s", __LINE__, qt_mac_keyboard_symbols[i].desc); -#endif - return qt_mac_keyboard_symbols[i].qt_code; + if (auto qtKey = standardKeys.value(key)) { + // To work like Qt for X11 we issue Backtab when Shift + Tab are pressed + if (qtKey == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { + qCDebug(lcQpaKeyMapperKeys, "Got key: Qt::Key_Backtab"); + return Qt::Key_Backtab; } + + qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey; + return qtKey; } - //last ditch try to match the scan code - for (int i = 0; qt_mac_keyvkey_symbols[i].qt_code; i++) { - if (qt_mac_keyvkey_symbols[i].mac_code == virtualKey) { -#ifdef DEBUG_KEY_BINDINGS - qDebug("%d: got key: %s", __LINE__, qt_mac_keyvkey_symbols[i].desc); -#endif - return qt_mac_keyvkey_symbols[i].qt_code; - } + // Last ditch try to match the scan code + if (auto qtKey = virtualKeys.value(virtualKey)) { + qCDebug(lcQpaKeyMapperKeys) << "Got scancode" << qtKey; + return qtKey; } - // check if they belong to key codes in private unicode range - if (key >= 0xf700 && key <= 0xf747) { - if (key >= 0xf704 && key <= 0xf726) { - return Qt::Key_F1 + (key.unicode() - 0xf704) ; + // Check if they belong to key codes in private unicode range + if (key >= NSUpArrowFunctionKey && key <= NSModeSwitchFunctionKey) { + if (auto qtKey = functionKeys.value(key)) { + qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey; + return qtKey; + } else if (key >= NSF1FunctionKey && key <= NSF35FunctionKey) { + auto functionKey = Qt::Key_F1 + (key.unicode() - NSF1FunctionKey) ; + qCDebug(lcQpaKeyMapperKeys) << "Got" << functionKey; + return functionKey; } - for (int i = 0; qt_mac_private_unicode[i].qt_code; i++) { - if (qt_mac_private_unicode[i].mac_code == key) { - return qt_mac_private_unicode[i].qt_code; - } - } - } - //oh well -#ifdef DEBUG_KEY_BINDINGS - qDebug("Unknown case.. %s:%d %d[%d] %d", __FILE__, __LINE__, key.unicode(), key.toLatin1(), virtualKey); -#endif + qCDebug(lcQpaKeyMapperKeys, "Unknown case.. %d[%d] %d", key.unicode(), key.toLatin1(), virtualKey); return Qt::Key_unknown; } QCocoaKeyMapper::QCocoaKeyMapper() { - memset(keyLayout, 0, sizeof(keyLayout)); + memset(m_keyLayout, 0, sizeof(m_keyLayout)); } QCocoaKeyMapper::~QCocoaKeyMapper() @@ -352,48 +290,49 @@ QCocoaKeyMapper::~QCocoaKeyMapper() Qt::KeyboardModifiers QCocoaKeyMapper::queryKeyboardModifiers() { - return qt_mac_get_modifiers(GetCurrentKeyModifiers()); + return fromCarbonModifiers(GetCurrentKeyModifiers()); } bool QCocoaKeyMapper::updateKeyboard() { - const UCKeyboardLayout *uchrData = nullptr; QCFType source = TISCopyInputMethodKeyboardLayoutOverride(); if (!source) source = TISCopyCurrentKeyboardInputSource(); - if (keyboard_mode != NullMode && source == currentInputSource) { + + if (m_keyboardMode != NullMode && source == m_currentInputSource) return false; - } + Q_ASSERT(source); - CFDataRef data = static_cast(TISGetInputSourceProperty(source, - kTISPropertyUnicodeKeyLayoutData)); - uchrData = data ? reinterpret_cast(CFDataGetBytePtr(data)) : nullptr; - - keyboard_kind = LMGetKbdType(); - if (uchrData) { - keyboard_layout_format = uchrData; - keyboard_mode = UnicodeMode; + m_currentInputSource = source; + m_keyboardKind = LMGetKbdType(); + m_deadKeyState = 0; + + deleteLayouts(); + + if (auto data = CFDataRef(TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData))) { + const UCKeyboardLayout *uchrData = reinterpret_cast(CFDataGetBytePtr(data)); + Q_ASSERT(uchrData); + m_keyboardLayoutFormat = uchrData; + m_keyboardMode = UnicodeMode; } else { - keyboard_layout_format = nullptr; - keyboard_mode = NullMode; + m_keyboardLayoutFormat = nullptr; + m_keyboardMode = NullMode; } - currentInputSource = source; - keyboard_dead = 0; - const auto newMode = keyboard_mode; - deleteLayouts(); - keyboard_mode = newMode; + qCDebug(lcQpaKeyMapper) << "Updated keyboard to" + << QString::fromCFString(CFStringRef(TISGetInputSourceProperty( + m_currentInputSource, kTISPropertyLocalizedName))); return true; } void QCocoaKeyMapper::deleteLayouts() { - keyboard_mode = NullMode; + m_keyboardMode = NullMode; for (int i = 0; i < 255; ++i) { - if (keyLayout[i]) { - delete keyLayout[i]; - keyLayout[i] = nullptr; + if (m_keyLayout[i]) { + delete m_keyLayout[i]; + m_keyLayout[i] = nullptr; } } } @@ -404,69 +343,98 @@ void QCocoaKeyMapper::clearMappings() updateKeyboard(); } +static constexpr Qt::KeyboardModifiers modifierCombinations[] = { + Qt::NoModifier, // 0 + Qt::ShiftModifier, // 1 + Qt::ControlModifier, // 2 + Qt::ControlModifier | Qt::ShiftModifier, // 3 + Qt::AltModifier, // 4 + Qt::AltModifier | Qt::ShiftModifier, // 5 + Qt::AltModifier | Qt::ControlModifier, // 6 + Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 + Qt::MetaModifier, // 8 + Qt::MetaModifier | Qt::ShiftModifier, // 9 + Qt::MetaModifier | Qt::ControlModifier, // 10 + Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier, // 11 + Qt::MetaModifier | Qt::AltModifier, // 12 + Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier, // 13 + Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier, // 14 + Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 15 +}; + +/* + Updates the key map for the given \macVirtualKey by applying + all possible modifier combinations. +*/ void QCocoaKeyMapper::updateKeyMap(unsigned short macVirtualKey, QChar unicodeKey) { updateKeyboard(); - if (keyLayout[macVirtualKey]) + if (m_keyLayout[macVirtualKey]) return; - UniCharCount buffer_size = 10; - UniChar buffer[buffer_size]; - keyLayout[macVirtualKey] = new KeyboardLayoutItem; - for (int i = 0; i < 16; ++i) { - UniCharCount out_buffer_size = 0; - keyLayout[macVirtualKey]->qtKey[i] = 0; - - const UInt32 keyModifier = ((qt_mac_get_mac_modifiers(ModsTbl[i]) >> 8) & 0xFF); - OSStatus err = UCKeyTranslate(keyboard_layout_format, macVirtualKey, kUCKeyActionDown, keyModifier, - keyboard_kind, 0, &keyboard_dead, buffer_size, &out_buffer_size, buffer); - if (err == noErr && out_buffer_size) { - const QChar unicode(buffer[0]); - int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey); - if (qtkey == Qt::Key_unknown) - qtkey = unicode.unicode(); - keyLayout[macVirtualKey]->qtKey[i] = qtkey; - } else { - int qtkey = qt_mac_get_key(keyModifier, unicodeKey, macVirtualKey); - if (qtkey == Qt::Key_unknown) - qtkey = unicodeKey.unicode(); - keyLayout[macVirtualKey]->qtKey[i] = qtkey; - } - } -#ifdef DEBUG_KEY_MAPS - qDebug("updateKeyMap for virtual key = 0x%02x!", (uint)macVirtualKey); + qCDebug(lcQpaKeyMapper, "Updating key map for virtual key = 0x%02x!", (uint)macVirtualKey); + + UniCharCount maxStringLength = 10; + UniChar unicodeString[maxStringLength]; + m_keyLayout[macVirtualKey] = new KeyboardLayoutItem; + for (int i = 0; i < 16; ++i) { - qDebug(" [%d] (%d,0x%02x,'%c')", i, - keyLayout[macVirtualKey]->qtKey[i], - keyLayout[macVirtualKey]->qtKey[i], - keyLayout[macVirtualKey]->qtKey[i]); + UniCharCount actualStringLength = 0; + m_keyLayout[macVirtualKey]->qtKey[i] = 0; + + auto qtModifiers = modifierCombinations[i]; + auto carbonModifiers = toCarbonModifiers(qtModifiers); + const UInt32 modifierKeyState = (carbonModifiers >> 8) & 0xFF; + OSStatus err = UCKeyTranslate(m_keyboardLayoutFormat, macVirtualKey, + kUCKeyActionDown, modifierKeyState, m_keyboardKind, OptionBits(0), + &m_deadKeyState, maxStringLength, &actualStringLength, unicodeString); + + // Use translated unicode key if valid + if (err == noErr && actualStringLength) + unicodeKey = QChar(unicodeString[0]); + + int qtkey = toKeyCode(unicodeKey, macVirtualKey, modifierKeyState); + if (qtkey == Qt::Key_unknown) + qtkey = unicodeKey.unicode(); + + m_keyLayout[macVirtualKey]->qtKey[i] = qtkey; + + qCDebug(lcQpaKeyMapper, " [%d] (%d,0x%02x,'%c')", i, qtkey, qtkey, qtkey); } -#endif } QList QCocoaKeyMapper::possibleKeys(const QKeyEvent *event) const { QList ret; - const_cast(this)->updateKeyMap(event->nativeVirtualKey(), QChar(event->key())); - KeyboardLayoutItem *kbItem = keyLayout[event->nativeVirtualKey()]; + auto virtualKey = event->nativeVirtualKey(); + const_cast(this)->updateKeyMap(virtualKey, QChar(event->key())); + KeyboardLayoutItem *keyLayout = m_keyLayout[virtualKey]; - if (!kbItem) // Key is not in any keyboard layout (e.g. eisu-key on Japanese keyboard) + if (!keyLayout) // Key is not in any keyboard layout (e.g. eisu-key on Japanese keyboard) return ret; - int baseKey = kbItem->qtKey[0]; - Qt::KeyboardModifiers keyMods = event->modifiers(); + int baseKey = keyLayout->qtKey[Qt::NoModifier]; + auto eventModifiers = event->modifiers(); - ret << int(baseKey + keyMods); // The base key is _always_ valid, of course + // The base key is always valid + ret << int(baseKey + eventModifiers); for (int i = 1; i < 8; ++i) { - Qt::KeyboardModifiers neededMods = ModsTbl[i]; - int key = kbItem->qtKey[i]; - if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) { - ret << int(key + (keyMods & ~neededMods)); - } + int keyAfterApplyingModifiers = keyLayout->qtKey[i]; + if (!keyAfterApplyingModifiers) + continue; + if (keyAfterApplyingModifiers == baseKey) + continue; + + // Include key if event modifiers includes, or matches + // perfectly, the current candidate modifiers. + auto candidateModifiers = modifierCombinations[i]; + if ((eventModifiers & candidateModifiers) == candidateModifiers) + ret << int(keyAfterApplyingModifiers + (eventModifiers & ~candidateModifiers)); } + return ret; } -- cgit v1.2.3