Skip to content

Commit

Permalink
Merge pull request #15 from games-on-whales/fix-rumble-poll
Browse files Browse the repository at this point in the history
fix: avoid busy polling in rumble thread
  • Loading branch information
ABeltramo authored Aug 19, 2024
2 parents 25e7525 + e9d16ed commit 5d4b8b2
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 17 deletions.
31 changes: 16 additions & 15 deletions src/uhid/include/uhid/uhid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <fcntl.h>
#include <functional>
#include <inputtino/result.hpp>
#include <iostream>
#include <linux/uhid.h>
#include <memory>
#include <poll.h>
Expand Down Expand Up @@ -50,7 +51,7 @@ static inputtino::Result<bool> uhid_write(int fd, const struct uhid_event *ev) {
class Device {
private:
Device(std::shared_ptr<std::thread> ev_thread, std::shared_ptr<ThreadState> state)
: ev_thread(std::move(ev_thread)), state(std::move(state)){};
: ev_thread(std::move(ev_thread)), state(std::move(state)) {};
std::shared_ptr<std::thread> ev_thread;
std::shared_ptr<ThreadState> state;
std::shared_ptr<std::function<void(const uhid_event &ev, int fd)>> on_event;
Expand Down Expand Up @@ -81,7 +82,7 @@ class Device {

~Device() {
if (state) {
struct uhid_event ev {};
struct uhid_event ev{};
ev.type = UHID_DESTROY;
uhid_write(state->fd, &ev);

Expand All @@ -100,6 +101,8 @@ static void set_c_str(const std::string &str, unsigned char *c_str) {
c_str[str.length()] = 0;
}

constexpr int UHID_POLL_TIMEOUT = 500; // ms

inputtino::Result<Device> Device::create(const DeviceDefinition &definition,
const std::function<void(const uhid_event &ev, int fd)> &on_event) {

Expand All @@ -124,30 +127,28 @@ inputtino::Result<Device> Device::create(const DeviceDefinition &definition,
state->fd = fd;
state->on_event = on_event;
auto thread = std::make_shared<std::thread>([state]() {
ssize_t ret;
struct pollfd pfds[1];
pfds[0].fd = state->fd;
pfds[0].events = POLLIN;
std::array<pollfd, 1> pfds = {pollfd{.fd = state->fd, .events = POLLIN}};
int poll_rs = 0;

while (!state->stop_repeat_thread) {
ret = poll(pfds, 1, -1);
if (ret < 0) {
fprintf(stderr, "Cannot poll for fds: %m\n");
poll_rs = poll(pfds.data(), pfds.size(), UHID_POLL_TIMEOUT);
if (poll_rs < 0) {
std::cerr << "Failed polling uhid fd; ret=" << strerror(errno) << std::endl;
break;
}
if (pfds[0].revents & POLLHUP) {
fprintf(stderr, "Received HUP on uhid-cdev\n");
std::cerr << "HUP on uhid-cdev" << std::endl;
break;
}
if (pfds[0].revents & POLLIN) {
struct uhid_event ev {};
ret = read(state->fd, &ev, sizeof(ev));
struct uhid_event ev{};
auto ret = read(state->fd, &ev, sizeof(ev));
if (ret == 0) {
fprintf(stderr, "Read HUP on uhid-cdev\n");
std::cerr << "Read HUP on uhid-cdev" << std::endl;
} else if (ret < 0) {
fprintf(stderr, "Cannot read uhid-cdev: %m\n");
std::cerr << "Cannot read uhid-cdev: " << strerror(errno) << std::endl;
} else if (ret != sizeof(ev)) {
fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n", ret, sizeof(ev));
std::cerr << "Invalid size read from uhid-dev" << ret << " != " << sizeof(ev) << std::endl;
} else {
if (state->on_event) {
state->on_event(ev, state->fd);
Expand Down
13 changes: 11 additions & 2 deletions src/uinput/joypad_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
#include <linux/input.h>
#include <linux/uinput.h>
#include <optional>
#include <poll.h>
#include <thread>

namespace inputtino {

using namespace std::chrono_literals;

static constexpr long MAX_GAIN = 0xFFFF;
constexpr long MAX_GAIN = 0xFFFF;
constexpr int RUMBLE_POLL_TIMEOUT = 500; // ms

/**
* Joypads will also have one `/dev/input/js*` device as child, we want to expose that as well
Expand Down Expand Up @@ -165,8 +167,15 @@ static void event_listener(const std::shared_ptr<BaseJoypadState> &state) {
/* This can only be set globally when receiving FF_GAIN */
unsigned int current_gain = MAX_GAIN;

std::array<pollfd, 1> pfds = {pollfd{.fd = uinput_fd, .events = POLLIN}};
int poll_rs = 0;

while (!state->stop_listening_events) {
std::this_thread::sleep_for(20ms); // TODO: configurable?
poll_rs = poll(pfds.data(), pfds.size(), RUMBLE_POLL_TIMEOUT);
if (poll_rs < 0) {
std::cerr << "Failed polling uinput fd; ret=" << strerror(errno);
return;
}

auto events = fetch_events(uinput_fd);
for (auto ev : events) {
Expand Down

0 comments on commit 5d4b8b2

Please sign in to comment.