Files
furui/device.c
2026-04-01 22:30:31 +09:00

544 lines
12 KiB
C

/*
This file is part of Furui.
Furui is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Furui is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Furui. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <libevdev/libevdev.h>
#include "core.h"
#include "device.h"
#define FURUI_INPUT_DIRECTORY "/dev/input"
#define FURUI_EVENT_PREFIX "event"
struct furui_device *furui_device_new(const char *path) {
int fd;
struct libevdev *dev;
int err;
struct furui_device *device;
int path_length;
const char *name;
int name_length;
const char *uniq;
int uniq_length;
struct furui_uiarray *uiarray;
fd = open(path, O_RDONLY);
if (fd == -1) {
return NULL;
}
dev = libevdev_new();
if (dev == NULL) {
return NULL;
}
err = libevdev_set_fd(dev, fd);
if (err < 0) {
perror("libevdev_set_fd");
exit(EXIT_FAILURE);
}
device = malloc(sizeof(struct furui_device));
if (device == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
path_length = strlen(path);
device->path = malloc(path_length + 1);
if (device->path == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
strncpy(device->path, path, path_length + 1);
name = libevdev_get_name(dev);
name_length = strlen(name);
device->name = malloc(name_length + 1);
if (device->name == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
strncpy(device->name, name, name_length + 1);
uniq = libevdev_get_uniq(dev);
if (uniq == NULL) {
device->uniq = NULL;
} else {
uniq_length = strlen(uniq);
device->uniq = malloc(uniq_length + 1);
if (device->uniq == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
strncpy(device->uniq, uniq, uniq_length + 1);
}
device->has_keys = libevdev_has_event_type(dev, EV_KEY);
uiarray = furui_uiarray_new();
if (device->has_keys) {
unsigned int code;
for (code = 0; code < KEY_CNT; code++) {
if (libevdev_has_event_code(dev, EV_KEY, code)) {
furui_uiarray_append(uiarray, code);
}
}
}
device->key_count = furui_uiarray_get_length(uiarray);
device->keys = furui_uiarray_to_array(uiarray);
furui_uiarray_free(uiarray);
device->has_rels = libevdev_has_event_type(dev, EV_REL);
uiarray = furui_uiarray_new();
if (device->has_rels) {
unsigned int code;
for (code = 0; code < REL_CNT; code++) {
if (libevdev_has_event_code(dev, EV_REL, code)) {
furui_uiarray_append(uiarray, code);
}
}
}
device->rel_count = furui_uiarray_get_length(uiarray);
device->rels = furui_uiarray_to_array(uiarray);
furui_uiarray_free(uiarray);
device->has_abses = libevdev_has_event_type(dev, EV_ABS);
uiarray = furui_uiarray_new();
if (device->has_abses) {
unsigned int code;
for (code = 0; code < ABS_CNT; code++) {
if (libevdev_has_event_code(dev, EV_ABS, code)) {
furui_uiarray_append(uiarray, code);
}
}
}
device->abs_count = furui_uiarray_get_length(uiarray);
device->abses = furui_uiarray_to_array(uiarray);
furui_uiarray_free(uiarray);
libevdev_free(dev);
close(fd);
return device;
}
void furui_device_free(struct furui_device *device) {
if (device == NULL) {
return;
}
free(device->path);
free(device->name);
free(device->uniq);
free(device->keys);
free(device->rels);
free(device->abses);
free(device);
}
struct furui_device *furui_device_copy(struct furui_device *device) {
struct furui_device *copy;
int path_length;
char *path;
int name_length;
char *name;
int uniq_length;
char *uniq;
if (device == NULL) {
return NULL;
}
copy = malloc(sizeof(struct furui_device));
if (copy == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
path_length = strlen(device->path);
path = malloc(path_length + 1);
if (path == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
strncpy(path, device->path, path_length + 1);
copy->path = path;
name_length = strlen(device->name);
name = malloc(name_length + 1);
if (name == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
strncpy(name, device->name, name_length + 1);
copy->name = name;
if (device->uniq == NULL) {
copy->uniq = NULL;
} else {
uniq_length = strlen(device->uniq);
uniq = malloc(uniq_length + 1);
if (uniq == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
strncpy(uniq, device->uniq, uniq_length + 1);
copy->uniq = uniq;
}
copy->has_keys = device->has_keys;
copy->key_count = device->key_count;
copy->keys = malloc(sizeof(unsigned int) * copy->key_count);
if (copy->keys == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
memcpy(copy->keys, device->keys, sizeof(unsigned int) * copy->key_count);
copy->has_rels = device->has_rels;
copy->rel_count = device->rel_count;
copy->rels = malloc(sizeof(unsigned int) * copy->rel_count);
if (copy->rels == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
memcpy(copy->rels, device->rels, sizeof(unsigned int) * copy->rel_count);
copy->has_abses = device->has_abses;
copy->abs_count = device->abs_count;
copy->abses = malloc(sizeof(unsigned int) * copy->abs_count);
if (copy->abses == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
memcpy(copy->abses, device->abses, sizeof(unsigned int) * copy->abs_count);
return copy;
}
int furui_device_has_key_code(struct furui_device *device, unsigned int code) {
int i;
if (!device->has_keys) {
return 0;
}
for (i = 0; i < device->key_count; i++) {
if (device->keys[i] == code) {
return 1;
}
}
return 0;
}
int furui_device_has_rel_code(struct furui_device *device, unsigned int code) {
int i;
if (!device->has_rels) {
return 0;
}
for (i = 0; i < device->rel_count; i++) {
if (device->rels[i] == code) {
return 1;
}
}
return 0;
}
int furui_device_has_abs_code(struct furui_device *device, unsigned int code) {
int i;
if (!device->has_abses) {
return 0;
}
for (i = 0; i < device->abs_count; i++) {
if (device->abses[i] == code) {
return 1;
}
}
return 0;
}
static void print_escaped(const char *s) {
while (*s != '\0') {
if (*s == ':') {
putc('\\', stdout);
}
putc(*s, stdout);
s++;
}
}
void furui_device_print(const struct furui_device *device) {
printf("%s:", device->path);
print_escaped(device->name);
printf(":");
if (device->uniq != NULL) {
print_escaped(device->uniq);
}
printf(":");
if (device->has_keys) {
printf("key=%d", device->key_count);
}
printf(":");
if (device->has_rels) {
printf("rel=%d", device->rel_count);
}
printf(":");
if (device->has_abses) {
printf("abs=%d", device->abs_count);
}
}
#define FURUI_DEVICE_MAYBE_KEYBOARD_KEYS_SIZE 6
static int maybe_keyboard_keys[FURUI_DEVICE_MAYBE_KEYBOARD_KEYS_SIZE] = {
KEY_A, KEY_B, KEY_C,
KEY_X, KEY_Y, KEY_Z,
};
int furui_device_maybe_keyboard(struct furui_device *device) {
int i;
if (!device->has_keys) {
return 0;
}
for (i = 0; i < FURUI_DEVICE_MAYBE_KEYBOARD_KEYS_SIZE; i++) {
if (!furui_device_has_key_code(device, maybe_keyboard_keys[i])) {
return 0;
}
}
return 1;
}
#define FURUI_DEVICE_MAYBE_GAMEPAD_KEYS_SIZE 8
static int maybe_gamepad_keys[FURUI_DEVICE_MAYBE_GAMEPAD_KEYS_SIZE] = {
BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP,
BTN_A, BTN_B,
BTN_X, BTN_Y,
};
int furui_device_maybe_gamepad(struct furui_device *device) {
int i;
if (!device->has_keys) {
return 0;
}
for (i = 0; i < FURUI_DEVICE_MAYBE_GAMEPAD_KEYS_SIZE; i++) {
if (furui_device_has_key_code(device, maybe_gamepad_keys[i])) {
return 1;
}
}
return 0;
}
struct furui_device_list_element *furui_device_list_element_new(struct furui_device *device) {
struct furui_device_list_element *element;
element = malloc(sizeof(struct furui_device_list_element));
if (element == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
element->next = NULL;
element->value = device;
return element;
}
void furui_device_list_element_free(struct furui_device_list_element *element) {
if (element == NULL) {
return;
}
furui_device_free(element->value);
free(element);
}
struct furui_device_list *furui_device_list_new(void) {
struct furui_device_list *list;
list = malloc(sizeof(struct furui_device_list));
if (list == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
list->head = NULL;
list->count = 0;
return list;
}
void furui_device_list_free(struct furui_device_list *list) {
struct furui_device_list_element *element;
if (list == NULL) {
return;
}
element = list->head;
while (element != NULL) {
struct furui_device_list_element *next = element->next;
furui_device_list_element_free(element);
element = next;
}
}
unsigned int furui_device_list_get_count(struct furui_device_list *list) {
if (list == NULL) {
fprintf(stderr, "Null pointer exception at furui_device_list_get_count\n");
exit(EXIT_FAILURE);
}
return list->count;
}
struct furui_device *furui_device_list_get_element_at(struct furui_device_list *list, unsigned int index) {
struct furui_device_list_element *element;
unsigned int i = 0;
if (list == NULL) {
fprintf(stderr, "Null pointer exception at furui_device_list_get_count\n");
exit(EXIT_FAILURE);
}
element = list->head;
while (element != NULL && i != index) {
element = element->next;
i++;
}
if (element == NULL) {
return NULL;
}
return element->value;
}
void furui_device_list_add_element(struct furui_device_list *list, struct furui_device *device) {
if (list == NULL) {
fprintf(stderr, "Null pointer exception at furui_device_list_add_element\n");
exit(EXIT_FAILURE);
}
if (list->count == 0) {
struct furui_device_list_element *new_element = furui_device_list_element_new(device);
list->head = new_element;
list->count = 1;
} else {
struct furui_device_list_element *element = list->head;
struct furui_device_list_element *new_element = furui_device_list_element_new(device);
while (element->next != NULL) {
element = element->next;
}
element->next = new_element;
list->count++;
}
}
static int is_event_device(const struct dirent *d) {
return strncmp(FURUI_EVENT_PREFIX, d->d_name, strlen(FURUI_EVENT_PREFIX)) == 0;
}
struct furui_device_list *furui_device_list_scan(void) {
struct dirent **namelist;
int n;
int i;
struct furui_device_list *list = furui_device_list_new();
n = scandir(FURUI_INPUT_DIRECTORY, &namelist, is_event_device, alphasort);
if (n == -1) {
perror("scandir");
exit(EXIT_FAILURE);
}
for (i = 0; i < n; i++) {
int path_length = strlen(FURUI_INPUT_DIRECTORY) + 1 + strlen(namelist[i]->d_name) + 1;
char path[path_length];
struct furui_device *device;
snprintf(path, path_length, "%s/%s", FURUI_INPUT_DIRECTORY, namelist[i]->d_name);
device = furui_device_new(path);
if (device == NULL) {
continue;
}
furui_device_list_add_element(list, device);
}
free(namelist);
return list;
}
void furui_device_list_print(struct furui_device_list *list) {
struct furui_device_list_element *element;
int index = 0;
if (list == NULL) {
fprintf(stderr, "Null pointer exception at furui_device_list_print\n");
exit(EXIT_FAILURE);
}
element = list->head;
while (element != NULL) {
printf("%d:", index);
furui_device_print(element->value);
printf("\n");
element = element->next;
index++;
}
}
void furui_device_list_filter(struct furui_device_list *list, int (*filter)(struct furui_device *)) {
struct furui_device_list_element *prev;
struct furui_device_list_element *element;
if (list == NULL) {
return;
}
prev = NULL;
element = list->head;
while (element != NULL) {
if (!filter(element->value)) {
struct furui_device_list_element *to_free = element;
if (prev == NULL) {
list->head = element->next;
} else {
prev->next = element->next;
}
element = element->next;
furui_device_list_element_free(to_free);
list->count--;
} else {
prev = element;
element = element->next;
}
}
}