diff options
Diffstat (limited to 'src/move.c')
-rw-r--r-- | src/move.c | 729 |
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; + +} + |