diff -Nru qtbase-opensource-src-5.9.5+dfsg/debian/changelog qtbase-opensource-src-5.9.5+dfsg/debian/changelog --- qtbase-opensource-src-5.9.5+dfsg/debian/changelog 2019-03-25 15:03:42.000000000 +0000 +++ qtbase-opensource-src-5.9.5+dfsg/debian/changelog 2019-06-20 10:59:52.000000000 +0000 @@ -1,3 +1,17 @@ +qtbase-opensource-src (5.9.5+dfsg-0ubuntu2.3) bionic; urgency=medium + + * Backport upstream patch to fix nullptr dereference in HTTP handler + (LP: #1833536). + + -- Dmitry Shachnev Thu, 20 Jun 2019 13:59:52 +0300 + +qtbase-opensource-src (5.9.5+dfsg-0ubuntu2.2) bionic; urgency=medium + + * Backport two upstream patches to support legacy X11 keymaps + (LP: #1831505). + + -- Dmitry Shachnev Sat, 08 Jun 2019 10:32:11 +0300 + qtbase-opensource-src (5.9.5+dfsg-0ubuntu2.1) bionic-security; urgency=medium * SECURITY UPDATE: double-free or corruption via illegal XML document diff -Nru qtbase-opensource-src-5.9.5+dfsg/debian/patches/http_nullptr_dereference.patch qtbase-opensource-src-5.9.5+dfsg/debian/patches/http_nullptr_dereference.patch --- qtbase-opensource-src-5.9.5+dfsg/debian/patches/http_nullptr_dereference.patch 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.9.5+dfsg/debian/patches/http_nullptr_dereference.patch 2019-06-20 10:59:52.000000000 +0000 @@ -0,0 +1,23 @@ +Description: don't retry a ssl connection if encryption was never finished + As explained in the inline comment we don't actually have a + protocol handler until we're done encrypting when we use SSL, but we + would still retry the connection if an error occurred between + "connected" and "encrypted". This would then lead us to fail an assert + that checked if a protocol handler had been set. +Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=e431a3ac027915db +Last-Update: 2019-06-20 + +--- a/src/network/access/qhttpnetworkconnectionchannel.cpp ++++ b/src/network/access/qhttpnetworkconnectionchannel.cpp +@@ -884,7 +884,10 @@ void QHttpNetworkConnectionChannel::_q_e + } else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) { + // Try to reconnect/resend before sending an error. + // While "Reading" the _q_disconnected() will handle this. +- if (reconnectAttempts-- > 0) { ++ // If we're using ssl then the protocolHandler is not initialized until ++ // "encrypted" has been emitted, since retrying requires the protocolHandler (asserted) ++ // we will not try if encryption is not done. ++ if (!pendingEncrypt && reconnectAttempts-- > 0) { + resendCurrentRequest(); + return; + } else { diff -Nru qtbase-opensource-src-5.9.5+dfsg/debian/patches/refactor_updateModifiers.patch qtbase-opensource-src-5.9.5+dfsg/debian/patches/refactor_updateModifiers.patch --- qtbase-opensource-src-5.9.5+dfsg/debian/patches/refactor_updateModifiers.patch 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.9.5+dfsg/debian/patches/refactor_updateModifiers.patch 2019-06-20 10:59:52.000000000 +0000 @@ -0,0 +1,171 @@ +Description: refactor QXcbKeyboard::updateModifiers() + The current implementation is poorly documented and hides the mapping + between keysyms and modifier bits. + . + This changeset adds documentation about the inner workings and makes + the keysym/modifier bit mapping reusable. (The latter will be needed for + xkb keymap synthesis if the XKEYBOARD extension is unavailable.) +Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=f8b164e1c37ca901 +Last-Update: 2019-06-07 + +--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp ++++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp +@@ -1397,15 +1397,51 @@ void QXcbKeyboard::updateVModToRModMappi + #endif + } + ++// Small helper: set modifier bit, if modifier position is valid ++static inline void applyModifier(uint *mask, int modifierBit) ++{ ++ if (modifierBit >= 0 && modifierBit < 8) ++ *mask |= 1 << modifierBit; ++} ++ + void QXcbKeyboard::updateModifiers() + { ++ memset(&rmod_masks, 0, sizeof(rmod_masks)); ++ ++ // Compute X modifier bits for Qt modifiers ++ KeysymModifierMap keysymMods(keysymsToModifiers()); ++ applyModifier(&rmod_masks.alt, keysymMods.value(XK_Alt_L, -1)); ++ applyModifier(&rmod_masks.alt, keysymMods.value(XK_Alt_R, -1)); ++ applyModifier(&rmod_masks.meta, keysymMods.value(XK_Meta_L, -1)); ++ applyModifier(&rmod_masks.meta, keysymMods.value(XK_Meta_R, -1)); ++ applyModifier(&rmod_masks.altgr, keysymMods.value(XK_Mode_switch, -1)); ++ applyModifier(&rmod_masks.super, keysymMods.value(XK_Super_L, -1)); ++ applyModifier(&rmod_masks.super, keysymMods.value(XK_Super_R, -1)); ++ applyModifier(&rmod_masks.hyper, keysymMods.value(XK_Hyper_L, -1)); ++ applyModifier(&rmod_masks.hyper, keysymMods.value(XK_Hyper_R, -1)); ++ ++ resolveMaskConflicts(); ++} ++ ++// Small helper: check if an array of xcb_keycode_t contains a certain code ++static inline bool keycodes_contains(xcb_keycode_t *codes, xcb_keycode_t which) ++{ ++ while (*codes != XCB_NO_SYMBOL) { ++ if (*codes == which) return true; ++ codes++; ++ } ++ return false; ++} ++ ++QXcbKeyboard::KeysymModifierMap QXcbKeyboard::keysymsToModifiers() ++{ + // The core protocol does not provide a convenient way to determine the mapping + // of modifier bits. Clients must retrieve and search the modifier map to determine + // the keycodes bound to each modifier, and then retrieve and search the keyboard + // mapping to determine the keysyms bound to the keycodes. They must repeat this + // process for all modifiers whenever any part of the modifier mapping is changed. +- memset(&rmod_masks, 0, sizeof(rmod_masks)); + ++ KeysymModifierMap map; + xcb_generic_error_t *error = 0; + xcb_connection_t *conn = xcb_connection(); + xcb_get_modifier_mapping_cookie_t modMapCookie = xcb_get_modifier_mapping(conn); +@@ -1414,7 +1450,7 @@ void QXcbKeyboard::updateModifiers() + if (error) { + qWarning("Qt: failed to get modifier mapping"); + free(error); +- return; ++ return map; + } + + // for Alt and Meta L and R are the same +@@ -1430,27 +1466,54 @@ void QXcbKeyboard::updateModifiers() + modKeyCodes[i] = xcb_key_symbols_get_keycode(m_key_symbols, symbols[i]); + + xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply); +- const int w = modMapReply->keycodes_per_modifier; +- for (size_t i = 0; i < numSymbols; ++i) { +- for (int bit = 0; bit < 8; ++bit) { +- uint mask = 1 << bit; +- for (int x = 0; x < w; ++x) { +- xcb_keycode_t keyCode = modMap[x + bit * w]; +- xcb_keycode_t *itk = modKeyCodes[i]; +- while (itk && *itk != XCB_NO_SYMBOL) +- if (*itk++ == keyCode) { +- uint sym = symbols[i]; +- if ((sym == XK_Alt_L || sym == XK_Alt_R)) +- rmod_masks.alt = mask; +- if ((sym == XK_Meta_L || sym == XK_Meta_R)) +- rmod_masks.meta = mask; +- if (sym == XK_Mode_switch) +- rmod_masks.altgr = mask; +- if ((sym == XK_Super_L) || (sym == XK_Super_R)) +- rmod_masks.super = mask; +- if ((sym == XK_Hyper_L) || (sym == XK_Hyper_R)) +- rmod_masks.hyper = mask; +- } ++ const int modMapLength = xcb_get_modifier_mapping_keycodes_length(modMapReply); ++ /* For each modifier of "Shift, Lock, Control, Mod1, Mod2, Mod3, ++ * Mod4, and Mod5" the modifier map contains keycodes_per_modifier ++ * key codes that are associated with a modifier. ++ * ++ * As an example, take this 'xmodmap' output: ++ * xmodmap: up to 4 keys per modifier, (keycodes in parentheses): ++ * ++ * shift Shift_L (0x32), Shift_R (0x3e) ++ * lock Caps_Lock (0x42) ++ * control Control_L (0x25), Control_R (0x69) ++ * mod1 Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd) ++ * mod2 Num_Lock (0x4d) ++ * mod3 ++ * mod4 Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf) ++ * mod5 ISO_Level3_Shift (0x5c), Mode_switch (0xcb) ++ * ++ * The corresponding raw modifier map would contain keycodes for: ++ * Shift_L (0x32), Shift_R (0x3e), 0, 0, ++ * Caps_Lock (0x42), 0, 0, 0, ++ * Control_L (0x25), Control_R (0x69), 0, 0, ++ * Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd), 0, ++ * Num_Lock (0x4d), 0, 0, 0, ++ * 0,0,0,0, ++ * Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf), ++ * ISO_Level3_Shift (0x5c), Mode_switch (0xcb), 0, 0 ++ */ ++ ++ /* Create a map between a modifier keysym (as per the symbols array) ++ * and the modifier bit it's associated with (if any). ++ * As modMap contains key codes, search modKeyCodes for a match; ++ * if one is found we can look up the associated keysym. ++ * Together with the modifier index this will be used ++ * to compute a mapping between X modifier bits and Qt's ++ * modifiers (Alt, Ctrl etc). */ ++ for (int i = 0; i < modMapLength; i++) { ++ if (modMap[i] == XCB_NO_SYMBOL) ++ continue; ++ // Get key symbol for key code ++ for (size_t k = 0; k < numSymbols; k++) { ++ if (modKeyCodes[k] && keycodes_contains(modKeyCodes[k], modMap[i])) { ++ // Key code is for modifier. Record mapping ++ xcb_keysym_t sym = symbols[k]; ++ /* As per modMap layout explanation above, dividing ++ * by keycodes_per_modifier gives the 'row' in the ++ * modifier map, which in turn is the modifier bit. */ ++ map[sym] = i / modMapReply->keycodes_per_modifier; ++ break; + } + } + } +@@ -1458,7 +1521,8 @@ void QXcbKeyboard::updateModifiers() + for (size_t i = 0; i < numSymbols; ++i) + free(modKeyCodes[i]); + free(modMapReply); +- resolveMaskConflicts(); ++ ++ return map; + } + + void QXcbKeyboard::resolveMaskConflicts() +--- a/src/plugins/platforms/xcb/qxcbkeyboard.h ++++ b/src/plugins/platforms/xcb/qxcbkeyboard.h +@@ -96,6 +96,8 @@ protected: + void clearXKBConfig(); + // when XKEYBOARD not present on the X server + void updateModifiers(); ++ typedef QMap KeysymModifierMap; ++ KeysymModifierMap keysymsToModifiers(); + // when XKEYBOARD is present on the X server + void updateVModMapping(); + void updateVModToRModMapping(); diff -Nru qtbase-opensource-src-5.9.5+dfsg/debian/patches/series qtbase-opensource-src-5.9.5+dfsg/debian/patches/series --- qtbase-opensource-src-5.9.5+dfsg/debian/patches/series 2019-03-25 15:03:06.000000000 +0000 +++ qtbase-opensource-src-5.9.5+dfsg/debian/patches/series 2019-06-20 10:59:52.000000000 +0000 @@ -8,6 +8,9 @@ CVE-2018-15518.patch CVE-2018-19870.patch CVE-2018-19873.patch +refactor_updateModifiers.patch +support_legacy_x11_keymaps.patch +http_nullptr_dereference.patch # Debian specific. gnukfreebsd.diff diff -Nru qtbase-opensource-src-5.9.5+dfsg/debian/patches/support_legacy_x11_keymaps.patch qtbase-opensource-src-5.9.5+dfsg/debian/patches/support_legacy_x11_keymaps.patch --- qtbase-opensource-src-5.9.5+dfsg/debian/patches/support_legacy_x11_keymaps.patch 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.9.5+dfsg/debian/patches/support_legacy_x11_keymaps.patch 2019-06-20 10:59:52.000000000 +0000 @@ -0,0 +1,395 @@ +Description: support legacy X11 keymaps + Not all X server vendors support the XKB protocol. Furthermore, + while X.org seems to use keycodes that match the usual keyboard + scancodes, other vendors may not do so. This means that using an + XKB keymap suitable for an X.org server results in garbled input + with servers for other vendors. + . + Both of these issues are addressed by using the core keycode + information as a fallback. +Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=3edcd9420e3ad661 +Last-Update: 2019-06-07 + +--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp ++++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp +@@ -59,6 +59,10 @@ + #undef KeyRelease + #endif + ++#if QT_CONFIG(xcb_xlib) ++#include ++#endif ++ + #ifndef XK_ISO_Left_Tab + #define XK_ISO_Left_Tab 0xFE20 + #endif +@@ -776,9 +780,310 @@ void QXcbKeyboard::printKeymapError(cons + "directory contains recent enough contents, to update please see http://cgit.freedesktop.org/xkeyboard-config/ ."); + } + ++#if QT_CONFIG(xcb_xlib) ++/* Look at a pair of unshifted and shifted key symbols. ++ * If the 'unshifted' symbol is uppercase and there is no shifted symbol, ++ * return the matching lowercase symbol; otherwise return 0. ++ * The caller can then use the previously 'unshifted' symbol as the new ++ * 'shifted' (uppercase) symbol and the symbol returned by the function ++ * as the new 'unshifted' (lowercase) symbol.) */ ++static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifted) ++{ ++ if (shifted != XKB_KEY_NoSymbol) // Has a shifted symbol ++ return 0; ++ ++ KeySym xlower; ++ KeySym xupper; ++ /* libxkbcommon >= 0.8.0 will have public API functions providing ++ * functionality equivalent to XConvertCase(), use these once the ++ * minimal libxkbcommon version is high enough. After that the ++ * xcb-xlib dependency can be removed */ ++ XConvertCase(static_cast(unshifted), &xlower, &xupper); ++ ++ if (xlower != xupper // Check if symbol is cased ++ && unshifted == static_cast(xupper)) { // Unshifted must be upper case ++ return static_cast(xlower); ++ } ++ return 0; ++} ++ ++static QByteArray symbolsGroupString(const xcb_keysym_t *symbols, int count) ++{ ++ // Don't output trailing NoSymbols ++ while (count > 0 && symbols[count - 1] == XKB_KEY_NoSymbol) ++ count--; ++ ++ QByteArray groupString; ++ for (int symIndex = 0; symIndex < count; symIndex++) { ++ xcb_keysym_t sym = symbols[symIndex]; ++ char symString[64]; ++ if (sym == XKB_KEY_NoSymbol) ++ strcpy(symString, "NoSymbol"); ++ else ++ xkb_keysym_get_name(sym, symString, sizeof(symString)); ++ ++ if (!groupString.isEmpty()) ++ groupString += ", "; ++ groupString += symString; ++ } ++ return groupString; ++} ++ ++struct xkb_keymap *QXcbKeyboard::keymapFromCore() ++{ ++ /* Construct an XKB keymap string from information queried from ++ * the X server */ ++ QByteArray keymap; ++ keymap += "xkb_keymap {\n"; ++ ++ const xcb_keycode_t minKeycode = connection()->setup()->min_keycode; ++ const xcb_keycode_t maxKeycode = connection()->setup()->max_keycode; ++ ++ // Generate symbolic names from keycodes ++ { ++ keymap += ++ "xkb_keycodes \"core\" {\n" ++ "\tminimum = " + QByteArray::number(minKeycode) + ";\n" ++ "\tmaximum = " + QByteArray::number(maxKeycode) + ";\n"; ++ for (int code = minKeycode; code <= maxKeycode; code++) { ++ auto codeStr = QByteArray::number(code); ++ keymap += " = " + codeStr + ";\n"; ++ } ++ /* TODO: indicators? ++ */ ++ keymap += "};\n"; // xkb_keycodes ++ } ++ ++ /* Set up default types (xkbcommon automatically assigns these to ++ * symbols, but doesn't have shift info) */ ++ keymap += ++ "xkb_types \"core\" {\n" ++ "virtual_modifiers NumLock,Alt,LevelThree;\n" ++ "type \"ONE_LEVEL\" {\n" ++ "modifiers= none;\n" ++ "level_name[Level1] = \"Any\";\n" ++ "};\n" ++ "type \"TWO_LEVEL\" {\n" ++ "modifiers= Shift;\n" ++ "map[Shift]= Level2;\n" ++ "level_name[Level1] = \"Base\";\n" ++ "level_name[Level2] = \"Shift\";\n" ++ "};\n" ++ "type \"ALPHABETIC\" {\n" ++ "modifiers= Shift+Lock;\n" ++ "map[Shift]= Level2;\n" ++ "map[Lock]= Level2;\n" ++ "level_name[Level1] = \"Base\";\n" ++ "level_name[Level2] = \"Caps\";\n" ++ "};\n" ++ "type \"KEYPAD\" {\n" ++ "modifiers= Shift+NumLock;\n" ++ "map[Shift]= Level2;\n" ++ "map[NumLock]= Level2;\n" ++ "level_name[Level1] = \"Base\";\n" ++ "level_name[Level2] = \"Number\";\n" ++ "};\n" ++ "type \"FOUR_LEVEL\" {\n" ++ "modifiers= Shift+LevelThree;\n" ++ "map[Shift]= Level2;\n" ++ "map[LevelThree]= Level3;\n" ++ "map[Shift+LevelThree]= Level4;\n" ++ "level_name[Level1] = \"Base\";\n" ++ "level_name[Level2] = \"Shift\";\n" ++ "level_name[Level3] = \"Alt Base\";\n" ++ "level_name[Level4] = \"Shift Alt\";\n" ++ "};\n" ++ "type \"FOUR_LEVEL_ALPHABETIC\" {\n" ++ "modifiers= Shift+Lock+LevelThree;\n" ++ "map[Shift]= Level2;\n" ++ "map[Lock]= Level2;\n" ++ "map[LevelThree]= Level3;\n" ++ "map[Shift+LevelThree]= Level4;\n" ++ "map[Lock+LevelThree]= Level4;\n" ++ "map[Shift+Lock+LevelThree]= Level3;\n" ++ "level_name[Level1] = \"Base\";\n" ++ "level_name[Level2] = \"Shift\";\n" ++ "level_name[Level3] = \"Alt Base\";\n" ++ "level_name[Level4] = \"Shift Alt\";\n" ++ "};\n" ++ "type \"FOUR_LEVEL_SEMIALPHABETIC\" {\n" ++ "modifiers= Shift+Lock+LevelThree;\n" ++ "map[Shift]= Level2;\n" ++ "map[Lock]= Level2;\n" ++ "map[LevelThree]= Level3;\n" ++ "map[Shift+LevelThree]= Level4;\n" ++ "map[Lock+LevelThree]= Level3;\n" ++ "preserve[Lock+LevelThree]= Lock;\n" ++ "map[Shift+Lock+LevelThree]= Level4;\n" ++ "preserve[Shift+Lock+LevelThree]= Lock;\n" ++ "level_name[Level1] = \"Base\";\n" ++ "level_name[Level2] = \"Shift\";\n" ++ "level_name[Level3] = \"Alt Base\";\n" ++ "level_name[Level4] = \"Shift Alt\";\n" ++ "};\n" ++ "type \"FOUR_LEVEL_KEYPAD\" {\n" ++ "modifiers= Shift+NumLock+LevelThree;\n" ++ "map[Shift]= Level2;\n" ++ "map[NumLock]= Level2;\n" ++ "map[LevelThree]= Level3;\n" ++ "map[Shift+LevelThree]= Level4;\n" ++ "map[NumLock+LevelThree]= Level4;\n" ++ "map[Shift+NumLock+LevelThree]= Level3;\n" ++ "level_name[Level1] = \"Base\";\n" ++ "level_name[Level2] = \"Number\";\n" ++ "level_name[Level3] = \"Alt Base\";\n" ++ "level_name[Level4] = \"Alt Number\";\n" ++ "};\n" ++ "};\n"; // xkb_types ++ ++ // Generate mapping between symbolic names and keysyms ++ { ++ QVector xkeymap; ++ int keysymsPerKeycode = 0; ++ { ++ int keycodeCount = maxKeycode - minKeycode + 1; ++ ++ xcb_get_keyboard_mapping_cookie_t keymapCookie; ++ xcb_get_keyboard_mapping_reply_t *keymapReply; ++ keymapCookie = xcb_get_keyboard_mapping(xcb_connection(), minKeycode, keycodeCount); ++ keymapReply = xcb_get_keyboard_mapping_reply(xcb_connection(), keymapCookie, NULL); ++ if (keymapReply) { ++ keysymsPerKeycode = keymapReply->keysyms_per_keycode; ++ int numSyms = keycodeCount * keysymsPerKeycode; ++ auto keymapPtr = xcb_get_keyboard_mapping_keysyms(keymapReply); ++ xkeymap.resize(numSyms); ++ for (int i = 0; i < numSyms; i++) ++ xkeymap[i] = keymapPtr[i]; ++ free(keymapReply); ++ } else { ++ qWarning("Qt: failed to retrieve the keyboard mapping from XKB"); ++ } ++ } ++ if (xkeymap.isEmpty()) ++ return nullptr; ++ ++ KeysymModifierMap keysymMods(keysymsToModifiers()); ++ static const char *const builtinModifiers[] = ++ { "Shift", "Lock", "Control", "Mod1", "Mod2", "Mod3", "Mod4", "Mod5" }; ++ ++ /* Level 3 symbols (e.g. AltGr+something) seem to come in two flavors: ++ * - as a proper level 3 in group 1, at least on recent X.org versions ++ * - 'disguised' as group 2, on 'legacy' X servers ++ * In the 2nd case, remap group 2 to level 3, that seems to work better ++ * in practice */ ++ bool mapGroup2ToLevel3 = keysymsPerKeycode < 5; ++ ++ keymap += "xkb_symbols \"core\" {\n"; ++ for (int code = minKeycode; code <= maxKeycode; code++) { ++ auto codeMap = xkeymap.constData() + (code - minKeycode) * keysymsPerKeycode; ++ ++ const int maxGroup1 = 4; // We only support 4 shift states anyway ++ const int maxGroup2 = 2; // Only 3rd and 4th keysym are group 2 ++ xcb_keysym_t symbolsGroup1[maxGroup1]; ++ xcb_keysym_t symbolsGroup2[maxGroup2]; ++ for (int i = 0; i < maxGroup1 + maxGroup2; i++) { ++ xcb_keysym_t sym = i < keysymsPerKeycode ? codeMap[i] : XKB_KEY_NoSymbol; ++ if (mapGroup2ToLevel3) { ++ // Merge into single group ++ if (i < maxGroup1) ++ symbolsGroup1[i] = sym; ++ } else { ++ // Preserve groups ++ if (i < 2) ++ symbolsGroup1[i] = sym; ++ else if (i < 4) ++ symbolsGroup2[i - 2] = sym; ++ else ++ symbolsGroup1[i - 2] = sym; ++ } ++ } ++ ++ /* Fix symbols so the unshifted and shifted symbols have ++ * lower resp. upper case */ ++ if (auto lowered = getUnshiftedXKey(symbolsGroup1[0], symbolsGroup1[1])) { ++ symbolsGroup1[1] = symbolsGroup1[0]; ++ symbolsGroup1[0] = lowered; ++ } ++ if (auto lowered = getUnshiftedXKey(symbolsGroup2[0], symbolsGroup2[1])) { ++ symbolsGroup2[1] = symbolsGroup2[0]; ++ symbolsGroup2[0] = lowered; ++ } ++ ++ QByteArray groupStr1 = symbolsGroupString(symbolsGroup1, maxGroup1); ++ if (groupStr1.isEmpty()) ++ continue; ++ ++ keymap += "key { "; ++ keymap += "symbols[Group1] = [ " + groupStr1 + " ]"; ++ QByteArray groupStr2 = symbolsGroupString(symbolsGroup2, maxGroup2); ++ if (!groupStr2.isEmpty()) ++ keymap += ", symbols[Group2] = [ " + groupStr2 + " ]"; ++ ++ // See if this key code is for a modifier ++ xcb_keysym_t modifierSym = XKB_KEY_NoSymbol; ++ for (int symIndex = 0; symIndex < keysymsPerKeycode; symIndex++) { ++ xcb_keysym_t sym = codeMap[symIndex]; ++ ++ if (sym == XKB_KEY_Alt_L ++ || sym == XKB_KEY_Meta_L ++ || sym == XKB_KEY_Mode_switch ++ || sym == XKB_KEY_Super_L ++ || sym == XKB_KEY_Super_R ++ || sym == XKB_KEY_Hyper_L ++ || sym == XKB_KEY_Hyper_R) { ++ modifierSym = sym; ++ break; ++ } ++ } ++ ++ // AltGr ++ if (modifierSym == XKB_KEY_Mode_switch) ++ keymap += ", virtualMods=LevelThree"; ++ keymap += " };\n"; // key ++ ++ // Generate modifier mappings ++ int modNum = keysymMods.value(modifierSym, -1); ++ if (modNum != -1) { ++ // Here modNum is always < 8 (see keysymsToModifiers()) ++ keymap += QByteArray("modifier_map ") + builtinModifiers[modNum] ++ + " { };\n"; ++ } ++ } ++ // TODO: indicators? ++ keymap += "};\n"; // xkb_symbols ++ } ++ ++ // We need an "Alt" modifier, provide via the xkb_compatibility section ++ keymap += ++ "xkb_compatibility \"core\" {\n" ++ "virtual_modifiers NumLock,Alt,LevelThree;\n" ++ "interpret Alt_L+AnyOf(all) {\n" ++ "virtualModifier= Alt;\n" ++ "action= SetMods(modifiers=modMapMods,clearLocks);\n" ++ "};\n" ++ "interpret Alt_R+AnyOf(all) {\n" ++ "virtualModifier= Alt;\n" ++ "action= SetMods(modifiers=modMapMods,clearLocks);\n" ++ "};\n" ++ "};\n"; ++ ++ /* TODO: There is an issue with modifier state not being handled ++ * correctly if using Xming with XKEYBOARD disabled. */ ++ ++ keymap += "};\n"; // xkb_keymap ++ ++ return xkb_keymap_new_from_buffer(xkb_context, ++ keymap.constData(), ++ keymap.size(), ++ XKB_KEYMAP_FORMAT_TEXT_V1, ++ static_cast(0)); ++} ++#endif ++ + void QXcbKeyboard::updateKeymap() + { + m_config = true; ++ m_keymap_is_core = false; + // set xkb context object + if (!xkb_context) { + if (qEnvironmentVariableIsSet("QT_XKB_CONFIG_ROOT")) { +@@ -812,9 +1117,23 @@ void QXcbKeyboard::updateKeymap() + } + #endif + if (!xkb_keymap) { +- // Compile a keymap from RMLVO (rules, models, layouts, variants and options) names ++ // Read xkb RMLVO (rules, models, layouts, variants and options) names + readXKBConfig(); +- xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); ++#if QT_CONFIG(xcb_xlib) ++ bool rmlvo_is_incomplete = !xkb_names.rules || !(*xkb_names.rules) ++ || !xkb_names.model || !(*xkb_names.model) ++ || !xkb_names.layout || !(*xkb_names.layout); ++ if (rmlvo_is_incomplete) { ++ // Try to build xkb map from core mapping information ++ xkb_keymap = keymapFromCore(); ++ m_keymap_is_core = xkb_keymap != 0; ++ } ++#endif ++ if (!xkb_keymap) { ++ // Compile a keymap from RMLVO ++ xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, ++ static_cast (0)); ++ } + if (!xkb_keymap) { + // last fallback is to used hard-coded keymap name, see DEFAULT_XKB_* in xkbcommon.pri + qWarning() << "Qt: Could not determine keyboard configuration data" +@@ -1006,9 +1325,11 @@ xkb_keysym_t QXcbKeyboard::lookupLatinKe + // If user layouts don't contain any layout that results in a latin key, we query a + // key from "US" layout, this allows for latin-key-based shorcuts to work even when + // users have only one (non-latin) layout set. ++ // But don't do this if using keymap obtained through the core protocol, as the key ++ // codes may not match up with those expected by the XKB keymap. + xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED); + xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED); +- if (sym == XKB_KEY_NoSymbol && !m_hasLatinLayout) { ++ if (sym == XKB_KEY_NoSymbol && !m_hasLatinLayout && !m_keymap_is_core) { + if (!latin_keymap) { + const struct xkb_rule_names names = { xkb_names.rules, xkb_names.model, "us", 0, 0 }; + latin_keymap = xkb_keymap_new_from_names(xkb_context, &names, (xkb_keymap_compile_flags)0); +--- a/src/plugins/platforms/xcb/qxcbkeyboard.h ++++ b/src/plugins/platforms/xcb/qxcbkeyboard.h +@@ -94,6 +94,9 @@ protected: + + void readXKBConfig(); + void clearXKBConfig(); ++#if QT_CONFIG(xcb_xlib) ++ struct xkb_keymap *keymapFromCore(); ++#endif + // when XKEYBOARD not present on the X server + void updateModifiers(); + typedef QMap KeysymModifierMap; +@@ -109,6 +112,7 @@ private: + void updateXKBStateFromState(struct xkb_state *kb_state, quint16 state); + + bool m_config = false; ++ bool m_keymap_is_core = false; + xcb_keycode_t m_autorepeat_code = 0; + + struct xkb_context *xkb_context = nullptr;