From afd279dd7684bfd28b07cf668a1aaf213239cfd4 Mon Sep 17 00:00:00 2001 From: Damien Lespiau <damien.lespiau@intel.com> Date: Tue, 9 Nov 2010 12:50:23 -0500 Subject: [PATCH] evdev: Merge clutter-event-evdev.[ch] into the device manager The device manager now fully owns the GSources corresponding to the devices it manages. This will allow not only to remove the source when udev signals a device removal but also handle read() errors gracefully by removing the faulty device from the manager. --- clutter/Makefile.am | 2 - clutter/egl/clutter-backend-egl.c | 1 - clutter/evdev/clutter-device-manager-evdev.c | 318 +++++++++++++++- clutter/evdev/clutter-device-manager-evdev.h | 3 + clutter/evdev/clutter-event-evdev.c | 367 ------------------- clutter/evdev/clutter-event-evdev.h | 36 -- 6 files changed, 320 insertions(+), 407 deletions(-) delete mode 100644 clutter/evdev/clutter-event-evdev.c delete mode 100644 clutter/evdev/clutter-event-evdev.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 68853026c..e827878a1 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -437,13 +437,11 @@ evdev_c_priv = \ $(srcdir)/evdev/clutter-xkb-utils.c \ $(srcdir)/evdev/clutter-device-manager-evdev.c \ $(srcdir)/evdev/clutter-input-device-evdev.c \ - $(srcdir)/evdev/clutter-event-evdev.c \ $(NULL) evdev_h_priv = \ $(srcdir)/evdev/clutter-xkb-utils.h \ $(srcdir)/evdev/clutter-device-manager-evdev.h \ $(srcdir)/evdev/clutter-input-device-evdev.h \ - $(srcdir)/evdev/clutter-event-evdev.h \ $(NULL) egl_cex_c_priv = $(srcdir)/egl/clutter-backend-cex100.c diff --git a/clutter/egl/clutter-backend-egl.c b/clutter/egl/clutter-backend-egl.c index a7cef9ff8..1466c2c3b 100644 --- a/clutter/egl/clutter-backend-egl.c +++ b/clutter/egl/clutter-backend-egl.c @@ -42,7 +42,6 @@ #ifdef HAVE_EVDEV #include "clutter-device-manager-evdev.h" -#include "clutter-event-evdev.h" #endif #include "clutter-debug.h" diff --git a/clutter/evdev/clutter-device-manager-evdev.c b/clutter/evdev/clutter-device-manager-evdev.c index 3fbd913b6..df96e9458 100644 --- a/clutter/evdev/clutter-device-manager-evdev.c +++ b/clutter/evdev/clutter-device-manager-evdev.c @@ -26,15 +26,25 @@ #endif #include <linux/input.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <glib.h> #include <gudev/gudev.h> #include "clutter-backend.h" #include "clutter-debug.h" #include "clutter-device-manager.h" #include "clutter-device-manager-private.h" +#include "clutter-event.h" #include "clutter-input-device-evdev.h" +#include "clutter-main.h" #include "clutter-private.h" +#include "clutter-xkb-utils.h" #include "clutter-device-manager-evdev.h" @@ -51,7 +61,8 @@ struct _ClutterDeviceManagerEvdevPrivate { GUdevClient *udev_client; - GSList *devices; + GSList *devices; /* list of ClutterInputDeviceEvdevs */ + GSList *event_sources; /* list of the event sources */ ClutterInputDevice *core_pointer; ClutterInputDevice *core_keyboard; @@ -59,6 +70,249 @@ struct _ClutterDeviceManagerEvdevPrivate static const gchar *subsystems[] = { "input", NULL }; +/* + * ClutterEventSource management + * + * The device manager is responsible for managing the GSource when devices + * appear and disappear from the system + */ + + +const char *option_xkb_layout = "us"; +const char *option_xkb_variant = ""; +const char *option_xkb_options = ""; + +/* + * ClutterEventSource for reading input devices + */ + +typedef struct _ClutterEventSource ClutterEventSource; + +struct _ClutterEventSource +{ + GSource source; + + ClutterInputDeviceEvdev *device; /* back pointer to the evdev device */ + GPollFD event_poll_fd; /* file descriptor of the /dev node */ + struct xkb_desc *xkb; /* compiled xkb keymap */ + uint32_t modifier_state; /* remember the modifier state */ +}; + +static gboolean +clutter_event_prepare (GSource *source, + gint *timeout) +{ + gboolean retval; + + clutter_threads_enter (); + + *timeout = -1; + retval = clutter_events_pending (); + + clutter_threads_leave (); + + return retval; +} + +static gboolean +clutter_event_check (GSource *source) +{ + ClutterEventSource *event_source = (ClutterEventSource *) source; + gboolean retval; + + clutter_threads_enter (); + + retval = ((event_source->event_poll_fd.revents & G_IO_IN) || + clutter_events_pending ()); + + clutter_threads_leave (); + + return retval; +} + +static gboolean +clutter_event_dispatch (GSource *g_source, + GSourceFunc callback, + gpointer user_data) +{ + ClutterEventSource *source = (ClutterEventSource *) g_source; + ClutterInputDevice *input_device = (ClutterInputDevice *) source->device; + ClutterMainContext *clutter_context; + struct input_event ev[8]; + ClutterEvent *event = NULL; + ClutterStage *stage; + gint len, i; + + clutter_threads_enter (); + + clutter_context = _clutter_context_get_default (); + stage = CLUTTER_STAGE (clutter_stage_get_default ()); + + /* 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) + { + g_warning ("Could not read device (%d)", errno); + } + goto out; + } + + for (i = 0; i < len / sizeof (ev[0]); i++) + { + struct input_event *e = &ev[i]; + uint32_t _time; + + _time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000; + + switch (e->type) + { + case EV_KEY: + + /* don't repeat mouse buttons */ + if (e->code >= BTN_MOUSE && e->code < KEY_OK) + if (e->value == 2) + continue; + + event = + _clutter_key_event_new_from_evdev (input_device, + stage, + source->xkb, + _time, e->code, e->value, + &source->modifier_state); + + break; + case EV_SYN: + /* Nothing to do here? */ + break; + case EV_MSC: + /* Nothing to do here? */ + break; + case EV_ABS: + case EV_REL: + default: + g_warning ("Unhandled event of type %d", e->type); + break; + } + + if (event) + { + g_queue_push_head (clutter_context->events_queue, event); + event = NULL; + } + } + } + + /* Pop an event off the queue if any */ + event = clutter_event_get (); + + if (event) + { + /* forward the event into clutter for emission etc. */ + clutter_do_event (event); + clutter_event_free (event); + } + +out: + clutter_threads_leave (); + + return TRUE; +} +static GSourceFuncs event_funcs = { + clutter_event_prepare, + clutter_event_check, + clutter_event_dispatch, + NULL +}; + +static GSource * +clutter_event_source_new (ClutterInputDeviceEvdev *input_device) +{ + GSource *source = g_source_new (&event_funcs, sizeof (ClutterEventSource)); + ClutterEventSource *event_source = (ClutterEventSource *) source; + const gchar *node_path; + gint fd; + + /* grab the udev input device node and open it */ + node_path = _clutter_input_device_evdev_get_device_path (input_device); + + CLUTTER_NOTE (EVENT, "Creating GSource for device %s", node_path); + + fd = open (node_path, O_RDONLY | O_NONBLOCK); + if (fd < 0) + { + g_warning ("Could not open device %s: %s", node_path, strerror (errno)); + return NULL; + } + + /* setup the source */ + event_source->device = input_device; + event_source->event_poll_fd.fd = fd; + event_source->event_poll_fd.events = G_IO_IN; + + /* create the xkb description */ + event_source->xkb = _clutter_xkb_desc_new (NULL, + option_xkb_layout, + option_xkb_variant, + option_xkb_options); + if (G_UNLIKELY (event_source->xkb == NULL)) + { + g_warning ("Could not compile keymap %s:%s:%s", option_xkb_layout, + option_xkb_variant, option_xkb_options); + close (fd); + g_source_unref (source); + return NULL; + } + + /* and finally configure and attach the GSource */ + g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); + g_source_add_poll (source, &event_source->event_poll_fd); + g_source_set_can_recurse (source, TRUE); + g_source_attach (source, NULL); + + return source; +} + +static void +clutter_event_source_free (ClutterEventSource *source) +{ + GSource *g_source = (GSource *) source; + const gchar *node_path; + + node_path = _clutter_input_device_evdev_get_device_path (source->device); + + CLUTTER_NOTE (EVENT, "Removing GSource for device %s", node_path); + + /* ignore the return value of close, it's not like we can do something + * about it */ + close (source->event_poll_fd.fd); + + g_source_destroy (g_source); + g_source_unref (g_source); +} + +static ClutterEventSource * +find_source_by_device (ClutterDeviceManagerEvdev *manager, + ClutterInputDevice *device) +{ + ClutterDeviceManagerEvdevPrivate *priv = manager->priv; + GSList *l; + + for (l = priv->event_sources; l; l = g_slist_next (l)) + { + ClutterEventSource *source = l->data; + + if (source->device == (ClutterInputDeviceEvdev *) device) + return source; + } + + return NULL; +} + static gboolean is_evdev (const gchar *sysfs_path) { @@ -200,11 +454,15 @@ clutter_device_manager_evdev_add_device (ClutterDeviceManager *manager, ClutterDeviceManagerEvdev *manager_evdev; ClutterDeviceManagerEvdevPrivate *priv; ClutterInputDeviceType device_type; + ClutterInputDeviceEvdev *device_evdev; gboolean is_pointer, is_keyboard; + GSource *source; manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (manager); priv = manager_evdev->priv; + device_evdev = CLUTTER_INPUT_DEVICE_EVDEV (device); + device_type = clutter_input_device_get_device_type (device); is_pointer = device_type == CLUTTER_POINTER_DEVICE; is_keyboard = device_type == CLUTTER_KEYBOARD_DEVICE; @@ -216,6 +474,11 @@ clutter_device_manager_evdev_add_device (ClutterDeviceManager *manager, if (is_keyboard && priv->core_keyboard == NULL) priv->core_keyboard = device; + + /* Install the GSource for this device */ + source = clutter_event_source_new (device_evdev); + if (G_LIKELY (source)) + priv->event_sources = g_slist_prepend (priv->event_sources, source); } static void @@ -224,11 +487,24 @@ clutter_device_manager_evdev_remove_device (ClutterDeviceManager *manager, { ClutterDeviceManagerEvdev *manager_evdev; ClutterDeviceManagerEvdevPrivate *priv; + ClutterEventSource *source; manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (manager); priv = manager_evdev->priv; + /* Remove the device */ priv->devices = g_slist_remove (priv->devices, device); + + /* Remove the source */ + source = find_source_by_device (manager_evdev, device); + if (G_UNLIKELY (source == NULL)) + { + g_warning ("Trying to remove a device without a source installed ?!"); + return; + } + + clutter_event_source_free (source); + priv->event_sources = g_slist_remove (priv->event_sources, source); } static const GSList * @@ -321,12 +597,29 @@ clutter_device_manager_evdev_finalize (GObject *object) { ClutterDeviceManagerEvdev *manager_evdev; ClutterDeviceManagerEvdevPrivate *priv; + GSList *l; manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (object); priv = manager_evdev->priv; g_object_unref (priv->udev_client); + for (l = priv->devices; l; l = g_slist_next (l)) + { + ClutterInputDevice *device = l->data; + + g_object_unref (device); + } + g_slist_free (priv->devices); + + for (l = priv->event_sources; l; l = g_slist_next (l)) + { + ClutterEventSource *source = l->data; + + clutter_event_source_free (source); + } + g_slist_free (priv->event_sources); + G_OBJECT_CLASS (clutter_device_manager_evdev_parent_class)->finalize (object); } @@ -354,3 +647,26 @@ clutter_device_manager_evdev_init (ClutterDeviceManagerEvdev *self) { self->priv = CLUTTER_DEVICE_MANAGER_EVDEV_GET_PRIVATE (self); } + +/* + * _clutter_events_evdev_init() and _clutter_events_evdev_uninit() are the two + * symbol to use the evdev event backend from the EGL backend + */ + +void +_clutter_events_evdev_init (ClutterBackend *backend) +{ + CLUTTER_NOTE (EVENT, "Initializing evdev backend"); + + /* We just have to create the singleon here */ + clutter_device_manager_get_default (); +} + +void +_clutter_events_evdev_uninit (ClutterBackend *backend) +{ + ClutterDeviceManager *manager; + + manager = clutter_device_manager_get_default (); + g_object_unref (manager); +} diff --git a/clutter/evdev/clutter-device-manager-evdev.h b/clutter/evdev/clutter-device-manager-evdev.h index ba5d82a40..a5e9f52f8 100644 --- a/clutter/evdev/clutter-device-manager-evdev.h +++ b/clutter/evdev/clutter-device-manager-evdev.h @@ -53,6 +53,9 @@ struct _ClutterDeviceManagerEvdevClass GType clutter_device_manager_evdev_get_type (void) G_GNUC_CONST; +void _clutter_events_evdev_init (ClutterBackend *backend); +void _clutter_events_evdev_uninit (ClutterBackend *backend); + G_END_DECLS #endif /* __CLUTTER_DEVICE_MANAGER_EVDEV_H__ */ diff --git a/clutter/evdev/clutter-event-evdev.c b/clutter/evdev/clutter-event-evdev.c deleted file mode 100644 index b714fd343..000000000 --- a/clutter/evdev/clutter-event-evdev.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Copyright (C) 2006-2007 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Author: Damien Lespiau <damien.lespiau@intel.com> - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <linux/input.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <unistd.h> - -#include <glib.h> -#include <gudev/gudev.h> - -#include "clutter-backend.h" -#include "clutter-debug.h" -#include "clutter-device-manager.h" -#include "clutter-event.h" -#include "clutter-input-device-evdev.h" -#include "clutter-main.h" -#include "clutter-private.h" -#include "clutter-xkb-utils.h" - -/* List of all the event sources */ -static GSList *event_sources = NULL; - -const char *option_xkb_layout = "us"; -const char *option_xkb_variant = ""; -const char *option_xkb_options = ""; - -/* - * ClutterEventSource for reading input devices - */ - -typedef struct _ClutterEventSource ClutterEventSource; - -struct _ClutterEventSource -{ - GSource source; - - ClutterInputDeviceEvdev *device; /* back pointer to the evdev device */ - GPollFD event_poll_fd; /* file descriptor of the /dev node */ - struct xkb_desc *xkb; /* compiled xkb keymap */ - uint32_t modifier_state; /* remember the modifier state */ -}; - -static gboolean -clutter_event_prepare (GSource *source, - gint *timeout) -{ - gboolean retval; - - clutter_threads_enter (); - - *timeout = -1; - retval = clutter_events_pending (); - - clutter_threads_leave (); - - return retval; -} - -static gboolean -clutter_event_check (GSource *source) -{ - ClutterEventSource *event_source = (ClutterEventSource *) source; - gboolean retval; - - clutter_threads_enter (); - - retval = ((event_source->event_poll_fd.revents & G_IO_IN) || - clutter_events_pending ()); - - clutter_threads_leave (); - - return retval; -} - -static gboolean -clutter_event_dispatch (GSource *g_source, - GSourceFunc callback, - gpointer user_data) -{ - ClutterEventSource *source = (ClutterEventSource *) g_source; - ClutterInputDevice *input_device = (ClutterInputDevice *) source->device; - ClutterMainContext *clutter_context; - struct input_event ev[8]; - ClutterEvent *event = NULL; - ClutterStage *stage; - gint len, i; - - clutter_threads_enter (); - - clutter_context = _clutter_context_get_default (); - stage = CLUTTER_STAGE (clutter_stage_get_default ()); - - /* 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) - { - g_warning ("Could not read device (%d)", errno); - } - goto out; - } - - for (i = 0; i < len / sizeof (ev[0]); i++) - { - struct input_event *e = &ev[i]; - uint32_t _time; - - _time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000; - - switch (e->type) - { - case EV_KEY: - - /* don't repeat mouse buttons */ - if (e->code >= BTN_MOUSE && e->code < KEY_OK) - if (e->value == 2) - continue; - - event = - _clutter_key_event_new_from_evdev (input_device, - stage, - source->xkb, - _time, e->code, e->value, - &source->modifier_state); - - break; - case EV_SYN: - /* Nothing to do here? */ - break; - case EV_MSC: - /* Nothing to do here? */ - break; - case EV_ABS: - case EV_REL: - default: - g_warning ("Unhandled event of type %d", e->type); - break; - } - - if (event) - { - g_queue_push_head (clutter_context->events_queue, event); - event = NULL; - } - } - } - - /* Pop an event off the queue if any */ - event = clutter_event_get (); - - if (event) - { - /* forward the event into clutter for emission etc. */ - clutter_do_event (event); - clutter_event_free (event); - } - -out: - clutter_threads_leave (); - - return TRUE; -} -static GSourceFuncs event_funcs = { - clutter_event_prepare, - clutter_event_check, - clutter_event_dispatch, - NULL -}; - -static GSource * -clutter_event_source_new (ClutterInputDeviceEvdev *input_device) -{ - GSource *source = g_source_new (&event_funcs, sizeof (ClutterEventSource)); - ClutterEventSource *event_source = (ClutterEventSource *) source; - const gchar *node_path; - gint fd; - - /* grab the udev input device node and open it */ - node_path = _clutter_input_device_evdev_get_device_path (input_device); - - CLUTTER_NOTE (EVENT, "Creating GSource for device %s", node_path); - - fd = open (node_path, O_RDONLY | O_NONBLOCK); - if (fd < 0) - { - g_warning ("Could not open device %s: %s", node_path, strerror (errno)); - return NULL; - } - - /* setup the source */ - event_source->device = input_device; - event_source->event_poll_fd.fd = fd; - event_source->event_poll_fd.events = G_IO_IN; - - /* create the xkb description */ - event_source->xkb = _clutter_xkb_desc_new (NULL, - option_xkb_layout, - option_xkb_variant, - option_xkb_options); - if (G_UNLIKELY (event_source->xkb == NULL)) - { - g_warning ("Could not compile keymap %s:%s:%s", option_xkb_layout, - option_xkb_variant, option_xkb_options); - close (fd); - g_source_unref (source); - return NULL; - } - - /* and finally configure and attach the GSource */ - g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); - g_source_add_poll (source, &event_source->event_poll_fd); - g_source_set_can_recurse (source, TRUE); - g_source_attach (source, NULL); - - return source; -} - -static void -clutter_event_source_free (ClutterEventSource *source) -{ - GSource *g_source = (GSource *) source; - const gchar *node_path; - - node_path = _clutter_input_device_evdev_get_device_path (source->device); - - CLUTTER_NOTE (EVENT, "Removing GSource for device %s", node_path); - - /* ignore the return value of close, it's not like we can do something - * about it */ - close (source->event_poll_fd.fd); - - g_source_destroy (g_source); - g_source_unref (g_source); -} - -static void -_clutter_event_evdev_add_source (ClutterInputDeviceEvdev *input_device) -{ - GSource *source; - - source = clutter_event_source_new (input_device); - if (G_LIKELY (source)) - event_sources = g_slist_prepend (event_sources, source); -} - -static void -_clutter_event_evdev_remove_source (ClutterEventSource *source) -{ - clutter_event_source_free (source); - event_sources = g_slist_remove (event_sources, source); -} - -static void -on_device_added (ClutterDeviceManager *manager, - ClutterInputDevice *device, - gpointer data) -{ - ClutterInputDeviceEvdev *input_device = CLUTTER_INPUT_DEVICE_EVDEV (device); - - _clutter_event_evdev_add_source (input_device); -} - -static ClutterEventSource * -find_source_by_device (ClutterInputDevice *device) -{ - GSList *l; - - for (l = event_sources; l; l = g_slist_next (l)) - { - ClutterEventSource *source = l->data; - - if (source->device == (ClutterInputDeviceEvdev *) device) - return source; - } - - return NULL; -} - -static void -on_device_removed (ClutterDeviceManager *manager, - ClutterInputDevice *device, - gpointer data) -{ - ClutterEventSource *source; - - source = find_source_by_device (device); - if (G_UNLIKELY (source == NULL)) - { - g_warning ("Trying to remove a device without a source installed ?!"); - return; - } - - _clutter_event_evdev_remove_source (source); -} - -void -_clutter_events_evdev_init (ClutterBackend *backend) -{ - ClutterDeviceManager *device_manager; - GSList *devices, *l; - - CLUTTER_NOTE (EVENT, "Initializing evdev backend"); - - /* Of course, we assume that the singleton will be a - * ClutterDeviceManagerEvdev */ - device_manager = clutter_device_manager_get_default (); - - devices = clutter_device_manager_list_devices (device_manager); - for (l = devices; l; l = g_slist_next (l)) - { - ClutterInputDeviceEvdev *input_device = l->data; - - _clutter_event_evdev_add_source (input_device); - } - - /* make sure to add/remove sources when devices are added/removed */ - g_signal_connect (device_manager, "device-added", - G_CALLBACK (on_device_added), NULL); - g_signal_connect (device_manager, "device-removed", - G_CALLBACK (on_device_removed), NULL); -} - -void -_clutter_events_evdev_uninit (ClutterBackend *backend) -{ - GSList *l; - - for (l = event_sources; l; l = g_slist_next (l)) - { - ClutterEventSource *source = l->data; - - clutter_event_source_free (source); - } - g_slist_free (event_sources); -} diff --git a/clutter/evdev/clutter-event-evdev.h b/clutter/evdev/clutter-event-evdev.h deleted file mode 100644 index 2c6830af0..000000000 --- a/clutter/evdev/clutter-event-evdev.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Clutter. - * - * An OpenGL based 'interactive canvas' library. - * - * Copyright (C) 2010 Intel Corp. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - * Author: Damien Lespiau <damien.lespiau@intel.com> - */ - -#ifndef __CLUTTER_EVENT_EVDEV_H__ -#define __CLUTTER_EVENT_EVDEV_H__ - -#include <glib.h> - -#include <clutter/clutter-backend.h> - -G_BEGIN_DECLS - -void _clutter_events_evdev_init (ClutterBackend *backend); -void _clutter_events_evdev_uninit (ClutterBackend *backend); - -#endif /* __CLUTTER_EVENT_EVDEV_H__ */