mutter/src/keybindings.c

708 lines
20 KiB
C
Raw Normal View History

2001-06-06 00:47:37 -04:00
/* Metacity Keybindings */
/*
* Copyright (C) 2001 Havoc Pennington
*
* 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 "keybindings.h"
#include "workspace.h"
#include "errors.h"
2001-06-23 01:49:35 -04:00
#include "ui.h"
#include "frame.h"
2001-07-12 01:53:56 -04:00
#include "place.h"
2001-06-06 00:47:37 -04:00
#include <X11/keysym.h>
/* Plainly we'll want some more configurable keybinding system
* eventually.
*/
typedef void (* MetaKeyHandler) (MetaDisplay *display,
2001-06-23 01:49:35 -04:00
MetaWindow *window,
2001-06-06 00:47:37 -04:00
XEvent *event,
gpointer data);
static void handle_activate_workspace (MetaDisplay *display,
2001-06-23 01:49:35 -04:00
MetaWindow *window,
XEvent *event,
gpointer data);
static void handle_activate_menu (MetaDisplay *display,
MetaWindow *window,
XEvent *event,
gpointer data);
2001-06-23 22:22:10 -04:00
static void handle_tab_forward (MetaDisplay *display,
MetaWindow *window,
XEvent *event,
gpointer data);
static void handle_tab_backward (MetaDisplay *display,
MetaWindow *window,
XEvent *event,
gpointer data);
2001-06-23 23:18:10 -04:00
static void handle_focus_previous (MetaDisplay *display,
MetaWindow *window,
XEvent *event,
gpointer data);
2001-06-06 00:47:37 -04:00
typedef struct _MetaKeyBinding MetaKeyBinding;
struct _MetaKeyBinding
{
KeySym keysym;
gulong mask;
2001-06-23 22:22:10 -04:00
int event_type;
2001-06-06 00:47:37 -04:00
MetaKeyHandler handler;
gpointer data;
int keycode;
};
#define INTERESTING_MODIFIERS (ShiftMask | ControlMask | Mod1Mask)
2001-06-23 01:49:35 -04:00
static MetaKeyBinding screen_bindings[] = {
2001-06-23 22:22:10 -04:00
{ XK_F1, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (0), 0 },
{ XK_F2, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (1), 0 },
{ XK_F3, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (2), 0 },
{ XK_F4, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (3), 0 },
{ XK_F5, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (4), 0 },
{ XK_F6, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (5), 0 },
{ XK_1, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (0), 0 },
{ XK_2, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (1), 0 },
{ XK_3, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (2), 0 },
{ XK_4, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (3), 0 },
{ XK_5, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (4), 0 },
{ XK_6, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (5), 0 },
2001-06-24 02:47:54 -04:00
{ XK_Tab, Mod1Mask, KeyPress, handle_tab_forward, NULL, 0 },
{ XK_ISO_Left_Tab, ShiftMask | Mod1Mask, KeyPress, handle_tab_backward, NULL, 0 },
{ XK_Tab, ShiftMask | Mod1Mask, KeyPress, handle_tab_backward, NULL, 0 },
{ XK_Escape, Mod1Mask, KeyPress, handle_focus_previous, NULL, 0 },
2001-06-23 22:22:10 -04:00
{ None, 0, 0, NULL, NULL, 0 }
2001-06-06 00:47:37 -04:00
};
2001-06-23 01:49:35 -04:00
static MetaKeyBinding window_bindings[] = {
2001-06-23 22:22:10 -04:00
{ XK_space, Mod1Mask, KeyPress, handle_activate_menu, NULL, 0 },
{ XK_Tab, Mod1Mask, KeyPress, handle_tab_forward, NULL, 0 },
{ XK_ISO_Left_Tab, ShiftMask | Mod1Mask, KeyPress, handle_tab_backward, NULL, 0 },
{ XK_Tab, ShiftMask | Mod1Mask, KeyPress, handle_tab_backward, NULL, 0 },
2001-06-23 23:18:10 -04:00
{ XK_Escape, Mod1Mask, KeyPress, handle_focus_previous, NULL, 0 },
2001-06-23 22:22:10 -04:00
{ None, 0, 0, NULL, NULL, 0 }
2001-06-23 01:49:35 -04:00
};
static void
init_bindings (MetaDisplay *display,
MetaKeyBinding *bindings)
2001-06-06 00:47:37 -04:00
{
int i;
i = 0;
2001-06-23 01:49:35 -04:00
while (bindings[i].keysym != None)
2001-06-06 00:47:37 -04:00
{
bindings[i].keycode = XKeysymToKeycode (display->xdisplay,
bindings[i].keysym);
++i;
}
2001-06-23 01:49:35 -04:00
}
2001-06-06 00:47:37 -04:00
void
2001-06-23 01:49:35 -04:00
meta_display_init_keys (MetaDisplay *display)
{
init_bindings (display, screen_bindings);
init_bindings (display, window_bindings);
}
static void
grab_keys (MetaKeyBinding *bindings,
MetaDisplay *display,
Window xwindow)
2001-06-06 00:47:37 -04:00
{
int i;
i = 0;
2001-06-23 01:49:35 -04:00
while (bindings[i].keysym != None)
2001-06-06 00:47:37 -04:00
{
if (bindings[i].keycode != 0)
{
int result;
2001-06-23 01:49:35 -04:00
meta_error_trap_push (display);
XGrabKey (display->xdisplay, bindings[i].keycode,
bindings[i].mask, xwindow, True,
2001-06-06 00:47:37 -04:00
GrabModeAsync, GrabModeAsync);
2001-06-23 01:49:35 -04:00
result = meta_error_trap_pop (display);
2001-06-06 00:47:37 -04:00
if (result != Success)
{
const char *name;
name = XKeysymToString (bindings[i].keysym);
if (name == NULL)
name = "(unknown)";
if (result == BadAccess)
meta_warning (_("Some other program is already using the key %s as a binding\n"), name);
}
}
++i;
}
}
2001-06-23 01:49:35 -04:00
static void
ungrab_keys (MetaKeyBinding *bindings,
MetaDisplay *display,
Window xwindow)
2001-06-06 00:47:37 -04:00
{
int i;
i = 0;
2001-06-23 01:49:35 -04:00
while (bindings[i].keysym != None)
2001-06-06 00:47:37 -04:00
{
if (bindings[i].keycode != 0)
{
2001-06-23 01:49:35 -04:00
meta_error_trap_push (display);
XUngrabKey (display->xdisplay, bindings[i].keycode,
bindings[i].mask, xwindow);
meta_error_trap_pop (display);
2001-06-06 00:47:37 -04:00
}
++i;
}
}
void
2001-06-23 01:49:35 -04:00
meta_screen_grab_keys (MetaScreen *screen)
{
grab_keys (screen_bindings, screen->display, screen->xroot);
}
void
meta_screen_ungrab_keys (MetaScreen *screen)
{
ungrab_keys (screen_bindings, screen->display, screen->xroot);
}
void
meta_window_grab_keys (MetaWindow *window)
{
2001-07-11 02:22:00 -04:00
if (window->all_keys_grabbed)
return;
2001-06-23 01:49:35 -04:00
if (window->keys_grabbed)
{
if (window->frame && !window->grab_on_frame)
ungrab_keys (window_bindings, window->display,
window->xwindow);
else if (window->frame == NULL &&
window->grab_on_frame)
; /* continue to regrab on client window */
else
return; /* already all good */
}
2001-06-23 02:54:28 -04:00
/* no keybindings for Emacs ;-) */
if (window->res_class &&
g_strcasecmp (window->res_class, "Emacs") == 0)
return;
2001-06-23 01:49:35 -04:00
grab_keys (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_keys (window_bindings, window->display,
window->frame->xwindow);
else if (!window->grab_on_frame)
ungrab_keys (window_bindings, window->display,
window->xwindow);
}
}
2001-07-11 02:22:00 -04:00
gboolean
meta_window_grab_all_keys (MetaWindow *window)
{
int result;
Window grabwindow;
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_window_focus (window, CurrentTime);
grabwindow = window->frame ? window->frame->xwindow : window->xwindow;
meta_error_trap_push (window->display);
XGrabKey (window->display->xdisplay, AnyKey, AnyModifier,
grabwindow, True,
GrabModeAsync, GrabModeAsync);
result = meta_error_trap_pop (window->display);
if (result != Success)
{
meta_verbose ("Global key grab failed for window %s\n", window->desc);
return FALSE;
}
else
{
window->keys_grabbed = FALSE;
window->all_keys_grabbed = TRUE;
window->grab_on_frame = window->frame != NULL;
return TRUE;
}
}
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;
meta_error_trap_push (window->display);
XUngrabKey (window->display->xdisplay,
AnyKey, AnyModifier,
grabwindow);
meta_error_trap_pop (window->display);
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;
XModifierKeymap *mod_keymap;
gboolean retval = FALSE;
/* FIXME this is ass-slow, cache the modmap */
mod_keymap = XGetModifierMapping (display->xdisplay);
map_size = 8 * mod_keymap->max_keypermod;
i = 0;
while (i < map_size) {
if (keycode == mod_keymap->modifiermap[i]) {
retval = TRUE;
break;
}
++i;
}
XFreeModifiermap (mod_keymap);
return retval;
}
2001-06-23 01:49:35 -04:00
static void
process_event (MetaKeyBinding *bindings,
MetaDisplay *display,
MetaWindow *window,
2001-07-11 02:22:00 -04:00
XEvent *event,
KeySym keysym)
2001-06-06 00:47:37 -04:00
{
int i;
i = 0;
2001-06-23 01:49:35 -04:00
while (bindings[i].keysym != None)
2001-06-06 00:47:37 -04:00
{
if (bindings[i].keysym == keysym &&
((event->xkey.state & INTERESTING_MODIFIERS) ==
2001-06-23 22:22:10 -04:00
bindings[i].mask) &&
bindings[i].event_type == event->type)
2001-06-06 00:47:37 -04:00
{
2001-06-23 01:49:35 -04:00
(* bindings[i].handler) (display, window, event, bindings[i].data);
2001-06-06 00:47:37 -04:00
break;
}
++i;
}
}
2001-06-23 01:49:35 -04:00
void
2001-06-23 22:22:10 -04:00
meta_display_process_key_event (MetaDisplay *display,
2001-06-23 01:49:35 -04:00
MetaWindow *window,
XEvent *event)
{
2001-07-11 02:22:00 -04:00
KeySym keysym;
gboolean handled;
g_return_if_fail (window != NULL);
keysym = XKeycodeToKeysym (display->xdisplay, event->xkey.keycode, 0);
meta_verbose ("Processing key %s event, keysym: %s state: 0x%x window: %s\n",
event->type == KeyPress ? "press" : "release",
XKeysymToString (keysym), event->xkey.state,
window->desc);
if (!window->all_keys_grabbed)
{
/* Do the normal keybindings */
process_event (screen_bindings, display, window, event, keysym);
process_event (window_bindings, display, window, event, keysym);
return;
}
/* If we get here we have a global grab, because
* we're in some special keyboard mode such as window move
* mode.
*/
if (display->grab_op == META_GRAB_OP_NONE)
return;
/* don't end grabs on modifier key presses */
if (is_modifier (display, event->xkey.keycode))
return;
handled = FALSE;
if (display->grab_op == META_GRAB_OP_KEYBOARD_MOVING &&
display->grab_window == window)
{
int x, y;
int incr;
gboolean smart_snap;
2001-07-12 01:53:56 -04:00
int edge;
if (event->type == KeyRelease)
return; /* don't care about releases */
2001-07-11 02:22:00 -04:00
if (window == NULL)
meta_bug ("NULL window while META_GRAB_OP_MOVING\n");
meta_window_get_position (window, &x, &y);
2001-07-12 01:53:56 -04:00
smart_snap = (event->xkey.state & ShiftMask) != 0;
2001-07-11 02:22:00 -04:00
#define SMALL_INCREMENT 1
#define NORMAL_INCREMENT 10
if (smart_snap)
2001-07-12 01:53:56 -04:00
incr = 0;
2001-07-11 02:22:00 -04:00
else if (event->xkey.state & ControlMask)
incr = SMALL_INCREMENT;
else
incr = NORMAL_INCREMENT;
2001-07-12 01:53:56 -04: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 02:22:00 -04:00
switch (keysym)
{
case XK_Up:
2001-07-12 01:53:56 -04:00
case XK_KP_Up:
edge = meta_window_find_next_horizontal_edge (window, FALSE);
2001-07-11 02:22:00 -04:00
y -= incr;
2001-07-12 01:53:56 -04:00
if (smart_snap || ((edge > y) && ABS (edge - y) < incr))
y = edge;
2001-07-11 02:22:00 -04:00
handled = TRUE;
break;
case XK_Down:
2001-07-12 01:53:56 -04:00
case XK_KP_Down:
edge = meta_window_find_next_horizontal_edge (window, TRUE);
2001-07-11 02:22:00 -04:00
y += incr;
2001-07-12 01:53:56 -04:00
if (smart_snap || ((edge < y) && ABS (edge - y) < incr))
y = edge;
2001-07-11 02:22:00 -04:00
handled = TRUE;
break;
case XK_Left:
2001-07-12 01:53:56 -04:00
case XK_KP_Left:
edge = meta_window_find_next_vertical_edge (window, FALSE);
2001-07-11 02:22:00 -04:00
x -= incr;
2001-07-12 01:53:56 -04:00
if (smart_snap || ((edge > x) && ABS (edge - x) < incr))
x = edge;
2001-07-11 02:22:00 -04:00
handled = TRUE;
break;
case XK_Right:
2001-07-12 01:53:56 -04:00
case XK_KP_Right:
edge = meta_window_find_next_vertical_edge (window, TRUE);
2001-07-11 02:22:00 -04:00
x += incr;
2001-07-12 01:53:56 -04:00
if (smart_snap || ((edge < x) && ABS (edge - x) < incr))
x = edge;
2001-07-11 02:22:00 -04:00
handled = TRUE;
break;
2001-07-12 01:53:56 -04:00
case XK_Escape:
/* End move and restore to original position */
meta_window_move_resize (display->grab_window,
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;
2001-07-11 02:22:00 -04:00
default:
break;
}
if (handled)
meta_window_move (window, x, y);
}
/* end grab if a key that isn't used gets pressed */
if (!handled)
{
meta_verbose ("Ending grab op %d on key press event sym %s\n",
display->grab_op, XKeysymToString (keysym));
meta_display_end_grab_op (display, event->xkey.time);
}
2001-06-23 01:49:35 -04:00
}
2001-06-06 00:47:37 -04:00
static void
handle_activate_workspace (MetaDisplay *display,
2001-06-23 23:18:10 -04:00
MetaWindow *event_window,
2001-06-06 00:47:37 -04:00
XEvent *event,
gpointer data)
{
int which;
MetaWorkspace *workspace;
which = GPOINTER_TO_INT (data);
workspace = meta_display_get_workspace_by_index (display, which);
if (workspace)
{
2001-07-03 22:10:54 -04:00
Window move_frame;
MetaWindow *move_window;
move_window = NULL;
move_frame = meta_ui_get_moving_frame (workspace->screen->ui);
if (move_frame != None)
{
move_window = meta_display_lookup_x_window (display, move_frame);
if (move_window == NULL ||
move_window->frame == NULL)
meta_bug ("No move_frame window 0x%lx!\n", move_frame);
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);
}
}
2001-06-06 00:47:37 -04:00
meta_workspace_activate (workspace);
2001-07-03 22:10:54 -04:00
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 00:47:37 -04:00
}
else
{
/* We could offer to create it I suppose */
}
}
2001-06-23 01:49:35 -04:00
static void
handle_activate_menu (MetaDisplay *display,
2001-06-23 23:18:10 -04:00
MetaWindow *event_window,
2001-06-23 01:49:35 -04:00
XEvent *event,
gpointer data)
{
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-23 22:22:10 -04:00
static void
handle_tab_forward (MetaDisplay *display,
2001-06-23 23:18:10 -04:00
MetaWindow *event_window,
2001-06-23 22:22:10 -04:00
XEvent *event,
gpointer data)
{
2001-06-23 23:18:10 -04:00
MetaWindow *window;
2001-06-23 22:22:10 -04:00
meta_verbose ("Tab forward\n");
window = NULL;
if (display->focus_window != NULL)
{
2001-06-23 23:41:44 -04:00
window = meta_stack_get_tab_next (display->focus_window->screen->stack,
display->focus_window,
FALSE);
2001-06-23 22:22:10 -04:00
}
if (window == NULL)
2001-06-23 23:18:10 -04:00
{
2001-06-23 23:41:44 -04:00
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)
2001-06-23 23:18:10 -04:00
{
2001-06-23 23:41:44 -04:00
window = meta_stack_get_tab_next (screen->stack,
event_window,
FALSE);
2001-06-23 23:18:10 -04:00
}
}
2001-06-23 22:22:10 -04:00
2001-06-23 23:41:44 -04:00
if (window)
{
meta_window_raise (window);
meta_window_focus (window, event->xkey.time);
}
2001-06-23 22:22:10 -04:00
}
static void
handle_tab_backward (MetaDisplay *display,
2001-06-23 23:18:10 -04:00
MetaWindow *event_window,
2001-06-23 22:22:10 -04:00
XEvent *event,
gpointer data)
{
2001-06-23 23:18:10 -04:00
MetaWindow *window;
2001-06-23 22:22:10 -04:00
meta_verbose ("Tab backward\n");
window = NULL;
if (display->focus_window != NULL)
{
2001-06-23 23:41:44 -04:00
window = meta_stack_get_tab_next (display->focus_window->screen->stack,
display->focus_window,
TRUE);
2001-06-23 22:22:10 -04:00
}
2001-06-23 23:41:44 -04:00
2001-06-23 22:22:10 -04:00
if (window == NULL)
2001-06-23 23:18:10 -04:00
{
2001-06-23 23:41:44 -04:00
MetaScreen *screen;
screen = meta_display_screen_for_root (display,
event->xkey.root);
2001-06-23 23:18:10 -04:00
2001-06-23 23:41:44 -04: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-23 23:18:10 -04:00
{
2001-06-23 23:41:44 -04:00
window = meta_stack_get_tab_next (screen->stack,
event_window,
TRUE);
2001-06-23 23:18:10 -04:00
}
}
2001-06-23 22:22:10 -04:00
2001-06-23 23:41:44 -04:00
if (window)
{
meta_window_raise (window);
meta_window_focus (window, event->xkey.time);
}
2001-06-23 22:22:10 -04:00
}
2001-06-23 23:18:10 -04:00
static void
handle_focus_previous (MetaDisplay *display,
MetaWindow *event_window,
XEvent *event,
gpointer data)
{
MetaWindow *window;
meta_verbose ("Focus previous window\n");
window = display->prev_focus_window;
2001-06-23 23:41:44 -04:00
if (window == NULL)
{
/* Pick first window in tab order */
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_stack_get_tab_next (screen->stack,
event_window,
TRUE);
}
}
2001-06-23 23:18:10 -04:00
if (window)
2001-06-23 23:41:44 -04:00
{
meta_window_raise (window);
meta_window_focus (window, event->xkey.time);
}
2001-06-23 23:18:10 -04:00
}