2
* Copyright © 2005 Novell, Inc.
3
* Copyright © 2012 Canonical Ltd.
5
* Permission to use, copy, modify, distribute, and sell this software
6
* and its documentation for any purpose is hereby granted without
7
* fee, provided that the above copyright notice appear in all copies
8
* and that both that copyright notice and this permission notice
9
* appear in supporting documentation, and that the name of
10
* Novell, Inc. not be used in advertising or publicity pertaining to
11
* distribution of the software without specific, written prior permission.
12
* Novell, Inc. makes no representations about the suitability of this
13
* software for any purpose. It is provided "as is" without express or
16
* NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
18
* NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
20
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
22
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
* Authors: David Reveman <davidr@novell.com>
25
* Daniel d'Andrada <daniel.dandrada@canonical.com>
28
#include <core/core.h>
29
#include <core/atoms.h>
31
#include "resize-logic.h"
33
#include "resize_options.h"
35
#include "screen-interface.h"
36
#include "gl-screen-interface.h"
37
#include "composite-screen-interface.h"
38
#include "window-interface.h"
39
#include "composite-window-interface.h"
40
#include "gl-window-interface.h"
41
#include "resize-window-interface.h"
42
#include "property-writer-interface.h"
44
#define XWINDOWCHANGES_INIT {0, 0, 0, 0, 0, None, 0}
49
#define TOUCH_BOTTOM 4
51
using namespace resize;
53
ResizeLogic::ResizeLogic() :
57
maximized_vertically (false),
64
isConstrained (false),
65
offWorkAreaConstrained (true),
70
rKeys[0].name = "Left";
73
rKeys[0].warpMask = ResizeLeftMask | ResizeRightMask;
74
rKeys[0].resizeMask = ResizeLeftMask;
76
rKeys[1].name = "Right";
79
rKeys[1].warpMask = ResizeLeftMask | ResizeRightMask;
80
rKeys[1].resizeMask = ResizeRightMask;
85
rKeys[2].warpMask = ResizeUpMask | ResizeDownMask;
86
rKeys[2].resizeMask = ResizeUpMask;
88
rKeys[3].name = "Down";
91
rKeys[3].warpMask = ResizeUpMask | ResizeDownMask;
92
rKeys[3].resizeMask = ResizeDownMask;
95
ResizeLogic::~ResizeLogic()
100
ResizeLogic::handleEvent (XEvent *event)
102
switch (event->type) {
104
if (event->xkey.root == mScreen->root ())
105
handleKeyEvent (event->xkey.keycode);
108
if (event->xbutton.root == mScreen->root ())
112
if (releaseButton == -1 ||
113
(int) event->xbutton.button == releaseButton)
115
CompAction *action = &options->optionGetInitiateButton ();
117
terminateResize (action, CompAction::StateTermButton,
124
if (event->xmotion.root == mScreen->root ())
125
handleMotionEvent (pointerX, pointerY);
129
if (event->xcrossing.root == mScreen->root ())
130
handleMotionEvent (pointerX, pointerY);
133
if (event->xclient.message_type == Atoms::wmMoveResize)
135
CompWindowInterface *w;
136
unsigned long type = event->xclient.data.l[2];
138
if (type <= WmMoveResizeSizeLeft ||
139
type == WmMoveResizeSizeKeyboard)
141
w = mScreen->findWindow (event->xclient.window);
144
CompOption::Vector o (0);
146
o.push_back (CompOption ("window",
147
CompOption::TypeInt));
148
o[0].value ().set ((int) event->xclient.window);
150
o.push_back (CompOption ("external",
151
CompOption::TypeBool));
152
o[1].value ().set (true);
154
if (event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
156
initiateResizeDefaultMode (&options->optionGetInitiateKey (),
157
CompAction::StateInitKey,
162
/* TODO: not only button 1 */
163
if (pointerMods & Button1Mask)
165
static unsigned int mask[] = {
166
ResizeUpMask | ResizeLeftMask,
168
ResizeUpMask | ResizeRightMask,
170
ResizeDownMask | ResizeRightMask,
172
ResizeDownMask | ResizeLeftMask,
175
o.push_back (CompOption ("modifiers",
176
CompOption::TypeInt));
177
o.push_back (CompOption ("x",
178
CompOption::TypeInt));
179
o.push_back (CompOption ("y",
180
CompOption::TypeInt));
181
o.push_back (CompOption ("direction",
182
CompOption::TypeInt));
183
o.push_back (CompOption ("button",
184
CompOption::TypeInt));
186
o[2].value ().set ((int) pointerMods);
188
((int) event->xclient.data.l[0]);
190
((int) event->xclient.data.l[1]);
192
((int) mask[event->xclient.data.l[2]]);
194
((int) (event->xclient.data.l[3] ?
195
event->xclient.data.l[3] : -1));
197
initiateResizeDefaultMode (
198
&options->optionGetInitiateButton (),
199
CompAction::StateInitButton, o);
201
handleMotionEvent (pointerX, pointerY);
206
else if (this->w && type == WmMoveResizeCancel)
208
if (this->w->id () == event->xclient.window)
210
terminateResize (&options->optionGetInitiateButton (),
211
CompAction::StateCancel, noOptions ());
212
terminateResize (&options->optionGetInitiateKey (),
213
CompAction::StateCancel, noOptions ());
219
if (w && w->id () == event->xdestroywindow.window)
221
terminateResize (&options->optionGetInitiateButton (), 0, noOptions ());
222
terminateResize (&options->optionGetInitiateKey (), 0, noOptions ());
226
if (w && w->id () == event->xunmap.window)
228
terminateResize (&options->optionGetInitiateButton (), 0, noOptions ());
229
terminateResize (&options->optionGetInitiateKey (), 0, noOptions ());
235
if (event->type == mScreen->xkbEvent ())
237
XkbAnyEvent *xkbEvent = (XkbAnyEvent *) event;
239
if (xkbEvent->xkb_type == XkbStateNotify)
241
XkbStateNotifyEvent *stateEvent = (XkbStateNotifyEvent *) event;
243
/* Check if we need to change to outline mode */
245
unsigned int mods = 0xffffffff;
246
bool modifierMode = false;
252
if ((stateEvent->mods & mods) == mods)
255
mode = ResizeOptions::ModeOutline;
260
mods = rectangleMask;
262
if ((stateEvent->mods & mods) == mods)
265
mode = ResizeOptions::ModeRectangle;
272
if ((stateEvent->mods & mods) == mods)
275
mode = ResizeOptions::ModeStretch;
282
/* No modifier mode set, check match options */
285
if (w->evaluate (options->optionGetNormalMatch ()))
288
mode = ResizeOptions::ModeNormal;
291
if (w->evaluate (options->optionGetOutlineMatch ()))
294
mode = ResizeOptions::ModeOutline;
297
if (w->evaluate (options->optionGetRectangleMatch ()))
300
mode = ResizeOptions::ModeRectangle;
303
if (w->evaluate (options->optionGetStretchMatch ()))
306
mode = ResizeOptions::ModeStretch;
311
mode = options->optionGetMode ();
313
if (w && oldMode != mode)
317
getStretchRectangle (&box);
318
damageRectangle (&box);
319
getPaintRectangle (&box);
320
damageRectangle (&box);
322
box.x1 = w->outputRect ().x ();
323
box.y1 = w->outputRect ().y ();
324
box.x2 = box.x1 + w->outputRect ().width ();
325
box.y2 = box.y1 + w->outputRect ().height ();
327
damageRectangle (&box);
330
if ((stateEvent->mods & mods) == mods)
336
if (!w->evaluate (options->optionGetResizeFromCenterMatch ()))
348
mScreen->handleEvent (event);
350
if (event->type == mScreen->syncEvent () + XSyncAlarmNotify)
354
XSyncAlarmNotifyEvent *sa;
356
sa = (XSyncAlarmNotifyEvent *) event;
358
if (w->syncAlarm () == sa->alarm)
365
ResizeLogic::handleKeyEvent (KeyCode keycode)
369
int widthInc, heightInc;
371
widthInc = w->sizeHints ().width_inc;
372
heightInc = w->sizeHints ().height_inc;
374
if (widthInc < MIN_KEY_WIDTH_INC)
375
widthInc = MIN_KEY_WIDTH_INC;
377
if (heightInc < MIN_KEY_HEIGHT_INC)
378
heightInc = MIN_KEY_HEIGHT_INC;
380
for (unsigned int i = 0; i < NUM_KEYS; i++)
382
if (keycode != key[i])
385
if (mask & rKeys[i].warpMask)
387
XWarpPointer (mScreen->dpy (), None, None, 0, 0, 0, 0,
388
rKeys[i].dx * widthInc, rKeys[i].dy * heightInc);
392
int x, y, left, top, width, height;
394
CompWindow::Geometry server = w->serverGeometry ();
395
const CompWindowExtents &border = w->border ();
397
left = server.x () - border.left;
398
top = server.y () - border.top;
399
width = border.left + server.width () + border.right;
400
height = border.top + server.height () + border.bottom;
402
x = left + width * (rKeys[i].dx + 1) / 2;
403
y = top + height * (rKeys[i].dy + 1) / 2;
405
mScreen->warpPointer (x - pointerX, y - pointerY);
407
mask = rKeys[i].resizeMask;
409
mScreen->updateGrab (grabIndex, cursor[i]);
417
ResizeLogic::handleMotionEvent (int xRoot, int yRoot)
422
int wi, he, cwi, che; /* size of window contents (c prefix for constrained)*/
423
int wX, wY, wWidth, wHeight; /* rect. for window contents+borders */
425
wi = savedGeometry.width;
426
he = savedGeometry.height;
430
setUpMask (xRoot, yRoot);
434
accumulatePointerMotion (xRoot, yRoot);
437
if (mask & ResizeLeftMask)
439
else if (mask & ResizeRightMask)
442
if (mask & ResizeUpMask)
444
else if (mask & ResizeDownMask)
447
if (w->state () & CompWindowStateMaximizedVertMask)
448
he = w->serverGeometry ().height ();
450
if (w->state () & CompWindowStateMaximizedHorzMask)
451
wi = w->serverGeometry ().width ();
456
if (w->constrainNewWindowSize (wi, he, &cwi, &che) &&
457
mode != ResizeOptions::ModeNormal)
461
/* Also, damage relevant paint rectangles */
462
if (mode == ResizeOptions::ModeRectangle ||
463
mode == ResizeOptions::ModeOutline)
464
getPaintRectangle (&box);
465
else if (mode == ResizeOptions::ModeStretch)
466
getStretchRectangle (&box);
468
damageRectangle (&box);
471
if (offWorkAreaConstrained)
472
constrainToWorkArea (che, cwi);
477
/* compute rect. for window + borders */
478
computeWindowPlusBordersRect (wX, wY, wWidth, wHeight, /*out*/
481
snapWindowToWorkAreaBoundaries (wi, he, wX, wY, wWidth, wHeight);
484
limitMovementToConstraintRegion (wi, he, /*in/out*/
486
wX, wY, wWidth, wHeight); /*in*/
488
if (mode != ResizeOptions::ModeNormal)
490
if (mode == ResizeOptions::ModeStretch)
491
getStretchRectangle (&box);
493
getPaintRectangle (&box);
495
damageRectangle (&box);
498
enableOrDisableVerticalMaximization (yRoot);
500
computeGeometry (wi, he);
502
if (mode != ResizeOptions::ModeNormal)
504
if (mode == ResizeOptions::ModeStretch)
505
getStretchRectangle (&box);
507
getPaintRectangle (&box);
509
damageRectangle (&box);
516
updateWindowProperty ();
522
ResizeLogic::updateWindowSize ()
527
if (w->serverGeometry ().width () != geometry.width ||
528
w->serverGeometry ().height () != geometry.height)
530
XWindowChanges xwc = XWINDOWCHANGES_INIT;
534
xwc.width = geometry.width;
535
xwc.height = geometry.height;
537
w->sendSyncRequest ();
539
w->configureXWindow (CWX | CWY | CWWidth | CWHeight, &xwc);
544
ResizeLogic::updateWindowProperty ()
546
CompOption::Vector data = resizeInformationAtom->getReadTemplate ();;
549
if (data.size () != 4)
564
resizeInformationAtom->updateProperty (w->id (), data, XA_CARDINAL);
568
ResizeLogic::sendResizeNotify ()
572
xev.xclient.type = ClientMessage;
573
xev.xclient.display = mScreen->dpy ();
574
xev.xclient.format = 32;
576
xev.xclient.message_type = resizeNotifyAtom;
577
xev.xclient.window = w->id ();
579
xev.xclient.data.l[0] = geometry.x;
580
xev.xclient.data.l[1] = geometry.y;
581
xev.xclient.data.l[2] = geometry.width;
582
xev.xclient.data.l[3] = geometry.height;
583
xev.xclient.data.l[4] = 0;
585
XSendEvent (mScreen->dpy (), mScreen->root (), false,
586
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
590
ResizeLogic::finishResizing ()
594
resizeInformationAtom->deleteProperty (w->id ());
600
ResizeLogic::getPaintRectangle (BoxPtr pBox)
602
pBox->x1 = geometry.x - w->border ().left;
603
pBox->y1 = geometry.y - w->border ().top;
604
pBox->x2 = geometry.x + geometry.width +
605
w->serverGeometry ().border () * 2 + w->border ().right;
608
pBox->y2 = geometry.y + w->size ().height () + w->border ().bottom;
610
pBox->y2 = geometry.y + geometry.height +
611
w->serverGeometry ().border () * 2 + w->border ().bottom;
615
ResizeLogic::getStretchRectangle (BoxPtr pBox)
618
float xScale, yScale;
620
getPaintRectangle (&box);
621
w->getResizeInterface ()->getStretchScale (&box, &xScale, &yScale);
623
pBox->x1 = (int) (box.x1 - (w->output ().left - w->border ().left) * xScale);
624
pBox->y1 = (int) (box.y1 - (w->output ().top - w->border ().top) * yScale);
625
pBox->x2 = (int) (box.x2 + w->output ().right * xScale);
626
pBox->y2 = (int) (box.y2 + w->output ().bottom * yScale);
630
ResizeLogic::cursorFromResizeMask (unsigned int mask)
634
if (mask & ResizeLeftMask)
636
if (mask & ResizeDownMask)
637
cursor = downLeftCursor;
638
else if (mask & ResizeUpMask)
639
cursor = upLeftCursor;
643
else if (mask & ResizeRightMask)
645
if (mask & ResizeDownMask)
646
cursor = downRightCursor;
647
else if (mask & ResizeUpMask)
648
cursor = upRightCursor;
650
cursor = rightCursor;
652
else if (mask & ResizeUpMask)
665
ResizeLogic::snapWindowToWorkAreaBoundaries (int &wi, int &he,
667
int &wWidth, int &wHeight)
669
int workAreaSnapDistance = 15;
671
/* Check if resized edge(s) are near output work-area boundaries */
672
foreach (CompOutput &output, mScreen->outputDevs ())
674
const CompRect &workArea = output.workArea ();
676
/* if window and work-area intersect in x axis */
677
if (wX + wWidth > workArea.x () &&
680
if (mask & ResizeLeftMask)
682
int dw = workArea.x () - wX;
684
if (0 < dw && dw < workAreaSnapDistance)
691
else if (mask & ResizeRightMask)
693
int dw = wX + wWidth - workArea.x2 ();
695
if (0 < dw && dw < workAreaSnapDistance)
703
/* if window and work-area intersect in y axis */
704
if (wY + wHeight > workArea.y () &&
707
if (mask & ResizeUpMask)
709
int dh = workArea.y () - wY;
711
if (0 < dh && dh < workAreaSnapDistance)
718
else if (mask & ResizeDownMask)
720
int dh = wY + wHeight - workArea.y2 ();
722
if (0 < dh && dh < workAreaSnapDistance)
733
ResizeLogic::setUpMask (int xRoot, int yRoot)
736
int minPointerOffsetX, minPointerOffsetY;
738
CompWindow::Geometry server = w->serverGeometry ();
740
xDist = xRoot - (server.x () + (server.width () / 2));
741
yDist = yRoot - (server.y () + (server.height () / 2));
743
/* decision threshold is 10% of window size */
744
minPointerOffsetX = MIN (20, server.width () / 10);
745
minPointerOffsetY = MIN (20, server.height () / 10);
747
/* if we reached the threshold in one direction,
748
make the threshold in the other direction smaller
749
so there is a chance that this threshold also can
750
be reached (by diagonal movement) */
751
if (abs (xDist) > minPointerOffsetX)
752
minPointerOffsetY /= 2;
753
else if (abs (yDist) > minPointerOffsetY)
754
minPointerOffsetX /= 2;
756
if (abs (xDist) > minPointerOffsetX)
759
mask |= ResizeRightMask;
761
mask |= ResizeLeftMask;
764
if (abs (yDist) > minPointerOffsetY)
767
mask |= ResizeDownMask;
769
mask |= ResizeUpMask;
772
/* if the pointer movement was enough to determine a
773
direction, warp the pointer to the appropriate edge
774
and set the right cursor */
779
int pointerAdjustX = 0;
780
int pointerAdjustY = 0;
782
action = &options->optionGetInitiateKey ();
783
action->setState (action->state () |
784
CompAction::StateTermButton);
786
if (mask & ResizeRightMask)
787
pointerAdjustX = server.x () + server.width () +
788
w->border ().right - xRoot;
789
else if (mask & ResizeLeftMask)
790
pointerAdjustX = server.x () - w->border ().left -
793
if (mask & ResizeDownMask)
794
pointerAdjustY = server.y () + server.height () +
795
w->border ().bottom - yRoot;
796
else if (mask & ResizeUpMask)
797
pointerAdjustY = server.y () - w->border ().top - yRoot;
799
mScreen->warpPointer (pointerAdjustX, pointerAdjustY);
801
cursor = cursorFromResizeMask (mask);
802
mScreen->updateGrab (grabIndex, cursor);
807
ResizeLogic::accumulatePointerMotion (int xRoot, int yRoot)
809
/* only accumulate pointer movement if a mask is
810
already set as we don't have a use for the
811
difference information otherwise */
813
if (centered || options->optionGetResizeFromCenter ())
815
pointerDx += (xRoot - lastPointerX) * 2;
816
pointerDy += (yRoot - lastPointerY) * 2;
820
pointerDx += xRoot - lastPointerX;
821
pointerDy += yRoot - lastPointerY;
824
/* If we hit the edge of the screen while resizing
825
* the window and the adjacent window edge has not hit
826
* the edge of the screen, then accumulate pointer motion
827
* in the opposite direction. (So the apparant x / y
828
* mixup here is intentional)
833
if (mask == ResizeLeftMask)
836
geometry.x - w->border ().left > grabWindowWorkArea->left ())
837
pointerDx += abs (yRoot - lastPointerY) * -1;
839
else if (mask == ResizeRightMask)
841
if (xRoot == mScreen->width () -1 &&
842
geometry.x + geometry.width + w->border ().right < grabWindowWorkArea->right ())
843
pointerDx += abs (yRoot - lastPointerY);
845
if (mask == ResizeUpMask)
848
geometry.y - w->border ().top > grabWindowWorkArea->top ())
849
pointerDy += abs (xRoot - lastPointerX) * -1;
851
else if (mask == ResizeDownMask)
853
if (yRoot == mScreen->height () -1 &&
854
geometry.y + geometry.height + w->border ().bottom < grabWindowWorkArea->bottom ())
855
pointerDx += abs (yRoot - lastPointerY);
861
ResizeLogic::constrainToWorkArea (int &che, int &cwi)
863
if (mask & ResizeUpMask)
865
int decorTop = savedGeometry.y + savedGeometry.height -
866
(che + w->border ().top);
868
if (grabWindowWorkArea->y () > decorTop)
869
che -= grabWindowWorkArea->y () - decorTop;
871
if (mask & ResizeDownMask)
873
int decorBottom = savedGeometry.y + che + w->border ().bottom;
876
grabWindowWorkArea->y () + grabWindowWorkArea->height ())
877
che -= decorBottom - (grabWindowWorkArea->y () +
878
grabWindowWorkArea->height ());
880
if (mask & ResizeLeftMask)
882
int decorLeft = savedGeometry.x + savedGeometry.width -
883
(cwi + w->border ().left);
885
if (grabWindowWorkArea->x () > decorLeft)
886
cwi -= grabWindowWorkArea->x () - decorLeft;
888
if (mask & ResizeRightMask)
890
int decorRight = savedGeometry.x + cwi + w->border ().right;
893
grabWindowWorkArea->x () + grabWindowWorkArea->width ())
894
cwi -= decorRight - (grabWindowWorkArea->x () +
895
grabWindowWorkArea->width ());
900
ResizeLogic::limitMovementToConstraintRegion (int &wi, int &he,
901
int xRoot, int yRoot,
903
int wWidth, int wHeight)
907
/* rect. for a minimal height window + borders
908
(used for the constraining in X axis) */
909
int minimalInputHeight = minHeight +
910
w->border ().top + w->border ().bottom;
912
/* small hot-spot square (on window's corner or edge) that is to be
913
constrained to the combined output work-area region */
915
int width = w->border ().top; /* square size = title bar height */
917
bool status; /* whether or not hot-spot is in the region */
919
/* compute x & y for constrained hot-spot rect */
920
if (mask & ResizeLeftMask)
922
else if (mask & ResizeRightMask)
923
x = wX + wWidth - width;
925
x = MIN (MAX (xRoot, wX), wX + wWidth - width);
927
if (mask & ResizeUpMask)
929
else if (mask & ResizeDownMask)
930
y = wY + wHeight - height;
932
y = MIN (MAX (yRoot, wY), wY + wHeight - height);
934
status = constraintRegion.contains (x, y, width, height);
936
/* only constrain movement if previous position was valid */
939
bool xStatus = false;
946
if (mask & (ResizeLeftMask | ResizeRightMask))
950
if (mask & ResizeUpMask)
951
yForXResize = wY + wHeight - minimalInputHeight;
952
else if (mask & ResizeDownMask)
953
yForXResize = wY + minimalInputHeight - height;
957
if (!constraintRegion.contains (x, yForXResize,
960
if (lastGoodHotSpotY >= 0)
961
yForXResize = lastGoodHotSpotY;
966
if (mask & ResizeLeftMask)
968
while ((nw > minWidth) && !xStatus)
970
xStatus = constraintRegion.contains (nx, yForXResize,
984
else if (mask & ResizeRightMask)
986
while ((nw > minWidth) && !xStatus)
988
xStatus = constraintRegion.contains (nx, yForXResize,
1003
if (mask & ResizeUpMask)
1005
while ((nh > minHeight) && !status)
1007
status = constraintRegion.contains (x, y,
1018
else if (mask & ResizeDownMask)
1020
while ((nh > minHeight) && !status)
1022
status = constraintRegion.contains (x, y,
1034
if (((mask & (ResizeLeftMask | ResizeRightMask)) && xStatus) ||
1035
((mask & (ResizeUpMask | ResizeDownMask)) && status))
1037
/* hot-spot inside work-area region, store good values */
1038
lastGoodHotSpotY = y;
1039
lastGoodSize = CompSize (wi, he);
1043
/* failed to find a good hot-spot position, restore size */
1044
wi = lastGoodSize.width ();
1045
he = lastGoodSize.height ();
1050
inRegionStatus = status;
1055
ResizeLogic::computeWindowPlusBordersRect (int &wX, int &wY,
1056
int &wWidth, int &wHeight,
1059
wWidth = wi + w->border ().left + w->border ().right;
1060
wHeight = he + w->border ().top + w->border ().bottom;
1062
if (centered || options->optionGetResizeFromCenter ())
1064
if (mask & ResizeLeftMask)
1065
wX = geometry.x + geometry.width -
1066
(wi + w->border ().left);
1068
wX = geometry.x - w->border ().left;
1070
if (mask & ResizeUpMask)
1071
wY = geometry.y + geometry.height -
1072
(he + w->border ().top);
1074
wY = geometry.y - w->border ().top;
1078
if (mask & ResizeLeftMask)
1079
wX = savedGeometry.x + savedGeometry.width -
1080
(wi + w->border ().left);
1082
wX = savedGeometry.x - w->border ().left;
1084
if (mask & ResizeUpMask)
1085
wY = savedGeometry.y + savedGeometry.height -
1086
(he + w->border ().top);
1088
wY = savedGeometry.y - w->border ().top;
1093
ResizeLogic::enableOrDisableVerticalMaximization (int yRoot)
1095
/* maximum distance between the pointer and a work area edge (top or bottom)
1096
for a vertical maximization */
1097
const int max_edge_distance = 5;
1099
if (!options->optionGetMaximizeVertically())
1102
if (centered || options->optionGetResizeFromCenter ())
1104
if (maximized_vertically)
1106
geometry = geometryWithoutVertMax;
1107
maximized_vertically = false;
1110
else if (mask & ResizeUpMask)
1112
if (yRoot - grabWindowWorkArea->top() <= max_edge_distance
1113
&& !maximized_vertically)
1115
maximized_vertically = true;
1116
geometryWithoutVertMax = geometry;
1118
else if (yRoot - grabWindowWorkArea->top() > max_edge_distance
1119
&& maximized_vertically)
1121
geometry = geometryWithoutVertMax;
1122
maximized_vertically = false;
1125
else if (mask & ResizeDownMask)
1127
if (grabWindowWorkArea->bottom() - yRoot <= max_edge_distance
1128
&& !maximized_vertically)
1130
maximized_vertically = true;
1131
geometryWithoutVertMax = geometry;
1133
else if (grabWindowWorkArea->bottom() - yRoot > max_edge_distance
1134
&& maximized_vertically)
1136
geometry = geometryWithoutVertMax;
1137
maximized_vertically = false;
1143
ResizeLogic::computeGeometry(int wi, int he)
1145
XRectangle *regular_geometry;
1146
if (maximized_vertically)
1147
regular_geometry = &geometryWithoutVertMax;
1149
regular_geometry = &geometry;
1151
if (centered || options->optionGetResizeFromCenter ())
1153
if ((mask & ResizeLeftMask) || (mask & ResizeRightMask))
1154
regular_geometry->x -= ((wi - regular_geometry->width) / 2);
1155
if ((mask & ResizeUpMask) || (mask & ResizeDownMask))
1156
regular_geometry->y -= ((he - regular_geometry->height) / 2);
1160
if (mask & ResizeLeftMask)
1161
regular_geometry->x -= wi - regular_geometry->width;
1162
if (mask & ResizeUpMask)
1163
regular_geometry->y -= he - regular_geometry->height;
1166
regular_geometry->width = wi;
1167
regular_geometry->height = he;
1169
if (maximized_vertically)
1171
geometry.x = geometryWithoutVertMax.x;
1172
geometry.width = geometryWithoutVertMax.width;
1173
geometry.y = grabWindowWorkArea->y() + w->border().top;
1174
geometry.height = grabWindowWorkArea->height() - w->border().top
1175
- w->border().bottom;
1181
ResizeLogic::initiateResize (CompAction *action,
1182
CompAction::State state,
1183
CompOption::Vector &options,
1184
unsigned int initMode)
1186
CompWindowInterface *w;
1189
xid = CompOption::getIntOptionNamed (options, "window");
1191
w = mScreen->findWindow (xid);
1192
if (w && (w->actions () & CompWindowActionResizeMask))
1197
CompWindow::Geometry server = w->serverGeometry ();
1199
x = CompOption::getIntOptionNamed (options, "x", pointerX);
1200
y = CompOption::getIntOptionNamed (options, "y", pointerY);
1202
button = CompOption::getIntOptionNamed (options, "button", -1);
1204
mask = CompOption::getIntOptionNamed (options, "direction");
1206
/* Initiate the resize in the direction suggested by the
1207
* sector of the window the mouse is in, eg drag in top left
1208
* will resize up and to the left. Keyboard resize starts out
1209
* with the cursor in the middle of the window and then starts
1210
* resizing the edge corresponding to the next key press. */
1211
if (state & CompAction::StateInitKey)
1217
int sectorSizeX = server.width () / 3;
1218
int sectorSizeY = server.height () / 3;
1219
int posX = x - server.x ();
1220
int posY = y - server.y ();
1222
if (posX < sectorSizeX)
1223
mask |= ResizeLeftMask;
1224
else if (posX > (2 * sectorSizeX))
1225
mask |= ResizeRightMask;
1227
if (posY < sectorSizeY)
1228
mask |= ResizeUpMask;
1229
else if (posY > (2 * sectorSizeY))
1230
mask |= ResizeDownMask;
1232
/* if the pointer was in the middle of the window,
1233
just prevent input to the window */
1239
if (mScreen->otherGrabExist ("resize", NULL))
1245
if (w->type () & (CompWindowTypeDesktopMask |
1246
CompWindowTypeDockMask |
1247
CompWindowTypeFullscreenMask))
1250
if (w->overrideRedirect ())
1253
if (state & CompAction::StateInitButton)
1254
action->setState (action->state () | CompAction::StateTermButton);
1257
mask &= ~(ResizeUpMask | ResizeDownMask);
1261
savedGeometry.x = server.x ();
1262
savedGeometry.y = server.y ();
1263
savedGeometry.width = server.width ();
1264
savedGeometry.height = server.height ();
1266
geometry = savedGeometry;
1268
pointerDx = x - pointerX;
1269
pointerDy = y - pointerY;
1271
centered |= w->evaluate (this->options->optionGetResizeFromCenterMatch ());
1273
if ((w->state () & MAXIMIZE_STATE) == MAXIMIZE_STATE)
1275
/* if the window is fully maximized, showing the outline or
1276
rectangle would be visually distracting as the window can't
1277
be resized anyway; so we better don't use them in this case */
1278
mode = ResizeOptions::ModeNormal;
1280
else if (!gScreen || !cScreen ||
1281
!cScreen->compositingActive ())
1283
mode = ResizeOptions::ModeNormal;
1290
if (mode != ResizeOptions::ModeNormal)
1292
if (w->getGLInterface () && mode == ResizeOptions::ModeStretch)
1293
w->getGLInterface ()->glPaintSetEnabled (true);
1294
if (w->getCompositeInterface () && mode == ResizeOptions::ModeStretch)
1295
w->getCompositeInterface ()->damageRectSetEnabled (true);
1296
gScreen->glPaintOutputSetEnabled (true);
1303
if (state & CompAction::StateInitKey)
1304
cursor = middleCursor;
1306
cursor = cursorFromResizeMask (mask);
1308
grabIndex = mScreen->pushGrab (cursor, "resize");
1314
unsigned int grabMask = CompWindowGrabResizeMask |
1315
CompWindowGrabButtonMask;
1316
bool sourceExternalApp =
1317
CompOption::getBoolOptionNamed (options, "external", false);
1319
if (sourceExternalApp)
1320
grabMask |= CompWindowGrabExternalAppMask;
1322
releaseButton = button;
1324
w->grabNotify (x, y, state, grabMask);
1326
/* Click raise happens implicitly on buttons 1, 2 and 3 so don't
1327
* restack this window again if the action buttonbinding was from
1328
* one of those buttons */
1329
if (mScreen->getOption ("raise_on_click")->value ().b () &&
1330
button != Button1 && button != Button2 && button != Button3)
1331
w->updateAttributes (CompStackingUpdateModeAboveFullscreen);
1333
/* using the paint rectangle is enough here
1334
as we don't have any stretch yet */
1335
getPaintRectangle (&box);
1336
damageRectangle (&box);
1338
if (state & CompAction::StateInitKey)
1342
xRoot = server.x () + (server.width () / 2);
1343
yRoot = server.y () + (server.height () / 2);
1345
mScreen->warpPointer (xRoot - pointerX, yRoot - pointerY);
1348
isConstrained = sourceExternalApp;
1350
/* Update offWorkAreaConstrained and workArea at grab time */
1351
offWorkAreaConstrained = false;
1352
if (sourceExternalApp)
1354
int output = w->outputDevice ();
1355
int lco, tco, bco, rco;
1356
bool sl = mScreen->outputDevs ().at (output).workArea ().left () >
1357
w->serverGeometry ().left ();
1358
bool sr = mScreen->outputDevs ().at (output).workArea ().right () <
1359
w->serverGeometry ().right ();
1360
bool st = mScreen->outputDevs ().at (output).workArea ().top () >
1361
w->serverGeometry ().top ();
1362
bool sb = mScreen->outputDevs ().at (output).workArea ().bottom () <
1363
w->serverGeometry ().bottom ();
1365
lco = tco = bco = rco = output;
1367
/* Prevent resizing beyond work area edges when resize is
1368
initiated externally (e.g. with window frame or menu)
1369
and not with a key (e.g. alt+button) */
1370
offWorkAreaConstrained = true;
1372
lco = getOutputForEdge (output, TOUCH_RIGHT, sl);
1373
rco = getOutputForEdge (output, TOUCH_LEFT, sr);
1374
tco = getOutputForEdge (output, TOUCH_BOTTOM, st);
1375
bco = getOutputForEdge (output, TOUCH_TOP, sb);
1377
/* Now we need to form one big rect which describes
1378
* the available workarea */
1380
int left = mScreen->outputDevs ().at (lco).workArea ().left ();
1381
int right = mScreen->outputDevs ().at (rco).workArea ().right ();
1382
int top = mScreen->outputDevs ().at (tco).workArea ().top ();
1383
int bottom = mScreen->outputDevs ().at (bco).workArea ().bottom ();
1385
grabWindowWorkArea.reset (new CompRect (0, 0, 0, 0));
1387
grabWindowWorkArea->setLeft (left);
1388
grabWindowWorkArea->setRight (right);
1389
grabWindowWorkArea->setTop (top);
1390
grabWindowWorkArea->setBottom (bottom);
1394
inRegionStatus = false;
1395
lastGoodHotSpotY = -1;
1396
lastGoodSize = w->serverSize ();
1398
/* Combine the work areas of all outputs */
1399
constraintRegion = emptyRegion;
1400
foreach (CompOutput &output, mScreen->outputDevs ())
1401
constraintRegion += output.workArea ();
1405
maximized_vertically = false;
1412
ResizeLogic::terminateResize (CompAction *action,
1413
CompAction::State state,
1414
CompOption::Vector &options)
1418
XWindowChanges xwc = XWINDOWCHANGES_INIT;
1419
unsigned int mask = 0;
1421
if (mode == ResizeOptions::ModeNormal)
1423
if (state & CompAction::StateCancel)
1425
xwc.x = savedGeometry.x;
1426
xwc.y = savedGeometry.y;
1427
xwc.width = savedGeometry.width;
1428
xwc.height = savedGeometry.height;
1430
mask = CWX | CWY | CWWidth | CWHeight;
1432
else if (maximized_vertically)
1434
w->maximize (CompWindowStateMaximizedVertMask);
1436
xwc.x = geometryWithoutVertMax.x;
1437
xwc.y = geometryWithoutVertMax.y;
1438
xwc.width = geometryWithoutVertMax.width;
1439
xwc.height = geometryWithoutVertMax.height;
1441
mask = CWX | CWY | CWWidth | CWHeight;
1446
XRectangle finalGeometry;
1448
if (state & CompAction::StateCancel)
1449
finalGeometry = savedGeometry;
1451
finalGeometry = geometry;
1453
if (memcmp (&finalGeometry, &savedGeometry, sizeof (finalGeometry)) == 0)
1457
if (mode == ResizeOptions::ModeStretch)
1458
getStretchRectangle (&box);
1460
getPaintRectangle (&box);
1462
damageRectangle (&box);
1466
if (maximized_vertically)
1468
w->maximize(CompWindowStateMaximizedVertMask);
1469
xwc.x = finalGeometry.x;
1470
xwc.width = finalGeometry.width;
1471
mask = CWX | CWWidth ;
1475
xwc.x = finalGeometry.x;
1476
xwc.y = finalGeometry.y;
1477
xwc.width = finalGeometry.width;
1478
xwc.height = finalGeometry.height;
1479
mask = CWX | CWY | CWWidth | CWHeight;
1484
if (mode != ResizeOptions::ModeNormal)
1486
if (w->getGLInterface () && mode == ResizeOptions::ModeStretch)
1487
w->getGLInterface ()->glPaintSetEnabled (false);
1488
if (w->getCompositeInterface () && mode == ResizeOptions::ModeStretch)
1489
w->getCompositeInterface ()->damageRectSetEnabled (false);
1490
gScreen->glPaintOutputSetEnabled (false);
1494
if ((mask & CWWidth) &&
1495
xwc.width == (int) w->serverGeometry ().width ())
1498
if ((mask & CWHeight) &&
1499
xwc.height == (int) w->serverGeometry ().height ())
1504
if (mask & (CWWidth | CWHeight))
1505
w->sendSyncRequest ();
1507
w->configureXWindow (mask, &xwc);
1514
mScreen->removeGrab (grabIndex, NULL);
1521
action->setState (action->state () & ~(CompAction::StateTermKey |
1522
CompAction::StateTermButton));
1528
ResizeLogic::initiateResizeDefaultMode (CompAction *action,
1529
CompAction::State state,
1530
CompOption::Vector &options)
1532
CompWindowInterface *w;
1535
w = mScreen->findWindow (CompOption::getIntOptionNamed (options, "window"));
1539
mode = this->options->optionGetMode ();
1541
if (w->evaluate (this->options->optionGetNormalMatch ()))
1542
mode = ResizeOptions::ModeNormal;
1543
if (w->evaluate (this->options->optionGetOutlineMatch ()))
1544
mode = ResizeOptions::ModeOutline;
1545
if (w->evaluate (this->options->optionGetRectangleMatch ()))
1546
mode = ResizeOptions::ModeRectangle;
1547
if (w->evaluate (this->options->optionGetStretchMatch ()))
1548
mode = ResizeOptions::ModeStretch;
1550
return initiateResize (action, state, options, mode);
1554
ResizeLogic::damageRectangle (BoxPtr pBox)
1564
cScreen->damageRegion (CompRect (x1, y1, x2 - x1, y2 - y1));
1567
/* Be a little bit intelligent about how we calculate
1568
* the workarea. Basically we want to be enclosed in
1569
* any area that is obstructed by panels, but not
1570
* where two outputs meet
1572
* Also, it does not make sense to resize over
1573
* non-touching outputs, so detect that case too
1577
ResizeLogic::getOutputForEdge (int windowOutput, unsigned int touch, bool skipFirst)
1580
int ret = windowOutput;
1582
getPointForTp (touch, windowOutput, op, wap);
1584
if ((op == wap) || skipFirst)
1586
int co = windowOutput;
1592
co = findTouchingOutput (op, touch);
1594
/* Could not find a leftmost output from here
1595
* so we must have hit the edge of the universe */
1603
getPointForTp (touch, co, op, wap);
1605
/* There is something in the way here.... */
1619
ResizeLogic::findTouchingOutput (int touchPoint, unsigned int side)
1621
for (unsigned int i = 0; i < mScreen->outputDevs ().size (); i++)
1623
CompOutput &o = mScreen->outputDevs ().at (i);
1624
if (side == TOUCH_LEFT)
1626
if (o.left () == touchPoint)
1629
if (side == TOUCH_RIGHT)
1631
if (o.right () == touchPoint)
1634
if (side == TOUCH_TOP)
1636
if (o.top () == touchPoint)
1639
if (side == TOUCH_BOTTOM)
1641
if (o.bottom () == touchPoint)
1650
ResizeLogic::getPointForTp (unsigned int tp,
1651
unsigned int output,
1655
CompRect og = CompRect (mScreen->outputDevs ().at (output));
1656
CompRect wag = mScreen->outputDevs ().at (output).workArea ();
1670
wap = wag.bottom ();