geometry.cpp 126 KB
Newer Older
1
/********************************************************************
2
3
4
5
6
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7
Copyright (C) 2009 Lucas Murray <lmurray@undefinedfire.com>
8

9
10
11
12
13
14
15
16
17
18
19
20
21
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
22
23
24
25
26
27
28
29
30

/*

 This file contains things relevant to geometry, i.e. workspace size,
 window positions and window sizes.

*/

#include "client.h"
31
#include "composite.h"
32
33
34
35
#include "workspace.h"

#include <kapplication.h>
#include <kglobal.h>
Luboš Luňák's avatar
Luboš Luňák committed
36
#include <kwindowsystem.h>
37
38
39
40
41
42

#include "placement.h"
#include "notifications.h"
#include "geometrytip.h"
#include "rules.h"
#include "effects.h"
43
#include <QtGui/QDesktopWidget>
44
45
#include <QPainter>
#include <QVarLengthArray>
46
#include <QX11Info>
47

48
#include <KDE/KGlobalSettings>
49
#include "outline.h"
50
51
52
53
54
55
56
57

namespace KWin
{

//********************************************
// Workspace
//********************************************

58
59
60
extern int screen_number;
extern bool is_multihead;

61
62
63
64
/*!
  Resizes the workspace after an XRANDR screen size change
 */
void Workspace::desktopResized()
65
{
66
67
    QRect geom;
    for (int i = 0; i < QApplication::desktop()->screenCount(); i++) {
68
69
70
        //do NOT use - QApplication::desktop()->screenGeometry(i) there could be a virtual geometry
        // see bug #302783
        geom |= QApplication::desktop()->screen(i)->geometry();
71
    }
72
73
74
    NETSize desktop_geometry;
    desktop_geometry.width = geom.width();
    desktop_geometry.height = geom.height();
75
    rootInfo->setDesktopGeometry(-1, desktop_geometry);
76
77

    updateClientArea();
78
    saveOldScreenSizes(); // after updateClientArea(), so that one still uses the previous one
79
#ifdef KWIN_BUILD_SCREENEDGES
Arthur Arlt's avatar
Arthur Arlt committed
80
    m_screenEdge.update(true);
81
#endif
82
83
84
    if (effects) {
        static_cast<EffectsHandlerImpl*>(effects)->desktopResized(geom.size());
    }
85
}
86

87
88
void Workspace::saveOldScreenSizes()
{
89
    olddisplaysize = QSize( displayWidth(), displayHeight());
90
91
92
93
94
95
96
    oldscreensizes.clear();
    for( int i = 0;
         i < numScreens();
         ++i )
        oldscreensizes.append( screenGeometry( i ));
}

97
98
99
100
101
102
103
104
105
106
107
108
/*!
  Updates the current client areas according to the current clients.

  If the area changes or force is true, the new areas are propagated to the world.

  The client area is the area that is available for clients (that
  which is not taken by windows like panels, the top-of-screen menu
  etc).

  \sa clientArea()
 */

109
110
void Workspace::updateClientArea(bool force)
{
111
    int nscreens = QApplication::desktop()->screenCount();
112
    kDebug(1212) << "screens: " << nscreens << "desktops: " << numberOfDesktops();
113
114
115
116
    QVector< QRect > new_wareas(numberOfDesktops() + 1);
    QVector< StrutRects > new_rmoveareas(numberOfDesktops() + 1);
    QVector< QVector< QRect > > new_sareas(numberOfDesktops() + 1);
    QVector< QRect > screens(nscreens);
117
118
119
120
    QRect desktopArea;
    for (int i = 0; i < QApplication::desktop()->screenCount(); i++) {
        desktopArea |= QApplication::desktop()->screenGeometry(i);
    }
121
    for (int iS = 0;
122
            iS < nscreens;
123
            iS ++) {
124
        screens [iS] = QApplication::desktop()->screenGeometry(iS);
125
126
    }
    for (int i = 1;
127
            i <= numberOfDesktops();
128
            ++i) {
Luboš Luňák's avatar
Luboš Luňák committed
129
        new_wareas[ i ] = desktopArea;
130
131
132
133
        new_sareas[ i ].resize(nscreens);
        for (int iS = 0;
                iS < nscreens;
                iS ++)
Luboš Luňák's avatar
Luboš Luňák committed
134
            new_sareas[ i ][ iS ] = screens[ iS ];
135
136
137
    }
    for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it) {
        if (!(*it)->hasStrut())
Luboš Luňák's avatar
Luboš Luňák committed
138
            continue;
139
        QRect r = (*it)->adjustedClientArea(desktopArea, desktopArea);
140
        StrutRects strutRegion = (*it)->strutRects();
141
142
143
144
145
146
147
148
149

        // Ignore offscreen xinerama struts. These interfere with the larger monitors on the setup
        // and should be ignored so that applications that use the work area to work out where
        // windows can go can use the entire visible area of the larger monitors.
        // This goes against the EWMH description of the work area but it is a toss up between
        // having unusable sections of the screen (Which can be quite large with newer monitors)
        // or having some content appear offscreen (Relatively rare compared to other).
        bool hasOffscreenXineramaStrut = (*it)->hasOffscreenXineramaStrut();

150
151
152
153
154
155
        if ((*it)->isOnAllDesktops()) {
            for (int i = 1;
                    i <= numberOfDesktops();
                    ++i) {
                if (!hasOffscreenXineramaStrut)
                    new_wareas[ i ] = new_wareas[ i ].intersected(r);
156
                new_rmoveareas[ i ] += strutRegion;
157
158
159
                for (int iS = 0;
                        iS < nscreens;
                        iS ++) {
Luboš Luňák's avatar
Luboš Luňák committed
160
                    new_sareas[ i ][ iS ] = new_sareas[ i ][ iS ].intersected(
161
                                                (*it)->adjustedClientArea(desktopArea, screens[ iS ]));
Luboš Luňák's avatar
Luboš Luňák committed
162
163
                }
            }
164
165
166
167
168
169
170
        } else {
            if (!hasOffscreenXineramaStrut)
                new_wareas[(*it)->desktop()] = new_wareas[(*it)->desktop()].intersected(r);
            new_rmoveareas[(*it)->desktop()] += strutRegion;
            for (int iS = 0;
                    iS < nscreens;
                    iS ++) {
171
//                            kDebug (1212) << "adjusting new_sarea: " << screens[ iS ];
172
173
174
                new_sareas[(*it)->desktop()][ iS ]
                = new_sareas[(*it)->desktop()][ iS ].intersected(
                      (*it)->adjustedClientArea(desktopArea, screens[ iS ]));
Luboš Luňák's avatar
Luboš Luňák committed
175
            }
176
        }
177
    }
178
#if 0
179
    for (int i = 1;
180
            i <= numberOfDesktops();
181
182
183
184
185
186
            ++i) {
        for (int iS = 0;
                iS < nscreens;
                iS ++)
            kDebug(1212) << "new_sarea: " << new_sareas[ i ][ iS ];
    }
187
188
189
190
#endif

