mutter/src/backends/meta-input-mapper.c
Carlos Garnacho bf89ada2c1 backends: Disambiguate output mapped to tablet with connector name
In some circumstances, we may end up with outputs with the same
vendor/product/serial, in which case we have a hard time finding the
right one to map tablets to, since configuration only has these 3
pieces of data.

Add the handling of a 4th argument containing the output name
based on the connector (e.g. HDMI-1), so that it can be used to
disambiguate the output if necessary.

This only kicks in if there actually are multiple outputs with the
same EDID data. A goal of the configuration as it was stored was to
remain useful if the user changed how the device is physically
connected to the computer, this remains true for the vast majority
of users having a single thing of each.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3556>
2024-02-06 10:03:48 +00:00

1131 lines
32 KiB
C

/* -*- 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 <http://www.gnu.org/licenses/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#include "config.h"
#include "backends/meta-input-device-private.h"
#include "backends/meta-input-mapper-private.h"
#include "backends/meta-monitor-manager-private.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-backend-private.h"
#include "meta-dbus-input-mapping.h"
#define META_INPUT_MAPPING_DBUS_SERVICE "org.gnome.Mutter.InputMapping"
#define META_INPUT_MAPPING_DBUS_PATH "/org/gnome/Mutter/InputMapping"
#define MAX_SIZE_MATCH_DIFF 0.05
enum
{
PROP_0,
PROP_BACKEND,
N_PROPS
};
static GParamSpec *obj_props[N_PROPS];
typedef struct _MetaMapperInputInfo MetaMapperInputInfo;
typedef struct _MetaMapperOutputInfo MetaMapperOutputInfo;
typedef struct _MappingHelper MappingHelper;
typedef struct _DeviceCandidates DeviceCandidates;
typedef struct _DeviceMatch DeviceMatch;
struct _MetaInputMapper
{
MetaDBusInputMappingSkeleton parent_instance;
MetaBackend *backend;
MetaMonitorManager *monitor_manager;
ClutterSeat *seat;
GHashTable *input_devices; /* ClutterInputDevice -> MetaMapperInputInfo */
GHashTable *output_devices; /* MetaLogicalMonitor -> MetaMapperOutputInfo */
guint dbus_name_id;
};
typedef enum
{
META_MATCH_EDID_VENDOR, /* EDID vendor match, eg. "WAC" for Wacom */
META_MATCH_EDID_PARTIAL, /* Partial EDID model match, eg. "Cintiq" */
META_MATCH_EDID_FULL, /* Full EDID model match, eg. "Cintiq 12WX" */
META_MATCH_SIZE, /* Size from input device and output match */
META_MATCH_IS_BUILTIN, /* Output is builtin, applies mainly to system-integrated devices */
META_MATCH_CONFIG, /* Specified by config */
N_OUTPUT_MATCHES
} MetaOutputMatchType;
struct _MetaMapperInputInfo
{
ClutterInputDevice *device;
MetaInputMapper *mapper;
MetaMapperOutputInfo *output;
GSettings *settings;
guint builtin : 1;
};
struct _MetaMapperOutputInfo
{
MetaLogicalMonitor *logical_monitor;
GList *input_devices;
};
struct _MappingHelper
{
GArray *device_maps;
};
struct _DeviceMatch
{
MetaMonitor *monitor;
uint32_t score;
};
struct _DeviceCandidates
{
MetaMapperInputInfo *input;
GArray *matches; /* Array of DeviceMatch */
int best;
};
enum
{
DEVICE_MAPPED,
DEVICE_ENABLED,
DEVICE_ASPECT_RATIO,
N_SIGNALS
};
static guint signals[N_SIGNALS] = { 0, };
static void mapper_output_info_remove_input (MetaMapperOutputInfo *output,
MetaMapperInputInfo *input);
static void mapper_recalculate_input (MetaInputMapper *mapper,
MetaMapperInputInfo *input);
static void meta_input_mapping_init_iface (MetaDBusInputMappingIface *iface);
G_DEFINE_TYPE_WITH_CODE (MetaInputMapper, meta_input_mapper,
META_DBUS_TYPE_INPUT_MAPPING_SKELETON,
G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_INPUT_MAPPING,
meta_input_mapping_init_iface))
static GSettings *
get_device_settings (ClutterInputDevice *device)
{
const char *group, *schema, *vendor, *product;
ClutterInputDeviceType type;
GSettings *settings;
char *path;
type = clutter_input_device_get_device_type (device);
if (type == CLUTTER_TOUCHSCREEN_DEVICE)
{
group = "touchscreens";
schema = "org.gnome.desktop.peripherals.touchscreen";
}
else if (type == CLUTTER_TABLET_DEVICE ||
type == CLUTTER_PEN_DEVICE ||
type == CLUTTER_ERASER_DEVICE ||
type == CLUTTER_CURSOR_DEVICE ||
type == CLUTTER_PAD_DEVICE)
{
group = "tablets";
schema = "org.gnome.desktop.peripherals.tablet";
}
else
{
return NULL;
}
vendor = clutter_input_device_get_vendor_id (device);
product = clutter_input_device_get_product_id (device);
path = g_strdup_printf ("/org/gnome/desktop/peripherals/%s/%s:%s/",
group, vendor, product);
settings = g_settings_new_with_path (schema, path);
g_free (path);
return settings;
}
static void
settings_output_changed_cb (GSettings *settings,
const char *key,
MetaMapperInputInfo *info)
{
if (info->output != NULL)
mapper_output_info_remove_input (info->output, info);
mapper_recalculate_input (info->mapper, info);
}
static MetaMapperInputInfo *
mapper_input_info_new (ClutterInputDevice *device,
MetaInputMapper *mapper)
{
MetaMapperInputInfo *info;
info = g_new0 (MetaMapperInputInfo, 1);
info->mapper = mapper;
info->device = device;
info->settings = get_device_settings (device);
g_signal_connect (info->settings, "changed::output",
G_CALLBACK (settings_output_changed_cb), info);
return info;
}
static void
mapper_input_info_free (MetaMapperInputInfo *info)
{
g_signal_handlers_disconnect_by_func (info->settings, settings_output_changed_cb, info);
g_object_unref (info->settings);
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 void
mapper_input_info_set_output (MetaMapperInputInfo *input,
MetaMapperOutputInfo *output,
MetaMonitor *monitor)
{
MetaInputMapper *mapper = input->mapper;
float matrix[6] = { 1, 0, 0, 0, 1, 0 };
double aspect_ratio;
int width, height;
if (input->output == output)
return;
input->output = output;
/* These devices don't require emission about mapping/ratio */
if (clutter_input_device_get_device_type (input->device) == CLUTTER_PAD_DEVICE)
return;
if (output && monitor)
{
meta_monitor_manager_get_monitor_matrix (mapper->monitor_manager,
monitor,
output->logical_monitor,
matrix);
meta_monitor_get_current_resolution (monitor, &width, &height);
}
else
{
meta_monitor_manager_get_screen_size (mapper->monitor_manager,
&width, &height);
}
aspect_ratio = (double) width / height;
g_signal_emit (input->mapper, signals[DEVICE_MAPPED], 0,
input->device, matrix);
g_signal_emit (input->mapper, signals[DEVICE_ASPECT_RATIO], 0,
input->device, aspect_ratio);
}
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);
mapper_input_info_set_output (input, output, monitor);
}
static void
mapper_output_info_remove_input (MetaMapperOutputInfo *output,
MetaMapperInputInfo *input)
{
g_assert (input->output == output);
output->input_devices = g_list_remove (output->input_devices, input);
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);
}
}
static void
clear_candidates (DeviceCandidates *candidates)
{
g_clear_pointer (&candidates->matches, g_array_unref);
}
static void
mapping_helper_init (MappingHelper *helper)
{
helper->device_maps = g_array_new (FALSE, FALSE, sizeof (DeviceCandidates));
g_array_set_clear_func (helper->device_maps,
(GDestroyNotify) clear_candidates);
}
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 char *dev_name;
const char *vendor;
const char *serial;
dev_name = clutter_input_device_get_device_name (input->device);
vendor = meta_monitor_get_vendor (monitor);
if (!vendor || strcasestr (dev_name, vendor) == NULL)
return FALSE;
*match_type = META_MATCH_EDID_VENDOR;
serial = meta_monitor_get_serial (monitor);
if (!serial || strcasestr (dev_name, serial) != NULL)
{
*match_type = META_MATCH_EDID_FULL;
}
else
{
const char *product;
product = meta_monitor_get_product (monitor);
if (product)
{
g_auto (GStrv) split = NULL;
int i;
split = g_strsplit (product, " ", -1);
for (i = 0; split[i]; i++)
{
if (strcasestr (dev_name, split[i]) != NULL)
{
*match_type = META_MATCH_EDID_PARTIAL;
break;
}
}
}
}
return TRUE;
}
static gboolean
match_size (MetaMapperInputInfo *input,
MetaMonitor *monitor)
{
double w_diff, h_diff;
int o_width, o_height;
unsigned int i_width, i_height;
if (!clutter_input_device_get_dimensions (input->device, &i_width, &i_height))
return FALSE;
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));
return w_diff < MAX_SIZE_MATCH_DIFF && h_diff < MAX_SIZE_MATCH_DIFF;
}
static gboolean
match_builtin (MetaInputMapper *mapper,
MetaMonitor *monitor)
{
return monitor == meta_monitor_manager_get_laptop_panel (mapper->monitor_manager);
}
static gboolean
monitor_has_twin (MetaMonitor *monitor,
GList *monitors)
{
GList *l;
for (l = monitors; l; l = l->next)
{
if (l->data == monitor)
continue;
if (g_strcmp0 (meta_monitor_get_vendor (monitor),
meta_monitor_get_vendor (l->data)) == 0 &&
g_strcmp0 (meta_monitor_get_product (monitor),
meta_monitor_get_product (l->data)) == 0 &&
g_strcmp0 (meta_monitor_get_serial (monitor),
meta_monitor_get_serial (l->data)) == 0)
return TRUE;
}
return FALSE;
}
static gboolean
match_config (MetaMapperInputInfo *info,
MetaMonitor *monitor,
GList *monitors)
{
gboolean match = FALSE;
char **edid;
guint n_values;
edid = g_settings_get_strv (info->settings, "output");
n_values = g_strv_length (edid);
if (n_values < 3)
{
g_warning ("EDID configuration for device '%s' "
"is incorrect, must have at least 3 values",
clutter_input_device_get_device_name (info->device));
goto out;
}
if (!*edid[0] && !*edid[1] && !*edid[2])
goto out;
match = (g_strcmp0 (meta_monitor_get_vendor (monitor), edid[0]) == 0 &&
g_strcmp0 (meta_monitor_get_product (monitor), edid[1]) == 0 &&
g_strcmp0 (meta_monitor_get_serial (monitor), edid[2]) == 0);
if (match && n_values >= 4 && monitor_has_twin (monitor, monitors))
{
/* The 4th value if set contains the ID (e.g. HDMI-1), use it
* for disambiguation if multiple monitors with the same
* EDID data are found.
*/
MetaOutput *output;
output = meta_monitor_get_main_output (monitor);
match = g_strcmp0 (meta_output_get_name (output), edid[3]) == 0;
}
out:
g_strfreev (edid);
return match;
}
static int
sort_by_score (DeviceMatch *match1,
DeviceMatch *match2)
{
return (int) match2->score - match1->score;
}
static void
guess_candidates (MetaInputMapper *mapper,
MetaMapperInputInfo *input,
DeviceCandidates *info)
{
GList *monitors, *l;
gboolean builtin = FALSE;
gboolean integrated = TRUE;
gboolean automatic;
g_autoptr (GVariant) user_value = NULL;
#ifdef HAVE_LIBWACOM
if (clutter_input_device_get_device_type (input->device) != CLUTTER_TOUCHSCREEN_DEVICE)
{
WacomDevice *wacom_device;
WacomIntegrationFlags flags = 0;
wacom_device =
meta_input_device_get_wacom_device (META_INPUT_DEVICE (input->device));
if (wacom_device)
{
flags = libwacom_get_integration_flags (wacom_device);
integrated = (flags & (WACOM_DEVICE_INTEGRATED_SYSTEM |
WACOM_DEVICE_INTEGRATED_DISPLAY)) != 0;
builtin = (flags & WACOM_DEVICE_INTEGRATED_SYSTEM) != 0;
}
}
#endif
user_value = g_settings_get_user_value (input->settings, "output");
automatic = user_value == NULL;
monitors = meta_monitor_manager_get_monitors (mapper->monitor_manager);
for (l = monitors; l; l = l->next)
{
MetaOutputMatchType edid_match;
DeviceMatch match = { l->data, 0 };
g_assert (META_IS_MONITOR (l->data));
if (automatic && integrated && match_edid (input, l->data, &edid_match))
match.score |= 1 << edid_match;
if (automatic && integrated && match_size (input, l->data))
match.score |= 1 << META_MATCH_SIZE;
if (automatic && builtin && match_builtin (mapper, l->data))
match.score |= 1 << META_MATCH_IS_BUILTIN;
if (!automatic && match_config (input, l->data, monitors))
match.score |= 1 << META_MATCH_CONFIG;
if (match.score > 0)
g_array_append_val (info->matches, match);
}
if (info->matches->len == 0)
{
if (clutter_input_device_get_device_type (input->device) ==
CLUTTER_TOUCHSCREEN_DEVICE)
{
DeviceMatch match = { 0 };
match.monitor =
meta_monitor_manager_get_laptop_panel (mapper->monitor_manager);
if (match.monitor != NULL)
g_array_append_val (info->matches, match);
}
info->best = 0;
}
else
{
DeviceMatch *best;
g_array_sort (info->matches, (GCompareFunc) sort_by_score);
best = &g_array_index (info->matches, DeviceMatch, 0);
info->best = best->score;
}
}
static void
mapping_helper_add (MappingHelper *helper,
MetaMapperInputInfo *input,
MetaInputMapper *mapper)
{
DeviceCandidates info = { 0, };
guint i, pos = 0;
info.input = input;
info.matches = g_array_new (FALSE, TRUE, sizeof (DeviceMatch));
guess_candidates (mapper, input, &info);
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, j;
/* Now, decide which input claims which output */
for (i = 0; i < helper->device_maps->len; i++)
{
DeviceCandidates *info;
info = &g_array_index (helper->device_maps, DeviceCandidates, i);
g_debug ("Applying mapping %d to input device '%s', type %d", i,
clutter_input_device_get_device_name (info->input->device),
clutter_input_device_get_device_type (info->input->device));
for (j = 0; j < info->matches->len; j++)
{
MetaLogicalMonitor *logical_monitor;
MetaMapperOutputInfo *output;
MetaMonitor *monitor;
DeviceMatch *match;
match = &g_array_index (info->matches, DeviceMatch, j);
g_debug ("Output candidate '%s', score %x",
meta_monitor_get_display_name (match->monitor),
match->score);
monitor = match->monitor;
logical_monitor = meta_monitor_get_logical_monitor (monitor);
output = g_hash_table_lookup (mapper->output_devices,
logical_monitor);
if (!output)
continue;
g_debug ("Matched input '%s' with output '%s'",
clutter_input_device_get_device_name (info->input->device),
meta_monitor_get_display_name (match->monitor));
mapper_output_info_add_input (output, info->input, monitor);
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_power_save_mode_changed_cb (MetaMonitorManager *monitor_manager,
MetaPowerSaveChangeReason reason,
MetaInputMapper *mapper)
{
ClutterInputDevice *device;
MetaLogicalMonitor *logical_monitor;
MetaMonitor *builtin;
MetaPowerSave power_save_mode;
gboolean on;
power_save_mode =
meta_monitor_manager_get_power_save_mode (mapper->monitor_manager);
on = power_save_mode == META_POWER_SAVE_ON;
builtin = meta_monitor_manager_get_laptop_panel (monitor_manager);
if (!builtin)
return;
logical_monitor = meta_monitor_get_logical_monitor (builtin);
if (!logical_monitor)
return;
device =
meta_input_mapper_get_logical_monitor_device (mapper,
logical_monitor,
CLUTTER_TOUCHSCREEN_DEVICE);
if (!device)
return;
g_signal_emit (mapper, signals[DEVICE_ENABLED], 0, device, on);
}
static void
input_mapper_device_removed_cb (ClutterSeat *seat,
ClutterInputDevice *device,
MetaInputMapper *mapper)
{
meta_input_mapper_remove_device (mapper, device);
}
static void
meta_input_mapper_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaInputMapper *mapper = META_INPUT_MAPPER (object);
switch (prop_id)
{
case PROP_BACKEND:
mapper->backend = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_input_mapper_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaInputMapper *mapper = META_INPUT_MAPPER (object);
switch (prop_id)
{
case PROP_BACKEND:
g_value_set_object (value, mapper->backend);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_input_mapper_finalize (GObject *object)
{
MetaInputMapper *mapper = META_INPUT_MAPPER (object);
g_clear_handle_id (&mapper->dbus_name_id, g_bus_unown_name);
g_signal_handlers_disconnect_by_func (mapper->monitor_manager,
input_mapper_monitors_changed_cb,
mapper);
g_signal_handlers_disconnect_by_func (mapper->seat,
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);
G_OBJECT_CLASS (meta_input_mapper_parent_class)->constructed (object);
mapper->seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
g_signal_connect (mapper->seat, "device-removed",
G_CALLBACK (input_mapper_device_removed_cb), mapper);
mapper->monitor_manager = meta_backend_get_monitor_manager (mapper->backend);
g_signal_connect (mapper->monitor_manager, "monitors-changed-internal",
G_CALLBACK (input_mapper_monitors_changed_cb), mapper);
g_signal_connect (mapper->monitor_manager, "power-save-mode-changed",
G_CALLBACK (input_mapper_power_save_mode_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;
object_class->set_property = meta_input_mapper_set_property;
object_class->get_property = meta_input_mapper_get_property;
obj_props[PROP_BACKEND] =
g_param_spec_object ("backend", NULL, NULL,
META_TYPE_BACKEND,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, obj_props);
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, 2,
CLUTTER_TYPE_INPUT_DEVICE,
G_TYPE_POINTER);
signals[DEVICE_ENABLED] =
g_signal_new ("device-enabled",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2,
CLUTTER_TYPE_INPUT_DEVICE,
G_TYPE_BOOLEAN);
signals[DEVICE_ASPECT_RATIO] =
g_signal_new ("device-aspect-ratio",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2,
CLUTTER_TYPE_INPUT_DEVICE,
G_TYPE_DOUBLE);
}
static void
on_bus_acquired (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
MetaRemoteDesktop *remote_desktop = user_data;
GDBusInterfaceSkeleton *interface_skeleton =
G_DBUS_INTERFACE_SKELETON (remote_desktop);
g_autoptr (GError) error = NULL;
if (!g_dbus_interface_skeleton_export (interface_skeleton,
connection,
META_INPUT_MAPPING_DBUS_PATH,
&error))
g_warning ("Failed to export input mapping object: %s", error->message);
}
static void
on_name_acquired (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
g_info ("Acquired name %s", name);
}
static void
on_name_lost (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
g_info ("Lost or failed to acquire name %s", name);
}
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);
mapper->dbus_name_id =
g_bus_own_name (G_BUS_TYPE_SESSION,
META_INPUT_MAPPING_DBUS_SERVICE,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_bus_acquired,
on_name_acquired,
on_name_lost,
mapper,
NULL);
}
static gboolean
handle_get_device_mapping (MetaDBusInputMapping *skeleton,
GDBusMethodInvocation *invocation,
const char *device_node)
{
MetaInputMapper *input_mapper = META_INPUT_MAPPER (skeleton);
ClutterInputDevice *device = NULL;
g_autoptr (GList) devices = NULL;
MetaLogicalMonitor *logical_monitor;
GList *l;
devices = clutter_seat_list_devices (input_mapper->seat);
for (l = devices; l; l = l->next)
{
if (g_strcmp0 (clutter_input_device_get_device_node (l->data),
device_node) == 0)
{
device = l->data;
break;
}
}
if (!device)
{
g_dbus_method_invocation_return_error (invocation,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"Device does not exist");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
logical_monitor =
meta_input_mapper_get_device_logical_monitor (input_mapper, device);
if (logical_monitor)
{
MtkRectangle rect;
rect = meta_logical_monitor_get_layout (logical_monitor);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("((iiii))",
rect.x,
rect.y,
rect.width,
rect.height));
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
else
{
g_dbus_method_invocation_return_error (invocation,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"Device is not mapped to any output");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
}
static void
meta_input_mapping_init_iface (MetaDBusInputMappingIface *iface)
{
iface->handle_get_device_mapping = handle_get_device_mapping;
}
MetaInputMapper *
meta_input_mapper_new (MetaBackend *backend)
{
return g_object_new (META_TYPE_INPUT_MAPPER,
"backend", backend,
NULL);
}
void
meta_input_mapper_add_device (MetaInputMapper *mapper,
ClutterInputDevice *device)
{
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);
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);
}
}
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;
}
static ClutterInputDevice *
find_grouped_pen (ClutterInputDevice *device)
{
GList *l, *devices;
ClutterInputDeviceType device_type;
ClutterInputDevice *pen = NULL;
ClutterSeat *seat;
device_type = clutter_input_device_get_device_type (device);
if (device_type == CLUTTER_TABLET_DEVICE ||
device_type == CLUTTER_PEN_DEVICE)
return device;
seat = clutter_input_device_get_seat (device);
devices = clutter_seat_list_devices (seat);
for (l = devices; l; l = l->next)
{
ClutterInputDevice *other_device = l->data;
device_type = clutter_input_device_get_device_type (other_device);
if ((device_type == CLUTTER_TABLET_DEVICE ||
device_type == CLUTTER_PEN_DEVICE) &&
clutter_input_device_is_grouped (device, other_device))
{
pen = other_device;
break;
}
}
g_list_free (devices);
return pen;
}
MetaLogicalMonitor *
meta_input_mapper_get_device_logical_monitor (MetaInputMapper *mapper,
ClutterInputDevice *device)
{
MetaMapperOutputInfo *output;
MetaLogicalMonitor *logical_monitor;
GHashTableIter iter;
GList *l;
if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE)
{
device = find_grouped_pen (device);
if (!device)
return NULL;
}
g_hash_table_iter_init (&iter, mapper->output_devices);
while (g_hash_table_iter_next (&iter, (gpointer *) &logical_monitor,
(gpointer *) &output))
{
for (l = output->input_devices; l; l = l->next)
{
MetaMapperInputInfo *input = l->data;
if (input->device == device)
return logical_monitor;
}
}
return NULL;
}
GSettings *
meta_input_mapper_get_tablet_settings (MetaInputMapper *mapper,
ClutterInputDevice *device)
{
MetaMapperInputInfo *input;
g_return_val_if_fail (META_IS_INPUT_MAPPER (mapper), NULL);
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL);
input = g_hash_table_lookup (mapper->input_devices, device);
if (!input)
return NULL;
return input->settings;
}