summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp190
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h5
2 files changed, 147 insertions, 48 deletions
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index dae3a79628..260fb46309 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -721,7 +721,7 @@ void QXcbKeyboard::updateKeymap()
if (xkb_keymap) {
new_state = xkb_state_new(xkb_keymap);
} else {
- printKeymapError("Failed to compile a keymap!");
+ printKeymapError("Qt: Failed to compile a keymap!");
m_config = false;
return;
}
@@ -737,6 +737,8 @@ void QXcbKeyboard::updateKeymap()
xkb_state = new_state;
if (!connection()->hasXKB())
updateXKBMods();
+
+ checkForLatinLayout();
}
#ifndef QT_NO_XKB
@@ -824,37 +826,137 @@ void QXcbKeyboard::updateXKBMods()
xkb_mods.mod5 = xkb_keymap_mod_get_index(xkb_keymap, "Mod5");
}
+static bool isLatin(xkb_keysym_t sym)
+{
+ return ((sym >= 'a' && sym <= 'z') || (sym >= 'A' && sym <= 'Z'));
+}
+
+void QXcbKeyboard::checkForLatinLayout()
+{
+ m_hasLatinLayout = false;
+ const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(xkb_keymap);
+ const xcb_keycode_t minKeycode = connection()->setup()->min_keycode;
+ const xcb_keycode_t maxKeycode = connection()->setup()->max_keycode;
+ struct xkb_state *kb_state = xkb_state_new(xkb_keymap);
+ for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) {
+ xkb_state_update_mask(kb_state, 0, 0, 0, 0, 0, layout);
+ for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, code);
+ // if layout can produce any of these latin letters (chosen
+ // arbitrarily) then it must be a latin key based layout
+ if (sym == XK_q || sym == XK_a || sym == XK_e) {
+ m_hasLatinLayout = true;
+ xkb_state_unref(kb_state);
+ return;
+ }
+ }
+ }
+ xkb_state_unref(kb_state);
+}
+
+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);
+ // 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)
+ continue;
+ if (isLatin(syms[0])) {
+ sym = syms[0];
+ break;
+ }
+ }
+ // If user layouts don't contain any layout that results in a latin key, we query a
+ // key from "US" layout, this allows for latin-key-based shorcuts to work even when
+ // users have only one (non-latin) layout set.
+ xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED);
+ xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED);
+ if (sym == XKB_KEY_NoSymbol && !m_hasLatinLayout) {
+ if (!latin_keymap) {
+ const struct xkb_rule_names names = { xkb_names.rules, xkb_names.model, "us", 0, 0 };
+ latin_keymap = xkb_keymap_new_from_names(xkb_context, &names, (xkb_keymap_compile_flags)0);
+ static bool printFailure = true;
+ if (!latin_keymap && printFailure) {
+ // print message about failure to compile US keymap only once,
+ // no need to do this on every key press.
+ printFailure = false;
+ printKeymapError("Qt: Failed to compile US keymap, shortcut handling with "
+ "non-Latin keyboard layouts may not be fully functional!");
+ }
+ }
+ if (latin_keymap) {
+ struct xkb_state *latin_state = xkb_state_new(latin_keymap);
+ if (latin_state) {
+ xkb_state_update_mask(latin_state, 0, latchedMods, lockedMods, 0, 0, 0);
+ sym = xkb_state_key_get_one_sym(latin_state, keycode);
+ xkb_state_unref(latin_state);
+ } else {
+ qWarning("QXcbKeyboard: failed to create a state for US keymap!");
+ }
+ }
+ }
+ if (sym == XKB_KEY_NoSymbol)
+ return sym;
+ // Check for uniqueness, consider the following setup:
+ // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active).
+ // In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+<physical x key>,
+ // because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained
+ // 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired
+ // then the obtained key is not unique. This prevents ctrl+<physical q key> from generating a ctrl+q
+ // shortcut in the above described setup. We don't want ctrl+<physical x key> and ctrl+<physical q key> to
+ // 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;
+ struct xkb_state *kb_state = xkb_state_new(xkb_keymap);
+ for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) {
+ xkb_state_update_mask(kb_state, 0, latchedMods, lockedMods, 0, 0, prevLayout);
+ for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
+ xkb_keysym_t prevSym = xkb_state_key_get_one_sym(kb_state, code);
+ if (prevSym == sym) {
+ sym = XKB_KEY_NoSymbol;
+ break;
+ }
+ }
+ }
+ xkb_state_unref(kb_state);
+ return sym;
+}
+
QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
{
// turn off the modifier bits which doesn't participate in shortcuts
Qt::KeyboardModifiers notNeeded = Qt::MetaModifier | Qt::KeypadModifier | Qt::GroupSwitchModifier;
Qt::KeyboardModifiers modifiers = event->modifiers() &= ~notNeeded;
// create a fresh kb state and test against the relevant modifier combinations
- // NOTE: it should be possible to query the keymap directly, once it gets
- // supported by libxkbcommon
- struct xkb_state * kb_state = xkb_state_new(xkb_keymap);
+ struct xkb_state *kb_state = xkb_state_new(xkb_keymap);
if (!kb_state) {
- qWarning("QXcbKeyboard: failed to compile xkb keymap");
+ 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 baseLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_DEPRESSED);
- xkb_layout_index_t latchedLayout = xkb_state_serialize_layout(xkb_state, XKB_STATE_LAYOUT_LATCHED);
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_state_update_mask(kb_state, 0, latchedMods, lockedMods,
- baseLayout, latchedLayout, lockedLayout);
+ xkb_state_update_mask(kb_state, 0, latchedMods, lockedMods, 0, 0, lockedLayout);
- xkb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, event->nativeScanCode());
+ quint32 keycode = event->nativeScanCode();
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, keycode);
if (sym == XKB_KEY_NoSymbol) {
xkb_state_unref(kb_state);
return QList<int>();
}
QList<int> result;
- int baseQtKey = keysymToQtKey(sym, modifiers, lookupString(kb_state, event->nativeScanCode()));
+ int baseQtKey = keysymToQtKey(sym, modifiers, lookupString(kb_state, keycode));
result += (baseQtKey + modifiers); // The base key is _always_ valid, of course
xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(xkb_keymap, "Shift");
@@ -866,48 +968,33 @@ QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
Q_ASSERT(controlMod < 32);
xkb_mod_mask_t depressed;
- struct xkb_keymap *fallback_keymap = 0;
int qtKey = 0;
- //obtain a list of possible shortcuts for the given key event
+ // obtain a list of possible shortcuts for the given key event
for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) {
Qt::KeyboardModifiers neededMods = ModsTbl[i];
if ((modifiers & neededMods) == neededMods) {
-
- depressed = 0;
- if (neededMods & Qt::AltModifier)
- depressed |= (1 << altMod);
- if (neededMods & Qt::ShiftModifier)
- depressed |= (1 << shiftMod);
- if (neededMods & Qt::ControlModifier)
- depressed |= (1 << controlMod);
-
- // update a keyboard state from a set of explicit masks
if (i == 8) {
- // Add a fall back key for layouts with non Latin-1 characters
- if (baseQtKey > 255) {
- struct xkb_rule_names names = { xkb_names.rules, xkb_names.model, "us", 0, 0 };
- fallback_keymap = xkb_keymap_new_from_names(xkb_context, &names, (xkb_keymap_compile_flags)0);
- if (!fallback_keymap)
- continue;
- xkb_state_unref(kb_state);
- kb_state = xkb_state_new(fallback_keymap);
- if (!kb_state)
- continue;
- } else
+ if (isLatin(baseQtKey))
continue;
+ // add a latin key as a fall back key
+ sym = lookupLatinKeysym(keycode);
} else {
- xkb_state_update_mask(kb_state, depressed, latchedMods, lockedMods,
- baseLayout, latchedLayout, lockedLayout);
+ depressed = 0;
+ if (neededMods & Qt::AltModifier)
+ depressed |= (1 << altMod);
+ if (neededMods & Qt::ShiftModifier)
+ depressed |= (1 << shiftMod);
+ if (neededMods & Qt::ControlModifier)
+ depressed |= (1 << controlMod);
+ xkb_state_update_mask(kb_state, depressed, latchedMods, lockedMods, 0, 0, lockedLayout);
+ sym = xkb_state_key_get_one_sym(kb_state, keycode);
}
- sym = xkb_state_key_get_one_sym(kb_state, event->nativeScanCode());
-
if (sym == XKB_KEY_NoSymbol)
continue;
Qt::KeyboardModifiers mods = modifiers & ~neededMods;
- qtKey = keysymToQtKey(sym, mods, lookupString(kb_state, event->nativeScanCode()));
-
- if (qtKey == baseQtKey || qtKey == 0)
+ qtKey = keysymToQtKey(sym, mods, lookupString(kb_state, keycode));
+ if (!qtKey || qtKey == baseQtKey)
continue;
// catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +,
@@ -926,8 +1013,6 @@ QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
}
}
xkb_state_unref(kb_state);
- xkb_keymap_unref(fallback_keymap);
-
return result;
}
@@ -1002,6 +1087,8 @@ QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection)
, xkb_context(0)
, xkb_keymap(0)
, xkb_state(0)
+ , latin_keymap(0)
+ , m_hasLatinLayout(false)
{
memset(&xkb_names, 0, sizeof(xkb_names));
#ifndef QT_NO_XKB
@@ -1029,6 +1116,7 @@ QXcbKeyboard::~QXcbKeyboard()
xkb_state_unref(xkb_state);
xkb_keymap_unref(xkb_keymap);
xkb_context_unref(xkb_context);
+ xkb_keymap_unref(latin_keymap);
if (!connection()->hasXKB())
xcb_key_symbols_free(m_key_symbols);
clearXKBConfig();
@@ -1324,7 +1412,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (type == QEvent::KeyPress)
targetWindow->updateNetWmUserTime(time);
- // It is crucial the order of xkb_state_key_get_one_sym & xkb_state_update_key operations is not reversed!
xcb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, code);
QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
@@ -1348,15 +1435,22 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
return;
}
- Qt::KeyboardModifiers modifiers = translateModifiers(state);
-
QString string = lookupString(xkb_state, code);
int count = string.size();
string.truncate(count);
- int qtcode = keysymToQtKey(sym, modifiers, string);
- bool isAutoRepeat = false;
+ // Ι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);
+ bool isAutoRepeat = false;
if (type == QEvent::KeyPress) {
if (m_autorepeat_code == code) {
isAutoRepeat = true;
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index e71165d824..9f1cf165cb 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -91,6 +91,9 @@ protected:
void updateVModMapping();
void updateVModToRModMapping();
+ xkb_keysym_t lookupLatinKeysym(xkb_keycode_t keycode) const;
+ void checkForLatinLayout();
+
private:
bool m_config;
xcb_keycode_t m_autorepeat_code;
@@ -99,6 +102,7 @@ private:
struct xkb_keymap *xkb_keymap;
struct xkb_state *xkb_state;
struct xkb_rule_names xkb_names;
+ mutable struct xkb_keymap *latin_keymap;
struct _mod_masks {
uint alt;
@@ -128,6 +132,7 @@ private:
_mod_masks vmod_masks;
int core_device_id;
#endif
+ bool m_hasLatinLayout;
};
QT_END_NAMESPACE