    bool changed = force;

191
    if (screenarea.isEmpty())
192
193
        changed = true;

194
195
196
197
    for (int i = 1;
            !changed && i <= numberOfDesktops();
            ++i) {
        if (workarea[ i ] != new_wareas[ i ])
Luboš Luňák's avatar
Luboš Luňák committed
198
            changed = true;
199
        if (restrictedmovearea[ i ] != new_rmoveareas[ i ])
200
            changed = true;
201
        if (screenarea[ i ].size() != new_sareas[ i ].size())
Luboš Luňák's avatar
Luboš Luňák committed
202
            changed = true;
203
204
205
        for (int iS = 0;
                !changed && iS < nscreens;
                iS ++)
Luboš Luňák's avatar
Luboš Luňák committed
206
            if (new_sareas[ i ][ iS ] != screenarea [ i ][ iS ])
207
                changed = true;
208
    }
209

210
    if (changed) {
211
        workarea = new_wareas;
212
213
        oldrestrictedmovearea = restrictedmovearea;
        restrictedmovearea = new_rmoveareas;
214
215
        screenarea = new_sareas;
        NETRect r;
216
        for (int i = 1; i <= numberOfDesktops(); i++) {
217
218
219
220
            r.pos.x = workarea[ i ].x();
            r.pos.y = workarea[ i ].y();
            r.size.width = workarea[ i ].width();
            r.size.height = workarea[ i ].height();
221
222
            rootInfo->setWorkArea(i, r);
        }
223

224
225
226
        for (ClientList::ConstIterator it = clients.constBegin();
                it != clients.constEnd();
                ++it)
227
            (*it)->checkWorkspacePosition();
228
229
230
        for (ClientList::ConstIterator it = desktops.constBegin();
                it != desktops.constEnd();
                ++it)
231
            (*it)->checkWorkspacePosition();
232

233
        oldrestrictedmovearea.clear(); // reset, no longer valid or needed
234
235
    }

236
237
238
    kDebug(1212) << "Done.";
}

239
void Workspace::updateClientArea()
240
241
242
{
    updateClientArea(false);
}
243
244
245
246
247
248
249
250
251


/*!
  returns the area available for clients. This is the desktop
  geometry minus windows on the dock.  Placement algorithms should
  refer to this rather than geometry().

  \sa geometry()
 */
252

253
254
255
QRect Workspace::clientArea(clientAreaOption opt, int screen, int desktop) const
{
    if (desktop == NETWinInfo::OnAllDesktops || desktop == 0)
256
        desktop = currentDesktop();
257
    if (screen == -1)
258
        screen = activeScreen();
259

260
261
262
263
    QRect sarea, warea;

    if (is_multihead) {
        sarea = (!screenarea.isEmpty()
264
                   && screen < screenarea[ desktop ].size()) // screens may be missing during KWin initialization or screen config changes
265
                  ? screenarea[ desktop ][ screen_number ]
266
                  : QApplication::desktop()->screenGeometry(screen_number);
267
        warea = workarea[ desktop ].isNull()
268
                ? QApplication::desktop()->screenGeometry(screen_number)
269
270
271
272
273
                : workarea[ desktop ];
    } else {
        sarea = (!screenarea.isEmpty()
                && screen < screenarea[ desktop ].size()) // screens may be missing during KWin initialization or screen config changes
                ? screenarea[ desktop ][ screen ]
274
                : QApplication::desktop()->screenGeometry(screen);
275
        warea = workarea[ desktop ].isNull()
276
                ? QRect(0, 0, displayWidth(), displayHeight())
277
278
279
                : workarea[ desktop ];
    }

280
281
    switch(opt) {
    case MaximizeArea:
Martin Flöser's avatar
Martin Flöser committed
282
    case PlacementArea:
283
284
285
286
            return sarea;
    case MaximizeFullArea:
    case FullScreenArea:
    case MovementArea:
Martin Flöser's avatar
Martin Flöser committed
287
    case ScreenArea:
288
        if (is_multihead)
289
            return QApplication::desktop()->screenGeometry(screen_number);
290
        else
291
            return QApplication::desktop()->screenGeometry(screen);
292
    case WorkArea:
293
294
295
296
        if (is_multihead)
            return sarea;
        else
            return warea;
297
    case FullArea:
298
        return QRect(0, 0, displayWidth(), displayHeight());
299
    }
300
301
    abort();
}
302

