aboutsummaryrefslogtreecommitdiff
path: root/src/move.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/move.c')
-rw-r--r--src/move.c729
1 files changed, 729 insertions, 0 deletions
diff --git a/src/move.c b/src/move.c
new file mode 100644
index 0000000..882ac90
--- /dev/null
+++ b/src/move.c
@@ -0,0 +1,729 @@
+/****************************************************************************
+ * Functions to handle moving client windows.
+ * Copyright (C) 2004 Joe Wingbermuehle
+ ****************************************************************************/
+
+#include "jwm.h"
+#include "move.h"
+#include "client.h"
+#include "border.h"
+#include "outline.h"
+#include "error.h"
+#include "screen.h"
+#include "main.h"
+#include "cursor.h"
+#include "event.h"
+#include "pager.h"
+#include "key.h"
+#include "tray.h"
+#include "status.h"
+
+typedef struct {
+ int valid;
+ int left, right;
+ int top, bottom;
+} RectangleType;
+
+static int shouldStopMove;
+static SnapModeType snapMode = SNAP_BORDER;
+static int snapDistance = DEFAULT_SNAP_DISTANCE;
+
+static MoveModeType moveMode = MOVE_OPAQUE;
+
+static void StopMove(ClientNode *np, int doMove, int oldx, int oldy);
+static void MoveController(int wasDestroyed);
+
+static void DoSnap(ClientNode *np, int north, int west);
+static void DoSnapScreen(ClientNode *np, int north, int west);
+static void DoSnapBorder(ClientNode *np, int north, int west);
+static int ShouldSnap(const ClientNode *np);
+static void GetClientRectangle(const ClientNode *np, RectangleType *r);
+
+static int CheckOverlapTopBottom(const RectangleType *a,
+ const RectangleType *b);
+static int CheckOverlapLeftRight(const RectangleType *a,
+ const RectangleType *b);
+
+static int CheckLeftValid(const RectangleType *client,
+ const RectangleType *other, const RectangleType *left);
+static int CheckRightValid(const RectangleType *client,
+ const RectangleType *other, const RectangleType *right);
+static int CheckTopValid(const RectangleType *client,
+ const RectangleType *other, const RectangleType *top);
+static int CheckBottomValid(const RectangleType *client,
+ const RectangleType *other, const RectangleType *bottom);
+
+/****************************************************************************
+ ****************************************************************************/
+void SetSnapMode(SnapModeType mode) {
+ snapMode = mode;
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void SetMoveMode(MoveModeType mode) {
+ moveMode = mode;
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void SetSnapDistance(const char *value) {
+ int temp;
+
+ Assert(value);
+
+ temp = atoi(value);
+ if(temp > MAX_SNAP_DISTANCE || temp < MIN_SNAP_DISTANCE) {
+ snapDistance = DEFAULT_SNAP_DISTANCE;
+ Warning("invalid snap distance specified: %d", temp);
+ } else {
+ snapDistance = temp;
+ }
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void SetDefaultSnapDistance() {
+ snapDistance = DEFAULT_SNAP_DISTANCE;
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void MoveController(int wasDestroyed) {
+
+ if(moveMode == MOVE_OUTLINE) {
+ ClearOutline();
+ }
+
+ JXUngrabPointer(display, CurrentTime);
+ JXUngrabKeyboard(display, CurrentTime);
+
+ DestroyMoveWindow();
+ shouldStopMove = 1;
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+int MoveClient(ClientNode *np, int startx, int starty) {
+
+ XEvent event;
+ int oldx, oldy;
+ int doMove;
+ int north, south, east, west;
+ int height;
+
+ Assert(np);
+
+ if(!(np->state.border & BORDER_MOVE)) {
+ return 0;
+ }
+
+ GrabMouseForMove();
+
+ np->controller = MoveController;
+ shouldStopMove = 0;
+
+ oldx = np->x;
+ oldy = np->y;
+
+ if(!(GetMouseMask() & (Button1Mask | Button2Mask))) {
+ StopMove(np, 0, oldx, oldy);
+ return 0;
+ }
+
+ GetBorderSize(np, &north, &south, &east, &west);
+
+ startx -= west;
+ starty -= north;
+
+ doMove = 0;
+ for(;;) {
+
+ WaitForEvent(&event);
+
+ if(shouldStopMove) {
+ np->controller = NULL;
+ SetDefaultCursor(np->parent);
+ return doMove;
+ }
+
+ switch(event.type) {
+ case ButtonRelease:
+ if(event.xbutton.button == Button1
+ || event.xbutton.button == Button2) {
+ StopMove(np, doMove, oldx, oldy);
+ return doMove;
+ }
+ break;
+ case MotionNotify:
+
+ DiscardMotionEvents(&event, np->window);
+
+ np->x = event.xmotion.x_root - startx;
+ np->y = event.xmotion.y_root - starty;
+
+ DoSnap(np, north, west);
+
+ if(!doMove && (abs(np->x - oldx) > MOVE_DELTA
+ || abs(np->y - oldy) > MOVE_DELTA)) {
+
+ if(np->state.status & STAT_MAXIMIZED) {
+ MaximizeClient(np);
+ startx = west + np->width / 2;
+ starty = north / 2;
+ MoveMouse(np->parent, startx, starty);
+ }
+
+ CreateMoveWindow(np);
+ doMove = 1;
+ }
+
+ if(doMove) {
+
+ if(moveMode == MOVE_OUTLINE) {
+ ClearOutline();
+ height = north + south;
+ if(!(np->state.status & STAT_SHADED)) {
+ height += np->height;
+ }
+ DrawOutline(np->x - west, np->y - north,
+ np->width + west + east, height);
+ } else {
+ JXMoveWindow(display, np->parent, np->x - west,
+ np->y - north);
+ SendConfigureEvent(np);
+ }
+ UpdateMoveWindow(np);
+ UpdatePager();
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/****************************************************************************
+ ****************************************************************************/
+int MoveClientKeyboard(ClientNode *np) {
+
+ XEvent event;
+ int oldx, oldy;
+ int moved;
+ int height;
+ int north, south, east, west;
+
+ Assert(np);
+
+ if(!(np->state.border & BORDER_MOVE)) {
+ return 0;
+ }
+
+ if(np->state.status & STAT_MAXIMIZED) {
+ MaximizeClient(np);
+ }
+
+ GrabMouseForMove();
+ if(JXGrabKeyboard(display, np->window, True, GrabModeAsync,
+ GrabModeAsync, CurrentTime) != GrabSuccess) {
+ Debug("could not grab keyboard for client move");
+ return 0;
+ }
+
+ GetBorderSize(np, &north, &south, &east, &west);
+
+ oldx = np->x;
+ oldy = np->y;
+
+ np->controller = MoveController;
+ shouldStopMove = 0;
+
+ CreateMoveWindow(np);
+ UpdateMoveWindow(np);
+
+ MoveMouse(rootWindow, np->x, np->y);
+ DiscardMotionEvents(&event, np->window);
+
+ if(np->state.status & STAT_SHADED) {
+ height = 0;
+ } else {
+ height = np->height;
+ }
+
+ for(;;) {
+
+ WaitForEvent(&event);
+
+ if(shouldStopMove) {
+ np->controller = NULL;
+ SetDefaultCursor(np->parent);
+ return 1;
+ }
+
+ moved = 0;
+
+ if(event.type == KeyPress) {
+
+ while(JXCheckTypedWindowEvent(display, np->window, KeyPress, &event));
+
+ switch(GetKey(&event.xkey) & 0xFF) {
+ case KEY_UP:
+ if(np->y + height > 0) {
+ np->y -= 10;
+ }
+ break;
+ case KEY_DOWN:
+ if(np->y < rootHeight) {
+ np->y += 10;
+ }
+ break;
+ case KEY_RIGHT:
+ if(np->x < rootWidth) {
+ np->x += 10;
+ }
+ break;
+ case KEY_LEFT:
+ if(np->x + np->width > 0) {
+ np->x -= 10;
+ }
+ break;
+ default:
+ StopMove(np, 1, oldx, oldy);
+ return 1;
+ }
+
+ MoveMouse(rootWindow, np->x, np->y);
+ JXCheckTypedWindowEvent(display, np->window, MotionNotify, &event);
+
+ moved = 1;
+
+ } else if(event.type == MotionNotify) {
+
+ while(JXCheckTypedWindowEvent(display, np->window,
+ MotionNotify, &event));
+
+ np->x = event.xmotion.x;
+ np->y = event.xmotion.y;
+
+ moved = 1;
+
+ } else if(event.type == ButtonRelease) {
+
+ StopMove(np, 1, oldx, oldy);
+ return 1;
+
+ }
+
+ if(moved) {
+
+ if(moveMode == MOVE_OUTLINE) {
+ ClearOutline();
+ DrawOutline(np->x - west, np->y - west,
+ np->width + west + east, height + north + west);
+ } else {
+ JXMoveWindow(display, np->parent, np->x - west, np->y - north);
+ SendConfigureEvent(np);
+ }
+
+ UpdateMoveWindow(np);
+ UpdatePager();
+
+ }
+
+ }
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void StopMove(ClientNode *np, int doMove, int oldx, int oldy) {
+
+ int north, south, east, west;
+
+ Assert(np);
+ Assert(np->controller);
+
+ (np->controller)(0);
+
+ np->controller = NULL;
+
+ SetDefaultCursor(np->parent);
+
+ if(!doMove) {
+ np->x = oldx;
+ np->y = oldy;
+ return;
+ }
+
+ GetBorderSize(np, &north, &south, &east, &west);
+
+ JXMoveWindow(display, np->parent, np->x - west, np->y - north);
+ SendConfigureEvent(np);
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void DoSnap(ClientNode *np, int north, int west) {
+ switch(snapMode) {
+ case SNAP_BORDER:
+ DoSnapBorder(np, north, west);
+ DoSnapScreen(np, north, west);
+ break;
+ case SNAP_SCREEN:
+ DoSnapScreen(np, north, west);
+ break;
+ default:
+ break;
+ }
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void DoSnapScreen(ClientNode *np, int north, int west) {
+
+ RectangleType client;
+ int screen;
+ const ScreenType *sp;
+ int screenCount;
+
+ GetClientRectangle(np, &client);
+
+ screenCount = GetScreenCount();
+ for(screen = 0; screen < screenCount; screen++) {
+
+ sp = GetScreen(screen);
+
+ if(abs(client.right - sp->width - sp->x) <= snapDistance) {
+ np->x = sp->x + sp->width - west - np->width;
+ }
+ if(abs(client.left - sp->x) <= snapDistance) {
+ np->x = sp->x + west;
+ }
+ if(abs(client.bottom - sp->height - sp->y) <= snapDistance) {
+ np->y = sp->y + sp->height - west;
+ if(!(np->state.status & STAT_SHADED)) {
+ np->y -= np->height;
+ }
+ }
+ if(abs(client.top - sp->y) <= snapDistance) {
+ np->y = north + sp->y;
+ }
+
+ }
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void DoSnapBorder(ClientNode *np, int north, int west) {
+
+ const ClientNode *tp;
+ const TrayType *tray;
+ RectangleType client, other;
+ RectangleType left = { 0 };
+ RectangleType right = { 0 };
+ RectangleType top = { 0 };
+ RectangleType bottom = { 0 };
+ int layer;
+
+ GetClientRectangle(np, &client);
+
+ other.valid = 1;
+
+ /* Work from the bottom of the window stack to the top. */
+ for(layer = 0; layer < LAYER_COUNT; layer++) {
+
+ /* Check tray windows. */
+ for(tray = GetTrays(); tray; tray = tray->next) {
+
+ if(tray->hidden) {
+ continue;
+ }
+
+ other.left = tray->x;
+ other.right = tray->x + tray->width;
+ other.top = tray->y;
+ other.bottom = tray->y + tray->height;
+
+ left.valid = CheckLeftValid(&client, &other, &left);
+ right.valid = CheckRightValid(&client, &other, &right);
+ top.valid = CheckTopValid(&client, &other, &top);
+ bottom.valid = CheckBottomValid(&client, &other, &bottom);
+
+ if(CheckOverlapTopBottom(&client, &other)) {
+ if(abs(client.left - other.right) <= snapDistance) {
+ left = other;
+ }
+ if(abs(client.right - other.left) <= snapDistance) {
+ right = other;
+ }
+ }
+ if(CheckOverlapLeftRight(&client, &other)) {
+ if(abs(client.top - other.bottom) <= snapDistance) {
+ top = other;
+ }
+ if(abs(client.bottom - other.top) <= snapDistance) {
+ bottom = other;
+ }
+ }
+
+ }
+
+ /* Check client windows. */
+ for(tp = nodeTail[layer]; tp; tp = tp->prev) {
+
+ if(tp == np || !ShouldSnap(tp)) {
+ continue;
+ }
+
+ GetClientRectangle(tp, &other);
+
+ /* Check if this border invalidates any previous value. */
+ left.valid = CheckLeftValid(&client, &other, &left);
+ right.valid = CheckRightValid(&client, &other, &right);
+ top.valid = CheckTopValid(&client, &other, &top);
+ bottom.valid = CheckBottomValid(&client, &other, &bottom);
+
+ /* Compute the new snap values. */
+ if(CheckOverlapTopBottom(&client, &other)) {
+ if(abs(client.left - other.right) <= snapDistance) {
+ left = other;
+ }
+ if(abs(client.right - other.left) <= snapDistance) {
+ right = other;
+ }
+ }
+ if(CheckOverlapLeftRight(&client, &other)) {
+ if(abs(client.top - other.bottom) <= snapDistance) {
+ top = other;
+ }
+ if(abs(client.bottom - other.top) <= snapDistance) {
+ bottom = other;
+ }
+ }
+
+ }
+
+ }
+
+ if(right.valid) {
+ np->x = right.left - np->width - west;
+ }
+ if(left.valid) {
+ np->x = left.right + west;
+ }
+ if(bottom.valid) {
+ np->y = bottom.top - west;
+ if(!(np->state.status & STAT_SHADED)) {
+ np->y -= np->height;
+ }
+ }
+ if(top.valid) {
+ np->y = top.bottom + north;
+ }
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+int ShouldSnap(const ClientNode *np) {
+ if(np->state.status & STAT_HIDDEN) {
+ return 0;
+ } else if(np->state.status & STAT_MINIMIZED) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/****************************************************************************
+ ****************************************************************************/
+void GetClientRectangle(const ClientNode *np, RectangleType *r) {
+
+ int border;
+
+ r->left = np->x;
+ r->right = np->x + np->width;
+ r->top = np->y;
+ if(np->state.status & STAT_SHADED) {
+ r->bottom = np->y;
+ } else {
+ r->bottom = np->y + np->height;
+ }
+
+ if(np->state.border & BORDER_OUTLINE) {
+ border = borderWidth;
+ r->left -= border;
+ r->right += border;
+ r->bottom += border;
+ } else {
+ border = 0;
+ }
+
+ if(np->state.border & BORDER_TITLE) {
+ r->top -= titleHeight + border;
+ } else {
+ r->top -= border;
+ }
+
+ r->valid = 1;
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+int CheckOverlapTopBottom(const RectangleType *a, const RectangleType *b) {
+ if(a->top >= b->bottom) {
+ return 0;
+ } else if(a->bottom <= b->top) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/****************************************************************************
+ ****************************************************************************/
+int CheckOverlapLeftRight(const RectangleType *a, const RectangleType *b) {
+ if(a->left >= b->right) {
+ return 0;
+ } else if(a->right <= b->left) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/****************************************************************************
+ * Check if the current left snap position is valid.
+ * client - The window being moved.
+ * other - A window higher in stacking order than previously check windows.
+ * left - The top/bottom of the current left snap window.
+ * Returns 1 if the current left snap position is still valid, otherwise 0.
+ ****************************************************************************/
+int CheckLeftValid(const RectangleType *client,
+ const RectangleType *other, const RectangleType *left) {
+
+ if(!left->valid) {
+ return 0;
+ }
+
+ if(left->right > other->right) {
+ return 1;
+ }
+
+ /* If left and client go higher than other then still valid. */
+ if(left->top < other->top && client->top < other->top) {
+ return 1;
+ }
+
+ /* If left and client go lower than other then still valid. */
+ if(left->bottom > other->bottom && client->bottom > other->bottom) {
+ return 1;
+ }
+
+ if(other->left >= left->right) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+int CheckRightValid(const RectangleType *client,
+ const RectangleType *other, const RectangleType *right) {
+
+ if(!right->valid) {
+ return 0;
+ }
+
+ if(right->left < other->left) {
+ return 1;
+ }
+
+ /* If right and client go higher than other then still valid. */
+ if(right->top < other->top && client->top < other->top) {
+ return 1;
+ }
+
+ /* If right and client go lower than other then still valid. */
+ if(right->bottom > other->bottom && client->bottom > other->bottom) {
+ return 1;
+ }
+
+ if(other->right <= right->left) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+int CheckTopValid(const RectangleType *client,
+ const RectangleType *other, const RectangleType *top) {
+
+ if(!top->valid) {
+ return 0;
+ }
+
+ if(top->bottom > other->bottom) {
+ return 1;
+ }
+
+ /* If top and client are to the left of other then still valid. */
+ if(top->left < other->left && client->left < other->left) {
+ return 1;
+ }
+
+ /* If top and client are to the right of other then still valid. */
+ if(top->right > other->right && client->right > other->right) {
+ return 1;
+ }
+
+ if(other->top >= top->bottom) {
+ return 1;
+ }
+
+ return 0;
+
+}
+
+/****************************************************************************
+ ****************************************************************************/
+int CheckBottomValid(const RectangleType *client,
+ const RectangleType *other, const RectangleType *bottom) {
+
+ if(!bottom->valid) {
+ return 0;
+ }
+
+ if(bottom->top < other->top) {
+ return 1;
+ }
+
+ /* If bottom and client are to the left of other then still valid. */
+ if(bottom->left < other->left && client->left < other->left) {
+ return 1;
+ }
+
+ /* If bottom and client are to the right of other then still valid. */
+ if(bottom->right > other->right && client->right > other->right) {
+ return 1;
+ }
+
+ if(other->bottom <= bottom->top) {
+ return 1;
+ }
+
+ return 0;
+
+}
+