[mutter] keybindings: Allow to add/remove keybindings at runtime



commit 0e50287aea24af9a165a78856da05c1eb1a63e04
Author: Florian MÃllner <fmuellner gnome org>
Date:   Fri Nov 4 19:18:57 2011 +0100

    keybindings: Allow to add/remove keybindings at runtime
    
    Add meta_display_add_keybinding()/meta_display_remove_keybinding(),
    which allow to add/remove keybindings dynamically at runtime.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=663428

 src/core/keybindings-private.h |    3 +
 src/core/keybindings.c         |  144 ++++++++++++++++++++++++++++++++++++++--
 src/core/prefs.c               |   52 ++++++++++++++-
 src/meta/display.h             |   10 +++
 src/meta/keybindings.h         |    5 ++
 src/meta/prefs.h               |   14 +++-
 6 files changed, 214 insertions(+), 14 deletions(-)
---
diff --git a/src/core/keybindings-private.h b/src/core/keybindings-private.h
index a3dbfcc..507b162 100644
--- a/src/core/keybindings-private.h
+++ b/src/core/keybindings-private.h
@@ -77,6 +77,9 @@ gboolean meta_prefs_add_keybinding          (const char           *name,
                                              MetaKeyBindingAction  action,
                                              MetaKeyBindingFlags   flags);
 
+gboolean meta_prefs_remove_keybinding       (const char    *name);
+
+
 #endif
 
 
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 8897eac..5510d28 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -59,6 +59,49 @@ static gboolean add_builtin_keybinding (MetaDisplay          *display,
                                         MetaKeyHandlerFunc    handler,
                                         int                   handler_arg);
 
+static void
+meta_key_binding_free (MetaKeyBinding *binding)
+{
+  g_slice_free (MetaKeyBinding, binding);
+}
+
+static MetaKeyBinding *
+meta_key_binding_copy (MetaKeyBinding *binding)
+{
+  return g_slice_dup (MetaKeyBinding, binding);
+}
+
+GType
+meta_key_binding_get_type (void)
+{
+  static GType type_id = 0;
+
+  if (G_UNLIKELY (type_id == 0))
+    type_id = g_boxed_type_register_static (g_intern_static_string ("MetaKeyBinding"),
+                                            (GBoxedCopyFunc)meta_key_binding_copy,
+                                            (GBoxedFreeFunc)meta_key_binding_free);
+
+  return type_id;
+}
+
+const char *
+meta_key_binding_get_name (MetaKeyBinding *binding)
+{
+  return binding->name;
+}
+
+MetaVirtualModifier
+meta_key_binding_get_modifiers (MetaKeyBinding *binding)
+{
+  return binding->modifiers;
+}
+
+guint
+meta_key_binding_get_mask (MetaKeyBinding *binding)
+{
+  return binding->mask;
+}
+
 /* These can't be bound to anything, but they are used to handle
  * various other events.  TODO: Possibly we should include them as event
  * handler functions and have some kind of flag to say they're unbindable.
@@ -501,13 +544,15 @@ display_get_keybinding (MetaDisplay  *display,
 }
 
 static gboolean
-add_builtin_keybinding (MetaDisplay          *display,
-                        const char           *name,
-                        const char           *schema,
-                        MetaKeyBindingFlags   flags,
-                        MetaKeyBindingAction  action,
-                        MetaKeyHandlerFunc    func,
-                        int                   data)
+add_keybinding_internal (MetaDisplay          *display,
+                         const char           *name,
+                         const char           *schema,
+                         MetaKeyBindingFlags   flags,
+                         MetaKeyBindingAction  action,
+                         MetaKeyHandlerFunc    func,
+                         int                   data,
+                         gpointer              user_data,
+                         GDestroyNotify        free_data)
 {
   MetaKeyHandler *handler;
 
@@ -520,18 +565,103 @@ add_builtin_keybinding (MetaDisplay          *display,
   handler->default_func = func;
   handler->data = data;
   handler->flags = flags;
+  handler->user_data = user_data;
+  handler->user_data_free_func = free_data;
 
   g_hash_table_insert (key_handlers, g_strdup (name), handler);
 
   return TRUE;
 }
 
+static gboolean
+add_builtin_keybinding (MetaDisplay          *display,
+                        const char           *name,
+                        const char           *schema,
+                        MetaKeyBindingFlags   flags,
+                        MetaKeyBindingAction  action,
+                        MetaKeyHandlerFunc    handler,
+                        int                   handler_arg)
+{
+  return add_keybinding_internal (display, name, schema,
+                                  flags | META_KEY_BINDING_BUILTIN,
+                                  action, handler, handler_arg, NULL, NULL);
+}
+
+/**
+ * meta_display_add_keybinding:
+ * @display: a #MetaDisplay
+ * @name: the binding's name
+ * @schema: the #GSettings schema where @name is stored
+ * @flags: flags to specify binding details
+ * @handler: function to run when the keybinding is invoked
+ * @user_data: the data to pass to @handler
+ * @free_data: function to free @user_data
+ *
+ * Add a keybinding at runtime. The key @name in @schema needs to be of
+ * type %G_VARIANT_TYPE_STRING_ARRAY, with each string describing a
+ * keybinding in the form of "<Control>a" or "<Shift><Alt>F1". The parser
+ * is fairly liberal and allows lower or upper case, and also abbreviations
+ * such as "<Ctl>" and "<Ctrl>". If the key is set to the empty list or a
+ * list with a single element of either "" or "disabled", the keybinding is
+ * disabled.
+ * If %META_KEY_BINDING_REVERSES is specified in @flags, the binding
+ * may be reversed by holding down the "shift" key; therefore, "<Shift>"
+ * cannot be one of the keys used. @handler is expected to check for the
+ * "shift" modifier in this case and reverse its action.
+ *
+ * Use meta_display_remove_keybinding() to remove the binding.
+ *
+ * Returns: %TRUE if the keybinding was added successfully,
+ *          otherwise %FALSE
+ */
+gboolean
+meta_display_add_keybinding (MetaDisplay         *display,
+                             const char          *name,
+                             const char          *schema,
+                             MetaKeyBindingFlags  flags,
+                             MetaKeyHandlerFunc   handler,
+                             gpointer             user_data,
+                             GDestroyNotify       free_data)
+{
+  return add_keybinding_internal (display, name, schema, flags,
+                                  META_KEYBINDING_ACTION_NONE,
+                                  handler, 0, user_data, free_data);
+}
+
+/**
+ * meta_display_remove_keybinding:
+ * @display: the #MetaDisplay
+ * @name: name of the keybinding to remove
+ *
+ * Remove keybinding @name; the function will fail if @name is not a known
+ * keybinding or has not been added with meta_display_add_keybinding().
+ *
+ * Returns: %TRUE if the binding has been removed sucessfully,
+ *          otherwise %FALSE
+ */
+gboolean
+meta_display_remove_keybinding (MetaDisplay *display,
+                                const char  *name)
+{
+  if (!meta_prefs_remove_keybinding (name))
+    return FALSE;
+
+  g_hash_table_remove (key_handlers, name);
+
+  return TRUE;
+}
+
 /**
  * meta_display_get_keybinding_action:
  * @display: A #MetaDisplay
  * @keycode: Raw keycode
  * @mask: Event mask
  *
+ * Get the #MetaKeyBindingAction bound to %keycode. Only builtin
+ * keybindings have an associated #MetaKeyBindingAction, for
+ * bindings added dynamically with meta_display_add_keybinding()
+ * the function will always return %META_KEYBINDING_ACTION_NONE.
+ *
  * Returns: The action that should be taken for the given key, or
  * %META_KEYBINDING_ACTION_NONE.
  */