303

304
305
QRect Workspace::clientArea(clientAreaOption opt, const QPoint& p, int desktop) const
{
306
    int screen = QApplication::desktop()->screenNumber(p);
307
308
    return clientArea(opt, screen, desktop);
}
309

310
311
312
313
QRect Workspace::clientArea(clientAreaOption opt, const Client* c) const
{
    return clientArea(opt, c->geometry().center(), c->desktop());
}
314

315
316
317
QRegion Workspace::restrictedMoveArea(int desktop, StrutAreas areas) const
{
    if (desktop == NETWinInfo::OnAllDesktops || desktop == 0)
318
319
        desktop = currentDesktop();
    QRegion region;
320
321
322
    foreach (const StrutRect & rect, restrictedmovearea[desktop])
    if (areas & rect.area())
        region += rect;
323
    return region;
324
}
325

326
327
328
329
330
bool Workspace::inUpdateClientArea() const
{
    return !oldrestrictedmovearea.isEmpty();
}

331
332
333
QRegion Workspace::previousRestrictedMoveArea(int desktop, StrutAreas areas) const
{
    if (desktop == NETWinInfo::OnAllDesktops || desktop == 0)
334
335
        desktop = currentDesktop();
    QRegion region;
336
337
338
    foreach (const StrutRect & rect, oldrestrictedmovearea.at(desktop))
    if (areas & rect.area())
        region += rect;
339
    return region;
340
}
341

342
QVector< QRect > Workspace::previousScreenSizes() const
343
{
344
    return oldscreensizes;
345
346
}

347
348
349
350
351
352
353
354
355
356
int Workspace::oldDisplayWidth() const
{
    return olddisplaysize.width();
}

int Workspace::oldDisplayHeight() const
{
    return olddisplaysize.height();
}

357
358
359
360
/*!
  Client \a c is moved around to position \a pos. This gives the
  workspace the opportunity to interveniate and to implement
  snap-to-windows functionality.
361
362
363
364

  The parameter \a snapAdjust is a multiplier used to calculate the
  effective snap zones. When 1.0, it means that the snap zones will be
  used without change.
365
 */
