summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbkeyboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbkeyboard.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp635
1 files changed, 525 insertions, 110 deletions
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 2e29c208c7..2ed66394c9 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -53,24 +53,91 @@
#include <stdio.h>
#include <X11/keysym.h>
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
#include <X11/extensions/XI2proto.h>
#undef KeyPress
#undef KeyRelease
#endif
+#if QT_CONFIG(xcb_xlib)
+#include <X11/Xutil.h>
+#undef KeyPress
+#undef KeyRelease
+#endif
+
#ifndef XK_ISO_Left_Tab
#define XK_ISO_Left_Tab 0xFE20
#endif
-#ifndef XK_dead_hook
-#define XK_dead_hook 0xFE61
+#ifndef XK_dead_a
+#define XK_dead_a 0xFE80
+#endif
+
+#ifndef XK_dead_A
+#define XK_dead_A 0xFE81
+#endif
+
+#ifndef XK_dead_e
+#define XK_dead_e 0xFE82
+#endif
+
+#ifndef XK_dead_E
+#define XK_dead_E 0xFE83
+#endif
+
+#ifndef XK_dead_i
+#define XK_dead_i 0xFE84
+#endif
+
+#ifndef XK_dead_I
+#define XK_dead_I 0xFE85
+#endif
+
+#ifndef XK_dead_o
+#define XK_dead_o 0xFE86
+#endif
+
+#ifndef XK_dead_O
+#define XK_dead_O 0xFE87
+#endif
+
+#ifndef XK_dead_u
+#define XK_dead_u 0xFE88
+#endif
+
+#ifndef XK_dead_U
+#define XK_dead_U 0xFE89
+#endif
+
+#ifndef XK_dead_small_schwa
+#define XK_dead_small_schwa 0xFE8A
+#endif
+
+#ifndef XK_dead_capital_schwa
+#define XK_dead_capital_schwa 0xFE8B
+#endif
+
+#ifndef XK_dead_greek
+#define XK_dead_greek 0xFE8C
+#endif
+
+#ifndef XK_dead_lowline
+#define XK_dead_lowline 0xFE90
#endif
-#ifndef XK_dead_horn
-#define XK_dead_horn 0xFE62
+#ifndef XK_dead_aboveverticalline
+#define XK_dead_aboveverticalline 0xFE91
#endif
+#ifndef XK_dead_belowverticalline
+#define XK_dead_belowverticalline 0xFE92
+#endif
+
+#ifndef XK_dead_longsolidusoverlay
+#define XK_dead_longsolidusoverlay 0xFE93
+#endif
+
+
#ifndef XK_Codeinput
#define XK_Codeinput 0xFF37
#endif
@@ -429,6 +496,36 @@ static const unsigned int KeyTbl[] = {
XK_dead_belowdot, Qt::Key_Dead_Belowdot,
XK_dead_hook, Qt::Key_Dead_Hook,
XK_dead_horn, Qt::Key_Dead_Horn,
+ XK_dead_stroke, Qt::Key_Dead_Stroke,
+ XK_dead_abovecomma, Qt::Key_Dead_Abovecomma,
+ XK_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma,
+ XK_dead_doublegrave, Qt::Key_Dead_Doublegrave,
+ XK_dead_belowring, Qt::Key_Dead_Belowring,
+ XK_dead_belowmacron, Qt::Key_Dead_Belowmacron,
+ XK_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex,
+ XK_dead_belowtilde, Qt::Key_Dead_Belowtilde,
+ XK_dead_belowbreve, Qt::Key_Dead_Belowbreve,
+ XK_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis,
+ XK_dead_invertedbreve, Qt::Key_Dead_Invertedbreve,
+ XK_dead_belowcomma, Qt::Key_Dead_Belowcomma,
+ XK_dead_currency, Qt::Key_Dead_Currency,
+ XK_dead_a, Qt::Key_Dead_a,
+ XK_dead_A, Qt::Key_Dead_A,
+ XK_dead_e, Qt::Key_Dead_e,
+ XK_dead_E, Qt::Key_Dead_E,
+ XK_dead_i, Qt::Key_Dead_i,
+ XK_dead_I, Qt::Key_Dead_I,
+ XK_dead_o, Qt::Key_Dead_o,
+ XK_dead_O, Qt::Key_Dead_O,
+ XK_dead_u, Qt::Key_Dead_u,
+ XK_dead_U, Qt::Key_Dead_U,
+ XK_dead_small_schwa, Qt::Key_Dead_Small_Schwa,
+ XK_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa,
+ XK_dead_greek, Qt::Key_Dead_Greek,
+ XK_dead_lowline, Qt::Key_Dead_Lowline,
+ XK_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline,
+ XK_dead_belowverticalline, Qt::Key_Dead_Belowverticalline,
+ XK_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay,
// Special keys from X.org - This include multimedia keys,
// wireless/bluetooth/uwb keys, special launcher keys, etc.
@@ -612,23 +709,18 @@ Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const
void QXcbKeyboard::readXKBConfig()
{
clearXKBConfig();
- xcb_generic_error_t *error;
- xcb_get_property_cookie_t cookie;
- xcb_get_property_reply_t *config_reply;
xcb_connection_t *c = xcb_connection();
xcb_window_t rootWindow = connection()->rootWindow();
- cookie = xcb_get_property(c, 0, rootWindow,
- atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024);
-
- config_reply = xcb_get_property_reply(c, cookie, &error);
+ auto config_reply = Q_XCB_REPLY(xcb_get_property, c, 0, rootWindow,
+ atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024);
if (!config_reply) {
qWarning("Qt: Couldn't interpret the _XKB_RULES_NAMES property");
return;
}
- char *xkb_config = (char *)xcb_get_property_value(config_reply);
- int length = xcb_get_property_value_length(config_reply);
+ char *xkb_config = (char *)xcb_get_property_value(config_reply.get());
+ int length = xcb_get_property_value_length(config_reply.get());
// on old X servers xkb_config can be 0 even if config_reply indicates a succesfull read
if (!xkb_config || length == 0)
@@ -653,8 +745,6 @@ void QXcbKeyboard::readXKBConfig()
xkb_names.layout = qstrdup(names[2]);
xkb_names.variant = qstrdup(names[3]);
xkb_names.options = qstrdup(names[4]);
-
- free(config_reply);
}
void QXcbKeyboard::clearXKBConfig()
@@ -685,9 +775,303 @@ void QXcbKeyboard::printKeymapError(const char *error) const
"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<KeySym>(unshifted), &xlower, &xupper);
+
+ if (xlower != xupper // Check if symbol is cased
+ && unshifted == static_cast<xcb_keysym_t>(xupper)) { // Unshifted must be upper case
+ return static_cast<xcb_keysym_t>(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 += "<K" + codeStr + "> = " + 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<xcb_keysym_t> xkeymap;
+ int keysymsPerKeycode = 0;
+ {
+ int keycodeCount = maxKeycode - minKeycode + 1;
+ if (auto keymapReply = Q_XCB_REPLY(xcb_get_keyboard_mapping, xcb_connection(),
+ minKeycode, keycodeCount)) {
+ keysymsPerKeycode = keymapReply->keysyms_per_keycode;
+ int numSyms = keycodeCount * keysymsPerKeycode;
+ auto keymapPtr = xcb_get_keyboard_mapping_keysyms(keymapReply.get());
+ xkeymap.resize(numSyms);
+ for (int i = 0; i < numSyms; i++)
+ xkeymap[i] = keymapPtr[i];
+ }
+ }
+ 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 <K" + QByteArray::number(code) + "> { ";
+ 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]
+ + " { <K" + QByteArray::number(code) + "> };\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<xkb_keymap_compile_flags>(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")) {
@@ -721,9 +1105,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<xkb_keymap_compile_flags> (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"
@@ -807,7 +1205,7 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
}
}
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
{
if (m_config && !connection()->hasXKB()) {
@@ -915,9 +1313,11 @@ xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const
// 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);
@@ -1124,7 +1524,7 @@ int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modi
modifiers |= Qt::KeypadModifier;
} else if (text.length() == 1 && text.unicode()->unicode() > 0x1f
&& text.unicode()->unicode() != 0x7f
- && !(keysym >= XK_dead_grave && keysym <= XK_dead_currency)) {
+ && !(keysym >= XK_dead_grave && keysym <= XK_dead_longsolidusoverlay)) {
code = text.unicode()->toUpper().unicode();
} else {
// any other keys
@@ -1172,23 +1572,19 @@ QXcbKeyboard::~QXcbKeyboard()
void QXcbKeyboard::updateVModMapping()
{
#if QT_CONFIG(xkb)
- xcb_xkb_get_names_cookie_t names_cookie;
- xcb_xkb_get_names_reply_t *name_reply;
xcb_xkb_get_names_value_list_t names_list;
memset(&vmod_masks, 0, sizeof(vmod_masks));
- names_cookie = xcb_xkb_get_names(xcb_connection(),
- XCB_XKB_ID_USE_CORE_KBD,
- XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES);
-
- name_reply = xcb_xkb_get_names_reply(xcb_connection(), names_cookie, 0);
+ auto name_reply = Q_XCB_REPLY(xcb_xkb_get_names, xcb_connection(),
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES);
if (!name_reply) {
qWarning("Qt: failed to retrieve the virtual modifier names from XKB");
return;
}
- const void *buffer = xcb_xkb_get_names_value_list(name_reply);
+ const void *buffer = xcb_xkb_get_names_value_list(name_reply.get());
xcb_xkb_get_names_value_list_unpack(buffer,
name_reply->nTypes,
name_reply->indicators,
@@ -1233,32 +1629,27 @@ void QXcbKeyboard::updateVModMapping()
else if (qstrcmp(vmod_name, "Hyper") == 0)
vmod_masks.hyper = bit;
}
-
- free(name_reply);
#endif
}
void QXcbKeyboard::updateVModToRModMapping()
{
#if QT_CONFIG(xkb)
- xcb_xkb_get_map_cookie_t map_cookie;
- xcb_xkb_get_map_reply_t *map_reply;
xcb_xkb_get_map_map_t map;
memset(&rmod_masks, 0, sizeof(rmod_masks));
- map_cookie = xcb_xkb_get_map(xcb_connection(),
- XCB_XKB_ID_USE_CORE_KBD,
- XCB_XKB_MAP_PART_VIRTUAL_MODS,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-
- map_reply = xcb_xkb_get_map_reply(xcb_connection(), map_cookie, 0);
+ auto map_reply = Q_XCB_REPLY(xcb_xkb_get_map,
+ xcb_connection(),
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_MAP_PART_VIRTUAL_MODS,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (!map_reply) {
qWarning("Qt: failed to retrieve the virtual modifier map from XKB");
return;
}
- const void *buffer = xcb_xkb_get_map_map(map_reply);
+ const void *buffer = xcb_xkb_get_map_map(map_reply.get());
xcb_xkb_get_map_map_unpack(buffer,
map_reply->nTypes,
map_reply->nKeySyms,
@@ -1301,29 +1692,60 @@ void QXcbKeyboard::updateVModToRModMapping()
rmod_masks.hyper = modmap;
}
- free(map_reply);
resolveMaskConflicts();
#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));
- xcb_generic_error_t *error = 0;
- xcb_connection_t *conn = xcb_connection();
- xcb_get_modifier_mapping_cookie_t modMapCookie = xcb_get_modifier_mapping(conn);
- xcb_get_modifier_mapping_reply_t *modMapReply =
- xcb_get_modifier_mapping_reply(conn, modMapCookie, &error);
- if (error) {
+ KeysymModifierMap map;
+
+ auto modMapReply = Q_XCB_REPLY(xcb_get_modifier_mapping, xcb_connection());
+ if (!modMapReply) {
qWarning("Qt: failed to get modifier mapping");
- free(error);
- return;
+ return map;
}
// for Alt and Meta L and R are the same
@@ -1338,36 +1760,63 @@ void QXcbKeyboard::updateModifiers()
for (size_t i = 0; i < numSymbols; ++i)
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;
- }
+ xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply.get());
+ const int modMapLength = xcb_get_modifier_mapping_keycodes_length(modMapReply.get());
+ /* 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;
}
}
}
for (size_t i = 0; i < numSymbols; ++i)
free(modKeyCodes[i]);
- free(modMapReply);
- resolveMaskConflicts();
+
+ return map;
}
void QXcbKeyboard::resolveMaskConflicts()
@@ -1449,8 +1898,6 @@ private:
void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
quint16 state, xcb_timestamp_t time)
{
- Q_XCB_NOOP(connection());
-
if (!m_config)
return;
@@ -1471,30 +1918,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
updateXKBStateFromState(kb_state, state);
xcb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, code);
-
- QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
- QMetaMethod method;
-
- if (inputContext) {
- int methodIndex = inputContext->metaObject()->indexOfMethod("x11FilterEvent(uint,uint,uint,bool)");
- if (methodIndex != -1)
- method = inputContext->metaObject()->method(methodIndex);
- }
-
- if (method.isValid()) {
- bool retval = false;
- method.invoke(inputContext, Qt::DirectConnection,
- Q_RETURN_ARG(bool, retval),
- Q_ARG(uint, sym),
- Q_ARG(uint, code),
- Q_ARG(uint, state),
- Q_ARG(bool, type == QEvent::KeyPress));
- if (retval) {
- xkb_state_unref(kb_state);
- return;
- }
- }
-
QString string = lookupString(kb_state, code);
// Ιf control modifier is set we should prefer latin character, this is
@@ -1526,6 +1949,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
}
bool filtered = false;
+ QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
if (inputContext) {
QKeyEvent event(type, qtcode, modifiers, code, sym, state, string, isAutoRepeat, string.length());
event.setTimestamp(time);
@@ -1548,16 +1972,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (isAutoRepeat && type == QEvent::KeyRelease) {
// since we removed it from the event queue using checkEvent we need to send the key press here
filtered = false;
- if (method.isValid()) {
- method.invoke(inputContext, Qt::DirectConnection,
- Q_RETURN_ARG(bool, filtered),
- Q_ARG(uint, sym),
- Q_ARG(uint, code),
- Q_ARG(uint, state),
- Q_ARG(bool, true));
- }
-
- if (!filtered && inputContext) {
+ if (inputContext) {
QKeyEvent event(QEvent::KeyPress, qtcode, modifiers, code, sym, state, string, isAutoRepeat, string.length());
event.setTimestamp(time);
filtered = inputContext->filterEvent(&event);