summaryrefslogtreecommitdiffstats
path: root/plugins/wacom/gsd-wacom-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/wacom/gsd-wacom-manager.c')
-rw-r--r--plugins/wacom/gsd-wacom-manager.c528
1 files changed, 528 insertions, 0 deletions
diff --git a/plugins/wacom/gsd-wacom-manager.c b/plugins/wacom/gsd-wacom-manager.c
new file mode 100644
index 0000000..0364063
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-manager.c
@@ -0,0 +1,528 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <locale.h>
+
+#include <gdk/gdk.h>
+
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#if HAVE_WACOM
+#include <libwacom/libwacom.h>
+#endif
+
+#include "gsd-enums.h"
+#include "gnome-settings-profile.h"
+#include "gnome-settings-bus.h"
+#include "gsd-wacom-manager.h"
+#include "gsd-wacom-oled.h"
+#include "gsd-settings-migrate.h"
+#include "gsd-input-helper.h"
+
+
+#define UNKNOWN_DEVICE_NOTIFICATION_TIMEOUT 15000
+
+#define GSD_DBUS_NAME "org.gnome.SettingsDaemon"
+#define GSD_DBUS_PATH "/org/gnome/SettingsDaemon"
+#define GSD_DBUS_BASE_INTERFACE "org.gnome.SettingsDaemon"
+
+#define GSD_WACOM_DBUS_PATH GSD_DBUS_PATH "/Wacom"
+#define GSD_WACOM_DBUS_NAME GSD_DBUS_NAME ".Wacom"
+
+#define LEFT_HANDED_KEY "left-handed"
+
+static const gchar introspection_xml[] =
+"<node name='/org/gnome/SettingsDaemon/Wacom'>"
+" <interface name='org.gnome.SettingsDaemon.Wacom'>"
+" <method name='SetOLEDLabels'>"
+" <arg name='device_path' direction='in' type='s'/>"
+" <arg name='labels' direction='in' type='as'/>"
+" </method>"
+" </interface>"
+"</node>";
+
+struct _GsdWacomManager
+{
+ GObject parent;
+
+ guint start_idle_id;
+ GdkSeat *seat;
+ guint device_added_id;
+
+ GsdShell *shell_proxy;
+
+ gchar *machine_id;
+
+#if HAVE_WACOM
+ WacomDeviceDatabase *wacom_db;
+#endif
+
+ /* DBus */
+ GDBusNodeInfo *introspection_data;
+ GDBusConnection *dbus_connection;
+ GCancellable *dbus_cancellable;
+ guint dbus_register_object_id;
+ guint name_id;
+};
+
+static void gsd_wacom_manager_class_init (GsdWacomManagerClass *klass);
+static void gsd_wacom_manager_init (GsdWacomManager *wacom_manager);
+static void gsd_wacom_manager_finalize (GObject *object);
+
+static gboolean is_opaque_tablet (GsdWacomManager *manager,
+ GdkDevice *device);
+
+G_DEFINE_TYPE (GsdWacomManager, gsd_wacom_manager, G_TYPE_OBJECT)
+
+static gpointer manager_object = NULL;
+
+static GVariant *
+map_tablet_mapping (GVariant *value, GVariant *old_default, GVariant *new_default)
+{
+ const gchar *mapping;
+
+ mapping = g_variant_get_boolean (value) ? "absolute" : "relative";
+ return g_variant_new_string (mapping);
+}
+
+static GVariant *
+map_tablet_left_handed (GVariant *value, GVariant *old_default, GVariant *new_default)
+{
+ const gchar *rotation = g_variant_get_string (value, NULL);
+ return g_variant_new_boolean (g_strcmp0 (rotation, "half") == 0 ||
+ g_strcmp0 (rotation, "ccw") == 0);
+}
+
+static void
+migrate_tablet_settings (GsdWacomManager *manager,
+ GdkDevice *device)
+{
+ GsdSettingsMigrateEntry tablet_settings[] = {
+ { "is-absolute", "mapping", map_tablet_mapping },
+ { "keep-aspect", "keep-aspect", NULL },
+ { "rotation", "left-handed", map_tablet_left_handed },
+ };
+ gchar *old_path, *new_path;
+ const gchar *vendor, *product;
+
+ vendor = gdk_device_get_vendor_id (device);
+ product = gdk_device_get_product_id (device);
+
+ old_path = g_strdup_printf ("/org/gnome/settings-daemon/peripherals/wacom/%s-usb:%s:%s/",
+ manager->machine_id, vendor, product);
+ new_path = g_strdup_printf ("/org/gnome/desktop/peripherals/tablets/%s:%s/",
+ vendor, product);
+
+ gsd_settings_migrate_check ("org.gnome.settings-daemon.peripherals.wacom.deprecated",
+ old_path,
+ "org.gnome.desktop.peripherals.tablet",
+ new_path,
+ tablet_settings, G_N_ELEMENTS (tablet_settings));
+
+ /* Opaque tablets' mapping may be modified by users, so only these
+ * need migration of settings.
+ */
+ if (is_opaque_tablet (manager, device)) {
+ GsdSettingsMigrateEntry display_setting[] = {
+ { "display", "output", NULL },
+ };
+
+ gsd_settings_migrate_check ("org.gnome.desktop.peripherals.tablet.deprecated",
+ new_path,
+ "org.gnome.desktop.peripherals.tablet",
+ new_path,
+ display_setting, G_N_ELEMENTS (display_setting));
+ }
+
+ g_free (old_path);
+ g_free (new_path);
+}
+
+static void
+gsd_wacom_manager_class_init (GsdWacomManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gsd_wacom_manager_finalize;
+}
+
+static gchar *
+get_device_path (GdkDevice *device)
+{
+#if HAVE_WAYLAND
+ if (gnome_settings_is_wayland ())
+ return g_strdup (gdk_wayland_device_get_node_path (device));
+ else
+#endif
+ return xdevice_get_device_node (gdk_x11_device_get_id (device));
+}
+
+static gboolean
+is_opaque_tablet (GsdWacomManager *manager,
+ GdkDevice *device)
+{
+ gboolean is_opaque = FALSE;
+#if HAVE_WACOM
+ WacomDevice *wacom_device;
+ gchar *devpath;
+
+ devpath = get_device_path (device);
+ wacom_device = libwacom_new_from_path (manager->wacom_db, devpath,
+ WFALLBACK_GENERIC, NULL);
+ if (wacom_device) {
+ WacomIntegrationFlags integration_flags;
+
+ integration_flags = libwacom_get_integration_flags (wacom_device);
+ is_opaque = (integration_flags &
+ (WACOM_DEVICE_INTEGRATED_DISPLAY | WACOM_DEVICE_INTEGRATED_SYSTEM)) == 0;
+ libwacom_destroy (wacom_device);
+ }
+
+#endif
+ return is_opaque;
+}
+
+static GdkDevice *
+lookup_device_by_path (GsdWacomManager *manager,
+ const gchar *path)
+{
+ GList *devices, *l;
+
+ devices = gdk_seat_get_slaves (manager->seat,
+ GDK_SEAT_CAPABILITY_ALL);
+
+ for (l = devices; l; l = l->next) {
+ GdkDevice *device = l->data;
+ gchar *dev_path = get_device_path (device);
+
+ if (g_strcmp0 (dev_path, path) == 0) {
+ g_free (dev_path);
+ return device;
+ }
+
+ g_free (dev_path);
+ }
+
+ g_list_free (devices);
+
+ return NULL;
+}
+
+static GSettings *
+device_get_settings (GdkDevice *device)
+{
+ GSettings *settings;
+ gchar *path;
+
+ path = g_strdup_printf ("/org/gnome/desktop/peripherals/tablets/%s:%s/",
+ gdk_device_get_vendor_id (device),
+ gdk_device_get_product_id (device));
+ settings = g_settings_new_with_path ("org.gnome.desktop.peripherals.tablet",
+ path);
+ g_free (path);
+
+ return settings;
+}
+
+static void
+handle_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer data)
+{
+ GsdWacomManager *self = GSD_WACOM_MANAGER (data);
+ GError *error = NULL;
+ GdkDevice *device;
+
+ if (g_strcmp0 (method_name, "SetOLEDLabels") == 0) {
+ gchar *device_path, *label;
+ gboolean left_handed;
+ GSettings *settings;
+ GVariantIter *iter;
+ gint i = 0;
+
+ g_variant_get (parameters, "(sas)", &device_path, &iter);
+ device = lookup_device_by_path (self, device_path);
+ if (!device) {
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ return;
+ }
+
+ settings = device_get_settings (device);
+ left_handed = g_settings_get_boolean (settings, LEFT_HANDED_KEY);
+ g_object_unref (settings);
+
+ while (g_variant_iter_loop (iter, "s", &label)) {
+ if (!set_oled (device_path, left_handed, i, label, &error)) {
+ g_free (label);
+ break;
+ }
+ i++;
+ }
+
+ g_variant_iter_free (iter);
+
+ if (error)
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ else
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ }
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+ handle_method_call,
+ NULL, /* Get Property */
+ NULL, /* Set Property */
+};
+
+static void
+device_added_cb (GdkSeat *seat,
+ GdkDevice *device,
+ GsdWacomManager *manager)
+{
+ if (gdk_device_get_source (device) == GDK_SOURCE_PEN &&
+ gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_SLAVE) {
+ migrate_tablet_settings (manager, device);
+ }
+}
+
+static void
+add_devices (GsdWacomManager *manager,
+ GdkSeatCapabilities capabilities)
+{
+ GList *devices, *l;
+
+ devices = gdk_seat_get_slaves (manager->seat, capabilities);
+ for (l = devices; l ; l = l->next)
+ device_added_cb (manager->seat, l->data, manager);
+ g_list_free (devices);
+}
+
+static void
+set_devicepresence_handler (GsdWacomManager *manager)
+{
+ GdkSeat *seat;
+
+ seat = gdk_display_get_default_seat (gdk_display_get_default ());
+ manager->device_added_id = g_signal_connect (seat, "device-added",
+ G_CALLBACK (device_added_cb), manager);
+ manager->seat = seat;
+}
+
+static void
+gsd_wacom_manager_init (GsdWacomManager *manager)
+{
+#if HAVE_WACOM
+ manager->wacom_db = libwacom_database_new ();
+#endif
+}
+
+static gboolean
+gsd_wacom_manager_idle_cb (GsdWacomManager *manager)
+{
+ gnome_settings_profile_start (NULL);
+
+ set_devicepresence_handler (manager);
+
+ add_devices (manager, GDK_SEAT_CAPABILITY_TABLET_STYLUS);
+
+ gnome_settings_profile_end (NULL);
+
+ manager->start_idle_id = 0;
+
+ return FALSE;
+}
+
+static void
+on_bus_gotten (GObject *source_object,
+ GAsyncResult *res,
+ GsdWacomManager *manager)
+{
+ GDBusConnection *connection;
+ GError *error = NULL;
+
+ connection = g_bus_get_finish (res, &error);
+
+ if (connection == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Couldn't get session bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ manager->dbus_connection = connection;
+ manager->dbus_register_object_id = g_dbus_connection_register_object (connection,
+ GSD_WACOM_DBUS_PATH,
+ manager->introspection_data->interfaces[0],
+ &interface_vtable,
+ manager,
+ NULL,
+ &error);
+
+ if (manager->dbus_register_object_id == 0) {
+ g_warning ("Error registering object: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ manager->name_id = g_bus_own_name_on_connection (connection,
+ GSD_WACOM_DBUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+}
+
+static void
+register_manager (GsdWacomManager *manager)
+{
+ manager->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ manager->dbus_cancellable = g_cancellable_new ();
+ g_assert (manager->introspection_data != NULL);
+
+ g_bus_get (G_BUS_TYPE_SESSION,
+ manager->dbus_cancellable,
+ (GAsyncReadyCallback) on_bus_gotten,
+ manager);
+}
+
+static gchar *
+get_machine_id (void)
+{
+ gchar *no_per_machine_file, *machine_id = NULL;
+ gboolean per_machine;
+ gsize len;
+
+ no_per_machine_file = g_build_filename (g_get_user_config_dir (), "gnome-settings-daemon", "no-per-machine-config", NULL);
+ per_machine = !g_file_test (no_per_machine_file, G_FILE_TEST_EXISTS);
+ g_free (no_per_machine_file);
+
+ if (!per_machine ||
+ (!g_file_get_contents ("/etc/machine-id", &machine_id, &len, NULL) &&
+ !g_file_get_contents ("/var/lib/dbus/machine-id", &machine_id, &len, NULL))) {
+ return g_strdup ("00000000000000000000000000000000");
+ }
+
+ machine_id[len - 1] = '\0';
+ return machine_id;
+}
+
+gboolean
+gsd_wacom_manager_start (GsdWacomManager *manager,
+ GError **error)
+{
+ gnome_settings_profile_start (NULL);
+
+ register_manager (manager_object);
+
+ manager->machine_id = get_machine_id ();
+
+ manager->start_idle_id = g_idle_add ((GSourceFunc) gsd_wacom_manager_idle_cb, manager);
+ g_source_set_name_by_id (manager->start_idle_id, "[gnome-settings-daemon] gsd_wacom_manager_idle_cb");
+
+ gnome_settings_profile_end (NULL);
+
+ return TRUE;
+}
+
+void
+gsd_wacom_manager_stop (GsdWacomManager *manager)
+{
+ g_debug ("Stopping wacom manager");
+
+ g_clear_pointer (&manager->machine_id, g_free);
+
+ if (manager->name_id != 0) {
+ g_bus_unown_name (manager->name_id);
+ manager->name_id = 0;
+ }
+
+ if (manager->dbus_register_object_id) {
+ g_dbus_connection_unregister_object (manager->dbus_connection,
+ manager->dbus_register_object_id);
+ manager->dbus_register_object_id = 0;
+ }
+
+ if (manager->seat != NULL) {
+ g_signal_handler_disconnect (manager->seat, manager->device_added_id);
+ manager->seat = NULL;
+ }
+}
+
+static void
+gsd_wacom_manager_finalize (GObject *object)
+{
+ GsdWacomManager *wacom_manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSD_IS_WACOM_MANAGER (object));
+
+ wacom_manager = GSD_WACOM_MANAGER (object);
+
+ g_return_if_fail (wacom_manager != NULL);
+
+ gsd_wacom_manager_stop (wacom_manager);
+
+ if (wacom_manager->start_idle_id != 0)
+ g_source_remove (wacom_manager->start_idle_id);
+
+ g_clear_object (&wacom_manager->shell_proxy);
+
+#if HAVE_WACOM
+ libwacom_database_destroy (wacom_manager->wacom_db);
+#endif
+
+ G_OBJECT_CLASS (gsd_wacom_manager_parent_class)->finalize (object);
+}
+
+GsdWacomManager *
+gsd_wacom_manager_new (void)
+{
+ if (manager_object != NULL) {
+ g_object_ref (manager_object);
+ } else {
+ manager_object = g_object_new (GSD_TYPE_WACOM_MANAGER, NULL);
+ g_object_add_weak_pointer (manager_object,
+ (gpointer *) &manager_object);
+ }
+
+ return GSD_WACOM_MANAGER (manager_object);
+}