evdev: First stab at an evdev backend
authorDamien Lespiau <damien.lespiau@intel.com>
Thu, 4 Nov 2010 14:38:32 +0000 (10:38 -0400)
committerDamien Lespiau <damien.lespiau@intel.com>
Tue, 30 Nov 2010 14:40:37 +0000 (14:40 +0000)
This backend is a event backend that can be enabled for EGL (for now).
It uses udev (gudev) to query input devices on a linux system, listens to
keyboard events from input devices and xkbcommon to translate raw key
codes into key keysyms.

This commit only supports key events, more to follow.

12 files changed:
clutter/Makefile.am
clutter/clutter-device-manager-private.h
clutter/clutter-input-device.h
clutter/egl/clutter-backend-egl.c
clutter/egl/clutter-backend-egl.h
clutter/evdev/clutter-device-manager-evdev.c [new file with mode: 0644]
clutter/evdev/clutter-device-manager-evdev.h [new file with mode: 0644]
clutter/evdev/clutter-event-evdev.c [new file with mode: 0644]
clutter/evdev/clutter-event-evdev.h [new file with mode: 0644]
clutter/evdev/clutter-input-device-evdev.c [new file with mode: 0644]
clutter/evdev/clutter-input-device-evdev.h [new file with mode: 0644]
configure.ac

index 813ca61..6885302 100644 (file)
@@ -433,14 +433,34 @@ egl_source_c_priv =
 
 egl_tslib_c = $(srcdir)/egl/clutter-event-tslib.c
 
+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
 egl_cex_h_priv = $(srcdir)/egl/clutter-backend-cex100.h
 egl_cex_h = $(srcdir)/egl/clutter-cex100.h
 
 if SUPPORT_EGL
+
 if USE_TSLIB
 egl_source_c_priv += $(egl_tslib_c)
 endif # SUPPORT_TSLIB
+
+if SUPPORT_EVDEV
+egl_source_c_priv += $(evdev_c_priv)
+egl_source_h_priv += $(evdev_h_priv)
+endif # SUPPORT_EVDEV
+
 if SUPPORT_CEX100
 egl_source_c_priv += $(egl_cex_c_priv)
 egl_source_h_priv += $(egl_cex_h_priv)
index c4aeff3..ea21926 100644 (file)
@@ -26,6 +26,7 @@
 #define __CLUTTER_DEVICE_MANAGER_PRIVATE_H__
 
 #include <clutter/clutter-device-manager.h>
+#include <clutter/clutter-event.h>
 
 G_BEGIN_DECLS
 
index 85ee592..a07983e 100644 (file)
@@ -53,6 +53,10 @@ typedef struct _ClutterInputDeviceClass ClutterInputDeviceClass;
  * @CLUTTER_POINTER_DEVICE: A pointer device
  * @CLUTTER_KEYBOARD_DEVICE: A keyboard device
  * @CLUTTER_EXTENSION_DEVICE: A generic extension device
+ * @CLUTTER_JOYSTICK_DEVICE: A joystick device
+ * @CLUTTER_TABLET_DEVICE: A tablet device
+ * @CLUTTER_TOUCHPAD_DEVICE: A touchpad device
+ * @CLUTTER_TOUCHSCREEN_DEVICE: A touch screen device
  * @CLUTTER_N_DEVICE_TYPES: The number of device types
  *
  * The types of input devices available.
@@ -66,6 +70,10 @@ typedef enum {
   CLUTTER_POINTER_DEVICE,
   CLUTTER_KEYBOARD_DEVICE,
   CLUTTER_EXTENSION_DEVICE,
+  CLUTTER_JOYSTICK_DEVICE,
+  CLUTTER_TABLET_DEVICE,
+  CLUTTER_TOUCHPAD_DEVICE,
+  CLUTTER_TOUCHSCREEN_DEVICE,
 
   CLUTTER_N_DEVICE_TYPES
 } ClutterInputDeviceType;
