[mutter/wip/is-switch-fixes: 3/4] Grab and emit a signal when XK_ISO_Next_Group is pressed



commit 0e83b748d5f60a61d352505b657bfa05b0aea8f0
Author: Rui Matos <tiagomatos gmail com>
Date:   Thu Mar 28 17:57:12 2013 +0100

    Grab and emit a signal when XK_ISO_Next_Group is pressed
    
    This will make it possible to implement input source switching in
    gnome-shell using the popular modifiers-only keybinding that's
    implemented on the X server through an XKB option.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=697002

 src/core/display-private.h |    1 +
 src/core/display.c         |   15 +++
 src/core/keybindings.c     |  229 ++++++++++++++++++++++++++++++++++++++++++++
 src/core/prefs.c           |   64 ++++++++++++-
 src/meta/prefs.h           |    2 +
 5 files changed, 310 insertions(+), 1 deletions(-)
---
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 1f73fc3..52550bc 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -458,6 +458,7 @@ void meta_display_overlay_key_activate (MetaDisplay *display);
 void meta_display_accelerator_activate (MetaDisplay *display,
                                         guint        action,
                                         guint        deviceid);
+void meta_display_modifiers_accelerator_activate (MetaDisplay *display);
 
 /* In above-tab-keycode.c */
 guint meta_display_get_above_tab_keycode (MetaDisplay *display);
