From 8b919b0d33fed60da715588582e4b7b1126f5e72 Mon Sep 17 00:00:00 2001 From: Gaspar Chilingarov Date: Sun, 16 Sep 2018 16:55:16 +0300 Subject: [PATCH] support moving window between heads using keyboard Window Maker allows to perform practically all operations with windows using only keyboard. One of the actions so far which required using mouse was dragging window from one head (monitor) to another. This patch introduces support for keyboard shortcuts. These shortcuts move windows in circular fashion (if you have 3 and more monitors). In case of 2 or 3 monitors arranged horizontally - window will just move right/left. In case of 3x3 setup - it is impossible to move window to central monitor with keyboard. - preserves window position and size (if display sizes are same) - otherwise tries to fit window to smaller display --- WPrefs.app/KeyboardShortcuts.c | 4 ++ src/actions.c | 85 ++++++++++++++++++++++++++++++++++++++++++ src/defaults.c | 4 ++ src/event.c | 7 ++++ src/keybind.h | 4 ++ 5 files changed, 104 insertions(+) diff --git a/WPrefs.app/KeyboardShortcuts.c b/WPrefs.app/KeyboardShortcuts.c index a9241e95..88e33788 100644 --- a/WPrefs.app/KeyboardShortcuts.c +++ b/WPrefs.app/KeyboardShortcuts.c @@ -148,6 +148,10 @@ static const struct { { "WindowShortcut9Key", N_("Shortcut for window 9") }, { "WindowShortcut10Key", N_("Shortcut for window 10") }, + /* Head Selection */ + { "MoveTo12to6Head", N_("Move to right/bottom/left/top head") }, + { "MoveTo6to12Head", N_("Move to left/top/right/bottom head") }, + /* Misc. */ { "WindowRelaunchKey", N_("Launch new instance of application") }, { "ScreenSwitchKey", N_("Switch to Next Screen/Monitor") }, diff --git a/src/actions.c b/src/actions.c index 337c2ab9..7e465e0c 100644 --- a/src/actions.c +++ b/src/actions.c @@ -669,6 +669,91 @@ void handleMaximize(WWindow *wwin, int directions) } } +/* Moving window between heads + * + * Window is moved between all avaiable heads for current screen in + * requested direction. + * + * If heads have different sizes, window position will be adjusted to be inside + * screen. Window will be unmaximized on move, so that it can be immediately be + * maximixed by another shortcut. + * + * + * direction = 0 means move clock-wise from 12h position to 6h + * (first try moving right, then bottom, then left, then up) + * direction = 1 means move clock-wise from 6h position to 12h + * (first try moving left, then up, then right, then bottom) + * + * + * As side effect this mean if you have 9 monitors - you will not be able to + * move window into central cell without mouse. + * + * For common cases with 1 or 2 extra monitors this should work just fine. + * + * */ + +void moveBetweenHeads(WWindow *wwin, int direction) +{ + int head = wGetHeadForWindow(wwin); + int destHead = -1; + + unsigned int new_width, new_height; + int offsetX, newX = 0; + int offsetY, newY = 0; + WArea totalArea, oldHeadArea, destHeadArea; + WScreen *scr = wwin->screen_ptr; + + int try_movements[2][4] = { + {DIRECTION_RIGHT, DIRECTION_DOWN, DIRECTION_LEFT, DIRECTION_UP}, + {DIRECTION_LEFT, DIRECTION_UP, DIRECTION_RIGHT, DIRECTION_DOWN} + }; + + /* loop through directions array and try movements until one works */ + for (int try_movement_idx = 0; + destHead == -1 && try_movement_idx < 4; try_movement_idx++) { + destHead = wGetHeadRelativeToCurrentHead(wwin->screen_ptr, + head, try_movements[direction][try_movement_idx]); + } + + if (destHead != -1) { + totalArea.x1 = 0; + totalArea.y1 = 0; + totalArea.x2 = scr->scr_width; + totalArea.y2 = scr->scr_height; + + oldHeadArea = wGetUsableAreaForHead(scr, head, &totalArea, True); + destHeadArea = wGetUsableAreaForHead(scr, destHead, &totalArea, True); + + offsetX = wwin->frame_x - oldHeadArea.x1; + offsetY = wwin->frame_y - oldHeadArea.y1; + + newX = destHeadArea.x1 + offsetX; + newY = destHeadArea.y1 + offsetY; + + new_width = wwin->client.width; + new_height = wwin->client.height; + + /* try to brind window inside head coordinates */ + if (newX > destHeadArea.x2) + newX = destHeadArea.x2 - wwin->client.width; + + if (newX < destHeadArea.x1) + newX = destHeadArea.x1; + + if (newY > destHeadArea.y2) + newY = destHeadArea.y2 - wwin->client.height; + + if (newY < destHeadArea.y1) + newY = destHeadArea.y1; + + /* unset maximization state */ + wwin->flags.maximized = 0; + wWindowConfigure(wwin, newX, newY, new_width, new_height); + } + + return; +} + /* the window boundary coordinates */ typedef struct { int left; diff --git a/src/defaults.c b/src/defaults.c index 7aee170b..23a0cff9 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -770,6 +770,10 @@ WDefaultEntry optionList[] = { NULL, getKeybind, setKeyGrab, NULL, NULL}, {"WindowShortcut10Key", "None", (void *)WKBD_WINDOW10, NULL, getKeybind, setKeyGrab, NULL, NULL}, + {"MoveTo12to6Head", "None", (void *)WKBD_MOVE_12_TO_6_HEAD, + NULL, getKeybind, setKeyGrab, NULL, NULL}, + {"MoveTo6to12Head", "None", (void *)WKBD_MOVE_6_TO_12_HEAD, + NULL, getKeybind, setKeyGrab, NULL, NULL}, {"WindowRelaunchKey", "None", (void *)WKBD_RELAUNCH, NULL, getKeybind, setKeyGrab, NULL, NULL}, {"ScreenSwitchKey", "None", (void *)WKBD_SWITCH_SCREEN, diff --git a/src/event.c b/src/event.c index 440cc95f..39bf5507 100644 --- a/src/event.c +++ b/src/event.c @@ -1802,6 +1802,13 @@ static void handleKeyPress(XEvent * event) break; + case WKBD_MOVE_12_TO_6_HEAD: + case WKBD_MOVE_6_TO_12_HEAD: + if (wwin) + moveBetweenHeads(wwin, command - WKBD_MOVE_12_TO_6_HEAD); + + break; + case WKBD_RELAUNCH: if (ISMAPPED(wwin) && ISFOCUSED(wwin)) (void) RelaunchWindow(wwin); diff --git a/src/keybind.h b/src/keybind.h index 214fd913..fe8d4c95 100644 --- a/src/keybind.h +++ b/src/keybind.h @@ -132,6 +132,10 @@ enum { WKBD_WINDOW9, WKBD_WINDOW10, + /* shortcuts to move window between heads */ + WKBD_MOVE_12_TO_6_HEAD, + WKBD_MOVE_6_TO_12_HEAD, + /* launch a new instance of the active window */ WKBD_RELAUNCH, -- 2.11.4.GIT