index 0bcb696..a7cef9f 100644 (file)
 #include "clutter-stage-egl.h"
 #include "clutter-egl.h"
 
+#ifdef HAVE_EVDEV
+#include "clutter-device-manager-evdev.h"
+#include "clutter-event-evdev.h"
+#endif
+
 #include "clutter-debug.h"
 #include "clutter-private.h"
 #include "clutter-main.h"
@@ -516,14 +521,35 @@ clutter_backend_egl_redraw (ClutterBackend *backend,
   _clutter_stage_egl_redraw (CLUTTER_STAGE_EGL (impl), stage);
 }
 
-#ifdef HAVE_TSLIB
+static ClutterDeviceManager *
+clutter_backend_egl_get_device_manager (ClutterBackend *backend)
+{
+  ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend);
+
+  if (G_UNLIKELY (backend_egl->device_manager == NULL))
+    {
+#ifdef HAVE_EVDEV
+      backend_egl->device_manager =
+       g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_EVDEV,
+                     "backend", backend_egl,
+                     NULL);
+#endif
+    }
+
+  return backend_egl->device_manager;
+}
+
 static void
 clutter_backend_egl_init_events (ClutterBackend *backend)
 {
+#ifdef HAVE_TSLIB
   /* XXX: This should be renamed to _clutter_events_tslib_init */
   _clutter_events_egl_init (CLUTTER_BACKEND_EGL (backend));
-}
 #endif
+#ifdef HAVE_EVDEV
+  _clutter_events_evdev_init (CLUTTER_BACKEND (backend));
+#endif
+}
 
 static void
 clutter_backend_egl_finalize (GObject *gobject)
@@ -827,16 +853,15 @@ _clutter_backend_egl_class_init (ClutterBackendEGLClass *klass)
   gobject_class->dispose     = clutter_backend_egl_dispose;
   gobject_class->finalize    = clutter_backend_egl_finalize;
 
-  backend_class->pre_parse        = clutter_backend_egl_pre_parse;
-  backend_class->post_parse       = clutter_backend_egl_post_parse;
-  backend_class->get_features     = clutter_backend_egl_get_features;
-#ifdef HAVE_TSLIB
-  backend_class->init_events      = clutter_backend_egl_init_events;
-#endif
-  backend_class->create_stage     = clutter_backend_egl_create_stage;
-  backend_class->create_context   = clutter_backend_egl_create_context;
-  backend_class->ensure_context   = clutter_backend_egl_ensure_context;
-  backend_class->redraw           = clutter_backend_egl_redraw;
+  backend_class->pre_parse          = clutter_backend_egl_pre_parse;
+  backend_class->post_parse         = clutter_backend_egl_post_parse;
+  backend_class->get_features       = clutter_backend_egl_get_features;
+  backend_class->get_device_manager = clutter_backend_egl_get_device_manager;
+  backend_class->init_events        = clutter_backend_egl_init_events;
+  backend_class->create_stage       = clutter_backend_egl_create_stage;
+  backend_class->create_context     = clutter_backend_egl_create_context;
+  backend_class->ensure_context     = clutter_backend_egl_ensure_context;
+  backend_class->redraw             = clutter_backend_egl_redraw;
 
 #ifdef COGL_HAS_XLIB_SUPPORT
   backendx11_class->get_visual_info = clutter_backend_egl_get_visual_info;
index 404681d..c946380 100644 (file)
@@ -27,6 +27,9 @@
 
 #include <glib-object.h>
 #include <clutter/clutter-event.h>
+#include <clutter/clutter-backend.h>
+#include <clutter/clutter-device-manager.h>
+
 #ifdef COGL_HAS_XLIB_SUPPORT
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
@@ -103,6 +106,9 @@ struct _ClutterBackendEGL
   /* main stage singleton */
   ClutterStageWindow *stage;
 
+  /* device manager (ie evdev) */
+  ClutterDeviceManager *device_manager;
+
   /* event source */
   GSource *event_source;
 
