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 }