You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3689 lines
117 KiB

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Muffin Keybindings */
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2002 Red Hat Inc.
* Copyright (C) 2003 Rob Adams
* Copyright (C) 2004-2006 Elijah Newren
*
* 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., 51 Franklin Street - Suite 500, Boston, MA
* 02110-1335, USA.
*/
/**
* SECTION:keybindings
* @title: MetaKeybinding
* @short_description: Key bindings
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define _SVID_SOURCE /* for putenv() */
#include <config.h>
#include "keybindings-private.h"
#include "workspace-private.h"
#include <meta/errors.h>
#include "edge-resistance.h"
#include "ui.h"
#include "frame.h"
#include "place.h"
#include <meta/prefs.h>
#include <meta/util.h>
#include <X11/keysym.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_XKB
#include <X11/XKBlib.h>
#endif
#define SCHEMA_MUFFIN_KEYBINDINGS "org.cinnamon.desktop.keybindings.wm"
#define SCHEMA_MUFFIN "org.cinnamon.muffin"
static gboolean all_bindings_disabled = FALSE;
static gboolean modifier_only_is_down = FALSE;
static gboolean add_builtin_keybinding (MetaDisplay *display,
const char *name,
const char *schema,
MetaKeyBindingFlags flags,
MetaKeyBindingAction action,
MetaKeyHandlerFunc handler,
int handler_arg);
static void invoke_handler_by_name (MetaDisplay *display,
MetaScreen *screen,
const char *handler_name,
MetaWindow *window,
XEvent *event);
enum {
META_MOVE_TO_XCHANGE_FLAG = 8, // 1000
META_MOVE_TO_YCHANGE_FLAG = 4, // 0100
META_MOVE_TO_RIGHT_FLAG = 2, // 0010
META_MOVE_TO_BOTTOM_FLAG = 1 // 0001
};
// Using flags above
enum {
META_MOVE_TO_SE = 15, // 1111
META_MOVE_TO_NE = 14, // 1110
META_MOVE_TO_SW = 13, // 1101
META_MOVE_TO_NW = 12, // 1100
META_MOVE_TO_E = 10, // 1010
META_MOVE_TO_W = 8, // 1000
META_MOVE_TO_S = 5, // 0101
META_MOVE_TO_N = 4 // 0100
};
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.
*/
static gboolean process_mouse_move_resize_grab (MetaDisplay *display,
MetaScreen *screen,
MetaWindow *window,
XEvent *event,
KeySym keysym);
static gboolean process_keyboard_move_grab (MetaDisplay *display,
MetaScreen *screen,
MetaWindow *window,
XEvent *event,
KeySym keysym);
static gboolean process_keyboard_resize_grab (MetaDisplay *display,
MetaScreen *screen,
MetaWindow *window,
XEvent *event,
KeySym keysym);
static void regrab_key_bindings (MetaDisplay *display);
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)
{
if (display->keymap)
meta_XFree (display->keymap);
/* This is expensive to compute, so we'll lazily load if and when we first
* need it */
display->above_tab_keycode = 0;
display->keymap = XGetKeyboardMapping (display->xdisplay,
display->min_keycode,
display->max_keycode -
display->min_keycode + 1,
&display->keysyms_per_keycode);
}
static void
reload_modmap (MetaDisplay *display)
{
XModifierKeymap *modmap;
int map_size;
int i;
if (display->modmap)
XFreeModifiermap (display->modmap);
modmap = XGetModifierMapping (display->xdisplay);
display->modmap = modmap;
display->ignored_modifier_mask = 0;
/* Multiple bits may get set in each of these */
display->num_lock_mask = 0;
display->scroll_lock_mask = 0;
display->meta_mask = 0;
display->hyper_mask = 0;
display->super_mask = 0;
/* there are 8 modifiers, and the first 3 are shift, shift lock,
* and control
*/
map_size = 8 * modmap->max_keypermod;
i = 3 * modmap->max_keypermod;
while (i < map_size)
{
/* get the key code at this point in the map,
* see if its keysym is one we're interested in
*/
int keycode = modmap->modifiermap[i];
if (keycode >= display->min_keycode &&
keycode <= display->max_keycode)
{
int j = 0;
KeySym *syms = display->keymap +
(keycode - display->min_keycode) * display->keysyms_per_keycode;
while (j < display->keysyms_per_keycode)
{
if (syms[j] != 0)
{
const char *str;
str = XKeysymToString (syms[j]);
meta_topic (META_DEBUG_KEYBINDINGS,
"Keysym %s bound to modifier 0x%x\n",
str ? str : "none",
(1 << ( i / modmap->max_keypermod)));
}
if (syms[j] == XK_Num_Lock)
{
/* Mod1Mask is 1 << 3 for example, i.e. the
* fourth modifier, i / keyspermod is the modifier
* index
*/
display->num_lock_mask |= (1 << ( i / modmap->max_keypermod));
}
else if (syms[j] == XK_Scroll_Lock)
{
display->scroll_lock_mask |= (1 << ( i / modmap->max_keypermod));
}
else if (syms[j] == XK_Super_L ||
syms[j] == XK_Super_R)
{
display->super_mask |= (1 << ( i / modmap->max_keypermod));
}
else if (syms[j] == XK_Hyper_L ||
syms[j] == XK_Hyper_R)
{
display->hyper_mask |= (1 << ( i / modmap->max_keypermod));
}
else if (syms[j] == XK_Meta_L ||
syms[j] == XK_Meta_R)
{
display->meta_mask |= (1 << ( i / modmap->max_keypermod));
}
++j;
}
}
++i;
}
display->ignored_modifier_mask = (display->num_lock_mask |
display->scroll_lock_mask |
LockMask);
meta_topic (META_DEBUG_KEYBINDINGS,
"Ignoring modmask 0x%x num lock 0x%x scroll lock 0x%x hyper 0x%x super 0x%x meta 0x%x\n",
display->ignored_modifier_mask,
display->num_lock_mask,
display->scroll_lock_mask,
display->hyper_mask,
display->super_mask,
display->meta_mask);
}
static guint
keysym_to_keycode (MetaDisplay *display,
guint keysym)
{
if (keysym == META_KEY_ABOVE_TAB)
return meta_display_get_above_tab_keycode (display);
else
return XKeysymToKeycode (display->xdisplay, keysym);
}
static void
reload_keycodes (MetaDisplay *display)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Reloading keycodes for binding tables\n");
if (display->key_bindings)
{
int i;
i = 0;
while (i < display->n_key_bindings)
{
if (display->key_bindings[i].keysym != 0)
{
display->key_bindings[i].keycode =
keysym_to_keycode (display, display->key_bindings[i].keysym);
}
++i;
}
}
}
static void
reload_modifiers (MetaDisplay *display)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Reloading keycodes for binding tables\n");
if (display->key_bindings)
{
int i;
i = 0;
while (i < display->n_key_bindings)
{
meta_display_devirtualize_modifiers (display,
display->key_bindings[i].modifiers,
&display->key_bindings[i].mask);
meta_topic (META_DEBUG_KEYBINDINGS,
" Devirtualized mods 0x%x -> 0x%x (%s)\n",
display->key_bindings[i].modifiers,
display->key_bindings[i].mask,
display->key_bindings[i].name);
++i;
}
}
}
static int
count_bindings (GList *prefs)
{
GList *p;
int count;
count = 0;
p = prefs;
while (p)
{
MetaKeyPref *pref = (MetaKeyPref*)p->data;
GSList *tmp = pref->bindings;
while (tmp)
{
MetaKeyCombo *combo = tmp->data;
if (combo && (combo->keysym != None || combo->keycode != 0))
{
count += 1;
if (pref->add_shift &&
(combo->modifiers & META_VIRTUAL_SHIFT_MASK) == 0)
count += 1;
}
tmp = tmp->next;
}
p = p->next;
}
return count;
}
static void
rebuild_binding_table (MetaDisplay *display,
MetaKeyBinding **bindings_p,
int *n_bindings_p,
GList *prefs)
{
GList *p;
int n_bindings;
int i;
n_bindings = count_bindings (prefs);
g_free (*bindings_p);
*bindings_p = g_new0 (MetaKeyBinding, n_bindings);
i = 0;
p = prefs;
while (p)
{
MetaKeyPref *pref = (MetaKeyPref*)p->data;
GSList *tmp = pref->bindings;
while (tmp)
{
MetaKeyCombo *combo = tmp->data;
if (combo && (combo->keysym != None || combo->keycode != 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;
++i;
if (pref->add_shift &&
(combo->modifiers & META_VIRTUAL_SHIFT_MASK) == 0)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Binding %s also needs Shift grabbed\n",
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 |
META_VIRTUAL_SHIFT_MASK;
(*bindings_p)[i].mask = 0;
++i;
}
}
tmp = tmp->next;
}
p = p->next;
}
g_assert (i == n_bindings);
*n_bindings_p = i;
meta_topic (META_DEBUG_KEYBINDINGS,
" %d bindings in table\n",
*n_bindings_p);
}
static void
rebuild_key_binding_table (MetaDisplay *display)
{
GList *prefs;
meta_topic (META_DEBUG_KEYBINDINGS,
"Rebuilding key binding table from preferences\n");
prefs = meta_prefs_get_keybindings ();
rebuild_binding_table (display,
&display->key_bindings,
&display->n_key_bindings,
prefs);
g_list_free (prefs);
}
static void
regrab_key_bindings (MetaDisplay *display)
{
GSList *tmp;
GSList *windows;
meta_error_trap_push (display); /* for efficiency push outer trap */
tmp = display->screens;
while (tmp != NULL)
{
MetaScreen *screen = tmp->data;
meta_screen_ungrab_keys (screen);
meta_screen_grab_keys (screen);
tmp = tmp->next;
}
windows = meta_display_list_windows (display, META_LIST_DEFAULT);
tmp = windows;
while (tmp != NULL)
{
MetaWindow *w = tmp->data;
meta_window_ungrab_keys (w);
meta_window_grab_keys (w);
tmp = tmp->next;
}
meta_error_trap_pop (display);
g_slist_free (windows);
}
static MetaKeyBinding *
display_get_keybinding (MetaDisplay *display,
unsigned int keysym,
unsigned int keycode,
unsigned long mask)
{
int i;
i = display->n_key_bindings - 1;
while (i >= 0)
{
if (display->key_bindings[i].keysym == keysym &&
display->key_bindings[i].keycode == keycode &&
display->key_bindings[i].mask == mask)
{
return &display->key_bindings[i];
}
--i;
}
return NULL;
}
static gboolean
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;
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;
handler->action = action;
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 "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1". The parser
* is fairly liberal and allows lower or upper case, and also abbreviations
* such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;". 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, "&lt;Shift&gt;"
* 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;
}
static gboolean
add_custom_keybinding_internal (MetaDisplay *display,
const char *name,
const char **bindings,
MetaKeyBindingFlags flags,
MetaKeyBindingAction action,
MetaKeyHandlerFunc func,
int data,
gpointer user_data,
GDestroyNotify free_data)
{
MetaKeyHandler *handler;
if (!meta_prefs_add_custom_keybinding (name, bindings, 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;
handler->action = action;
handler->user_data = user_data;
handler->user_data_free_func = free_data;
g_hash_table_insert (key_handlers, g_strdup (name), handler);
return TRUE;
}
/**
* meta_display_add_custom_keybinding:
* @display: a #MetaDisplay
* @name: the binding's unique name
* @bindings: (allow-none) (array zero-terminated=1): array of parseable keystrokes
* @callback: function to run when the keybinding is invoked
* @user_data: the data to pass to @handler
* @free_data: function to free @user_data
*
*
* Use meta_display_remove_custom_keybinding() to remove the binding.
*
* Returns: %TRUE if the keybinding was added successfully,
* otherwise %FALSE
*/
gboolean
meta_display_add_custom_keybinding (MetaDisplay *display,
const char *name,
const char **bindings,
MetaKeyHandlerFunc callback,
gpointer user_data,
GDestroyNotify free_data)
{
return add_custom_keybinding_internal (display, name, bindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_CUSTOM,
(MetaKeyHandlerFunc)callback, 0, user_data, free_data);
}
/**
* meta_display_remove_custom_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_custom_keybinding().
*
* Returns: %TRUE if the binding has been removed sucessfully,
* otherwise %FALSE
*/
gboolean
meta_display_remove_custom_keybinding (MetaDisplay *display,
const char *name)
{
if (!meta_prefs_remove_custom_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.
*/
MetaKeyBindingAction
meta_display_get_keybinding_action (MetaDisplay *display,
unsigned int keycode,
unsigned long mask)
{
MetaKeyBinding *binding;
KeySym keysym;
keysym = XkbKeycodeToKeysym (display->xdisplay, keycode, 0, 0);
mask = mask & 0xff & ~display->ignored_modifier_mask;
binding = display_get_keybinding (display, keysym, keycode, mask);
if (!binding && keycode == meta_display_get_above_tab_keycode (display))
binding = display_get_keybinding (display, META_KEY_ABOVE_TAB, keycode, mask);
if (binding)
return meta_prefs_get_keybinding_action (binding->name);
else
return META_KEYBINDING_ACTION_NONE;
}
void
meta_display_keybinding_action_invoke_by_code (MetaDisplay *display,
unsigned int keycode,
unsigned long mask)
{
MetaKeyBinding *binding;
KeySym keysym;
keysym = XkbKeycodeToKeysym (display->xdisplay, keycode, 0, 0);
mask = mask & 0xff & ~display->ignored_modifier_mask;
binding = display_get_keybinding (display, keysym, keycode, mask);
if (!binding && keycode == meta_display_get_above_tab_keycode (display))
binding = display_get_keybinding (display, META_KEY_ABOVE_TAB, keycode, mask);
if (binding)
invoke_handler_by_name (display, NULL, binding->name, NULL, NULL);
}
LOCAL_SYMBOL void
meta_display_process_mapping_event (MetaDisplay *display,
XEvent *event)
{
gboolean keymap_changed = FALSE;
gboolean modmap_changed = FALSE;
#ifdef HAVE_XKB
if (event->type == display->xkb_base_event_type)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"XKB mapping changed, will redo keybindings\n");
keymap_changed = TRUE;
modmap_changed = TRUE;
}
else
#endif
if (event->xmapping.request == MappingModifier)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Received MappingModifier event, will reload modmap and redo keybindings\n");
modmap_changed = TRUE;
}
else if (event->xmapping.request == MappingKeyboard)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Received MappingKeyboard event, will reload keycodes and redo keybindings\n");
keymap_changed = TRUE;
}
/* Now to do the work itself */
if (keymap_changed || modmap_changed)
{
if (keymap_changed)
reload_keymap (display);
/* Deciphering the modmap depends on the loaded keysyms to find out
* what modifiers is Super and so forth, so we need to reload it
* even when only the keymap changes */
reload_modmap (display);
if (keymap_changed)
reload_keycodes (display);
reload_modifiers (display);
regrab_key_bindings (display);
}
}
static gboolean
rebuild_keybindings_at_idle (MetaDisplay *display)
{
display->rebuild_keybinding_idle_id = 0;
rebuild_key_binding_table (display);
reload_keycodes (display);
reload_modifiers (display);
regrab_key_bindings (display);
return FALSE;
}
static void
queue_rebuild_keybindings (MetaDisplay *display)
{
if (display->rebuild_keybinding_idle_id > 0)
{
g_source_remove (display->rebuild_keybinding_idle_id);
display->rebuild_keybinding_idle_id = 0;
}
display->rebuild_keybinding_idle_id = g_idle_add_full (G_PRIORITY_LOW,
(GSourceFunc) rebuild_keybindings_at_idle,
display, NULL);
}
static void
bindings_changed_callback (MetaPreference pref,
void *data)
{
MetaDisplay *display;
display = data;
switch (pref)
{
case META_PREF_KEYBINDINGS:
queue_rebuild_keybindings (display);
break;
default:
break;
}
}
/**
* meta_display_rebuild_keybindings:
* @display: the #MetaDisplay
*
* Rebuild all keybindings (typically done after adding, removing, or changing
* one or more keybindings)
*
*/
void
meta_display_rebuild_keybindings (MetaDisplay *display)
{
queue_rebuild_keybindings (display);
}
LOCAL_SYMBOL void
meta_display_shutdown_keys (MetaDisplay *display)
{
/* Note that display->xdisplay is invalid in this function */
meta_prefs_remove_listener (bindings_changed_callback, display);
if (display->keymap)
meta_XFree (display->keymap);
if (display->modmap)
XFreeModifiermap (display->modmap);
g_free (display->key_bindings);
}
static const char*
keysym_name (int keysym)
{
const char *name;
name = XKeysymToString (keysym);
if (name == NULL)
name = "(unknown)";
return name;
}
/* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */
static void
meta_change_keygrab (MetaDisplay *display,
Window xwindow,
gboolean grab,
int keysym,
unsigned int keycode,
int modmask)
{
unsigned int ignored_mask;
/* Grab keycode/modmask, together with
* all combinations of ignored modifiers.
* X provides no better way to do this.
*/
meta_topic (META_DEBUG_KEYBINDINGS,
"%s keybinding %s keycode %d mask 0x%x on 0x%lx\n",
grab ? "Grabbing" : "Ungrabbing",
keysym_name (keysym), keycode,
modmask, xwindow);
/* efficiency, avoid so many XSync() */
meta_error_trap_push (display);
ignored_mask = 0;
while (ignored_mask <= display->ignored_modifier_mask)
{
if (ignored_mask & ~(display->ignored_modifier_mask))
{
/* Not a combination of ignored modifiers
* (it contains some non-ignored modifiers)
*/
++ignored_mask;
continue;
}
if (meta_is_debugging ())
meta_error_trap_push_with_return (display);
if (grab)
XGrabKey (display->xdisplay, keycode,
modmask | ignored_mask,
xwindow,
True,
GrabModeAsync, GrabModeSync);
else
XUngrabKey (display->xdisplay, keycode,
modmask | ignored_mask,
xwindow);
if (meta_is_debugging ())
{
int result;
result = meta_error_trap_pop_with_return (display);
if (grab && result != Success)
{
if (result == BadAccess)
meta_warning (_("Some other program is already using the key %s with modifiers %x as a binding\n"), keysym_name (keysym), modmask | ignored_mask);
else
meta_topic (META_DEBUG_KEYBINDINGS,
"Failed to grab key %s with modifiers %x\n",
keysym_name (keysym), modmask | ignored_mask);
}
}
++ignored_mask;
}
meta_error_trap_pop (display);
}
static void
meta_grab_key (MetaDisplay *display,
Window xwindow,
int keysym,
unsigned int keycode,
int modmask)
{
meta_change_keygrab (display, xwindow, TRUE, keysym, keycode, modmask);
}
static void
grab_keys (MetaKeyBinding *bindings,
int n_bindings,
MetaDisplay *display,
Window xwindow,
gboolean binding_per_window)
{
int i;
g_assert (n_bindings == 0 || bindings != NULL);
meta_error_trap_push (display);
i = 0;
while (i < n_bindings)
{
if (!!binding_per_window ==
!!(bindings[i].handler->flags & META_KEY_BINDING_PER_WINDOW) &&
bindings[i].keycode != 0)
{
meta_grab_key (display, xwindow,
bindings[i].keysym,
bindings[i].keycode,
bindings[i].mask);
}
++i;
}
meta_error_trap_pop (display);
}
static void
ungrab_all_keys (MetaDisplay *display,
Window xwindow)
{
if (meta_is_debugging ())
meta_error_trap_push_with_return (display);
else
meta_error_trap_push (display);
XUngrabKey (display->xdisplay, AnyKey, AnyModifier,
xwindow);
if (meta_is_debugging ())
{
int result;
result = meta_error_trap_pop_with_return (display);
if (result != Success)
meta_topic (META_DEBUG_KEYBINDINGS,
"Ungrabbing all keys on 0x%lx failed\n", xwindow);
}