366
367
368
369
370
QPoint Workspace::adjustClientPosition(Client* c, QPoint pos, bool unrestricted, double snapAdjust)
{
    //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone
    //CT adapted for kwin on 25Nov1999
    //aleXXX 02Nov2000 added second snapping mode
371
372
    if (options->windowSnapZone() || options->borderSnapZone() || options->centerSnapZone()) {
        const bool sOWO = options->isSnapOnlyWhenOverlapping();
373
        const QRect maxRect = clientArea(MovementArea, pos + c->rect().center(), c->desktop());
374
        const int xmin = maxRect.left();
375
        const int xmax = maxRect.right() + 1;             //desk size
376
        const int ymin = maxRect.top();
377
        const int ymax = maxRect.bottom() + 1;
378
379
380
381
382

        const int cx(pos.x());
        const int cy(pos.y());
        const int cw(c->width());
        const int ch(c->height());
383
384
        const int rx(cx + cw);
        const int ry(cy + ch);               //these don't change
385
386
387
388
389
390
391

        int nx(cx), ny(cy);                         //buffers
        int deltaX(xmax);
        int deltaY(ymax);   //minimum distance to other clients

        int lx, ly, lrx, lry; //coords and size for the comparison client, l

392
        // border snap
393
        int snap = options->borderSnapZone() * snapAdjust; //snap trigger
394
395
396
        if (snap) {
            if ((sOWO ? (cx < xmin) : true) && (qAbs(xmin - cx) < snap)) {
                deltaX = xmin - cx;
397
                nx = xmin;
398
399
400
            }
            if ((sOWO ? (rx > xmax) : true) && (qAbs(rx - xmax) < snap) && (qAbs(xmax - rx) < deltaX)) {
                deltaX = rx - xmax;
401
                nx = xmax - cw;
402
            }
403

404
405
            if ((sOWO ? (cy < ymin) : true) && (qAbs(ymin - cy) < snap)) {
                deltaY = ymin - cy;
406
                ny = ymin;
407
408
409
            }
            if ((sOWO ? (ry > ymax) : true) && (qAbs(ry - ymax) < snap) && (qAbs(ymax - ry) < deltaY)) {
                deltaY = ry - ymax;
410
411
                ny = ymax - ch;
            }
412
        }
413

414
        // windows snap
415
        snap = options->windowSnapZone() * snapAdjust;
416
        if (snap) {
417
            QList<Client *>::ConstIterator l;
418
            for (l = clients.constBegin(); l != clients.constEnd(); ++l) {
419
                if ((((*l)->isOnDesktop(c->desktop()) && !(*l)->isMinimized())
420
421
                        || (c->isOnDesktop(NET::OnAllDesktops) && (*l)->isOnDesktop(Workspace::currentDesktop())
                            && !(*l)->isMinimized()))
Thomas Lübking's avatar
Thomas Lübking committed
422
                        && (!(*l)->tabGroup() || (*l) == (*l)->tabGroup()->current())
423
                        && (*l) != c) {
424
425
426
427
428
                    lx = (*l)->x();
                    ly = (*l)->y();
                    lrx = lx + (*l)->width();
                    lry = ly + (*l)->height();

429
430
431
432
433
                    if (((cy <= lry) && (cy  >= ly))  ||
                            ((ry >= ly) && (ry  <= lry))  ||
                            ((cy <= ly) && (ry >= lry))) {
                        if ((sOWO ? (cx < lrx) : true) && (qAbs(lrx - cx) < snap) && (qAbs(lrx - cx) < deltaX)) {
                            deltaX = qAbs(lrx - cx);
434
                            nx = lrx;
435
436
                        }
                        if ((sOWO ? (rx > lx) : true) && (qAbs(rx - lx) < snap) && (qAbs(rx - lx) < deltaX)) {
Pino Toscano's avatar
Pino Toscano committed
437
                            deltaX = qAbs(rx - lx);
438
439
                            nx = lx - cw;
                        }
440
                    }
441

442
443
444
445
446
                    if (((cx <= lrx) && (cx  >= lx))  ||
                            ((rx >= lx) && (rx  <= lrx))  ||
                            ((cx <= lx) && (rx >= lrx))) {
                        if ((sOWO ? (cy < lry) : true) && (qAbs(lry - cy) < snap) && (qAbs(lry - cy) < deltaY)) {
                            deltaY = qAbs(lry - cy);
447
                            ny = lry;
448
449
450
451
                        }
                        //if ( (qAbs( ry-ly ) < snap) && (qAbs( ry - ly ) < deltaY ))
                        if ((sOWO ? (ry > ly) : true) && (qAbs(ry - ly) < snap) && (qAbs(ry - ly) < deltaY)) {
                            deltaY = qAbs(ry - ly);
452
453
                            ny = ly - ch;
                        }
454
                    }
Lucas Murray's avatar
Lucas Murray committed
455
456

                    // Corner snapping
457
458
459
                    if (nx == lrx || nx + cw == lx) {
                        if ((sOWO ? (ry > lry) : true) && (qAbs(lry - ry) < snap) && (qAbs(lry - ry) < deltaY)) {
                            deltaY = qAbs(lry - ry);
Lucas Murray's avatar
Lucas Murray committed
460
                            ny = lry - ch;
461
462
463
                        }
                        if ((sOWO ? (cy < ly) : true) && (qAbs(cy - ly) < snap) && (qAbs(cy - ly) < deltaY)) {
                            deltaY = qAbs(cy - ly);
Lucas Murray's avatar
Lucas Murray committed
464
465
                            ny = ly;
                        }
466
467
468
469
                    }
                    if (ny == lry || ny + ch == ly) {
                        if ((sOWO ? (rx > lrx) : true) && (qAbs(lrx - rx) < snap) && (qAbs(lrx - rx) < deltaX)) {
                            deltaX = qAbs(lrx - rx);
Lucas Murray's avatar
Lucas Murray committed
470
                            nx = lrx - cw;
471
472
473
                        }
                        if ((sOWO ? (cx < lx) : true) && (qAbs(cx - lx) < snap) && (qAbs(cx - lx) < deltaX)) {
                            deltaX = qAbs(cx - lx);
Lucas Murray's avatar
Lucas Murray committed
474
475
                            nx = lx;
                        }
476
477
478
                    }
                }
            }
479
        }
480

481
        // center snap
482
        snap = options->centerSnapZone() * snapAdjust; //snap trigger
483
484
485
486
487
        if (snap) {
            int diffX = qAbs((xmin + xmax) / 2 - (cx + cw / 2));
            int diffY = qAbs((ymin + ymax) / 2 - (cy + ch / 2));
            if (diffX < snap && diffY < snap && diffX < deltaX && diffY < deltaY) {
                // Snap to center of screen
488
489
                deltaX = diffX;
                deltaY = diffY;
490
491
                nx = (xmin + xmax) / 2 - cw / 2;
                ny = (ymin + ymax) / 2 - ch / 2;
492
            } else if (options->borderSnapZone()) {
493
494
495
                // Enhance border snap
                if ((nx == xmin || nx == xmax - cw) && diffY < snap && diffY < deltaY) {
                    // Snap to vertical center on screen edge
496
                    deltaY = diffY;
497
498
499
500
                    ny = (ymin + ymax) / 2 - ch / 2;
                } else if (((unrestricted ? ny == ymin : ny <= ymin) || ny == ymax - ch) &&
                          diffX < snap && diffX < deltaX) {
                    // Snap to horizontal center on screen edge
501
                    deltaX = diffX;
502
                    nx = (xmin + xmax) / 2 - cw / 2;
503
504
                }
            }
505
        }
506

507
508
        pos = QPoint(nx, ny);
    }
509
510
    return pos;
}
511

