mirror of
https://github.com/brl/mutter.git
synced 2024-11-30 20:01:00 -05:00
dd2beae6a8
As with other parts, make objects have the ability to walk up the ownership chain to the context, to get things like the Wayland compositor or backend instances. Contains these squashed commits: display: Don't get backend from singleton window: Don't get backend from singleton keybindings: Don't get backend from singleton workspace: Don't get backend from singleton display: Don't get Wayland compositor from singleton selection: Add display getter context/main: Get backend directly from the context clipboard-manager: Don't get display from singleton stack-tracker: Don't use singleton MetaLater API startup-notification: Hook up sequences and activations to display This allows using context aware API directly. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2718>
4551 lines
148 KiB
C
4551 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;
|
|
}
|
|
|
|
MetaVirtualModifier
|
|
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,
|
|
MetaVirtualModifier modifiers,
|
|
unsigned int *mask)
|
|
{
|
|
*mask = 0;
|
|
|
|
if (modifiers & META_VIRTUAL_SHIFT_MASK)
|
|
*mask |= ShiftMask;
|
|
if (modifiers & META_VIRTUAL_CONTROL_MASK)
|
|
*mask |= ControlMask;
|
|
if (modifiers & META_VIRTUAL_ALT_MASK)
|
|
*mask |= Mod1Mask;
|
|
if (modifiers & META_VIRTUAL_META_MASK)
|
|
*mask |= keys->meta_mask;
|
|
if (modifiers & META_VIRTUAL_HYPER_MASK)
|
|
*mask |= keys->hyper_mask;
|
|
if (modifiers & META_VIRTUAL_SUPER_MASK)
|
|
*mask |= keys->super_mask;
|
|
if (modifiers & META_VIRTUAL_MOD2_MASK)
|
|
*mask |= Mod2Mask;
|
|
if (modifiers & META_VIRTUAL_MOD3_MASK)
|
|
*mask |= Mod3Mask;
|
|
if (modifiers & META_VIRTUAL_MOD4_MASK)
|
|
*mask |= Mod4Mask;
|
|
if (modifiers & META_VIRTUAL_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 "<Control>a" or "<Shift><Alt>F1". The parser
|
|
* is fairly liberal and allows lower or upper case, and also abbreviations
|
|
* such as "<Ctl>" and "<Ctrl>". If the key is set to the empty list or a
|
|
* list with a single element of either "" or "disabled", the keybinding is
|
|
* disabled.
|
|
*
|
|
* 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;
|
|
MetaVirtualModifier 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 (display->grab_op == META_GRAB_OP_NONE)
|
|
return TRUE;
|
|
|
|
/* 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
|
|
{
|
|
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);
|
|
}
|