544 lines
12 KiB
C
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;
|
|
}
|
|
}
|
|
}
|