windowmaker-wl/src/moveres.c

2458 lines
66 KiB
C

/*
* Window Maker window manager
*
* Copyright (c) 1997-2003 Alfredo K. Kojima
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "wconfig.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "WindowMaker.h"
#include "framewin.h"
#include "window.h"
#include "client.h"
#include "icon.h"
#include "dock.h"
#include "actions.h"
#include "workspace.h"
#include "placement.h"
#include "geomview.h"
#include "screen.h"
#include "xinerama.h"
#include "backend/backend.h"
#include <WINGs/WINGsP.h>
/* How many different types of geometry/position
display thingies are there? */
#define NUM_DISPLAYS 5
#define LEFT 1
#define RIGHT 2
#define UP 4
#define DOWN 8
/* True if window currently has a border. This also includes borderless
* windows which are currently selected
*/
#define HAS_BORDER_WITH_SELECT(w) ((w)->flags.selected || HAS_BORDER(w))
/*
*----------------------------------------------------------------------
* checkMouseSamplingRate-
* For lowering the mouse motion sampling rate for machines where
* it's too high (SGIs). If it returns False then the event should be
* ignored.
*----------------------------------------------------------------------
*/
static Bool checkMouseSamplingRate(WMEvent * ev)
{
static Time previousMotion = 0;
if (ev->type == WME_MOTION) {
if (ev->u.motion.time - previousMotion < DELAY_BETWEEN_MOUSE_SAMPLING) {
return 0;
} else {
previousMotion = ev->u.motion.time;
}
}
return True;
}
/*
*----------------------------------------------------------------------
* moveGeometryDisplayCentered
*
* routine that moves the geometry/position window on scr so it is
* centered over the given coordinates (x,y). Also the window position
* is clamped so it stays on the screen at all times.
*----------------------------------------------------------------------
*/
static void moveGeometryDisplayCentered(WScreen * scr, int x, int y)
{
unsigned int w = WMWidgetWidth(scr->gview);
unsigned int h = WMWidgetHeight(scr->gview);
int x1 = 0, y1 = 0, x2 = scr->scr_width, y2 = scr->scr_height;
x -= w / 2;
y -= h / 2;
/* dead area check */
if (scr->head_count) {
WMRect rect;
int head, flags;
rect.pos.x = x;
rect.pos.y = y;
rect.size.width = w;
rect.size.height = h;
head = wGetRectPlacementInfo(scr, rect, &flags);
if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
rect = wGetRectForHead(scr, head);
x1 = rect.pos.x;
y1 = rect.pos.y;
x2 = x1 + rect.size.width;
y2 = y1 + rect.size.height;
}
}
if (x < x1 + 1)
x = x1 + 1;
else if (x > (x2 - w))
x = x2 - w;
if (y < y1 + 1)
y = y1 + 1;
else if (y > (y2 - h))
y = y2 - h;
WMMoveWidget(scr->gview, x, y);
}
static void showPosition(WWindow * wwin, int x, int y)
{
WScreen *scr = wwin->screen_ptr;
if (wPreferences.move_display == WDIS_NEW) {
#if 0
int width = wwin->frame->core->width;
int height = wwin->frame->core->height;
GC lgc = scr->line_gc;
XSetForeground(dpy, lgc, scr->line_pixel);
sprintf(num, "%i", x);
XDrawLine(dpy, scr->root_win, lgc, 0, y - 1, scr->scr_width, y - 1);
XDrawLine(dpy, scr->root_win, lgc, 0, y + height + 2, scr->scr_width, y + height + 2);
XDrawLine(dpy, scr->root_win, lgc, x - 1, 0, x - 1, scr->scr_height);
XDrawLine(dpy, scr->root_win, lgc, x + width + 2, 0, x + width + 2, scr->scr_height);
#endif
} else {
WSetGeometryViewShownPosition(scr->gview, x, y);
}
}
static void cyclePositionDisplay(WWindow * wwin, int x, int y, int w, int h)
{
WScreen *scr = wwin->screen_ptr;
WMRect rect;
wPreferences.move_display++;
wPreferences.move_display %= NUM_DISPLAYS;
if (wPreferences.move_display == WDIS_NEW) {
wPreferences.move_display++;
wPreferences.move_display %= NUM_DISPLAYS;
}
if (wPreferences.move_display == WDIS_NONE) {
WMUnmapWidget(scr->gview);
} else {
if (wPreferences.move_display == WDIS_CENTER) {
rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
moveGeometryDisplayCentered(scr, rect.pos.x + rect.size.width / 2,
rect.pos.y + rect.size.height / 2);
} else if (wPreferences.move_display == WDIS_TOPLEFT) {
rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
moveGeometryDisplayCentered(scr, rect.pos.x + 1, rect.pos.y + 1);
} else if (wPreferences.move_display == WDIS_FRAME_CENTER) {
moveGeometryDisplayCentered(scr, x + w / 2, y + h / 2);
}
WMMapWidget(scr->gview);
}
}
static void mapPositionDisplay(WWindow * wwin, int x, int y, int w, int h)
{
WScreen *scr = wwin->screen_ptr;
WMRect rect;
if (wPreferences.move_display == WDIS_NEW || wPreferences.move_display == WDIS_NONE) {
return;
} else if (wPreferences.move_display == WDIS_CENTER) {
rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
moveGeometryDisplayCentered(scr, rect.pos.x + rect.size.width / 2,
rect.pos.y + rect.size.height / 2);
} else if (wPreferences.move_display == WDIS_TOPLEFT) {
rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
moveGeometryDisplayCentered(scr, rect.pos.x + 1, rect.pos.y + 1);
} else if (wPreferences.move_display == WDIS_FRAME_CENTER) {
moveGeometryDisplayCentered(scr, x + w / 2, y + h / 2);
}
WMMapWidget(scr->gview);
WSetGeometryViewShownPosition(scr->gview, x, y);
}
static void showGeometry(WWindow * wwin, int x1, int y1, int x2, int y2, int direction)
{
WScreen *scr = wwin->screen_ptr;
int ty, by, my, x, y, mx, s;
char num[16];
WSegment segment[4];
int fw, fh;
/* This seems necessary for some odd reason (too lazy to write x1-1 and
* x2-1 everywhere below in the code). But why only for x? */
x1--;
x2--;
if (HAS_BORDER_WITH_SELECT(wwin)) {
x1 += scr->frame_border_width;
x2 += scr->frame_border_width;
y1 += scr->frame_border_width;
y2 += scr->frame_border_width;
}
ty = y1 + wwin->frame->top_width;
by = y2 - wwin->frame->bottom_width;
if (wPreferences.size_display == WDIS_NEW) {
fw = wm_backend->overlay_text_width(scr, "8888", 4);
fh = scr->tech_draw_font->ascent + scr->tech_draw_font->descent;
/* vertical geometry */
if (((direction & LEFT) && (x2 < scr->scr_width - fw)) || (x1 < fw)) {
x = x2;
s = -15;
} else {
x = x1;
s = 15;
}
my = (ty + by) / 2;
/* top arrow & end bar */
segment[0].x1 = x - (s + 6);
segment[0].y1 = ty;
segment[0].x2 = x - (s - 10);
segment[0].y2 = ty;
/* arrowhead */
segment[1].x1 = x - (s - 2);
segment[1].y1 = ty + 1;
segment[1].x2 = x - (s - 5);
segment[1].y2 = ty + 7;
segment[2].x1 = x - (s - 2);
segment[2].y1 = ty + 1;
segment[2].x2 = x - (s + 1);
segment[2].y2 = ty + 7;
/* line */
segment[3].x1 = x - (s - 2);
segment[3].y1 = ty + 1;
segment[3].x2 = x - (s - 2);
segment[3].y2 = my - fh / 2 - 1;
wm_backend->overlay_draw_segments(scr, segment, 4);
/* bottom arrow & end bar */
segment[0].y1 = by;
segment[0].y2 = by;
/* arrowhead */
segment[1].y1 = by - 1;
segment[1].y2 = by - 7;
segment[2].y1 = by - 1;
segment[2].y2 = by - 7;
/* line */
segment[3].y1 = my + fh / 2 + 2;
segment[3].y2 = by - 1;
wm_backend->overlay_draw_segments(scr, segment, 4);
snprintf(num, sizeof(num), "%i", (by - ty - wwin->normal_hints->base_height) /
wwin->normal_hints->height_inc);
fw = wm_backend->overlay_text_width(scr, num, strlen(num));
/* Display the height. */
wm_backend->overlay_draw_string(scr, x - s + 3 - fw / 2,
my + scr->tech_draw_font->ascent - fh / 2 + 1, num, strlen(num));
/* horizontal geometry */
if (y1 < 15) {
y = y2;
s = -15;
} else {
y = y1;
s = 15;
}
mx = x1 + (x2 - x1) / 2;
snprintf(num, sizeof(num), "%i", (x2 - x1 - wwin->normal_hints->base_width) /
wwin->normal_hints->width_inc);
fw = wm_backend->overlay_text_width(scr, num, strlen(num));
/* left arrow & end bar */
segment[0].x1 = x1;
segment[0].y1 = y - (s + 6);
segment[0].x2 = x1;
segment[0].y2 = y - (s - 10);
/* arrowhead */
segment[1].x1 = x1 + 7;
segment[1].y1 = y - (s + 1);
segment[1].x2 = x1 + 1;
segment[1].y2 = y - (s - 2);
segment[2].x1 = x1 + 1;
segment[2].y1 = y - (s - 2);
segment[2].x2 = x1 + 7;
segment[2].y2 = y - (s - 5);
/* line */
segment[3].x1 = x1 + 1;
segment[3].y1 = y - (s - 2);
segment[3].x2 = mx - fw / 2 - 2;
segment[3].y2 = y - (s - 2);
wm_backend->overlay_draw_segments(scr, segment, 4);
/* right arrow & end bar */
segment[0].x1 = x2 + 1;
segment[0].x2 = x2 + 1;
/* arrowhead */
segment[1].x1 = x2 - 6;
segment[1].x2 = x2;
segment[2].x1 = x2;
segment[2].x2 = x2 - 6;
/* line */
segment[3].x1 = mx + fw / 2 + 2;
segment[3].x2 = x2;
wm_backend->overlay_draw_segments(scr, segment, 4);
/* Display the width. */
wm_backend->overlay_draw_string(scr, mx - fw / 2 + 1,
y - s + scr->tech_draw_font->ascent - fh / 2 + 1, num, strlen(num));
} else {
WSetGeometryViewShownSize(scr->gview, (x2 - x1 - wwin->normal_hints->base_width)
/ wwin->normal_hints->width_inc,
(by - ty - wwin->normal_hints->base_height)
/ wwin->normal_hints->height_inc);
}
}
static void cycleGeometryDisplay(WWindow * wwin, int x, int y, int w, int h, int dir)
{
WScreen *scr = wwin->screen_ptr;
WMRect rect;
wPreferences.size_display++;
wPreferences.size_display %= NUM_DISPLAYS;
if (wPreferences.size_display == WDIS_NEW || wPreferences.size_display == WDIS_NONE) {
WMUnmapWidget(scr->gview);
} else {
if (wPreferences.size_display == WDIS_CENTER) {
rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
moveGeometryDisplayCentered(scr, rect.pos.x + rect.size.width / 2,
rect.pos.y + rect.size.height / 2);
} else if (wPreferences.size_display == WDIS_TOPLEFT) {
rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
moveGeometryDisplayCentered(scr, rect.pos.x + 1, rect.pos.y + 1);
} else if (wPreferences.size_display == WDIS_FRAME_CENTER) {
moveGeometryDisplayCentered(scr, x + w / 2, y + h / 2);
}
WMMapWidget(scr->gview);
showGeometry(wwin, x, y, x + w, y + h, dir);
}
}
static void mapGeometryDisplay(WWindow * wwin, int x, int y, int w, int h)
{
WScreen *scr = wwin->screen_ptr;
WMRect rect;
if (wPreferences.size_display == WDIS_NEW || wPreferences.size_display == WDIS_NONE)
return;
if (wPreferences.size_display == WDIS_CENTER) {
rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
moveGeometryDisplayCentered(scr, rect.pos.x + rect.size.width / 2,
rect.pos.y + rect.size.height / 2);
} else if (wPreferences.size_display == WDIS_TOPLEFT) {
rect = wGetRectForHead(scr, wGetHeadForWindow(wwin));
moveGeometryDisplayCentered(scr, rect.pos.x + 1, rect.pos.y + 1);
} else if (wPreferences.size_display == WDIS_FRAME_CENTER) {
moveGeometryDisplayCentered(scr, x + w / 2, y + h / 2);
}
WMMapWidget(scr->gview);
showGeometry(wwin, x, y, x + w, y + h, 0);
}
static void doWindowMove(WWindow * wwin, WMArray * array, int dx, int dy)
{
WWindow *tmpw;
WScreen *scr = wwin->screen_ptr;
int x, y;
if (!array || !WMGetArrayItemCount(array)) {
wWindowMove(wwin, wwin->frame_x + dx, wwin->frame_y + dy);
} else {
WMArrayIterator iter;
WM_ITERATE_ARRAY(array, tmpw, iter) {
x = tmpw->frame_x + dx;
y = tmpw->frame_y + dy;
#if 1 /* XXX: with xinerama patch was #if 0, check this */
/* don't let windows become unreachable */
if (x + (int)tmpw->frame->core->width < 20)
x = 20 - (int)tmpw->frame->core->width;
else if (x + 20 > scr->scr_width)
x = scr->scr_width - 20;
if (y + (int)tmpw->frame->core->height < 20)
y = 20 - (int)tmpw->frame->core->height;
else if (y + 20 > scr->scr_height)
y = scr->scr_height - 20;
#else
wScreenBringInside(scr, &x, &y,
(int)tmpw->frame->core->width, (int)tmpw->frame->core->height);
#endif
wWindowMove(tmpw, x, y);
}
}
}
static void drawTransparentFrame(WWindow * wwin, int x, int y, int width, int height)
{
WScreen *scr = wwin->screen_ptr;
int h = 0;
int bottom = 0;
if (HAS_BORDER_WITH_SELECT(wwin)) {
x += scr->frame_border_width;
y += scr->frame_border_width;
}
if (HAS_TITLEBAR(wwin) && !wwin->flags.shaded) {
h = WMFontHeight(wwin->screen_ptr->title_font) + (wPreferences.window_title_clearance +
TITLEBAR_EXTEND_SPACE) * 2;
if (h > wPreferences.window_title_max_height)
h = wPreferences.window_title_max_height;
if (h < wPreferences.window_title_min_height)
h = wPreferences.window_title_min_height;
}
if (HAS_RESIZEBAR(wwin) && !wwin->flags.shaded) {
/* Can't use wwin-frame->bottom_width because, in some cases
(e.g. interactive placement), frame does not point to anything. */
bottom = RESIZEBAR_HEIGHT;
}
wm_backend->overlay_draw_rect(scr, x - 1, y - 1, width + 1, height + 1);
if (h > 0) {
wm_backend->overlay_draw_line(scr, x, y + h - 1, x + width, y + h - 1);
}
if (bottom > 0) {
wm_backend->overlay_draw_line(scr, x, y + height - bottom, x + width, y + height - bottom);
}
}
static void drawFrames(WWindow * wwin, WMArray * array, int dx, int dy)
{
WWindow *tmpw;
int scr_width = wwin->screen_ptr->scr_width;
int scr_height = wwin->screen_ptr->scr_height;
int x, y;
if (!array) {
x = wwin->frame_x + dx;
y = wwin->frame_y + dy;
drawTransparentFrame(wwin, x, y, wwin->frame->core->width, wwin->frame->core->height);
} else {
WMArrayIterator iter;
WM_ITERATE_ARRAY(array, tmpw, iter) {
x = tmpw->frame_x + dx;
y = tmpw->frame_y + dy;
/* don't let windows become unreachable */
#if 1 /* XXX: was 0 in XINERAMA patch, check */
if (x + (int)tmpw->frame->core->width < 20)
x = 20 - (int)tmpw->frame->core->width;
else if (x + 20 > scr_width)
x = scr_width - 20;
if (y + (int)tmpw->frame->core->height < 20)
y = 20 - (int)tmpw->frame->core->height;
else if (y + 20 > scr_height)
y = scr_height - 20;
#else
wScreenBringInside(wwin->screen_ptr, &x, &y,
(int)tmpw->frame->core->width, (int)tmpw->frame->core->height);
#endif
drawTransparentFrame(tmpw, x, y, tmpw->frame->core->width, tmpw->frame->core->height);
}
}
}
static void flushMotion(void)
{
wm_backend->event_sync(0);
wm_backend->event_drain_motion();
}
static void crossWorkspace(WScreen * scr, WWindow * wwin, int opaque_move, int new_workspace, int rewind)
{
/* do not let window be unmapped */
if (opaque_move) {
wwin->flags.changing_workspace = 1;
wWindowChangeWorkspace(wwin, new_workspace);
}
/* go to new workspace */
wWorkspaceChange(scr, new_workspace);
wwin->flags.changing_workspace = 0;
if (rewind)
wm_backend->pointer_warp(scr->scr_width - 20, 0);
else
wm_backend->pointer_warp(-(scr->scr_width - 20), 0);
flushMotion();
if (!opaque_move) {
wm_backend->pointer_grab(scr->root_win, WNATIVE_NONE,
PointerMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_MOVE, True, 0);
}
}
typedef struct {
/* arrays of WWindows sorted by the respective border position */
WWindow **topList; /* top border */
WWindow **leftList; /* left border */
WWindow **rightList; /* right border */
WWindow **bottomList; /* bottom border */
int count;
/* index of window in the above lists indicating the relative position
* of the window with the others */
int topIndex;
int leftIndex;
int rightIndex;
int bottomIndex;
int rubCount; /* for workspace switching */
int winWidth, winHeight; /* width/height of the window */
int realX, realY; /* actual position of the window */
int calcX, calcY; /* calculated position of window */
int omouseX, omouseY; /* old mouse position */
int mouseX, mouseY; /* last known position of the pointer */
enum {
SNAP_NONE,
SNAP_LEFT,
SNAP_RIGHT,
SNAP_TOP,
SNAP_BOTTOM,
SNAP_TOPLEFT,
SNAP_TOPRIGHT,
SNAP_BOTTOMLEFT,
SNAP_BOTTOMRIGHT
} snap;
} MoveData;
#define WTOP(w) (w)->frame_y
#define WLEFT(w) (w)->frame_x
#define WRIGHT(w) ((w)->frame_x + (int)(w)->frame->core->width - 1 + \
(HAS_BORDER_WITH_SELECT(w) ? 2*(w)->screen_ptr->frame_border_width : 0))
#define WBOTTOM(w) ((w)->frame_y + (int)(w)->frame->core->height - 1 + \
(HAS_BORDER_WITH_SELECT(w) ? 2*(w)->screen_ptr->frame_border_width : 0))
static int compareWTop(const void *a, const void *b)
{
WWindow *wwin1 = *(WWindow **) a;
WWindow *wwin2 = *(WWindow **) b;
if (WTOP(wwin1) > WTOP(wwin2))
return -1;
else if (WTOP(wwin1) < WTOP(wwin2))
return 1;
else
return 0;
}
static int compareWLeft(const void *a, const void *b)
{
WWindow *wwin1 = *(WWindow **) a;
WWindow *wwin2 = *(WWindow **) b;
if (WLEFT(wwin1) > WLEFT(wwin2))
return -1;
else if (WLEFT(wwin1) < WLEFT(wwin2))
return 1;
else
return 0;
}
static int compareWRight(const void *a, const void *b)
{
WWindow *wwin1 = *(WWindow **) a;
WWindow *wwin2 = *(WWindow **) b;
if (WRIGHT(wwin1) < WRIGHT(wwin2))
return -1;
else if (WRIGHT(wwin1) > WRIGHT(wwin2))
return 1;
else
return 0;
}
static int compareWBottom(const void *a, const void *b)
{
WWindow *wwin1 = *(WWindow **) a;
WWindow *wwin2 = *(WWindow **) b;
if (WBOTTOM(wwin1) < WBOTTOM(wwin2))
return -1;
else if (WBOTTOM(wwin1) > WBOTTOM(wwin2))
return 1;
else
return 0;
}
static void updateResistance(MoveData *data, int newX, int newY)
{
int i;
int newX2 = newX + data->winWidth;
int newY2 = newY + data->winHeight;
Bool ok = False;
if (newX < data->realX) {
if (data->rightIndex > 0 && newX < WRIGHT(data->rightList[data->rightIndex - 1])) {
ok = True;
} else if (data->leftIndex <= data->count - 1 && newX2 <= WLEFT(data->leftList[data->leftIndex])) {
ok = True;
}
} else if (newX > data->realX) {
if (data->leftIndex > 0 && newX2 > WLEFT(data->leftList[data->leftIndex - 1])) {
ok = True;
} else if (data->rightIndex <= data->count - 1
&& newX >= WRIGHT(data->rightList[data->rightIndex])) {
ok = True;
}
}
if (!ok) {
if (newY < data->realY) {
if (data->bottomIndex > 0 && newY < WBOTTOM(data->bottomList[data->bottomIndex - 1])) {
ok = True;
} else if (data->topIndex <= data->count - 1
&& newY2 <= WTOP(data->topList[data->topIndex])) {
ok = True;
}
} else if (newY > data->realY) {
if (data->topIndex > 0 && newY2 > WTOP(data->topList[data->topIndex - 1])) {
ok = True;
} else if (data->bottomIndex <= data->count - 1
&& newY >= WBOTTOM(data->bottomList[data->bottomIndex])) {
ok = True;
}
}
}
if (!ok)
return;
/* TODO: optimize this */
if (data->realY < WBOTTOM(data->bottomList[0])) {
data->bottomIndex = 0;
}
if (data->realX < WRIGHT(data->rightList[0])) {
data->rightIndex = 0;
}
if ((data->realX + data->winWidth) > WLEFT(data->leftList[0])) {
data->leftIndex = 0;
}
if ((data->realY + data->winHeight) > WTOP(data->topList[0])) {
data->topIndex = 0;
}
for (i = 0; i < data->count; i++) {
if (data->realY > WBOTTOM(data->bottomList[i])) {
data->bottomIndex = i + 1;
}
if (data->realX > WRIGHT(data->rightList[i])) {
data->rightIndex = i + 1;
}
if ((data->realX + data->winWidth) < WLEFT(data->leftList[i])) {
data->leftIndex = i + 1;
}
if ((data->realY + data->winHeight) < WTOP(data->topList[i])) {
data->topIndex = i + 1;
}
}
}
static void freeMoveData(MoveData * data)
{
if (data->topList)
wfree(data->topList);
if (data->leftList)
wfree(data->leftList);
if (data->rightList)
wfree(data->rightList);
if (data->bottomList)
wfree(data->bottomList);
}
static void updateMoveData(WWindow * wwin, MoveData * data)
{
WScreen *scr = wwin->screen_ptr;
WWindow *tmp;
int i;
data->count = 0;
tmp = scr->focused_window;
while (tmp) {
if (tmp != wwin && scr->current_workspace == WIN_WORKSPACE(tmp)
&& !tmp->flags.miniaturized
&& !tmp->flags.hidden && !tmp->flags.obscured && !WFLAGP(tmp, sunken)) {
data->topList[data->count] = tmp;
data->leftList[data->count] = tmp;
data->rightList[data->count] = tmp;
data->bottomList[data->count] = tmp;
data->count++;
}
tmp = tmp->prev;
}
if (data->count == 0) {
data->topIndex = 0;
data->leftIndex = 0;
data->rightIndex = 0;
data->bottomIndex = 0;
return;
}
/* order from closest to the border of the screen to farthest */
qsort(data->topList, data->count, sizeof(data->topList[0]), compareWTop);
qsort(data->leftList, data->count, sizeof(data->leftList[0]), compareWLeft);
qsort(data->rightList, data->count, sizeof(data->rightList[0]), compareWRight);
qsort(data->bottomList, data->count, sizeof(data->bottomList[0]), compareWBottom);
/* figure the position of the window relative to the others */
data->topIndex = -1;
data->leftIndex = -1;
data->rightIndex = -1;
data->bottomIndex = -1;
if (WTOP(wwin) < WBOTTOM(data->bottomList[0])) {
data->bottomIndex = 0;
}
if (WLEFT(wwin) < WRIGHT(data->rightList[0])) {
data->rightIndex = 0;
}
if (WRIGHT(wwin) > WLEFT(data->leftList[0])) {
data->leftIndex = 0;
}
if (WBOTTOM(wwin) > WTOP(data->topList[0])) {
data->topIndex = 0;
}
for (i = 0; i < data->count; i++) {
if (WTOP(wwin) >= WBOTTOM(data->bottomList[i])) {
data->bottomIndex = i + 1;
}
if (WLEFT(wwin) >= WRIGHT(data->rightList[i])) {
data->rightIndex = i + 1;
}
if (WRIGHT(wwin) <= WLEFT(data->leftList[i])) {
data->leftIndex = i + 1;
}
if (WBOTTOM(wwin) <= WTOP(data->topList[i])) {
data->topIndex = i + 1;
}
}
}
static void initMoveData(WWindow * wwin, MoveData * data)
{
int i;
WWindow *tmp;
memset(data, 0, sizeof(MoveData));
for (i = 0, tmp = wwin->screen_ptr->focused_window; tmp != NULL; tmp = tmp->prev, i++) ;
if (i > 1) {
data->topList = wmalloc(sizeof(WWindow *) * i);
data->leftList = wmalloc(sizeof(WWindow *) * i);
data->rightList = wmalloc(sizeof(WWindow *) * i);
data->bottomList = wmalloc(sizeof(WWindow *) * i);
updateMoveData(wwin, data);
}
data->realX = wwin->frame_x;
data->realY = wwin->frame_y;
data->calcX = wwin->frame_x;
data->calcY = wwin->frame_y;
data->winWidth = wwin->frame->core->width + (HAS_BORDER_WITH_SELECT(wwin) ? 2 * wwin->screen_ptr->frame_border_width : 0);
data->winHeight = wwin->frame->core->height + (HAS_BORDER_WITH_SELECT(wwin) ? 2 * wwin->screen_ptr->frame_border_width : 0);
data->snap = SNAP_NONE;
}
static Bool checkWorkspaceChange(WWindow * wwin, MoveData * data, Bool opaqueMove)
{
WScreen *scr = wwin->screen_ptr;
Bool changed = False;
if (data->mouseX <= 1) {
if (scr->current_workspace > 0) {
crossWorkspace(scr, wwin, opaqueMove, scr->current_workspace - 1, True);
changed = True;
data->rubCount = 0;
} else if (scr->current_workspace == 0 && wPreferences.ws_cycle) {
crossWorkspace(scr, wwin, opaqueMove, scr->workspace_count - 1, True);
changed = True;
data->rubCount = 0;
}
} else if (data->mouseX >= scr->scr_width - 2) {
if (scr->current_workspace == scr->workspace_count - 1) {
if (wPreferences.ws_cycle || scr->workspace_count == MAX_WORKSPACES) {
crossWorkspace(scr, wwin, opaqueMove, 0, False);
changed = True;
data->rubCount = 0;
}
/* if user insists on trying to go to next workspace even when
* it's already the last, create a new one */
else if (data->omouseX == data->mouseX && wPreferences.ws_advance) {
/* detect user "rubbing" the window against the edge */
if (data->rubCount > 0 && data->omouseY - data->mouseY > MOVE_THRESHOLD) {
data->rubCount = -(data->rubCount + 1);
} else if (data->rubCount <= 0 && data->mouseY - data->omouseY > MOVE_THRESHOLD) {
data->rubCount = -data->rubCount + 1;
}
}
/* create a new workspace */
if (abs(data->rubCount) > 2) {
/* go to next workspace */
wWorkspaceNew(scr);
crossWorkspace(scr, wwin, opaqueMove, scr->current_workspace + 1, False);
changed = True;
data->rubCount = 0;
}
} else if (scr->current_workspace < scr->workspace_count) {
/* go to next workspace */
crossWorkspace(scr, wwin, opaqueMove, scr->current_workspace + 1, False);
changed = True;
data->rubCount = 0;
}
} else {
data->rubCount = 0;
}
return changed;
}
static void
updateWindowPosition(WWindow * wwin, MoveData * data, Bool doResistance,
Bool opaqueMove, int newMouseX, int newMouseY)
{
WScreen *scr = wwin->screen_ptr;
int dx, dy; /* how much mouse moved */
int winL, winR, winT, winB; /* requested new window position */
int newX, newY; /* actual new window position */
Bool hresist, vresist;
Bool attract;
hresist = False;
vresist = False;
/* check the direction of the movement */
dx = newMouseX - data->mouseX;
dy = newMouseY - data->mouseY;
data->omouseX = data->mouseX;
data->omouseY = data->mouseY;
data->mouseX = newMouseX;
data->mouseY = newMouseY;
winL = data->calcX + dx;
winR = data->calcX + data->winWidth + dx;
winT = data->calcY + dy;
winB = data->calcY + data->winHeight + dy;
newX = data->realX;
newY = data->realY;
if (doResistance) {
int l_edge, r_edge;
int edge_l, edge_r;
int t_edge, b_edge;
int edge_t, edge_b;
int resist;
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
attract = wPreferences.attract;
/* horizontal movement: check horizontal edge resistances */
if (dx || dy) {
WMRect rect;
int i, head;
/* window is the leftmost window: check against screen edge */
/* Add inter head resistance 1/2 (if needed) */
head = wGetHeadForPointerLocation(scr);
rect = wGetRectForHead(scr, head);
l_edge = WMAX(scr->totalUsableArea[head].x1, rect.pos.x);
edge_l = l_edge - resist;
edge_r = WMIN(scr->totalUsableArea[head].x2, rect.pos.x + rect.size.width);
r_edge = edge_r + resist;
/* 1 */
if ((data->rightIndex >= 0) && (data->rightIndex <= data->count)) {
WWindow *looprw;
for (i = data->rightIndex - 1; i >= 0; i--) {
looprw = data->rightList[i];
if (!(data->realY > WBOTTOM(looprw)
|| (data->realY + data->winHeight) < WTOP(looprw))) {
if (attract || ((data->realX < (WRIGHT(looprw) + 2)) && dx < 0)) {
l_edge = WRIGHT(looprw) + 1;
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
}
break;
}
}
if (attract) {
for (i = data->rightIndex; i < data->count; i++) {
looprw = data->rightList[i];
if (!(data->realY > WBOTTOM(looprw)
|| (data->realY + data->winHeight) < WTOP(looprw))) {
r_edge = WRIGHT(looprw) + 1;
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
break;
}
}
}
}
if ((data->leftIndex >= 0) && (data->leftIndex <= data->count)) {
WWindow *looprw;
for (i = data->leftIndex - 1; i >= 0; i--) {
looprw = data->leftList[i];
if (!(data->realY > WBOTTOM(looprw)
|| (data->realY + data->winHeight) < WTOP(looprw))) {
if (attract
|| (((data->realX + data->winWidth) > (WLEFT(looprw) - 1))
&& dx > 0)) {
edge_r = WLEFT(looprw);
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
}
break;
}
}
if (attract)
for (i = data->leftIndex; i < data->count; i++) {
looprw = data->leftList[i];
if (!(data->realY > WBOTTOM(looprw)
|| (data->realY + data->winHeight) < WTOP(looprw))) {
edge_l = WLEFT(looprw);
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
break;
}
}
}
/*
printf("%d %d\n",winL,winR);
printf("l_ %d r_ %d _l %d _r %d\n",l_edge,r_edge,edge_l,edge_r);
*/
if ((winL - l_edge) < (r_edge - winL)) {
if (resist > 0) {
if ((attract && winL <= l_edge + resist && winL >= l_edge - resist)
|| (dx < 0 && winL <= l_edge && winL >= l_edge - resist)) {
newX = l_edge;
hresist = True;
}
}
} else {
if (resist > 0 && attract && winL >= r_edge - resist && winL <= r_edge + resist) {
newX = r_edge;
hresist = True;
}
}
if ((winR - edge_l) < (edge_r - winR)) {
if (resist > 0 && attract && winR <= edge_l + resist && winR >= edge_l - resist) {
newX = edge_l - data->winWidth;
hresist = True;
}
} else {
if (resist > 0) {
if ((attract && winR >= edge_r - resist && winR <= edge_r + resist)
|| (dx > 0 && winR >= edge_r && winR <= edge_r + resist)) {
newX = edge_r - data->winWidth;
hresist = True;
}
}
}
/* VeRT */
/* Add inter head resistance 2/2 (if needed) */
t_edge = WMAX(scr->totalUsableArea[head].y1, rect.pos.y);
edge_t = t_edge - resist;
edge_b = WMIN(scr->totalUsableArea[head].y2, rect.pos.y + rect.size.height);
b_edge = edge_b + resist;
if ((data->bottomIndex >= 0) && (data->bottomIndex <= data->count)) {
WWindow *looprw;
for (i = data->bottomIndex - 1; i >= 0; i--) {
looprw = data->bottomList[i];
if (!(data->realX > WRIGHT(looprw)
|| (data->realX + data->winWidth) < WLEFT(looprw))) {
if (attract || ((data->realY < (WBOTTOM(looprw) + 2)) && dy < 0)) {
t_edge = WBOTTOM(looprw) + 1;
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
}
break;
}
}
if (attract) {
for (i = data->bottomIndex; i < data->count; i++) {
looprw = data->bottomList[i];
if (!(data->realX > WRIGHT(looprw)
|| (data->realX + data->winWidth) < WLEFT(looprw))) {
b_edge = WBOTTOM(looprw) + 1;
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
break;
}
}
}
}
if ((data->topIndex >= 0) && (data->topIndex <= data->count)) {
WWindow *looprw;
for (i = data->topIndex - 1; i >= 0; i--) {
looprw = data->topList[i];
if (!(data->realX > WRIGHT(looprw)
|| (data->realX + data->winWidth) < WLEFT(looprw))) {
if (attract
|| (((data->realY + data->winHeight) > (WTOP(looprw) - 1))
&& dy > 0)) {
edge_b = WTOP(looprw);
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
}
break;
}
}
if (attract)
for (i = data->topIndex; i < data->count; i++) {
looprw = data->topList[i];
if (!(data->realX > WRIGHT(looprw)
|| (data->realX + data->winWidth) < WLEFT(looprw))) {
edge_t = WTOP(looprw);
resist = WIN_RESISTANCE(wPreferences.edge_resistance);
break;
}
}
}
if ((winT - t_edge) < (b_edge - winT)) {
if (resist > 0) {
if ((attract && winT <= t_edge + resist && winT >= t_edge - resist)
|| (dy < 0 && winT <= t_edge && winT >= t_edge - resist)) {
newY = t_edge;
vresist = True;
}
}
} else {
if (resist > 0 && attract && winT >= b_edge - resist && winT <= b_edge + resist) {
newY = b_edge;
vresist = True;
}
}
if ((winB - edge_t) < (edge_b - winB)) {
if (resist > 0 && attract && winB <= edge_t + resist && winB >= edge_t - resist) {
newY = edge_t - data->winHeight;
vresist = True;
}
} else {
if (resist > 0) {
if ((attract && winB >= edge_b - resist && winB <= edge_b + resist)
|| (dy > 0 && winB >= edge_b && winB <= edge_b + resist)) {
newY = edge_b - data->winHeight;
vresist = True;
}
}
}
}
/* END VeRT */
}
/* update window position */
data->calcX += dx;
data->calcY += dy;
if (((dx > 0 && data->calcX - data->realX > 0)
|| (dx < 0 && data->calcX - data->realX < 0)) && !hresist)
newX = data->calcX;
if (((dy > 0 && data->calcY - data->realY > 0)
|| (dy < 0 && data->calcY - data->realY < 0)) && !vresist)
newY = data->calcY;
if (data->realX != newX || data->realY != newY) {
if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
showPosition(wwin, data->realX, data->realY);
}
if (opaqueMove) {
doWindowMove(wwin, scr->selected_windows, newX - wwin->frame_x, newY - wwin->frame_y);
} else {
/* erase frames */
drawFrames(wwin, scr->selected_windows,
data->realX - wwin->frame_x, data->realY - wwin->frame_y);
}
if (!scr->selected_windows && wPreferences.move_display == WDIS_FRAME_CENTER) {
moveGeometryDisplayCentered(scr, newX + data->winWidth / 2, newY + data->winHeight / 2);
}
if (!opaqueMove) {
/* draw frames */
drawFrames(wwin, scr->selected_windows, newX - wwin->frame_x, newY - wwin->frame_y);
}
if (!scr->selected_windows) {
showPosition(wwin, newX, newY);
}
}
/* recalc relative window position */
if (doResistance && (data->realX != newX || data->realY != newY)) {
updateResistance(data, newX, newY);
}
data->realX = newX;
data->realY = newY;
}
static void draw_snap_frame(WWindow *wwin, int direction)
{
WScreen *scr;
int head, x, y;
unsigned int width, height;
WMRect rect;
scr = wwin->screen_ptr;
head = wGetHeadForWindow(wwin);
rect = wGetRectForHead(scr, head);
x = rect.pos.x;
y = rect.pos.y;
width = rect.size.width;
height = rect.size.height;
switch (direction) {
case SNAP_LEFT:
width /= 2;
break;
case SNAP_RIGHT:
width /= 2;
x += width;
break;
case SNAP_TOP:
if (!wPreferences.snap_to_top_maximizes_fullscreen)
height /= 2;
break;
case SNAP_BOTTOM:
height /= 2;
y += height;
break;
case SNAP_TOPLEFT:
width /= 2;
height /= 2;
break;
case SNAP_TOPRIGHT:
width /= 2;
height /= 2;
x += width;
break;
case SNAP_BOTTOMLEFT:
width /= 2;
height /= 2;
y += height;
break;
case SNAP_BOTTOMRIGHT:
width /= 2;
height /= 2;
x += width;
y += height;
break;
}
drawTransparentFrame(wwin, x, y, width, height);
}
static int get_snap_direction(WScreen *scr, int x, int y)
{
int edge, corner;
edge = wPreferences.snap_edge_detect;
corner = wPreferences.snap_corner_detect;
if (x < corner && y < corner)
return SNAP_TOPLEFT;
if (x < corner && y >= scr->scr_height - corner)
return SNAP_BOTTOMLEFT;
if (x < edge)
return SNAP_LEFT;
if (x >= scr->scr_width - corner && y < corner)
return SNAP_TOPRIGHT;
if (x >= scr->scr_width - corner && y >= scr->scr_height - corner)
return SNAP_BOTTOMRIGHT;
if (x >= scr->scr_width - edge)
return SNAP_RIGHT;
if (y < edge)
return SNAP_TOP;
if (y >= scr->scr_height - edge)
return SNAP_BOTTOM;
return SNAP_NONE;
}
static void do_snap(WWindow *wwin, MoveData *data, Bool opaqueMove)
{
int directions;
WScreen *scr;
directions = 0;
scr = wwin->screen_ptr;
/* erase frames */
if (!opaqueMove)
drawFrames(wwin, scr->selected_windows, data->realX - wwin->frame_x, data->realY - wwin->frame_y);
draw_snap_frame(wwin, data->snap);
switch (data->snap) {
case SNAP_NONE:
return;
case SNAP_LEFT:
directions = MAX_VERTICAL | MAX_LEFTHALF;
break;
case SNAP_RIGHT:
directions = MAX_VERTICAL | MAX_RIGHTHALF;
break;
case SNAP_TOP:
if (wPreferences.snap_to_top_maximizes_fullscreen)
directions = MAX_HORIZONTAL | MAX_VERTICAL;
else
directions = MAX_HORIZONTAL | MAX_TOPHALF;
break;
case SNAP_BOTTOM:
directions = MAX_HORIZONTAL | MAX_BOTTOMHALF;
break;
case SNAP_TOPLEFT:
directions = MAX_TOPHALF | MAX_LEFTHALF;
break;
case SNAP_TOPRIGHT:
directions = MAX_TOPHALF | MAX_RIGHTHALF;
break;
case SNAP_BOTTOMLEFT:
directions = MAX_BOTTOMHALF | MAX_LEFTHALF;
break;
case SNAP_BOTTOMRIGHT:
directions = MAX_BOTTOMHALF | MAX_RIGHTHALF;
break;
}
if (directions)
handleMaximize(wwin, directions);
data->snap = SNAP_NONE;
}
#define _KS KEY_CONTROL_WINDOW_WEIGHT
#define MOVABLE_BIT 0x01
#define RESIZABLE_BIT 0x02
int wKeyboardMoveResizeWindow(WWindow * wwin)
{
WScreen *scr = wwin->screen_ptr;
WNativeWindow root = scr->root_win;
WMEvent event;
if (!wwin->frame)
return 0; /* Phase 72: Wayland — no frame for keyboard move/resize */
int w = wwin->frame->core->width;
int h = wwin->frame->core->height;
int scr_width = wwin->screen_ptr->scr_width;
int scr_height = wwin->screen_ptr->scr_height;
int vert_border = wwin->frame->top_width + wwin->frame->bottom_width;
int src_x = wwin->frame_x;
int src_y = wwin->frame_y;
int original_w = w;
int original_h = h;
int done, off_x, off_y, ww, wh;
int kspeed = _KS;
int opaqueMoveResize = wPreferences.opaque_move_resize_keyboard;
unsigned long lastTime = 0;
unsigned int shiftl, shiftr, ctrlmode;
unsigned long keysym = 0;
int moment = 0;
int modes = ((IS_MOVABLE(wwin) ? MOVABLE_BIT : 0) | (IS_RESIZABLE(wwin) ? RESIZABLE_BIT : 0));
int head = ((wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1)
? wGetHeadForWindow(wwin)
: scr->primary_head);
shiftl = wm_backend->keysym_to_keycode(WM_KEY_SHIFT_L);
shiftr = wm_backend->keysym_to_keycode(WM_KEY_SHIFT_R);
ctrlmode = done = off_x = off_y = 0;
if (modes == RESIZABLE_BIT) {
ctrlmode = 1;
}
wm_backend->event_sync(0);
wusleep(10000);
wm_backend->keyboard_grab(root);
if (!wwin->flags.selected) {
wUnselectWindows(scr);
}
wm_backend->server_grab();
wm_backend->pointer_grab(scr->root_win, WNATIVE_NONE,
PointerMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_NORMAL, True, 0);
if (!opaqueMoveResize) {
if (wwin->flags.shaded || scr->selected_windows) {
if (scr->selected_windows)
drawFrames(wwin, scr->selected_windows, off_x, off_y);
else
drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
} else {
drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
}
}
if ((wwin->flags.shaded || scr->selected_windows) && (!scr->selected_windows)) {
mapPositionDisplay(wwin, src_x, src_y, w, h);
}
ww = w;
wh = h;
while (1) {
/*
looper.ox=off_x;
looper.oy=off_y;
*/
do {
WMMaskEvent(scr->wmscreen, KeyPressMask | ButtonReleaseMask
| ButtonPressMask | ExposureMask, &event);
if (event.type == WME_EXPOSE) {
WMHandleEvent(&event);
}
} while (event.type == WME_EXPOSE);
if (!opaqueMoveResize) {
if (wwin->flags.shaded || scr->selected_windows) {
if (scr->selected_windows)
drawFrames(wwin, scr->selected_windows, off_x, off_y);
else
drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
/*** I HATE EDGE RESISTANCE - ]d ***/
} else {
drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, ww, wh);
}
}
if (ctrlmode)
showGeometry(wwin, src_x + off_x, src_y + off_y, src_x + off_x + ww, src_y + off_y + wh,
0);
wm_backend->server_ungrab();
wm_backend->event_sync(0);
switch (event.type) {
case WME_KEY_PRESS:
/* accelerate */
if (event.u.key.time - lastTime > 50) {
kspeed /= (1 + (event.u.key.time - lastTime) / 100);
} else {
if (kspeed < 20) {
kspeed++;
}
}
if (kspeed < _KS)
kspeed = _KS;
lastTime = event.u.key.time;
if (modes == (MOVABLE_BIT | RESIZABLE_BIT)) {
if ((event.u.key.state & ControlMask) && !wwin->flags.shaded) {
ctrlmode = 1;
wUnselectWindows(scr);
} else {
ctrlmode = 0;
}
}
if (event.u.key.keycode == shiftl || event.u.key.keycode == shiftr) {
if (ctrlmode)
cycleGeometryDisplay(wwin, src_x + off_x, src_y + off_y, ww, wh, 0);
else
cyclePositionDisplay(wwin, src_x + off_x, src_y + off_y, ww, wh);
} else {
keysym = wm_backend->keysym_from_keycode(event.u.key.keycode, 0);
switch (keysym) {
case WM_KEY_RETURN:
case WM_KEY_KP_ENTER:
done = 2;
break;
case WM_KEY_ESCAPE:
done = 1;
break;
case WM_KEY_UP:
case WM_KEY_KP_UP:
case 'k':
if (ctrlmode) {
if (moment != UP)
h = wh;
h -= kspeed;
moment = UP;
if (h < 1)
h = 1;
} else
off_y -= kspeed;
break;
case WM_KEY_DOWN:
case WM_KEY_KP_DOWN:
case 'j':
if (ctrlmode) {
if (moment != DOWN)
h = wh;
h += kspeed;
moment = DOWN;
} else
off_y += kspeed;
break;
case WM_KEY_LEFT:
case WM_KEY_KP_LEFT:
case 'h':
if (ctrlmode) {
if (moment != LEFT)
w = ww;
w -= kspeed;
if (w < 1)
w = 1;
moment = LEFT;
} else
off_x -= kspeed;
break;
case WM_KEY_RIGHT:
case WM_KEY_KP_RIGHT:
case 'l':
if (ctrlmode) {
if (moment != RIGHT)
w = ww;
w += kspeed;
moment = RIGHT;
} else
off_x += kspeed;
break;
}
ww = w;
wh = h;
wh -= vert_border;
wWindowConstrainSize(wwin, (unsigned int *)&ww, (unsigned int *)&wh);
wh += vert_border;
if (wPreferences.ws_cycle) {
if (src_x + off_x + ww < 20) {
if (!scr->current_workspace)
wWorkspaceChange(scr, scr->workspace_count - 1);
else
wWorkspaceChange(scr, scr->current_workspace - 1);
off_x += scr_width;
} else if (src_x + off_x + 20 > scr_width) {
if (scr->current_workspace == scr->workspace_count - 1)
wWorkspaceChange(scr, 0);
else
wWorkspaceChange(scr, scr->current_workspace + 1);
off_x -= scr_width;
}
} else {
if (src_x + off_x + ww < 20)
off_x = 20 - ww - src_x;
else if (src_x + off_x + 20 > scr_width)
off_x = scr_width - 20 - src_x;
}
if (src_y + off_y + wh < 20) {
off_y = 20 - wh - src_y;
} else if (src_y + off_y + 20 > scr_height) {
off_y = scr_height - 20 - src_y;
}
}
break;
case WME_BUTTON_PRESS:
case WME_BUTTON_RELEASE:
done = 1;
break;
case WME_EXPOSE:
WMHandleEvent(&event);
wm_backend->event_flush_expose();
break;
default:
WMHandleEvent(&event);
break;
}
wm_backend->server_grab();
/*xxx */
if (wwin->flags.shaded && !scr->selected_windows) {
moveGeometryDisplayCentered(scr, src_x + off_x + w / 2, src_y + off_y + h / 2);
} else {
if (ctrlmode) {
WMUnmapWidget(scr->gview);
mapGeometryDisplay(wwin, src_x + off_x, src_y + off_y, ww, wh);
} else if (!scr->selected_windows) {
WMUnmapWidget(scr->gview);
mapPositionDisplay(wwin, src_x + off_x, src_y + off_y, ww, wh);
}
}
if (!opaqueMoveResize) {
if (wwin->flags.shaded || scr->selected_windows) {
if (scr->selected_windows)
drawFrames(wwin, scr->selected_windows, off_x, off_y);
else
drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
} else {
drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, ww, wh);
}
}
if (ctrlmode) {
showGeometry(wwin, src_x + off_x, src_y + off_y, src_x + off_x + ww, src_y + off_y + wh,
0);
} else if (!scr->selected_windows)
showPosition(wwin, src_x + off_x, src_y + off_y);
if (opaqueMoveResize) {
wm_backend->server_ungrab();
wWindowConfigure(wwin, src_x + off_x, src_y + off_y, ww, wh - vert_border);
};
if (done) {
if (!opaqueMoveResize) { /* ctrlmode => resize */
if (wwin->flags.shaded || scr->selected_windows) {
if (scr->selected_windows)
drawFrames(wwin, scr->selected_windows, off_x, off_y);
else
drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, w, h);
} else {
drawTransparentFrame(wwin, src_x + off_x, src_y + off_y, ww, wh);
}
}
if (ctrlmode) {
showGeometry(wwin, src_x + off_x, src_y + off_y, src_x + off_x + ww,
src_y + off_y + wh, 0);
WMUnmapWidget(scr->gview);
} else
WMUnmapWidget(scr->gview);
wm_backend->keyboard_ungrab();
wm_backend->pointer_ungrab(0);
wm_backend->server_ungrab();
if (done == 2) {
if (wwin->flags.shaded || scr->selected_windows) {
if (!scr->selected_windows) {
wWindowMove(wwin, src_x + off_x, src_y + off_y);
wWindowSynthConfigureNotify(wwin);
} else {
WMArrayIterator iter;
WWindow *foo;
doWindowMove(wwin, scr->selected_windows, off_x, off_y);
WM_ITERATE_ARRAY(scr->selected_windows, foo, iter) {
wWindowSynthConfigureNotify(foo);
}
}
} else {
if (ww != original_w)
wwin->flags.maximized &= ~(MAX_HORIZONTAL | MAX_TOPHALF | MAX_BOTTOMHALF | MAX_MAXIMUS | MAX_CENTRAL);
if (wh != original_h)
wwin->flags.maximized &= ~(MAX_VERTICAL | MAX_LEFTHALF | MAX_RIGHTHALF | MAX_MAXIMUS | MAX_CENTRAL);
wWindowConfigure(wwin, src_x + off_x, src_y + off_y, ww, wh - vert_border);
wWindowSynthConfigureNotify(wwin);
}
wWindowChangeWorkspace(wwin, scr->current_workspace);
wSetFocusTo(scr, wwin);
}
if (wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1 &&
head != wGetHeadForWindow(wwin)) {
wArrangeIcons(scr, True);
}
update_saved_geometry(wwin);
return 1;
}
}
}
/*
*----------------------------------------------------------------------
* wMouseMoveWindow--
* Move the named window and the other selected ones (if any),
* interactively. Also shows the position of the window, if only one
* window is being moved.
* If the window is not on the selected window list, the selected
* windows are deselected.
* If shift is pressed during the operation, the position display
* is changed to another type.
*
* Returns:
* True if the window was moved, False otherwise.
*
* Side effects:
* The window(s) position is changed, and the client(s) are
* notified about that.
* The position display configuration may be changed.
*----------------------------------------------------------------------
*/
int wMouseMoveWindow(WWindow * wwin, XEvent * ev)
{
WScreen *scr = wwin->screen_ptr;
WMEvent event;
WNativeWindow root = scr->root_win;
unsigned int shiftl, shiftr;
int done = 0;
int started = 0;
int warped = 0;
/* This needs not to change while moving, else bad things can happen */
int opaqueMove = wPreferences.opaque_move;
MoveData moveData;
int head = ((wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1)
? wGetHeadForWindow(wwin)
: scr->primary_head);
if (!IS_MOVABLE(wwin))
return False;
if (!wwin->frame)
return False; /* Phase 72: Wayland — no frame for mouse move */
if (wPreferences.opaque_move && !wPreferences.use_saveunders) {
wm_backend->window_set_save_under(wwin->frame->core->window, 1);
}
initMoveData(wwin, &moveData);
moveData.mouseX = ev->xmotion.x_root;
moveData.mouseY = ev->xmotion.y_root;
if (!wwin->flags.selected) {
/* this window is not selected, unselect others and move only wwin */
wUnselectWindows(scr);
}
shiftl = wm_backend->keysym_to_keycode(WM_KEY_SHIFT_L);
shiftr = wm_backend->keysym_to_keycode(WM_KEY_SHIFT_R);
while (!done) {
if (warped) {
/* XWarpPointer() doesn't seem to generate Motion events, so
* we've got to simulate them */
wm_backend->pointer_query(root, &event.u.motion.x_root,
&event.u.motion.y_root);
} else {
WMMaskEvent(scr->wmscreen, KeyPressMask | ButtonMotionMask
| PointerMotionHintMask
| ButtonReleaseMask | ButtonPressMask | ExposureMask, &event);
if (event.type == WME_MOTION) {
/* compress MotionNotify events */
wm_backend->event_drain_motion();
if (!checkMouseSamplingRate(&event))
continue;
}
}
switch (event.type) {
case WME_KEY_PRESS:
if ((event.u.key.keycode == shiftl || event.u.key.keycode == shiftr)
&& started && !scr->selected_windows) {
if (!opaqueMove) {
drawFrames(wwin, scr->selected_windows,
moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
}
if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
showPosition(wwin, moveData.realX, moveData.realY);
wm_backend->server_ungrab();
}
cyclePositionDisplay(wwin, moveData.realX, moveData.realY,
moveData.winWidth, moveData.winHeight);
if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
wm_backend->server_grab();
showPosition(wwin, moveData.realX, moveData.realY);
}
if (!opaqueMove) {
drawFrames(wwin, scr->selected_windows,
moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
}
/*} else {
WMHandleEvent(&event); this causes problems needs fixing */
}
break;
case WME_MOTION:
if (IS_RESIZABLE(wwin) && wPreferences.window_snapping) {
int snap_direction;
snap_direction = get_snap_direction(scr, moveData.mouseX, moveData.mouseY);
if (!wPreferences.no_autowrap &&
snap_direction != SNAP_TOP &&
snap_direction != SNAP_BOTTOM)
snap_direction = SNAP_NONE;
if (moveData.snap != snap_direction) {
/* erase old frame */
if (moveData.snap)
draw_snap_frame(wwin, moveData.snap);
/* draw new frame */
if (snap_direction)
draw_snap_frame(wwin, snap_direction);
moveData.snap = snap_direction;
}
}
if (started) {
/* erase snap frame */
if (moveData.snap)
draw_snap_frame(wwin, moveData.snap);
updateWindowPosition(wwin, &moveData,
scr->selected_windows == NULL
&& wPreferences.edge_resistance > 0,
opaqueMove, event.u.motion.x_root, event.u.motion.y_root);
/* redraw snap frame */
if (moveData.snap)
draw_snap_frame(wwin, moveData.snap);
if (!warped && !wPreferences.no_autowrap) {
int oldWorkspace = scr->current_workspace;
if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
showPosition(wwin, moveData.realX, moveData.realY);
wm_backend->server_ungrab();
}
if (!opaqueMove) {
drawFrames(wwin, scr->selected_windows,
moveData.realX - wwin->frame_x,
moveData.realY - wwin->frame_y);
}
if (checkWorkspaceChange(wwin, &moveData, opaqueMove)) {
if (scr->current_workspace != oldWorkspace
&& wPreferences.edge_resistance > 0
&& scr->selected_windows == NULL)
updateMoveData(wwin, &moveData);
warped = 1;
}
if (!opaqueMove) {
drawFrames(wwin, scr->selected_windows,
moveData.realX - wwin->frame_x,
moveData.realY - wwin->frame_y);
}
if (wPreferences.move_display == WDIS_NEW && !scr->selected_windows) {
wm_backend->event_sync(0);
showPosition(wwin, moveData.realX, moveData.realY);
wm_backend->server_grab();
}
} else {
warped = 0;
}
} else if (abs(ev->xmotion.x_root - event.u.motion.x_root) >= MOVE_THRESHOLD
|| abs(ev->xmotion.y_root - event.u.motion.y_root) >= MOVE_THRESHOLD) {
if (wwin->flags.maximized) {
if (wPreferences.drag_maximized_window == DRAGMAX_RESTORE) {
float titlebar_ratio;
int new_x, new_y;
titlebar_ratio = (moveData.mouseX - wwin->frame_x) /
(float)wwin->frame->core->width;
new_y = wwin->frame_y;
wUnmaximizeWindow(wwin);
new_x = moveData.mouseX - titlebar_ratio * wwin->frame->core->width;
wWindowMove(wwin, new_x, new_y);
moveData.realX = moveData.calcX = wwin->frame_x;
moveData.realY = moveData.calcY = wwin->frame_y;
}
if (wPreferences.drag_maximized_window == DRAGMAX_UNMAXIMIZE) {
wwin->flags.maximized = 0;
wwin->flags.old_maximized = 0;
}
}
wm_backend->pointer_change_grab(
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_MOVE, 0);
started = 1;
wm_backend->keyboard_grab(root);
if (!scr->selected_windows)
mapPositionDisplay(wwin, moveData.realX, moveData.realY,
moveData.winWidth, moveData.winHeight);
if (started && !opaqueMove)
drawFrames(wwin, scr->selected_windows, 0, 0);
if (!opaqueMove || (wPreferences.move_display == WDIS_NEW
&& !scr->selected_windows)) {
wm_backend->server_grab();
if (wPreferences.move_display == WDIS_NEW)
showPosition(wwin, moveData.realX, moveData.realY);
}
}
break;
case WME_BUTTON_PRESS:
break;
case WME_BUTTON_RELEASE:
if (event.u.button.button != ev->xbutton.button)
break;
if (started) {
if (moveData.snap)
do_snap(wwin, &moveData, opaqueMove);
else if (!opaqueMove) {
drawFrames(wwin, scr->selected_windows,
moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
wm_backend->event_sync(0);
doWindowMove(wwin, scr->selected_windows,
moveData.realX - wwin->frame_x,
moveData.realY - wwin->frame_y);
}
#ifndef CONFIGURE_WINDOW_WHILE_MOVING
wWindowSynthConfigureNotify(wwin);
#endif
wm_backend->keyboard_ungrab();
wm_backend->server_ungrab();
if (!opaqueMove) {
wWindowChangeWorkspace(wwin, scr->current_workspace);
wSetFocusTo(scr, wwin);
}
if (wPreferences.move_display == WDIS_NEW)
showPosition(wwin, moveData.realX, moveData.realY);
/* discard all enter/leave events that happened until
* the time the button was released */
wm_backend->event_discard_enter_leave_until(
(unsigned long)event.u.button.time);
if (!scr->selected_windows) {
/* get rid of the geometry window */
WMUnmapWidget(scr->gview);
}
}
done = 1;
break;
default:
if (started && !opaqueMove) {
drawFrames(wwin, scr->selected_windows,
moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
wm_backend->server_ungrab();
WMHandleEvent(&event);
wm_backend->event_sync(0);
wm_backend->server_grab();
drawFrames(wwin, scr->selected_windows,
moveData.realX - wwin->frame_x, moveData.realY - wwin->frame_y);
} else {
WMHandleEvent(&event);
}
break;
}
}
if (wPreferences.opaque_move && !wPreferences.use_saveunders) {
wm_backend->window_set_save_under(wwin->frame->core->window, 0);
}
freeMoveData(&moveData);
if (started && wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1 &&
head != wGetHeadForWindow(wwin)) {
wArrangeIcons(scr, True);
}
if (started)
update_saved_geometry(wwin);
return started;
}
#define RESIZEBAR 1
#define HCONSTRAIN 2
static int getResizeDirection(WWindow * wwin, int x, int y, int dy, int flags)
{
int w = wwin->frame->core->width - 1;
int cw = wwin->frame->resizebar_corner_width;
int dir;
/* if not resizing through the resizebar */
if (!(flags & RESIZEBAR)) {
int xdir = (abs(x) < (wwin->client.width / 2)) ? LEFT : RIGHT;
int ydir = (abs(y) < (wwin->client.height / 2)) ? UP : DOWN;
/* How much resize space is allowed */
int spacew = wwin->client.width / 3;
int spaceh = wwin->client.height / 3;
/* Determine where x fits */
if ((abs(x) > wwin->client.width/2 - spacew/2) &&
(abs(x) < wwin->client.width/2 + spacew/2)) {
/* Resize vertically */
xdir = 0;
} else if ((abs(y) > wwin->client.height/2 - spaceh/2) &&
(abs(y) < wwin->client.height/2 + spaceh/2)) {
/* Resize horizontally */
ydir = 0;
}
return (xdir | ydir);
}
/* window is too narrow. allow diagonal resize */
if (cw * 2 >= w) {
int ydir;
if (flags & HCONSTRAIN)
ydir = 0;
else
ydir = DOWN;
if (x < cw)
return (LEFT | ydir);
else
return (RIGHT | ydir);
}
/* vertical resize */
if ((x > cw) && (x < w - cw))
return DOWN;
if (x < cw)
dir = LEFT;
else
dir = RIGHT;
if ((abs(dy) > 0) && !(flags & HCONSTRAIN))
dir |= DOWN;
return dir;
}
void wMouseResizeWindow(WWindow * wwin, XEvent * ev)
{
WMEvent event;
WScreen *scr = wwin->screen_ptr;
WNativeWindow root = scr->root_win;
if (!wwin->frame)
return; /* Phase 72: Wayland — no frame for mouse resize */
int vert_border = wwin->frame->top_width + wwin->frame->bottom_width;
int fw = wwin->frame->core->width;
int fh = wwin->frame->core->height;
int fx = wwin->frame_x;
int fy = wwin->frame_y;
int is_resizebar = (wwin->frame->resizebar && ev->xany.window == wwin->frame->resizebar->window);
int orig_x, orig_y;
int started;
int dw, dh;
int rw = fw, rh = fh;
int rx1, ry1, rx2, ry2;
int res = 0;
unsigned int shiftl, shiftr;
int orig_fx = fx;
int orig_fy = fy;
int orig_fw = fw;
int orig_fh = fh;
int original_fw = fw;
int original_fh = fh;
int head = ((wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1)
? wGetHeadForWindow(wwin)
: scr->primary_head);
int opaqueResize = wPreferences.opaque_resize;
if (!IS_RESIZABLE(wwin))
return;
if (wwin->flags.shaded) {
wwarning("internal error: tryein");
return;
}
orig_x = ev->xbutton.x_root;
orig_y = ev->xbutton.y_root;
started = 0;
wUnselectWindows(scr);
rx1 = fx;
rx2 = fx + fw - 1;
ry1 = fy;
ry2 = fy + fh - 1;
shiftl = wm_backend->keysym_to_keycode(WM_KEY_SHIFT_L);
shiftr = wm_backend->keysym_to_keycode(WM_KEY_SHIFT_R);
while (1) {
WMMaskEvent(scr->wmscreen, KeyPressMask | ButtonMotionMask
| ButtonReleaseMask | PointerMotionHintMask | ButtonPressMask | ExposureMask, &event);
if (!checkMouseSamplingRate(&event))
continue;
switch (event.type) {
case WME_KEY_PRESS:
showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
if (!opaqueResize) {
if ((event.u.key.keycode == shiftl || event.u.key.keycode == shiftr)
&& started) {
drawTransparentFrame(wwin, fx, fy, fw, fh);
cycleGeometryDisplay(wwin, fx, fy, fw, fh, res);
drawTransparentFrame(wwin, fx, fy, fw, fh);
}
};
showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
break;
case WME_MOTION:
if (started) {
wm_backend->event_drain_motion();
dw = 0;
dh = 0;
orig_fx = fx;
orig_fy = fy;
orig_fw = fw;
orig_fh = fh;
if (res & LEFT)
dw = orig_x - event.u.motion.x_root;
else if (res & RIGHT)
dw = event.u.motion.x_root - orig_x;
if (res & UP)
dh = orig_y - event.u.motion.y_root;
else if (res & DOWN)
dh = event.u.motion.y_root - orig_y;
orig_x = event.u.motion.x_root;
orig_y = event.u.motion.y_root;
rw += dw;
rh += dh;
fw = rw;
fh = rh - vert_border;
wWindowConstrainSize(wwin, (unsigned int *)&fw, (unsigned int *)&fh);
fh += vert_border;
if (res & LEFT)
fx = rx2 - fw + 1;
else if (res & RIGHT)
fx = rx1;
if (res & UP)
fy = ry2 - fh + 1;
else if (res & DOWN)
fy = ry1;
} else if (abs(orig_x - event.u.motion.x_root) >= MOVE_THRESHOLD
|| abs(orig_y - event.u.motion.y_root) >= MOVE_THRESHOLD) {
int tx, ty;
int flags;
wm_backend->coords_translate(root, wwin->frame->core->window,
orig_x, orig_y, &tx, &ty);
/* check if resizing through resizebar */
if (is_resizebar)
flags = RESIZEBAR;
else
flags = 0;
if (is_resizebar && ((ev->xbutton.state & ShiftMask)
|| abs(orig_y - event.u.motion.y_root) < HRESIZE_THRESHOLD))
flags |= HCONSTRAIN;
res = getResizeDirection(wwin, tx, ty, orig_y - event.u.motion.y_root, flags);
if (res == (UP | LEFT))
wm_backend->pointer_change_grab(
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_TOPLEFTRESIZE, 0);
else if (res == (UP | RIGHT))
wm_backend->pointer_change_grab(
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_TOPRIGHTRESIZE, 0);
else if (res == (DOWN | LEFT))
wm_backend->pointer_change_grab(
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_BOTTOMLEFTRESIZE, 0);
else if (res == (DOWN | RIGHT))
wm_backend->pointer_change_grab(
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_BOTTOMRIGHTRESIZE, 0);
else if (res == DOWN || res == UP)
wm_backend->pointer_change_grab(
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_VERTICALRESIZE, 0);
else if (res & (DOWN | UP))
wm_backend->pointer_change_grab(
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_VERTICALRESIZE, 0);
else if (res & (LEFT | RIGHT))
wm_backend->pointer_change_grab(
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_HORIZONRESIZE, 0);
wm_backend->keyboard_grab(root);
wm_backend->server_grab();
/* Draw the resize frame for the first time. */
mapGeometryDisplay(wwin, fx, fy, fw, fh);
if (!opaqueResize)
drawTransparentFrame(wwin, fx, fy, fw, fh);
showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
started = 1;
}
if (started) {
if (!opaqueResize)
drawTransparentFrame(wwin, orig_fx, orig_fy, orig_fw, orig_fh);
if (wPreferences.size_display == WDIS_FRAME_CENTER)
moveGeometryDisplayCentered(scr, fx + fw / 2, fy + fh / 2);
if (!opaqueResize)
drawTransparentFrame(wwin, fx, fy, fw, fh);
if (fh != orig_fh || fw != orig_fw) {
if (wPreferences.size_display == WDIS_NEW)
showGeometry(wwin, orig_fx, orig_fy, orig_fx + orig_fw,
orig_fy + orig_fh, res);
showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
}
if (opaqueResize) {
/* Fist clean the geometry line */
showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
/* Now, continue drawing */
wm_backend->server_ungrab();
moveGeometryDisplayCentered(scr, fx + fw / 2, fy + fh / 2);
wWindowConfigure(wwin, fx, fy, fw, fh - vert_border);
showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
};
}
break;
case WME_BUTTON_PRESS:
break;
case WME_BUTTON_RELEASE:
if (event.u.button.button != ev->xbutton.button)
break;
if (started) {
showGeometry(wwin, fx, fy, fx + fw, fy + fh, res);
if (!opaqueResize)
drawTransparentFrame(wwin, fx, fy, fw, fh);
wm_backend->keyboard_ungrab();
WMUnmapWidget(scr->gview);
wm_backend->server_ungrab();
if (fw != original_fw)
wwin->flags.maximized &= ~(MAX_HORIZONTAL | MAX_TOPHALF | MAX_BOTTOMHALF | MAX_MAXIMUS | MAX_CENTRAL);
if (fh != original_fh)
wwin->flags.maximized &= ~(MAX_VERTICAL | MAX_LEFTHALF | MAX_RIGHTHALF | MAX_MAXIMUS | MAX_CENTRAL);
wWindowConfigure(wwin, fx, fy, fw, fh - vert_border);
wWindowSynthConfigureNotify(wwin);
}
return;
default:
WMHandleEvent(&event);
}
}
if (wPreferences.auto_arrange_icons && wXineramaHeads(scr) > 1 && head != wGetHeadForWindow(wwin))
wArrangeIcons(scr, True);
}
#undef LEFT
#undef RIGHT
#undef UP
#undef DOWN
#undef HCONSTRAIN
#undef RESIZEBAR
void wUnselectWindows(WScreen * scr)
{
WWindow *wwin;
if (!scr->selected_windows)
return;
while (WMGetArrayItemCount(scr->selected_windows)) {
wwin = WMGetFromArray(scr->selected_windows, 0);
if (wwin->flags.miniaturized && wwin->icon && wwin->icon->selected)
wIconSelect(wwin->icon);
wSelectWindow(wwin, False);
}
WMFreeArray(scr->selected_windows);
scr->selected_windows = NULL;
}
static void selectWindowsInside(WScreen * scr, int x1, int y1, int x2, int y2)
{
WWindow *tmpw;
/* select the windows and put them in the selected window list */
tmpw = scr->focused_window;
while (tmpw != NULL) {
if (!(tmpw->flags.miniaturized || tmpw->flags.hidden)) {
if ((WIN_WORKSPACE(tmpw) == scr->current_workspace || IS_OMNIPRESENT(tmpw))
&& (tmpw->frame_x >= x1) && (tmpw->frame_y >= y1)
&& (tmpw->frame->core->width + tmpw->frame_x <= x2)
&& (tmpw->frame->core->height + tmpw->frame_y <= y2)) {
wSelectWindow(tmpw, True);
}
}
tmpw = tmpw->prev;
}
}
void wSelectWindows(WScreen * scr, int xp, int yp)
{
WMEvent event;
WNativeWindow root = scr->root_win;
int w = 0, h = 0;
int x = xp, y = yp;
if (!wm_backend->pointer_grab(scr->root_win, WNATIVE_NONE,
ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
WCUR_NORMAL, False, 0)) {
return;
}
wm_backend->server_grab();
wUnselectWindows(scr);
wm_backend->overlay_draw_rect(scr, xp, yp, w, h);
while (1) {
WMMaskEvent(scr->wmscreen, ButtonReleaseMask | PointerMotionMask | ButtonPressMask, &event);
switch (event.type) {
case WME_MOTION:
wm_backend->overlay_draw_rect(scr, x, y, w, h);
x = event.u.motion.x_root;
if (x < xp) {
w = xp - x;
} else {
w = x - xp;
x = xp;
}
y = event.u.motion.y_root;
if (y < yp) {
h = yp - y;
} else {
h = y - yp;
y = yp;
}
wm_backend->overlay_draw_rect(scr, x, y, w, h);
break;
case WME_BUTTON_PRESS:
break;
case WME_BUTTON_RELEASE:
wm_backend->overlay_draw_rect(scr, x, y, w, h);
wm_backend->server_ungrab();
wm_backend->pointer_ungrab(0);
selectWindowsInside(scr, x, y, x + w, y + h);
return;
default:
WMHandleEvent(&event);
break;
}
}
}
void InteractivePlaceWindow(WWindow * wwin, int *x_ret, int *y_ret, unsigned width, unsigned height)
{
WScreen *scr = wwin->screen_ptr;
WNativeWindow root = scr->root_win;
int x, y, h = 0;
WMEvent event;
unsigned int shiftl, shiftr;
if (!wm_backend->pointer_grab(root, WNATIVE_NONE,
PointerMotionMask | ButtonPressMask,
WCUR_NORMAL, True, 0)) {
*x_ret = 0;
*y_ret = 0;
return;
}
if (HAS_TITLEBAR(wwin)) {
h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance +
TITLEBAR_EXTEND_SPACE) * 2;
if (h > wPreferences.window_title_max_height)
h = wPreferences.window_title_max_height;
if (h < wPreferences.window_title_min_height)
h = wPreferences.window_title_min_height;
height += h;
}
if (HAS_RESIZEBAR(wwin)) {
height += RESIZEBAR_HEIGHT;
}
wm_backend->keyboard_grab(root);
wm_backend->pointer_query(root, &x, &y);
mapPositionDisplay(wwin, x - width / 2, y - h / 2, width, height);
drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
shiftl = wm_backend->keysym_to_keycode(WM_KEY_SHIFT_L);
shiftr = wm_backend->keysym_to_keycode(WM_KEY_SHIFT_R);
while (1) {
WMMaskEvent(scr->wmscreen, PointerMotionMask | ButtonPressMask | ExposureMask | KeyPressMask, &event);
if (!checkMouseSamplingRate(&event))
continue;
switch (event.type) {
case WME_KEY_PRESS:
if ((event.u.key.keycode == shiftl)
|| (event.u.key.keycode == shiftr)) {
drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
cyclePositionDisplay(wwin, x - width / 2, y - h / 2, width, height);
drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
}
break;
case WME_MOTION:
drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
x = event.u.motion.x_root;
y = event.u.motion.y_root;
if (wPreferences.move_display == WDIS_FRAME_CENTER)
moveGeometryDisplayCentered(scr, x, y + (height - h) / 2);
showPosition(wwin, x - width / 2, y - h / 2);
drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
break;
case WME_BUTTON_PRESS:
drawTransparentFrame(wwin, x - width / 2, y - h / 2, width, height);
wm_backend->event_sync(0);
*x_ret = x - width / 2;
*y_ret = y - h / 2;
wm_backend->pointer_ungrab(0);
wm_backend->keyboard_ungrab();
/* get rid of the geometry window */
WMUnmapWidget(scr->gview);
return;
default:
WMHandleEvent(&event);
break;
}
}
}