From 0e50287aea24af9a165a78856da05c1eb1a63e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Fri, 4 Nov 2011 19:18:57 +0100 Subject: [PATCH] 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 a3dbfcc2d..507b162f2 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 8897eacff..5510d2876 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 "a" or "F1". The parser + * is fairly liberal and allows lower or upper case, and also abbreviations + * such as "" and "". 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, "" + * 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 3455de0af..0d3fd5900 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 feeb12c6c..a9edc003d 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 7b5171bff..862e1b214 100644 --- a/src/meta/keybindings.h +++ b/src/meta/keybindings.h @@ -23,6 +23,11 @@ #include #include +#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 d973e4b22..6007c7c03 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);