/**************************************************************************** ** ** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qxkbcommon_p.h" #include #include #include #include #include #include QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcXkbcommon, "qt.xkbcommon") static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, xkb_state *state, xkb_keycode_t code, bool superAsMeta, bool hyperAsMeta); typedef struct xkb2qt { unsigned int xkb; unsigned int qt; constexpr bool operator <=(const xkb2qt &that) const noexcept { return xkb <= that.xkb; } constexpr bool operator <(const xkb2qt &that) const noexcept { return xkb < that.xkb; } } xkb2qt_t; template struct Xkb2Qt { using Type = xkb2qt_t; static constexpr Type data() noexcept { return Type{Xkb, Qt}; } }; static constexpr const auto KeyTbl = qMakeArray( QSortedData< // misc keys Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq // cursor movement Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // modifiers Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt<0x1000FF74, Qt::Key_Backtab>, // hardcoded HP backtab Xkb2Qt<0x1005FF10, Qt::Key_F11>, // hardcoded Sun F36 (labeled F11) Xkb2Qt<0x1005FF11, Qt::Key_F12>, // hardcoded Sun F37 (labeled F12) // numeric and function keypad keys Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // special non-XF86 function keys Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // International input method support keys // International & multi-key character composition Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Misc Functions Xkb2Qt, Xkb2Qt, // Japanese keyboard support Xkb2Qt, Xkb2Qt, //Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, //Xkb2Qt, //Xkb2Qt, //Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Korean keyboard support Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, //Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, //Xkb2Qt, //Xkb2Qt, //Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, //Xkb2Qt, Xkb2Qt, // dead keys Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // Special keys from X.org - This include multimedia keys, // wireless/bluetooth/uwb keys, special launcher keys, etc. Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // ### Qt 6: remap properly Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, // ### Qt 6: remap properly Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt, Xkb2Qt >::Data{} ); xkb_keysym_t QXkbCommon::qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks) { xkb_keysym_t lower, upper; xkbcommon_XConvertCase(ks, &lower, &upper); return upper; } QString QXkbCommon::lookupString(struct xkb_state *state, xkb_keycode_t code) { QVarLengthArray chars(32); const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL chars.resize(size + 1); xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); } return QString::fromUtf8(chars.constData(), size); } QString QXkbCommon::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) { QVarLengthArray chars(32); const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); if (size == 0) return QString(); // the keysym does not have a Unicode representation if (Q_UNLIKELY(size > chars.size())) { chars.resize(size); xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); } return QString::fromUtf8(chars.constData(), size - 1); } QVector QXkbCommon::toKeysym(QKeyEvent *event) { QVector keysyms; int qtKey = event->key(); if (qtKey >= Qt::Key_F1 && qtKey <= Qt::Key_F35) { keysyms.append(XKB_KEY_F1 + (qtKey - Qt::Key_F1)); } else if (event->modifiers() & Qt::KeypadModifier) { if (qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9) keysyms.append(XKB_KEY_KP_0 + (qtKey - Qt::Key_0)); } else if (isLatin(qtKey) && event->text().isUpper()) { keysyms.append(qtKey); } if (!keysyms.isEmpty()) return keysyms; // check if we have a direct mapping auto it = std::find_if(KeyTbl.cbegin(), KeyTbl.cend(), [&qtKey](xkb2qt_t elem) { return elem.qt == static_cast(qtKey); }); if (it != KeyTbl.end()) { keysyms.append(it->xkb); return keysyms; } QVector ucs4; if (event->text().isEmpty()) ucs4.append(qtKey); else ucs4 = event->text().toUcs4(); // From libxkbcommon keysym-utf.c: // "We allow to represent any UCS character in the range U-00000000 to // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff." for (uint utf32 : qAsConst(ucs4)) keysyms.append(utf32 | 0x01000000); return keysyms; } int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers) { return keysymToQtKey(keysym, modifiers, nullptr, 0); } int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, xkb_state *state, xkb_keycode_t code, bool superAsMeta, bool hyperAsMeta) { // Note 1: All standard key sequences on linux (as defined in platform theme) // that use a latin character also contain a control modifier, which is why // checking for Qt::ControlModifier is sufficient here. It is possible to // override QPlatformTheme::keyBindings() and provide custom sequences for // QKeySequence::StandardKey. Custom sequences probably should respect this // convention (alternatively, we could test against other modifiers here). // Note 2: The possibleKeys() shorcut mechanism is not affected by this value // adjustment and does its own thing. if (modifiers & Qt::ControlModifier) { // With standard shortcuts we should prefer a latin character, this is // for checks like "some qkeyevent == QKeySequence::Copy" to work even // when using for example 'russian' keyboard layout. if (!QXkbCommon::isLatin(keysym)) { xkb_keysym_t latinKeysym = QXkbCommon::lookupLatinKeysym(state, code); if (latinKeysym != XKB_KEY_NoSymbol) keysym = latinKeysym; } } return keysymToQtKey_internal(keysym, modifiers, state, code, superAsMeta, hyperAsMeta); } static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, xkb_state *state, xkb_keycode_t code, bool superAsMeta, bool hyperAsMeta) { int qtKey = 0; // lookup from direct mapping if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) { // function keys qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1); } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) { // numeric keypad keys qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0); } else if (QXkbCommon::isLatin(keysym)) { qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(keysym); } else { // check if we have a direct mapping xkb2qt_t searchKey{keysym, 0}; auto it = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey); if (it != KeyTbl.end() && !(searchKey < *it)) qtKey = it->qt; } if (qtKey) return qtKey; // lookup from unicode QString text; if (!state || modifiers & Qt::ControlModifier) { // Control modifier changes the text to ASCII control character, therefore we // can't use this text to map keysym to a qt key. We can use the same keysym // (it is not affectd by transformation) to obtain untransformed text. For details // see "Appendix A. Default Symbol Transformations" in the XKB specification. text = QXkbCommon::lookupStringNoKeysymTransformations(keysym); } else { text = QXkbCommon::lookupString(state, code); } if (!text.isEmpty()) { if (text.unicode()->isDigit()) { // Ensures that also non-latin digits are mapped to corresponding qt keys, // e.g CTRL + ۲ (arabic two), is mapped to CTRL + Qt::Key_2. qtKey = Qt::Key_0 + text.unicode()->digitValue(); } else { qtKey = text.unicode()->toUpper().unicode(); } } // translate Super/Hyper keys to Meta if we're using them as the MetaModifier if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R)) qtKey = Qt::Key_Meta; if (hyperAsMeta && (qtKey == Qt::Key_Hyper_L || qtKey == Qt::Key_Hyper_R)) qtKey = Qt::Key_Meta; return qtKey; } Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state) { Qt::KeyboardModifiers modifiers = Qt::NoModifier; if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) modifiers |= Qt::ControlModifier; if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0) modifiers |= Qt::AltModifier; if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0) modifiers |= Qt::ShiftModifier; if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0) modifiers |= Qt::MetaModifier; return modifiers; } // Possible modifier states. 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::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts }; QList QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event, bool superAsMeta, bool hyperAsMeta) { QList result; quint32 keycode = event->nativeScanCode(); Qt::KeyboardModifiers modifiers = event->modifiers(); xkb_keymap *keymap = xkb_state_get_keymap(state); // turn off the modifier bits which doesn't participate in shortcuts Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier; modifiers &= ~notNeeded; // create a fresh kb state and test against the relevant modifier combinations ScopedXKBState scopedXkbQueryState(xkb_state_new(keymap)); xkb_state *queryState = scopedXkbQueryState.get(); if (!queryState) { qCWarning(lcXkbcommon) << Q_FUNC_INFO << "failed to compile xkb keymap"; return result; } // get kb state from the master state and update the temporary state xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(state, XKB_STATE_LAYOUT_LOCKED); xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_DEPRESSED); xkb_state_update_mask(queryState, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout); // handle shortcuts for level three and above xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(queryState, keycode); xkb_level_index_t levelIndex = 0; if (layoutIndex != XKB_LAYOUT_INVALID) { levelIndex = xkb_state_key_get_level(queryState, keycode, layoutIndex); if (levelIndex == XKB_LEVEL_INVALID) levelIndex = 0; } if (levelIndex <= 1) xkb_state_update_mask(queryState, 0, latchedMods, lockedMods, 0, 0, lockedLayout); xkb_keysym_t sym = xkb_state_key_get_one_sym(queryState, keycode); if (sym == XKB_KEY_NoSymbol) return result; int baseQtKey = keysymToQtKey_internal(sym, modifiers, queryState, keycode, superAsMeta, hyperAsMeta); if (baseQtKey) result += (baseQtKey + modifiers); xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(keymap, "Shift"); xkb_mod_index_t altMod = xkb_keymap_mod_get_index(keymap, "Alt"); xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(keymap, "Control"); xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(keymap, "Meta"); Q_ASSERT(shiftMod < 32); Q_ASSERT(altMod < 32); Q_ASSERT(controlMod < 32); xkb_mod_mask_t depressed; int qtKey = 0; // obtain a list of possible shortcuts for the given key event for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) { Qt::KeyboardModifiers neededMods = ModsTbl[i]; if ((modifiers & neededMods) == neededMods) { if (i == 8) { if (isLatin(baseQtKey)) continue; // add a latin key as a fall back key sym = lookupLatinKeysym(state, keycode); } else { depressed = 0; if (neededMods & Qt::AltModifier) depressed |= (1 << altMod); if (neededMods & Qt::ShiftModifier) depressed |= (1 << shiftMod); if (neededMods & Qt::ControlModifier) depressed |= (1 << controlMod); if (metaMod < 32 && neededMods & Qt::MetaModifier) depressed |= (1 << metaMod); xkb_state_update_mask(queryState, depressed, latchedMods, lockedMods, 0, 0, lockedLayout); sym = xkb_state_key_get_one_sym(queryState, keycode); } if (sym == XKB_KEY_NoSymbol) continue; Qt::KeyboardModifiers mods = modifiers & ~neededMods; qtKey = keysymToQtKey_internal(sym, mods, queryState, keycode, superAsMeta, hyperAsMeta); if (!qtKey || qtKey == baseQtKey) continue; // catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +, // but Ctrl++ is more specific than +, so we should skip the last one bool ambiguous = false; for (int shortcut : qAsConst(result)) { if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) { ambiguous = true; break; } } if (ambiguous) continue; result += (qtKey + mods); } } return result; } void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap) { const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(keymap); const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap); const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap); const xkb_keysym_t *keysyms = nullptr; int nrLatinKeys = 0; for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) { for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &keysyms); if (keysyms && isLatin(keysyms[0])) nrLatinKeys++; if (nrLatinKeys > 10) // arbitrarily chosen threshold return; } } // This means that lookupLatinKeysym() will not find anything and latin // key shortcuts might not work. This is a bug in the affected desktop // environment. Usually can be solved via system settings by adding e.g. 'us' // layout to the list of seleced layouts, or by using command line, "setxkbmap // -layout rus,en". The position of latin key based layout in the list of the // selected layouts is irrelevant. Properly functioning desktop environments // handle this behind the scenes, even if no latin key based layout has been // explicitly listed in the selected layouts. qCDebug(lcXkbcommon, "no keyboard layouts with latin keys present"); } xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode) { xkb_layout_index_t layout; xkb_keysym_t sym = XKB_KEY_NoSymbol; xkb_keymap *keymap = xkb_state_get_keymap(state); const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(keymap, keycode); const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(state, keycode); // Look at user layouts in the order in which they are defined in system // settings to find a latin keysym. for (layout = 0; layout < layoutCount; ++layout) { if (layout == currentLayout) continue; const xkb_keysym_t *syms = nullptr; xkb_level_index_t level = xkb_state_key_get_level(state, keycode, layout); if (xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms) != 1) continue; if (isLatin(syms[0])) { sym = syms[0]; break; } } if (sym == XKB_KEY_NoSymbol) return sym; xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); // Check for uniqueness, consider the following setup: // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active). // In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+, // because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained // 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired // then the obtained key is not unique. This prevents ctrl+ from generating a ctrl+q // shortcut in the above described setup. We don't want ctrl+ and ctrl+ to // generate the same shortcut event in this case. const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap); const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap); ScopedXKBState queryState(xkb_state_new(keymap)); for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) { xkb_state_update_mask(queryState.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout); for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { xkb_keysym_t prevSym = xkb_state_key_get_one_sym(queryState.get(), code); if (prevSym == sym) { sym = XKB_KEY_NoSymbol; break; } } } return sym; } void QXkbCommon::setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context) { if (!inputContext || !context) return; const char *const inputContextClassName = "QComposeInputContext"; const char *const normalizedSignature = "setXkbContext(xkb_context*)"; if (inputContext->objectName() != QLatin1String(inputContextClassName)) return; static const QMetaMethod setXkbContext = [&]() { int methodIndex = inputContext->metaObject()->indexOfMethod(normalizedSignature); QMetaMethod method = inputContext->metaObject()->method(methodIndex); Q_ASSERT(method.isValid()); if (!method.isValid()) qCWarning(lcXkbcommon) << normalizedSignature << "not found on" << inputContextClassName; return method; }(); if (!setXkbContext.isValid()) return; setXkbContext.invoke(inputContext, Qt::DirectConnection, Q_ARG(struct xkb_context*, context)); } QT_END_NAMESPACE