512
513
514
515
516
QRect Workspace::adjustClientSize(Client* c, QRect moveResizeGeom, int mode)
{
    //adapted from adjustClientPosition on 29May2004
    //this function is called when resizing a window and will modify
    //the new dimensions to snap to other windows/borders if appropriate
517
518
    if (options->windowSnapZone() || options->borderSnapZone()) {  // || options->centerSnapZone )
        const bool sOWO = options->isSnapOnlyWhenOverlapping();
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537

        const QRect maxRect = clientArea(MovementArea, c->rect().center(), c->desktop());
        const int xmin = maxRect.left();
        const int xmax = maxRect.right();               //desk size
        const int ymin = maxRect.top();
        const int ymax = maxRect.bottom();

        const int cx(moveResizeGeom.left());
        const int cy(moveResizeGeom.top());
        const int rx(moveResizeGeom.right());
        const int ry(moveResizeGeom.bottom());

        int newcx(cx), newcy(cy);                         //buffers
        int newrx(rx), newry(ry);
        int deltaX(xmax);
        int deltaY(ymax);   //minimum distance to other clients

        int lx, ly, lrx, lry; //coords and size for the comparison client, l

538
        // border snap
539
        int snap = options->borderSnapZone(); //snap trigger
540
        if (snap) {
541
542
543
544
            deltaX = int(snap);
            deltaY = int(snap);

#define SNAP_BORDER_TOP \
545
546
547
548
549
    if ((sOWO?(newcy<ymin):true) && (qAbs(ymin-newcy)<deltaY)) \
    { \
        deltaY = qAbs(ymin-newcy); \
        newcy = ymin; \
    }
550
551

#define SNAP_BORDER_BOTTOM \
552
553
554
555
556
    if ((sOWO?(newry>ymax):true) && (qAbs(ymax-newry)<deltaY)) \
    { \
        deltaY = qAbs(ymax-newcy); \
        newry = ymax; \
    }
557
558

#define SNAP_BORDER_LEFT \
559
560
561
562
563
    if ((sOWO?(newcx<xmin):true) && (qAbs(xmin-newcx)<deltaX)) \
    { \
        deltaX = qAbs(xmin-newcx); \
        newcx = xmin; \
    }
564
565

#define SNAP_BORDER_RIGHT \
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
    if ((sOWO?(newrx>xmax):true) && (qAbs(xmax-newrx)<deltaX)) \
    { \
        deltaX = qAbs(xmax-newrx); \
        newrx = xmax; \
    }
            switch(mode) {
            case PositionBottomRight:
                SNAP_BORDER_BOTTOM
                SNAP_BORDER_RIGHT
                break;
            case PositionRight:
                SNAP_BORDER_RIGHT
                break;
            case PositionBottom:
                SNAP_BORDER_BOTTOM
                break;
            case PositionTopLeft:
                SNAP_BORDER_TOP
                SNAP_BORDER_LEFT
                break;
            case PositionLeft:
                SNAP_BORDER_LEFT
                break;
            case PositionTop:
                SNAP_BORDER_TOP
                break;
            case PositionTopRight:
                SNAP_BORDER_TOP
                SNAP_BORDER_RIGHT
                break;
            case PositionBottomLeft:
                SNAP_BORDER_BOTTOM
                SNAP_BORDER_LEFT
                break;
            default:
                abort();
                break;
            }
604
605


606
        }
607

608
        // windows snap
609
        snap = options->windowSnapZone();
610
        if (snap) {
611
612
613
            deltaX = int(snap);
            deltaY = int(snap);
            QList<Client *>::ConstIterator l;
614
            for (l = clients.constBegin(); l != clients.constEnd(); ++l) {
615
                if ((*l)->isOnDesktop(currentDesktop()) &&
616
617
618
619
620
621
                        !(*l)->isMinimized()
                        && (*l) != c) {
                    lx = (*l)->x() - 1;
                    ly = (*l)->y() - 1;
                    lrx = (*l)->x() + (*l)->width();
                    lry = (*l)->y() + (*l)->height();
622
623

#define WITHIN_HEIGHT ((( newcy <= lry ) && ( newcy  >= ly  ))  || \
624
625
                       (( newry >= ly  ) && ( newry  <= lry ))  || \
                       (( newcy <= ly  ) && ( newry >= lry  )) )
626
627

#define WITHIN_WIDTH  ( (( cx <= lrx ) && ( cx  >= lx  ))  || \
628
629
                        (( rx >= lx  ) && ( rx  <= lrx ))  || \
                        (( cx <= lx  ) && ( rx >= lrx  )) )
630
631

#define SNAP_WINDOW_TOP  if ( (sOWO?(newcy<lry):true) \
632
633
634
635
636
                              && WITHIN_WIDTH  \
                              && (qAbs( lry - newcy ) < deltaY) ) {  \
    deltaY = qAbs( lry - newcy ); \
    newcy=lry; \
}
637
638

#define SNAP_WINDOW_BOTTOM  if ( (sOWO?(newry>ly):true)  \
639
640
641
642
643
                                 && WITHIN_WIDTH  \
                                 && (qAbs( ly - newry ) < deltaY) ) {  \
    deltaY = qAbs( ly - newry );  \
    newry=ly;  \
}
644
645

#define SNAP_WINDOW_LEFT  if ( (sOWO?(newcx<lrx):true)  \
646
647
648
649
650
                               && WITHIN_HEIGHT  \
                               && (qAbs( lrx - newcx ) < deltaX)) {  \
    deltaX = qAbs( lrx - newcx );  \
    newcx=lrx;  \
}
651
652

