diff options
Diffstat (limited to 'src/key.c')
-rw-r--r-- | src/key.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/src/key.c b/src/key.c new file mode 100644 index 0000000..7a15ed0 --- /dev/null +++ b/src/key.c @@ -0,0 +1,371 @@ +/* + * \/\/\ + * + * Copyright (C) 2012-2016 Vito Caputo - <vcaputo@gnugeneration.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <X11/Xlib.h> +#include <X11/keysym.h> +#include <stdlib.h> + +#include "composite.h" +#include "desktop.h" +#include "launch.h" +#include "list.h" +#include "vwm.h" +#include "window.h" +#include "xwindow.h" + +static int key_is_grabbed; /* flag for tracking keyboard grab state */ + +/* Poll the keyboard state to see if _any_ keys are pressed */ +static int keys_pressed(vwm_t *vwm) { + int i; + char state[32]; + + XQueryKeymap(vwm->display, state); + + for (i = 0; i < sizeof(state); i++) { + if (state[i]) return 1; + } + + return 0; +} + +/* Called in response to KeyRelease events, for now only interesting for detecting when Mod1 is released termintaing + * window cycling for application of MRU on the focused window */ +void vwm_key_released(vwm_t *vwm, Window win, XKeyReleasedEvent *keyrelease) +{ + vwm_window_t *vwin; + KeySym sym; + + switch ((sym = XLookupKeysym(keyrelease, 0))) { + case XK_Alt_R: + case XK_Alt_L: /* TODO: actually use the modifier mapping, for me XK_Alt_[LR] is Mod1. XGetModifierMapping()... */ + VWM_TRACE("XK_Alt_[LR] released"); + + /* aborted? try restore focused_origin */ + if (key_is_grabbed > 1 && vwm->focused_origin) { + VWM_TRACE("restoring %p on %p", vwm->focused_origin, vwm->focused_origin->desktop); + vwm_desktop_focus(vwm, vwm->focused_origin->desktop); + vwm_win_focus(vwm, vwm->focused_origin); + } + + /* make the focused window the most recently used */ + if ((vwin = vwm_win_focused(vwm))) vwm_win_mru(vwm, vwin); + + /* make the focused desktop the most recently used */ + if (vwm->focused_context == VWM_CONTEXT_DESKTOP && vwm->focused_desktop) vwm_desktop_mru(vwm, vwm->focused_desktop); + + break; + + default: + VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym); + break; + } + + if (key_is_grabbed && !keys_pressed(vwm)) { + XUngrabKeyboard(vwm->display, CurrentTime); + XFlush(vwm->display); + key_is_grabbed = 0; + vwm->fence_mask = 0; /* reset the fence mask on release for VWM_FENCE_MASKED_VIOLATE */ + } +} + + +/* Called in response to KeyPress events, I currenly only grab Mod1 keypress events */ +void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress) +{ + vwm_window_t *vwin; + KeySym sym; + static KeySym last_sym; + static typeof(keypress->state) last_state; + static int repeat_cnt = 0; + int do_grab = 0; + char *quit_console_args[] = {"/bin/sh", "-c", "screen -dr " CONSOLE_SESSION_STRING " -X quit", NULL}; + + sym = XLookupKeysym(keypress, 0); + + /* detect repeaters, note repeaters do not span interrupted Mod1 sequences! */ + if (key_is_grabbed && sym == last_sym && keypress->state == last_state) { + repeat_cnt++; + } else { + repeat_cnt = 0; + } + + vwin = vwm_win_focused(vwm); + + switch (sym) { + +#define launcher(_sym, _label, _argv)\ + case _sym: \ + { \ + char *args[] = {"/bin/sh", "-c", "screen -dr " CONSOLE_SESSION_STRING " -X screen /bin/sh -i -x -c \"" _argv " || sleep 86400\"", NULL};\ + vwm_launch(vwm, args, VWM_LAUNCH_MODE_BG);\ + break; \ + } +#include "launchers.def" +#undef launcher + case XK_Alt_L: /* transaction abort */ + case XK_Alt_R: + if (key_is_grabbed) key_is_grabbed++; + VWM_TRACE("aborting with origin %p", vwm->focused_origin); + break; + + case XK_grave: /* toggle shelf visibility */ + vwm_context_focus(vwm, VWM_CONTEXT_OTHER); + break; + + case XK_Tab: /* cycle focused window */ + do_grab = 1; /* update MRU window on commit (Mod1 release) */ + + /* focus the next window, note this doesn't affect MRU yet, that happens on Mod1 release */ + if (vwin) { + if (keypress->state & ShiftMask) { + vwm_win_focus_next(vwm, vwin, VWM_FENCE_MASKED_VIOLATE); + } else { + vwm_win_focus_next(vwm, vwin, VWM_FENCE_RESPECT); + } + } + break; + + case XK_space: { /* cycle focused desktop utilizing MRU */ + vwm_desktop_t *next_desktop = vwm_desktop_next_mru(vwm, vwm->focused_desktop); + + do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ + + if (keypress->state & ShiftMask) { + /* migrate the focused window with the desktop focus to the most recently used desktop */ + if (vwin) vwm_win_migrate(vwm, vwin, next_desktop); + } else { + vwm_desktop_focus(vwm, next_desktop); + } + break; + } + + case XK_d: /* destroy focused */ + if (vwin) { + if (keypress->state & ShiftMask) { /* brutally destroy the focused window */ + XKillClient(vwm->display, vwin->xwindow->id); + } else { /* kindly destroy the focused window */ + vwm_xwin_message(vwm, vwin->xwindow, vwm->wm_protocols_atom, vwm->wm_delete_atom); + } + } else if (vwm->focused_context == VWM_CONTEXT_DESKTOP) { + /* destroy the focused desktop when destroy occurs without any windows */ + vwm_desktop_destroy(vwm, vwm->focused_desktop); + } + break; + + case XK_Escape: /* leave VWM rudely, after triple press */ + do_grab = 1; + + if (repeat_cnt == 2) { + vwm_launch(vwm, quit_console_args, VWM_LAUNCH_MODE_FG); + exit(42); + } + break; + + case XK_v: /* instantiate (and focus) a new (potentially empty, unless migrating) virtual desktop */ + do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ + + if (keypress->state & ShiftMask) { + if (vwin) { + /* migrate the focused window to a newly created virtual desktop, focusing the new desktop simultaneously */ + vwm_win_migrate(vwm, vwin, vwm_desktop_create(vwm, NULL)); + } + } else { + vwm_desktop_focus(vwm, vwm_desktop_create(vwm, NULL)); + vwm_desktop_mru(vwm, vwm->focused_desktop); + } + break; + + case XK_h: /* previous virtual desktop, if we're in the shelf context this will simply switch to desktop context */ + do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ + + if (keypress->state & ShiftMask) { + if (vwin) { + /* migrate the focused window with the desktop focus to the previous desktop */ + vwm_win_migrate(vwm, vwin, vwm_desktop_prev(vwm, vwin->desktop)); + } + } else { + if (vwm->focused_context == VWM_CONTEXT_SHELF) { + /* focus the focused desktop instead of the shelf */ + vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP); + } else { + /* focus the previous desktop */ + vwm_desktop_focus(vwm, vwm_desktop_prev(vwm, vwm->focused_desktop)); + } + } + break; + + case XK_l: /* next virtual desktop, if we're in the shelf context this will simply switch to desktop context */ + do_grab = 1; /* update MRU desktop on commit (Mod1 release) */ + + if (keypress->state & ShiftMask) { + if (vwin) { + /* migrate the focused window with the desktop focus to the next desktop */ + vwm_win_migrate(vwm, vwin, vwm_desktop_next(vwm, vwin->desktop)); + } + } else { + if (vwm->focused_context == VWM_CONTEXT_SHELF) { + /* focus the focused desktop instead of the shelf */ + vwm_context_focus(vwm, VWM_CONTEXT_DESKTOP); + } else { + /* focus the next desktop */ + vwm_desktop_focus(vwm, vwm_desktop_next(vwm, vwm->focused_desktop)); + } + } + break; + + case XK_k: /* raise or shelve the focused window */ + if (vwin) { + if (keypress->state & ShiftMask) { /* shelf the window and focus the shelf */ + if (vwm->focused_context != VWM_CONTEXT_SHELF) { + /* shelve the focused window while focusing the shelf */ + vwm_win_shelve(vwm, vwin); + vwm_context_focus(vwm, VWM_CONTEXT_SHELF); + } + } else { + do_grab = 1; + + XRaiseWindow(vwm->display, vwin->xwindow->id); + + if (repeat_cnt == 1) { + /* double: reraise & fullscreen */ + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL); + } else if (repeat_cnt == 2) { + /* triple: reraise & fullscreen w/borders obscured by screen perimiter */ + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_ALL); + } else if (vwm->xinerama_screens_cnt > 1) { + if (repeat_cnt == 3) { + /* triple: reraise & fullscreen across all screens */ + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_FULL); + } else if (repeat_cnt == 4) { + /* quadruple: reraise & fullscreen w/borders obscured by screen perimiter */ + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_TOTAL, VWM_WIN_AUTOCONF_ALL); + } + } + XFlush(vwm->display); + } + } + break; + + case XK_j: /* lower or unshelve the focused window */ + if (vwin) { + if (keypress->state & ShiftMask) { /* unshelf the window to the focused desktop, and focus the desktop */ + if (vwm->focused_context == VWM_CONTEXT_SHELF) { + /* unshelve the focused window, focus the desktop it went to */ + vwm_win_migrate(vwm, vwin, vwm->focused_desktop); + } + } else { + if (vwin->autoconfigured == VWM_WIN_AUTOCONF_ALL) { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL); + } else { + XLowerWindow(vwm->display, vwin->xwindow->id); + } + XFlush(vwm->display); + } + } + break; + + case XK_Return: /* (full-screen / restore) focused window */ + if (vwin) { + if (vwin->autoconfigured) { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_NONE); + } else { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_FULL); + } + } + break; + + case XK_s: /* shelve focused window */ + if (vwin && !vwin->shelved) vwm_win_shelve(vwm, vwin); + break; + + case XK_bracketleft: /* reconfigure the focused window to occupy the left or top half of the screen or left quarters on repeat */ + if (vwin) { + do_grab = 1; + + if (keypress->state & ShiftMask) { + if (!repeat_cnt) { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_TOP); + } else { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_TOP_LEFT); + } + } else { + if (!repeat_cnt) { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_LEFT); + } else { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_BOTTOM_LEFT); + } + } + } + break; + + case XK_bracketright: /* reconfigure the focused window to occupy the right or bottom half of the screen or right quarters on repeat */ + if (vwin) { + do_grab = 1; + + if (keypress->state & ShiftMask) { + if (!repeat_cnt) { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_BOTTOM); + } else { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_BOTTOM_RIGHT); + } + } else { + if (!repeat_cnt) { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_HALF, VWM_SIDE_RIGHT); + } else { + vwm_win_autoconf(vwm, vwin, VWM_SCREEN_REL_XWIN, VWM_WIN_AUTOCONF_QUARTER, VWM_CORNER_TOP_RIGHT); + } + } + } + break; + + case XK_semicolon: /* toggle composited overlays */ + vwm_composite_toggle(vwm); + break; + + case XK_apostrophe: /* reset snowflakes of the focused window, suppressed when not compositing */ + if (vwin) { + vwm_overlay_xwin_reset_snowflakes(vwm, vwin->xwindow); + } + break; + + case XK_Right: /* increase sampling frequency */ + vwm_overlay_rate_increase(vwm); + break; + + case XK_Left: /* decrease sampling frequency */ + vwm_overlay_rate_decrease(vwm); + break; + + default: + VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym); + break; + } + + /* if what we're doing requests a grab, if not already grabbed, grab keyboard */ + if (!key_is_grabbed && do_grab) { + VWM_TRACE("saving focused_origin of %p", vwin); + vwm->focused_origin = vwin; /* for returning to on abort */ + XGrabKeyboard(vwm->display, VWM_XROOT(vwm), False, GrabModeAsync, GrabModeAsync, CurrentTime); + key_is_grabbed = 1; + } + + /* remember the symbol for repeater detection */ + last_sym = sym; + last_state = keypress->state; +} |