aboutsummaryrefslogtreecommitdiffstats
path: root/libgamepad_generate_sync_events.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgamepad_generate_sync_events.c')
-rw-r--r--libgamepad_generate_sync_events.c94
1 files changed, 94 insertions, 0 deletions
diff --git a/libgamepad_generate_sync_events.c b/libgamepad_generate_sync_events.c
new file mode 100644
index 0000000..97a2beb
--- /dev/null
+++ b/libgamepad_generate_sync_events.c
@@ -0,0 +1,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;
+}