From c6493885c3f18042f39273a2efa4fc3616d932b2 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 4 Nov 2010 10:38:32 -0400 Subject: [PATCH] evdev: First stab at an evdev backend 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. --- clutter/Makefile.am | 20 ++ clutter/clutter-device-manager-private.h | 1 + clutter/clutter-input-device.h | 8 + clutter/egl/clutter-backend-egl.c | 49 ++- clutter/egl/clutter-backend-egl.h | 6 + clutter/evdev/clutter-device-manager-evdev.c | 276 ++++++++++++++++ clutter/evdev/clutter-device-manager-evdev.h | 58 ++++ clutter/evdev/clutter-event-evdev.c | 318 +++++++++++++++++++ clutter/evdev/clutter-event-evdev.h | 36 +++ clutter/evdev/clutter-input-device-evdev.c | 197 ++++++++++++ clutter/evdev/clutter-input-device-evdev.h | 66 ++++ configure.ac | 12 +- 12 files changed, 1034 insertions(+), 13 deletions(-) create mode 100644 clutter/evdev/clutter-device-manager-evdev.c create mode 100644 clutter/evdev/clutter-device-manager-evdev.h create mode 100644 clutter/evdev/clutter-event-evdev.c create mode 100644 clutter/evdev/clutter-event-evdev.h create mode 100644 clutter/evdev/clutter-input-device-evdev.c create mode 100644 clutter/evdev/clutter-input-device-evdev.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 813ca61af..68853026c 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -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) diff --git a/clutter/clutter-device-manager-private.h b/clutter/clutter-device-manager-private.h index c4aeff31d..ea21926c0 100644 --- a/clutter/clutter-device-manager-private.h +++ b/clutter/clutter-device-manager-private.h @@ -26,6 +26,7 @@ #define __CLUTTER_DEVICE_MANAGER_PRIVATE_H__ #include +#include G_BEGIN_DECLS diff --git a/clutter/clutter-input-device.h b/clutter/clutter-input-device.h index 85ee592f5..a07983e01 100644 --- a/clutter/clutter-input-device.h +++ b/clutter/clutter-input-device.h @@ -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; diff --git a/clutter/egl/clutter-backend-egl.c b/clutter/egl/clutter-backend-egl.c index 0bcb6962f..a7cef9ff8 100644 --- a/clutter/egl/clutter-backend-egl.c +++ b/clutter/egl/clutter-backend-egl.c @@ -40,6 +40,11 @@ #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; diff --git a/clutter/egl/clutter-backend-egl.h b/clutter/egl/clutter-backend-egl.h index 404681d61..c946380b6 100644 --- a/clutter/egl/clutter-backend-egl.h +++ b/clutter/egl/clutter-backend-egl.h @@ -27,6 +27,9 @@ #include #include +#include +#include + #ifdef COGL_HAS_XLIB_SUPPORT #include #include @@ -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 index 000000000..c1c651402 --- /dev/null +++ b/clutter/evdev/clutter-device-manager-evdev.c @@ -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 . + * + * Author: Damien Lespiau + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#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 index 000000000..ba5d82a40 --- /dev/null +++ b/clutter/evdev/clutter-device-manager-evdev.h @@ -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 . + * + * Author: Damien Lespiau + */ + +#ifndef __CLUTTER_DEVICE_MANAGER_EVDEV_H__ +#define __CLUTTER_DEVICE_MANAGER_EVDEV_H__ + +#include + +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 index 000000000..0a33357e4 --- /dev/null +++ b/clutter/evdev/clutter-event-evdev.c @@ -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 . + * + * Author: Damien Lespiau + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 index 000000000..2c6830af0 --- /dev/null +++ b/clutter/evdev/clutter-event-evdev.h @@ -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 . + * + * Author: Damien Lespiau + */ + +#ifndef __CLUTTER_EVENT_EVDEV_H__ +#define __CLUTTER_EVENT_EVDEV_H__ + +#include + +#include + +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 index 000000000..fa769bde2 --- /dev/null +++ b/clutter/evdev/clutter-input-device-evdev.c @@ -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 . + * + * Author: Damien Lespiau + */ + +#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 index 000000000..4b76e6223 --- /dev/null +++ b/clutter/evdev/clutter-input-device-evdev.h @@ -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 . + * + * Author: Damien Lespiau + */ + +#ifndef __CLUTTER_INPUT_DEVICE_EVDEV_H__ +#define __CLUTTER_INPUT_DEVICE_EVDEV_H__ + +#include +#include + +#include + +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__ */ diff --git a/configure.ac b/configure.ac index 0efce5fa8..29beffbad 100644 --- a/configure.ac +++ b/configure.ac @@ -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"])