diff --git a/SOURCES/input-device-mapping.patch b/SOURCES/input-device-mapping.patch new file mode 100644 index 0000000..aec0703 --- /dev/null +++ b/SOURCES/input-device-mapping.patch @@ -0,0 +1,2783 @@ +diff --git a/configure.ac b/configure.ac +index 0e5d5f9..bfbecd8 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -253,10 +253,12 @@ 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]) + else + AC_MSG_ERROR([GUdev is necessary to compile Wacom support]) + fi ++ AC_DEFINE_UNQUOTED(HAVE_WACOM, 1, [Define to 1 if wacom support is available]) + have_wacom=yes + fi + ;; +diff --git a/plugins/common/Makefile.am b/plugins/common/Makefile.am +index b0e907c..3b84b21 100644 +--- a/plugins/common/Makefile.am ++++ b/plugins/common/Makefile.am +@@ -3,25 +3,35 @@ plugin_name = common + noinst_LTLIBRARIES = libcommon.la + + libcommon_la_SOURCES = \ ++ edid.h \ ++ edid-parse.c \ ++ gsd-device-mapper.c \ ++ gsd-device-mapper.h \ + gsd-keygrab.c \ + gsd-keygrab.h \ + gsd-input-helper.c \ + gsd-input-helper.h + + libcommon_la_CPPFLAGS = \ ++ -I$(top_srcdir)/data/ \ + $(AM_CPPFLAGS) + + libcommon_la_CFLAGS = \ + $(PLUGIN_CFLAGS) \ ++ $(GNOME_DESKTOP_CFLAGS) \ + $(SETTINGS_PLUGIN_CFLAGS) \ + $(COMMON_CFLAGS) \ ++ $(LIBWACOM_CFLAGS) \ + $(AM_CFLAGS) + + libcommon_la_LDFLAGS = \ + $(GSD_PLUGIN_LDFLAGS) + + libcommon_la_LIBADD = \ ++ -lm \ + $(SETTINGS_PLUGIN_LIBS) \ ++ $(GNOME_DESKTOP_LIBS) \ ++ $(LIBWACOM_LIBS) \ + $(COMMON_LIBS) + + libexec_PROGRAMS = gsd-test-input-helper +diff --git a/plugins/common/edid-parse.c b/plugins/common/edid-parse.c +new file mode 100644 +index 0000000..5b3283a +--- /dev/null ++++ b/plugins/common/edid-parse.c +@@ -0,0 +1,539 @@ ++/* ++ * Copyright 2007 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * on the rights to use, copy, modify, merge, publish, distribute, sub ++ * license, and/or sell copies of the Software, and to permit persons to whom ++ * the Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++/* Author: Soren Sandmann */ ++ ++#include "edid.h" ++#include ++#include ++#include ++#include ++ ++static int ++get_bit (int in, int bit) ++{ ++ return (in & (1 << bit)) >> bit; ++} ++ ++static int ++get_bits (int in, int begin, int end) ++{ ++ int mask = (1 << (end - begin + 1)) - 1; ++ ++ return (in >> begin) & mask; ++} ++ ++static int ++decode_header (const uchar *edid) ++{ ++ if (memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0) ++ return TRUE; ++ return FALSE; ++} ++ ++static int ++decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info) ++{ ++ int is_model_year; ++ ++ /* Manufacturer Code */ ++ info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6); ++ info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3; ++ info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7); ++ info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4); ++ info->manufacturer_code[3] = '\0'; ++ ++ info->manufacturer_code[0] += 'A' - 1; ++ info->manufacturer_code[1] += 'A' - 1; ++ info->manufacturer_code[2] += 'A' - 1; ++ ++ /* Product Code */ ++ info->product_code = edid[0x0b] << 8 | edid[0x0a]; ++ ++ /* Serial Number */ ++ info->serial_number = ++ edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24; ++ ++ /* Week and Year */ ++ is_model_year = FALSE; ++ switch (edid[0x10]) ++ { ++ case 0x00: ++ info->production_week = -1; ++ break; ++ ++ case 0xff: ++ info->production_week = -1; ++ is_model_year = TRUE; ++ break; ++ ++ default: ++ info->production_week = edid[0x10]; ++ break; ++ } ++ ++ if (is_model_year) ++ { ++ info->production_year = -1; ++ info->model_year = 1990 + edid[0x11]; ++ } ++ else ++ { ++ info->production_year = 1990 + edid[0x11]; ++ info->model_year = -1; ++ } ++ ++ return TRUE; ++} ++ ++static int ++decode_edid_version (const uchar *edid, MonitorInfo *info) ++{ ++ info->major_version = edid[0x12]; ++ info->minor_version = edid[0x13]; ++ ++ return TRUE; ++} ++ ++static int ++decode_display_parameters (const uchar *edid, MonitorInfo *info) ++{ ++ /* Digital vs Analog */ ++ info->is_digital = get_bit (edid[0x14], 7); ++ ++ if (info->is_digital) ++ { ++ int bits; ++ ++ static const int bit_depth[8] = ++ { ++ -1, 6, 8, 10, 12, 14, 16, -1 ++ }; ++ ++ static const Interface interfaces[6] = ++ { ++ UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT ++ }; ++ ++ bits = get_bits (edid[0x14], 4, 6); ++ info->connector.digital.bits_per_primary = bit_depth[bits]; ++ ++ bits = get_bits (edid[0x14], 0, 3); ++ ++ if (bits <= 5) ++ info->connector.digital.interface = interfaces[bits]; ++ else ++ info->connector.digital.interface = UNDEFINED; ++ } ++ else ++ { ++ int bits = get_bits (edid[0x14], 5, 6); ++ ++ static const double levels[][3] = ++ { ++ { 0.7, 0.3, 1.0 }, ++ { 0.714, 0.286, 1.0 }, ++ { 1.0, 0.4, 1.4 }, ++ { 0.7, 0.0, 0.7 }, ++ }; ++ ++ info->connector.analog.video_signal_level = levels[bits][0]; ++ info->connector.analog.sync_signal_level = levels[bits][1]; ++ info->connector.analog.total_signal_level = levels[bits][2]; ++ ++ info->connector.analog.blank_to_black = get_bit (edid[0x14], 4); ++ ++ info->connector.analog.separate_hv_sync = get_bit (edid[0x14], 3); ++ info->connector.analog.composite_sync_on_h = get_bit (edid[0x14], 2); ++ info->connector.analog.composite_sync_on_green = get_bit (edid[0x14], 1); ++ ++ info->connector.analog.serration_on_vsync = get_bit (edid[0x14], 0); ++ } ++ ++ /* Screen Size / Aspect Ratio */ ++ if (edid[0x15] == 0 && edid[0x16] == 0) ++ { ++ info->width_mm = -1; ++ info->height_mm = -1; ++ info->aspect_ratio = -1.0; ++ } ++ else if (edid[0x16] == 0) ++ { ++ info->width_mm = -1; ++ info->height_mm = -1; ++ info->aspect_ratio = 100.0 / (edid[0x15] + 99); ++ } ++ else if (edid[0x15] == 0) ++ { ++ info->width_mm = -1; ++ info->height_mm = -1; ++ info->aspect_ratio = 100.0 / (edid[0x16] + 99); ++ info->aspect_ratio = 1/info->aspect_ratio; /* portrait */ ++ } ++ else ++ { ++ info->width_mm = 10 * edid[0x15]; ++ info->height_mm = 10 * edid[0x16]; ++ } ++ ++ /* Gamma */ ++ if (edid[0x17] == 0xFF) ++ info->gamma = -1.0; ++ else ++ info->gamma = (edid[0x17] + 100.0) / 100.0; ++ ++ /* Features */ ++ info->standby = get_bit (edid[0x18], 7); ++ info->suspend = get_bit (edid[0x18], 6); ++ info->active_off = get_bit (edid[0x18], 5); ++ ++ if (info->is_digital) ++ { ++ info->connector.digital.rgb444 = TRUE; ++ if (get_bit (edid[0x18], 3)) ++ info->connector.digital.ycrcb444 = 1; ++ if (get_bit (edid[0x18], 4)) ++ info->connector.digital.ycrcb422 = 1; ++ } ++ else ++ { ++ int bits = get_bits (edid[0x18], 3, 4); ++ ColorType color_type[4] = ++ { ++ MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR ++ }; ++ ++ info->connector.analog.color_type = color_type[bits]; ++ } ++ ++ info->srgb_is_standard = get_bit (edid[0x18], 2); ++ ++ /* In 1.3 this is called "has preferred timing" */ ++ info->preferred_timing_includes_native = get_bit (edid[0x18], 1); ++ ++ /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */ ++ info->continuous_frequency = get_bit (edid[0x18], 0); ++ return TRUE; ++} ++ ++static double ++decode_fraction (int high, int low) ++{ ++ double result = 0.0; ++ int i; ++ ++ high = (high << 2) | low; ++ ++ for (i = 0; i < 10; ++i) ++ result += get_bit (high, i) * pow (2, i - 10); ++ ++ return result; ++} ++ ++static int ++decode_color_characteristics (const uchar *edid, MonitorInfo *info) ++{ ++ info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7)); ++ info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4)); ++ info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3)); ++ info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1)); ++ info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7)); ++ info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5)); ++ info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3)); ++ info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1)); ++ ++ return TRUE; ++} ++ ++static int ++decode_established_timings (const uchar *edid, MonitorInfo *info) ++{ ++ static const Timing established[][8] = ++ { ++ { ++ { 800, 600, 60 }, ++ { 800, 600, 56 }, ++ { 640, 480, 75 }, ++ { 640, 480, 72 }, ++ { 640, 480, 67 }, ++ { 640, 480, 60 }, ++ { 720, 400, 88 }, ++ { 720, 400, 70 } ++ }, ++ { ++ { 1280, 1024, 75 }, ++ { 1024, 768, 75 }, ++ { 1024, 768, 70 }, ++ { 1024, 768, 60 }, ++ { 1024, 768, 87 }, ++ { 832, 624, 75 }, ++ { 800, 600, 75 }, ++ { 800, 600, 72 } ++ }, ++ { ++ { 0, 0, 0 }, ++ { 0, 0, 0 }, ++ { 0, 0, 0 }, ++ { 0, 0, 0 }, ++ { 0, 0, 0 }, ++ { 0, 0, 0 }, ++ { 0, 0, 0 }, ++ { 1152, 870, 75 } ++ }, ++ }; ++ ++ int i, j, idx; ++ ++ idx = 0; ++ for (i = 0; i < 3; ++i) ++ { ++ for (j = 0; j < 8; ++j) ++ { ++ int byte = edid[0x23 + i]; ++ ++ if (get_bit (byte, j) && established[i][j].frequency != 0) ++ info->established[idx++] = established[i][j]; ++ } ++ } ++ return TRUE; ++} ++ ++static int ++decode_standard_timings (const uchar *edid, MonitorInfo *info) ++{ ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ { ++ int first = edid[0x26 + 2 * i]; ++ int second = edid[0x27 + 2 * i]; ++ ++ if (first != 0x01 && second != 0x01) ++ { ++ int w = 8 * (first + 31); ++ int h = 0; ++ ++ switch (get_bits (second, 6, 7)) ++ { ++ case 0x00: h = (w / 16) * 10; break; ++ case 0x01: h = (w / 4) * 3; break; ++ case 0x02: h = (w / 5) * 4; break; ++ case 0x03: h = (w / 16) * 9; break; ++ } ++ ++ info->standard[i].width = w; ++ info->standard[i].height = h; ++ info->standard[i].frequency = get_bits (second, 0, 5) + 60; ++ } ++ } ++ ++ return TRUE; ++} ++ ++static void ++decode_lf_string (const uchar *s, int n_chars, char *result) ++{ ++ int i; ++ for (i = 0; i < n_chars; ++i) ++ { ++ if (s[i] == 0x0a) ++ { ++ *result++ = '\0'; ++ break; ++ } ++ else if (s[i] == 0x00) ++ { ++ /* Convert embedded 0's to spaces */ ++ *result++ = ' '; ++ } ++ else ++ { ++ *result++ = s[i]; ++ } ++ } ++} ++ ++static void ++decode_display_descriptor (const uchar *desc, ++ MonitorInfo *info) ++{ ++ switch (desc[0x03]) ++ { ++ case 0xFC: ++ decode_lf_string (desc + 5, 13, info->dsc_product_name); ++ break; ++ case 0xFF: ++ decode_lf_string (desc + 5, 13, info->dsc_serial_number); ++ break; ++ case 0xFE: ++ decode_lf_string (desc + 5, 13, info->dsc_string); ++ break; ++ case 0xFD: ++ /* Range Limits */ ++ break; ++ case 0xFB: ++ /* Color Point */ ++ break; ++ case 0xFA: ++ /* Timing Identifications */ ++ break; ++ case 0xF9: ++ /* Color Management */ ++ break; ++ case 0xF8: ++ /* Timing Codes */ ++ break; ++ case 0xF7: ++ /* Established Timings */ ++ break; ++ case 0x10: ++ break; ++ } ++} ++ ++static void ++decode_detailed_timing (const uchar *timing, ++ DetailedTiming *detailed) ++{ ++ int bits; ++ StereoType stereo[] = ++ { ++ NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT, ++ TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN, ++ FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE ++ }; ++ ++ detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000; ++ detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4); ++ detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8); ++ detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4); ++ detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8); ++ detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8; ++ detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8; ++ detailed->v_front_porch = ++ get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4; ++ detailed->v_sync = ++ get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4; ++ detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8; ++ detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8; ++ detailed->right_border = timing[0x0f]; ++ detailed->top_border = timing[0x10]; ++ ++ detailed->interlaced = get_bit (timing[0x11], 7); ++ ++ /* Stereo */ ++ bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0); ++ detailed->stereo = stereo[bits]; ++ ++ /* Sync */ ++ bits = timing[0x11]; ++ ++ detailed->digital_sync = get_bit (bits, 4); ++ if (detailed->digital_sync) ++ { ++ detailed->connector.digital.composite = !get_bit (bits, 3); ++ ++ if (detailed->connector.digital.composite) ++ { ++ detailed->connector.digital.serrations = get_bit (bits, 2); ++ detailed->connector.digital.negative_vsync = FALSE; ++ } ++ else ++ { ++ detailed->connector.digital.serrations = FALSE; ++ detailed->connector.digital.negative_vsync = !get_bit (bits, 2); ++ } ++ ++ detailed->connector.digital.negative_hsync = !get_bit (bits, 0); ++ } ++ else ++ { ++ detailed->connector.analog.bipolar = get_bit (bits, 3); ++ detailed->connector.analog.serrations = get_bit (bits, 2); ++ detailed->connector.analog.sync_on_green = !get_bit (bits, 1); ++ } ++} ++ ++static int ++decode_descriptors (const uchar *edid, MonitorInfo *info) ++{ ++ int i; ++ int timing_idx; ++ ++ timing_idx = 0; ++ ++ for (i = 0; i < 4; ++i) ++ { ++ int index = 0x36 + i * 18; ++ ++ if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00) ++ { ++ decode_display_descriptor (edid + index, info); ++ } ++ else ++ { ++ decode_detailed_timing (edid + index, &(info->detailed_timings[timing_idx++])); ++ } ++ } ++ ++ info->n_detailed_timings = timing_idx; ++ ++ return TRUE; ++} ++ ++static void ++decode_check_sum (const uchar *edid, ++ MonitorInfo *info) ++{ ++ int i; ++ uchar check = 0; ++ ++ for (i = 0; i < 128; ++i) ++ check += edid[i]; ++ ++ info->checksum = check; ++} ++ ++MonitorInfo * ++decode_edid (const uchar *edid) ++{ ++ MonitorInfo *info = g_new0 (MonitorInfo, 1); ++ ++ decode_check_sum (edid, info); ++ ++ if (decode_header (edid) ++ && decode_vendor_and_product_identification (edid, info) ++ && decode_edid_version (edid, info) ++ && decode_display_parameters (edid, info) ++ && decode_color_characteristics (edid, info) ++ && decode_established_timings (edid, info) ++ && decode_standard_timings (edid, info) ++ && decode_descriptors (edid, info)) ++ { ++ return info; ++ } ++ else ++ { ++ g_free (info); ++ return NULL; ++ } ++} +diff --git a/plugins/common/edid.h b/plugins/common/edid.h +new file mode 100644 +index 0000000..703c639 +--- /dev/null ++++ b/plugins/common/edid.h +@@ -0,0 +1,195 @@ ++/* edid.h ++ * ++ * Copyright 2007, 2008, Red Hat, Inc. ++ * ++ * This file is part of the Gnome Library. ++ * ++ * The Gnome Library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * The Gnome Library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public ++ * License along with the Gnome Library; see the file COPYING.LIB. If not, ++ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ * Author: Soren Sandmann ++ */ ++ ++#ifndef EDID_H ++#define EDID_H ++ ++typedef unsigned char uchar; ++typedef struct MonitorInfo MonitorInfo; ++typedef struct Timing Timing; ++typedef struct DetailedTiming DetailedTiming; ++ ++typedef enum ++{ ++ UNDEFINED, ++ DVI, ++ HDMI_A, ++ HDMI_B, ++ MDDI, ++ DISPLAY_PORT ++} Interface; ++ ++typedef enum ++{ ++ UNDEFINED_COLOR, ++ MONOCHROME, ++ RGB, ++ OTHER_COLOR ++} ColorType; ++ ++typedef enum ++{ ++ NO_STEREO, ++ FIELD_RIGHT, ++ FIELD_LEFT, ++ TWO_WAY_RIGHT_ON_EVEN, ++ TWO_WAY_LEFT_ON_EVEN, ++ FOUR_WAY_INTERLEAVED, ++ SIDE_BY_SIDE ++} StereoType; ++ ++struct Timing ++{ ++ int width; ++ int height; ++ int frequency; ++}; ++ ++struct DetailedTiming ++{ ++ int pixel_clock; ++ int h_addr; ++ int h_blank; ++ int h_sync; ++ int h_front_porch; ++ int v_addr; ++ int v_blank; ++ int v_sync; ++ int v_front_porch; ++ int width_mm; ++ int height_mm; ++ int right_border; ++ int top_border; ++ int interlaced; ++ StereoType stereo; ++ ++ int digital_sync; ++ union ++ { ++ struct ++ { ++ int bipolar; ++ int serrations; ++ int sync_on_green; ++ } analog; ++ ++ struct ++ { ++ int composite; ++ int serrations; ++ int negative_vsync; ++ int negative_hsync; ++ } digital; ++ } connector; ++}; ++ ++struct MonitorInfo ++{ ++ int checksum; ++ char manufacturer_code[4]; ++ int product_code; ++ unsigned int serial_number; ++ ++ int production_week; /* -1 if not specified */ ++ int production_year; /* -1 if not specified */ ++ int model_year; /* -1 if not specified */ ++ ++ int major_version; ++ int minor_version; ++ ++ int is_digital; ++ ++ union ++ { ++ struct ++ { ++ int bits_per_primary; ++ Interface interface; ++ int rgb444; ++ int ycrcb444; ++ int ycrcb422; ++ } digital; ++ ++ struct ++ { ++ double video_signal_level; ++ double sync_signal_level; ++ double total_signal_level; ++ ++ int blank_to_black; ++ ++ int separate_hv_sync; ++ int composite_sync_on_h; ++ int composite_sync_on_green; ++ int serration_on_vsync; ++ ColorType color_type; ++ } analog; ++ } connector; ++ ++ int width_mm; /* -1 if not specified */ ++ int height_mm; /* -1 if not specified */ ++ double aspect_ratio; /* -1.0 if not specififed */ ++ ++ double gamma; /* -1.0 if not specified */ ++ ++ int standby; ++ int suspend; ++ int active_off; ++ ++ int srgb_is_standard; ++ int preferred_timing_includes_native; ++ int continuous_frequency; ++ ++ double red_x; ++ double red_y; ++ double green_x; ++ double green_y; ++ double blue_x; ++ double blue_y; ++ double white_x; ++ double white_y; ++ ++ Timing established[24]; /* Terminated by 0x0x0 */ ++ Timing standard[8]; ++ ++ int n_detailed_timings; ++ DetailedTiming detailed_timings[4]; /* If monitor has a preferred ++ * mode, it is the first one ++ * (whether it has, is ++ * determined by the ++ * preferred_timing_includes ++ * bit. ++ */ ++ ++ /* Optional product description */ ++ char dsc_serial_number[14]; ++ char dsc_product_name[14]; ++ char dsc_string[14]; /* Unspecified ASCII data */ ++}; ++ ++MonitorInfo *decode_edid (const uchar *data); ++char *make_display_name (const MonitorInfo *info); ++char *make_display_size_string (int width_mm, int height_mm); ++ ++#endif +diff --git a/plugins/common/gsd-device-mapper.c b/plugins/common/gsd-device-mapper.c +new file mode 100644 +index 0000000..d8ea62a +--- /dev/null ++++ b/plugins/common/gsd-device-mapper.c +@@ -0,0 +1,1293 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2014 Carlos Garnacho ++ * ++ * 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 ++#include ++#include ++ ++#if HAVE_WACOM ++#include ++#endif ++ ++#include "gsd-device-mapper.h" ++#include "gsd-input-helper.h" ++#include "gsd-enums.h" ++#include "edid.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); ++static GsdOutputInfo * output_info_new (GnomeRROutput *output); ++ ++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); ++} ++ ++static void ++get_edid (GnomeRROutput *output, ++ gchar **vendor, ++ gchar **product, ++ gchar **serial) ++{ ++ const guchar *edid_data; ++ MonitorInfo *info; ++ ++ if (vendor) ++ *vendor = NULL; ++ if (product) ++ *product = NULL; ++ if (serial) ++ *serial = NULL; ++ ++ edid_data = gnome_rr_output_get_edid_data (output, NULL); ++ ++ if (!edid_data) ++ return; ++ ++ info = decode_edid (edid_data); ++ ++ if (vendor) ++ *vendor = g_strndup (info->manufacturer_code, 4); ++ if (product) { ++ if (info->dsc_product_name[0]) ++ *product = g_strndup (info->dsc_product_name, 14); ++ else ++ *product = g_strdup_printf ("0x%04x", (unsigned) info->product_code); ++ } ++ ++ if (serial) { ++ if (info->dsc_serial_number[0]) ++ *serial = g_strndup (info->dsc_serial_number, 14); ++ else ++ *serial = g_strdup_printf ("0x%04x", (unsigned) info->serial_number); ++ } ++ ++ g_free (info); ++} ++ ++/* 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; ++ ++ get_edid (outputs[i], &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_debug ("Checking match between ['%s','%s','%s'] and ['%s','%s','%s']", ++ edid[0], edid[1], edid[2], vendor, product, serial); ++ ++ g_free (vendor); ++ g_free (product); ++ g_free (serial); ++ ++ if (match) { ++ g_debug ("Found a 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_laptop (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) ++ get_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; ++ GnomeRRCrtc *crtc; ++ ++ output = input_info_get_output (input); ++ ++ if (output) ++ crtc = gnome_rr_output_get_crtc (output->output); ++ ++ if (!output || !crtc) { ++ 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; ++ int monitor; ++ ++ g_debug ("Mapping '%s' to output '%s'", ++ gdk_device_get_name (input->device), ++ gnome_rr_output_get_name (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); ++ g_debug ("Mapping input device '%s', candidates: %d, Best candidate: %s", ++ gdk_device_get_name (info->input->device), info->n_candidates, ++ (info->highest_prio < N_OUTPUT_PRIORITIES) ? ++ gnome_rr_output_get_name (info->candidates[info->highest_prio]) : "none"); ++ ++ 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) { ++ g_debug ("Output '%s' had no information associated, creating it ad-hoc", ++ gnome_rr_output_get_name (info->candidates[j])); ++ output = output_info_new (info->candidates[j]); ++ g_hash_table_insert (mapper->output_devices, ++ info->candidates[j], output); ++ } ++ ++ 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 devices ++ * with the same capabilities, so punt 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); ++ g_debug ("(Re)mapping all 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; ++ ++ g_debug ("(Re)mapping input device '%s'", ++ gdk_device_get_name (input->device)); ++ ++ 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; ++ else ++ return FALSE; ++ ++ 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); ++ g_debug ("(Re)building output map\n"); ++ ++ 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 ++ * ++ * 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 ++#undef GNOME_DESKTOP_USE_UNSTABLE_API ++#include ++ ++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 d5d2a2a..fcf6768 100644 +--- a/plugins/common/gsd-input-helper.c ++++ b/plugins/common/gsd-input-helper.c +@@ -571,3 +571,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 0bf328a..262f3b7 100644 +--- a/plugins/common/gsd-input-helper.h ++++ b/plugins/common/gsd-input-helper.h +@@ -82,6 +82,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 */ +diff --git a/plugins/wacom/gsd-wacom-manager.c b/plugins/wacom/gsd-wacom-manager.c +index 5b01047..6ec2d8a 100644 +--- a/plugins/wacom/gsd-wacom-manager.c ++++ b/plugins/wacom/gsd-wacom-manager.c +@@ -48,6 +48,7 @@ + #include "gsd-wacom-manager.h" + #include "gsd-wacom-device.h" + #include "gsd-wacom-osd-window.h" ++#include "gsd-device-mapper.h" + + #define GSD_WACOM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_WACOM_MANAGER, GsdWacomManagerPrivate)) + +@@ -81,6 +82,8 @@ struct GsdWacomManagerPrivate + GHashTable *devices; /* key = GdkDevice, value = GsdWacomDevice */ + GList *rr_screens; + ++ GsdDeviceMapper *device_mapper; ++ + /* button capture */ + GSList *screens; + int opcode; +@@ -173,22 +176,6 @@ wacom_set_property (GsdWacomDevice *device, + } + + static void +-set_rotation (GsdWacomDevice *device, +- GsdWacomRotation rotation) +-{ +- gchar rot = rotation; +- PropertyHelper property = { +- .name = "Wacom Rotation", +- .nitems = 1, +- .format = 8, +- .type = XA_INTEGER, +- .data.c = &rot, +- }; +- +- wacom_set_property (device, &property); +-} +- +-static void + set_pressurecurve (GsdWacomDevice *device, + GVariant *value) + { +@@ -237,73 +224,6 @@ set_area (GsdWacomDevice *device, + g_variant_unref (value); + } + +-/* Returns the rotation to apply a device relative to the current rotation of the output */ +-static GsdWacomRotation +-get_relative_rotation (GsdWacomRotation device_rotation, +- GsdWacomRotation output_rotation) +-{ +- GsdWacomRotation rotations[] = { GSD_WACOM_ROTATION_HALF, +- GSD_WACOM_ROTATION_CW, +- GSD_WACOM_ROTATION_NONE, +- GSD_WACOM_ROTATION_CCW }; +- guint i; +- +- if (device_rotation == output_rotation) +- return GSD_WACOM_ROTATION_NONE; +- +- if (output_rotation == GSD_WACOM_ROTATION_NONE) +- return device_rotation; +- +- for (i = 0; i < G_N_ELEMENTS (rotations); i++){ +- if (device_rotation == rotations[i]) +- break; +- } +- +- if (output_rotation == GSD_WACOM_ROTATION_HALF) +- return rotations[(i + G_N_ELEMENTS (rotations) - 2) % G_N_ELEMENTS (rotations)]; +- +- if (output_rotation == GSD_WACOM_ROTATION_CW) +- return rotations[(i + G_N_ELEMENTS (rotations) - 1) % G_N_ELEMENTS (rotations)]; +- +- if (output_rotation == GSD_WACOM_ROTATION_CCW) +- return rotations[(i + 1) % G_N_ELEMENTS (rotations)]; +- +- /* fallback */ +- return GSD_WACOM_ROTATION_NONE; +-} +- +-static void +-set_display (GsdWacomDevice *device, +- GVariant *value) +-{ +- GsdWacomRotation device_rotation; +- GsdWacomRotation output_rotation; +- GSettings *settings; +- float matrix[NUM_ELEMS_MATRIX]; +- PropertyHelper property = { +- .name = "Coordinate Transformation Matrix", +- .nitems = NUM_ELEMS_MATRIX, +- .format = 32, +- .type = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "FLOAT", True), +- }; +- +- gsd_wacom_device_get_display_matrix (device, matrix); +- +- property.data.i = (gint*)(&matrix); +- g_debug ("Applying matrix to device..."); +- wacom_set_property (device, &property); +- +- /* Compute rotation to apply relative to the output */ +- settings = gsd_wacom_device_get_settings (device); +- device_rotation = g_settings_get_enum (settings, KEY_ROTATION); +- output_rotation = gsd_wacom_device_get_display_rotation (device); +- +- /* Apply display rotation to device */ +- set_rotation (device, get_relative_rotation (device_rotation, output_rotation)); +- +- g_variant_unref (value); +-} +- + static void + set_absolute (GsdWacomDevice *device, + gint is_absolute) +@@ -377,7 +297,8 @@ set_keep_aspect (GsdWacomDevice *device, + { + GVariant *values[4], *variant; + guint i; +- ++ GdkDevice *gdk_device; ++ GsdDeviceMapper *mapper; + gint *area; + gint monitor = GSD_WACOM_SET_ALL_MONITORS; + GsdWacomRotation rotation; +@@ -412,7 +333,9 @@ set_keep_aspect (GsdWacomDevice *device, + } + + /* Get corresponding monitor size */ +- monitor = gsd_wacom_device_get_display_monitor (device); ++ mapper = gsd_device_mapper_get (); ++ g_object_get (device, "gdk-device", &gdk_device, NULL); ++ monitor = gsd_device_mapper_get_device_monitor (mapper, gdk_device); + + /* Adjust area to match the monitor aspect ratio */ + g_debug ("Initial device area: (%d,%d) (%d,%d)", area[0], area[1], area[2], area[3]); +@@ -715,7 +638,6 @@ set_wacom_settings (GsdWacomManager *manager, + gsd_wacom_device_type_to_string (gsd_wacom_device_get_device_type (device))); + + settings = gsd_wacom_device_get_settings (device); +- set_rotation (device, g_settings_get_enum (settings, KEY_ROTATION)); + set_touch (device, g_settings_get_boolean (settings, KEY_TOUCH)); + + type = gsd_wacom_device_get_device_type (device); +@@ -760,7 +682,6 @@ set_wacom_settings (GsdWacomManager *manager, + set_keep_aspect (device, g_settings_get_boolean (settings, KEY_KEEP_ASPECT)); + set_area (device, g_settings_get_value (settings, KEY_AREA)); + } +- set_display (device, g_settings_get_value (settings, KEY_DISPLAY)); + + /* only pen and eraser have pressure threshold and curve settings */ + if (type == WACOM_TYPE_STYLUS || +@@ -779,8 +700,7 @@ wacom_settings_changed (GSettings *settings, + type = gsd_wacom_device_get_device_type (device); + + if (g_str_equal (key, KEY_ROTATION)) { +- if (type != WACOM_TYPE_PAD) +- set_rotation (device, g_settings_get_enum (settings, key)); ++ /* Real device rotation is handled in GsdDeviceMapper */ + } else if (g_str_equal (key, KEY_TOUCH)) { + set_touch (device, g_settings_get_boolean (settings, key)); + } else if (g_str_equal (key, KEY_TPCBUTTON)) { +@@ -796,9 +716,7 @@ wacom_settings_changed (GSettings *settings, + type != WACOM_TYPE_TOUCH) + set_area (device, g_settings_get_value (settings, key)); + } else if (g_str_equal (key, KEY_DISPLAY)) { +- if (type != WACOM_TYPE_CURSOR && +- type != WACOM_TYPE_PAD) +- set_display (device, g_settings_get_value (settings, key)); ++ /* Unhandled, GsdDeviceMapper handles this */ + } else if (g_str_equal (key, KEY_KEEP_ASPECT)) { + if (type != WACOM_TYPE_CURSOR && + type != WACOM_TYPE_PAD && +@@ -973,6 +891,16 @@ device_added_cb (GdkDeviceManager *device_manager, + g_signal_connect (G_OBJECT (settings), "changed", + G_CALLBACK (wacom_settings_changed), device); + ++ /* Map devices, the xrandr module handles touchscreens in general, so bypass these here */ ++ if (gsd_wacom_device_get_device_type (device) == WACOM_TYPE_PAD || ++ gsd_wacom_device_get_device_type (device) == WACOM_TYPE_STYLUS || ++ gsd_wacom_device_get_device_type (device) == WACOM_TYPE_ERASER || ++ (gsd_wacom_device_get_device_type (device) == WACOM_TYPE_TOUCH && ++ !gsd_wacom_device_is_screen_tablet (device))) { ++ gsd_device_mapper_add_input (manager->priv->device_mapper, ++ gdk_device, settings); ++ } ++ + if (gsd_wacom_device_get_device_type (device) == WACOM_TYPE_STYLUS || + gsd_wacom_device_get_device_type (device) == WACOM_TYPE_ERASER) { + GList *styli, *l; +@@ -1003,6 +931,9 @@ device_removed_cb (GdkDeviceManager *device_manager, + gdk_device_get_name (gdk_device)); + g_hash_table_remove (manager->priv->devices, gdk_device); + ++ gsd_device_mapper_remove_input (manager->priv->device_mapper, ++ gdk_device); ++ + /* Enable this chunk of code if you want to valgrind + * test-wacom. It will exit when there are no Wacom devices left */ + #if 0 +@@ -1171,9 +1102,11 @@ generate_key (GsdWacomTabletButton *wbutton, + } + + static void +-switch_monitor (GsdWacomDevice *device) ++switch_monitor (GsdWacomManager *manager, ++ GsdWacomDevice *device) + { + gint current_monitor, n_monitors; ++ GdkDevice *gdk_device; + + /* We dont; do that for screen tablets, sorry... */ + if (gsd_wacom_device_is_screen_tablet (device)) +@@ -1185,15 +1118,19 @@ switch_monitor (GsdWacomDevice *device) + if (n_monitors < 2) + return; + +- current_monitor = gsd_wacom_device_get_display_monitor (device); ++ g_object_get (device, "gdk-device", &gdk_device, NULL); ++ current_monitor = ++ gsd_device_mapper_get_device_monitor (manager->priv->device_mapper, ++ gdk_device); + + /* Select next monitor */ + current_monitor++; + + if (current_monitor >= n_monitors) +- current_monitor = GSD_WACOM_SET_ALL_MONITORS; ++ current_monitor = 0; + +- gsd_wacom_device_set_display (device, current_monitor); ++ gsd_device_mapper_set_device_monitor (manager->priv->device_mapper, ++ gdk_device, current_monitor); + } + + static const char* +@@ -1303,7 +1240,7 @@ filter_button_events (XEvent *xevent, + /* Switch monitor */ + if (g_settings_get_enum (wbutton->settings, KEY_ACTION_TYPE) == GSD_WACOM_ACTION_TYPE_SWITCH_MONITOR) { + if (xiev->evtype == XI_ButtonRelease) +- switch_monitor (device); ++ switch_monitor (manager, device); + return GDK_FILTER_REMOVE; + } + +@@ -1343,6 +1280,8 @@ gsd_wacom_manager_idle_cb (GsdWacomManager *manager) + + gnome_settings_profile_start (NULL); + ++ manager->priv->device_mapper = gsd_device_mapper_get (); ++ + manager->priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); + + set_devicepresence_handler (manager); +@@ -1402,7 +1341,6 @@ on_screen_changed_cb (GnomeRRScreen *rr_screen, + set_keep_aspect (device, g_settings_get_boolean (settings, KEY_KEEP_ASPECT)); + set_area (device, g_settings_get_value (settings, KEY_AREA)); + } +- set_display (device, g_settings_get_value (settings, KEY_DISPLAY)); + } + g_list_free (devices); + } +diff --git a/plugins/xrandr/gsd-xrandr-manager.c b/plugins/xrandr/gsd-xrandr-manager.c +index 8e6aa83..bc02f67 100644 +--- a/plugins/xrandr/gsd-xrandr-manager.c ++++ b/plugins/xrandr/gsd-xrandr-manager.c +@@ -47,16 +47,13 @@ + #include + #include + +-#ifdef HAVE_WACOM +-#include +-#endif /* HAVE_WACOM */ +- + #include "gsd-enums.h" + #include "gsd-input-helper.h" + #include "gnome-settings-plugin.h" + #include "gnome-settings-profile.h" + #include "gnome-settings-session.h" + #include "gsd-xrandr-manager.h" ++#include "gsd-device-mapper.h" + + #define GSD_XRANDR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_XRANDR_MANAGER, GsdXrandrManagerPrivate)) + +@@ -117,16 +114,17 @@ struct GsdXrandrManagerPrivate { + GDBusConnection *connection; + GCancellable *bus_cancellable; + ++ GsdDeviceMapper *device_mapper; ++ GdkDeviceManager *device_manager; ++ guint device_added_id; ++ guint device_removed_id; ++ + /* fn-F7 status */ + int current_fn_f7_config; /* -1 if no configs */ + GnomeRRConfig **fn_f7_configs; /* NULL terminated, NULL if there are no configs */ + + /* Last time at which we got a "screen got reconfigured" event; see on_randr_event() */ + guint32 last_config_timestamp; +- +-#ifdef HAVE_WACOM +- WacomDeviceDatabase *wacom_db; +-#endif /* HAVE_WACOM */ + }; + + static const GnomeRRRotation possible_rotations[] = { +@@ -1491,137 +1489,6 @@ get_next_rotation (GnomeRRRotation allowed_rotations, GnomeRRRotation current_ro + } + } + +-struct { +- GnomeRRRotation rotation; +- /* evdev */ +- gboolean x_axis_inversion; +- gboolean y_axis_inversion; +- gboolean axes_swap; +-} evdev_rotations[] = { +- { GNOME_RR_ROTATION_0, 0, 0, 0 }, +- { GNOME_RR_ROTATION_90, 1, 0, 1 }, +- { GNOME_RR_ROTATION_180, 1, 1, 0 }, +- { GNOME_RR_ROTATION_270, 0, 1, 1 } +-}; +- +-static guint +-get_rotation_index (GnomeRRRotation rotation) +-{ +- guint i; +- +- for (i = 0; i < G_N_ELEMENTS (evdev_rotations); i++) { +- if (evdev_rotations[i].rotation == rotation) +- return i; +- } +- g_assert_not_reached (); +-} +- +-static gboolean +-is_wacom_tablet_device (GsdXrandrManager *mgr, +- XDeviceInfo *device_info) +-{ +-#ifdef HAVE_WACOM +- GsdXrandrManagerPrivate *priv = mgr->priv; +- gchar *device_node; +- WacomDevice *wacom_device; +- gboolean is_tablet = FALSE; +- +- if (priv->wacom_db == NULL) +- priv->wacom_db = libwacom_database_new (); +- +- device_node = xdevice_get_device_node (device_info->id); +- if (device_node == NULL) +- return FALSE; +- +- wacom_device = libwacom_new_from_path (priv->wacom_db, device_node, FALSE, NULL); +- g_free (device_node); +- if (wacom_device == NULL) { +- g_free (device_node); +- return FALSE; +- } +- is_tablet = libwacom_has_touch (wacom_device) && +- libwacom_is_builtin (wacom_device); +- +- libwacom_destroy (wacom_device); +- +- return is_tablet; +-#else /* HAVE_WACOM */ +- return FALSE; +-#endif /* HAVE_WACOM */ +-} +- +-static void +-rotate_touchscreens (GsdXrandrManager *mgr, +- GnomeRRRotation rotation) +-{ +- XDeviceInfo *device_info; +- gint n_devices; +- guint i, rot_idx; +- +- if (!supports_xinput_devices ()) +- return; +- +- g_debug ("Rotating touchscreen devices"); +- +- device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); +- if (device_info == NULL) +- return; +- +- rot_idx = get_rotation_index (rotation); +- +- for (i = 0; i < n_devices; i++) { +- if (is_wacom_tablet_device (mgr, &device_info[i])) { +- g_debug ("Not rotating tablet device '%s'", device_info[i].name); +- continue; +- } +- +- if (device_info_is_touchscreen (&device_info[i]) || +- device_info_is_tablet (&device_info[i])) { +- XDevice *device; +- char c = evdev_rotations[rot_idx].axes_swap; +- PropertyHelper axes_swap = { +- .name = "Evdev Axes Swap", +- .nitems = 1, +- .format = 8, +- .type = XA_INTEGER, +- .data.c = &c, +- }; +- +- g_debug ("About to rotate '%s'", device_info[i].name); +- +- gdk_error_trap_push (); +- device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_info[i].id); +- if (gdk_error_trap_pop () || (device == NULL)) +- continue; +- +- if (device_set_property (device, device_info[i].name, &axes_swap) != FALSE) { +- char axis[] = { +- evdev_rotations[rot_idx].x_axis_inversion, +- evdev_rotations[rot_idx].y_axis_inversion +- }; +- PropertyHelper axis_invert = { +- .name = "Evdev Axis Inversion", +- .nitems = 2, +- .format = 8, +- .type = XA_INTEGER, +- .data.c = axis, +- }; +- +- device_set_property (device, device_info[i].name, &axis_invert); +- +- g_debug ("Rotated '%s' to configuration '%d, %d, %d'", +- device_info[i].name, +- evdev_rotations[rot_idx].x_axis_inversion, +- evdev_rotations[rot_idx].y_axis_inversion, +- evdev_rotations[rot_idx].axes_swap); +- } +- +- XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device); +- } +- } +- XFreeDeviceList (device_info); +-} +- + /* We use this when the XF86RotateWindows key is pressed, or the + * orientation of a tablet changes. The key is present + * on some tablet PCs; they use it so that the user can rotate the tablet +@@ -1639,7 +1506,7 @@ handle_rotate_windows (GsdXrandrManager *mgr, + int num_allowed_rotations; + GnomeRRRotation allowed_rotations; + GnomeRRRotation next_rotation; +- gboolean success, show_error; ++ gboolean show_error; + + g_debug ("Handling XF86RotateWindows with rotation %d", rotation); + +@@ -1673,9 +1540,7 @@ handle_rotate_windows (GsdXrandrManager *mgr, + + gnome_rr_output_info_set_rotation (rotatable_output_info, next_rotation); + +- success = apply_configuration (mgr, current, timestamp, show_error, TRUE); +- if (success) +- rotate_touchscreens (mgr, next_rotation); ++ apply_configuration (mgr, current, timestamp, show_error, TRUE); + + out: + g_object_unref (current); +@@ -2040,6 +1905,56 @@ power_client_changed_cb (UpClient *client, gpointer data) + } + } + ++static void ++manager_device_added (GsdXrandrManager *manager, ++ GdkDevice *device) ++{ ++ if (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER || ++ gdk_device_get_source (device) != GDK_SOURCE_TOUCHSCREEN) ++ return; ++ ++ gsd_device_mapper_add_input (manager->priv->device_mapper, device, NULL); ++} ++ ++static void ++manager_device_removed (GsdXrandrManager *manager, ++ GdkDevice *device) ++{ ++ if (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER || ++ gdk_device_get_source (device) != GDK_SOURCE_TOUCHSCREEN) ++ return; ++ ++ gsd_device_mapper_remove_input (manager->priv->device_mapper, device); ++} ++ ++static void ++manager_init_devices (GsdXrandrManager *manager) ++{ ++ GdkDisplay *display; ++ GList *devices, *d; ++ GdkScreen *screen; ++ ++ screen = gdk_screen_get_default (); ++ display = gdk_screen_get_display (screen); ++ ++ manager->priv->device_mapper = gsd_device_mapper_get (); ++ manager->priv->device_manager = gdk_display_get_device_manager (display); ++ manager->priv->device_added_id = ++ g_signal_connect_swapped (manager->priv->device_manager, "device-added", ++ G_CALLBACK (manager_device_added), manager); ++ manager->priv->device_removed_id = ++ g_signal_connect_swapped (manager->priv->device_manager, "device-removed", ++ G_CALLBACK (manager_device_removed), manager); ++ ++ devices = gdk_device_manager_list_devices (manager->priv->device_manager, ++ GDK_DEVICE_TYPE_SLAVE); ++ ++ for (d = devices; d; d = d->next) ++ manager_device_added (manager, d->data); ++ ++ g_list_free (devices); ++} ++ + gboolean + gsd_xrandr_manager_start (GsdXrandrManager *manager, + GError **error) +@@ -2081,6 +1996,8 @@ gsd_xrandr_manager_start (GsdXrandrManager *manager, + log_msg ("State of screen after initial configuration:\n"); + log_screen (manager->priv->rw_screen); + ++ manager_init_devices (manager); ++ + log_close (); + + gnome_settings_profile_end (NULL); +@@ -2127,12 +2044,12 @@ gsd_xrandr_manager_stop (GsdXrandrManager *manager) + manager->priv->connection = NULL; + } + +-#ifdef HAVE_WACOM +- if (manager->priv->wacom_db != NULL) { +- libwacom_database_destroy (manager->priv->wacom_db); +- manager->priv->wacom_db = NULL; ++ if (manager->priv->device_manager != NULL) { ++ g_signal_handler_disconnect (manager->priv->device_manager, ++ manager->priv->device_added_id); ++ g_signal_handler_disconnect (manager->priv->device_manager, ++ manager->priv->device_removed_id); + } +-#endif /* HAVE_WACOM */ + + log_open (); + log_msg ("STOPPING XRANDR PLUGIN\n------------------------------------------------------------\n"); diff --git a/SPECS/gnome-settings-daemon.spec b/SPECS/gnome-settings-daemon.spec index f9ed4ab..7d045d1 100644 --- a/SPECS/gnome-settings-daemon.spec +++ b/SPECS/gnome-settings-daemon.spec @@ -1,6 +1,6 @@ Name: gnome-settings-daemon Version: 3.8.6.1 -Release: 9%{?dist} +Release: 9%{?dist}.1 Summary: The daemon sharing settings from GNOME to GTK+/KDE applications Group: System Environment/Daemons @@ -23,6 +23,8 @@ Patch6: translations.patch Patch7: 0001-keyboard-Apply-num-lock-to-newly-connected-keyboards.patch # https://bugzilla.redhat.com/show_bug.cgi?id=1081093 Patch8: 0001-power-Check-that-the-output-is-connected-when-determ.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1096064 +Patch9: input-device-mapping.patch Requires: control-center-filesystem Requires: colord @@ -96,6 +98,7 @@ The %{name}-updates package contains the updates plugin for %{name} %patch6 -p2 -b .translations %patch7 -p1 %patch8 -p1 +%patch9 -p1 autoreconf -i -f @@ -272,6 +275,10 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : %{_datadir}/glib-2.0/schemas/org.gnome.settings-daemon.plugins.updates.gschema.xml %changelog +* Mon May 12 2014 Carlos Garnacho - 3.8.6.1-9.1 +- Fix mapping of touchscreens and other tablet devices +Resolves: #1096064 + * Fri May 2 2014 Rui Matos - 3.8.6.1-9 - Check that the output is connected when determining if it's ON Resolves: #1081093