#define SNAP_WINDOW_RIGHT  if ( (sOWO?(newrx>lx):true)  \
653
654
655
656
657
658
                                && WITHIN_HEIGHT  \
                                && (qAbs( lx - newrx ) < deltaX))  \
{  \
    deltaX = qAbs( lx - newrx );  \
    newrx=lx;  \
}
659

Lucas Murray's avatar
Lucas Murray committed
660
#define SNAP_WINDOW_C_TOP  if ( (sOWO?(newcy<ly):true)  \
661
662
663
664
665
                                && (newcx == lrx || newrx == lx)  \
                                && qAbs(ly-newcy) < deltaY ) {  \
    deltaY = qAbs( ly - newcy + 1 ); \
    newcy = ly + 1; \
}
Lucas Murray's avatar
Lucas Murray committed
666
667

#define SNAP_WINDOW_C_BOTTOM  if ( (sOWO?(newry>lry):true)  \
668
669
670
671
672
                                   && (newcx == lrx || newrx == lx)  \
                                   && qAbs(lry-newry) < deltaY ) {  \
    deltaY = qAbs( lry - newry - 1 ); \
    newry = lry - 1; \
}
Lucas Murray's avatar
Lucas Murray committed
673
674

#define SNAP_WINDOW_C_LEFT  if ( (sOWO?(newcx<lx):true)  \
675
676
677
678
679
                                 && (newcy == lry || newry == ly)  \
                                 && qAbs(lx-newcx) < deltaX ) {  \
    deltaX = qAbs( lx - newcx + 1 ); \
    newcx = lx + 1; \
}
Lucas Murray's avatar
Lucas Murray committed
680
681

#define SNAP_WINDOW_C_RIGHT  if ( (sOWO?(newrx>lrx):true)  \
682
683
684
685
686
687
688
689
                                  && (newcy == lry || newry == ly)  \
                                  && qAbs(lrx-newrx) < deltaX ) {  \
    deltaX = qAbs( lrx - newrx - 1 ); \
    newrx = lrx - 1; \
}

                    switch(mode) {
                    case PositionBottomRight:
690
691
                        SNAP_WINDOW_BOTTOM
                        SNAP_WINDOW_RIGHT
Lucas Murray's avatar
Lucas Murray committed
692
693
                        SNAP_WINDOW_C_BOTTOM
                        SNAP_WINDOW_C_RIGHT
694
                        break;
695
                    case PositionRight:
696
                        SNAP_WINDOW_RIGHT
Lucas Murray's avatar
Lucas Murray committed
697
                        SNAP_WINDOW_C_RIGHT
698
                        break;
699
                    case PositionBottom:
700
                        SNAP_WINDOW_BOTTOM
Lucas Murray's avatar
Lucas Murray committed
701
                        SNAP_WINDOW_C_BOTTOM
702
                        break;
703
                    case PositionTopLeft:
704
705
                        SNAP_WINDOW_TOP
                        SNAP_WINDOW_LEFT
Lucas Murray's avatar
Lucas Murray committed
706
707
                        SNAP_WINDOW_C_TOP
                        SNAP_WINDOW_C_LEFT
708
                        break;
709
                    case PositionLeft:
710
                        SNAP_WINDOW_LEFT
Lucas Murray's avatar
Lucas Murray committed
711
                        SNAP_WINDOW_C_LEFT
712
                        break;
713
                    case PositionTop:
714
                        SNAP_WINDOW_TOP
Lucas Murray's avatar
Lucas Murray committed
715
                        SNAP_WINDOW_C_TOP
716
                        break;
717
                    case PositionTopRight:
718
719
                        SNAP_WINDOW_TOP
                        SNAP_WINDOW_RIGHT
Lucas Murray's avatar
Lucas Murray committed
720
721
                        SNAP_WINDOW_C_TOP
                        SNAP_WINDOW_C_RIGHT
722
                        break;
723
                    case PositionBottomLeft:
724
725
                        SNAP_WINDOW_BOTTOM
                        SNAP_WINDOW_LEFT
Lucas Murray's avatar
Lucas Murray committed
726
727
                        SNAP_WINDOW_C_BOTTOM
                        SNAP_WINDOW_C_LEFT
728
                        break;
729
                    default:
730
                        abort();
731
732
733
734
                        break;
                    }
                }
            }
735
        }
736

737
        // center snap
