/* -*- 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"
#ifdef HAVE_LIBGUDEV
#include
#endif
#include "backends/meta-input-device-private.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;
typedef struct _DeviceMatch DeviceMatch;
struct _MetaInputMapper
{
GObject parent_instance;
MetaMonitorManager *monitor_manager;
ClutterSeat *seat;
GHashTable *input_devices; /* ClutterInputDevice -> MetaMapperInputInfo */
GHashTable *output_devices; /* MetaLogicalMonitor -> MetaMapperOutputInfo */
#ifdef HAVE_LIBGUDEV
GUdevClient *udev_client;
#endif
};
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_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;
MetaInputCapabilityFlags attached_caps;
};
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);
G_DEFINE_TYPE (MetaInputMapper, meta_input_mapper, G_TYPE_OBJECT)
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 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)
{
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);
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
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 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
{
int i;
g_auto (GStrv) split = NULL;
split = g_strsplit (meta_monitor_get_product (monitor), " ", -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
input_device_get_physical_size (MetaInputMapper *mapper,
ClutterInputDevice *device,
double *width,
double *height)
{
#ifdef HAVE_LIBGUDEV
g_autoptr (GUdevDevice) udev_device = NULL;
const char *node;
node = clutter_input_device_get_device_node (device);
if (!node)
return FALSE;
udev_device = g_udev_client_query_by_device_file (mapper->udev_client, node);
if (udev_device &&
g_udev_device_has_property (udev_device, "ID_INPUT_WIDTH_MM"))
{
*width = g_udev_device_get_property_as_double (udev_device,
"ID_INPUT_WIDTH_MM");
*height = g_udev_device_get_property_as_double (udev_device,
"ID_INPUT_HEIGHT_MM");
return TRUE;
}
#endif
return FALSE;
}
static gboolean
match_size (MetaMapperInputInfo *input,
MetaMonitor *monitor)
{
double w_diff, h_diff;
int o_width, o_height;
double i_width, i_height;
if (!input_device_get_physical_size (input->mapper, 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
match_config (MetaMapperInputInfo *info,
MetaMonitor *monitor)
{
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 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);
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;
#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
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 (match_edid (input, l->data, &edid_match))
match.score |= 1 << edid_match;
if (integrated && match_size (input, l->data))
match.score |= 1 << META_MATCH_SIZE;
if (builtin && match_builtin (mapper, l->data))
match.score |= 1 << META_MATCH_IS_BUILTIN;
if (match_config (input, l->data))
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', capabilities %x", i,
clutter_input_device_get_device_name (info->input->device),
mapper_input_info_get_caps (info->input));
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;
if (output->attached_caps & mapper_input_info_get_caps (info->input))
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,
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_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->seat,
input_mapper_device_removed_cb,
mapper);
g_hash_table_unref (mapper->input_devices);
g_hash_table_unref (mapper->output_devices);
#ifdef HAVE_LIBGUDEV
g_clear_object (&mapper->udev_client);
#endif
G_OBJECT_CLASS (meta_input_mapper_parent_class)->finalize (object);
}
static void
meta_input_mapper_constructed (GObject *object)
{
#ifdef HAVE_LIBGUDEV
const char *udev_subsystems[] = { "input", NULL };
#endif
MetaInputMapper *mapper = META_INPUT_MAPPER (object);
MetaBackend *backend;
G_OBJECT_CLASS (meta_input_mapper_parent_class)->constructed (object);
#ifdef HAVE_LIBGUDEV
mapper->udev_client = g_udev_client_new (udev_subsystems);
#endif
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);
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);
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;
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
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)
{
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;
}