diff options
Diffstat (limited to 'panels/common/gsd-device-manager.c')
-rw-r--r-- | panels/common/gsd-device-manager.c | 692 |
1 files changed, 692 insertions, 0 deletions
diff --git a/panels/common/gsd-device-manager.c b/panels/common/gsd-device-manager.c new file mode 100644 index 0000000..9509b3f --- /dev/null +++ b/panels/common/gsd-device-manager.c @@ -0,0 +1,692 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2014 Red Hat + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Carlos Garnacho <carlosg@gnome.org> + */ + +#include "config.h" + +#include <string.h> +#include <gudev/gudev.h> + +#include "gsd-device-manager.h" +#include "gsd-common-enums.h" +#include "gnome-settings-bus.h" +#include "gsd-input-helper.h" + +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif +#ifdef GDK_WINDOWING_WAYLAND +#include <gdk/gdkwayland.h> +#endif + +typedef struct +{ + gchar *name; + gchar *device_file; + gchar *vendor_id; + gchar *product_id; + gchar *group; + GsdDeviceType type; + guint width; + guint height; +} GsdDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (GsdDevice, gsd_device, G_TYPE_OBJECT) + +typedef struct +{ + GObject parent_instance; + GHashTable *devices; + GUdevClient *udev_client; +} GsdDeviceManagerPrivate; + +enum { + PROP_NAME = 1, + PROP_DEVICE_FILE, + PROP_VENDOR_ID, + PROP_PRODUCT_ID, + PROP_TYPE, + PROP_WIDTH, + PROP_HEIGHT, + PROP_GROUP +}; + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + DEVICE_CHANGED, + N_SIGNALS +}; + +/* Index matches GsdDeviceType */ +const gchar *udev_ids[] = { + "ID_INPUT_MOUSE", + "ID_INPUT_KEYBOARD", + "ID_INPUT_TOUCHPAD", + "ID_INPUT_TABLET", + "ID_INPUT_TOUCHSCREEN", + "ID_INPUT_TABLET_PAD", +}; + +static guint signals[N_SIGNALS] = { 0 }; + +G_DEFINE_TYPE_WITH_PRIVATE (GsdDeviceManager, gsd_device_manager, G_TYPE_OBJECT) + +static void +gsd_device_init (GsdDevice *device) +{ +} + +static void +gsd_device_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsdDevicePrivate *priv; + + priv = gsd_device_get_instance_private (GSD_DEVICE (object)); + + switch (prop_id) { + case PROP_NAME: + priv->name = g_value_dup_string (value); + break; + case PROP_DEVICE_FILE: + priv->device_file = g_value_dup_string (value); + break; + case PROP_VENDOR_ID: + priv->vendor_id = g_value_dup_string (value); + break; + case PROP_PRODUCT_ID: + priv->product_id = g_value_dup_string (value); + break; + case PROP_TYPE: + priv->type = g_value_get_flags (value); + break; + case PROP_WIDTH: + priv->width = g_value_get_uint (value); + break; + case PROP_HEIGHT: + priv->height = g_value_get_uint (value); + break; + case PROP_GROUP: + priv->group = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsd_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsdDevicePrivate *priv; + + priv = gsd_device_get_instance_private (GSD_DEVICE (object)); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_DEVICE_FILE: + g_value_set_string (value, priv->device_file); + break; + case PROP_VENDOR_ID: + g_value_set_string (value, priv->vendor_id); + break; + case PROP_PRODUCT_ID: + g_value_set_string (value, priv->product_id); + break; + case PROP_TYPE: + g_value_set_flags (value, priv->type); + break; + case PROP_WIDTH: + g_value_set_uint (value, priv->width); + break; + case PROP_HEIGHT: + g_value_set_uint (value, priv->height); + break; + case PROP_GROUP: + g_value_set_string (value, priv->group); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsd_device_finalize (GObject *object) +{ + GsdDevicePrivate *priv; + + priv = gsd_device_get_instance_private (GSD_DEVICE (object)); + + g_free (priv->name); + g_free (priv->vendor_id); + g_free (priv->product_id); + g_free (priv->device_file); + g_free (priv->group); + + G_OBJECT_CLASS (gsd_device_parent_class)->finalize (object); +} + +static void +gsd_device_class_init (GsdDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gsd_device_set_property; + object_class->get_property = gsd_device_get_property; + object_class->finalize = gsd_device_finalize; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "Name", + "Name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_DEVICE_FILE, + g_param_spec_string ("device-file", + "Device file", + "Device file", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_VENDOR_ID, + g_param_spec_string ("vendor-id", + "Vendor ID", + "Vendor ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_PRODUCT_ID, + g_param_spec_string ("product-id", + "Product ID", + "Product ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_TYPE, + g_param_spec_flags ("type", + "Device type", + "Device type", + GSD_TYPE_DEVICE_TYPE, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_WIDTH, + g_param_spec_uint ("width", + "Width", + "Width", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_HEIGHT, + g_param_spec_uint ("height", + "Height", + "Height", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_GROUP, + g_param_spec_string ("group", + "Group", + "Group", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gsd_device_manager_finalize (GObject *object) +{ + GsdDeviceManager *manager = GSD_DEVICE_MANAGER (object); + GsdDeviceManagerPrivate *priv = gsd_device_manager_get_instance_private (manager); + + g_hash_table_destroy (priv->devices); + g_object_unref (priv->udev_client); + + G_OBJECT_CLASS (gsd_device_manager_parent_class)->finalize (object); +} + +static GList * +gsd_device_manager_real_list_devices (GsdDeviceManager *manager, + GsdDeviceType type) +{ + GsdDeviceManagerPrivate *priv = gsd_device_manager_get_instance_private (manager); + GsdDeviceType device_type; + GList *devices = NULL; + GHashTableIter iter; + GsdDevice *device; + + g_hash_table_iter_init (&iter, priv->devices); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &device)) { + device_type = gsd_device_get_device_type (device); + + if ((device_type & type) == type) + devices = g_list_prepend (devices, device); + } + + return devices; +} + +static GsdDevice * +gsd_device_manager_real_lookup_device (GsdDeviceManager *manager, + GdkDevice *gdk_device) +{ + GsdDeviceManagerPrivate *priv = gsd_device_manager_get_instance_private (manager); + GdkDisplay *display = gdk_device_get_display (gdk_device); + const gchar *node_path = NULL; + GHashTableIter iter; + GsdDevice *device; + +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY (display)) + node_path = xdevice_get_device_node (gdk_x11_device_get_id (gdk_device)); +#endif +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (display)) + node_path = g_strdup (gdk_wayland_device_get_node_path (gdk_device)); +#endif + if (!node_path) + return NULL; + + g_hash_table_iter_init (&iter, priv->devices); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &device)) { + if (g_strcmp0 (node_path, + gsd_device_get_device_file (device)) == 0) { + return device; + } + } + + return NULL; +} + +static void +gsd_device_manager_class_init (GsdDeviceManagerClass *klass) +{ + GsdDeviceManagerClass *manager_class = GSD_DEVICE_MANAGER_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsd_device_manager_finalize; + manager_class->list_devices = gsd_device_manager_real_list_devices; + manager_class->lookup_device = gsd_device_manager_real_lookup_device; + + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + GSD_TYPE_DEVICE_MANAGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdDeviceManagerClass, device_added), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + GSD_TYPE_DEVICE | G_SIGNAL_TYPE_STATIC_SCOPE); + + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + GSD_TYPE_DEVICE_MANAGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdDeviceManagerClass, device_removed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + GSD_TYPE_DEVICE | G_SIGNAL_TYPE_STATIC_SCOPE); + + signals[DEVICE_CHANGED] = + g_signal_new ("device-changed", + GSD_TYPE_DEVICE_MANAGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsdDeviceManagerClass, device_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + GSD_TYPE_DEVICE | G_SIGNAL_TYPE_STATIC_SCOPE); +} + +static GsdDeviceType +udev_device_get_device_type (GUdevDevice *device) +{ + GsdDeviceType type = 0; + gint i; + + for (i = 0; i < G_N_ELEMENTS (udev_ids); i++) { + if (g_udev_device_get_property_as_boolean (device, udev_ids[i])) + type |= (1 << i); + } + + return type; +} + +static gboolean +device_is_evdev (GUdevDevice *device) +{ + const gchar *device_file; + + device_file = g_udev_device_get_device_file (device); + + if (!device_file || strstr (device_file, "/event") == NULL) + return FALSE; + + return g_udev_device_get_property_as_boolean (device, "ID_INPUT"); +} + +static GsdDevice * +create_device (GUdevDevice *udev_device) +{ + const gchar *vendor, *product, *name, *group; + guint width, height; + g_autoptr(GUdevDevice) parent = NULL; + + parent = g_udev_device_get_parent (udev_device); + g_assert (parent != NULL); + + name = g_udev_device_get_sysfs_attr (parent, "name"); + vendor = g_udev_device_get_property (udev_device, "ID_VENDOR_ID"); + product = g_udev_device_get_property (udev_device, "ID_MODEL_ID"); + + if (!vendor || !product) { + vendor = g_udev_device_get_sysfs_attr (udev_device, "device/id/vendor"); + product = g_udev_device_get_sysfs_attr (udev_device, "device/id/product"); + } + + width = g_udev_device_get_property_as_int (udev_device, "ID_INPUT_WIDTH_MM"); + height = g_udev_device_get_property_as_int (udev_device, "ID_INPUT_HEIGHT_MM"); + + group = g_udev_device_get_property (udev_device, "LIBINPUT_DEVICE_GROUP"); + + return g_object_new (GSD_TYPE_DEVICE, + "name", name, + "device-file", g_udev_device_get_device_file (udev_device), + "type", udev_device_get_device_type (udev_device), + "vendor-id", vendor, + "product-id", product, + "width", width, + "height", height, + "group", group, + NULL); +} + +static void +add_device (GsdDeviceManager *manager, + GUdevDevice *udev_device) +{ + GsdDeviceManagerPrivate *priv = gsd_device_manager_get_instance_private (manager); + GUdevDevice *parent; + GsdDevice *device; + const gchar *syspath; + + parent = g_udev_device_get_parent (udev_device); + + if (!parent) + return; + + device = create_device (udev_device); + syspath = g_udev_device_get_sysfs_path (udev_device); + g_hash_table_insert (priv->devices, g_strdup (syspath), device); + g_signal_emit_by_name (manager, "device-added", device); +} + +static void +remove_device (GsdDeviceManager *manager, + GUdevDevice *udev_device) +{ + GsdDeviceManagerPrivate *priv = gsd_device_manager_get_instance_private (manager); + GsdDevice *device; + const gchar *syspath; + + syspath = g_udev_device_get_sysfs_path (udev_device); + device = g_hash_table_lookup (priv->devices, syspath); + + if (!device) + return; + + g_hash_table_steal (priv->devices, syspath); + g_signal_emit_by_name (manager, "device-removed", device); + + g_object_unref (device); +} + +static void +udev_event_cb (GUdevClient *client, + gchar *action, + GUdevDevice *device, + GsdDeviceManager *manager) +{ + if (!device_is_evdev (device)) + return; + + if (g_strcmp0 (action, "add") == 0) { + add_device (manager, device); + } else if (g_strcmp0 (action, "remove") == 0) { + remove_device (manager, device); + } +} + +static void +gsd_device_manager_init (GsdDeviceManager *manager) +{ + GsdDeviceManagerPrivate *priv = gsd_device_manager_get_instance_private (manager); + const gchar *subsystems[] = { "input", NULL }; + g_autoptr(GList) devices = NULL; + GList *l; + + priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + priv->udev_client = g_udev_client_new (subsystems); + g_signal_connect (priv->udev_client, "uevent", + G_CALLBACK (udev_event_cb), manager); + + devices = g_udev_client_query_by_subsystem (priv->udev_client, + subsystems[0]); + + for (l = devices; l; l = l->next) { + g_autoptr(GUdevDevice) device = l->data; + + if (device_is_evdev (device)) + add_device (manager, device); + } +} + +GsdDeviceManager * +gsd_device_manager_get (void) +{ + GsdDeviceManager *manager; + GdkScreen *screen; + + screen = gdk_screen_get_default (); + g_return_val_if_fail (screen != NULL, NULL); + + manager = g_object_get_data (G_OBJECT (screen), "gsd-device-manager-data"); + + if (!manager) { + manager = g_object_new (GSD_TYPE_DEVICE_MANAGER, + NULL); + + g_object_set_data_full (G_OBJECT (screen), "gsd-device-manager-data", + manager, (GDestroyNotify) g_object_unref); + } + + return manager; +} + +GList * +gsd_device_manager_list_devices (GsdDeviceManager *manager, + GsdDeviceType type) +{ + g_return_val_if_fail (GSD_IS_DEVICE_MANAGER (manager), NULL); + + return GSD_DEVICE_MANAGER_GET_CLASS (manager)->list_devices (manager, type); +} + +GsdDeviceType +gsd_device_get_device_type (GsdDevice *device) +{ + GsdDevicePrivate *priv; + + g_return_val_if_fail (GSD_IS_DEVICE (device), 0); + + priv = gsd_device_get_instance_private (device); + + return priv->type; +} + +void +gsd_device_get_device_ids (GsdDevice *device, + const gchar **vendor, + const gchar **product) +{ + GsdDevicePrivate *priv; + + g_return_if_fail (GSD_IS_DEVICE (device)); + + priv = gsd_device_get_instance_private (device); + + if (vendor) + *vendor = priv->vendor_id; + if (product) + *product = priv->product_id; +} + +GSettings * +gsd_device_get_settings (GsdDevice *device) +{ + const gchar *schema = NULL, *vendor, *product; + GsdDeviceType type; + g_autofree gchar *path = NULL; + + g_return_val_if_fail (GSD_IS_DEVICE (device), NULL); + + type = gsd_device_get_device_type (device); + + if (type & (GSD_DEVICE_TYPE_TOUCHSCREEN | GSD_DEVICE_TYPE_TABLET)) { + gsd_device_get_device_ids (device, &vendor, &product); + + if (type & GSD_DEVICE_TYPE_TOUCHSCREEN) { + schema = "org.gnome.desktop.peripherals.touchscreen"; + path = g_strdup_printf ("/org/gnome/desktop/peripherals/touchscreens/%s:%s/", + vendor, product); + } else if (type & GSD_DEVICE_TYPE_TABLET) { + schema = "org.gnome.desktop.peripherals.tablet"; + path = g_strdup_printf ("/org/gnome/desktop/peripherals/tablets/%s:%s/", + vendor, product); + } + } else if (type & (GSD_DEVICE_TYPE_MOUSE | GSD_DEVICE_TYPE_TOUCHPAD)) { + schema = "org.gnome.desktop.peripherals.mouse"; + } else if (type & GSD_DEVICE_TYPE_KEYBOARD) { + schema = "org.gnome.desktop.peripherals.keyboard"; + } else { + return NULL; + } + + if (path) { + return g_settings_new_with_path (schema, path); + } else { + return g_settings_new (schema); + } +} + +const gchar * +gsd_device_get_name (GsdDevice *device) +{ + GsdDevicePrivate *priv; + + g_return_val_if_fail (GSD_IS_DEVICE (device), NULL); + + priv = gsd_device_get_instance_private (device); + + return priv->name; +} + +const gchar * +gsd_device_get_device_file (GsdDevice *device) +{ + GsdDevicePrivate *priv; + + g_return_val_if_fail (GSD_IS_DEVICE (device), NULL); + + priv = gsd_device_get_instance_private (device); + + return priv->device_file; +} + +gboolean +gsd_device_get_dimensions (GsdDevice *device, + guint *width, + guint *height) +{ + GsdDevicePrivate *priv; + + g_return_val_if_fail (GSD_IS_DEVICE (device), FALSE); + + priv = gsd_device_get_instance_private (device); + + if (width) + *width = priv->width; + if (height) + *height = priv->height; + + return priv->width > 0 && priv->height > 0; +} + +GsdDevice * +gsd_device_manager_lookup_gdk_device (GsdDeviceManager *manager, + GdkDevice *gdk_device) +{ + GsdDeviceManagerClass *klass; + + g_return_val_if_fail (GSD_IS_DEVICE_MANAGER (manager), NULL); + g_return_val_if_fail (GDK_IS_DEVICE (gdk_device), NULL); + + klass = GSD_DEVICE_MANAGER_GET_CLASS (manager); + if (!klass->lookup_device) + return NULL; + + return klass->lookup_device (manager, gdk_device); +} + +gboolean +gsd_device_shares_group (GsdDevice *device1, + GsdDevice *device2) +{ + GsdDevicePrivate *priv1, *priv2; + + priv1 = gsd_device_get_instance_private (GSD_DEVICE (device1)); + priv2 = gsd_device_get_instance_private (GSD_DEVICE (device2)); + + /* Don't group NULLs together */ + if (!priv1->group && !priv2->group) + return FALSE; + + return g_strcmp0 (priv1->group, priv2->group) == 0; +} |