2022-11-13 18:10:09 +01:00
|
|
|
// This makes debugging on windows less painful
|
|
|
|
|
//#define HAVE_LIBEVDEV
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBEVDEV
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
#include "stdafx.h"
|
2022-11-13 18:10:09 +01:00
|
|
|
#include "evdev_gun_handler.h"
|
2022-11-27 12:20:05 +01:00
|
|
|
#include "util/logs.hpp"
|
|
|
|
|
|
2022-11-13 18:10:09 +01:00
|
|
|
#include <libudev.h>
|
2022-11-30 00:25:09 +01:00
|
|
|
#include <libevdev/libevdev.h>
|
2022-11-13 18:10:09 +01:00
|
|
|
#include <fcntl.h>
|
2022-11-27 12:20:05 +01:00
|
|
|
#include <unistd.h>
|
2022-11-13 18:10:09 +01:00
|
|
|
|
|
|
|
|
LOG_CHANNEL(evdev_log, "evdev");
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
constexpr usz max_devices = 8;
|
2022-11-13 18:10:09 +01:00
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
const std::map<gun_button, int> button_map
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
{gun_button::btn_left, BTN_LEFT},
|
|
|
|
|
{gun_button::btn_right, BTN_RIGHT},
|
|
|
|
|
{gun_button::btn_middle, BTN_MIDDLE},
|
|
|
|
|
{gun_button::btn_1, BTN_1},
|
|
|
|
|
{gun_button::btn_2, BTN_2},
|
|
|
|
|
{gun_button::btn_3, BTN_3},
|
|
|
|
|
{gun_button::btn_4, BTN_4},
|
|
|
|
|
{gun_button::btn_5, BTN_5}
|
|
|
|
|
};
|
2022-11-13 18:10:09 +01:00
|
|
|
|
|
|
|
|
struct event_udev_entry
|
|
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
const char* devnode = nullptr;
|
|
|
|
|
struct udev_list_entry* item = nullptr;
|
2022-11-13 18:10:09 +01:00
|
|
|
};
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
bool event_is_number(const char* s)
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
if (!s)
|
|
|
|
|
return false;
|
2022-11-13 18:10:09 +01:00
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
const usz len = strlen(s);
|
|
|
|
|
if (len == 0)
|
|
|
|
|
return false;
|
2022-11-13 18:10:09 +01:00
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
for (usz n = 0; n < len; n++)
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
if (s[n] < '0' || s[n] > '9')
|
|
|
|
|
return false;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
2022-11-27 12:20:05 +01:00
|
|
|
return true;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// compare /dev/input/eventX and /dev/input/eventY where X and Y are numbers
|
|
|
|
|
int event_strcmp_events(const char* x, const char* y)
|
|
|
|
|
{
|
|
|
|
|
// find a common string
|
2022-11-27 12:20:05 +01:00
|
|
|
int n = 0;
|
|
|
|
|
while (x && y && x[n] == y[n] && x[n] != '\0' && y[n] != '\0')
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if remaining string is a number
|
2022-11-27 12:20:05 +01:00
|
|
|
if (event_is_number(x + n) && event_is_number(y + n))
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
const int a = atoi(x + n);
|
|
|
|
|
const int b = atoi(y + n);
|
2022-11-13 18:10:09 +01:00
|
|
|
|
|
|
|
|
if (a == b)
|
|
|
|
|
return 0;
|
|
|
|
|
if (a < b)
|
|
|
|
|
return -1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
return strcmp(x, y);
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
evdev_gun_handler::evdev_gun_handler()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
evdev_gun_handler::~evdev_gun_handler()
|
|
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
for (evdev_gun& gun : m_devices)
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-30 00:25:09 +01:00
|
|
|
if (gun.device)
|
|
|
|
|
{
|
|
|
|
|
const int fd = libevdev_get_fd(gun.device);
|
|
|
|
|
libevdev_free(gun.device);
|
|
|
|
|
close(fd);
|
|
|
|
|
}
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
if (m_udev != nullptr)
|
|
|
|
|
udev_unref(m_udev);
|
|
|
|
|
evdev_log.notice("Lightgun: Shutdown udev initialization");
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
int evdev_gun_handler::get_button(u32 gunno, gun_button button) const
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
const auto& buttons = ::at32(m_devices, gunno).buttons;
|
|
|
|
|
|
|
|
|
|
if (const auto it = buttons.find(::at32(button_map, button)); it != buttons.end())
|
|
|
|
|
{
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
int evdev_gun_handler::get_axis_x(u32 gunno) const
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
const evdev_axis& axis = ::at32(::at32(m_devices, gunno).axis, ABS_X);
|
|
|
|
|
return axis.value - axis.min;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
int evdev_gun_handler::get_axis_y(u32 gunno) const
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
const evdev_axis& axis = ::at32(::at32(m_devices, gunno).axis, ABS_Y);
|
|
|
|
|
return axis.value - axis.min;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
int evdev_gun_handler::get_axis_x_max(u32 gunno) const
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
const evdev_axis& axis = ::at32(::at32(m_devices, gunno).axis, ABS_X);
|
|
|
|
|
return axis.max - axis.min;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
int evdev_gun_handler::get_axis_y_max(u32 gunno) const
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
const evdev_axis& axis = ::at32(::at32(m_devices, gunno).axis, ABS_Y);
|
|
|
|
|
return axis.max - axis.min;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
void evdev_gun_handler::poll(u32 index)
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
if (!m_is_init || index >= m_devices.size())
|
|
|
|
|
return;
|
2022-11-13 18:10:09 +01:00
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
evdev_gun& gun = ::at32(m_devices, index);
|
|
|
|
|
|
2022-11-30 00:25:09 +01:00
|
|
|
if (!gun.device)
|
|
|
|
|
return;
|
2022-11-27 12:20:05 +01:00
|
|
|
|
2022-11-30 00:25:09 +01:00
|
|
|
// Try to fetch all new events from the joystick.
|
|
|
|
|
input_event evt;
|
|
|
|
|
int ret = LIBEVDEV_READ_STATUS_SUCCESS;
|
|
|
|
|
while (ret >= 0)
|
|
|
|
|
{
|
|
|
|
|
if (ret == LIBEVDEV_READ_STATUS_SYNC)
|
|
|
|
|
{
|
|
|
|
|
// Grab any pending sync event.
|
|
|
|
|
ret = libevdev_next_event(gun.device, LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_SYNC, &evt);
|
|
|
|
|
}
|
|
|
|
|
else
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-30 00:25:09 +01:00
|
|
|
ret = libevdev_next_event(gun.device, LIBEVDEV_READ_FLAG_NORMAL, &evt);
|
|
|
|
|
}
|
2022-11-27 12:20:05 +01:00
|
|
|
|
2022-11-30 00:25:09 +01:00
|
|
|
if (ret == LIBEVDEV_READ_STATUS_SUCCESS)
|
|
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
switch (evt.type)
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
case EV_KEY:
|
|
|
|
|
gun.buttons[evt.code] = evt.value;
|
|
|
|
|
break;
|
|
|
|
|
case EV_ABS:
|
|
|
|
|
gun.axis[evt.code].value = evt.value;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-30 00:25:09 +01:00
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
// -EAGAIN signifies no available events, not an actual *error*.
|
|
|
|
|
if (ret != -EAGAIN)
|
|
|
|
|
evdev_log.error("Failed to read latest event from lightgun device: %s [errno %d]", strerror(-ret), -ret);
|
|
|
|
|
}
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
bool evdev_gun_handler::is_init() const
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
return m_is_init;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
u32 evdev_gun_handler::get_num_guns() const
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
return ::narrow<u32>(m_devices.size());
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool evdev_gun_handler::init()
|
|
|
|
|
{
|
|
|
|
|
if (m_is_init)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
evdev_log.notice("Lightgun: Begin udev initialization");
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
m_devices.clear();
|
|
|
|
|
|
2022-11-13 18:10:09 +01:00
|
|
|
m_udev = udev_new();
|
|
|
|
|
if (m_udev == nullptr)
|
2022-11-27 12:20:05 +01:00
|
|
|
{
|
|
|
|
|
evdev_log.error("Lightgun: Failed udev initialization");
|
2022-11-13 18:10:09 +01:00
|
|
|
return false;
|
2022-11-27 12:20:05 +01:00
|
|
|
}
|
2022-11-13 18:10:09 +01:00
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
if (udev_enumerate* enumerate = udev_enumerate_new(m_udev))
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
|
|
|
|
udev_enumerate_add_match_property(enumerate, "ID_INPUT_MOUSE", "1");
|
|
|
|
|
udev_enumerate_add_match_subsystem(enumerate, "input");
|
|
|
|
|
udev_enumerate_scan_devices(enumerate);
|
2022-11-27 12:20:05 +01:00
|
|
|
udev_list_entry* devs = udev_enumerate_get_list_entry(enumerate);
|
|
|
|
|
|
|
|
|
|
std::vector<event_udev_entry> sorted_devices;
|
2022-11-13 18:10:09 +01:00
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
for (udev_list_entry* item = devs; item && sorted_devices.size() < max_devices; item = udev_list_entry_get_next(item))
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
|
|
|
|
const char* name = udev_list_entry_get_name(item);
|
2022-11-27 12:20:05 +01:00
|
|
|
udev_device* dev = udev_device_new_from_syspath(m_udev, name);
|
2022-11-13 18:10:09 +01:00
|
|
|
const char* devnode = udev_device_get_devnode(dev);
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
if (devnode != nullptr)
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
event_udev_entry new_device{};
|
|
|
|
|
new_device.devnode = devnode;
|
|
|
|
|
new_device.item = item;
|
|
|
|
|
sorted_devices.push_back(std::move(new_device));
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
udev_device_unref(dev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
// Sort the udev entries by devnode name so that they are created in the proper order
|
|
|
|
|
std::sort(sorted_devices.begin(), sorted_devices.end(), [](const event_udev_entry& a, const event_udev_entry& b)
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-27 12:20:05 +01:00
|
|
|
return event_strcmp_events(a.devnode, b.devnode);
|
|
|
|
|
});
|
2022-11-13 18:10:09 +01:00
|
|
|
|
2022-11-27 12:20:05 +01:00
|
|
|
for (const event_udev_entry& entry : sorted_devices)
|
|
|
|
|
{
|
|
|
|
|
// Get the filename of the /sys entry for the device and create a udev_device object (dev) representing it.
|
|
|
|
|
const char* name = udev_list_entry_get_name(entry.item);
|
2022-11-13 18:10:09 +01:00
|
|
|
evdev_log.notice("Lightgun: found device %s", name);
|
2022-11-27 12:20:05 +01:00
|
|
|
|
|
|
|
|
udev_device* dev = udev_device_new_from_syspath(m_udev, name);
|
2022-11-13 18:10:09 +01:00
|
|
|
const char* devnode = udev_device_get_devnode(dev);
|
2022-11-30 00:25:09 +01:00
|
|
|
const int fd = open(devnode, O_RDONLY | O_NONBLOCK);
|
|
|
|
|
struct libevdev* device = nullptr;
|
|
|
|
|
const int rc = libevdev_new_from_fd(fd, &device);
|
|
|
|
|
if (rc < 0)
|
|
|
|
|
{
|
|
|
|
|
// If it's just a bad file descriptor, don't bother logging, but otherwise, log it.
|
|
|
|
|
if (rc != -9)
|
|
|
|
|
evdev_log.warning("Failed to connect to lightgun device at %s, the error was: %s", devnode, strerror(-rc));
|
|
|
|
|
libevdev_free(device);
|
|
|
|
|
close(fd);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-11-13 18:10:09 +01:00
|
|
|
|
2022-11-30 00:25:09 +01:00
|
|
|
if (libevdev_has_event_type(device, EV_KEY) &&
|
|
|
|
|
libevdev_has_event_type(device, EV_ABS))
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-30 00:25:09 +01:00
|
|
|
bool is_valid = true;
|
|
|
|
|
|
|
|
|
|
evdev_gun gun{};
|
|
|
|
|
gun.device = device;
|
|
|
|
|
|
|
|
|
|
for (int code : { ABS_X, ABS_Y })
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-30 00:25:09 +01:00
|
|
|
if (const input_absinfo* info = libevdev_get_abs_info(device, code))
|
2022-11-13 18:10:09 +01:00
|
|
|
{
|
2022-11-30 00:25:09 +01:00
|
|
|
gun.axis[code].min = info->minimum;
|
|
|
|
|
gun.axis[code].max = info->maximum;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2022-11-30 00:25:09 +01:00
|
|
|
evdev_log.notice("Lightgun: device %s not valid. axis %d not found", name, code);
|
|
|
|
|
is_valid = false;
|
|
|
|
|
break;
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-30 00:25:09 +01:00
|
|
|
|
|
|
|
|
if (is_valid)
|
|
|
|
|
{
|
|
|
|
|
evdev_log.notice("Lightgun: Adding device %d: %s, ABS_X(%i, %i), ABS_Y(%i, %i)", m_devices.size(), name, gun.axis[ABS_X].min, gun.axis[ABS_X].max, gun.axis[ABS_Y].min, gun.axis[ABS_Y].max);
|
|
|
|
|
m_devices.push_back(gun);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
close(fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_devices.size() >= max_devices)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
evdev_log.notice("Lightgun: device %s not valid. No axis or key events found", name);
|
|
|
|
|
close(fd);
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
udev_enumerate_unref(enumerate);
|
2022-11-27 12:20:05 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
evdev_log.error("Lightgun: Failed udev enumeration");
|
2022-11-13 18:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_is_init = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|