fix global shortcuts for non-Latin symbols

Re-use Qt's implementation of handling non-Latin layouts here
For full ASCII range support (Alt+`, etc.) Qt needs to be patched still,
see QTBUG-90611

BUG: 375518
This commit is contained in:
Andrey Butirsky 2021-03-15 20:34:41 +03:00
parent bc3ac17141
commit 91f47c9092
10 changed files with 122 additions and 363 deletions

View file

@ -189,6 +189,8 @@ set_package_properties(XKB PROPERTIES
PURPOSE "Required for building KWin with Wayland support"
)
find_package(Qt5XkbCommonSupport REQUIRED)
find_package(Libinput 1.9)
set_package_properties(Libinput PROPERTIES TYPE REQUIRED PURPOSE "Required for input handling on Wayland.")

View file

@ -366,6 +366,7 @@ target_link_libraries(testXkb
KF5::WindowSystem
XKB::XKB
Qt5::XkbCommonSupportPrivate
)
add_test(NAME kwin-testXkb COMMAND testXkb)
ecm_mark_as_test(testXkb)

View file

@ -40,6 +40,8 @@ private Q_SLOTS:
void init();
void cleanup();
void testNonLatinLayout_data();
void testNonLatinLayout();
void testConsumedShift();
void testRepeatedTrigger();
void testUserActionsMenu();
@ -62,6 +64,7 @@ void GlobalShortcutsTest::initTestCase()
kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
qputenv("KWIN_XKB_DEFAULT_KEYMAP", "1");
qputenv("XKB_DEFAULT_RULES", "evdev");
qputenv("XKB_DEFAULT_LAYOUT", "us,ru");
kwinApp()->start();
QVERIFY(applicationStartedSpy.wait());
@ -80,6 +83,84 @@ void GlobalShortcutsTest::cleanup()
Test::destroyWaylandConnection();
}
Q_DECLARE_METATYPE(Qt::Modifier)
void GlobalShortcutsTest::testNonLatinLayout_data()
{
QTest::addColumn<int>("modifierKey");
QTest::addColumn<Qt::Modifier>("qtModifier");
QTest::addColumn<int>("key");
QTest::addColumn<Qt::Key>("qtKey");
for (const auto &modifier :
QVector<QPair<int, Qt::Modifier>> {
{KEY_LEFTCTRL, Qt::CTRL},
{KEY_LEFTALT, Qt::ALT},
{KEY_LEFTSHIFT, Qt::SHIFT},
{KEY_LEFTMETA, Qt::META},
} )
{
for (const auto &key :
QVector<QPair<int, Qt::Key>> {
// Tab is example of a key usually the same on different layouts, check it first
{KEY_TAB, Qt::Key_Tab},
// Then check a key with a Latin letter.
// The symbol will probably be differ on non-Latin layout.
// On Russian layout, "w" key has a cyrillic letter "ц"
{KEY_W, Qt::Key_W},
#if QT_VERSION_MAJOR > 5 // since Qt 5 LTS is frozen
// More common case with any Latin1 symbol keys, including punctuation, should work also.
// "`" key has a "ё" letter on Russian layout
// FIXME: QTBUG-90611
{KEY_GRAVE, Qt::Key_QuoteLeft},
#endif
} )
{
QTest::newRow(QKeySequence(modifier.second + key.second).toString().toLatin1().constData())
<< modifier.first << modifier.second << key.first << key.second;
}
}
}
void GlobalShortcutsTest::testNonLatinLayout()
{
// Shortcuts on non-Latin layouts should still work, see BUG 375518
auto xkb = input()->keyboard()->xkb();
xkb->switchToLayout(1);
QCOMPARE(xkb->layoutName(), QStringLiteral("Russian"));
QFETCH(int, modifierKey);
QFETCH(Qt::Modifier, qtModifier);
QFETCH(int, key);
QFETCH(Qt::Key, qtKey);
const QKeySequence seq(qtModifier + qtKey);
QScopedPointer<QAction> action(new QAction(nullptr));
action->setProperty("componentName", QStringLiteral(KWIN_NAME));
action->setObjectName("globalshortcuts-test-non-latin-layout");
QSignalSpy triggeredSpy(action.data(), &QAction::triggered);
QVERIFY(triggeredSpy.isValid());
KGlobalAccel::self()->stealShortcutSystemwide(seq);
KGlobalAccel::self()->setShortcut(action.data(), {seq}, KGlobalAccel::NoAutoloading);
input()->registerShortcut(seq, action.data());
quint32 timestamp = 0;
kwinApp()->platform()->keyboardKeyPressed(modifierKey, timestamp++);
QCOMPARE(input()->keyboardModifiers(), qtModifier);
kwinApp()->platform()->keyboardKeyPressed(key, timestamp++);
kwinApp()->platform()->keyboardKeyReleased(key, timestamp++);
kwinApp()->platform()->keyboardKeyReleased(modifierKey, timestamp++);
QTRY_COMPARE_WITH_TIMEOUT(triggeredSpy.count(), 1, 100);
}
void GlobalShortcutsTest::testConsumedShift()
{
// this test verifies that a shortcut with a consumed shift modifier triggers
@ -327,6 +408,8 @@ void GlobalShortcutsTest::testWaylandClientShortcut()
void GlobalShortcutsTest::testSetupWindowShortcut()
{
// QTBUG-62102
QScopedPointer<Surface> surface(Test::createSurface());
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);

View file

@ -19,8 +19,6 @@ class XkbTest : public QObject
private Q_SLOTS:
void testToQtKey_data();
void testToQtKey();
void testFromQtKey_data();
void testFromQtKey();
};
// from kwindowsystem/src/platforms/xcb/kkeyserver.cpp
@ -480,24 +478,5 @@ void XkbTest::testToQtKey()
QTEST(xkb.toQtKey(keySym), "qt");
}
void XkbTest::testFromQtKey_data()
{
QTest::addColumn<Qt::Key>("qt");
QTest::addColumn<xkb_keysym_t>("keySym");
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
for (std::size_t i = 0; i < sizeof(g_rgQtToSymX) / sizeof(TransKey); i++) {
const QByteArray row = QByteArray::number(g_rgQtToSymX[i].keySymX, 16);
QTest::newRow(row.constData()) << g_rgQtToSymX[i].keySymQt << g_rgQtToSymX[i].keySymX << g_rgQtToSymX[i].modifiers;
}
}
void XkbTest::testFromQtKey()
{
Xkb xkb;
QFETCH(Qt::Key, qt);
QFETCH(Qt::KeyboardModifiers, modifiers);
QTEST(xkb.fromQtKey(qt, modifiers), "keySym");
}
QTEST_MAIN(XkbTest)
#include "test_xkb.moc"

