/*-
* Copyright (c) 2001 Jordan DeLong
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "wm.h"
/*
* because X11 treats the NumLock and ScrollLock as modifiers, the user's
* keybindings wont be grabbed when the locks are on without grabbing for
* those also. the masks are available here.
*/
u_int numlock_mask, scroll_mask;
/* our list of keys that we bind */
static SLIST_HEAD(, keybind) keys_list = SLIST_HEAD_INITIALIZER(keys_list);
/* shutdown keys; free allocated memory and the like */
void keys_shutdown() {
keybind_t *key, *next;
/* free memory allocated in our list of keys */
key = SLIST_FIRST(&keys_list);
while (key) {
/* some keybinding types have allocated memory */
switch (key->action) {
case KEY_COMMAND:
free(key->dat.cmd);
break;
case KEY_SETVIEWPORT:
free(key->dat.pt);
break;
}
/* free the keybind_t */
next = SLIST_NEXT(key, k_list);
free(key);
key = next;
}
SLIST_INIT(&keys_list);
}
/* add a key binding */
keybind_t *keys_add(int keycode, u_int modifiers, int action, void* dat) {
keybind_t *key;
key = malloc(sizeof(keybind_t));
if (!key)
return NULL;
key->keycode = keycode;
key->modifiers = modifiers;
key->action = action;
key->dat.generic = dat;
SLIST_INSERT_HEAD(&keys_list, key, k_list);
return key;
}
static __inline void gethackmods() {
static int masks[8] = {
ShiftMask, LockMask, ControlMask, Mod1Mask,
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
};
XModifierKeymap *modmap;
KeyCode ncode, scode;
int i;
/* determine modifier mappings for the below hack */
modmap = XGetModifierMapping(display);
if (modmap) {
numlock_mask = scroll_mask = 0;
ncode = XKeysymToKeycode(display, XK_Num_Lock);
scode = XKeysymToKeycode(display, XK_Scroll_Lock);
for (i = 0; i < 8 * modmap->max_keypermod; i++)
if (ncode && modmap->modifiermap[i] == ncode)
numlock_mask = masks[i / modmap->max_keypermod];
else if (scode && modmap->modifiermap[i] == scode)
scroll_mask = masks[i / modmap->max_keypermod];
XFreeModifiermap(modmap);
} else
numlock_mask = scroll_mask = 0;
}
static __inline void grabhack(Window win, int keycode, u_int modifiers) {
if (numlock_mask) {
XGrabKey(display, keycode, modifiers | numlock_mask,
win, 1, GrabModeAsync, GrabModeAsync);
XGrabKey(display, keycode, modifiers | numlock_mask | LockMask,
win, 1, GrabModeAsync, GrabModeAsync);
}
if (scroll_mask) {
XGrabKey(display, keycode, modifiers | scroll_mask,
win, 1, GrabModeAsync, GrabModeAsync);
XGrabKey(display, keycode, modifiers | scroll_mask | LockMask,
win, 1, GrabModeAsync, GrabModeAsync);
}
if (numlock_mask && scroll_mask) {
XGrabKey(display, keycode, modifiers | numlock_mask | scroll_mask,
win, 1, GrabModeAsync, GrabModeAsync);
XGrabKey(display, keycode, modifiers | numlock_mask | scroll_mask | LockMask,
win, 1, GrabModeAsync, GrabModeAsync);
}
XGrabKey(display, keycode, modifiers | LockMask, win, 1,
GrabModeAsync, GrabModeAsync);
XGrabKey(display, keycode, modifiers, win, 1,
GrabModeAsync, GrabModeAsync);
}
void buttongrabhack(Window win, int button, u_int modifiers) {
if (numlock_mask) {
XGrabButton(display, AnyButton, modifiers | numlock_mask, win,
1, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
XGrabButton(display, AnyButton, modifiers | numlock_mask | LockMask, win,
1, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
}
if (scroll_mask) {
XGrabButton(display, AnyButton, modifiers | scroll_mask, win,
1, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
XGrabButton(display, AnyButton, modifiers | scroll_mask | LockMask, win,
1, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
}
if (numlock_mask && scroll_mask) {
XGrabButton(display, AnyButton, modifiers | numlock_mask | scroll_mask,
win, 1, ButtonPressMask, GrabModeSync, GrabModeAsync,
None, None);
XGrabButton(display, AnyButton, modifiers | numlock_mask | scroll_mask | LockMask,
win, 1, ButtonPressMask, GrabModeSync, GrabModeAsync,
None, None);
}
XGrabButton(display, AnyButton, modifiers | LockMask, win, 1, ButtonPressMask,
GrabModeSync, GrabModeAsync, None, None);
XGrabButton(display, AnyButton, modifiers, win, 1, ButtonPressMask,
GrabModeSync, GrabModeAsync, None, None);
}
/* do grabs on the root window of screen */
void keys_grab(screen_t *screen) {
keybind_t *key;
gethackmods();
SLIST_FOREACH(key, &keys_list, k_list)
grabhack(screen->root, key->keycode, key->modifiers);
}
/* move the viewport */
static void keys_moveport(screen_t *screen, int dir) {
int vx, vy;
switch (dir) {
case MV_UP: vx = 0; vy = -1; break;
case MV_DOWN: vx = 0; vy = 1; break;
case MV_LEFT: vx = -1; vy = 0; break;
case MV_RIGHT: vx = 1; vy = 0; break;
case MV_UPRIGHT: vx = 1; vy = -1; break;
case MV_DOWNRIGHT: vx = 1; vy = 1; break;
case MV_DOWNLEFT: vx = -1; vy = 1; break;
default:
case MV_UPLEFT: vx = -1; vy = -1; break;
}
workspace_viewport_move(screen, screen->desktop, vx, vy);
}
/*
* focus and raise a win, we don't use focus_setfocused because
* when going across screens in the all or screen style cyclings
* it would try to just set workspace->focused pointers.
*/
#define FOCUSRAISE(foc) do { \
focus_client((foc)); \
stacking_raise(client_focused); \
} while (0)
/* forward loop through screens, check if foc to focus foc, for cycle */
#define SCREEN_FLOOP(foc, currentscr) do { \
screen = TAILQ_NEXT((currentscr), s_list); \
\
while (screen != (currentscr)) { \
if (!screen) { \
screen = TAILQ_FIRST(&screen_list); \
continue; \
} \
\
if ((foc)) { \
FOCUSRAISE((foc)); \
break; \
} \
screen = TAILQ_NEXT(screen, s_list); \
} \
} while (0)
/* backward loop through screens, like floop */
#define SCREEN_BLOOP(foc, currentscr) do { \
screen = TAILQ_PREV((currentscr), screenlist, s_list); \
\
while (screen != (currentscr)) { \
if (!screen) { \
screen = TAILQ_LAST(&screen_list, screenlist); \
continue; \
} \
\
if ((foc)) { \
FOCUSRAISE((foc)); \
break; \
} \
\
screen = TAILQ_PREV(screen, screenlist, s_list); \
} \
} while (0)
/* cycle screens */
static void keys_cyclescr(screen_t *keyscr, client_t *client, int cycletype) {
screen_t *screen;
screen_t *pointer_screen;
Window pointer_root;
Window dumwin;
int dumint;
/*
* screen-based cycling doesn't require visible client windows: it
* warps the pointer to new screens, and focused the focused window
* on the screen if there is one. The currently 'focused' screen
* is the one w/ the mouse pointer, because that is where keybinds
* will take effect. So event if the focused window is on a different
* screen we treat the mouse pointer screen as the focused screen.
*/
XQueryPointer(display, keyscr->root, &pointer_root, &dumwin, &dumint, &dumint,
&dumint, &dumint, &dumint);
TAILQ_FOREACH(pointer_screen, &screen_list, s_list)
if (pointer_screen->root == pointer_root)
break;
/*
* now we know which screen we're on; put it on the
* screen it needs to go to.
*/
switch (cycletype) {
case CF_FSCR:
screen = TAILQ_NEXT(pointer_screen, s_list);
if (!screen)
screen = TAILQ_FIRST(&screen_list);
/* only refocus if this isn't the screen of the focused window */
if (screen == pointer_screen)
goto warp;
if (screen->desktop->current_space->focused)
if (!client || client->screen != screen)
FOCUSRAISE(screen->desktop->current_space->focused);
case CF_BSCR:
default:
screen = TAILQ_PREV(pointer_screen, screenlist, s_list);
if (!screen)
screen = TAILQ_LAST(&screen_list, screenlist);
/* only refocus if this isn't the screen of the focused window */
if (screen == pointer_screen)
goto warp;
if (screen->desktop->current_space->focused)
if (!client || client->screen != screen)
FOCUSRAISE(screen->desktop->current_space->focused);
}
warp:
/* put pointer in the middle of the screen */
XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
screen->width / 2, screen->height / 2);
}
/*
* cycle input focus, slightly complicated becase we support several cycling
* types here (for multiscreen), see keys.h for info on the types.
*/
static void keys_cyclefoc(screen_t *keyscr, client_t *client, int cycletype) {
screen_t *screen;
/* screen based cycling; not strictly for just focus cycling */
if (cycletype == CF_FSCR || cycletype == CF_BSCR) {
keys_cyclescr(keyscr, client, cycletype);
return;
}
/* if no window is focused, this is easier */
if (!client) {
if (cycletype % 2 == 0) {
if (TAILQ_FIRST(&keyscr->desktop->current_space->w_foclist))
FOCUSRAISE(TAILQ_FIRST(&keyscr->desktop->current_space->w_foclist));
else if (cycletype == CF_FALL)
SCREEN_FLOOP(TAILQ_FIRST(&screen->desktop->current_space->w_foclist),
keyscr);
} else {
if (TAILQ_LAST(&keyscr->desktop->current_space->w_foclist, foclist))
FOCUSRAISE(TAILQ_LAST(&keyscr->desktop->current_space->w_foclist, foclist));
else if (cycletype == CF_BALL)
SCREEN_BLOOP(TAILQ_LAST(&screen->desktop->current_space->w_foclist, foclist),
keyscr);
}
return;
}
/* if the type is div by 2, it's a forward cycling type */
if (cycletype % 2 == 0) {
if (TAILQ_NEXT(client, c_focus)) {
FOCUSRAISE(TAILQ_NEXT(client, c_focus));
} else {
if (cycletype == CF_FALL)
SCREEN_FLOOP(TAILQ_FIRST(&screen->desktop->current_space->w_foclist),
client->screen);
else if (TAILQ_FIRST(&client->screen->desktop->current_space->w_foclist))
FOCUSRAISE(TAILQ_FIRST(&client->screen->desktop->current_space->w_foclist));
}
} else {
if (TAILQ_PREV(client, foclist, c_focus)) {
FOCUSRAISE(TAILQ_PREV(client, foclist, c_focus));
} else {
if (cycletype == CF_BALL)
SCREEN_BLOOP(TAILQ_LAST(&screen->desktop->current_space->w_foclist,
foclist), client->screen);
else if (TAILQ_LAST(&client->screen->desktop->current_space->w_foclist, foclist))
FOCUSRAISE(TAILQ_LAST(&client->screen->desktop->current_space->w_foclist, foclist));
}
}
}
#undef SCREEN_BLOOP
#undef SCREEN_FLOOP
#undef FOCUSRAISE
/* perform an action for a key */
static void keys_action(screen_t *screen, keybind_t *key) {
client_t *client = client_focused;
switch (key->action) {
case KEY_ICONIFY:
if (client)
action_iconify(client);
break;
case KEY_ZOOM:
if (client)
action_zoom(client);
break;
case KEY_SWITCHDESK:
desktop_switch(screen, key->dat.num);
break;
case KEY_MOVEVIEWPORT:
keys_moveport(screen, key->dat.dir);
break;
case KEY_SETVIEWPORT:
workspace_viewport_move(screen, screen->desktop,
key->dat.pt->x - screen->desktop->viewx,
key->dat.pt->y - screen->desktop->viewy);
break;
case KEY_COMMAND:
action_exec(screen->num, key->dat.cmd);
break;
case KEY_DELETE:
if (client && !client->flags.nodelete)
action_sendcmesg(client->window, WM_DELETE_WINDOW, CurrentTime);
break;
case KEY_CYCLEFOCUS:
keys_cyclefoc(screen, client, key->dat.cycletype);
break;
case KEY_RAISE:
if (client)
stacking_raise(client);
break;
case KEY_LOWER:
if (client)
stacking_lower(client);
break;
case KEY_DGROUPSWITCH:
if (client)
dgroup_switch(client, NULL);
break;
case KEY_STICKY:
if (client)
client->flags.sticky = ~client->flags.sticky;
break;
case KEY_ABORT:
if (!fork())
abort();
break;
case KEY_RESTART:
restart_bin = binary_name;
case KEY_EXIT:
restart_flag = 1;
break;
}
}
/* handles when we get keypresses from our grabs */
void keys_press(screen_t *screen, XKeyEvent *e) {
keybind_t *key;
SLIST_FOREACH(key, &keys_list, k_list)
if (e->keycode == key->keycode
&& (e->state & ~(numlock_mask |
scroll_mask | LockMask)) == key->modifiers) {
keys_action(screen, key);
return;
}
}
syntax highlighted by Code2HTML, v. 0.9.1