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
This commit is contained in:
Florian Müllner 2011-11-04 19:18:57 +01:00
parent d42a2a3c27
commit 0e50287aea
6 changed files with 214 additions and 14 deletions

View File

@ -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

View File

@ -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.
*/

View File

@ -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 *

View File

@ -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);

View File

@ -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,

View File

@ -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);