summaryrefslogtreecommitdiff
path: root/libevdev/libevdev.c
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2014-04-07 15:16:28 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2014-04-24 08:17:01 +1000
commit050bca91a1e8dd2e46c219c0b282092cb053166c (patch)
tree64aa1d91e1ec7768fcaf034f4087868ee2393ca7 /libevdev/libevdev.c
parent8b01184404dafb0dc121a1cdef066ea78ba6d21c (diff)
downloadlibevdev-050bca91a1e8dd2e46c219c0b282092cb053166c.tar.gz
Drain all events before synchronizing after SYN_DROPPED
The kernel ring buffer drops all events on SYN_DROPPED, but then continues to fill up again. So by the time we read the events, the kernel's client buffer is essentially like this: SYN_DROPPED, ev1, ev2, ev3, ...., evN The kernel's device state represents the device after evN, and that is what the ioctls return. For EV_KEY, EV_SND, EV_LED and EV_SW the kernel removes potential duplicates from the client buffer [1], it doesn't do so for EV_ABS. So we can't actually sync while there are events on the wire because the events represent an earlier state. So simply discard all events in the kernel buffer, synchronize, and then start processing again. We lose some granularity but at least the events are correct. [1] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/input/evdev.c?id=483180281f0ac60d1138710eb21f4b9961901294 Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'libevdev/libevdev.c')
-rw-r--r--libevdev/libevdev.c58
1 files changed, 38 insertions, 20 deletions
diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
index 7e8d3db..a316831 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -726,32 +726,50 @@ read_more_events(struct libevdev *dev)
return 0;
}
-static int
-sync_state(struct libevdev *dev)
+static inline void
+drain_events(struct libevdev *dev)
{
- int i;
- int rc = 0;
- struct input_event *ev;
+ int rc;
+ size_t nelem;
+ int iterations = 0;
+ const int max_iterations = 8; /* EVDEV_BUF_PACKETS in
+ kernel/drivers/input/evedev.c */
+
+ queue_shift_multiple(dev, queue_num_elements(dev), NULL);
- /* FIXME: if we have events in the queue after the SYN_DROPPED (which was
- queue[0]) we need to shift this backwards. Except that chances are that the
- queue may be either full or too full to prepend all the events needed for
- SYNC_IN_PROGRESS.
+ do {
+ rc = read_more_events(dev);
+ if (rc == -EAGAIN)
+ return;
- so we search for the last sync event in the queue and drop everything before
- including that event and rely on the kernel to tell us the right value for that
- bitfield during the sync process.
+ if (rc < 0) {
+ log_error("Failed to drain events before sync.\n");
+ return;
+ }
+
+ nelem = queue_num_elements(dev);
+ queue_shift_multiple(dev, nelem, NULL);
+ } while (iterations++ < max_iterations && nelem >= queue_size(dev));
+
+ /* Our buffer should be roughly the same or bigger than the kernel
+ buffer in most cases, so we usually don't expect to recurse. If
+ we do, make sure we stop after max_iterations and proceed with
+ what we have. This could happen if events queue up faster than
+ we can drain them.
*/
+ if (iterations >= max_iterations)
+ log_info("Unable to drain events, buffer size mismatch.\n");
+}
- for (i = queue_num_elements(dev) - 1; i >= 0; i--) {
- struct input_event e = {{0,0}, 0, 0, 0};
- queue_peek(dev, i, &e);
- if (e.type == EV_SYN)
- break;
- }
+static int
+sync_state(struct libevdev *dev)
+{
+ int rc = 0;
+ struct input_event *ev;
- if (i > 0)
- queue_shift_multiple(dev, i + 1, NULL);
+ /* see section "Discarding events before synchronizing" in
+ * libevdev/libevdev.h */
+ drain_events(dev);
if (libevdev_has_event_type(dev, EV_KEY))
rc = sync_key_state(dev);