summaryrefslogtreecommitdiffstats
path: root/plugins/wacom
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/wacom')
-rw-r--r--plugins/wacom/gsd-wacom-manager.c528
-rw-r--r--plugins/wacom/gsd-wacom-manager.h39
-rw-r--r--plugins/wacom/gsd-wacom-oled-constants.h41
-rw-r--r--plugins/wacom/gsd-wacom-oled-helper.c422
-rw-r--r--plugins/wacom/gsd-wacom-oled.c258
-rw-r--r--plugins/wacom/gsd-wacom-oled.h34
-rw-r--r--plugins/wacom/main.c7
-rw-r--r--plugins/wacom/meson.build62
-rw-r--r--plugins/wacom/org.gnome.settings-daemon.plugins.wacom.policy.in.in46
9 files changed, 1437 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);
+}
diff --git a/plugins/wacom/gsd-wacom-manager.h b/plugins/wacom/gsd-wacom-manager.h
new file mode 100644
index 0000000..faef607
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-manager.h
@@ -0,0 +1,39 @@
+/* -*- 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/>.
+ *
+ */
+
+#ifndef __GSD_WACOM_MANAGER_H
+#define __GSD_WACOM_MANAGER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_WACOM_MANAGER (gsd_wacom_manager_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsdWacomManager, gsd_wacom_manager, GSD, WACOM_MANAGER, GObject)
+
+GsdWacomManager * gsd_wacom_manager_new (void);
+gboolean gsd_wacom_manager_start (GsdWacomManager *manager,
+ GError **error);
+void gsd_wacom_manager_stop (GsdWacomManager *manager);
+
+G_END_DECLS
+
+#endif /* __GSD_WACOM_MANAGER_H */
diff --git a/plugins/wacom/gsd-wacom-oled-constants.h b/plugins/wacom/gsd-wacom-oled-constants.h
new file mode 100644
index 0000000..e93f744
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-oled-constants.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Przemo Firszt <przemo@firszt.eu>
+ *
+ * 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/>.
+ *
+ */
+
+#ifndef __GSD_WACOM_OLED_CONSTANTS_H
+#define __GSD_WACOM_OLED_CONSTANTS_H
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GSD_WACOM_OLED_TYPE_USB,
+ GSD_WACOM_OLED_TYPE_BLUETOOTH,
+ GSD_WACOM_OLED_TYPE_RAW_BLUETOOTH
+} GsdWacomOledType;
+
+/* OLED parameters */
+#define OLED_WIDTH 64 /*Width of OLED icon - hardware dependent*/
+#define OLED_HEIGHT 32 /*Height of OLED icon - hardware dependent*/
+#define LABEL_SIZE 30 /*Maximum length of text for OLED icon*/
+#define MAX_TOKEN (LABEL_SIZE >> 1) /*Maximum number of tokens equals half of maximum number of characters*/
+#define MAX_IMAGE_SIZE 1024 /*Size of buffer for storing OLED image*/
+#define MAX_1ST_LINE_LEN 10 /*Maximum number of characters in 1st line of OLED icon*/
+
+G_END_DECLS
+
+#endif /* __GSD_WACOM_OLED_CONSTANTS_H */
diff --git a/plugins/wacom/gsd-wacom-oled-helper.c b/plugins/wacom/gsd-wacom-oled-helper.c
new file mode 100644
index 0000000..86f2891
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-oled-helper.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2012 Przemo Firszt <przemo@firszt.eu>
+ *
+ * The code is derived from gsd-wacom-led-helper.c
+ * written by:
+ * Copyright (C) 2010-2011 Richard Hughes <richard@hughsie.com>
+ * Copyright (C) 2012 Bastien Nocera <hadess@hadess.net>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <unistd.h>
+
+#include <stdlib.h>
+#include "config.h"
+
+#include <glib.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <gudev/gudev.h>
+
+#include "gsd-wacom-oled-constants.h"
+
+#define USB_PIXELS_PER_BYTE 2
+#define BT_PIXELS_PER_BYTE 8
+#define USB_BUF_LEN OLED_HEIGHT * OLED_WIDTH / USB_PIXELS_PER_BYTE
+#define BT_BUF_LEN OLED_WIDTH * OLED_HEIGHT / BT_PIXELS_PER_BYTE
+
+static void
+oled_scramble_icon (guchar *image)
+{
+ guchar buf[USB_BUF_LEN];
+ int x, y, i;
+ guchar l1, l2, h1, h2;
+
+ for (i = 0; i < USB_BUF_LEN; i++)
+ buf[i] = image[i];
+
+ for (y = 0; y < (OLED_HEIGHT / 2); y++) {
+ for (x = 0; x < (OLED_WIDTH / 2); x++) {
+ l1 = (0x0F & (buf[OLED_HEIGHT - 1 - x + OLED_WIDTH * y]));
+ l2 = (0x0F & (buf[OLED_HEIGHT - 1 - x + OLED_WIDTH * y] >> 4));
+ h1 = (0xF0 & (buf[OLED_WIDTH - 1 - x + OLED_WIDTH * y] << 4));
+ h2 = (0xF0 & (buf[OLED_WIDTH - 1 - x + OLED_WIDTH * y]));
+
+ image[2 * x + OLED_WIDTH * y] = h1 | l1;
+ image[2 * x + 1 + OLED_WIDTH * y] = h2 | l2;
+ }
+ }
+}
+
+static void
+oled_bt_scramble_icon (guchar *input_image)
+{
+ unsigned char image[BT_BUF_LEN];
+ unsigned mask;
+ unsigned s1;
+ unsigned s2;
+ unsigned r1 ;
+ unsigned r2 ;
+ unsigned r;
+ unsigned char buf[256];
+ int i, w, x, y, z;
+
+ for (i = 0; i < BT_BUF_LEN; i++)
+ image[i] = input_image[i];
+
+ for (x = 0; x < 32; x++) {
+ for (y = 0; y < 8; y++)
+ buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
+ }
+
+ /* Change 76543210 into GECA6420 as required by Intuos4 WL
+ * HGFEDCBA HFDB7531
+ */
+ for (x = 0; x < 4; x++) {
+ for (y = 0; y < 4; y++) {
+ for (z = 0; z < 8; z++) {
+ mask = 0x0001;
+ r1 = 0;
+ r2 = 0;
+ i = (x << 6) + (y << 4) + z;
+ s1 = buf[i];
+ s2 = buf[i+8];
+ for (w = 0; w < 8; w++) {
+ r1 |= (s1 & mask);
+ r2 |= (s2 & mask);
+ s1 <<= 1;
+ s2 <<= 1;
+ mask <<= 2;
+ }
+ r = r1 | (r2 << 1);
+ i = (x << 6) + (y << 4) + (z << 1);
+ image[i] = 0xFF & r;
+ image[i+1] = (0xFF00 & r) >> 8;
+ }
+ }
+ }
+ for (i = 0; i < BT_BUF_LEN; i++)
+ input_image[i] = image[i];
+}
+
+static void
+gsd_wacom_oled_convert_1_bit (guchar *image)
+{
+ guchar buf[BT_BUF_LEN];
+ guchar b0, b1, b2, b3, b4, b5, b6, b7;
+ int i;
+
+ for (i = 0; i < BT_BUF_LEN; i++) {
+ b0 = 0b10000000 & (image[(4 * i) + 0] >> 0);
+ b1 = 0b01000000 & (image[(4 * i) + 0] << 3);
+ b2 = 0b00100000 & (image[(4 * i) + 1] >> 2);
+ b3 = 0b00010000 & (image[(4 * i) + 1] << 1);
+ b4 = 0b00001000 & (image[(4 * i) + 2] >> 4);
+ b5 = 0b00000100 & (image[(4 * i) + 2] >> 1);
+ b6 = 0b00000010 & (image[(4 * i) + 3] >> 6);
+ b7 = 0b00000001 & (image[(4 * i) + 3] >> 3);
+ buf[i] = b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7;
+ }
+ for (i = 0; i < BT_BUF_LEN; i++)
+ image[i] = buf[i];
+}
+
+static int
+gsd_wacom_oled_prepare_buf (guchar *image, GsdWacomOledType type)
+{
+ int len = 0;
+
+ switch (type) {
+ case GSD_WACOM_OLED_TYPE_USB:
+ /* Image has to be scrambled for devices connected over USB ... */
+ oled_scramble_icon (image);
+ len = USB_BUF_LEN;
+ break;
+ case GSD_WACOM_OLED_TYPE_BLUETOOTH:
+ /* ... but for bluetooth it has to be converted to 1 bit colour instead of scrambling */
+ gsd_wacom_oled_convert_1_bit (image);
+ len = BT_BUF_LEN;
+ break;
+ case GSD_WACOM_OLED_TYPE_RAW_BLUETOOTH:
+ /* Image has also to be scrambled for devices connected over BT using the raw API ... */
+ gsd_wacom_oled_convert_1_bit (image);
+ len = BT_BUF_LEN;
+ oled_bt_scramble_icon (image);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return len;
+}
+
+static gboolean
+gsd_wacom_oled_helper_write (const gchar *filename, gchar *buffer, GsdWacomOledType type, GError **error)
+{
+ guchar *image;
+ gint retval;
+ gsize length;
+ gint fd = -1;
+ gboolean ret = TRUE;
+
+ fd = open (filename, O_WRONLY);
+ if (fd < 0) {
+ ret = FALSE;
+ g_set_error (error, 1, 0, "Failed to open filename: %s", filename);
+ goto out;
+ }
+
+ image = g_base64_decode (buffer, &length);
+ if (length != USB_BUF_LEN) {
+ ret = FALSE;
+ g_set_error (error, 1, 0, "Base64 buffer has length of %" G_GSIZE_FORMAT " (expected %i)", length, USB_BUF_LEN);
+ goto out;
+ }
+ if (!image) {
+ ret = FALSE;
+ g_set_error (error, 1, 0, "Decoding base64 buffer failed");
+ goto out;
+ }
+
+ length = gsd_wacom_oled_prepare_buf (image, type);
+ if (!length) {
+ ret = FALSE;
+ g_set_error (error, 1, 0, "Invalid image buffer length");
+ goto out;
+ }
+
+ retval = write (fd, image, length);
+ if (retval != length) {
+ ret = FALSE;
+ g_set_error (error, 1, 0, "Writing to %s failed", filename);
+ }
+
+ g_free (image);
+out:
+ if (fd >= 0)
+ close (fd);
+ return ret;
+}
+
+static char *
+get_oled_sysfs_path (GUdevDevice *device,
+ int button_num)
+{
+ char *status;
+ char *filename;
+
+ status = g_strdup_printf ("button%d_rawimg", button_num);
+ filename = g_build_filename (g_udev_device_get_sysfs_path (device), "wacom_led", status, NULL);
+ g_free (status);
+
+ return filename;
+}
+
+static char *
+get_bt_oled_sysfs_path (GUdevDevice *device, int button_num)
+{
+ char *status;
+ char *filename;
+
+ status = g_strdup_printf ("/oled%i_img", button_num);
+ filename = g_build_filename (g_udev_device_get_sysfs_path (device), status, NULL);
+ g_free (status);
+ return filename;
+}
+
+static char *
+get_bt_oled_filename (GUdevClient *client, GUdevDevice *device, int button_num)
+{
+ GUdevDevice *hid_dev;
+ const char *dev_uniq;
+ GList *hid_list;
+ GList *element;
+ const char *dev_hid_uniq;
+ char *filename = NULL;
+
+ dev_uniq = g_udev_device_get_property (device, "UNIQ");
+
+ hid_list = g_udev_client_query_by_subsystem (client, "hid");
+ element = g_list_first(hid_list);
+ while (element) {
+ hid_dev = (GUdevDevice*)element->data;
+ dev_hid_uniq = g_udev_device_get_property (hid_dev, "HID_UNIQ");
+ if (g_strrstr (dev_uniq, dev_hid_uniq)){
+ filename = get_bt_oled_sysfs_path (hid_dev, button_num);
+ g_object_unref (hid_dev);
+ break;
+ }
+ g_object_unref (hid_dev);
+ element = g_list_next(element);
+ }
+ g_list_free(hid_list);
+ return filename;
+}
+
+static char *
+get_oled_sys_path (GUdevClient *client,
+ GUdevDevice *device,
+ int button_num,
+ gboolean usb,
+ GsdWacomOledType *type)
+{
+ GUdevDevice *parent;
+ char *filename = NULL;
+
+ /* check for new unified hid implementation first */
+ parent = g_udev_device_get_parent_with_subsystem (device, "hid", NULL);
+ if (parent) {
+ filename = get_oled_sysfs_path (parent, button_num);
+ g_object_unref (parent);
+ if(g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ *type = usb ? GSD_WACOM_OLED_TYPE_USB : GSD_WACOM_OLED_TYPE_RAW_BLUETOOTH;
+ return filename;
+ }
+ g_clear_pointer (&filename, g_free);
+ }
+
+ /* old kernel */
+ if (usb) {
+ parent = g_udev_device_get_parent_with_subsystem (device, "usb", "usb_interface");
+ if (!parent)
+ goto no_parent;
+
+ filename = get_oled_sysfs_path (parent, button_num);
+ *type = GSD_WACOM_OLED_TYPE_USB;
+ } else if (g_strrstr (g_udev_device_get_property (device, "DEVPATH"), "bluetooth")) {
+ parent = g_udev_device_get_parent_with_subsystem (device, "input", NULL);
+ if (!parent)
+ goto no_parent;
+
+ filename = get_bt_oled_filename (client, parent, button_num);
+ *type = GSD_WACOM_OLED_TYPE_BLUETOOTH;
+ } else {
+ g_critical ("Not an expected device: '%s'",
+ g_udev_device_get_device_file (device));
+ goto out_err;
+ }
+
+ g_object_unref (parent);
+
+ return filename;
+
+no_parent:
+ g_debug ("Could not find proper parent device for '%s'",
+ g_udev_device_get_device_file (device));
+
+out_err:
+ return NULL;
+}
+
+
+int main (int argc, char **argv)
+{
+ GOptionContext *context;
+ GUdevClient *client;
+ GUdevDevice *device;
+ int uid, euid;
+ char *filename;
+ GError *error = NULL;
+ const char * const subsystems[] = { "input", NULL };
+ int ret = 1;
+ gboolean usb;
+ GsdWacomOledType type;
+
+ char *path = NULL;
+ char *buffer = NULL;
+ int button_num = -1;
+
+ const GOptionEntry options[] = {
+ { "path", '\0', 0, G_OPTION_ARG_FILENAME, &path, "Device path for the Wacom device", NULL },
+ { "buffer", '\0', 0, G_OPTION_ARG_STRING, &buffer, "Image to set base64 encoded", NULL },
+ { "button", '\0', 0, G_OPTION_ARG_INT, &button_num, "Which button icon to set", NULL },
+ { NULL}
+ };
+
+ /* get calling process */
+ uid = getuid ();
+ euid = geteuid ();
+ if (uid != 0 || euid != 0) {
+ g_print ("This program can only be used by the root user\n");
+ return 1;
+ }
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_summary (context, "GNOME Settings Daemon Wacom OLED Icon Helper");
+ g_option_context_add_main_entries (context, options, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+
+ if (path == NULL ||
+ button_num < 0 ||
+ buffer == NULL) {
+ char *txt;
+
+ txt = g_option_context_get_help (context, FALSE, NULL);
+ g_print ("%s", txt);
+ g_free (txt);
+
+ g_option_context_free (context);
+
+ return 1;
+ }
+ g_option_context_free (context);
+
+ client = g_udev_client_new (subsystems);
+ device = g_udev_client_query_by_device_file (client, path);
+ if (device == NULL) {
+ g_critical ("Could not find device '%s' in udev database", path);
+ goto out;
+ }
+
+ if (g_udev_device_get_property_as_boolean (device, "ID_INPUT_TABLET") == FALSE &&
+ g_udev_device_get_property_as_boolean (device, "ID_INPUT_TOUCHPAD") == FALSE) {
+ g_critical ("Device '%s' is not a Wacom tablet", path);
+ goto out;
+ }
+
+ if (g_strcmp0 (g_udev_device_get_property (device, "ID_BUS"), "usb") != 0)
+ usb = FALSE;
+ else
+ usb = TRUE;
+
+ filename = get_oled_sys_path (client, device, button_num, usb, &type);
+ if (!filename)
+ goto out;
+
+ if (gsd_wacom_oled_helper_write (filename, buffer, type, &error) == FALSE) {
+ g_critical ("Could not set OLED icon for '%s': %s", path, error->message);
+ g_error_free (error);
+ g_free (filename);
+ goto out;
+ }
+ g_free (filename);
+
+ g_debug ("Successfully set OLED icon for '%s', button %d", path, button_num);
+
+ ret = 0;
+
+out:
+ g_free (path);
+ g_free (buffer);
+
+ g_clear_object (&device);
+ g_clear_object (&client);
+
+ return ret;
+}
diff --git a/plugins/wacom/gsd-wacom-oled.c b/plugins/wacom/gsd-wacom-oled.c
new file mode 100644
index 0000000..4c2daf1
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-oled.c
@@ -0,0 +1,258 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Przemo Firszt <przemo@firszt.eu>
+ *
+ * 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 <unistd.h>
+#include <math.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gsd-wacom-oled.h"
+
+#define MAGIC_BASE64 "base64:" /*Label starting with base64: is treated as already encoded*/
+#define MAGIC_BASE64_LEN strlen(MAGIC_BASE64)
+
+static void
+oled_surface_to_image (guchar *image,
+ cairo_surface_t *surface)
+{
+ unsigned char *csurf;
+ int i, x, y;
+ unsigned char lo, hi;
+
+ cairo_surface_flush (surface);
+ csurf = cairo_image_surface_get_data (surface);
+ i = 0;
+ for (y = 0; y < OLED_HEIGHT; y++) {
+ for (x = 0; x < (OLED_WIDTH / 2); x++) {
+ hi = 0xf0 & csurf[4 * OLED_WIDTH * y + 8 * x + 1];
+ lo = 0x0f & (csurf[4 * OLED_WIDTH * y + 8 * x + 5] >> 4);
+ image[i] = hi | lo;
+ i++;
+ }
+ }
+}
+
+static void
+oled_split_text (char *label,
+ char *line1,
+ char *line2)
+{
+ char delimiters[5] = "+-_ ";
+ char **token;
+ int token_len[MAX_TOKEN];
+ gsize length;
+ int i;
+
+ if (g_utf8_strlen (label, LABEL_SIZE) <= MAX_1ST_LINE_LEN) {
+ g_utf8_strncpy (line1, label, MAX_1ST_LINE_LEN);
+ return;
+ }
+
+ token = g_strsplit_set (label, delimiters, -1);
+
+ if (g_utf8_strlen (token[0], LABEL_SIZE) > MAX_1ST_LINE_LEN) {
+ g_utf8_strncpy (line1, label, MAX_1ST_LINE_LEN);
+ g_utf8_strncpy (line2, label + MAX_1ST_LINE_LEN, LABEL_SIZE - MAX_1ST_LINE_LEN);
+ return;
+ }
+
+ for (i = 0; token[i] != NULL; i++)
+ token_len[i] = g_utf8_strlen (token[i], LABEL_SIZE);
+
+ length = token_len[0];
+ i = 0;
+ while ((length + token_len[i + 1] + 1) <= MAX_1ST_LINE_LEN) {
+ i++;
+ length = length + token_len[i] + 1;
+ }
+
+ g_utf8_strncpy (line1, label, length);
+ g_utf8_strncpy (line2, label + length + 1, LABEL_SIZE - length);
+
+ return;
+}
+
+static void
+oled_render_text (char *label,
+ guchar *image,
+ gboolean left_handed)
+{
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ int width, height;
+ double dx, dy;
+ char line1[LABEL_SIZE + 1] = "";
+ char line2[LABEL_SIZE + 1] = "";
+ char *buf;
+
+ oled_split_text (label ,line1, line2);
+
+ buf = g_strdup_printf ("%s\n%s", line1, line2);
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, OLED_WIDTH, OLED_HEIGHT);
+ cr = cairo_create (surface);
+
+ /* Rotate text so it's seen correctly on the device, or at
+ * least from top to bottom for LTR text in vertical modes.
+ */
+ if (left_handed) {
+ cairo_translate (cr, OLED_WIDTH, OLED_HEIGHT);
+ cairo_scale (cr, -1, -1);
+ }
+
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.99);
+ cairo_paint (cr);
+
+ layout = pango_cairo_create_layout (cr);
+ pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+ pango_layout_set_text (layout, buf, - 1);
+ g_free (buf);
+ desc = pango_font_description_new ();
+
+ pango_font_description_set_family (desc, "Terminal");
+ pango_font_description_set_absolute_size (desc, PANGO_SCALE * 11);
+ pango_layout_set_font_description (layout, desc);
+ pango_font_description_free (desc);
+ pango_layout_get_size (layout, &width, &height);
+ width = width/PANGO_SCALE;
+ cairo_new_path (cr);
+
+ dx = trunc (((double)OLED_WIDTH - width) / 2);
+
+ if (*line2 == '\0')
+ dy = 10;
+ else
+ dy = 4;
+
+ cairo_move_to (cr, dx, dy);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
+ pango_cairo_update_layout (cr, layout);
+
+ pango_cairo_layout_path (cr, layout);
+ cairo_fill (cr);
+
+ oled_surface_to_image (image, surface);
+
+ g_object_unref (layout);
+ cairo_destroy (cr);
+
+ cairo_surface_destroy (surface);
+}
+
+char *
+gsd_wacom_oled_gdkpixbuf_to_base64 (GdkPixbuf *pixbuf)
+{
+ int i, x, y, ch, rs;
+ guchar *pix, *p;
+ guchar *image;
+ guchar lo, hi;
+ char *base_string, *base64;
+
+ if (OLED_WIDTH != gdk_pixbuf_get_width (pixbuf))
+ return NULL;
+
+ if (OLED_HEIGHT != gdk_pixbuf_get_height (pixbuf))
+ return NULL;
+
+ ch = gdk_pixbuf_get_n_channels (pixbuf);
+ rs = gdk_pixbuf_get_rowstride (pixbuf);
+ pix = gdk_pixbuf_get_pixels (pixbuf);
+
+ image = g_malloc (MAX_IMAGE_SIZE);
+ i = 0;
+ for (y = 0; y < OLED_HEIGHT; y++) {
+ for (x = 0; x < (OLED_WIDTH / 2); x++) {
+ p = pix + y * rs + 2 * x * ch;
+ hi = 0xf0 & ((p[0] + p[1] + p[2])/ 3 * p[3] / 0xff);
+ p = pix + y * rs + ((2 * x) + 1) * ch;
+ lo = 0x0f & (((p[0] + p[1] + p[2])/ 3 * p[3] / 0xff) >> 4);
+ image[i] = hi | lo;
+ i++;
+ }
+ }
+
+ base_string = g_base64_encode (image, MAX_IMAGE_SIZE);
+ base64 = g_strconcat (MAGIC_BASE64, base_string, NULL);
+ g_free (base_string);
+ g_free (image);
+
+ return base64;
+}
+
+static char *
+oled_encode_image (char *label,
+ gboolean left_handed)
+{
+ unsigned char *image;
+
+ image = g_malloc (MAX_IMAGE_SIZE);
+
+ /* convert label to image */
+ oled_render_text (label, image, left_handed);
+
+ return (g_base64_encode (image, MAX_IMAGE_SIZE));
+}
+
+gboolean
+set_oled (const gchar *device_path,
+ gboolean left_handed,
+ guint button,
+ char *label,
+ GError **error)
+{
+ char *command;
+ gboolean ret;
+ char *buffer;
+ gint status;
+
+#ifndef HAVE_GUDEV
+ /* Not implemented on non-Linux systems */
+ return TRUE;
+#endif
+
+ if (g_str_has_prefix (label, MAGIC_BASE64)) {
+ buffer = g_strdup (label + MAGIC_BASE64_LEN);
+ } else {
+ buffer = oled_encode_image (label, left_handed);
+ }
+
+ g_debug ("Setting OLED label '%s' on button %d (device %s)", label, button, device_path);
+
+ command = g_strdup_printf ("pkexec " LIBEXECDIR "/gsd-wacom-oled-helper --path %s --button %d --buffer %s",
+ device_path, button, buffer);
+ ret = g_spawn_command_line_sync (command,
+ NULL,
+ NULL,
+ &status,
+ error);
+
+ if (ret == TRUE && status != 0)
+ ret = FALSE;
+
+ g_free (command);
+
+ return ret;
+}
diff --git a/plugins/wacom/gsd-wacom-oled.h b/plugins/wacom/gsd-wacom-oled.h
new file mode 100644
index 0000000..dc5d39c
--- /dev/null
+++ b/plugins/wacom/gsd-wacom-oled.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Przemo Firszt <przemo@firszt.eu>
+ *
+ * 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 "gsd-wacom-oled-constants.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#ifndef __GSD_WACOM_OLED_H
+#define __GSD_WACOM_OLED_H
+
+G_BEGIN_DECLS
+
+gboolean set_oled (const gchar *device_path, gboolean left_handed, guint button, char *label, GError **error);
+char *gsd_wacom_oled_gdkpixbuf_to_base64 (GdkPixbuf *pixbuf);
+
+G_END_DECLS
+
+#endif /* __GSD_WACOM_OLED_H */
diff --git a/plugins/wacom/main.c b/plugins/wacom/main.c
new file mode 100644
index 0000000..a19a71b
--- /dev/null
+++ b/plugins/wacom/main.c
@@ -0,0 +1,7 @@
+#define NEW gsd_wacom_manager_new
+#define START gsd_wacom_manager_start
+#define STOP gsd_wacom_manager_stop
+#define MANAGER GsdWacomManager
+#include "gsd-wacom-manager.h"
+
+#include "daemon-skeleton-gtk.h"
diff --git a/plugins/wacom/meson.build b/plugins/wacom/meson.build
new file mode 100644
index 0000000..c00323d
--- /dev/null
+++ b/plugins/wacom/meson.build
@@ -0,0 +1,62 @@
+policy = 'org.gnome.settings-daemon.plugins.wacom.policy'
+
+policy_in = configure_file(
+ input: policy + '.in.in',
+ output: policy + '.in',
+ configuration: plugins_conf
+)
+
+i18n.merge_file(
+ input: policy_in,
+ output: policy,
+ po_dir: po_dir,
+ install: true,
+ install_dir: join_paths(gsd_datadir, 'polkit-1', 'actions')
+)
+
+sources = files(
+ 'gsd-wacom-manager.c',
+ 'gsd-wacom-oled.c',
+ 'main.c'
+)
+
+deps = plugins_deps + [
+ gtk_dep,
+ libcommon_dep,
+ m_dep,
+ pango_dep
+]
+
+if enable_wacom
+ deps += libwacom_dep
+endif
+
+cflags += ['-DLIBEXECDIR="@0@"'.format(gsd_libexecdir)]
+
+executable(
+ 'gsd-' + plugin_name,
+ sources,
+ include_directories: [top_inc, data_inc],
+ dependencies: deps,
+ c_args: cflags,
+ install: true,
+ install_rpath: gsd_pkglibdir,
+ install_dir: gsd_libexecdir
+)
+
+if enable_gudev
+ deps = [
+ gudev_dep,
+ m_dep
+ ]
+
+ executable(
+ 'gsd-wacom-oled-helper',
+ 'gsd-wacom-oled-helper.c',
+ include_directories: top_inc,
+ dependencies: deps,
+ install: true,
+ install_rpath: gsd_pkglibdir,
+ install_dir: gsd_libexecdir
+ )
+endif
diff --git a/plugins/wacom/org.gnome.settings-daemon.plugins.wacom.policy.in.in b/plugins/wacom/org.gnome.settings-daemon.plugins.wacom.policy.in.in
new file mode 100644
index 0000000..fe0df93
--- /dev/null
+++ b/plugins/wacom/org.gnome.settings-daemon.plugins.wacom.policy.in.in
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+<policyconfig>
+
+ <!--
+ Policy definitions for gnome-settings-daemon system-wide actions.
+ -->
+
+ <vendor>GNOME Settings Daemon</vendor>
+ <vendor_url>http://git.gnome.org/browse/gnome-settings-daemon</vendor_url>
+ <icon_name>input-tablet</icon_name>
+
+ <action id="org.gnome.settings-daemon.plugins.wacom.wacom-led-helper">
+ <!-- SECURITY:
+ - A normal active user on the local machine does not need permission
+ to change the LED setting for a Wacom tablet
+ -->
+ <description>Modify the lit LED for a Wacom tablet</description>
+ <message>Authentication is required to modify the lit LED for a Wacom tablet</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.exec.path">@libexecdir@/gsd-wacom-led-helper</annotate>
+ </action>
+
+ <action id="org.gnome.settings-daemon.plugins.wacom.wacom-oled-helper">
+ <!-- SECURITY:
+ - A normal active user on the local machine does not need permission
+ to change the OLED images for a Wacom tablet
+ -->
+ <description>Modify the OLED image for a Wacom tablet</description>
+ <message>Authentication is required to modify the OLED image for a Wacom tablet</message>
+ <defaults>
+ <allow_any>no</allow_any>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.exec.path">@libexecdir@/gsd-wacom-oled-helper</annotate>
+ </action>
+
+</policyconfig>
+