[gnome-settings-daemon] common: Add GsdDeviceMapper



commit bf2f0d5d955f9fe5ca9f880e9f52246016e60e79
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Feb 3 12:49:52 2014 +0100

    common: Add GsdDeviceMapper
    
    This is an object attached to the default screen, meant to be used across
    several g-s-d plugins. On one hand, it keeps track of monitors and their
    layout/rotation/mode. On the other hand, g-s-d plugins tell GsdDeviceMapper
    which GdkDevices will be handled by this object, so a monitor is chosen
    to map the GdkDevice to, applying what we know about the input device.
    
    The primary source to know the corresponding monitor is the configuration,
    if an input device has a configured display in GSettings, and the monitor
    is currently present, that will be the one the input device will map to.
    
    When an input device has no settings, or an unconfigured monitor, multiple
    things are checked in order to find out a good guess for the GdkDevice.
    - System-integrated input devices go to the built-in output
    - Several EDID checks are done for Wacom devices with a built-in screen
    - Sanity checks are done on outputs with screen-integrated input devices,
      if a monitor already has a GdkDevice with similar capabilities (eg. a
      system-integrated pen), this output will be punted, so there is no
      accumulation of input devices.
    
    If an output is found on some or other way, the GdkDevice will be
    mapped to that output, and follow it if the display configuration
    changes, or device left/right handedness changes.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=709600

 configure.ac                       |    1 +
 plugins/common/Makefile.am         |    7 +
 plugins/common/gsd-device-mapper.c | 1232 ++++++++++++++++++++++++++++++++++++
 plugins/common/gsd-device-mapper.h |   68 ++
 plugins/common/gsd-input-helper.c  |   35 +
 plugins/common/gsd-input-helper.h  |    2 +
 6 files changed, 1345 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 419ad4c..9135900 100644
--- a/configure.ac
+++ b/configure.ac
@@ -281,6 +281,7 @@ case $host_os in
       have_wacom=no
     else
       if test x$enable_gudev != xno; then
+        PKG_CHECK_MODULES(LIBWACOM, [libwacom >= $LIBWACOM_REQUIRED_VERSION])
         PKG_CHECK_MODULES(WACOM, [libwacom >= $LIBWACOM_REQUIRED_VERSION x11 xi xtst gudev-1.0 
gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION xorg-wacom librsvg-2.0 >= $LIBRSVG_REQUIRED_VERSION 
libnotify >= $LIBNOTIFY_REQUIRED_VERSION pango >= $PANGO_REQUIRED_VERSION])
         PKG_CHECK_MODULES(WACOM_OLED, [gudev-1.0])
       else
diff --git a/plugins/common/Makefile.am b/plugins/common/Makefile.am
index a26f4a7..fd2a7af 100644
--- a/plugins/common/Makefile.am
+++ b/plugins/common/Makefile.am
@@ -3,6 +3,8 @@ plugin_name = common
 noinst_LTLIBRARIES = libcommon.la
 
 libcommon_la_SOURCES = \
+       gsd-device-mapper.c     \
+       gsd-device-mapper.h     \
        gsd-keygrab.c           \
        gsd-keygrab.h           \
        gsd-input-helper.c      \
@@ -13,12 +15,15 @@ libcommon_la_SOURCES = \
 libcommon_la_CPPFLAGS = \
        -I$(top_srcdir)/gnome-settings-daemon   \
        -I$(top_builddir)/gnome-settings-daemon \
+       -I$(top_srcdir)/data/                           \
        $(AM_CPPFLAGS)
 
 libcommon_la_CFLAGS = \
        $(PLUGIN_CFLAGS)                \
        $(SETTINGS_PLUGIN_CFLAGS)       \
        $(COMMON_CFLAGS)                \
+       $(GNOME_DESKTOP_CFLAGS)         \
+       $(LIBWACOM_CFLAGS)              \
        $(AM_CFLAGS)
 
 libcommon_la_LDFLAGS = \
@@ -26,6 +31,8 @@ libcommon_la_LDFLAGS = \
 
 libcommon_la_LIBADD  = \
        $(SETTINGS_PLUGIN_LIBS)         \
+       $(GNOME_DESKTOP_LIBS)           \
+       $(LIBWACOM_LIBS)                \
        $(COMMON_LIBS)
 
 libexec_PROGRAMS = gsd-test-input-helper
