diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-02-24 21:29:47 +0100 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-02-24 21:29:47 +0100 |
commit | 8a9f77ead179ce88df5113290fa0201872fcd74e (patch) | |
tree | 3abf6515ebba2412dc9a36a84d95a9870915e4b2 /src/plugins | |
parent | efb46ea7ccf6fe8f89a8228bb5afe34c51901824 (diff) | |
parent | 9df6f3367e971d4fad63e59bb42615e2b7a580ce (diff) |
Merge remote-tracking branch 'origin/5.11' into dev
Change-Id: Icddf8720dae2cf594e16bcddab4d1cafc9d094c0
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.cpp | 433 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.h | 29 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbxkbcommon.h | 233 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/xcb_qpa_lib.pro | 3 |
4 files changed, 427 insertions, 271 deletions
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 20b5fe039a..d00d69586a 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -39,18 +39,17 @@ #include "qxcbkeyboard.h" #include "qxcbwindow.h" #include "qxcbscreen.h" +#include "qxcbxkbcommon.h" #include <qpa/qwindowsysteminterface.h> #include <qpa/qplatforminputcontext.h> #include <qpa/qplatformintegration.h> #include <qpa/qplatformcursor.h> -#include <QtCore/QTextCodec> -#include <QtCore/QMetaMethod> -#include <QtCore/QDir> +#include <QtCore/QMetaEnum> + #include <private/qguiapplication_p.h> -#include <stdio.h> #include <X11/keysym.h> #if QT_CONFIG(xinput2) @@ -59,12 +58,6 @@ #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 @@ -706,76 +699,6 @@ Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const return ret; } -void QXcbKeyboard::readXKBConfig() -{ - clearXKBConfig(); - - xcb_connection_t *c = xcb_connection(); - xcb_window_t rootWindow = connection()->rootWindow(); - - 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.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) - return; - // ### TODO some X servers don't set _XKB_RULES_NAMES at all, in these cases it is filled - // with gibberish, we would need to do some kind of sanity check - - 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]); -} - -void QXcbKeyboard::clearXKBConfig() -{ - if (xkb_names.rules) - delete[] xkb_names.rules; - if (xkb_names.model) - delete[] xkb_names.model; - if (xkb_names.layout) - delete[] xkb_names.layout; - if (xkb_names.variant) - delete[] xkb_names.variant; - if (xkb_names.options) - delete[] xkb_names.options; - memset(&xkb_names, 0, sizeof(xkb_names)); -} - -void QXcbKeyboard::printKeymapError(const char *error) const -{ - qWarning() << error; - if (xkb_context) { - qWarning("Current XKB configuration data search paths are: "); - for (unsigned int i = 0; i < xkb_context_num_include_paths(xkb_context); ++i) - qWarning() << xkb_context_include_path_get(xkb_context, i); - } - qWarning("Use QT_XKB_CONFIG_ROOT environmental variable to provide an additional search path, " - "add ':' as separator to provide several search paths and/or make sure that XKB configuration data " - "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. @@ -787,18 +710,15 @@ static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifte 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); + xcb_keysym_t xlower; + xcb_keysym_t xupper; + xkbcommon_XConvertCase(unshifted, &xlower, &xupper); + + if (xlower != xupper // Check if symbol is cased + && unshifted == xupper) { // Unshifted must be upper case + return xlower; } + return 0; } @@ -1060,92 +980,55 @@ struct xkb_keymap *QXcbKeyboard::keymapFromCore() keymap += "};\n"; // xkb_keymap - return xkb_keymap_new_from_buffer(xkb_context, + return xkb_keymap_new_from_buffer(m_xkbContext.get(), keymap.constData(), keymap.size(), XKB_KEYMAP_FORMAT_TEXT_V1, - static_cast<xkb_keymap_compile_flags>(0)); + XKB_KEYMAP_COMPILE_NO_FLAGS); } -#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")) { - xkb_context = xkb_context_new((xkb_context_flags)XKB_CONTEXT_NO_DEFAULT_INCLUDES); - const QList<QByteArray> xkbRootList = QByteArray(qgetenv("QT_XKB_CONFIG_ROOT")).split(':'); - for (const QByteArray &xkbRoot : xkbRootList) - xkb_context_include_path_append(xkb_context, xkbRoot.constData()); - } else { - xkb_context = xkb_context_new((xkb_context_flags)0); - } - if (!xkb_context) { - printKeymapError("Qt: Failed to create XKB context!"); + + if (!m_xkbContext) { + m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES)); + if (!m_xkbContext) { + qCWarning(lcQpaKeyboard, "failed to create XKB context"); m_config = false; return; } - // log only critical errors, we do our own error logging from printKeymapError() - xkb_context_set_log_level(xkb_context, (xkb_log_level)XKB_LOG_LEVEL_CRITICAL); + xkb_log_level logLevel = lcQpaKeyboard().isDebugEnabled() ? + XKB_LOG_LEVEL_DEBUG : XKB_LOG_LEVEL_CRITICAL; + xkb_context_set_log_level(m_xkbContext.get(), logLevel); } - // update xkb keymap object - xkb_keymap_unref(xkb_keymap); - xkb_keymap = 0; - struct xkb_state *new_state = 0; #if QT_CONFIG(xkb) if (connection()->hasXKB()) { - xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, xcb_connection(), core_device_id, (xkb_keymap_compile_flags)0); - if (xkb_keymap) { - // Create a new keyboard state object for a keymap - new_state = xkb_x11_state_new_from_device(xkb_keymap, xcb_connection(), core_device_id); - } - } + m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), xcb_connection(), + core_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS)); + if (m_xkbKeymap) + m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), xcb_connection(), core_device_id)); + } else { #endif - if (!xkb_keymap) { - // Read xkb RMLVO (rules, models, layouts, variants and options) names - readXKBConfig(); -#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; - } + m_xkbKeymap.reset(keymapFromCore()); + if (m_xkbKeymap) + m_xkbState.reset(xkb_state_new(m_xkbKeymap.get())); +#if QT_CONFIG(xkb) + } #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" - " from X server, will use hard-coded keymap configuration."; - clearXKBConfig(); - xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); - } - if (xkb_keymap) { - new_state = xkb_state_new(xkb_keymap); - } else { - printKeymapError("Qt: Failed to compile a keymap!"); - m_config = false; - return; - } + if (!m_xkbKeymap) { + qCWarning(lcQpaKeyboard, "failed to compile a keymap"); + m_config = false; + return; } - if (!new_state) { - qWarning("Qt: Failed to create xkb state!"); + if (!m_xkbState) { + qCWarning(lcQpaKeyboard, "failed to create XKB state"); m_config = false; return; } - // update xkb state object - xkb_state_unref(xkb_state); - xkb_state = new_state; + updateXKBMods(); checkForLatinLayout(); @@ -1156,7 +1039,7 @@ void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state) { if (m_config && connection()->hasXKB()) { const xkb_state_component newState - = xkb_state_update_mask(xkb_state, + = xkb_state_update_mask(m_xkbState.get(), state->baseMods, state->latchedMods, state->lockedMods, @@ -1201,7 +1084,7 @@ void QXcbKeyboard::updateXKBStateFromState(struct xkb_state *kb_state, quint16 s void QXcbKeyboard::updateXKBStateFromCore(quint16 state) { if (m_config && !connection()->hasXKB()) { - updateXKBStateFromState(xkb_state, state); + updateXKBStateFromState(m_xkbState.get(), state); } } @@ -1211,7 +1094,7 @@ void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo) if (m_config && !connection()->hasXKB()) { xXIModifierInfo *mods = static_cast<xXIModifierInfo *>(modInfo); xXIGroupInfo *group = static_cast<xXIGroupInfo *>(groupInfo); - const xkb_state_component newState = xkb_state_update_mask(xkb_state, + const xkb_state_component newState = xkb_state_update_mask(m_xkbState.get(), mods->base_mods, mods->latched_mods, mods->locked_mods, @@ -1252,14 +1135,14 @@ quint32 QXcbKeyboard::xkbModMask(quint16 state) void QXcbKeyboard::updateXKBMods() { - xkb_mods.shift = xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_SHIFT); - xkb_mods.lock = xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CAPS); - xkb_mods.control = xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL); - xkb_mods.mod1 = xkb_keymap_mod_get_index(xkb_keymap, "Mod1"); - xkb_mods.mod2 = xkb_keymap_mod_get_index(xkb_keymap, "Mod2"); - xkb_mods.mod3 = xkb_keymap_mod_get_index(xkb_keymap, "Mod3"); - xkb_mods.mod4 = xkb_keymap_mod_get_index(xkb_keymap, "Mod4"); - xkb_mods.mod5 = xkb_keymap_mod_get_index(xkb_keymap, "Mod5"); + xkb_mods.shift = xkb_keymap_mod_get_index(m_xkbKeymap.get(), XKB_MOD_NAME_SHIFT); + xkb_mods.lock = xkb_keymap_mod_get_index(m_xkbKeymap.get(), XKB_MOD_NAME_CAPS); + xkb_mods.control = xkb_keymap_mod_get_index(m_xkbKeymap.get(), XKB_MOD_NAME_CTRL); + xkb_mods.mod1 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod1"); + xkb_mods.mod2 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod2"); + xkb_mods.mod3 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod3"); + xkb_mods.mod4 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod4"); + xkb_mods.mod5 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod5"); } static bool isLatin(xkb_keysym_t sym) @@ -1269,11 +1152,11 @@ static bool isLatin(xkb_keysym_t sym) void QXcbKeyboard::checkForLatinLayout() const { - const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(xkb_keymap); + const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(m_xkbKeymap.get()); const xcb_keycode_t minKeycode = connection()->setup()->min_keycode; const xcb_keycode_t maxKeycode = connection()->setup()->max_keycode; - ScopedXKBState state(xkb_state_new(xkb_keymap)); + ScopedXKBState state(xkb_state_new(m_xkbKeymap.get())); for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) { xkb_state_update_mask(state.get(), 0, 0, 0, 0, 0, layout); for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) { @@ -1299,16 +1182,16 @@ xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const { xkb_layout_index_t layout; xkb_keysym_t sym = XKB_KEY_NoSymbol; - const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(xkb_keymap, keycode); - const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(xkb_state, keycode); + const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(m_xkbKeymap.get(), keycode); + const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(m_xkbState.get(), keycode); // Look at user layouts in the order in which they are defined in system // settings to find a latin keysym. for (layout = 0; layout < layoutCount; ++layout) { if (layout == currentLayout) continue; const xkb_keysym_t *syms; - xkb_level_index_t level = xkb_state_key_get_level(xkb_state, keycode, layout); - if (xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, layout, level, &syms) != 1) + xkb_level_index_t level = xkb_state_key_get_level(m_xkbState.get(), keycode, layout); + if (xkb_keymap_key_get_syms_by_level(m_xkbKeymap.get(), keycode, layout, level, &syms) != 1) continue; if (isLatin(syms[0])) { sym = syms[0]; @@ -1319,8 +1202,8 @@ xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const if (sym == XKB_KEY_NoSymbol) return sym; - 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); + xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LATCHED); + xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LOCKED); // Check for uniqueness, consider the following setup: // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active). @@ -1332,7 +1215,7 @@ xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const // generate the same shortcut event in this case. const xcb_keycode_t minKeycode = connection()->setup()->min_keycode; const xcb_keycode_t maxKeycode = connection()->setup()->max_keycode; - ScopedXKBState state(xkb_state_new(xkb_keymap)); + ScopedXKBState state(xkb_state_new(m_xkbKeymap.get())); for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) { xkb_state_update_mask(state.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout); for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) { @@ -1347,22 +1230,29 @@ xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const return sym; } +static const char *qtKeyName(int qtKey) +{ + int keyEnumIndex = qt_getQtMetaObject()->indexOfEnumerator("Key"); + QMetaEnum keyEnum = qt_getQtMetaObject()->enumerator(keyEnumIndex); + return keyEnum.valueToKey(qtKey); +} + QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const { // turn off the modifier bits which doesn't participate in shortcuts Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier; Qt::KeyboardModifiers modifiers = event->modifiers() &= ~notNeeded; // create a fresh kb state and test against the relevant modifier combinations - struct xkb_state *kb_state = xkb_state_new(xkb_keymap); + struct xkb_state *kb_state = xkb_state_new(m_xkbKeymap.get()); if (!kb_state) { qWarning("QXcbKeyboard: failed to compile xkb keymap!"); return QList<int>(); } // get kb state from the master xkb_state and update the temporary kb_state - xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_LOCKED); - 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); - xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_DEPRESSED); + xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(m_xkbState.get(), XKB_STATE_LAYOUT_LOCKED); + xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LATCHED); + xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LOCKED); + xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_DEPRESSED); xkb_state_update_mask(kb_state, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout); quint32 keycode = event->nativeScanCode(); @@ -1384,14 +1274,14 @@ QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const } QList<int> result; - int baseQtKey = keysymToQtKey(sym, modifiers, lookupString(kb_state, keycode)); + int baseQtKey = keysymToQtKey(sym, modifiers, kb_state, keycode); if (baseQtKey) result += (baseQtKey + modifiers); - xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(xkb_keymap, "Shift"); - xkb_mod_index_t altMod = xkb_keymap_mod_get_index(xkb_keymap, "Alt"); - xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(xkb_keymap, "Control"); - xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(xkb_keymap, "Meta"); + xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Shift"); + xkb_mod_index_t altMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Alt"); + xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Control"); + xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Meta"); Q_ASSERT(shiftMod < 32); Q_ASSERT(altMod < 32); @@ -1425,7 +1315,7 @@ QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const continue; Qt::KeyboardModifiers mods = modifiers & ~neededMods; - qtKey = keysymToQtKey(sym, mods, lookupString(kb_state, keycode)); + qtKey = keysymToQtKey(sym, mods, kb_state, keycode); if (!qtKey || qtKey == baseQtKey) continue; @@ -1446,77 +1336,88 @@ QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const } xkb_state_unref(kb_state); return result; - } +} -int QXcbKeyboard::keysymToQtKey(xcb_keysym_t key) const +int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modifiers, + struct xkb_state *state, xcb_keycode_t code) const { - int code = 0; - int i = 0; - while (KeyTbl[i]) { - if (key == KeyTbl[i]) { - code = (int)KeyTbl[i+1]; - break; + int qtKey = 0; + + // lookup from direct mapping + if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) { + // function keys + qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1); + } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) { + // numeric keypad keys + qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0); + } else if (isLatin(keysym)) { + qtKey = xkbcommon_xkb_keysym_to_upper(keysym); + } else { + // check if we have a direct mapping + int i = 0; + while (KeyTbl[i]) { + if (keysym == KeyTbl[i]) { + qtKey = KeyTbl[i + 1]; + break; + } + i += 2; + } + } + + QString text; + bool fromUnicode = qtKey == 0; + if (fromUnicode) { // lookup from unicode + if (modifiers & Qt::ControlModifier) { + // Control modifier changes the text to ASCII control character, therefore we + // can't use this text to map keysym to a qt key. We can use the same keysym + // (it is not affectd by transformation) to obtain untransformed text. For details + // see "Appendix A. Default Symbol Transformations" in the XKB specification. + text = lookupStringNoKeysymTransformations(keysym); + } else { + text = lookupString(state, code); + } + if (!text.isEmpty()) { + if (text.unicode()->isDigit()) { + // Ensures that also non-latin digits are mapped to corresponding qt keys, + // e.g CTRL + ۲ (arabic two), is mapped to CTRL + Qt::Key_2. + qtKey = Qt::Key_0 + text.unicode()->digitValue(); + } else { + qtKey = text.unicode()->toUpper().unicode(); + } } - i += 2; } if (rmod_masks.meta) { // translate Super/Hyper keys to Meta if we're using them as the MetaModifier - if (rmod_masks.meta == rmod_masks.super && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) { - code = Qt::Key_Meta; - } else if (rmod_masks.meta == rmod_masks.hyper && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) { - code = Qt::Key_Meta; + if (rmod_masks.meta == rmod_masks.super && (qtKey == Qt::Key_Super_L + || qtKey == Qt::Key_Super_R)) { + qtKey = Qt::Key_Meta; + } else if (rmod_masks.meta == rmod_masks.hyper && (qtKey == Qt::Key_Hyper_L + || qtKey == Qt::Key_Hyper_R)) { + qtKey = Qt::Key_Meta; } } - return code; -} - -int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, const QString &text) const -{ - int code = 0; -#ifndef QT_NO_TEXTCODEC - QTextCodec *systemCodec = QTextCodec::codecForLocale(); -#endif - // 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 (extended ACSII codes), but should - // rather use the QKeyEvent::text(). - if (keysym < 128 || (keysym < 256 -#ifndef QT_NO_TEXTCODEC - && systemCodec->mibEnum() == 4 -#endif - )) { - // upper-case key, if known - code = isprint((int)keysym) ? toupper((int)keysym) : 0; - } else if (keysym >= XK_F1 && keysym <= XK_F35) { - // function keys - code = Qt::Key_F1 + ((int)keysym - XK_F1); - } else if (keysym >= XK_KP_Space && keysym <= XK_KP_9) { - if (keysym >= XK_KP_0) { - // numeric keypad keys - code = Qt::Key_0 + ((int)keysym - XK_KP_0); + if (Q_UNLIKELY(lcQpaKeyboard().isDebugEnabled())) { + char keysymName[64]; + xkb_keysym_get_name(keysym, keysymName, sizeof(keysymName)); + QString keysymInHex = QString(QStringLiteral("0x%1")).arg(keysym, 0, 16); + if (qtKeyName(qtKey)) { + qCDebug(lcQpaKeyboard).nospace() << "keysym: " << keysymName << "(" + << keysymInHex << ") mapped to Qt::" << qtKeyName(qtKey) << " | text: " << text + << " | qt key: " << qtKey << " mapped from unicode number: " << fromUnicode; } else { - code = keysymToQtKey(keysym); + qCDebug(lcQpaKeyboard).nospace() << "no Qt::Key for keysym: " << keysymName + << "(" << keysymInHex << ") | text: " << text << " | qt key: " << qtKey; } - modifiers |= Qt::KeypadModifier; - } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f - && text.unicode()->unicode() != 0x7f - && !(keysym >= XK_dead_grave && keysym <= XK_dead_longsolidusoverlay)) { - code = text.unicode()->toUpper().unicode(); - } else { - // any other keys - code = keysymToQtKey(keysym); } - return code; + return qtKey; } QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) : QXcbObject(connection) { - memset(&xkb_names, 0, sizeof(xkb_names)); #if QT_CONFIG(xkb) core_device_id = 0; if (connection->hasXKB()) { @@ -1539,12 +1440,8 @@ QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) QXcbKeyboard::~QXcbKeyboard() { - xkb_state_unref(xkb_state); - xkb_keymap_unref(xkb_keymap); - xkb_context_unref(xkb_context); if (!connection()->hasXKB()) xcb_key_symbols_free(m_key_symbols); - clearXKBConfig(); } void QXcbKeyboard::updateVModMapping() @@ -1890,24 +1787,36 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, // this way we allow for synthetic events to have different state // from the current state i.e. you can have Alt+Ctrl pressed // and receive a synthetic key event that has neither Alt nor Ctrl pressed - struct xkb_state *kb_state = xkb_state_new(xkb_keymap); - if (!kb_state) + ScopedXKBState xkbState(xkb_state_new(m_xkbKeymap.get())); + if (!xkbState) return; - updateXKBStateFromState(kb_state, state); + updateXKBStateFromState(xkbState.get(), state); - xcb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, code); - QString string = lookupString(kb_state, code); + xcb_keysym_t sym = xkb_state_key_get_one_sym(xkbState.get(), code); + QString string = lookupString(xkbState.get(), code); - // Ιf control modifier is set we should prefer latin character, this is - // used for standard shortcuts in checks like "key == QKeySequence::Copy", - // users can still see the actual X11 keysym with QKeyEvent::nativeVirtualKey Qt::KeyboardModifiers modifiers = translateModifiers(state); - xcb_keysym_t translatedSym = XKB_KEY_NoSymbol; - if (modifiers & Qt::ControlModifier && !isLatin(sym)) - translatedSym = lookupLatinKeysym(code); - if (translatedSym == XKB_KEY_NoSymbol) - translatedSym = sym; - int qtcode = keysymToQtKey(translatedSym, modifiers, string); + if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9) + modifiers |= Qt::KeypadModifier; + + // Note 1: All standard key sequences on linux (as defined in platform theme) + // that use a latin character also contain a control modifier, which is why + // checking for Qt::ControlModifier is sufficient here. It is possible to + // override QPlatformTheme::keyBindings() and provide custom sequences for + // QKeySequence::StandardKey. Custom sequences probably should respect this + // convention (alternatively, we could test against other modifiers here). + // Note 2: The possibleKeys() shorcut mechanism is not affected by this value + // adjustment and does its own thing. + xcb_keysym_t latinKeysym = XKB_KEY_NoSymbol; + if (modifiers & Qt::ControlModifier) { + // With standard shortcuts we should prefer a latin character, this is + // in checks like "event == QKeySequence::Copy". + if (!isLatin(sym)) + latinKeysym = lookupLatinKeysym(code); + } + + int qtcode = keysymToQtKey(latinKeysym != XKB_KEY_NoSymbol ? latinKeysym : sym, + modifiers, xkbState.get(), code); bool isAutoRepeat = false; if (type == QEvent::KeyPress) { @@ -1959,7 +1868,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, QWindowSystemInterface::handleExtendedKeyEvent(window, time, QEvent::KeyPress, qtcode, modifiers, code, sym, state, string, isAutoRepeat); } - xkb_state_unref(kb_state); } QString QXcbKeyboard::lookupString(struct xkb_state *state, xcb_keycode_t code) const @@ -1973,6 +1881,17 @@ QString QXcbKeyboard::lookupString(struct xkb_state *state, xcb_keycode_t code) return QString::fromUtf8(chars.constData(), size); } +QString QXcbKeyboard::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) const +{ + QVarLengthArray<char, 32> chars(32); + const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); + if (Q_UNLIKELY(size > chars.size())) { + chars.resize(size); + xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); + } + return QString::fromUtf8(chars.constData(), size); +} + void QXcbKeyboard::handleKeyPressEvent(const xcb_key_press_event_t *event) { handleKeyEvent(event->event, QEvent::KeyPress, event->detail, event->state, event->time); diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index 5cb91ed315..736b32a2fd 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -88,15 +88,12 @@ protected: void resolveMaskConflicts(); QString lookupString(struct xkb_state *state, xcb_keycode_t code) const; - int keysymToQtKey(xcb_keysym_t keysym) const; - int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, const QString &text) const; - void printKeymapError(const char *error) const; + QString lookupStringNoKeysymTransformations(xkb_keysym_t keysym) const; + int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modifiers, + struct xkb_state *state, xcb_keycode_t code) const; - 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<xcb_keysym_t, int> KeysymModifierMap; @@ -112,14 +109,8 @@ 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; - struct xkb_keymap *xkb_keymap = nullptr; - struct xkb_state *xkb_state = nullptr; - struct xkb_rule_names xkb_names; - struct _mod_masks { uint alt; uint altgr; @@ -152,7 +143,19 @@ private: struct XKBStateDeleter { void operator()(struct xkb_state *state) const { return xkb_state_unref(state); } }; + struct XKBKeymapDeleter { + void operator()(struct xkb_keymap *keymap) const { return xkb_keymap_unref(keymap); } + }; + struct XKBContextDeleter { + void operator()(struct xkb_context *context) const { return xkb_context_unref(context); } + }; using ScopedXKBState = std::unique_ptr<struct xkb_state, XKBStateDeleter>; + using ScopedXKBKeymap = std::unique_ptr<struct xkb_keymap, XKBKeymapDeleter>; + using ScopedXKBContext = std::unique_ptr<struct xkb_context, XKBContextDeleter>; + + ScopedXKBState m_xkbState; + ScopedXKBKeymap m_xkbKeymap; + ScopedXKBContext m_xkbContext; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbxkbcommon.h b/src/plugins/platforms/xcb/qxcbxkbcommon.h new file mode 100644 index 0000000000..422c0c0f12 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbxkbcommon.h @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* XConvertCase was copied from src/3rdparty/xkbcommon/src/keysym.c, + which contains the following license information: + + Copyright 1985, 1987, 1990, 1998 The Open Group + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the authors or their + institutions shall not be used in advertising or otherwise to promote the + sale, use or other dealings in this Software without prior written + authorization from the authors. + + + + Copyright © 2009 Dan Nicholson + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* + The following code modifications were applied: + + XConvertCase() was renamed to xkbcommon_XConvertCase(), to not confuse it + with Xlib's XConvertCase(). + + UCSConvertCase() was renamed to qt_UCSConvertCase() and function's body was + replaced to use Qt APIs for doing case conversion, which should give us better + results instead of using the less complete version from keysym.c +*/ + +#include <xkbcommon/xkbcommon.h> +#include <QtCore/QChar> + +QT_BEGIN_NAMESPACE + +static void qt_UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper) +{ + *lower = QChar::toLower(code); + *upper = QChar::toUpper(code); +} + +void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper) +{ + /* Latin 1 keysym */ + if (sym < 0x100) { + qt_UCSConvertCase(sym, lower, upper); + return; + } + + /* Unicode keysym */ + if ((sym & 0xff000000) == 0x01000000) { + qt_UCSConvertCase((sym & 0x00ffffff), lower, upper); + *upper |= 0x01000000; + *lower |= 0x01000000; + return; + } + + /* Legacy keysym */ + + *lower = sym; + *upper = sym; + + switch (sym >> 8) { + case 1: /* Latin 2 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym == XKB_KEY_Aogonek) + *lower = XKB_KEY_aogonek; + else if (sym >= XKB_KEY_Lstroke && sym <= XKB_KEY_Sacute) + *lower += (XKB_KEY_lstroke - XKB_KEY_Lstroke); + else if (sym >= XKB_KEY_Scaron && sym <= XKB_KEY_Zacute) + *lower += (XKB_KEY_scaron - XKB_KEY_Scaron); + else if (sym >= XKB_KEY_Zcaron && sym <= XKB_KEY_Zabovedot) + *lower += (XKB_KEY_zcaron - XKB_KEY_Zcaron); + else if (sym == XKB_KEY_aogonek) + *upper = XKB_KEY_Aogonek; + else if (sym >= XKB_KEY_lstroke && sym <= XKB_KEY_sacute) + *upper -= (XKB_KEY_lstroke - XKB_KEY_Lstroke); + else if (sym >= XKB_KEY_scaron && sym <= XKB_KEY_zacute) + *upper -= (XKB_KEY_scaron - XKB_KEY_Scaron); + else if (sym >= XKB_KEY_zcaron && sym <= XKB_KEY_zabovedot) + *upper -= (XKB_KEY_zcaron - XKB_KEY_Zcaron); + else if (sym >= XKB_KEY_Racute && sym <= XKB_KEY_Tcedilla) + *lower += (XKB_KEY_racute - XKB_KEY_Racute); + else if (sym >= XKB_KEY_racute && sym <= XKB_KEY_tcedilla) + *upper -= (XKB_KEY_racute - XKB_KEY_Racute); + break; + case 2: /* Latin 3 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Hstroke && sym <= XKB_KEY_Hcircumflex) + *lower += (XKB_KEY_hstroke - XKB_KEY_Hstroke); + else if (sym >= XKB_KEY_Gbreve && sym <= XKB_KEY_Jcircumflex) + *lower += (XKB_KEY_gbreve - XKB_KEY_Gbreve); + else if (sym >= XKB_KEY_hstroke && sym <= XKB_KEY_hcircumflex) + *upper -= (XKB_KEY_hstroke - XKB_KEY_Hstroke); + else if (sym >= XKB_KEY_gbreve && sym <= XKB_KEY_jcircumflex) + *upper -= (XKB_KEY_gbreve - XKB_KEY_Gbreve); + else if (sym >= XKB_KEY_Cabovedot && sym <= XKB_KEY_Scircumflex) + *lower += (XKB_KEY_cabovedot - XKB_KEY_Cabovedot); + else if (sym >= XKB_KEY_cabovedot && sym <= XKB_KEY_scircumflex) + *upper -= (XKB_KEY_cabovedot - XKB_KEY_Cabovedot); + break; + case 3: /* Latin 4 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Rcedilla && sym <= XKB_KEY_Tslash) + *lower += (XKB_KEY_rcedilla - XKB_KEY_Rcedilla); + else if (sym >= XKB_KEY_rcedilla && sym <= XKB_KEY_tslash) + *upper -= (XKB_KEY_rcedilla - XKB_KEY_Rcedilla); + else if (sym == XKB_KEY_ENG) + *lower = XKB_KEY_eng; + else if (sym == XKB_KEY_eng) + *upper = XKB_KEY_ENG; + else if (sym >= XKB_KEY_Amacron && sym <= XKB_KEY_Umacron) + *lower += (XKB_KEY_amacron - XKB_KEY_Amacron); + else if (sym >= XKB_KEY_amacron && sym <= XKB_KEY_umacron) + *upper -= (XKB_KEY_amacron - XKB_KEY_Amacron); + break; + case 6: /* Cyrillic */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Serbian_DJE && sym <= XKB_KEY_Serbian_DZE) + *lower -= (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje); + else if (sym >= XKB_KEY_Serbian_dje && sym <= XKB_KEY_Serbian_dze) + *upper += (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje); + else if (sym >= XKB_KEY_Cyrillic_YU && sym <= XKB_KEY_Cyrillic_HARDSIGN) + *lower -= (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu); + else if (sym >= XKB_KEY_Cyrillic_yu && sym <= XKB_KEY_Cyrillic_hardsign) + *upper += (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu); + break; + case 7: /* Greek */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XKB_KEY_Greek_ALPHAaccent && sym <= XKB_KEY_Greek_OMEGAaccent) + *lower += (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent); + else if (sym >= XKB_KEY_Greek_alphaaccent && sym <= XKB_KEY_Greek_omegaaccent && + sym != XKB_KEY_Greek_iotaaccentdieresis && + sym != XKB_KEY_Greek_upsilonaccentdieresis) + *upper -= (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent); + else if (sym >= XKB_KEY_Greek_ALPHA && sym <= XKB_KEY_Greek_OMEGA) + *lower += (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA); + else if (sym >= XKB_KEY_Greek_alpha && sym <= XKB_KEY_Greek_omega && + sym != XKB_KEY_Greek_finalsmallsigma) + *upper -= (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA); + break; + case 0x13: /* Latin 9 */ + if (sym == XKB_KEY_OE) + *lower = XKB_KEY_oe; + else if (sym == XKB_KEY_oe) + *upper = XKB_KEY_OE; + else if (sym == XKB_KEY_Ydiaeresis) + *lower = XKB_KEY_ydiaeresis; + break; + } +} + +xkb_keysym_t xkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks) +{ + xkb_keysym_t lower, upper; + + xkbcommon_XConvertCase(ks, &lower, &upper); + + return upper; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro index 00cce13fd0..ffc8a29116 100644 --- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro +++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro @@ -46,7 +46,8 @@ HEADERS = \ qxcbcursor.h \ qxcbimage.h \ qxcbxsettings.h \ - qxcbsystemtraytracker.h + qxcbsystemtraytracker.h \ + qxcbxkbcommon.h load(qt_build_paths) |