Merge pull request #8473 from jordan-woyak/evdev-combine
ControllerInterface: Combine evdev devices with the same unique ID.
This commit is contained in:
commit
d5dcd91c94
|
@ -4,14 +4,14 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <libudev.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <libudev.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/Flag.h"
|
||||
|
@ -35,42 +35,80 @@ protected:
|
|||
libevdev* const m_dev;
|
||||
};
|
||||
|
||||
class Button final : public Input
|
||||
class Button : public Input
|
||||
{
|
||||
public:
|
||||
Button(u8 index, u16 code, libevdev* dev) : Input(code, dev), m_index(index) {}
|
||||
|
||||
std::string GetName() const override
|
||||
{
|
||||
// Buttons below 0x100 are mostly keyboard keys, and the names make sense
|
||||
if (m_code < 0x100)
|
||||
{
|
||||
const char* name = libevdev_event_code_get_name(EV_KEY, m_code);
|
||||
if (name)
|
||||
return std::string(StripSpaces(name));
|
||||
}
|
||||
// But controllers use codes above 0x100, and the standard label often doesn't match.
|
||||
// We are better off with Button 0 and so on.
|
||||
return "Button " + std::to_string(m_index);
|
||||
}
|
||||
|
||||
ControlState GetState() const override
|
||||
ControlState GetState() const final override
|
||||
{
|
||||
int value = 0;
|
||||
libevdev_fetch_event_value(m_dev, EV_KEY, m_code, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::optional<std::string> GetEventCodeName() const
|
||||
{
|
||||
if (const char* code_name = libevdev_event_code_get_name(EV_KEY, m_code))
|
||||
{
|
||||
const auto name = StripSpaces(code_name);
|
||||
|
||||
for (auto remove_prefix : {"BTN_", "KEY_"})
|
||||
{
|
||||
if (name.find(remove_prefix) == 0)
|
||||
return std::string(name.substr(std::strlen(remove_prefix)));
|
||||
}
|
||||
|
||||
return std::string(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetIndexedName() const { return "Button " + std::to_string(m_index); }
|
||||
|
||||
const u8 m_index;
|
||||
};
|
||||
|
||||
class NumberedButton final : public Button
|
||||
{
|
||||
public:
|
||||
using Button::Button;
|
||||
|
||||
std::string GetName() const override { return GetIndexedName(); }
|
||||
};
|
||||
|
||||
class NamedButton final : public Button
|
||||
{
|
||||
public:
|
||||
using Button::Button;
|
||||
|
||||
bool IsMatchingName(std::string_view name) const final override
|
||||
{
|
||||
// Match either the "START" naming provided by evdev or the "Button 0"-like naming.
|
||||
return name == GetEventCodeName() || name == GetIndexedName();
|
||||
}
|
||||
|
||||
std::string GetName() const override { return GetEventCodeName().value_or(GetIndexedName()); }
|
||||
};
|
||||
|
||||
class NamedButtonWithNoBackwardsCompat final : public Button
|
||||
{
|
||||
public:
|
||||
using Button::Button;
|
||||
|
||||
std::string GetName() const override { return GetEventCodeName().value_or(GetIndexedName()); }
|
||||
};
|
||||
|
||||
class AnalogInput : public Input
|
||||
{
|
||||
public:
|
||||
using Input::Input;
|
||||
|
||||
ControlState GetState() const override
|
||||
ControlState GetState() const final override
|
||||
{
|
||||
int value = 0;
|
||||
libevdev_fetch_event_value(m_dev, EV_ABS, m_code, &value);
|
||||
|
@ -83,7 +121,7 @@ protected:
|
|||
int m_base;
|
||||
};
|
||||
|
||||
class Axis final : public AnalogInput
|
||||
class Axis : public AnalogInput
|
||||
{
|
||||
public:
|
||||
Axis(u8 index, u16 code, bool upper, libevdev* dev) : AnalogInput(code, dev), m_index(index)
|
||||
|
@ -95,7 +133,10 @@ public:
|
|||
m_range = (upper ? max : min) - m_base;
|
||||
}
|
||||
|
||||
std::string GetName() const override
|
||||
std::string GetName() const override { return GetIndexedName(); }
|
||||
|
||||
protected:
|
||||
std::string GetIndexedName() const
|
||||
{
|
||||
return "Axis " + std::to_string(m_index) + (m_range < 0 ? '-' : '+');
|
||||
}
|
||||
|
@ -140,6 +181,20 @@ public:
|
|||
bool IsDetectable() override { return false; }
|
||||
};
|
||||
|
||||
class CursorInput final : public Axis
|
||||
{
|
||||
public:
|
||||
using Axis::Axis;
|
||||
|
||||
std::string GetName() const final override
|
||||
{
|
||||
// "Cursor X-" naming.
|
||||
return std::string("Cursor ") + char('X' + m_code) + (m_range < 0 ? '-' : '+');
|
||||
}
|
||||
|
||||
bool IsDetectable() override { return false; }
|
||||
};
|
||||
|
||||
static std::thread s_hotplug_thread;
|
||||
static Common::Flag s_hotplug_thread_running;
|
||||
static int s_wakeup_eventfd;
|
||||
|
@ -147,7 +202,75 @@ static int s_wakeup_eventfd;
|
|||
// There is no easy way to get the device name from only a dev node
|
||||
// during a device removed event, since libevdev can't work on removed devices;
|
||||
// sysfs is not stable, so this is probably the easiest way to get a name for a node.
|
||||
static std::map<std::string, std::string> s_devnode_name_map;
|
||||
static std::map<std::string, std::weak_ptr<evdevDevice>> s_devnode_objects;
|
||||
|
||||
std::shared_ptr<evdevDevice> FindDeviceWithUniqueID(const char* unique_id)
|
||||
{
|
||||
if (!unique_id)
|
||||
return nullptr;
|
||||
|
||||
for (auto& [node, dev] : s_devnode_objects)
|
||||
{
|
||||
if (const auto device = dev.lock())
|
||||
{
|
||||
const auto* dev_uniq = device->GetUniqueID();
|
||||
|
||||
if (dev_uniq && std::strcmp(dev_uniq, unique_id) == 0)
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AddDeviceNode(const char* devnode)
|
||||
{
|
||||
// Unfortunately udev gives us no way to filter out the non event device interfaces.
|
||||
// So we open it and see if it works with evdev ioctls or not.
|
||||
|
||||
// The device file will be read on one of the main threads, so we open in non-blocking mode.
|
||||
const int fd = open(devnode, O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
libevdev* dev = nullptr;
|
||||
if (libevdev_new_from_fd(fd, &dev) != 0)
|
||||
{
|
||||
// This usually fails because the device node isn't an evdev device, such as /dev/input/js0
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto uniq = libevdev_get_uniq(dev);
|
||||
auto evdev_device = FindDeviceWithUniqueID(uniq);
|
||||
if (evdev_device)
|
||||
{
|
||||
NOTICE_LOG(SERIALINTERFACE, "evdev combining devices with unique id: %s", uniq);
|
||||
|
||||
evdev_device->AddNode(devnode, fd, dev);
|
||||
|
||||
// Remove and re-add device as naming and inputs may have changed.
|
||||
// This will also give it the correct index and invoke device change callbacks.
|
||||
g_controller_interface.RemoveDevice([&evdev_device](const auto* device) {
|
||||
return static_cast<const evdevDevice*>(device) == evdev_device.get();
|
||||
});
|
||||
|
||||
g_controller_interface.AddDevice(evdev_device);
|
||||
}
|
||||
else
|
||||
{
|
||||
evdev_device = std::make_shared<evdevDevice>();
|
||||
|
||||
const bool was_interesting = evdev_device->AddNode(devnode, fd, dev);
|
||||
|
||||
if (was_interesting)
|
||||
g_controller_interface.AddDevice(evdev_device);
|
||||
}
|
||||
|
||||
s_devnode_objects.emplace(devnode, std::move(evdev_device));
|
||||
}
|
||||
|
||||
static void HotplugThreadFunc()
|
||||
{
|
||||
|
@ -190,28 +313,21 @@ static void HotplugThreadFunc()
|
|||
|
||||
if (strcmp(action, "remove") == 0)
|
||||
{
|
||||
const auto it = s_devnode_name_map.find(devnode);
|
||||
if (it == s_devnode_name_map.end())
|
||||
{
|
||||
// We don't know the name for this device, so it is probably not an evdev device.
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<evdevDevice> ptr;
|
||||
|
||||
const std::string& name = it->second;
|
||||
g_controller_interface.RemoveDevice([&name](const auto& device) {
|
||||
return device->GetSource() == "evdev" && device->GetName() == name && !device->IsValid();
|
||||
const auto it = s_devnode_objects.find(devnode);
|
||||
if (it != s_devnode_objects.end())
|
||||
ptr = it->second.lock();
|
||||
|
||||
// If we don't recognize this device, ptr will be null and no device will be removed.
|
||||
|
||||
g_controller_interface.RemoveDevice([&ptr](const auto* device) {
|
||||
return static_cast<const evdevDevice*>(device) == ptr.get();
|
||||
});
|
||||
|
||||
s_devnode_name_map.erase(devnode);
|
||||
}
|
||||
else if (strcmp(action, "add") == 0)
|
||||
{
|
||||
const auto device = std::make_shared<evdevDevice>(devnode);
|
||||
if (device->IsInteresting())
|
||||
{
|
||||
s_devnode_name_map.emplace(devnode, device->GetName());
|
||||
g_controller_interface.AddDevice(std::move(device));
|
||||
}
|
||||
AddDeviceNode(devnode);
|
||||
}
|
||||
}
|
||||
NOTICE_LOG(SERIALINTERFACE, "evdev hotplug thread stopped");
|
||||
|
@ -250,7 +366,6 @@ static void StopHotplugThread()
|
|||
|
||||
void Init()
|
||||
{
|
||||
s_devnode_name_map.clear();
|
||||
StartHotplugThread();
|
||||
}
|
||||
|
||||
|
@ -277,19 +392,9 @@ void PopulateDevices()
|
|||
|
||||
udev_device* dev = udev_device_new_from_syspath(udev, path);
|
||||
|
||||
const char* devnode = udev_device_get_devnode(dev);
|
||||
if (devnode)
|
||||
{
|
||||
// Unfortunately udev gives us no way to filter out the non event device interfaces.
|
||||
// So we open it and see if it works with evdev ioctls or not.
|
||||
const auto input = std::make_shared<evdevDevice>(devnode);
|
||||
if (const char* devnode = udev_device_get_devnode(dev))
|
||||
AddDeviceNode(devnode);
|
||||
|
||||
if (input->IsInteresting())
|
||||
{
|
||||
s_devnode_name_map.emplace(devnode, input->GetName());
|
||||
g_controller_interface.AddDevice(std::move(input));
|
||||
}
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
udev_enumerate_unref(enumerate);
|
||||
|
@ -301,50 +406,64 @@ void Shutdown()
|
|||
StopHotplugThread();
|
||||
}
|
||||
|
||||
evdevDevice::evdevDevice(const std::string& devnode) : m_devfile(devnode)
|
||||
bool evdevDevice::AddNode(std::string devnode, int fd, libevdev* dev)
|
||||
{
|
||||
// The device file will be read on one of the main threads, so we open in non-blocking mode.
|
||||
m_fd = open(devnode.c_str(), O_RDWR | O_NONBLOCK);
|
||||
if (m_fd == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_nodes.emplace_back(Node{std::move(devnode), fd, dev});
|
||||
|
||||
if (libevdev_new_from_fd(m_fd, &m_dev) != 0)
|
||||
{
|
||||
// This usually fails because the device node isn't an evdev device, such as /dev/input/js0
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
return;
|
||||
}
|
||||
// Take on the alphabetically first name.
|
||||
const auto potential_new_name = StripSpaces(libevdev_get_name(dev));
|
||||
if (m_name.empty() || potential_new_name < m_name)
|
||||
m_name = potential_new_name;
|
||||
|
||||
m_name = StripSpaces(libevdev_get_name(m_dev));
|
||||
const bool is_motion_device = libevdev_has_property(dev, INPUT_PROP_ACCELEROMETER);
|
||||
const bool is_pointing_device = libevdev_has_property(dev, INPUT_PROP_BUTTONPAD);
|
||||
|
||||
// If a device has BTN_JOYSTICK it probably uses event codes counting up from 0x120
|
||||
// which have very useless and wrong names.
|
||||
const bool has_btn_joystick = libevdev_has_event_code(dev, EV_KEY, BTN_JOYSTICK);
|
||||
const bool has_sensible_button_names = !has_btn_joystick;
|
||||
|
||||
// Buttons (and keyboard keys)
|
||||
int num_buttons = 0;
|
||||
for (int key = 0; key < KEY_MAX; key++)
|
||||
for (int key = 0; key != KEY_CNT; ++key)
|
||||
{
|
||||
if (libevdev_has_event_code(m_dev, EV_KEY, key))
|
||||
AddInput(new Button(num_buttons++, key, m_dev));
|
||||
if (libevdev_has_event_code(dev, EV_KEY, key))
|
||||
{
|
||||
if (is_pointing_device || is_motion_device)
|
||||
{
|
||||
// This node will probably be combined with another with regular buttons.
|
||||
// We don't want to match "Button 0" names here as it will name clash.
|
||||
AddInput(new NamedButtonWithNoBackwardsCompat(num_buttons, key, dev));
|
||||
}
|
||||
else if (has_sensible_button_names)
|
||||
{
|
||||
AddInput(new NamedButton(num_buttons, key, dev));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddInput(new NumberedButton(num_buttons, key, dev));
|
||||
}
|
||||
|
||||
++num_buttons;
|
||||
}
|
||||
}
|
||||
|
||||
int first_axis_code = 0;
|
||||
int num_axis = 0;
|
||||
|
||||
int num_motion_axis = 0;
|
||||
if (libevdev_has_property(m_dev, INPUT_PROP_ACCELEROMETER))
|
||||
if (is_motion_device)
|
||||
{
|
||||
// If INPUT_PROP_ACCELEROMETER is set then X,Y,Z,RX,RY,RZ contain motion data.
|
||||
|
||||
auto add_motion_inputs = [&num_motion_axis, this](int first_code, double scale) {
|
||||
auto add_motion_inputs = [&num_axis, dev, this](int first_code, double scale) {
|
||||
for (int i = 0; i != 3; ++i)
|
||||
{
|
||||
const int code = first_code + i;
|
||||
if (libevdev_has_event_code(m_dev, EV_ABS, code))
|
||||
if (libevdev_has_event_code(dev, EV_ABS, code))
|
||||
{
|
||||
AddInput(new MotionDataInput(code, scale * -1, m_dev));
|
||||
AddInput(new MotionDataInput(code, scale, m_dev));
|
||||
AddInput(new MotionDataInput(code, scale * -1, dev));
|
||||
AddInput(new MotionDataInput(code, scale, dev));
|
||||
|
||||
++num_motion_axis;
|
||||
++num_axis;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -357,61 +476,78 @@ evdevDevice::evdevDevice(const std::string& devnode) : m_devfile(devnode)
|
|||
add_motion_inputs(ABS_X, accel_scale);
|
||||
add_motion_inputs(ABS_RX, gyro_scale);
|
||||
|
||||
// evdev says regular axes should not be mixed with motion data,
|
||||
// but we'll keep looking for regular axes after RZ just in case.
|
||||
first_axis_code = ABS_RZ + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Absolute axis (thumbsticks)
|
||||
int num_axis = 0;
|
||||
for (int axis = first_axis_code; axis != ABS_CNT; ++axis)
|
||||
if (is_pointing_device)
|
||||
{
|
||||
if (libevdev_has_event_code(m_dev, EV_ABS, axis))
|
||||
auto add_cursor_input = [&num_axis, dev, this](int code) {
|
||||
if (libevdev_has_event_code(dev, EV_ABS, code))
|
||||
{
|
||||
AddInput(new CursorInput(num_axis, code, false, dev));
|
||||
AddInput(new CursorInput(num_axis, code, true, dev));
|
||||
|
||||
++num_axis;
|
||||
}
|
||||
};
|
||||
|
||||
add_cursor_input(ABS_X);
|
||||
add_cursor_input(ABS_Y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Axes beyond ABS_MISC have strange behavior (for multi-touch) which we do not handle.
|
||||
const int abs_axis_code_count = ABS_MISC;
|
||||
|
||||
// Absolute axis (thumbsticks)
|
||||
for (int axis = 0; axis != abs_axis_code_count; ++axis)
|
||||
{
|
||||
if (libevdev_has_event_code(dev, EV_ABS, axis))
|
||||
{
|
||||
AddAnalogInputs(new Axis(num_axis, axis, false, m_dev),
|
||||
new Axis(num_axis, axis, true, m_dev));
|
||||
AddAnalogInputs(new Axis(num_axis, axis, false, dev), new Axis(num_axis, axis, true, dev));
|
||||
++num_axis;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable autocenter
|
||||
if (libevdev_has_event_code(m_dev, EV_FF, FF_AUTOCENTER))
|
||||
if (libevdev_has_event_code(dev, EV_FF, FF_AUTOCENTER))
|
||||
{
|
||||
input_event ie = {};
|
||||
ie.type = EV_FF;
|
||||
ie.code = FF_AUTOCENTER;
|
||||
ie.value = 0;
|
||||
|
||||
static_cast<void>(write(m_fd, &ie, sizeof(ie)));
|
||||
static_cast<void>(write(fd, &ie, sizeof(ie)));
|
||||
}
|
||||
|
||||
// Constant FF effect
|
||||
if (libevdev_has_event_code(m_dev, EV_FF, FF_CONSTANT))
|
||||
if (libevdev_has_event_code(dev, EV_FF, FF_CONSTANT))
|
||||
{
|
||||
AddOutput(new ConstantEffect(m_fd));
|
||||
AddOutput(new ConstantEffect(fd));
|
||||
}
|
||||
|
||||
// Periodic FF effects
|
||||
if (libevdev_has_event_code(m_dev, EV_FF, FF_PERIODIC))
|
||||
if (libevdev_has_event_code(dev, EV_FF, FF_PERIODIC))
|
||||
{
|
||||
for (auto wave : {FF_SINE, FF_SQUARE, FF_TRIANGLE, FF_SAW_UP, FF_SAW_DOWN})
|
||||
{
|
||||
if (libevdev_has_event_code(m_dev, EV_FF, wave))
|
||||
AddOutput(new PeriodicEffect(m_fd, wave));
|
||||
if (libevdev_has_event_code(dev, EV_FF, wave))
|
||||
AddOutput(new PeriodicEffect(fd, wave));
|
||||
}
|
||||
}
|
||||
|
||||
// Rumble (i.e. Left/Right) (i.e. Strong/Weak) effect
|
||||
if (libevdev_has_event_code(m_dev, EV_FF, FF_RUMBLE))
|
||||
if (libevdev_has_event_code(dev, EV_FF, FF_RUMBLE))
|
||||
{
|
||||
AddOutput(new RumbleEffect(m_fd, RumbleEffect::Motor::Strong));
|
||||
AddOutput(new RumbleEffect(m_fd, RumbleEffect::Motor::Weak));
|
||||
AddOutput(new RumbleEffect(fd, RumbleEffect::Motor::Strong));
|
||||
AddOutput(new RumbleEffect(fd, RumbleEffect::Motor::Weak));
|
||||
}
|
||||
|
||||
// TODO: Add leds as output devices
|
||||
|
||||
// Filter out interesting devices (see description below)
|
||||
m_interesting = num_motion_axis != 0 || num_axis >= 2 || num_buttons >= 8;
|
||||
return num_axis >= 2 || num_buttons >= 8;
|
||||
|
||||
// On modern linux systems, there are a lot of event devices that aren't controllers.
|
||||
// For example, the PC Speaker is an event device. Webcams sometimes show up as
|
||||
|
@ -434,23 +570,32 @@ evdevDevice::evdevDevice(const std::string& devnode) : m_devfile(devnode)
|
|||
// with 5 or 6 special buttons, which is why the threshold is set to 8 to
|
||||
// match a NES controller.
|
||||
//
|
||||
// --- OR ---
|
||||
//
|
||||
// Any Motion Axis:
|
||||
// This rule is to catch any theoretical motion controllers with only a few
|
||||
// buttons that the user might want to use as a controller.
|
||||
//
|
||||
// This heuristic is quite loose. The user may still see weird devices showing up
|
||||
// as controllers, but it hopefully shouldn't filter out anything they actually
|
||||
// want to use.
|
||||
}
|
||||
|
||||
const char* evdevDevice::GetUniqueID() const
|
||||
{
|
||||
if (m_nodes.empty())
|
||||
return nullptr;
|
||||
|
||||
const auto uniq = libevdev_get_uniq(m_nodes.front().device);
|
||||
|
||||
// Some devices (e.g. Mayflash adapter) return an empty string which is not very unique.
|
||||
if (uniq && std::strlen(uniq) == 0)
|
||||
return nullptr;
|
||||
|
||||
return uniq;
|
||||
}
|
||||
|
||||
evdevDevice::~evdevDevice()
|
||||
{
|
||||
if (m_fd != -1)
|
||||
for (auto& node : m_nodes)
|
||||
{
|
||||
libevdev_free(m_dev);
|
||||
close(m_fd);
|
||||
s_devnode_objects.erase(node.devnode);
|
||||
libevdev_free(node.device);
|
||||
close(node.fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -459,30 +604,39 @@ void evdevDevice::UpdateInput()
|
|||
// Run through all evdev events
|
||||
// libevdev will keep track of the actual controller state internally which can be queried
|
||||
// later with libevdev_fetch_event_value()
|
||||
int rc = LIBEVDEV_READ_STATUS_SUCCESS;
|
||||
while (rc >= 0)
|
||||
for (auto& node : m_nodes)
|
||||
{
|
||||
input_event ev;
|
||||
if (LIBEVDEV_READ_STATUS_SYNC == rc)
|
||||
rc = libevdev_next_event(m_dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
|
||||
else
|
||||
rc = libevdev_next_event(m_dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
|
||||
int rc = LIBEVDEV_READ_STATUS_SUCCESS;
|
||||
while (rc >= 0)
|
||||
{
|
||||
input_event ev;
|
||||
if (LIBEVDEV_READ_STATUS_SYNC == rc)
|
||||
rc = libevdev_next_event(node.device, LIBEVDEV_READ_FLAG_SYNC, &ev);
|
||||
else
|
||||
rc = libevdev_next_event(node.device, LIBEVDEV_READ_FLAG_NORMAL, &ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool evdevDevice::IsValid() const
|
||||
{
|
||||
int current_fd = libevdev_get_fd(m_dev);
|
||||
if (current_fd == -1)
|
||||
return false;
|
||||
|
||||
libevdev* device;
|
||||
if (libevdev_new_from_fd(current_fd, &device) != 0)
|
||||
for (auto& node : m_nodes)
|
||||
{
|
||||
close(current_fd);
|
||||
return false;
|
||||
const int current_fd = libevdev_get_fd(node.device);
|
||||
|
||||
if (current_fd == -1)
|
||||
return false;
|
||||
|
||||
libevdev* device = nullptr;
|
||||
if (libevdev_new_from_fd(current_fd, &device) != 0)
|
||||
{
|
||||
close(current_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
libevdev_free(device);
|
||||
}
|
||||
libevdev_free(device);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,18 +76,26 @@ public:
|
|||
void UpdateInput() override;
|
||||
bool IsValid() const override;
|
||||
|
||||
evdevDevice(const std::string& devnode);
|
||||
~evdevDevice();
|
||||
|
||||
// Return true if node was "interesting".
|
||||
bool AddNode(std::string devnode, int fd, libevdev* dev);
|
||||
|
||||
const char* GetUniqueID() const;
|
||||
|
||||
std::string GetName() const override { return m_name; }
|
||||
std::string GetSource() const override { return "evdev"; }
|
||||
bool IsInteresting() const { return m_interesting; }
|
||||
|
||||
private:
|
||||
const std::string m_devfile;
|
||||
int m_fd;
|
||||
libevdev* m_dev;
|
||||
std::string m_name;
|
||||
bool m_interesting = false;
|
||||
|
||||
struct Node
|
||||
{
|
||||
std::string devnode;
|
||||
int fd;
|
||||
libevdev* device;
|
||||
};
|
||||
|
||||
std::vector<Node> m_nodes;
|
||||
};
|
||||
} // namespace ciface::evdev
|
||||
|
|
Loading…
Reference in New Issue