diff --git a/clutter/evdev/clutter-device-manager-evdev.c b/clutter/evdev/clutter-device-manager-evdev.c
new file mode 100644 (file)
index 0000000..c1c6514
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * 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>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <linux/input.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-input-device-evdev.h"
+#include "clutter-private.h"
+
+#include "clutter-device-manager-evdev.h"
+
+#define CLUTTER_DEVICE_MANAGER_EVDEV_GET_PRIVATE(obj)               \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((obj),                              \
+                                CLUTTER_TYPE_DEVICE_MANAGER_EVDEV,  \
+                                ClutterDeviceManagerEvdevPrivate))
+
+G_DEFINE_TYPE (ClutterDeviceManagerEvdev,
+               clutter_device_manager_evdev,
+               CLUTTER_TYPE_DEVICE_MANAGER);
+
+struct _ClutterDeviceManagerEvdevPrivate
+{
+  GUdevClient *udev_client;
+
+  GSList *devices;
+
+  ClutterInputDevice *core_pointer;
+  ClutterInputDevice *core_keyboard;
+};
+
+static const gchar *subsystems[] = { "input", NULL };
+
+static void
+evdev_add_device (ClutterDeviceManagerEvdev *manager_evdev,
+                  GUdevDevice               *udev_device)
+{
+  ClutterDeviceManager *manager = (ClutterDeviceManager *) manager_evdev;
+  ClutterInputDeviceType type = CLUTTER_EXTENSION_DEVICE;
+  ClutterInputDevice *device;
+  const gchar *dev_file;
+  const gchar * const *keys;
+  guint i;
+
+  dev_file = g_udev_device_get_device_file (udev_device);
+
+  if (g_udev_device_get_property (udev_device, "ID_INPUT") == NULL)
+    {
+      CLUTTER_NOTE (EVENT, "Ignoring device %s without ID_INPUT set", dev_file);
+      return;
+    }
+
+  keys = g_udev_device_get_property_keys (udev_device);
+  for (i = 0; keys[i]; i++)
+    {
+      /* Clutter assumes that device types are exclusive in the
+       * ClutterInputDevice API */
+      if (strcmp (keys[i], "ID_INPUT_KEY") == 0)
+        type = CLUTTER_KEYBOARD_DEVICE;
+      else if (strcmp (keys[i], "ID_INPUT_MOUSE") == 0)
+        type = CLUTTER_POINTER_DEVICE;
+      else if (strcmp (keys[i], "ID_INPUT_JOYSTICK") == 0)
+        type = CLUTTER_JOYSTICK_DEVICE;
+      else if (strcmp (keys[i], "ID_INPUT_TABLET") == 0)
+        type = CLUTTER_TABLET_DEVICE;
+      else if (strcmp (keys[i], "ID_INPUT_TOUCHPAD") == 0)
+        type = CLUTTER_TOUCHPAD_DEVICE;
+      else if (strcmp (keys[i], "ID_INPUT_TOUCHSCREEN") == 0)
+        type = CLUTTER_TOUCHSCREEN_DEVICE;
+    }
+
+  device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_EVDEV,
+                         "id", 0,
+                         "name", "Evdev device", /* FIXME */
+                         "device-type", type,
+                         "sysfs-path", sysfs_path,
+                         "device-path", device_file,
+                         NULL);
+  _clutter_device_manager_add_device (manager, device);
+
+  CLUTTER_NOTE (EVENT, "Added device %s, type %d", dev_file, type);
+}
+
+/*
+ * ClutterDeviceManager implementation
+ */
+
+static void
+clutter_device_manager_evdev_add_device (ClutterDeviceManager *manager,
+                                         ClutterInputDevice   *device)
+{
+  ClutterDeviceManagerEvdev *manager_evdev;
+  ClutterDeviceManagerEvdevPrivate *priv;
+  ClutterInputDeviceType device_type;
+  gboolean is_pointer, is_keyboard;
+
+  manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (manager);
+  priv = manager_evdev->priv;
+
+  device_type = clutter_input_device_get_device_type (device);
+  is_pointer  = device_type == CLUTTER_POINTER_DEVICE;
+  is_keyboard = device_type == CLUTTER_KEYBOARD_DEVICE;
+
+  priv->devices = g_slist_prepend (priv->devices, device);
+
+  if (is_pointer && priv->core_pointer == NULL)
+    priv->core_pointer = device;
+
+  if (is_keyboard && priv->core_keyboard == NULL)
+    priv->core_keyboard = device;
+}
+
+static void
+clutter_device_manager_evdev_remove_device (ClutterDeviceManager *manager,
+                                            ClutterInputDevice   *device)
+{
+  ClutterDeviceManagerEvdev *manager_evdev;
+  ClutterDeviceManagerEvdevPrivate *priv;
+
+  manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (manager);
+  priv = manager_evdev->priv;
+
+  priv->devices = g_slist_remove (priv->devices, device);
+}
+
+static const GSList *
+clutter_device_manager_evdev_get_devices (ClutterDeviceManager *manager)
+{
+  return CLUTTER_DEVICE_MANAGER_EVDEV (manager)->priv->devices;
+}
+
+static ClutterInputDevice *
+clutter_device_manager_evdev_get_core_device (ClutterDeviceManager   *manager,
+                                              ClutterInputDeviceType  type)
+{
+  ClutterDeviceManagerEvdev *manager_evdev;
+  ClutterDeviceManagerEvdevPrivate *priv;
+
+  manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (manager);
+  priv = manager_evdev->priv;
+
+  switch (type)
+    {
+    case CLUTTER_POINTER_DEVICE:
+      return priv->core_pointer;
+
+    case CLUTTER_KEYBOARD_DEVICE:
+      return priv->core_keyboard;
+
+    case CLUTTER_EXTENSION_DEVICE:
+    default:
+      return NULL;
+    }
+
+  return NULL;
+}
+
+static ClutterInputDevice *
+clutter_device_manager_evdev_get_device (ClutterDeviceManager *manager,
+                                         gint                  id)
+{
+  ClutterDeviceManagerEvdev *manager_evdev;
+  ClutterDeviceManagerEvdevPrivate *priv;
+  GSList *l;
+
+  manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (manager);
+  priv = manager_evdev->priv;
+
+  for (l = priv->devices; l; l = l->next)
+    {
+      ClutterInputDevice *device = l->data;
+
+      if (clutter_input_device_get_device_id (device) == id)
+        return device;
+    }
+
+  return NULL;
+}
+
+/*
+ * GObject implementation
+ */
+
+static void
+clutter_device_manager_evdev_constructed (GObject *gobject)
+{
+  ClutterDeviceManagerEvdev *manager_evdev;
+  ClutterDeviceManagerEvdevPrivate *priv;
+  GList *devices, *l;
+
+  manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (gobject);
+  priv = manager_evdev->priv;
+
+  priv->udev_client = g_udev_client_new (subsystems);
+
+  devices = g_udev_client_query_by_subsystem (priv->udev_client, subsystems[0]);
+  for (l = devices; l; l = g_list_next (l))
+    {
+      GUdevDevice *device = l->data;
+
+      evdev_add_device (manager_evdev, device);
+      g_object_unref (device);
+    }
+  g_list_free (devices);
+
+  /* subcribe for events on input devices */
+  g_signal_connect (priv->udev_client, "uevent",
+                    G_CALLBACK (on_uevent), manager_evdev);
+}
+
+static void
+clutter_device_manager_evdev_finalize (GObject *object)
+{
+  ClutterDeviceManagerEvdev *manager_evdev;
+  ClutterDeviceManagerEvdevPrivate *priv;
+
+  manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (object);
+  priv = manager_evdev->priv;
+
+  g_object_unref (priv->udev_client);
+
+  G_OBJECT_CLASS (clutter_device_manager_evdev_parent_class)->finalize (object);
+}
+
+static void
+clutter_device_manager_evdev_class_init (ClutterDeviceManagerEvdevClass *klass)
+{
+  ClutterDeviceManagerClass *manager_class;
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+
+  g_type_class_add_private (klass, sizeof (ClutterDeviceManagerEvdevPrivate));
+
+  gobject_class->constructed = clutter_device_manager_evdev_constructed;
+  gobject_class->finalize = clutter_device_manager_evdev_finalize;
+
+  manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass);
+  manager_class->add_device = clutter_device_manager_evdev_add_device;
+  manager_class->remove_device = clutter_device_manager_evdev_remove_device;
+  manager_class->get_devices = clutter_device_manager_evdev_get_devices;
+  manager_class->get_core_device = clutter_device_manager_evdev_get_core_device;
+  manager_class->get_device = clutter_device_manager_evdev_get_device;
+}
+
+static void
+clutter_device_manager_evdev_init (ClutterDeviceManagerEvdev *self)
+{
+  self->priv = CLUTTER_DEVICE_MANAGER_EVDEV_GET_PRIVATE (self);
+}
diff --git a/clutter/evdev/clutter-device-manager-evdev.h b/clutter/evdev/clutter-device-manager-evdev.h
new file mode 100644 (file)
index 0000000..ba5d82a
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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_DEVICE_MANAGER_EVDEV_H__
+#define __CLUTTER_DEVICE_MANAGER_EVDEV_H__
+
+#include <clutter/clutter-device-manager.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_DEVICE_MANAGER_EVDEV            (clutter_device_manager_evdev_get_type ())
+#define CLUTTER_DEVICE_MANAGER_EVDEV(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DEVICE_MANAGER_EVDEV, ClutterDeviceManagerEvdev))
+#define CLUTTER_IS_DEVICE_MANAGER_EVDEV(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DEVICE_MANAGER_EVDEV))
+#define CLUTTER_DEVICE_MANAGER_EVDEV_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DEVICE_MANAGER_EVDEV, ClutterDeviceManagerEvdevClass))
+#define CLUTTER_IS_DEVICE_MANAGER_EVDEV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DEVICE_MANAGER_EVDEV))
+#define CLUTTER_DEVICE_MANAGER_EVDEV_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DEVICE_MANAGER_EVDEV, ClutterDeviceManagerEvdevClass))
+
+typedef struct _ClutterDeviceManagerEvdev         ClutterDeviceManagerEvdev;
+typedef struct _ClutterDeviceManagerEvdevClass    ClutterDeviceManagerEvdevClass;
+typedef struct _ClutterDeviceManagerEvdevPrivate  ClutterDeviceManagerEvdevPrivate;
+
+struct _ClutterDeviceManagerEvdev
+{
+  ClutterDeviceManager parent_instance;
+
+  ClutterDeviceManagerEvdevPrivate *priv;
+};
+
+struct _ClutterDeviceManagerEvdevClass
+{
+  ClutterDeviceManagerClass parent_class;
+};
+
+GType clutter_device_manager_evdev_get_type (void) G_GNUC_CONST;
+
+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
new file mode 100644 (file)
index 0000000..0a33357
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * 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);
+}
+
+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);
+    }
+}
+
+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
new file mode 100644 (file)
index 0000000..2c6830a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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__ */
diff --git a/clutter/evdev/clutter-input-device-evdev.c b/clutter/evdev/clutter-input-device-evdev.c
new file mode 100644 (file)
index 0000000..fa769bd
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "clutter/clutter-device-manager-private.h"
+#include "clutter-private.h"
+
+#include "clutter-input-device-evdev.h"
+
+typedef struct _ClutterInputDeviceClass        ClutterInputDeviceEvdevClass;
+typedef struct _ClutterInputDeviceEvdevPrivate ClutterInputDeviceEvdevPrivate;
+
+#define INPUT_DEVICE_EVDEV_PRIVATE(o)                             \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o),                              \
+                                CLUTTER_TYPE_INPUT_DEVICE_EVDEV,  \
+                                ClutterInputDeviceEvdevPrivate))
+
+enum
+{
+  PROP_0,
+
+  PROP_SYSFS_PATH,
+  PROP_DEVICE_PATH,
+
+  PROP_LAST
+};
+
+struct _ClutterInputDeviceEvdevPrivate
+{
+  gchar *sysfs_path;
+  gchar *device_path;
+};
+
+struct _ClutterInputDeviceEvdev
+{
+  ClutterInputDevice parent;
+
+  ClutterInputDeviceEvdevPrivate *priv;
+};
+
+G_DEFINE_TYPE (ClutterInputDeviceEvdev,
+               clutter_input_device_evdev,
+               CLUTTER_TYPE_INPUT_DEVICE)
+
+static GParamSpec *obj_props[PROP_LAST];
+
+static void
+clutter_input_device_evdev_get_property (GObject    *object,
+                                         guint       property_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  ClutterInputDeviceEvdev  *input = CLUTTER_INPUT_DEVICE_EVDEV (object);
+  ClutterInputDeviceEvdevPrivate *priv = input->priv;
+
+  switch (property_id)
+    {
+    case PROP_SYSFS_PATH:
+      g_value_set_string (value, priv->sysfs_path);
+      break;
+    case PROP_DEVICE_PATH:
+      g_value_set_string (value, priv->device_path);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+clutter_input_device_evdev_set_property (GObject      *object,
+                                         guint         property_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  ClutterInputDeviceEvdev  *input = CLUTTER_INPUT_DEVICE_EVDEV (object);
+  ClutterInputDeviceEvdevPrivate *priv = input->priv;
+
+  switch (property_id)
+    {
+    case PROP_SYSFS_PATH:
+      priv->sysfs_path = g_value_dup_string (value);
+      break;
+    case PROP_DEVICE_PATH:
+      priv->device_path = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+clutter_input_device_evdev_finalize (GObject *object)
+{
+  ClutterInputDeviceEvdev  *input = CLUTTER_INPUT_DEVICE_EVDEV (object);
+  ClutterInputDeviceEvdevPrivate *priv = input->priv;
+
+  g_free (priv->sysfs_path);
+  g_free (priv->device_path);
+
+  G_OBJECT_CLASS (clutter_input_device_evdev_parent_class)->finalize (object);
+}
+
+static void
+clutter_input_device_evdev_class_init (ClutterInputDeviceEvdevClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  g_type_class_add_private (klass, sizeof (ClutterInputDeviceEvdevPrivate));
+
+  object_class->get_property = clutter_input_device_evdev_get_property;
+  object_class->set_property = clutter_input_device_evdev_set_property;
+  object_class->finalize = clutter_input_device_evdev_finalize;
+
+  /*
+   * ClutterInputDeviceEvdev:udev-device:
+   *
+   * The Sysfs path of the device
+   *
+   * Since: 1.6
+   */
+  pspec =
+    g_param_spec_string ("sysfs-path",
+                         P_("sysfs Path"),
+                         P_("Path of the device in sysfs"),
+                         NULL,
+                         G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE);
+  obj_props[PROP_SYSFS_PATH] = pspec;
+  g_object_class_install_property (object_class, PROP_SYSFS_PATH, pspec);
+
+  /*
+   * ClutterInputDeviceEvdev:device-path
+   *
+   * The path of the device file.
+   *
+   * Since: 1.6
+   */
+  pspec =
+    g_param_spec_string ("device-path",
+                         P_("Device Path"),
+                         P_("Path of the device node"),
+                         NULL,
+                         G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE);
+  obj_props[PROP_DEVICE_PATH] = pspec;
+  g_object_class_install_property (object_class, PROP_DEVICE_PATH, pspec);
+}
+
+static void
+clutter_input_device_evdev_init (ClutterInputDeviceEvdev *self)
+{
+  self->priv = INPUT_DEVICE_EVDEV_PRIVATE (self);
+}
+
+ClutterInputDeviceEvdev *
+_clutter_input_device_evdev_new (void)
+{
+  return g_object_new (CLUTTER_TYPE_INPUT_DEVICE_EVDEV, NULL);
+}
+
+const gchar *
+_clutter_input_device_evdev_get_sysfs_path (ClutterInputDeviceEvdev *device)
+{
+  g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE_EVDEV (device), NULL);
+
+  return device->priv->sysfs_path;
+}
+
+const gchar *
+_clutter_input_device_evdev_get_device_path (ClutterInputDeviceEvdev *device)
+{
+  g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE_EVDEV (device), NULL);
+
+  return device->priv->device_path;
+}
diff --git a/clutter/evdev/clutter-input-device-evdev.h b/clutter/evdev/clutter-input-device-evdev.h
new file mode 100644 (file)
index 0000000..4b76e62
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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_INPUT_DEVICE_EVDEV_H__
+#define __CLUTTER_INPUT_DEVICE_EVDEV_H__
+
+#include <glib-object.h>
+#include <gudev/gudev.h>
+
+#include <clutter/clutter-input-device.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_INPUT_DEVICE_EVDEV clutter_input_device_evdev_get_type()
+
+#define CLUTTER_INPUT_DEVICE_EVDEV(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+  CLUTTER_TYPE_INPUT_DEVICE_EVDEV, ClutterInputDeviceEvdev))
+
+#define CLUTTER_INPUT_DEVICE_EVDEV_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+  CLUTTER_TYPE_INPUT_DEVICE_EVDEV, ClutterInputDeviceEvdevClass))
+
+#define CLUTTER_IS_INPUT_DEVICE_EVDEV(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+  CLUTTER_TYPE_INPUT_DEVICE_EVDEV))
+
+#define CLUTTER_IS_INPUT_DEVICE_EVDEV_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+  CLUTTER_TYPE_INPUT_DEVICE_EVDEV))
+
+#define CLUTTER_INPUT_DEVICE_EVDEV_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+  CLUTTER_TYPE_INPUT_DEVICE_EVDEV, ClutterInputDeviceEvdevClass))
+
+typedef struct _ClutterInputDeviceEvdev ClutterInputDeviceEvdev;
+
+GType                     clutter_input_device_evdev_get_type         (void) G_GNUC_CONST;
+
+ClutterInputDeviceEvdev * _clutter_input_device_evdev_new             (void);
+const gchar *             _clutter_input_device_evdev_get_sysfs_path  (ClutterInputDeviceEvdev *device);
+const gchar *             _clutter_input_device_evdev_get_device_path (ClutterInputDeviceEvdev *device);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_INPUT_DEVICE_EVDEV_H__ */
index 0efce5f..29beffb 100644 (file)
@@ -315,7 +315,16 @@ AS_CASE([$CLUTTER_FLAVOUR],
 
           AS_IF([test x"$found_gdl" = "xno"], [AC_MSG_ERROR([libgdl.h not found])])
 
-          FLAVOUR_LIBS="$FLAVOUR_LIBS -lgdl"
+          # evdev
+          PKG_CHECK_MODULES(EVDEV, [gudev-1.0 xkbcommon],
+                            [have_evdev=yes], [have_evdev=no])
+          AS_IF([test "x$have_evdev" = "xyes"],
+                [AC_DEFINE([HAVE_EVDEV], 1,
+                           [Have evdev support for input handling])]
+          )
+
+          FLAVOUR_CFLAGS="$FLAVOUR_CFLAGS $EVDEV_CFLAGS"
+          FLAVOUR_LIBS="$FLAVOUR_LIBS -lgdl $EVDEV_LIBS"
         ],
 
         [fruity],
@@ -580,6 +589,7 @@ AS_IF([test "x$COGL_DRIVER" = "xgles"],
 )
 
 AM_CONDITIONAL(USE_TSLIB, [test "x$have_tslib" = "xyes"])
+AM_CONDITIONAL(SUPPORT_EVDEV, [test "x$have_evdev" = "xyes"])
 
 AM_CONDITIONAL(USE_GLES2_WRAPPER, [test "x$use_gles2_wrapper" = "xyes"])