Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 42 additions & 12 deletions src/client/input/StandaloneInputBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "StandaloneInputBackend.h"
#include "StandaloneInputDevice.h"
#include <cerrno>
#include <libevdev-cpp/Device.h>
#include <libevdev-cpp/UInputDevice.h>
#include <libinput-cpp/Device.h>
Expand All @@ -26,6 +27,7 @@
#include <libinput-cpp/KeyboardEvent.h>
#include <libinput-cpp/PointerEvent.h>
#include <libinput-cpp/TouchEvent.h>
#include <libinputactions/globals.h>
#include <libinputactions/input/devices/InputDeviceState.h>
#include <libinputactions/input/events.h>
#include <libudev-cpp/Device.h>
Expand Down Expand Up @@ -94,14 +96,9 @@ void StandaloneInputBackend::reset()
m_virtualKeyboard.reset();
m_virtualMouse.reset();

for (const auto &device : m_devices) {
if (device->properties().grab()) {
// The compositor will take long enough to detect device removal that it will start generating key repeat events, reset the device to prevent that
device->resetVirtualDeviceState();
}
removeDevice(device.get());
while (!m_devices.empty()) {
removeDeviceAt(m_devices.begin());
}
m_devices.clear();
m_udevMonitor.reset();
m_deviceInitializationRetryTimer.stop();
LibinputInputBackend::reset();
Expand All @@ -113,6 +110,10 @@ void StandaloneInputBackend::evdevDeviceAdded(const QString &path)
return;
}

// A node can only be created at a path after the previous one was removed, so any device still held for this path refers to a dead node (the remove
// event may have been missed, e.g. if the udev monitor socket overflowed)
evdevDeviceRemoved(path);

if (!tryAddEvdevDevice(path)) {
m_deviceInitializationQueue[path] = 0;
}
Expand All @@ -121,7 +122,8 @@ void StandaloneInputBackend::evdevDeviceAdded(const QString &path)
bool StandaloneInputBackend::tryAddEvdevDevice(const QString &path)
{
for (const auto &device : m_devices) {
if (path == m_virtualKeyboard->path() || path == m_virtualMouse->path() || device->isDeviceOwnedByThisDevice(path)) {
// Never hold two devices for one path
if (path == m_virtualKeyboard->path() || path == m_virtualMouse->path() || device->path() == path || device->isDeviceOwnedByThisDevice(path)) {
return true;
}
}
Expand Down Expand Up @@ -153,13 +155,25 @@ bool StandaloneInputBackend::tryAddEvdevDevice(const QString &path)

void StandaloneInputBackend::evdevDeviceRemoved(const QString &path)
{
m_deviceInitializationQueue.erase(path);

auto it = std::ranges::find_if(m_devices, [&path](const auto &device) {
return device->path() == path;
});
if (it != m_devices.end()) {
removeDevice(it->get());
m_devices.erase(it);
removeDeviceAt(it);
}
}

StandaloneInputBackend::DeviceList::iterator StandaloneInputBackend::removeDeviceAt(DeviceList::iterator it)
{
const auto &device = *it;
if (device->properties().grab()) {
// The compositor will take long enough to detect device removal that it will start generating key repeat events, reset the device to prevent that
device->resetVirtualDeviceState();
}
removeDevice(device.get());
return m_devices.erase(it);
}

void StandaloneInputBackend::deviceInitializationRetryTimerTick()
Expand All @@ -180,7 +194,8 @@ void StandaloneInputBackend::deviceInitializationRetryTimerTick()
const auto &device = it->get();

if (!device->isLibinputEventInjectionDeviceInitialized() && device->tryInitializeLibinputEventInjectionDevice() == MAX_INITIALIZATION_ATTEMPTS) {
it = m_devices.erase(it);
qWarning(INPUTACTIONS, "Failed to add device \"%s\" to libinput, removing it", device->path().toStdString().c_str());
it = removeDeviceAt(it);
continue;
}

Expand Down Expand Up @@ -312,18 +327,26 @@ LibinputEventsProcessingResult StandaloneInputBackend::handleLibinputEvents(Stan
void StandaloneInputBackend::poll()
{
std::vector<EvdevEvent> frame;
for (const auto &device : m_devices) {
for (auto it = m_devices.begin(); it != m_devices.end();) {
const auto &device = *it;
if (!device->properties().grab()) {
handleLibinputEvents(device.get(), device->libinput());
++it;
continue;
}

int status{};
bool removed{};
while (true) {
input_event evdevEvent{};
auto flags = status == LIBEVDEV_READ_STATUS_SYNC ? LIBEVDEV_READ_FLAG_SYNC : LIBEVDEV_READ_FLAG_NORMAL;
status = device->libevdev()->nextEvent(flags, evdevEvent);
if (status != LIBEVDEV_READ_STATUS_SUCCESS && status != LIBEVDEV_READ_STATUS_SYNC) {
if (status == -ENODEV) {
qWarning(INPUTACTIONS, "Device \"%s\" is gone, removing it", device->path().toStdString().c_str());
removed = true;
break;
}
// Handle events generated after a delay, e.g. pointer button after tapping
handleLibinputEvents(device.get(), device->libinput());
break;
Expand Down Expand Up @@ -373,10 +396,17 @@ void StandaloneInputBackend::poll()
frame.clear();
}

if (removed) {
frame.clear();
it = removeDeviceAt(it);
continue;
}

if (device->type() == InputDeviceType::Touchpad && device->physicalState().validTouchPoints().empty()) {
device->setTouchpadNeutral(true);
device->setTouchpadBlocked(false);
}
++it;
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/client/input/StandaloneInputBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ private slots:
void deviceInitializationRetryTimerTick();

private:
using DeviceList = std::vector<std::unique_ptr<StandaloneInputDevice>>;

void poll();

void evdevDeviceAdded(const QString &path);
Expand All @@ -71,6 +73,11 @@ private slots:
*/
bool tryAddEvdevDevice(const QString &path);
void evdevDeviceRemoved(const QString &path);
/**
* Removes the device and erases it from m_devices.
* @return Iterator following the removed device.
*/
DeviceList::iterator removeDeviceAt(DeviceList::iterator it);

bool handleEvent(StandaloneInputDevice *sender, const libinput::Event &event);
LibinputEventsProcessingResult handleLibinputEvents(StandaloneInputDevice *device, libinput::PathContext *libinputContext);
Expand All @@ -85,7 +92,7 @@ private slots:
std::map<QString, uint32_t> m_deviceInitializationQueue;
QTimer m_deviceInitializationRetryTimer;

std::vector<std::unique_ptr<StandaloneInputDevice>> m_devices;
DeviceList m_devices;
std::optional<EvdevVirtualKeyboard> m_virtualKeyboard;
std::optional<EvdevVirtualMouse> m_virtualMouse;
};
Expand Down
Loading