summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbkeyboard.cpp
diff options
context:
space:
mode:
authorGatis Paeglis <gatis.paeglis@digia.com>2013-03-25 14:44:48 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-05-07 22:43:14 +0200
commit05351b0cde3e80d2d0ae3a838e802ff6086e4b59 (patch)
treedbcbb18247aafe9485e7ccbbbc903c858bb1c75d /src/plugins/platforms/xcb/qxcbkeyboard.cpp
parent9c1d62cc95ed2579fa25d83901c7a1aa315aa2d3 (diff)
Utilize libxkbcommon API for the keyboard backend on X11
libxkbcommon is a keymap compiler [1] which utilizes xkb keyboard configuration database [2]. xkbcommon simplifies keyboard state handling by providing xkb_state object which holds the current state of all modifiers, groups, LEDs, etc, relating to the provided keymap. Detecting modifier mapping should become simpler once it gets supported in libxkbcommon. Also with xinput2 we could get rid of the XkbStateNotify events, because xinput2 key press/release already include all the mod/group info. [1] http://xkbcommon.org/ [2] http://www.freedesktop.org/wiki/Software/XKeyboardConfig This patch: - Removes a dependency to -lxcb-keysyms - Removes a dependency to XLib call - XLookupString - Enables required functionality to fix QTBUG-27681 Task-number: QTBUG-27680 Change-Id: I10e10f873821ee02f6df72238e215a541150f38f Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbkeyboard.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp727
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