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) {}
							 |