summaryrefslogtreecommitdiff
path: root/src/x11/keybindings-x11.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/x11/keybindings-x11.c')
-rw-r--r--src/x11/keybindings-x11.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/src/x11/keybindings-x11.c b/src/x11/keybindings-x11.c
new file mode 100644
index 000000000..240d262e5
--- /dev/null
+++ b/src/x11/keybindings-x11.c
@@ -0,0 +1,440 @@
+#include "backends/x11/meta-backend-x11.h"
+#include "backends/x11/meta-input-device-x11.h"
+#include "core/frame.h"
+#include "x11/keybindings-x11-private.h"
+#include "x11/meta-x11-display-private.h"
+#include "x11/window-x11.h"
+
+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);
+}
+
+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);
+}
+
+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;
+}
+
+/* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */
+void
+meta_change_keygrab (MetaKeyBindingManager *keys,
+ Window xwindow,
+ gboolean grab,
+ MetaResolvedKeyCombo *resolved_combo)
+{
+ 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);
+
+ MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ());
+ Display *xdisplay = meta_backend_x11_get_xdisplay (backend);
+ GArray *mods;
+ int i;
+
+ /* 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);
+}
+
+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);
+}
+
+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;
+ }
+}
+
+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;
+}
+
+void
+meta_x11_display_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);
+}
+
+void
+meta_x11_display_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);
+}