1 /*
2 * Copyright �� 2005 Novell, Inc.
3 * Copyright �� 2012 Canonical Ltd.
4 *
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
14 * implied warranty.
15 *
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.
23 *
24 * Authors: David Reveman <davidr@novell.com>
25 * Daniel d'Andrada <daniel.dandrada@canonical.com>
26 */
27
28 #include <core/core.h>
29 #include <core/atoms.h>
30
31 #include "resize-logic.h"
32
33 #include "resize_options.h"
34
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"
43
44 #define XWINDOWCHANGES_INIT {0, 0, 0, 0, 0, None, 0}
45
46 static const unsigned short TOUCH_LEFT = 1;
47 static const unsigned short TOUCH_RIGHT = 2;
48 static const unsigned short TOUCH_TOP = 3;
49 static const unsigned short TOUCH_BOTTOM = 4;
50
51 using namespace resize;
52
53 ResizeLogic::ResizeLogic() :
54 mScreen (NULL),
55 w (NULL),
56 centered (false),
57 maximized_vertically (false),
58 outlineMask (0),
59 rectangleMask (0),
60 stretchMask (0),
61 centeredMask (0),
62 releaseButton (0),
63 grabIndex (0),
64 isConstrained (false),
65 offWorkAreaConstrained (true),
66 options (NULL),
67 cScreen (NULL),
68 gScreen (NULL)
69 {
70 rKeys[0].name = "Left";
71 rKeys[0].dx = -1;
72 rKeys[0].dy = 0;
73 rKeys[0].warpMask = ResizeLeftMask | ResizeRightMask;
74 rKeys[0].resizeMask = ResizeLeftMask;
75
76 rKeys[1].name = "Right";
77 rKeys[1].dx = 1;
78 rKeys[1].dy = 0;
79 rKeys[1].warpMask = ResizeLeftMask | ResizeRightMask;
80 rKeys[1].resizeMask = ResizeRightMask;
81
82 rKeys[2].name = "Up";
83 rKeys[2].dx = 0;
84 rKeys[2].dy = -1;
85 rKeys[2].warpMask = ResizeUpMask | ResizeDownMask;
86 rKeys[2].resizeMask = ResizeUpMask;
87
88 rKeys[3].name = "Down";
89 rKeys[3].dx = 0;
90 rKeys[3].dy = 1;
91 rKeys[3].warpMask = ResizeUpMask | ResizeDownMask;
92 rKeys[3].resizeMask = ResizeDownMask;
93 }
94
95 ResizeLogic::~ResizeLogic()
96 {
97 }
98
99 void
100 ResizeLogic::handleEvent (XEvent *event)
101 {
102 switch (event->type) {
103 case KeyPress:
104 if (event->xkey.root == mScreen->root ())
105 handleKeyEvent (event->xkey.keycode);
106 break;
107 case ButtonRelease:
108 if (event->xbutton.root == mScreen->root ())
109 {
110 if (grabIndex)
111 {
112 if (releaseButton == -1 ||
113 (int) event->xbutton.button == releaseButton)
114 {
115 CompAction *action = &options->optionGetInitiateButton ();
116
117 terminateResize (action, CompAction::StateTermButton,
118 noOptions ());
119 }
120 }
121 }
122 break;
123 case MotionNotify:
124 if (event->xmotion.root == mScreen->root ())
125 handleMotionEvent (pointerX, pointerY);
126 break;
127 case EnterNotify:
128 case LeaveNotify:
129 if (event->xcrossing.root == mScreen->root ())
130 handleMotionEvent (pointerX, pointerY);
131 break;
132 case ClientMessage:
133 if (event->xclient.message_type == Atoms::wmMoveResize)
134 {
135 unsigned long type = event->xclient.data.l[2];
136
137 if (type <= WmMoveResizeSizeLeft ||
138 type == WmMoveResizeSizeKeyboard)
139 {
140 CompWindowInterface *w;
141 w = mScreen->findWindow (event->xclient.window);
142 if (w)
143 {
144 mScreen->freeWindowInterface (w);
145 w = NULL;
146
147 CompOption::Vector o (0);
148
149 o.push_back (CompOption ("window",
150 CompOption::TypeInt));
151 o[0].value ().set ((int) event->xclient.window);
152
153 o.push_back (CompOption ("external",
154 CompOption::TypeBool));
155 o[1].value ().set (true);
156
157 if (event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
158 {
159 initiateResizeDefaultMode (&options->optionGetInitiateKey (),
160 CompAction::StateInitKey,
161 o);
162 }
163 else
164 {
165 /* TODO: not only button 1 */
166 if (pointerMods & Button1Mask)
167 {
168 static unsigned int mask[] = {
169 ResizeUpMask | ResizeLeftMask,
170 ResizeUpMask,
171 ResizeUpMask | ResizeRightMask,
172 ResizeRightMask,
173 ResizeDownMask | ResizeRightMask,
174 ResizeDownMask,
175 ResizeDownMask | ResizeLeftMask,
176 ResizeLeftMask,
177 };
178 o.push_back (CompOption ("modifiers",
179 CompOption::TypeInt));
180 o.push_back (CompOption ("x",
181 CompOption::TypeInt));
182 o.push_back (CompOption ("y",
183 CompOption::TypeInt));
184 o.push_back (CompOption ("direction",
185 CompOption::TypeInt));
186 o.push_back (CompOption ("button",
187 CompOption::TypeInt));
188
189 o[2].value ().set ((int) pointerMods);
190 o[3].value ().set
191 ((int) event->xclient.data.l[0]);
192 o[4].value ().set
193 ((int) event->xclient.data.l[1]);
194 o[5].value ().set
195 ((int) mask[event->xclient.data.l[2]]);
196 o[6].value ().set
197 ((int) (event->xclient.data.l[3] ?
198 event->xclient.data.l[3] : -1));
199
200 initiateResizeDefaultMode (
201 &options->optionGetInitiateButton (),
202 CompAction::StateInitButton, o);
203
204 handleMotionEvent (pointerX, pointerY);
205 }
206 }
207 }
208 }
209 else if (this->w && type == WmMoveResizeCancel)
210 {
211 if (this->w->id () == event->xclient.window)
212 {
213 terminateResize (&options->optionGetInitiateButton (),
214 CompAction::StateCancel, noOptions ());
215 terminateResize (&options->optionGetInitiateKey (),
216 CompAction::StateCancel, noOptions ());
217 }
218 }
219 }
220 break;
221 case DestroyNotify:
222 if (w && w->id () == event->xdestroywindow.window)
223 {
224 terminateResize (&options->optionGetInitiateButton (), 0, noOptions ());
225 terminateResize (&options->optionGetInitiateKey (), 0, noOptions ());
226 }
227 break;
228 case UnmapNotify:
229 if (w && w->id () == event->xunmap.window)
230 {
231 terminateResize (&options->optionGetInitiateButton (), 0, noOptions ());
232 terminateResize (&options->optionGetInitiateKey (), 0, noOptions ());
233 }
234 default:
235 break;
236 }
237
238 if (event->type == mScreen->xkbEvent ())
239 {
240 XkbAnyEvent *xkbEvent = (XkbAnyEvent *) event;
241
242 if (xkbEvent->xkb_type == XkbStateNotify)
243 {
244 XkbStateNotifyEvent *stateEvent = (XkbStateNotifyEvent *) event;
245
246 /* Check if we need to change to outline mode */
247
248 unsigned int mods = 0xffffffff;
249 bool modifierMode = false;
250 int oldMode = mode;
251
252 if (outlineMask)
253 mods = outlineMask;
254
255 if ((stateEvent->mods & mods) == mods)
256 {
257 modifierMode = true;
258 mode = ResizeOptions::ModeOutline;
259 }
260
261 mods = 0xffffffff;
262 if (rectangleMask)
263 mods = rectangleMask;
264
265 if ((stateEvent->mods & mods) == mods)
266 {
267 modifierMode = true;
268 mode = ResizeOptions::ModeRectangle;
269 }
270
271 mods = 0xffffffff;
272 if (stretchMask)
273 mods = stretchMask;
274
275 if ((stateEvent->mods & mods) == mods)
276 {
277 modifierMode = true;
278 mode = ResizeOptions::ModeStretch;
279 }
280
281 mods = 0xffffffff;
282 if (centeredMask)
283 mods = centeredMask;
284
285 /* No modifier mode set, check match options */
286 if (w)
287 {
288 if (w->evaluate (options->optionGetNormalMatch ()))
289 {
290 modifierMode = true;
291 mode = ResizeOptions::ModeNormal;
292 }
293
294 if (w->evaluate (options->optionGetOutlineMatch ()))
295 {
296 modifierMode = true;
297 mode = ResizeOptions::ModeOutline;
298 }
299
300 if (w->evaluate (options->optionGetRectangleMatch ()))
301 {
302 modifierMode = true;
303 mode = ResizeOptions::ModeRectangle;
304 }
305
306 if (w->evaluate (options->optionGetStretchMatch ()))
307 {
308 modifierMode = true;
309 mode = ResizeOptions::ModeStretch;
310 }
311 }
312
313 if (!modifierMode)
314 mode = options->optionGetMode ();
315
316 if (w && oldMode != mode)
317 {
318 Box box;
319
320 getStretchRectangle (&box);
321 damageRectangle (&box);
322 getPaintRectangle (&box);
323 damageRectangle (&box);
324
325 box.x1 = w->outputRect ().x ();
326 box.y1 = w->outputRect ().y ();
327 box.x2 = box.x1 + w->outputRect ().width ();
328 box.y2 = box.y1 + w->outputRect ().height ();
329
330 damageRectangle (&box);
331 }
332
333 if ((stateEvent->mods & mods) == mods)
334 {
335 centered = true;
336 }
337 else if (w)
338 {
339 if (!w->evaluate (options->optionGetResizeFromCenterMatch ()))
340 centered = false;
341 else
342 centered = true;
343 }
344 else
345 {
346 centered = false;
347 }
348 }
349 }
350
351 mScreen->handleEvent (event);
352
353 if (event->type == mScreen->syncEvent () + XSyncAlarmNotify)
354 {
355 if (w)
356 {
357 XSyncAlarmNotifyEvent *sa;
358
359 sa = (XSyncAlarmNotifyEvent *) event;
360
361 if (w->syncAlarm () == sa->alarm)
362 updateWindowSize ();
363 }
364 }
365 }
366
367 void
368 ResizeLogic::handleKeyEvent (KeyCode keycode)
369 {
370 if (grabIndex && w)
371 {
372 int widthInc, heightInc;
373
374 widthInc = w->sizeHints ().width_inc;
375 heightInc = w->sizeHints ().height_inc;
376
377 if (widthInc < MIN_KEY_WIDTH_INC)
378 widthInc = MIN_KEY_WIDTH_INC;
379
380 if (heightInc < MIN_KEY_HEIGHT_INC)
381 heightInc = MIN_KEY_HEIGHT_INC;
382
383 for (unsigned int i = 0; i < NUM_KEYS; i++)
384 {
385 if (keycode != key[i])
386 continue;
387
388 if (mask & rKeys[i].warpMask)
389 {
390 XWarpPointer (mScreen->dpy (), None, None, 0, 0, 0, 0,
391 rKeys[i].dx * widthInc, rKeys[i].dy * heightInc);
392 }
393 else
394 {
395 int x, y, left, top, width, height;
396
397 CompWindow::Geometry server = w->serverGeometry ();
398 const CompWindowExtents &border = w->border ();
399
400 left = server.x () - border.left;
401 top = server.y () - border.top;
402 width = border.left + server.width () + border.right;
403 height = border.top + server.height () + border.bottom;
404
405 x = left + width * (rKeys[i].dx + 1) / 2;
406 y = top + height * (rKeys[i].dy + 1) / 2;
407
408 mScreen->warpPointer (x - pointerX, y - pointerY);
409
410 mask = rKeys[i].resizeMask;
411
412 mScreen->updateGrab (grabIndex, cursor[i]);
413 }
414 break;
415 }
416 }
417 }
418
419 void
420 ResizeLogic::handleMotionEvent (int xRoot, int yRoot)
421 {
Condition "this->grabIndex", taking true branch
422 if (grabIndex)
423 {
424 BoxRec box;
425 int wi, he, cwi, che; /* size of window contents (c prefix for constrained)*/
426 int wX, wY, wWidth, wHeight; /* rect. for window contents+borders */
427
428 wi = savedGeometry.width;
429 he = savedGeometry.height;
430
Condition "!this->mask", taking true branch
431 if (!mask)
432 {
433 setUpMask (xRoot, yRoot);
Falling through to end of if statement
434 }
435 else
436 {
437 accumulatePointerMotion (xRoot, yRoot);
End of if statement
438 }
439
Condition "this->mask & (4L /* 1L << 2 */)", taking false branch
440 if (mask & ResizeLeftMask)
441 wi -= pointerDx;
Condition "this->mask & (8L /* 1L << 3 */)", taking false branch
442 else if (mask & ResizeRightMask)
443 wi += pointerDx;
444
Condition "this->mask & (1L /* 1L << 0 */)", taking false branch
445 if (mask & ResizeUpMask)
446 he -= pointerDy;
Condition "this->mask & (2L /* 1L << 1 */)", taking false branch
447 else if (mask & ResizeDownMask)
448 he += pointerDy;
449
Condition "this->w->state() & (4U /* 1 << 2 */)", taking true branch
450 if (w->state () & CompWindowStateMaximizedVertMask)
451 he = w->serverGeometry ().height ();
452
Condition "this->w->state() & (8U /* 1 << 3 */)", taking true branch
453 if (w->state () & CompWindowStateMaximizedHorzMask)
454 wi = w->serverGeometry ().width ();
455
456 cwi = wi;
457 che = he;
458
Condition "this->w->constrainNewWindowSize(wi, he, &cwi, &che)", taking true branch
Condition "this->mode != ResizeOptions::ModeNormal", taking true branch
459 if (w->constrainNewWindowSize (wi, he, &cwi, &che) &&
460 mode != ResizeOptions::ModeNormal)
461 {
CID 12555 - UNINIT
Declaring variable "box" without initializer.
462 Box box;
463
464 /* Also, damage relevant paint rectangles */
Condition "this->mode == ResizeOptions::ModeRectangle", taking false branch
Condition "this->mode == ResizeOptions::ModeOutline", taking false branch
465 if (mode == ResizeOptions::ModeRectangle ||
466 mode == ResizeOptions::ModeOutline)
467 getPaintRectangle (&box);
Condition "this->mode == ResizeOptions::ModeStretch", taking false branch
468 else if (mode == ResizeOptions::ModeStretch)
469 getStretchRectangle (&box);
470
CID 12555 - UNINIT
Using uninitialized value "box.x1" when calling "ResizeLogic::damageRectangle(BoxPtr)".
Using uninitialized value "box.x2" when calling "ResizeLogic::damageRectangle(BoxPtr)".
Using uninitialized value "box.y1" when calling "ResizeLogic::damageRectangle(BoxPtr)".
Using uninitialized value "box.y2" when calling "ResizeLogic::damageRectangle(BoxPtr)".
471 damageRectangle (&box);
472 }
473
474 if (offWorkAreaConstrained)
475 constrainToWorkArea (che, cwi);
476
477 wi = cwi;
478 he = che;
479
480 /* compute rect. for window + borders */
481 computeWindowPlusBordersRect (wX, wY, wWidth, wHeight, /*out*/
482 wi, he); /*in*/
483
484 snapWindowToWorkAreaBoundaries (wi, he, wX, wY, wWidth, wHeight);
485
486 if (isConstrained)
487 limitMovementToConstraintRegion (wi, he, /*in/out*/
488 xRoot, yRoot,
489 wX, wY, wWidth, wHeight); /*in*/
490
491 if (mode != ResizeOptions::ModeNormal)
492 {
493 if (mode == ResizeOptions::ModeStretch)
494 getStretchRectangle (&box);
495 else
496 getPaintRectangle (&box);
497
498 damageRectangle (&box);
499 }
500
501 enableOrDisableVerticalMaximization (yRoot);
502
503 computeGeometry (wi, he);
504
505 if (mode != ResizeOptions::ModeNormal)
506 {
507 if (mode == ResizeOptions::ModeStretch)
508 getStretchRectangle (&box);
509 else
510 getPaintRectangle (&box);
511
512 damageRectangle (&box);
513 }
514 else
515 {
516 updateWindowSize ();
517 }
518
519 updateWindowProperty ();
520 sendResizeNotify ();
521 }
522 }
523
524 void
525 ResizeLogic::updateWindowSize ()
526 {
527 if (w->syncWait ())
528 return;
529
530 if (w->serverGeometry ().width () != geometry.width ||
531 w->serverGeometry ().height () != geometry.height)
532 {
533 XWindowChanges xwc = XWINDOWCHANGES_INIT;
534
535 xwc.x = geometry.x;
536 xwc.y = geometry.y;
537 xwc.width = geometry.width;
538 xwc.height = geometry.height;
539
540 w->sendSyncRequest ();
541
542 w->configureXWindow (CWX | CWY | CWWidth | CWHeight, &xwc);
543 }
544 }
545
546 void
547 ResizeLogic::updateWindowProperty ()
548 {
549 CompOption::Vector data = resizeInformationAtom->getReadTemplate ();;
550 CompOption::Value v;
551
552 if (data.size () != 4)
553 return;
554
555 v = geometry.x;
556 data.at (0).set (v);
557
558 v = geometry.y;
559 data.at (1).set (v);
560
561 v = geometry.width;
562 data.at (2).set (v);
563
564 v = geometry.height;
565 data.at (3).set (v);
566
567 resizeInformationAtom->updateProperty (w->id (), data, XA_CARDINAL);
568 }
569
570 void
571 ResizeLogic::sendResizeNotify ()
572 {
573 XEvent xev;
574
575 xev.xclient.type = ClientMessage;
576 xev.xclient.display = mScreen->dpy ();
577 xev.xclient.format = 32;
578
579 xev.xclient.message_type = resizeNotifyAtom;
580 xev.xclient.window = w->id ();
581
582 xev.xclient.data.l[0] = geometry.x;
583 xev.xclient.data.l[1] = geometry.y;
584 xev.xclient.data.l[2] = geometry.width;
585 xev.xclient.data.l[3] = geometry.height;
586 xev.xclient.data.l[4] = 0;
587
588 XSendEvent (mScreen->dpy (), mScreen->root (), false,
589 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
590 }
591
592 void
593 ResizeLogic::finishResizing ()
594 {
595 w->ungrabNotify ();
596
597 resizeInformationAtom->deleteProperty (w->id ());
598
599 mScreen->freeWindowInterface (w);
600 w = NULL;
601 }
602
603 void
604 ResizeLogic::getPaintRectangle (BoxPtr pBox)
605 {
606 pBox->x1 = geometry.x - w->border ().left;
607 pBox->y1 = geometry.y - w->border ().top;
608 pBox->x2 = geometry.x + geometry.width +
609 w->serverGeometry ().border () * 2 + w->border ().right;
610
611 if (w->shaded ())
612 pBox->y2 = geometry.y + w->size ().height () + w->border ().bottom;
613 else
614 pBox->y2 = geometry.y + geometry.height +
615 w->serverGeometry ().border () * 2 + w->border ().bottom;
616 }
617
618 void
619 ResizeLogic::getStretchRectangle (BoxPtr pBox)
620 {
621 BoxRec box;
622 float xScale, yScale;
623
624 getPaintRectangle (&box);
625 w->getResizeInterface ()->getStretchScale (&box, &xScale, &yScale);
626
627 pBox->x1 = (int) (box.x1 - (w->output ().left - w->border ().left) * xScale);
628 pBox->y1 = (int) (box.y1 - (w->output ().top - w->border ().top) * yScale);
629 pBox->x2 = (int) (box.x2 + w->output ().right * xScale);
630 pBox->y2 = (int) (box.y2 + w->output ().bottom * yScale);
631 }
632
633 Cursor
634 ResizeLogic::cursorFromResizeMask (unsigned int mask)
635 {
636 Cursor cursor;
637
638 if (mask & ResizeLeftMask)
639 {
640 if (mask & ResizeDownMask)
641 cursor = downLeftCursor;
642 else if (mask & ResizeUpMask)
643 cursor = upLeftCursor;
644 else
645 cursor = leftCursor;
646 }
647 else if (mask & ResizeRightMask)
648 {
649 if (mask & ResizeDownMask)
650 cursor = downRightCursor;
651 else if (mask & ResizeUpMask)
652 cursor = upRightCursor;
653 else
654 cursor = rightCursor;
655 }
656 else if (mask & ResizeUpMask)
657 {
658 cursor = upCursor;
659 }
660 else
661 {
662 cursor = downCursor;
663 }
664
665 return cursor;
666 }
667
668 void
669 ResizeLogic::snapWindowToWorkAreaBoundaries (int &wi, int &he,
670 int &wX, int &wY,
671 int &wWidth, int &wHeight)
672 {
673 int workAreaSnapDistance = 15;
674
675 /* Check if resized edge(s) are near output work-area boundaries */
676 foreach (CompOutput &output, mScreen->outputDevs ())
677 {
678 const CompRect &workArea = output.workArea ();
679
680 /* if window and work-area intersect in x axis */
681 if (wX + wWidth > workArea.x () &&
682 wX < workArea.x2 ())
683 {
684 if (mask & ResizeLeftMask)
685 {
686 int dw = workArea.x () - wX;
687
688 if (0 < dw && dw < workAreaSnapDistance)
689 {
690 wi -= dw;
691 wWidth -= dw;
692 wX += dw;
693 }
694 }
695 else if (mask & ResizeRightMask)
696 {
697 int dw = wX + wWidth - workArea.x2 ();
698
699 if (0 < dw && dw < workAreaSnapDistance)
700 {
701 wi -= dw;
702 wWidth -= dw;
703 }
704 }
705 }
706
707 /* if window and work-area intersect in y axis */
708 if (wY + wHeight > workArea.y () &&
709 wY < workArea.y2 ())
710 {
711 if (mask & ResizeUpMask)
712 {
713 int dh = workArea.y () - wY;
714
715 if (0 < dh && dh < workAreaSnapDistance)
716 {
717 he -= dh;
718 wHeight -= dh;
719 wY += dh;
720 }
721 }
722 else if (mask & ResizeDownMask)
723 {
724 int dh = wY + wHeight - workArea.y2 ();
725
726 if (0 < dh && dh < workAreaSnapDistance)
727 {
728 he -= dh;
729 wHeight -= dh;
730 }
731 }
732 }
733 }
734 }
735
736 void
737 ResizeLogic::setUpMask (int xRoot, int yRoot)
738 {
739 int xDist, yDist;
740 int minPointerOffsetX, minPointerOffsetY;
741
742 CompWindow::Geometry server = w->serverGeometry ();
743
744 xDist = xRoot - (server.x () + (server.width () / 2));
745 yDist = yRoot - (server.y () + (server.height () / 2));
746
747 /* decision threshold is 10% of window size */
748 minPointerOffsetX = MIN (20, server.width () / 10);
749 minPointerOffsetY = MIN (20, server.height () / 10);
750
751 /* if we reached the threshold in one direction,
752 make the threshold in the other direction smaller
753 so there is a chance that this threshold also can
754 be reached (by diagonal movement) */
755 if (abs (xDist) > minPointerOffsetX)
756 minPointerOffsetY /= 2;
757 else if (abs (yDist) > minPointerOffsetY)
758 minPointerOffsetX /= 2;
759
760 if (abs (xDist) > minPointerOffsetX)
761 {
762 if (xDist > 0)
763 mask |= ResizeRightMask;
764 else
765 mask |= ResizeLeftMask;
766 }
767
768 if (abs (yDist) > minPointerOffsetY)
769 {
770 if (yDist > 0)
771 mask |= ResizeDownMask;
772 else
773 mask |= ResizeUpMask;
774 }
775
776 /* if the pointer movement was enough to determine a
777 direction, warp the pointer to the appropriate edge
778 and set the right cursor */
779 if (mask)
780 {
781 Cursor cursor;
782 CompAction *action;
783 int pointerAdjustX = 0;
784 int pointerAdjustY = 0;
785
786 action = &options->optionGetInitiateKey ();
787 action->setState (action->state () |
788 CompAction::StateTermButton);
789
790 if (mask & ResizeRightMask)
791 pointerAdjustX = server.x () + server.width () +
792 w->border ().right - xRoot;
793 else if (mask & ResizeLeftMask)
794 pointerAdjustX = server.x () - w->border ().left -
795 xRoot;
796
797 if (mask & ResizeDownMask)
798 pointerAdjustY = server.y () + server.height () +
799 w->border ().bottom - yRoot;
800 else if (mask & ResizeUpMask)
801 pointerAdjustY = server.y () - w->border ().top - yRoot;
802
803 mScreen->warpPointer (pointerAdjustX, pointerAdjustY);
804
805 cursor = cursorFromResizeMask (mask);
806 mScreen->updateGrab (grabIndex, cursor);
807 }
808 }
809
810 void
811 ResizeLogic::accumulatePointerMotion (int xRoot, int yRoot)
812 {
813 /* only accumulate pointer movement if a mask is
814 already set as we don't have a use for the
815 difference information otherwise */
816
817 if (centered || options->optionGetResizeFromCenter ())
818 {
819 pointerDx += (xRoot - lastPointerX) * 2;
820 pointerDy += (yRoot - lastPointerY) * 2;
821 }
822 else
823 {
824 pointerDx += xRoot - lastPointerX;
825 pointerDy += yRoot - lastPointerY;
826 }
827
828 /* If we hit the edge of the screen while resizing
829 * the window and the adjacent window edge has not hit
830 * the edge of the screen, then accumulate pointer motion
831 * in the opposite direction. (So the apparant x / y
832 * mixup here is intentional)
833 */
834
835 if (isConstrained)
836 {
837 if (mask == ResizeLeftMask)
838 {
839 if (xRoot == 0 &&
840 geometry.x - w->border ().left > grabWindowWorkArea->left ())
841 pointerDx += abs (yRoot - lastPointerY) * -1;
842 }
843 else if (mask == ResizeRightMask)
844 {
845 if (xRoot == mScreen->width () -1 &&
846 geometry.x + geometry.width + w->border ().right < grabWindowWorkArea->right ())
847 pointerDx += abs (yRoot - lastPointerY);
848 }
849 if (mask == ResizeUpMask)
850 {
851 if (yRoot == 0 &&
852 geometry.y - w->border ().top > grabWindowWorkArea->top ())
853 pointerDy += abs (xRoot - lastPointerX) * -1;
854 }
855 else if (mask == ResizeDownMask)
856 {
857 if (yRoot == mScreen->height () -1 &&
858 geometry.y + geometry.height + w->border ().bottom < grabWindowWorkArea->bottom ())
859 pointerDx += abs (yRoot - lastPointerY);
860 }
861 }
862 }
863
864 void
865 ResizeLogic::constrainToWorkArea (int &che, int &cwi)
866 {
867 if (mask & ResizeUpMask)
868 {
869 int decorTop = savedGeometry.y + savedGeometry.height -
870 (che + w->border ().top);
871
872 if (grabWindowWorkArea->y () > decorTop)
873 che -= grabWindowWorkArea->y () - decorTop;
874 }
875 if (mask & ResizeDownMask)
876 {
877 int decorBottom = savedGeometry.y + che + w->border ().bottom;
878
879 if (decorBottom >
880 grabWindowWorkArea->y () + grabWindowWorkArea->height ())
881 che -= decorBottom - (grabWindowWorkArea->y () +
882 grabWindowWorkArea->height ());
883 }
884 if (mask & ResizeLeftMask)
885 {
886 int decorLeft = savedGeometry.x + savedGeometry.width -
887 (cwi + w->border ().left);
888
889 if (grabWindowWorkArea->x () > decorLeft)
890 cwi -= grabWindowWorkArea->x () - decorLeft;
891 }
892 if (mask & ResizeRightMask)
893 {
894 int decorRight = savedGeometry.x + cwi + w->border ().right;
895
896 if (decorRight >
897 grabWindowWorkArea->x () + grabWindowWorkArea->width ())
898 cwi -= decorRight - (grabWindowWorkArea->x () +
899 grabWindowWorkArea->width ());
900 }
901 }
902
903 void
904 ResizeLogic::limitMovementToConstraintRegion (int &wi, int &he,
905 int xRoot, int yRoot,
906 int wX, int wY,
907 int wWidth, int wHeight)
908 {
909 int minHeight = 50;
910
911 /* rect. for a minimal height window + borders
912 (used for the constraining in X axis) */
913 int minimalInputHeight = minHeight +
914 w->border ().top + w->border ().bottom;
915
916 /* small hot-spot square (on window's corner or edge) that is to be
917 constrained to the combined output work-area region */
918 int x, y;
919 int width = w->border ().top; /* square size = title bar height */
920 int height = width;
921 bool status; /* whether or not hot-spot is in the region */
922
923 /* compute x & y for constrained hot-spot rect */
924 if (mask & ResizeLeftMask)
925 x = wX;
926 else if (mask & ResizeRightMask)
927 x = wX + wWidth - width;
928 else
929 x = MIN (MAX (xRoot, wX), wX + wWidth - width);
930
931 if (mask & ResizeUpMask)
932 y = wY;
933 else if (mask & ResizeDownMask)
934 y = wY + wHeight - height;
935 else
936 y = MIN (MAX (yRoot, wY), wY + wHeight - height);
937
938 status = constraintRegion.contains (x, y, width, height);
939
940 /* only constrain movement if previous position was valid */
941 if (inRegionStatus)
942 {
943 bool xStatus = false;
944 int yForXResize = y;
945 int nx = x;
946 int nw = wi;
947 int nh = he;
948 int minWidth = 50;
949
950 if (mask & (ResizeLeftMask | ResizeRightMask))
951 {
952 xStatus = status;
953
954 if (mask & ResizeUpMask)
955 yForXResize = wY + wHeight - minimalInputHeight;
956 else if (mask & ResizeDownMask)
957 yForXResize = wY + minimalInputHeight - height;
958 else
959 yForXResize = y;
960
961 if (!constraintRegion.contains (x, yForXResize,
962 width, height))
963 {
964 if (lastGoodHotSpotY >= 0)
965 yForXResize = lastGoodHotSpotY;
966 else
967 yForXResize = y;
968 }
969 }
970 if (mask & ResizeLeftMask)
971 {
972 while ((nw > minWidth) && !xStatus)
973 {
974 xStatus = constraintRegion.contains (nx, yForXResize,
975 width, height);
976 if (!xStatus)
977 {
978 nw--;
979 nx++;
980 }
981 }
982 if (nw > minWidth)
983 {
984 x = nx;
985 wi = nw;
986 }
987 }
988 else if (mask & ResizeRightMask)
989 {
990 while ((nw > minWidth) && !xStatus)
991 {
992 xStatus = constraintRegion.contains (nx, yForXResize,
993 width, height);
994 if (!xStatus)
995 {
996 nw--;
997 nx--;
998 }
999 }
1000 if (nw > minWidth)
1001 {
1002 x = nx;
1003 wi = nw;
1004 }
1005 }
1006
1007 if (mask & ResizeUpMask)
1008 {
1009 while ((nh > minHeight) && !status)
1010 {
1011 status = constraintRegion.contains (x, y,
1012 width, height);
1013 if (!status)
1014 {
1015 nh--;
1016 y++;
1017 }
1018 }
1019 if (nh > minHeight)
1020 he = nh;
1021 }
1022 else if (mask & ResizeDownMask)
1023 {
1024 while ((nh > minHeight) && !status)
1025 {
1026 status = constraintRegion.contains (x, y,
1027 width, height);
1028 if (!status)
1029 {
1030 nh--;
1031 y--;
1032 }
1033 }
1034 if (nh > minHeight)
1035 he = nh;
1036 }
1037
1038 if (((mask & (ResizeLeftMask | ResizeRightMask)) && xStatus) ||
1039 ((mask & (ResizeUpMask | ResizeDownMask)) && status))
1040 {
1041 /* hot-spot inside work-area region, store good values */
1042 lastGoodHotSpotY = y;
1043 lastGoodSize = CompSize (wi, he);
1044 }
1045 else
1046 {
1047 /* failed to find a good hot-spot position, restore size */
1048 wi = lastGoodSize.width ();
1049 he = lastGoodSize.height ();
1050 }
1051 }
1052 else
1053 {
1054 inRegionStatus = status;
1055 }
1056 }
1057
1058 void
1059 ResizeLogic::computeWindowPlusBordersRect (int &wX, int &wY,
1060 int &wWidth, int &wHeight,
1061 int wi, int he)
1062 {
1063 wWidth = wi + w->border ().left + w->border ().right;
1064 wHeight = he + w->border ().top + w->border ().bottom;
1065
1066 if (centered || options->optionGetResizeFromCenter ())
1067 {
1068 if (mask & ResizeLeftMask)
1069 wX = geometry.x + geometry.width -
1070 (wi + w->border ().left);
1071 else
1072 wX = geometry.x - w->border ().left;
1073
1074 if (mask & ResizeUpMask)
1075 wY = geometry.y + geometry.height -
1076 (he + w->border ().top);
1077 else
1078 wY = geometry.y - w->border ().top;
1079 }
1080 else
1081 {
1082 if (mask & ResizeLeftMask)
1083 wX = savedGeometry.x + savedGeometry.width -
1084 (wi + w->border ().left);
1085 else
1086 wX = savedGeometry.x - w->border ().left;
1087
1088 if (mask & ResizeUpMask)
1089 wY = savedGeometry.y + savedGeometry.height -
1090 (he + w->border ().top);
1091 else
1092 wY = savedGeometry.y - w->border ().top;
1093 }
1094 }
1095
1096 void
1097 ResizeLogic::enableOrDisableVerticalMaximization (int yRoot)
1098 {
1099 /* maximum distance between the pointer and a work area edge (top or bottom)
1100 for a vertical maximization */
1101 const int max_edge_distance = 5;
1102
1103 if (!options->optionGetMaximizeVertically())
1104 return;
1105
1106 if (!offWorkAreaConstrained)
1107 return;
1108
1109 if (centered || options->optionGetResizeFromCenter ())
1110 {
1111 if (maximized_vertically)
1112 {
1113 geometry = geometryWithoutVertMax;
1114 maximized_vertically = false;
1115 }
1116 }
1117 else if (mask & ResizeUpMask)
1118 {
1119 if (yRoot - grabWindowWorkArea->top() <= max_edge_distance
1120 && !maximized_vertically)
1121 {
1122 maximized_vertically = true;
1123 geometryWithoutVertMax = geometry;
1124 }
1125 else if (yRoot - grabWindowWorkArea->top() > max_edge_distance
1126 && maximized_vertically)
1127 {
1128 geometry = geometryWithoutVertMax;
1129 maximized_vertically = false;
1130 }
1131 }
1132 else if (mask & ResizeDownMask)
1133 {
1134 if (grabWindowWorkArea->bottom() - yRoot <= max_edge_distance
1135 && !maximized_vertically)
1136 {
1137 maximized_vertically = true;
1138 geometryWithoutVertMax = geometry;
1139 }
1140 else if (grabWindowWorkArea->bottom() - yRoot > max_edge_distance
1141 && maximized_vertically)
1142 {
1143 geometry = geometryWithoutVertMax;
1144 maximized_vertically = false;
1145 }
1146 }
1147 }
1148
1149 void
1150 ResizeLogic::computeGeometry(int wi, int he)
1151 {
1152 XRectangle *regular_geometry;
1153 if (maximized_vertically)
1154 regular_geometry = &geometryWithoutVertMax;
1155 else
1156 regular_geometry = &geometry;
1157
1158 if (centered || options->optionGetResizeFromCenter ())
1159 {
1160 if ((mask & ResizeLeftMask) || (mask & ResizeRightMask))
1161 regular_geometry->x -= ((wi - regular_geometry->width) / 2);
1162 if ((mask & ResizeUpMask) || (mask & ResizeDownMask))
1163 regular_geometry->y -= ((he - regular_geometry->height) / 2);
1164 }
1165 else
1166 {
1167 if (mask & ResizeLeftMask)
1168 regular_geometry->x -= wi - regular_geometry->width;
1169 if (mask & ResizeUpMask)
1170 regular_geometry->y -= he - regular_geometry->height;
1171 }
1172
1173 regular_geometry->width = wi;
1174 regular_geometry->height = he;
1175
1176 if (maximized_vertically)
1177 {
1178 geometry.x = geometryWithoutVertMax.x;
1179 geometry.width = geometryWithoutVertMax.width;
1180 geometry.y = grabWindowWorkArea->y() + w->border().top;
1181 geometry.height = grabWindowWorkArea->height() - w->border().top
1182 - w->border().bottom;
1183 }
1184 }
1185
1186
1187 bool
1188 ResizeLogic::initiateResize (CompAction *action,
1189 CompAction::State state,
1190 CompOption::Vector &options,
1191 unsigned int initMode)
1192 {
1193 CompWindowInterface *w;
1194 Window xid;
1195
1196 xid = CompOption::getIntOptionNamed (options, "window");
1197
1198 w = mScreen->findWindow (xid);
1199 if (w && (w->actions () & CompWindowActionResizeMask))
1200 {
1201 int x, y;
1202 int button;
1203
1204 CompWindow::Geometry server = w->serverGeometry ();
1205
1206 x = CompOption::getIntOptionNamed (options, "x", pointerX);
1207 y = CompOption::getIntOptionNamed (options, "y", pointerY);
1208
1209 button = CompOption::getIntOptionNamed (options, "button", -1);
1210
1211 mask = CompOption::getIntOptionNamed (options, "direction");
1212
1213 /* Initiate the resize in the direction suggested by the
1214 * sector of the window the mouse is in, eg drag in top left
1215 * will resize up and to the left. Keyboard resize starts out
1216 * with the cursor in the middle of the window and then starts
1217 * resizing the edge corresponding to the next key press. */
1218 if (state & CompAction::StateInitKey)
1219 {
1220 mask = 0;
1221 }
1222 else if (!mask)
1223 {
1224 int sectorSizeX = server.width () / 3;
1225 int sectorSizeY = server.height () / 3;
1226 int posX = x - server.x ();
1227 int posY = y - server.y ();
1228
1229 if (posX < sectorSizeX)
1230 mask |= ResizeLeftMask;
1231 else if (posX > (2 * sectorSizeX))
1232 mask |= ResizeRightMask;
1233
1234 if (posY < sectorSizeY)
1235 mask |= ResizeUpMask;
1236 else if (posY > (2 * sectorSizeY))
1237 mask |= ResizeDownMask;
1238
1239 /* if the pointer was in the middle of the window,
1240 just prevent input to the window */
1241
1242 if (!mask)
1243 {
1244 mScreen->freeWindowInterface (w);
1245 return true;
1246 }
1247 }
1248
1249 if (mScreen->otherGrabExist ("resize", NULL) ||
1250 this->w ||
1251 (w->type () & (CompWindowTypeDesktopMask |
1252 CompWindowTypeDockMask |
1253 CompWindowTypeFullscreenMask)) ||
1254 w->overrideRedirect ())
1255 {
1256 mScreen->freeWindowInterface (w);
1257 return false;
1258 }
1259
1260 if (state & CompAction::StateInitButton)
1261 action->setState (action->state () | CompAction::StateTermButton);
1262
1263 if (w->shaded ())
1264 mask &= ~(ResizeUpMask | ResizeDownMask);
1265
1266 this->w = w;
1267
1268 savedGeometry.x = server.x ();
1269 savedGeometry.y = server.y ();
1270 savedGeometry.width = server.width ();
1271 savedGeometry.height = server.height ();
1272
1273 geometry = savedGeometry;
1274
1275 pointerDx = x - pointerX;
1276 pointerDy = y - pointerY;
1277
1278 centered |= w->evaluate (this->options->optionGetResizeFromCenterMatch ());
1279
1280 if ((w->state () & MAXIMIZE_STATE) == MAXIMIZE_STATE)
1281 {
1282 /* if the window is fully maximized, showing the outline or
1283 rectangle would be visually distracting as the window can't
1284 be resized anyway; so we better don't use them in this case */
1285 mode = ResizeOptions::ModeNormal;
1286 }
1287 else if (!gScreen || !cScreen ||
1288 !cScreen->compositingActive ())
1289 {
1290 mode = ResizeOptions::ModeNormal;
1291 }
1292 else
1293 {
1294 mode = initMode;
1295 }
1296
1297 if (mode != ResizeOptions::ModeNormal)
1298 {
1299 if (w->getGLInterface () && mode == ResizeOptions::ModeStretch)
1300 w->getGLInterface ()->glPaintSetEnabled (true);
1301 if (w->getCompositeInterface () && mode == ResizeOptions::ModeStretch)
1302 w->getCompositeInterface ()->damageRectSetEnabled (true);
1303 gScreen->glPaintOutputSetEnabled (true);
1304 }
1305
1306 if (!grabIndex)
1307 {
1308 Cursor cursor;
1309
1310 if (state & CompAction::StateInitKey)
1311 cursor = middleCursor;
1312 else
1313 cursor = cursorFromResizeMask (mask);
1314
1315 grabIndex = mScreen->pushGrab (cursor, "resize");
1316 }
1317
1318 if (grabIndex)
1319 {
1320 BoxRec box;
1321 unsigned int grabMask = CompWindowGrabResizeMask |
1322 CompWindowGrabButtonMask;
1323 bool sourceExternalApp =
1324 CompOption::getBoolOptionNamed (options, "external", false);
1325
1326 if (sourceExternalApp)
1327 grabMask |= CompWindowGrabExternalAppMask;
1328
1329 releaseButton = button;
1330
1331 w->grabNotify (x, y, state, grabMask);
1332
1333 /* Click raise happens implicitly on buttons 1, 2 and 3 so don't
1334 * restack this window again if the action buttonbinding was from
1335 * one of those buttons */
1336 if (mScreen->getOption ("raise_on_click")->value ().b () &&
1337 button != Button1 && button != Button2 && button != Button3)
1338 w->updateAttributes (CompStackingUpdateModeAboveFullscreen);
1339
1340 /* using the paint rectangle is enough here
1341 as we don't have any stretch yet */
1342 getPaintRectangle (&box);
1343 damageRectangle (&box);
1344
1345 if (state & CompAction::StateInitKey)
1346 {
1347 int xRoot, yRoot;
1348
1349 xRoot = server.x () + (server.width () / 2);
1350 yRoot = server.y () + (server.height () / 2);
1351
1352 mScreen->warpPointer (xRoot - pointerX, yRoot - pointerY);
1353 }
1354
1355 isConstrained = sourceExternalApp;
1356
1357 /* Update offWorkAreaConstrained and workArea at grab time */
1358 offWorkAreaConstrained = false;
1359 if (sourceExternalApp)
1360 {
1361 int output = w->outputDevice ();
1362 int lco, tco, bco, rco;
1363 bool sl = mScreen->outputDevs ().at (output).workArea ().left () >
1364 w->serverGeometry ().left ();
1365 bool sr = mScreen->outputDevs ().at (output).workArea ().right () <
1366 w->serverGeometry ().right ();
1367 bool st = mScreen->outputDevs ().at (output).workArea ().top () >
1368 w->serverGeometry ().top ();
1369 bool sb = mScreen->outputDevs ().at (output).workArea ().bottom () <
1370 w->serverGeometry ().bottom ();
1371
1372 lco = tco = bco = rco = output;
1373
1374 /* Prevent resizing beyond work area edges when resize is
1375 initiated externally (e.g. with window frame or menu)
1376 and not with a key (e.g. alt+button) */
1377 offWorkAreaConstrained = true;
1378
1379 lco = getOutputForEdge (output, TOUCH_RIGHT, sl);
1380 rco = getOutputForEdge (output, TOUCH_LEFT, sr);
1381 tco = getOutputForEdge (output, TOUCH_BOTTOM, st);
1382 bco = getOutputForEdge (output, TOUCH_TOP, sb);
1383
1384 /* Now we need to form one big rect which describes
1385 * the available workarea */
1386
1387 int left = mScreen->outputDevs ().at (lco).workArea ().left ();
1388 int right = mScreen->outputDevs ().at (rco).workArea ().right ();
1389 int top = mScreen->outputDevs ().at (tco).workArea ().top ();
1390 int bottom = mScreen->outputDevs ().at (bco).workArea ().bottom ();
1391
1392 grabWindowWorkArea.reset (new CompRect (0, 0, 0, 0));
1393 {
1394 grabWindowWorkArea->setLeft (left);
1395 grabWindowWorkArea->setRight (right);
1396 grabWindowWorkArea->setTop (top);
1397 grabWindowWorkArea->setBottom (bottom);
1398 }
1399
1400
1401 inRegionStatus = false;
1402 lastGoodHotSpotY = -1;
1403 lastGoodSize = w->serverSize ();
1404
1405 /* Combine the work areas of all outputs */
1406 constraintRegion = emptyRegion;
1407 foreach (CompOutput &output, mScreen->outputDevs ())
1408 constraintRegion += output.workArea ();
1409 }
1410 }
1411
1412 maximized_vertically = false;
1413 }
1414 else if (w)
1415 mScreen->freeWindowInterface (w);
1416
1417 return false;
1418 }
1419
1420 bool
1421 ResizeLogic::terminateResize (CompAction *action,
1422 CompAction::State state,
1423 CompOption::Vector &options)
1424 {
1425 if (w)
1426 {
1427 XWindowChanges xwc = XWINDOWCHANGES_INIT;
1428 unsigned int mask = 0;
1429
1430 if (mode == ResizeOptions::ModeNormal)
1431 {
1432 if (state & CompAction::StateCancel)
1433 {
1434 xwc.x = savedGeometry.x;
1435 xwc.y = savedGeometry.y;
1436 xwc.width = savedGeometry.width;
1437 xwc.height = savedGeometry.height;
1438
1439 mask = CWX | CWY | CWWidth | CWHeight;
1440 }
1441 else if (maximized_vertically)
1442 {
1443 w->maximize (CompWindowStateMaximizedVertMask);
1444
1445 xwc.x = geometryWithoutVertMax.x;
1446 xwc.y = geometryWithoutVertMax.y;
1447 xwc.width = geometryWithoutVertMax.width;
1448 xwc.height = geometryWithoutVertMax.height;
1449
1450 mask = CWX | CWY | CWWidth | CWHeight;
1451 }
1452 }
1453 else
1454 {
1455 XRectangle finalGeometry;
1456
1457 if (state & CompAction::StateCancel)
1458 finalGeometry = savedGeometry;
1459 else
1460 finalGeometry = geometry;
1461
1462 if (memcmp (&finalGeometry, &savedGeometry, sizeof (finalGeometry)) == 0)
1463 {
1464 BoxRec box;
1465
1466 if (mode == ResizeOptions::ModeStretch)
1467 getStretchRectangle (&box);
1468 else
1469 getPaintRectangle (&box);
1470
1471 damageRectangle (&box);
1472 }
1473 else
1474 {
1475 if (maximized_vertically)
1476 {
1477 w->maximize(CompWindowStateMaximizedVertMask);
1478 xwc.x = finalGeometry.x;
1479 xwc.width = finalGeometry.width;
1480 mask = CWX | CWWidth ;
1481 }
1482 else
1483 {
1484 xwc.x = finalGeometry.x;
1485 xwc.y = finalGeometry.y;
1486 xwc.width = finalGeometry.width;
1487 xwc.height = finalGeometry.height;
1488 mask = CWX | CWY | CWWidth | CWHeight;
1489 }
1490
1491 }
1492
1493 if (mode != ResizeOptions::ModeNormal)
1494 {
1495 if (w->getGLInterface () && mode == ResizeOptions::ModeStretch)
1496 w->getGLInterface ()->glPaintSetEnabled (false);
1497 if (w->getCompositeInterface () && mode == ResizeOptions::ModeStretch)
1498 w->getCompositeInterface ()->damageRectSetEnabled (false);
1499 gScreen->glPaintOutputSetEnabled (false);
1500 }
1501 }
1502
1503 if ((mask & CWWidth) &&
1504 xwc.width == (int) w->serverGeometry ().width ())
1505 mask &= ~CWWidth;
1506
1507 if ((mask & CWHeight) &&
1508 xwc.height == (int) w->serverGeometry ().height ())
1509 mask &= ~CWHeight;
1510
1511 if (mask)
1512 {
1513 if (mask & (CWWidth | CWHeight))
1514 w->sendSyncRequest ();
1515
1516 w->configureXWindow (mask, &xwc);
1517 }
1518
1519 finishResizing ();
1520
1521 if (grabIndex)
1522 {
1523 mScreen->removeGrab (grabIndex, NULL);
1524 grabIndex = 0;
1525 }
1526
1527 releaseButton = 0;
1528 }
1529
1530 action->setState (action->state () & ~(CompAction::StateTermKey |
1531 CompAction::StateTermButton));
1532
1533 return false;
1534 }
1535
1536 bool
1537 ResizeLogic::initiateResizeDefaultMode (CompAction *action,
1538 CompAction::State state,
1539 CompOption::Vector &options)
1540 {
1541 CompWindowInterface *w;
1542 unsigned int mode;
1543
1544 w = mScreen->findWindow (CompOption::getIntOptionNamed (options, "window"));
1545 if (!w)
1546 return false;
1547
1548 mode = this->options->optionGetMode ();
1549
1550 if (w->evaluate (this->options->optionGetNormalMatch ()))
1551 mode = ResizeOptions::ModeNormal;
1552 if (w->evaluate (this->options->optionGetOutlineMatch ()))
1553 mode = ResizeOptions::ModeOutline;
1554 if (w->evaluate (this->options->optionGetRectangleMatch ()))
1555 mode = ResizeOptions::ModeRectangle;
1556 if (w->evaluate (this->options->optionGetStretchMatch ()))
1557 mode = ResizeOptions::ModeStretch;
1558
1559 mScreen->freeWindowInterface (w);
1560
1561 return initiateResize (action, state, options, mode);
1562 }
1563
1564 void
1565 ResizeLogic::damageRectangle (BoxPtr pBox)
1566 {
1567 int x1, x2, y1, y2;
1568
1569 x1 = pBox->x1 - 1;
1570 y1 = pBox->y1 - 1;
1571 x2 = pBox->x2 + 1;
1572 y2 = pBox->y2 + 1;
1573
1574 if (cScreen)
1575 cScreen->damageRegion (CompRect (x1, y1, x2 - x1, y2 - y1));
1576 }
1577
1578 /* Be a little bit intelligent about how we calculate
1579 * the workarea. Basically we want to be enclosed in
1580 * any area that is obstructed by panels, but not
1581 * where two outputs meet
1582 *
1583 * Also, it does not make sense to resize over
1584 * non-touching outputs, so detect that case too
1585 * */
1586
1587 int
1588 ResizeLogic::getOutputForEdge (int windowOutput, unsigned int touch, bool skipFirst)
1589 {
1590 int op, wap;
1591 int ret = windowOutput;
1592
1593 getPointForTp (touch, windowOutput, op, wap);
1594
1595 if ((op == wap) || skipFirst)
1596 {
1597 int co = windowOutput;
1598
1599 do
1600 {
1601 int oco = co;
1602
1603 co = findTouchingOutput (op, touch);
1604
1605 /* Could not find a leftmost output from here
1606 * so we must have hit the edge of the universe */
1607 if (co == -1)
1608 {
1609 ret = oco;
1610 co = -1;
1611 break;
1612 }
1613
1614 getPointForTp (touch, co, op, wap);
1615
1616 /* There is something in the way here.... */
1617 if (op != wap)
1618 {
1619 ret = co;
1620 co = -1;
1621 }
1622 }
1623 while (co != -1);
1624 }
1625
1626 return ret;
1627 }
1628
1629 unsigned int
1630 ResizeLogic::findTouchingOutput (int touchPoint, unsigned int side)
1631 {
1632 for (unsigned int i = 0; i < mScreen->outputDevs ().size (); i++)
1633 {
1634 CompOutput &o = mScreen->outputDevs ().at (i);
1635 if (side == TOUCH_LEFT)
1636 {
1637 if (o.left () == touchPoint)
1638 return i;
1639 }
1640 if (side == TOUCH_RIGHT)
1641 {
1642 if (o.right () == touchPoint)
1643 return i;
1644 }
1645 if (side == TOUCH_TOP)
1646 {
1647 if (o.top () == touchPoint)
1648 return i;
1649 }
1650 if (side == TOUCH_BOTTOM)
1651 {
1652 if (o.bottom () == touchPoint)
1653 return i;
1654 }
1655 }
1656
1657 return -1;
1658 }
1659
1660 void
1661 ResizeLogic::getPointForTp (unsigned int tp,
1662 unsigned int output,
1663 int &op,
1664 int &wap)
1665 {
1666 CompRect og = CompRect (mScreen->outputDevs ().at (output));
1667 CompRect wag = mScreen->outputDevs ().at (output).workArea ();
1668
1669 switch (tp)
1670 {
1671 case TOUCH_LEFT:
1672 op = og.right ();
1673 wap = wag.right ();
1674 break;
1675 case TOUCH_RIGHT:
1676 op = og.left ();
1677 wap = wag.left ();
1678 break;
1679 case TOUCH_TOP:
1680 op = og.bottom ();
1681 wap = wag.bottom ();
1682 break;
1683 case TOUCH_BOTTOM:
1684 op = og.top ();
1685 wap = wag.top ();
1686 break;
1687 default:
1688 return;
1689 }
1690 }