fix(wayland): sync locked modifiers from EI keyboard modifiers event

fixes: #8437
This commit is contained in:
OceanMan20 2026-09-15 10:23:40 -07:00 committed by Nick Bolton
parent 23b0d7792d
commit ac475f0b1b
5 changed files with 50 additions and 1 deletions

View file

@ -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); 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() void EiKeyState::clearStaleModifiers()
{ {
const auto lockedMods = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LOCKED); const auto lockedMods = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LOCKED);

View file

@ -35,6 +35,7 @@ public:
void pollPressedKeys(KeyButtonSet &pressedKeys) const override; void pollPressedKeys(KeyButtonSet &pressedKeys) const override;
KeyID mapKeyFromKeyval(std::uint32_t keyval) const; KeyID mapKeyFromKeyval(std::uint32_t keyval) const;
void updateXkbState(std::uint32_t keyval, bool isPressed); void updateXkbState(std::uint32_t keyval, bool isPressed);
void updateLockedModifiers(xkb_mod_mask_t lockedMods);
void clearStaleModifiers() override; void clearStaleModifiers() override;
protected: protected:

View file

@ -1025,7 +1025,8 @@ void EiScreen::handleSystemEvent(const Event &)
} }
break; break;
case EI_EVENT_KEYBOARD_MODIFIERS: 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; break;
// events below are for a receiver context (barriers) // events below are for a receiver context (barriers)

View file

@ -102,4 +102,36 @@ void EiKeyStateTests::clearStaleModifiers_shiftDownAndNumLockOn_shiftClearedAndN
QVERIFY((keyState.pollActiveModifiers() & KeyModifierNumLock) != 0); 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) QTEST_MAIN(EiKeyStateTests)

View file

@ -18,6 +18,7 @@ class EiKeyStateTests : public QObject
private Q_SLOTS: private Q_SLOTS:
void initTestCase(); void initTestCase();
void clearStaleModifiers_shiftDownAndNumLockOn_shiftClearedAndNumLockPreserved(); void clearStaleModifiers_shiftDownAndNumLockOn_shiftClearedAndNumLockPreserved();
void updateLockedModifiers_compositorLockState_numLockFollowsCompositor();
private: private:
Arch m_arch; Arch m_arch;