/* 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; }