738
739
740
741
742
743
744
745
746
        //snap = options->centerSnapZone;
        //if (snap)
        //    {
        //    // Don't resize snap to center as it interferes too much
        //    // There are two ways of implementing this if wanted:
        //    // 1) Snap only to the same points that the move snap does, and
        //    // 2) Snap to the horizontal and vertical center lines of the screen
        //    }

747
        moveResizeGeom = QRect(QPoint(newcx, newcy), QPoint(newrx, newry));
748
    }
749
750
    return moveResizeGeom;
}
751
752
753
754

/*!
  Marks the client as being moved around by the user.
 */
755
756
void Workspace::setClientIsMoving(Client *c)
{
757
758
759
760
761
762
763
    Q_ASSERT(!c || !movingClient); // Catch attempts to move a second
    // window while still moving the first one.
    movingClient = c;
    if (movingClient)
        ++block_focus;
    else
        --block_focus;
764
}
765
766
767
768
769

/*!
  Cascades all clients on the current desktop
 */
void Workspace::cascadeDesktop()
770
{
771
// TODO XINERAMA this probably is not right for xinerama
772
773
774
    Q_ASSERT(block_stacking_updates == 0);
    initPositioning->reinitCascading(currentDesktop());
    QRect area = clientArea(PlacementArea, QPoint(0, 0), currentDesktop());
775
776
777
778
    foreach (Toplevel *toplevel, stackingOrder()) {
        Client *client = qobject_cast<Client*>(toplevel);
        if (!client ||
                (!client->isOnDesktop(currentDesktop())) ||
779
780
781
                (client->isMinimized())                  ||
                (client->isOnAllDesktops())              ||
                (!client->isMovable()))
782
            continue;
783
        initPositioning->placeCascaded(client, area);
784
    }
785
}
786
787
788
789
790
791

/*!
  Unclutters the current desktop by smart-placing all clients
  again.
 */
void Workspace::unclutterDesktop()
792
793
794
795
796
797
{
    for (int i = clients.size() - 1; i >= 0; i--) {
        if ((!clients.at(i)->isOnDesktop(currentDesktop())) ||
                (clients.at(i)->isMinimized())                  ||
                (clients.at(i)->isOnAllDesktops())              ||
                (!clients.at(i)->isMovable()))
798
            continue;
799
        initPositioning->placeSmart(clients.at(i), QRect());
800
    }
801
}
802

803
804
805
// When kwin crashes, windows will not be gravitated back to their original position
// and will remain offset by the size of the decoration. So when restarting, fix this
// (the property with the size of the frame remains on the window after the crash).
806
void Workspace::fixPositionAfterCrash(xcb_window_t w, const xcb_get_geometry_reply_t *geometry)
807
808
{
    NETWinInfo i(display(), w, rootWindow(), NET::WMFrameExtents);
809
    NETStrut frame = i.frameExtents();
810
811
812
813
814

    if (frame.left != 0 || frame.top != 0) {
        const uint32_t values[] = { geometry->x - frame.left, geometry->y - frame.top };
        xcb_configure_window(connection(), w, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values);
    }
815
}
816

817
818
819
820
821
//********************************************
// Client
//********************************************


822
823
824
void Client::keepInArea(QRect area, bool partial)
{
    if (partial) {
825
        // increase the area so that can have only 100 pixels in the area
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
        area.setLeft(qMin(area.left() - width() + 100, area.left()));
        area.setTop(qMin(area.top() - height() + 100, area.top()));
        area.setRight(qMax(area.right() + width() - 100, area.right()));
        area.setBottom(qMax(area.bottom() + height() - 100, area.bottom()));
    }
    if (!partial) {
        // resize to fit into area
        if (area.width() < width() || area.height() < height())
            resizeWithChecks(qMin(area.width(), width()), qMin(area.height(), height()));
    }
    if (geometry().right() > area.right() && width() < area.width())
        move(area.right() - width() + 1, y());
    if (geometry().bottom() > area.bottom() && height() < area.height())
        move(x(), area.bottom() - height() + 1);
    if (!area.contains(geometry().topLeft())) {
841
842
        int tx = x();
        int ty = y();
843
        if (tx < area.x())
844
            tx = area.x();
845
        if (ty < area.y())
846
            ty = area.y();
847
        move(tx, ty);
848
    }
849
}
850
851
852
853
854
855
856
857

/*!
  Returns \a area with the client's strut taken into account.

  Used from Workspace in updateClientArea.
 */
// TODO move to Workspace?