diff --git a/plugins/common/gsd-device-mapper.c b/plugins/common/gsd-device-mapper.c
new file mode 100644
index 0000000..82a4d3d
--- /dev/null
+++ b/plugins/common/gsd-device-mapper.c
@@ -0,0 +1,1232 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2014 Carlos Garnacho <carlosg gnome org>
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gtk/gtkx.h>
+#include <X11/Xatom.h>
+
+#if HAVE_WACOM
+#include <libwacom/libwacom.h>
+#endif
+
+#include "gsd-device-mapper.h"
+#include "gsd-input-helper.h"
+#include "gsd-enums.h"
+
+typedef struct _GsdInputInfo GsdInputInfo;
+typedef struct _GsdOutputInfo GsdOutputInfo;
+typedef struct _MappingHelper MappingHelper;
+typedef struct _DeviceMapHelper DeviceMapHelper;
+
+#define NUM_ELEMS_MATRIX 9
+#define KEY_DISPLAY  "display"
+#define KEY_ROTATION "rotation"
+
+typedef enum {
+       GSD_INPUT_IS_SYSTEM_INTEGRATED = 1 << 0, /* eg. laptop tablets/touchscreens */
+       GSD_INPUT_IS_SCREEN_INTEGRATED = 1 << 1, /* eg. Wacom Cintiq devices */
+       GSD_INPUT_IS_TOUCH             = 1 << 2, /* touch device, either touchscreen or tablet */
+       GSD_INPUT_IS_PEN               = 1 << 3, /* pen device, either touchscreen or tablet */
+       GSD_INPUT_IS_ERASER            = 1 << 4, /* eraser device, either touchscreen or tablet */
+       GSD_INPUT_IS_PAD               = 1 << 5  /* pad device, most usually in tablets */
+} GsdInputCapabilityFlags;
+
+typedef enum {
+       GSD_PRIO_BUILTIN,            /* Output is builtin, applies mainly to system-integrated devices */
+       GSD_PRIO_EDID_MATCH_FULL,    /* Full EDID model match, eg. "Cintiq 12WX" */
+       GSD_PRIO_EDID_MATCH_PARTIAL, /* Partial EDID model match, eg. "Cintiq" */
+       GSD_PRIO_EDID_MATCH_VENDOR,  /* EDID vendor match, eg. "WAC" for Wacom */
+       N_OUTPUT_PRIORITIES
+} GsdOutputPriority;
+
+struct _GsdInputInfo {
+       GdkDevice *device;
+       GSettings *settings;
+       GsdDeviceMapper *mapper;
+       GsdOutputInfo *output;
+       GsdOutputInfo *guessed_output;
+       guint changed_id;
+       GsdInputCapabilityFlags capabilities;
+};
+
+struct _GsdOutputInfo {
+       GnomeRROutput *output;
+       GList *input_devices;
+};
+
+struct _DeviceMapHelper {
+       GsdInputInfo *input;
+       GnomeRROutput *candidates[N_OUTPUT_PRIORITIES];
+       GsdOutputPriority highest_prio;
+       guint n_candidates;
+};
+
+struct _MappingHelper {
+       GArray *device_maps;
+};
+
+struct _GsdDeviceMapper {
+       GObject parent_instance;
+       GdkScreen *screen;
+       GnomeRRScreen *rr_screen;
+       GHashTable *input_devices; /* GdkDevice -> GsdInputInfo */
+       GHashTable *output_devices; /* GnomeRROutput -> GsdOutputInfo */
+#if HAVE_WACOM
+       WacomDeviceDatabase *wacom_db;
+#endif
+};
+
+struct _GsdDeviceMapperClass {
+       GObjectClass parent_class;
+};
+
+/* Array order matches GsdWacomRotation */
+struct {
+       GnomeRRRotation rotation;
+       /* Coordinate Transformation Matrix */
+       gfloat matrix[NUM_ELEMS_MATRIX];
+} rotation_matrices[] = {
+       { GNOME_RR_ROTATION_0, { 1, 0, 0, 0, 1, 0, 0, 0, 1 } },
+       { GNOME_RR_ROTATION_90, { 0, -1, 1, 1, 0, 0, 0, 0, 1 } },
+       { GNOME_RR_ROTATION_270, { 0, 1, 0, -1, 0, 1, 0,  0, 1 } },
+       { GNOME_RR_ROTATION_180, { -1, 0, 1, 0, -1, 1, 0, 0, 1 } }
+};
+
+enum {
+       PROP_0,
+       PROP_SCREEN
+};
+
+enum {
+       DEVICE_CHANGED,
+       N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+static void gsd_device_mapper_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GsdDeviceMapper, gsd_device_mapper, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                               gsd_device_mapper_initable_iface_init))
+
+static XDevice *
+open_device (GdkDevice *device)
+{
+       XDevice *xdev;
+       int id;
+
+       id = gdk_x11_device_get_id (device);
+
+       gdk_error_trap_push ();
+       xdev = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), id);
+       if (gdk_error_trap_pop () || (xdev == NULL))
+               return NULL;
+
+       return xdev;
+}
+
+static gboolean
+device_apply_property (GdkDevice      *device,
+                      PropertyHelper *property)
+{
+       gboolean retval;
+       XDevice *xdev;
+
+       xdev = open_device (device);
+
+       if (!xdev)
+               return FALSE;
+
+       retval = device_set_property (xdev, gdk_device_get_name (device), property);
+       XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdev);
+       return retval;
+}
+
+static gboolean
+device_set_matrix (GdkDevice *device,
+                  gfloat     matrix[NUM_ELEMS_MATRIX])
+{
+       PropertyHelper property = {
+               .name = "Coordinate Transformation Matrix",
+               .nitems = 9,
+               .format = 32,
+               .type = gdk_x11_get_xatom_by_name ("FLOAT"),
+               .data.i = (int *) matrix,
+       };
+
+       g_debug ("Setting '%s' matrix to:\n %f,%f,%f,\n %f,%f,%f,\n %f,%f,%f",
+                gdk_device_get_name (device),
+                matrix[0], matrix[1], matrix[2],
+                matrix[3], matrix[4], matrix[5],
+                matrix[6], matrix[7], matrix[8]);
+
+       return device_apply_property (device, &property);
+}
+
+/* Finds an output which matches the given EDID information. Any NULL
+ * parameter will be interpreted to match any value. */
+static GnomeRROutput *
+find_output_by_edid (GnomeRRScreen *rr_screen,
+                    const gchar   *edid[3])
+{
+       GnomeRROutput **outputs;
+       GnomeRROutput *retval = NULL;
+       guint i;
+
+       outputs = gnome_rr_screen_list_outputs (rr_screen);
+
+       for (i = 0; outputs[i] != NULL; i++) {
+               gchar *vendor, *product, *serial;
+               gboolean match;
+
+               gnome_rr_output_get_ids_from_edid (outputs[i], &vendor,
+                                                  &product, &serial);
+
+               g_debug ("Checking for match between ['%s','%s','%s'] and ['%s','%s','%s']", \
+                        edid[0], edid[1], edid[2], vendor, product, serial);
+
+               match = (edid[0] == NULL || g_strcmp0 (edid[0], vendor)  == 0) && \
+                       (edid[1] == NULL || g_strcmp0 (edid[1], product) == 0) && \
+                       (edid[2] == NULL || g_strcmp0 (edid[2], serial)  == 0);
+
+               g_free (vendor);
+               g_free (product);
+               g_free (serial);
+
+               if (match) {
+                       retval = outputs[i];
+                       break;
+               }
+       }
+
+       if (retval == NULL)
+               g_debug ("Did not find a matching output for EDID '%s,%s,%s'",
+                        edid[0], edid[1], edid[2]);
+       return retval;
+}
+
+static GnomeRROutput *
+find_builtin_output (GnomeRRScreen *rr_screen)
+{
+       GnomeRROutput **outputs;
+       guint i;
+
+       outputs = gnome_rr_screen_list_outputs (rr_screen);
+
+       for (i = 0; outputs[i] != NULL; i++) {
+               if (!gnome_rr_output_is_builtin_display (outputs[i]))
+                       continue;
+
+               return outputs[i];
+       }
+
+       g_debug ("Did not find a built-in monitor");
+       return NULL;
+}
+
+static GnomeRROutput *
+monitor_to_output (GsdDeviceMapper *mapper,
+                  gint             monitor_num)
+{
+       GnomeRROutput **outputs;
+       guint i;
+
+       outputs = gnome_rr_screen_list_outputs (mapper->rr_screen);
+
+       for (i = 0; outputs[i] != NULL; i++) {
+               GnomeRRCrtc *crtc = gnome_rr_output_get_crtc (outputs[i]);
+               gint x, y;
+
+               if (!crtc)
+                       continue;
+
+               gnome_rr_crtc_get_position (crtc, &x, &y);
+
+               if (monitor_num == gdk_screen_get_monitor_at_point (mapper->screen, x, y))
+                       return outputs[i];
+       }
+
+       return NULL;
+}
+
+static MappingHelper *
+mapping_helper_new (void)
+{
+       MappingHelper *helper;
+
+       helper = g_new0 (MappingHelper, 1);
+       helper->device_maps = g_array_new (FALSE, FALSE, sizeof (DeviceMapHelper));
+
+       return helper;
+}
+
+static void
+mapping_helper_free (MappingHelper *helper)
+{
+       g_array_unref (helper->device_maps);
+       g_free (helper);
+}
+
+static void
+mapping_helper_add (MappingHelper *helper,
+                   GsdInputInfo  *input,
+                   GnomeRROutput *outputs[N_OUTPUT_PRIORITIES])
+{
+       guint i, pos, highest = N_OUTPUT_PRIORITIES;
+       DeviceMapHelper info = { 0 }, *prev;
+
+       info.input = input;
+
+       for (i = 0; i < N_OUTPUT_PRIORITIES; i++) {
+               if (outputs[i] == NULL)
+                       continue;
+
+               if (highest > i)
+                       highest = i;
+
+               info.candidates[i] = outputs[i];
+               info.n_candidates++;
+       }
+
+       info.highest_prio = highest;
+       pos = helper->device_maps->len;
+
+       for (i = 0; i < helper->device_maps->len; i++) {
+               prev = &g_array_index (helper->device_maps, DeviceMapHelper, i);
+
+               if (prev->highest_prio < info.highest_prio)
+                       pos = i;
+       }
+
+       if (pos >= helper->device_maps->len)
+               g_array_append_val (helper->device_maps, info);
+       else
+               g_array_insert_val (helper->device_maps, pos, info);
+}
+
+/* This function gets a map of outputs, sorted by confidence, for a given device,
+ * the array can actually contain NULLs if no output matched a priority. */
+static void
+input_info_guess_candidates (GsdInputInfo  *input,
+                            GnomeRROutput *outputs[N_OUTPUT_PRIORITIES])
+{
+       gboolean found = FALSE;
+       const gchar *name;
+       gchar **split;
+
+       name = gdk_device_get_name (input->device);
+       split = g_strsplit (name, " ", -1);
+
+       /* On Wacom devices that are integrated on a not-in-system screen (eg. Cintiqs),
+        * there is usually a minimal relation between the input device name and the EDID
+        * vendor/model fields. Attempt to find matching outputs and fill in the map
+        * from GSD_PRIO_EDID_MATCH_FULL to GSD_PRIO_EDID_MATCH_VENDOR.
+        */
+       if (input->capabilities & GSD_INPUT_IS_SCREEN_INTEGRATED &&
+           g_str_has_prefix (name, "Wacom ")) {
+               gchar *product = g_strdup_printf ("%s %s", split[1], split[2]);
+               const gchar *edids[][3] = {
+                       { "WAC", product, NULL },
+                       { "WAC", split[1], NULL },
+                       { "WAC", NULL, NULL }
+               };
+               gint i;
+
+               for (i = 0; i < G_N_ELEMENTS (edids); i++) {
+                       /* i + 1 matches the desired priority, we skip GSD_PRIO_BUILTIN here */
+                       outputs[i + 1] =
+                               find_output_by_edid (input->mapper->rr_screen,
+                                                    edids[i]);
+                       found |= outputs[i + 1] != NULL;
+               }
+
+               g_free (product);
+       }
+
+       /* For input devices that we certainly know that are system-integrated, or
+        * for screen-integrated devices we weren't able to find an output for,
+        * find the builtin screen.
+        */
+       if ((input->capabilities & GSD_INPUT_IS_SYSTEM_INTEGRATED) ||
+           (!found && input->capabilities & GSD_INPUT_IS_SCREEN_INTEGRATED)) {
+               outputs[GSD_PRIO_BUILTIN] =
+                       find_builtin_output (input->mapper->rr_screen);
+       }
+
+       g_strfreev (split);
+}
+
+static gboolean
+output_has_input_type (GsdOutputInfo *info,
+                      guint          capabilities)
+{
+       GList *devices;
+
+       for (devices = info->input_devices; devices; devices = devices->next) {
+               GsdInputInfo *input = devices->data;
+
+               if (input->capabilities == capabilities)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static GnomeRROutput *
+settings_get_display (GSettings              *settings,
+                     GsdDeviceMapper *mapper)
+{
+       GnomeRROutput *output = NULL;
+       gchar **edid;
+       guint nvalues;
+
+       edid = g_settings_get_strv (settings, KEY_DISPLAY);
+       nvalues = g_strv_length (edid);
+
+       if (nvalues == 3) {
+               output = find_output_by_edid (mapper->rr_screen, (const gchar **) edid);
+       } else {
+               g_warning ("Unable to get display property. Got %d items, "
+                          "expected %d items.\n", nvalues, 3);
+       }
+
+       g_strfreev (edid);
+
+       return output;
+}
+
+static void
+settings_set_display (GSettings            *settings,
+                     GnomeRROutput *output)
+{
+       gchar **prev, *edid[4] = { NULL, NULL, NULL, NULL };
+       GVariant *value;
+       gsize nvalues;
+
+       prev = g_settings_get_strv (settings, KEY_DISPLAY);
+       nvalues = g_strv_length (prev);
+
+       if (output)
+               gnome_rr_output_get_ids_from_edid (output, &edid[0],
+                                                  &edid[1], &edid[2]);
+
+       if (nvalues != 3 ||
+           g_strcmp0 (prev[0], edid[0]) != 0 ||
+           g_strcmp0 (prev[1], edid[1]) != 0 ||
+           g_strcmp0 (prev[2], edid[2]) != 0) {
+               value = g_variant_new_strv ((const gchar * const *) &edid, 3);
+               g_settings_set_value (settings, KEY_DISPLAY, value);
+       }
+
+       g_free (edid[0]);
+       g_free (edid[1]);
+       g_free (edid[2]);
+       g_strfreev (prev);
+}
+
+static void
+input_info_set_output (GsdInputInfo  *input,
+                      GsdOutputInfo *output,
+                      gboolean       guessed,
+                      gboolean       save)
+{
+       GnomeRROutput *rr_output = NULL;
+       GsdOutputInfo **ptr;
+
+       if (guessed) {
+               /* If there is already a non-guessed input, go for it */
+               if (input->output)
+                       return;
+
+               ptr = &input->guessed_output;
+       } else {
+               /* Unset guessed output */
+               if (input->guessed_output)
+                       input_info_set_output (input, NULL, TRUE, FALSE);
+               ptr = &input->output;
+       }
+
+       if (*ptr == output)
+               return;
+
+       if (*ptr) {
+               (*ptr)->input_devices = g_list_remove ((*ptr)->input_devices,
+                                                      input);
+       }
+
+       if (output) {
+               output->input_devices = g_list_prepend (output->input_devices,
+                                                       input);
+               rr_output = output->output;
+       }
+
+       if (input->settings && !guessed && save)
+               settings_set_display (input->settings, rr_output);
+
+       *ptr = output;
+}
+
+static GsdOutputInfo *
+input_info_get_output (GsdInputInfo *input)
+{
+       if (input->output)
+               return input->output;
+
+       if (input->guessed_output)
+               return input->guessed_output;
+
+       if (g_hash_table_size (input->mapper->output_devices) == 1) {
+               GsdOutputInfo *output;
+               GHashTableIter iter;
+
+               g_hash_table_iter_init (&iter, input->mapper->output_devices);
+               g_hash_table_iter_next (&iter, NULL, (gpointer *) &output);
+
+               return output;
+       }
+
+       return NULL;
+}
+
+static void
+init_device_rotation_matrix (GsdWacomRotation rotation,
+                            float            matrix[NUM_ELEMS_MATRIX])
+{
+        memcpy (matrix, rotation_matrices[rotation].matrix,
+                sizeof (rotation_matrices[rotation].matrix));
+}
+
+static void
+init_output_rotation_matrix (GnomeRRRotation rotation,
+                            float           matrix[NUM_ELEMS_MATRIX])
+{
+       guint i;
+
+       for (i = 0; i < G_N_ELEMENTS (rotation_matrices); i++) {
+               if (rotation_matrices[i].rotation != rotation)
+                       continue;
+
+               memcpy (matrix, rotation_matrices[i].matrix, sizeof (rotation_matrices[i].matrix));
+               return;
+       }
+
+       /* We know nothing about this rotation */
+       init_device_rotation_matrix (GSD_WACOM_ROTATION_NONE, matrix);
+}
+
+static void
+multiply_matrix (float a[NUM_ELEMS_MATRIX],
+                float b[NUM_ELEMS_MATRIX],
+                float res[NUM_ELEMS_MATRIX])
+{
+       float result[NUM_ELEMS_MATRIX];
+
+       result[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
+       result[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
+       result[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
+       result[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
+       result[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
+       result[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
+       result[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
+       result[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
+       result[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
+
+       memcpy (res, result, sizeof (result));
+}
+
+static void
+calculate_viewport_matrix (const GdkRectangle mapped,
+                          const GdkRectangle desktop,
+                          float              viewport[NUM_ELEMS_MATRIX])
+{
+       float x_scale = (float) mapped.x / desktop.width;
+       float y_scale = (float) mapped.y / desktop.height;
+       float width_scale  = (float) mapped.width / desktop.width;
+       float height_scale = (float) mapped.height / desktop.height;
+
+       viewport[0] = width_scale;
+       viewport[1] = 0.0f;
+       viewport[2] = x_scale;
+
+       viewport[3] = 0.0f;
+       viewport[4] = height_scale;
+       viewport[5] = y_scale;
+
+       viewport[6] = 0.0f;
+       viewport[7] = 0.0f;
+       viewport[8] = 1.0f;
+}
+
+static gint
+monitor_for_output (GnomeRROutput *output)
+{
+       GdkScreen *screen = gdk_screen_get_default ();
+       GnomeRRCrtc *crtc = gnome_rr_output_get_crtc (output);
+       gint x, y;
+
+       if (!crtc)
+               return -1;
+
+       gnome_rr_crtc_get_position (crtc, &x, &y);
+
+       return gdk_screen_get_monitor_at_point (screen, x, y);
+}
+
+static void
+input_info_get_matrix (GsdInputInfo *input,
+                      float         matrix[NUM_ELEMS_MATRIX])
+{
+       GsdOutputInfo *output;
+
+       output = input_info_get_output (input);
+
+       if (!output) {
+               init_output_rotation_matrix (GNOME_RR_ROTATION_0, matrix);
+       } else {
+               GdkScreen *screen = gdk_screen_get_default ();
+               float viewport[NUM_ELEMS_MATRIX];
+               float output_rot[NUM_ELEMS_MATRIX];
+               GdkRectangle display, desktop = { 0 };
+               GnomeRRRotation rotation;
+               GnomeRRCrtc *crtc;
+               int monitor;
+
+               g_debug ("Mapping '%s' to output '%s'",
+                        gdk_device_get_name (input->device),
+                        gnome_rr_output_get_name (output->output));
+
+               crtc = gnome_rr_output_get_crtc (output->output);
+               rotation = gnome_rr_crtc_get_current_rotation (crtc);
+               init_output_rotation_matrix (rotation, output_rot);
+
+               desktop.width = gdk_screen_get_width (screen);
+               desktop.height = gdk_screen_get_height (screen);
+
+               monitor = monitor_for_output (output->output);
+               gdk_screen_get_monitor_geometry (screen, monitor, &display);
+               calculate_viewport_matrix (display, desktop, viewport);
+
+               multiply_matrix (viewport, output_rot, matrix);
+       }
+
+       /* Apply device rotation after output rotation */
+       if (input->settings &&
+           (input->capabilities &
+            (GSD_INPUT_IS_SYSTEM_INTEGRATED | GSD_INPUT_IS_SCREEN_INTEGRATED)) == 0) {
+               gint rotation;
+
+               rotation = g_settings_get_enum (input->settings, KEY_ROTATION);
+
+               if (rotation > 0) {
+                       float device_rot[NUM_ELEMS_MATRIX];
+
+                       g_debug ("Applying device rotation %d to '%s'",
+                                rotation, gdk_device_get_name (input->device));
+
+                       init_device_rotation_matrix (rotation, device_rot);
+                       multiply_matrix (matrix, device_rot, matrix);
+               }
+       }
+}
+
+static void
+input_info_remap (GsdInputInfo *input)
+{
+       float matrix[NUM_ELEMS_MATRIX] = { 0 };
+
+       if (input->capabilities & GSD_INPUT_IS_PAD)
+               return;
+
+       input_info_get_matrix (input, matrix);
+
+       g_debug ("About to remap device '%s'",
+                gdk_device_get_name (input->device));
+
+       if (!device_set_matrix (input->device, matrix)) {
+               g_warning ("Failed to map device '%s'",
+                          gdk_device_get_name (input->device));
+       }
+
+       g_signal_emit (input->mapper, signals[DEVICE_CHANGED], 0, input->device);
+}
+
+static void
+mapper_apply_helper_info (GsdDeviceMapper *mapper,
+                         MappingHelper   *helper)
+{
+       guint i, j;
+
+       /* Now, decide which input claims which output */
+       for (i = 0; i < helper->device_maps->len; i++) {
+               GsdOutputInfo *last = NULL, *output = NULL;
+               DeviceMapHelper *info;
+
+               info = &g_array_index (helper->device_maps, DeviceMapHelper, i);
+
+               for (j = 0; j < N_OUTPUT_PRIORITIES; j++) {
+                       if (!info->candidates[j])
+                               continue;
+
+                       output = g_hash_table_lookup (mapper->output_devices,
+                                                     info->candidates[j]);
+
+                       if (!output)
+                               continue;
+
+                       last = output;
+
+                       if ((info->input->capabilities &
+                            (GSD_INPUT_IS_SYSTEM_INTEGRATED | GSD_INPUT_IS_SCREEN_INTEGRATED))) {
+                               /* A single output is hardly going to have multiple integrated input
+                                * devices with the same capabilities, so punt to any next output.
+                                */
+                               if (output_has_input_type (output, info->input->capabilities))
+                                       continue;
+                       }
+
+                       input_info_set_output (info->input, output, TRUE, FALSE);
+                       break;
+               }
+
+               /* Assign the last candidate if we came up empty */
+               if (!info->input->guessed_output && last)
+                       input_info_set_output (info->input, last, TRUE, FALSE);
+
+               input_info_remap (info->input);
+       }
+}
+
+static void
+mapper_recalculate_candidates (GsdDeviceMapper *mapper)
+{
+       MappingHelper *helper;
+       GHashTableIter iter;
+       GsdInputInfo *input;
+
+       helper = mapping_helper_new ();
+       g_hash_table_iter_init (&iter, mapper->input_devices);
+
+       while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &input)) {
+               GnomeRROutput *outputs[N_OUTPUT_PRIORITIES] = { 0 };
+
+               /* Device has an output from settings */
+               if (input->output)
+                       continue;
+
+               input_info_guess_candidates (input, outputs);
+               mapping_helper_add (helper, input, outputs);
+       }
+
+       mapper_apply_helper_info (mapper, helper);
+       mapping_helper_free (helper);
+}
+
+static void
+mapper_recalculate_input (GsdDeviceMapper *mapper,
+                         GsdInputInfo    *input)
+{
+       GnomeRROutput *outputs[N_OUTPUT_PRIORITIES] = { 0 };
+       MappingHelper *helper;
+
+       /* Device has an output from settings */
+       if (input->output)
+               return;
+
+       helper = mapping_helper_new ();
+       input_info_guess_candidates (input, outputs);
+       mapping_helper_add (helper, input, outputs);
+
+       mapper_apply_helper_info (mapper, helper);
+       mapping_helper_free (helper);
+}
+
+static gboolean
+input_info_update_capabilities_from_tool_type (GsdInputInfo *info)
+{
+       const char *tool_type;
+       int deviceid;
+
+       deviceid = gdk_x11_device_get_id (info->device);
+       tool_type = xdevice_get_wacom_tool_type (deviceid);
+
+       if (!tool_type)
+               return FALSE;
+
+       if (g_str_equal (tool_type, "STYLUS"))
+               info->capabilities |= GSD_INPUT_IS_PEN;
+       else if (g_str_equal (tool_type, "ERASER"))
+               info->capabilities |= GSD_INPUT_IS_ERASER;
+       else if (g_str_equal (tool_type, "PAD"))
+               info->capabilities |= GSD_INPUT_IS_PAD;
+
+       return TRUE;
+}
+
+static void
+input_info_update_capabilities (GsdInputInfo *info)
+{
+#if HAVE_WACOM
+       WacomDevice *wacom_device;
+       gchar *devpath;
+
+       info->capabilities = 0;
+       devpath = xdevice_get_device_node (gdk_x11_device_get_id (info->device));
+       wacom_device = libwacom_new_from_path (info->mapper->wacom_db, devpath,
+                                              WFALLBACK_GENERIC, NULL);
+
+       if (wacom_device) {
+               WacomIntegrationFlags integration_flags;
+
+               integration_flags = libwacom_get_integration_flags (wacom_device);
+
+               if (integration_flags & WACOM_DEVICE_INTEGRATED_DISPLAY)
+                       info->capabilities |= GSD_INPUT_IS_SCREEN_INTEGRATED;
+
+               if (integration_flags & WACOM_DEVICE_INTEGRATED_SYSTEM)
+                       info->capabilities |= GSD_INPUT_IS_SYSTEM_INTEGRATED;
+
+               libwacom_destroy (wacom_device);
+       }
+
+       g_free (devpath);
+#else
+       info->capabilities = 0;
+#endif /* HAVE_WACOM */
+
+       if (!input_info_update_capabilities_from_tool_type (info)) {
+               GdkInputSource source;
+
+               /* Fallback to GdkInputSource */
+               source = gdk_device_get_source (info->device);
+
+               if (source == GDK_SOURCE_TOUCHSCREEN)
+                       info->capabilities |= GSD_INPUT_IS_TOUCH | GSD_INPUT_IS_SCREEN_INTEGRATED;
+               else if (source == GDK_SOURCE_PEN)
+                       info->capabilities |= GSD_INPUT_IS_PEN;
+               else if (source == GDK_SOURCE_ERASER)
+                       info->capabilities |= GSD_INPUT_IS_ERASER;
+       }
+}
+
+static void
+device_settings_changed_cb (GSettings   *settings,
+                           gchar        *key,
+                           GsdInputInfo *input)
+{
+       if (g_str_equal (key, KEY_DISPLAY)) {
+               GnomeRROutput *rr_output;
+               GsdOutputInfo *output;
+
+               rr_output = settings_get_display (settings, input->mapper);
+
+               if (rr_output) {
+                       output = g_hash_table_lookup (input->mapper->output_devices,
+                                                     rr_output);
+                       input_info_set_output (input, output, FALSE, FALSE);
+                       input_info_remap (input);
+               } else {
+                       /* Guess an output for this device */
+                       mapper_recalculate_input (input->mapper, input);
+               }
+       } else if (g_str_equal (key, KEY_ROTATION)) {
+               /* Remap the device so the new rotation is applied */
+               input_info_remap (input);
+       }
+}
+
+static GsdInputInfo *
+input_info_new (GdkDevice      *device,
+               GSettings       *settings,
+               GsdDeviceMapper *mapper)
+{
+       GnomeRROutput *rr_output = NULL;
+       GsdOutputInfo *output = NULL;
+       GsdInputInfo *info;
+
+       info = g_new0 (GsdInputInfo, 1);
+       info->device = device;
+       info->settings = (settings) ? g_object_ref (settings) : NULL;
+       info->mapper = mapper;
+
+       if (info->settings) {
+               info->changed_id = g_signal_connect (info->settings, "changed",
+                                                    G_CALLBACK (device_settings_changed_cb),
+                                                    info);
+
+               /* Assign output from config */
+               rr_output = settings_get_display (settings, mapper);
+       }
+
+       input_info_update_capabilities (info);
+
+       if (rr_output) {
+               output = g_hash_table_lookup (mapper->output_devices,
+                                             rr_output);
+               input_info_set_output (info, output, FALSE, FALSE);
+               input_info_remap (info);
+       } else {
+               mapper_recalculate_input (mapper, info);
+       }
+
+       return info;
+}
+
+static void
+input_info_free (GsdInputInfo *info)
+{
+       input_info_set_output (info, NULL, FALSE, FALSE);
+       input_info_set_output (info, NULL, TRUE, FALSE);
+
+       if (info->settings && info->changed_id)
+               g_signal_handler_disconnect (info->settings, info->changed_id);
+
+       if (info->settings)
+               g_object_unref (info->settings);
+
+       g_free (info);
+}
+
+static GsdOutputInfo *
+output_info_new (GnomeRROutput *output)
+{
+       GsdOutputInfo *info;
+
+       info = g_new0 (GsdOutputInfo, 1);
+       info->output = output;
+
+       return info;
+}
+
+static void
+output_info_free (GsdOutputInfo *info)
+{
+       while (info->input_devices) {
+               GsdInputInfo *input = info->input_devices->data;
+
+               if (input->output == info)
+                       input_info_set_output (input, NULL, FALSE, FALSE);
+               if (input->guessed_output == info)
+                       input_info_set_output (input, NULL, TRUE, FALSE);
+       }
+
+       g_free (info);
+}
+
+static void
+gsd_device_mapper_finalize (GObject *object)
+{
+       GsdDeviceMapper *mapper = GSD_DEVICE_MAPPER (object);
+
+       g_hash_table_unref (mapper->input_devices);
+
+       if (mapper->output_devices)
+               g_hash_table_unref (mapper->output_devices);
+
+#if HAVE_WACOM
+       libwacom_database_destroy (mapper->wacom_db);
+#endif
+
+       G_OBJECT_CLASS (gsd_device_mapper_parent_class)->finalize (object);
+}
+
+static void
+_device_mapper_update_outputs (GsdDeviceMapper *mapper)
+{
+       GnomeRROutput **outputs;
+       GHashTable *map;
+       gint i = 0;
+
+       map = g_hash_table_new_full (NULL, NULL, NULL,
+                                    (GDestroyNotify) output_info_free);
+       outputs = gnome_rr_screen_list_outputs (mapper->rr_screen);
+
+       while (outputs[i]) {
+               GsdOutputInfo *info = NULL;
+
+               if (mapper->output_devices) {
+                       info = g_hash_table_lookup (mapper->output_devices,
+                                                   outputs[i]);
+
+                       if (info)
+                               g_hash_table_steal (mapper->output_devices,
+                                                   outputs[i]);
+               }
+
+               if (!info)
+                       info = output_info_new (outputs[i]);
+
+               g_hash_table_insert (map, outputs[i], info);
+               i++;
+       }
+
+       if (mapper->output_devices)
+               g_hash_table_unref (mapper->output_devices);
+
+       mapper->output_devices = map;
+       mapper_recalculate_candidates (mapper);
+}
+
+static void
+outputs_changed_cb (GnomeRRScreen   *rr_screen,
+                   GnomeRROutput   *output,
+                   GsdDeviceMapper *mapper)
+{
+       _device_mapper_update_outputs (mapper);
+}
+
+static void
+screen_changed_cb (GnomeRRScreen   *rr_screen,
+                  GsdDeviceMapper *mapper)
+{
+       _device_mapper_update_outputs (mapper);
+}
+
+static gboolean
+gsd_device_mapper_initable_init (GInitable     *initable,
+                                GCancellable  *cancellable,
+                                GError       **error)
+{
+       GsdDeviceMapper *mapper;
+
+       mapper = GSD_DEVICE_MAPPER (initable);
+       mapper->rr_screen = gnome_rr_screen_new (mapper->screen, error);
+
+       if (!mapper->rr_screen)
+               return FALSE;
+
+       g_signal_connect (mapper->rr_screen, "changed",
+                         G_CALLBACK (screen_changed_cb), initable);
+       g_signal_connect (mapper->rr_screen, "output-connected",
+                         G_CALLBACK (outputs_changed_cb), initable);
+       g_signal_connect (mapper->rr_screen, "output-disconnected",
+                         G_CALLBACK (outputs_changed_cb), initable);
+       _device_mapper_update_outputs (GSD_DEVICE_MAPPER (initable));
+       return TRUE;
+}
+
+static void
+gsd_device_mapper_initable_iface_init (GInitableIface *iface)
+{
+       iface->init = gsd_device_mapper_initable_init;
+}
+
+static void
+gsd_device_mapper_set_property (GObject             *object,
+                               guint         param_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+       GsdDeviceMapper *mapper = GSD_DEVICE_MAPPER (object);
+
+       switch (param_id) {
+       case PROP_SCREEN:
+               mapper->screen = g_value_get_object (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+       }
+}
+
+static void
+gsd_device_mapper_get_property (GObject           *object,
+                               guint       param_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+       GsdDeviceMapper *mapper = GSD_DEVICE_MAPPER (object);
+
+       switch (param_id) {
+       case PROP_SCREEN:
+               g_value_set_object (value, mapper->screen);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+       }
+}
+
+static void
+gsd_device_mapper_class_init (GsdDeviceMapperClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->set_property = gsd_device_mapper_set_property;
+       object_class->get_property = gsd_device_mapper_get_property;
+       object_class->finalize = gsd_device_mapper_finalize;
+
+       g_object_class_install_property (object_class,
+                                        PROP_SCREEN,
+                                        g_param_spec_object ("screen",
+                                                             "Screen",
+                                                             "Screen",
+                                                             GDK_TYPE_SCREEN,
+                                                             G_PARAM_CONSTRUCT_ONLY |
+                                                             G_PARAM_READWRITE));
+       signals[DEVICE_CHANGED] =
+               g_signal_new ("device-changed",
+                             GSD_TYPE_DEVICE_MAPPER,
+                             G_SIGNAL_RUN_LAST, 0,
+                             NULL, NULL, NULL,
+                             G_TYPE_NONE, 1, GDK_TYPE_DEVICE);
+}
+
+static void
+gsd_device_mapper_init (GsdDeviceMapper *mapper)
+{
+       mapper->input_devices = g_hash_table_new_full (NULL, NULL, NULL,
+                                                      (GDestroyNotify) input_info_free);
+#if HAVE_WACOM
+       mapper->wacom_db = libwacom_database_new ();
+#endif
+}
+
+GsdDeviceMapper *
+gsd_device_mapper_get (void)
+{
+       GsdDeviceMapper *mapper;
+       GdkScreen *screen;
+
+       screen = gdk_screen_get_default ();
+       g_return_val_if_fail (screen != NULL, NULL);
+
+       mapper = g_object_get_data (G_OBJECT (screen), "gsd-device-mapper-data");
+
+       if (!mapper) {
+               GError *error = NULL;
+
+               mapper = g_initable_new (GSD_TYPE_DEVICE_MAPPER, NULL, &error,
+                                        "screen", screen, NULL);
+               if (error) {
+                       g_critical ("Could not create device mapper: %s", error->message);
+                       g_error_free (error);
+               } else {
+                       g_object_set_data_full (G_OBJECT (screen), "gsd-device-mapper-data",
+                                               mapper, (GDestroyNotify) g_object_unref);
+               }
+       }
+
+       return mapper;
+}
+
+void
+gsd_device_mapper_add_input (GsdDeviceMapper *mapper,
+                            GdkDevice       *device,
+                            GSettings       *settings)
+{
+       GsdInputInfo *info;
+
+       g_return_if_fail (mapper != NULL);
+       g_return_if_fail (GDK_IS_DEVICE (device));
+       g_return_if_fail (!settings || G_IS_SETTINGS (settings));
+
+       if (g_hash_table_contains (mapper->input_devices, device))
+               return;
+
+       info = input_info_new (device, settings, mapper);
+       g_hash_table_insert (mapper->input_devices, device, info);
+}
+
+void
+gsd_device_mapper_remove_input (GsdDeviceMapper *mapper,
+                               GdkDevice       *device)
+{
+       g_return_if_fail (mapper != NULL);
+       g_return_if_fail (GDK_IS_DEVICE (device));
+
+       g_hash_table_remove (mapper->input_devices, device);
+}
+
+GnomeRROutput *
+gsd_device_mapper_get_device_output (GsdDeviceMapper *mapper,
+                                    GdkDevice       *device)
+{
+       GsdOutputInfo *output;
+       GsdInputInfo *input;
+
+       g_return_val_if_fail (mapper != NULL, NULL);
+       g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+
+       input = g_hash_table_lookup (mapper->input_devices, device);
+       output = input_info_get_output (input);
+
+       if (!output)
+               return NULL;
+
+       return output->output;
+}
+
+gint
+gsd_device_mapper_get_device_monitor (GsdDeviceMapper *mapper,
+                                     GdkDevice       *device)
+{
+       GsdOutputInfo *output;
+       GsdInputInfo *input;
+
+       g_return_val_if_fail (GSD_IS_DEVICE_MAPPER (mapper), -1);
+       g_return_val_if_fail (GDK_IS_DEVICE (device), -1);
+
+       input = g_hash_table_lookup (mapper->input_devices, device);
+
+       if (!input)
+               return -1;
+
+       output = input_info_get_output (input);
+
+       if (!output)
+               return -1;
+
+       return monitor_for_output (output->output);
+}
+
+void
+gsd_device_mapper_set_device_output (GsdDeviceMapper *mapper,
+                                    GdkDevice       *device,
+                                    GnomeRROutput   *output)
+{
+       GsdInputInfo *input_info;
+       GsdOutputInfo *output_info;
+
+       g_return_if_fail (mapper != NULL);
+       g_return_if_fail (GDK_IS_DEVICE (device));
+
+       input_info = g_hash_table_lookup (mapper->input_devices, device);
+       output_info = g_hash_table_lookup (mapper->output_devices, output);
+
+       if (!input_info || !output_info)
+               return;
+
+       input_info_set_output (input_info, output_info, FALSE, TRUE);
+       input_info_remap (input_info);
+}
+
+void
+gsd_device_mapper_set_device_monitor (GsdDeviceMapper *mapper,
+                                     GdkDevice       *device,
+                                     gint             monitor_num)
+{
+       GnomeRROutput *output;
+
+       g_return_if_fail (GSD_IS_DEVICE_MAPPER (mapper));
+       g_return_if_fail (GDK_IS_DEVICE (device));
+
+       output = monitor_to_output (mapper, monitor_num);
+       gsd_device_mapper_set_device_output (mapper, device, output);
+}
diff --git a/plugins/common/gsd-device-mapper.h b/plugins/common/gsd-device-mapper.h
new file mode 100644
index 0000000..cce4b60
--- /dev/null
+++ b/plugins/common/gsd-device-mapper.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2014 Carlos Garnacho <carlosg gnome org>
+ *
+ * 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.
+ */
+
+#ifndef __GSD_DEVICE_MAPPER_H__
+#define __GSD_DEVICE_MAPPER_H__
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-rr.h>
+#undef GNOME_DESKTOP_USE_UNSTABLE_API
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_DEVICE_MAPPER        (gsd_device_mapper_get_type ())
+#define GSD_DEVICE_MAPPER(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_DEVICE_MAPPER, 
GsdDeviceMapper))
+#define GSD_DEVICE_MAPPER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_DEVICE_MAPPER, 
GsdDeviceMapperClass))
+#define GSD_IS_DEVICE_MAPPER(o)               (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_DEVICE_MAPPER))
+#define GSD_IS_DEVICE_MAPPER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_DEVICE_MAPPER))
+#define GSD_DEVICE_MAPPER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_DEVICE_MAPPER, 
GsdDeviceMapperClass))
+
+typedef struct _GsdDeviceMapper GsdDeviceMapper;
+typedef struct _GsdDeviceMapperClass GsdDeviceMapperClass;
+
+GType            gsd_device_mapper_get_type          (void) G_GNUC_CONST;
+GsdDeviceMapper * gsd_device_mapper_get                      (void);
+
+void             gsd_device_mapper_add_input         (GsdDeviceMapper *mapper,
+                                                      GdkDevice       *device,
+                                                      GSettings       *settings);
+void             gsd_device_mapper_remove_input      (GsdDeviceMapper *mapper,
+                                                      GdkDevice       *device);
+void             gsd_device_mapper_add_output        (GsdDeviceMapper *mapper,
+                                                      GnomeRROutput   *output);
+void             gsd_device_mapper_remove_output     (GsdDeviceMapper *mapper,
+                                                      GnomeRROutput   *output);
+
+GnomeRROutput  * gsd_device_mapper_get_device_output (GsdDeviceMapper *mapper,
+                                                      GdkDevice       *device);
+
+void             gsd_device_mapper_set_device_output (GsdDeviceMapper *mapper,
+                                                      GdkDevice       *device,
+                                                      GnomeRROutput   *output);
+
+gint             gsd_device_mapper_get_device_monitor (GsdDeviceMapper *mapper,
+                                                       GdkDevice       *device);
+void             gsd_device_mapper_set_device_monitor (GsdDeviceMapper *mapper,
+                                                       GdkDevice       *device,
+                                                       gint             monitor_num);
+
+G_END_DECLS
+
+#endif /* __GSD_DEVICE_MAPPER_H__ */
diff --git a/plugins/common/gsd-input-helper.c b/plugins/common/gsd-input-helper.c
index 6293c62..d359e87 100644
--- a/plugins/common/gsd-input-helper.c
+++ b/plugins/common/gsd-input-helper.c
@@ -575,3 +575,38 @@ get_disabled_devices (GdkDeviceManager *manager)
 
         return ret;
 }
+
+const char *
+xdevice_get_wacom_tool_type (int deviceid)
+{
+        unsigned long nitems, bytes_after;
+        unsigned char *data = NULL;
+        Atom prop, realtype, tool;
+        GdkDisplay *display;
+        int realformat, rc;
+        const gchar *ret = NULL;
+
+        gdk_error_trap_push ();
+
+        display = gdk_display_get_default ();
+        prop = gdk_x11_get_xatom_by_name ("Wacom Tool Type");
+
+        rc = XIGetProperty (GDK_DISPLAY_XDISPLAY (display),
+                            deviceid, prop, 0, 1, False,
+                            XA_ATOM, &realtype, &realformat, &nitems,
+                            &bytes_after, &data);
+
+        gdk_error_trap_pop_ignored ();
+
+        if (rc != Success || nitems == 0)
+                return NULL;
+
+        if (realtype == XA_ATOM) {
+                tool = *((Atom*) data);
+                ret = gdk_x11_get_xatom_name (tool);
+        }
+
+        XFree (data);
+
+        return ret;
+}
diff --git a/plugins/common/gsd-input-helper.h b/plugins/common/gsd-input-helper.h
index 2b19155..66b2062 100644
--- a/plugins/common/gsd-input-helper.h
+++ b/plugins/common/gsd-input-helper.h
@@ -81,6 +81,8 @@ GList *   get_disabled_devices     (GdkDeviceManager       *manager);
 char *    xdevice_get_device_node  (int                     deviceid);
 int       xdevice_get_last_tool_id (int                     deviceid);
 
+const char * xdevice_get_wacom_tool_type (int               deviceid);
+
 G_END_DECLS
 
 #endif /* __GSD_INPUT_HELPER_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]