1
0
Fork 0
mutter-performance-source/src/keybindings.c

2673 lines
76 KiB
C
Raw Normal View History

2001-06-06 04:47:37 +00:00
/* Metacity Keybindings */
/*
* Copyright (C) 2001 Havoc Pennington, 2002 Red Hat Inc.
2001-06-06 04:47:37 +00:00
*
* 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 <config.h>
2001-06-06 04:47:37 +00:00
#include "keybindings.h"
#include "workspace.h"
#include "errors.h"
2001-06-23 05:49:35 +00:00
#include "ui.h"
#include "frame.h"
2001-07-12 05:53:56 +00:00
#include "place.h"
#include "prefs.h"
2001-06-06 04:47:37 +00:00
#include <X11/keysym.h>
#include <string.h>
2001-06-06 04:47:37 +00:00
static gboolean all_bindings_disabled = FALSE;
2001-06-06 04:47:37 +00:00
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_cycle_forward (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);
static void handle_run_command (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);
2001-06-06 04:47:37 +00:00
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;
2001-06-06 04:47:37 +00:00
struct _MetaKeyBinding
{
const char *name;
2001-06-06 04:47:37 +00:00
KeySym keysym;
unsigned int mask;
2001-06-06 04:47:37 +00:00
int keycode;
MetaVirtualModifier modifiers;
const MetaKeyHandler *handler;
2001-06-06 04:47:37 +00:00
};
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_CYCLE_WINDOWS, handle_cycle_forward,
GINT_TO_POINTER (META_TAB_LIST_NORMAL) },
{ META_KEYBINDING_CYCLE_PANELS, handle_cycle_forward,
GINT_TO_POINTER (META_TAB_LIST_DOCKS) },
{ META_KEYBINDING_SHOW_DESKTOP, handle_toggle_desktop,
NULL },
{ META_KEYBINDING_COMMAND_1, handle_run_command,
GINT_TO_POINTER (0) },
{ META_KEYBINDING_COMMAND_2, handle_run_command,
GINT_TO_POINTER (1) },
{ META_KEYBINDING_COMMAND_3, handle_run_command,
GINT_TO_POINTER (2) },
{ META_KEYBINDING_COMMAND_4, handle_run_command,
GINT_TO_POINTER (3) },
{ META_KEYBINDING_COMMAND_5, handle_run_command,
GINT_TO_POINTER (4) },
{ META_KEYBINDING_COMMAND_6, handle_run_command,
GINT_TO_POINTER (5) },
{ META_KEYBINDING_COMMAND_7, handle_run_command,
GINT_TO_POINTER (6) },
{ META_KEYBINDING_COMMAND_8, handle_run_command,
GINT_TO_POINTER (7) },
{ META_KEYBINDING_COMMAND_9, handle_run_command,
GINT_TO_POINTER (8) },
{ META_KEYBINDING_COMMAND_10, handle_run_command,
GINT_TO_POINTER (9) },
{ META_KEYBINDING_COMMAND_11, handle_run_command,
GINT_TO_POINTER (10) },
{ META_KEYBINDING_COMMAND_12, handle_run_command,
GINT_TO_POINTER (11) },
{ 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 }
2001-06-23 05:49:35 +00:00
};
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)
2001-06-06 04:47:37 +00:00
{
XModifierKeymap *modmap;
int map_size;
2001-06-06 04:47:37 +00:00
int i;
if (display->modmap)
XFreeModifiermap (display->modmap);
2001-06-06 04:47:37 +00:00
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)
2001-06-06 04:47:37 +00:00
{
/* 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));
}
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;
}
}
2001-06-06 04:47:37 +00:00
++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->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
devirtualize_modifiers (MetaDisplay *display,
MetaVirtualModifier modifiers,
unsigned int *mask)
{
*mask = 0;
if (modifiers & META_VIRTUAL_SHIFT_MASK)
*mask |= ShiftMask;
if (modifiers & META_VIRTUAL_CONTROL_MASK)
*mask |= ControlMask;
if (modifiers & META_VIRTUAL_ALT_MASK)
*mask |= Mod1Mask;
if (modifiers & META_VIRTUAL_META_MASK)
*mask |= display->meta_mask;
if (modifiers & META_VIRTUAL_HYPER_MASK)
*mask |= display->hyper_mask;
if (modifiers & META_VIRTUAL_SUPER_MASK)
*mask |= display->super_mask;
if (modifiers & META_VIRTUAL_MOD2_MASK)
*mask |= Mod2Mask;
if (modifiers & META_VIRTUAL_MOD3_MASK)
*mask |= Mod3Mask;
if (modifiers & META_VIRTUAL_MOD4_MASK)
*mask |= Mod4Mask;
if (modifiers & META_VIRTUAL_MOD5_MASK)
*mask |= Mod5Mask;
}
static void
reload_modifiers (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)
{
devirtualize_modifiers (display,
display->screen_bindings[i].modifiers,
&display->screen_bindings[i].mask);
++i;
}
}
if (display->window_bindings)
{
int i;
i = 0;
while (display->window_bindings[i].keysym != None)
{
devirtualize_modifiers (display,
display->window_bindings[i].modifiers,
&display->window_bindings[i].mask);
++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].modifiers = prefs[src].modifiers;
display->screen_bindings[dest].mask = 0;
display->screen_bindings[dest].keycode = 0;
++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].modifiers = prefs[src].modifiers;
display->window_bindings[dest].mask = 0;
display->window_bindings[dest].keycode = 0;
++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);
}
static MetaKeyBindingAction
display_get_keybinding_action (MetaDisplay *display,
unsigned int keysym,
unsigned long mask)
{
int i;
i = display->n_screen_bindings - 1;
while (i >= 0)
{
if (display->screen_bindings[i].keysym == keysym &&
display->screen_bindings[i].mask == mask)
{
return meta_prefs_get_keybinding_action (display->screen_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_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);
reload_modifiers (display);
regrab_screen_bindings (display);
break;
case META_PREF_WINDOW_KEYBINDINGS:
rebuild_window_binding_table (display);
reload_keycodes (display);
reload_modifiers (display);
regrab_window_bindings (display);
break;
default:
break;
}
}
2001-06-06 04:47:37 +00:00
void
2001-06-23 05:49:35 +00:00
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->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);
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)
{
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;
2001-06-23 05:49:35 +00:00
}
/* 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);
}
2001-06-23 05:49:35 +00:00
static void
grab_keys (MetaKeyBinding *bindings,
int n_bindings,
2001-06-23 05:49:35 +00:00
MetaDisplay *display,
Window xwindow)
2001-06-06 04:47:37 +00:00
{
int i;
g_assert (n_bindings == 0 || bindings != NULL);
2001-06-06 04:47:37 +00:00
i = 0;
while (i < n_bindings)
2001-06-06 04:47:37 +00:00
{
if (bindings[i].keycode != 0)
{
meta_grab_key (display, xwindow,
bindings[i].keysym,
bindings[i].keycode,
bindings[i].mask);
2001-06-06 04:47:37 +00:00
}
++i;
}
}
2001-06-23 05:49:35 +00:00
static void
ungrab_all_keys (MetaDisplay *display,
Window xwindow)
2001-06-06 04:47:37 +00:00
{
int result;
meta_error_trap_push (display);
2001-06-06 04:47:37 +00:00
XUngrabKey (display->xdisplay, AnyKey, AnyModifier,
xwindow);
2001-06-06 04:47:37 +00:00
result = meta_error_trap_pop (display);
if (result != Success)
meta_topic (META_DEBUG_KEYBINDINGS,
"Ungrabbing all keys on 0x%lx failed\n", xwindow);
2001-06-06 04:47:37 +00:00
}
void
meta_screen_grab_keys (MetaScreen *screen)
2001-06-23 05:49:35 +00:00
{
if (screen->all_keys_grabbed)
return;
if (screen->keys_grabbed)
return;
grab_keys (screen->display->screen_bindings,
screen->display->n_screen_bindings,
screen->display, screen->xroot);
screen->keys_grabbed = TRUE;
2001-06-23 05:49:35 +00:00
}
void
meta_screen_ungrab_keys (MetaScreen *screen)
{
if (screen->keys_grabbed)
{
ungrab_all_keys (screen->display, screen->xroot);
screen->keys_grabbed = FALSE;
}
2001-06-23 05:49:35 +00:00
}
void
meta_window_grab_keys (MetaWindow *window)
{
2001-07-11 06:22:00 +00:00
if (window->all_keys_grabbed)
return;
2001-06-23 05:49:35 +00:00
if (window->keys_grabbed)
{
if (window->frame && !window->grab_on_frame)
ungrab_all_keys (window->display, window->xwindow);
2001-06-23 05:49:35 +00:00
else if (window->frame == NULL &&
window->grab_on_frame)
; /* continue to regrab on client window */
else
return; /* already all good */
}
2001-06-23 06:54:28 +00:00
grab_keys (window->display->window_bindings,
window->display->n_window_bindings,
window->display,
2001-06-23 05:49:35 +00:00
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);
2001-06-23 05:49:35 +00:00
else if (!window->grab_on_frame)
ungrab_all_keys (window->display,
window->xwindow);
window->keys_grabbed = FALSE;
2001-06-23 05:49:35 +00:00
}
}
static gboolean
grab_keyboard (MetaDisplay *display,
Window xwindow)
{
int result;
/* 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_keyboard (MetaDisplay *display)
{
Time timestamp;
timestamp = meta_display_get_current_time (display);
meta_error_trap_push (display);
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_keyboard (screen->display, screen->xroot);
if (retval)
screen->all_keys_grabbed = TRUE;
else
meta_screen_grab_keys (screen);
return retval;
}
void
meta_screen_ungrab_all_keys (MetaScreen *screen)
{
if (screen->all_keys_grabbed)
{
ungrab_keyboard (screen->display);
screen->all_keys_grabbed = FALSE;
screen->keys_grabbed = FALSE;
/* Re-establish our standard bindings */
meta_screen_grab_keys (screen);
}
}
2001-07-11 06:22:00 +00:00
gboolean
meta_window_grab_all_keys (MetaWindow *window)
{
Window grabwindow;
gboolean retval;
2001-07-11 06:22:00 +00:00
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));
2001-07-11 06:22:00 +00:00
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);
if (retval)
2001-07-11 06:22:00 +00:00
{
window->keys_grabbed = FALSE;
window->all_keys_grabbed = TRUE;
window->grab_on_frame = window->frame != NULL;
2001-07-11 06:22:00 +00:00
}
return retval;
2001-07-11 06:22:00 +00:00
}
void
meta_window_ungrab_all_keys (MetaWindow *window)
2001-07-11 06:22:00 +00:00
{
if (window->all_keys_grabbed)
{
ungrab_keyboard (window->display);
2001-07-11 06:22:00 +00:00
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;
2001-07-11 06:22:00 +00:00
}
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;
}
2001-06-23 05:49:35 +00:00
static void
process_event (MetaKeyBinding *bindings,
int n_bindings,
const MetaKeyHandler *handlers,
MetaDisplay *display,
MetaWindow *window,
XEvent *event,
KeySym keysym)
2001-06-06 04:47:37 +00:00
{
int i;
/* we used to have release-based bindings but no longer. */
if (event->type == KeyRelease)
return;
2001-06-06 04:47:37 +00:00
i = 0;
while (i < n_bindings)
2001-06-06 04:47:37 +00:00
{
if (bindings[i].keysym == keysym &&
((event->xkey.state & ~(display->ignored_modifier_mask)) ==
2001-06-24 02:22:10 +00:00
bindings[i].mask) &&
event->type == KeyPress)
2001-06-06 04:47:37 +00:00
{
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;
2001-06-06 04:47:37 +00:00
}
++i;
}
meta_topic (META_DEBUG_KEYBINDINGS,
"No handler found for this event in this binding table\n");
2001-06-06 04:47:37 +00:00
}
2001-06-23 05:49:35 +00:00
void
2001-06-24 02:22:10 +00:00
meta_display_process_key_event (MetaDisplay *display,
2001-06-23 05:49:35 +00:00
MetaWindow *window,
XEvent *event)
{
2001-07-11 06:22:00 +00:00
KeySym keysym;
gboolean handled;
gboolean all_keys_grabbed;
const char *str;
MetaScreen *screen;
2001-07-11 06:22:00 +00:00
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 */
2001-07-11 06:22:00 +00:00
keysym = XKeycodeToKeysym (display->xdisplay, event->xkey.keycode, 0);
str = XKeysymToString (keysym);
2001-07-11 06:22:00 +00:00
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)");
2001-07-11 06:22:00 +00:00
all_keys_grabbed = window ? window->all_keys_grabbed : screen->all_keys_grabbed;
if (!all_keys_grabbed)
2001-07-11 06:22:00 +00:00
{
/* Do the normal keybindings */
process_event (display->screen_bindings,
display->n_screen_bindings,
screen_handlers,
display, NULL, event, keysym);
2001-07-26 03:58:24 +00:00
if (window)
process_event (display->window_bindings,
display->n_window_bindings,
window_handlers,
display, window, event, keysym);
2001-07-11 06:22:00 +00:00
return;
}
if (display->grab_op == META_GRAB_OP_NONE)
return;
2001-07-11 06:22:00 +00:00
/* If we get here we have a global grab, because
* we're in some special keyboard mode such as window move
* mode.
*/
handled = FALSE;
2001-07-26 03:14:45 +00:00
if (window ? (window == display->grab_window) :
(screen == display->grab_screen))
2001-07-11 06:22:00 +00:00
{
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:
case META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL:
case META_GRAB_OP_KEYBOARD_ESCAPING_DOCK:
meta_topic (META_DEBUG_KEYBINDINGS,
"Processing event for keyboard tabbing/cycling\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;
2001-07-11 06:22:00 +00:00
/* don't care about releases, but eat them, don't end grab */
if (event->type == KeyRelease)
return TRUE;
2001-07-11 06:22:00 +00:00
/* 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;
2001-07-11 06:22:00 +00:00
#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;
2001-07-12 05:53:56 +00:00
/* 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.
*/
2001-07-11 06:22:00 +00:00
switch (keysym)
{
case XK_Up:
case XK_KP_Up:
edge = meta_window_find_next_horizontal_edge (window, FALSE);
y -= incr;
2001-07-12 05:53:56 +00:00
if (smart_snap || ((edge > y) && ABS (edge - y) < incr))
y = edge;
2001-07-12 05:53:56 +00:00
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;
2001-07-12 05:53:56 +00:00
handled = TRUE;
break;
case XK_Left:
case XK_KP_Left:
edge = meta_window_find_next_vertical_edge (window, FALSE);
x -= incr;
2001-07-12 05:53:56 +00:00
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;
}
2001-07-12 05:53:56 +00:00
if (handled)
meta_window_move (window, TRUE, x, y);
2001-07-11 06:22:00 +00:00
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:
switch (gravity)
{
case EastGravity:
case SouthEastGravity:
case NorthEastGravity:
/* Move left edge left */
edge = meta_window_find_next_vertical_edge (window, TRUE);
x -= width_inc;
if (smart_snap || ((edge > x) && ABS (edge - x) < width_inc))
x = edge;
width += (orig_x - x);
break;
case WestGravity:
case SouthWestGravity:
case NorthWestGravity:
/* Move right edge left */
edge = meta_window_find_next_vertical_edge (window, FALSE);
width -= width_inc;
if (smart_snap || ((edge > (x+width)) &&
ABS (edge - (x+width)) < width_inc))
width = edge - x;
handled = TRUE;
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 */
edge = meta_window_find_next_vertical_edge (window, FALSE);
x += width_inc;
if (smart_snap || ((edge < x) && ABS (edge - x) < width_inc))
x = edge;
width -= (x - orig_x);
break;
case WestGravity:
case SouthWestGravity:
case NorthWestGravity:
/* Move right edge right */
edge = meta_window_find_next_vertical_edge (window, TRUE);
width += width_inc;
if (smart_snap || ((edge > (x+width)) &&
ABS (edge - (x+width)) < width_inc))
width = edge - x;
handled = TRUE;
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)
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;
gboolean popup_not_showing;
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);
2001-07-26 03:14:45 +00:00
if (event->type == KeyRelease &&
keycode_is_primary_modifier (display, event->xkey.keycode,
display->grab_mask))
2001-07-11 06:22:00 +00:00
{
/* 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 = display_get_keybinding_action (display,
keysym,
display->grab_mask);
/* FIXME weird side effect here is that you can use the Escape
* key while tabbing, or the tab key while escaping
*/
popup_not_showing = FALSE;
switch (action)
{
case META_KEYBINDING_ACTION_CYCLE_PANELS:
case META_KEYBINDING_ACTION_CYCLE_WINDOWS:
popup_not_showing = TRUE;
/* FALL THRU */
case META_KEYBINDING_ACTION_SWITCH_PANELS:
case META_KEYBINDING_ACTION_SWITCH_WINDOWS:
meta_topic (META_DEBUG_KEYBINDINGS,
"Key pressed, moving tab focus in popup\n");
if (event->xkey.state & ShiftMask)
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;
target_xwindow =
(Window) meta_ui_tab_popup_get_selected (screen->tab_popup);
target_window =
meta_display_lookup_x_window (display, target_xwindow);
if (target_window)
{
meta_window_raise (target_window);
}
}
return TRUE;
break;
default:
break;
2001-07-11 06:22:00 +00:00
}
/* end grab */
meta_topic (META_DEBUG_KEYBINDINGS,
"Ending tabbing/cycling, uninteresting key pressed\n");
return FALSE;
2001-06-23 05:49:35 +00:00
}
2001-06-06 04:47:37 +00:00
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);
}
}
2001-06-06 04:47:37 +00:00
static void
handle_activate_workspace (MetaDisplay *display,
MetaWindow *event_window,
XEvent *event,
MetaKeyBinding *binding)
2001-06-06 04:47:37 +00:00
{
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);
2001-06-06 04:47:37 +00:00
}
else
{
/* We could offer to create it I suppose */
}
}
static void
error_on_command (int command_index,
const char *command,
const char *message)
{
GError *err;
char *argv[6];
char *key;
meta_warning ("Error on command %d \"%s\": %s\n",
command_index, command, message);
key = meta_prefs_get_gconf_key_for_command (command_index);
argv[0] = METACITY_LIBEXECDIR"/metacity-dialog";
argv[1] = "--command-failed-error";
argv[2] = key;
argv[3] = (char*) (command ? command : "");
argv[4] = (char*) message;
argv[5] = NULL;
err = NULL;
if (!g_spawn_async_with_pipes ("/",
argv,
NULL,
0,
NULL, NULL,
NULL,
NULL,
NULL,
NULL,
&err))
{
meta_warning (_("Error launching metacity-dialog to print an error about a command: %s\n"),
err->message);
g_error_free (err);
}
g_free (key);
}
static void
handle_run_command (MetaDisplay *display,
MetaWindow *window,
XEvent *event,
MetaKeyBinding *binding)
{
int which;
const char *command;
GError *err;
which = GPOINTER_TO_INT (binding->handler->data);
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);
g_free (s);
return;
}
err = NULL;
if (!g_spawn_command_line_async (command, &err))
{
error_on_command (which, command, err->message);
g_error_free (err);
}
}
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 = display_get_keybinding_action (display,
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);
}
2001-06-23 05:49:35 +00:00
static void
handle_activate_menu (MetaDisplay *display,
MetaWindow *event_window,
XEvent *event,
MetaKeyBinding *binding)
2001-06-23 05:49:35 +00:00
{
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);
}
}
2001-06-24 02:22:10 +00:00
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;
}
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;
}
g_assert_not_reached ();
return 0;
}
2001-06-24 02:22:10 +00:00
static void
do_choose_window (MetaDisplay *display,
MetaWindow *event_window,
XEvent *event,
MetaKeyBinding *binding,
gboolean show_popup)
2001-06-24 02:22:10 +00:00
{
2001-06-24 03:18:10 +00:00
MetaWindow *window;
MetaTabList type;
gboolean backward;
2001-06-24 03:18:10 +00:00
type = GPOINTER_TO_INT (binding->handler->data);
2001-06-24 02:22:10 +00:00
meta_topic (META_DEBUG_KEYBINDINGS,
"Tab list = %d show_popup = %d\n", type, show_popup);
2001-06-24 03:41:44 +00:00
/* backward if shift is down, this isn't configurable */
backward = (event->xkey.state & ShiftMask) != 0;
2001-06-24 02:22:10 +00:00
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);
2001-06-24 02:22:10 +00:00
}
2001-06-24 03:41:44 +00:00
2001-06-24 02:22:10 +00:00
if (window == NULL)
2001-06-24 03:18:10 +00:00
{
2001-06-24 03:41:44 +00:00
MetaScreen *screen;
screen = meta_display_screen_for_root (display,
event->xkey.root);
2001-06-24 03:18:10 +00:00
2001-06-24 03:41:44 +00:00
/* We get the screen because event_window may be NULL,
* in which case we can't use event_window->screen
*/
if (screen)
2001-06-24 03:18:10 +00:00
{
window = meta_display_get_tab_next (screen->display,
type,
screen,
screen->active_workspace,
NULL,
backward);
2001-06-24 03:18:10 +00:00
}
}
2001-06-24 02:22:10 +00:00
2001-06-24 03:41:44 +00:00
if (window)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Starting tab/cycle between windows\n");
if (meta_display_begin_grab_op (window->display,
window->screen,
display->focus_window ?
display->focus_window : window,
show_popup ?
tab_op_from_tab_type (type) :
cycle_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);
if (show_popup)
meta_ui_tab_popup_set_showing (window->screen->tab_popup,
TRUE);
else
meta_window_raise (window);
}
2001-06-24 03:41:44 +00:00
}
2001-06-24 02:22:10 +00:00
}
2001-06-24 03:18:10 +00:00
static void
handle_tab_forward (MetaDisplay *display,
MetaWindow *event_window,
XEvent *event,
MetaKeyBinding *binding)
2001-06-24 03:18:10 +00:00
{
do_choose_window (display, event_window, event, binding, TRUE);
}
static void
handle_cycle_forward (MetaDisplay *display,
MetaWindow *event_window,
XEvent *event,
MetaKeyBinding *binding)
{
do_choose_window (display, event_window, event, binding, FALSE);
2001-06-24 03:18:10 +00:00
}
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)
{
if (window)
{
meta_window_raise (window);
meta_display_begin_grab_op (window->display,
window->screen,
window,
META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
FALSE, 0, 0,
event->xkey.time,
0, 0);
}
}
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 space */
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");
}