xcb: refactor QXcbKeyboard::keysymToQtKey() and fix bug

Now also digits from other alphabets e.g ۲ (arabic two) are mapped
to Qt::Key_* digit keys.

Re-factored logic:

- All known dead keys have direct mappings since
1d86e5f84a. Don't special treat them
in "unicode mapping" code path.

- Removed the ISO8859-1 legacy logic, which is leftover from Qt4
where keysym to Qt decoding was done from raw data. In Qt5 we always
get a utf8 string from xkb_state_key_get_utf8(). Furthermore,
ISO8859-1 and utf8 encode ASCII exactly the same way.

- Set Qt::KeypadModifier from key input handler methods. This logic
does not belong in keysymToQtKey().

Note:

KeyTbl[] and keysymToQtKey() have been duplicated in several places
in Qt. That stuff will be cleaned up as part of QTBUG-65503. This
change will make those cleanups easier.

Task-number: QTBUG-58865
Task-number: QTBUG-65503
Change-Id: Iaf10205a26804f7fc03eb8a16a0879f1bd7bf332
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
bb10
Gatis Paeglis 2018-02-09 13:57:41 +01:00
parent d5abf54597
commit 7878bb685f
3 changed files with 102 additions and 61 deletions

View File

@ -46,12 +46,10 @@
#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)
@ -1232,6 +1230,13 @@ 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
@ -1269,7 +1274,7 @@ 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);
@ -1310,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;
@ -1331,71 +1336,83 @@ 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)
@ -1778,16 +1795,19 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
xcb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, code);
QString string = lookupString(kb_state, code);
Qt::KeyboardModifiers modifiers = translateModifiers(state);
if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9)
modifiers |= Qt::KeypadModifier;
// Ι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);
int qtcode = keysymToQtKey(translatedSym, modifiers, kb_state, code);
bool isAutoRepeat = false;
if (type == QEvent::KeyPress) {
@ -1853,6 +1873,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);

View File

@ -88,8 +88,9 @@ 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;
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;
struct xkb_keymap *keymapFromCore();

View File

@ -221,4 +221,13 @@ void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t
}
}
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