2
* Copyright © 2005 Novell, Inc.
4
* Permission to use, copy, modify, distribute, and sell this software
5
* and its documentation for any purpose is hereby granted without
6
* fee, provided that the above copyright notice appear in all copies
7
* and that both that copyright notice and this permission notice
8
* appear in supporting documentation, and that the name of
9
* Novell, Inc. not be used in advertising or publicity pertaining to
10
* distribution of the software without specific, written prior permission.
11
* Novell, Inc. makes no representations about the suitability of this
12
* software for any purpose. It is provided "as is" without express or
15
* NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17
* NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23
* Author: David Reveman <davidr@novell.com>
31
#include <X11/Xatom.h>
32
#include <X11/cursorfont.h>
34
#include <core/core.h>
35
#include <core/atoms.h>
38
COMPIZ_PLUGIN_20090315 (resize, ResizePluginVTable)
41
ResizeScreen::getPaintRectangle (BoxPtr pBox)
43
pBox->x1 = geometry.x - w->border ().left;
44
pBox->y1 = geometry.y - w->border ().top;
45
pBox->x2 = geometry.x + geometry.width +
46
w->serverGeometry ().border () * 2 + w->border ().right;
49
pBox->y2 = geometry.y + w->size ().height () + w->border ().bottom;
51
pBox->y2 = geometry.y + geometry.height +
52
w->serverGeometry ().border () * 2 + w->border ().bottom;
56
ResizeWindow::getStretchScale (BoxPtr pBox, float *xScale, float *yScale)
58
CompRect rect (window->borderRect ());
60
*xScale = (rect.width ()) ? (pBox->x2 - pBox->x1) /
61
(float) rect.width () : 1.0f;
62
*yScale = (rect.height ()) ? (pBox->y2 - pBox->y1) /
63
(float) rect.height () : 1.0f;
67
ResizeScreen::getStretchRectangle (BoxPtr pBox)
72
getPaintRectangle (&box);
73
ResizeWindow::get (w)->getStretchScale (&box, &xScale, &yScale);
75
pBox->x1 = (int) (box.x1 - (w->output ().left - w->border ().left) * xScale);
76
pBox->y1 = (int) (box.y1 - (w->output ().top - w->border ().top) * yScale);
77
pBox->x2 = (int) (box.x2 + w->output ().right * xScale);
78
pBox->y2 = (int) (box.y2 + w->output ().bottom * yScale);
82
ResizeScreen::damageRectangle (BoxPtr pBox)
92
cScreen->damageRegion (CompRect (x1, y1, x2 - x1, y2 - y1));
96
ResizeScreen::cursorFromResizeMask (unsigned int mask)
100
if (mask & ResizeLeftMask)
102
if (mask & ResizeDownMask)
103
cursor = downLeftCursor;
104
else if (mask & ResizeUpMask)
105
cursor = upLeftCursor;
109
else if (mask & ResizeRightMask)
111
if (mask & ResizeDownMask)
112
cursor = downRightCursor;
113
else if (mask & ResizeUpMask)
114
cursor = upRightCursor;
116
cursor = rightCursor;
118
else if (mask & ResizeUpMask)
131
ResizeScreen::sendResizeNotify ()
135
xev.xclient.type = ClientMessage;
136
xev.xclient.display = screen->dpy ();
137
xev.xclient.format = 32;
139
xev.xclient.message_type = resizeNotifyAtom;
140
xev.xclient.window = w->id ();
142
xev.xclient.data.l[0] = geometry.x;
143
xev.xclient.data.l[1] = geometry.y;
144
xev.xclient.data.l[2] = geometry.width;
145
xev.xclient.data.l[3] = geometry.height;
146
xev.xclient.data.l[4] = 0;
148
XSendEvent (screen->dpy (), screen->root (), false,
149
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
153
ResizeScreen::updateWindowProperty ()
155
CompOption::Vector data = resizeInformationAtom.getReadTemplate ();;
158
if (data.size () != 4)
173
resizeInformationAtom.updateProperty (w->id (), data, XA_CARDINAL);
177
ResizeScreen::finishResizing ()
181
resizeInformationAtom.deleteProperty (w->id ());
187
#define TOUCH_RIGHT 2
189
#define TOUCH_BOTTOM 4
192
findTouchingOutput (int touchPoint, unsigned int side)
194
for (unsigned int i = 0; i < screen->outputDevs ().size (); i++)
196
CompOutput &o = screen->outputDevs ().at (i);
197
if (side == TOUCH_LEFT)
199
if (o.left () == touchPoint)
202
if (side == TOUCH_RIGHT)
204
if (o.right () == touchPoint)
207
if (side == TOUCH_TOP)
209
if (o.top () == touchPoint)
212
if (side == TOUCH_BOTTOM)
214
if (o.bottom () == touchPoint)
223
getPointForTp (unsigned int tp, unsigned int output, int &op, int &wap)
225
CompRect og = CompRect (screen->outputDevs ().at (output));
226
CompRect wag = screen->outputDevs ().at (output).workArea ();
251
/* Be a little bit intelligent about how we calculate
252
* the workarea. Basically we want to be enclosed in
253
* any area that is obstructed by panels, but not
254
* where two outputs meet
256
* Also, it does not make sense to resize over
257
* non-touching outputs, so detect that case too
261
getOutputForEdge (int windowOutput, unsigned int touch, bool skipFirst)
264
int ret = windowOutput;
266
getPointForTp (touch, windowOutput, op, wap);
268
if ((op == wap) || skipFirst)
270
int co = windowOutput;
276
co = findTouchingOutput (op, touch);
278
/* Could not find a leftmost output from here
279
* so we must have hit the edge of the universe */
287
getPointForTp (touch, co, op, wap);
289
/* There is something in the way here.... */
303
resizeInitiate (CompAction *action,
304
CompAction::State state,
305
CompOption::Vector &options,
311
xid = CompOption::getIntOptionNamed (options, "window");
313
w = screen->findWindow (xid);
314
if (w && (w->actions () & CompWindowActionResizeMask))
320
RESIZE_SCREEN (screen);
322
CompWindow::Geometry server = w->serverGeometry ();
324
x = CompOption::getIntOptionNamed (options, "x", pointerX);
325
y = CompOption::getIntOptionNamed (options, "y", pointerY);
327
button = CompOption::getIntOptionNamed (options, "button", -1);
329
mask = CompOption::getIntOptionNamed (options, "direction");
331
/* Initiate the resize in the direction suggested by the
332
* sector of the window the mouse is in, eg drag in top left
333
* will resize up and to the left. Keyboard resize starts out
334
* with the cursor in the middle of the window and then starts
335
* resizing the edge corresponding to the next key press. */
336
if (state & CompAction::StateInitKey)
342
int sectorSizeX = server.width () / 3;
343
int sectorSizeY = server.height () / 3;
344
int posX = x - server.x ();
345
int posY = y - server.y ();
347
if (posX < sectorSizeX)
348
mask |= ResizeLeftMask;
349
else if (posX > (2 * sectorSizeX))
350
mask |= ResizeRightMask;
352
if (posY < sectorSizeY)
353
mask |= ResizeUpMask;
354
else if (posY > (2 * sectorSizeY))
355
mask |= ResizeDownMask;
357
/* if the pointer was in the middle of the window,
358
just prevent input to the window */
364
if (screen->otherGrabExist ("resize", NULL))
370
if (w->type () & (CompWindowTypeDesktopMask |
371
CompWindowTypeDockMask |
372
CompWindowTypeFullscreenMask))
375
if (w->overrideRedirect ())
378
if (state & CompAction::StateInitButton)
379
action->setState (action->state () | CompAction::StateTermButton);
382
mask &= ~(ResizeUpMask | ResizeDownMask);
387
rs->savedGeometry.x = server.x ();
388
rs->savedGeometry.y = server.y ();
389
rs->savedGeometry.width = server.width ();
390
rs->savedGeometry.height = server.height ();
392
rs->geometry = rs->savedGeometry;
394
rs->pointerDx = x - pointerX;
395
rs->pointerDy = y - pointerY;
397
rs->centered = rs->optionGetResizeFromCenterMatch ().evaluate (w);
399
if ((w->state () & MAXIMIZE_STATE) == MAXIMIZE_STATE)
401
/* if the window is fully maximized, showing the outline or
402
rectangle would be visually distracting as the window can't
403
be resized anyway; so we better don't use them in this case */
404
rs->mode = ResizeOptions::ModeNormal;
406
else if (!rs->gScreen || !rs->cScreen ||
407
!rs->cScreen->compositingActive ())
409
rs->mode = ResizeOptions::ModeNormal;
416
if (rs->mode != ResizeOptions::ModeNormal)
419
if (rw->gWindow && rs->mode == ResizeOptions::ModeStretch)
420
rw->gWindow->glPaintSetEnabled (rw, true);
421
if (rw->cWindow && rs->mode == ResizeOptions::ModeStretch)
422
rw->cWindow->damageRectSetEnabled (rw, true);
423
rs->gScreen->glPaintOutputSetEnabled (rs, true);
430
if (state & CompAction::StateInitKey)
431
cursor = rs->middleCursor;
433
cursor = rs->cursorFromResizeMask (mask);
435
rs->grabIndex = screen->pushGrab (cursor, "resize");
441
unsigned int grabMask = CompWindowGrabResizeMask |
442
CompWindowGrabButtonMask;
443
bool sourceExternalApp =
444
CompOption::getBoolOptionNamed (options, "external", false);
446
if (sourceExternalApp)
447
grabMask |= CompWindowGrabExternalAppMask;
449
rs->releaseButton = button;
451
w->grabNotify (x, y, state, grabMask);
453
if (screen->getOption ("raise_on_click")->value ().b ())
454
w->updateAttributes (CompStackingUpdateModeAboveFullscreen);
456
/* using the paint rectangle is enough here
457
as we don't have any stretch yet */
458
rs->getPaintRectangle (&box);
459
rs->damageRectangle (&box);
461
if (state & CompAction::StateInitKey)
465
xRoot = server.x () + (server.width () / 2);
466
yRoot = server.y () + (server.height () / 2);
468
screen->warpPointer (xRoot - pointerX, yRoot - pointerY);
471
rs->isConstrained = sourceExternalApp;
473
/* Update offWorkAreaConstrained and workArea at grab time */
474
rs->offWorkAreaConstrained = false;
475
if (sourceExternalApp)
477
int output = w->outputDevice ();
478
int lco, tco, bco, rco;
479
bool sl = screen->outputDevs ().at (output).workArea ().left () >
480
w->serverGeometry ().left ();
481
bool sr = screen->outputDevs ().at (output).workArea ().right () <
482
w->serverGeometry ().right ();
483
bool st = screen->outputDevs ().at (output).workArea ().top () >
484
w->serverGeometry ().top ();
485
bool sb = screen->outputDevs ().at (output).workArea ().bottom () <
486
w->serverGeometry ().bottom ();
488
lco = tco = bco = rco = output;
490
/* Prevent resizing beyond work area edges when resize is
491
initiated externally (e.g. with window frame or menu)
492
and not with a key (e.g. alt+button) */
493
rs->offWorkAreaConstrained = true;
495
lco = getOutputForEdge (output, TOUCH_RIGHT, sl);
496
rco = getOutputForEdge (output, TOUCH_LEFT, sr);
497
tco = getOutputForEdge (output, TOUCH_BOTTOM, st);
498
bco = getOutputForEdge (output, TOUCH_TOP, sb);
500
/* Now we need to form one big rect which describes
501
* the available workarea */
503
int left = screen->outputDevs ().at (lco).workArea ().left ();
504
int right = screen->outputDevs ().at (rco).workArea ().right ();
505
int top = screen->outputDevs ().at (tco).workArea ().top ();
506
int bottom = screen->outputDevs ().at (bco).workArea ().bottom ();
508
if (rs->grabWindowWorkArea)
509
delete rs->grabWindowWorkArea;
511
rs->grabWindowWorkArea = new CompRect (0, 0, 0, 0);
512
rs->grabWindowWorkArea->setLeft (left);
513
rs->grabWindowWorkArea->setRight (right);
514
rs->grabWindowWorkArea->setTop (top);
515
rs->grabWindowWorkArea->setBottom (bottom);
518
rs->inRegionStatus = false;
519
rs->lastGoodHotSpotY = -1;
520
rs->lastGoodSize = w->serverSize ();
522
/* Combine the work areas of all outputs */
523
rs->constraintRegion = emptyRegion;
524
foreach (CompOutput &output, ::screen->outputDevs ())
525
rs->constraintRegion += output.workArea ();
534
resizeInitiateDefaultMode (CompAction *action,
535
CompAction::State state,
536
CompOption::Vector &options)
541
RESIZE_SCREEN (screen);
543
w = screen->findWindow (CompOption::getIntOptionNamed (options, "window"));
547
mode = rs->optionGetMode ();
549
if (rs->optionGetNormalMatch ().evaluate (w))
550
mode = ResizeOptions::ModeNormal;
551
if (rs->optionGetOutlineMatch ().evaluate (w))
552
mode = ResizeOptions::ModeOutline;
553
if (rs->optionGetRectangleMatch ().evaluate (w))
554
mode = ResizeOptions::ModeRectangle;
555
if (rs->optionGetStretchMatch ().evaluate (w))
556
mode = ResizeOptions::ModeStretch;
558
return resizeInitiate (action, state, options, mode);
562
resizeTerminate (CompAction *action,
563
CompAction::State state,
564
CompOption::Vector &options)
566
RESIZE_SCREEN (screen);
570
CompWindow *w = rs->w;
572
unsigned int mask = 0;
574
if (rs->mode == ResizeOptions::ModeNormal)
576
if (state & CompAction::StateCancel)
578
xwc.x = rs->savedGeometry.x;
579
xwc.y = rs->savedGeometry.y;
580
xwc.width = rs->savedGeometry.width;
581
xwc.height = rs->savedGeometry.height;
583
mask = CWX | CWY | CWWidth | CWHeight;
590
if (state & CompAction::StateCancel)
591
geometry = rs->savedGeometry;
593
geometry = rs->geometry;
595
if (memcmp (&geometry, &rs->savedGeometry, sizeof (geometry)) == 0)
599
if (rs->mode == ResizeOptions::ModeStretch)
600
rs->getStretchRectangle (&box);
602
rs->getPaintRectangle (&box);
604
rs->damageRectangle (&box);
610
xwc.width = geometry.width;
611
xwc.height = geometry.height;
613
mask = CWX | CWY | CWWidth | CWHeight;
616
if (rs->mode != ResizeOptions::ModeNormal)
618
RESIZE_WINDOW (rs->w);
619
if (rw->gWindow && rs->mode == ResizeOptions::ModeStretch)
620
rw->gWindow->glPaintSetEnabled (rw, false);
621
if (rw->cWindow && rs->mode == ResizeOptions::ModeStretch)
622
rw->cWindow->damageRectSetEnabled (rw, false);
623
rs->gScreen->glPaintOutputSetEnabled (rs, false);
627
if ((mask & CWWidth) &&
628
xwc.width == (int) w->serverGeometry ().width ())
631
if ((mask & CWHeight) &&
632
xwc.height == (int) w->serverGeometry ().height ())
637
if (mask & (CWWidth | CWHeight))
638
w->sendSyncRequest ();
640
w->configureXWindow (mask, &xwc);
643
if (!(mask & (CWWidth | CWHeight)))
644
rs->finishResizing ();
648
screen->removeGrab (rs->grabIndex, NULL);
652
rs->releaseButton = 0;
655
action->setState (action->state () & ~(CompAction::StateTermKey |
656
CompAction::StateTermButton));
663
ResizeScreen::updateWindowSize ()
668
if (w->serverGeometry ().width () != geometry.width ||
669
w->serverGeometry ().height () != geometry.height)
675
xwc.width = geometry.width;
676
xwc.height = geometry.height;
678
w->sendSyncRequest ();
680
w->configureXWindow (CWX | CWY | CWWidth | CWHeight, &xwc);
685
ResizeScreen::handleKeyEvent (KeyCode keycode)
689
int widthInc, heightInc;
691
widthInc = w->sizeHints ().width_inc;
692
heightInc = w->sizeHints ().height_inc;
694
if (widthInc < MIN_KEY_WIDTH_INC)
695
widthInc = MIN_KEY_WIDTH_INC;
697
if (heightInc < MIN_KEY_HEIGHT_INC)
698
heightInc = MIN_KEY_HEIGHT_INC;
700
for (unsigned int i = 0; i < NUM_KEYS; i++)
702
if (keycode != key[i])
705
if (mask & rKeys[i].warpMask)
707
XWarpPointer (screen->dpy (), None, None, 0, 0, 0, 0,
708
rKeys[i].dx * widthInc, rKeys[i].dy * heightInc);
712
int x, y, left, top, width, height;
714
CompWindow::Geometry server = w->serverGeometry ();
715
const CompWindowExtents &border = w->border ();
717
left = server.x () - border.left;
718
top = server.y () - border.top;
719
width = border.left + server.width () + border.right;
720
height = border.top + server.height () + border.bottom;
722
x = left + width * (rKeys[i].dx + 1) / 2;
723
y = top + height * (rKeys[i].dy + 1) / 2;
725
screen->warpPointer (x - pointerX, y - pointerY);
727
mask = rKeys[i].resizeMask;
729
screen->updateGrab (grabIndex, cursor[i]);
737
ResizeScreen::handleMotionEvent (int xRoot, int yRoot)
742
int wi, he, cwi, che; /* size of window contents (c prefix for constrained)*/
743
int wX, wY, wWidth, wHeight; /* rect. for window contents+borders */
744
int workAreaSnapDistance = 15;
746
wi = savedGeometry.width;
747
he = savedGeometry.height;
752
int minPointerOffsetX, minPointerOffsetY;
754
CompWindow::Geometry server = w->serverGeometry ();
756
xDist = xRoot - (server.x () + (server.width () / 2));
757
yDist = yRoot - (server.y () + (server.height () / 2));
759
/* decision threshold is 10% of window size */
760
minPointerOffsetX = MIN (20, server.width () / 10);
761
minPointerOffsetY = MIN (20, server.height () / 10);
763
/* if we reached the threshold in one direction,
764
make the threshold in the other direction smaller
765
so there is a chance that this threshold also can
766
be reached (by diagonal movement) */
767
if (abs (xDist) > minPointerOffsetX)
768
minPointerOffsetY /= 2;
769
else if (abs (yDist) > minPointerOffsetY)
770
minPointerOffsetX /= 2;
772
if (abs (xDist) > minPointerOffsetX)
775
mask |= ResizeRightMask;
777
mask |= ResizeLeftMask;
780
if (abs (yDist) > minPointerOffsetY)
783
mask |= ResizeDownMask;
785
mask |= ResizeUpMask;
788
/* if the pointer movement was enough to determine a
789
direction, warp the pointer to the appropriate edge
790
and set the right cursor */
795
int pointerAdjustX = 0;
796
int pointerAdjustY = 0;
798
action = &optionGetInitiateKey ();
799
action->setState (action->state () |
800
CompAction::StateTermButton);
802
if (mask & ResizeRightMask)
803
pointerAdjustX = server.x () + server.width () +
804
w->border ().right - xRoot;
805
else if (mask & ResizeLeftMask)
806
pointerAdjustX = server.x () - w->border ().left -
809
if (mask & ResizeDownMask)
810
pointerAdjustY = server.y () + server.height () +
811
w->border ().bottom - yRoot;
812
else if (mask & ResizeUpMask)
813
pointerAdjustY = server.y () - w->border ().top - yRoot;
815
screen->warpPointer (pointerAdjustX, pointerAdjustY);
817
cursor = cursorFromResizeMask (mask);
818
screen->updateGrab (grabIndex, cursor);
823
/* only accumulate pointer movement if a mask is
824
already set as we don't have a use for the
825
difference information otherwise */
829
pointerDx += (xRoot - lastPointerX) * 2;
830
pointerDy += (yRoot - lastPointerY) * 2;
834
pointerDx += xRoot - lastPointerX;
835
pointerDy += yRoot - lastPointerY;
838
/* If we hit the edge of the screen while resizing
839
* the window and the adjacent window edge has not hit
840
* the edge of the screen, then accumulate pointer motion
841
* in the opposite direction. (So the apparant x / y
842
* mixup here is intentional)
847
if (mask == ResizeLeftMask)
850
geometry.x - w->border ().left > grabWindowWorkArea->left ())
851
pointerDx += abs (yRoot - lastPointerY) * -1;
853
else if (mask == ResizeRightMask)
855
if (xRoot == screen->width () -1 &&
856
geometry.x + geometry.width + w->border ().right < grabWindowWorkArea->right ())
857
pointerDx += abs (yRoot - lastPointerY);
859
if (mask == ResizeUpMask)
862
geometry.y - w->border ().top > grabWindowWorkArea->top ())
863
pointerDy += abs (xRoot - lastPointerX) * -1;
865
else if (mask == ResizeDownMask)
867
if (yRoot == screen->height () -1 &&
868
geometry.y + geometry.height + w->border ().bottom < grabWindowWorkArea->bottom ())
869
pointerDx += abs (yRoot - lastPointerY);
874
if (mask & ResizeLeftMask)
876
else if (mask & ResizeRightMask)
879
if (mask & ResizeUpMask)
881
else if (mask & ResizeDownMask)
884
if (w->state () & CompWindowStateMaximizedVertMask)
885
he = w->serverGeometry ().height ();
887
if (w->state () & CompWindowStateMaximizedHorzMask)
888
wi = w->serverGeometry ().width ();
893
if (w->constrainNewWindowSize (wi, he, &cwi, &che))
897
/* Also, damage relevant paint rectangles */
898
if (mode == ResizeOptions::ModeRectangle ||
899
mode == ResizeOptions::ModeOutline)
900
getPaintRectangle (&box);
901
else if (mode == ResizeOptions::ModeStretch)
902
getStretchRectangle (&box);
904
damageRectangle (&box);
907
/* constrain to work area */
908
if (offWorkAreaConstrained)
910
if (mask & ResizeUpMask)
912
int decorTop = savedGeometry.y + savedGeometry.height -
913
(che + w->border ().top);
915
if (grabWindowWorkArea->y () > decorTop)
916
che -= grabWindowWorkArea->y () - decorTop;
918
if (mask & ResizeDownMask)
920
int decorBottom = savedGeometry.y + che + w->border ().bottom;
923
grabWindowWorkArea->y () + grabWindowWorkArea->height ())
924
che -= decorBottom - (grabWindowWorkArea->y () +
925
grabWindowWorkArea->height ());
927
if (mask & ResizeLeftMask)
929
int decorLeft = savedGeometry.x + savedGeometry.width -
930
(cwi + w->border ().left);
932
if (grabWindowWorkArea->x () > decorLeft)
933
cwi -= grabWindowWorkArea->x () - decorLeft;
935
if (mask & ResizeRightMask)
937
int decorRight = savedGeometry.x + cwi + w->border ().right;
940
grabWindowWorkArea->x () + grabWindowWorkArea->width ())
941
cwi -= decorRight - (grabWindowWorkArea->x () +
942
grabWindowWorkArea->width ());
949
/* compute rect. for window + borders */
950
wWidth = wi + w->border ().left + w->border ().right;
951
wHeight = he + w->border ().top + w->border ().bottom;
955
if (mask & ResizeLeftMask)
956
wX = geometry.x + geometry.width -
957
(wi + w->border ().left);
959
wX = geometry.x - w->border ().left;
961
if (mask & ResizeUpMask)
962
wY = geometry.y + geometry.height -
963
(he + w->border ().top);
965
wY = geometry.y - w->border ().top;
969
if (mask & ResizeLeftMask)
970
wX = savedGeometry.x + savedGeometry.width -
971
(wi + w->border ().left);
973
wX = savedGeometry.x - w->border ().left;
975
if (mask & ResizeUpMask)
976
wY = savedGeometry.y + savedGeometry.height -
977
(he + w->border ().top);
979
wY = savedGeometry.y - w->border ().top;
982
/* Check if resized edge(s) are near output work-area boundaries */
983
foreach (CompOutput &output, ::screen->outputDevs ())
985
const CompRect &workArea = output.workArea ();
987
/* if window and work-area intersect in x axis */
988
if (wX + wWidth > workArea.x () &&
991
if (mask & ResizeLeftMask)
993
int dw = workArea.x () - wX;
995
if (0 < dw && dw < workAreaSnapDistance)
1002
else if (mask & ResizeRightMask)
1004
int dw = wX + wWidth - workArea.x2 ();
1006
if (0 < dw && dw < workAreaSnapDistance)
1014
/* if window and work-area intersect in y axis */
1015
if (wY + wHeight > workArea.y () &&
1016
wY < workArea.y2 ())
1018
if (mask & ResizeUpMask)
1020
int dh = workArea.y () - wY;
1022
if (0 < dh && dh < workAreaSnapDistance)
1029
else if (mask & ResizeDownMask)
1031
int dh = wY + wHeight - workArea.y2 ();
1033
if (0 < dh && dh < workAreaSnapDistance)
1047
/* rect. for a minimal height window + borders
1048
(used for the constraining in X axis) */
1049
int minimalInputHeight = minHeight +
1050
w->border ().top + w->border ().bottom;
1052
/* small hot-spot square (on window's corner or edge) that is to be
1053
constrained to the combined output work-area region */
1055
int width = w->border ().top; /* square size = title bar height */
1057
bool status; /* whether or not hot-spot is in the region */
1059
/* compute x & y for constrained hot-spot rect */
1060
if (mask & ResizeLeftMask)
1062
else if (mask & ResizeRightMask)
1063
x = wX + wWidth - width;
1065
x = MIN (MAX (xRoot, wX), wX + wWidth - width);
1067
if (mask & ResizeUpMask)
1069
else if (mask & ResizeDownMask)
1070
y = wY + wHeight - height;
1072
y = MIN (MAX (yRoot, wY), wY + wHeight - height);
1074
status = constraintRegion.contains (x, y, width, height);
1076
/* only constrain movement if previous position was valid */
1079
bool xStatus = false;
1080
int yForXResize = y;
1085
if (mask & (ResizeLeftMask | ResizeRightMask))
1089
if (mask & ResizeUpMask)
1090
yForXResize = wY + wHeight - minimalInputHeight;
1091
else if (mask & ResizeDownMask)
1092
yForXResize = wY + minimalInputHeight - height;
1096
if (!constraintRegion.contains (x, yForXResize,
1099
if (lastGoodHotSpotY >= 0)
1100
yForXResize = lastGoodHotSpotY;
1105
if (mask & ResizeLeftMask)
1107
while ((nw > minWidth) && !xStatus)
1109
xStatus = constraintRegion.contains (nx, yForXResize,
1123
else if (mask & ResizeRightMask)
1125
while ((nw > minWidth) && !xStatus)
1127
xStatus = constraintRegion.contains (nx, yForXResize,
1142
if (mask & ResizeUpMask)
1144
while ((nh > minHeight) && !status)
1146
status = constraintRegion.contains (x, y,
1157
else if (mask & ResizeDownMask)
1159
while ((nh > minHeight) && !status)
1161
status = constraintRegion.contains (x, y,
1173
if (((mask & (ResizeLeftMask | ResizeRightMask)) && xStatus) ||
1174
((mask & (ResizeUpMask | ResizeDownMask)) && status))
1176
/* hot-spot inside work-area region, store good values */
1177
lastGoodHotSpotY = y;
1178
lastGoodSize = CompSize (wi, he);
1182
/* failed to find a good hot-spot position, restore size */
1183
wi = lastGoodSize.width ();
1184
he = lastGoodSize.height ();
1189
inRegionStatus = status;
1193
if (mode != ResizeOptions::ModeNormal)
1195
if (mode == ResizeOptions::ModeStretch)
1196
getStretchRectangle (&box);
1198
getPaintRectangle (&box);
1200
damageRectangle (&box);
1205
if ((mask & ResizeLeftMask) || (mask & ResizeRightMask))
1206
geometry.x -= ((wi - geometry.width) / 2);
1207
if ((mask & ResizeUpMask) || (mask & ResizeDownMask))
1208
geometry.y -= ((he - geometry.height) / 2);
1212
if (mask & ResizeLeftMask)
1213
geometry.x -= wi - geometry.width;
1214
if (mask & ResizeUpMask)
1215
geometry.y -= he - geometry.height;
1218
geometry.width = wi;
1219
geometry.height = he;
1221
if (mode != ResizeOptions::ModeNormal)
1223
if (mode == ResizeOptions::ModeStretch)
1224
getStretchRectangle (&box);
1226
getPaintRectangle (&box);
1228
damageRectangle (&box);
1232
updateWindowSize ();
1235
updateWindowProperty ();
1236
sendResizeNotify ();
1241
ResizeScreen::handleEvent (XEvent *event)
1243
switch (event->type) {
1245
if (event->xkey.root == screen->root ())
1246
handleKeyEvent (event->xkey.keycode);
1249
if (event->xbutton.root == screen->root ())
1253
if (releaseButton == -1 ||
1254
(int) event->xbutton.button == releaseButton)
1256
CompAction *action = &optionGetInitiateButton ();
1258
resizeTerminate (action, CompAction::StateTermButton,
1265
if (event->xmotion.root == screen->root ())
1266
handleMotionEvent (pointerX, pointerY);
1270
if (event->xcrossing.root == screen->root ())
1271
handleMotionEvent (pointerX, pointerY);
1274
if (event->xclient.message_type == Atoms::wmMoveResize)
1277
unsigned long type = event->xclient.data.l[2];
1279
RESIZE_SCREEN (screen);
1281
if (type <= WmMoveResizeSizeLeft ||
1282
type == WmMoveResizeSizeKeyboard)
1284
w = screen->findWindow (event->xclient.window);
1287
CompOption::Vector o (0);
1289
o.push_back (CompOption ("window",
1290
CompOption::TypeInt));
1291
o[0].value ().set ((int) event->xclient.window);
1293
o.push_back (CompOption ("external",
1294
CompOption::TypeBool));
1295
o[1].value ().set (true);
1297
if (event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
1299
resizeInitiateDefaultMode (&optionGetInitiateKey (),
1300
CompAction::StateInitKey,
1305
static unsigned int mask[] = {
1306
ResizeUpMask | ResizeLeftMask,
1308
ResizeUpMask | ResizeRightMask,
1310
ResizeDownMask | ResizeRightMask,
1312
ResizeDownMask | ResizeLeftMask,
1316
/* TODO: not only button 1 */
1317
if (pointerMods & Button1Mask)
1319
o.push_back (CompOption ("modifiers",
1320
CompOption::TypeInt));
1321
o.push_back (CompOption ("x",
1322
CompOption::TypeInt));
1323
o.push_back (CompOption ("y",
1324
CompOption::TypeInt));
1325
o.push_back (CompOption ("direction",
1326
CompOption::TypeInt));
1327
o.push_back (CompOption ("button",
1328
CompOption::TypeInt));
1330
o[2].value ().set ((int) pointerMods);
1332
((int) event->xclient.data.l[0]);
1334
((int) event->xclient.data.l[1]);
1336
((int) mask[event->xclient.data.l[2]]);
1338
((int) (event->xclient.data.l[3] ?
1339
event->xclient.data.l[3] : -1));
1341
resizeInitiateDefaultMode (
1342
&optionGetInitiateButton (),
1343
CompAction::StateInitButton, o);
1345
ResizeScreen::get (screen)->
1346
handleMotionEvent (pointerX, pointerY);
1351
else if (rs->w && type == WmMoveResizeCancel)
1353
if (rs->w->id () == event->xclient.window)
1355
resizeTerminate (&optionGetInitiateButton (),
1356
CompAction::StateCancel, noOptions);
1357
resizeTerminate (&optionGetInitiateKey (),
1358
CompAction::StateCancel, noOptions);
1364
if (w && w->id () == event->xdestroywindow.window)
1366
resizeTerminate (&optionGetInitiateButton (), 0, noOptions);
1367
resizeTerminate (&optionGetInitiateKey (), 0, noOptions);
1371
if (w && w->id () == event->xunmap.window)
1373
resizeTerminate (&optionGetInitiateButton (), 0, noOptions);
1374
resizeTerminate (&optionGetInitiateKey (), 0, noOptions);
1380
if (event->type == screen->xkbEvent () && w)
1382
XkbAnyEvent *xkbEvent = (XkbAnyEvent *) event;
1384
if (xkbEvent->xkb_type == XkbStateNotify)
1386
XkbStateNotifyEvent *stateEvent = (XkbStateNotifyEvent *) event;
1388
/* Check if we need to change to outline mode */
1390
unsigned int mods = 0xffffffff;
1391
bool modifierMode = false;
1396
if ((stateEvent->mods & mods) == mods)
1398
modifierMode = true;
1399
mode = ResizeOptions::ModeOutline;
1404
mods = rectangleMask;
1406
if ((stateEvent->mods & mods) == mods)
1408
modifierMode = true;
1409
mode = ResizeOptions::ModeRectangle;
1416
if ((stateEvent->mods & mods) == mods)
1418
modifierMode = true;
1419
mode = ResizeOptions::ModeStretch;
1424
mods = centeredMask;
1427
mode = optionGetMode ();
1429
if (oldMode != mode)
1433
getStretchRectangle (&box);
1434
damageRectangle (&box);
1435
getPaintRectangle (&box);
1436
damageRectangle (&box);
1438
box.x1 = w->outputRect ().x ();
1439
box.y1 = w->outputRect ().y ();
1440
box.x2 = box.x1 + w->outputRect ().width ();
1441
box.y2 = box.y1 + w->outputRect ().height ();
1443
damageRectangle (&box);
1446
if ((stateEvent->mods & mods) == mods)
1449
!optionGetResizeFromCenterMatch ().evaluate (w)))
1454
screen->handleEvent (event);
1456
if (event->type == screen->syncEvent () + XSyncAlarmNotify)
1460
XSyncAlarmNotifyEvent *sa;
1462
sa = (XSyncAlarmNotifyEvent *) event;
1464
if (w->syncAlarm () == sa->alarm)
1465
updateWindowSize ();
1471
ResizeWindow::resizeNotify (int dx, int dy, int dwidth, int dheight)
1473
window->resizeNotify (dx, dy, dwidth, dheight);
1475
if (rScreen->w == window && !rScreen->grabIndex)
1476
rScreen->finishResizing ();
1480
ResizeScreen::glPaintRectangle (const GLScreenPaintAttrib &sAttrib,
1481
const GLMatrix &transform,
1483
unsigned short *borderColor,
1484
unsigned short *fillColor)
1487
GLMatrix sTransform (transform);
1489
getPaintRectangle (&box);
1493
sTransform.toScreenSpace (output, -DEFAULT_Z_CAMERA);
1495
glLoadMatrixf (sTransform.getMatrix ());
1497
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
1498
glEnable (GL_BLEND);
1500
/* fill rectangle */
1503
glColor4usv (fillColor);
1504
glRecti (box.x1, box.y2, box.x2, box.y1);
1508
glColor4usv (borderColor);
1510
glBegin (GL_LINE_LOOP);
1511
glVertex2i (box.x1, box.y1);
1512
glVertex2i (box.x2, box.y1);
1513
glVertex2i (box.x2, box.y2);
1514
glVertex2i (box.x1, box.y2);
1518
glColor4usv (defaultColor);
1519
glDisable (GL_BLEND);
1520
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
1525
ResizeScreen::glPaintOutput (const GLScreenPaintAttrib &sAttrib,
1526
const GLMatrix &transform,
1527
const CompRegion ®ion,
1535
if (mode == ResizeOptions::ModeStretch)
1536
mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
1539
status = gScreen->glPaintOutput (sAttrib, transform, region, output, mask);
1543
unsigned short *border, *fill;
1545
border = optionGetBorderColor ();
1546
fill = optionGetFillColor ();
1549
case ResizeOptions::ModeOutline:
1550
glPaintRectangle (sAttrib, transform, output, border, NULL);
1552
case ResizeOptions::ModeRectangle:
1553
glPaintRectangle (sAttrib, transform, output, border, fill);
1563
ResizeWindow::glPaint (const GLWindowPaintAttrib &attrib,
1564
const GLMatrix &transform,
1565
const CompRegion ®ion,
1570
if (window == rScreen->w && rScreen->mode == ResizeOptions::ModeStretch)
1572
GLMatrix wTransform (transform);
1574
float xOrigin, yOrigin;
1575
float xScale, yScale;
1578
if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
1581
status = gWindow->glPaint (attrib, transform, region,
1582
mask | PAINT_WINDOW_NO_CORE_INSTANCE_MASK);
1584
GLFragment::Attrib fragment (gWindow->lastPaintAttrib ());
1586
if (window->alpha () || fragment.getOpacity () != OPAQUE)
1587
mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
1589
rScreen->getPaintRectangle (&box);
1590
getStretchScale (&box, &xScale, &yScale);
1592
x = window->geometry (). x ();
1593
y = window->geometry (). y ();
1595
xOrigin = x - window->border ().left;
1596
yOrigin = y - window->border ().top;
1598
wTransform.translate (xOrigin, yOrigin, 0.0f);
1599
wTransform.scale (xScale, yScale, 1.0f);
1600
wTransform.translate ((rScreen->geometry.x - x) / xScale - xOrigin,
1601
(rScreen->geometry.y - y) / yScale - yOrigin,
1605
glLoadMatrixf (wTransform.getMatrix ());
1607
gWindow->glDraw (wTransform, fragment, region,
1608
mask | PAINT_WINDOW_TRANSFORMED_MASK);
1614
status = gWindow->glPaint (attrib, transform, region, mask);
1621
ResizeWindow::damageRect (bool initial, const CompRect &rect)
1623
bool status = false;
1625
if (window == rScreen->w && rScreen->mode == ResizeOptions::ModeStretch)
1629
rScreen->getStretchRectangle (&box);
1630
rScreen->damageRectangle (&box);
1635
status |= cWindow->damageRect (initial, rect);
1640
/* We have to make some assumptions here in order to do this neatly,
1641
* see build/generated/resize_options.h for more info */
1643
#define ResizeModeShiftMask (1 << 0)
1644
#define ResizeModeAltMask (1 << 1)
1645
#define ResizeModeControlMask (1 << 2)
1646
#define ResizeModeMetaMask (1 << 3)
1649
ResizeScreen::resizeMaskValueToKeyMask (int valueMask,
1652
if (valueMask & ResizeModeShiftMask)
1654
if (valueMask & ResizeModeAltMask)
1655
*mask |= CompAltMask;
1656
if (valueMask & ResizeModeControlMask)
1657
*mask |= ControlMask;
1658
if (valueMask & ResizeModeMetaMask)
1659
*mask |= CompMetaMask;
1663
ResizeScreen::optionChanged (CompOption *option,
1664
ResizeOptions::Options num)
1671
case ResizeOptions::OutlineModifier:
1672
mask = &outlineMask;
1673
valueMask = optionGetOutlineModifierMask ();
1675
case ResizeOptions::RectangleModifier:
1676
mask = &rectangleMask;
1677
valueMask = optionGetRectangleModifierMask ();
1679
case ResizeOptions::StretchModifier:
1680
mask = &stretchMask;
1681
valueMask = optionGetStretchModifierMask ();
1683
case ResizeOptions::CenteredModifier:
1684
mask = ¢eredMask;
1685
valueMask = optionGetCenteredModifierMask ();
1692
resizeMaskValueToKeyMask (valueMask, mask);
1695
ResizeScreen::ResizeScreen (CompScreen *s) :
1696
PluginClassHandler<ResizeScreen,CompScreen> (s),
1697
gScreen (GLScreen::get (s)),
1698
cScreen (CompositeScreen::get (s)),
1705
isConstrained (false),
1706
offWorkAreaConstrained (true),
1707
grabWindowWorkArea (NULL)
1709
CompOption::Vector atomTemplate;
1710
Display *dpy = s->dpy ();
1711
ResizeOptions::ChangeNotify notify =
1712
boost::bind (&ResizeScreen::optionChanged, this, _1, _2);
1714
atomTemplate.resize (4);
1716
for (int i = 0; i < 4; i++)
1719
snprintf (buf, 4, "%i", i);
1720
CompString tmpName (buf);
1722
atomTemplate.at (i).setName (tmpName, CompOption::TypeInt);
1725
resizeNotifyAtom = XInternAtom (s->dpy (), "_COMPIZ_RESIZE_NOTIFY", 0);
1726
resizeInformationAtom = PropertyWriter ("_COMPIZ_RESIZE_INFORMATION",
1729
for (unsigned int i = 0; i < NUM_KEYS; i++)
1730
key[i] = XKeysymToKeycode (s->dpy (), XStringToKeysym (rKeys[i].name));
1734
leftCursor = XCreateFontCursor (dpy, XC_left_side);
1735
rightCursor = XCreateFontCursor (dpy, XC_right_side);
1736
upCursor = XCreateFontCursor (dpy, XC_top_side);
1737
upLeftCursor = XCreateFontCursor (dpy, XC_top_left_corner);
1738
upRightCursor = XCreateFontCursor (dpy, XC_top_right_corner);
1739
downCursor = XCreateFontCursor (dpy, XC_bottom_side);
1740
downLeftCursor = XCreateFontCursor (dpy, XC_bottom_left_corner);
1741
downRightCursor = XCreateFontCursor (dpy, XC_bottom_right_corner);
1742
middleCursor = XCreateFontCursor (dpy, XC_fleur);
1744
cursor[0] = leftCursor;
1745
cursor[1] = rightCursor;
1746
cursor[2] = upCursor;
1747
cursor[3] = downCursor;
1749
optionSetInitiateKeyInitiate (resizeInitiateDefaultMode);
1750
optionSetInitiateKeyTerminate (resizeTerminate);
1751
optionSetInitiateButtonInitiate (resizeInitiateDefaultMode);
1752
optionSetInitiateButtonTerminate (resizeTerminate);
1754
optionSetOutlineModifierNotify (notify);
1755
optionSetRectangleModifierNotify (notify);
1756
optionSetStretchModifierNotify (notify);
1757
optionSetCenteredModifierNotify (notify);
1759
resizeMaskValueToKeyMask (optionGetOutlineModifierMask (), &outlineMask);
1760
resizeMaskValueToKeyMask (optionGetRectangleModifierMask (), &rectangleMask);
1761
resizeMaskValueToKeyMask (optionGetStretchModifierMask (), &stretchMask);
1762
resizeMaskValueToKeyMask (optionGetCenteredModifierMask (), ¢eredMask);
1764
ScreenInterface::setHandler (s);
1767
GLScreenInterface::setHandler (gScreen, false);
1770
ResizeScreen::~ResizeScreen ()
1772
Display *dpy = screen->dpy ();
1775
XFreeCursor (dpy, leftCursor);
1777
XFreeCursor (dpy, rightCursor);
1779
XFreeCursor (dpy, upCursor);
1781
XFreeCursor (dpy, downCursor);
1783
XFreeCursor (dpy, middleCursor);
1785
XFreeCursor (dpy, upLeftCursor);
1787
XFreeCursor (dpy, upRightCursor);
1789
XFreeCursor (dpy, downLeftCursor);
1790
if (downRightCursor)
1791
XFreeCursor (dpy, downRightCursor);
1794
ResizeWindow::ResizeWindow (CompWindow *w) :
1795
PluginClassHandler<ResizeWindow,CompWindow> (w),
1797
gWindow (GLWindow::get (w)),
1798
cWindow (CompositeWindow::get (w)),
1799
rScreen (ResizeScreen::get (screen))
1801
WindowInterface::setHandler (window);
1804
CompositeWindowInterface::setHandler (cWindow, false);
1807
GLWindowInterface::setHandler (gWindow, false);
1811
ResizeWindow::~ResizeWindow ()
1817
ResizePluginVTable::init ()
1819
if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))