/* Metacity Keybindings */ /* * Copyright (C) 2001 Havoc Pennington, 2002 Red Hat Inc. * * 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. */ #include #include "keybindings.h" #include "workspace.h" #include "errors.h" #include "ui.h" #include "frame.h" #include "place.h" #include "prefs.h" #include #include static gboolean all_bindings_disabled = FALSE; typedef void (* MetaKeyHandlerFunc) (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_activate_workspace (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_activate_menu (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_tab_forward (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_focus_previous (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_toggle_fullscreen (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_toggle_desktop (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_toggle_maximize (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_toggle_shade (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_close_window (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_minimize_window (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_begin_move (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_begin_resize (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_toggle_sticky (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_move_to_workspace (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_workspace_switch (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static void handle_raise_or_lower (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); /* debug */ static void handle_spew_mark (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding); static gboolean process_keyboard_move_grab (MetaDisplay *display, MetaWindow *window, XEvent *event, KeySym keysym); static gboolean process_keyboard_resize_grab (MetaDisplay *display, MetaWindow *window, XEvent *event, KeySym keysym); static gboolean process_tab_grab (MetaDisplay *display, MetaWindow *window, XEvent *event, KeySym keysym); static gboolean process_workspace_switch_grab (MetaDisplay *display, XEvent *event, KeySym keysym); static void regrab_screen_bindings (MetaDisplay *display); static void regrab_window_bindings (MetaDisplay *display); typedef struct { const char *name; MetaKeyHandlerFunc func; void *data; } MetaKeyHandler; struct _MetaKeyBinding { const char *name; KeySym keysym; unsigned int mask; int keycode; const MetaKeyHandler *handler; }; static const MetaKeyHandler screen_handlers[] = { { META_KEYBINDING_WORKSPACE_1, handle_activate_workspace, GINT_TO_POINTER (0) }, { META_KEYBINDING_WORKSPACE_2, handle_activate_workspace, GINT_TO_POINTER (1) }, { META_KEYBINDING_WORKSPACE_3, handle_activate_workspace, GINT_TO_POINTER (2) }, { META_KEYBINDING_WORKSPACE_4, handle_activate_workspace, GINT_TO_POINTER (3) }, { META_KEYBINDING_WORKSPACE_5, handle_activate_workspace, GINT_TO_POINTER (4) }, { META_KEYBINDING_WORKSPACE_6, handle_activate_workspace, GINT_TO_POINTER (5) }, { META_KEYBINDING_WORKSPACE_7, handle_activate_workspace, GINT_TO_POINTER (6) }, { META_KEYBINDING_WORKSPACE_8, handle_activate_workspace, GINT_TO_POINTER (7) }, { META_KEYBINDING_WORKSPACE_9, handle_activate_workspace, GINT_TO_POINTER (8) }, { META_KEYBINDING_WORKSPACE_10, handle_activate_workspace, GINT_TO_POINTER (9) }, { META_KEYBINDING_WORKSPACE_11, handle_activate_workspace, GINT_TO_POINTER (10) }, { META_KEYBINDING_WORKSPACE_12, handle_activate_workspace, GINT_TO_POINTER (11) }, { META_KEYBINDING_WORKSPACE_LEFT, handle_workspace_switch, GINT_TO_POINTER (META_MOTION_LEFT) }, { META_KEYBINDING_WORKSPACE_RIGHT, handle_workspace_switch, GINT_TO_POINTER (META_MOTION_RIGHT) }, { META_KEYBINDING_WORKSPACE_UP, handle_workspace_switch, GINT_TO_POINTER (META_MOTION_UP) }, { META_KEYBINDING_WORKSPACE_DOWN, handle_workspace_switch, GINT_TO_POINTER (META_MOTION_DOWN) }, { META_KEYBINDING_SWITCH_WINDOWS, handle_tab_forward, GINT_TO_POINTER (META_TAB_LIST_NORMAL) }, { META_KEYBINDING_SWITCH_PANELS, handle_tab_forward, GINT_TO_POINTER (META_TAB_LIST_DOCKS) }, { META_KEYBINDING_FOCUS_PREVIOUS, handle_focus_previous, NULL }, { META_KEYBINDING_SHOW_DESKTOP, handle_toggle_desktop, NULL }, { NULL, NULL, NULL } }; static const MetaKeyHandler window_handlers[] = { { META_KEYBINDING_WINDOW_MENU, handle_activate_menu, NULL }, { META_KEYBINDING_TOGGLE_FULLSCREEN, handle_toggle_fullscreen, NULL }, { META_KEYBINDING_TOGGLE_MAXIMIZE, handle_toggle_maximize, NULL }, { META_KEYBINDING_TOGGLE_SHADE, handle_toggle_shade, NULL }, { META_KEYBINDING_CLOSE, handle_close_window, NULL }, { META_KEYBINDING_MINIMIZE, handle_minimize_window, NULL }, { META_KEYBINDING_BEGIN_MOVE, handle_begin_move, }, { META_KEYBINDING_BEGIN_RESIZE, handle_begin_resize, }, { META_KEYBINDING_TOGGLE_STICKY, handle_toggle_sticky, }, { META_KEYBINDING_MOVE_WORKSPACE_1, handle_move_to_workspace, GINT_TO_POINTER (0) }, { META_KEYBINDING_MOVE_WORKSPACE_2, handle_move_to_workspace, GINT_TO_POINTER (1) }, { META_KEYBINDING_MOVE_WORKSPACE_3, handle_move_to_workspace, GINT_TO_POINTER (2) }, { META_KEYBINDING_MOVE_WORKSPACE_4, handle_move_to_workspace, GINT_TO_POINTER (3) }, { META_KEYBINDING_MOVE_WORKSPACE_5, handle_move_to_workspace, GINT_TO_POINTER (4) }, { META_KEYBINDING_MOVE_WORKSPACE_6, handle_move_to_workspace, GINT_TO_POINTER (5) }, { META_KEYBINDING_MOVE_WORKSPACE_7, handle_move_to_workspace, GINT_TO_POINTER (6) }, { META_KEYBINDING_MOVE_WORKSPACE_8, handle_move_to_workspace, GINT_TO_POINTER (7) }, { META_KEYBINDING_MOVE_WORKSPACE_9, handle_move_to_workspace, GINT_TO_POINTER (8) }, { META_KEYBINDING_MOVE_WORKSPACE_10, handle_move_to_workspace, GINT_TO_POINTER (9) }, { META_KEYBINDING_MOVE_WORKSPACE_11, handle_move_to_workspace, GINT_TO_POINTER (10) }, { META_KEYBINDING_MOVE_WORKSPACE_12, handle_move_to_workspace, GINT_TO_POINTER (11) }, { META_KEYBINDING_MOVE_WORKSPACE_LEFT, handle_move_to_workspace, GINT_TO_POINTER (META_MOTION_LEFT) }, { META_KEYBINDING_MOVE_WORKSPACE_RIGHT, handle_move_to_workspace, GINT_TO_POINTER (META_MOTION_RIGHT) }, { META_KEYBINDING_MOVE_WORKSPACE_UP, handle_move_to_workspace, GINT_TO_POINTER (META_MOTION_UP) }, { META_KEYBINDING_MOVE_WORKSPACE_DOWN, handle_move_to_workspace, GINT_TO_POINTER (META_MOTION_DOWN) }, { META_KEYBINDING_RAISE_OR_LOWER, handle_raise_or_lower, NULL}, { NULL, NULL, NULL } }; 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, &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; /* 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 : "(null)", (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)); } ++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\n", display->ignored_modifier_mask, display->num_lock_mask, display->scroll_lock_mask); } static void reload_keycodes (MetaDisplay *display) { meta_topic (META_DEBUG_KEYBINDINGS, "Reloading keycodes for binding tables\n"); if (display->screen_bindings) { int i; i = 0; while (display->screen_bindings[i].keysym != None) { display->screen_bindings[i].keycode = XKeysymToKeycode (display->xdisplay, display->screen_bindings[i].keysym); ++i; } } if (display->window_bindings) { int i; i = 0; while (display->window_bindings[i].keysym != None) { display->window_bindings[i].keycode = XKeysymToKeycode (display->xdisplay, display->window_bindings[i].keysym); ++i; } } } static void rebuild_screen_binding_table (MetaDisplay *display) { const MetaKeyPref *prefs; int n_bindings; int src, dest; meta_topic (META_DEBUG_KEYBINDINGS, "Rebuilding screen binding table from preferences\n"); meta_prefs_get_screen_bindings (&prefs, &n_bindings); g_free (display->screen_bindings); display->screen_bindings = g_new0 (MetaKeyBinding, n_bindings); src = 0; dest = 0; while (src < n_bindings) { if (prefs[src].keysym != None) { display->screen_bindings[dest].name = prefs[src].name; display->screen_bindings[dest].keysym = prefs[src].keysym; display->screen_bindings[dest].mask = prefs[src].mask; ++dest; } ++src; } display->n_screen_bindings = dest; meta_topic (META_DEBUG_KEYBINDINGS, "%d screen bindings in table\n", display->n_screen_bindings); } static void rebuild_window_binding_table (MetaDisplay *display) { const MetaKeyPref *prefs; int n_bindings; int src, dest; meta_topic (META_DEBUG_KEYBINDINGS, "Rebuilding window binding table from preferences\n"); meta_prefs_get_window_bindings (&prefs, &n_bindings); g_free (display->window_bindings); display->window_bindings = g_new0 (MetaKeyBinding, n_bindings); src = 0; dest = 0; while (src < n_bindings) { if (prefs[src].keysym != None) { display->window_bindings[dest].name = prefs[src].name; display->window_bindings[dest].keysym = prefs[src].keysym; display->window_bindings[dest].mask = prefs[src].mask; ++dest; } ++src; } display->n_window_bindings = dest; meta_topic (META_DEBUG_KEYBINDINGS, "%d window bindings in table\n", display->n_window_bindings); } static void regrab_screen_bindings (MetaDisplay *display) { GSList *tmp; tmp = display->screens; while (tmp != NULL) { MetaScreen *screen = tmp->data; meta_screen_ungrab_keys (screen); meta_screen_grab_keys (screen); tmp = tmp->next; } } static void regrab_window_bindings (MetaDisplay *display) { GSList *windows; GSList *tmp; 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; } g_slist_free (windows); } 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); regrab_screen_bindings (display); regrab_window_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_screen_bindings (display); regrab_window_bindings (display); } } static void bindings_changed_callback (MetaPreference pref, void *data) { MetaDisplay *display; display = data; switch (pref) { case META_PREF_SCREEN_KEYBINDINGS: rebuild_screen_binding_table (display); reload_keycodes (display); regrab_screen_bindings (display); break; case META_PREF_WINDOW_KEYBINDINGS: rebuild_window_binding_table (display); reload_keycodes (display); regrab_window_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->screen_bindings = NULL; display->n_screen_bindings = 0; display->window_bindings = NULL; display->n_window_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_window_binding_table (display); rebuild_screen_binding_table (display); reload_keycodes (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) { meta_prefs_remove_listener (bindings_changed_callback, display); if (display->keymap) meta_XFree (display->keymap); if (display->modmap) XFreeModifiermap (display->modmap); g_free (display->screen_bindings); g_free (display->window_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, int keycode, int modmask) { int result; 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 mask 0x%x on 0x%lx\n", grab ? "Grabbing" : "Ungrabbing", keysym_name (keysym), modmask, xwindow); ignored_mask = 0; while (ignored_mask < (int) 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; } meta_error_trap_push (display); if (grab) XGrabKey (display->xdisplay, keycode, modmask | ignored_mask, xwindow, True, GrabModeAsync, GrabModeSync); else XUngrabKey (display->xdisplay, keycode, modmask | ignored_mask, xwindow); result = meta_error_trap_pop (display); if (grab && result != Success) { if (result == BadAccess) meta_warning (_("Some other program is already using the key %s with modifiers %x as a binding\n"), keysym_name (keysym), modmask | ignored_mask); else meta_topic (META_DEBUG_KEYBINDINGS, "Failed to grab key %s with modifiers %x\n", keysym_name (keysym), modmask | ignored_mask); } ++ignored_mask; } } static void meta_grab_key (MetaDisplay *display, Window xwindow, int keysym, 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) { int i; g_assert (n_bindings == 0 || bindings != NULL); i = 0; while (i < n_bindings) { if (bindings[i].keycode != 0) { meta_grab_key (display, xwindow, bindings[i].keysym, bindings[i].keycode, bindings[i].mask); } ++i; } } static void ungrab_all_keys (MetaDisplay *display, Window xwindow) { int result; meta_error_trap_push (display); XUngrabKey (display->xdisplay, AnyKey, AnyModifier, xwindow); result = meta_error_trap_pop (display); if (result != Success) meta_topic (META_DEBUG_KEYBINDINGS, "Ungrabbing all keys on 0x%lx failed\n", xwindow); } void meta_screen_grab_keys (MetaScreen *screen) { if (screen->keys_grabbed) return; grab_keys (screen->display->screen_bindings, screen->display->n_screen_bindings, screen->display, screen->xroot); 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->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->window_bindings, window->display->n_window_bindings, window->display, window->frame ? window->frame->xwindow : window->xwindow); 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; } } static gboolean grab_all_keys_and_keyboard (MetaDisplay *display, Window xwindow) { int result; meta_error_trap_push (display); XGrabKey (display->xdisplay, AnyKey, AnyModifier, xwindow, True, GrabModeAsync, GrabModeAsync); result = meta_error_trap_pop (display); if (result != Success) { meta_topic (META_DEBUG_KEYBINDINGS, "Global key grab failed\n"); return FALSE; } /* Also grab the keyboard, so we get key releases and all key * presses */ meta_error_trap_push (display); XGrabKeyboard (display->xdisplay, xwindow, True, GrabModeAsync, GrabModeAsync, meta_display_get_current_time (display)); result = meta_error_trap_pop (display); if (result != Success) { meta_topic (META_DEBUG_KEYBINDINGS, "XGrabKeyboard() failed\n"); return FALSE; } meta_topic (META_DEBUG_KEYBINDINGS, "Grabbed all keys\n"); return TRUE; } static void ungrab_all_keys_and_keyboard (MetaDisplay *display, Window xwindow) { Time timestamp; timestamp = meta_display_get_current_time (display); meta_error_trap_push (display); XUngrabKey (display->xdisplay, AnyKey, AnyModifier, xwindow); meta_topic (META_DEBUG_KEYBINDINGS, "Ungrabbing keyboard with timestamp %lu\n", timestamp); XUngrabKeyboard (display->xdisplay, timestamp); meta_error_trap_pop (display); } gboolean meta_screen_grab_all_keys (MetaScreen *screen) { 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_all_keys_and_keyboard (screen->display, screen->xroot); if (retval) { screen->keys_grabbed = FALSE; screen->all_keys_grabbed = TRUE; } return retval; } void meta_screen_ungrab_all_keys (MetaScreen *screen) { if (screen->all_keys_grabbed) { ungrab_all_keys_and_keyboard (screen->display, screen->xroot); 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) { 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, meta_display_get_current_time (window->display)); grabwindow = window->frame ? window->frame->xwindow : window->xwindow; meta_topic (META_DEBUG_KEYBINDINGS, "Grabbing all keys on window %s\n", window->desc); retval = grab_all_keys_and_keyboard (window->display, grabwindow); 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) { if (window->all_keys_grabbed) { Window grabwindow; grabwindow = (window->frame && window->grab_on_frame) ? window->frame->xwindow : window->xwindow; ungrab_all_keys_and_keyboard (window->display, grabwindow); 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 gboolean keycode_is_primary_modifier (MetaDisplay *display, unsigned int keycode, 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; meta_topic (META_DEBUG_KEYBINDINGS, "Checking whether code 0x%x is the primary modifier of mask 0x%x\n", keycode, entire_binding_mask); i = 0; while (i < (int) G_N_ELEMENTS (masks)) { if (entire_binding_mask & masks[i]) return is_specific_modifier (display, keycode, masks[i]); ++i; } return FALSE; } 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 process_event (MetaKeyBinding *bindings, int n_bindings, const MetaKeyHandler *handlers, MetaDisplay *display, MetaWindow *window, XEvent *event, KeySym keysym) { int i; /* we used to have release-based bindings but no longer. */ if (event->type == KeyRelease) return; i = 0; while (i < n_bindings) { if (bindings[i].keysym == keysym && ((event->xkey.state & ~(display->ignored_modifier_mask)) == bindings[i].mask) && event->type == KeyPress) { const MetaKeyHandler *handler; if (bindings[i].handler) handler = bindings[i].handler; else { handler = find_handler (handlers, bindings[i].name); bindings[i].handler = handler; /* cache */ } 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); (* handler->func) (display, window, event, &bindings[i]); return; } ++i; } meta_topic (META_DEBUG_KEYBINDINGS, "No handler found for this event in this binding table\n"); } void meta_display_process_key_event (MetaDisplay *display, MetaWindow *window, XEvent *event) { KeySym keysym; gboolean handled; 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; screen = meta_display_screen_for_xwindow (display, event->xany.window); /* window may be NULL */ keysym = XKeycodeToKeysym (display->xdisplay, event->xkey.keycode, 0); str = XKeysymToString (keysym); meta_topic (META_DEBUG_KEYBINDINGS, "Processing key %s event, keysym: %s state: 0x%x window: %s\n", event->type == KeyPress ? "press" : "release", str ? str : "(null)", event->xkey.state, window ? window->desc : "(no window)"); all_keys_grabbed = window ? window->all_keys_grabbed : screen->all_keys_grabbed; if (!all_keys_grabbed) { /* Do the normal keybindings */ process_event (display->screen_bindings, display->n_screen_bindings, screen_handlers, display, NULL, event, keysym); if (window) process_event (display->window_bindings, display->n_window_bindings, window_handlers, display, window, event, keysym); return; } 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. */ handled = FALSE; if (window ? (window == display->grab_window) : (screen == display->grab_screen)) { switch (display->grab_op) { case META_GRAB_OP_KEYBOARD_MOVING: meta_topic (META_DEBUG_KEYBINDINGS, "Processing event for keyboard move\n"); g_assert (window != NULL); handled = process_keyboard_move_grab (display, 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); handled = process_keyboard_resize_grab (display, window, event, keysym); break; case META_GRAB_OP_KEYBOARD_TABBING_NORMAL: case META_GRAB_OP_KEYBOARD_TABBING_DOCK: meta_topic (META_DEBUG_KEYBINDINGS, "Processing event for keyboard tabbing\n"); g_assert (window != NULL); handled = process_tab_grab (display, window, event, keysym); break; case META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING: meta_topic (META_DEBUG_KEYBINDINGS, "Processing event for keyboard workspace switching\n"); handled = process_workspace_switch_grab (display, event, keysym); break; default: break; } } /* end grab if a key that isn't used gets pressed */ if (!handled) { meta_topic (META_DEBUG_KEYBINDINGS, "Ending grab op %d on key event sym %s\n", display->grab_op, XKeysymToString (keysym)); meta_display_end_grab_op (display, event->xkey.time); } } static gboolean process_keyboard_move_grab (MetaDisplay *display, MetaWindow *window, XEvent *event, KeySym keysym) { gboolean handled; int x, y; int incr; gboolean smart_snap; int edge; 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; 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 = 0; else if (event->xkey.state & ControlMask) incr = SMALL_INCREMENT; else incr = NORMAL_INCREMENT; /* 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_Up: case XK_KP_Up: edge = meta_window_find_next_horizontal_edge (window, FALSE); y -= incr; if (smart_snap || ((edge > y) && ABS (edge - y) < incr)) y = edge; handled = TRUE; break; case XK_Down: case XK_KP_Down: edge = meta_window_find_next_horizontal_edge (window, TRUE); y += incr; if (smart_snap || ((edge < y) && ABS (edge - y) < incr)) y = edge; handled = TRUE; break; case XK_Left: case XK_KP_Left: edge = meta_window_find_next_vertical_edge (window, FALSE); x -= incr; if (smart_snap || ((edge > x) && ABS (edge - x) < incr)) x = edge; handled = TRUE; break; case XK_Right: case XK_KP_Right: edge = meta_window_find_next_vertical_edge (window, TRUE); x += incr; if (smart_snap || ((edge < x) && ABS (edge - x) < incr)) x = edge; handled = TRUE; break; case XK_Escape: /* End move and restore to original position */ 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); break; default: break; } if (handled) meta_window_move (window, TRUE, x, y); return handled; } static gboolean process_keyboard_resize_grab (MetaDisplay *display, MetaWindow *window, XEvent *event, KeySym keysym) { gboolean handled; int height_inc; int width_inc; int x, y; int orig_x, orig_y; int width, height; gboolean smart_snap; int edge; 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 */ 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); return TRUE; } 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_SW; handled = TRUE; break; case XK_Right: case XK_KP_Right: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SE; 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_NW; handled = TRUE; break; case XK_Right: case XK_KP_Right: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NE; 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_NW; handled = TRUE; break; case XK_Down: case XK_KP_Down: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SW; 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_NE; handled = TRUE; break; case XK_Down: case XK_KP_Down: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SE; 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) return TRUE; meta_window_get_position (window, &orig_x, &orig_y); x = orig_x; y = orig_y; 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 = 0; width_inc = 0; } else if (event->xkey.state & ControlMask) { if (window->size_hints.width_inc > 1) width_inc = window->size_hints.width_inc; else width_inc = SMALL_INCREMENT; if (window->size_hints.height_inc > 1) height_inc = window->size_hints.height_inc; else height_inc = SMALL_INCREMENT; } else { if (window->size_hints.width_inc > 1) width_inc = window->size_hints.width_inc; else width_inc = NORMAL_INCREMENT; if (window->size_hints.height_inc > 1) height_inc = window->size_hints.height_inc; else height_inc = NORMAL_INCREMENT; } /* 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_Up: case XK_KP_Up: switch (gravity) { case NorthGravity: case NorthWestGravity: case NorthEastGravity: /* Move bottom edge up */ edge = meta_window_find_next_horizontal_edge (window, TRUE); height -= height_inc; if (smart_snap || ((edge > (y+height)) && ABS (edge - (y+height)) < height_inc)) height = edge - y; handled = TRUE; break; case SouthGravity: case SouthWestGravity: case SouthEastGravity: /* Move top edge up */ edge = meta_window_find_next_horizontal_edge (window, FALSE); y -= height_inc; if (smart_snap || ((edge > y) && ABS (edge - y) < height_inc)) y = edge; height += (orig_y - y); 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 */ edge = meta_window_find_next_horizontal_edge (window, TRUE); height += height_inc; if (smart_snap || ((edge < (y+height)) && ABS (edge - (y+height)) < height_inc)) height = edge - y; handled = TRUE; break; case SouthGravity: case SouthWestGravity: case SouthEastGravity: /* Move top edge down */ edge = meta_window_find_next_horizontal_edge (window, FALSE); y += height_inc; if (smart_snap || ((edge < y) && ABS (edge - y) < height_inc)) y = edge; height -= (y - orig_y); break; case EastGravity: case WestGravity: case CenterGravity: g_assert_not_reached (); break; } handled = TRUE; break; case XK_Left: case XK_KP_Left: /* FIXME */ break; case XK_Right: case XK_KP_Right: /* FIXME */ 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_window_move_resize (window, TRUE, x, y, width, height); return handled; } static gboolean process_tab_grab (MetaDisplay *display, MetaWindow *window, XEvent *event, KeySym keysym) { MetaScreen *screen; MetaKeyBindingAction action; window = NULL; /* be sure we don't use this, it's irrelevant */ screen = display->grab_window->screen; g_return_val_if_fail (screen->tab_popup != NULL, FALSE); if (event->type == KeyRelease && keycode_is_primary_modifier (display, event->xkey.keycode, display->grab_mask)) { /* 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) { 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); meta_topic (META_DEBUG_KEYBINDINGS, "Activating target window\n"); meta_topic (META_DEBUG_FOCUS, "Activating %s due to tab popup selection\n", target_window->desc); meta_window_activate (target_window, 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; action = meta_prefs_get_keybinding_action (keysym, display->grab_mask); switch (action) { case META_KEYBINDING_ACTION_SWITCH_PANELS: case META_KEYBINDING_ACTION_SWITCH_WINDOWS: if (event->xkey.state & ShiftMask) meta_ui_tab_popup_backward (screen->tab_popup); else meta_ui_tab_popup_forward (screen->tab_popup); /* continue grab */ meta_topic (META_DEBUG_KEYBINDINGS, "Tab key pressed, moving tab focus in popup\n"); return TRUE; break; default: break; } /* end grab */ meta_topic (META_DEBUG_KEYBINDINGS, "Ending tabbing, uninteresting key pressed\n"); return FALSE; } static void switch_to_workspace (MetaDisplay *display, MetaWorkspace *workspace) { MetaWindow *move_window; move_window = NULL; if (display->grab_op == META_GRAB_OP_MOVING) move_window = display->grab_window; if (move_window != NULL) { if (move_window->on_all_workspaces) move_window = NULL; /* don't move it after all */ /* We put the window on the new workspace, flip spaces, * then remove from old workspace, so the window * never gets unmapped and we maintain the button grab * on it. */ if (move_window) { if (!meta_workspace_contains_window (workspace, move_window)) meta_workspace_add_window (workspace, move_window); } } meta_workspace_activate (workspace); if (move_window) { /* Lamely rely on prepend */ g_assert (move_window->workspaces->data == workspace); while (move_window->workspaces->next) /* while list size > 1 */ meta_workspace_remove_window (move_window->workspaces->next->data, move_window); } } static void handle_activate_workspace (MetaDisplay *display, MetaWindow *event_window, XEvent *event, MetaKeyBinding *binding) { int which; MetaWorkspace *workspace; which = GPOINTER_TO_INT (binding->handler->data); workspace = NULL; if (which < 0) { MetaScreen *screen; screen = meta_display_screen_for_root (display, event->xkey.root); if (screen == NULL) return; workspace = meta_workspace_get_neighbor (screen->active_workspace, which); } else { workspace = meta_display_get_workspace_by_index (display, which); } if (workspace) { switch_to_workspace (display, workspace); } else { /* We could offer to create it I suppose */ } } static gboolean process_workspace_switch_grab (MetaDisplay *display, XEvent *event, KeySym keysym) { MetaScreen *screen; MetaWorkspace *workspace; screen = display->grab_screen; g_return_val_if_fail (screen->tab_popup != NULL, FALSE); if (event->type == KeyRelease && keycode_is_primary_modifier (display, event->xkey.keycode, display->grab_mask)) { /* 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) { meta_topic (META_DEBUG_KEYBINDINGS, "Ending grab early so we can focus the target workspace\n"); meta_display_end_grab_op (display, event->xkey.time); meta_topic (META_DEBUG_KEYBINDINGS, "Activating target workspace\n"); switch_to_workspace (display, target_workspace); 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; /* 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 = meta_prefs_get_keybinding_action (keysym, 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"); return TRUE; } } /* end grab */ meta_topic (META_DEBUG_KEYBINDINGS, "Ending workspace tabbing, uninteresting key pressed\n"); return FALSE; } static void handle_toggle_desktop (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { if (display->showing_desktop) meta_display_unshow_desktop (display); else meta_display_show_desktop (display); } static void handle_activate_menu (MetaDisplay *display, MetaWindow *event_window, XEvent *event, MetaKeyBinding *binding) { if (display->focus_window) { int x, y; meta_window_get_position (display->focus_window, &x, &y); meta_window_show_menu (display->focus_window, x, y, 0, event->xkey.time); } } static MetaGrabOp 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; } g_assert_not_reached (); return 0; } static void handle_tab_forward (MetaDisplay *display, MetaWindow *event_window, XEvent *event, MetaKeyBinding *binding) { MetaWindow *window; MetaTabList type; gboolean backward; type = GPOINTER_TO_INT (binding->handler->data); meta_topic (META_DEBUG_KEYBINDINGS, "Tab type = %d\n", type); /* backward if shift is down, this isn't configurable */ backward = (event->xkey.state & ShiftMask) != 0; window = NULL; if (display->focus_window != NULL) { window = meta_display_get_tab_next (display, type, display->focus_window->screen, display->focus_window->screen->active_workspace, display->focus_window, backward); } if (window == NULL) { MetaScreen *screen; screen = meta_display_screen_for_root (display, event->xkey.root); /* We get the screen because event_window may be NULL, * in which case we can't use event_window->screen */ if (screen) { window = meta_display_get_tab_next (screen->display, type, screen, screen->active_workspace, NULL, backward); } } if (window) { meta_topic (META_DEBUG_KEYBINDINGS, "Starting tab between windows, showing popup\n"); if (meta_display_begin_grab_op (window->display, window->screen, display->focus_window ? display->focus_window : window, op_from_tab_type (type), FALSE, 0, event->xkey.state & ~(display->ignored_modifier_mask), event->xkey.time, 0, 0)) { meta_ui_tab_popup_select (window->screen->tab_popup, (MetaTabEntryKey) window->xwindow); /* only after selecting proper window */ meta_ui_tab_popup_set_showing (window->screen->tab_popup, TRUE); } } } static MetaWindow * get_previous_focus_window (MetaDisplay *display, MetaScreen *screen) { MetaWindow *window = NULL; /* get previously-focused window, front of list is currently * focused window */ if (display->mru_list && display->mru_list->next) { window = display->mru_list->next->data; } if (window && !meta_window_visible_on_workspace (window, screen->active_workspace)) { window = NULL; } if (window == NULL) { /* Pick first window in tab order */ window = meta_display_get_tab_next (screen->display, META_TAB_LIST_NORMAL, screen, screen->active_workspace, NULL, TRUE); } if (window && !meta_window_visible_on_workspace (window, screen->active_workspace)) { window = NULL; } return window; } static void handle_focus_previous (MetaDisplay *display, MetaWindow *event_window, XEvent *event, MetaKeyBinding *binding) { MetaWindow *window; MetaScreen *screen; meta_topic (META_DEBUG_KEYBINDINGS, "Focus previous window\n"); screen = meta_display_screen_for_root (display, event->xkey.root); if (screen == NULL) return; window = get_previous_focus_window (display, screen); if (window) { meta_window_raise (window); meta_topic (META_DEBUG_FOCUS, "Focusing %s due to 'focus previous' keybinding\n", window->desc); meta_window_focus (window, event->xkey.time); } } static void handle_toggle_fullscreen (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { if (window) { if (window->fullscreen) meta_window_unmake_fullscreen (window); else meta_window_make_fullscreen (window); } } static void handle_toggle_maximize (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { if (window) { if (window->maximized) meta_window_unmaximize (window); else meta_window_maximize (window); } } static void handle_toggle_shade (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { if (window) { if (window->shaded) meta_window_unshade (window); else meta_window_shade (window); } } static void handle_close_window (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { if (window) if (window->has_close_func) meta_window_delete (window, event->xkey.time); } static void handle_minimize_window (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { if (window) if (window->has_minimize_func) meta_window_minimize (window); } static void handle_begin_move (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { if (window) { meta_window_raise (window); meta_display_begin_grab_op (window->display, window->screen, window, META_GRAB_OP_KEYBOARD_MOVING, FALSE, 0, 0, event->xkey.time, 0, 0); } } static void handle_begin_resize (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { /* FIXME */ } static void handle_toggle_sticky (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { if (window) { if (window->on_all_workspaces) meta_window_unstick (window); else meta_window_stick (window); } } static void handle_move_to_workspace (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { int which; MetaWorkspace *workspace; which = GPOINTER_TO_INT (binding->handler->data); if (window == NULL || window->always_sticky) return; workspace = NULL; if (which < 0) { MetaScreen *screen; screen = meta_display_screen_for_root (display, event->xkey.root); if (screen == NULL) return; workspace = meta_workspace_get_neighbor (screen->active_workspace, which); } else { workspace = meta_display_get_workspace_by_index (display, which); } if (workspace) { /* Activate second, so the window is never unmapped */ meta_window_change_workspace (window, workspace); meta_workspace_activate (workspace); } else { /* We could offer to create it I suppose */ } } static void handle_raise_or_lower (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { /* Get window at pointer */ MetaScreen *screen; screen = meta_display_screen_for_root (display, event->xbutton.root); if (screen == NULL) return; if (window) { 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; 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_workspace_switch (MetaDisplay *display, MetaWindow *window, XEvent *event, MetaKeyBinding *binding) { int motion; MetaScreen *screen; motion = GPOINTER_TO_INT (binding->handler->data); g_assert (motion < 0); screen = meta_display_screen_for_root (display, event->xkey.root); if (screen == NULL) return; meta_topic (META_DEBUG_KEYBINDINGS, "Starting tab between workspaces, showing popup\n"); if (meta_display_begin_grab_op (display, screen, NULL, META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING, FALSE, 0, event->xkey.state & ~(display->ignored_modifier_mask), event->xkey.time, 0, 0)) { MetaWorkspace *next; next = meta_workspace_get_neighbor (screen->active_workspace, motion); g_assert (next); meta_ui_tab_popup_select (screen->tab_popup, (MetaTabEntryKey) next); /* only after selecting proper window */ meta_ui_tab_popup_set_showing (screen->tab_popup, TRUE); } } static void handle_spew_mark (MetaDisplay *display, 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"); }