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

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