View file

@ -227,6 +227,7 @@ target_link_libraries(kwin
Libinput::Libinput
UDev::UDev
XKB::XKB
Qt5::XkbCommonSupportPrivate
epoxy::epoxy
Threads::Threads

View file

@ -990,11 +990,10 @@ class InternalWindowEventFilter : public InputEventFilter {
return false;
}
auto xkb = input()->keyboard()->xkb();
Qt::Key key = xkb->toQtKey(xkb->toKeysym(event->nativeScanCode()));
if (key == Qt::Key_Super_L || key == Qt::Key_Super_R) {
// workaround for QTBUG-62102
key = Qt::Key_Meta;
}
Qt::Key key = xkb->toQtKey( xkb->toKeysym(event->nativeScanCode()),
event->nativeScanCode(),
Qt::KeyboardModifiers(),
true /* workaround for QTBUG-62102 */ );
QKeyEvent internalEvent(event->type(), key,
event->modifiers(), event->nativeScanCode(), event->nativeVirtualKey(),
event->nativeModifiers(), event->text());

View file

@ -202,8 +202,9 @@ void KeyboardInputRedirection::processKey(uint32_t key, InputRedirection::Keyboa
}
const xkb_keysym_t keySym = m_xkb->currentKeysym();
const Qt::KeyboardModifiers globalShortcutsModifiers = m_xkb->modifiersRelevantForGlobalShortcuts(key);
KeyEvent event(type,
m_xkb->toQtKey(keySym),
m_xkb->toQtKey(keySym, key, globalShortcutsModifiers ? Qt::ControlModifier : Qt::KeyboardModifiers()),
m_xkb->modifiers(),
key,
keySym,
@ -211,7 +212,7 @@ void KeyboardInputRedirection::processKey(uint32_t key, InputRedirection::Keyboa
autoRepeat,
time,
device);
event.setModifiersRelevantForGlobalShortcuts(m_xkb->modifiersRelevantForGlobalShortcuts());
event.setModifiersRelevantForGlobalShortcuts(globalShortcutsModifiers);
m_input->processSpies(std::bind(&InputEventSpy::keyEvent, std::placeholders::_1, &event));
if (!m_inited) {

View file

@ -7,7 +7,6 @@
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "xkb.h"
#include "xkb_qt_mapping.h"
#include "utils.h"
// frameworks
#include <KConfigGroup>
@ -17,6 +16,7 @@
// Qt
#include <QTemporaryFile>
#include <QKeyEvent>
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
// xkbcommon
#include <xkbcommon/xkbcommon-compose.h>
#include <xkbcommon/xkbcommon-keysyms.h>
@ -353,7 +353,7 @@ void Xkb::updateModifiers()
if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
mods |= Qt::MetaModifier;
}
if (isKeypadKey(m_keysym)) {
if (m_keysym >= XKB_KEY_KP_Space && m_keysym <= XKB_KEY_KP_9) {
mods |= Qt::KeypadModifier;
}
m_modifiers = mods;
@ -427,7 +427,7 @@ void Xkb::updateConsumedModifiers(uint32_t key)
m_consumedModifiers = mods;
}
Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts() const
Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts(uint32_t scanCode) const
{
if (!m_state) {
return Qt::NoModifier;
@ -452,7 +452,7 @@ Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts() const
// in that case the shift should be removed from the consumed modifiers again
// otherwise it would not be possible to trigger e.g. Shift+W as a shortcut
// see BUG: 370341
if (QChar(toQtKey(m_keysym)).isLetter()) {
if (QChar(toQtKey(m_keysym, scanCode, Qt::ControlModifier)).isLetter()) {
consumedMods = Qt::KeyboardModifiers();
}
}
@ -481,24 +481,28 @@ QString Xkb::toString(xkb_keysym_t keysym)
return QString::fromUtf8(byteArray.constData());
}
Qt::Key Xkb::toQtKey(xkb_keysym_t keysym) const
Qt::Key Xkb::toQtKey(xkb_keysym_t keySym,
uint32_t scanCode,
Qt::KeyboardModifiers modifiers,
bool superAsMeta) const
{
return xkbToQtKey(keysym);
}
// FIXME: passing superAsMeta doesn't have impact due to bug in the Qt function, so handle it below
Qt::Key qtKey = Qt::Key( QXkbCommon::keysymToQtKey(keySym, modifiers, m_state, scanCode + 8, superAsMeta) );
xkb_keysym_t Xkb::fromQtKey(Qt::Key key, Qt::KeyboardModifiers mods) const
{
return qtKeyToXkb(key, mods);
}
xkb_keysym_t Xkb::fromKeyEvent(QKeyEvent *event) const
{
xkb_keysym_t sym = xkb_keysym_from_name(event->text().toUtf8().constData(), XKB_KEYSYM_NO_FLAGS);
if (sym == XKB_KEY_NoSymbol) {
// mapping from text failed, try mapping through KKeyServer
sym = fromQtKey(Qt::Key(event->key() & ~Qt::KeyboardModifierMask), event->modifiers());
// FIXME: workarounds for symbols currently wrong/not mappable via keysymToQtKey()
if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R)) {
// translate Super/Hyper keys to Meta if we're using them as the MetaModifier
qtKey = Qt::Key_Meta;
} else if (qtKey > 0xff && keySym <= 0xff) {
// XKB_KEY_mu, XKB_KEY_ydiaeresis go here
qtKey = Qt::Key(keySym);
#if QT_VERSION_MAJOR < 6 // since Qt 5 LTS is frozen
} else if (keySym == XKB_KEY_Sys_Req) {
// fixed in QTBUG-92087
qtKey = Qt::Key_SysReq;
#endif
}
return sym;
return qtKey;
}
bool Xkb::shouldKeyRepeat(quint32 key) const

View file

@ -54,11 +54,12 @@ public:
return m_keysym;
}
QString toString(xkb_keysym_t keysym);
Qt::Key toQtKey(xkb_keysym_t keysym) const;
xkb_keysym_t fromQtKey(Qt::Key key, Qt::KeyboardModifiers mods) const;
xkb_keysym_t fromKeyEvent(QKeyEvent *event) const;
Qt::Key toQtKey(xkb_keysym_t keysym,
uint32_t scanCode = 0,
Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(),
bool superAsMeta = false) const;
Qt::KeyboardModifiers modifiers() const;
Qt::KeyboardModifiers modifiersRelevantForGlobalShortcuts() const;
Qt::KeyboardModifiers modifiersRelevantForGlobalShortcuts(uint32_t scanCode = 0) const;
bool shouldKeyRepeat(quint32 key) const;
void switchToNextLayout();

View file

@ -1,312 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_XKB_QT_MAPPING_H
#define KWIN_XKB_QT_MAPPING_H
#include <QtGlobal>
#include <map>
#include <xkbcommon/xkbcommon-keysyms.h>
namespace KWin
{
// based on mapping in kwindowsystem/src/platforms/xcb/kkeyserver.cpp
// adjusted to XKB
static const std::map<xkb_keysym_t, Qt::Key> s_mapping{
{ XKB_KEY_Escape, Qt::Key_Escape },
{ XKB_KEY_Tab, Qt::Key_Tab },
{ XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab },
{ XKB_KEY_BackSpace, Qt::Key_Backspace },
{ XKB_KEY_Return, Qt::Key_Return },
{ XKB_KEY_Insert, Qt::Key_Insert },
{ XKB_KEY_Delete, Qt::Key_Delete },
{ XKB_KEY_Pause, Qt::Key_Pause },
{ XKB_KEY_Print, Qt::Key_Print },
{ XKB_KEY_Sys_Req, Qt::Key_SysReq },
{ XKB_KEY_Home, Qt::Key_Home },
{ XKB_KEY_End, Qt::Key_End },
{ XKB_KEY_Left, Qt::Key_Left },
{ XKB_KEY_Up, Qt::Key_Up },
{ XKB_KEY_Right, Qt::Key_Right },
{ XKB_KEY_Down, Qt::Key_Down },
{ XKB_KEY_Prior, Qt::Key_PageUp },
{ XKB_KEY_Next, Qt::Key_PageDown },
{ XKB_KEY_Caps_Lock, Qt::Key_CapsLock },
{ XKB_KEY_Num_Lock, Qt::Key_NumLock },
{ XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock },
{ XKB_KEY_F1, Qt::Key_F1 },
{ XKB_KEY_F2, Qt::Key_F2 },
{ XKB_KEY_F3, Qt::Key_F3 },
{ XKB_KEY_F4, Qt::Key_F4 },
{ XKB_KEY_F5, Qt::Key_F5 },
{ XKB_KEY_F6, Qt::Key_F6 },
{ XKB_KEY_F7, Qt::Key_F7 },
{ XKB_KEY_F8, Qt::Key_F8 },
{ XKB_KEY_F9, Qt::Key_F9 },
{ XKB_KEY_F10, Qt::Key_F10 },
{ XKB_KEY_F11, Qt::Key_F11 },
{ XKB_KEY_F12, Qt::Key_F12 },
{ XKB_KEY_F13, Qt::Key_F13 },
{ XKB_KEY_F14, Qt::Key_F14 },
{ XKB_KEY_F15, Qt::Key_F15 },
{ XKB_KEY_F16, Qt::Key_F16 },
{ XKB_KEY_F17, Qt::Key_F17 },
{ XKB_KEY_F18, Qt::Key_F18 },
{ XKB_KEY_F19, Qt::Key_F19 },
{ XKB_KEY_F20, Qt::Key_F20 },
{ XKB_KEY_F21, Qt::Key_F21 },
{ XKB_KEY_F22, Qt::Key_F22 },
{ XKB_KEY_F23, Qt::Key_F23 },
{ XKB_KEY_F24, Qt::Key_F24 },
{ XKB_KEY_F25, Qt::Key_F25 },
{ XKB_KEY_F26, Qt::Key_F26 },
{ XKB_KEY_F27, Qt::Key_F27 },
{ XKB_KEY_F28, Qt::Key_F28 },
{ XKB_KEY_F29, Qt::Key_F29 },
{ XKB_KEY_F30, Qt::Key_F30 },
{ XKB_KEY_F31, Qt::Key_F31 },
{ XKB_KEY_F32, Qt::Key_F32 },
{ XKB_KEY_F33, Qt::Key_F33 },
{ XKB_KEY_F34, Qt::Key_F34 },
{ XKB_KEY_F35, Qt::Key_F35 },
{ XKB_KEY_Super_L, Qt::Key_Super_L },
{ XKB_KEY_Super_R, Qt::Key_Super_R },
{ XKB_KEY_Menu, Qt::Key_Menu },
{ XKB_KEY_Hyper_L, Qt::Key_Hyper_L },
{ XKB_KEY_Hyper_R, Qt::Key_Hyper_R },
{ XKB_KEY_Help, Qt::Key_Help },
{ XKB_KEY_KP_Space, Qt::Key_Space },
{ XKB_KEY_KP_Tab, Qt::Key_Tab },
{ XKB_KEY_KP_Enter, Qt::Key_Enter },
{ XKB_KEY_KP_Home, Qt::Key_Home },
{ XKB_KEY_KP_Left, Qt::Key_Left },
{ XKB_KEY_KP_Up, Qt::Key_Up },
{ XKB_KEY_KP_Right, Qt::Key_Right },
{ XKB_KEY_KP_Down, Qt::Key_Down },
{ XKB_KEY_KP_Prior, Qt::Key_PageUp },
{ XKB_KEY_KP_Next, Qt::Key_PageDown },
{ XKB_KEY_KP_End, Qt::Key_End },
{ XKB_KEY_KP_Begin, Qt::Key_Clear },
{ XKB_KEY_KP_Insert, Qt::Key_Insert },
{ XKB_KEY_KP_Delete, Qt::Key_Delete },
{ XKB_KEY_KP_Equal, Qt::Key_Equal },
{ XKB_KEY_KP_Multiply, Qt::Key_Asterisk },
{ XKB_KEY_KP_Add, Qt::Key_Plus },
{ XKB_KEY_KP_Separator, Qt::Key_Comma },
{ XKB_KEY_KP_Subtract, Qt::Key_Minus },
{ XKB_KEY_KP_Decimal, Qt::Key_Period },
{ XKB_KEY_KP_Divide, Qt::Key_Slash },
{ XKB_KEY_XF86Back, Qt::Key_Back },
{ XKB_KEY_XF86Forward, Qt::Key_Forward },
{ XKB_KEY_XF86Stop, Qt::Key_Stop },
{ XKB_KEY_XF86Refresh, Qt::Key_Refresh },
{ XKB_KEY_XF86Favorites, Qt::Key_Favorites },
{ XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia },
{ XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl },
{ XKB_KEY_XF86HomePage, Qt::Key_HomePage },
{ XKB_KEY_XF86Search, Qt::Key_Search },
{ XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown },
{ XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute },
{ XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp },
{ XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay },
{ XKB_KEY_XF86AudioPause, Qt::Key_MediaPause },
{ XKB_KEY_XF86AudioStop, Qt::Key_MediaStop },
{ XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious },
{ XKB_KEY_XF86AudioNext, Qt::Key_MediaNext },
{ XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord },
{ XKB_KEY_XF86Mail, Qt::Key_LaunchMail },
{ XKB_KEY_XF86MyComputer, Qt::Key_Launch0 },
{ XKB_KEY_XF86Calculator, Qt::Key_Launch1 },
{ XKB_KEY_XF86Memo, Qt::Key_Memo },
{ XKB_KEY_XF86ToDoList, Qt::Key_ToDoList },
{ XKB_KEY_XF86Calendar, Qt::Key_Calendar },
{ XKB_KEY_XF86PowerDown, Qt::Key_PowerDown },
{ XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust },
{ XKB_KEY_XF86Standby, Qt::Key_Standby },
{ XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp },
{ XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown },
{ XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff },
{ XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp },
{ XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown },
{ XKB_KEY_XF86PowerOff, Qt::Key_PowerOff },
{ XKB_KEY_XF86WakeUp, Qt::Key_WakeUp },
{ XKB_KEY_XF86Eject, Qt::Key_Eject },
{ XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver },
{ XKB_KEY_XF86WWW, Qt::Key_WWW },
{ XKB_KEY_XF86Sleep, Qt::Key_Sleep },
{ XKB_KEY_XF86LightBulb, Qt::Key_LightBulb },
{ XKB_KEY_XF86Shop, Qt::Key_Shop },
{ XKB_KEY_XF86History, Qt::Key_History },
{ XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite },
{ XKB_KEY_XF86HotLinks, Qt::Key_HotLinks },
{ XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust },
{ XKB_KEY_XF86Finance, Qt::Key_Finance },
{ XKB_KEY_XF86Community, Qt::Key_Community },
{ XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind },
{ XKB_KEY_XF86BackForward, Qt::Key_BackForward },
{ XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft },
{ XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight },
{ XKB_KEY_XF86Book, Qt::Key_Book },
{ XKB_KEY_XF86CD, Qt::Key_CD },
{ XKB_KEY_XF86Calculater, Qt::Key_Calculator },
{ XKB_KEY_XF86Clear, Qt::Key_Clear },
{ XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab },
{ XKB_KEY_XF86Close, Qt::Key_Close },
{ XKB_KEY_XF86Copy, Qt::Key_Copy },
{ XKB_KEY_XF86Cut, Qt::Key_Cut },
{ XKB_KEY_XF86Display, Qt::Key_Display },
{ XKB_KEY_XF86DOS, Qt::Key_DOS },
{ XKB_KEY_XF86Documents, Qt::Key_Documents },
{ XKB_KEY_XF86Excel, Qt::Key_Excel },
{ XKB_KEY_XF86Explorer, Qt::Key_Explorer },
{ XKB_KEY_XF86Game, Qt::Key_Game },
{ XKB_KEY_XF86Go, Qt::Key_Go },
{ XKB_KEY_XF86iTouch, Qt::Key_iTouch },
{ XKB_KEY_XF86LogOff, Qt::Key_LogOff },
{ XKB_KEY_XF86Market, Qt::Key_Market },
{ XKB_KEY_XF86Meeting, Qt::Key_Meeting },
{ XKB_KEY_XF86MenuKB, Qt::Key_MenuKB },
{ XKB_KEY_XF86MenuPB, Qt::Key_MenuPB },
{ XKB_KEY_XF86MySites, Qt::Key_MySites },
{ XKB_KEY_XF86News, Qt::Key_News },
{ XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome },
{ XKB_KEY_XF86Option, Qt::Key_Option },
{ XKB_KEY_XF86Paste, Qt::Key_Paste },
{ XKB_KEY_XF86Phone, Qt::Key_Phone },
{ XKB_KEY_XF86Reply, Qt::Key_Reply },
{ XKB_KEY_XF86Reload, Qt::Key_Reload },
{ XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows },
{ XKB_KEY_XF86RotationPB, Qt::Key_RotationPB },
{ XKB_KEY_XF86RotationKB, Qt::Key_RotationKB },
{ XKB_KEY_XF86Save, Qt::Key_Save },
{ XKB_KEY_XF86Send, Qt::Key_Send },
{ XKB_KEY_XF86Spell, Qt::Key_Spell },
{ XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen },
{ XKB_KEY_XF86Support, Qt::Key_Support },
{ XKB_KEY_XF86TaskPane, Qt::Key_TaskPane },
{ XKB_KEY_XF86Terminal, Qt::Key_Terminal },
{ XKB_KEY_XF86Tools, Qt::Key_Tools },
{ XKB_KEY_XF86Travel, Qt::Key_Travel },
{ XKB_KEY_XF86Video, Qt::Key_Video },
{ XKB_KEY_XF86Word, Qt::Key_Word },
{ XKB_KEY_XF86Xfer, Qt::Key_Xfer },
{ XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn },
{ XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut },
{ XKB_KEY_XF86Away, Qt::Key_Away },
{ XKB_KEY_XF86Messenger, Qt::Key_Messenger },
{ XKB_KEY_XF86WebCam, Qt::Key_WebCam },
{ XKB_KEY_XF86MailForward, Qt::Key_MailForward },
{ XKB_KEY_XF86Pictures, Qt::Key_Pictures },
{ XKB_KEY_XF86Music, Qt::Key_Music },
{ XKB_KEY_XF86Battery, Qt::Key_Battery },
{ XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth },
{ XKB_KEY_XF86WLAN, Qt::Key_WLAN },
{ XKB_KEY_XF86UWB, Qt::Key_UWB },
{ XKB_KEY_XF86AudioForward, Qt::Key_AudioForward },
{ XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat },
{ XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay },
{ XKB_KEY_XF86Subtitle, Qt::Key_Subtitle },
{ XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack },
{ XKB_KEY_XF86Time, Qt::Key_Time },
{ XKB_KEY_XF86Select, Qt::Key_Select },
{ XKB_KEY_XF86View, Qt::Key_View },
{ XKB_KEY_XF86TopMenu, Qt::Key_TopMenu },
{ XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth },
{ XKB_KEY_XF86Suspend, Qt::Key_Suspend },
{ XKB_KEY_XF86Hibernate, Qt::Key_Hibernate },
{ XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle },
{ XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn },
{ XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff },
{ XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute },
{ XKB_KEY_XF86Launch0, Qt::Key_Launch2 },
{ XKB_KEY_XF86Launch1, Qt::Key_Launch3 },
{ XKB_KEY_XF86Launch2, Qt::Key_Launch4 },
{ XKB_KEY_XF86Launch3, Qt::Key_Launch5 },
{ XKB_KEY_XF86Launch4, Qt::Key_Launch6 },
{ XKB_KEY_XF86Launch5, Qt::Key_Launch7 },
{ XKB_KEY_XF86Launch6, Qt::Key_Launch8 },
{ XKB_KEY_XF86Launch7, Qt::Key_Launch9 },
{ XKB_KEY_XF86Launch8, Qt::Key_LaunchA },
{ XKB_KEY_XF86Launch9, Qt::Key_LaunchB },
{ XKB_KEY_XF86LaunchA, Qt::Key_LaunchC },
{ XKB_KEY_XF86LaunchB, Qt::Key_LaunchD },
{ XKB_KEY_XF86LaunchC, Qt::Key_LaunchE },
{ XKB_KEY_XF86LaunchD, Qt::Key_LaunchF }
};
static inline Qt::Key xkbToQtKey(xkb_keysym_t keySym)
{
Qt::Key key = Qt::Key_unknown;
if (keySym >= XKB_KEY_KP_0 && keySym <= XKB_KEY_KP_9) {
// numeric keypad keys
key = Qt::Key(int(Qt::Key_0) + int(keySym) - XKB_KEY_KP_0);
} else if ((keySym >= XKB_KEY_a && keySym <= XKB_KEY_z) ||
(keySym >= XKB_KEY_agrave && keySym < XKB_KEY_division) ||
(keySym > XKB_KEY_division && keySym <= XKB_KEY_thorn)) {
key = Qt::Key(QChar(keySym).toUpper().unicode());
} else if (keySym < 0x3000) {
key = Qt::Key(keySym);
}
if (key == Qt::Key_unknown) {
const auto it = s_mapping.find(keySym);
if (it != s_mapping.end()) {
key = it->second;
}
}
return key;
}
static inline bool isKeypadKey(xkb_keysym_t keySym)
{
return keySym >= XKB_KEY_KP_Space && keySym <= XKB_KEY_KP_9;
}
static inline xkb_keysym_t qtKeyToXkb(Qt::Key qtKey, Qt::KeyboardModifiers modifiers)
{
xkb_keysym_t sym = XKB_KEY_NoSymbol;
if (modifiers.testFlag(Qt::KeypadModifier) && qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9) {
sym = XKB_KEY_KP_0 + qtKey - Qt::Key_0;
} else if (qtKey < 0x1000 && !modifiers.testFlag(Qt::KeypadModifier)) {
QChar character(qtKey);
if (!modifiers.testFlag(Qt::ShiftModifier)) {
character = character.toLower();
}
sym = character.unicode();
}
if (sym == XKB_KEY_NoSymbol) {
std::vector<xkb_keysym_t> possibleMatches;
for (auto pair : s_mapping) {
if (pair.second == qtKey) {
possibleMatches.emplace_back(pair.first);
}
}
if (!possibleMatches.empty()) {
sym = possibleMatches.front();
for (auto match : possibleMatches) {
// is the current match better than existing?
if (modifiers.testFlag(Qt::KeypadModifier)) {
if (isKeypadKey(match)) {
sym = match;
}
} else {
if (isKeypadKey(sym)) {
sym = match;
}
}
}
}
}
return sym;
}
}
#endif