Fix sticky keys for AltGr

We map AltGr to Qt::GroupSwitchModifier, but then ignore it

Instead map it to Mod5, which for some reason xkbcommon doesn't expose a define for

Also, since the Qt modifiers enum doesn't map nicely to XKB modifiers introduce our own enum to avoid confusion

CCBUG: 444335
This commit is contained in:
Nicolas Fella 2024-03-05 20:33:45 +01:00
parent 18535ea959
commit 463d77ec18
4 changed files with 76 additions and 45 deletions

View file

@ -34,6 +34,7 @@ private Q_SLOTS:
void init();
void cleanup();
void testStick();
void testStick_data();
void testLock();
};
@ -43,6 +44,12 @@ void StickyKeysTest::initTestCase()
kaccessConfig.group(QStringLiteral("Keyboard")).writeEntry("StickyKeys", true);
kaccessConfig.sync();
// Use a keyboard layout where right alt triggers Mod5/AltGr
KConfig kxkbrc("kxkbrc");
kxkbrc.group(QStringLiteral("Layout")).writeEntry("LayoutList", "us");
kxkbrc.group(QStringLiteral("Layout")).writeEntry("VariantList", "altgr-intl");
kxkbrc.sync();
qRegisterMetaType<KWin::Window *>();
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
QVERIFY(waylandServer()->init(s_socketName));
@ -68,8 +75,22 @@ void StickyKeysTest::cleanup()
Test::destroyWaylandConnection();
}
void StickyKeysTest::testStick_data()
{
QTest::addColumn<int>("modifierKey");
QTest::addColumn<int>("expectedMods");
QTest::addRow("Shift") << KEY_LEFTSHIFT << 1;
QTest::addRow("Ctrl") << KEY_LEFTCTRL << 4;
QTest::addRow("Alt") << KEY_LEFTALT << 8;
QTest::addRow("AltGr") << KEY_RIGHTALT << 128;
}
void StickyKeysTest::testStick()
{
QFETCH(int, modifierKey);
QFETCH(int, expectedMods);
std::unique_ptr<KWayland::Client::Keyboard> keyboard(Test::waylandSeat()->createKeyboard());
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
@ -85,26 +106,26 @@ void StickyKeysTest::testStick()
quint32 timestamp = 0;
// press Ctrl to latch it
Test::keyboardKeyPressed(KEY_LEFTCTRL, ++timestamp);
// press mod to latch it
Test::keyboardKeyPressed(modifierKey, ++timestamp);
QVERIFY(modifierSpy.wait());
// arguments are: quint32 depressed, quint32 latched, quint32 locked, quint32 group
QCOMPARE(modifierSpy.first()[0], 4); // verify that Ctrl is depressed
QCOMPARE(modifierSpy.first()[1], 4); // verify that Ctrl is latched
QCOMPARE(modifierSpy.first()[0], expectedMods); // verify that mod is depressed
QCOMPARE(modifierSpy.first()[1], expectedMods); // verify that mod is latched
modifierSpy.clear();
// release Ctrl, the modified should still be latched
Test::keyboardKeyReleased(KEY_LEFTCTRL, ++timestamp);
// release mod, the modified should still be latched
Test::keyboardKeyReleased(modifierKey, ++timestamp);
QVERIFY(modifierSpy.wait());
QCOMPARE(modifierSpy.first()[0], 0); // verify that Ctrl is not depressed
QCOMPARE(modifierSpy.first()[1], 4); // verify that Ctrl is still latched
QCOMPARE(modifierSpy.first()[0], 0); // verify that mod is not depressed
QCOMPARE(modifierSpy.first()[1], expectedMods); // verify that mod is still latched
// press and release a letter, this unlatches the modifier
modifierSpy.clear();
Test::keyboardKeyPressed(KEY_A, ++timestamp);
QVERIFY(modifierSpy.wait());
QCOMPARE(modifierSpy.first()[0], 0); // verify that Ctrl is not depressed
QCOMPARE(modifierSpy.first()[1], 0); // verify that Ctrl is not latched any more
QCOMPARE(modifierSpy.first()[0], 0); // verify that mod is not depressed
QCOMPARE(modifierSpy.first()[1], 0); // verify that mod is not latched any more
Test::keyboardKeyReleased(KEY_A, ++timestamp);
}

View file

@ -24,21 +24,21 @@ StickyKeysFilter::StickyKeysFilter()
}
}
Qt::KeyboardModifier keyToModifier(Qt::Key key)
KWin::Xkb::Modifier keyToModifier(Qt::Key key)
{
if (key == Qt::Key_Shift) {
return Qt::ShiftModifier;
return KWin::Xkb::Shift;
} else if (key == Qt::Key_Alt) {
return Qt::AltModifier;
return KWin::Xkb::Mod1;
} else if (key == Qt::Key_Control) {
return Qt::ControlModifier;
return KWin::Xkb::Control;
} else if (key == Qt::Key_AltGr) {
return Qt::GroupSwitchModifier;
return KWin::Xkb::Mod5;
} else if (key == Qt::Key_Meta) {
return Qt::MetaModifier;
return KWin::Xkb::Mod4;
}
return Qt::NoModifier;
return KWin::Xkb::NoModifier;
}
void StickyKeysFilter::loadConfig(const KConfigGroup &group)

View file

@ -658,6 +658,7 @@ void Xkb::updateKeymap(xkb_keymap *keymap)
m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT);
m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO);
m_numModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_NUM);
m_mod5Modifier = xkb_keymap_mod_get_index(m_keymap, "Mod5");
m_numLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_NUM);
m_capsLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_CAPS);
@ -1001,41 +1002,39 @@ bool Xkb::switchToLayout(xkb_layout_index_t layout)
return true;
}
void Xkb::setModifierLatched(Qt::KeyboardModifier mod, bool latched)
void Xkb::setModifierLatched(KWin::Xkb::Modifier mod, bool latched)
{
xkb_mod_index_t modifier = XKB_MOD_INVALID;
switch (mod) {
case Qt::NoModifier: {
case NoModifier: {
break;
}
case Qt::ShiftModifier: {
case Shift: {
modifier = m_shiftModifier;
break;
}
case Qt::AltModifier: {
case Mod1: {
modifier = m_altModifier;
break;
}
case Qt::ControlModifier: {
case Control: {
modifier = m_controlModifier;
break;
}
case Qt::MetaModifier: {
case Mod4: {
modifier = m_metaModifier;
break;
}
case Qt::GroupSwitchModifier: {
// TODO
case Mod5: {
modifier = m_mod5Modifier;
break;
}
case Qt::KeypadModifier: {
case Mod2:
case Mod3:
case Lock:
break;
}
case Qt::KeyboardModifierMask: {
break;
}
}
if (modifier != XKB_MOD_INVALID) {
std::bitset<sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.latched};
@ -1048,41 +1047,39 @@ void Xkb::setModifierLatched(Qt::KeyboardModifier mod, bool latched)
}
}
void Xkb::setModifierLocked(Qt::KeyboardModifier mod, bool locked)
void Xkb::setModifierLocked(KWin::Xkb::Modifier mod, bool locked)
{
xkb_mod_index_t modifier = XKB_MOD_INVALID;
switch (mod) {
case Qt::NoModifier: {
case NoModifier: {
break;
}
case Qt::ShiftModifier: {
case Shift: {
modifier = m_shiftModifier;
break;
}
case Qt::AltModifier: {
case Mod1: {
modifier = m_altModifier;
break;
}
case Qt::ControlModifier: {
case Control: {
modifier = m_controlModifier;
break;
}
case Qt::MetaModifier: {
case Mod4: {
modifier = m_metaModifier;
break;
}
case Qt::GroupSwitchModifier: {
// TODO
case Mod5: {
modifier = m_mod5Modifier;
break;
}
case Qt::KeypadModifier: {
case Mod2:
case Mod3:
case Lock:
break;
}
case Qt::KeyboardModifierMask: {
break;
}
}
if (modifier != XKB_MOD_INVALID) {
std::bitset<sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.locked};

View file

@ -39,6 +39,18 @@ class KWIN_EXPORT Xkb : public QObject
{
Q_OBJECT
public:
enum Modifier {
NoModifier,
Shift,
Lock,
Control,
Mod1,
Mod2,
Mod3,
Mod4,
Mod5,
};
Xkb(bool followLocale1 = false);
~Xkb() override;
void setConfig(const KSharedConfigPtr &config);
@ -64,8 +76,8 @@ public:
void switchToPreviousLayout();
bool switchToLayout(xkb_layout_index_t layout);
void setModifierLatched(Qt::KeyboardModifier mod, bool latched);
void setModifierLocked(Qt::KeyboardModifier mod, bool locked);
void setModifierLatched(KWin::Xkb::Modifier mod, bool latched);
void setModifierLocked(KWin::Xkb::Modifier mod, bool locked);
LEDs leds() const
{
@ -141,6 +153,7 @@ private:
xkb_mod_index_t m_altModifier;
xkb_mod_index_t m_metaModifier;
xkb_mod_index_t m_numModifier;
xkb_mod_index_t m_mod5Modifier;
xkb_led_index_t m_numLock;
xkb_led_index_t m_capsLock;
xkb_led_index_t m_scrollLock;