[mutter/wip/carlosg/input-thread: 73/130] backends: Split pad action mapping to a separate object




commit 852661b9f3cff9730c9891d8c546890c9c6b479a
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Jul 15 22:38:00 2020 +0200

    backends: Split pad action mapping to a separate object
    
    This now lives in the core, and will get updated from events in the
    UI thread.
    
    https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1403

 src/backends/meta-input-settings-private.h |  11 -
 src/backends/meta-input-settings.c         | 638 ---------------------
 src/core/display-private.h                 |   2 +
 src/core/display.c                         |   9 +-
 src/core/events.c                          |   3 +-
 src/core/meta-pad-action-mapper.c          | 858 +++++++++++++++++++++++++++++
 src/core/meta-pad-action-mapper.h          |  46 ++
 src/meson.build                            |   1 +
 src/wayland/meta-wayland-tablet-pad.c      |  17 +-
 9 files changed, 922 insertions(+), 663 deletions(-)
---
diff --git a/src/backends/meta-input-settings-private.h b/src/backends/meta-input-settings-private.h
index 3cf7b5f0e4..79bac8e07a 100644
--- a/src/backends/meta-input-settings-private.h
+++ b/src/backends/meta-input-settings-private.h
@@ -143,17 +143,6 @@ GSettings *           meta_input_settings_get_tablet_settings (MetaInputSettings
 MetaLogicalMonitor *  meta_input_settings_get_tablet_logical_monitor (MetaInputSettings  *settings,
                                                                       ClutterInputDevice *device);
 
-gboolean                   meta_input_settings_is_pad_button_grabbed     (MetaInputSettings  *input_settings,
-                                                                          ClutterInputDevice *pad,
-                                                                          guint               button);
-
-gboolean                   meta_input_settings_handle_pad_event          (MetaInputSettings    
*input_settings,
-                                                                          const ClutterEvent   *event);
-gchar *                    meta_input_settings_get_pad_action_label      (MetaInputSettings  *input_settings,
-                                                                          ClutterInputDevice *pad,
-                                                                          MetaPadActionType   action,
-                                                                          guint               number);
-
 void meta_input_settings_maybe_save_numlock_state    (MetaInputSettings *input_settings);
 void meta_input_settings_maybe_restore_numlock_state (MetaInputSettings *input_settings);
 
diff --git a/src/backends/meta-input-settings.c b/src/backends/meta-input-settings.c
index 0fcac9095c..6c0040ec23 100644
--- a/src/backends/meta-input-settings.c
+++ b/src/backends/meta-input-settings.c
@@ -81,18 +81,8 @@ struct _MetaInputSettingsPrivate
 
   GHashTable *current_tools;
 
-  ClutterVirtualInputDevice *virtual_pad_keyboard;
-
   GHashTable *two_finger_devices;
 
-  /* Pad ring/strip emission */
-  struct {
-    ClutterInputDevice *pad;
-    MetaPadActionType action;
-    guint number;
-    gdouble value;
-  } last_pad_action_info;
-
   /* For absolute devices with no mapping in settings */
   MetaInputMapper *input_mapper;
 };
@@ -111,15 +101,6 @@ typedef void (*ConfigUintFunc)   (MetaInputSettings  *input_settings,
                                   ClutterInputDevice *device,
                                   guint               value);
 
-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;
-
 G_DEFINE_TYPE_WITH_PRIVATE (MetaInputSettings, meta_input_settings, G_TYPE_OBJECT)
 
 static GSList *
@@ -155,8 +136,6 @@ meta_input_settings_dispose (GObject *object)
 
   g_signal_handlers_disconnect_by_data (priv->seat, settings);
 
-  g_clear_object (&priv->virtual_pad_keyboard);
-
   g_clear_object (&priv->mouse_settings);
   g_clear_object (&priv->touchpad_settings);
   g_clear_object (&priv->trackball_settings);
@@ -1587,63 +1566,6 @@ lookup_tool_settings (ClutterInputDeviceTool *tool,
   return tool_settings;
 }
 
