~ubuntu-branches/ubuntu/quantal/compiz/quantal

« back to all changes in this revision

Viewing changes to plugins/resize/src/logic/src/resize-logic.cpp

  • Committer: Sebastien Bacher
  • Date: 2012-08-23 15:45:49 UTC
  • mfrom: (3269.2.21 gsettings-transition)
  • Revision ID: seb128@ubuntu.com-20120823154549-llj2223vo54js0li
  - Restore the fix for avoiding re-entering the glib event loop and 
    crashing. (LP: #1036490)
  - Including rsvg-cairo.h is now deprecated. (LP: #1039482)
  - Add keybinding integration for gnome-control-center
  - Separate the configuration for the minimize and unminimize animations.
    (LP: #1036739)
  - Ignore mouse press and release if it does not happen inside of the expo
    screen. (LP: #1036542). 
  - Additional keys from metacity to convert to GSettings
  - Maximize vertically if pointer reaches top or bottom edges.
  - Explicitly depend on resize-options. (LP: #1039834)
  - Add some docstring warnings about using those classes directly.
  - Fix some memory management issues on the mock objects, namely:
    Make destructor functions virtual where appropriate, so that we can add 
    them to lists polymorphically. Support polymorphic ccs*Unref function.
    Also make some other necessary changes to libcompizconfig.
  - Detect if rsvg 2.36.2 is installed (LP: #1039843)
  - GSettings integration backend, created a testsuite for it. Refactors a 
    bunch of the backend code, adds appropriate testsuites and refactors 
    the integration code to make it more flexible. (LP: #1035261)
* Add support for disabling gconf schemas installation
  - However, not enabled because of gtk-window-decorator for now
* Add libcompizconfig_gsettings_backend.so
* Workaround to install xml files

Show diffs side-by-side

added added

removed removed

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