aboutsummaryrefslogtreecommitdiffstats
path: root/libgamepad_list_superdevices.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgamepad_list_superdevices.c')
-rw-r--r--libgamepad_list_superdevices.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/libgamepad_list_superdevices.c b/libgamepad_list_superdevices.c
new file mode 100644
index 0000000..f8c7c4d
--- /dev/null
+++ b/libgamepad_list_superdevices.c
@@ -0,0 +1,77 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+int
+libgamepad_list_superdevices(char ***devicesp, size_t *ndevicesp)
+{
+ char path[PATH_MAX], target[PATH_MAX]; /* TODO do not use PATH_MAX */
+ int dirfd;
+ DIR *dir;
+ struct dirent *f;
+ ssize_t r;
+ char **paths = NULL;
+ size_t n = 0, i;
+ void *new;
+ int saved_errno = errno;
+
+ *devicesp = NULL;
+ *ndevicesp = 0;
+
+ dirfd = open("/sys/class/input/", O_DIRECTORY);
+ if (dirfd < 0)
+ return -1;
+
+ dir = fdopendir(dirfd);
+ if (!dir) {
+ close(dirfd);
+ return -1;
+ }
+
+ while (errno = 0, (f = readdir(dir))) {
+ if (!strncmp(f->d_name, "event", 5) && isdigit(f->d_name[5])) {
+ stpcpy(stpcpy(stpcpy(path, "/sys/class/input/"), f->d_name), "/device/device");
+ dirfd = open(path, O_PATH);
+ if (dirfd < 0)
+ continue;
+ sprintf(path, "/dev/fd/%i", dirfd);
+ r = readlink(path, target, sizeof(target) - 1);
+ close(dirfd);
+ if (r < 0)
+ continue;
+ target[r] = '\0';
+ if (paths)
+ for (i = 0; i < n; i++)
+ if (!strcmp(paths[i], target))
+ goto next;
+ stpcpy(stpcpy(path, target), "/input");
+ if (access(path, F_OK))
+ continue;
+ new = realloc(paths, (n + 1) * sizeof(*paths));
+ if (!new)
+ goto fail;
+ paths = new;
+ paths[n] = strdup(target);
+ if (!paths[n])
+ goto fail;
+ n += 1;
+ }
+ next:;
+ }
+
+ if (errno)
+ goto fail;
+ closedir(dir);
+
+ errno = saved_errno;
+ *devicesp = paths;
+ *ndevicesp = n;
+ return 0;
+
+fail:
+ for (i = 0; i < n; i++)
+ free(paths[i]);
+ free(paths);
+ closedir(dir);
+ return -1;
+}