Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qapplekeymapper.mm
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qglobal.h>
5
6#ifdef Q_OS_MACOS
7#include <AppKit/AppKit.h>
8#endif
9
10#if defined(QT_PLATFORM_UIKIT)
11#include <UIKit/UIKit.h>
12#endif
13
14#include "qapplekeymapper_p.h"
15
16#include <QtCore/qloggingcategory.h>
17#include <QtGui/QGuiApplication>
18
20
21Q_LOGGING_CATEGORY(lcQpaKeyMapperKeys, "qt.qpa.keymapper.keys");
22
23static Qt::KeyboardModifiers swapModifiersIfNeeded(const Qt::KeyboardModifiers modifiers)
24{
26 return modifiers;
27
28 Qt::KeyboardModifiers swappedModifiers = modifiers;
29 swappedModifiers &= ~(Qt::MetaModifier | Qt::ControlModifier);
30
32 swappedModifiers |= Qt::MetaModifier;
34 swappedModifiers |= Qt::ControlModifier;
35
36 return swappedModifiers;
37}
38
39#ifdef Q_OS_MACOS
40static constexpr std::tuple<NSEventModifierFlags, Qt::KeyboardModifier> cocoaModifierMap[] = {
41 { NSEventModifierFlagShift, Qt::ShiftModifier },
42 { NSEventModifierFlagControl, Qt::ControlModifier },
43 { NSEventModifierFlagCommand, Qt::MetaModifier },
44 { NSEventModifierFlagOption, Qt::AltModifier },
45 { NSEventModifierFlagNumericPad, Qt::KeypadModifier }
46};
47
48Qt::KeyboardModifiers QAppleKeyMapper::fromCocoaModifiers(NSEventModifierFlags cocoaModifiers)
49{
50 Qt::KeyboardModifiers qtModifiers = Qt::NoModifier;
51 for (const auto &[cocoaModifier, qtModifier] : cocoaModifierMap) {
52 if (cocoaModifiers & cocoaModifier)
53 qtModifiers |= qtModifier;
54 }
55
56 return swapModifiersIfNeeded(qtModifiers);
57}
58
59NSEventModifierFlags QAppleKeyMapper::toCocoaModifiers(Qt::KeyboardModifiers qtModifiers)
60{
61 qtModifiers = swapModifiersIfNeeded(qtModifiers);
62
63 NSEventModifierFlags cocoaModifiers = 0;
64 for (const auto &[cocoaModifier, qtModifier] : cocoaModifierMap) {
65 if (qtModifiers & qtModifier)
66 cocoaModifiers |= cocoaModifier;
67 }
68
69 return cocoaModifiers;
70}
71
72using CarbonModifiers = UInt32; // As opposed to EventModifiers which is UInt16
73
74static CarbonModifiers toCarbonModifiers(Qt::KeyboardModifiers qtModifiers)
75{
76 qtModifiers = swapModifiersIfNeeded(qtModifiers);
77
78 static constexpr std::tuple<int, Qt::KeyboardModifier> carbonModifierMap[] = {
79 { shiftKey, Qt::ShiftModifier },
80 { controlKey, Qt::ControlModifier },
81 { cmdKey, Qt::MetaModifier },
82 { optionKey, Qt::AltModifier },
83 { kEventKeyModifierNumLockMask, Qt::KeypadModifier }
84 };
85
86 CarbonModifiers carbonModifiers = 0;
87 for (const auto &[carbonModifier, qtModifier] : carbonModifierMap) {
88 if (qtModifiers & qtModifier)
89 carbonModifiers |= carbonModifier;
90 }
91
92 return carbonModifiers;
93}
94
95// Keyboard keys (non-modifiers)
96static QHash<char16_t, Qt::Key> standardKeys = {
97 { kHomeCharCode, Qt::Key_Home },
98 { kEnterCharCode, Qt::Key_Enter },
99 { kEndCharCode, Qt::Key_End },
100 { kBackspaceCharCode, Qt::Key_Backspace },
101 { kTabCharCode, Qt::Key_Tab },
102 { kPageUpCharCode, Qt::Key_PageUp },
103 { kPageDownCharCode, Qt::Key_PageDown },
104 { kReturnCharCode, Qt::Key_Return },
105 { kEscapeCharCode, Qt::Key_Escape },
106 { kLeftArrowCharCode, Qt::Key_Left },
107 { kRightArrowCharCode, Qt::Key_Right },
108 { kUpArrowCharCode, Qt::Key_Up },
109 { kDownArrowCharCode, Qt::Key_Down },
110 { kHelpCharCode, Qt::Key_Help },
111 { kDeleteCharCode, Qt::Key_Delete },
112 // ASCII maps, for debugging
113 { ':', Qt::Key_Colon },
114 { ';', Qt::Key_Semicolon },
115 { '<', Qt::Key_Less },
116 { '=', Qt::Key_Equal },
117 { '>', Qt::Key_Greater },
118 { '?', Qt::Key_Question },
119 { '@', Qt::Key_At },
120 { ' ', Qt::Key_Space },
121 { '!', Qt::Key_Exclam },
122 { '"', Qt::Key_QuoteDbl },
123 { '#', Qt::Key_NumberSign },
124 { '$', Qt::Key_Dollar },
125 { '%', Qt::Key_Percent },
126 { '&', Qt::Key_Ampersand },
127 { '\'', Qt::Key_Apostrophe },
128 { '(', Qt::Key_ParenLeft },
129 { ')', Qt::Key_ParenRight },
130 { '*', Qt::Key_Asterisk },
131 { '+', Qt::Key_Plus },
132 { ',', Qt::Key_Comma },
133 { '-', Qt::Key_Minus },
134 { '.', Qt::Key_Period },
135 { '/', Qt::Key_Slash },
136 { '[', Qt::Key_BracketLeft },
137 { ']', Qt::Key_BracketRight },
138 { '\\', Qt::Key_Backslash },
139 { '_', Qt::Key_Underscore },
140 { '`', Qt::Key_QuoteLeft },
141 { '{', Qt::Key_BraceLeft },
142 { '}', Qt::Key_BraceRight },
143 { '|', Qt::Key_Bar },
144 { '~', Qt::Key_AsciiTilde },
145 { '^', Qt::Key_AsciiCircum }
146};
147
148static QHash<char16_t, Qt::Key> virtualKeys = {
149 { kVK_F1, Qt::Key_F1 },
150 { kVK_F2, Qt::Key_F2 },
151 { kVK_F3, Qt::Key_F3 },
152 { kVK_F4, Qt::Key_F4 },
153 { kVK_F5, Qt::Key_F5 },
154 { kVK_F6, Qt::Key_F6 },
155 { kVK_F7, Qt::Key_F7 },
156 { kVK_F8, Qt::Key_F8 },
157 { kVK_F9, Qt::Key_F9 },
158 { kVK_F10, Qt::Key_F10 },
159 { kVK_F11, Qt::Key_F11 },
160 { kVK_F12, Qt::Key_F12 },
161 { kVK_F13, Qt::Key_F13 },
162 { kVK_F14, Qt::Key_F14 },
163 { kVK_F15, Qt::Key_F15 },
164 { kVK_F16, Qt::Key_F16 },
165 { kVK_Return, Qt::Key_Return },
166 { kVK_Tab, Qt::Key_Tab },
167 { kVK_Escape, Qt::Key_Escape },
168 { kVK_Help, Qt::Key_Help },
169 { kVK_UpArrow, Qt::Key_Up },
170 { kVK_DownArrow, Qt::Key_Down },
171 { kVK_LeftArrow, Qt::Key_Left },
172 { kVK_RightArrow, Qt::Key_Right },
173 { kVK_PageUp, Qt::Key_PageUp },
174 { kVK_PageDown, Qt::Key_PageDown }
175};
176
177static QHash<char16_t, Qt::Key> functionKeys = {
178 { NSUpArrowFunctionKey, Qt::Key_Up },
179 { NSDownArrowFunctionKey, Qt::Key_Down },
180 { NSLeftArrowFunctionKey, Qt::Key_Left },
181 { NSRightArrowFunctionKey, Qt::Key_Right },
182 // F1-35 function keys handled manually below
183 { NSInsertFunctionKey, Qt::Key_Insert },
184 { NSDeleteFunctionKey, Qt::Key_Delete },
185 { NSHomeFunctionKey, Qt::Key_Home },
186 { NSEndFunctionKey, Qt::Key_End },
187 { NSPageUpFunctionKey, Qt::Key_PageUp },
188 { NSPageDownFunctionKey, Qt::Key_PageDown },
189 { NSPrintScreenFunctionKey, Qt::Key_Print },
190 { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
191 { NSPauseFunctionKey, Qt::Key_Pause },
192 { NSSysReqFunctionKey, Qt::Key_SysReq },
193 { NSMenuFunctionKey, Qt::Key_Menu },
194 { NSPrintFunctionKey, Qt::Key_Printer },
195 { NSClearDisplayFunctionKey, Qt::Key_Clear },
196 { NSInsertCharFunctionKey, Qt::Key_Insert },
197 { NSDeleteCharFunctionKey, Qt::Key_Delete },
198 { NSSelectFunctionKey, Qt::Key_Select },
199 { NSExecuteFunctionKey, Qt::Key_Execute },
200 { NSUndoFunctionKey, Qt::Key_Undo },
201 { NSRedoFunctionKey, Qt::Key_Redo },
202 { NSFindFunctionKey, Qt::Key_Find },
203 { NSHelpFunctionKey, Qt::Key_Help },
204 { NSModeSwitchFunctionKey, Qt::Key_Mode_switch }
205};
206
207static int toKeyCode(const QChar &key, int virtualKey, int modifiers)
208{
209 qCDebug(lcQpaKeyMapperKeys, "Mapping key: %d (0x%04x) / vk %d (0x%04x)",
210 key.unicode(), key.unicode(), virtualKey, virtualKey);
211
212 if (key == QChar(kClearCharCode) && virtualKey == 0x47)
213 return Qt::Key_Clear;
214
215 if (key.isDigit()) {
216 qCDebug(lcQpaKeyMapperKeys, "Got digit key: %d", key.digitValue());
217 return key.digitValue() + Qt::Key_0;
218 }
219
220 if (key.isLetter()) {
221 qCDebug(lcQpaKeyMapperKeys, "Got letter key: %d", (key.toUpper().unicode() - 'A'));
222 return (key.toUpper().unicode() - 'A') + Qt::Key_A;
223 }
224 if (key.isSymbol()) {
225 qCDebug(lcQpaKeyMapperKeys, "Got symbol key: %d", (key.unicode()));
226 return key.unicode();
227 }
228
229 if (auto qtKey = standardKeys.value(key.unicode())) {
230 // To work like Qt for X11 we issue Backtab when Shift + Tab are pressed
232 qCDebug(lcQpaKeyMapperKeys, "Got key: Qt::Key_Backtab");
233 return Qt::Key_Backtab;
234 }
235
236 qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey;
237 return qtKey;
238 }
239
240 // Last ditch try to match the scan code
241 if (auto qtKey = virtualKeys.value(virtualKey)) {
242 qCDebug(lcQpaKeyMapperKeys) << "Got scancode" << qtKey;
243 return qtKey;
244 }
245
246 // Check if they belong to key codes in private unicode range
247 if (key >= QChar(NSUpArrowFunctionKey) && key <= QChar(NSModeSwitchFunctionKey)) {
248 if (auto qtKey = functionKeys.value(key.unicode())) {
249 qCDebug(lcQpaKeyMapperKeys) << "Got" << qtKey;
250 return qtKey;
251 } else if (key >= QChar(NSF1FunctionKey) && key <= QChar(NSF35FunctionKey)) {
252 auto functionKey = Qt::Key_F1 + (key.unicode() - NSF1FunctionKey) ;
253 qCDebug(lcQpaKeyMapperKeys) << "Got" << functionKey;
254 return functionKey;
255 }
256 }
257
258 qCDebug(lcQpaKeyMapperKeys, "Unknown case.. %d[%d] %d", key.unicode(), key.toLatin1(), virtualKey);
259 return Qt::Key_unknown;
260}
261
262// --------- Cocoa key mapping moved from Qt Core ---------
263
264static const int NSEscapeCharacter = 27; // not defined by Cocoa headers
265
266static const QHash<char16_t, Qt::Key> cocoaKeys = {
267 { NSEnterCharacter, Qt::Key_Enter },
268 { NSBackspaceCharacter, Qt::Key_Backspace },
269 { NSTabCharacter, Qt::Key_Tab },
270 { NSNewlineCharacter, Qt::Key_Return },
271 { NSCarriageReturnCharacter, Qt::Key_Return },
272 { NSBackTabCharacter, Qt::Key_Backtab },
273 { NSEscapeCharacter, Qt::Key_Escape },
274 { NSDeleteCharacter, Qt::Key_Backspace },
275 { NSUpArrowFunctionKey, Qt::Key_Up },
276 { NSDownArrowFunctionKey, Qt::Key_Down },
277 { NSLeftArrowFunctionKey, Qt::Key_Left },
278 { NSRightArrowFunctionKey, Qt::Key_Right },
279 { NSF1FunctionKey, Qt::Key_F1 },
280 { NSF2FunctionKey, Qt::Key_F2 },
281 { NSF3FunctionKey, Qt::Key_F3 },
282 { NSF4FunctionKey, Qt::Key_F4 },
283 { NSF5FunctionKey, Qt::Key_F5 },
284 { NSF6FunctionKey, Qt::Key_F6 },
285 { NSF7FunctionKey, Qt::Key_F7 },
286 { NSF8FunctionKey, Qt::Key_F8 },
287 { NSF9FunctionKey, Qt::Key_F9 },
288 { NSF10FunctionKey, Qt::Key_F10 },
289 { NSF11FunctionKey, Qt::Key_F11 },
290 { NSF12FunctionKey, Qt::Key_F12 },
291 { NSF13FunctionKey, Qt::Key_F13 },
292 { NSF14FunctionKey, Qt::Key_F14 },
293 { NSF15FunctionKey, Qt::Key_F15 },
294 { NSF16FunctionKey, Qt::Key_F16 },
295 { NSF17FunctionKey, Qt::Key_F17 },
296 { NSF18FunctionKey, Qt::Key_F18 },
297 { NSF19FunctionKey, Qt::Key_F19 },
298 { NSF20FunctionKey, Qt::Key_F20 },
299 { NSF21FunctionKey, Qt::Key_F21 },
300 { NSF22FunctionKey, Qt::Key_F22 },
301 { NSF23FunctionKey, Qt::Key_F23 },
302 { NSF24FunctionKey, Qt::Key_F24 },
303 { NSF25FunctionKey, Qt::Key_F25 },
304 { NSF26FunctionKey, Qt::Key_F26 },
305 { NSF27FunctionKey, Qt::Key_F27 },
306 { NSF28FunctionKey, Qt::Key_F28 },
307 { NSF29FunctionKey, Qt::Key_F29 },
308 { NSF30FunctionKey, Qt::Key_F30 },
309 { NSF31FunctionKey, Qt::Key_F31 },
310 { NSF32FunctionKey, Qt::Key_F32 },
311 { NSF33FunctionKey, Qt::Key_F33 },
312 { NSF34FunctionKey, Qt::Key_F34 },
313 { NSF35FunctionKey, Qt::Key_F35 },
314 { NSInsertFunctionKey, Qt::Key_Insert },
315 { NSDeleteFunctionKey, Qt::Key_Delete },
316 { NSHomeFunctionKey, Qt::Key_Home },
317 { NSEndFunctionKey, Qt::Key_End },
318 { NSPageUpFunctionKey, Qt::Key_PageUp },
319 { NSPageDownFunctionKey, Qt::Key_PageDown },
320 { NSPrintScreenFunctionKey, Qt::Key_Print },
321 { NSScrollLockFunctionKey, Qt::Key_ScrollLock },
322 { NSPauseFunctionKey, Qt::Key_Pause },
323 { NSSysReqFunctionKey, Qt::Key_SysReq },
324 { NSMenuFunctionKey, Qt::Key_Menu },
325 { NSHelpFunctionKey, Qt::Key_Help },
326};
327
328QChar QAppleKeyMapper::toCocoaKey(Qt::Key key)
329{
330 // Prioritize overloaded keys
331 if (key == Qt::Key_Return)
332 return QChar(NSCarriageReturnCharacter);
333 if (key == Qt::Key_Backspace)
334 return QChar(NSBackspaceCharacter);
335
336 Q_CONSTINIT static QHash<Qt::Key, char16_t> reverseCocoaKeys;
337 if (reverseCocoaKeys.isEmpty()) {
338 reverseCocoaKeys.reserve(cocoaKeys.size());
339 for (auto it = cocoaKeys.begin(); it != cocoaKeys.end(); ++it)
340 reverseCocoaKeys.insert(it.value(), it.key());
341 }
342
343 return reverseCocoaKeys.value(key);
344}
345
346Qt::Key QAppleKeyMapper::fromCocoaKey(QChar keyCode)
347{
348 if (auto key = cocoaKeys.value(keyCode.unicode()))
349 return key;
350
351 return Qt::Key(keyCode.toUpper().unicode());
352}
353
354// ------------------------------------------------
355
356Qt::KeyboardModifiers QAppleKeyMapper::queryKeyboardModifiers() const
357{
358 return fromCocoaModifiers(NSEvent.modifierFlags);
359}
360
361bool QAppleKeyMapper::updateKeyboard()
362{
363 QCFType<TISInputSourceRef> source = TISCopyInputMethodKeyboardLayoutOverride();
364 if (!source)
365 source = TISCopyCurrentKeyboardInputSource();
366
367 if (m_keyboardMode != NullMode && source == m_currentInputSource)
368 return false;
369
371 m_currentInputSource = source;
372 m_keyboardKind = LMGetKbdType();
373
374 m_keyMap.clear();
375
376 if (auto data = CFDataRef(TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData))) {
377 const UCKeyboardLayout *uchrData = reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data));
378 Q_ASSERT(uchrData);
379 m_keyboardLayoutFormat = uchrData;
380 m_keyboardMode = UnicodeMode;
381 } else {
382 m_keyboardLayoutFormat = nullptr;
383 m_keyboardMode = NullMode;
384 }
385
386 qCDebug(lcQpaKeyMapper) << "Updated keyboard to"
387 << QString::fromCFString(CFStringRef(TISGetInputSourceProperty(
388 m_currentInputSource, kTISPropertyLocalizedName)));
389
390 return true;
391}
392
393static constexpr Qt::KeyboardModifiers modifierCombinations[] = {
394 Qt::NoModifier, // 0
398 Qt::AltModifier, // 4
402 Qt::MetaModifier, // 8
410};
411
412/*
413 Returns a key map for the given \virtualKey based on all
414 possible modifier combinations.
415*/
416const QAppleKeyMapper::KeyMap &QAppleKeyMapper::keyMapForKey(VirtualKeyCode virtualKey) const
417{
418 static_assert(sizeof(modifierCombinations) / sizeof(Qt::KeyboardModifiers) == kNumModifierCombinations);
419
420 const_cast<QAppleKeyMapper *>(this)->updateKeyboard();
421
422 auto &keyMap = m_keyMap[virtualKey];
424 return keyMap; // Already filled
425
426 qCDebug(lcQpaKeyMapper, "Updating key map for virtual key 0x%02x", (uint)virtualKey);
427
428 // Key mapping via [NSEvent charactersByApplyingModifiers:] only works for key down
429 // events, but we might (wrongly) get into this code path for other key events such
430 // as NSEventTypeFlagsChanged.
431 const bool canMapCocoaEvent = NSApp.currentEvent.type == NSEventTypeKeyDown;
432
433 if (!canMapCocoaEvent)
434 qCWarning(lcQpaKeyMapper) << "Could not map key to character for event" << NSApp.currentEvent;
435
436 for (int i = 0; i < kNumModifierCombinations; ++i) {
437 Q_ASSERT(!i || keyMap[i] == 0);
438
439 auto qtModifiers = modifierCombinations[i];
440 auto carbonModifiers = toCarbonModifiers(qtModifiers);
441 const UInt32 modifierKeyState = (carbonModifiers >> 8) & 0xFF;
442
443 UInt32 deadKeyState = 0;
444 static const UniCharCount maxStringLength = 10;
445 static UniChar unicodeString[maxStringLength];
446 UniCharCount actualStringLength = 0;
447 OSStatus err = UCKeyTranslate(m_keyboardLayoutFormat, virtualKey,
448 kUCKeyActionDown, modifierKeyState, m_keyboardKind,
449 kUCKeyTranslateNoDeadKeysMask, &deadKeyState,
450 maxStringLength, &actualStringLength,
451 unicodeString);
452
453 // Use translated Unicode key if valid
454 QChar carbonUnicodeKey;
455 if (err == noErr && actualStringLength)
456 carbonUnicodeKey = QChar(unicodeString[0]);
457
458 if (@available(macOS 10.15, *)) {
459 if (canMapCocoaEvent) {
460 // Until we've verified that the Cocoa API works as expected
461 // we first run the event through the Carbon APIs and then
462 // compare the results to Cocoa.
463 auto cocoaModifiers = toCocoaModifiers(qtModifiers);
464 auto *charactersWithModifiers = [NSApp.currentEvent charactersByApplyingModifiers:cocoaModifiers];
465
466 QChar cocoaUnicodeKey;
467 if (charactersWithModifiers.length > 0)
468 cocoaUnicodeKey = QChar([charactersWithModifiers characterAtIndex:0]);
469
470 if (cocoaUnicodeKey != carbonUnicodeKey) {
471 qCWarning(lcQpaKeyMapper) << "Mismatch between Cocoa" << cocoaUnicodeKey
472 << "and Carbon" << carbonUnicodeKey << "for virtual key" << virtualKey
473 << "with" << qtModifiers;
474 }
475 }
476 }
477
478 int qtKey = toKeyCode(carbonUnicodeKey, virtualKey, qtModifiers);
479 if (qtKey == Qt::Key_unknown)
480 qtKey = carbonUnicodeKey.unicode();
481
482 keyMap[i] = qtKey;
483
484 qCDebug(lcQpaKeyMapper).verbosity(0) << "\t" << qtModifiers
485 << "+" << qUtf8Printable(QString::asprintf("0x%02x", virtualKey))
486 << "=" << qUtf8Printable(QString::asprintf("%d / 0x%02x /", qtKey, qtKey))
488 }
489
490 return keyMap;
491}
492
493/*
494 Compute the possible key combinations that can map to the event's
495 virtual key and modifiers, in the current keyboard layout.
496
497 For example, given a normal US keyboard layout, the virtual key
498 23 combined with the Alt (⌥) and Shift (⇧) modifiers, can map
499 to the following key combinations:
500
501 - Alt+Shift+5
502 - Alt+%
503 - Shift+∞
504 - fi
505
506 The function builds on a key map produced by keyMapForKey(),
507 where each modifier-key combination has been mapped to the
508 key it will produce.
509*/
510QList<QKeyCombination> QAppleKeyMapper::possibleKeyCombinations(const QKeyEvent *event) const
511{
512 QList<QKeyCombination> ret;
513
514 const auto nativeVirtualKey = event->nativeVirtualKey();
515 if (!nativeVirtualKey)
516 return ret;
517
518 auto keyMap = keyMapForKey(nativeVirtualKey);
519
520 auto unmodifiedKey = keyMap[Qt::NoModifier];
521 Q_ASSERT(unmodifiedKey != Qt::Key_unknown);
522
523 auto eventModifiers = event->modifiers();
524
525 int startingModifierLayer = 0;
526 if (toCocoaModifiers(eventModifiers) & NSEventModifierFlagCommand) {
527 // When the Command key is pressed AppKit seems to do key equivalent
528 // matching using a Latin/Roman interpretation of the current keyboard
529 // layout. For example, for a Greek layout, pressing Option+Command+C
530 // produces a key event with chars="ç" and unmodchars="ψ", but AppKit
531 // still treats this as a match for a key equivalent of Option+Command+C.
532 // We can't do the same by just applying the modifiers to our key map,
533 // as that too contains "ψ" for the Option+Command combination. What we
534 // can do instead is take advantage of the fact that the Command
535 // modifier layer in all/most keyboard layouts contains a Latin
536 // layer. We then combine that with the modifiers of the event
537 // to produce the resulting "Latin" key combination.
538 static constexpr int kCommandLayer = 2;
540 int(eventModifiers) + int(keyMap[kCommandLayer]));
541
542 // If the unmodified key is outside of Latin1, we also treat
543 // that as a valid key combination, even if AppKit natively
544 // does not. For example, for a Greek layout, we still want
545 // to support Option+Command+ψ as a key combination, as it's
546 // unlikely to clash with the Latin key combination we added
547 // above.
548
549 // However, if the unmodified key is within Latin1, we skip
550 // it, to avoid these types of conflicts. For example, in
551 // the same Greek layout, pressing the key next to Tab will
552 // produce a Latin ';' symbol, but we've already treated that
553 // as 'q' above, thanks to the Command modifier, so we skip
554 // the potential Command+; key combination. This is also in
555 // line with what AppKit natively does.
556
557 // Skipping Latin1 unmodified keys also handles the case of
558 // a Latin layout, where the unmodified and modified keys
559 // are the same.
560
561 if (unmodifiedKey <= 0xff)
562 startingModifierLayer = 1;
563 }
564
565 // FIXME: We only compute the first 8 combinations. Why?
566 for (int i = startingModifierLayer; i < 15; ++i) {
567 auto keyAfterApplyingModifiers = keyMap[i];
568 if (!keyAfterApplyingModifiers)
569 continue;
570
571 // Include key if the event modifiers match exactly,
572 // or are a superset of the current candidate modifiers.
573 auto candidateModifiers = modifierCombinations[i];
574 if ((eventModifiers & candidateModifiers) == candidateModifiers) {
575 // If the event includes more modifiers than the candidate they
576 // will need to be included in the resulting key combination.
577 auto additionalModifiers = eventModifiers & ~candidateModifiers;
578
579 auto keyCombination = QKeyCombination::fromCombined(
580 int(additionalModifiers) + int(keyAfterApplyingModifiers));
581
582 // If there's an existing key combination with the same key,
583 // but a different set of modifiers, we want to choose only
584 // one of them, by priority (see below).
585 const auto existingCombination = std::find_if(
586 ret.begin(), ret.end(), [&](auto existingCombination) {
587 return existingCombination.key() == keyAfterApplyingModifiers;
588 });
589
590 if (existingCombination != ret.end()) {
591 // We prioritize the combination with the more specific
592 // modifiers. In the case where the number of modifiers
593 // are the same, we want to prioritize Command over Option
594 // over Control over Shift. Unfortunately the order (and
595 // hence value) of the modifiers in Qt::KeyboardModifier
596 // does not match our preferred order when Control and
597 // Meta is switched, but we can work around that by
598 // explicitly swapping the modifiers and using that
599 // for the comparison. This also works when the
600 // Qt::AA_MacDontSwapCtrlAndMeta application attribute
601 // is set, as the incoming modifiers are then left
602 // as is, and we can still trust the order.
603 auto existingModifiers = swapModifiersIfNeeded(existingCombination->keyboardModifiers());
604 auto replacementModifiers = swapModifiersIfNeeded(additionalModifiers);
605 if (replacementModifiers > existingModifiers)
606 *existingCombination = keyCombination;
607 } else {
608 // All is good, no existing combination has this key
609 ret << keyCombination;
610 }
611 }
612 }
613
614 return ret;
615}
616
617
618
619#else // iOS
620
621Qt::Key QAppleKeyMapper::fromNSString(Qt::KeyboardModifiers qtModifiers, NSString *characters,
622 NSString *charactersIgnoringModifiers, QString &text)
623{
624 if ([characters isEqualToString:@"\t"]) {
625 if (qtModifiers & Qt::ShiftModifier)
626 return Qt::Key_Backtab;
627 return Qt::Key_Tab;
628 } else if ([characters isEqualToString:@"\r"]) {
629 if (qtModifiers & Qt::KeypadModifier)
630 return Qt::Key_Enter;
631 return Qt::Key_Return;
632 }
633 if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
634 QChar ch;
635 if (((qtModifiers & Qt::MetaModifier) || (qtModifiers & Qt::AltModifier)) &&
636 ([charactersIgnoringModifiers length] != 0)) {
637 ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
638 } else if ([characters length] != 0) {
639 ch = QChar([characters characterAtIndex:0]);
640 }
641 if (!(qtModifiers & (Qt::ControlModifier | Qt::MetaModifier)) &&
642 (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) {
643 text = QString::fromNSString(characters);
644 }
645 if (!ch.isNull())
646 return Qt::Key(ch.toUpper().unicode());
647 }
648 return Qt::Key_unknown;
649}
650
651// Keyboard keys (non-modifiers)
652API_AVAILABLE(ios(13.4)) Qt::Key QAppleKeyMapper::fromUIKitKey(NSString *keyCode)
653{
654 static QHash<NSString *, Qt::Key> uiKitKeys = {
655 { UIKeyInputF1, Qt::Key_F1 },
656 { UIKeyInputF2, Qt::Key_F2 },
657 { UIKeyInputF3, Qt::Key_F3 },
658 { UIKeyInputF4, Qt::Key_F4 },
659 { UIKeyInputF5, Qt::Key_F5 },
660 { UIKeyInputF6, Qt::Key_F6 },
661 { UIKeyInputF7, Qt::Key_F7 },
662 { UIKeyInputF8, Qt::Key_F8 },
663 { UIKeyInputF9, Qt::Key_F9 },
664 { UIKeyInputF10, Qt::Key_F10 },
665 { UIKeyInputF11, Qt::Key_F11 },
666 { UIKeyInputF12, Qt::Key_F12 },
667 { UIKeyInputHome, Qt::Key_Home },
668 { UIKeyInputEnd, Qt::Key_End },
669 { UIKeyInputPageUp, Qt::Key_PageUp },
670 { UIKeyInputPageDown, Qt::Key_PageDown },
671 { UIKeyInputEscape, Qt::Key_Escape },
672 { UIKeyInputUpArrow, Qt::Key_Up },
673 { UIKeyInputDownArrow, Qt::Key_Down },
674 { UIKeyInputLeftArrow, Qt::Key_Left },
675 { UIKeyInputRightArrow, Qt::Key_Right }
676 };
677
678 if (auto key = uiKitKeys.value(keyCode))
679 return key;
680
681 return Qt::Key_unknown;
682}
683
684static constexpr std::tuple<ulong, Qt::KeyboardModifier> uiKitModifierMap[] = {
685 { UIKeyModifierShift, Qt::ShiftModifier },
686 { UIKeyModifierControl, Qt::ControlModifier },
687 { UIKeyModifierCommand, Qt::MetaModifier },
688 { UIKeyModifierAlternate, Qt::AltModifier },
689 { UIKeyModifierNumericPad, Qt::KeypadModifier }
690};
691
692ulong QAppleKeyMapper::toUIKitModifiers(Qt::KeyboardModifiers qtModifiers)
693{
694 qtModifiers = swapModifiersIfNeeded(qtModifiers);
695
696 ulong nativeModifiers = 0;
697 for (const auto &[nativeModifier, qtModifier] : uiKitModifierMap) {
698 if (qtModifiers & qtModifier)
699 nativeModifiers |= nativeModifier;
700 }
701
702 return nativeModifiers;
703}
704
705Qt::KeyboardModifiers QAppleKeyMapper::fromUIKitModifiers(ulong nativeModifiers)
706{
707 Qt::KeyboardModifiers qtModifiers = Qt::NoModifier;
708 for (const auto &[nativeModifier, qtModifier] : uiKitModifierMap) {
709 if (nativeModifiers & nativeModifier)
710 qtModifiers |= qtModifier;
711 }
712
713 return swapModifiersIfNeeded(qtModifiers);
714}
715#endif
716
static ulong toUIKitModifiers(Qt::KeyboardModifiers)
static Qt::Key fromNSString(Qt::KeyboardModifiers qtMods, NSString *characters, NSString *charactersIgnoringModifiers, QString &text)
static Qt::KeyboardModifiers fromUIKitModifiers(ulong uikitModifiers)
QList< QKeyCombination > possibleKeyCombinations(const QKeyEvent *event) const override
Qt::KeyboardModifiers queryKeyboardModifiers() const override
\inmodule QtCore
static bool testAttribute(Qt::ApplicationAttribute attribute)
Returns true if attribute attribute is set; otherwise returns false.
static constexpr QKeyCombination fromCombined(int combined)
The QKeyEvent class describes a key event.
Definition qevent.h:424
The QKeySequence class encapsulates a key sequence as used by shortcuts.
QString toString(SequenceFormat format=PortableText) const
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
EGLImageKHR int int EGLuint64KHR * modifiers
QString text
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ Key_Escape
Definition qnamespace.h:663
@ Key_F20
Definition qnamespace.h:709
@ Key_Tab
Definition qnamespace.h:664
@ Key_Select
@ Key_ParenRight
Definition qnamespace.h:523
@ Key_F30
Definition qnamespace.h:719
@ Key_Plus
Definition qnamespace.h:525
@ Key_Return
Definition qnamespace.h:667
@ Key_QuoteLeft
Definition qnamespace.h:578
@ Key_Right
Definition qnamespace.h:679
@ Key_Greater
Definition qnamespace.h:544
@ Key_Enter
Definition qnamespace.h:668
@ Key_F7
Definition qnamespace.h:696
@ Key_PageUp
Definition qnamespace.h:681
@ Key_Printer
@ Key_F22
Definition qnamespace.h:711
@ Key_Execute
@ Key_F23
Definition qnamespace.h:712
@ Key_Space
Definition qnamespace.h:513
@ Key_F29
Definition qnamespace.h:718
@ Key_F24
Definition qnamespace.h:713
@ Key_F32
Definition qnamespace.h:721
@ Key_F17
Definition qnamespace.h:706
@ Key_At
Definition qnamespace.h:546
@ Key_F21
Definition qnamespace.h:710
@ Key_QuoteDbl
Definition qnamespace.h:516
@ Key_Undo
@ Key_Colon
Definition qnamespace.h:540
@ Key_F35
Definition qnamespace.h:724
@ Key_Backspace
Definition qnamespace.h:666
@ Key_Backtab
Definition qnamespace.h:665
@ Key_F6
Definition qnamespace.h:695
@ Key_Insert
Definition qnamespace.h:669
@ Key_BracketRight
Definition qnamespace.h:575
@ Key_Left
Definition qnamespace.h:677
@ Key_BracketLeft
Definition qnamespace.h:573
@ Key_A
Definition qnamespace.h:547
@ Key_NumberSign
Definition qnamespace.h:517
@ Key_0
Definition qnamespace.h:530
@ Key_F9
Definition qnamespace.h:698
@ Key_F27
Definition qnamespace.h:716
@ Key_Redo
@ Key_AsciiCircum
Definition qnamespace.h:576
@ Key_Question
Definition qnamespace.h:545
@ Key_Find
@ Key_Dollar
Definition qnamespace.h:518
@ Key_SysReq
Definition qnamespace.h:673
@ Key_F11
Definition qnamespace.h:700
@ Key_Equal
Definition qnamespace.h:543
@ Key_Exclam
Definition qnamespace.h:515
@ Key_Print
Definition qnamespace.h:672
@ Key_Pause
Definition qnamespace.h:671
@ Key_F26
Definition qnamespace.h:715
@ Key_Up
Definition qnamespace.h:678
@ Key_Minus
Definition qnamespace.h:527
@ Key_F3
Definition qnamespace.h:692
@ Key_F16
Definition qnamespace.h:705
@ Key_Down
Definition qnamespace.h:680
@ Key_F18
Definition qnamespace.h:707
@ Key_F33
Definition qnamespace.h:722
@ Key_ParenLeft
Definition qnamespace.h:522
@ Key_F4
Definition qnamespace.h:693
@ Key_Percent
Definition qnamespace.h:519
@ Key_Underscore
Definition qnamespace.h:577
@ Key_F2
Definition qnamespace.h:691
@ Key_Delete
Definition qnamespace.h:670
@ Key_AsciiTilde
Definition qnamespace.h:582
@ Key_Backslash
Definition qnamespace.h:574
@ Key_Less
Definition qnamespace.h:542
@ Key_F28
Definition qnamespace.h:717
@ Key_Help
Definition qnamespace.h:730
@ Key_ScrollLock
Definition qnamespace.h:689
@ Key_F31
Definition qnamespace.h:720
@ Key_F1
Definition qnamespace.h:690
@ Key_Semicolon
Definition qnamespace.h:541
@ Key_F14
Definition qnamespace.h:703
@ Key_Slash
Definition qnamespace.h:529
@ Key_Period
Definition qnamespace.h:528
@ Key_Menu
Definition qnamespace.h:727
@ Key_PageDown
Definition qnamespace.h:682
@ Key_Bar
Definition qnamespace.h:580
@ Key_F19
Definition qnamespace.h:708
@ Key_F5
Definition qnamespace.h:694
@ Key_Home
Definition qnamespace.h:675
@ Key_F10
Definition qnamespace.h:699
@ Key_Clear
Definition qnamespace.h:674
@ Key_F34
Definition qnamespace.h:723
@ Key_F25
Definition qnamespace.h:714
@ Key_BraceRight
Definition qnamespace.h:581
@ Key_Mode_switch
Definition qnamespace.h:747
@ Key_Comma
Definition qnamespace.h:526
@ Key_F8
Definition qnamespace.h:697
@ Key_F13
Definition qnamespace.h:702
@ Key_BraceLeft
Definition qnamespace.h:579
@ Key_Asterisk
Definition qnamespace.h:524
@ Key_Apostrophe
Definition qnamespace.h:521
@ Key_F12
Definition qnamespace.h:701
@ Key_unknown
@ Key_F15
Definition qnamespace.h:704
@ Key_End
Definition qnamespace.h:676
@ Key_Ampersand
Definition qnamespace.h:520
@ ShiftModifier
@ ControlModifier
@ MetaModifier
@ KeypadModifier
@ NoModifier
@ AltModifier
@ AA_MacDontSwapCtrlAndMeta
Definition qnamespace.h:432
API_AVAILABLE(ios(13.4)) Qt
static Qt::KeyboardModifiers swapModifiersIfNeeded(const Qt::KeyboardModifiers modifiers)
static constexpr std::tuple< ulong, Qt::KeyboardModifier > uiKitModifierMap[]
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
return ret
GLuint64 key
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLsizei GLsizei GLchar * source
struct _cl_event * event
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QString qtKey(CFStringRef cfkey)
#define qUtf8Printable(string)
Definition qstring.h:1535
unsigned long ulong
Definition qtypes.h:35
unsigned int uint
Definition qtypes.h:34
static const struct @450 keyMap[]
QList< QChar > characters