aboutsummaryrefslogtreecommitdiffstats
path: root/libgamepad_generate_sync_events.c
blob: 97a2bebe340e601ba372482383de375993c6f85a (plain) (blame)
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
/* See LICENSE file for copyright and license details. */
#include "common.h"

#define DEFER_EINTR(ERROR_CONDITION, GOTO_ON_FAILURE)\
	DEFER_EINTR_(device, ERROR_CONDITION, GOTO_ON_FAILURE)


static int
require_evbuf_size(struct libgamepad_device *device, size_t n)
{
	void *new;
	if (n < device->internals->evbuf_size)
		return 0;
	if (n > SIZE_MAX / sizeof(*device->internals->evbuf)) {
		errno = ENOMEM;
		return -1;
	}
	new = realloc(device->internals->evbuf, n * sizeof(*device->internals->evbuf));
	if (!new)
		return -1;
	device->internals->evbuf = new;
	device->internals->evbuf_size = n;
	return 0;
}


int
libgamepad_generate_sync_events(struct libgamepad_device *device)
{
	unsigned long int bits[ELEMS_REQUIRED(ELEMSOF(device->button_map), long int)];
	struct input_absinfo abs;
	size_t queued = device->internals->ev_queued;
	int16_t j;
	unsigned int i, max;
	int r, saved_errno = errno;

	if (device->internals->deferred_error) {
		errno = device->internals->deferred_error;
		device->internals->deferred_error = 0;
		return -1;
	}

	if (device->nbuttons) {
		DEFER_EINTR((r = ioctl(device->fd, EVIOCGKEY(sizeof(bits)), bits)) < 0, fail);
		max = (unsigned int)r * 8;
		for (i = 0; i < max && i < ELEMSOF(device->button_map); i++) {
			j = device->button_map[i];
			if (j >= 0 && GETBIT(bits, i) != GETBIT(device->internals->buttons, (uint16_t)j)) {
				if (require_evbuf_size(device, queued + 1))
					goto fail;
				device->internals->evbuf[queued].input_event_sec = 0;
				device->internals->evbuf[queued].input_event_usec = 0;
				device->internals->evbuf[queued].type = EV_KEY;
				device->internals->evbuf[queued].code = (uint16_t)i;
				device->internals->evbuf[queued].value = (int32_t)GETBIT(bits, i);
				queued += 1;
			}
		}
	}

	for (i = 0; i < device->nabsolute_axes; i++) {
		DEFER_EINTR(ioctl(device->fd, EVIOCGABS((unsigned int)device->absolute_axes[i]), &abs) < 0, fail);
		if (device->internals->absinfo[i].value != abs.value) {
			if (require_evbuf_size(device, queued + 1))
				goto fail;
			device->internals->evbuf[queued].input_event_sec = 0;
			device->internals->evbuf[queued].input_event_usec = 0;
			device->internals->evbuf[queued].type = EV_ABS;
			device->internals->evbuf[queued].code = device->absolute_axes[i];
			device->internals->evbuf[queued].value = abs.value;
			queued += 1;
		}
	}
	/* TODO synchronise multitouch state */

	if (queued > device->internals->ev_queued) {
			if (require_evbuf_size(device, queued + 1))
				goto fail;
			device->internals->evbuf[queued].input_event_sec = 0;
			device->internals->evbuf[queued].input_event_usec = 0;
			device->internals->evbuf[queued].type = EV_SYN;
			device->internals->evbuf[queued].code = SYN_REPORT;
			device->internals->evbuf[queued].value = 0;
			queued += 1;
	}

	device->internals->require_sync = 0;
	device->internals->ev_queued = queued;
	errno = saved_errno;
	return 0;

fail:
	return -1;
}