diff options
Diffstat (limited to 'libgamepad_generate_sync_events.c')
-rw-r--r-- | libgamepad_generate_sync_events.c | 94 |
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; +} |