mutter/src/core/meta-pad-action-mapper.c
Jonas Ådahl ccb6a7e84f monitor: Allow vendor/product/serial to return NULL
Same applies to MetaOutput. The reason for this is to make it possible
to more reliably know when there was EDID telling us about these
details. This will be used for colord integration.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2141>
2022-09-01 14:31:40 +00:00

927 lines
29 KiB
C

/*
* Copyright (C) 2014-2020 Red Hat
*
* 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.
*
* Written by:
* Carlos Garnacho <carlosg@gnome.org>
*/
#include "config.h"
#include <glib/gi18n-lib.h>
#include <gsettings-desktop-schemas/gdesktop-enums.h>
#ifdef HAVE_LIBWACOM
#include <libwacom/libwacom.h>
#endif
#include "core/meta-pad-action-mapper.h"
#include "backends/meta-input-device-private.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-monitor.h"
#include "core/display-private.h"
typedef struct _PadMappingInfo PadMappingInfo;
struct _PadMappingInfo
{
ClutterInputDevice *device;
GSettings *settings;
guint *group_modes;
};
typedef enum
{
META_PAD_DIRECTION_NONE = -1,
META_PAD_DIRECTION_UP = 0,
META_PAD_DIRECTION_DOWN,
META_PAD_DIRECTION_CW,
META_PAD_DIRECTION_CCW,
} MetaPadDirection;
struct _MetaPadActionMapper
{
GObject parent_class;
GHashTable *pads;
ClutterSeat *seat;
ClutterVirtualInputDevice *virtual_pad_keyboard;
MetaMonitorManager *monitor_manager;
/* Pad ring/strip emission */
struct {
ClutterInputDevice *pad;
MetaPadActionType action;
guint number;
double value;
} last_pad_action_info;
};
G_DEFINE_TYPE (MetaPadActionMapper, meta_pad_action_mapper, G_TYPE_OBJECT)
static void
meta_pad_action_mapper_finalize (GObject *object)
{
MetaPadActionMapper *mapper = META_PAD_ACTION_MAPPER (object);
g_hash_table_unref (mapper->pads);
g_object_unref (mapper->monitor_manager);
g_clear_object (&mapper->virtual_pad_keyboard);
G_OBJECT_CLASS (meta_pad_action_mapper_parent_class)->finalize (object);
}
static void
meta_pad_action_mapper_class_init (MetaPadActionMapperClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_pad_action_mapper_finalize;
}
static GSettings *
lookup_device_settings (ClutterInputDevice *device)
{
const char *vendor, *product;
GSettings *settings;
char *path;
vendor = clutter_input_device_get_vendor_id (device);
product = clutter_input_device_get_product_id (device);
path = g_strdup_printf ("/org/gnome/desktop/peripherals/tablets/%s:%s/",
vendor, product);
settings = g_settings_new_with_path ("org.gnome.desktop.peripherals.tablet",
path);
g_free (path);
return settings;
}
static PadMappingInfo *
pad_mapping_info_new (ClutterInputDevice *pad)
{
PadMappingInfo *info;
info = g_new0 (PadMappingInfo, 1);
info->device = pad;
info->settings = lookup_device_settings (pad);
info->group_modes =
g_new0 (guint, clutter_input_device_get_n_mode_groups (pad));
return info;
}
static void
pad_mapping_info_free (PadMappingInfo *info)
{
g_object_unref (info->settings);
g_free (info->group_modes);
g_free (info);
}
static void
device_added (ClutterSeat *seat,
ClutterInputDevice *device,
MetaPadActionMapper *mapper)
{
PadMappingInfo *info;
if ((clutter_input_device_get_capabilities (device) &
CLUTTER_INPUT_CAPABILITY_TABLET_PAD) != 0)
{
info = pad_mapping_info_new (device);
g_hash_table_insert (mapper->pads, device, info);
}
}
static void
device_removed (ClutterSeat *seat,
ClutterInputDevice *device,
MetaPadActionMapper *mapper)
{
g_hash_table_remove (mapper->pads, device);
}
static void
meta_pad_action_mapper_init (MetaPadActionMapper *mapper)
{
mapper->pads = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) pad_mapping_info_free);
mapper->seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
g_signal_connect (mapper->seat, "device-added",
G_CALLBACK (device_added), mapper);
g_signal_connect (mapper->seat, "device-removed",
G_CALLBACK (device_removed), mapper);
}
MetaPadActionMapper *
meta_pad_action_mapper_new (MetaMonitorManager *monitor_manager)
{
MetaPadActionMapper *action_mapper;
action_mapper = g_object_new (META_TYPE_PAD_ACTION_MAPPER, NULL);
g_set_object (&action_mapper->monitor_manager, monitor_manager);
return action_mapper;
}
static GSettings *
lookup_pad_action_settings (ClutterInputDevice *device,
MetaPadActionType action,
guint number,
MetaPadDirection direction,
int mode)
{
const char *vendor, *product, *action_type, *detail_type = NULL;
GSettings *settings;
GString *path;
char action_label;
vendor = clutter_input_device_get_vendor_id (device);
product = clutter_input_device_get_product_id (device);
action_label = 'A' + number;
switch (action)
{
case META_PAD_ACTION_BUTTON:
action_type = "button";
break;
case META_PAD_ACTION_RING:
g_assert (direction == META_PAD_DIRECTION_CW ||
direction == META_PAD_DIRECTION_CCW);
action_type = "ring";
detail_type = (direction == META_PAD_DIRECTION_CW) ? "cw" : "ccw";
break;
case META_PAD_ACTION_STRIP:
g_assert (direction == META_PAD_DIRECTION_UP ||
direction == META_PAD_DIRECTION_DOWN);
action_type = "strip";
detail_type = (direction == META_PAD_DIRECTION_UP) ? "up" : "down";
break;
default:
return NULL;
}
path = g_string_new (NULL);
g_string_append_printf (path, "/org/gnome/desktop/peripherals/tablets/%s:%s/%s%c",
vendor, product, action_type, action_label);
if (detail_type)
g_string_append_printf (path, "-%s", detail_type);
if (mode >= 0)
g_string_append_printf (path, "-mode-%d", mode);
g_string_append_c (path, '/');
settings = g_settings_new_with_path ("org.gnome.desktop.peripherals.tablet.pad-button",
path->str);
g_string_free (path, TRUE);
return settings;
}
static GDesktopPadButtonAction
meta_pad_action_mapper_get_button_action (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
guint button)
{
GDesktopPadButtonAction action;
GSettings *settings;
g_return_val_if_fail (META_IS_PAD_ACTION_MAPPER (mapper),
G_DESKTOP_PAD_BUTTON_ACTION_NONE);
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad),
G_DESKTOP_PAD_BUTTON_ACTION_NONE);
settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON,
button, META_PAD_DIRECTION_NONE, -1);
action = g_settings_get_enum (settings, "action");
g_object_unref (settings);
return action;
}
static gboolean
cycle_logical_monitors (MetaPadActionMapper *mapper,
gboolean skip_all_monitors,
MetaLogicalMonitor *current_logical_monitor,
MetaLogicalMonitor **next_logical_monitor)
{
MetaMonitorManager *monitor_manager = mapper->monitor_manager;
GList *logical_monitors;
/* We cycle between:
* - the span of all monitors (current_output = NULL), only for
* non-integrated devices.
* - each monitor individually.
*/
logical_monitors =
meta_monitor_manager_get_logical_monitors (monitor_manager);
if (!current_logical_monitor)
{
*next_logical_monitor = logical_monitors->data;
}
else
{
GList *l;
l = g_list_find (logical_monitors, current_logical_monitor);
if (l->next)
*next_logical_monitor = l->next->data;
else if (skip_all_monitors)
*next_logical_monitor = logical_monitors->data;
else
*next_logical_monitor = NULL;
}
return TRUE;
}
static MetaMonitor *
logical_monitor_find_monitor (MetaLogicalMonitor *logical_monitor,
const char *vendor,
const char *product,
const char *serial)
{
GList *monitors;
GList *l;
monitors = meta_logical_monitor_get_monitors (logical_monitor);
for (l = monitors; l; l = l->next)
{
MetaMonitor *monitor = l->data;
if (g_strcmp0 (meta_monitor_get_vendor (monitor), vendor) == 0 &&
g_strcmp0 (meta_monitor_get_product (monitor), product) == 0 &&
g_strcmp0 (meta_monitor_get_serial (monitor), serial) == 0)
return monitor;
}
return NULL;
}
static void
meta_pad_action_mapper_find_monitor (MetaPadActionMapper *mapper,
GSettings *settings,
ClutterInputDevice *device,
MetaMonitor **out_monitor,
MetaLogicalMonitor **out_logical_monitor)
{
MetaMonitorManager *monitor_manager;
MetaMonitor *monitor;
guint n_values;
GList *logical_monitors;
GList *l;
char **edid;
edid = g_settings_get_strv (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 (device));
goto out;
}
if (!*edid[0] && !*edid[1] && !*edid[2])
goto out;
monitor_manager = mapper->monitor_manager;
logical_monitors =
meta_monitor_manager_get_logical_monitors (monitor_manager);
for (l = logical_monitors; l; l = l->next)
{
MetaLogicalMonitor *logical_monitor = l->data;
monitor = logical_monitor_find_monitor (logical_monitor,
edid[0], edid[1], edid[2]);
if (monitor)
{
if (out_monitor)
*out_monitor = monitor;
if (out_logical_monitor)
*out_logical_monitor = logical_monitor;
break;
}
}
out:
g_strfreev (edid);
}
static void
meta_pad_action_mapper_cycle_tablet_output (MetaPadActionMapper *mapper,
ClutterInputDevice *device)
{
PadMappingInfo *info;
MetaLogicalMonitor *logical_monitor = NULL;
const char *edid[4] = { 0 }, *pretty_name = NULL;
gboolean is_integrated_device = FALSE;
#ifdef HAVE_LIBWACOM
WacomDevice *wacom_device;
#endif
g_return_if_fail (META_IS_PAD_ACTION_MAPPER (mapper));
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
g_return_if_fail ((clutter_input_device_get_capabilities (device) &
(CLUTTER_INPUT_CAPABILITY_TABLET_TOOL |
CLUTTER_INPUT_CAPABILITY_TABLET_PAD)) != 0);
info = g_hash_table_lookup (mapper->pads, device);
g_return_if_fail (info != NULL);
#ifdef HAVE_LIBWACOM
wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
if (wacom_device)
{
pretty_name = libwacom_get_name (wacom_device);
is_integrated_device =
libwacom_get_integration_flags (wacom_device) != WACOM_DEVICE_INTEGRATED_NONE;
}
#endif
meta_pad_action_mapper_find_monitor (mapper, info->settings, device,
NULL, &logical_monitor);
if (!cycle_logical_monitors (mapper,
is_integrated_device,
logical_monitor,
&logical_monitor))
return;
if (logical_monitor)
{
MetaMonitor *monitor;
const char *vendor;
const char *product;
const char *serial;
/* Pick an arbitrary monitor in the logical monitor to represent it. */
monitor = meta_logical_monitor_get_monitors (logical_monitor)->data;
vendor = meta_monitor_get_vendor (monitor);
product = meta_monitor_get_product (monitor);
serial = meta_monitor_get_serial (monitor);
edid[0] = vendor ? vendor : "";
edid[1] = product ? product : "";
edid[2] = serial ? serial : "";
}
else
{
edid[0] = "";
edid[1] = "";
edid[2] = "";
}
g_settings_set_strv (info->settings, "output", edid);
meta_display_show_tablet_mapping_notification (meta_get_display (),
device, pretty_name);
}
gboolean
meta_pad_action_mapper_is_button_grabbed (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
guint button)
{
g_return_val_if_fail (META_IS_PAD_ACTION_MAPPER (mapper), FALSE);
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), FALSE);
g_return_val_if_fail ((clutter_input_device_get_capabilities (pad) &
CLUTTER_INPUT_CAPABILITY_TABLET_PAD) != 0, FALSE);
return (meta_pad_action_mapper_get_button_action (mapper, pad, button) !=
G_DESKTOP_PAD_BUTTON_ACTION_NONE);
}
static void
emulate_modifiers (ClutterVirtualInputDevice *device,
ClutterModifierType mods,
ClutterKeyState state)
{
guint i;
struct {
ClutterModifierType mod;
guint keyval;
} mod_map[] = {
{ CLUTTER_SHIFT_MASK, CLUTTER_KEY_Shift_L },
{ CLUTTER_CONTROL_MASK, CLUTTER_KEY_Control_L },
{ CLUTTER_MOD1_MASK, CLUTTER_KEY_Meta_L }
};
for (i = 0; i < G_N_ELEMENTS (mod_map); i++)
{
if ((mods & mod_map[i].mod) == 0)
continue;
clutter_virtual_input_device_notify_keyval (device,
clutter_get_current_event_time (),
mod_map[i].keyval, state);
}
}
static void
meta_pad_action_mapper_emulate_keybinding (MetaPadActionMapper *mapper,
const char *accel,
gboolean is_press)
{
ClutterKeyState state;
guint key, mods;
if (!accel || !*accel)
return;
/* FIXME: This is appalling */
gtk_accelerator_parse (accel, &key, &mods);
if (!mapper->virtual_pad_keyboard)
{
ClutterBackend *backend;
ClutterSeat *seat;
backend = clutter_get_default_backend ();
seat = clutter_backend_get_default_seat (backend);
mapper->virtual_pad_keyboard =
clutter_seat_create_virtual_device (seat,
CLUTTER_KEYBOARD_DEVICE);
}
state = is_press ? CLUTTER_KEY_STATE_PRESSED : CLUTTER_KEY_STATE_RELEASED;
if (is_press)
emulate_modifiers (mapper->virtual_pad_keyboard, mods, state);
clutter_virtual_input_device_notify_keyval (mapper->virtual_pad_keyboard,
clutter_get_current_event_time (),
key, state);
if (!is_press)
emulate_modifiers (mapper->virtual_pad_keyboard, mods, state);
}
static gboolean
meta_pad_action_mapper_handle_button (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
const ClutterPadButtonEvent *event)
{
GDesktopPadButtonAction action;
int button, group, mode, n_modes = 0;
gboolean is_press;
GSettings *settings;
char *accel;
g_return_val_if_fail (META_IS_PAD_ACTION_MAPPER (mapper), FALSE);
g_return_val_if_fail (event->type == CLUTTER_PAD_BUTTON_PRESS ||
event->type == CLUTTER_PAD_BUTTON_RELEASE, FALSE);
button = event->button;
mode = event->mode;
group = clutter_input_device_get_mode_switch_button_group (pad, button);
is_press = event->type == CLUTTER_PAD_BUTTON_PRESS;
if (group >= 0)
n_modes = clutter_input_device_get_group_n_modes (pad, group);
if (is_press && n_modes > 0)
{
const char *pretty_name = NULL;
PadMappingInfo *info;
#ifdef HAVE_LIBWACOM
WacomDevice *wacom_device;
#endif
info = g_hash_table_lookup (mapper->pads, pad);
#ifdef HAVE_LIBWACOM
wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (pad));
if (wacom_device)
pretty_name = libwacom_get_name (wacom_device);
#endif
meta_display_notify_pad_group_switch (meta_get_display (), pad,
pretty_name, group, mode, n_modes);
info->group_modes[group] = mode;
}
action = meta_pad_action_mapper_get_button_action (mapper, pad, button);
switch (action)
{
case G_DESKTOP_PAD_BUTTON_ACTION_SWITCH_MONITOR:
if (is_press)
meta_pad_action_mapper_cycle_tablet_output (mapper, pad);
return TRUE;
case G_DESKTOP_PAD_BUTTON_ACTION_HELP:
if (is_press)
meta_display_request_pad_osd (meta_get_display (), pad, FALSE);
return TRUE;
case G_DESKTOP_PAD_BUTTON_ACTION_KEYBINDING:
settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON,
button, META_PAD_DIRECTION_NONE, -1);
accel = g_settings_get_string (settings, "keybinding");
meta_pad_action_mapper_emulate_keybinding (mapper, accel, is_press);
g_object_unref (settings);
g_free (accel);
return TRUE;
case G_DESKTOP_PAD_BUTTON_ACTION_NONE:
default:
return FALSE;
}
}
static gboolean
meta_pad_action_mapper_get_action_direction (MetaPadActionMapper *mapper,
const ClutterEvent *event,
MetaPadDirection *direction)
{
ClutterInputDevice *pad = clutter_event_get_device (event);
MetaPadActionType pad_action;
gboolean has_direction = FALSE;
MetaPadDirection inc_dir, dec_dir;
guint number;
double value;
*direction = META_PAD_DIRECTION_NONE;
switch (event->type)
{
case CLUTTER_PAD_RING:
pad_action = META_PAD_ACTION_RING;
number = event->pad_ring.ring_number;
value = event->pad_ring.angle;
inc_dir = META_PAD_DIRECTION_CW;
dec_dir = META_PAD_DIRECTION_CCW;
break;
case CLUTTER_PAD_STRIP:
pad_action = META_PAD_ACTION_STRIP;
number = event->pad_strip.strip_number;
value = event->pad_strip.value;
inc_dir = META_PAD_DIRECTION_DOWN;
dec_dir = META_PAD_DIRECTION_UP;
break;
default:
return FALSE;
}
if (mapper->last_pad_action_info.pad == pad &&
mapper->last_pad_action_info.action == pad_action &&
mapper->last_pad_action_info.number == number &&
value >= 0 && mapper->last_pad_action_info.value >= 0)
{
*direction = (value - mapper->last_pad_action_info.value) > 0 ?
inc_dir : dec_dir;
has_direction = TRUE;
}
mapper->last_pad_action_info.pad = pad;
mapper->last_pad_action_info.action = pad_action;
mapper->last_pad_action_info.number = number;
mapper->last_pad_action_info.value = value;
return has_direction;
}
static gboolean
meta_pad_action_mapper_handle_action (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
const ClutterEvent *event,
MetaPadActionType action,
guint number,
guint mode)
{
MetaPadDirection direction = META_PAD_DIRECTION_NONE;
g_autoptr (GSettings) settings1 = NULL, settings2 = NULL;
g_autofree char *accel1 = NULL, *accel2 = NULL;
gboolean handled;
if (action == META_PAD_ACTION_RING)
{
settings1 = lookup_pad_action_settings (pad, action, number,
META_PAD_DIRECTION_CW, mode);
settings2 = lookup_pad_action_settings (pad, action, number,
META_PAD_DIRECTION_CCW, mode);
}
else if (action == META_PAD_ACTION_STRIP)
{
settings1 = lookup_pad_action_settings (pad, action, number,
META_PAD_DIRECTION_UP, mode);
settings2 = lookup_pad_action_settings (pad, action, number,
META_PAD_DIRECTION_DOWN, mode);
}
else
{
return FALSE;
}
accel1 = g_settings_get_string (settings1, "keybinding");
accel2 = g_settings_get_string (settings2, "keybinding");
handled = ((accel1 && *accel1) || (accel2 && *accel2));
if (meta_pad_action_mapper_get_action_direction (mapper, event, &direction))
{
const gchar *accel;
if (direction == META_PAD_DIRECTION_UP ||
direction == META_PAD_DIRECTION_CW)
accel = accel1;
else if (direction == META_PAD_DIRECTION_DOWN ||
direction == META_PAD_DIRECTION_CCW)
accel = accel2;
if (accel && *accel)
{
meta_pad_action_mapper_emulate_keybinding (mapper, accel, TRUE);
meta_pad_action_mapper_emulate_keybinding (mapper, accel, FALSE);
}
}
return handled;
}
gboolean
meta_pad_action_mapper_handle_event (MetaPadActionMapper *mapper,
const ClutterEvent *event)
{
ClutterInputDevice *pad;
pad = clutter_event_get_source_device ((ClutterEvent *) event);
switch (event->type)
{
case CLUTTER_PAD_BUTTON_PRESS:
case CLUTTER_PAD_BUTTON_RELEASE:
return meta_pad_action_mapper_handle_button (mapper, pad,
&event->pad_button);
case CLUTTER_PAD_RING:
return meta_pad_action_mapper_handle_action (mapper, pad, event,
META_PAD_ACTION_RING,
event->pad_ring.ring_number,
event->pad_ring.mode);
case CLUTTER_PAD_STRIP:
return meta_pad_action_mapper_handle_action (mapper, pad, event,
META_PAD_ACTION_STRIP,
event->pad_strip.strip_number,
event->pad_strip.mode);
default:
return FALSE;
}
}
static void
format_directional_action (GString *str,
MetaPadDirection direction,
const gchar *action)
{
switch (direction)
{
case META_PAD_DIRECTION_CW:
g_string_append_printf (str, "⭮ %s", action);
break;
case META_PAD_DIRECTION_CCW:
g_string_append_printf (str, "⭯ %s", action);
break;
case META_PAD_DIRECTION_UP:
g_string_append_printf (str, "↥ %s", action);
break;
case META_PAD_DIRECTION_DOWN:
g_string_append_printf (str, "↧ %s", action);
break;
case META_PAD_DIRECTION_NONE:
g_assert_not_reached ();
}
}
static char *
compose_directional_action_label (MetaPadDirection direction1,
GSettings *value1,
MetaPadDirection direction2,
GSettings *value2)
{
g_autofree char *accel1 = NULL, *accel2 = NULL;
GString *str;
accel1 = g_settings_get_string (value1, "keybinding");
accel2 = g_settings_get_string (value2, "keybinding");
if ((!accel1 || !*accel1) && ((!accel2 || !*accel2)))
return NULL;
str = g_string_new (NULL);
if (accel1 && *accel1)
format_directional_action (str, direction1, accel1);
if (accel2 && *accel2)
{
if (str->len != 0)
g_string_append (str, " / ");
format_directional_action (str, direction2, accel2);
}
return g_string_free (str, FALSE);
}
static char *
meta_pad_action_mapper_get_ring_label (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
guint number,
guint mode)
{
GSettings *settings1, *settings2;
char *label;
/* We only allow keybinding actions with those */
settings1 = lookup_pad_action_settings (pad, META_PAD_ACTION_RING, number,
META_PAD_DIRECTION_CW, mode);
settings2 = lookup_pad_action_settings (pad, META_PAD_ACTION_RING, number,
META_PAD_DIRECTION_CCW, mode);
label = compose_directional_action_label (META_PAD_DIRECTION_CW, settings1,
META_PAD_DIRECTION_CCW, settings2);
g_object_unref (settings1);
g_object_unref (settings2);
return label;
}
static char *
meta_pad_action_mapper_get_strip_label (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
guint number,
guint mode)
{
GSettings *settings1, *settings2;
char *label;
/* We only allow keybinding actions with those */
settings1 = lookup_pad_action_settings (pad, META_PAD_ACTION_STRIP, number,
META_PAD_DIRECTION_UP, mode);
settings2 = lookup_pad_action_settings (pad, META_PAD_ACTION_STRIP, number,
META_PAD_DIRECTION_DOWN, mode);
label = compose_directional_action_label (META_PAD_DIRECTION_UP, settings1,
META_PAD_DIRECTION_DOWN, settings2);
g_object_unref (settings1);
g_object_unref (settings2);
return label;
}
static char *
meta_pad_action_mapper_get_button_label (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
guint button)
{
GDesktopPadButtonAction action;
int group;
g_return_val_if_fail (META_IS_PAD_ACTION_MAPPER (mapper), NULL);
g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), NULL);
g_return_val_if_fail ((clutter_input_device_get_capabilities (pad) &
CLUTTER_INPUT_CAPABILITY_TABLET_PAD) != 0, NULL);
group = clutter_input_device_get_mode_switch_button_group (pad, button);
if (group >= 0)
{
/* TRANSLATORS: This string refers to a button that switches between
* different modes.
*/
return g_strdup_printf (_("Mode Switch (Group %d)"), group);
}
action = meta_pad_action_mapper_get_button_action (mapper, pad, button);
switch (action)
{
case G_DESKTOP_PAD_BUTTON_ACTION_KEYBINDING:
{
GSettings *settings;
char *accel;
settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON,
button, META_PAD_DIRECTION_NONE, -1);
accel = g_settings_get_string (settings, "keybinding");
g_object_unref (settings);
return accel;
}
case G_DESKTOP_PAD_BUTTON_ACTION_SWITCH_MONITOR:
/* TRANSLATORS: This string refers to an action, cycles drawing tablets'
* mapping through the available outputs.
*/
return g_strdup (_("Switch monitor"));
case G_DESKTOP_PAD_BUTTON_ACTION_HELP:
return g_strdup (_("Show on-screen help"));
case G_DESKTOP_PAD_BUTTON_ACTION_NONE:
default:
return NULL;
}
}
static guint
get_current_pad_mode (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
MetaPadActionType action_type,
guint number)
{
PadMappingInfo *info;
guint group = 0, n_groups;
info = g_hash_table_lookup (mapper->pads, pad);
n_groups = clutter_input_device_get_n_mode_groups (pad);
if (!info->group_modes || n_groups == 0)
return 0;
if (action_type == META_PAD_ACTION_RING ||
action_type == META_PAD_ACTION_STRIP)
{
/* Assume features are evenly distributed in groups */
group = number % n_groups;
}
return info->group_modes[group];
}
char *
meta_pad_action_mapper_get_action_label (MetaPadActionMapper *mapper,
ClutterInputDevice *pad,
MetaPadActionType action_type,
guint number)
{
guint mode;
switch (action_type)
{
case META_PAD_ACTION_BUTTON:
return meta_pad_action_mapper_get_button_label (mapper, pad, number);
case META_PAD_ACTION_RING:
mode = get_current_pad_mode (mapper, pad, action_type, number);
return meta_pad_action_mapper_get_ring_label (mapper, pad, number, mode);
case META_PAD_ACTION_STRIP:
mode = get_current_pad_mode (mapper, pad, action_type, number);
return meta_pad_action_mapper_get_strip_label (mapper, pad, number, mode);
}
return NULL;
}