mirror of
https://gitlab.freedesktop.org/libevdev/libevdev.git
synced 2026-01-08 08:10:15 +01:00
Drop the callback interface, replace with libevdev_next_event
Callbacks looked good on paper, but synaptics ran into an issue already that it just couldn't easily pass around the state needed in the actual event processing function. Replace with a new interface that only returns the next event (still reading more off the fd while doing so). Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
e19994c34c
commit
f3d94ecfd7
4 changed files with 99 additions and 150 deletions
|
|
@ -37,10 +37,6 @@
|
|||
|
||||
struct libevdev {
|
||||
int fd;
|
||||
libevdev_callback_proc callback;
|
||||
libevdev_callback_proc sync_callback;
|
||||
void *userdata;
|
||||
|
||||
libevdev_log_func_t log;
|
||||
|
||||
char name[MAX_NAME];
|
||||
|
|
@ -62,6 +58,7 @@ struct libevdev {
|
|||
struct input_event *queue;
|
||||
size_t queue_size; /**< size of queue in elements */
|
||||
size_t queue_next; /**< next event index */
|
||||
size_t queue_nsync; /**< number of sync events */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -195,21 +195,13 @@ libevdev_set_fd(struct libevdev* dev, int fd)
|
|||
|
||||
if (dev->fd == -1) {
|
||||
libevdev_log_func_t log;
|
||||
libevdev_callback_proc cb, scb;
|
||||
void *userdata;
|
||||
|
||||
/* these may be set before set_fd */
|
||||
cb = dev->callback;
|
||||
scb = dev->sync_callback;
|
||||
userdata = dev->userdata;
|
||||
log = dev->log;
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
|
||||
dev->fd = -1;
|
||||
dev->callback = cb;
|
||||
dev->sync_callback = scb;
|
||||
dev->userdata = userdata;
|
||||
dev->log = log;
|
||||
}
|
||||
|
||||
|
|
@ -279,19 +271,6 @@ libevdev_get_fd(const struct libevdev* dev)
|
|||
return dev->fd;
|
||||
}
|
||||
|
||||
int
|
||||
libevdev_set_callbacks(struct libevdev *dev,
|
||||
libevdev_callback_proc callback,
|
||||
libevdev_callback_proc sync_callback,
|
||||
void *userdata)
|
||||
{
|
||||
dev->callback = callback;
|
||||
dev->sync_callback = sync_callback;
|
||||
dev->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_event(struct input_event *ev, int type, int code, int value)
|
||||
{
|
||||
|
|
@ -308,20 +287,18 @@ sync_key_state(struct libevdev *dev)
|
|||
int rc;
|
||||
int i;
|
||||
unsigned long keystate[NLONGS(KEY_MAX)];
|
||||
struct input_event ev;
|
||||
|
||||
rc = ioctl(dev->fd, EVIOCGKEY(sizeof(keystate)), keystate);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < KEY_MAX; i++) {
|
||||
struct input_event *ev = &dev->queue[dev->queue_next++];
|
||||
int old, new;
|
||||
old = bit_is_set(dev->key_values, i);
|
||||
new = bit_is_set(keystate, i);
|
||||
if (old ^ new) {
|
||||
init_event(&ev, EV_KEY, i, new ? 1 : 0);
|
||||
dev->sync_callback(dev, &ev, dev->userdata);
|
||||
}
|
||||
if (old ^ new)
|
||||
init_event(ev, EV_KEY, i, new ? 1 : 0);
|
||||
set_bit_state(dev->key_values, i, new);
|
||||
}
|
||||
|
||||
|
|
@ -335,23 +312,25 @@ sync_abs_state(struct libevdev *dev)
|
|||
{
|
||||
int rc;
|
||||
int i;
|
||||
struct input_event ev;
|
||||
|
||||
for (i = ABS_X; i <= ABS_MAX; i++) {
|
||||
struct input_absinfo abs_info;
|
||||
|
||||
if (i >= ABS_MT_MIN && i <= ABS_MT_MAX)
|
||||
continue;
|
||||
|
||||
if (bit_is_set(dev->abs_bits, i)) {
|
||||
struct input_absinfo abs_info;
|
||||
rc = ioctl(dev->fd, EVIOCGABS(i), &abs_info);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
if (!bit_is_set(dev->abs_bits, i))
|
||||
continue;
|
||||
|
||||
if (dev->abs_info[i].value != abs_info.value) {
|
||||
init_event(&ev, EV_ABS, i, abs_info.value);
|
||||
dev->sync_callback(dev, &ev, dev->userdata);
|
||||
dev->abs_info[i].value = abs_info.value;
|
||||
}
|
||||
rc = ioctl(dev->fd, EVIOCGABS(i), &abs_info);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
if (dev->abs_info[i].value != abs_info.value) {
|
||||
struct input_event *ev = &dev->queue[dev->queue_next++];
|
||||
|
||||
init_event(ev, EV_ABS, i, abs_info.value);
|
||||
dev->abs_info[i].value = abs_info.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -369,7 +348,6 @@ sync_mt_state(struct libevdev *dev)
|
|||
int code;
|
||||
int val[MAX_SLOTS];
|
||||
} mt_state[ABS_MT_CNT];
|
||||
struct input_event ev;
|
||||
|
||||
for (i = ABS_MT_MIN; i < ABS_MT_MAX; i++) {
|
||||
int idx;
|
||||
|
|
@ -385,16 +363,19 @@ sync_mt_state(struct libevdev *dev)
|
|||
|
||||
for (i = 0; i < dev->num_slots; i++) {
|
||||
int j;
|
||||
init_event(&ev, EV_ABS, ABS_MT_SLOT, i);
|
||||
dev->sync_callback(dev, &ev, dev->userdata);
|
||||
struct input_event *ev;
|
||||
|
||||
ev = &dev->queue[dev->queue_next++];
|
||||
init_event(ev, EV_ABS, ABS_MT_SLOT, i);
|
||||
for (j = ABS_MT_MIN; j < ABS_MT_MAX; j++) {
|
||||
int jdx = j - ABS_MT_MIN;
|
||||
|
||||
if (dev->mt_slot_vals[i][jdx] != mt_state[jdx].val[i]) {
|
||||
init_event(&ev, EV_ABS, j, mt_state[jdx].val[i]);
|
||||
dev->sync_callback(dev, &ev, dev->userdata);
|
||||
dev->mt_slot_vals[i][jdx] = mt_state[jdx].val[i];
|
||||
}
|
||||
if (dev->mt_slot_vals[i][jdx] == mt_state[jdx].val[i])
|
||||
continue;
|
||||
|
||||
ev = &dev->queue[dev->queue_next++];
|
||||
init_event(ev, EV_ABS, j, mt_state[jdx].val[i]);
|
||||
dev->mt_slot_vals[i][jdx] = mt_state[jdx].val[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -404,21 +385,26 @@ out:
|
|||
}
|
||||
|
||||
static int
|
||||
sync_state(struct libevdev *dev, unsigned int flags)
|
||||
sync_state(struct libevdev *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct input_event ev;
|
||||
struct input_event *ev;
|
||||
|
||||
/* FIXME: if we have events in the queue after the SYN_DROPPED (which was
|
||||
queue[0]) we need to shift this backwards somehow.
|
||||
*/
|
||||
|
||||
if (libevdev_has_event_type(dev, EV_KEY))
|
||||
rc = sync_key_state(dev); /* FIXME: handle ER_SINGLE */
|
||||
rc = sync_key_state(dev);
|
||||
if (rc == 0 && libevdev_has_event_type(dev, EV_ABS))
|
||||
rc = sync_abs_state(dev);
|
||||
if (rc == 0 && libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT))
|
||||
rc = sync_mt_state(dev);
|
||||
|
||||
init_event(&ev, EV_SYN, SYN_REPORT, 0);
|
||||
dev->sync_callback(dev, &ev, dev->userdata);
|
||||
ev = &dev->queue[dev->queue_next++];
|
||||
init_event(ev, EV_SYN, SYN_REPORT, 0);
|
||||
|
||||
dev->queue_nsync = dev->queue_next;
|
||||
dev->need_sync = 0;
|
||||
|
||||
return rc;
|
||||
|
|
@ -487,61 +473,65 @@ read_more_events(struct libevdev *dev)
|
|||
|
||||
len = read(dev->fd, &dev->queue[dev->queue_next], free_elem * sizeof(struct input_event));
|
||||
if (len < 0) {
|
||||
if (errno != EAGAIN || dev->queue_next == 0)
|
||||
return -errno;
|
||||
return -errno;
|
||||
} else if (len > 0 && len % sizeof(struct input_event) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
dev->queue_next += len/sizeof(struct input_event);
|
||||
else if (len > 0)
|
||||
dev->queue_next += len/sizeof(struct input_event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libevdev_read_events(struct libevdev *dev,
|
||||
unsigned int flags)
|
||||
int libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev)
|
||||
{
|
||||
int nevents, processed;
|
||||
int i;
|
||||
int rc = 0;
|
||||
int i;
|
||||
|
||||
if (dev->fd < 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (flags & ER_SYNC) {
|
||||
if (!dev->need_sync)
|
||||
return 0;
|
||||
return sync_state(dev, flags);
|
||||
if (!dev->need_sync && dev->queue_nsync == 0)
|
||||
return -EAGAIN;
|
||||
else if (dev->need_sync) {
|
||||
rc = sync_state(dev);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
}
|
||||
} else if (dev->need_sync) {
|
||||
/* FIXME: if a client decides not to sync, drop all sync events */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Always read in some more events. Best case this smoothes over a potential SYN_DROPPED,
|
||||
worst case we don't read fast enough and end up with SYN_DROPPED anyway */
|
||||
rc = read_more_events(dev);
|
||||
if (rc < 0)
|
||||
if (rc < 0 && rc != -EAGAIN)
|
||||
goto out;
|
||||
|
||||
nevents = dev->queue_next;
|
||||
for (processed = 0; processed < nevents; processed++) {
|
||||
struct input_event *e = &dev->queue[processed];
|
||||
if (dev->queue_next == 0)
|
||||
return -EAGAIN;
|
||||
|
||||
update_state(dev, e);
|
||||
*ev = dev->queue[0];
|
||||
|
||||
if (e->type == EV_SYN && e->code == SYN_DROPPED) {
|
||||
dev->need_sync = 0;
|
||||
rc = 1;
|
||||
break;
|
||||
} else if (libevdev_has_event_code(dev, e->type, e->code)) {
|
||||
if (dev->callback(dev, e, dev->userdata) != 0) {
|
||||
rc = -ECANCELED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
update_state(dev, ev);
|
||||
|
||||
rc = 0;
|
||||
if (ev->type == EV_SYN && ev->code == SYN_DROPPED) {
|
||||
dev->need_sync = 1;
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nevents - processed; i++) {
|
||||
dev->queue[i] = dev->queue[i + processed];
|
||||
for (i = 0; dev->queue_next > 1 && i < dev->queue_next - 1; i++)
|
||||
dev->queue[i] = dev->queue[i + 1];
|
||||
|
||||
dev->queue_next--;
|
||||
|
||||
if (flags & ER_SYNC && dev->queue_nsync > 0) {
|
||||
dev->queue_nsync--;
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
dev->queue_next -= processed;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,70 +129,31 @@ int libevdev_change_fd(struct libevdev* dev, int fd);
|
|||
int libevdev_get_fd(const struct libevdev* dev);
|
||||
|
||||
/**
|
||||
* Event callback used to report events back to the client.
|
||||
* Get the next event from the device.
|
||||
*
|
||||
* If this function returns -1, event processing is interrupted and returned
|
||||
* to the caller of libevdev_read_events. A future call to
|
||||
* libevdev_read_events will continue with the next event in the queue.
|
||||
* In normal mode, this function returns 0 and returns the event in the
|
||||
* parameter ev. If no events are available at this time, it returns -EAGAIN
|
||||
* and ev is undefined.
|
||||
*
|
||||
* @param dev The device this callback was invoked on
|
||||
* @param ev The event read from the kernel
|
||||
* @param userdata Previously assigned caller-specific data
|
||||
* If a SYN_DROPPED is read from the device, this function returns 1. The
|
||||
* caller should now call this function with the ER_SYNC flag set, to get
|
||||
* the set of events that make up the device state diff. This function
|
||||
* returns 1 for each event part of that diff, until it returns -EAGAIN once
|
||||
* all events have been synced.
|
||||
*
|
||||
* @return 0 on success, or -1 on failure.
|
||||
*
|
||||
* @see libevdev_read_events
|
||||
*/
|
||||
typedef int (*libevdev_callback_proc)(struct libevdev *dev, struct input_event *ev, void *userdata);
|
||||
|
||||
/**
|
||||
* Set the callbacks to be used when reading events off the fd.
|
||||
* Two callbacks are used, one for reporting events during normal operation, one for
|
||||
* reporting events during an event sync. If either is NULL, no events are
|
||||
* reported for that mode.
|
||||
*
|
||||
* @param callback Callback used for regular events when read off the fd
|
||||
* @param sync_callback Callback used for events while re-syncing after a
|
||||
* SYN_DROPPED event.
|
||||
* @param userdata Caller-specific data, passed back through the callbacks.
|
||||
*
|
||||
* @return zero on success, -1 on failure
|
||||
*
|
||||
* @see libevdev_read_events
|
||||
* @note This function may be called before libevdev_set_fd.
|
||||
*/
|
||||
int libevdev_set_callbacks(struct libevdev *dev,
|
||||
libevdev_callback_proc callback,
|
||||
libevdev_callback_proc sync_callback,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* Read events off the fd and call the matching callback.
|
||||
*
|
||||
* Depending on the flags, the behaviour changes as follows:
|
||||
* - ER_SINGLE: read a single event off the fd (i.e. up to the next EV_SYN).
|
||||
* This should only be used if the caller is time-sensitive and event
|
||||
* processing of multiple events may prevent other computation.
|
||||
* - ER_ALL: read all the events off the fd until a read would block or
|
||||
* an SYN_DROPPED event is read.
|
||||
* - ER_SYNC: switch to sync mode and report all events that are required to
|
||||
* bring the device state back in sync with the kernel device state.
|
||||
*
|
||||
* @param fd The file descriptor previously set, open in non-blocking mode. If
|
||||
* the file descriptor differs from the previous one, -EBADF is returned.
|
||||
* @param flags The set of flags to decide how to handle events.
|
||||
* If a device needs to be synced by the caller but the caller does not call
|
||||
* with the ER_SYNC flag set, all events from the diff are dropped and event
|
||||
* processing continues as normal.
|
||||
*
|
||||
* @return On failure, a negative errno is returned.
|
||||
* @retval 0 One or more events where read of the fd
|
||||
* @retval -EAGAIN No events are currently on the fd
|
||||
* @retval -ECANCELLED The callback returned non-zero
|
||||
* @retval 1 A SYN_DROPPED event was received
|
||||
*
|
||||
* @see libevdev_set_callbacks
|
||||
* @retval 0 One or more events where read of the device
|
||||
* @retval -EAGAIN No events are currently available on the device
|
||||
* @retval 1 A SYN_DROPPED event was received, or a synced event was
|
||||
* returned.
|
||||
*
|
||||
* @note This function is signal-safe.
|
||||
*/
|
||||
int libevdev_read_events(struct libevdev *dev, unsigned int flags);
|
||||
int libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev);
|
||||
|
||||
/**
|
||||
* @return The device name as read off the kernel device
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ print_code_bits(struct libevdev *dev, unsigned int type, unsigned int max)
|
|||
}
|
||||
}
|
||||
|
||||
int callback(struct libevdev *dev, struct input_event *ev, void *userdata)
|
||||
int print_event(struct input_event *ev)
|
||||
{
|
||||
if (ev->type == EV_SYN)
|
||||
printf("Event: time %ld.%06ld, ++++++++++++++++++++ %s +++++++++++++++\n",
|
||||
|
|
@ -65,10 +65,10 @@ int callback(struct libevdev *dev, struct input_event *ev, void *userdata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sync_callback(struct libevdev *dev, struct input_event *ev, void *userdata)
|
||||
int print_sync_event(struct input_event *ev)
|
||||
{
|
||||
printf("SYNC: ");
|
||||
callback(dev, ev, userdata);
|
||||
print_event(ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -96,17 +96,18 @@ main(int argc, char **argv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (libevdev_set_callbacks(dev, &callback, &sync_callback, NULL) != 0) {
|
||||
fprintf(stderr, "Failed to set callbacks");
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
rc = libevdev_read_events(dev, ER_ALL);
|
||||
struct input_event ev;
|
||||
rc = libevdev_next_event(dev, 0, &ev);
|
||||
if (rc == 1) {
|
||||
printf("::::::::::::::::::::: dropped ::::::::::::::::::::::\n");
|
||||
rc = libevdev_read_events(dev, ER_ALL | ER_SYNC);
|
||||
}
|
||||
while (rc == 1) {
|
||||
print_sync_event(&ev);
|
||||
rc = libevdev_next_event(dev, ER_SYNC, &ev);
|
||||
}
|
||||
printf("::::::::::::::::::::: re-synced ::::::::::::::::::::::\n");
|
||||
} else if (rc == 0)
|
||||
print_event(&ev);
|
||||
} while (rc == 1 || rc == 0 || rc == -EAGAIN);
|
||||
|
||||
if (rc != 0 && rc != -EAGAIN)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue