aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--common.h21
-rw-r--r--config.mk4
-rw-r--r--libgamepad.h180
-rw-r--r--libgamepad_close_device.c16
-rw-r--r--libgamepad_create_attachment_monitor.c2
-rw-r--r--libgamepad_drain__.c55
-rw-r--r--libgamepad_drain_events.c18
-rw-r--r--libgamepad_generate_sync_events.c94
-rw-r--r--libgamepad_get_absolute_axis_name.c2
-rw-r--r--libgamepad_get_button_name.c2
-rw-r--r--libgamepad_get_relative_axis_name.c2
-rw-r--r--libgamepad_next_event.c145
-rw-r--r--libgamepad_open_device.c53
-rw-r--r--test-input.c28
-rw-r--r--test-visual.c6
16 files changed, 502 insertions, 129 deletions
diff --git a/Makefile b/Makefile
index 0ef9633..24dc048 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,10 @@ OBJ =\
libgamepad_create_attachment_monitor.o\
libgamepad_destroy_attachment_monitor.o\
libgamepad_disable_force_feedback_autocenter.o\
+ libgamepad_drain__.o\
+ libgamepad_drain_events.o\
libgamepad_find_sound_devices.o\
+ libgamepad_generate_sync_events.o\
libgamepad_get_absolute_axis_by_name.o\
libgamepad_get_absolute_axis_info.o\
libgamepad_get_absolute_axis_name.o\
diff --git a/common.h b/common.h
index 9889858..0458063 100644
--- a/common.h
+++ b/common.h
@@ -8,6 +8,7 @@
#include <fcntl.h>
#include <limits.h>
#include <math.h>
+#include <poll.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -17,7 +18,6 @@
#include <time.h>
#include <unistd.h>
-#include <libevdev/libevdev.h>
#include <libsha2.h>
#include <libudev.h>
@@ -34,8 +34,19 @@
#define ELEMSOF(A) (sizeof(A) / sizeof(*(A)))
#define BITSOF(T) (8 * sizeof(T))
+#define XORBIT(ARR, I) (READ_(ARR, I) ^= 1 << SHIFT_(ARR, I))
#define SETBIT(ARR, I) (READ_(ARR, I) |= 1 << SHIFT_(ARR, I))
#define GETBIT(ARR, I) ((READ_(ARR, I) >> SHIFT_(ARR, I)) & 1)
+#define ELEMS_REQUIRED(BITS, TYPE) ((BITS + BITSOF(TYPE) - 1) / BITSOF(TYPE))
+
+#define DEFER_EINTR_(DEVICE, ERROR_CONDITION, GOTO_ON_FAILURE)\
+ do {\
+ while (ERROR_CONDITION) {\
+ if (errno != EINTR)\
+ goto GOTO_ON_FAILURE;\
+ (DEVICE)->internals->deferred_error = EINTR;\
+ }\
+ } while (0)
struct libgamepad_attachment_monitor {
@@ -45,11 +56,14 @@ struct libgamepad_attachment_monitor {
struct libgamepad_device_internals {
+ int deferred_error;
int close_fd;
int require_sync;
- struct libevdev *dev;
struct input_absinfo *absinfo;
uint8_t *buttons;
+ struct input_event *evbuf;
+ size_t evbuf_size;
+ size_t ev_queued;
};
@@ -64,3 +78,6 @@ extern const char *libgamepad_absolute_axis_names__[
extern const char *libgamepad_relative_axis_names__[
#include "relative-axis.count"
];
+
+
+int libgamepad_drain__(struct libgamepad_device *device);
diff --git a/config.mk b/config.mk
index 7e9fb7c..8ace118 100644
--- a/config.mk
+++ b/config.mk
@@ -8,7 +8,7 @@ CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
CFLAGS = -Wall -g
LDFLAGS =
-LIBS_CFLAGS = $$(pkg-config --cflags libevdev)
-LIBS_LDFLAGS = $$(pkg-config --libs libevdev) -ludev -lm -lsha2
+LIBS_CFLAGS =
+LIBS_LDFLAGS = -ludev -lm -lsha2
INPUT_EVENT_CODES_H = /usr/include/linux/input-event-codes.h
diff --git a/libgamepad.h b/libgamepad.h
index 5cee653..7a046a3 100644
--- a/libgamepad.h
+++ b/libgamepad.h
@@ -12,8 +12,6 @@
#include <time.h>
#include <unistd.h>
-#include <libevdev/libevdev.h>
-
#if defined(__GNUC__)
# define LIBGAMEPAD_PURE__ __attribute__((__pure__))
@@ -144,7 +142,7 @@ enum libgamepad_game_controller {
*/
LIBGAMEPAD_CONTROLLER_OTHER,
- /* TODO force feedback capabilities */
+ /* TODO Document force feedback capabilities */
/**
* Amazon Luna Controller
@@ -1328,17 +1326,56 @@ enum libgamepad_type {
*/
enum libgamepad_input_type {
/**
+ * Signals thatevents where dropped because
+ * the application did not read fast enough
+ *
+ * When received, the next call to `libgamepad_next_event`
+ * will drain the event queue and generate synchronisation
+ * events (which have the timestamp set to zero) to bring
+ * the applications state of the buttons/keys and absolute
+ * axes up to date
+ */
+ LIBGAMEPAD_EVENT_DROP,
+
+ /**
+ * Signals that all previous events, from the
+ * last `LIBGAMEPAD_EVENT_END` or `LIBGAMEPAD_EVENT_DROP`
+ * where part of the same report from the input device
+ *
+ * This event will always be sent, however it may have
+ * been dropped when a `LIBGAMEPAD_EVENT_DROP` is sent
+ */
+ LIBGAMEPAD_EVENT_END,
+
+ /**
* Button/key
+ *
+ * Expectation when `LIBGAMEPAD_EVENT_DROP` has been
+ * received: a full press and release, or release and
+ * press, may be missing the if read interval is longer
+ * than such an event would span
*/
LIBGAMEPAD_BUTTON,
/**
* Absolute axis
+ *
+ * Expectation when `LIBGAMEPAD_EVENT_DROP` has been
+ * received: less smooth transition between two states,
+ * and quick movements in one direction and back may
+ * be missing, but only such movements that are sorter
+ * than the read duration
*/
LIBGAMEPAD_ABSOLUTE_AXIS,
/**
* Relative axis
+ *
+ * Expectation when `LIBGAMEPAD_EVENT_DROP` has been
+ * received: movement details are lost and calculate
+ * position is off (it is possible that the driver
+ * sends synchronisations event to adjust for the
+ * position)
*/
LIBGAMEPAD_RELATIVE_AXIS
};
@@ -1349,29 +1386,30 @@ enum libgamepad_input_type {
*/
struct libgamepad_input_event {
/**
- * The affect input type
+ * The affected input type
*/
enum libgamepad_input_type type;
/**
- * Whether the event was polled in a
- * synchronisation read
- */
- short int sync_event;
-
- /**
- * The button/key or axis affect by the event
+ * The button/key or axis affected by the event
+ *
+ * Not set if `.type` is `LIBGAMEPAD_EVENT_DROP`
*/
uint16_t code;
/**
* The new value on the button/key or axis,
* or the delta if on a relative axis
+ *
+ * Not set if `.type` is `LIBGAMEPAD_EVENT_DROP`
*/
int32_t value;
/**
* Event timestamp
+ *
+ * Set to zero if not supported by the driver
+ * or if the event generated by the library
*/
struct timeval time;
};
@@ -1454,6 +1492,13 @@ struct libgamepad_device {
int fd;
/**
+ * Specifies whether the library shall automatically call
+ * `libgamepad_generate_sync_events` if events are dropped
+ * or the event queue is manually drained
+ */
+ int auto_sync;
+
+ /**
* Bus type the device is connect via, see BUS_-prefixed
* constants in <linux/input.h>
*/
@@ -1663,6 +1708,11 @@ int libgamepad_get_attachment_event(LIBGAMEPAD_ATTACHMENT_MONITOR *, char **, si
/**
* Open a subdevice
*
+ * This function may temporarily modify the file status
+ * flags (which affects all duplicate file descriptors
+ * share the same open file descriptor) of `dirfd` if
+ * `path` is `NULL` or empty
+ *
* @param devicep Output parameter for the device information and handles;
* deallocated with `libgamepad_close_device`
* @param dirfd File descriptor to path that `path` is relative to
@@ -1678,40 +1728,83 @@ int libgamepad_get_attachment_event(LIBGAMEPAD_ATTACHMENT_MONITOR *, char **, si
* @return 0 on success, -1 on failure
*
* This function may fail for any reason specified for
- * malloc(3), openat(3) or libevdev_new_from_fd(3)
+ * realloc(3), openat(3), ioctl(3), or read(3)
*/
int libgamepad_open_device(struct libgamepad_device *, int, const char *, int);
/**
* Close a subdevice
*
- * @param device Device information and handles, may be `NULL`
+ * @param device Device information and handles, may be `NULL`
+ * @return Normally 0, -1 if there was a deferred error
*/
-void libgamepad_close_device(struct libgamepad_device *);
+int libgamepad_close_device(struct libgamepad_device *);
/**
* Get the next event on a subdevice
*
- * NB! events may be queued internally (by libevdev), therefore
- * an application must either call this function in a loop in
- * its own thread, or have the device opened in non-blocking
- * mode and even the poll the device's file descriptor for
- * read-readyness and when it is ready clal this function in
- * a loop until it sets `errno` to `EAGAIN`.
+ * This function may temporarily modify the file status
+ * flags of the open file decriptor it uses to access
+ * the device
*
* @param device Device to read an event from
- * @param eventp Output parameter for the event
- * @return 1 if an event was returned,
- * 0 if no event was returned,
- * -1 on failure
+ * @param events Output buffer for the events (will be in order)
+ * @param max The number of elements `events` fit
+ * (may be 0 for error condition checking)
+ * @return The number of returned events (may be more or
+ * less than actually read), -1 on failure
*
* This function may fail for any reason specified for
- * libevdev_next_event(3), including:
+ * realloc(3) and read(3), including:
* - EAGAIN no more data currently available (non-blocking mode)
* - EINTR system call was interrupted, try again
+ * (may be deferred to `libgamepad_close_device`)
* - ENODEV device has been unplugged or access has been revoked
*/
-int libgamepad_next_event(struct libgamepad_device *, struct libgamepad_input_event *);
+ssize_t libgamepad_next_event(struct libgamepad_device *, struct libgamepad_input_event *, size_t);
+
+/**
+ * Remove all events on a subdevice queued by kernel
+ * and queue synchronisation events so that the application
+ * will have (after reading them) up to date button/key and
+ * absolute axis states
+ *
+ * This function is normally used internally, but can be
+ * useful after calling `libgamepad_set_clock`
+ *
+ * This function may temporarily modify the file status
+ * flags of the open file decriptor it uses to access
+ * the device
+ *
+ * @param device Device to drain queue for
+ * @return 0 on success, -1 on failure
+ *
+ * This function may fail for any reason specified for
+ * realloc(3) and read(3), including:
+ * - ENODEV device has been unplugged or access has been revoked
+ * except for EINTR which is deferred to the next call to
+ * `libgamepad_next_event` or `libgamepad_close_device`
+ * (whichever comes first) for the same device, and EAGAIN
+ */
+int libgamepad_drain_events(struct libgamepad_device *);
+
+/**
+ * Generates any events needed to keep the applications
+ * button/key and absolute axes states up to date
+ *
+ * This function is normally used internally, but can be
+ * useful after calling `libgamepad_drain_events` if
+ * automatic generation of synchronisation events have
+ * been disabled by modifying `device->auto_sync`
+ *
+ * This function may temporarily modify the file status
+ * flags of the open file decriptor it uses to access
+ * the device
+ *
+ * @param device Device to synchronise
+ * @return 0 on success, -1 on failure
+ */
+int libgamepad_generate_sync_events(struct libgamepad_device *);
/**
@@ -1733,7 +1826,7 @@ int libgamepad_next_event(struct libgamepad_device *, struct libgamepad_input_ev
* @param code The button/key
* @return The button/key's name, `NULL` if not found
*/
-const char *libgamepad_get_button_name(const struct libgamepad_device *, uint16_t);
+LIBGAMEPAD_PURE__ const char *libgamepad_get_button_name(const struct libgamepad_device *, uint16_t);
/**
* Get the name of an absolute axis
@@ -1754,7 +1847,7 @@ const char *libgamepad_get_button_name(const struct libgamepad_device *, uint16_
* @param code The axis
* @return The axis' name, `NULL` if not found
*/
-const char *libgamepad_get_absolute_axis_name(const struct libgamepad_device *, uint16_t);
+LIBGAMEPAD_PURE__ const char *libgamepad_get_absolute_axis_name(const struct libgamepad_device *, uint16_t);
/**
* Get the name of a relative axis
@@ -1775,7 +1868,7 @@ const char *libgamepad_get_absolute_axis_name(const struct libgamepad_device *,
* @param code The axis
* @return The axis' name, `NULL` if not found
*/
-const char *libgamepad_get_relative_axis_name(const struct libgamepad_device *, uint16_t);
+LIBGAMEPAD_PURE__ const char *libgamepad_get_relative_axis_name(const struct libgamepad_device *, uint16_t);
/**
* Get a button/key code from it's name
@@ -1875,6 +1968,10 @@ libgamepad_get_device_has_ff_effect(const struct libgamepad_device *device, uint
* libgamepad reads the current state when a device
* is opened.
*
+ * This information is not necessarily up to date
+ * before the first `LIBGAMEPAD_EVENT_END` has been
+ * reported
+ *
* @param device The device to retrieve the information for
* @param code The button/key
* @return 1 if the button/key is pressed down,
@@ -1894,12 +1991,21 @@ int libgamepad_get_button_is_pressed(struct libgamepad_device *, uint16_t);
* libgamepad reads the current state when a device
* is opened.
*
+ * The value of `.value` in the returned pointer is
+ * not necessarily up to date before the first
+ * `LIBGAMEPAD_EVENT_END` has been reported
+ *
* @param device The device to retrieve the information for
* @param code The axis
- * @return Information about the axis
+ * @return Information about the axis; this is a pointer
+ * stored inside `device`, some function calls
+ * may alter the contents of invalidate the pointer,
+ * `libgamepad_close_device(device)` will always
+ * invalidate the pointer and it is expected that
+ * `libgamepad_next_event` often alters `.value`
*/
-LIBGAMEPAD_PURE__ const struct input_absinfo *libgamepad_get_absolute_axis_info(struct libgamepad_device *, uint16_t);
-/* `struct input_absinfo` is defined in <linux/input.h> */
+LIBGAMEPAD_PURE__ /* `struct input_absinfo` is defined in <linux/input.h> */
+const struct input_absinfo *libgamepad_get_absolute_axis_info(struct libgamepad_device *, uint16_t);
/**
@@ -1940,6 +2046,16 @@ libgamepad_ungrab(struct libgamepad_device *device)
/**
* Set the clock that event timestamps shall be reported in
*
+ * If your application depends on new events using the
+ * selected clock (if any at all), call `libgamepad_drain_events`
+ * after this function
+ *
+ * Support clocks are (in Linux 5.18):
+ * - CLOCK_REALTIME
+ * - CLOCK_MONOTONIC
+ * - CLOCK_BOOTTIME
+ * As usual, CLOCK_MONOTONIC_RAW is not supported
+ *
* @param device The device to configure
* @param clockid The clock to use on event timestamps
* @return 0 on success, -1 on failure
diff --git a/libgamepad_close_device.c b/libgamepad_close_device.c
index 273c7f8..896d22e 100644
--- a/libgamepad_close_device.c
+++ b/libgamepad_close_device.c
@@ -2,17 +2,20 @@
#include "common.h"
-void
+int
libgamepad_close_device(struct libgamepad_device *device)
{
+ int deferred_error = 0;
if (device) {
if (device->internals) {
+ deferred_error = device->internals->deferred_error;
if (device->internals->close_fd)
- close(device->fd);
- if (device->internals->dev)
- libevdev_free(device->internals->dev);
+ if (close(device->fd))
+ if (!deferred_error)
+ deferred_error = errno;
free(device->internals->absinfo);
free(device->internals->buttons);
+ free(device->internals->evbuf);
free(device->internals);
device->internals = NULL;
}
@@ -28,5 +31,10 @@ libgamepad_close_device(struct libgamepad_device *device)
device->name = NULL;
device->unique_id = NULL;
device->physical_location = NULL;
+ if (deferred_error) {
+ errno = deferred_error;
+ return -1;
+ }
}
+ return 0;
}
diff --git a/libgamepad_create_attachment_monitor.c b/libgamepad_create_attachment_monitor.c
index 20423b0..2776476 100644
--- a/libgamepad_create_attachment_monitor.c
+++ b/libgamepad_create_attachment_monitor.c
@@ -5,7 +5,7 @@
int
libgamepad_create_attachment_monitor(LIBGAMEPAD_ATTACHMENT_MONITOR **monitorp)
{
- *monitorp = calloc(1, sizeof(*monitorp));
+ *monitorp = calloc(1, sizeof(**monitorp));
if (!*monitorp)
return -1;
diff --git a/libgamepad_drain__.c b/libgamepad_drain__.c
new file mode 100644
index 0000000..38e898e
--- /dev/null
+++ b/libgamepad_drain__.c
@@ -0,0 +1,55 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+#define DEFER_EINTR(ERROR_CONDITION)\
+ DEFER_EINTR_(device, ERROR_CONDITION, fail)
+
+
+int
+libgamepad_drain__(struct libgamepad_device *device)
+{
+ struct input_event buf[4 * 8];
+ int flags, saved_errno = errno;
+ ssize_t r;
+
+ if (device->internals->deferred_error) {
+ errno = device->internals->deferred_error;
+ device->internals->deferred_error = 0;
+ return -1;
+ }
+
+ flags = fcntl(device->fd, F_GETFL);
+ if (flags < 0)
+ return -1;
+
+ if (!(flags & O_NONBLOCK))
+ if (fcntl(device->fd, F_GETFL, flags | O_NONBLOCK) < 0)
+ goto fail;
+
+ for (;;) {
+ r = read(device->fd, buf, sizeof(buf));
+ if (r < (ssize_t)sizeof(buf)) {
+ if (r > 0)
+ break;
+ else if (!r)
+ goto nodev;
+ else if (errno == EAGAIN)
+ break;
+ else if (errno == EINTR)
+ device->internals->deferred_error = EINTR;
+ else
+ goto fail;
+ }
+ }
+
+ if (!(flags & O_NONBLOCK))
+ DEFER_EINTR(fcntl(device->fd, F_SETFL, flags) < 0);
+
+ errno = saved_errno;
+ return 0;
+
+nodev:
+ errno = ENODEV;
+fail:
+ return -1;
+}
diff --git a/libgamepad_drain_events.c b/libgamepad_drain_events.c
new file mode 100644
index 0000000..b68ecad
--- /dev/null
+++ b/libgamepad_drain_events.c
@@ -0,0 +1,18 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+int
+libgamepad_drain_events(struct libgamepad_device *device)
+{
+ if (libgamepad_drain__(device))
+ return -1;
+ device->internals->ev_queued = 0;
+
+ if (device->auto_sync) {
+ return libgamepad_generate_sync_events(device);
+ } else {
+ device->internals->require_sync = 0;
+ return 0;
+ }
+}
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;
+}
diff --git a/libgamepad_get_absolute_axis_name.c b/libgamepad_get_absolute_axis_name.c
index 79f216c..eddb4a2 100644
--- a/libgamepad_get_absolute_axis_name.c
+++ b/libgamepad_get_absolute_axis_name.c
@@ -3,7 +3,7 @@
const char *
-libgamepad_get_absolute_axis_name(const struct libgamepad_device *device, uint16_t code) /* will not be pure */
+libgamepad_get_absolute_axis_name(const struct libgamepad_device *device, uint16_t code)
{
if (device)
return NULL;
diff --git a/libgamepad_get_button_name.c b/libgamepad_get_button_name.c
index 031e528..37ad9ac 100644
--- a/libgamepad_get_button_name.c
+++ b/libgamepad_get_button_name.c
@@ -3,7 +3,7 @@
const char *
-libgamepad_get_button_name(const struct libgamepad_device *device, uint16_t code) /* will not be pure */
+libgamepad_get_button_name(const struct libgamepad_device *device, uint16_t code)
{
if (device)
return NULL;
diff --git a/libgamepad_get_relative_axis_name.c b/libgamepad_get_relative_axis_name.c
index 5a16945..3e67bbd 100644
--- a/libgamepad_get_relative_axis_name.c
+++ b/libgamepad_get_relative_axis_name.c
@@ -3,7 +3,7 @@
const char *
-libgamepad_get_relative_axis_name(const struct libgamepad_device *device, uint16_t code) /* will not be pure */
+libgamepad_get_relative_axis_name(const struct libgamepad_device *device, uint16_t code)
{
if (device)
return NULL;
diff --git a/libgamepad_next_event.c b/libgamepad_next_event.c
index c74d641..b9d8986 100644
--- a/libgamepad_next_event.c
+++ b/libgamepad_next_event.c
@@ -2,56 +2,125 @@
#include "common.h"
-int
-libgamepad_next_event(struct libgamepad_device *device, struct libgamepad_input_event *eventp)
+static int
+update_button(struct libgamepad_device *device, uint16_t code, int32_t value)
{
- int r;
- struct input_event ev;
+ int16_t index;
+ if (code > ELEMSOF(device->button_map))
+ return 0;
+ index = device->button_map[code];
+ if (index < 0 || GETBIT(device->internals->buttons, (size_t)index) == value)
+ return 1;
+ XORBIT(device->internals->buttons, (size_t)index);
+ return 1;
+}
+
+
+static int
+update_absolute_axis(struct libgamepad_device *device, uint16_t code, int32_t value)
+{
+ int16_t index;
+ if (code > ELEMSOF(device->absolute_axis_map))
+ return 0;
+ index = device->absolute_axis_map[code];
+ if (index < 0 || device->internals->absinfo[index].value == value)
+ return 0;
+ device->internals->absinfo[index].value = value;
+ return 1;
+}
+
+
+ssize_t
+libgamepad_next_event(struct libgamepad_device *device, struct libgamepad_input_event *events, size_t max)
+{
+ size_t i, dequeued = 0;
+ void *new;
+ ssize_t r;
+ struct input_event *ev;
+
+ if (device->internals->deferred_error) {
+ errno = device->internals->deferred_error;
+ device->internals->deferred_error = 0;
+ return -1;
+ }
+
+ if (device->internals->require_sync)
+ if (libgamepad_drain_events(device))
+ return -1;
- if (!device->internals->require_sync) {
- r = libevdev_next_event(device->internals->dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
- if (r < 0) {
- errno = -r;
+ if (max > device->internals->evbuf_size) {
+ if (max > SIZE_MAX / sizeof(*device->internals->evbuf)) {
+ errno = ENOMEM;
return -1;
}
- } else {
- r = libevdev_next_event(device->internals->dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
- if (r == -EAGAIN) {
- device->internals->require_sync = 0; /* yes, this is how it is document */
- return 0;
- } else if (r < 0) {
- errno = -r;
+ new = realloc(device->internals->evbuf, max * sizeof(*device->internals->evbuf));
+ if (!new)
+ return -1;
+ device->internals->evbuf = new;
+ device->internals->evbuf_size = max;
+ }
+
+ dequeued = device->internals->ev_queued < max ? device->internals->ev_queued : max;
+ if (!dequeued || dequeued < max) {
+ r = read(device->fd, &device->internals->evbuf[dequeued], (max - dequeued) * sizeof(*device->internals->evbuf));
+ if (r <= 0) {
+ if (!r && (max - dequeued))
+ errno = ENODEV;
+ return -1;
+ } else if ((size_t)r % sizeof(*device->internals->evbuf)) {
+ errno = EBADF;
return -1;
}
- if (r != LIBEVDEV_READ_STATUS_SYNC) /* just to be safe */
- device->internals->require_sync = 0;
+ } else {
+ r = 0;
}
+ r += (ssize_t)(dequeued * sizeof(*device->internals->evbuf));
- eventp->sync_event = (r == LIBEVDEV_READ_STATUS_SYNC);
- eventp->code = ev.code;
- eventp->value = ev.value;
- eventp->time.tv_sec = ev.input_event_sec;
- eventp->time.tv_usec = ev.input_event_usec;
+ i = 0;
+ for (ev = device->internals->evbuf; r; r -= (ssize_t)sizeof(*device->internals->evbuf), ev++) {
+ events[i].code = ev->code;
+ events[i].value = ev->value;
+ events[i].time.tv_sec = ev->input_event_sec;
+ events[i].time.tv_usec = ev->input_event_usec;
- switch (ev.type) {
- case EV_KEY:
- eventp->type = LIBGAMEPAD_BUTTON;
- return 1;
+ switch (ev->type) {
+ case EV_SYN:
+ events[i].code = 0;
+ events[i].value = 0;
+ if (ev->code == SYN_REPORT) {
+ events[i++].type = LIBGAMEPAD_EVENT_END;
+ } else if (ev->code == SYN_DROPPED) {
+ device->internals->require_sync = device->auto_sync;
+ events[i++].type = LIBGAMEPAD_EVENT_DROP;
+ goto out;
+ }
+ break;
- case EV_ABS:
- eventp->type = LIBGAMEPAD_ABSOLUTE_AXIS;
- return 1;
+ case EV_KEY:
+ ev->value = !!ev->value;
+ if (update_button(device, ev->code, ev->value))
+ events[i++].type = LIBGAMEPAD_BUTTON;
+ break;
- case EV_REL:
- eventp->type = LIBGAMEPAD_RELATIVE_AXIS;
- return 1;
+ case EV_REL:
+ events[i++].type = LIBGAMEPAD_RELATIVE_AXIS;
+ break;
- case EV_SYN:
- if (ev.code == SYN_DROPPED)
- device->internals->require_sync = 1;
- return 0;
+ case EV_ABS:
+ if (update_absolute_axis(device, ev->code, ev->value))
+ events[i++].type = LIBGAMEPAD_ABSOLUTE_AXIS;
+ break;
- default:
- return 0;
+ default:
+ break;
+ }
+ }
+
+out:
+ if (dequeued) {
+ device->internals->ev_queued -= dequeued;
+ memmove(&device->internals->evbuf[0], &device->internals->evbuf[dequeued],
+ device->internals->ev_queued * sizeof(*device->internals->evbuf));
}
+ return (ssize_t)i;
}
diff --git a/libgamepad_open_device.c b/libgamepad_open_device.c
index 22163a4..27c5600 100644
--- a/libgamepad_open_device.c
+++ b/libgamepad_open_device.c
@@ -1,10 +1,12 @@
/* See LICENSE file for copyright and license details. */
#include "common.h"
-#define ELEMS_REQUIRED(BITS, TYPE) ((BITS + BITSOF(TYPE) - 1) / BITSOF(TYPE))
#define MAX2(A, B) ((A) > (B) ? (A) : (B))
#define MAX4(A, B, C, D) (MAX2(MAX2(A, B), MAX2(C, D)))
+#define DEFER_EINTR(ERROR_CONDITION, GOTO_ON_FAILURE)\
+ DEFER_EINTR_(devicep, ERROR_CONDITION, GOTO_ON_FAILURE)
+
static void
store_uint(uint64_t value, char *buf)
@@ -128,7 +130,7 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
ELEMSOF(devicep->absolute_axis_map),
ELEMSOF(devicep->relative_axis_map),
ELEMSOF(devicep->force_feedback_support)), long int)];
- int err, r;
+ int r, saved_errno = errno;
int16_t j;
uint16_t n;
unsigned int i, max;
@@ -139,6 +141,7 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
memset(devicep, 0, sizeof(*devicep));
devicep->fd = dirfd;
+ devicep->auto_sync = 1;
devicep->internals = calloc(1, sizeof(*devicep->internals));
if (!devicep->internals)
@@ -153,28 +156,17 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
}
- if (ioctl(devicep->fd, EVIOCGID, &id) < 0)
- goto fail;
+ DEFER_EINTR(ioctl(devicep->fd, EVIOCGID, &id) < 0, fail);
devicep->bus_type = (unsigned int)id.bustype;
devicep->vendor = (unsigned int)id.vendor;
devicep->product = (unsigned int)id.product;
devicep->version = (unsigned int)id.version;
- err = libevdev_new_from_fd(devicep->fd, &devicep->internals->dev);
- if (err < 0) {
- libgamepad_close_device(devicep);
- errno = -err;
- return -1;
- }
-
-
#define GET_STRING(EVIOCMACRO, OUTPUT)\
do {\
for (;;) {\
- r = ioctl(devicep->fd, EVIOCMACRO(bufsize), buf);\
- if (r < 0)\
- goto fail_free_buf;\
+ DEFER_EINTR((r = ioctl(devicep->fd, EVIOCMACRO(bufsize), buf)) < 0, fail_free_buf);\
if ((size_t)r < bufsize)\
break;\
new = realloc(buf, bufsize += 32);\
@@ -196,9 +188,7 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
#define CREATE_MAPS(EVENTS, SINGULAR, PLURAL)\
do {\
/* Code-to-index map */\
- r = ioctl(devicep->fd, EVIOCGBIT(EVENTS, sizeof(bits)), bits);\
- if (r < 0)\
- goto fail;\
+ DEFER_EINTR((r = ioctl(devicep->fd, EVIOCGBIT(EVENTS, sizeof(bits)), bits)) < 0, fail);\
max = (unsigned int)r * 8;\
devicep->n##PLURAL = 0;\
for (i = 0; i < ELEMSOF(devicep->SINGULAR##_map); i++) {\
@@ -217,7 +207,6 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
devicep->PLURAL[n++] = (uint16_t)i;\
}\
} while (0)
-
CREATE_MAPS(EV_KEY, button, buttons);
CREATE_MAPS(EV_ABS, absolute_axis, absolute_axes);
CREATE_MAPS(EV_REL, relative_axis, relative_axes);
@@ -228,18 +217,23 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
if (!devicep->internals->absinfo)
goto fail;
}
- for (i = 0; i < devicep->nabsolute_axes; i++)
- if (ioctl(devicep->fd, EVIOCGABS((unsigned int)devicep->absolute_axes[i]), &devicep->internals->absinfo[i]) < 0)
- goto fail;
+ for (i = 0; i < devicep->nabsolute_axes; i++) {
+ DEFER_EINTR(ioctl(devicep->fd, EVIOCGABS((unsigned int)devicep->absolute_axes[i]),
+ &devicep->internals->absinfo[i]) < 0, fail);
+ if (devicep->internals->absinfo[i].minimum == devicep->internals->absinfo[i].maximum) {
+ if (devicep->absolute_axes[i] == ABS_MT_TRACKING_ID) {
+ devicep->internals->absinfo[i].minimum = -1;
+ devicep->internals->absinfo[i].maximum = 0xFFFF;
+ }
+ }
+ }
if (devicep->nbuttons) {
devicep->internals->buttons = calloc((devicep->nbuttons + 7) / 8, sizeof(*devicep->internals->buttons));
if (!devicep->internals->buttons)
goto fail;
- r = ioctl(devicep->fd, EVIOCGKEY(sizeof(bits)), bits);
- if (r < 0)
- goto fail;
+ DEFER_EINTR((r = ioctl(devicep->fd, EVIOCGKEY(sizeof(bits)), bits)) < 0, fail);
max = (unsigned int)r * 8;
for (i = 0; i < max && i < ELEMSOF(devicep->button_map); i++) {
j = devicep->button_map[i];
@@ -249,9 +243,7 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
}
- r = ioctl(devicep->fd, EVIOCGBIT(EV_FF, sizeof(bits)), bits);
- if (r < 0)
- goto fail;
+ DEFER_EINTR((r = ioctl(devicep->fd, EVIOCGBIT(EV_FF, sizeof(bits)), bits)) < 0, fail);
max = (unsigned int)r * 8;
for (i = 0; i < BITSOF(devicep->force_feedback_support); i++)
if (i < max && ((bits[i / BITSOF(*bits)] >> (i % BITSOF(*bits))) & 1))
@@ -261,13 +253,12 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
if (get_fingerprint(devicep, devicep->fingerprint, devicep->fingerprint_unique))
goto fail;
- /* TODO get actual KEY/ABS state */
-
+ errno = saved_errno;
return 0;
fail_free_buf:
free(buf);
fail:
- libgamepad_close_device(devicep);
+ libgamepad_close_device(devicep); /* sets `errno` to `EINTR` if it was deferred */
return -1;
}
diff --git a/test-input.c b/test-input.c
index d74e684..9e2dc37 100644
--- a/test-input.c
+++ b/test-input.c
@@ -3,6 +3,7 @@
#include <assert.h>
#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -31,7 +32,7 @@ int
main(int argc, char *argv[])
{
struct libgamepad_input_event event;
- int r;
+ ssize_t r;
if (argc != 2) {
fprintf(stderr, "Please provide the path to the subdevice as the only command line argument\n");
@@ -54,7 +55,7 @@ main(int argc, char *argv[])
}
for (;;) {
- r = libgamepad_next_event(&gamepad, &event);
+ r = libgamepad_next_event(&gamepad, &event, 1);
if (r <= 0) {
if (!r || errno == EINTR)
continue;
@@ -62,16 +63,17 @@ main(int argc, char *argv[])
return 1;
}
printf("[%lli.%06li] ", (long long int)event.time.tv_sec, event.time.tv_usec);
- if (event.sync_event)
- printf("[sync] ");
- if (event.type == LIBGAMEPAD_BUTTON) {
- printf("%s ", libgamepad_get_button_name(NULL, event.code));
- } else if (event.type == LIBGAMEPAD_ABSOLUTE_AXIS) {
- printf("%s ", libgamepad_get_absolute_axis_name(NULL, event.code));
- } else {
- assert(event.type == LIBGAMEPAD_RELATIVE_AXIS);
- printf("%s ", libgamepad_get_relative_axis_name(NULL, event.code));
- }
- printf("%lli\n", (long long int)event.value);
+ if (event.type == LIBGAMEPAD_BUTTON)
+ printf("%s %ji\n", libgamepad_get_button_name(NULL, event.code), (intmax_t)event.value);
+ else if (event.type == LIBGAMEPAD_ABSOLUTE_AXIS)
+ printf("%s %ji\n", libgamepad_get_absolute_axis_name(NULL, event.code), (intmax_t)event.value);
+ else if (event.type == LIBGAMEPAD_RELATIVE_AXIS)
+ printf("%s %ji\n", libgamepad_get_relative_axis_name(NULL, event.code), (intmax_t)event.value);
+ else if (event.type == LIBGAMEPAD_EVENT_DROP)
+ printf("event drop\n");
+ else if (event.type == LIBGAMEPAD_EVENT_END)
+ printf("event conclusion\n");
+ else
+ printf("unknown event\n");
}
}
diff --git a/test-visual.c b/test-visual.c
index 9743006..bd3a610 100644
--- a/test-visual.c
+++ b/test-visual.c
@@ -39,7 +39,7 @@ draw_axis(char buffer[], const struct abs_axis *axis, size_t len)
x = (size_t)(axis->value - axis->min) * len;
x += (size_t)(axis->max - axis->min) / 2;
x /= (size_t)(axis->max - axis->min);
- memmove(&buffer[x + sizeof("\033[m") - 1], &buffer[x], len - x);
+ memmove(&buffer[x + sizeof("\033[m") - 1], &buffer[x], len + 1 - x);
memcpy(&buffer[x], "\033[m", sizeof("\033[m") - 1);
return buffer;
}
@@ -63,7 +63,7 @@ main(int argc, char *argv[])
ssize_t saxis_len;
size_t axis_len, req_axis_len;
size_t max_max_len = 0, max_min_len = 0;
- int r;
+ ssize_t r;
if (argc != 2) {
fprintf(stderr, "Please provide the path to the subdevice as the only command line argument\n");
@@ -152,7 +152,7 @@ main(int argc, char *argv[])
for (;;) {
/* TODO use nonblocking; listen for window resize */
- r = libgamepad_next_event(&gamepad, &event);
+ r = libgamepad_next_event(&gamepad, &event, 1);
if (r <= 0) {
if (!r || errno == EINTR)
continue;