From 9699932a1281f4bb910c58c81a3d501f61c1cf2e Mon Sep 17 00:00:00 2001 From: enzious Date: Sat, 18 Oct 2025 05:03:15 -0500 Subject: [PATCH 1/2] fix: client to server modifier press/release Pressing a modifier on the client and moving to the server and releasing it breaks the modifier on the client. This resolves the issue. --- src/lib/deskflow/IKeyState.h | 10 ++++++++++ src/lib/deskflow/IPlatformScreen.h | 3 ++- src/lib/deskflow/KeyState.cpp | 1 + src/lib/deskflow/PlatformScreen.cpp | 5 +++++ src/lib/deskflow/PlatformScreen.h | 1 + src/lib/platform/EiKeyState.cpp | 9 +++++++++ src/lib/platform/EiKeyState.h | 1 + 7 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/lib/deskflow/IKeyState.h b/src/lib/deskflow/IKeyState.h index 210cb3d50..a8cad37a0 100644 --- a/src/lib/deskflow/IKeyState.h +++ b/src/lib/deskflow/IKeyState.h @@ -116,6 +116,16 @@ class IKeyState : public IInterface */ virtual void fakeAllKeysUp() = 0; + //! Clear stale modifiers + /*! + Clears stuck modifier state in platform-specific keyboard tracking (e.g. XKB). + Default implementation does nothing. + */ + virtual void clearStaleModifiers() + { + // Default implementation does nothing + } + //! Fake ctrl+alt+del /*! Synthesize a press of ctrl+alt+del. Return true if processing is diff --git a/src/lib/deskflow/IPlatformScreen.h b/src/lib/deskflow/IPlatformScreen.h index 4c66bbba0..80752102f 100644 --- a/src/lib/deskflow/IPlatformScreen.h +++ b/src/lib/deskflow/IPlatformScreen.h @@ -198,12 +198,13 @@ class IPlatformScreen : public IScreen, public IPrimaryScreen, public ISecondary virtual KeyModifierMask pollActiveModifiers() const = 0; virtual SInt32 pollActiveGroup() const = 0; virtual void pollPressedKeys(KeyButtonSet &pressedKeys) const = 0; + virtual void clearStaleModifiers() override = 0; + // Drag-and-drop overrides virtual String &getDraggingFilename() = 0; virtual void clearDraggingFilename() = 0; virtual bool isDraggingStarted() = 0; virtual bool isFakeDraggingStarted() = 0; - virtual void fakeDraggingFiles(DragFileList fileList) = 0; virtual const String &getDropTarget() const = 0; diff --git a/src/lib/deskflow/KeyState.cpp b/src/lib/deskflow/KeyState.cpp index b3dc5d914..61062642a 100644 --- a/src/lib/deskflow/KeyState.cpp +++ b/src/lib/deskflow/KeyState.cpp @@ -770,6 +770,7 @@ void KeyState::updateKeyState() } // get the current modifier state + clearStaleModifiers(); m_mask = pollActiveModifiers(); // set active modifiers diff --git a/src/lib/deskflow/PlatformScreen.cpp b/src/lib/deskflow/PlatformScreen.cpp index 7046ce300..8a0b3f02b 100644 --- a/src/lib/deskflow/PlatformScreen.cpp +++ b/src/lib/deskflow/PlatformScreen.cpp @@ -99,6 +99,11 @@ void PlatformScreen::pollPressedKeys(KeyButtonSet &pressedKeys) const getKeyState()->pollPressedKeys(pressedKeys); } +void PlatformScreen::clearStaleModifiers() +{ + getKeyState()->clearStaleModifiers(); +} + bool PlatformScreen::isDraggingStarted() { if (App::instance().argsBase().m_enableDragDrop) { diff --git a/src/lib/deskflow/PlatformScreen.h b/src/lib/deskflow/PlatformScreen.h index 69e65ffeb..5dbffa7dc 100644 --- a/src/lib/deskflow/PlatformScreen.h +++ b/src/lib/deskflow/PlatformScreen.h @@ -74,6 +74,7 @@ class PlatformScreen : public IPlatformScreen virtual KeyModifierMask pollActiveModifiers() const; virtual SInt32 pollActiveGroup() const; virtual void pollPressedKeys(KeyButtonSet &pressedKeys) const; + virtual void clearStaleModifiers() override; virtual void setDraggingStarted(bool started) { diff --git a/src/lib/platform/EiKeyState.cpp b/src/lib/platform/EiKeyState.cpp index b9a48a345..86deb1fd4 100644 --- a/src/lib/platform/EiKeyState.cpp +++ b/src/lib/platform/EiKeyState.cpp @@ -321,4 +321,13 @@ void EiKeyState::update_xkb_state(uint32_t keyval, bool is_pressed) xkb_state_update_key(xkb_state_, keyval, is_pressed ? XKB_KEY_DOWN : XKB_KEY_UP); } +void EiKeyState::clearStaleModifiers() +{ + // Recreate the XKB state to clear stuck modifiers that happen when + // modifier keys are press on client and released on server + if (xkb_state_) { + xkb_state_unref(xkb_state_); + } + xkb_state_ = xkb_state_new(xkb_keymap_); +} } // namespace deskflow diff --git a/src/lib/platform/EiKeyState.h b/src/lib/platform/EiKeyState.h index 0084ae0a0..9912f1871 100644 --- a/src/lib/platform/EiKeyState.h +++ b/src/lib/platform/EiKeyState.h @@ -46,6 +46,7 @@ class EiKeyState : public KeyState void pollPressedKeys(KeyButtonSet &pressedKeys) const override; KeyID map_key_from_keyval(std::uint32_t keyval) const; void update_xkb_state(std::uint32_t keyval, bool is_pressed); + void clearStaleModifiers() override; protected: // KeyState overrides From 05485140d184c758c4f310ec20505d3875de9ad8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 30 Oct 2025 10:22:32 +0000 Subject: [PATCH 2/2] fix: remove override specifier from clearStaleModifiers method --- src/lib/deskflow/IPlatformScreen.h | 2 +- src/lib/deskflow/PlatformScreen.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/deskflow/IPlatformScreen.h b/src/lib/deskflow/IPlatformScreen.h index 80752102f..1ca2ceea2 100644 --- a/src/lib/deskflow/IPlatformScreen.h +++ b/src/lib/deskflow/IPlatformScreen.h @@ -198,7 +198,7 @@ class IPlatformScreen : public IScreen, public IPrimaryScreen, public ISecondary virtual KeyModifierMask pollActiveModifiers() const = 0; virtual SInt32 pollActiveGroup() const = 0; virtual void pollPressedKeys(KeyButtonSet &pressedKeys) const = 0; - virtual void clearStaleModifiers() override = 0; + virtual void clearStaleModifiers() = 0; // Drag-and-drop overrides virtual String &getDraggingFilename() = 0; diff --git a/src/lib/deskflow/PlatformScreen.h b/src/lib/deskflow/PlatformScreen.h index 5dbffa7dc..f267f74a6 100644 --- a/src/lib/deskflow/PlatformScreen.h +++ b/src/lib/deskflow/PlatformScreen.h @@ -74,7 +74,7 @@ class PlatformScreen : public IPlatformScreen virtual KeyModifierMask pollActiveModifiers() const; virtual SInt32 pollActiveGroup() const; virtual void pollPressedKeys(KeyButtonSet &pressedKeys) const; - virtual void clearStaleModifiers() override; + virtual void clearStaleModifiers(); virtual void setDraggingStarted(bool started) {