evdev: fix input lag when processing input from output repaint
authorAnder Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
Tue, 20 Mar 2012 23:52:57 +0000 (19:52 -0400)
committerKristian Høgsberg <krh@bitplanet.net>
Wed, 21 Mar 2012 02:43:57 +0000 (22:43 -0400)
When the compositor is in a repaint cycle, input is processed only once
per frame. However, a call to evdev_input_device_data() would handle at
most 8 events at time. When there was more than 8 events pending for a
given frame, input lag would occur. This was most visible with multi
touch input.

This patch changes the evdev_input_device_data() so that it will handle
all the events available in the fd. In order to do that, the fd is put
in non-blocking mode, so that it is possible to loop on read and stop
on EAGAIN instead of blocking.

src/evdev.c

index 9b8d756..e0a5966 100644 (file)
@@ -322,35 +322,18 @@ evdev_flush_motion(struct evdev_input_device *device, uint32_t time)
                device->type &= ~EVDEV_ABSOLUTE_MOTION;
        }
 }
-#define NUM_EVENTS 8
 
-static int
-evdev_input_device_data(int fd, uint32_t mask, void *data)
+static void
+evdev_process_events(struct evdev_input_device *device,
+                    struct input_event *ev, int count)
 {
-       struct weston_compositor *ec;
-       struct evdev_input_device *device = data;
-       struct input_event ev[NUM_EVENTS], *e, *end;
-       int len;
+       struct input_event *e, *end;
        uint32_t time = 0;
 
-       ec = device->master->base.compositor;
-       if (!ec->focus)
-               return 1;
-
-       if (device->mtdev)
-               len = mtdev_get(device->mtdev, fd, ev, NUM_EVENTS) *
-                     sizeof (struct input_event);
-       else
-               len = read(fd, &ev, sizeof ev);
-       if (len < 0 || len % sizeof e[0] != 0) {
-               /* FIXME: call device_removed when errno is ENODEV. */;
-               return 1;
-       }
-
        device->type = 0;
 
        e = ev;
-       end = (void *) ev + len;
+       end = e + count;
        for (e = ev; e < end; e++) {
                time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000;
 
@@ -373,11 +356,43 @@ evdev_input_device_data(int fd, uint32_t mask, void *data)
        }
 
        evdev_flush_motion(device, time);
+}
+
+static int
+evdev_input_device_data(int fd, uint32_t mask, void *data)
+{
+       struct weston_compositor *ec;
+       struct evdev_input_device *device = data;
+       struct input_event ev[32];
+       int len;
+
+       ec = device->master->base.compositor;
+       if (!ec->focus)
+               return 1;
+
+       /* If the compositor is repainting, this function is called only once
+        * per frame and we have to process all the events available on the
+        * fd, otherwise there will be input lag. */
+       do {
+               if (device->mtdev)
+                       len = mtdev_get(device->mtdev, fd, ev,
+                                       ARRAY_LENGTH(ev)) *
+                               sizeof (struct input_event);
+               else
+                       len = read(fd, &ev, sizeof ev);
+
+               if (len < 0 || len % sizeof ev[0] != 0) {
+                       /* FIXME: call device_removed when errno is ENODEV. */
+                       return 1;
+               }
+
+               evdev_process_events(device, ev, len / sizeof ev[0]);
+
+       } while (len > 0);
 
        return 1;
 }
 
-
 /* copied from udev/extras/input_id/input_id.c */
 /* we must use this kernel-compatible implementation */
 #define BITS_PER_LONG (sizeof(unsigned long) * 8)
@@ -463,7 +478,9 @@ evdev_input_device_create(struct evdev_input *master,
        device->rel.dx = 0;
        device->rel.dy = 0;
 
-       /* if O_NONBLOCK is not set, mtdev_get() blocks */
+       /* Use non-blocking mode so that we can loop on read on
+        * evdev_input_device_data() until all events on the fd are
+        * read.  mtdev_get() also expects this. */
        device->fd = open(path, O_RDONLY | O_NONBLOCK);
        if (device->fd < 0)
                goto err0;