diff --git a/src/lib/platform/EiKeyState.cpp b/src/lib/platform/EiKeyState.cpp index 1d9dcb5dc..69bf61c40 100644 --- a/src/lib/platform/EiKeyState.cpp +++ b/src/lib/platform/EiKeyState.cpp @@ -345,6 +345,20 @@ void EiKeyState::updateXkbState(uint32_t keyval, bool isPressed) xkb_state_update_key(m_xkbState, keyval, isPressed ? XKB_KEY_DOWN : XKB_KEY_UP); } +void EiKeyState::updateLockedModifiers(xkb_mod_mask_t lockedMods) +{ + // The compositor's lock state (Caps/Num/Scroll Lock) is authoritative; it + // also covers locks toggled while we weren't seeing the key events. + const auto depressedMods = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_DEPRESSED); + const auto latchedMods = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LATCHED); + const auto depressedLayout = xkb_state_serialize_layout(m_xkbState, XKB_STATE_LAYOUT_DEPRESSED); + const auto latchedLayout = xkb_state_serialize_layout(m_xkbState, XKB_STATE_LAYOUT_LATCHED); + const auto lockedLayout = xkb_state_serialize_layout(m_xkbState, XKB_STATE_LAYOUT_LOCKED); + xkb_state_update_mask( + m_xkbState, depressedMods, latchedMods, lockedMods, depressedLayout, latchedLayout, lockedLayout + ); +} + void EiKeyState::clearStaleModifiers() { const auto lockedMods = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LOCKED); diff --git a/src/lib/platform/EiKeyState.h b/src/lib/platform/EiKeyState.h index aa4005fb8..8f4b941da 100644 --- a/src/lib/platform/EiKeyState.h +++ b/src/lib/platform/EiKeyState.h @@ -35,6 +35,7 @@ public: void pollPressedKeys(KeyButtonSet &pressedKeys) const override; KeyID mapKeyFromKeyval(std::uint32_t keyval) const; void updateXkbState(std::uint32_t keyval, bool isPressed); + void updateLockedModifiers(xkb_mod_mask_t lockedMods); void clearStaleModifiers() override; protected: diff --git a/src/lib/platform/EiScreen.cpp b/src/lib/platform/EiScreen.cpp index 7e66daa13..3c423afa8 100644 --- a/src/lib/platform/EiScreen.cpp +++ b/src/lib/platform/EiScreen.cpp @@ -1025,7 +1025,8 @@ void EiScreen::handleSystemEvent(const Event &) } break; case EI_EVENT_KEYBOARD_MODIFIERS: - // FIXME + LOG_VERBOSE("event: keyboard modifiers locked=0x%x", ei_event_keyboard_get_xkb_mods_locked(event)); + m_keyState->updateLockedModifiers(ei_event_keyboard_get_xkb_mods_locked(event)); break; // events below are for a receiver context (barriers) diff --git a/src/unittests/platform/EiKeyStateTests.cpp b/src/unittests/platform/EiKeyStateTests.cpp index a58635669..fadb17fe3 100644 --- a/src/unittests/platform/EiKeyStateTests.cpp +++ b/src/unittests/platform/EiKeyStateTests.cpp @@ -102,4 +102,36 @@ void EiKeyStateTests::clearStaleModifiers_shiftDownAndNumLockOn_shiftClearedAndN QVERIFY((keyState.pollActiveModifiers() & KeyModifierNumLock) != 0); } +void EiKeyStateTests::updateLockedModifiers_compositorLockState_numLockFollowsCompositor() +{ + // Real modifier Mod2 is index 4 (Shift, Lock, Control, Mod1, Mod2, ...). + constexpr std::uint32_t xkbMod2Mask = 1 << 4; + + TestAppUtil appUtil; + EventQueue eventQueue; + deskflow::EiKeyState keyState(nullptr, &eventQueue); + + QTemporaryFile keymapFile; + QVERIFY(keymapFile.open()); + const QByteArray keymapData = QByteArray::fromRawData(TestKeymap, sizeof(TestKeymap) - 1); + QCOMPARE(keymapFile.write(keymapData), keymapData.size()); + QVERIFY(keymapFile.flush()); + keyState.init(keymapFile.handle(), keymapFile.size()); + + keyState.updateXkbState(LeftShiftKeycode, true); + QVERIFY((keyState.pollActiveModifiers() & KeyModifierNumLock) == 0); + + // NumLock toggled on locally, reported by the compositor + keyState.updateLockedModifiers(xkbMod2Mask); + QVERIFY((keyState.pollActiveModifiers() & KeyModifierNumLock) != 0); + QVERIFY((keyState.pollActiveModifiers() & KeyModifierShift) != 0); + + keyState.clearStaleModifiers(); + QVERIFY((keyState.pollActiveModifiers() & KeyModifierNumLock) != 0); + + // NumLock toggled off locally + keyState.updateLockedModifiers(0); + QVERIFY((keyState.pollActiveModifiers() & KeyModifierNumLock) == 0); +} + QTEST_MAIN(EiKeyStateTests) diff --git a/src/unittests/platform/EiKeyStateTests.h b/src/unittests/platform/EiKeyStateTests.h index 76b2756d8..8228a554b 100644 --- a/src/unittests/platform/EiKeyStateTests.h +++ b/src/unittests/platform/EiKeyStateTests.h @@ -18,6 +18,7 @@ class EiKeyStateTests : public QObject private Q_SLOTS: void initTestCase(); void clearStaleModifiers_shiftDownAndNumLockOn_shiftClearedAndNumLockPreserved(); + void updateLockedModifiers_compositorLockState_numLockFollowsCompositor(); private: Arch m_arch;