diff --git a/src/core/prefs.c b/src/core/prefs.c
index 3455de0..0d3fd59 100644
--- a/src/core/prefs.c
+++ b/src/core/prefs.c
@@ -1919,8 +1919,9 @@ meta_prefs_add_keybinding (const char           *name,
   if (settings == NULL)
     {
       settings = g_settings_new (schema);
-      g_signal_connect (settings, "changed",
-                        G_CALLBACK (bindings_changed), NULL);
+      if ((flags & META_KEY_BINDING_BUILTIN) != 0)
+        g_signal_connect (settings, "changed",
+                          G_CALLBACK (bindings_changed), NULL);
       g_hash_table_insert (settings_schemas, g_strdup (schema), settings);
     }
 
@@ -1931,6 +1932,7 @@ meta_prefs_add_keybinding (const char           *name,
   pref->bindings = NULL;
   pref->add_shift = (flags & META_KEY_BINDING_REVERSES) != 0;
   pref->per_window = (flags & META_KEY_BINDING_PER_WINDOW) != 0;
+  pref->builtin = (flags & META_KEY_BINDING_BUILTIN) != 0;
 
   strokes = g_settings_get_strv (settings, name);
   update_binding (pref, strokes);
@@ -1938,11 +1940,55 @@ meta_prefs_add_keybinding (const char           *name,
 
   g_hash_table_insert (key_bindings, g_strdup (name), pref);
 
+  if (!pref->builtin)
+    {
+      guint id;
+      char *changed_signal = g_strdup_printf ("changed::%s", name);
+      id = g_signal_connect (settings, changed_signal,
+                             G_CALLBACK (bindings_changed), NULL);
+      g_free (changed_signal);
+
+      g_object_set_data (G_OBJECT (settings), name, GUINT_TO_POINTER (id));
+
+      queue_changed (META_PREF_KEYBINDINGS);
+    }
+
+  return TRUE;
+}
+
+gboolean
+meta_prefs_remove_keybinding (const char *name)
+{
+  MetaKeyPref *pref;
+  GSettings   *settings;
+  guint        id;
+
+  pref = g_hash_table_lookup (key_bindings, name);
+  if (!pref)
+    {
+      meta_warning ("Trying to remove non-existent keybinding \"%s\".\n", name);
+      return FALSE;
+    }
+
+  if (pref->builtin)
+    {
+      meta_warning ("Trying to remove builtin keybinding \"%s\".\n", name);
+      return FALSE;
+    }
+
+  settings = SETTINGS (pref->schema);
+  id = GPOINTER_TO_UINT (g_object_steal_data (G_OBJECT (settings), name));
+  g_signal_handler_disconnect (settings, id);
+
+  g_hash_table_remove (key_bindings, name);
+
+  queue_changed (META_PREF_KEYBINDINGS);
+
   return TRUE;
 }
 
 /**
- * meta_prefs_get_keybindings: (skip)
+ * meta_prefs_get_keybindings:
  * Return: (element-type MetaKeyPref) (transfer container):
  */
 GList *
diff --git a/src/meta/display.h b/src/meta/display.h
index feeb12c..a9edc00 100644
--- a/src/meta/display.h
+++ b/src/meta/display.h
@@ -124,6 +124,16 @@ void     meta_display_end_grab_op   (MetaDisplay *display,
 
 MetaGrabOp meta_display_get_grab_op (MetaDisplay *display);
 
+gboolean meta_display_add_keybinding    (MetaDisplay         *display,
+                                         const char          *name,
+                                         const char          *schema,
+                                         MetaKeyBindingFlags  flags,
+                                         MetaKeyHandlerFunc   handler,
+                                         gpointer             user_data,
+                                         GDestroyNotify       free_data);
+gboolean meta_display_remove_keybinding (MetaDisplay         *display,
+                                         const char          *name);
+
 MetaKeyBindingAction meta_display_get_keybinding_action (MetaDisplay  *display,
                                                          unsigned int  keycode,
                                                          unsigned long mask);
diff --git a/src/meta/keybindings.h b/src/meta/keybindings.h
index 7b5171b..862e1b2 100644
--- a/src/meta/keybindings.h
+++ b/src/meta/keybindings.h
@@ -23,6 +23,11 @@
 #include <meta/display.h>
 #include <meta/common.h>
 
+#define META_TYPE_KEY_BINDING               (meta_key_binding_get_type ())
+
+const char          *meta_key_binding_get_name      (MetaKeyBinding *binding);
+MetaVirtualModifier  meta_key_binding_get_modifiers (MetaKeyBinding *binding);
+guint                meta_key_binding_get_mask      (MetaKeyBinding *binding);
 
 gboolean meta_keybindings_set_custom_handler (const gchar        *name,
 					      MetaKeyHandlerFunc  handler,
diff --git a/src/meta/prefs.h b/src/meta/prefs.h
index d973e4b..6007c7c 100644
--- a/src/meta/prefs.h
+++ b/src/meta/prefs.h
@@ -238,8 +238,9 @@ typedef enum
 {
   META_KEY_BINDING_NONE,
   META_KEY_BINDING_PER_WINDOW  = 1 << 0,
-  META_KEY_BINDING_REVERSES    = 1 << 1,
-  META_KEY_BINDING_IS_REVERSED = 1 << 2
+  META_KEY_BINDING_BUILTIN     = 1 << 1,
+  META_KEY_BINDING_REVERSES    = 1 << 2,
+  META_KEY_BINDING_IS_REVERSED = 1 << 3
 } MetaKeyBindingFlags;
 
 typedef struct
@@ -250,7 +251,8 @@ typedef struct
 } MetaKeyCombo;
 
 /**
- * MetaKeyHandlerFunc: (skip)
+ * MetaKeyHandlerFunc:
+ * @event: (type gpointer):
  *
  */
 typedef void (* MetaKeyHandlerFunc) (MetaDisplay    *display,
@@ -262,7 +264,6 @@ typedef void (* MetaKeyHandlerFunc) (MetaDisplay    *display,
 
 typedef struct _MetaKeyHandler MetaKeyHandler;
 
-
 typedef struct
 {
   char *name;
@@ -282,8 +283,13 @@ typedef struct
 
   /** for keybindings that apply only to a window */
   gboolean      per_window:1;
+
+  /** for keybindings not added with meta_display_add_keybinding() */
+  gboolean      builtin:1;
 } MetaKeyPref;
 
+GType meta_key_binding_get_type    (void);
+
 GList *meta_prefs_get_keybindings (void);
 
 MetaKeyBindingAction meta_prefs_get_keybinding_action (const char *name);



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