Switch to a state machine to handle incomplete syncs
authorPeter Hutterer <peter.hutterer@who-t.net>
Tue, 2 Jul 2013 00:50:37 +0000 (10:50 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Tue, 2 Jul 2013 04:02:55 +0000 (14:02 +1000)
A caller may start syncing but switch back to normal half-way through the
sync. In that case, we need to drop all sync events and continue with
regular events only.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
libevdev/libevdev-int.h
libevdev/libevdev.c

index 1a4f224..34f8e05 100644 (file)
                _a > _b ? _a : _b; \
                })
 
+/**
+ * Sync state machine:
+ * default state: SYNC_NONE
+ *
+ * SYNC_NONE → SYN_DROPPED or forced sync → SYNC_NEEDED
+ * SYNC_NEEDED → libevdev_next_event(LIBEVDEV_READ_SYNC) → SYNC_IN_PROGRESS
+ * SYNC_NEEDED → libevdev_next_event(LIBEVDEV_READ_SYNC_NONE) → SYNC_NONE
+ * SYNC_IN_PROGRESS → libevdev_next_event(LIBEVDEV_READ_SYNC_NONE) → SYNC_NONE
+ * SYNC_IN_PROGRESS → no sync events left → SYNC_NONE
+ *
+ */
+enum SyncState {
+       SYNC_NONE,
+       SYNC_NEEDED,
+       SYNC_IN_PROGRESS,
+};
+
 struct libevdev {
        int fd;
        libevdev_log_func_t log;
@@ -78,7 +95,7 @@ struct libevdev {
        int current_slot;
        int rep_values[2];
 
-       int need_sync;
+       enum SyncState sync_state;
        int grabbed;
 
        struct input_event *queue;
index b625934..445cc54 100644 (file)
@@ -74,6 +74,7 @@ libevdev_new(void)
        dev->current_slot = -1;
        dev->log = libevdev_noop_log_func;
        dev->grabbed = LIBEVDEV_UNGRAB;
+       dev->sync_state = SYNC_NONE;
 
        return dev;
 }
@@ -401,7 +402,7 @@ sync_state(struct libevdev *dev)
        /* 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
-          syncing.
+          SYNC_IN_PROGRESS.
 
           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
@@ -429,7 +430,6 @@ sync_state(struct libevdev *dev)
        init_event(dev, ev, EV_SYN, SYN_REPORT, 0);
 
        dev->queue_nsync = queue_num_elements(dev);
-       dev->need_sync = 0;
 
        return rc;
 }
@@ -540,23 +540,32 @@ int libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_e
                return -EINVAL;
 
        if (flags & LIBEVDEV_READ_SYNC) {
-               if (!dev->need_sync && dev->queue_nsync == 0)
-                       return -EAGAIN;
-               else if (dev->need_sync) {
+               if (dev->sync_state == SYNC_NEEDED) {
                        rc = sync_state(dev);
                        if (rc != 0)
                                return rc;
+                       dev->sync_state = SYNC_IN_PROGRESS;
+               }
+
+               if (dev->queue_nsync == 0) {
+                       dev->sync_state = SYNC_NONE;
+                       return -EAGAIN;
                }
-       } else if (dev->need_sync) {
+
+       } else if (dev->sync_state != SYNC_NONE) {
                struct input_event e;
 
                /* call update_state for all events here, otherwise the library has the wrong view
                   of the device too */
-               while (queue_shift(dev, &e) == 0)
+               while (queue_shift(dev, &e) == 0) {
+                       dev->queue_nsync--;
                        update_state(dev, &e);
+               }
+
+               dev->sync_state = SYNC_NONE;
        }
 
-       /* FIXME: if the first event after syncing is a SYN_DROPPED, log this */
+       /* FIXME: if the first event after SYNC_IN_PROGRESS is a SYN_DROPPED, log this */
 
        /* 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.
@@ -573,7 +582,7 @@ int libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_e
                }
 
                if (flags & LIBEVDEV_FORCE_SYNC) {
-                       dev->need_sync = 1;
+                       dev->sync_state = SYNC_NEEDED;
                        rc = 1;
                        goto out;
                }
@@ -589,13 +598,15 @@ int libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_e
 
        rc = 0;
        if (ev->type == EV_SYN && ev->code == SYN_DROPPED) {
-               dev->need_sync = 1;
+               dev->sync_state = SYNC_NEEDED;
                rc = 1;
        }
 
        if (flags & LIBEVDEV_READ_SYNC && dev->queue_nsync > 0) {
                dev->queue_nsync--;
                rc = 1;
+               if (dev->queue_nsync == 0)
+                       dev->sync_state = SYNC_NONE;
        }
 
 out: