aboutsummaryrefslogtreecommitdiffhomepage
path: root/evdev.c
blob: 6b9d49418cf485dfbeab34002260f7b884f4441c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#define _GNU_SOURCE

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.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"

struct applause_evdev {
	int			fd;
	jack_ringbuffer_t	*buffer;
	pthread_t		thread;
};

const char *
applause_dirent_name(const struct dirent *entry)
{
	return entry->d_name;
}

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