Commit d42a2a3c authored by Florian Müllner's avatar Florian Müllner
Browse files

keybindings: Store keybindings dynamically

Rather than defining keybindings in static arrays generated at compile
time, store them in a hash table initialized in meta_display_init_keys()
and filled in init_builtin_keybindings().

This is a prerequisite for allowing to add/remove keybindings at runtime.

https://bugzilla.gnome.org/show_bug.cgi?id=663428
parent 78849bef
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
src/compositor/compositor.c
src/core/all-keybindings.h
src/core/bell.c
src/core/core.c
src/core/delete.c
......
......@@ -152,7 +152,6 @@ libmutter_la_SOURCES = \
meta/theme.h \
ui/theme-private.h \
ui/ui.c \
core/all-keybindings.h \
meta/preview-widget.h \
ui/preview-widget.c \
$(mutter_built_sources)
......
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2008 Thomas Thurman
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
/**
* A list of screen keybinding information.
*
* Each action which can have a keystroke bound to it is listed below.
* To use this file, define keybind() to be a seven-argument macro (you can
* throw any of the arguments you please away), include this file,
* and then undefine the macro again.
*
* (If you aren't familiar with this technique, sometimes called "x-macros",
* see DDJ of May 2001: <http://www.ddj.com/cpp/184401387>.)
*
* This makes it possible to keep all information about all the keybindings
* in the same place. The only exception is the code to run when an action
* is actually invoked; while we *could* have put that in this file, it would
* have made debugging ridiculously difficult. Instead, each action should
* have a corresponding static function named handle_<name>() in
* keybindings.c.
*
* The arguments to keybind() are:
* 1) the name of the binding; a bareword identifier
* (it's fine if it happens to clash with a C reserved word)
* 2) the name of the function which implements it.
* Clearly we could have guessed this from the binding very often,
* but we choose to write it in full for the benefit of grep.
* 3) an integer parameter to pass to the handler
* 4) a set of boolean flags, ORed together:
* BINDING_PER_WINDOW - this is a window-based binding.
* It is only valid if there is a
* current window, and will operate in
* some way on that window.
* BINDING_REVERSES - the binding can reverse if you hold down Shift
* BINDING_IS_REVERSED - the same, but the senses are reversed from the
* handler's point of view (let me know if I should
* explain this better)
* or 0 if no flag applies.
*
* Don't try to do XML entity escaping anywhere in the strings.
*/
#ifndef keybind
#error "keybind () must be defined when you include screen-bindings.h"
#endif
/***********************************/
#ifndef _BINDINGS_DEFINED_CONSTANTS
#define _BINDINGS_DEFINED_CONSTANTS 1
#define BINDING_PER_WINDOW 0x01
#define BINDING_REVERSES 0x02
#define BINDING_IS_REVERSED 0x04
#endif /* _BINDINGS_DEFINED_CONSTANTS */
/***********************************/
/* convenience, since in this file they must always be set together */
#define REVERSES_AND_REVERSED (BINDING_REVERSES | BINDING_IS_REVERSED)
keybind (switch-to-workspace-1, handle_switch_to_workspace, 0, 0)
keybind (switch-to-workspace-2, handle_switch_to_workspace, 1, 0)
keybind (switch-to-workspace-3, handle_switch_to_workspace, 2, 0)
keybind (switch-to-workspace-4, handle_switch_to_workspace, 3, 0)
keybind (switch-to-workspace-5, handle_switch_to_workspace, 4, 0)
keybind (switch-to-workspace-6, handle_switch_to_workspace, 5, 0)
keybind (switch-to-workspace-7, handle_switch_to_workspace, 6, 0)
keybind (switch-to-workspace-8, handle_switch_to_workspace, 7, 0)
keybind (switch-to-workspace-9, handle_switch_to_workspace, 8, 0)
keybind (switch-to-workspace-10, handle_switch_to_workspace, 9, 0)
keybind (switch-to-workspace-11, handle_switch_to_workspace, 10, 0)
keybind (switch-to-workspace-12, handle_switch_to_workspace, 11, 0)
/* META_MOTION_* are negative, and so distinct from workspace numbers,
* which are always zero or positive.
* If you make use of these constants, you will need to include workspace.h
* (which you're probably using already for other reasons anyway).
* If your definition of keybind() throws them away, you don't need to include
* workspace.h, of course.
*/
keybind (switch-to-workspace-left, handle_switch_to_workspace,
META_MOTION_LEFT, 0)
keybind (switch-to-workspace-right, handle_switch_to_workspace,
META_MOTION_RIGHT, 0)
keybind (switch-to-workspace-up, handle_switch_to_workspace,
META_MOTION_UP, 0)
keybind (switch-to-workspace-down, handle_switch_to_workspace,
META_MOTION_DOWN, 0)
/***********************************/
/* The ones which have inverses. These can't be bound to any keystroke
* containing Shift because Shift will invert their "backward" state.
*
* TODO: "NORMAL" and "DOCKS" should be renamed to the same name as their
* action, for obviousness.
*
* TODO: handle_switch and handle_cycle should probably really be the
* same function checking a bit in the parameter for difference.
*/
keybind (switch-group, handle_switch, META_TAB_LIST_GROUP, BINDING_REVERSES)
keybind (switch-group-backward, handle_switch,
META_TAB_LIST_GROUP, REVERSES_AND_REVERSED)
keybind (switch-windows, handle_switch, META_TAB_LIST_NORMAL, BINDING_REVERSES)
keybind (switch-windows-backward, handle_switch, META_TAB_LIST_NORMAL,
REVERSES_AND_REVERSED)
keybind (switch-panels, handle_switch, META_TAB_LIST_DOCKS, BINDING_REVERSES)
keybind (switch-panels-backward, handle_switch, META_TAB_LIST_DOCKS,
REVERSES_AND_REVERSED)
keybind (cycle-group, handle_cycle, META_TAB_LIST_GROUP, BINDING_REVERSES)
keybind (cycle-group-backward, handle_cycle, META_TAB_LIST_GROUP,
REVERSES_AND_REVERSED)
keybind (cycle-windows, handle_cycle, META_TAB_LIST_NORMAL, BINDING_REVERSES)
keybind (cycle-windows-backward, handle_cycle, META_TAB_LIST_NORMAL,
REVERSES_AND_REVERSED)
keybind (cycle-panels, handle_cycle, META_TAB_LIST_DOCKS, BINDING_REVERSES)
keybind (cycle-panels-backward, handle_cycle, META_TAB_LIST_DOCKS,
REVERSES_AND_REVERSED)
/***********************************/
/* These two are special pseudo-bindings that are provided for allowing
* custom handlers, but will never be bound to a key. While a tab
* grab is in effect, they are invoked for releasing the primary modifier
* or pressing some unbound key, respectively.
*/
keybind (tab-popup-select, handle_tab_popup_select, 0, 0)
keybind (tab-popup-cancel, handle_tab_popup_cancel, 0, 0)
/***********************************/
keybind (show-desktop, handle_show_desktop, 0, 0)
keybind (panel-main-menu, handle_panel,
META_KEYBINDING_ACTION_PANEL_MAIN_MENU, 0)
keybind (panel-run-dialog, handle_panel,
META_KEYBINDING_ACTION_PANEL_RUN_DIALOG, 0)
keybind (toggle-recording, handle_toggle_recording, 0, 0)
/* FIXME: No description because this is undocumented */
keybind (set-spew-mark, handle_set_spew_mark, 0, 0)
#undef REVERSES_AND_REVERSED
/************************ PER WINDOW BINDINGS ************************/
/* These take a window as an extra parameter; they have no effect
* if no window is active.
*/
keybind (activate-window-menu, handle_activate_window_menu, 0,
BINDING_PER_WINDOW)
keybind (toggle-fullscreen, handle_toggle_fullscreen, 0, BINDING_PER_WINDOW)
keybind (toggle-maximized, handle_toggle_maximized, 0, BINDING_PER_WINDOW)
keybind (toggle-above, handle_toggle_above, 0, BINDING_PER_WINDOW)
keybind (maximize, handle_maximize, 0, BINDING_PER_WINDOW)
keybind (unmaximize, handle_unmaximize, 0, BINDING_PER_WINDOW)
keybind (toggle-shaded, handle_toggle_shaded, 0, BINDING_PER_WINDOW)
keybind (minimize, handle_minimize, 0, BINDING_PER_WINDOW)
keybind (close, handle_close, 0, BINDING_PER_WINDOW)
keybind (begin-move, handle_begin_move, 0, BINDING_PER_WINDOW)
keybind (begin-resize, handle_begin_resize, 0, BINDING_PER_WINDOW)
keybind (toggle-on-all-workspaces, handle_toggle_on_all_workspaces, 0,
BINDING_PER_WINDOW)
keybind (move-to-workspace-1, handle_move_to_workspace, 0, BINDING_PER_WINDOW)
keybind (move-to-workspace-2, handle_move_to_workspace, 1, BINDING_PER_WINDOW)
keybind (move-to-workspace-3, handle_move_to_workspace, 2, BINDING_PER_WINDOW)
keybind (move-to-workspace-4, handle_move_to_workspace, 3, BINDING_PER_WINDOW)
keybind (move-to-workspace-5, handle_move_to_workspace, 4, BINDING_PER_WINDOW)
keybind (move-to-workspace-6, handle_move_to_workspace, 5, BINDING_PER_WINDOW)
keybind (move-to-workspace-7, handle_move_to_workspace, 6, BINDING_PER_WINDOW)
keybind (move-to-workspace-8, handle_move_to_workspace, 7, BINDING_PER_WINDOW)
keybind (move-to-workspace-9, handle_move_to_workspace, 8, BINDING_PER_WINDOW)
keybind (move-to-workspace-10, handle_move_to_workspace, 9, BINDING_PER_WINDOW)
keybind (move-to-workspace-11, handle_move_to_workspace, 10, BINDING_PER_WINDOW)
keybind (move-to-workspace-12, handle_move_to_workspace, 11, BINDING_PER_WINDOW)
/* META_MOTION_* are negative, and so distinct from workspace numbers,
* which are always zero or positive.
* If you make use of these constants, you will need to include workspace.h
* (which you're probably using already for other reasons anyway).
* If your definition of keybind() throws them away, you don't need to include
* workspace.h, of course.
*/
keybind (move-to-workspace-left, handle_move_to_workspace,
META_MOTION_LEFT, BINDING_PER_WINDOW)
keybind (move-to-workspace-right, handle_move_to_workspace,
META_MOTION_RIGHT, BINDING_PER_WINDOW)
keybind (move-to-workspace-up, handle_move_to_workspace,
META_MOTION_UP, BINDING_PER_WINDOW)
keybind (move-to-workspace-down, handle_move_to_workspace,
META_MOTION_DOWN, BINDING_PER_WINDOW)
keybind (raise-or-lower, handle_raise_or_lower, 0, BINDING_PER_WINDOW)
keybind (raise, handle_raise, 0, BINDING_PER_WINDOW)
keybind (lower, handle_lower, 0, BINDING_PER_WINDOW)
keybind (maximize-vertically, handle_maximize_vertically, 0, BINDING_PER_WINDOW)
keybind (maximize-horizontally, handle_maximize_horizontally, 0,
BINDING_PER_WINDOW)
keybind (move-to-corner-nw, handle_move_to_corner_nw, 0, BINDING_PER_WINDOW)
keybind (move-to-corner-ne, handle_move_to_corner_ne, 0, BINDING_PER_WINDOW)
keybind (move-to-corner-sw, handle_move_to_corner_sw, 0, BINDING_PER_WINDOW)
keybind (move-to-corner-se, handle_move_to_corner_se, 0, BINDING_PER_WINDOW)
keybind (move-to-side-n, handle_move_to_side_n, 0, BINDING_PER_WINDOW)
keybind (move-to-side-s, handle_move_to_side_s, 0, BINDING_PER_WINDOW)
keybind (move-to-side-e, handle_move_to_side_e, 0, BINDING_PER_WINDOW)
keybind (move-to-side-w, handle_move_to_side_w, 0, BINDING_PER_WINDOW)
keybind (move-to-center, handle_move_to_center, 0, BINDING_PER_WINDOW)
/* eof all-keybindings.h */
......@@ -31,6 +31,26 @@
#include <meta/keybindings.h>
struct _MetaKeyHandler
{
char *name;
MetaKeyHandlerFunc func;
MetaKeyHandlerFunc default_func;
gint data, flags;
gpointer user_data;
GDestroyNotify user_data_free_func;
};
struct _MetaKeyBinding
{
const char *name;
KeySym keysym;
KeyCode keycode;
unsigned int mask;
MetaVirtualModifier modifiers;
MetaKeyHandler *handler;
};
void meta_display_init_keys (MetaDisplay *display);
void meta_display_shutdown_keys (MetaDisplay *display);
void meta_screen_grab_keys (MetaScreen *screen);
......@@ -52,6 +72,11 @@ void meta_set_keybindings_disabled (gboolean setting);
void meta_display_process_mapping_event (MetaDisplay *display,
XEvent *event);
gboolean meta_prefs_add_keybinding (const char *name,
const char *schema,
MetaKeyBindingAction action,
MetaKeyBindingFlags flags);
#endif
......
......@@ -46,19 +46,18 @@
#include <X11/XKBlib.h>
#endif
#define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings"
#define SCHEMA_MUTTER_KEYBINDINGS "org.gnome.mutter.keybindings"
static gboolean all_bindings_disabled = FALSE;
/* Prototypes for handlers */
#define keybind(name, handler, param, flags) \
static void \
handler (MetaDisplay *display,\
MetaScreen *screen,\
MetaWindow *window,\
XEvent *event,\
MetaKeyBinding *binding, \
gpointer user_data);
#include "all-keybindings.h"
#undef keybind
static gboolean add_builtin_keybinding (MetaDisplay *display,
const char *name,
const char *schema,
MetaKeyBindingFlags flags,
MetaKeyBindingAction action,
MetaKeyHandlerFunc handler,
int handler_arg);
/* These can't be bound to anything, but they are used to handle
* various other events. TODO: Possibly we should include them as event
......@@ -103,13 +102,18 @@ static gboolean process_workspace_switch_grab (MetaDisplay *display,
static void regrab_key_bindings (MetaDisplay *display);
#define keybind(name, handler, param, flags) \
{ #name, handler, handler, param, flags, NULL, NULL },
static MetaKeyHandler key_handlers[] = {
#include "all-keybindings.h"
{ NULL, NULL, NULL, 0, 0, NULL, NULL }
};
#undef keybind
static GHashTable *key_handlers;
#define HANDLER(name) g_hash_table_lookup (key_handlers, (name))
static void
key_handler_free (MetaKeyHandler *handler)
{
g_free (handler->name);
if (handler->user_data_free_func && handler->user_data)
handler->user_data_free_func (handler->user_data);
g_free (handler);
}
static void
reload_keymap (MetaDisplay *display)
......@@ -302,17 +306,17 @@ reload_modifiers (MetaDisplay *display)
static int
count_bindings (const MetaKeyPref *prefs,
int n_prefs)
count_bindings (GList *prefs)
{
int i;
GList *p;
int count;
count = 0;
i = 0;
while (i < n_prefs)
p = prefs;
while (p)
{
GSList *tmp = prefs[i].bindings;
MetaKeyPref *pref = (MetaKeyPref*)p->data;
GSList *tmp = pref->bindings;
while (tmp)
{
......@@ -322,58 +326,40 @@ count_bindings (const MetaKeyPref *prefs,
{
count += 1;
if (prefs[i].add_shift &&
if (pref->add_shift &&
(combo->modifiers & META_VIRTUAL_SHIFT_MASK) == 0)
count += 1;
}
tmp = tmp->next;
}
++i;
}
return count;
}
/* FIXME: replace this with a temporary hash */
static MetaKeyHandler*
find_handler (MetaKeyHandler *handlers,
const char *name)
{
MetaKeyHandler *iter;
iter = handlers;
while (iter->name)
{
if (strcmp (iter->name, name) == 0)
return iter;
++iter;
p = p->next;
}
return NULL;
return count;
}
static void
rebuild_binding_table (MetaDisplay *display,
MetaKeyBinding **bindings_p,
int *n_bindings_p,
const MetaKeyPref *prefs,
int n_prefs)
rebuild_binding_table (MetaDisplay *display,
MetaKeyBinding **bindings_p,
int *n_bindings_p,
GList *prefs)
{
GList *p;
int n_bindings;
int src, dest;
int i;
n_bindings = count_bindings (prefs, n_prefs);
n_bindings = count_bindings (prefs);
g_free (*bindings_p);
*bindings_p = g_new0 (MetaKeyBinding, n_bindings);
src = 0;
dest = 0;
while (src < n_prefs)
i = 0;
p = prefs;
while (p)
{
GSList *tmp = prefs[src].bindings;
MetaKeyPref *pref = (MetaKeyPref*)p->data;
GSList *tmp = pref->bindings;
while (tmp)
{
......@@ -381,45 +367,45 @@ rebuild_binding_table (MetaDisplay *display,
if (combo && (combo->keysym != None || combo->keycode != 0))
{
MetaKeyHandler *handler = find_handler (key_handlers, prefs[src].name);
(*bindings_p)[dest].name = prefs[src].name;
(*bindings_p)[dest].handler = handler;
(*bindings_p)[dest].keysym = combo->keysym;
(*bindings_p)[dest].keycode = combo->keycode;
(*bindings_p)[dest].modifiers = combo->modifiers;
(*bindings_p)[dest].mask = 0;
MetaKeyHandler *handler = HANDLER (pref->name);
(*bindings_p)[i].name = pref->name;
(*bindings_p)[i].handler = handler;
(*bindings_p)[i].keysym = combo->keysym;
(*bindings_p)[i].keycode = combo->keycode;
(*bindings_p)[i].modifiers = combo->modifiers;
(*bindings_p)[i].mask = 0;
++dest;
++i;
if (prefs[src].add_shift &&
if (pref->add_shift &&
(combo->modifiers & META_VIRTUAL_SHIFT_MASK) == 0)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Binding %s also needs Shift grabbed\n",
prefs[src].name);
pref->name);
(*bindings_p)[dest].name = prefs[src].name;
(*bindings_p)[dest].handler = handler;
(*bindings_p)[dest].keysym = combo->keysym;
(*bindings_p)[dest].keycode = combo->keycode;
(*bindings_p)[dest].modifiers = combo->modifiers |
(*bindings_p)[i].name = pref->name;
(*bindings_p)[i].handler = handler;
(*bindings_p)[i].keysym = combo->keysym;
(*bindings_p)[i].keycode = combo->keycode;
(*bindings_p)[i].modifiers = combo->modifiers |
META_VIRTUAL_SHIFT_MASK;
(*bindings_p)[dest].mask = 0;
(*bindings_p)[i].mask = 0;
++dest;
++i;
}
}
tmp = tmp->next;
}
++src;
p = p->next;
}
g_assert (dest == n_bindings);
g_assert (i == n_bindings);
*n_bindings_p = dest;
*n_bindings_p = i;
meta_topic (META_DEBUG_KEYBINDINGS,
" %d bindings in table\n",
......@@ -429,17 +415,17 @@ rebuild_binding_table (MetaDisplay *display,
static void
rebuild_key_binding_table (MetaDisplay *display)
{
const MetaKeyPref *prefs;
int n_prefs;
GList *prefs;
meta_topic (META_DEBUG_KEYBINDINGS,
"Rebuilding key binding table from preferences\n");
meta_prefs_get_key_bindings (&prefs, &n_prefs);
prefs = meta_prefs_get_keybindings ();
rebuild_binding_table (display,
&display->key_bindings,
&display->n_key_bindings,
prefs, n_prefs);
prefs);
g_list_free (prefs);
}
static void
......@@ -514,6 +500,32 @@ display_get_keybinding (MetaDisplay *display,
return NULL;
}
static gboolean
add_builtin_keybinding (MetaDisplay *display,
const char *name,
const char *schema,
MetaKeyBindingFlags flags,
MetaKeyBindingAction action,
MetaKeyHandlerFunc func,
int data)
{
MetaKeyHandler *handler;
if (!meta_prefs_add_keybinding (name, schema, action, flags))
return FALSE;
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup (name);
handler->func = func;
handler->default_func = func;
handler->data = data;
handler->flags = flags;
g_hash_table_insert (key_handlers, g_strdup (name), handler);
return TRUE;
}
/**
* meta_display_get_keybinding_action:
* @display: A #MetaDisplay
......@@ -621,55 +633,6 @@ bindings_changed_callback (MetaPreference pref,
}
void
meta_display_init_keys (MetaDisplay *display)
{
/* Keybindings */
display->keymap = NULL;
display->keysyms_per_keycode = 0;
display->modmap = NULL;
display->min_keycode = 0;
display->max_keycode = 0;
display->ignored_modifier_mask = 0;
display->num_lock_mask = 0;
display->scroll_lock_mask = 0;
display->hyper_mask = 0;
display->super_mask = 0;
display->meta_mask = 0;
display->key_bindings = NULL;
display->n_key_bindings = 0;
XDisplayKeycodes (display->xdisplay,
&display->min_keycode,
&display->max_keycode);
meta_topic (META_DEBUG_KEYBINDINGS,
"Display has keycode range %d to %d\n",
display->min_keycode,
display->max_keycode);
reload_keymap (display);
reload_modmap (display);
rebuild_key_binding_table (display);
rebuild_special_bindings (display);
reload_keycodes (display);
reload_modifiers (display);
/* Keys are actually grabbed in meta_screen_grab_keys() */