macOS: Modernize QCocoaKeyMapper key map

Change-Id: I3e8a4cfa611b6ae575466c493aac438dc831e89a
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Tor Arne Vestbø 2020-07-10 12:13:36 +02:00
parent ce9efcf4a9
commit fab3dfff7d
2 changed files with 36 additions and 53 deletions

View File

@ -69,16 +69,10 @@ QT_BEGIN_NAMESPACE
15. Meta + Alt + Control 15. Meta + Alt + Control
16. Meta + Alt + Control + Shift 16. Meta + Alt + Control + Shift
*/ */
struct KeyboardLayoutItem {
quint32 qtKey[16]; // Can by any Qt::Key_<foo>, or unicode character
};
class QCocoaKeyMapper class QCocoaKeyMapper
{ {
public: public:
QCocoaKeyMapper();
~QCocoaKeyMapper();
static Qt::KeyboardModifiers queryKeyboardModifiers(); static Qt::KeyboardModifiers queryKeyboardModifiers();
QList<int> possibleKeys(const QKeyEvent *event) const; QList<int> possibleKeys(const QKeyEvent *event) const;
@ -89,9 +83,18 @@ public:
static Qt::Key fromCocoaKey(QChar keyCode); static Qt::Key fromCocoaKey(QChar keyCode);
private: private:
using VirtualKeyCode = unsigned short;
struct KeyMap : std::array<char32_t, 16>
{
// Initialize first element to a sentinel that allows us
// to distinguish an uninitialized map from an initialized.
// Using 0 would not allow us to map U+0000 (NUL), however
// unlikely that is.
KeyMap() : std::array<char32_t, 16>{Qt::Key_unknown} {}
};
bool updateKeyboard(); bool updateKeyboard();
void deleteLayouts(); const KeyMap &keyMapForKey(VirtualKeyCode virtualKey, QChar unicodeKey) const;
KeyboardLayoutItem *keyMapForKey(unsigned short macVirtualKey, QChar unicodeKey) const;
QCFType<TISInputSourceRef> m_currentInputSource = nullptr; QCFType<TISInputSourceRef> m_currentInputSource = nullptr;
@ -99,7 +102,8 @@ private:
const UCKeyboardLayout *m_keyboardLayoutFormat = nullptr; const UCKeyboardLayout *m_keyboardLayoutFormat = nullptr;
KeyboardLayoutKind m_keyboardKind = kKLKCHRuchrKind; KeyboardLayoutKind m_keyboardKind = kKLKCHRuchrKind;
mutable UInt32 m_deadKeyState = 0; // Maintains dead key state beween calls to UCKeyTranslate mutable UInt32 m_deadKeyState = 0; // Maintains dead key state beween calls to UCKeyTranslate
mutable KeyboardLayoutItem *m_keyLayout[256];
mutable QHash<VirtualKeyCode, KeyMap> m_keyMap;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -381,16 +381,6 @@ Qt::Key QCocoaKeyMapper::fromCocoaKey(QChar keyCode)
// ------------------------------------------------ // ------------------------------------------------
QCocoaKeyMapper::QCocoaKeyMapper()
{
memset(m_keyLayout, 0, sizeof(m_keyLayout));
}
QCocoaKeyMapper::~QCocoaKeyMapper()
{
deleteLayouts();
}
Qt::KeyboardModifiers QCocoaKeyMapper::queryKeyboardModifiers() Qt::KeyboardModifiers QCocoaKeyMapper::queryKeyboardModifiers()
{ {
return fromCocoaModifiers(NSEvent.modifierFlags); return fromCocoaModifiers(NSEvent.modifierFlags);
@ -410,7 +400,7 @@ bool QCocoaKeyMapper::updateKeyboard()
m_keyboardKind = LMGetKbdType(); m_keyboardKind = LMGetKbdType();
m_deadKeyState = 0; m_deadKeyState = 0;
deleteLayouts(); m_keyMap.clear();
if (auto data = CFDataRef(TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData))) { if (auto data = CFDataRef(TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData))) {
const UCKeyboardLayout *uchrData = reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)); const UCKeyboardLayout *uchrData = reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data));
@ -429,17 +419,6 @@ bool QCocoaKeyMapper::updateKeyboard()
return true; return true;
} }
void QCocoaKeyMapper::deleteLayouts()
{
m_keyboardMode = NullMode;
for (int i = 0; i < 255; ++i) {
if (m_keyLayout[i]) {
delete m_keyLayout[i];
m_keyLayout[i] = nullptr;
}
}
}
static constexpr Qt::KeyboardModifiers modifierCombinations[] = { static constexpr Qt::KeyboardModifiers modifierCombinations[] = {
Qt::NoModifier, // 0 Qt::NoModifier, // 0
Qt::ShiftModifier, // 1 Qt::ShiftModifier, // 1
@ -460,31 +439,31 @@ static constexpr Qt::KeyboardModifiers modifierCombinations[] = {
}; };
/* /*
Returns a key map for the given \macVirtualKey based on all Returns a key map for the given \virtualKey based on all
possible modifier combinations. possible modifier combinations.
*/ */
KeyboardLayoutItem *QCocoaKeyMapper::keyMapForKey(unsigned short macVirtualKey, QChar unicodeKey) const const QCocoaKeyMapper::KeyMap &QCocoaKeyMapper::keyMapForKey(VirtualKeyCode virtualKey, QChar unicodeKey) const
{ {
const_cast<QCocoaKeyMapper *>(this)->updateKeyboard(); const_cast<QCocoaKeyMapper *>(this)->updateKeyboard();
Q_ASSERT(macVirtualKey < 256); auto &keyMap = m_keyMap[virtualKey];
if (auto *existingKeyMap = m_keyLayout[macVirtualKey]) if (keyMap[Qt::NoModifier] != Qt::Key_unknown)
return existingKeyMap; return keyMap; // Already filled
qCDebug(lcQpaKeyMapper, "Updating key map for virtual key = 0x%02x!", (uint)macVirtualKey); qCDebug(lcQpaKeyMapper, "Updating key map for virtual key = 0x%02x!", (uint)virtualKey);
UniCharCount maxStringLength = 10; UniCharCount maxStringLength = 10;
UniChar unicodeString[maxStringLength]; UniChar unicodeString[maxStringLength];
m_keyLayout[macVirtualKey] = new KeyboardLayoutItem;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
UniCharCount actualStringLength = 0; Q_ASSERT(!i || keyMap[i] == 0);
m_keyLayout[macVirtualKey]->qtKey[i] = 0;
auto qtModifiers = modifierCombinations[i]; auto qtModifiers = modifierCombinations[i];
auto carbonModifiers = toCarbonModifiers(qtModifiers); auto carbonModifiers = toCarbonModifiers(qtModifiers);
const UInt32 modifierKeyState = (carbonModifiers >> 8) & 0xFF; const UInt32 modifierKeyState = (carbonModifiers >> 8) & 0xFF;
OSStatus err = UCKeyTranslate(m_keyboardLayoutFormat, macVirtualKey,
UniCharCount actualStringLength = 0;
OSStatus err = UCKeyTranslate(m_keyboardLayoutFormat, virtualKey,
kUCKeyActionDown, modifierKeyState, m_keyboardKind, OptionBits(0), kUCKeyActionDown, modifierKeyState, m_keyboardKind, OptionBits(0),
&m_deadKeyState, maxStringLength, &actualStringLength, unicodeString); &m_deadKeyState, maxStringLength, &actualStringLength, unicodeString);
@ -492,36 +471,36 @@ KeyboardLayoutItem *QCocoaKeyMapper::keyMapForKey(unsigned short macVirtualKey,
if (err == noErr && actualStringLength) if (err == noErr && actualStringLength)
unicodeKey = QChar(unicodeString[0]); unicodeKey = QChar(unicodeString[0]);
int qtkey = toKeyCode(unicodeKey, macVirtualKey, qtModifiers); int qtkey = toKeyCode(unicodeKey, virtualKey, qtModifiers);
if (qtkey == Qt::Key_unknown) if (qtkey == Qt::Key_unknown)
qtkey = unicodeKey.unicode(); qtkey = unicodeKey.unicode();
m_keyLayout[macVirtualKey]->qtKey[i] = qtkey; keyMap[i] = qtkey;
qCDebug(lcQpaKeyMapper, " [%d] (%d,0x%02x,'%c')", i, qtkey, qtkey, qtkey); qCDebug(lcQpaKeyMapper, " [%d] (%d,0x%02x,'%c')", i, qtkey, qtkey, qtkey);
} }
return m_keyLayout[macVirtualKey]; return keyMap;
} }
QList<int> QCocoaKeyMapper::possibleKeys(const QKeyEvent *event) const QList<int> QCocoaKeyMapper::possibleKeys(const QKeyEvent *event) const
{ {
QList<int> ret; QList<int> ret;
auto *keyMap = keyMapForKey(event->nativeVirtualKey(), QChar(event->key())); auto keyMap = keyMapForKey(event->nativeVirtualKey(), QChar(event->key()));
Q_ASSERT(keyMap);
auto unmodifiedKey = keyMap[Qt::NoModifier];
Q_ASSERT(unmodifiedKey != Qt::Key_unknown);
int baseKey = keyMap->qtKey[Qt::NoModifier];
auto eventModifiers = event->modifiers(); auto eventModifiers = event->modifiers();
// The base key is always valid // The base key, with the complete set of modifiers,
ret << int(baseKey + eventModifiers); // is always valid, and the first priority.
ret << int(unmodifiedKey + eventModifiers);
for (int i = 1; i < 8; ++i) { for (int i = 1; i < 8; ++i) {
int keyAfterApplyingModifiers = keyMap->qtKey[i]; auto keyAfterApplyingModifiers = keyMap[i];
if (!keyAfterApplyingModifiers) if (keyAfterApplyingModifiers == unmodifiedKey)
continue;
if (keyAfterApplyingModifiers == baseKey)
continue; continue;
// Include key if event modifiers includes, or matches // Include key if event modifiers includes, or matches