evdev: switch to libevdev for fetching the events

libevdev is a library that wraps the evdev subsystem, with
the ability to synchronize the state after a SYN_DROPPED event
from the kernel.

https://bugzilla.gnome.org/show_bug.cgi?id=706494
This commit is contained in:
Giovanni Campagna 2013-09-06 16:03:29 +02:00
parent 19536c8835
commit cd1749a2a5
3 changed files with 179 additions and 123 deletions

View File

@ -38,6 +38,11 @@ When building the CEx100 backend, Clutter also depends on:
• libgdl • libgdl
When building the evdev input backend, Clutter also depends on:
• xkbcommon
• libevdev
If you are building the API reference you will also need: If you are building the API reference you will also need:
• GTK-Doc ≥ @GTK_DOC_REQ_VERSION@ • GTK-Doc ≥ @GTK_DOC_REQ_VERSION@

View File

@ -35,6 +35,7 @@
#include <glib.h> #include <glib.h>
#include <gudev/gudev.h> #include <gudev/gudev.h>
#include <libevdev/libevdev.h>
#include "clutter-backend.h" #include "clutter-backend.h"
#include "clutter-debug.h" #include "clutter-debug.h"
@ -124,6 +125,7 @@ struct _ClutterEventSource
ClutterInputDeviceEvdev *device; /* back pointer to the slave evdev device */ ClutterInputDeviceEvdev *device; /* back pointer to the slave evdev device */
GPollFD event_poll_fd; /* file descriptor of the /dev node */ GPollFD event_poll_fd; /* file descriptor of the /dev node */
struct libevdev *dev;
}; };
static gboolean static gboolean
@ -411,73 +413,23 @@ notify_button (ClutterEventSource *source,
queue_event (event); queue_event (event);
} }
static gboolean static void
clutter_event_dispatch (GSource *g_source, dispatch_one_event (ClutterEventSource *source,
GSourceFunc callback, struct input_event *e,
gpointer user_data) int *dx,
int *dy)
{ {
ClutterEventSource *source = (ClutterEventSource *) g_source; guint32 _time;
ClutterInputDevice *input_device = (ClutterInputDevice *) source->device;
struct input_event ev[8];
ClutterEvent *event;
gint len, i, dx = 0, dy = 0;
uint32_t _time;
ClutterStage *stage;
_clutter_threads_acquire_lock ();
stage = _clutter_input_device_get_stage (input_device);
/* Don't queue more events if we haven't finished handling the previous batch
*/
if (!clutter_events_pending ())
{
len = read (source->event_poll_fd.fd, &ev, sizeof (ev));
if (len < 0 || len % sizeof (ev[0]) != 0)
{
if (errno != EAGAIN)
{
ClutterDeviceManager *manager;
ClutterInputDevice *device;
const gchar *device_path;
device = CLUTTER_INPUT_DEVICE (source->device);
if (CLUTTER_HAS_DEBUG (EVENT))
{
device_path =
_clutter_input_device_evdev_get_device_path (source->device);
CLUTTER_NOTE (EVENT, "Could not read device (%s), removing.",
device_path);
}
/* remove the faulty device */
manager = clutter_device_manager_get_default ();
_clutter_device_manager_remove_device (manager, device);
}
goto out;
}
/* Drop events if we don't have any stage to forward them to */
if (!stage)
goto out;
for (i = 0; i < len / sizeof (ev[0]); i++)
{
struct input_event *e = &ev[i];
_time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000; _time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000;
switch (e->type) switch (e->type)
{ {
case EV_KEY: case EV_KEY:
/* don't repeat mouse buttons */ /* don't repeat mouse buttons */
if (e->code >= BTN_MOUSE && e->code < KEY_OK) if (e->code >= BTN_MOUSE && e->code < KEY_OK)
if (e->value == AUTOREPEAT_VALUE) if (e->value == AUTOREPEAT_VALUE)
continue; return;
switch (e->code) switch (e->code)
{ {
@ -510,11 +462,11 @@ clutter_event_dispatch (GSource *g_source,
break; break;
case EV_SYN: case EV_SYN:
/* Nothing to do here? */ /* Nothing to do here */
break; break;
case EV_MSC: case EV_MSC:
/* Nothing to do here? */ /* Nothing to do here */
break; break;
case EV_REL: case EV_REL:
@ -522,10 +474,10 @@ clutter_event_dispatch (GSource *g_source,
switch (e->code) switch (e->code)
{ {
case REL_X: case REL_X:
dx += e->value; *dx += e->value;
break; break;
case REL_Y: case REL_Y:
dy += e->value; *dy += e->value;
break; break;
/* Note: we assume that REL_WHEEL is for *vertical* scroll wheels. /* Note: we assume that REL_WHEEL is for *vertical* scroll wheels.
@ -543,11 +495,108 @@ clutter_event_dispatch (GSource *g_source,
g_warning ("Unhandled event of type %d", e->type); g_warning ("Unhandled event of type %d", e->type);
break; break;
} }
}
static void
sync_source (ClutterEventSource *source)
{
struct input_event ev;
int err;
int dx = 0, dy = 0;
const gchar *device_path;
/* We read a SYN_DROPPED, ignore it and sync the device */
err = libevdev_next_event (source->dev, LIBEVDEV_READ_SYNC, &ev);
while (err == 1)
{
dispatch_one_event (source, &ev, &dx, &dy);
err = libevdev_next_event (source->dev, LIBEVDEV_READ_SYNC, &ev);
}
if (err != -EAGAIN && CLUTTER_HAS_DEBUG (EVENT))
{
device_path = _clutter_input_device_evdev_get_device_path (source->device);
CLUTTER_NOTE (EVENT, "Could not sync device (%s).", device_path);
} }
if (dx != 0 || dy != 0) if (dx != 0 || dy != 0)
{
guint32 _time = ev.time.tv_sec * 1000 + ev.time.tv_usec / 1000;
notify_relative_motion (source, _time, dx, dy); notify_relative_motion (source, _time, dx, dy);
} }
}
static void
fail_source (ClutterEventSource *source,
int error)
{
ClutterDeviceManager *manager;
ClutterInputDevice *device;
const gchar *device_path;
device = CLUTTER_INPUT_DEVICE (source->device);
if (CLUTTER_HAS_DEBUG (EVENT))
{
device_path = _clutter_input_device_evdev_get_device_path (source->device);
CLUTTER_NOTE (EVENT, "Could not read device (%s): %s. Removing.",
device_path, strerror (error));
}
/* remove the faulty device */
manager = clutter_device_manager_get_default ();
_clutter_device_manager_remove_device (manager, device);
}
static gboolean
clutter_event_dispatch (GSource *g_source,
GSourceFunc callback,
gpointer user_data)
{
ClutterEventSource *source = (ClutterEventSource *) g_source;
ClutterInputDevice *input_device = (ClutterInputDevice *) source->device;
struct input_event ev;
ClutterEvent *event;
ClutterStage *stage;
int err, dx = 0, dy = 0;
_clutter_threads_acquire_lock ();
stage = _clutter_input_device_get_stage (input_device);
/* Don't queue more events if we haven't finished handling the previous batch
*/
if (clutter_events_pending ())
goto queue_event;
err = libevdev_next_event (source->dev, LIBEVDEV_READ_NORMAL, &ev);
while (err != -EAGAIN)
{
if (err == 1)
sync_source (source);
else if (err == 0)
dispatch_one_event (source, &ev, &dx, &dy);
else
{
fail_source (source, -err);
goto out;
}
err = libevdev_next_event (source->dev, LIBEVDEV_READ_NORMAL, &ev);
}
if (dx != 0 || dy != 0)
{
guint32 _time = ev.time.tv_sec * 1000 + ev.time.tv_usec / 1000;
notify_relative_motion (source, _time, dx, dy);
}
queue_event:
/* Drop events if we don't have any stage to forward them to */
if (stage == NULL)
goto out;
/* Pop an event off the queue if any */ /* Pop an event off the queue if any */
event = clutter_event_get (); event = clutter_event_get ();
@ -619,14 +668,15 @@ clutter_event_source_new (ClutterInputDeviceEvdev *input_device)
} }
} }
/* Tell evdev to use the monotonic clock for its timestamps */
clkid = CLOCK_MONOTONIC;
ioctl (fd, EVIOCSCLOCKID, &clkid);
/* setup the source */ /* setup the source */
event_source->device = input_device; event_source->device = input_device;
event_source->event_poll_fd.fd = fd; event_source->event_poll_fd.fd = fd;
event_source->event_poll_fd.events = G_IO_IN; event_source->event_poll_fd.events = G_IO_IN;
libevdev_new_from_fd (fd, &event_source->dev);
/* Tell evdev to use the monotonic clock for its timestamps */
clkid = CLOCK_MONOTONIC;
ioctl (fd, EVIOCSCLOCKID, &clkid);
/* and finally configure and attach the GSource */ /* and finally configure and attach the GSource */
g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS);
@ -650,6 +700,7 @@ clutter_event_source_free (ClutterEventSource *source)
/* ignore the return value of close, it's not like we can do something /* ignore the return value of close, it's not like we can do something
* about it */ * about it */
close (source->event_poll_fd.fd); close (source->event_poll_fd.fd);
libevdev_free (source->dev);
g_source_destroy (g_source); g_source_destroy (g_source);
g_source_unref (g_source); g_source_unref (g_source);

View File

@ -480,7 +480,7 @@ AS_IF([test "x$enable_evdev" = "xyes"],
AS_IF([test "x$have_evdev" = "xyes"], AS_IF([test "x$have_evdev" = "xyes"],
[ [
CLUTTER_INPUT_BACKENDS="$CLUTTER_INPUT_BACKENDS evdev" CLUTTER_INPUT_BACKENDS="$CLUTTER_INPUT_BACKENDS evdev"
BACKEND_PC_FILES="$BACKEND_PC_FILES gudev-1.0 xkbcommon" BACKEND_PC_FILES="$BACKEND_PC_FILES gudev-1.0 libevdev xkbcommon"
experimental_input_backend="yes" experimental_input_backend="yes"
AC_DEFINE([HAVE_EVDEV], [1], [Have evdev support for input handling]) AC_DEFINE([HAVE_EVDEV], [1], [Have evdev support for input handling])
SUPPORT_EVDEV=1 SUPPORT_EVDEV=1