[mutter] keybindings: Grab and emit a signal when XK_ISO_Next_Group is pressed



commit 2af49e503f1e5db3e9af867199c36e337d7d0fd2
Author: Rui Matos <tiagomatos gmail com>
Date:   Wed Apr 10 13:34:26 2013 +0200

    keybindings: 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 |    3 +
 src/core/display.c         |   30 ++++++
 src/core/keybindings.c     |  229 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 262 insertions(+), 0 deletions(-)
---
diff --git a/src/core/display-private.h b/src/core/display-private.h
index c9a7d23..a311317 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -237,6 +237,8 @@ struct _MetaDisplay
   unsigned int meta_mask;
   MetaKeyCombo overlay_key_combo;
   gboolean overlay_key_only_pressed;
+  MetaKeyCombo *iso_next_group_combos;
+  int n_iso_next_group_combos;
   
   /* Monitor cache */
   unsigned int monitor_cache_invalidated : 1;
@@ -456,6 +458,7 @@ void meta_display_overlay_key_activate (MetaDisplay *display);
 void meta_display_accelerator_activate (MetaDisplay *display,
                                         guint        action,
                                         guint        deviceid);
+gboolean 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 aa3338a..60cb00c 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,25 @@ meta_display_class_init (MetaDisplayClass *klass)
                   NULL, NULL, NULL,
                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
 
+  /**
+   * MetaDisplay::modifiers-accelerator-activated:
+   * @display: the #MetaDisplay instance
+   *
+   * The ::modifiers-accelerator-activated signal will be emitted when
+   * a special modifiers-only keybinding is activated.
+   *
+   * Returns: %TRUE means that the keyboard device should remain
+   *    frozen and %FALSE for the default behavior of unfreezing the
+   *    keyboard.
+   */
+  display_signals[MODIFIERS_ACCELERATOR_ACTIVATED] =
+    g_signal_new ("modifiers-accelerator-activated",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  g_signal_accumulator_first_wins, NULL, NULL,
+                  G_TYPE_BOOLEAN, 0);
+
   display_signals[WINDOW_CREATED] =
     g_signal_new ("window-created",
                   G_TYPE_FROM_CLASS (klass),
@@ -5855,6 +5875,16 @@ meta_display_accelerator_activate (MetaDisplay *display,
                  0, action, deviceid);
 }
 
+gboolean
+meta_display_modifiers_accelerator_activate (MetaDisplay *display)
+{
+  gboolean freeze;
+
+  g_signal_emit (display, display_signals[MODIFIERS_ACCELERATOR_ACTIVATED], 0, &freeze);
+
+  return freeze;
+}
+
 void
 meta_display_get_compositor_version (MetaDisplay *display,
                                      int         *major,
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 94f86dc..baceb6d 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -302,6 +302,172 @@ reload_modmap (MetaDisplay *display)
               display->meta_mask);
 }
 
+/* Original code from gdk_x11_keymap_get_entries_for_keyval() in
+ * gdkkeys-x11.c */
+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)
+{
+  const char *iso_next_group_option;
+  MetaKeyCombo *combos;
+  int *keycodes;
+  int n_keycodes;
+  int n_combos;
+  int i;
+
+  g_clear_pointer (&display->iso_next_group_combos, g_free);
+  display->n_iso_next_group_combos = 0;
+
+  iso_next_group_option = meta_prefs_get_iso_next_group_option ();
+  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);
+
+  display->n_iso_next_group_combos = n_combos;
+  display->iso_next_group_combos = combos;
+}
+
 static guint
 keysym_to_keycode (MetaDisplay *display,
                    guint        keysym)
@@ -328,6 +494,8 @@ reload_keycodes (MetaDisplay *display)
       display->overlay_key_combo.keycode = 0;
     }
 
+  reload_iso_next_group_combos (display);
+
   if (display->key_bindings)
     {
       int i;
@@ -1026,6 +1194,22 @@ meta_screen_change_keygrabs (MetaScreen *screen,
                          display->overlay_key_combo.keycode,
                          display->overlay_key_combo.modifiers);
 
+  if (display->iso_next_group_combos)
+    {
+      int i = 0;
+      while (i < display->n_iso_next_group_combos)
+        {
+          if (display->iso_next_group_combos[i].keycode != 0)
+            {
+              meta_change_keygrab (display, screen->xroot, grab,
+                                   display->iso_next_group_combos[i].keysym,
+                                   display->iso_next_group_combos[i].keycode,
+                                   display->iso_next_group_combos[i].modifiers);
+            }
+          ++i;
+        }
+    }
+
   change_binding_keygrabs (screen->display->key_bindings,
                            screen->display->n_key_bindings,
                            screen->display, screen->xroot,
@@ -1801,6 +1985,41 @@ 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 < display->n_iso_next_group_combos; ++i)
+    {
+      if (event->detail == (int)display->iso_next_group_combos[i].keycode &&
+          mods == display->iso_next_group_combos[i].modifiers)
+        {
+          /* If the signal handler returns TRUE the keyboard will
+             remain frozen. It's the signal handler's responsibility
+             to unfreeze it. */
+          if (!meta_display_modifiers_accelerator_activate (display))
+            XIAllowEvents (display->xdisplay, event->deviceid,
+                           XIAsyncDevice, event->time);
+          activate = TRUE;
+          break;
+        }
+    }
+
+  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
@@ -1875,6 +2094,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,
@@ -4569,6 +4792,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;


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