199 lines
5.3 KiB
C
199 lines
5.3 KiB
C
|
#include "gamepad.h"
|
||
|
|
||
|
#include <kinc/input/gamepad.h>
|
||
|
|
||
|
#include <fcntl.h>
|
||
|
#include <libudev.h>
|
||
|
#include <linux/joystick.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
struct HIDGamepad {
|
||
|
int idx;
|
||
|
char gamepad_dev_name[256];
|
||
|
char name[385];
|
||
|
int file_descriptor;
|
||
|
bool connected;
|
||
|
struct js_event gamepadEvent;
|
||
|
};
|
||
|
|
||
|
static void HIDGamepad_open(struct HIDGamepad *pad) {
|
||
|
pad->file_descriptor = open(pad->gamepad_dev_name, O_RDONLY | O_NONBLOCK);
|
||
|
if (pad->file_descriptor < 0) {
|
||
|
pad->connected = false;
|
||
|
}
|
||
|
else {
|
||
|
pad->connected = true;
|
||
|
|
||
|
char buf[128];
|
||
|
if (ioctl(pad->file_descriptor, JSIOCGNAME(sizeof(buf)), buf) < 0) {
|
||
|
strncpy(buf, "Unknown", sizeof(buf));
|
||
|
}
|
||
|
snprintf(pad->name, sizeof(pad->name), "%s(%s)", buf, pad->gamepad_dev_name);
|
||
|
kinc_internal_gamepad_trigger_connect(pad->idx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void HIDGamepad_init(struct HIDGamepad *pad, int index) {
|
||
|
pad->file_descriptor = -1;
|
||
|
pad->connected = false;
|
||
|
pad->gamepad_dev_name[0] = 0;
|
||
|
if (index >= 0 && index < 12) {
|
||
|
pad->idx = index;
|
||
|
snprintf(pad->gamepad_dev_name, sizeof(pad->gamepad_dev_name), "/dev/input/js%d", pad->idx);
|
||
|
HIDGamepad_open(pad);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void HIDGamepad_close(struct HIDGamepad *pad) {
|
||
|
if (pad->connected) {
|
||
|
kinc_internal_gamepad_trigger_disconnect(pad->idx);
|
||
|
close(pad->file_descriptor);
|
||
|
pad->file_descriptor = -1;
|
||
|
pad->connected = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HIDGamepad_processEvent(struct HIDGamepad *pad, struct js_event e) {
|
||
|
switch (e.type) {
|
||
|
case JS_EVENT_BUTTON:
|
||
|
kinc_internal_gamepad_trigger_button(pad->idx, e.number, e.value);
|
||
|
break;
|
||
|
case JS_EVENT_AXIS: {
|
||
|
float value = e.number % 2 == 0 ? e.value : -e.value;
|
||
|
kinc_internal_gamepad_trigger_axis(pad->idx, e.number, value / 32767.0f);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HIDGamepad_update(struct HIDGamepad *pad) {
|
||
|
if (pad->connected) {
|
||
|
while (read(pad->file_descriptor, &pad->gamepadEvent, sizeof(pad->gamepadEvent)) > 0) {
|
||
|
HIDGamepad_processEvent(pad, pad->gamepadEvent);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct HIDGamepadUdevHelper {
|
||
|
struct udev *udevPtr;
|
||
|
struct udev_monitor *udevMonitorPtr;
|
||
|
int udevMonitorFD;
|
||
|
};
|
||
|
|
||
|
static struct HIDGamepadUdevHelper udev_helper;
|
||
|
|
||
|
static struct HIDGamepad gamepads[KINC_GAMEPAD_MAX_COUNT];
|
||
|
|
||
|
static void HIDGamepadUdevHelper_openOrCloseGamepad(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) {
|
||
|
const char *action = udev_device_get_action(dev);
|
||
|
if (!action)
|
||
|
action = "add";
|
||
|
|
||
|
const char *joystickDevnodeName = strstr(udev_device_get_devnode(dev), "js");
|
||
|
|
||
|
if (joystickDevnodeName) {
|
||
|
int joystickDevnodeIndex;
|
||
|
sscanf(joystickDevnodeName, "js%d", &joystickDevnodeIndex);
|
||
|
|
||
|
if (!strcmp(action, "add")) {
|
||
|
HIDGamepad_open(&gamepads[joystickDevnodeIndex]);
|
||
|
}
|
||
|
|
||
|
if (!strcmp(action, "remove")) {
|
||
|
HIDGamepad_close(&gamepads[joystickDevnodeIndex]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void HIDGamepadUdevHelper_processDevice(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) {
|
||
|
if (dev) {
|
||
|
if (udev_device_get_devnode(dev))
|
||
|
HIDGamepadUdevHelper_openOrCloseGamepad(helper, dev);
|
||
|
|
||
|
udev_device_unref(dev);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void HIDGamepadUdevHelper_init(struct HIDGamepadUdevHelper *helper) {
|
||
|
struct udev *udevPtrNew = udev_new();
|
||
|
|
||
|
// enumerate
|
||
|
struct udev_enumerate *enumerate = udev_enumerate_new(udevPtrNew);
|
||
|
|
||
|
udev_enumerate_add_match_subsystem(enumerate, "input");
|
||
|
udev_enumerate_scan_devices(enumerate);
|
||
|
|
||
|
struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
|
||
|
struct udev_list_entry *entry;
|
||
|
|
||
|
udev_list_entry_foreach(entry, devices) {
|
||
|
const char *path = udev_list_entry_get_name(entry);
|
||
|
struct udev_device *dev = udev_device_new_from_syspath(udevPtrNew, path);
|
||
|
HIDGamepadUdevHelper_processDevice(helper, dev);
|
||
|
}
|
||
|
|
||
|
udev_enumerate_unref(enumerate);
|
||
|
|
||
|
// setup mon
|
||
|
helper->udevMonitorPtr = udev_monitor_new_from_netlink(udevPtrNew, "udev");
|
||
|
|
||
|
udev_monitor_filter_add_match_subsystem_devtype(helper->udevMonitorPtr, "input", NULL);
|
||
|
udev_monitor_enable_receiving(helper->udevMonitorPtr);
|
||
|
|
||
|
helper->udevMonitorFD = udev_monitor_get_fd(helper->udevMonitorPtr);
|
||
|
|
||
|
helper->udevPtr = udevPtrNew;
|
||
|
}
|
||
|
|
||
|
static void HIDGamepadUdevHelper_update(struct HIDGamepadUdevHelper *helper) {
|
||
|
fd_set fds;
|
||
|
FD_ZERO(&fds);
|
||
|
FD_SET(helper->udevMonitorFD, &fds);
|
||
|
|
||
|
if (FD_ISSET(helper->udevMonitorFD, &fds)) {
|
||
|
struct udev_device *dev = udev_monitor_receive_device(helper->udevMonitorPtr);
|
||
|
HIDGamepadUdevHelper_processDevice(helper, dev);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void HIDGamepadUdevHelper_close(struct HIDGamepadUdevHelper *helper) {
|
||
|
udev_unref(helper->udevPtr);
|
||
|
}
|
||
|
|
||
|
void kinc_linux_initHIDGamepads() {
|
||
|
for (int i = 0; i < KINC_GAMEPAD_MAX_COUNT; ++i) {
|
||
|
HIDGamepad_init(&gamepads[i], i);
|
||
|
}
|
||
|
HIDGamepadUdevHelper_init(&udev_helper);
|
||
|
}
|
||
|
|
||
|
void kinc_linux_updateHIDGamepads() {
|
||
|
HIDGamepadUdevHelper_update(&udev_helper);
|
||
|
for (int i = 0; i < KINC_GAMEPAD_MAX_COUNT; ++i) {
|
||
|
HIDGamepad_update(&gamepads[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void kinc_linux_closeHIDGamepads() {
|
||
|
HIDGamepadUdevHelper_close(&udev_helper);
|
||
|
}
|
||
|
|
||
|
const char *kinc_gamepad_vendor(int gamepad) {
|
||
|
return "Linux gamepad";
|
||
|
}
|
||
|
|
||
|
const char *kinc_gamepad_product_name(int gamepad) {
|
||
|
return gamepad >= 0 && gamepad < KINC_GAMEPAD_MAX_COUNT ? gamepads[gamepad].name : "";
|
||
|
}
|
||
|
|
||
|
bool kinc_gamepad_connected(int gamepad) {
|
||
|
return gamepad >= 0 && gamepad < KINC_GAMEPAD_MAX_COUNT && gamepads[gamepad].connected;
|
||
|
}
|
||
|
|
||
|
void kinc_gamepad_rumble(int gamepad, float left, float right) {}
|