mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
244 lines
7.7 KiB
C++
244 lines
7.7 KiB
C++
#include "../include/GlobalHotkeysJoystick.hpp"
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/eventfd.h>
|
|
|
|
namespace gsr {
|
|
static constexpr double double_click_timeout_seconds = 0.33;
|
|
|
|
// Returns -1 on error
|
|
static int get_js_dev_input_id_from_filepath(const char *dev_input_filepath) {
|
|
if(strncmp(dev_input_filepath, "/dev/input/js", 13) != 0)
|
|
return -1;
|
|
|
|
int dev_input_id = -1;
|
|
if(sscanf(dev_input_filepath + 13, "%d", &dev_input_id) == 1)
|
|
return dev_input_id;
|
|
return -1;
|
|
}
|
|
|
|
GlobalHotkeysJoystick::~GlobalHotkeysJoystick() {
|
|
if(event_fd > 0) {
|
|
const uint64_t exit = 1;
|
|
write(event_fd, &exit, sizeof(exit));
|
|
}
|
|
|
|
if(read_thread.joinable())
|
|
read_thread.join();
|
|
|
|
if(event_fd > 0)
|
|
close(event_fd);
|
|
|
|
for(int i = 0; i < num_poll_fd; ++i) {
|
|
close(poll_fd[i].fd);
|
|
}
|
|
}
|
|
|
|
bool GlobalHotkeysJoystick::start() {
|
|
if(num_poll_fd > 0)
|
|
return false;
|
|
|
|
event_fd = eventfd(0, 0);
|
|
if(event_fd <= 0)
|
|
return false;
|
|
|
|
event_index = num_poll_fd;
|
|
poll_fd[num_poll_fd] = {
|
|
event_fd,
|
|
POLLIN,
|
|
0
|
|
};
|
|
extra_data[num_poll_fd] = {
|
|
-1
|
|
};
|
|
++num_poll_fd;
|
|
|
|
if(!hotplug.start()) {
|
|
fprintf(stderr, "Warning: failed to setup hotplugging\n");
|
|
} else {
|
|
hotplug_poll_index = num_poll_fd;
|
|
poll_fd[num_poll_fd] = {
|
|
hotplug.steal_fd(),
|
|
POLLIN,
|
|
0
|
|
};
|
|
extra_data[num_poll_fd] = {
|
|
-1
|
|
};
|
|
++num_poll_fd;
|
|
}
|
|
|
|
char dev_input_path[128];
|
|
for(int i = 0; i < 8; ++i) {
|
|
snprintf(dev_input_path, sizeof(dev_input_path), "/dev/input/js%d", i);
|
|
add_device(dev_input_path, false);
|
|
}
|
|
|
|
if(num_poll_fd == 0)
|
|
fprintf(stderr, "Info: no joysticks found, assuming they might be connected later\n");
|
|
|
|
read_thread = std::thread(&GlobalHotkeysJoystick::read_events, this);
|
|
return true;
|
|
}
|
|
|
|
bool GlobalHotkeysJoystick::bind_action(const std::string &id, GlobalHotkeyCallback callback) {
|
|
if(num_poll_fd == 0)
|
|
return false;
|
|
return bound_actions_by_id.insert(std::make_pair(id, std::move(callback))).second;
|
|
}
|
|
|
|
void GlobalHotkeysJoystick::poll_events() {
|
|
if(num_poll_fd == 0)
|
|
return;
|
|
|
|
if(save_replay) {
|
|
save_replay = false;
|
|
auto it = bound_actions_by_id.find("save_replay");
|
|
if(it != bound_actions_by_id.end())
|
|
it->second("save_replay");
|
|
}
|
|
}
|
|
|
|
void GlobalHotkeysJoystick::read_events() {
|
|
js_event event;
|
|
while(poll(poll_fd, num_poll_fd, -1) > 0) {
|
|
for(int i = 0; i < num_poll_fd; ++i) {
|
|
if(poll_fd[i].revents & (POLLHUP|POLLERR|POLLNVAL)) {
|
|
if(i == event_index)
|
|
goto done;
|
|
|
|
if(remove_poll_fd(i))
|
|
--i; // This item was removed so we want to repeat the same index to continue to the next item
|
|
|
|
continue;
|
|
}
|
|
|
|
if(!(poll_fd[i].revents & POLLIN))
|
|
continue;
|
|
|
|
if(i == event_index) {
|
|
goto done;
|
|
} else if(i == hotplug_poll_index) {
|
|
hotplug.process_event_data(poll_fd[i].fd, [&](HotplugAction hotplug_action, const char *devname) {
|
|
char dev_input_filepath[1024];
|
|
snprintf(dev_input_filepath, sizeof(dev_input_filepath), "/dev/%s", devname);
|
|
switch(hotplug_action) {
|
|
case HotplugAction::ADD: {
|
|
// Cant open the /dev/input device immediately or it fails.
|
|
// TODO: Remove this hack when a better solution is found.
|
|
usleep(50 * 1000);
|
|
add_device(dev_input_filepath);
|
|
break;
|
|
}
|
|
case HotplugAction::REMOVE: {
|
|
if(remove_device(dev_input_filepath))
|
|
--i; // This item was removed so we want to repeat the same index to continue to the next item
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
process_js_event(poll_fd[i].fd, event);
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
;
|
|
}
|
|
|
|
void GlobalHotkeysJoystick::process_js_event(int fd, js_event &event) {
|
|
if(read(fd, &event, sizeof(event)) != sizeof(event))
|
|
return;
|
|
|
|
if((event.type & JS_EVENT_BUTTON) == 0)
|
|
return;
|
|
|
|
if(event.number == 8 && event.value == 1) {
|
|
const double now = double_click_clock.get_elapsed_time_seconds();
|
|
if(!prev_time_clicked.has_value()) {
|
|
prev_time_clicked = now;
|
|
return;
|
|
}
|
|
|
|
if(prev_time_clicked.has_value()) {
|
|
const bool double_clicked = (now - prev_time_clicked.value()) < double_click_timeout_seconds;
|
|
if(double_clicked) {
|
|
save_replay = true;
|
|
prev_time_clicked.reset();
|
|
} else {
|
|
prev_time_clicked = now;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GlobalHotkeysJoystick::add_device(const char *dev_input_filepath, bool print_error) {
|
|
if(num_poll_fd >= max_js_poll_fd) {
|
|
fprintf(stderr, "Warning: failed to add joystick device %s, too many joysticks have been added\n", dev_input_filepath);
|
|
return false;
|
|
}
|
|
|
|
const int dev_input_id = get_js_dev_input_id_from_filepath(dev_input_filepath);
|
|
if(dev_input_id == -1)
|
|
return false;
|
|
|
|
const int fd = open(dev_input_filepath, O_RDONLY);
|
|
if(fd <= 0) {
|
|
if(print_error)
|
|
fprintf(stderr, "Error: failed to add joystick %s, error: %s\n", dev_input_filepath, strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
poll_fd[num_poll_fd] = {
|
|
fd,
|
|
POLLIN,
|
|
0
|
|
};
|
|
|
|
extra_data[num_poll_fd] = {
|
|
dev_input_id
|
|
};
|
|
|
|
++num_poll_fd;
|
|
fprintf(stderr, "Info: added joystick: %s\n", dev_input_filepath);
|
|
return true;
|
|
}
|
|
|
|
bool GlobalHotkeysJoystick::remove_device(const char *dev_input_filepath) {
|
|
const int dev_input_id = get_js_dev_input_id_from_filepath(dev_input_filepath);
|
|
if(dev_input_id == -1)
|
|
return false;
|
|
|
|
const int poll_fd_index = get_poll_fd_index_by_dev_input_id(dev_input_id);
|
|
if(poll_fd_index == -1)
|
|
return false;
|
|
|
|
fprintf(stderr, "Info: removed joystick: %s\n", dev_input_filepath);
|
|
return remove_poll_fd(poll_fd_index);
|
|
}
|
|
|
|
bool GlobalHotkeysJoystick::remove_poll_fd(int index) {
|
|
if(index < 0 || index >= num_poll_fd)
|
|
return false;
|
|
|
|
close(poll_fd[index].fd);
|
|
for(int i = index + 1; i < num_poll_fd; ++i) {
|
|
poll_fd[i - 1] = poll_fd[i];
|
|
extra_data[i - 1] = extra_data[i];
|
|
}
|
|
--num_poll_fd;
|
|
return true;
|
|
}
|
|
|
|
int GlobalHotkeysJoystick::get_poll_fd_index_by_dev_input_id(int dev_input_id) const {
|
|
for(int i = 0; i < num_poll_fd; ++i) {
|
|
if(dev_input_id == extra_data[i].dev_input_id)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|