aboutsummaryrefslogtreecommitdiffhomepage
path: root/evdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'evdev.c')
-rw-r--r--evdev.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/evdev.c b/evdev.c
new file mode 100644
index 0000000..2d179d2
--- /dev/null
+++ b/evdev.c
@@ -0,0 +1,133 @@
+#define _GNU_SOURCE
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <linux/input.h>
+
+#include <pthread.h>
+
+#include <jack/ringbuffer.h>
+
+#include "evdev.h"
+
+typedef struct applause_evdev {
+ int fd;
+ jack_ringbuffer_t *buffer;
+ pthread_t thread;
+} applause_evdev;
+
+char *
+applause_evdev_getname(const char *node)
+{
+ int rc, fd;
+
+ fd = open(node, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ char name[256];
+ rc = ioctl(fd, EVIOCGNAME(sizeof(name)), name);
+ close(fd);
+
+ return rc < 0 ? NULL : strdup(name);
+}
+
+static void *
+applause_evdev_thread_cb(void *userdata)
+{
+ applause_evdev *evdev = userdata;
+
+ for (;;) {
+ struct input_event event;
+ applause_evdev_sample sample;
+
+ /*
+ * FIXME: How to detect thread cancellation reliably?
+ */
+ if (read(evdev->fd, &event, sizeof(event)) != sizeof(event))
+ break;
+
+ /*
+ * FIXME: Why do we get one "empty" event after each successful read?
+ */
+ if (event.type == 0)
+ continue;
+
+ sample.type = event.type;
+ sample.code = event.code;
+ sample.value = event.value;
+ jack_ringbuffer_write(evdev->buffer, (const char *)&sample, sizeof(sample));
+ }
+
+ /* never reached */
+ return NULL;
+}
+
+applause_evdev *
+applause_evdev_new(const char *node, bool grab)
+{
+ applause_evdev *evdev = calloc(1, sizeof(applause_evdev));
+ if (!evdev)
+ return NULL;
+
+ evdev->fd = open(node, O_RDONLY);
+ if (evdev->fd < 0)
+ goto error;
+
+ /*
+ * This will fail if the device is already grabbed
+ * which makes sense since you cannot receive events for such a device.
+ */
+ if (grab && ioctl(evdev->fd, EVIOCGRAB, true))
+ goto error;
+
+ evdev->buffer = jack_ringbuffer_create(sizeof(applause_evdev_sample)*1024);
+ if (!evdev->buffer)
+ goto error;
+
+ if (pthread_create(&evdev->thread, NULL, applause_evdev_thread_cb, evdev))
+ goto error;
+
+ return evdev;
+
+error:
+ if (evdev->buffer)
+ jack_ringbuffer_free(evdev->buffer);
+ if (evdev->fd > 0)
+ close(evdev->fd);
+ free(evdev);
+ return NULL;
+}
+
+void
+applause_evdev_pull(applause_evdev *evdev, applause_evdev_sample *sample)
+{
+ memset(sample, 0, sizeof(*sample));
+ jack_ringbuffer_read(evdev->buffer, (char *)sample, sizeof(*sample));
+}
+
+void
+applause_evdev_free(applause_evdev *evdev)
+{
+ /*
+ * NOTE: It's important to support evdev == NULL so that applause_evdev_free()
+ * can be passed safely to ffi.gc().
+ */
+ if (!evdev)
+ return;
+
+ pthread_cancel(evdev->thread);
+ pthread_join(evdev->thread, NULL);
+ jack_ringbuffer_free(evdev->buffer);
+ close(evdev->fd);
+ free(evdev);
+}