summaryrefslogtreecommitdiff
path: root/src/core/keybindings.c
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-12-01 23:52:01 -0300
committerPerberos <[email protected]>2011-12-01 23:52:01 -0300
commit28a029a4990d2a84f9d6a0b890eba812ea503998 (patch)
tree7a69477d0dd6bf351801fa9698d95224e4fe47b6 /src/core/keybindings.c
downloadmarco-28a029a4990d2a84f9d6a0b890eba812ea503998.tar.bz2
marco-28a029a4990d2a84f9d6a0b890eba812ea503998.tar.xz
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'src/core/keybindings.c')
-rw-r--r--src/core/keybindings.c3352
1 files changed, 3352 insertions, 0 deletions
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
new file mode 100644
index 00000000..b9371c85
--- /dev/null
+++ b/src/core/keybindings.c
@@ -0,0 +1,3352 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* Marco Keybindings */
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2002 Red Hat Inc.
+ * Copyright (C) 2003 Rob Adams
+ * Copyright (C) 2004-2006 Elijah Newren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+#define _SVID_SOURCE /* for putenv() */
+
+#include <config.h>
+#include "keybindings.h"
+#include "workspace.h"
+#include "errors.h"
+#include "edge-resistance.h"
+#include "ui.h"
+#include "frame-private.h"
+#include "place.h"
+#include "prefs.h"
+#include "effects.h"
+#include "util.h"
+
+#include <X11/keysym.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+#endif
+
+static gboolean all_bindings_disabled = FALSE;
+
+typedef void (* MetaKeyHandlerFunc) (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding);
+
+/* Prototypes for handlers */
+#define keybind(name, handler, param, flags, stroke, description) \
+static void \
+handler (MetaDisplay *display,\
+ MetaScreen *screen,\
+ MetaWindow *window,\
+ XEvent *event,\
+ MetaKeyBinding *binding);
+#include "all-keybindings.h"
+#undef keybind
+
+/* 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 void handle_workspace_switch (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding);
+
+static gboolean process_mouse_move_resize_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ KeySym keysym);
+
+static gboolean process_keyboard_move_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ KeySym keysym);
+
+static gboolean process_keyboard_resize_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ KeySym keysym);
+
+static gboolean process_tab_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ XEvent *event,
+ KeySym keysym);
+
+static gboolean process_workspace_switch_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ XEvent *event,
+ KeySym keysym);
+
+static void regrab_key_bindings (MetaDisplay *display);
+
+typedef struct
+{
+ const char *name;
+ MetaKeyHandlerFunc func;
+ gint data, flags;
+} MetaKeyHandler;
+
+struct _MetaKeyBinding
+{
+ const char *name;
+ KeySym keysym;
+ KeyCode keycode;
+ unsigned int mask;
+ MetaVirtualModifier modifiers;
+ const MetaKeyHandler *handler;
+};
+
+#define keybind(name, handler, param, flags, stroke, description) \
+ { #name, handler, param, flags },
+static const MetaKeyHandler key_handlers[] = {
+#include "all-keybindings.h"
+ { NULL, NULL, 0, 0 }
+};
+#undef keybind
+
+static void
+reload_keymap (MetaDisplay *display)
+{
+ if (display->keymap)
+ meta_XFree (display->keymap);
+
+ display->keymap = XGetKeyboardMapping (display->xdisplay,
+ display->min_keycode,
+ display->max_keycode -
+ display->min_keycode + 1,
+ &display->keysyms_per_keycode);
+}
+
+static void
+reload_modmap (MetaDisplay *display)
+{
+ XModifierKeymap *modmap;
+ int map_size;
+ int i;
+
+ if (display->modmap)
+ XFreeModifiermap (display->modmap);
+
+ modmap = XGetModifierMapping (display->xdisplay);
+ display->modmap = modmap;
+
+ display->ignored_modifier_mask = 0;
+
+ /* Multiple bits may get set in each of these */
+ display->num_lock_mask = 0;
+ display->scroll_lock_mask = 0;
+ display->meta_mask = 0;
+ display->hyper_mask = 0;
+ display->super_mask = 0;
+
+ /* there are 8 modifiers, and the first 3 are shift, shift lock,
+ * and control
+ */
+ map_size = 8 * modmap->max_keypermod;
+ i = 3 * modmap->max_keypermod;
+ while (i < map_size)
+ {
+ /* get the key code at this point in the map,
+ * see if its keysym is one we're interested in
+ */
+ int keycode = modmap->modifiermap[i];
+
+ if (keycode >= display->min_keycode &&
+ keycode <= display->max_keycode)
+ {
+ int j = 0;
+ KeySym *syms = display->keymap +
+ (keycode - display->min_keycode) * display->keysyms_per_keycode;
+
+ while (j < display->keysyms_per_keycode)
+ {
+ if (syms[j] != 0)
+ {
+ const char *str;
+
+ str = XKeysymToString (syms[j]);
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Keysym %s bound to modifier 0x%x\n",
+ str ? str : "none",
+ (1 << ( i / modmap->max_keypermod)));
+ }
+
+ if (syms[j] == XK_Num_Lock)
+ {
+ /* Mod1Mask is 1 << 3 for example, i.e. the
+ * fourth modifier, i / keyspermod is the modifier
+ * index
+ */
+
+ display->num_lock_mask |= (1 << ( i / modmap->max_keypermod));
+ }
+ else if (syms[j] == XK_Scroll_Lock)
+ {
+ display->scroll_lock_mask |= (1 << ( i / modmap->max_keypermod));
+ }
+ else if (syms[j] == XK_Super_L ||
+ syms[j] == XK_Super_R)
+ {
+ display->super_mask |= (1 << ( i / modmap->max_keypermod));
+ }
+ else if (syms[j] == XK_Hyper_L ||
+ syms[j] == XK_Hyper_R)
+ {
+ display->hyper_mask |= (1 << ( i / modmap->max_keypermod));
+ }
+ else if (syms[j] == XK_Meta_L ||
+ syms[j] == XK_Meta_R)
+ {
+ display->meta_mask |= (1 << ( i / modmap->max_keypermod));
+ }
+
+ ++j;
+ }
+ }
+
+ ++i;
+ }
+
+ display->ignored_modifier_mask = (display->num_lock_mask |
+ display->scroll_lock_mask |
+ LockMask);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ignoring modmask 0x%x num lock 0x%x scroll lock 0x%x hyper 0x%x super 0x%x meta 0x%x\n",
+ display->ignored_modifier_mask,
+ display->num_lock_mask,
+ display->scroll_lock_mask,
+ display->hyper_mask,
+ display->super_mask,
+ display->meta_mask);
+}
+
+static void
+reload_keycodes (MetaDisplay *display)
+{
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Reloading keycodes for binding tables\n");
+
+ if (display->key_bindings)
+ {
+ int i;
+
+ i = 0;
+ while (i < display->n_key_bindings)
+ {
+ if (display->key_bindings[i].keycode == 0)
+ display->key_bindings[i].keycode = XKeysymToKeycode (
+ display->xdisplay, display->key_bindings[i].keysym);
+
+ ++i;
+ }
+ }
+}
+
+static void
+reload_modifiers (MetaDisplay *display)
+{
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Reloading keycodes for binding tables\n");
+
+ if (display->key_bindings)
+ {
+ int i;
+
+ i = 0;
+ while (i < display->n_key_bindings)
+ {
+ meta_display_devirtualize_modifiers (display,
+ display->key_bindings[i].modifiers,
+ &display->key_bindings[i].mask);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ " Devirtualized mods 0x%x -> 0x%x (%s)\n",
+ display->key_bindings[i].modifiers,
+ display->key_bindings[i].mask,
+ display->key_bindings[i].name);
+
+ ++i;
+ }
+ }
+}
+
+
+static int
+count_bindings (const MetaKeyPref *prefs,
+ int n_prefs)
+{
+ int i;
+ int count;
+
+ count = 0;
+ i = 0;
+ while (i < n_prefs)
+ {
+ GSList *tmp = prefs[i].bindings;
+
+ while (tmp)
+ {
+ MetaKeyCombo *combo = tmp->data;
+
+ if (combo && (combo->keysym != None || combo->keycode != 0))
+ {
+ count += 1;
+
+ if (prefs[i].add_shift &&
+ (combo->modifiers & META_VIRTUAL_SHIFT_MASK) == 0)
+ count += 1;
+ }
+
+ tmp = tmp->next;
+ }
+
+ ++i;
+ }
+
+ return count;
+}
+
+/* FIXME: replace this with a temporary hash */
+static const MetaKeyHandler*
+find_handler (const MetaKeyHandler *handlers,
+ const char *name)
+{
+ const MetaKeyHandler *iter;
+
+ iter = handlers;
+ while (iter->name)
+ {
+ if (strcmp (iter->name, name) == 0)
+ return iter;
+
+ ++iter;
+ }
+
+ return NULL;
+}
+
+static void
+rebuild_binding_table (MetaDisplay *display,
+ MetaKeyBinding **bindings_p,
+ int *n_bindings_p,
+ const MetaKeyPref *prefs,
+ int n_prefs)
+{
+ int n_bindings;
+ int src, dest;
+
+ n_bindings = count_bindings (prefs, n_prefs);
+ g_free (*bindings_p);
+ *bindings_p = g_new0 (MetaKeyBinding, n_bindings);
+
+ src = 0;
+ dest = 0;
+ while (src < n_prefs)
+ {
+ GSList *tmp = prefs[src].bindings;
+
+ while (tmp)
+ {
+ MetaKeyCombo *combo = tmp->data;
+
+ if (combo && (combo->keysym != None || combo->keycode != 0))
+ {
+ const MetaKeyHandler *handler = find_handler (key_handlers, prefs[src].name);
+
+ (*bindings_p)[dest].name = prefs[src].name;
+ (*bindings_p)[dest].handler = handler;
+ (*bindings_p)[dest].keysym = combo->keysym;
+ (*bindings_p)[dest].keycode = combo->keycode;
+ (*bindings_p)[dest].modifiers = combo->modifiers;
+ (*bindings_p)[dest].mask = 0;
+
+ ++dest;
+
+ if (prefs[src].add_shift &&
+ (combo->modifiers & META_VIRTUAL_SHIFT_MASK) == 0)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Binding %s also needs Shift grabbed\n",
+ prefs[src].name);
+
+ (*bindings_p)[dest].name = prefs[src].name;
+ (*bindings_p)[dest].handler = handler;
+ (*bindings_p)[dest].keysym = combo->keysym;
+ (*bindings_p)[dest].keycode = combo->keycode;
+ (*bindings_p)[dest].modifiers = combo->modifiers |
+ META_VIRTUAL_SHIFT_MASK;
+ (*bindings_p)[dest].mask = 0;
+
+ ++dest;
+ }
+ }
+
+ tmp = tmp->next;
+ }
+
+ ++src;
+ }
+
+ g_assert (dest == n_bindings);
+
+ *n_bindings_p = dest;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ " %d bindings in table\n",
+ *n_bindings_p);
+}
+
+static void
+rebuild_key_binding_table (MetaDisplay *display)
+{
+ const MetaKeyPref *prefs;
+ int n_prefs;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Rebuilding key binding table from preferences\n");
+
+ meta_prefs_get_key_bindings (&prefs, &n_prefs);
+ rebuild_binding_table (display,
+ &display->key_bindings,
+ &display->n_key_bindings,
+ prefs, n_prefs);
+}
+
+static void
+regrab_key_bindings (MetaDisplay *display)
+{
+ GSList *tmp;
+ GSList *windows;
+
+ meta_error_trap_push (display); /* for efficiency push outer trap */
+
+ tmp = display->screens;
+ while (tmp != NULL)
+ {
+ MetaScreen *screen = tmp->data;
+
+ meta_screen_ungrab_keys (screen);
+ meta_screen_grab_keys (screen);
+
+ tmp = tmp->next;
+ }
+
+ windows = meta_display_list_windows (display);
+ tmp = windows;
+ while (tmp != NULL)
+ {
+ MetaWindow *w = tmp->data;
+
+ meta_window_ungrab_keys (w);
+ meta_window_grab_keys (w);
+
+ tmp = tmp->next;
+ }
+ meta_error_trap_pop (display, FALSE);
+
+ g_slist_free (windows);
+}
+
+static MetaKeyBindingAction
+display_get_keybinding_action (MetaDisplay *display,
+ unsigned int keysym,
+ unsigned int keycode,
+ unsigned long mask)
+{
+ int i;
+
+ i = display->n_key_bindings - 1;
+ while (i >= 0)
+ {
+ if (display->key_bindings[i].keysym == keysym &&
+ display->key_bindings[i].keycode == keycode &&
+ display->key_bindings[i].mask == mask)
+ {
+ return meta_prefs_get_keybinding_action (display->key_bindings[i].name);
+ }
+
+ --i;
+ }
+
+ return META_KEYBINDING_ACTION_NONE;
+}
+
+void
+meta_display_process_mapping_event (MetaDisplay *display,
+ XEvent *event)
+{
+ if (event->xmapping.request == MappingModifier)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Received MappingModifier event, will reload modmap and redo keybindings\n");
+
+ reload_modmap (display);
+
+ reload_modifiers (display);
+
+ regrab_key_bindings (display);
+ }
+ else if (event->xmapping.request == MappingKeyboard)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Received MappingKeyboard event, will reload keycodes and redo keybindings\n");
+
+ reload_keymap (display);
+ reload_modmap (display);
+
+ reload_keycodes (display);
+
+ regrab_key_bindings (display);
+ }
+}
+
+static void
+bindings_changed_callback (MetaPreference pref,
+ void *data)
+{
+ MetaDisplay *display;
+
+ display = data;
+
+ switch (pref)
+ {
+ case META_PREF_KEYBINDINGS:
+ rebuild_key_binding_table (display);
+ reload_keycodes (display);
+ reload_modifiers (display);
+ regrab_key_bindings (display);
+ break;
+ default:
+ break;
+ }
+}
+
+
+void
+meta_display_init_keys (MetaDisplay *display)
+{
+ /* Keybindings */
+ display->keymap = NULL;
+ display->keysyms_per_keycode = 0;
+ display->modmap = NULL;
+ display->min_keycode = 0;
+ display->max_keycode = 0;
+ display->ignored_modifier_mask = 0;
+ display->num_lock_mask = 0;
+ display->scroll_lock_mask = 0;
+ display->hyper_mask = 0;
+ display->super_mask = 0;
+ display->meta_mask = 0;
+ display->key_bindings = NULL;
+ display->n_key_bindings = 0;
+
+ XDisplayKeycodes (display->xdisplay,
+ &display->min_keycode,
+ &display->max_keycode);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Display has keycode range %d to %d\n",
+ display->min_keycode,
+ display->max_keycode);
+
+ reload_keymap (display);
+ reload_modmap (display);
+
+ rebuild_key_binding_table (display);
+
+ reload_keycodes (display);
+ reload_modifiers (display);
+
+ /* Keys are actually grabbed in meta_screen_grab_keys() */
+
+ meta_prefs_add_listener (bindings_changed_callback, display);
+}
+
+void
+meta_display_shutdown_keys (MetaDisplay *display)
+{
+ /* Note that display->xdisplay is invalid in this function */
+
+ meta_prefs_remove_listener (bindings_changed_callback, display);
+
+ if (display->keymap)
+ meta_XFree (display->keymap);
+
+ if (display->modmap)
+ XFreeModifiermap (display->modmap);
+ g_free (display->key_bindings);
+}
+
+static const char*
+keysym_name (int keysym)
+{
+ const char *name;
+
+ name = XKeysymToString (keysym);
+ if (name == NULL)
+ name = "(unknown)";
+
+ return name;
+}
+
+/* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */
+static void
+meta_change_keygrab (MetaDisplay *display,
+ Window xwindow,
+ gboolean grab,
+ int keysym,
+ unsigned int keycode,
+ int modmask)
+{
+ unsigned int ignored_mask;
+
+ /* Grab keycode/modmask, together with
+ * all combinations of ignored modifiers.
+ * X provides no better way to do this.
+ */
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "%s keybinding %s keycode %d mask 0x%x on 0x%lx\n",
+ grab ? "Grabbing" : "Ungrabbing",
+ keysym_name (keysym), keycode,
+ modmask, xwindow);
+
+ /* efficiency, avoid so many XSync() */
+ meta_error_trap_push (display);
+
+ ignored_mask = 0;
+ while (ignored_mask <= display->ignored_modifier_mask)
+ {
+ if (ignored_mask & ~(display->ignored_modifier_mask))
+ {
+ /* Not a combination of ignored modifiers
+ * (it contains some non-ignored modifiers)
+ */
+ ++ignored_mask;
+ continue;
+ }
+
+ if (meta_is_debugging ())
+ meta_error_trap_push_with_return (display);
+ if (grab)
+ XGrabKey (display->xdisplay, keycode,
+ modmask | ignored_mask,
+ xwindow,
+ True,
+ GrabModeAsync, GrabModeSync);
+ else
+ XUngrabKey (display->xdisplay, keycode,
+ modmask | ignored_mask,
+ xwindow);
+
+ if (meta_is_debugging ())
+ {
+ int result;
+
+ result = meta_error_trap_pop_with_return (display, FALSE);
+
+ if (grab && result != Success)
+ {
+ if (result == BadAccess)
+ meta_warning (_("Some other program is already using the key %s with modifiers %x as a binding\n"), keysym_name (keysym), modmask | ignored_mask);
+ else
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Failed to grab key %s with modifiers %x\n",
+ keysym_name (keysym), modmask | ignored_mask);
+ }
+ }
+
+ ++ignored_mask;
+ }
+
+ meta_error_trap_pop (display, FALSE);
+}
+
+static void
+meta_grab_key (MetaDisplay *display,
+ Window xwindow,
+ int keysym,
+ unsigned int keycode,
+ int modmask)
+{
+ meta_change_keygrab (display, xwindow, TRUE, keysym, keycode, modmask);
+}
+
+static void
+grab_keys (MetaKeyBinding *bindings,
+ int n_bindings,
+ MetaDisplay *display,
+ Window xwindow,
+ gboolean binding_per_window)
+{
+ int i;
+
+ g_assert (n_bindings == 0 || bindings != NULL);
+
+ meta_error_trap_push (display);
+
+ i = 0;
+ while (i < n_bindings)
+ {
+ if (!!binding_per_window ==
+ !!(bindings[i].handler->flags & BINDING_PER_WINDOW) &&
+ bindings[i].keycode != 0)
+ {
+ meta_grab_key (display, xwindow,
+ bindings[i].keysym,
+ bindings[i].keycode,
+ bindings[i].mask);
+ }
+
+ ++i;
+ }
+
+ meta_error_trap_pop (display, FALSE);
+}
+
+static void
+ungrab_all_keys (MetaDisplay *display,
+ Window xwindow)
+{
+ if (meta_is_debugging ())
+ meta_error_trap_push_with_return (display);
+ else
+ meta_error_trap_push (display);
+
+ XUngrabKey (display->xdisplay, AnyKey, AnyModifier,
+ xwindow);
+
+ if (meta_is_debugging ())
+ {
+ int result;
+
+ result = meta_error_trap_pop_with_return (display, FALSE);
+
+ if (result != Success)
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ungrabbing all keys on 0x%lx failed\n", xwindow);
+ }
+ else
+ meta_error_trap_pop (display, FALSE);
+}
+
+void
+meta_screen_grab_keys (MetaScreen *screen)
+{
+ if (screen->all_keys_grabbed)
+ return;
+
+ if (screen->keys_grabbed)
+ return;
+
+ grab_keys (screen->display->key_bindings,
+ screen->display->n_key_bindings,
+ screen->display, screen->xroot,
+ FALSE);
+
+ screen->keys_grabbed = TRUE;
+}
+
+void
+meta_screen_ungrab_keys (MetaScreen *screen)
+{
+ if (screen->keys_grabbed)
+ {
+ ungrab_all_keys (screen->display, screen->xroot);
+ screen->keys_grabbed = FALSE;
+ }
+}
+
+void
+meta_window_grab_keys (MetaWindow *window)
+{
+ if (window->all_keys_grabbed)
+ return;
+
+ if (window->type == META_WINDOW_DOCK)
+ {
+ if (window->keys_grabbed)
+ ungrab_all_keys (window->display, window->xwindow);
+ window->keys_grabbed = FALSE;
+ return;
+ }
+
+ if (window->keys_grabbed)
+ {
+ if (window->frame && !window->grab_on_frame)
+ ungrab_all_keys (window->display, window->xwindow);
+ else if (window->frame == NULL &&
+ window->grab_on_frame)
+ ; /* continue to regrab on client window */
+ else
+ return; /* already all good */
+ }
+
+ grab_keys (window->display->key_bindings,
+ window->display->n_key_bindings,
+ window->display,
+ window->frame ? window->frame->xwindow : window->xwindow,
+ TRUE);
+
+ window->keys_grabbed = TRUE;
+ window->grab_on_frame = window->frame != NULL;
+}
+
+void
+meta_window_ungrab_keys (MetaWindow *window)
+{
+ if (window->keys_grabbed)
+ {
+ if (window->grab_on_frame &&
+ window->frame != NULL)
+ ungrab_all_keys (window->display,
+ window->frame->xwindow);
+ else if (!window->grab_on_frame)
+ ungrab_all_keys (window->display,
+ window->xwindow);
+
+ window->keys_grabbed = FALSE;
+ }
+}
+
+#ifdef WITH_VERBOSE_MODE
+static const char*
+grab_status_to_string (int status)
+{
+ switch (status)
+ {
+ case AlreadyGrabbed:
+ return "AlreadyGrabbed";
+ case GrabSuccess:
+ return "GrabSuccess";
+ case GrabNotViewable:
+ return "GrabNotViewable";
+ case GrabFrozen:
+ return "GrabFrozen";
+ case GrabInvalidTime:
+ return "GrabInvalidTime";
+ default:
+ return "(unknown)";
+ }
+}
+#endif /* WITH_VERBOSE_MODE */
+
+static gboolean
+grab_keyboard (MetaDisplay *display,
+ Window xwindow,
+ guint32 timestamp)
+{
+ int result;
+ int grab_status;
+
+ /* Grab the keyboard, so we get key releases and all key
+ * presses
+ */
+ meta_error_trap_push_with_return (display);
+
+ grab_status = XGrabKeyboard (display->xdisplay,
+ xwindow, True,
+ GrabModeAsync, GrabModeAsync,
+ timestamp);
+
+ if (grab_status != GrabSuccess)
+ {
+ meta_error_trap_pop_with_return (display, TRUE);
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "XGrabKeyboard() returned failure status %s time %u\n",
+ grab_status_to_string (grab_status),
+ timestamp);
+ return FALSE;
+ }
+ else
+ {
+ result = meta_error_trap_pop_with_return (display, TRUE);
+ if (result != Success)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "XGrabKeyboard() resulted in an error\n");
+ return FALSE;
+ }
+ }
+
+ meta_topic (META_DEBUG_KEYBINDINGS, "Grabbed all keys\n");
+
+ return TRUE;
+}
+
+static void
+ungrab_keyboard (MetaDisplay *display, guint32 timestamp)
+{
+ meta_error_trap_push (display);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ungrabbing keyboard with timestamp %u\n",
+ timestamp);
+ XUngrabKeyboard (display->xdisplay, timestamp);
+ meta_error_trap_pop (display, FALSE);
+}
+
+gboolean
+meta_screen_grab_all_keys (MetaScreen *screen, guint32 timestamp)
+{
+ gboolean retval;
+
+ if (screen->all_keys_grabbed)
+ return FALSE;
+
+ if (screen->keys_grabbed)
+ meta_screen_ungrab_keys (screen);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Grabbing all keys on RootWindow\n");
+ retval = grab_keyboard (screen->display, screen->xroot, timestamp);
+ if (retval)
+ screen->all_keys_grabbed = TRUE;
+ else
+ meta_screen_grab_keys (screen);
+
+ return retval;
+}
+
+void
+meta_screen_ungrab_all_keys (MetaScreen *screen, guint32 timestamp)
+{
+ if (screen->all_keys_grabbed)
+ {
+ ungrab_keyboard (screen->display, timestamp);
+
+ screen->all_keys_grabbed = FALSE;
+ screen->keys_grabbed = FALSE;
+
+ /* Re-establish our standard bindings */
+ meta_screen_grab_keys (screen);
+ }
+}
+
+gboolean
+meta_window_grab_all_keys (MetaWindow *window,
+ guint32 timestamp)
+{
+ Window grabwindow;
+ gboolean retval;
+
+ 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\n",
+ window->desc);
+ meta_window_focus (window, timestamp);
+
+ grabwindow = window->frame ? window->frame->xwindow : window->xwindow;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Grabbing all keys on window %s\n", window->desc);
+ retval = grab_keyboard (window->display, grabwindow, timestamp);
+ 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)
+ {
+ ungrab_keyboard (window->display, 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);
+ }
+}
+
+static gboolean
+is_modifier (MetaDisplay *display,
+ unsigned int keycode)
+{
+ int i;
+ int map_size;
+ gboolean retval = FALSE;
+
+ g_assert (display->modmap);
+
+ map_size = 8 * display->modmap->max_keypermod;
+ i = 0;
+ while (i < map_size)
+ {
+ if (keycode == display->modmap->modifiermap[i])
+ {
+ retval = TRUE;
+ break;
+ }
+ ++i;
+ }
+
+ return retval;
+}
+
+/* Indexes:
+ * shift = 0
+ * lock = 1
+ * control = 2
+ * mod1 = 3
+ * mod2 = 4
+ * mod3 = 5
+ * mod4 = 6
+ * mod5 = 7
+ */
+
+static gboolean
+is_specific_modifier (MetaDisplay *display,
+ unsigned int keycode,
+ unsigned int mask)
+{
+ int i;
+ int end;
+ gboolean retval = FALSE;
+ int mod_index;
+
+ g_assert (display->modmap);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Checking whether code 0x%x is bound to modifier 0x%x\n",
+ keycode, mask);
+
+ mod_index = 0;
+ mask = mask >> 1;
+ while (mask != 0)
+ {
+ mod_index += 1;
+ mask = mask >> 1;
+ }
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Modifier has index %d\n", mod_index);
+
+ end = (mod_index + 1) * display->modmap->max_keypermod;
+ i = mod_index * display->modmap->max_keypermod;
+ while (i < end)
+ {
+ if (keycode == display->modmap->modifiermap[i])
+ {
+ retval = TRUE;
+ break;
+ }
+ ++i;
+ }
+
+ return retval;
+}
+
+static unsigned int
+get_primary_modifier (MetaDisplay *display,
+ unsigned int entire_binding_mask)
+{
+ /* The idea here is to see if the "main" modifier
+ * for Alt+Tab has been pressed/released. So if the binding
+ * is Alt+Shift+Tab then releasing Alt is the thing that
+ * ends the operation. It's pretty random how we order
+ * these.
+ */
+ unsigned int masks[] = { Mod5Mask, Mod4Mask, Mod3Mask,
+ Mod2Mask, Mod1Mask, ControlMask,
+ ShiftMask, LockMask };
+
+ int i;
+
+ i = 0;
+ while (i < (int) G_N_ELEMENTS (masks))
+ {
+ if (entire_binding_mask & masks[i])
+ return masks[i];
+ ++i;
+ }
+
+ return 0;
+}
+
+static gboolean
+keycode_is_primary_modifier (MetaDisplay *display,
+ unsigned int keycode,
+ unsigned int entire_binding_mask)
+{
+ unsigned int primary_modifier;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Checking whether code 0x%x is the primary modifier of mask 0x%x\n",
+ keycode, entire_binding_mask);
+
+ primary_modifier = get_primary_modifier (display, entire_binding_mask);
+ if (primary_modifier != 0)
+ return is_specific_modifier (display, keycode, primary_modifier);
+ else
+ return FALSE;
+}
+
+static gboolean
+primary_modifier_still_pressed (MetaDisplay *display,
+ unsigned int entire_binding_mask)
+{
+ unsigned int primary_modifier;
+ int x, y, root_x, root_y;
+ Window root, child;
+ guint mask;
+ MetaScreen *random_screen;
+ Window random_xwindow;
+
+ primary_modifier = get_primary_modifier (display, entire_binding_mask);
+
+ random_screen = display->screens->data;
+ random_xwindow = random_screen->no_focus_window;
+ XQueryPointer (display->xdisplay,
+ random_xwindow, /* some random window */
+ &root, &child,
+ &root_x, &root_y,
+ &x, &y,
+ &mask);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Primary modifier 0x%x full grab mask 0x%x current state 0x%x\n",
+ primary_modifier, entire_binding_mask, mask);
+
+ if ((mask & primary_modifier) == 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/* now called from only one place, may be worth merging */
+static gboolean
+process_event (MetaKeyBinding *bindings,
+ int n_bindings,
+ MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ KeySym keysym,
+ gboolean on_window)
+{
+ int i;
+
+ /* we used to have release-based bindings but no longer. */
+ if (event->type == KeyRelease)
+ return FALSE;
+
+ /*
+ * TODO: This would be better done with a hash table;
+ * it doesn't suit to use O(n) for such a common operation.
+ */
+ for (i=0; i<n_bindings; i++)
+ {
+ const MetaKeyHandler *handler = bindings[i].handler;
+
+ if ((!on_window && handler->flags & BINDING_PER_WINDOW) ||
+ event->type != KeyPress ||
+ bindings[i].keycode != event->xkey.keycode ||
+ ((event->xkey.state & 0xff & ~(display->ignored_modifier_mask)) !=
+ bindings[i].mask))
+ continue;
+
+ /*
+ * window must be non-NULL for on_window to be true,
+ * and so also window must be non-NULL if we get here and
+ * this is a BINDING_PER_WINDOW binding.
+ */
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Binding keycode 0x%x mask 0x%x matches event 0x%x state 0x%x\n",
+ bindings[i].keycode, bindings[i].mask,
+ event->xkey.keycode, event->xkey.state);
+
+ if (handler == NULL)
+ meta_bug ("Binding %s has no handler\n", bindings[i].name);
+ else
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Running handler for %s\n",
+ bindings[i].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;
+
+ (* handler->func) (display, screen,
+ bindings[i].handler->flags & BINDING_PER_WINDOW? window: NULL,
+ event,
+ &bindings[i]);
+ return TRUE;
+ }
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "No handler found for this event in this binding table\n");
+ return FALSE;
+}
+
+/* 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.
+ * 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).
+ */
+void
+meta_display_process_key_event (MetaDisplay *display,
+ MetaWindow *window,
+ XEvent *event)
+{
+ KeySym keysym;
+ gboolean keep_grab;
+ gboolean all_keys_grabbed;
+ const char *str;
+ MetaScreen *screen;
+
+ XAllowEvents (display->xdisplay,
+ all_bindings_disabled ? ReplayKeyboard : AsyncKeyboard,
+ event->xkey.time);
+ if (all_bindings_disabled)
+ return;
+
+ /* if key event was on root window, we have a shortcut */
+ screen = meta_display_screen_for_root (display, event->xkey.window);
+
+ /* else round-trip to server */
+ if (screen == NULL)
+ screen = meta_display_screen_for_xwindow (display,
+ event->xany.window);
+
+ if (screen == NULL)
+ return; /* event window is destroyed */
+
+ /* ignore key events on popup menus and such. */
+ if (window == NULL &&
+ meta_ui_window_is_widget (screen->ui, event->xany.window))
+ return;
+
+ /* window may be NULL */
+
+ keysym = XKeycodeToKeysym (display->xdisplay, event->xkey.keycode, 0);
+
+ str = XKeysymToString (keysym);
+
+ /* was topic */
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Processing key %s event, keysym: %s state: 0x%x window: %s\n",
+ event->type == KeyPress ? "press" : "release",
+ str ? str : "none", event->xkey.state,
+ window ? window->desc : "(no window)");
+
+ keep_grab = TRUE;
+ all_keys_grabbed = window ? window->all_keys_grabbed : screen->all_keys_grabbed;
+ if (all_keys_grabbed)
+ {
+ if (display->grab_op == META_GRAB_OP_NONE)
+ return;
+ /* If we get here we have a global grab, because
+ * we're in some special keyboard mode such as window move
+ * mode.
+ */
+ if (window ? (window == display->grab_window) :
+ (screen == display->grab_screen))
+ {
+ switch (display->grab_op)
+ {
+ case META_GRAB_OP_MOVING:
+ case META_GRAB_OP_RESIZING_SE:
+ case META_GRAB_OP_RESIZING_S:
+ case META_GRAB_OP_RESIZING_SW:
+ case META_GRAB_OP_RESIZING_N:
+ case META_GRAB_OP_RESIZING_NE:
+ case META_GRAB_OP_RESIZING_NW:
+ case META_GRAB_OP_RESIZING_W:
+ case META_GRAB_OP_RESIZING_E:
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Processing event for mouse-only move/resize\n");
+ g_assert (window != NULL);
+ keep_grab = process_mouse_move_resize_grab (display, screen,
+ window, event, keysym);
+ break;
+
+ case META_GRAB_OP_KEYBOARD_MOVING:
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Processing event for keyboard move\n");
+ g_assert (window != NULL);
+ keep_grab = process_keyboard_move_grab (display, screen,
+ window, event, keysym);
+ break;
+
+ case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+ case META_GRAB_OP_KEYBOARD_RESIZING_S:
+ case META_GRAB_OP_KEYBOARD_RESIZING_N:
+ case META_GRAB_OP_KEYBOARD_RESIZING_W:
+ case META_GRAB_OP_KEYBOARD_RESIZING_E:
+ 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:
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Processing event for keyboard resize\n");
+ g_assert (window != NULL);
+ keep_grab = process_keyboard_resize_grab (display, screen,
+ window, event, keysym);
+ break;
+
+ case META_GRAB_OP_KEYBOARD_TABBING_NORMAL:
+ case META_GRAB_OP_KEYBOARD_TABBING_DOCK:
+ case META_GRAB_OP_KEYBOARD_TABBING_GROUP:
+ case META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL:
+ case META_GRAB_OP_KEYBOARD_ESCAPING_DOCK:
+ case META_GRAB_OP_KEYBOARD_ESCAPING_GROUP:
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Processing event for keyboard tabbing/cycling\n");
+ keep_grab = process_tab_grab (display, screen, event, keysym);
+ break;
+
+ case META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING:
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Processing event for keyboard workspace switching\n");
+ keep_grab = process_workspace_switch_grab (display, screen, event, keysym);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (!keep_grab)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ending grab op %u on key event sym %s\n",
+ display->grab_op, XKeysymToString (keysym));
+ meta_display_end_grab_op (display, event->xkey.time);
+ return;
+ }
+ }
+ /* Do the normal keybindings */
+ process_event (display->key_bindings,
+ display->n_key_bindings,
+ display, screen, window, event, keysym,
+ !all_keys_grabbed && window);
+}
+
+static gboolean
+process_mouse_move_resize_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ KeySym keysym)
+{
+ /* don't care about releases, but eat them, don't end grab */
+ if (event->type == KeyRelease)
+ return TRUE;
+
+ if (keysym == XK_Escape)
+ {
+ /* 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. In
+ * wireframe mode, we just need to set grab_was_cancelled to tru
+ * to avoid avoid moveresizing to the position of the wireframe.
+ */
+ if (window->shaken_loose)
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
+ else if (!display->grab_wireframe_active)
+ meta_window_move_resize (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);
+ else
+ display->grab_was_cancelled = TRUE;
+
+ /* End grab */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+process_keyboard_move_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ KeySym keysym)
+{
+ gboolean handled;
+ int x, y;
+ int incr;
+ gboolean smart_snap;
+
+ handled = FALSE;
+
+ /* don't care about releases, but eat them, don't end grab */
+ if (event->type == KeyRelease)
+ return TRUE;
+
+ /* don't end grab on modifier key presses */
+ if (is_modifier (display, event->xkey.keycode))
+ return TRUE;
+
+ if (display->grab_wireframe_active)
+ {
+ x = display->grab_wireframe_rect.x;
+ y = display->grab_wireframe_rect.y;
+ }
+ else
+ {
+ meta_window_get_position (window, &x, &y);
+ }
+
+ smart_snap = (event->xkey.state & ShiftMask) != 0;
+
+#define SMALL_INCREMENT 1
+#define NORMAL_INCREMENT 10
+
+ if (smart_snap)
+ incr = 1;
+ else if (event->xkey.state & ControlMask)
+ incr = SMALL_INCREMENT;
+ else
+ incr = NORMAL_INCREMENT;
+
+ if (keysym == XK_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. In wireframe
+ * mode, we just need to set grab_was_cancelled to tru to avoid
+ * avoid moveresizing to the position of the wireframe.
+ */
+ if (window->shaken_loose)
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
+ else if (!display->grab_wireframe_active)
+ meta_window_move_resize (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);
+ else
+ display->grab_was_cancelled = TRUE;
+ }
+
+ /* 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 (keysym)
+ {
+ case XK_KP_Home:
+ case XK_KP_Prior:
+ case XK_Up:
+ case XK_KP_Up:
+ y -= incr;
+ handled = TRUE;
+ break;
+ case XK_KP_End:
+ case XK_KP_Next:
+ case XK_Down:
+ case XK_KP_Down:
+ y += incr;
+ handled = TRUE;
+ break;
+ }
+
+ switch (keysym)
+ {
+ case XK_KP_Home:
+ case XK_KP_End:
+ case XK_Left:
+ case XK_KP_Left:
+ x -= incr;
+ handled = TRUE;
+ break;
+ case XK_KP_Prior:
+ case XK_KP_Next:
+ case XK_Right:
+ case XK_KP_Right:
+ x += incr;
+ handled = TRUE;
+ break;
+ }
+
+ if (handled)
+ {
+ MetaRectangle old_rect;
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Computed new window location %d,%d due to keypress\n",
+ x, y);
+
+ if (display->grab_wireframe_active)
+ old_rect = display->grab_wireframe_rect;
+ else
+ meta_window_get_client_root_coords (window, &old_rect);
+
+ meta_window_edge_resistance_for_move (window,
+ old_rect.x,
+ old_rect.y,
+ &x,
+ &y,
+ NULL,
+ smart_snap,
+ TRUE);
+
+ if (display->grab_wireframe_active)
+ {
+ meta_window_update_wireframe (window, x, y,
+ display->grab_wireframe_rect.width,
+ display->grab_wireframe_rect.height);
+ }
+ else
+ {
+ meta_window_move (window, TRUE, x, y);
+ }
+
+ meta_window_update_keyboard_move (window);
+ }
+
+ return handled;
+}
+
+static gboolean
+process_keyboard_resize_grab_op_change (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ KeySym keysym)
+{
+ gboolean handled;
+
+ handled = FALSE;
+ switch (display->grab_op)
+ {
+ case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+ switch (keysym)
+ {
+ case XK_Up:
+ case XK_KP_Up:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
+ handled = TRUE;
+ break;
+ case XK_Down:
+ case XK_KP_Down:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
+ handled = TRUE;
+ break;
+ case XK_Left:
+ case XK_KP_Left:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
+ handled = TRUE;
+ break;
+ case XK_Right:
+ case XK_KP_Right:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
+ handled = TRUE;
+ break;
+ }
+ break;
+
+ case META_GRAB_OP_KEYBOARD_RESIZING_S:
+ switch (keysym)
+ {
+ case XK_Left:
+ case XK_KP_Left:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
+ handled = TRUE;
+ break;
+ case XK_Right:
+ case XK_KP_Right:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
+ handled = TRUE;
+ break;
+ }
+ break;
+
+ case META_GRAB_OP_KEYBOARD_RESIZING_N:
+ switch (keysym)
+ {
+ case XK_Left:
+ case XK_KP_Left:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
+ handled = TRUE;
+ break;
+ case XK_Right:
+ case XK_KP_Right:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
+ handled = TRUE;
+ break;
+ }
+ break;
+
+ case META_GRAB_OP_KEYBOARD_RESIZING_W:
+ switch (keysym)
+ {
+ case XK_Up:
+ case XK_KP_Up:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
+ handled = TRUE;
+ break;
+ case XK_Down:
+ case XK_KP_Down:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
+ handled = TRUE;
+ break;
+ }
+ break;
+
+ case META_GRAB_OP_KEYBOARD_RESIZING_E:
+ switch (keysym)
+ {
+ case XK_Up:
+ case XK_KP_Up:
+ display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
+ handled = TRUE;
+ break;
+ case XK_Down:
+ case XK_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,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ KeySym keysym)
+{
+ gboolean handled;
+ int height_inc;
+ int width_inc;
+ int width, height;
+ gboolean smart_snap;
+ int gravity;
+
+ handled = FALSE;
+
+ /* don't care about releases, but eat them, don't end grab */
+ if (event->type == KeyRelease)
+ return TRUE;
+
+ /* don't end grab on modifier key presses */
+ if (is_modifier (display, event->xkey.keycode))
+ return TRUE;
+
+ if (keysym == XK_Escape)
+ {
+ /* End resize and restore to original state. If not in
+ * wireframe mode, we need to do a moveresize now to get the
+ * position back to the original. If we are in wireframe mode,
+ * we need to avoid moveresizing to the position of the
+ * wireframe.
+ */
+ if (!display->grab_wireframe_active)
+ meta_window_move_resize (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);
+ else
+ display->grab_was_cancelled = TRUE;
+
+ return FALSE;
+ }
+
+ if (process_keyboard_resize_grab_op_change (display, screen, window,
+ event, keysym))
+ return TRUE;
+
+ if (display->grab_wireframe_active)
+ {
+ width = display->grab_wireframe_rect.width;
+ height = display->grab_wireframe_rect.height;
+ }
+ else
+ {
+ width = window->rect.width;
+ height = window->rect.height;
+ }
+
+ gravity = meta_resize_gravity_from_grab_op (display->grab_op);
+
+ smart_snap = (event->xkey.state & ShiftMask) != 0;
+
+#define SMALL_INCREMENT 1
+#define NORMAL_INCREMENT 10
+
+ if (smart_snap)
+ {
+ height_inc = 1;
+ width_inc = 1;
+ }
+ else if (event->xkey.state & ControlMask)
+ {
+ 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 (keysym)
+ {
+ case XK_Up:
+ case XK_KP_Up:
+ switch (gravity)
+ {
+ case NorthGravity:
+ case NorthWestGravity:
+ case NorthEastGravity:
+ /* Move bottom edge up */
+ height -= height_inc;
+ break;
+
+ case SouthGravity:
+ case SouthWestGravity:
+ case SouthEastGravity:
+ /* Move top edge up */
+ height += height_inc;
+ break;
+
+ case EastGravity:
+ case WestGravity:
+ case CenterGravity:
+ g_assert_not_reached ();
+ break;
+ }
+
+ handled = TRUE;
+ break;
+
+ case XK_Down:
+ case XK_KP_Down:
+ switch (gravity)
+ {
+ case NorthGravity:
+ case NorthWestGravity:
+ case NorthEastGravity:
+ /* Move bottom edge down */
+ height += height_inc;
+ break;
+
+ case SouthGravity:
+ case SouthWestGravity:
+ case SouthEastGravity:
+ /* Move top edge down */
+ height -= height_inc;
+ break;
+
+ case EastGravity:
+ case WestGravity:
+ case CenterGravity:
+ g_assert_not_reached ();
+ break;
+ }
+
+ handled = TRUE;
+ break;
+
+ case XK_Left:
+ case XK_KP_Left:
+ switch (gravity)
+ {
+ case EastGravity:
+ case SouthEastGravity:
+ case NorthEastGravity:
+ /* Move left edge left */
+ width += width_inc;
+ break;
+
+ case WestGravity:
+ case SouthWestGravity:
+ case NorthWestGravity:
+ /* Move right edge left */
+ width -= width_inc;
+ break;
+
+ case NorthGravity:
+ case SouthGravity:
+ case CenterGravity:
+ g_assert_not_reached ();
+ break;
+ }
+
+ handled = TRUE;
+ break;
+
+ case XK_Right:
+ case XK_KP_Right:
+ switch (gravity)
+ {
+ case EastGravity:
+ case SouthEastGravity:
+ case NorthEastGravity:
+ /* Move left edge right */
+ width -= width_inc;
+ break;
+
+ case WestGravity:
+ case SouthWestGravity:
+ case NorthWestGravity:
+ /* Move right edge right */
+ width += width_inc;
+ break;
+
+ case NorthGravity:
+ case SouthGravity:
+ case CenterGravity:
+ 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)
+ {
+ MetaRectangle old_rect;
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Computed new window size due to keypress: "
+ "%dx%d, gravity %s\n",
+ width, height, meta_gravity_to_string (gravity));
+
+ if (display->grab_wireframe_active)
+ old_rect = display->grab_wireframe_rect;
+ else
+ old_rect = window->rect; /* Don't actually care about x,y */
+
+ /* Do any edge resistance/snapping */
+ meta_window_edge_resistance_for_resize (window,
+ old_rect.width,
+ old_rect.height,
+ &width,
+ &height,
+ gravity,
+ NULL,
+ smart_snap,
+ TRUE);
+
+ if (display->grab_wireframe_active)
+ {
+ MetaRectangle new_position;
+ meta_rectangle_resize_with_gravity (&display->grab_wireframe_rect,
+ &new_position,
+ gravity,
+ width,
+ height);
+ meta_window_update_wireframe (window,
+ new_position.x,
+ new_position.y,
+ new_position.width,
+ new_position.height);
+ }
+ else
+ {
+ /* We don't need to update unless the specified width and height
+ * are actually different from what we had before.
+ */
+ if (window->rect.width != width || window->rect.height != height)
+ meta_window_resize_with_gravity (window,
+ TRUE,
+ width,
+ height,
+ gravity);
+ }
+ meta_window_update_keyboard_resize (window, FALSE);
+ }
+
+ return handled;
+}
+
+static gboolean
+end_keyboard_grab (MetaDisplay *display,
+ unsigned int keycode)
+{
+#ifdef HAVE_XKB
+ if (display->xkb_base_event_type > 0)
+ {
+ unsigned int primary_modifier;
+ XkbStateRec state;
+
+ primary_modifier = get_primary_modifier (display, display->grab_mask);
+
+ XkbGetState (display->xdisplay, XkbUseCoreKbd, &state);
+
+ if (!(primary_modifier & state.mods))
+ return TRUE;
+ }
+ else
+#endif
+ {
+ if (keycode_is_primary_modifier (display, keycode, display->grab_mask))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+process_tab_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ XEvent *event,
+ KeySym keysym)
+{
+ MetaKeyBindingAction action;
+ gboolean popup_not_showing;
+ gboolean backward;
+ gboolean key_used;
+ Window prev_xwindow;
+ MetaWindow *prev_window;
+
+ if (screen != display->grab_screen)
+ return FALSE;
+
+ g_return_val_if_fail (screen->tab_popup != NULL, FALSE);
+
+ if (event->type == KeyRelease &&
+ end_keyboard_grab (display, event->xkey.keycode))
+ {
+ /* We're done, move to the new window. */
+ Window target_xwindow;
+ MetaWindow *target_window;
+
+ target_xwindow =
+ (Window) meta_ui_tab_popup_get_selected (screen->tab_popup);
+ target_window =
+ meta_display_lookup_x_window (display, target_xwindow);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ending tab operation, primary modifier released\n");
+
+ if (target_window)
+ {
+ target_window->tab_unminimized = FALSE;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Activating target window\n");
+
+ meta_topic (META_DEBUG_FOCUS, "Activating %s due to tab popup "
+ "selection and turning mouse_mode off\n",
+ target_window->desc);
+ display->mouse_mode = FALSE;
+ meta_window_activate (target_window, event->xkey.time);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ending grab early so we can focus the target window\n");
+ meta_display_end_grab_op (display, event->xkey.time);
+
+ return TRUE; /* we already ended the grab */
+ }
+
+ return FALSE; /* end grab */
+ }
+
+ /* don't care about other releases, but eat them, don't end grab */
+ if (event->type == KeyRelease)
+ return TRUE;
+
+ /* don't end grab on modifier key presses */
+ if (is_modifier (display, event->xkey.keycode))
+ return TRUE;
+
+ prev_xwindow = (Window) meta_ui_tab_popup_get_selected (screen->tab_popup);
+ prev_window = meta_display_lookup_x_window (display, prev_xwindow);
+ action = display_get_keybinding_action (display,
+ keysym,
+ event->xkey.keycode,
+ display->grab_mask);
+
+ /* Cancel when alt-Escape is pressed during using alt-Tab, and vice
+ * versa.
+ */
+ switch (action)
+ {
+ case META_KEYBINDING_ACTION_CYCLE_PANELS:
+ case META_KEYBINDING_ACTION_CYCLE_WINDOWS:
+ case META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD:
+ case META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD:
+ /* CYCLE_* are traditionally Escape-based actions,
+ * and should cancel traditionally Tab-based ones.
+ */
+ switch (display->grab_op)
+ {
+ case META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL:
+ case META_GRAB_OP_KEYBOARD_ESCAPING_DOCK:
+ /* carry on */
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+ case META_KEYBINDING_ACTION_SWITCH_PANELS:
+ case META_KEYBINDING_ACTION_SWITCH_WINDOWS:
+ case META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD:
+ case META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD:
+ /* SWITCH_* are traditionally Tab-based actions,
+ * and should cancel traditionally Escape-based ones.
+ */
+ switch (display->grab_op)
+ {
+ case META_GRAB_OP_KEYBOARD_TABBING_NORMAL:
+ case META_GRAB_OP_KEYBOARD_TABBING_DOCK:
+ /* carry on */
+ break;
+ default:
+ /* Also, we must re-lower and re-minimize whatever window
+ * we'd previously raised and unminimized.
+ */
+ meta_stack_set_positions (screen->stack,
+ screen->display->grab_old_window_stacking);
+ if (prev_window && prev_window->tab_unminimized)
+ {
+ meta_window_minimize (prev_window);
+ prev_window->tab_unminimized = FALSE;
+ }
+ return FALSE;
+ }
+ break;
+ case META_KEYBINDING_ACTION_CYCLE_GROUP:
+ case META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD:
+ case META_KEYBINDING_ACTION_SWITCH_GROUP:
+ case META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD:
+ switch (display->grab_op)
+ {
+ case META_GRAB_OP_KEYBOARD_ESCAPING_GROUP:
+ case META_GRAB_OP_KEYBOARD_TABBING_GROUP:
+ /* carry on */
+ break;
+ default:
+ return FALSE;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ /* !! TO HERE !!
+ * alt-f6 during alt-{Tab,Escape} does not end the grab
+ * but does change the grab op (and redraws the window,
+ * of course).
+ * See _{SWITCH,CYCLE}_GROUP.@@@
+ */
+
+ popup_not_showing = FALSE;
+ key_used = FALSE;
+ backward = FALSE;
+
+ switch (action)
+ {
+ case META_KEYBINDING_ACTION_CYCLE_PANELS:
+ case META_KEYBINDING_ACTION_CYCLE_WINDOWS:
+ case META_KEYBINDING_ACTION_CYCLE_GROUP:
+ popup_not_showing = TRUE;
+ key_used = TRUE;
+ break;
+ case META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD:
+ case META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD:
+ case META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD:
+ popup_not_showing = TRUE;
+ key_used = TRUE;
+ backward = TRUE;
+ break;
+ case META_KEYBINDING_ACTION_SWITCH_PANELS:
+ case META_KEYBINDING_ACTION_SWITCH_WINDOWS:
+ case META_KEYBINDING_ACTION_SWITCH_GROUP:
+ key_used = TRUE;
+ break;
+ case META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD:
+ case META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD:
+ case META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD:
+ key_used = TRUE;
+ backward = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ if (key_used)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Key pressed, moving tab focus in popup\n");
+
+ if (event->xkey.state & ShiftMask)
+ backward = !backward;
+
+ if (backward)
+ meta_ui_tab_popup_backward (screen->tab_popup);
+ else
+ meta_ui_tab_popup_forward (screen->tab_popup);
+
+ if (popup_not_showing)
+ {
+ /* We can't actually change window focus, due to the grab.
+ * but raise the window.
+ */
+ Window target_xwindow;
+ MetaWindow *target_window;
+
+ meta_stack_set_positions (screen->stack,
+ display->grab_old_window_stacking);
+
+ target_xwindow =
+ (Window) meta_ui_tab_popup_get_selected (screen->tab_popup);
+ target_window =
+ meta_display_lookup_x_window (display, target_xwindow);
+
+ if (prev_window && prev_window->tab_unminimized)
+ {
+ prev_window->tab_unminimized = FALSE;
+ meta_window_minimize (prev_window);
+ }
+
+ if (target_window)
+ {
+ meta_window_raise (target_window);
+ target_window->tab_unminimized = target_window->minimized;
+ meta_window_unminimize (target_window);
+ }
+ }
+ }
+ else
+ {
+ /* end grab */
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ending tabbing/cycling, uninteresting key pressed\n");
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Syncing to old stack positions.\n");
+ meta_stack_set_positions (screen->stack,
+ screen->display->grab_old_window_stacking);
+
+ if (prev_window && prev_window->tab_unminimized)
+ {
+ meta_window_minimize (prev_window);
+ prev_window->tab_unminimized = FALSE;
+ }
+ }
+
+ return key_used;
+}
+
+static void
+handle_switch_to_workspace (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *event_window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ gint which = binding->handler->data;
+ MetaWorkspace *workspace;
+
+ if (which < 0)
+ {
+ /* Negative workspace numbers are directions with respect to the
+ * current workspace. While we could insta-switch here by setting
+ * workspace to the result of meta_workspace_get_neighbor(), when
+ * people request a workspace switch to the left or right via
+ * the keyboard, they actually want a tab popup. So we should
+ * go there instead.
+ *
+ * Note that we're the only caller of that function, so perhaps
+ * we should merge with it.
+ */
+ handle_workspace_switch (display, screen, event_window, event, binding);
+ return;
+ }
+
+ workspace = meta_screen_get_workspace_by_index (screen, which);
+
+ if (workspace)
+ {
+ meta_workspace_activate (workspace, event->xkey.time);
+ }
+ else
+ {
+ /* We could offer to create it I suppose */
+ }
+}
+
+static void
+error_on_command (int command_index,
+ const char *command,
+ const char *message,
+ int screen_number,
+ guint32 timestamp)
+{
+ if (command_index < 0)
+ meta_warning ("Error on terminal command \"%s\": %s\n", command, message);
+ else
+ meta_warning ("Error on command %d \"%s\": %s\n",
+ command_index, command, message);
+
+ /*
+ marco-dialog said:
+
+ FIXME offer to change the value of the command's mateconf key
+ */
+
+ if (command && strcmp(command, "")!=0)
+ {
+ char *text = g_strdup_printf (
+ /* Displayed when a keybinding which is
+ * supposed to launch a program fails.
+ */
+ _("There was an error running "
+ "<tt>%s</tt>:\n\n%s"),
+ command,
+ message);
+
+ meta_show_dialog ("--error",
+ text,
+ NULL,
+ screen_number,
+ NULL, NULL, 0,
+ NULL, NULL);
+
+ g_free (text);
+
+ }
+ else
+ {
+ meta_show_dialog ("--error",
+ message,
+ NULL,
+ screen_number,
+ NULL, NULL, 0,
+ NULL, NULL);
+ }
+}
+
+static void
+set_display_setup_func (void *data)
+{
+ const char *screen_name = data;
+ char *full;
+
+ full = g_strdup_printf ("DISPLAY=%s", screen_name);
+
+ putenv (full);
+
+ /* do not free full, because putenv is lame */
+}
+
+static gboolean
+meta_spawn_command_line_async_on_screen (const gchar *command_line,
+ MetaScreen *screen,
+ GError **error)
+{
+ gboolean retval;
+ gchar **argv = NULL;
+
+ g_return_val_if_fail (command_line != NULL, FALSE);
+
+ if (!g_shell_parse_argv (command_line,
+ NULL, &argv,
+ error))
+ return FALSE;
+
+ retval = g_spawn_async (NULL,
+ argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ set_display_setup_func,
+ screen->screen_name,
+ NULL,
+ error);
+ g_strfreev (argv);
+
+ return retval;
+}
+
+
+static void
+handle_run_command (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ gint which = binding->handler->data;
+ const char *command;
+ GError *err;
+
+ command = meta_prefs_get_command (which);
+
+ if (command == NULL)
+ {
+ char *s;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "No command %d to run in response to keybinding press\n",
+ which);
+
+ s = g_strdup_printf (_("No command %d has been defined.\n"),
+ which + 1);
+ error_on_command (which, NULL, s, screen->number, event->xkey.time);
+ g_free (s);
+
+ return;
+ }
+
+ err = NULL;
+ if (!meta_spawn_command_line_async_on_screen (command, screen, &err))
+ {
+ error_on_command (which, command, err->message, screen->number, event->xkey.time);
+
+ g_error_free (err);
+ }
+}
+
+
+static void
+handle_maximize_vertically (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ 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,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->has_resize_func)
+ {
+ if (window->maximized_horizontally)
+ meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL);
+ else
+ meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL);
+ }
+}
+
+/* Move a window to a corner; to_bottom/to_right are FALSE for the
+ * top or left edge, or TRUE for the bottom/right edge. xchange/ychange
+ * are FALSE if that dimension is not to be changed, TRUE otherwise.
+ * Together they describe which of the four corners, or four sides,
+ * is desired.
+ */
+static void
+handle_move_to_corner_backend (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ gboolean xchange,
+ gboolean ychange,
+ gboolean to_right,
+ gboolean to_bottom)
+{
+ MetaRectangle work_area;
+ MetaRectangle outer;
+ int orig_x, orig_y;
+ int new_x, new_y;
+ int frame_width, frame_height;
+
+ meta_window_get_work_area_all_xineramas (window, &work_area);
+ meta_window_get_outer_rect (window, &outer);
+ meta_window_get_position (window, &orig_x, &orig_y);
+
+ frame_width = (window->frame ? window->frame->child_x : 0);
+ frame_height = (window->frame ? window->frame->child_y : 0);
+
+ if (xchange) {
+ new_x = work_area.x + (to_right ?
+ (work_area.width + frame_width) - outer.width :
+ 0);
+ } else {
+ new_x = orig_x;
+ }
+
+ if (ychange) {
+ new_y = work_area.y + (to_bottom ?
+ (work_area.height + frame_height) - outer.height :
+ 0);
+ } else {
+ new_y = orig_y;
+ }
+
+ meta_window_move_resize (window,
+ FALSE,
+ new_x,
+ new_y,
+ window->rect.width,
+ window->rect.height);
+}
+
+static void
+handle_move_to_corner_nw (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ handle_move_to_corner_backend (display, screen, window, TRUE, TRUE, FALSE, FALSE);
+}
+
+static void
+handle_move_to_corner_ne (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ handle_move_to_corner_backend (display, screen, window, TRUE, TRUE, TRUE, FALSE);
+}
+
+static void
+handle_move_to_corner_sw (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ handle_move_to_corner_backend (display, screen, window, TRUE, TRUE, FALSE, TRUE);
+}
+
+static void
+handle_move_to_corner_se (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ handle_move_to_corner_backend (display, screen, window, TRUE, TRUE, TRUE, TRUE);
+}
+
+static void
+handle_move_to_side_n (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ handle_move_to_corner_backend (display, screen, window, FALSE, TRUE, FALSE, FALSE);
+}
+
+static void
+handle_move_to_side_s (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ handle_move_to_corner_backend (display, screen, window, FALSE, TRUE, FALSE, TRUE);
+}
+
+static void
+handle_move_to_side_e (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ handle_move_to_corner_backend (display, screen, window, TRUE, FALSE, TRUE, FALSE);
+}
+
+static void
+handle_move_to_side_w (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ handle_move_to_corner_backend (display, screen, window, TRUE, FALSE, FALSE, FALSE);
+}
+
+static void
+handle_move_to_center (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ MetaRectangle work_area;
+ MetaRectangle outer;
+ int orig_x, orig_y;
+ int frame_width, frame_height;
+
+ meta_window_get_work_area_all_xineramas (window, &work_area);
+ meta_window_get_outer_rect (window, &outer);
+ meta_window_get_position (window, &orig_x, &orig_y);
+
+ frame_width = (window->frame ? window->frame->child_x : 0);
+ frame_height = (window->frame ? window->frame->child_y : 0);
+
+ meta_window_move_resize (window,
+ TRUE,
+ work_area.x + (work_area.width +frame_width -outer.width )/2,
+ work_area.y + (work_area.height+frame_height-outer.height)/2,
+ window->rect.width,
+ window->rect.height);
+}
+
+static gboolean
+process_workspace_switch_grab (MetaDisplay *display,
+ MetaScreen *screen,
+ XEvent *event,
+ KeySym keysym)
+{
+ MetaWorkspace *workspace;
+
+ if (screen != display->grab_screen)
+ return FALSE;
+
+ g_return_val_if_fail (screen->tab_popup != NULL, FALSE);
+
+ if (event->type == KeyRelease &&
+ end_keyboard_grab (display, event->xkey.keycode))
+ {
+ /* We're done, move to the new workspace. */
+ MetaWorkspace *target_workspace;
+
+ target_workspace =
+ (MetaWorkspace *) meta_ui_tab_popup_get_selected (screen->tab_popup);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ending workspace tab operation, primary modifier released\n");
+
+ if (target_workspace == screen->active_workspace)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ending grab so we can focus on the target workspace\n");
+ meta_display_end_grab_op (display, event->xkey.time);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Focusing default window on target workspace\n");
+
+ meta_workspace_focus_default_window (target_workspace,
+ NULL,
+ event->xkey.time);
+
+ return TRUE; /* we already ended the grab */
+ }
+
+ /* Workspace switching should have already occurred on KeyPress */
+ meta_warning ("target_workspace != active_workspace. Some other event must have occurred.\n");
+
+ return FALSE; /* end grab */
+ }
+
+ /* don't care about other releases, but eat them, don't end grab */
+ if (event->type == KeyRelease)
+ return TRUE;
+
+ /* don't end grab on modifier key presses */
+ if (is_modifier (display, event->xkey.keycode))
+ return TRUE;
+
+ /* select the next workspace in the tabpopup */
+ workspace =
+ (MetaWorkspace *) meta_ui_tab_popup_get_selected (screen->tab_popup);
+
+ if (workspace)
+ {
+ MetaWorkspace *target_workspace;
+ MetaKeyBindingAction action;
+
+ action = display_get_keybinding_action (display,
+ keysym,
+ event->xkey.keycode,
+ display->grab_mask);
+
+ switch (action)
+ {
+ case META_KEYBINDING_ACTION_WORKSPACE_UP:
+ target_workspace = meta_workspace_get_neighbor (workspace,
+ META_MOTION_UP);
+ break;
+
+ case META_KEYBINDING_ACTION_WORKSPACE_DOWN:
+ target_workspace = meta_workspace_get_neighbor (workspace,
+ META_MOTION_DOWN);
+ break;
+
+ case META_KEYBINDING_ACTION_WORKSPACE_LEFT:
+ target_workspace = meta_workspace_get_neighbor (workspace,
+ META_MOTION_LEFT);
+ break;
+
+ case META_KEYBINDING_ACTION_WORKSPACE_RIGHT:
+ target_workspace = meta_workspace_get_neighbor (workspace,
+ META_MOTION_RIGHT);
+ break;
+
+ default:
+ target_workspace = NULL;
+ break;
+ }
+
+ if (target_workspace)
+ {
+ meta_ui_tab_popup_select (screen->tab_popup,
+ (MetaTabEntryKey) target_workspace);
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Tab key pressed, moving tab focus in popup\n");
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Activating target workspace\n");
+
+ meta_workspace_activate (target_workspace, event->xkey.time);
+
+ return TRUE; /* we already ended the grab */
+ }
+ }
+
+ /* end grab */
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Ending workspace tabbing & focusing default window; uninteresting key pressed\n");
+ workspace =
+ (MetaWorkspace *) meta_ui_tab_popup_get_selected (screen->tab_popup);
+ meta_workspace_focus_default_window (workspace, NULL, event->xkey.time);
+ return FALSE;
+}
+
+static void
+handle_show_desktop (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (screen->active_workspace->showing_desktop)
+ {
+ meta_screen_unshow_desktop (screen);
+ meta_workspace_focus_default_window (screen->active_workspace,
+ NULL,
+ event->xkey.time);
+ }
+ else
+ meta_screen_show_desktop (screen, event->xkey.time);
+}
+
+static void
+handle_panel (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ MetaKeyBindingAction action = binding->handler->data;
+ Atom action_atom;
+ XClientMessageEvent ev;
+
+ action_atom = None;
+ switch (action)
+ {
+ /* FIXME: The numbers are wrong */
+ case META_KEYBINDING_ACTION_PANEL_MAIN_MENU:
+ action_atom = display->atom__MATE_PANEL_ACTION_MAIN_MENU;
+ break;
+ case META_KEYBINDING_ACTION_PANEL_RUN_DIALOG:
+ action_atom = display->atom__MATE_PANEL_ACTION_RUN_DIALOG;
+ break;
+ default:
+ return;
+ }
+
+ ev.type = ClientMessage;
+ ev.window = screen->xroot;
+ ev.message_type = display->atom__MATE_PANEL_ACTION;
+ ev.format = 32;
+ ev.data.l[0] = action_atom;
+ ev.data.l[1] = event->xkey.time;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Sending panel message with timestamp %lu, and turning mouse_mode "
+ "off due to keybinding press\n", event->xkey.time);
+ display->mouse_mode = FALSE;
+
+ meta_error_trap_push (display);
+
+ /* Release the grab for the panel before sending the event */
+ XUngrabKeyboard (display->xdisplay, event->xkey.time);
+
+ XSendEvent (display->xdisplay,
+ screen->xroot,
+ False,
+ StructureNotifyMask,
+ (XEvent*) &ev);
+
+ meta_error_trap_pop (display, FALSE);
+}
+
+static void
+handle_activate_window_menu (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *event_window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (display->focus_window)
+ {
+ int x, y;
+
+ meta_window_get_position (display->focus_window,
+ &x, &y);
+
+ if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
+ x += display->focus_window->rect.width;
+
+ meta_window_show_menu (display->focus_window,
+ x, y,
+ 0,
+ event->xkey.time);
+ }
+}
+
+static MetaGrabOp
+tab_op_from_tab_type (MetaTabList type)
+{
+ switch (type)
+ {
+ case META_TAB_LIST_NORMAL:
+ return META_GRAB_OP_KEYBOARD_TABBING_NORMAL;
+ case META_TAB_LIST_DOCKS:
+ return META_GRAB_OP_KEYBOARD_TABBING_DOCK;
+ case META_TAB_LIST_GROUP:
+ return META_GRAB_OP_KEYBOARD_TABBING_GROUP;
+ }
+
+ g_assert_not_reached ();
+
+ return 0;
+}
+
+static MetaGrabOp
+cycle_op_from_tab_type (MetaTabList type)
+{
+ switch (type)
+ {
+ case META_TAB_LIST_NORMAL:
+ return META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL;
+ case META_TAB_LIST_DOCKS:
+ return META_GRAB_OP_KEYBOARD_ESCAPING_DOCK;
+ case META_TAB_LIST_GROUP:
+ return META_GRAB_OP_KEYBOARD_ESCAPING_GROUP;
+ }
+
+ g_assert_not_reached ();
+
+ return 0;
+}
+
+static void
+do_choose_window (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *event_window,
+ XEvent *event,
+ MetaKeyBinding *binding,
+ gboolean backward,
+ gboolean show_popup)
+{
+ MetaTabList type = binding->handler->data;
+ MetaWindow *initial_selection;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Tab list = %u show_popup = %d\n", type, show_popup);
+
+ /* reverse direction if shift is down */
+ if (event->xkey.state & ShiftMask)
+ backward = !backward;
+
+ initial_selection = meta_display_get_tab_next (display,
+ type,
+ screen,
+ screen->active_workspace,
+ NULL,
+ backward);
+
+ /* Note that focus_window may not be in the tab chain, but it's OK */
+ if (initial_selection == NULL)
+ initial_selection = meta_display_get_tab_current (display,
+ type, screen,
+ screen->active_workspace);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Initially selecting window %s\n",
+ initial_selection ? initial_selection->desc : "(none)");
+
+ if (initial_selection != NULL)
+ {
+ if (binding->mask == 0)
+ {
+ /* If no modifiers, we can't do the "hold down modifier to keep
+ * moving" thing, so we just instaswitch by one window.
+ */
+ meta_topic (META_DEBUG_FOCUS,
+ "Activating %s and turning off mouse_mode due to "
+ "switch/cycle windows with no modifiers\n",
+ initial_selection->desc);
+ display->mouse_mode = FALSE;
+ meta_window_activate (initial_selection, event->xkey.time);
+ }
+ else if (meta_display_begin_grab_op (display,
+ screen,
+ NULL,
+ show_popup ?
+ tab_op_from_tab_type (type) :
+ cycle_op_from_tab_type (type),
+ FALSE,
+ FALSE,
+ 0,
+ binding->mask,
+ event->xkey.time,
+ 0, 0))
+ {
+ if (!primary_modifier_still_pressed (display,
+ binding->mask))
+ {
+ /* This handles a race where modifier might be released
+ * before we establish the grab. must end grab
+ * prior to trying to focus a window.
+ */
+ meta_topic (META_DEBUG_FOCUS,
+ "Ending grab, activating %s, and turning off "
+ "mouse_mode due to switch/cycle windows where "
+ "modifier was released prior to grab\n",
+ initial_selection->desc);
+ meta_display_end_grab_op (display, event->xkey.time);
+ display->mouse_mode = FALSE;
+ meta_window_activate (initial_selection, event->xkey.time);
+ }
+ else
+ {
+ meta_ui_tab_popup_select (screen->tab_popup,
+ (MetaTabEntryKey) initial_selection->xwindow);
+
+ if (show_popup)
+ meta_ui_tab_popup_set_showing (screen->tab_popup,
+ TRUE);
+ else
+ {
+ meta_window_raise (initial_selection);
+ initial_selection->tab_unminimized =
+ initial_selection->minimized;
+ meta_window_unminimize (initial_selection);
+ }
+ }
+ }
+ }
+}
+
+static void
+handle_switch (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *event_window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ gint backwards = binding->handler->flags & BINDING_IS_REVERSED;
+
+ do_choose_window (display, screen, event_window, event, binding,
+ backwards, TRUE);
+}
+
+static void
+handle_cycle (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *event_window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ gint backwards = binding->handler->flags & BINDING_IS_REVERSED;
+
+ do_choose_window (display, screen, event_window, event, binding,
+ backwards, FALSE);
+}
+
+
+static void
+handle_toggle_fullscreen (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ 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,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->wm_state_above)
+ meta_window_unmake_above (window);
+ else
+ meta_window_make_above (window);
+}
+
+static void
+handle_toggle_maximized (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (META_WINDOW_MAXIMIZED (window))
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
+ else if (window->has_maximize_func)
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
+}
+
+static void
+handle_maximize (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->has_maximize_func)
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
+}
+
+static void
+handle_unmaximize (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->maximized_vertically || window->maximized_horizontally)
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
+}
+
+static void
+handle_toggle_shaded (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->shaded)
+ meta_window_unshade (window, event->xkey.time);
+ else if (window->has_shade_func)
+ meta_window_shade (window, event->xkey.time);
+}
+
+static void
+handle_close (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->has_close_func)
+ meta_window_delete (window, event->xkey.time);
+}
+
+static void
+handle_minimize (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->has_minimize_func)
+ meta_window_minimize (window);
+}
+
+static void
+handle_begin_move (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->has_move_func)
+ {
+ meta_window_begin_grab_op (window,
+ META_GRAB_OP_KEYBOARD_MOVING,
+ FALSE,
+ event->xkey.time);
+ }
+}
+
+static void
+handle_begin_resize (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->has_resize_func)
+ {
+ meta_window_begin_grab_op (window,
+ META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
+ FALSE,
+ event->xkey.time);
+ }
+}
+
+static void
+handle_toggle_on_all_workspaces (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ if (window->on_all_workspaces)
+ meta_window_unstick (window);
+ else
+ meta_window_stick (window);
+}
+
+static void
+handle_move_to_workspace (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ 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 (screen->active_workspace,
+ which);
+ }
+ else
+ {
+ workspace = meta_screen_get_workspace_by_index (screen, 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.\n");
+ workspace->screen->display->mouse_mode = FALSE;
+ meta_workspace_activate_with_focus (workspace,
+ window,
+ event->xkey.time);
+ }
+ }
+ else
+ {
+ /* We could offer to create it I suppose */
+ }
+}
+
+static void
+handle_raise_or_lower (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ /* Get window at pointer */
+
+ MetaWindow *above = NULL;
+
+ /* Check if top */
+ if (meta_stack_get_top (window->screen->stack) == window)
+ {
+ meta_window_lower (window);
+ return;
+ }
+
+ /* else check if windows in same layer are intersecting it */
+
+ above = meta_stack_get_above (window->screen->stack, window, TRUE);
+
+ while (above)
+ {
+ MetaRectangle tmp, win_rect, above_rect;
+
+ if (above->mapped)
+ {
+ meta_window_get_outer_rect (window, &win_rect);
+ meta_window_get_outer_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->screen->stack, above, TRUE);
+ }
+
+ /* window is not obscured */
+ meta_window_lower (window);
+}
+
+static void
+handle_raise (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ meta_window_raise (window);
+}
+
+static void
+handle_lower (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ meta_window_lower (window);
+}
+
+static void
+handle_workspace_switch (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ gint motion = binding->handler->data;
+ unsigned int grab_mask;
+
+ g_assert (motion < 0);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Starting tab between workspaces, showing popup\n");
+
+ /* FIXME should we use binding->mask ? */
+ grab_mask = event->xkey.state & ~(display->ignored_modifier_mask);
+
+ if (meta_display_begin_grab_op (display,
+ screen,
+ NULL,
+ META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING,
+ FALSE,
+ FALSE,
+ 0,
+ grab_mask,
+ event->xkey.time,
+ 0, 0))
+ {
+ MetaWorkspace *next;
+ gboolean grabbed_before_release;
+
+ next = meta_workspace_get_neighbor (screen->active_workspace, motion);
+ g_assert (next);
+
+ grabbed_before_release = primary_modifier_still_pressed (display, grab_mask);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Activating target workspace\n");
+
+ if (!grabbed_before_release)
+ {
+ /* end the grab right away, modifier possibly released
+ * before we could establish the grab and receive the
+ * release event. Must end grab before we can switch
+ * spaces.
+ */
+ meta_display_end_grab_op (display, event->xkey.time);
+ }
+
+ meta_workspace_activate (next, event->xkey.time);
+
+ if (grabbed_before_release)
+ {
+ meta_ui_tab_popup_select (screen->tab_popup, (MetaTabEntryKey) next);
+
+ /* only after selecting proper space */
+ meta_ui_tab_popup_set_showing (screen->tab_popup, TRUE);
+ }
+ }
+}
+
+static void
+handle_set_spew_mark (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ meta_verbose ("-- MARK MARK MARK MARK --\n");
+}
+
+void
+meta_set_keybindings_disabled (gboolean setting)
+{
+ all_bindings_disabled = setting;
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Keybindings %s\n", all_bindings_disabled ? "disabled" : "enabled");
+}
+
+static void
+handle_run_terminal (MetaDisplay *display,
+ MetaScreen *screen,
+ MetaWindow *window,
+ XEvent *event,
+ MetaKeyBinding *binding)
+{
+ const char *command;
+ GError *err;
+
+ command = meta_prefs_get_terminal_command ();
+
+ if (command == NULL)
+ {
+ char *s;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "No terminal command to run in response to "
+ "keybinding press\n");
+
+ s = g_strdup_printf (_("No terminal command has been defined.\n"));
+ error_on_command (-1, NULL, s, screen->number, event->xkey.time);
+ g_free (s);
+
+ return;
+ }
+
+ err = NULL;
+ if (!meta_spawn_command_line_async_on_screen (command, screen, &err))
+ {
+ error_on_command (-1, command, err->message, screen->number,
+ event->xkey.time);
+
+ g_error_free (err);
+ }
+}