diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h index b231a81..d287697 100644 --- a/libevdev/libevdev-int.h +++ b/libevdev/libevdev-int.h @@ -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 diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c index 2e74edd..11b3492 100644 --- a/libevdev/libevdev.c +++ b/libevdev/libevdev.c @@ -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; } diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h index 7339c30..b067015 100644 --- a/libevdev/libevdev.h +++ b/libevdev/libevdev.h @@ -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 diff --git a/test/libevdev-events.c b/test/libevdev-events.c index 982116c..8279229 100644 --- a/test/libevdev-events.c +++ b/test/libevdev-events.c @@ -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)