From 71929a3418da9bb0b7bd026cfb8e5e8a1fae3185 Mon Sep 17 00:00:00 2001 From: Timothy Redaelli Date: Thu, 10 Sep 2026 13:31:35 +0200 Subject: [PATCH] fix(wayland): track held mouse buttons in EiScreen isAnyMouseButtonDown() always returned false, so the server never considered the screen locked and could switch away mid-drag. Track the pressed buttons in a bitset fed by the button events. There is no way to poll them from libei, so drop the whole set whenever the state stops being trustworthy: a paused device is reset to neutral by the EIS side, a removed device sends nothing more, and releasing the input capture ends the event stream. That rule now lives in updateButtons(), which also puts EiScreen on the same resync path the other platforms already use. --- src/lib/platform/EiScreen.cpp | 22 +++++++++++++++++++--- src/lib/platform/EiScreen.h | 4 ++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/lib/platform/EiScreen.cpp b/src/lib/platform/EiScreen.cpp index e4f38f986..cac1f5493 100644 --- a/src/lib/platform/EiScreen.cpp +++ b/src/lib/platform/EiScreen.cpp @@ -22,6 +22,7 @@ #include "platform/PortalRemoteDesktop.h" #include +#include #include #include #include @@ -261,9 +262,13 @@ std::int32_t EiScreen::getJumpZoneSize() const return 1; } -bool EiScreen::isAnyMouseButtonDown(uint32_t &) const +bool EiScreen::isAnyMouseButtonDown(uint32_t &buttonID) const { - return false; + if (m_buttons.none()) + return false; + + buttonID = std::countr_zero(m_buttons.to_ulong()); + return true; } void EiScreen::getCursorCenter(int32_t &x, int32_t &y) const @@ -424,6 +429,8 @@ void EiScreen::enter() } else if (m_isPrimary) { LOG_DEBUG("releasing input capture at x=%i y=%i", m_cursorX, m_cursorY); m_portalInputCapture->release(m_cursorX, m_cursorY); + // no more button events once capture is released, so drop any held state + updateButtons(); } } @@ -631,6 +638,8 @@ void EiScreen::removeDevice(struct ei_device *device) if (wasTracked) { m_isEmulating = false; cancelIdleEmulationTimer(); + // same as for a paused device: no release events follow a removal + updateButtons(); } delete static_cast(ei_device_get_user_data(device)); @@ -757,6 +766,8 @@ void EiScreen::onButtonEvent(ei_event *event) return; } + m_buttons.set(buttonID, pressed); + auto eventType = pressed ? EventTypes::PrimaryScreenButtonDown : EventTypes::PrimaryScreenButtonUp; sendEvent(eventType, ButtonInfo::alloc(buttonID, mask)); @@ -968,6 +979,9 @@ void EiScreen::handleSystemEvent(const Event &) case EI_EVENT_DEVICE_PAUSED: LOG_DEBUG("device %s is paused", ei_device_get_name(device)); m_isEmulating = false; + // a paused device is reset to neutral by the EIS side and sends no + // further events, so the releases for held buttons never arrive + updateButtons(); cancelIdleEmulationTimer(); break; case EI_EVENT_DEVICE_RESUMED: @@ -1026,7 +1040,9 @@ void EiScreen::handleSystemEvent(const Event &) void EiScreen::updateButtons() { // libei relies on the EIS implementation to keep our button count correct, - // so there's not much we need to/can do here. + // and the held buttons cannot be polled, so resyncing means assuming that + // everything is released. + m_buttons.reset(); } IKeyState *EiScreen::getKeyState() const diff --git a/src/lib/platform/EiScreen.h b/src/lib/platform/EiScreen.h index ab7b21cd4..4a33cab4c 100644 --- a/src/lib/platform/EiScreen.h +++ b/src/lib/platform/EiScreen.h @@ -11,6 +11,7 @@ #include "deskflow/PlatformScreen.h" #include "platform/XDGPowerManager.h" +#include #include #include #include @@ -144,6 +145,9 @@ private: KeyID m_lastPressed = kKeyNone; + // mouse buttons currently held, indexed by ButtonID + std::bitset m_buttons; + // clipboard stuff EiClipboard *m_clipboard = nullptr; size_t m_maximumClipboardSize = INT_MAX;