diff --git a/src/core/display.c b/src/core/display.c
index f4f7e63..95b7274 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -139,6 +139,7 @@ enum
 {
   OVERLAY_KEY,
   ACCELERATOR_ACTIVATED,
+  MODIFIERS_ACCELERATOR_ACTIVATED,
   FOCUS_WINDOW,
   WINDOW_CREATED,
   WINDOW_DEMANDS_ATTENTION,
@@ -255,6 +256,14 @@ meta_display_class_init (MetaDisplayClass *klass)
                   NULL, NULL, NULL,
                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
 
+  display_signals[MODIFIERS_ACCELERATOR_ACTIVATED] =
+    g_signal_new ("modifiers-accelerator-activated",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+
   display_signals[WINDOW_CREATED] =
     g_signal_new ("window-created",
                   G_TYPE_FROM_CLASS (klass),
@@ -5682,6 +5691,12 @@ meta_display_accelerator_activate (MetaDisplay *display,
 }
 
 void
+meta_display_modifiers_accelerator_activate (MetaDisplay *display)
+{
+  g_signal_emit (display, display_signals[MODIFIERS_ACCELERATOR_ACTIVATED], 0);
+}
+
+void
 meta_display_get_compositor_version (MetaDisplay *display,
                                      int         *major,
                                      int         *minor)
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 78b22a7..b9321f6 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -153,6 +153,10 @@ static void regrab_key_bindings         (MetaDisplay *display);
 static GHashTable *key_handlers;
 static GHashTable *external_grabs;
 
+static char         *iso_next_group_option;
+static MetaKeyCombo *iso_next_group_combos;
+static int           n_iso_next_group_combos;
+
 #define HANDLER(name) g_hash_table_lookup (key_handlers, (name))
 
 static void
@@ -301,6 +305,168 @@ reload_modmap (MetaDisplay *display)
               display->meta_mask);
 }
 
+static int
+get_keycodes_for_keysym (MetaDisplay  *display,
+                         int           keysym,
+                         int         **keycodes)
+{
+  GArray *retval;
+  int n_keycodes;
+  int keycode;
+
+  retval = g_array_new (FALSE, FALSE, sizeof (int));
+
+  keycode = display->min_keycode;
+  while (keycode <= display->max_keycode)
+    {
+      const KeySym *syms = display->keymap + (keycode - display->min_keycode) * display->keysyms_per_keycode;
+      int i = 0;
+
+      while (i < display->keysyms_per_keycode)
+        {
+          if (syms[i] == (unsigned int)keysym)
+            g_array_append_val (retval, keycode);
+
+          ++i;
+        }
+
+      ++keycode;
+    }
+
+  n_keycodes = retval->len;
+  *keycodes = (int*) g_array_free (retval, n_keycodes == 0 ? TRUE : FALSE);
+
+  return n_keycodes;
+}
+
+static void
+reload_iso_next_group_combos (MetaDisplay *display)
+{
+  MetaKeyCombo *combos;
+  int *keycodes;
+  int n_keycodes;
+  int n_combos;
+  int i;
+
+  g_clear_pointer (&iso_next_group_combos, g_free);
+  n_iso_next_group_combos = 0;
+
+  if (iso_next_group_option == NULL)
+    return;
+
+  n_keycodes = get_keycodes_for_keysym (display, XK_ISO_Next_Group, &keycodes);
+
+  if (g_str_equal (iso_next_group_option, "toggle") ||
+      g_str_equal (iso_next_group_option, "lalt_toggle") ||
+      g_str_equal (iso_next_group_option, "lwin_toggle") ||
+      g_str_equal (iso_next_group_option, "rwin_toggle") ||
+      g_str_equal (iso_next_group_option, "lshift_toggle") ||
+      g_str_equal (iso_next_group_option, "rshift_toggle") ||
+      g_str_equal (iso_next_group_option, "lctrl_toggle") ||
+      g_str_equal (iso_next_group_option, "rctrl_toggle") ||
+      g_str_equal (iso_next_group_option, "sclk_toggle") ||
+      g_str_equal (iso_next_group_option, "menu_toggle") ||
+      g_str_equal (iso_next_group_option, "caps_toggle"))
+    {
+      n_combos = n_keycodes;
+      combos = g_new (MetaKeyCombo, n_combos);
+
+      for (i = 0; i < n_keycodes; ++i)
+        {
+          combos[i].keysym = XK_ISO_Next_Group;
+          combos[i].keycode = keycodes[i];
+          combos[i].modifiers = 0;
+        }
+    }
+  else if (g_str_equal (iso_next_group_option, "shift_caps_toggle") ||
+           g_str_equal (iso_next_group_option, "shifts_toggle"))
+    {
+      n_combos = n_keycodes;
+      combos = g_new (MetaKeyCombo, n_combos);
+
+      for (i = 0; i < n_keycodes; ++i)
+        {
+          combos[i].keysym = XK_ISO_Next_Group;
+          combos[i].keycode = keycodes[i];
+          combos[i].modifiers = ShiftMask;
+        }
+    }
+  else if (g_str_equal (iso_next_group_option, "alt_caps_toggle") ||
+           g_str_equal (iso_next_group_option, "alt_space_toggle"))
+    {
+      n_combos = n_keycodes;
+      combos = g_new (MetaKeyCombo, n_combos);
+
+      for (i = 0; i < n_keycodes; ++i)
+        {
+          combos[i].keysym = XK_ISO_Next_Group;
+          combos[i].keycode = keycodes[i];
+          combos[i].modifiers = Mod1Mask;
+        }
+    }
+  else if (g_str_equal (iso_next_group_option, "ctrl_shift_toggle") ||
+           g_str_equal (iso_next_group_option, "lctrl_lshift_toggle") ||
+           g_str_equal (iso_next_group_option, "rctrl_rshift_toggle"))
+    {
+      n_combos = n_keycodes * 2;
+      combos = g_new (MetaKeyCombo, n_combos);
+
+      for (i = 0; i < n_keycodes; ++i)
+        {
+          combos[i].keysym = XK_ISO_Next_Group;
+          combos[i].keycode = keycodes[i];
+          combos[i].modifiers = ShiftMask;
+
+          combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
+          combos[i + n_keycodes].keycode = keycodes[i];
+          combos[i + n_keycodes].modifiers = ControlMask;
+        }
+    }
+  else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle"))
+    {
+      n_combos = n_keycodes * 2;
+      combos = g_new (MetaKeyCombo, n_combos);
+
+      for (i = 0; i < n_keycodes; ++i)
+        {
+          combos[i].keysym = XK_ISO_Next_Group;
+          combos[i].keycode = keycodes[i];
+          combos[i].modifiers = Mod1Mask;
+
+          combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
+          combos[i + n_keycodes].keycode = keycodes[i];
+          combos[i + n_keycodes].modifiers = ControlMask;
+        }
+    }
+  else if (g_str_equal (iso_next_group_option, "alt_shift_toggle") ||
+           g_str_equal (iso_next_group_option, "lalt_lshift_toggle"))
+    {
+      n_combos = n_keycodes * 2;
+      combos = g_new (MetaKeyCombo, n_combos);
+
+      for (i = 0; i < n_keycodes; ++i)
+        {
+          combos[i].keysym = XK_ISO_Next_Group;
+          combos[i].keycode = keycodes[i];
+          combos[i].modifiers = Mod1Mask;
+
+          combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
+          combos[i + n_keycodes].keycode = keycodes[i];
+          combos[i + n_keycodes].modifiers = ShiftMask;
+        }
+    }
+  else
+    {
+      n_combos = 0;
+      combos = NULL;
+    }
+
+  g_free (keycodes);
+
+  n_iso_next_group_combos = n_combos;
+  iso_next_group_combos = combos;
+}
+
 static guint
 keysym_to_keycode (MetaDisplay *display,
                    guint        keysym)
@@ -327,6 +493,8 @@ reload_keycodes (MetaDisplay *display)
       display->overlay_key_combo.keycode = 0;
     }
 