-static GSettings *
-lookup_pad_action_settings (ClutterInputDevice *device,
-                            MetaPadActionType   action,
-                            guint               number,
-                            MetaPadDirection    direction,
-                            gint                mode)
-{
-  const gchar *vendor, *product, *action_type, *detail_type = NULL;
-  GSettings *settings;
-  GString *path;
-  gchar 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 void
 monitors_changed_cb (MetaMonitorManager *monitor_manager,
                      MetaInputSettings  *input_settings)
@@ -2196,566 +2118,6 @@ meta_input_settings_get_tablet_logical_monitor (MetaInputSettings  *settings,
   return logical_monitor;
 }
 
-static GDesktopPadButtonAction
-meta_input_settings_get_pad_button_action (MetaInputSettings   *input_settings,
-                                           ClutterInputDevice  *pad,
-                                           guint                button)
-{
-  GDesktopPadButtonAction action;
-  GSettings *settings;
-
-  g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings),
-                        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 (MetaInputSettings   *settings,
-                        gboolean             skip_all_monitors,
-                        MetaLogicalMonitor  *current_logical_monitor,
-                        MetaLogicalMonitor **next_logical_monitor)
-{
-  MetaInputSettingsPrivate *priv =
-    meta_input_settings_get_instance_private (settings);
-  MetaMonitorManager *monitor_manager = priv->monitor_manager;
-  GList *logical_monitors;
-
-  /* We cycle between:
-   * - the span of all monitors (current_logical_monitor = 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 void
-meta_input_settings_cycle_tablet_output (MetaInputSettings  *input_settings,
-                                         ClutterInputDevice *device)
-{
-  MetaInputSettingsPrivate *priv;
-  DeviceMappingInfo *info;
-  MetaLogicalMonitor *logical_monitor = NULL;
-  const gchar *edid[4] = { 0 }, *pretty_name = NULL;
-  gboolean is_integrated_device = FALSE;
-#ifdef HAVE_LIBWACOM
-  WacomDevice *wacom_device;
-#endif
-
-  g_return_if_fail (META_IS_INPUT_SETTINGS (input_settings));
-  g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
-  g_return_if_fail (clutter_input_device_get_device_type (device) == CLUTTER_TABLET_DEVICE ||
-                    clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE);
-
-  priv = meta_input_settings_get_instance_private (input_settings);
-  info = g_hash_table_lookup (priv->mappable_devices, 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_input_settings_find_monitor (input_settings, info->settings, device,
-                                    NULL, &logical_monitor);
-
-  if (!cycle_logical_monitors (input_settings,
-                               is_integrated_device,
-                               logical_monitor,
-                               &logical_monitor))
-    return;
-
-  if (logical_monitor)
-    {
-      MetaMonitor *monitor;
-
-      /* Pick an arbitrary monitor in the logical monitor to represent it. */
-      monitor = meta_logical_monitor_get_monitors (logical_monitor)->data;
-      edid[0] = meta_monitor_get_vendor (monitor);
-      edid[1] = meta_monitor_get_product (monitor);
-      edid[2] = meta_monitor_get_serial (monitor);
-    }
-  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);
-}
-
-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_input_settings_emulate_keybinding (MetaInputSettings  *input_settings,
-                                        const gchar        *accel,
-                                        gboolean            is_press)
-{
-  MetaInputSettingsPrivate *priv;
-  ClutterKeyState state;
-  guint key, mods;
-
-  if (!accel || !*accel)
-    return;
-
-  priv = meta_input_settings_get_instance_private (input_settings);
-
-  /* FIXME: This is appalling */
-  gtk_accelerator_parse (accel, &key, &mods);
-
-  if (!priv->virtual_pad_keyboard)
-    {
-      ClutterBackend *backend;
-      ClutterSeat *seat;
-
-      backend = clutter_get_default_backend ();
-      seat = clutter_backend_get_default_seat (backend);
-
-      priv->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 (priv->virtual_pad_keyboard, mods, state);
-
-  clutter_virtual_input_device_notify_keyval (priv->virtual_pad_keyboard,
-                                              clutter_get_current_event_time (),
-                                              key, state);
-  if (!is_press)
-    emulate_modifiers (priv->virtual_pad_keyboard, mods, state);
-}
-
-gboolean
-meta_input_settings_is_pad_button_grabbed (MetaInputSettings  *input_settings,
-                                           ClutterInputDevice *pad,
-                                           guint               button)
-{
-  g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings), FALSE);
-  g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), FALSE);
-  g_return_val_if_fail (clutter_input_device_get_device_type (pad) ==
-                        CLUTTER_PAD_DEVICE, FALSE);
-
-  return (meta_input_settings_get_pad_button_action (input_settings, pad, button) !=
-          G_DESKTOP_PAD_BUTTON_ACTION_NONE);
-}
-
-static gboolean
-meta_input_settings_handle_pad_button (MetaInputSettings           *input_settings,
-                                       ClutterInputDevice          *pad,
-                                       const ClutterPadButtonEvent *event)
-{
-  GDesktopPadButtonAction action;
-  gint button, group, mode;
-  gboolean is_press;
-  GSettings *settings;
-  gchar *accel;
-
-  g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings), 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 (is_press && group >= 0)
-    {
-      guint n_modes = clutter_input_device_get_group_n_modes (pad, group);
-      const gchar *pretty_name = NULL;
-      MetaInputSettingsPrivate *priv;
-      DeviceMappingInfo *info;
-#ifdef HAVE_LIBWACOM
-      WacomDevice *wacom_device;
-#endif
-
-      priv = meta_input_settings_get_instance_private (input_settings);
-      info = g_hash_table_lookup (priv->mappable_devices, 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_input_settings_get_pad_button_action (input_settings, pad, button);
-
-  switch (action)
-    {
-    case G_DESKTOP_PAD_BUTTON_ACTION_SWITCH_MONITOR:
-      if (is_press)
-        meta_input_settings_cycle_tablet_output (input_settings, 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_input_settings_emulate_keybinding (input_settings, 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_input_settings_handle_pad_action (MetaInputSettings         *input_settings,
-                                       ClutterInputDevice        *pad,
-                                       MetaPadActionType          action,
-                                       guint                      number,
-                                       MetaPadDirection           direction,
-                                       guint                      mode)
-{
-  GSettings *settings;
-  gboolean handled = FALSE;
-  gchar *accel;
-
-  settings = lookup_pad_action_settings (pad, action, number, direction, mode);
-  accel = g_settings_get_string (settings, "keybinding");
-
-  if (accel && *accel)
-    {
-      meta_input_settings_emulate_keybinding (input_settings, accel, TRUE);
-      meta_input_settings_emulate_keybinding (input_settings, accel, FALSE);
-      handled = TRUE;
-    }
-
-  g_object_unref (settings);
-  g_free (accel);
-
-  return handled;
-}
-
-static gboolean
-meta_input_settings_get_pad_action_direction (MetaInputSettings  *input_settings,
-                                              const ClutterEvent *event,
-                                              MetaPadDirection   *direction)
-{
-  MetaInputSettingsPrivate *priv;
-  ClutterInputDevice *pad = clutter_event_get_device (event);
-  MetaPadActionType pad_action;
-  gboolean has_direction = FALSE;
-  MetaPadDirection inc_dir, dec_dir;
-  guint number;
-  gdouble value;
-
-  priv = meta_input_settings_get_instance_private (input_settings);
-  *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 (priv->last_pad_action_info.pad == pad &&
-      priv->last_pad_action_info.action == pad_action &&
-      priv->last_pad_action_info.number == number &&
-      value >= 0 && priv->last_pad_action_info.value >= 0)
-    {
-      *direction = (value - priv->last_pad_action_info.value) > 0 ?
-        inc_dir : dec_dir;
-      has_direction = TRUE;
-    }
-
-  priv->last_pad_action_info.pad = pad;
-  priv->last_pad_action_info.action = pad_action;
-  priv->last_pad_action_info.number = number;
-  priv->last_pad_action_info.value = value;
-  return has_direction;
-}
-
-gboolean
-meta_input_settings_handle_pad_event (MetaInputSettings  *input_settings,
-                                      const ClutterEvent *event)
-{
-  ClutterInputDevice *pad;
-  MetaPadDirection direction = META_PAD_DIRECTION_NONE;
-
-  pad = clutter_event_get_source_device ((ClutterEvent *) event);
-
-  switch (event->type)
-    {
-    case CLUTTER_PAD_BUTTON_PRESS:
-    case CLUTTER_PAD_BUTTON_RELEASE:
-      return meta_input_settings_handle_pad_button (input_settings, pad,
-                                                    &event->pad_button);
-    case CLUTTER_PAD_RING:
-      if (!meta_input_settings_get_pad_action_direction (input_settings,
-                                                         event, &direction))
-        return FALSE;
-      return meta_input_settings_handle_pad_action (input_settings, pad,
-                                                    META_PAD_ACTION_RING,
-                                                    event->pad_ring.ring_number,
-                                                    direction,
-                                                    event->pad_ring.mode);
-    case CLUTTER_PAD_STRIP:
-      if (!meta_input_settings_get_pad_action_direction (input_settings,
-                                                         event, &direction))
-        return FALSE;
-      return meta_input_settings_handle_pad_action (input_settings, pad,
-                                                    META_PAD_ACTION_STRIP,
-                                                    event->pad_strip.strip_number,
-                                                    direction,
-                                                    event->pad_strip.mode);
-    default:
-      return FALSE;
-    }
-}
-
-static gchar *
-compose_directional_action_label (GSettings *direction1,
-                                  GSettings *direction2)
-{
-  gchar *accel1, *accel2, *str = NULL;
-
-  accel1 = g_settings_get_string (direction1, "keybinding");
-  accel2 = g_settings_get_string (direction2, "keybinding");
-
-  if (accel1 && *accel1 && accel2 && *accel2)
-    str = g_strdup_printf ("%s / %s", accel1, accel2);
-
-  g_free (accel1);
-  g_free (accel2);
-
-  return str;
-}
-
-static gchar *
-meta_input_settings_get_ring_label (MetaInputSettings  *settings,
-                                    ClutterInputDevice *pad,
-                                    guint               number,
-                                    guint               mode)
-{
-  GSettings *settings1, *settings2;
-  gchar *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 (settings1, settings2);
-  g_object_unref (settings1);
-  g_object_unref (settings2);
-
-  return label;
-}
-
-static gchar *
-meta_input_settings_get_strip_label (MetaInputSettings  *settings,
-                                     ClutterInputDevice *pad,
-                                     guint               number,
-                                     guint               mode)
-{
-  GSettings *settings1, *settings2;
-  gchar *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 (settings1, settings2);
-  g_object_unref (settings1);
-  g_object_unref (settings2);
-
-  return label;
-}
-
-static gchar *
-meta_input_settings_get_button_label (MetaInputSettings  *input_settings,
-                                      ClutterInputDevice *pad,
-                                      guint               button)
-{
-  GDesktopPadButtonAction action;
-  gint group;
-
-  g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings), NULL);
-  g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), NULL);
-  g_return_val_if_fail (clutter_input_device_get_device_type (pad) ==
-                        CLUTTER_PAD_DEVICE, 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_input_settings_get_pad_button_action (input_settings, pad, button);
-
-  switch (action)
-    {
-    case G_DESKTOP_PAD_BUTTON_ACTION_KEYBINDING:
-      {
-        GSettings *settings;
-        gchar *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 (MetaInputSettings  *input_settings,
-                      ClutterInputDevice *pad,
-                      MetaPadActionType   action_type,
-                      guint               number)
-{
-  MetaInputSettingsPrivate *priv;
-  DeviceMappingInfo *info;
-  guint group = 0, n_groups;
-
-  priv = meta_input_settings_get_instance_private (input_settings);
-  info = g_hash_table_lookup (priv->mappable_devices, 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];
-}
-
-gchar *
-meta_input_settings_get_pad_action_label (MetaInputSettings  *input_settings,
-                                          ClutterInputDevice *pad,
-                                          MetaPadActionType   action_type,
-                                          guint               number)
-{
-  guint mode;
-
-  switch (action_type)
-    {
-    case META_PAD_ACTION_BUTTON:
-      return meta_input_settings_get_button_label (input_settings, pad, number);
-    case META_PAD_ACTION_RING:
-      mode = get_current_pad_mode (input_settings, pad, action_type, number);
-      return meta_input_settings_get_ring_label (input_settings, pad,
-                                                 number, mode);
-    case META_PAD_ACTION_STRIP:
-      mode = get_current_pad_mode (input_settings, pad, action_type, number);
-      return meta_input_settings_get_strip_label (input_settings, pad,
-                                                  number, mode);
-    }
-
-  return NULL;
-}
-
 void
 meta_input_settings_maybe_save_numlock_state (MetaInputSettings *input_settings)
 {
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 78f25fdb6c..3d690fcb66 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -38,6 +38,7 @@
 #include "clutter/clutter.h"
 #include "core/keybindings-private.h"
 #include "core/meta-gesture-tracker-private.h"
+#include "core/meta-pad-action-mapper.h"
 #include "core/stack-tracker.h"
 #include "core/startup-notification-private.h"
 #include "meta/barrier.h"
@@ -211,6 +212,7 @@ struct _MetaDisplay
   ClutterEventSequence *pointer_emulating_sequence;
 
   ClutterActor *current_pad_osd;
+  MetaPadActionMapper *pad_action_mapper;
 
   MetaStartupNotification *startup_notification;
 
diff --git a/src/core/display.c b/src/core/display.c
index db06990ce5..7bb47d4b3b 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -854,6 +854,8 @@ meta_display_open (void)
   g_signal_connect (monitor_manager, "monitors-changed-internal",
                     G_CALLBACK (on_monitors_changed_internal), display);
 
+  display->pad_action_mapper = meta_pad_action_mapper_new (monitor_manager);
+
   settings = meta_backend_get_settings (backend);
   g_signal_connect (settings, "ui-scaling-factor-changed",
                     G_CALLBACK (on_ui_scaling_factor_changed), display);
@@ -1119,6 +1121,7 @@ meta_display_close (MetaDisplay *display,
 
   meta_clipboard_manager_shutdown (display);
   g_clear_object (&display->selection);
+  g_clear_object (&display->pad_action_mapper);
 
   g_object_unref (display);
   the_display = NULL;
@@ -2961,12 +2964,12 @@ meta_display_get_pad_action_label (MetaDisplay        *display,
                                    MetaPadActionType   action_type,
                                    guint               action_number)
 {
-  MetaInputSettings *settings;
   gchar *label;
 
   /* First, lookup the action, as imposed by settings */
-  settings = meta_backend_get_input_settings (meta_get_backend ());
-  label = meta_input_settings_get_pad_action_label (settings, pad, action_type, action_number);
+  label = meta_pad_action_mapper_get_action_label (display->pad_action_mapper,
+                                                   pad, action_type,
+                                                   action_number);
   if (label)
     return label;
 
diff --git a/src/core/events.c b/src/core/events.c
index 52828bdc93..fdc0bd8b51 100644
--- a/src/core/events.c
+++ b/src/core/events.c
@@ -265,8 +265,7 @@ meta_display_handle_event (MetaDisplay        *display,
       handle_pad_event = !display->current_pad_osd || is_mode_switch;
 
       if (handle_pad_event &&
-          meta_input_settings_handle_pad_event (meta_backend_get_input_settings (backend),
-                                                event))
+          meta_pad_action_mapper_handle_event (display->pad_action_mapper, event))
         {
           bypass_wayland = bypass_clutter = TRUE;
           goto out;
diff --git a/src/core/meta-pad-action-mapper.c b/src/core/meta-pad-action-mapper.c
new file mode 100644
index 0000000000..e5331e1dd2
--- /dev/null
+++ b/src/core/meta-pad-action-mapper.c
@@ -0,0 +1,858 @@
+/*
+ * 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_device_type (device) == CLUTTER_PAD_DEVICE)
+    {
+      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_device_type (device) == CLUTTER_TABLET_DEVICE ||
+                    clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE);
+
+  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;
+
+      /* Pick an arbitrary monitor in the logical monitor to represent it. */
+      monitor = meta_logical_monitor_get_monitors (logical_monitor)->data;
+      edid[0] = meta_monitor_get_vendor (monitor);
+      edid[1] = meta_monitor_get_product (monitor);
+      edid[2] = meta_monitor_get_serial (monitor);
+    }
+  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_device_type (pad) ==
+                        CLUTTER_PAD_DEVICE, 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;
+  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 (is_press && group >= 0)
+    {
+      guint n_modes = clutter_input_device_get_group_n_modes (pad, group);
+      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_handle_action (MetaPadActionMapper *mapper,
+                                      ClutterInputDevice  *pad,
+                                      MetaPadActionType    action,
+                                      guint                number,
+                                      MetaPadDirection     direction,
+                                      guint                mode)
+{
+  GSettings *settings;
+  gboolean handled = FALSE;
+  char *accel;
+
+  settings = lookup_pad_action_settings (pad, action, number, direction, mode);
+  accel = g_settings_get_string (settings, "keybinding");
+
+  if (accel && *accel)
+    {
+      meta_pad_action_mapper_emulate_keybinding (mapper, accel, TRUE);
+      meta_pad_action_mapper_emulate_keybinding (mapper, accel, FALSE);
+      handled = TRUE;
+    }
+
+  g_object_unref (settings);
+  g_free (accel);
+
+  return handled;
+}
+
+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;
+}
+
+gboolean
+meta_pad_action_mapper_handle_event (MetaPadActionMapper *mapper,
+                                     const ClutterEvent  *event)
+{
+  ClutterInputDevice *pad;
+  MetaPadDirection direction = META_PAD_DIRECTION_NONE;
+
+  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:
+      if (!meta_pad_action_mapper_get_action_direction (mapper,
+                                                        event, &direction))
+        return FALSE;
+      return meta_pad_action_mapper_handle_action (mapper, pad,
+                                                   META_PAD_ACTION_RING,
+                                                   event->pad_ring.ring_number,
+                                                   direction,
+                                                   event->pad_ring.mode);
+    case CLUTTER_PAD_STRIP:
+      if (!meta_pad_action_mapper_get_action_direction (mapper,
+                                                        event, &direction))
+        return FALSE;
+      return meta_pad_action_mapper_handle_action (mapper, pad,
+                                                   META_PAD_ACTION_STRIP,
+                                                   event->pad_strip.strip_number,
+                                                   direction,
+                                                   event->pad_strip.mode);
+    default:
+      return FALSE;
+    }
+}
+
+
+static char *
+compose_directional_action_label (GSettings *direction1,
+                                  GSettings *direction2)
+{
+  char *accel1, *accel2, *str = NULL;
+
+  accel1 = g_settings_get_string (direction1, "keybinding");
+  accel2 = g_settings_get_string (direction2, "keybinding");
+
+  if (accel1 && *accel1 && accel2 && *accel2)
+    str = g_strdup_printf ("%s / %s", accel1, accel2);
+
+  g_free (accel1);
+  g_free (accel2);
+
+  return str;
+}
+
+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 (settings1, 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 (settings1, 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_device_type (pad) ==
+                        CLUTTER_PAD_DEVICE, 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;
+}
diff --git a/src/core/meta-pad-action-mapper.h b/src/core/meta-pad-action-mapper.h
new file mode 100644
index 0000000000..63fc2261b5
--- /dev/null
+++ b/src/core/meta-pad-action-mapper.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 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>
+ */
+
+#ifndef META_PAD_ACTION_MAPPER_H
+#define META_PAD_ACTION_MAPPER_H
+
+#include "clutter/clutter.h"
+#include "meta/display.h"
+#include "meta/meta-monitor-manager.h"
+
+#define META_TYPE_PAD_ACTION_MAPPER (meta_pad_action_mapper_get_type ())
+G_DECLARE_FINAL_TYPE (MetaPadActionMapper, meta_pad_action_mapper,
+                      META, PAD_ACTION_MAPPER, GObject)
+
+MetaPadActionMapper * meta_pad_action_mapper_new (MetaMonitorManager *monitor_manager);
+
+gboolean meta_pad_action_mapper_is_button_grabbed (MetaPadActionMapper *mapper,
+                                                   ClutterInputDevice  *pad,
+                                                   guint                button);
+gboolean meta_pad_action_mapper_handle_event      (MetaPadActionMapper *mapper,
+                                                   const ClutterEvent  *event);
+gchar *  meta_pad_action_mapper_get_action_label  (MetaPadActionMapper *mapper,
+                                                   ClutterInputDevice  *pad,
+                                                   MetaPadActionType    action,
+                                                   guint                number);
+
+#endif /* META_PAD_ACTION_MAPPER_H */
diff --git a/src/meson.build b/src/meson.build
index 3da557f55e..1b40c168e0 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -374,6 +374,7 @@ mutter_sources = [
   'core/meta-inhibit-shortcuts-dialog-default.c',
   'core/meta-inhibit-shortcuts-dialog-default-private.h',
   'core/meta-launch-context.c',
+  'core/meta-pad-action-mapper.c',
   'core/meta-selection.c',
   'core/meta-selection-source.c',
   'core/meta-selection-source-memory.c',
diff --git a/src/wayland/meta-wayland-tablet-pad.c b/src/wayland/meta-wayland-tablet-pad.c
index 167d45948f..d96bdfed45 100644
--- a/src/wayland/meta-wayland-tablet-pad.c
+++ b/src/wayland/meta-wayland-tablet-pad.c
@@ -28,6 +28,7 @@
 #include <wayland-server.h>
 
 #include "backends/meta-input-settings-private.h"
+#include "core/display-private.h"
 #include "compositor/meta-surface-actor-wayland.h"
 #include "wayland/meta-wayland-private.h"
 #include "wayland/meta-wayland-tablet-pad-group.h"
@@ -244,15 +245,14 @@ tablet_pad_set_feedback (struct wl_client   *client,
 {
   MetaWaylandTabletPad *pad = wl_resource_get_user_data (resource);
   MetaWaylandTabletPadGroup *group = tablet_pad_lookup_button_group (pad, button);
-  MetaInputSettings *input_settings;
+  MetaPadActionMapper *mapper;
 
   if (!group || group->mode_switch_serial != serial)
     return;
 
-  input_settings = meta_backend_get_input_settings (meta_get_backend ());
+  mapper = meta_get_display ()->pad_action_mapper;
 
-  if (input_settings &&
-      meta_input_settings_is_pad_button_grabbed (input_settings, pad->device, button))
+  if (meta_pad_action_mapper_is_button_grabbed (mapper, pad->device, button))
     return;
 
   if (meta_wayland_tablet_pad_group_is_mode_switch_button (group, button))
@@ -367,15 +367,14 @@ static gboolean
 meta_wayland_tablet_pad_handle_event_action (MetaWaylandTabletPad *pad,
                                              const ClutterEvent   *event)
 {
-  MetaInputSettings *input_settings;
+  MetaPadActionMapper *mapper;
   ClutterInputDevice *device;
 
   device = clutter_event_get_source_device (event);
-  input_settings = meta_backend_get_input_settings (meta_get_backend ());
+  mapper = meta_get_display ()->pad_action_mapper;
 
-  if (input_settings &&
-      meta_input_settings_is_pad_button_grabbed (input_settings, device,
-                                                 event->pad_button.button))
+  if (meta_pad_action_mapper_is_button_grabbed (mapper, device,
+                                                event->pad_button.button))
     return TRUE;
 
   return FALSE;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]