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.
This commit is contained in:
Timothy Redaelli 2026-09-10 13:31:35 +02:00 committed by Nick Bolton
parent d6874dfe27
commit 71929a3418
2 changed files with 23 additions and 3 deletions

View file

@ -22,6 +22,7 @@
#include "platform/PortalRemoteDesktop.h"
#include <algorithm>
#include <bit>
#include <cmath>
#include <cstdlib>
#include <cstring>
@ -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
{
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<ScrollRemainder *>(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

View file

@ -11,6 +11,7 @@
#include "deskflow/PlatformScreen.h"
#include "platform/XDGPowerManager.h"
#include <bitset>
#include <climits>
#include <libei.h>
#include <map>
@ -144,6 +145,9 @@ private:
KeyID m_lastPressed = kKeyNone;
// mouse buttons currently held, indexed by ButtonID
std::bitset<NumButtonIDs> m_buttons;
// clipboard stuff
EiClipboard *m_clipboard = nullptr;
size_t m_maximumClipboardSize = INT_MAX;