+  reload_iso_next_group_combos (display);
+
   if (display->key_bindings)
     {
       int i;
@@ -528,6 +696,9 @@ rebuild_special_bindings (MetaDisplay *display)
 
   meta_prefs_get_overlay_binding (&combo);
   display->overlay_key_combo = combo;
+
+  g_free (iso_next_group_option);
+  iso_next_group_option = meta_prefs_get_iso_next_group_option ();
 }
 
 static void
@@ -1030,6 +1201,22 @@ meta_screen_grab_keys (MetaScreen *screen)
                    display->overlay_key_combo.keycode,
                    display->overlay_key_combo.modifiers);
 
+  if (iso_next_group_combos)
+    {
+      int i = 0;
+      while (i < n_iso_next_group_combos)
+        {
+          if (iso_next_group_combos[i].keycode != 0)
+            {
+              meta_grab_key (display, screen->xroot,
+                             iso_next_group_combos[i].keysym,
+                             iso_next_group_combos[i].keycode,
+                             iso_next_group_combos[i].modifiers);
+            }
+          ++i;
+        }
+    }
+
   grab_keys (screen->display->key_bindings,
              screen->display->n_key_bindings,
              screen->display, screen->xroot,
@@ -1788,6 +1975,38 @@ process_overlay_key (MetaDisplay *display,
     return FALSE;
 }
 
+static gboolean
+process_iso_next_group (MetaDisplay *display,
+                        MetaScreen *screen,
+                        XIDeviceEvent *event,
+                        KeySym keysym)
+{
+  gboolean activate;
+  unsigned int mods;
+  int i;
+
+  if (event->evtype != XI_KeyPress)
+    return FALSE;
+
+  activate = FALSE;
+  mods = (event->mods.effective & 0xff & ~(display->ignored_modifier_mask));
+
+  for (i = 0; i < n_iso_next_group_combos; ++i)
+    {
+      if (event->detail == (int)iso_next_group_combos[i].keycode &&
+          mods == iso_next_group_combos[i].modifiers)
+        {
+          activate = TRUE;
+          break;
+        }
+    }
+
+  if (activate)
+      meta_display_modifiers_accelerator_activate (display);
+
+  return activate;
+}
+
 /* Handle a key event. May be called recursively: some key events cause
  * grabs to be ended and then need to be processed again in their own
  * right. This cannot cause infinite recursion because we never call
@@ -1862,6 +2081,10 @@ meta_display_process_key_event (MetaDisplay   *display,
       handled = process_overlay_key (display, screen, event, keysym);
       if (handled)
         return TRUE;
+
+      handled = process_iso_next_group (display, screen, event, keysym);
+      if (handled)
+        return TRUE;
     }
 
   XIAllowEvents (display->xdisplay, event->deviceid,
@@ -4556,6 +4779,12 @@ meta_display_init_keys (MetaDisplay *display)
   g_hash_table_insert (key_handlers, g_strdup ("overlay-key"), handler);
 
   handler = g_new0 (MetaKeyHandler, 1);
+  handler->name = g_strdup ("iso-next-group");
+  handler->flags = META_KEY_BINDING_BUILTIN;
+
+  g_hash_table_insert (key_handlers, g_strdup ("iso-next-group"), handler);
+
+  handler = g_new0 (MetaKeyHandler, 1);
   handler->name = g_strdup ("external-grab");
   handler->func = handle_external_grab;
   handler->default_func = handle_external_grab;
diff --git a/src/core/prefs.c b/src/core/prefs.c
index 288a312..a780f86 100644
--- a/src/core/prefs.c
+++ b/src/core/prefs.c
@@ -55,6 +55,7 @@
 #define KEY_GNOME_ANIMATIONS "enable-animations"
 #define KEY_GNOME_CURSOR_THEME "cursor-theme"
 #define KEY_GNOME_CURSOR_SIZE "cursor-size"
+#define KEY_XKB_OPTIONS "xkb-options"
 
 #define KEY_OVERLAY_KEY "overlay-key"
 #define KEY_WORKSPACES_ONLY_ON_PRIMARY "workspaces-only-on-primary"
@@ -65,6 +66,7 @@
 #define SCHEMA_GENERAL         "org.gnome.desktop.wm.preferences"
 #define SCHEMA_MUTTER          "org.gnome.mutter"
 #define SCHEMA_INTERFACE       "org.gnome.desktop.interface"
+#define SCHEMA_INPUT_SOURCES   "org.gnome.desktop.input-sources"
 
 #define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s))
 
@@ -115,6 +117,7 @@ static gboolean workspaces_only_on_primary = FALSE;
 
 static gboolean no_tab_popup = FALSE;
 
+static char *iso_next_group_option = NULL;
 
 static void handle_preference_update_enum (GSettings *settings,
                                            gchar     *key);
@@ -140,6 +143,7 @@ static gboolean theme_name_handler (GVariant*, gpointer*, gpointer);
 static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer);
 static gboolean button_layout_handler (GVariant*, gpointer*, gpointer);
 static gboolean overlay_key_handler (GVariant*, gpointer*, gpointer);
+static gboolean iso_next_group_handler (GVariant*, gpointer*, gpointer);
 
 static void     do_override               (char *key, char *schema);
 
@@ -433,6 +437,14 @@ static MetaStringPreference preferences_string[] =
       overlay_key_handler,
       NULL,
     },
+    {
+      { "xkb-options",
+        SCHEMA_INPUT_SOURCES,
+        META_PREF_KEYBINDINGS,
+      },
+      iso_next_group_handler,
+      NULL,
+    },
     { { NULL, 0, 0 }, NULL },
   };
 
@@ -857,6 +869,11 @@ meta_prefs_init (void)
                     G_CALLBACK (settings_changed), NULL);
   g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings);
 
+  settings = g_settings_new (SCHEMA_INPUT_SOURCES);
+  g_signal_connect (settings, "changed::" KEY_XKB_OPTIONS,
+                    G_CALLBACK (settings_changed), NULL);
+  g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INPUT_SOURCES), settings);
+
 
   for (tmp = overridden_keys; tmp; tmp = tmp->next)
     {
@@ -1035,7 +1052,8 @@ settings_changed (GSettings *settings,
     handle_preference_update_bool (settings, key);
   else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
     handle_preference_update_int (settings, key);
-  else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
+  else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING) ||
+           g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY))
     {
       cursor = preferences_enum;
       found_enum = FALSE;
@@ -1554,6 +1572,44 @@ overlay_key_handler (GVariant *value,
   return TRUE;
 }
 
+static void
+set_iso_next_group_option (const char *option)
+{
+  if (g_strcmp0 (option, iso_next_group_option) == 0)
+    return;
+
+  g_free (iso_next_group_option);
+  iso_next_group_option = g_strdup (option);
+
+  queue_changed (META_PREF_KEYBINDINGS);
+}
+
+static gboolean
+iso_next_group_handler (GVariant *value,
+                        gpointer *result,
+                        gpointer  data)
+{
+  const char **xkb_options, **p;
+
+  *result = NULL; /* ignored */
+  xkb_options = g_variant_get_strv (value, NULL);
+
+  for (p = xkb_options; p && *p; ++p)
+    if (g_str_has_prefix (*p, "grp:"))
+      {
+        set_iso_next_group_option (*p + 4);
+        break;
+      }
+
+  /* If we didn't find it, it still needs to be disabled. */
+  if (p && *p == NULL)
+    set_iso_next_group_option (NULL);
+
+  g_free (xkb_options);
+
+  return TRUE;
+}
+
 const PangoFontDescription*
 meta_prefs_get_titlebar_font (void)
 {
@@ -2097,6 +2153,12 @@ meta_prefs_get_overlay_binding (MetaKeyCombo *combo)
   *combo = overlay_key_combo;
 }
 