858
859
QRect Client::adjustedClientArea(const QRect &desktopArea, const QRect& area) const
{
860
861
862
    QRect r = area;
    NETExtendedStrut str = strut();
    QRect stareaL = QRect(
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
                        0,
                        str . left_start,
                        str . left_width,
                        str . left_end - str . left_start + 1);
    QRect stareaR = QRect(
                        desktopArea . right() - str . right_width + 1,
                        str . right_start,
                        str . right_width,
                        str . right_end - str . right_start + 1);
    QRect stareaT = QRect(
                        str . top_start,
                        0,
                        str . top_end - str . top_start + 1,
                        str . top_width);
    QRect stareaB = QRect(
                        str . bottom_start,
                        desktopArea . bottom() - str . bottom_width + 1,
                        str . bottom_end - str . bottom_start + 1,
                        str . bottom_width);

    QRect screenarea = workspace()->clientArea(ScreenArea, this);
884
885
886
    // HACK: workarea handling is not xinerama aware, so if this strut
    // reserves place at a xinerama edge that's inside the virtual screen,
    // ignore the strut for workspace setting.
887
    if (area == QRect(0, 0, displayWidth(), displayHeight())) {
888
        if (stareaL.left() < screenarea.left())
889
            stareaL = QRect();
890
        if (stareaR.right() > screenarea.right())
891
            stareaR = QRect();
892
        if (stareaT.top() < screenarea.top())
893
            stareaT = QRect();
894
        if (stareaB.bottom() < screenarea.bottom())
895
            stareaB = QRect();
896
    }
897
898
899
    // Handle struts at xinerama edges that are inside the virtual screen.
    // They're given in virtual screen coordinates, make them affect only
    // their xinerama screen.
900
901
902
903
    stareaL.setLeft(qMax(stareaL.left(), screenarea.left()));
    stareaR.setRight(qMin(stareaR.right(), screenarea.right()));
    stareaT.setTop(qMax(stareaT.top(), screenarea.top()));
    stareaB.setBottom(qMin(stareaB.bottom(), screenarea.bottom()));
904

905
    if (stareaL . intersects(area)) {
906
//        kDebug (1212) << "Moving left of: " << r << " to " << stareaL.right() + 1;
907
        r . setLeft(stareaL . right() + 1);
908
    }
909
    if (stareaR . intersects(area)) {
910
//        kDebug (1212) << "Moving right of: " << r << " to " << stareaR.left() - 1;
911
        r . setRight(stareaR . left() - 1);
912
    }
913
    if (stareaT . intersects(area)) {
914
//        kDebug (1212) << "Moving top of: " << r << " to " << stareaT.bottom() + 1;
915
        r . setTop(stareaT . bottom() + 1);
916
    }
917
    if (stareaB . intersects(area)) {
918
//        kDebug (1212) << "Moving bottom of: " << r << " to " << stareaB.top() - 1;
919
        r . setBottom(stareaB . top() - 1);
920
921
    }
    return r;
922
}
923
924

NETExtendedStrut Client::strut() const
925
{
926
927
    NETExtendedStrut ext = info->extendedStrut();
    NETStrut str = info->strut();
928
929
    if (ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
            && (str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0)) {
930
        // build extended from simple
931
        if (str.left != 0) {
932
933
934
            ext.left_width = str.left;
            ext.left_start = 0;
            ext.left_end = displayHeight();
935
936
        }
        if (str.right != 0) {
937
938
939
            ext.right_width = str.right;
            ext.right_start = 0;
            ext.right_end = displayHeight();
940
941
        }
        if (str.top != 0) {
942
943
944
            ext.top_width = str.top;
            ext.top_start = 0;
            ext.top_end = displayWidth();
945
946
        }
        if (str.bottom != 0) {
947
948
949
950
951
            ext.bottom_width = str.bottom;
            ext.bottom_start = 0;
            ext.bottom_end = displayWidth();
        }
    }
952
953
    return ext;
}
954

955
956
957
StrutRect Client::strutRect(StrutArea area) const
{
    assert(area != StrutAreaAll);   // Not valid
958
    NETExtendedStrut strutArea = strut();
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
    switch(area) {
    case StrutAreaTop:
        if (strutArea.top_width != 0)
            return StrutRect(QRect(
                                 strutArea.top_start, 0,
                                 strutArea.top_end - strutArea.top_start, strutArea.top_width
                             ), StrutAreaTop);
        break;
    case StrutAreaRight:
        if (strutArea.right_width != 0)
            return StrutRect(QRect(
                                 displayWidth() - strutArea.right_width, strutArea.right_start,
                                 strutArea.right_width, strutArea.right_end - strutArea.right_start
                             ), StrutAreaRight);
        break;
    case StrutAreaBottom:
        if (strutArea.bottom_width != 0)
            return StrutRect(QRect(
                                 strutArea.bottom_start, displayHeight() - strutArea.bottom_width,
                                 strutArea.bottom_end - strutArea.bottom_start, strutArea.bottom_width
                             ), StrutAreaBottom);
        break;
    case StrutAreaLeft:
        if (strutArea.left_width != 0)
            return StrutRect(QRect(
                                 0, strutArea.left_start,
                                 strutArea.left_width, strutArea.left_end - strutArea.left_start
                             ), StrutAreaLeft);
        break;
    default:
        abort(); // Not valid
990
    }
991
992
    return StrutRect(); // Null rect
}
993
994

StrutRects Client::strutRects() const
995
{
996
    StrutRects region;
997
998
999
1000
    region += strutRect(StrutAreaTop);
    region += strutRect(StrutAreaRight);
    region += strutRect(StrutAreaBottom);
    region += strutRect(StrutAreaLeft);
1001
    return region;
1002
}
1003

1004
bool Client::hasStrut() const
1005
{
1006
    NETExtendedStrut ext = strut();
1007
    if (ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0)
1008
1009
        return false;
    return true;
1010
}
1011

1012
bool Client::hasOffscr