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.
This commit is contained in:
Damien Lespiau 2010-11-04 10:38:32 -04:00
parent 9f5f62b4b5
commit c6493885c3
12 changed files with 1034 additions and 13 deletions

View File

@ -433,14 +433,34 @@ egl_source_c_priv =
egl_tslib_c = $(srcdir)/egl/clutter-event-tslib.c 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_c_priv = $(srcdir)/egl/clutter-backend-cex100.c
egl_cex_h_priv = $(srcdir)/egl/clutter-backend-cex100.h egl_cex_h_priv = $(srcdir)/egl/clutter-backend-cex100.h
egl_cex_h = $(srcdir)/egl/clutter-cex100.h egl_cex_h = $(srcdir)/egl/clutter-cex100.h
if SUPPORT_EGL if SUPPORT_EGL
if USE_TSLIB if USE_TSLIB
egl_source_c_priv += $(egl_tslib_c) egl_source_c_priv += $(egl_tslib_c)
endif # SUPPORT_TSLIB 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 if SUPPORT_CEX100
egl_source_c_priv += $(egl_cex_c_priv) egl_source_c_priv += $(egl_cex_c_priv)
egl_source_h_priv += $(egl_cex_h_priv) egl_source_h_priv += $(egl_cex_h_priv)

View File

@ -26,6 +26,7 @@
#define __CLUTTER_DEVICE_MANAGER_PRIVATE_H__ #define __CLUTTER_DEVICE_MANAGER_PRIVATE_H__
#include <clutter/clutter-device-manager.h> #include <clutter/clutter-device-manager.h>
#include <clutter/clutter-event.h>
G_BEGIN_DECLS G_BEGIN_DECLS

View File

@ -53,6 +53,10 @@ typedef struct _ClutterInputDeviceClass ClutterInputDeviceClass;
* @CLUTTER_POINTER_DEVICE: A pointer device * @CLUTTER_POINTER_DEVICE: A pointer device
* @CLUTTER_KEYBOARD_DEVICE: A keyboard device * @CLUTTER_KEYBOARD_DEVICE: A keyboard device
* @CLUTTER_EXTENSION_DEVICE: A generic extension 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 * @CLUTTER_N_DEVICE_TYPES: The number of device types
* *
* The types of input devices available. * The types of input devices available.
@ -66,6 +70,10 @@ typedef enum {
CLUTTER_POINTER_DEVICE, CLUTTER_POINTER_DEVICE,
CLUTTER_KEYBOARD_DEVICE, CLUTTER_KEYBOARD_DEVICE,
CLUTTER_EXTENSION_DEVICE, CLUTTER_EXTENSION_DEVICE,
CLUTTER_JOYSTICK_DEVICE,
CLUTTER_TABLET_DEVICE,
CLUTTER_TOUCHPAD_DEVICE,
CLUTTER_TOUCHSCREEN_DEVICE,
CLUTTER_N_DEVICE_TYPES CLUTTER_N_DEVICE_TYPES
} ClutterInputDeviceType; } ClutterInputDeviceType;

View File

@ -40,6 +40,11 @@
#include "clutter-stage-egl.h" #include "clutter-stage-egl.h"
#include "clutter-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-debug.h"
#include "clutter-private.h" #include "clutter-private.h"
#include "clutter-main.h" #include "clutter-main.h"
@ -516,14 +521,35 @@ clutter_backend_egl_redraw (ClutterBackend *backend,
_clutter_stage_egl_redraw (CLUTTER_STAGE_EGL (impl), stage); _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 static void
clutter_backend_egl_init_events (ClutterBackend *backend) clutter_backend_egl_init_events (ClutterBackend *backend)
{ {
#ifdef HAVE_TSLIB
/* XXX: This should be renamed to _clutter_events_tslib_init */ /* XXX: This should be renamed to _clutter_events_tslib_init */
_clutter_events_egl_init (CLUTTER_BACKEND_EGL (backend)); _clutter_events_egl_init (CLUTTER_BACKEND_EGL (backend));
}
#endif #endif
#ifdef HAVE_EVDEV
_clutter_events_evdev_init (CLUTTER_BACKEND (backend));
#endif
}
static void static void
clutter_backend_egl_finalize (GObject *gobject) 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->dispose = clutter_backend_egl_dispose;
gobject_class->finalize = clutter_backend_egl_finalize; gobject_class->finalize = clutter_backend_egl_finalize;
backend_class->pre_parse = clutter_backend_egl_pre_parse; backend_class->pre_parse = clutter_backend_egl_pre_parse;
backend_class->post_parse = clutter_backend_egl_post_parse; backend_class->post_parse = clutter_backend_egl_post_parse;
backend_class->get_features = clutter_backend_egl_get_features; backend_class->get_features = clutter_backend_egl_get_features;
#ifdef HAVE_TSLIB backend_class->get_device_manager = clutter_backend_egl_get_device_manager;
backend_class->init_events = clutter_backend_egl_init_events; backend_class->init_events = clutter_backend_egl_init_events;
#endif backend_class->create_stage = clutter_backend_egl_create_stage;
backend_class->create_stage = clutter_backend_egl_create_stage; backend_class->create_context = clutter_backend_egl_create_context;
backend_class->create_context = clutter_backend_egl_create_context; backend_class->ensure_context = clutter_backend_egl_ensure_context;
backend_class->ensure_context = clutter_backend_egl_ensure_context; backend_class->redraw = clutter_backend_egl_redraw;
backend_class->redraw = clutter_backend_egl_redraw;
#ifdef COGL_HAS_XLIB_SUPPORT #ifdef COGL_HAS_XLIB_SUPPORT
backendx11_class->get_visual_info = clutter_backend_egl_get_visual_info; backendx11_class->get_visual_info = clutter_backend_egl_get_visual_info;

View File

@ -27,6 +27,9 @@
#include <glib-object.h> #include <glib-object.h>
#include <clutter/clutter-event.h> #include <clutter/clutter-event.h>
#include <clutter/clutter-backend.h>
#include <clutter/clutter-device-manager.h>
#ifdef COGL_HAS_XLIB_SUPPORT #ifdef COGL_HAS_XLIB_SUPPORT
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
@ -103,6 +106,9 @@ struct _ClutterBackendEGL
/* main stage singleton */ /* main stage singleton */
ClutterStageWindow *stage; ClutterStageWindow *stage;
/* device manager (ie evdev) */
ClutterDeviceManager *device_manager;
/* event source */ /* event source */
GSource *event_source; GSource *event_source;

View File

@ -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);
}

View File

@ -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__ */

View File

@ -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);
}

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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__ */

View File

@ -315,7 +315,16 @@ AS_CASE([$CLUTTER_FLAVOUR],
AS_IF([test x"$found_gdl" = "xno"], [AC_MSG_ERROR([libgdl.h not found])]) 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], [fruity],
@ -580,6 +589,7 @@ AS_IF([test "x$COGL_DRIVER" = "xgles"],
) )
AM_CONDITIONAL(USE_TSLIB, [test "x$have_tslib" = "xyes"]) 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"]) AM_CONDITIONAL(USE_GLES2_WRAPPER, [test "x$use_gles2_wrapper" = "xyes"])