From df48b948898ccb1666c791bd5f193b732dc71d01 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 20 Apr 2018 16:33:02 +0200 Subject: [PATCH 1/6] clutter: Add API to retrieve the physical size of absolute devices This will be used in upper layers to match abs input devices (touchscreens, tablets) to the corresponding output. --- .../clutter/clutter-device-manager-private.h | 4 ++ clutter/clutter/clutter-input-device.c | 12 +++++ clutter/clutter/clutter-input-device.h | 4 ++ .../evdev/clutter-input-device-evdev.c | 13 +++++ .../clutter/x11/clutter-input-device-xi2.c | 51 +++++++++++++++++++ 5 files changed, 84 insertions(+) diff --git a/clutter/clutter/clutter-device-manager-private.h b/clutter/clutter/clutter-device-manager-private.h index 2364fd27cb5..60cb99d55ba 100644 --- a/clutter/clutter/clutter-device-manager-private.h +++ b/clutter/clutter/clutter-device-manager-private.h @@ -167,6 +167,10 @@ struct _ClutterInputDeviceClass gboolean (* is_grouped) (ClutterInputDevice *device, ClutterInputDevice *other_device); + gboolean (* get_physical_size) (ClutterInputDevice *device, + gdouble *width, + gdouble *height); + /* Keyboard accessbility */ void (* process_kbd_a11y_event) (ClutterEvent *event, ClutterInputDevice *device, diff --git a/clutter/clutter/clutter-input-device.c b/clutter/clutter/clutter-input-device.c index 0697e3a1a50..5de169c11d7 100644 --- a/clutter/clutter/clutter-input-device.c +++ b/clutter/clutter/clutter-input-device.c @@ -2284,3 +2284,15 @@ clutter_input_device_is_grouped (ClutterInputDevice *device, return CLUTTER_INPUT_DEVICE_GET_CLASS (device)->is_grouped (device, other_device); } + +gboolean +clutter_input_device_get_physical_size (ClutterInputDevice *device, + gdouble *width, + gdouble *height) +{ + g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); + + return CLUTTER_INPUT_DEVICE_GET_CLASS (device)->get_physical_size (device, + width, + height); +} diff --git a/clutter/clutter/clutter-input-device.h b/clutter/clutter/clutter-input-device.h index c121fe12cd7..8dd2a54b9cf 100644 --- a/clutter/clutter/clutter-input-device.h +++ b/clutter/clutter/clutter-input-device.h @@ -171,6 +171,10 @@ void clutter_input_device_set_mapping_mode (ClutterInputDev CLUTTER_EXPORT gboolean clutter_input_device_is_grouped (ClutterInputDevice *device, ClutterInputDevice *other_device); +CLUTTER_EXPORT +gboolean clutter_input_device_get_physical_size (ClutterInputDevice *device, + gdouble *width, + gdouble *height); G_END_DECLS diff --git a/clutter/clutter/evdev/clutter-input-device-evdev.c b/clutter/clutter/evdev/clutter-input-device-evdev.c index 1113e3badd5..f49bc7a5228 100644 --- a/clutter/clutter/evdev/clutter-input-device-evdev.c +++ b/clutter/clutter/evdev/clutter-input-device-evdev.c @@ -1265,6 +1265,18 @@ clutter_input_device_evdev_release_touch_state (ClutterInputDeviceEvdev *device, GINT_TO_POINTER (touch_state->device_slot)); } +static gboolean +clutter_input_device_evdev_get_physical_size (ClutterInputDevice *device, + gdouble *width, + gdouble *height) +{ + struct libinput_device *libinput_device; + + libinput_device = clutter_evdev_input_device_get_libinput_device (device); + + return libinput_device_get_size (libinput_device, width, height) == 0; +} + static void clutter_input_device_evdev_class_init (ClutterInputDeviceEvdevClass *klass) { @@ -1280,6 +1292,7 @@ clutter_input_device_evdev_class_init (ClutterInputDeviceEvdevClass *klass) klass->get_group_n_modes = clutter_input_device_evdev_get_group_n_modes; klass->is_grouped = clutter_input_device_evdev_is_grouped; klass->process_kbd_a11y_event = clutter_input_device_evdev_process_kbd_a11y_event; + klass->get_physical_size = clutter_input_device_evdev_get_physical_size; obj_props[PROP_DEVICE_MATRIX] = g_param_spec_boxed ("device-matrix", diff --git a/clutter/clutter/x11/clutter-input-device-xi2.c b/clutter/clutter/x11/clutter-input-device-xi2.c index 1254aca3ae2..92d3637da91 100644 --- a/clutter/clutter/x11/clutter-input-device-xi2.c +++ b/clutter/clutter/x11/clutter-input-device-xi2.c @@ -180,6 +180,56 @@ clutter_input_device_xi2_is_mode_switch_button (ClutterInputDevice *device, return button_group == (int) group; } +static gboolean +clutter_input_device_xi2_get_physical_size (ClutterInputDevice *device, + gdouble *width, + gdouble *height) +{ + Display *xdisplay; + XIDeviceInfo *dev_info; + gdouble w = 0, h = 0; + int i, n_info, device_id; + + xdisplay = clutter_x11_get_default_display (); + device_id = clutter_input_device_get_device_id (device); + + clutter_x11_trap_x_errors (); + dev_info = XIQueryDevice (xdisplay, device_id, &n_info); + if (clutter_x11_untrap_x_errors ()) + return FALSE; + + if (!dev_info) + return FALSE; + + for (i = 0; i < dev_info->num_classes; i++) + { + XIValuatorClassInfo *valuator; + gdouble *value; + + if (dev_info->classes[i]->type != XIValuatorClass) + continue; + + valuator = (XIValuatorClassInfo *) dev_info->classes[i]; + + if (valuator->label == XInternAtom (xdisplay, "Abs X", True) || + valuator->label == XInternAtom (xdisplay, "Abs MT Position X", True)) + value = &w; + else if (valuator->label == XInternAtom (xdisplay, "Abs Y", True) || + valuator->label == XInternAtom (xdisplay, "Abs MT Position Y", True)) + value = &h; + else + continue; + + *value = (valuator->max - valuator->min) * 1000 / valuator->resolution; + } + + XIFreeDeviceInfo (dev_info); + *width = w; + *height = h; + + return (w > 0 && h > 0); +} + static void clutter_input_device_xi2_class_init (ClutterInputDeviceXI2Class *klass) { @@ -193,6 +243,7 @@ clutter_input_device_xi2_class_init (ClutterInputDeviceXI2Class *klass) device_class->is_grouped = clutter_input_device_xi2_is_grouped; device_class->get_group_n_modes = clutter_input_device_xi2_get_group_n_modes; device_class->is_mode_switch_button = clutter_input_device_xi2_is_mode_switch_button; + device_class->get_physical_size = clutter_input_device_xi2_get_physical_size; } static void -- GitLab From 08a5e660d3666c1ccfdbc8ccabddb53e39aeade2 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 20 Apr 2018 16:34:32 +0200 Subject: [PATCH 2/6] backends: Add MetaInputMapper This object takes care of mapping absolute devices to monitors, to do so it uses 3 heuristics, in this order of preference: - If a device is known to be builtin, it's assigned to the builtin monitor. - If input device and monitor match sizes (with an error margin of 5%) - If input device name and monitor vendor/product in EDID match somehow (from "full", through "partial", to just "vendor") The most favorable outputs are then assigned to each device, making sure not to assign two devices of the same kind to the same output. This object replaces (and is mostly 1:1 with) GsdDeviceMapper in g-s-d. That object would perform these same heuristics, and let mutter indirectly know through settings changes. This object allows doing the same in-process. --- src/Makefile.am | 2 + src/backends/meta-input-mapper-private.h | 40 ++ src/backends/meta-input-mapper.c | 617 +++++++++++++++++++++++ src/meson.build | 2 + 4 files changed, 661 insertions(+) create mode 100644 src/backends/meta-input-mapper-private.h create mode 100644 src/backends/meta-input-mapper.c diff --git a/src/Makefile.am b/src/Makefile.am index 2969f2ea0c1..ce590467824 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -141,6 +141,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES = \ backends/meta-idle-monitor-private.h \ backends/meta-idle-monitor-dbus.c \ backends/meta-idle-monitor-dbus.h \ + backends/meta-input-mapper.c \ + backends/meta-input-mapper-private.h \ backends/meta-input-settings.c \ backends/meta-input-settings-private.h \ backends/meta-logical-monitor.c \ diff --git a/src/backends/meta-input-mapper-private.h b/src/backends/meta-input-mapper-private.h new file mode 100644 index 00000000000..53bc66b06d6 --- /dev/null +++ b/src/backends/meta-input-mapper-private.h @@ -0,0 +1,40 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright 2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Author: Carlos Garnacho + */ + +#ifndef META_INPUT_MAPPER_H +#define META_INPUT_MAPPER_H + +#include + +#define META_TYPE_INPUT_MAPPER (meta_input_mapper_get_type ()) + +G_DECLARE_FINAL_TYPE (MetaInputMapper, meta_input_mapper, + META, INPUT_MAPPER, GObject) + +MetaInputMapper * meta_input_mapper_new (void); + +void meta_input_mapper_add_device (MetaInputMapper *mapper, + ClutterInputDevice *device, + gboolean builtin); +void meta_input_mapper_remove_device (MetaInputMapper *mapper, + ClutterInputDevice *device); + +#endif /* META_INPUT_MAPPER_H */ diff --git a/src/backends/meta-input-mapper.c b/src/backends/meta-input-mapper.c new file mode 100644 index 00000000000..026dacb5892 --- /dev/null +++ b/src/backends/meta-input-mapper.c @@ -0,0 +1,617 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright 2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Author: Carlos Garnacho + */ + +#include "config.h" + +#include "meta-input-mapper-private.h" +#include "meta-monitor-manager-private.h" +#include "meta-logical-monitor.h" +#include "meta-backend-private.h" + +#define MAX_SIZE_MATCH_DIFF 0.05 + +typedef struct _MetaMapperInputInfo MetaMapperInputInfo; +typedef struct _MetaMapperOutputInfo MetaMapperOutputInfo; +typedef struct _MappingHelper MappingHelper; +typedef struct _DeviceCandidates DeviceCandidates; + +struct _MetaInputMapper +{ + GObject parent_instance; + MetaMonitorManager *monitor_manager; + ClutterDeviceManager *input_device_manager; + GHashTable *input_devices; /* ClutterInputDevice -> MetaMapperInputInfo */ + GHashTable *output_devices; /* MetaLogicalMonitor -> MetaMapperOutputInfo */ +}; + +typedef enum { + META_INPUT_CAP_TOUCH = 1 << 0, /* touch device, either touchscreen or tablet */ + META_INPUT_CAP_STYLUS = 1 << 1, /* tablet pen */ + META_INPUT_CAP_ERASER = 1 << 2, /* tablet eraser */ + META_INPUT_CAP_PAD = 1 << 3, /* pad device, most usually in tablets */ + META_INPUT_CAP_CURSOR = 1 << 4 /* pointer-like device in tablets */ +} MetaInputCapabilityFlags; + +typedef enum { + META_MATCH_IS_BUILTIN, /* Output is builtin, applies mainly to system-integrated devices */ + META_MATCH_SIZE, /* Size from input device and output match */ + META_MATCH_EDID_FULL, /* Full EDID model match, eg. "Cintiq 12WX" */ + META_MATCH_EDID_PARTIAL, /* Partial EDID model match, eg. "Cintiq" */ + META_MATCH_EDID_VENDOR, /* EDID vendor match, eg. "WAC" for Wacom */ + N_OUTPUT_MATCHES +} MetaOutputMatchType; + +struct _MetaMapperInputInfo +{ + ClutterInputDevice *device; + MetaInputMapper *mapper; + MetaMapperOutputInfo *output; + guint builtin : 1; +}; + +struct _MetaMapperOutputInfo +{ + MetaLogicalMonitor *logical_monitor; + GList *input_devices; + MetaInputCapabilityFlags attached_caps; +}; + +struct _MappingHelper +{ + GArray *device_maps; +}; + +struct _DeviceCandidates +{ + MetaMapperInputInfo *input; + + MetaMonitor *candidates[N_OUTPUT_MATCHES]; + + MetaOutputMatchType best; +}; + +enum { + DEVICE_MAPPED, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + +G_DEFINE_TYPE (MetaInputMapper, meta_input_mapper, G_TYPE_OBJECT) + +static MetaMapperInputInfo * +mapper_input_info_new (ClutterInputDevice *device, + MetaInputMapper *mapper, + gboolean builtin) +{ + MetaMapperInputInfo *info; + + info = g_new0 (MetaMapperInputInfo, 1); + info->mapper = mapper; + info->device = device; + info->builtin = builtin; + + return info; +} + +static void +mapper_input_info_free (MetaMapperInputInfo *info) +{ + g_free (info); +} + +static MetaMapperOutputInfo * +mapper_output_info_new (MetaLogicalMonitor *logical_monitor) +{ + MetaMapperOutputInfo *info; + + info = g_new0 (MetaMapperOutputInfo, 1); + info->logical_monitor = logical_monitor; + + return info; +} + +static void +mapper_output_info_free (MetaMapperOutputInfo *info) +{ + g_free (info); +} + +static MetaInputCapabilityFlags +mapper_input_info_get_caps (MetaMapperInputInfo *info) +{ + ClutterInputDeviceType type; + + type = clutter_input_device_get_device_type (info->device); + + switch (type) + { + case CLUTTER_TOUCHSCREEN_DEVICE: + return META_INPUT_CAP_TOUCH; + case CLUTTER_TABLET_DEVICE: + case CLUTTER_PEN_DEVICE: + return META_INPUT_CAP_STYLUS; + case CLUTTER_ERASER_DEVICE: + return META_INPUT_CAP_ERASER; + case CLUTTER_CURSOR_DEVICE: + return META_INPUT_CAP_CURSOR; + case CLUTTER_PAD_DEVICE: + return META_INPUT_CAP_PAD; + default: + return 0; + } +} + +static void +mapper_input_info_set_output (MetaMapperInputInfo *input, + MetaMapperOutputInfo *output, + MetaMonitor *monitor) +{ + if (input->output == output) + return; + + input->output = output; + g_signal_emit (input->mapper, signals[DEVICE_MAPPED], 0, + input->device, + output ? output->logical_monitor : NULL, monitor); +} + +static void +mapper_output_info_add_input (MetaMapperOutputInfo *output, + MetaMapperInputInfo *input, + MetaMonitor *monitor) +{ + g_assert (input->output == NULL); + + output->input_devices = g_list_prepend (output->input_devices, input); + output->attached_caps |= mapper_input_info_get_caps (input); + + mapper_input_info_set_output (input, output, monitor); +} + +static void +mapper_output_info_remove_input (MetaMapperOutputInfo *output, + MetaMapperInputInfo *input) +{ + GList *l; + + g_assert (input->output == output); + + output->input_devices = g_list_remove (output->input_devices, input); + output->attached_caps = 0; + + for (l = output->input_devices; l; l = l->next) + output->attached_caps |= mapper_input_info_get_caps (l->data); + + mapper_input_info_set_output (input, NULL, NULL); +} + +static void +mapper_output_info_clear_inputs (MetaMapperOutputInfo *output) +{ + while (output->input_devices) + { + MetaMapperInputInfo *input = output->input_devices->data; + + mapper_input_info_set_output (input, NULL, NULL); + output->input_devices = g_list_remove (output->input_devices, input); + } + + output->attached_caps = 0; +} + +static void +mapping_helper_init (MappingHelper *helper) +{ + helper->device_maps = g_array_new (FALSE, FALSE, sizeof (DeviceCandidates)); +} + +static void +mapping_helper_release (MappingHelper *helper) +{ + g_array_unref (helper->device_maps); +} + +static gboolean +match_edid (MetaMapperInputInfo *input, + MetaMonitor *monitor, + MetaOutputMatchType *match_type) +{ + const gchar *dev_name; + + dev_name = clutter_input_device_get_device_name (input->device); + + if (strcasestr (dev_name, meta_monitor_get_vendor (monitor)) == NULL) + return FALSE; + + *match_type = META_MATCH_EDID_VENDOR; + + if (strcasestr (dev_name, meta_monitor_get_product (monitor)) != NULL) + { + *match_type = META_MATCH_EDID_FULL; + } + else + { + char **split; + int i = 0; + + split = g_strsplit (meta_monitor_get_product (monitor), " ", -1); + + while (split[i]) + { + if (strcasestr (dev_name, split[i]) != NULL) + { + *match_type = META_MATCH_EDID_PARTIAL; + break; + } + } + + g_strfreev (split); + } + + return TRUE; +} + +static gboolean +find_size_match (MetaMapperInputInfo *input, + GList *monitors, + MetaMonitor **matched_monitor) +{ + double min_w_diff, min_h_diff; + double i_width, i_height; + gboolean found = FALSE; + GList *l; + + min_w_diff = min_h_diff = MAX_SIZE_MATCH_DIFF; + + if (!clutter_input_device_get_physical_size (input->device, &i_width, &i_height)) + return FALSE; + + for (l = monitors; l; l = l->next) + { + MetaMonitor *monitor = l->data; + double w_diff, h_diff; + int o_width, o_height; + + meta_monitor_get_physical_dimensions (monitor, &o_width, &o_height); + w_diff = ABS (1 - ((double) o_width / i_width)); + h_diff = ABS (1 - ((double) o_height / i_height)); + + if (w_diff >= min_w_diff || h_diff >= min_h_diff) + continue; + + *matched_monitor = monitor; + min_w_diff = w_diff; + min_h_diff = h_diff; + found = TRUE; + } + + return found; +} + +static gboolean +find_builtin_output (MetaInputMapper *mapper, + MetaMonitor **matched_monitor) +{ + MetaMonitor *panel; + + panel = meta_monitor_manager_get_laptop_panel (mapper->monitor_manager); + *matched_monitor = panel; + return panel != NULL; +} + +static gboolean +guess_candidates (MetaInputMapper *mapper, + MetaMapperInputInfo *input, + DeviceCandidates *info) +{ + MetaOutputMatchType best = N_OUTPUT_MATCHES; + GList *monitors, *l; + MetaMonitor *matched_monitor = NULL; + + monitors = meta_monitor_manager_get_monitors (mapper->monitor_manager); + + for (l = monitors; l; l = l->next) + { + MetaOutputMatchType edid_match; + + if (match_edid (input, l->data, &edid_match)) + { + best = MIN (best, edid_match); + info->candidates[edid_match] = l->data; + } + } + + if (find_size_match (input, monitors, &matched_monitor)) + { + best = MIN (best, META_MATCH_SIZE); + info->candidates[META_MATCH_SIZE] = matched_monitor; + } + + if (input->builtin) + { + best = MIN (best, META_MATCH_IS_BUILTIN); + find_builtin_output (mapper, &info->candidates[META_MATCH_IS_BUILTIN]); + } + + if (best < N_OUTPUT_MATCHES) + { + info->best = best; + return TRUE; + } + else + { + return FALSE; + } +} + +static void +mapping_helper_add (MappingHelper *helper, + MetaMapperInputInfo *input, + MetaInputMapper *mapper) +{ + DeviceCandidates info = { 0, }; + guint i, pos = 0; + + info.input = input; + + if (!guess_candidates (mapper, input, &info)) + return; + + for (i = 0; i < helper->device_maps->len; i++) + { + DeviceCandidates *elem; + + elem = &g_array_index (helper->device_maps, DeviceCandidates, i); + + if (elem->best < info.best) + 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); +} + +static void +mapping_helper_apply (MappingHelper *helper, + MetaInputMapper *mapper) +{ + guint i; + + /* Now, decide which input claims which output */ + for (i = 0; i < helper->device_maps->len; i++) + { + MetaMapperOutputInfo *output; + DeviceCandidates *info; + MetaOutputMatchType j; + + info = &g_array_index (helper->device_maps, DeviceCandidates, i); + + for (j = 0; j < N_OUTPUT_MATCHES; j++) + { + MetaLogicalMonitor *logical_monitor; + + if (!info->candidates[j]) + continue; + + logical_monitor = + meta_monitor_get_logical_monitor (info->candidates[j]); + output = g_hash_table_lookup (mapper->output_devices, + logical_monitor); + + if (!output) + continue; + + if (output->attached_caps & mapper_input_info_get_caps (info->input)) + continue; + + mapper_output_info_add_input (output, info->input, + info->candidates[j]); + break; + } + } +} + +static void +mapper_recalculate_candidates (MetaInputMapper *mapper) +{ + MetaMapperInputInfo *input; + MappingHelper helper; + GHashTableIter iter; + + mapping_helper_init (&helper); + g_hash_table_iter_init (&iter, mapper->input_devices); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &input)) + mapping_helper_add (&helper, input, mapper); + + mapping_helper_apply (&helper, mapper); + mapping_helper_release (&helper); +} + +static void +mapper_recalculate_input (MetaInputMapper *mapper, + MetaMapperInputInfo *input) +{ + MappingHelper helper; + + mapping_helper_init (&helper); + mapping_helper_add (&helper, input, mapper); + mapping_helper_apply (&helper, mapper); + mapping_helper_release (&helper); +} + +static void +mapper_update_outputs (MetaInputMapper *mapper) +{ + MetaMapperOutputInfo *output; + GList *logical_monitors, *l; + GHashTableIter iter; + + g_hash_table_iter_init (&iter, mapper->output_devices); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &output)) + { + mapper_output_info_clear_inputs (output); + g_hash_table_iter_remove (&iter); + } + + logical_monitors = + meta_monitor_manager_get_logical_monitors (mapper->monitor_manager); + + for (l = logical_monitors; l; l = l->next) + { + MetaLogicalMonitor *logical_monitor = l->data; + MetaMapperOutputInfo *info; + + info = mapper_output_info_new (logical_monitor); + g_hash_table_insert (mapper->output_devices, logical_monitor, info); + } + + mapper_recalculate_candidates (mapper); +} + +static void +input_mapper_monitors_changed_cb (MetaMonitorManager *monitor_manager, + MetaInputMapper *mapper) +{ + mapper_update_outputs (mapper); +} + +static void +input_mapper_device_removed_cb (ClutterDeviceManager *device_manager, + ClutterInputDevice *device, + MetaInputMapper *mapper) +{ + meta_input_mapper_remove_device (mapper, device); +} + +static void +meta_input_mapper_finalize (GObject *object) +{ + MetaInputMapper *mapper = META_INPUT_MAPPER (object); + + g_signal_handlers_disconnect_by_func (mapper->monitor_manager, + input_mapper_monitors_changed_cb, + mapper); + g_signal_handlers_disconnect_by_func (mapper->input_device_manager, + input_mapper_device_removed_cb, + mapper); + + g_hash_table_unref (mapper->input_devices); + g_hash_table_unref (mapper->output_devices); + + G_OBJECT_CLASS (meta_input_mapper_parent_class)->finalize (object); +} + +static void +meta_input_mapper_constructed (GObject *object) +{ + MetaInputMapper *mapper = META_INPUT_MAPPER (object); + MetaBackend *backend; + + G_OBJECT_CLASS (meta_input_mapper_parent_class)->constructed (object); + + mapper->input_device_manager = clutter_device_manager_get_default (); + g_signal_connect (mapper->input_device_manager, "device-removed", + G_CALLBACK (input_mapper_device_removed_cb), mapper); + + backend = meta_get_backend (); + mapper->monitor_manager = meta_backend_get_monitor_manager (backend); + g_signal_connect (mapper->monitor_manager, "monitors-changed-internal", + G_CALLBACK (input_mapper_monitors_changed_cb), mapper); + + mapper_update_outputs (mapper); +} + +static void +meta_input_mapper_class_init (MetaInputMapperClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = meta_input_mapper_constructed; + object_class->finalize = meta_input_mapper_finalize; + + signals[DEVICE_MAPPED] = + g_signal_new ("device-mapped", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 3, + CLUTTER_TYPE_INPUT_DEVICE, + G_TYPE_POINTER, G_TYPE_POINTER); +} + +static void +meta_input_mapper_init (MetaInputMapper *mapper) +{ + mapper->input_devices = + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) mapper_input_info_free); + mapper->output_devices = + g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) mapper_output_info_free); +} + +MetaInputMapper * +meta_input_mapper_new (void) +{ + return g_object_new (META_TYPE_INPUT_MAPPER, NULL); +} + +void +meta_input_mapper_add_device (MetaInputMapper *mapper, + ClutterInputDevice *device, + gboolean builtin) +{ + MetaMapperInputInfo *info; + + g_return_if_fail (mapper != NULL); + g_return_if_fail (device != NULL); + + if (g_hash_table_contains (mapper->input_devices, device)) + return; + + info = mapper_input_info_new (device, mapper, builtin); + g_hash_table_insert (mapper->input_devices, device, info); + mapper_recalculate_input (mapper, info); +} + +void +meta_input_mapper_remove_device (MetaInputMapper *mapper, + ClutterInputDevice *device) +{ + MetaMapperInputInfo *input; + + g_return_if_fail (mapper != NULL); + g_return_if_fail (device != NULL); + + input = g_hash_table_lookup (mapper->input_devices, device); + + if (input) + { + if (input->output) + mapper_output_info_remove_input (input->output, input); + g_hash_table_remove (mapper->input_devices, device); + } +} diff --git a/src/meson.build b/src/meson.build index 13bea490b35..963d70d4857 100644 --- a/src/meson.build +++ b/src/meson.build @@ -184,6 +184,8 @@ mutter_sources = [ 'backends/meta-idle-monitor-dbus.c', 'backends/meta-idle-monitor-dbus.h', 'backends/meta-idle-monitor-private.h', + 'backends/meta-input-mapper.c', + 'backends/meta-input-mapper-private.h', 'backends/meta-input-settings.c', 'backends/meta-input-settings-private.h', 'backends/meta-logical-monitor.c', -- GitLab From 87858a4e01d9782eff110a8ad2516af7a2de69b0 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 20 Apr 2018 16:49:54 +0200 Subject: [PATCH 3/6] backends: Delegate on MetaInputMapper for unmapped display devices If a display device (touchscreen, tablet with libwacom integration flags) does not receive a monitor through settings. Delegate on the MetaInputMapper so it receives a mapping through heuristics. --- src/backends/meta-input-settings.c | 90 ++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c index 1d1a9ae35fa..33f90b76959 100644 --- a/src/backends/meta-input-settings.c +++ b/src/backends/meta-input-settings.c @@ -32,6 +32,7 @@ #include "backends/meta-backend-private.h" #include "backends/meta-input-settings-private.h" +#include "backends/meta-input-mapper-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "core/display-private.h" @@ -96,6 +97,9 @@ struct _MetaInputSettingsPrivate guint number; gdouble value; } last_pad_action_info; + + /* For absolute devices with no mapping in settings */ + MetaInputMapper *input_mapper; }; typedef void (*ConfigBoolFunc) (MetaInputSettings *input_settings, @@ -157,6 +161,7 @@ meta_input_settings_dispose (GObject *object) g_clear_object (&priv->keyboard_settings); g_clear_object (&priv->gsd_settings); g_clear_object (&priv->a11y_settings); + g_clear_object (&priv->input_mapper); g_clear_pointer (&priv->mappable_devices, g_hash_table_unref); g_clear_pointer (&priv->current_tools, g_hash_table_unref); @@ -846,6 +851,42 @@ out: g_strfreev (edid); } +static gboolean +meta_input_settings_delegate_on_mapper (MetaInputSettings *input_settings, + ClutterInputDevice *device) +{ + MetaInputSettingsPrivate *priv; + gboolean builtin = FALSE; + + priv = meta_input_settings_get_instance_private (input_settings); + +#ifdef HAVE_LIBWACOM + if (clutter_input_device_get_device_type (device) != CLUTTER_TOUCHSCREEN_DEVICE) + { + WacomDevice *wacom_device; + WacomIntegrationFlags flags = 0; + + wacom_device = + meta_input_settings_get_tablet_wacom_device (input_settings, + device); + + if (wacom_device) + { + flags = libwacom_get_integration_flags (wacom_device); + + if ((flags & (WACOM_DEVICE_INTEGRATED_SYSTEM | + WACOM_DEVICE_INTEGRATED_DISPLAY)) == 0) + return FALSE; + + builtin = (flags & WACOM_DEVICE_INTEGRATED_SYSTEM) != 0; + } + } +#endif + + meta_input_mapper_add_device (priv->input_mapper, device, builtin); + return TRUE; +} + static void update_tablet_keep_aspect (MetaInputSettings *input_settings, GSettings *settings, @@ -915,12 +956,21 @@ update_device_display (MetaInputSettings *input_settings, if (clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE || clutter_input_device_get_mapping_mode (device) == CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE) - meta_input_settings_find_monitor (input_settings, settings, device, - &monitor, &logical_monitor); - - if (monitor) - meta_monitor_manager_get_monitor_matrix (priv->monitor_manager, - monitor, logical_monitor, matrix); + { + meta_input_settings_find_monitor (input_settings, settings, device, + &monitor, &logical_monitor); + if (monitor) + { + meta_input_mapper_remove_device (priv->input_mapper, device); + meta_monitor_manager_get_monitor_matrix (priv->monitor_manager, + monitor, logical_monitor, matrix); + } + else + { + if (meta_input_settings_delegate_on_mapper (input_settings, device)) + return; + } + } input_settings_class->set_matrix (input_settings, device, matrix); @@ -1371,6 +1421,29 @@ monitors_changed_cb (MetaMonitorManager *monitor_manager, update_device_display (input_settings, info->settings, device); } +static void +input_mapper_device_mapped_cb (MetaInputMapper *mapper, + ClutterInputDevice *device, + MetaLogicalMonitor *logical_monitor, + MetaMonitor *monitor, + MetaInputSettings *input_settings) +{ + MetaInputSettingsPrivate *priv; + float matrix[6] = { 1, 0, 0, 0, 1, 0 }; + + priv = meta_input_settings_get_instance_private (input_settings); + + if (monitor && logical_monitor) + { + meta_monitor_manager_get_monitor_matrix (priv->monitor_manager, + monitor, logical_monitor, + matrix); + } + + META_INPUT_SETTINGS_GET_CLASS (input_settings)->set_matrix (input_settings, + device, matrix); +} + static void device_mapping_info_free (DeviceMappingInfo *info) { @@ -1593,6 +1666,7 @@ meta_input_settings_device_removed (ClutterDeviceManager *device_manager, MetaInputSettingsPrivate *priv; priv = meta_input_settings_get_instance_private (input_settings); + meta_input_mapper_remove_device (priv->input_mapper, device); g_hash_table_remove (priv->mappable_devices, device); g_hash_table_remove (priv->current_tools, device); @@ -1772,6 +1846,10 @@ meta_input_settings_init (MetaInputSettings *settings) #endif priv->two_finger_devices = g_hash_table_new (NULL, NULL); + + priv->input_mapper = meta_input_mapper_new (); + g_signal_connect (priv->input_mapper, "device-mapped", + G_CALLBACK (input_mapper_device_mapped_cb), settings); } GSettings * -- GitLab From faf89ff35fc5df123a303cc0ca99281b0dfa2115 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 20 Apr 2018 17:47:21 +0200 Subject: [PATCH 4/6] backends: Add MetaInputMapper method to lookup devices from outputs So we may know the device of a certain ClutterInputDeviceType that is mapped to the given output. --- src/backends/meta-input-mapper-private.h | 6 ++++++ src/backends/meta-input-mapper.c | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/backends/meta-input-mapper-private.h b/src/backends/meta-input-mapper-private.h index 53bc66b06d6..34314577f78 100644 --- a/src/backends/meta-input-mapper-private.h +++ b/src/backends/meta-input-mapper-private.h @@ -23,6 +23,7 @@ #define META_INPUT_MAPPER_H #include +#include "meta-monitor-manager-private.h" #define META_TYPE_INPUT_MAPPER (meta_input_mapper_get_type ()) @@ -37,4 +38,9 @@ void meta_input_mapper_add_device (MetaInputMapper *mapper, void meta_input_mapper_remove_device (MetaInputMapper *mapper, ClutterInputDevice *device); +ClutterInputDevice * +meta_input_mapper_get_logical_monitor_device (MetaInputMapper *mapper, + MetaLogicalMonitor *logical_monitor, + ClutterInputDeviceType device_type); + #endif /* META_INPUT_MAPPER_H */ diff --git a/src/backends/meta-input-mapper.c b/src/backends/meta-input-mapper.c index 026dacb5892..013c2a4271a 100644 --- a/src/backends/meta-input-mapper.c +++ b/src/backends/meta-input-mapper.c @@ -615,3 +615,26 @@ meta_input_mapper_remove_device (MetaInputMapper *mapper, g_hash_table_remove (mapper->input_devices, device); } } + +ClutterInputDevice * +meta_input_mapper_get_logical_monitor_device (MetaInputMapper *mapper, + MetaLogicalMonitor *logical_monitor, + ClutterInputDeviceType device_type) +{ + MetaMapperOutputInfo *output; + GList *l; + + output = g_hash_table_lookup (mapper->output_devices, logical_monitor); + if (!output) + return NULL; + + for (l = output->input_devices; l; l = l->next) + { + MetaMapperInputInfo *input = l->data; + + if (clutter_input_device_get_device_type (input->device) == device_type) + return input->device; + } + + return NULL; +} -- GitLab From d30301c00a6e884d1ccff4765d93c17e22ee2fc3 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 20 Apr 2018 17:48:25 +0200 Subject: [PATCH 5/6] backends: Turn builtin touchscreen on/off together with DPMS This takes over gsd-power code, that would disable touchscreens on DPMS off. https://bugzilla.gnome.org/show_bug.cgi?id=742598 https://gitlab.gnome.org/GNOME/gnome-settings-daemon/issues/29 --- src/backends/meta-input-settings.c | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c index 33f90b76959..1ab04f84b5b 100644 --- a/src/backends/meta-input-settings.c +++ b/src/backends/meta-input-settings.c @@ -1755,6 +1755,38 @@ check_mappable_devices (MetaInputSettings *input_settings) } } +static void +power_save_mode_changed_cb (MetaMonitorManager *manager, + GParamSpec *pspec, + gpointer user_data) +{ + MetaInputSettingsPrivate *priv; + ClutterInputDevice *device; + MetaLogicalMonitor *logical_monitor; + MetaMonitor *builtin; + gboolean on; + + on = (manager->power_save_mode == META_POWER_SAVE_ON); + priv = meta_input_settings_get_instance_private (user_data); + + builtin = meta_monitor_manager_get_laptop_panel (manager); + if (!builtin) + return; + + logical_monitor = meta_monitor_get_logical_monitor (builtin); + if (!logical_monitor) + return; + + device = + meta_input_mapper_get_logical_monitor_device (priv->input_mapper, + logical_monitor, + CLUTTER_TOUCHSCREEN_DEVICE); + if (!device) + return; + + clutter_input_device_set_enabled (device, on); +} + static void meta_input_settings_constructed (GObject *object) { @@ -1835,6 +1867,8 @@ meta_input_settings_init (MetaInputSettings *settings) priv->monitor_manager = g_object_ref (meta_monitor_manager_get ()); g_signal_connect (priv->monitor_manager, "monitors-changed-internal", G_CALLBACK (monitors_changed_cb), settings); + g_signal_connect (priv->monitor_manager, "notify::power-save-mode", + G_CALLBACK (power_save_mode_changed_cb), settings); #ifdef HAVE_LIBWACOM priv->wacom_db = libwacom_database_new (); -- GitLab From ffca9b999dfd96de66144cccb9cc450a4e382232 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 30 Jul 2018 21:16:24 +0200 Subject: [PATCH 6/6] backends: Update to new "output" setting for tablets/touchscreens --- src/backends/meta-input-settings.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c index 1ab04f84b5b..365577ec0d5 100644 --- a/src/backends/meta-input-settings.c +++ b/src/backends/meta-input-settings.c @@ -814,7 +814,7 @@ meta_input_settings_find_monitor (MetaInputSettings *input_settings, gchar **edid; priv = meta_input_settings_get_instance_private (input_settings); - edid = g_settings_get_strv (settings, "display"); + edid = g_settings_get_strv (settings, "output"); n_values = g_strv_length (edid); if (n_values != 3) @@ -1157,7 +1157,7 @@ mapped_device_changed_cb (GSettings *settings, const gchar *key, DeviceMappingInfo *info) { - if (strcmp (key, "display") == 0) + if (strcmp (key, "output") == 0) update_device_display (info->input_settings, settings, info->device); else if (strcmp (key, "mapping") == 0) update_tablet_mapping (info->input_settings, settings, info->device); @@ -2071,7 +2071,7 @@ meta_input_settings_cycle_tablet_output (MetaInputSettings *input_settings, edid[1] = ""; edid[2] = ""; } - g_settings_set_strv (info->settings, "display", edid); + g_settings_set_strv (info->settings, "output", edid); meta_display_show_tablet_mapping_notification (meta_get_display (), device, pretty_name); -- GitLab