diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbkeyboard.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.cpp | 727 |
1 files changed, 326 insertions, 401 deletions
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 51c400ed9c..1a96a49890 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -42,8 +42,6 @@ #include "qxcbkeyboard.h" #include "qxcbwindow.h" #include "qxcbscreen.h" -#include "qxlibconvenience.h" -#include <xcb/xcb_keysyms.h> #include <X11/keysym.h> #include <qpa/qwindowsysteminterface.h> #include <QtCore/QTextCodec> @@ -561,183 +559,158 @@ static const unsigned int KeyTbl[] = { 0, 0 }; -static const unsigned short katakanaKeysymsToUnicode[] = { - 0x0000, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1, - 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3, - 0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, - 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, - 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, - 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, - 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, - 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C -}; +Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const +{ + Qt::KeyboardModifiers ret = 0; + if (s & XCB_MOD_MASK_SHIFT) + ret |= Qt::ShiftModifier; + if (s & XCB_MOD_MASK_CONTROL) + ret |= Qt::ControlModifier; + if (s & rmod_masks.alt) + ret |= Qt::AltModifier; + if (s & rmod_masks.meta) + ret |= Qt::MetaModifier; + if (s & rmod_masks.altgr) + ret |= Qt::GroupSwitchModifier; + return ret; +} -static const unsigned short cyrillicKeysymsToUnicode[] = { - 0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, - 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f, - 0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f, - 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, - 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, - 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, - 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, - 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, - 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, - 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, - 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a -}; +void QXcbKeyboard::readXKBConfig(struct xkb_rule_names *xkb_names) +{ + xcb_generic_error_t *error; + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *config_reply; -static const unsigned short greekKeysymsToUnicode[] = { - 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, - 0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, - 0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, - 0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, - 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, - 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, - 0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 -}; + xcb_connection_t *c = xcb_connection(); + xcb_window_t rootWindow = connection()->rootWindow(); -static const unsigned short technicalKeysymsToUnicode[] = { - 0x0000, 0x23B7, 0x250C, 0x2500, 0x2320, 0x2321, 0x2502, 0x23A1, - 0x23A3, 0x23A4, 0x23A6, 0x239B, 0x239D, 0x239E, 0x23A0, 0x23A8, - 0x23AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222B, - 0x2234, 0x221D, 0x221E, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, - 0x223C, 0x2243, 0x0000, 0x0000, 0x0000, 0x21D4, 0x21D2, 0x2261, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221A, 0x0000, - 0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222A, 0x2227, 0x2228, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, - 0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193, 0x0000 -}; + cookie = xcb_get_property(c, 0, rootWindow, + atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024); -static const unsigned short specialKeysymsToUnicode[] = { - 0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x0000, 0x0000, - 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA, - 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, - 0x2502, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 -}; + config_reply = xcb_get_property_reply(c, cookie, &error); + 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 *names[5] = { 0, 0, 0, 0, 0 }; + char *p = xkb_config, *end = p + length; + int i = 0; + // The result from xcb_get_property_value() is not necessarily \0-terminated, + // we need to make sure that too many or missing '\0' symbols are handled safely. + do { + uint len = qstrnlen(p, length); + names[i++] = p; + p += len + 1; + length -= len + 1; + } while (p < end || i < 5); + + xkb_names->rules = qstrdup(names[0]); + xkb_names->model = qstrdup(names[1]); + xkb_names->layout = qstrdup(names[2]); + xkb_names->variant = qstrdup(names[3]); + xkb_names->options = qstrdup(names[4]); + + free(config_reply); +} -static const unsigned short publishingKeysymsToUnicode[] = { - 0x0000, 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, - 0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, - 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, - 0x2105, 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000, - 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, - 0x0000, 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af, - 0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, - 0x0000, 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae, - 0x25e6, 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa, - 0x25b2, 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000, - 0x2720, 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642, - 0x2640, 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e, 0x0000 -}; +void QXcbKeyboard::updateKeymap() +{ + m_config = true; + if (!xkb_context) { + xkb_context = xkb_context_new((xkb_context_flags)0); + if (!xkb_context) { + qWarning("Qt: Failed to create XKB context"); + m_config = false; + return; + } + } -static const unsigned short aplKeysymsToUnicode[] = { - 0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003e, 0x0000, - 0x2228, 0x2227, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x00af, 0x0000, 0x22a5, 0x2229, 0x230a, 0x0000, 0x005f, 0x0000, - 0x0000, 0x0000, 0x2218, 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb, - 0x0000, 0x0000, 0x0000, 0x2308, 0x0000, 0x0000, 0x222a, 0x0000, - 0x2283, 0x0000, 0x2282, 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x22a3, 0x0000, 0x0000, 0x0000 -}; + struct xkb_rule_names xkb_names = {0, 0, 0, 0, 0}; -static const unsigned short koreanKeysymsToUnicode[] = { - 0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, - 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f, - 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147, - 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f, - 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, - 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, - 0x3160, 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab, - 0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, - 0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, - 0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d, - 0x3171, 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e, - 0x11eb, 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9 -}; + readXKBConfig(&xkb_names); + // Compile a keymap from RMLVO (rules, models, layouts, variants and options) names + if (xkb_keymap) + xkb_keymap_unref(xkb_keymap); -static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4) -{ - switch (byte3) { - case 0x04: - // katakana - if (byte4 > 0xa0 && byte4 < 0xe0) - return QChar(katakanaKeysymsToUnicode[byte4 - 0xa0]); - else if (byte4 == 0x7e) - return QChar(0x203e); // Overline - break; - case 0x06: - // russian, use lookup table - if (byte4 > 0xa0) - return QChar(cyrillicKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x07: - // greek - if (byte4 > 0xa0) - return QChar(greekKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x08: - // technical - if (byte4 > 0xa0) - return QChar(technicalKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x09: - // special - if (byte4 >= 0xe0) - return QChar(specialKeysymsToUnicode[byte4 - 0xe0]); - break; - case 0x0a: - // publishing - if (byte4 > 0xa0) - return QChar(publishingKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x0b: - // APL - if (byte4 > 0xa0) - return QChar(aplKeysymsToUnicode[byte4 - 0xa0]); - break; - case 0x0e: - // Korean - if (byte4 > 0xa0) - return QChar(koreanKeysymsToUnicode[byte4 - 0xa0]); - break; - default: - break; + xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); + + delete[] xkb_names.rules; + delete[] xkb_names.model; + delete[] xkb_names.layout; + delete[] xkb_names.variant; + delete[] xkb_names.options; + + if (!xkb_keymap) { + qWarning("Qt: Failed to compile a keymap"); + m_config = false; + return; + } + // Create a new keyboard state object for a keymap + struct xkb_state *new_state = xkb_state_new(xkb_keymap); + if (!new_state) { + qWarning("Qt: Failed to create a new keyboard state"); + m_config = false; + return; + } + + if (xkb_state) { + xkb_state_unref(xkb_state); + xkb_state = new_state; + } else { + // get initial state from the X server (and keep it up-to-date at all times) + xkb_state = new_state; + xcb_xkb_get_state_cookie_t state; + xcb_xkb_get_state_reply_t *init_state; + + xcb_connection_t *c = xcb_connection(); + state = xcb_xkb_get_state(c, XCB_XKB_ID_USE_CORE_KBD); + init_state = xcb_xkb_get_state_reply(c, state, 0); + if (!init_state) { + qWarning("Qt: couldn't retrieve an initial keyboard state"); + return; + } + /* The xkb keyboard state is comprised of the state of all keyboard modifiers, + the keyboard group, and the state of the pointer buttons */ + xkb_state_update_mask(xkb_state, + init_state->baseMods, + init_state->latchedMods, + init_state->lockedMods, + init_state->baseGroup, + init_state->latchedGroup, + init_state->lockedGroup); + free(init_state); } - return QChar(0x0); } -Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) +void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state) { - Qt::KeyboardModifiers ret = 0; - if (s & XCB_MOD_MASK_SHIFT) - ret |= Qt::ShiftModifier; - if (s & XCB_MOD_MASK_CONTROL) - ret |= Qt::ControlModifier; - if (s & m_alt_mask) - ret |= Qt::AltModifier; - if (s & m_meta_mask) - ret |= Qt::MetaModifier; - return ret; + if (!m_config) + return; + + if (connection()->hasXKB()) { + + xkb_state_component newState; + newState = xkb_state_update_mask(xkb_state, + state->baseMods, + state->latchedMods, + state->lockedMods, + state->baseGroup, + state->latchedGroup, + state->lockedGroup); + + if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) { + qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)"); + } + } } -int QXcbKeyboard::translateKeySym(uint key) const +int QXcbKeyboard::keysymToQtKey(xcb_keysym_t key) const { - int code = -1; - int i = 0; // any other keys + int code = 0; + int i = 0; while (KeyTbl[i]) { if (key == KeyTbl[i]) { code = (int)KeyTbl[i+1]; @@ -745,104 +718,20 @@ int QXcbKeyboard::translateKeySym(uint key) const } i += 2; } - if (m_meta_mask) { - // translate Super/Hyper keys to Meta if we're using them as the MetaModifier - if (m_meta_mask == m_super_mask && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) { - code = Qt::Key_Meta; - } else if (m_meta_mask == m_hyper_mask && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) { - code = Qt::Key_Meta; - } - } + return code; } -QString QXcbKeyboard::translateKeySym(xcb_keysym_t keysym, uint xmodifiers, - int &code, Qt::KeyboardModifiers &modifiers, - QByteArray &chars, int &count) +int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, QString text) const { - // all keysyms smaller than 0xff00 are actally keys that can be mapped to unicode chars - - QTextCodec *mapper = QTextCodec::codecForLocale(); - QChar converted; - - if (/*count == 0 &&*/ keysym < 0xff00) { - unsigned char byte3 = (unsigned char)(keysym >> 8); - int mib = -1; - switch(byte3) { - case 0: // Latin 1 - case 1: // Latin 2 - case 2: //latin 3 - case 3: // latin4 - mib = byte3 + 4; break; - case 5: // arabic - mib = 82; break; - case 12: // Hebrew - mib = 85; break; - case 13: // Thai - mib = 2259; break; - case 4: // kana - case 6: // cyrillic - case 7: // greek - case 8: // technical, no mapping here at the moment - case 9: // Special - case 10: // Publishing - case 11: // APL - case 14: // Korean, no mapping - mib = -1; // manual conversion - mapper= 0; -#if !defined(QT_NO_XIM) - converted = keysymToUnicode(byte3, keysym & 0xff); -#endif - case 0x20: - // currency symbols - if (keysym >= 0x20a0 && keysym <= 0x20ac) { - mib = -1; // manual conversion - mapper = 0; - converted = (uint)keysym; - } - break; - default: - break; - } - if (mib != -1) { - mapper = QTextCodec::codecForMib(mib); - if (chars.isEmpty()) - chars.resize(1); - chars[0] = (unsigned char) (keysym & 0xff); // get only the fourth bit for conversion later - count = 1; - } - } else if (keysym >= 0x1000000 && keysym <= 0x100ffff) { - converted = (ushort) (keysym - 0x1000000); - mapper = 0; - } - if (count < (int)chars.size()-1) - chars[count] = '\0'; - - QString text; - if (!mapper && converted.unicode() != 0x0) { - text = converted; - } else if (!chars.isEmpty()) { - // convert chars (8bit) to text (unicode). - if (mapper) - text = mapper->toUnicode(chars.data(), count, 0); - if (text.isEmpty()) { - // no mapper, or codec couldn't convert to unicode (this - // can happen when running in the C locale or with no LANG - // set). try converting from latin-1 - text = QString::fromLatin1(chars); - } - } - - modifiers = translateModifiers(xmodifiers); - + int code = 0; + QTextCodec *systemCodec = QTextCodec::codecForLocale(); // Commentary in X11/keysymdef says that X codes match ASCII, so it // is safe to use the locale functions to process X codes in ISO8859-1. - // // This is mainly for compatibility - applications should not use the - // Qt keycodes between 128 and 255, but should rather use the - // QKeyEvent::text(). - // - if (keysym < 128 || (keysym < 256 && (!mapper || mapper->mibEnum()==4))) { + // Qt keycodes between 128 and 255 (extended ACSII codes), but should + // rather use the QKeyEvent::text(). + if (keysym < 128 || (keysym < 256 && systemCodec->mibEnum() == 4)) { // upper-case key, if known code = isprint((int)keysym) ? toupper((int)keysym) : 0; } else if (keysym >= XK_F1 && keysym <= XK_F35) { @@ -853,138 +742,193 @@ QString QXcbKeyboard::translateKeySym(xcb_keysym_t keysym, uint xmodifiers, // numeric keypad keys code = Qt::Key_0 + ((int)keysym - XK_KP_0); } else { - code = translateKeySym(keysym); + code = keysymToQtKey(keysym); } modifiers |= Qt::KeypadModifier; - } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f && text.unicode()->unicode() != 0x7f && !(keysym >= XK_dead_grave && keysym <= XK_dead_horn)) { + } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f + && text.unicode()->unicode() != 0x7f + && !(keysym >= XK_dead_grave && keysym <= XK_dead_currency)) { code = text.unicode()->toUpper().unicode(); } else { // any other keys - code = translateKeySym(keysym); - - if (code == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { - // map shift+tab to shift+backtab, QShortcutMap knows about it - // and will handle it. - code = Qt::Key_Backtab; - text = QString(); - } + code = keysymToQtKey(keysym); } - return text; + return code; } QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) : QXcbObject(connection) , m_autorepeat_code(0) + , xkb_context(0) + , xkb_keymap(0) + , xkb_state(0) + , core_device_id(0) { - m_key_symbols = xcb_key_symbols_alloc(xcb_connection()); - setupModifiers(); + if (connection->hasXKB()) { + + updateKeymap(); + updateVModMapping(); + updateVModToRModMapping(); + + // get the core keyboard id + xcb_xkb_get_device_info_cookie_t device_id_cookie; + xcb_xkb_get_device_info_reply_t *device_id; + + device_id_cookie = xcb_xkb_get_device_info(xcb_connection(), + XCB_XKB_ID_USE_CORE_KBD, + 0, 0, 0, 0, 0, 0); + + device_id = xcb_xkb_get_device_info_reply(xcb_connection(), device_id_cookie, 0); + if (!device_id) { + qWarning("Qt: couldn't get core keyboard device info"); + return; + } + + core_device_id = device_id->deviceID; + free(device_id); + } } QXcbKeyboard::~QXcbKeyboard() { - xcb_key_symbols_free(m_key_symbols); + if (xkb_state) + xkb_state_unref(xkb_state); + if (xkb_keymap) + xkb_keymap_unref(xkb_keymap); + if (xkb_context) + xkb_context_unref(xkb_context); } -void QXcbKeyboard::setupModifiers() +void QXcbKeyboard::updateVModMapping() { - m_alt_mask = 0; - m_super_mask = 0; - m_hyper_mask = 0; - m_meta_mask = 0; - m_mode_switch_mask = 0; - m_num_lock_mask = 0; - m_caps_lock_mask = 0; - - 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) { - qWarning("QXcbKeyboard: failed to get modifier mapping"); - free(error); + 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); + if (!name_reply) { + qWarning("Qt: failed to retrieve the virtual modifier names from XKB"); return; } - // for Alt and Meta L and R are the same - static const xcb_keysym_t symbols[] = { - XK_Alt_L, XK_Meta_L, XK_Super_L, XK_Super_R, - XK_Hyper_L, XK_Hyper_R, XK_Num_Lock, XK_Mode_switch, XK_Caps_Lock, - }; - static const size_t numSymbols = sizeof symbols / sizeof *symbols; - - // Figure out the modifier mapping, ICCCM 6.6 - xcb_keycode_t* modKeyCodes[numSymbols]; - 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) - setMask(symbols[i], mask); - } - } + const void *buffer = xcb_xkb_get_names_value_list(name_reply); + xcb_xkb_get_names_value_list_unpack(buffer, + name_reply->nTypes, + name_reply->indicators, + name_reply->virtualMods, + name_reply->groupNames, + name_reply->nKeys, + name_reply->nKeyAliases, + name_reply->nRadioGroups, + name_reply->which, + &names_list); + + int count = 0; + uint vmod_mask, bit; + char *vmod_name; + vmod_mask = name_reply->virtualMods; + // find the virtual modifiers for which names are defined. + for (bit = 1; vmod_mask; bit <<= 1) { + vmod_name = 0; + + if (!(vmod_mask & bit)) + continue; + + vmod_mask &= ~bit; + // virtualModNames - the list of virtual modifier atoms beginning with the lowest-numbered + // virtual modifier for which a name is defined and proceeding to the highest. + QByteArray atomName = connection()->atomName(names_list.virtualModNames[count]); + vmod_name = atomName.data(); + count++; + + if (!vmod_name) + continue; + + // similarly we could retrieve NumLock, Super, Hyper modifiers if needed. + if (qstrcmp(vmod_name, "Alt") == 0) + vmod_masks.alt = bit; + else if (qstrcmp(vmod_name, "Meta") == 0) + vmod_masks.meta = bit; + else if (qstrcmp(vmod_name, "AltGr") == 0) + vmod_masks.altgr = bit; } - for (size_t i = 0; i < numSymbols; ++i) - free(modKeyCodes[i]); - free(modMapReply); + free(name_reply); } -void QXcbKeyboard::setMask(uint sym, uint mask) +void QXcbKeyboard::updateVModToRModMapping() { - if (m_alt_mask == 0 - && m_meta_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && (sym == XK_Alt_L || sym == XK_Alt_R)) - m_alt_mask = mask; - - if (m_meta_mask == 0 - && m_alt_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && (sym == XK_Meta_L || sym == XK_Meta_R)) - m_meta_mask = mask; - - if (m_super_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_hyper_mask != mask - && (sym == XK_Super_L || sym == XK_Super_R)) - m_super_mask = mask; - - if (m_hyper_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_super_mask != mask - && (sym == XK_Hyper_L || sym == XK_Hyper_R)) - m_hyper_mask = mask; - - if (m_mode_switch_mask == 0 - && m_alt_mask != mask - && m_meta_mask != mask - && m_super_mask != mask - && m_hyper_mask != mask - && sym == XK_Mode_switch) - m_mode_switch_mask = mask; - - if (m_num_lock_mask == 0 && sym == XK_Num_Lock) - m_num_lock_mask = mask; - - if (m_caps_lock_mask == 0 && sym == XK_Caps_Lock) - m_caps_lock_mask = mask; -} + xcb_xkb_get_map_cookie_t map_cookie; + xcb_xkb_get_map_reply_t *map_reply; + xcb_xkb_get_map_map_t map; -// #define XCB_KEYBOARD_DEBUG + 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); + 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); + xcb_xkb_get_map_map_unpack(buffer, + map_reply->nTypes, + map_reply->nKeySyms, + map_reply->nKeyActions, + map_reply->totalActions, + map_reply->totalKeyBehaviors, + map_reply->nVModMapKeys, + map_reply->totalKeyExplicit, + map_reply->totalModMapKeys, + map_reply->totalVModMapKeys, + map_reply->present, + &map); + + uint vmod_mask, bit; + // the virtual modifiers mask for which a set of corresponding + // real modifiers is to be returned + vmod_mask = map_reply->virtualMods; + int count = 0; + + for (bit = 1; vmod_mask; bit <<= 1) { + uint modmap; + + if (!(vmod_mask & bit)) + continue; + + vmod_mask &= ~bit; + // real modifier bindings for the specified virtual modifiers + modmap = map.vmods_rtrn[count]; + count++; + + if (vmod_masks.alt == bit) + rmod_masks.alt = modmap; + else if (vmod_masks.meta == bit) + rmod_masks.meta = modmap; + else if (vmod_masks.altgr == bit) + rmod_masks.altgr = modmap; + } + +#if 0 + qDebug() << "alt: " << rmod_masks.alt; + qDebug() << "meta: " << rmod_masks.meta; + qDebug() << "altgr: " << rmod_masks.altgr; +#endif + + free(map_reply); +} class KeyChecker { @@ -1046,16 +990,12 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod quint16 state, xcb_timestamp_t time) { Q_XCB_NOOP(connection()); -#ifdef XCB_KEYBOARD_DEBUG - printf("key code: %d, state: %d, syms: ", code, state); - for (int i = 0; i <= 5; ++i) { - printf("%d ", xcb_key_symbols_get_keysym(m_key_symbols, code, i)); - } - printf("\n"); -#endif - QByteArray chars; - xcb_keysym_t sym = lookupString(window, state, code, type, &chars); + if (!m_config) + return; + + xcb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, code); + QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); QMetaMethod method; @@ -1077,12 +1017,12 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod return; } - Qt::KeyboardModifiers modifiers; - int qtcode = 0; - int count = chars.count(); - QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count); + Qt::KeyboardModifiers modifiers = translateModifiers(state); + QString string = keysymToUnicode(sym); + int count = string.size(); string.truncate(count); + int qtcode = keysymToQtKey(sym, modifiers, string); bool isAutoRepeat = false; if (type == QEvent::KeyPress) { @@ -1141,35 +1081,17 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod } } -xcb_keysym_t QXcbKeyboard::lookupString(QWindow *window, uint state, xcb_keycode_t code, - QEvent::Type type, QByteArray *chars) +QString QXcbKeyboard::keysymToUnicode(xcb_keysym_t sym) const { -#ifdef XCB_USE_XLIB - xcb_window_t xWindow = static_cast<QXcbWindow *>(window->handle())->xcb_window(); - xcb_window_t root = connection()->screens().at(0)->root(); - void *xDisplay = connection()->xlib_display(); - int xType = (type == QEvent::KeyRelease ? 3 : 2); - return q_XLookupString(xDisplay, xWindow, root, state, code, xType, chars); -#else - - // No XLookupString available. The following is really incomplete... - - int col = state & XCB_MOD_MASK_SHIFT ? 1 : 0; - const int altGrOffset = 4; - if (state & 128) - col += altGrOffset; - xcb_keysym_t sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col); - if (sym == XCB_NO_SYMBOL) - sym = xcb_key_symbols_get_keysym(m_key_symbols, code, col ^ 0x1); - if (state & XCB_MOD_MASK_LOCK && sym <= 0x7f && isprint(sym)) { - if (isupper(sym)) - sym = tolower(sym); - else - sym = toupper(sym); - } - return sym; + QByteArray chars; + int bytes; + chars.resize(7); -#endif + if ((bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size())) == -1) + qWarning("QXcbKeyboard::handleKeyEvent - buffer too small"); + chars.resize(bytes-1); + + return QString::fromUtf8(chars); } void QXcbKeyboard::handleKeyPressEvent(QXcbWindowEventListener *eventListener, const xcb_key_press_event_t *event) @@ -1189,10 +1111,13 @@ void QXcbKeyboard::handleKeyReleaseEvent(QXcbWindowEventListener *eventListener, handleKeyEvent(window->window(), QEvent::KeyRelease, event->detail, event->state, event->time); } -void QXcbKeyboard::handleMappingNotifyEvent(const xcb_mapping_notify_event_t *event) +void QXcbKeyboard::handleMappingNotifyEvent(const xcb_xkb_map_notify_event_t *) { - xcb_refresh_keyboard_mapping(m_key_symbols, const_cast<xcb_mapping_notify_event_t *>(event)); - setupModifiers(); + if (connection()->hasXKB()) { + updateKeymap(); + updateVModMapping(); + updateVModToRModMapping(); + } } QT_END_NAMESPACE |