+char *
+meta_prefs_get_iso_next_group_option (void)
+{
+  return g_strdup (iso_next_group_option);
+}
+
 GDesktopTitlebarAction
 meta_prefs_get_action_double_click_titlebar (void)
 {
diff --git a/src/meta/prefs.h b/src/meta/prefs.h
index effb252..4429707 100644
--- a/src/meta/prefs.h
+++ b/src/meta/prefs.h
@@ -353,6 +353,7 @@ typedef enum _MetaKeyBindingAction
   META_KEYBINDING_ACTION_MOVE_TO_SIDE_W,
   META_KEYBINDING_ACTION_MOVE_TO_CENTER,
   META_KEYBINDING_ACTION_OVERLAY_KEY,
+  META_KEYBINDING_ACTION_ISO_NEXT_GROUP,
 
   META_KEYBINDING_ACTION_LAST
 } MetaKeyBindingAction;
@@ -442,6 +443,7 @@ void meta_prefs_get_window_binding (const char          *name,
                                     MetaVirtualModifier *modifiers);
 
 void meta_prefs_get_overlay_binding (MetaKeyCombo *combo);
+char *meta_prefs_get_iso_next_group_option (void);
 
 gboolean           meta_prefs_get_visual_bell      (void);
 gboolean           meta_prefs_bell_is_audible      (void);


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