1
0
Fork 0
mutter-performance-source/src/core/keybindings.c
Carlos Garnacho 1e289e8d81 keybindings: Minor refactor
Rewrite this codepath so it handles the grab ops that it cares
about, and ignores the rest. This way the code works despite
possible future modifications to MetaGrabOp (e.g.
META_GRAB_OP_WAYLAND_POPUP removal).

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2683>
2023-01-30 10:56:29 +00:00

4547 lines
148 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Mutter 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, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:keybindings
* @Title: MetaKeybinding
* @Short_Description: Key bindings
*/
#include "config.h"
#include "backends/meta-backend-private.h"
#include "backends/meta-keymap-utils.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-monitor-manager-private.h"
#include "backends/x11/meta-backend-x11.h"
#include "backends/x11/meta-input-device-x11.h"
#include "compositor/compositor-private.h"
#include "core/edge-resistance.h"
#include "core/frame.h"
#include "core/keybindings-private.h"
#include "core/meta-accel-parse.h"
#include "core/meta-workspace-manager-private.h"
#include "core/workspace-private.h"
#include "meta/compositor.h"
#include "meta/meta-x11-errors.h"
#include "meta/prefs.h"
#include "x11/meta-x11-display-private.h"
#include "x11/window-x11.h"
#ifdef HAVE_NATIVE_BACKEND
#include "backends/native/meta-backend-native.h"
#endif
#ifdef __linux__
#include <linux/input.h>
#elif !defined KEY_GRAVE
#define KEY_GRAVE 0x29 /* assume the use of xf86-input-keyboard */
#endif
#define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings"
#define SCHEMA_MUTTER_KEYBINDINGS "org.gnome.mutter.keybindings"
#define SCHEMA_MUTTER_WAYLAND_KEYBINDINGS "org.gnome.mutter.wayland.keybindings"
#define META_KEY_BINDING_PRIMARY_LAYOUT 0
#define META_KEY_BINDING_SECONDARY_LAYOUT 1
/* Only for special modifier keys */
#define IGNORED_MODIFIERS (CLUTTER_LOCK_MASK | \
CLUTTER_MOD2_MASK | \
CLUTTER_BUTTON1_MASK | \
CLUTTER_BUTTON2_MASK | \
CLUTTER_BUTTON3_MASK | \
CLUTTER_BUTTON4_MASK | \
CLUTTER_BUTTON5_MASK)
static gboolean add_builtin_keybinding (MetaDisplay *display,
const char *name,
GSettings *settings,
MetaKeyBindingFlags flags,
MetaKeyBindingAction action,
MetaKeyHandlerFunc handler,
int handler_arg);
static void
resolved_key_combo_reset (MetaResolvedKeyCombo *resolved_combo)
{
g_free (resolved_combo->keycodes);
resolved_combo->len = 0;
resolved_combo->keycodes = NULL;
}
static void
resolved_key_combo_copy (MetaResolvedKeyCombo *from,
MetaResolvedKeyCombo *to)
{
to->len = from->len;
to->keycodes = g_memdup2 (from->keycodes,
from->len * sizeof (xkb_keycode_t));
}
static gboolean
resolved_key_combo_has_keycode (MetaResolvedKeyCombo *resolved_combo,
int keycode)
{
int i;
for (i = 0; i < resolved_combo->len; i++)
if ((int) resolved_combo->keycodes[i] == keycode)
return TRUE;
return FALSE;
}
static gboolean
resolved_key_combo_intersect (MetaResolvedKeyCombo *a,
MetaResolvedKeyCombo *b)
{
int i;
for (i = 0; i < a->len; i++)
if (resolved_key_combo_has_keycode (b, a->keycodes[i]))
return TRUE;
return FALSE;
}
static void
meta_key_binding_free (MetaKeyBinding *binding)
{
resolved_key_combo_reset (&binding->resolved_combo);
g_free (binding);
}
static MetaKeyBinding *
meta_key_binding_copy (MetaKeyBinding *binding)
{
MetaKeyBinding *clone = g_memdup2 (binding, sizeof (MetaKeyBinding));
resolved_key_combo_copy (&binding->resolved_combo,
&clone->resolved_combo);
return clone;
}
G_DEFINE_BOXED_TYPE(MetaKeyBinding,
meta_key_binding,
meta_key_binding_copy,
meta_key_binding_free)
const char *
meta_key_binding_get_name (MetaKeyBinding *binding)
{
return binding->name;
}
ClutterModifierType
meta_key_binding_get_modifiers (MetaKeyBinding *binding)
{
return binding->combo.modifiers;
}
gboolean
meta_key_binding_is_reversed (MetaKeyBinding *binding)
{
return (binding->handler->flags & META_KEY_BINDING_IS_REVERSED) != 0;
}
guint
meta_key_binding_get_mask (MetaKeyBinding *binding)
{
return binding->resolved_combo.mask;
}
gboolean
meta_key_binding_is_builtin (MetaKeyBinding *binding)
{
return binding->handler->flags & META_KEY_BINDING_BUILTIN;
}
/* 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,
MetaWindow *window,
ClutterKeyEvent *event);
static gboolean process_keyboard_move_grab (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event);
static gboolean process_keyboard_resize_grab (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event);
static void maybe_update_locate_pointer_keygrab (MetaDisplay *display,
gboolean grab);
static GHashTable *key_handlers;
static GHashTable *external_grabs;
#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);
}
typedef struct _MetaKeyGrab MetaKeyGrab;
struct _MetaKeyGrab {
char *name;
guint action;
MetaKeyCombo combo;
gint flags;
};
static void
meta_key_grab_free (MetaKeyGrab *grab)
{
g_free (grab->name);
g_free (grab);
}
static guint32
key_combo_key (MetaResolvedKeyCombo *resolved_combo,
int i)
{
/* On X, keycodes are only 8 bits while libxkbcommon supports 32 bit
keycodes, but since we're using the same XKB keymaps that X uses,
we won't find keycodes bigger than 8 bits in practice. The bits
that mutter cares about in the modifier mask are also all in the
lower 8 bits both on X and clutter key events. This means that we
can use a 32 bit integer to safely concatenate both keycode and
mask and thus making it easy to use them as an index in a
GHashTable. */
guint32 key = resolved_combo->keycodes[i] & 0xffff;
return (key << 16) | (resolved_combo->mask & 0xffff);
}
static void
reload_modmap (MetaKeyBindingManager *keys)
{
struct xkb_keymap *keymap = meta_backend_get_keymap (keys->backend);
struct xkb_state *scratch_state;
xkb_mod_mask_t scroll_lock_mask;
xkb_mod_mask_t dummy_mask;
/* Modifiers to find. */
struct {
const char *name;
xkb_mod_mask_t *mask_p;
xkb_mod_mask_t *virtual_mask_p;
} mods[] = {
{ "ScrollLock", &scroll_lock_mask, &dummy_mask },
{ "Meta", &keys->meta_mask, &keys->virtual_meta_mask },
{ "Hyper", &keys->hyper_mask, &keys->virtual_hyper_mask },
{ "Super", &keys->super_mask, &keys->virtual_super_mask },
};
scratch_state = xkb_state_new (keymap);
gsize i;
for (i = 0; i < G_N_ELEMENTS (mods); i++)
{
xkb_mod_mask_t *mask_p = mods[i].mask_p;
xkb_mod_mask_t *virtual_mask_p = mods[i].virtual_mask_p;
xkb_mod_index_t idx = xkb_keymap_mod_get_index (keymap, mods[i].name);
if (idx != XKB_MOD_INVALID)
{
xkb_mod_mask_t vmodmask = (1 << idx);
xkb_state_update_mask (scratch_state, vmodmask, 0, 0, 0, 0, 0);
*mask_p = xkb_state_serialize_mods (scratch_state, XKB_STATE_MODS_DEPRESSED) & ~vmodmask;
*virtual_mask_p = vmodmask;
}
else
{
*mask_p = 0;
*virtual_mask_p = 0;
}
}
xkb_state_unref (scratch_state);
keys->ignored_modifier_mask = (scroll_lock_mask | Mod2Mask | LockMask);
meta_topic (META_DEBUG_KEYBINDINGS,
"Ignoring modmask 0x%x scroll lock 0x%x hyper 0x%x super 0x%x meta 0x%x",
keys->ignored_modifier_mask,
scroll_lock_mask,
keys->hyper_mask,
keys->super_mask,
keys->meta_mask);
}
static gboolean
is_keycode_for_keysym (struct xkb_keymap *keymap,
xkb_layout_index_t layout,
xkb_level_index_t level,
xkb_keycode_t keycode,
xkb_keysym_t keysym)
{
const xkb_keysym_t *syms;
int num_syms, k;
num_syms = xkb_keymap_key_get_syms_by_level (keymap, keycode, layout, level, &syms);
for (k = 0; k < num_syms; k++)
{
if (syms[k] == keysym)
return TRUE;
}
return FALSE;
}
typedef struct
{
GArray *keycodes;
xkb_keysym_t keysym;
xkb_layout_index_t layout;
xkb_level_index_t level;
} FindKeysymData;
static void
get_keycodes_for_keysym_iter (struct xkb_keymap *keymap,
xkb_keycode_t keycode,
void *data)
{
FindKeysymData *search_data = data;
GArray *keycodes = search_data->keycodes;
xkb_keysym_t keysym = search_data->keysym;
xkb_layout_index_t layout = search_data->layout;
xkb_level_index_t level = search_data->level;
if (is_keycode_for_keysym (keymap, layout, level, keycode, keysym))
{
guint i;
gboolean missing = TRUE;
/* duplicate keycode detection */
for (i = 0; i < keycodes->len; i++)
if (g_array_index (keycodes, xkb_keysym_t, i) == keycode)
{
missing = FALSE;
break;
}
if (missing)
g_array_append_val (keycodes, keycode);
}
}
static void
add_keysym_keycodes_from_layout (int keysym,
MetaKeyBindingKeyboardLayout *layout,
GArray *keycodes)
{
xkb_level_index_t layout_level;
for (layout_level = 0;
layout_level < layout->n_levels && keycodes->len == 0;
layout_level++)
{
FindKeysymData search_data = (FindKeysymData) {
.keycodes = keycodes,
.keysym = keysym,
.layout = layout->index,
.level = layout_level
};
xkb_keymap_key_for_each (layout->keymap,
get_keycodes_for_keysym_iter,
&search_data);
}
}
/* Original code from gdk_x11_keymap_get_entries_for_keyval() in
* gdkkeys-x11.c */
static void
get_keycodes_for_keysym (MetaKeyBindingManager *keys,
int keysym,
MetaResolvedKeyCombo *resolved_combo)
{
unsigned int i;
GArray *keycodes;
int keycode;
keycodes = g_array_new (FALSE, FALSE, sizeof (xkb_keysym_t));
/* Special-case: Fake mutter keysym */
if (keysym == META_KEY_ABOVE_TAB)
{
keycode = KEY_GRAVE + 8;
g_array_append_val (keycodes, keycode);
goto out;
}
for (i = 0; i < G_N_ELEMENTS (keys->active_layouts); i++)
{
MetaKeyBindingKeyboardLayout *layout = &keys->active_layouts[i];
if (!layout->keymap)
continue;
add_keysym_keycodes_from_layout (keysym, layout, keycodes);
}
out:
resolved_combo->len = keycodes->len;
resolved_combo->keycodes =
(xkb_keycode_t *) g_array_free (keycodes,
keycodes->len == 0 ? TRUE : FALSE);
}
typedef struct _CalculateLayoutLevelsState
{
struct xkb_keymap *keymap;
xkb_layout_index_t layout_index;
xkb_level_index_t out_n_levels;
} CalculateLayoutLevelState;
static void
calculate_n_layout_levels_iter (struct xkb_keymap *keymap,
xkb_keycode_t keycode,
void *data)
{
CalculateLayoutLevelState *state = data;
xkb_level_index_t n_levels;
n_levels = xkb_keymap_num_levels_for_key (keymap,
keycode,
state->layout_index);
state->out_n_levels = MAX (n_levels, state->out_n_levels);
}
static xkb_level_index_t
calculate_n_layout_levels (struct xkb_keymap *keymap,
xkb_layout_index_t layout_index)
{
CalculateLayoutLevelState state = {
.keymap = keymap,
.layout_index = layout_index,
.out_n_levels = 0
};
xkb_keymap_key_for_each (keymap, calculate_n_layout_levels_iter, &state);
return state.out_n_levels;
}
static void
reload_iso_next_group_combos (MetaKeyBindingManager *keys)
{
const char *iso_next_group_option;
int i;
for (i = 0; i < keys->n_iso_next_group_combos; i++)
resolved_key_combo_reset (&keys->iso_next_group_combo[i]);
keys->n_iso_next_group_combos = 0;
iso_next_group_option = meta_prefs_get_iso_next_group_option ();
if (iso_next_group_option == NULL)
return;
get_keycodes_for_keysym (keys, XKB_KEY_ISO_Next_Group, keys->iso_next_group_combo);
if (keys->iso_next_group_combo[0].len == 0)
return;
keys->n_iso_next_group_combos = 1;
if (g_str_equal (iso_next_group_option, "toggle") ||
g_str_equal (iso_next_group_option, "lalt_toggle") ||
g_str_equal (iso_next_group_option, "lwin_toggle") ||
g_str_equal (iso_next_group_option, "rwin_toggle") ||
g_str_equal (iso_next_group_option, "lshift_toggle") ||
g_str_equal (iso_next_group_option, "rshift_toggle") ||
g_str_equal (iso_next_group_option, "lctrl_toggle") ||
g_str_equal (iso_next_group_option, "rctrl_toggle") ||
g_str_equal (iso_next_group_option, "sclk_toggle") ||
g_str_equal (iso_next_group_option, "menu_toggle") ||
g_str_equal (iso_next_group_option, "caps_toggle"))
{
keys->iso_next_group_combo[0].mask = 0;
}
else if (g_str_equal (iso_next_group_option, "shift_caps_toggle") ||
g_str_equal (iso_next_group_option, "shifts_toggle"))
{
keys->iso_next_group_combo[0].mask = ShiftMask;
}
else if (g_str_equal (iso_next_group_option, "alt_caps_toggle") ||
g_str_equal (iso_next_group_option, "alt_space_toggle"))
{
keys->iso_next_group_combo[0].mask = Mod1Mask;
}
else if (g_str_equal (iso_next_group_option, "ctrl_shift_toggle") ||
g_str_equal (iso_next_group_option, "lctrl_lshift_toggle") ||
g_str_equal (iso_next_group_option, "rctrl_rshift_toggle"))
{
resolved_key_combo_copy (&keys->iso_next_group_combo[0],
&keys->iso_next_group_combo[1]);
keys->iso_next_group_combo[0].mask = ShiftMask;
keys->iso_next_group_combo[1].mask = ControlMask;
keys->n_iso_next_group_combos = 2;
}
else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle"))
{
resolved_key_combo_copy (&keys->iso_next_group_combo[0],
&keys->iso_next_group_combo[1]);
keys->iso_next_group_combo[0].mask = Mod1Mask;
keys->iso_next_group_combo[1].mask = ControlMask;
keys->n_iso_next_group_combos = 2;
}
else if (g_str_equal (iso_next_group_option, "alt_shift_toggle") ||
g_str_equal (iso_next_group_option, "lalt_lshift_toggle"))
{
resolved_key_combo_copy (&keys->iso_next_group_combo[0],
&keys->iso_next_group_combo[1]);
keys->iso_next_group_combo[0].mask = Mod1Mask;
keys->iso_next_group_combo[1].mask = ShiftMask;
keys->n_iso_next_group_combos = 2;
}
else
{
resolved_key_combo_reset (keys->iso_next_group_combo);
keys->n_iso_next_group_combos = 0;
}
}
static void
devirtualize_modifiers (MetaKeyBindingManager *keys,
ClutterModifierType modifiers,
unsigned int *mask)
{
*mask = 0;
if (modifiers & CLUTTER_SHIFT_MASK)
*mask |= ShiftMask;
if (modifiers & CLUTTER_CONTROL_MASK)
*mask |= ControlMask;
if (modifiers & CLUTTER_MOD1_MASK)
*mask |= Mod1Mask;
if (modifiers & CLUTTER_META_MASK)
*mask |= keys->meta_mask;
if (modifiers & CLUTTER_HYPER_MASK)
*mask |= keys->hyper_mask;
if (modifiers & CLUTTER_SUPER_MASK)
*mask |= keys->super_mask;
if (modifiers & CLUTTER_MOD2_MASK)
*mask |= Mod2Mask;
if (modifiers & CLUTTER_MOD3_MASK)
*mask |= Mod3Mask;
if (modifiers & CLUTTER_MOD4_MASK)
*mask |= Mod4Mask;
if (modifiers & CLUTTER_MOD5_MASK)
*mask |= Mod5Mask;
}
static void
index_binding (MetaKeyBindingManager *keys,
MetaKeyBinding *binding)
{
int i;
for (i = 0; i < binding->resolved_combo.len; i++)
{
MetaKeyBinding *existing;
guint32 index_key;
index_key = key_combo_key (&binding->resolved_combo, i);
existing = g_hash_table_lookup (keys->key_bindings_index,
GINT_TO_POINTER (index_key));
if (existing != NULL)
{
/* Overwrite already indexed keycodes only for the first
* keycode, i.e. we give those primary keycodes precedence
* over non-first ones. */
if (i > 0)
continue;
meta_warning ("Overwriting existing binding of keysym %x"
" with keysym %x (keycode %x).",
binding->combo.keysym,
existing->combo.keysym,
binding->resolved_combo.keycodes[i]);
}
g_hash_table_replace (keys->key_bindings_index,
GINT_TO_POINTER (index_key), binding);
}
}
static void
resolve_key_combo (MetaKeyBindingManager *keys,
MetaKeyCombo *combo,
MetaResolvedKeyCombo *resolved_combo)
{
resolved_key_combo_reset (resolved_combo);
if (combo->keysym != 0)
{
get_keycodes_for_keysym (keys, combo->keysym, resolved_combo);
}
else if (combo->keycode != 0)
{
resolved_combo->keycodes = g_new0 (xkb_keycode_t, 1);
resolved_combo->keycodes[0] = combo->keycode;
resolved_combo->len = 1;
}
devirtualize_modifiers (keys, combo->modifiers, &resolved_combo->mask);
}
static void
binding_reload_combos_foreach (gpointer key,
gpointer value,
gpointer data)
{
MetaKeyBindingManager *keys = data;
MetaKeyBinding *binding = value;
resolve_key_combo (keys, &binding->combo, &binding->resolved_combo);
index_binding (keys, binding);
}
typedef struct _FindLatinKeysymsState
{
MetaKeyBindingKeyboardLayout *layout;
gboolean *required_keysyms_found;
int n_required_keysyms;
} FindLatinKeysymsState;
static void
find_latin_keysym (struct xkb_keymap *keymap,
xkb_keycode_t key,
void *data)
{
FindLatinKeysymsState *state = data;
int n_keysyms, i;
const xkb_keysym_t *keysyms;
n_keysyms = xkb_keymap_key_get_syms_by_level (state->layout->keymap,
key,
state->layout->index,
0,
&keysyms);
for (i = 0; i < n_keysyms; i++)
{
xkb_keysym_t keysym = keysyms[i];
if (keysym >= XKB_KEY_a && keysym <= XKB_KEY_z)
{
unsigned int keysym_index = keysym - XKB_KEY_a;
if (!state->required_keysyms_found[keysym_index])
{
state->required_keysyms_found[keysym_index] = TRUE;
state->n_required_keysyms--;
}
}
}
}
static gboolean
needs_secondary_layout (MetaKeyBindingKeyboardLayout *layout)
{
gboolean required_keysyms_found[] = {
FALSE, /* XKB_KEY_a */
FALSE, /* XKB_KEY_b */
FALSE, /* XKB_KEY_c */
FALSE, /* XKB_KEY_d */
FALSE, /* XKB_KEY_e */
FALSE, /* XKB_KEY_f */
FALSE, /* XKB_KEY_g */
FALSE, /* XKB_KEY_h */
FALSE, /* XKB_KEY_i */
FALSE, /* XKB_KEY_j */
FALSE, /* XKB_KEY_k */
FALSE, /* XKB_KEY_l */
FALSE, /* XKB_KEY_m */
FALSE, /* XKB_KEY_n */
FALSE, /* XKB_KEY_o */
FALSE, /* XKB_KEY_p */
FALSE, /* XKB_KEY_q */
FALSE, /* XKB_KEY_r */
FALSE, /* XKB_KEY_s */
FALSE, /* XKB_KEY_t */
FALSE, /* XKB_KEY_u */
FALSE, /* XKB_KEY_v */
FALSE, /* XKB_KEY_w */
FALSE, /* XKB_KEY_x */
FALSE, /* XKB_KEY_y */
FALSE, /* XKB_KEY_z */
};
FindLatinKeysymsState state = {
.layout = layout,
.required_keysyms_found = required_keysyms_found,
.n_required_keysyms = G_N_ELEMENTS (required_keysyms_found),
};
xkb_keymap_key_for_each (layout->keymap, find_latin_keysym, &state);
return state.n_required_keysyms != 0;
}
static void
clear_active_keyboard_layouts (MetaKeyBindingManager *keys)
{
unsigned int i;
for (i = 0; i < G_N_ELEMENTS (keys->active_layouts); i++)
{
MetaKeyBindingKeyboardLayout *layout = &keys->active_layouts[i];
g_clear_pointer (&layout->keymap, xkb_keymap_unref);
*layout = (MetaKeyBindingKeyboardLayout) { 0 };
}
}
static MetaKeyBindingKeyboardLayout
create_us_layout (void)
{
struct xkb_rule_names names;
struct xkb_keymap *keymap;
struct xkb_context *context;
names.rules = DEFAULT_XKB_RULES_FILE;
names.model = DEFAULT_XKB_MODEL;
names.layout = "us";
names.variant = "";
names.options = "";
context = meta_create_xkb_context ();
keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
xkb_context_unref (context);
return (MetaKeyBindingKeyboardLayout) {
.keymap = keymap,
.n_levels = calculate_n_layout_levels (keymap, 0),
};
}
static void
reload_active_keyboard_layouts (MetaKeyBindingManager *keys)
{
struct xkb_keymap *keymap;
xkb_layout_index_t layout_index;
MetaKeyBindingKeyboardLayout primary_layout;
clear_active_keyboard_layouts (keys);
keymap = meta_backend_get_keymap (keys->backend);
layout_index = meta_backend_get_keymap_layout_group (keys->backend);
primary_layout = (MetaKeyBindingKeyboardLayout) {
.keymap = xkb_keymap_ref (keymap),
.index = layout_index,
.n_levels = calculate_n_layout_levels (keymap, layout_index),
};
keys->active_layouts[META_KEY_BINDING_PRIMARY_LAYOUT] = primary_layout;
if (needs_secondary_layout (&primary_layout))
{
MetaKeyBindingKeyboardLayout us_layout;
us_layout = create_us_layout ();
keys->active_layouts[META_KEY_BINDING_SECONDARY_LAYOUT] = us_layout;
}
}
static void
reload_combos (MetaKeyBindingManager *keys)
{
g_hash_table_remove_all (keys->key_bindings_index);
reload_active_keyboard_layouts (keys);
resolve_key_combo (keys,
&keys->overlay_key_combo,
&keys->overlay_resolved_key_combo);
resolve_key_combo (keys,
&keys->locate_pointer_key_combo,
&keys->locate_pointer_resolved_key_combo);
reload_iso_next_group_combos (keys);
g_hash_table_foreach (keys->key_bindings, binding_reload_combos_foreach, keys);
}
static void
rebuild_binding_table (MetaKeyBindingManager *keys,
GList *prefs,
GList *grabs)
{
MetaKeyBinding *b;
GList *p, *g;
g_hash_table_remove_all (keys->key_bindings);
p = prefs;
while (p)
{
MetaKeyPref *pref = (MetaKeyPref*)p->data;
GSList *tmp = pref->combos;
while (tmp)
{
MetaKeyCombo *combo = tmp->data;
if (combo && (combo->keysym != None || combo->keycode != 0))
{
MetaKeyHandler *handler = HANDLER (pref->name);
b = g_new0 (MetaKeyBinding, 1);
b->name = pref->name;
b->handler = handler;
b->flags = handler->flags;
b->combo = *combo;
g_hash_table_add (keys->key_bindings, b);
}
tmp = tmp->next;
}
p = p->next;
}
g = grabs;
while (g)
{
MetaKeyGrab *grab = (MetaKeyGrab*)g->data;
if (grab->combo.keysym != None || grab->combo.keycode != 0)
{
MetaKeyHandler *handler = HANDLER ("external-grab");
b = g_new0 (MetaKeyBinding, 1);
b->name = grab->name;
b->handler = handler;
b->flags = grab->flags;
b->combo = grab->combo;
g_hash_table_add (keys->key_bindings, b);
}
g = g->next;
}
meta_topic (META_DEBUG_KEYBINDINGS,
" %d bindings in table",
g_hash_table_size (keys->key_bindings));
}
static void
rebuild_key_binding_table (MetaKeyBindingManager *keys)
{
GList *prefs, *grabs;
meta_topic (META_DEBUG_KEYBINDINGS,
"Rebuilding key binding table from preferences");
prefs = meta_prefs_get_keybindings ();
grabs = g_hash_table_get_values (external_grabs);
rebuild_binding_table (keys, prefs, grabs);
g_list_free (prefs);
g_list_free (grabs);
}
static void
rebuild_special_bindings (MetaKeyBindingManager *keys)
{
MetaKeyCombo combo;
meta_prefs_get_overlay_binding (&combo);
keys->overlay_key_combo = combo;
meta_prefs_get_locate_pointer_binding (&combo);
keys->locate_pointer_key_combo = combo;
}
static void
ungrab_key_bindings (MetaDisplay *display)
{
GSList *windows, *l;
if (display->x11_display)
meta_x11_display_ungrab_keys (display->x11_display);
windows = meta_display_list_windows (display, META_LIST_DEFAULT);
for (l = windows; l; l = l->next)
{
MetaWindow *w = l->data;
meta_window_ungrab_keys (w);
}
g_slist_free (windows);
}
static void
grab_key_bindings (MetaDisplay *display)
{
GSList *windows, *l;
if (display->x11_display)
meta_x11_display_grab_keys (display->x11_display);
windows = meta_display_list_windows (display, META_LIST_DEFAULT);
for (l = windows; l; l = l->next)
{
MetaWindow *w = l->data;
meta_window_grab_keys (w);
}
g_slist_free (windows);
}
static MetaKeyBinding *
get_keybinding (MetaKeyBindingManager *keys,
MetaResolvedKeyCombo *resolved_combo)
{
MetaKeyBinding *binding = NULL;
int i;
for (i = 0; i < resolved_combo->len; i++)
{
guint32 key;
key = key_combo_key (resolved_combo, i);
binding = g_hash_table_lookup (keys->key_bindings_index,
GINT_TO_POINTER (key));
if (binding != NULL)
break;
}
return binding;
}
static guint
next_dynamic_keybinding_action (void)
{
static guint num_dynamic_bindings = 0;
return META_KEYBINDING_ACTION_LAST + (++num_dynamic_bindings);
}
static gboolean
add_keybinding_internal (MetaDisplay *display,
const char *name,
GSettings *settings,
MetaKeyBindingFlags flags,
MetaKeyBindingAction action,
MetaKeyHandlerFunc func,
int data,
gpointer user_data,
GDestroyNotify free_data)
{
MetaKeyHandler *handler;
if (!meta_prefs_add_keybinding (name, settings, 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->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,
GSettings *settings,
MetaKeyBindingFlags flags,
MetaKeyBindingAction action,
MetaKeyHandlerFunc handler,
int handler_arg)
{
return add_keybinding_internal (display, name, settings,
flags | META_KEY_BINDING_BUILTIN,
action, handler, handler_arg, NULL, NULL);
}
/**
* meta_display_add_keybinding:
* @display: a #MetaDisplay
* @name: the binding's name
* @settings: the #GSettings object 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.
*
* Use meta_display_remove_keybinding() to remove the binding.
*
* Returns: the corresponding keybinding action if the keybinding was
* added successfully, otherwise %META_KEYBINDING_ACTION_NONE
*/
guint
meta_display_add_keybinding (MetaDisplay *display,
const char *name,
GSettings *settings,
MetaKeyBindingFlags flags,
MetaKeyHandlerFunc handler,
gpointer user_data,
GDestroyNotify free_data)
{
guint new_action = next_dynamic_keybinding_action ();
if (!add_keybinding_internal (display, name, settings, flags, new_action,
handler, 0, user_data, free_data))
return META_KEYBINDING_ACTION_NONE;
return new_action;
}
/**
* 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 successfully,
* 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 guint
get_keybinding_action (MetaKeyBindingManager *keys,
MetaResolvedKeyCombo *resolved_combo)
{
MetaKeyBinding *binding;
/* This is much more vague than the MetaDisplay::overlay-key signal,
* which is only emitted if the overlay-key is the only key pressed;
* as this method is primarily intended for plugins to allow processing
* of mutter keybindings while holding a grab, the overlay-key-only-pressed
* tracking is left to the plugin here.
*/
if (resolved_key_combo_intersect (resolved_combo,
&keys->overlay_resolved_key_combo))
return META_KEYBINDING_ACTION_OVERLAY_KEY;
if (resolved_key_combo_intersect (resolved_combo,
&keys->locate_pointer_resolved_key_combo))
return META_KEYBINDING_ACTION_LOCATE_POINTER_KEY;
binding = get_keybinding (keys, resolved_combo);
if (binding)
{
MetaKeyGrab *grab = g_hash_table_lookup (external_grabs, binding->name);
if (grab)
return grab->action;
else
return (guint) meta_prefs_get_keybinding_action (binding->name);
}
else
{
return META_KEYBINDING_ACTION_NONE;
}
}
static xkb_mod_mask_t
mask_from_event_params (MetaKeyBindingManager *keys,
unsigned long mask)
{
return mask & 0xff & ~keys->ignored_modifier_mask;
}
/**
* meta_display_get_keybinding_action:
* @display: A #MetaDisplay
* @keycode: Raw keycode
* @mask: Event mask
*
* Get the keybinding action bound to @keycode. Builtin keybindings
* have a fixed associated #MetaKeyBindingAction, for bindings added
* dynamically the function will return the keybinding action
* meta_display_add_keybinding() returns on registration.
*
* Returns: The action that should be taken for the given key, or
* %META_KEYBINDING_ACTION_NONE.
*/
guint
meta_display_get_keybinding_action (MetaDisplay *display,
unsigned int keycode,
unsigned long mask)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
xkb_keycode_t code = (xkb_keycode_t) keycode;
MetaResolvedKeyCombo resolved_combo = { &code, 1 };
resolved_combo.mask = mask_from_event_params (keys, mask);
return get_keybinding_action (keys, &resolved_combo);
}
static void
reload_keybindings (MetaDisplay *display)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
ungrab_key_bindings (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 (keys);
reload_combos (keys);
grab_key_bindings (display);
}
static GArray *
calc_grab_modifiers (MetaKeyBindingManager *keys,
unsigned int modmask)
{
unsigned int ignored_mask;
XIGrabModifiers mods;
GArray *mods_array = g_array_new (FALSE, TRUE, sizeof (XIGrabModifiers));
/* The X server crashes if XIAnyModifier gets passed in with any
other bits. It doesn't make sense to ask for a grab of
XIAnyModifier plus other bits anyway so we avoid that. */
if (modmask & XIAnyModifier)
{
mods = (XIGrabModifiers) { XIAnyModifier, 0 };
g_array_append_val (mods_array, mods);
return mods_array;
}
mods = (XIGrabModifiers) { modmask, 0 };
g_array_append_val (mods_array, mods);
for (ignored_mask = 1;
ignored_mask <= keys->ignored_modifier_mask;
++ignored_mask)
{
if (ignored_mask & keys->ignored_modifier_mask)
{
mods = (XIGrabModifiers) { modmask | ignored_mask, 0 };
g_array_append_val (mods_array, mods);
}
}
return mods_array;
}
static void
meta_change_button_grab (MetaKeyBindingManager *keys,
Window xwindow,
gboolean grab,
gboolean sync,
int button,
int modmask)
{
if (meta_is_wayland_compositor ())
return;
MetaBackendX11 *backend = META_BACKEND_X11 (keys->backend);
Display *xdisplay = meta_backend_x11_get_xdisplay (backend);
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
GArray *mods;
XISetMask (mask.mask, XI_ButtonPress);
XISetMask (mask.mask, XI_ButtonRelease);
XISetMask (mask.mask, XI_Motion);
mods = calc_grab_modifiers (keys, modmask);
/* GrabModeSync means freeze until XAllowEvents */
if (grab)
XIGrabButton (xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
button, xwindow, None,
sync ? XIGrabModeSync : XIGrabModeAsync,
XIGrabModeAsync, False,
&mask, mods->len, (XIGrabModifiers *)mods->data);
else
XIUngrabButton (xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
button, xwindow, mods->len, (XIGrabModifiers *)mods->data);
g_array_free (mods, TRUE);
}
ClutterModifierType
meta_display_get_compositor_modifiers (MetaDisplay *display)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
return keys->window_grab_modifiers;
}
static void
meta_change_buttons_grab (MetaKeyBindingManager *keys,
Window xwindow,
gboolean grab,
gboolean sync,
int modmask)
{
#define MAX_BUTTON 3
int i;
for (i = 1; i <= MAX_BUTTON; i++)
meta_change_button_grab (keys, xwindow, grab, sync, i, modmask);
}
void
meta_display_grab_window_buttons (MetaDisplay *display,
Window xwindow)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
/* Grab Alt + button1 for moving window.
* Grab Alt + button2 for resizing window.
* Grab Alt + button3 for popping up window menu.
* Grab Alt + Shift + button1 for snap-moving window.
*/
meta_verbose ("Grabbing window buttons for 0x%lx", xwindow);
/* FIXME If we ignored errors here instead of spewing, we could
* put one big error trap around the loop and avoid a bunch of
* XSync()
*/
if (keys->window_grab_modifiers != 0)
{
meta_change_buttons_grab (keys, xwindow, TRUE, FALSE,
keys->window_grab_modifiers);
/* In addition to grabbing Alt+Button1 for moving the window,
* grab Alt+Shift+Button1 for snap-moving the window. See bug
* 112478. Unfortunately, this doesn't work with
* Shift+Alt+Button1 for some reason; so at least part of the
* order still matters, which sucks (please FIXME).
*/
meta_change_button_grab (keys, xwindow,
TRUE,
FALSE,
1, keys->window_grab_modifiers | ShiftMask);
}
}
void
meta_display_ungrab_window_buttons (MetaDisplay *display,
Window xwindow)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
if (keys->window_grab_modifiers == 0)
return;
meta_change_buttons_grab (keys, xwindow, FALSE, FALSE,
keys->window_grab_modifiers);
}
static void
update_window_grab_modifiers (MetaDisplay *display)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
ClutterModifierType virtual_mods;
unsigned int mods;
virtual_mods = meta_prefs_get_mouse_button_mods ();
devirtualize_modifiers (keys, virtual_mods, &mods);
if (keys->window_grab_modifiers != mods)
{
keys->window_grab_modifiers = mods;
g_object_notify (G_OBJECT (display), "compositor-modifiers");
}
}
void
meta_display_grab_focus_window_button (MetaDisplay *display,
MetaWindow *window)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
/* Grab button 1 for activating unfocused windows */
meta_verbose ("Grabbing unfocused window buttons for %s", window->desc);
if (window->have_focus_click_grab)
{
meta_verbose (" (well, not grabbing since we already have the grab)");
return;
}
/* FIXME If we ignored errors here instead of spewing, we could
* put one big error trap around the loop and avoid a bunch of
* XSync()
*/
meta_change_buttons_grab (keys, window->xwindow, TRUE, TRUE, XIAnyModifier);
window->have_focus_click_grab = TRUE;
}
void
meta_display_ungrab_focus_window_button (MetaDisplay *display,
MetaWindow *window)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
meta_verbose ("Ungrabbing unfocused window buttons for %s", window->desc);
if (!window->have_focus_click_grab)
return;
meta_change_buttons_grab (keys, window->xwindow, FALSE, FALSE, XIAnyModifier);
window->have_focus_click_grab = FALSE;
}
static void
prefs_changed_callback (MetaPreference pref,
void *data)
{
MetaDisplay *display = data;
MetaKeyBindingManager *keys = &display->key_binding_manager;
switch (pref)
{
case META_PREF_LOCATE_POINTER:
maybe_update_locate_pointer_keygrab (display,
meta_prefs_is_locate_pointer_enabled());
break;
case META_PREF_KEYBINDINGS:
ungrab_key_bindings (display);
rebuild_key_binding_table (keys);
rebuild_special_bindings (keys);
reload_combos (keys);
grab_key_bindings (display);
break;
case META_PREF_MOUSE_BUTTON_MODS:
{
GSList *windows, *l;
windows = meta_display_list_windows (display, META_LIST_DEFAULT);
for (l = windows; l; l = l->next)
{
MetaWindow *w = l->data;
meta_display_ungrab_window_buttons (display, w->xwindow);
}
update_window_grab_modifiers (display);
for (l = windows; l; l = l->next)
{
MetaWindow *w = l->data;
if (w->type != META_WINDOW_DOCK)
meta_display_grab_window_buttons (display, w->xwindow);
}
g_slist_free (windows);
}
default:
break;
}
}
void
meta_display_shutdown_keys (MetaDisplay *display)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
meta_prefs_remove_listener (prefs_changed_callback, display);
g_hash_table_destroy (keys->key_bindings_index);
g_hash_table_destroy (keys->key_bindings);
clear_active_keyboard_layouts (keys);
}
/* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */
static void
meta_change_keygrab (MetaKeyBindingManager *keys,
Window xwindow,
gboolean grab,
MetaResolvedKeyCombo *resolved_combo)
{
MetaBackendX11 *backend_x11;
Display *xdisplay;
GArray *mods;
int i;
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
XISetMask (mask.mask, XI_KeyPress);
XISetMask (mask.mask, XI_KeyRelease);
if (meta_is_wayland_compositor ())
return;
backend_x11 = META_BACKEND_X11 (keys->backend);
xdisplay = meta_backend_x11_get_xdisplay (backend_x11);
/* Grab keycode/modmask, together with
* all combinations of ignored modifiers.
* X provides no better way to do this.
*/
mods = calc_grab_modifiers (keys, resolved_combo->mask);
for (i = 0; i < resolved_combo->len; i++)
{
xkb_keycode_t keycode = resolved_combo->keycodes[i];
meta_topic (META_DEBUG_KEYBINDINGS,
"%s keybinding keycode %d mask 0x%x on 0x%lx",
grab ? "Grabbing" : "Ungrabbing",
keycode, resolved_combo->mask, xwindow);
if (grab)
XIGrabKeycode (xdisplay,
META_VIRTUAL_CORE_KEYBOARD_ID,
keycode, xwindow,
XIGrabModeSync, XIGrabModeAsync,
False, &mask, mods->len, (XIGrabModifiers *)mods->data);
else
XIUngrabKeycode (xdisplay,
META_VIRTUAL_CORE_KEYBOARD_ID,
keycode, xwindow,
mods->len, (XIGrabModifiers *)mods->data);
}
g_array_free (mods, TRUE);
}
typedef struct
{
MetaKeyBindingManager *keys;
Window xwindow;
gboolean only_per_window;
gboolean grab;
} ChangeKeygrabData;
static void
change_keygrab_foreach (gpointer key,
gpointer value,
gpointer user_data)
{
ChangeKeygrabData *data = user_data;
MetaKeyBinding *binding = value;
gboolean binding_is_per_window = (binding->flags & META_KEY_BINDING_PER_WINDOW) != 0;
if (data->only_per_window != binding_is_per_window)
return;
/* Ignore the key bindings marked as META_KEY_BINDING_NO_AUTO_GRAB,
* those are handled separately
*/
if (binding->flags & META_KEY_BINDING_NO_AUTO_GRAB)
return;
if (binding->resolved_combo.len == 0)
return;
meta_change_keygrab (data->keys, data->xwindow, data->grab, &binding->resolved_combo);
}
static void
change_binding_keygrabs (MetaKeyBindingManager *keys,
Window xwindow,
gboolean only_per_window,
gboolean grab)
{
ChangeKeygrabData data;
data.keys = keys;
data.xwindow = xwindow;
data.only_per_window = only_per_window;
data.grab = grab;
g_hash_table_foreach (keys->key_bindings, change_keygrab_foreach, &data);
}
static void
maybe_update_locate_pointer_keygrab (MetaDisplay *display,
gboolean grab)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
if (!display->x11_display)
return;
if (keys->locate_pointer_resolved_key_combo.len != 0)
meta_change_keygrab (keys, display->x11_display->xroot,
(!!grab & !!meta_prefs_is_locate_pointer_enabled()),
&keys->locate_pointer_resolved_key_combo);
}
static void
meta_x11_display_change_keygrabs (MetaX11Display *x11_display,
gboolean grab)
{
MetaKeyBindingManager *keys = &x11_display->display->key_binding_manager;
int i;
if (keys->overlay_resolved_key_combo.len != 0)
meta_change_keygrab (keys, x11_display->xroot,
grab, &keys->overlay_resolved_key_combo);
maybe_update_locate_pointer_keygrab (x11_display->display, grab);
for (i = 0; i < keys->n_iso_next_group_combos; i++)
meta_change_keygrab (keys, x11_display->xroot,
grab, &keys->iso_next_group_combo[i]);
change_binding_keygrabs (keys, x11_display->xroot,
FALSE, grab);
}
void
meta_x11_display_grab_keys (MetaX11Display *x11_display)
{
if (x11_display->keys_grabbed)
return;
meta_x11_display_change_keygrabs (x11_display, TRUE);
x11_display->keys_grabbed = TRUE;
}
void
meta_x11_display_ungrab_keys (MetaX11Display *x11_display)
{
if (!x11_display->keys_grabbed)
return;
meta_x11_display_change_keygrabs (x11_display, FALSE);
x11_display->keys_grabbed = FALSE;
}
static void
change_window_keygrabs (MetaKeyBindingManager *keys,
Window xwindow,
gboolean grab)
{
change_binding_keygrabs (keys, xwindow, TRUE, grab);
}
void
meta_window_grab_keys (MetaWindow *window)
{
MetaDisplay *display = window->display;
MetaKeyBindingManager *keys = &display->key_binding_manager;
if (meta_is_wayland_compositor ())
return;
if (window->all_keys_grabbed)
return;
if (window->type == META_WINDOW_DOCK
|| window->override_redirect)
{
if (window->keys_grabbed)
change_window_keygrabs (keys, window->xwindow, FALSE);
window->keys_grabbed = FALSE;
return;
}
if (window->keys_grabbed)
{
if (window->frame && !window->grab_on_frame)
change_window_keygrabs (keys, window->xwindow, FALSE);
else if (window->frame == NULL &&
window->grab_on_frame)
; /* continue to regrab on client window */
else
return; /* already all good */
}
change_window_keygrabs (keys,
meta_window_x11_get_toplevel_xwindow (window),
TRUE);
window->keys_grabbed = TRUE;
window->grab_on_frame = window->frame != NULL;
}
void
meta_window_ungrab_keys (MetaWindow *window)
{
if (!meta_is_wayland_compositor () && window->keys_grabbed)
{
MetaDisplay *display = window->display;
MetaKeyBindingManager *keys = &display->key_binding_manager;
if (window->grab_on_frame &&
window->frame != NULL)
change_window_keygrabs (keys, window->frame->xwindow, FALSE);
else if (!window->grab_on_frame)
change_window_keygrabs (keys, window->xwindow, FALSE);
window->keys_grabbed = FALSE;
}
}
static void
handle_external_grab (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer user_data)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
guint action = get_keybinding_action (keys, &binding->resolved_combo);
meta_display_accelerator_activate (display, action, event);
}
guint
meta_display_grab_accelerator (MetaDisplay *display,
const char *accelerator,
MetaKeyBindingFlags flags)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
MetaKeyBinding *binding;
MetaKeyGrab *grab;
MetaKeyCombo combo = { 0 };
MetaResolvedKeyCombo resolved_combo = { NULL, 0 };
if (!meta_parse_accelerator (accelerator, &combo))
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Failed to parse accelerator");
meta_warning ("\"%s\" is not a valid accelerator", accelerator);
return META_KEYBINDING_ACTION_NONE;
}
resolve_key_combo (keys, &combo, &resolved_combo);
if (resolved_combo.len == 0)
return META_KEYBINDING_ACTION_NONE;
if (get_keybinding (keys, &resolved_combo))
{
resolved_key_combo_reset (&resolved_combo);
return META_KEYBINDING_ACTION_NONE;
}
if (!meta_is_wayland_compositor ())
{
meta_change_keygrab (keys, display->x11_display->xroot,
TRUE, &resolved_combo);
}
grab = g_new0 (MetaKeyGrab, 1);
grab->action = next_dynamic_keybinding_action ();
grab->name = meta_external_binding_name_for_action (grab->action);
grab->combo = combo;
grab->flags = flags;
g_hash_table_insert (external_grabs, grab->name, grab);
binding = g_new0 (MetaKeyBinding, 1);
binding->name = grab->name;
binding->handler = HANDLER ("external-grab");
binding->combo = combo;
binding->resolved_combo = resolved_combo;
binding->flags = flags;
g_hash_table_add (keys->key_bindings, binding);
index_binding (keys, binding);
return grab->action;
}
gboolean
meta_display_ungrab_accelerator (MetaDisplay *display,
guint action)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
MetaKeyBinding *binding;
MetaKeyGrab *grab;
g_autofree char *key = NULL;
MetaResolvedKeyCombo resolved_combo = { NULL, 0 };
g_return_val_if_fail (action != META_KEYBINDING_ACTION_NONE, FALSE);
key = meta_external_binding_name_for_action (action);
grab = g_hash_table_lookup (external_grabs, key);
if (!grab)
return FALSE;
resolve_key_combo (keys, &grab->combo, &resolved_combo);
binding = get_keybinding (keys, &resolved_combo);
if (binding)
{
int i;
if (!meta_is_wayland_compositor ())
{
meta_change_keygrab (keys, display->x11_display->xroot,
FALSE, &binding->resolved_combo);
}
for (i = 0; i < binding->resolved_combo.len; i++)
{
guint32 index_key = key_combo_key (&binding->resolved_combo, i);
g_hash_table_remove (keys->key_bindings_index, GINT_TO_POINTER (index_key));
}
g_hash_table_remove (keys->key_bindings, binding);
}
g_hash_table_remove (external_grabs, key);
resolved_key_combo_reset (&resolved_combo);
return TRUE;
}
static gboolean
grab_keyboard (MetaBackend *backend,
Window xwindow,
uint32_t timestamp,
int grab_mode)
{
MetaBackendX11 *backend_x11;
Display *xdisplay;
int grab_status;
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
XISetMask (mask.mask, XI_KeyPress);
XISetMask (mask.mask, XI_KeyRelease);
if (meta_is_wayland_compositor ())
return TRUE;
/* Grab the keyboard, so we get key releases and all key
* presses
*/
backend_x11 = META_BACKEND_X11 (backend);
xdisplay = meta_backend_x11_get_xdisplay (backend_x11);
/* Strictly, we only need to set grab_mode on the keyboard device
* while the pointer should always be XIGrabModeAsync. Unfortunately
* there is a bug in the X server, only fixed (link below) in 1.15,
* which swaps these arguments for keyboard devices. As such, we set
* both the device and the paired device mode which works around
* that bug and also works on fixed X servers.
*
* http://cgit.freedesktop.org/xorg/xserver/commit/?id=9003399708936481083424b4ff8f18a16b88b7b3
*/
grab_status = XIGrabDevice (xdisplay,
META_VIRTUAL_CORE_KEYBOARD_ID,
xwindow,
timestamp,
None,
grab_mode, grab_mode,
False, /* owner_events */
&mask);
return (grab_status == Success);
}
static void
ungrab_keyboard (MetaBackend *backend,
uint32_t timestamp)
{
MetaBackendX11 *backend_x11;
Display *xdisplay;
if (meta_is_wayland_compositor ())
return;
backend_x11 = META_BACKEND_X11 (backend);
xdisplay = meta_backend_x11_get_xdisplay (backend_x11);
XIUngrabDevice (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp);
}
gboolean
meta_window_grab_all_keys (MetaWindow *window,
guint32 timestamp)
{
MetaDisplay *display = meta_window_get_display (window);
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
Window grabwindow;
gboolean retval = TRUE;
if (window->all_keys_grabbed)
return FALSE;
if (window->keys_grabbed)
meta_window_ungrab_keys (window);
/* Make sure the window is focused, otherwise the grab
* won't do a lot of good.
*/
meta_topic (META_DEBUG_FOCUS,
"Focusing %s because we're grabbing all its keys",
window->desc);
meta_window_focus (window, timestamp);
if (!meta_is_wayland_compositor ())
{
grabwindow = meta_window_x11_get_toplevel_xwindow (window);
meta_topic (META_DEBUG_KEYBINDINGS,
"Grabbing all keys on window %s", window->desc);
retval = grab_keyboard (backend, grabwindow, timestamp, XIGrabModeAsync);
}
if (retval)
{
window->keys_grabbed = FALSE;
window->all_keys_grabbed = TRUE;
window->grab_on_frame = window->frame != NULL;
}
return retval;
}
void
meta_window_ungrab_all_keys (MetaWindow *window,
guint32 timestamp)
{
if (window->all_keys_grabbed)
{
if (!meta_is_wayland_compositor())
{
MetaDisplay *display = meta_window_get_display (window);
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
ungrab_keyboard (backend, timestamp);
}
window->grab_on_frame = FALSE;
window->all_keys_grabbed = FALSE;
window->keys_grabbed = FALSE;
/* Re-establish our standard bindings */
meta_window_grab_keys (window);
}
}
void
meta_display_freeze_keyboard (MetaDisplay *display, guint32 timestamp)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
if (!META_IS_BACKEND_X11 (backend))
return;
Window window = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend));
grab_keyboard (backend, window, timestamp, XIGrabModeSync);
}
void
meta_display_ungrab_keyboard (MetaDisplay *display, guint32 timestamp)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
ungrab_keyboard (backend, timestamp);
}
void
meta_display_unfreeze_keyboard (MetaDisplay *display, guint32 timestamp)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
if (!META_IS_BACKEND_X11 (backend))
return;
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
XIAllowEvents (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID,
XIAsyncDevice, timestamp);
/* We shouldn't need to unfreeze the pointer device here, however we
* have to, due to the workaround we do in grab_keyboard().
*/
XIAllowEvents (xdisplay, META_VIRTUAL_CORE_POINTER_ID,
XIAsyncDevice, timestamp);
}
static gboolean
is_modifier (xkb_keysym_t keysym)
{
switch (keysym)
{
case XKB_KEY_Shift_L:
case XKB_KEY_Shift_R:
case XKB_KEY_Control_L:
case XKB_KEY_Control_R:
case XKB_KEY_Caps_Lock:
case XKB_KEY_Shift_Lock:
case XKB_KEY_Meta_L:
case XKB_KEY_Meta_R:
case XKB_KEY_Alt_L:
case XKB_KEY_Alt_R:
case XKB_KEY_Super_L:
case XKB_KEY_Super_R:
case XKB_KEY_Hyper_L:
case XKB_KEY_Hyper_R:
return TRUE;
default:
return FALSE;
}
}
static void
invoke_handler (MetaDisplay *display,
MetaKeyHandler *handler,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding)
{
if (handler->func)
(* handler->func) (display,
handler->flags & META_KEY_BINDING_PER_WINDOW ?
window : NULL,
event,
binding,
handler->user_data);
else
(* handler->default_func) (display,
handler->flags & META_KEY_BINDING_PER_WINDOW ?
window: NULL,
event,
binding,
NULL);
}
static gboolean
meta_key_binding_has_handler_func (MetaKeyBinding *binding)
{
return (!!binding->handler->func || !!binding->handler->default_func);
}
static gboolean
process_event (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode;
MetaResolvedKeyCombo resolved_combo = { &keycode, 1 };
MetaKeyBinding *binding;
/* we used to have release-based bindings but no longer. */
if (event->type == CLUTTER_KEY_RELEASE)
return FALSE;
resolved_combo.mask = mask_from_event_params (keys, event->modifier_state);
binding = get_keybinding (keys, &resolved_combo);
if (!binding ||
(!window && binding->flags & META_KEY_BINDING_PER_WINDOW))
goto not_found;
if (binding->handler == NULL)
meta_bug ("Binding %s has no handler", binding->name);
if (!meta_key_binding_has_handler_func (binding))
goto not_found;
if (display->focus_window &&
!(binding->handler->flags & META_KEY_BINDING_NON_MASKABLE))
{
ClutterInputDevice *source;
source = clutter_event_get_source_device ((ClutterEvent *) event);
if (meta_window_shortcuts_inhibited (display->focus_window, source))
goto not_found;
}
/* If the compositor filtered out the keybindings, that
* means they don't want the binding to trigger, so we do
* the same thing as if the binding didn't exist. */
if (meta_compositor_filter_keybinding (display->compositor, binding))
goto not_found;
if (event->flags & CLUTTER_EVENT_FLAG_REPEATED &&
binding->flags & META_KEY_BINDING_IGNORE_AUTOREPEAT)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Ignore autorepeat for handler %s",
binding->name);
return TRUE;
}
meta_topic (META_DEBUG_KEYBINDINGS,
"Running handler for %s",
binding->name);
/* Global keybindings count as a let-the-terminal-lose-focus
* due to new window mapping until the user starts
* interacting with the terminal again.
*/
display->allow_terminal_deactivation = TRUE;
invoke_handler (display, binding->handler, window, event, binding);
return TRUE;
not_found:
meta_topic (META_DEBUG_KEYBINDINGS,
"No handler found for this event in this binding table");
return FALSE;
}
static gboolean
process_special_modifier_key (MetaDisplay *display,
ClutterKeyEvent *event,
MetaWindow *window,
gboolean *modifier_press_only,
MetaResolvedKeyCombo *resolved_key_combo,
GFunc trigger_callback)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
MetaBackend *backend = keys->backend;
Display *xdisplay;
if (META_IS_BACKEND_X11 (backend))
xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
else
xdisplay = NULL;
if (*modifier_press_only)
{
if (! resolved_key_combo_has_keycode (resolved_key_combo,
event->hardware_keycode))
{
*modifier_press_only = FALSE;
/* If this is a wayland session, we can avoid the shenanigans
* about passive grabs below, and let the event continue to
* be processed through the regular paths.
*/
if (!xdisplay)
return FALSE;
/* OK, the user hit modifier+key rather than pressing and
* releasing the modifier key alone. We want to handle the key
* sequence "normally". Unfortunately, using
* XAllowEvents(..., ReplayKeyboard, ...) doesn't quite
* work, since global keybindings won't be activated ("this
* time, however, the function ignores any passive grabs at
* above (toward the root of) the grab_window of the grab
* just released.") So, we first explicitly check for one of
* our global keybindings, and if not found, we then replay
* the event. Other clients with global grabs will be out of
* luck.
*/
if (process_event (display, window, event))
{
/* As normally, after we've handled a global key
* binding, we unfreeze the keyboard but keep the grab
* (this is important for something like cycling
* windows */
if (xdisplay)
XIAllowEvents (xdisplay,
meta_input_device_x11_get_device_id (event->device),
XIAsyncDevice, event->time);
}
else
{
/* Replay the event so it gets delivered to our
* per-window key bindings or to the application */
if (xdisplay)
XIAllowEvents (xdisplay,
meta_input_device_x11_get_device_id (event->device),
XIReplayDevice, event->time);
}
}
else if (event->type == CLUTTER_KEY_RELEASE)
{
MetaKeyBinding *binding;
*modifier_press_only = FALSE;
/* We want to unfreeze events, but keep the grab so that if the user
* starts typing into the overlay we get all the keys */
if (xdisplay)
XIAllowEvents (xdisplay,
meta_input_device_x11_get_device_id (event->device),
XIAsyncDevice, event->time);
binding = get_keybinding (keys, resolved_key_combo);
if (binding &&
meta_compositor_filter_keybinding (display->compositor, binding))
return TRUE;
trigger_callback (display, NULL);
}
else
{
/* In some rare race condition, mutter might not receive the Super_L
* KeyRelease event because:
* - the compositor might end the modal mode and call XIUngrabDevice
* while the key is still down
* - passive grabs are only activated on KeyPress and not KeyRelease.
*
* In this case, modifier_press_only might be wrong.
* Mutter still ought to acknowledge events, otherwise the X server
* will not send the next events.
*
* https://bugzilla.gnome.org/show_bug.cgi?id=666101
*/
if (xdisplay)
XIAllowEvents (xdisplay,
meta_input_device_x11_get_device_id (event->device),
XIAsyncDevice, event->time);
}
return TRUE;
}
else if (event->type == CLUTTER_KEY_PRESS &&
((event->modifier_state & ~(IGNORED_MODIFIERS)) & CLUTTER_MODIFIER_MASK) == 0 &&
resolved_key_combo_has_keycode (resolved_key_combo,
event->hardware_keycode))
{
*modifier_press_only = TRUE;
/* We keep the keyboard frozen - this allows us to use ReplayKeyboard
* on the next event if it's not the release of the modifier key */
if (xdisplay)
XIAllowEvents (xdisplay,
meta_input_device_x11_get_device_id (event->device),
XISyncDevice, event->time);
return TRUE;
}
else
return FALSE;
}
static gboolean
process_overlay_key (MetaDisplay *display,
ClutterKeyEvent *event,
MetaWindow *window)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
if (display->focus_window && !keys->overlay_key_only_pressed)
{
ClutterInputDevice *source;
source = clutter_event_get_source_device ((ClutterEvent *) event);
if (meta_window_shortcuts_inhibited (display->focus_window, source))
return FALSE;
}
return process_special_modifier_key (display,
event,
window,
&keys->overlay_key_only_pressed,
&keys->overlay_resolved_key_combo,
(GFunc) meta_display_overlay_key_activate);
}
static void
handle_locate_pointer (MetaDisplay *display)
{
meta_compositor_locate_pointer (display->compositor);
}
static gboolean
process_locate_pointer_key (MetaDisplay *display,
ClutterKeyEvent *event,
MetaWindow *window)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
return process_special_modifier_key (display,
event,
window,
&keys->locate_pointer_key_only_pressed,
&keys->locate_pointer_resolved_key_combo,
(GFunc) handle_locate_pointer);
}
static gboolean
process_iso_next_group (MetaDisplay *display,
ClutterKeyEvent *event)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
gboolean activate;
xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode;
xkb_mod_mask_t mask;
int i, j;
if (event->type == CLUTTER_KEY_RELEASE)
return FALSE;
activate = FALSE;
mask = mask_from_event_params (keys, event->modifier_state);
for (i = 0; i < keys->n_iso_next_group_combos; ++i)
{
for (j = 0; j < keys->iso_next_group_combo[i].len; ++j)
{
if (keycode == keys->iso_next_group_combo[i].keycodes[j] &&
mask == keys->iso_next_group_combo[i].mask)
{
/* If the signal handler returns TRUE the keyboard will
remain frozen. It's the signal handler's responsibility
to unfreeze it. */
if (!meta_display_modifiers_accelerator_activate (display))
meta_display_unfreeze_keyboard (display, event->time);
activate = TRUE;
break;
}
}
}
return activate;
}
static gboolean
process_key_event (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event)
{
gboolean keep_grab;
gboolean all_keys_grabbed;
all_keys_grabbed = window ? window->all_keys_grabbed : FALSE;
if (!all_keys_grabbed)
{
if (process_overlay_key (display, event, window))
return TRUE;
if (process_locate_pointer_key (display, event, window))
return FALSE; /* Continue with the event even if handled */
if (process_iso_next_group (display, event))
return TRUE;
}
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
if (META_IS_BACKEND_X11 (backend))
{
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
XIAllowEvents (xdisplay,
meta_input_device_x11_get_device_id (event->device),
XIAsyncDevice, event->time);
}
}
keep_grab = TRUE;
if (all_keys_grabbed)
{
/* If we get here we have a global grab, because
* we're in some special keyboard mode such as window move
* mode.
*/
if (window == display->grab_window)
{
if (display->grab_op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD)
{
if (display->grab_op == META_GRAB_OP_KEYBOARD_MOVING)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Processing event for keyboard move");
keep_grab = process_keyboard_move_grab (display, window, event);
}
else
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Processing event for keyboard resize");
keep_grab = process_keyboard_resize_grab (display, window, event);
}
}
else if (display->grab_op & META_GRAB_OP_MOVING)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Processing event for mouse-only move/resize");
keep_grab = process_mouse_move_resize_grab (display, window, event);
}
}
if (!keep_grab)
meta_display_end_grab_op (display, event->time);
return TRUE;
}
/* Do the normal keybindings */
return process_event (display, window, event);
}
/* Handle a key event. May be called recursively: some key events cause
* grabs to be ended and then need to be processed again in their own
* right. This cannot cause infinite recursion because we never call
* ourselves when there wasn't a grab, and we always clear the grab
* first; the invariant is enforced using an assertion. See #112560.
*
* The return value is whether we handled the key event.
*
* FIXME: We need to prove there are no race conditions here.
* FIXME: Does it correctly handle alt-Tab being followed by another
* grabbing keypress without letting go of alt?
* FIXME: An iterative solution would probably be simpler to understand
* (and help us solve the other fixmes).
*/
gboolean
meta_keybindings_process_event (MetaDisplay *display,
MetaWindow *window,
const ClutterEvent *event)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
switch (event->type)
{
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
case CLUTTER_TOUCH_BEGIN:
case CLUTTER_TOUCH_END:
case CLUTTER_SCROLL:
keys->overlay_key_only_pressed = FALSE;
keys->locate_pointer_key_only_pressed = FALSE;
return FALSE;
case CLUTTER_KEY_PRESS:
case CLUTTER_KEY_RELEASE:
return process_key_event (display, window, (ClutterKeyEvent *) event);
default:
return FALSE;
}
}
static gboolean
process_mouse_move_resize_grab (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event)
{
/* don't care about releases, but eat them, don't end grab */
if (event->type == CLUTTER_KEY_RELEASE)
return TRUE;
if (event->keyval == CLUTTER_KEY_Escape)
{
MetaTileMode tile_mode;
/* Hide the tiling preview if necessary */
if (display->preview_tile_mode != META_TILE_NONE)
meta_display_hide_tile_preview (display);
/* Restore the original tile mode */
tile_mode = display->grab_tile_mode;
window->tile_monitor_number = display->grab_tile_monitor_number;
/* End move or resize and restore to original state. If the
* window was a maximized window that had been "shaken loose" we
* need to remaximize it. In normal cases, we need to do a
* moveresize now to get the position back to the original.
*/
if (window->shaken_loose || tile_mode == META_TILE_MAXIMIZED)
meta_window_maximize (window, META_MAXIMIZE_BOTH);
else if (tile_mode != META_TILE_NONE)
meta_window_restore_tile (window,
tile_mode,
display->grab_initial_window_pos.width,
display->grab_initial_window_pos.height);
else
meta_window_move_resize_frame (display->grab_window,
TRUE,
display->grab_initial_window_pos.x,
display->grab_initial_window_pos.y,
display->grab_initial_window_pos.width,
display->grab_initial_window_pos.height);
/* End grab */
return FALSE;
}
return TRUE;
}
static gboolean
process_keyboard_move_grab (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event)
{
MetaEdgeResistanceFlags flags;
gboolean handled;
MetaRectangle frame_rect;
int x, y;
int incr;
handled = FALSE;
/* don't care about releases, but eat them, don't end grab */
if (event->type == CLUTTER_KEY_RELEASE)
return TRUE;
/* don't end grab on modifier key presses */
if (is_modifier (event->keyval))
return TRUE;
meta_window_get_frame_rect (window, &frame_rect);
x = frame_rect.x;
y = frame_rect.y;
flags = META_EDGE_RESISTANCE_KEYBOARD_OP | META_EDGE_RESISTANCE_WINDOWS;
if ((event->modifier_state & CLUTTER_SHIFT_MASK) != 0)
flags |= META_EDGE_RESISTANCE_SNAP;
#define SMALL_INCREMENT 1
#define NORMAL_INCREMENT 10
if (flags & META_EDGE_RESISTANCE_SNAP)
incr = 1;
else if (event->modifier_state & CLUTTER_CONTROL_MASK)
incr = SMALL_INCREMENT;
else
incr = NORMAL_INCREMENT;
if (event->keyval == CLUTTER_KEY_Escape)
{
/* End move and restore to original state. If the window was a
* maximized window that had been "shaken loose" we need to
* remaximize it. In normal cases, we need to do a moveresize
* now to get the position back to the original.
*/
if (window->shaken_loose)
meta_window_maximize (window, META_MAXIMIZE_BOTH);
else
meta_window_move_resize_frame (display->grab_window,
TRUE,
display->grab_initial_window_pos.x,
display->grab_initial_window_pos.y,
display->grab_initial_window_pos.width,
display->grab_initial_window_pos.height);
}
/* When moving by increments, we still snap to edges if the move
* to the edge is smaller than the increment. This is because
* Shift + arrow to snap is sort of a hidden feature. This way
* people using just arrows shouldn't get too frustrated.
*/
switch (event->keyval)
{
case CLUTTER_KEY_KP_Home:
case CLUTTER_KEY_KP_Prior:
case CLUTTER_KEY_Up:
case CLUTTER_KEY_KP_Up:
y -= incr;
handled = TRUE;
break;
case CLUTTER_KEY_KP_End:
case CLUTTER_KEY_KP_Next:
case CLUTTER_KEY_Down:
case CLUTTER_KEY_KP_Down:
y += incr;
handled = TRUE;
break;
}
switch (event->keyval)
{
case CLUTTER_KEY_KP_Home:
case CLUTTER_KEY_KP_End:
case CLUTTER_KEY_Left:
case CLUTTER_KEY_KP_Left:
x -= incr;
handled = TRUE;
break;
case CLUTTER_KEY_KP_Prior:
case CLUTTER_KEY_KP_Next:
case CLUTTER_KEY_Right:
case CLUTTER_KEY_KP_Right:
x += incr;
handled = TRUE;
break;
}
if (handled)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Computed new window location %d,%d due to keypress",
x, y);
meta_window_edge_resistance_for_move (window,
&x,
&y,
flags);
meta_window_move_frame (window, TRUE, x, y);
meta_window_update_keyboard_move (window);
}
return handled;
}
static gboolean
process_keyboard_resize_grab_op_change (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event)
{
gboolean handled;
handled = FALSE;
switch (display->grab_op)
{
case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
switch (event->keyval)
{
case CLUTTER_KEY_Up:
case CLUTTER_KEY_KP_Up:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
handled = TRUE;
break;
case CLUTTER_KEY_Down:
case CLUTTER_KEY_KP_Down:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
handled = TRUE;
break;
case CLUTTER_KEY_Left:
case CLUTTER_KEY_KP_Left:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
handled = TRUE;
break;
case CLUTTER_KEY_Right:
case CLUTTER_KEY_KP_Right:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
handled = TRUE;
break;
}
break;
case META_GRAB_OP_KEYBOARD_RESIZING_S:
switch (event->keyval)
{
case CLUTTER_KEY_Left:
case CLUTTER_KEY_KP_Left:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
handled = TRUE;
break;
case CLUTTER_KEY_Right:
case CLUTTER_KEY_KP_Right:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
handled = TRUE;
break;
}
break;
case META_GRAB_OP_KEYBOARD_RESIZING_N:
switch (event->keyval)
{
case CLUTTER_KEY_Left:
case CLUTTER_KEY_KP_Left:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
handled = TRUE;
break;
case CLUTTER_KEY_Right:
case CLUTTER_KEY_KP_Right:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
handled = TRUE;
break;
}
break;
case META_GRAB_OP_KEYBOARD_RESIZING_W:
switch (event->keyval)
{
case CLUTTER_KEY_Up:
case CLUTTER_KEY_KP_Up:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
handled = TRUE;
break;
case CLUTTER_KEY_Down:
case CLUTTER_KEY_KP_Down:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
handled = TRUE;
break;
}
break;
case META_GRAB_OP_KEYBOARD_RESIZING_E:
switch (event->keyval)
{
case CLUTTER_KEY_Up:
case CLUTTER_KEY_KP_Up:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
handled = TRUE;
break;
case CLUTTER_KEY_Down:
case CLUTTER_KEY_KP_Down:
display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
handled = TRUE;
break;
}
break;
case META_GRAB_OP_KEYBOARD_RESIZING_SE:
case META_GRAB_OP_KEYBOARD_RESIZING_NE:
case META_GRAB_OP_KEYBOARD_RESIZING_SW:
case META_GRAB_OP_KEYBOARD_RESIZING_NW:
break;
default:
g_assert_not_reached ();
break;
}
if (handled)
{
meta_window_update_keyboard_resize (window, TRUE);
return TRUE;
}
return FALSE;
}
static gboolean
process_keyboard_resize_grab (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event)
{
MetaRectangle frame_rect;
gboolean handled;
int height_inc;
int width_inc;
int width, height;
MetaEdgeResistanceFlags flags;
MetaGravity gravity;
handled = FALSE;
/* don't care about releases, but eat them, don't end grab */
if (event->type == CLUTTER_KEY_RELEASE)
return TRUE;
/* don't end grab on modifier key presses */
if (is_modifier (event->keyval))
return TRUE;
if (event->keyval == CLUTTER_KEY_Escape)
{
/* End resize and restore to original state. */
meta_window_move_resize_frame (display->grab_window,
TRUE,
display->grab_initial_window_pos.x,
display->grab_initial_window_pos.y,
display->grab_initial_window_pos.width,
display->grab_initial_window_pos.height);
return FALSE;
}
if (process_keyboard_resize_grab_op_change (display, window, event))
return TRUE;
width = window->rect.width;
height = window->rect.height;
meta_window_get_frame_rect (window, &frame_rect);
width = frame_rect.width;
height = frame_rect.height;
gravity = meta_resize_gravity_from_grab_op (display->grab_op);
flags = META_EDGE_RESISTANCE_KEYBOARD_OP;
if ((event->modifier_state & CLUTTER_SHIFT_MASK) != 0)
flags |= META_EDGE_RESISTANCE_SNAP;
#define SMALL_INCREMENT 1
#define NORMAL_INCREMENT 10
if (flags & META_EDGE_RESISTANCE_SNAP)
{
height_inc = 1;
width_inc = 1;
}
else if (event->modifier_state & CLUTTER_CONTROL_MASK)
{
width_inc = SMALL_INCREMENT;
height_inc = SMALL_INCREMENT;
}
else
{
width_inc = NORMAL_INCREMENT;
height_inc = NORMAL_INCREMENT;
}
/* If this is a resize increment window, make the amount we resize
* the window by match that amount (well, unless snap resizing...)
*/
if (window->size_hints.width_inc > 1)
width_inc = window->size_hints.width_inc;
if (window->size_hints.height_inc > 1)
height_inc = window->size_hints.height_inc;
switch (event->keyval)
{
case CLUTTER_KEY_Up:
case CLUTTER_KEY_KP_Up:
switch (gravity)
{
case META_GRAVITY_NORTH:
case META_GRAVITY_NORTH_WEST:
case META_GRAVITY_NORTH_EAST:
/* Move bottom edge up */
height -= height_inc;
break;
case META_GRAVITY_SOUTH:
case META_GRAVITY_SOUTH_WEST:
case META_GRAVITY_SOUTH_EAST:
/* Move top edge up */
height += height_inc;
break;
case META_GRAVITY_EAST:
case META_GRAVITY_WEST:
case META_GRAVITY_CENTER:
case META_GRAVITY_NONE:
case META_GRAVITY_STATIC:
g_assert_not_reached ();
break;
}
handled = TRUE;
break;
case CLUTTER_KEY_Down:
case CLUTTER_KEY_KP_Down:
switch (gravity)
{
case META_GRAVITY_NORTH:
case META_GRAVITY_NORTH_WEST:
case META_GRAVITY_NORTH_EAST:
/* Move bottom edge down */
height += height_inc;
break;
case META_GRAVITY_SOUTH:
case META_GRAVITY_SOUTH_WEST:
case META_GRAVITY_SOUTH_EAST:
/* Move top edge down */
height -= height_inc;
break;
case META_GRAVITY_EAST:
case META_GRAVITY_WEST:
case META_GRAVITY_CENTER:
case META_GRAVITY_NONE:
case META_GRAVITY_STATIC:
g_assert_not_reached ();
break;
}
handled = TRUE;
break;
case CLUTTER_KEY_Left:
case CLUTTER_KEY_KP_Left:
switch (gravity)
{
case META_GRAVITY_EAST:
case META_GRAVITY_SOUTH_EAST:
case META_GRAVITY_NORTH_EAST:
/* Move left edge left */
width += width_inc;
break;
case META_GRAVITY_WEST:
case META_GRAVITY_SOUTH_WEST:
case META_GRAVITY_NORTH_WEST:
/* Move right edge left */
width -= width_inc;
break;
case META_GRAVITY_NORTH:
case META_GRAVITY_SOUTH:
case META_GRAVITY_CENTER:
case META_GRAVITY_NONE:
case META_GRAVITY_STATIC:
g_assert_not_reached ();
break;
}
handled = TRUE;
break;
case CLUTTER_KEY_Right:
case CLUTTER_KEY_KP_Right:
switch (gravity)
{
case META_GRAVITY_EAST:
case META_GRAVITY_SOUTH_EAST:
case META_GRAVITY_NORTH_EAST:
/* Move left edge right */
width -= width_inc;
break;
case META_GRAVITY_WEST:
case META_GRAVITY_SOUTH_WEST:
case META_GRAVITY_NORTH_WEST:
/* Move right edge right */
width += width_inc;
break;
case META_GRAVITY_NORTH:
case META_GRAVITY_SOUTH:
case META_GRAVITY_CENTER:
case META_GRAVITY_NONE:
case META_GRAVITY_STATIC:
g_assert_not_reached ();
break;
}
handled = TRUE;
break;
default:
break;
}
/* fixup hack (just paranoia, not sure it's required) */
if (height < 1)
height = 1;
if (width < 1)
width = 1;
if (handled)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Computed new window size due to keypress: "
"%dx%d, gravity %s",
width, height, meta_gravity_to_string (gravity));
/* Do any edge resistance/snapping */
meta_window_edge_resistance_for_resize (window,
&width,
&height,
gravity,
flags);
meta_window_resize_frame_with_gravity (window,
TRUE,
width,
height,
gravity);
meta_window_update_keyboard_resize (window, FALSE);
}
return handled;
}
static void
handle_switch_to_last_workspace (MetaDisplay *display,
MetaWindow *event_window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
gint target = meta_workspace_manager_get_n_workspaces (workspace_manager) - 1;
MetaWorkspace *workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, target);
meta_workspace_activate (workspace, event->time);
}
static void
handle_switch_to_workspace (MetaDisplay *display,
MetaWindow *event_window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
gint which = binding->handler->data;
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
MetaWorkspace *workspace;
if (which < 0)
{
/* Negative workspace numbers are directions with respect to the
* current workspace.
*/
workspace = meta_workspace_get_neighbor (workspace_manager->active_workspace,
which);
}
else
{
workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which);
}
if (workspace)
{
meta_workspace_activate (workspace, event->time);
}
else
{
/* We could offer to create it I suppose */
}
}
static void
handle_maximize_vertically (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->has_resize_func)
{
if (window->maximized_vertically)
meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL);
else
meta_window_maximize (window, META_MAXIMIZE_VERTICAL);
}
}
static void
handle_maximize_horizontally (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->has_resize_func)
{
if (window->maximized_horizontally)
meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL);
else
meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL);
}
}
static void
handle_always_on_top (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->wm_state_above == FALSE)
meta_window_make_above (window);
else
meta_window_unmake_above (window);
}
static void
handle_move_to_corner_backend (MetaDisplay *display,
MetaWindow *window,
MetaGravity gravity)
{
MetaRectangle work_area;
MetaRectangle frame_rect;
int new_x, new_y;
if (!window->monitor)
return;
meta_window_get_work_area_current_monitor (window, &work_area);
meta_window_get_frame_rect (window, &frame_rect);
switch (gravity)
{
case META_GRAVITY_NORTH_WEST:
case META_GRAVITY_WEST:
case META_GRAVITY_SOUTH_WEST:
new_x = work_area.x;
break;
case META_GRAVITY_NORTH:
case META_GRAVITY_SOUTH:
new_x = frame_rect.x;
break;
case META_GRAVITY_NORTH_EAST:
case META_GRAVITY_EAST:
case META_GRAVITY_SOUTH_EAST:
new_x = work_area.x + work_area.width - frame_rect.width;
break;
default:
g_assert_not_reached ();
}
switch (gravity)
{
case META_GRAVITY_NORTH_WEST:
case META_GRAVITY_NORTH:
case META_GRAVITY_NORTH_EAST:
new_y = work_area.y;
break;
case META_GRAVITY_WEST:
case META_GRAVITY_EAST:
new_y = frame_rect.y;
break;
case META_GRAVITY_SOUTH_WEST:
case META_GRAVITY_SOUTH:
case META_GRAVITY_SOUTH_EAST:
new_y = work_area.y + work_area.height - frame_rect.height;
break;
default:
g_assert_not_reached ();
}
meta_window_move_frame (window,
TRUE,
new_x,
new_y);
}
static void
handle_move_to_corner_nw (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH_WEST);
}
static void
handle_move_to_corner_ne (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH_EAST);
}
static void
handle_move_to_corner_sw (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH_WEST);
}
static void
handle_move_to_corner_se (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH_EAST);
}
static void
handle_move_to_side_n (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH);
}
static void
handle_move_to_side_s (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH);
}
static void
handle_move_to_side_e (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
handle_move_to_corner_backend (display, window, META_GRAVITY_EAST);
}
static void
handle_move_to_side_w (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
handle_move_to_corner_backend (display, window, META_GRAVITY_WEST);
}
static void
handle_move_to_center (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaRectangle work_area;
MetaRectangle frame_rect;
meta_window_get_work_area_current_monitor (window, &work_area);
meta_window_get_frame_rect (window, &frame_rect);
meta_window_move_frame (window,
TRUE,
work_area.x + (work_area.width - frame_rect.width ) / 2,
work_area.y + (work_area.height - frame_rect.height) / 2);
}
static void
handle_show_desktop (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
if (workspace_manager->active_workspace->showing_desktop)
{
meta_workspace_manager_unshow_desktop (workspace_manager);
meta_workspace_focus_default_window (workspace_manager->active_workspace,
NULL,
event->time);
}
else
meta_workspace_manager_show_desktop (workspace_manager, event->time);
}
static void
handle_activate_window_menu (MetaDisplay *display,
MetaWindow *event_window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (display->focus_window)
{
int x, y;
MetaRectangle frame_rect;
cairo_rectangle_int_t child_rect;
meta_window_get_frame_rect (display->focus_window, &frame_rect);
meta_window_get_client_area_rect (display->focus_window, &child_rect);
x = frame_rect.x + child_rect.x;
if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL)
x += child_rect.width;
y = frame_rect.y + child_rect.y;
meta_window_show_menu (display->focus_window, META_WINDOW_MENU_WM, x, y);
}
}
static void
do_choose_window (MetaDisplay *display,
MetaWindow *event_window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gboolean backward)
{
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
MetaTabList type = binding->handler->data;
MetaWindow *window;
meta_topic (META_DEBUG_KEYBINDINGS,
"Tab list = %u", type);
window = meta_display_get_tab_next (display,
type,
workspace_manager->active_workspace,
NULL,
backward);
if (window)
meta_window_activate (window, event->time);
}
static void
handle_switch (MetaDisplay *display,
MetaWindow *event_window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
gboolean backwards = meta_key_binding_is_reversed (binding);
do_choose_window (display, event_window, event, binding, backwards);
}
static void
handle_cycle (MetaDisplay *display,
MetaWindow *event_window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
gboolean backwards = meta_key_binding_is_reversed (binding);
do_choose_window (display, event_window, event, binding, backwards);
}
static void
handle_toggle_fullscreen (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->fullscreen)
meta_window_unmake_fullscreen (window);
else if (window->has_fullscreen_func)
meta_window_make_fullscreen (window);
}
static void
handle_toggle_above (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->wm_state_above)
meta_window_unmake_above (window);
else
meta_window_make_above (window);
}
static void
handle_toggle_tiled (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaTileMode mode = binding->handler->data;
if ((META_WINDOW_TILED_LEFT (window) && mode == META_TILE_LEFT) ||
(META_WINDOW_TILED_RIGHT (window) && mode == META_TILE_RIGHT))
{
meta_window_untile (window);
}
else if (meta_window_can_tile_side_by_side (window))
{
window->tile_monitor_number = window->monitor->number;
/* Maximization constraints beat tiling constraints, so if the window
* is maximized, tiling won't have any effect unless we unmaximize it
* horizontally first; rather than calling meta_window_unmaximize(),
* we just set the flag and rely on meta_window_tile() syncing it to
* save an additional roundtrip.
*/
window->maximized_horizontally = FALSE;
meta_window_tile (window, mode);
}
}
static void
handle_toggle_maximized (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (META_WINDOW_MAXIMIZED (window))
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
else if (window->has_maximize_func)
meta_window_maximize (window, META_MAXIMIZE_BOTH);
}
static void
handle_maximize (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->has_maximize_func)
meta_window_maximize (window, META_MAXIMIZE_BOTH);
}
static void
handle_unmaximize (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->maximized_vertically || window->maximized_horizontally)
meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
}
static void
handle_toggle_shaded (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->shaded)
meta_window_unshade (window, event->time);
else if (window->has_shade_func)
meta_window_shade (window, event->time);
}
static void
handle_close (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->has_close_func)
meta_window_delete (window, event->time);
}
static void
handle_minimize (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->has_minimize_func)
meta_window_minimize (window);
}
static void
handle_begin_move (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->has_move_func)
{
meta_window_begin_grab_op (window,
META_GRAB_OP_KEYBOARD_MOVING,
FALSE,
event->time);
}
}
static void
handle_begin_resize (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->has_resize_func)
{
meta_window_begin_grab_op (window,
META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
FALSE,
event->time);
}
}
static void
handle_toggle_on_all_workspaces (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
if (window->on_all_workspaces_requested)
meta_window_unstick (window);
else
meta_window_stick (window);
}
static void
handle_move_to_workspace_last (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
gint which;
MetaWorkspace *workspace;
if (window->always_sticky)
return;
which = meta_workspace_manager_get_n_workspaces (workspace_manager) - 1;
workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which);
meta_window_change_workspace (window, workspace);
}
static void
handle_move_to_workspace (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaWorkspaceManager *workspace_manager = display->workspace_manager;
gint which = binding->handler->data;
gboolean flip = (which < 0);
MetaWorkspace *workspace;
/* If which is zero or positive, it's a workspace number, and the window
* should move to the workspace with that number.
*
* However, if it's negative, it's a direction with respect to the current
* position; it's expressed as a member of the MetaMotionDirection enum,
* all of whose members are negative. Such a change is called a flip.
*/
if (window->always_sticky)
return;
workspace = NULL;
if (flip)
{
workspace = meta_workspace_get_neighbor (workspace_manager->active_workspace,
which);
}
else
{
workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which);
}
if (workspace)
{
/* Activate second, so the window is never unmapped */
meta_window_change_workspace (window, workspace);
if (flip)
{
meta_topic (META_DEBUG_FOCUS,
"Resetting mouse_mode to FALSE due to "
"handle_move_to_workspace() call with flip set.");
meta_display_clear_mouse_mode (workspace->display);
meta_workspace_activate_with_focus (workspace,
window,
event->time);
}
}
else
{
/* We could offer to create it I suppose */
}
}
static void
handle_move_to_monitor (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
gint which = binding->handler->data;
MetaLogicalMonitor *current, *new;
current = window->monitor;
new = meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager,
current, which);
if (new == NULL)
return;
meta_window_move_to_monitor (window, new->number);
}
static void
handle_raise_or_lower (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
/* Get window at pointer */
MetaWindow *above = NULL;
/* Check if top */
if (meta_stack_get_top (window->display->stack) == window)
{
meta_window_lower (window);
return;
}
/* else check if windows in same layer are intersecting it */
above = meta_stack_get_above (window->display->stack, window, TRUE);
while (above)
{
MetaRectangle tmp, win_rect, above_rect;
if (above->mapped && meta_window_should_be_showing (above))
{
meta_window_get_frame_rect (window, &win_rect);
meta_window_get_frame_rect (above, &above_rect);
/* Check if obscured */
if (meta_rectangle_intersect (&win_rect, &above_rect, &tmp))
{
meta_window_raise (window);
return;
}
}
above = meta_stack_get_above (window->display->stack, above, TRUE);
}
/* window is not obscured */
meta_window_lower (window);
}
static void
handle_raise (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
meta_window_raise (window);
}
static void
handle_lower (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
meta_window_lower (window);
}
static void
handle_set_spew_mark (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
meta_verbose ("-- MARK MARK MARK MARK --");
}
#ifdef HAVE_NATIVE_BACKEND
static void
handle_switch_vt (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
gint vt = binding->handler->data;
GError *error = NULL;
if (!meta_backend_native_activate_vt (META_BACKEND_NATIVE (backend),
vt, &error))
{
g_warning ("Failed to switch VT: %s", error->message);
g_error_free (error);
}
}
#endif /* HAVE_NATIVE_BACKEND */
static void
handle_switch_monitor (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaMonitorSwitchConfigType config_type =
meta_monitor_manager_get_switch_config (monitor_manager);
if (!meta_monitor_manager_can_switch_config (monitor_manager))
return;
config_type = (config_type + 1) % (META_MONITOR_SWITCH_CONFIG_UNKNOWN);
meta_monitor_manager_switch_config (monitor_manager, config_type);
}
static void
handle_rotate_monitor (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
meta_monitor_manager_rotate_monitor (monitor_manager);
}
static void
handle_restore_shortcuts (MetaDisplay *display,
MetaWindow *window,
ClutterKeyEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
ClutterInputDevice *source;
if (!display->focus_window)
return;
source = clutter_event_get_source_device ((ClutterEvent *) event);
meta_topic (META_DEBUG_KEYBINDINGS, "Restoring normal keyboard shortcuts");
meta_window_force_restore_shortcuts (display->focus_window, source);
}
/**
* meta_keybindings_set_custom_handler:
* @name: The name of the keybinding to set
* @handler: (nullable): The new handler function
* @user_data: User data to pass to the callback
* @free_data: Will be called when this handler is overridden.
*
* Allows users to register a custom handler for a
* builtin key binding.
*
* Returns: %TRUE if the binding known as @name was found,
* %FALSE otherwise.
*/
gboolean
meta_keybindings_set_custom_handler (const gchar *name,
MetaKeyHandlerFunc handler,
gpointer user_data,
GDestroyNotify free_data)
{
MetaKeyHandler *key_handler = HANDLER (name);
if (!key_handler)
return FALSE;
if (key_handler->user_data_free_func && key_handler->user_data)
key_handler->user_data_free_func (key_handler->user_data);
key_handler->func = handler;
key_handler->user_data = user_data;
key_handler->user_data_free_func = free_data;
return TRUE;
}
static void
init_builtin_key_bindings (MetaDisplay *display)
{
GSettings *common_keybindings = g_settings_new (SCHEMA_COMMON_KEYBINDINGS);
GSettings *mutter_keybindings = g_settings_new (SCHEMA_MUTTER_KEYBINDINGS);
GSettings *mutter_wayland_keybindings = g_settings_new (SCHEMA_MUTTER_WAYLAND_KEYBINDINGS);
add_builtin_keybinding (display,
"switch-to-workspace-1",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_1,
handle_switch_to_workspace, 0);
add_builtin_keybinding (display,
"switch-to-workspace-2",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_2,
handle_switch_to_workspace, 1);
add_builtin_keybinding (display,
"switch-to-workspace-3",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_3,
handle_switch_to_workspace, 2);
add_builtin_keybinding (display,
"switch-to-workspace-4",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_4,
handle_switch_to_workspace, 3);
add_builtin_keybinding (display,
"switch-to-workspace-5",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_5,
handle_switch_to_workspace, 4);
add_builtin_keybinding (display,
"switch-to-workspace-6",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_6,
handle_switch_to_workspace, 5);
add_builtin_keybinding (display,
"switch-to-workspace-7",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_7,
handle_switch_to_workspace, 6);
add_builtin_keybinding (display,
"switch-to-workspace-8",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_8,
handle_switch_to_workspace, 7);
add_builtin_keybinding (display,
"switch-to-workspace-9",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_9,
handle_switch_to_workspace, 8);
add_builtin_keybinding (display,
"switch-to-workspace-10",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_10,
handle_switch_to_workspace, 9);
add_builtin_keybinding (display,
"switch-to-workspace-11",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_11,
handle_switch_to_workspace, 10);
add_builtin_keybinding (display,
"switch-to-workspace-12",
common_keybindings,
META_KEY_BINDING_NONE |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_WORKSPACE_12,
handle_switch_to_workspace, 11);
add_builtin_keybinding (display,
"switch-to-workspace-left",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_WORKSPACE_LEFT,
handle_switch_to_workspace, META_MOTION_LEFT);
add_builtin_keybinding (display,
"switch-to-workspace-right",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_WORKSPACE_RIGHT,
handle_switch_to_workspace, META_MOTION_RIGHT);
add_builtin_keybinding (display,
"switch-to-workspace-up",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_WORKSPACE_UP,
handle_switch_to_workspace, META_MOTION_UP);
add_builtin_keybinding (display,
"switch-to-workspace-down",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_WORKSPACE_DOWN,
handle_switch_to_workspace, META_MOTION_DOWN);
add_builtin_keybinding (display,
"switch-to-workspace-last",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_WORKSPACE_LAST,
handle_switch_to_last_workspace, 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.
*/
add_builtin_keybinding (display,
"switch-group",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_SWITCH_GROUP,
handle_switch, META_TAB_LIST_GROUP);
add_builtin_keybinding (display,
"switch-group-backward",
common_keybindings,
META_KEY_BINDING_IS_REVERSED,
META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD,
handle_switch, META_TAB_LIST_GROUP);
add_builtin_keybinding (display,
"switch-applications",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_SWITCH_APPLICATIONS,
handle_switch, META_TAB_LIST_NORMAL);
add_builtin_keybinding (display,
"switch-applications-backward",
common_keybindings,
META_KEY_BINDING_IS_REVERSED,
META_KEYBINDING_ACTION_SWITCH_APPLICATIONS_BACKWARD,
handle_switch, META_TAB_LIST_NORMAL);
add_builtin_keybinding (display,
"switch-windows",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_SWITCH_WINDOWS,
handle_switch, META_TAB_LIST_NORMAL);
add_builtin_keybinding (display,
"switch-windows-backward",
common_keybindings,
META_KEY_BINDING_IS_REVERSED,
META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD,
handle_switch, META_TAB_LIST_NORMAL);
add_builtin_keybinding (display,
"switch-panels",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_SWITCH_PANELS,
handle_switch, META_TAB_LIST_DOCKS);
add_builtin_keybinding (display,
"switch-panels-backward",
common_keybindings,
META_KEY_BINDING_IS_REVERSED,
META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD,
handle_switch, META_TAB_LIST_DOCKS);
add_builtin_keybinding (display,
"cycle-group",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_CYCLE_GROUP,
handle_cycle, META_TAB_LIST_GROUP);
add_builtin_keybinding (display,
"cycle-group-backward",
common_keybindings,
META_KEY_BINDING_IS_REVERSED,
META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD,
handle_cycle, META_TAB_LIST_GROUP);
add_builtin_keybinding (display,
"cycle-windows",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_CYCLE_WINDOWS,
handle_cycle, META_TAB_LIST_NORMAL);
add_builtin_keybinding (display,
"cycle-windows-backward",
common_keybindings,
META_KEY_BINDING_IS_REVERSED,
META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD,
handle_cycle, META_TAB_LIST_NORMAL);
add_builtin_keybinding (display,
"cycle-panels",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_CYCLE_PANELS,
handle_cycle, META_TAB_LIST_DOCKS);
add_builtin_keybinding (display,
"cycle-panels-backward",
common_keybindings,
META_KEY_BINDING_IS_REVERSED,
META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD,
handle_cycle, META_TAB_LIST_DOCKS);
/***********************************/
add_builtin_keybinding (display,
"show-desktop",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_SHOW_DESKTOP,
handle_show_desktop, 0);
add_builtin_keybinding (display,
"panel-run-dialog",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_PANEL_RUN_DIALOG,
NULL, META_KEYBINDING_ACTION_PANEL_RUN_DIALOG);
add_builtin_keybinding (display,
"set-spew-mark",
common_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_SET_SPEW_MARK,
handle_set_spew_mark, 0);
add_builtin_keybinding (display,
"switch-monitor",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_SWITCH_MONITOR,
handle_switch_monitor, 0);
add_builtin_keybinding (display,
"rotate-monitor",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_ROTATE_MONITOR,
handle_rotate_monitor, 0);
#ifdef HAVE_NATIVE_BACKEND
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
if (META_IS_BACKEND_NATIVE (backend))
{
add_builtin_keybinding (display,
"switch-to-session-1",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 1);
add_builtin_keybinding (display,
"switch-to-session-2",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 2);
add_builtin_keybinding (display,
"switch-to-session-3",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 3);
add_builtin_keybinding (display,
"switch-to-session-4",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 4);
add_builtin_keybinding (display,
"switch-to-session-5",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 5);
add_builtin_keybinding (display,
"switch-to-session-6",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 6);
add_builtin_keybinding (display,
"switch-to-session-7",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 7);
add_builtin_keybinding (display,
"switch-to-session-8",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 8);
add_builtin_keybinding (display,
"switch-to-session-9",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 9);
add_builtin_keybinding (display,
"switch-to-session-10",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 10);
add_builtin_keybinding (display,
"switch-to-session-11",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 11);
add_builtin_keybinding (display,
"switch-to-session-12",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 12);
}
#endif /* HAVE_NATIVE_BACKEND */
add_builtin_keybinding (display,
"restore-shortcuts",
mutter_wayland_keybindings,
META_KEY_BINDING_NON_MASKABLE,
META_KEYBINDING_ACTION_NONE,
handle_restore_shortcuts, 0);
/************************ PER WINDOW BINDINGS ************************/
/* These take a window as an extra parameter; they have no effect
* if no window is active.
*/
add_builtin_keybinding (display,
"activate-window-menu",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_ACTIVATE_WINDOW_MENU,
handle_activate_window_menu, 0);
add_builtin_keybinding (display,
"toggle-fullscreen",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_TOGGLE_FULLSCREEN,
handle_toggle_fullscreen, 0);
add_builtin_keybinding (display,
"toggle-maximized",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_TOGGLE_MAXIMIZED,
handle_toggle_maximized, 0);
add_builtin_keybinding (display,
"toggle-tiled-left",
mutter_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_TOGGLE_TILED_LEFT,
handle_toggle_tiled, META_TILE_LEFT);
add_builtin_keybinding (display,
"toggle-tiled-right",
mutter_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_TOGGLE_TILED_RIGHT,
handle_toggle_tiled, META_TILE_RIGHT);
add_builtin_keybinding (display,
"toggle-above",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_TOGGLE_ABOVE,
handle_toggle_above, 0);
add_builtin_keybinding (display,
"maximize",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MAXIMIZE,
handle_maximize, 0);
add_builtin_keybinding (display,
"unmaximize",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_UNMAXIMIZE,
handle_unmaximize, 0);
add_builtin_keybinding (display,
"toggle-shaded",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_TOGGLE_SHADED,
handle_toggle_shaded, 0);
add_builtin_keybinding (display,
"minimize",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MINIMIZE,
handle_minimize, 0);
add_builtin_keybinding (display,
"close",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_CLOSE,
handle_close, 0);
add_builtin_keybinding (display,
"begin-move",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_BEGIN_MOVE,
handle_begin_move, 0);
add_builtin_keybinding (display,
"begin-resize",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_BEGIN_RESIZE,
handle_begin_resize, 0);
add_builtin_keybinding (display,
"toggle-on-all-workspaces",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_TOGGLE_ON_ALL_WORKSPACES,
handle_toggle_on_all_workspaces, 0);
add_builtin_keybinding (display,
"move-to-workspace-1",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_1,
handle_move_to_workspace, 0);
add_builtin_keybinding (display,
"move-to-workspace-2",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_2,
handle_move_to_workspace, 1);
add_builtin_keybinding (display,
"move-to-workspace-3",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_3,
handle_move_to_workspace, 2);
add_builtin_keybinding (display,
"move-to-workspace-4",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_4,
handle_move_to_workspace, 3);
add_builtin_keybinding (display,
"move-to-workspace-5",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_5,
handle_move_to_workspace, 4);
add_builtin_keybinding (display,
"move-to-workspace-6",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_6,
handle_move_to_workspace, 5);
add_builtin_keybinding (display,
"move-to-workspace-7",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_7,
handle_move_to_workspace, 6);
add_builtin_keybinding (display,
"move-to-workspace-8",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_8,
handle_move_to_workspace, 7);
add_builtin_keybinding (display,
"move-to-workspace-9",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_9,
handle_move_to_workspace, 8);
add_builtin_keybinding (display,
"move-to-workspace-10",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_10,
handle_move_to_workspace, 9);
add_builtin_keybinding (display,
"move-to-workspace-11",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_11,
handle_move_to_workspace, 10);
add_builtin_keybinding (display,
"move-to-workspace-12",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_12,
handle_move_to_workspace, 11);
add_builtin_keybinding (display,
"move-to-workspace-last",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LAST,
handle_move_to_workspace_last, 0);
add_builtin_keybinding (display,
"move-to-workspace-left",
common_keybindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LEFT,
handle_move_to_workspace, META_MOTION_LEFT);
add_builtin_keybinding (display,
"move-to-workspace-right",
common_keybindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_RIGHT,
handle_move_to_workspace, META_MOTION_RIGHT);
add_builtin_keybinding (display,
"move-to-workspace-up",
common_keybindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_UP,
handle_move_to_workspace, META_MOTION_UP);
add_builtin_keybinding (display,
"move-to-workspace-down",
common_keybindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_DOWN,
handle_move_to_workspace, META_MOTION_DOWN);
add_builtin_keybinding (display,
"move-to-monitor-left",
common_keybindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_MOVE_TO_MONITOR_LEFT,
handle_move_to_monitor, META_DISPLAY_LEFT);
add_builtin_keybinding (display,
"move-to-monitor-right",
common_keybindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_MOVE_TO_MONITOR_RIGHT,
handle_move_to_monitor, META_DISPLAY_RIGHT);
add_builtin_keybinding (display,
"move-to-monitor-down",
common_keybindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_MOVE_TO_MONITOR_DOWN,
handle_move_to_monitor, META_DISPLAY_DOWN);
add_builtin_keybinding (display,
"move-to-monitor-up",
common_keybindings,
META_KEY_BINDING_PER_WINDOW,
META_KEYBINDING_ACTION_MOVE_TO_MONITOR_UP,
handle_move_to_monitor, META_DISPLAY_UP);
add_builtin_keybinding (display,
"raise-or-lower",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_RAISE_OR_LOWER,
handle_raise_or_lower, 0);
add_builtin_keybinding (display,
"raise",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_RAISE,
handle_raise, 0);
add_builtin_keybinding (display,
"lower",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_LOWER,
handle_lower, 0);
add_builtin_keybinding (display,
"maximize-vertically",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MAXIMIZE_VERTICALLY,
handle_maximize_vertically, 0);
add_builtin_keybinding (display,
"maximize-horizontally",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MAXIMIZE_HORIZONTALLY,
handle_maximize_horizontally, 0);
add_builtin_keybinding (display,
"always-on-top",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_ALWAYS_ON_TOP,
handle_always_on_top, 0);
add_builtin_keybinding (display,
"move-to-corner-nw",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_CORNER_NW,
handle_move_to_corner_nw, 0);
add_builtin_keybinding (display,
"move-to-corner-ne",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_CORNER_NE,
handle_move_to_corner_ne, 0);
add_builtin_keybinding (display,
"move-to-corner-sw",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_CORNER_SW,
handle_move_to_corner_sw, 0);
add_builtin_keybinding (display,
"move-to-corner-se",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_CORNER_SE,
handle_move_to_corner_se, 0);
add_builtin_keybinding (display,
"move-to-side-n",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_SIDE_N,
handle_move_to_side_n, 0);
add_builtin_keybinding (display,
"move-to-side-s",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_SIDE_S,
handle_move_to_side_s, 0);
add_builtin_keybinding (display,
"move-to-side-e",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_SIDE_E,
handle_move_to_side_e, 0);
add_builtin_keybinding (display,
"move-to-side-w",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_SIDE_W,
handle_move_to_side_w, 0);
add_builtin_keybinding (display,
"move-to-center",
common_keybindings,
META_KEY_BINDING_PER_WINDOW |
META_KEY_BINDING_IGNORE_AUTOREPEAT,
META_KEYBINDING_ACTION_MOVE_TO_CENTER,
handle_move_to_center, 0);
g_object_unref (common_keybindings);
g_object_unref (mutter_keybindings);
g_object_unref (mutter_wayland_keybindings);
}
void
meta_display_init_keys (MetaDisplay *display)
{
MetaKeyBindingManager *keys = &display->key_binding_manager;
MetaContext *context = meta_display_get_context (display);
MetaBackend *backend = meta_context_get_backend (context);
MetaKeyHandler *handler;
keys->backend = backend;
/* Keybindings */
keys->ignored_modifier_mask = 0;
keys->hyper_mask = 0;
keys->super_mask = 0;
keys->meta_mask = 0;
keys->key_bindings = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_key_binding_free);
keys->key_bindings_index = g_hash_table_new (NULL, NULL);
reload_modmap (keys);
key_handlers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) key_handler_free);
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup ("overlay-key");
handler->flags = META_KEY_BINDING_BUILTIN | META_KEY_BINDING_NO_AUTO_GRAB;
g_hash_table_insert (key_handlers, g_strdup (handler->name), handler);
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup ("locate-pointer-key");
handler->flags = META_KEY_BINDING_BUILTIN | META_KEY_BINDING_NO_AUTO_GRAB;
g_hash_table_insert (key_handlers, g_strdup (handler->name), handler);
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup ("iso-next-group");
handler->flags = META_KEY_BINDING_BUILTIN;
g_hash_table_insert (key_handlers, g_strdup (handler->name), handler);
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup ("external-grab");
handler->func = handle_external_grab;
handler->default_func = handle_external_grab;
g_hash_table_insert (key_handlers, g_strdup (handler->name), handler);
external_grabs = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify)meta_key_grab_free);
init_builtin_key_bindings (display);
rebuild_key_binding_table (keys);
rebuild_special_bindings (keys);
reload_combos (keys);
update_window_grab_modifiers (display);
/* Keys are actually grabbed in meta_screen_grab_keys() */
meta_prefs_add_listener (prefs_changed_callback, display);
g_signal_connect_swapped (backend, "keymap-changed",
G_CALLBACK (reload_keybindings), display);
g_signal_connect_swapped (backend, "keymap-layout-group-changed",
G_CALLBACK (reload_keybindings), display);
}