~lbrulet-8/compiz-plugins-main/fix-876591

« back to all changes in this revision

Viewing changes to .pc/fix-872161.patch/snap/src/snap.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2011-10-12 10:49:59 UTC
  • Revision ID: james.westby@ubuntu.com-20111012104959-sqqdj62grtdhz4ca
Tags: 1:0.9.6-0ubuntu4
* debian/patches/fix-872161.patch:
  - When grabbing a window to demaximize it after dragging it up,
    sometimes the position will not be where you expect it to be
    (LP: #872161)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Compiz snap plugin
 
3
 * Author : Guillaume "iXce" Seguin
 
4
 * Email  : ixce@beryl-project.org
 
5
 *
 
6
 * Ported to compiz by : Patrick "marex" Niklaus
 
7
 * Email               : marex@beryl-project.org
 
8
 *
 
9
 * Ported to C++ by : Travis Watkins
 
10
 * Email            : amaranth@ubuntu.com
 
11
 *
 
12
 * Copyright (C) 2009 Guillaume Seguin
 
13
 *
 
14
 * This program is free software; you can redistribute it and/or
 
15
 * modify it under the terms of the GNU General Public License
 
16
 * as published by the Free Software Foundation; either version 2
 
17
 * of the License, or (at your option) any later version.
 
18
 *
 
19
 * This program is distributed in the hope that it will be useful,
 
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
22
 * GNU General Public License for more details.
 
23
 *
 
24
 * You should have received a copy of the GNU General Public License
 
25
 * along with this program; if not, write to the Free Software
 
26
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
27
 */
 
28
 
 
29
/*
 
30
 * TODO
 
31
 *  - Apply Edge Resistance to resize
 
32
 */
 
33
 
 
34
#include <stdlib.h>
 
35
#include <string.h>
 
36
#include <math.h>
 
37
 
 
38
#include "snap.h"
 
39
 
 
40
 
 
41
COMPIZ_PLUGIN_20090315 (snap, SnapPluginVTable);
 
42
 
 
43
// helper functions
 
44
 
 
45
/*
 
46
 * Wrapper functions to avoid infinite notify loops
 
47
 */
 
48
void
 
49
SnapWindow::move (int dx, int dy)
 
50
{
 
51
    skipNotify = true;
 
52
    window->move (dx, dy, true);
 
53
    screen->warpPointer (dx, dy);
 
54
    skipNotify = false;
 
55
}
 
56
 
 
57
void
 
58
SnapWindow::resize (int dx, int dy, int dwidth, int dheight)
 
59
{
 
60
    CompWindow::Geometry geometry = window->geometry ();
 
61
    skipNotify = true;
 
62
    window->resize (geometry.x () + dx, geometry.y () + dy,
 
63
                    geometry.width () + dwidth, geometry.height () + dheight,
 
64
                    geometry.border ());
 
65
    skipNotify = false;
 
66
}
 
67
 
 
68
void
 
69
SnapWindow::addEdge (Window   id,
 
70
                     int       position,
 
71
                     int      start,
 
72
                     int      end,
 
73
                     EdgeType type,
 
74
                     bool     screenEdge)
 
75
{
 
76
    Edge edge;
 
77
 
 
78
    edge.position = position;
 
79
    edge.start = start;
 
80
    edge.end = end;
 
81
    edge.type = type;
 
82
    edge.screenEdge = screenEdge;
 
83
    edge.snapped = false;
 
84
    edge.passed = false;
 
85
    edge.id = id;
 
86
 
 
87
    edges.push_back (edge);
 
88
}
 
89
 
 
90
/*
 
91
 * Add an edge for each rectangle of the region
 
92
 */
 
93
void
 
94
SnapWindow::addRegionEdges (Edge *parent, CompRegion region)
 
95
{
 
96
    int position, start, end;
 
97
 
 
98
    foreach (const CompRect &r, region.rects ())
 
99
    {
 
100
        switch (parent->type)
 
101
        {
 
102
        case LeftEdge:
 
103
        case RightEdge:
 
104
            position = r.x1 ();
 
105
            start = r.y1 ();
 
106
            end = r.y2 ();
 
107
            break;
 
108
        case TopEdge:
 
109
        case BottomEdge:
 
110
        default:
 
111
            position = r.y1 ();
 
112
            start = r.x1 ();
 
113
            end = r.x2 ();
 
114
        }
 
115
        
 
116
        addEdge (parent->id, position, start, end,
 
117
                 parent->type, parent->screenEdge);
 
118
        edges.back ().passed = parent->passed;
 
119
    }
 
120
}
 
121
 
 
122
/* Checks if a window is considered a snap window. If it's
 
123
 * not visible, returns false. If it's a panel and we're
 
124
 * snapping to screen edges, it's considered a snap-window.
 
125
 */
 
126
 
 
127
#define UNLIKELY(x) __builtin_expect(!!(x),0)
 
128
 
 
129
static inline bool
 
130
isSnapWindow (CompWindow *w)
 
131
{
 
132
    SNAP_SCREEN (screen);
 
133
 
 
134
    if (UNLIKELY(!w))
 
135
        return false;
 
136
    if (!w->isViewable ())
 
137
        return false;
 
138
    if ((w->type () & SNAP_WINDOW_TYPE) && 
 
139
        (ss->optionGetEdgesCategoriesMask () & EdgesCategoriesWindowEdgesMask))
 
140
        return true;
 
141
    if (w->struts () && 
 
142
        (ss->optionGetEdgesCategoriesMask () & EdgesCategoriesScreenEdgesMask))
 
143
        return true;
 
144
    return false;
 
145
}
 
146
 
 
147
// Edges update functions ------------------------------------------------------
 
148
/*
 
149
 * Detect visible windows edges
 
150
 */
 
151
void
 
152
SnapWindow::updateWindowsEdges ()
 
153
{
 
154
    CompRegion edgeRegion, resultRegion;
 
155
    CompRect   input;
 
156
    bool       remove = false;
 
157
 
 
158
    // First add all the windows
 
159
    foreach (CompWindow *w, screen->windows ())
 
160
    {
 
161
 
 
162
        // Just check that we're not trying to snap to current window,
 
163
        // that the window is not invisible and of a valid type
 
164
        if (w == window || !isSnapWindow (w))
 
165
        {
 
166
            continue;
 
167
        }
 
168
 
 
169
        input = w->borderRect ();
 
170
        addEdge (w->id (), input.top (), input.left (),
 
171
                 input.right (), TopEdge, false);
 
172
        addEdge (w->id (), input.bottom (), input.left (),
 
173
                 input.right (), BottomEdge, false);
 
174
        addEdge (w->id (), input.left (), input.top (),
 
175
                 input.bottom (), LeftEdge, false);
 
176
        addEdge (w->id (), input.right (), input.top (),
 
177
                 input.bottom (), RightEdge, false);
 
178
    }
 
179
 
 
180
    // Now strip invisible edges
 
181
    // Loop through all the windows stack, and through all the edges
 
182
    // If an edge has been passed, check if it's in the region window,
 
183
    // if the edge is fully under the window, drop it, or if it's only
 
184
    // partly covered, cut it/split it in one/two smaller visible edges
 
185
    foreach (CompWindow *w, screen->windows ())
 
186
    {
 
187
        if (w == window || !isSnapWindow (w))
 
188
            continue;
 
189
 
 
190
        // can't use foreach here because we need the iterator for erase()
 
191
        for (std::list<Edge>::iterator it = edges.begin (); it != edges.end (); )
 
192
        {
 
193
            Edge     *e = &*it;
 
194
            CompRect rect;
 
195
 
 
196
            if (!e->passed)
 
197
            {
 
198
                if (e->id == w->id ())
 
199
                    e->passed = true;
 
200
                it++;
 
201
                continue;
 
202
            }
 
203
 
 
204
            switch (e->type)
 
205
            {
 
206
                case LeftEdge:
 
207
                case RightEdge:
 
208
                    rect.setGeometry (e->position,
 
209
                                      e->start,
 
210
                                      1,
 
211
                                      e->end - e->start);
 
212
                    break;
 
213
                case TopEdge:
 
214
                case BottomEdge:
 
215
                default:
 
216
                    rect.setGeometry (e->start,
 
217
                                      e->position,
 
218
                                      e->end - e->start,
 
219
                                      1);
 
220
            }
 
221
 
 
222
            // If the edge is in the window region, remove it,
 
223
            // if it's partly in the region, split it
 
224
            edgeRegion = CompRegion (rect);
 
225
            resultRegion = edgeRegion - w->region ();
 
226
            if (resultRegion.isEmpty ())
 
227
            {
 
228
                remove = true;
 
229
            }
 
230
            else if (edgeRegion != resultRegion)
 
231
            {
 
232
                addRegionEdges (e, resultRegion);
 
233
                remove = true;
 
234
            }
 
235
 
 
236
            if (remove)
 
237
            {
 
238
                it = edges.erase (it);
 
239
                remove = false;
 
240
            }
 
241
            else
 
242
            {
 
243
                it++;
 
244
            }
 
245
        }
 
246
    }
 
247
}
 
248
 
 
249
/*
 
250
 * Loop on outputDevs and add the extents as edges
 
251
 * Note that left side is a right edge, right side a left edge,
 
252
 * top side a bottom edge and bottom side a top edge,
 
253
 * since they will be snapped as the right/left/bottom/top edge of a window
 
254
 */
 
255
void
 
256
SnapWindow::updateScreenEdges ()
 
257
{
 
258
    CompRegion edgeRegion, resultRegion;
 
259
    bool remove = false;
 
260
 
 
261
    foreach (CompOutput output, screen->outputDevs ())
 
262
    {
 
263
        const CompRect& area = output.workArea ();
 
264
        addEdge (0, area.top (), area.left (), area.right () - 1,
 
265
                 BottomEdge, true);
 
266
        addEdge (0, area.bottom (), area.left (), area.right () - 1,
 
267
                 TopEdge, true);
 
268
        addEdge (0, area.left (), area.top (), area.bottom () - 1,
 
269
                 RightEdge, true);
 
270
        addEdge (0, area.right (), area.top (), area.bottom () - 1,
 
271
                 LeftEdge, true);
 
272
    }
 
273
 
 
274
    // Drop screen edges parts that are under struts, basically apply the
 
275
    // same strategy than for windows edges visibility
 
276
    foreach (CompWindow *w, screen->windows ())
 
277
    {
 
278
        if (w == window || !w->struts ())
 
279
            continue;
 
280
 
 
281
        for (std::list<Edge>::iterator it = edges.begin (); it != edges.end ();)
 
282
        {
 
283
            Edge     *e = &*it;
 
284
            CompRect rect;
 
285
 
 
286
            if (!e->screenEdge)
 
287
            {
 
288
                it++;
 
289
                continue;
 
290
            }
 
291
 
 
292
            switch (e->type)
 
293
            {
 
294
                case LeftEdge:
 
295
                case RightEdge:
 
296
                    rect.setGeometry (e->position,
 
297
                                      e->start,
 
298
                                      1,
 
299
                                      e->end - e->start);
 
300
                    break;
 
301
                case TopEdge:
 
302
                case BottomEdge:
 
303
                default:
 
304
                    rect.setGeometry (e->start,
 
305
                                      e->position,
 
306
                                      e->end - e->start,
 
307
                                      1);
 
308
            }
 
309
 
 
310
            edgeRegion = CompRegion (rect);
 
311
            resultRegion = edgeRegion - w->region ();
 
312
            if (resultRegion.isEmpty ())
 
313
            {
 
314
                remove = true;
 
315
            }
 
316
            else if (edgeRegion != resultRegion)
 
317
            {
 
318
                addRegionEdges (e, resultRegion);
 
319
                remove = true;
 
320
            }
 
321
 
 
322
            if (remove)
 
323
            {
 
324
                it = edges.erase (it);
 
325
                remove = false;
 
326
            }
 
327
            else
 
328
            {
 
329
                it++;
 
330
            }
 
331
        }
 
332
    }
 
333
}
 
334
 
 
335
/*
 
336
 * Clean edges and fill it again with appropriate edges
 
337
 */
 
338
void
 
339
SnapWindow::updateEdges ()
 
340
{
 
341
    SNAP_SCREEN (screen);
 
342
 
 
343
    edges.clear ();
 
344
    updateWindowsEdges ();
 
345
 
 
346
    if (ss->optionGetEdgesCategoriesMask () & EdgesCategoriesScreenEdgesMask)
 
347
        updateScreenEdges ();
 
348
}
 
349
 
 
350
// Edges checking functions (move) ---------------------------------------------
 
351
 
 
352
/*
 
353
 * Find nearest edge in the direction set by "type",
 
354
 * w is the grabbed window, position/start/end are the window edges coordinates
 
355
 * before : if true the window has to be before the edge (top/left being origin)
 
356
 * snapDirection : just an helper, related to type
 
357
 */
 
358
void
 
359
SnapWindow::moveCheckNearestEdge (int position,
 
360
                                  int start,
 
361
                                  int end,
 
362
                                  bool before,
 
363
                                  EdgeType type,
 
364
                                  int snapDirection)
 
365
{
 
366
    SNAP_SCREEN (screen);
 
367
 
 
368
    Edge *edge = &edges.front ();
 
369
    int dist, min = 65535;
 
370
 
 
371
    foreach (Edge &current, edges)
 
372
    {
 
373
        // Skip wrong type or outbound edges
 
374
        if (current.type != type || current.end < start || current.start > end)
 
375
            continue;
 
376
 
 
377
        // Compute distance
 
378
        dist = before ? position - current.position : current.position - position;
 
379
        // Update minimum distance if needed
 
380
        if (dist < min && dist >= 0)
 
381
        {
 
382
            min = dist;
 
383
            edge = &current;
 
384
        }
 
385
        // 0-dist edge, just break
 
386
        if (dist == 0)
 
387
            break;
 
388
        // Unsnap edges that aren't snapped anymore
 
389
        if (current.snapped && dist > ss->optionGetResistanceDistance ())
 
390
            current.snapped = false;
 
391
    }
 
392
    // We found a 0-dist edge, or we have a snapping candidate
 
393
    if (min == 0 || (min <= ss->optionGetAttractionDistance ()
 
394
        && ss->optionGetSnapTypeMask () & SnapTypeEdgeAttractionMask))
 
395
    {
 
396
        // Update snapping data
 
397
        if (ss->optionGetSnapTypeMask () & SnapTypeEdgeResistanceMask)
 
398
        {
 
399
            snapGeometry = window->geometry ();
 
400
            this->snapDirection |= snapDirection;
 
401
        }
 
402
        // Attract the window if needed, moving it of the correct dist
 
403
        if (min != 0 && !edge->snapped)
 
404
        {
 
405
            edge->snapped = true;
 
406
            switch (type)
 
407
            {
 
408
            case LeftEdge:
 
409
                move (min, 0);
 
410
                break;
 
411
            case RightEdge:
 
412
                move (-min, 0);
 
413
                break;
 
414
            case TopEdge:
 
415
                move (0, min);
 
416
                break;
 
417
            case BottomEdge:
 
418
                move (0, -min);
 
419
                break;
 
420
            default:
 
421
                break;
 
422
            }
 
423
        }
 
424
    }
 
425
}
 
426
 
 
427
/*
 
428
 * Call the previous function for each of the 4 sides of the window
 
429
 */
 
430
void
 
431
SnapWindow::moveCheckEdges ()
 
432
{
 
433
    CompRect input (window->borderRect ());
 
434
    moveCheckNearestEdge (input.left (), input.top (), input.bottom (),
 
435
                          true, RightEdge, HorizontalSnap);
 
436
    moveCheckNearestEdge (input.right (), input.top (), input.bottom (),
 
437
                          false, LeftEdge, HorizontalSnap);
 
438
    moveCheckNearestEdge (input.top (), input.left (), input.right (),
 
439
                          true, BottomEdge, VerticalSnap);
 
440
    moveCheckNearestEdge (input.bottom (), input.left (), input.right (),
 
441
                          false, TopEdge, VerticalSnap);
 
442
}
 
443
 
 
444
// Edges checking functions (resize) -------------------------------------------
 
445
 
 
446
/*
 
447
 * Similar function for Snap on Resize
 
448
 */
 
449
void
 
450
SnapWindow::resizeCheckNearestEdge (int position,
 
451
                                    int start,
 
452
                                    int end,
 
453
                                    bool before,
 
454
                                    EdgeType type,
 
455
                                    int snapDirection)
 
456
{
 
457
    SNAP_SCREEN (screen);
 
458
 
 
459
    Edge *edge = &edges.front ();
 
460
    int dist, min = 65535;
 
461
 
 
462
    foreach (Edge &current, edges)
 
463
    {
 
464
        // Skip wrong type or outbound edges
 
465
        if (current.type != type || current.end < start || current.start > end)
 
466
            continue;
 
467
 
 
468
        // Compute distance
 
469
        dist = before ? position - current.position : current.position - position;
 
470
        // Update minimum distance if needed
 
471
        if (dist < min && dist >= 0)
 
472
        {
 
473
            min = dist;
 
474
            edge = &current;
 
475
        }
 
476
        // 0-dist edge, just break
 
477
        if (dist == 0)
 
478
            break;
 
479
        // Unsnap edges that aren't snapped anymore
 
480
        if (current.snapped && dist > ss->optionGetResistanceDistance ())
 
481
            current.snapped = false;
 
482
    }
 
483
    // We found a 0-dist edge, or we have a snapping candidate
 
484
    if (min == 0 || (min <= ss->optionGetAttractionDistance ()
 
485
        && ss->optionGetSnapTypeMask () & SnapTypeEdgeAttractionMask))
 
486
    {
 
487
        // Update snapping data
 
488
        if (ss->optionGetSnapTypeMask () & SnapTypeEdgeResistanceMask)
 
489
        {
 
490
            snapGeometry = window->serverGeometry ();
 
491
            this->snapDirection |= snapDirection;
 
492
        }
 
493
        // FIXME : this needs resize-specific code.
 
494
        // Attract the window if needed, moving it of the correct dist
 
495
        if (min != 0 && !edge->snapped)
 
496
        {
 
497
            edge->snapped = true;
 
498
            switch (type)
 
499
            {
 
500
            case LeftEdge:
 
501
                resize (min, 0, 0, 0);
 
502
                break;
 
503
            case RightEdge:
 
504
                resize (-min, 0, 0, 0);
 
505
                break;
 
506
            case TopEdge:
 
507
                resize (0, min, 0, 0);
 
508
                break;
 
509
            case BottomEdge:
 
510
                resize (0, -min, 0, 0);
 
511
                break;
 
512
            default:
 
513
                break;
 
514
            }
 
515
        }
 
516
    }
 
517
}
 
518
 
 
519
/*
 
520
 * Call the previous function for each of the 4 sides of the window
 
521
 */
 
522
void
 
523
SnapWindow::resizeCheckEdges (int dx, int dy, int dwidth, int dheight)
 
524
{
 
525
    CompRect input (window->borderRect ());
 
526
 
 
527
    resizeCheckNearestEdge (input.left (), input.top (), input.bottom (),
 
528
                            true, RightEdge, HorizontalSnap);
 
529
    resizeCheckNearestEdge (input.right (), input.top (), input.bottom (),
 
530
                            false, LeftEdge, HorizontalSnap);
 
531
    resizeCheckNearestEdge (input.top (), input.left (), input.right (),
 
532
                            true, BottomEdge, VerticalSnap);
 
533
    resizeCheckNearestEdge (input.bottom (), input.left (), input.right (),
 
534
                            false, TopEdge, VerticalSnap);
 
535
}
 
536
 
 
537
// Check if avoidSnap is matched, and enable/disable snap consequently
 
538
void
 
539
SnapScreen::handleEvent (XEvent *event)
 
540
{
 
541
    if (event->type == screen->xkbEvent ())
 
542
    {
 
543
        XkbAnyEvent *xkbEvent = (XkbAnyEvent *) event;
 
544
 
 
545
        if (xkbEvent->xkb_type == XkbStateNotify)
 
546
        {
 
547
            XkbStateNotifyEvent *stateEvent = (XkbStateNotifyEvent *) event;
 
548
 
 
549
            unsigned int mods = 0xffffffff;
 
550
            if (avoidSnapMask)
 
551
                mods = avoidSnapMask;
 
552
 
 
553
            if ((stateEvent->mods & mods) == mods)
 
554
                snapping = false;
 
555
            else
 
556
                snapping = true;
 
557
        }
 
558
    }
 
559
 
 
560
    screen->handleEvent (event);
 
561
}
 
562
 
 
563
// Events notifications --------------------------------------------------------
 
564
 
 
565
void
 
566
SnapWindow::resizeNotify (int dx, int dy, int dwidth, int dheight)
 
567
{
 
568
    SNAP_SCREEN (screen);
 
569
 
 
570
    window->resizeNotify (dx, dy, dwidth, dheight);
 
571
 
 
572
    // avoid-infinite-notify-loop mode/not grabbed
 
573
    if (skipNotify || !(grabbed & ResizeGrab))
 
574
        return;
 
575
 
 
576
    // we have to avoid snapping but there's still some buffered moving
 
577
    if (!ss->snapping && (m_dx || m_dy || m_dwidth || m_dheight))
 
578
    {
 
579
        resize (m_dx, m_dy, m_dwidth, m_dheight);
 
580
        m_dx = m_dy = m_dwidth = m_dheight = 0;
 
581
        return;
 
582
    }
 
583
 
 
584
    // don't snap maximized windows
 
585
    if (window->state () & CompWindowStateMaximizedHorzMask)
 
586
        dx = 0;
 
587
 
 
588
    if (window->state () & CompWindowStateMaximizedVertMask)
 
589
        dy = 0;
 
590
 
 
591
    // avoiding snap, nothing buffered
 
592
    if (!ss->snapping)
 
593
        return;
 
594
 
 
595
    // apply edge resistance
 
596
    if (ss->optionGetSnapTypeMask () & SnapTypeEdgeResistanceMask)
 
597
    {
 
598
        // If there's horizontal snapping, add dx to current buffered
 
599
        // dx and resist (move by -dx) or release the window and move
 
600
        // by buffered dx - dx, same for dh
 
601
        if (!snapGeometry.isEmpty () && snapDirection & HorizontalSnap)
 
602
        {
 
603
            m_dx += dx;
 
604
            if (m_dx < ss->optionGetResistanceDistance ()
 
605
                && m_dx > -ss->optionGetResistanceDistance ())
 
606
            {
 
607
                resize (-dx, 0, 0, 0);
 
608
            }
 
609
            else
 
610
            {
 
611
                resize (m_dx - dx, 0, 0, 0);
 
612
                m_dx = 0;
 
613
                if (!m_dwidth)
 
614
                    snapDirection &= VerticalSnap;
 
615
            }
 
616
            m_dwidth += dwidth;
 
617
            if (m_dwidth < ss->optionGetResistanceDistance ()
 
618
                && m_dwidth > -ss->optionGetResistanceDistance ())
 
619
            {
 
620
                resize (0, 0, -dwidth, 0);
 
621
            }
 
622
            else
 
623
            {
 
624
                resize (0, 0, m_dwidth - dwidth, 0);
 
625
                m_dwidth = 0;
 
626
                if (!m_dx)
 
627
                    snapDirection &= VerticalSnap;
 
628
            }
 
629
        }
 
630
 
 
631
        // Same for vertical snapping and dy/dh
 
632
        if (snapGeometry.isEmpty () && snapDirection & VerticalSnap)
 
633
        {
 
634
            m_dy += dy;
 
635
            if (m_dy < ss->optionGetResistanceDistance ()
 
636
                && m_dy > -ss->optionGetResistanceDistance ())
 
637
            {
 
638
                resize (0, -dy, 0, 0);
 
639
            }
 
640
            else
 
641
            {
 
642
                resize (0, m_dy - dy, 0, 0);
 
643
                m_dy = 0;
 
644
                if (!m_dheight)
 
645
                    snapDirection &= HorizontalSnap;
 
646
            }
 
647
            m_dheight += dheight;
 
648
            if (m_dheight < ss->optionGetResistanceDistance ()
 
649
                && m_dheight > -ss->optionGetResistanceDistance ())
 
650
            {
 
651
                resize (0, 0, 0, -dheight);
 
652
            }
 
653
            else
 
654
            {
 
655
                resize (0, 0, 0, m_dheight - dheight);
 
656
                m_dheight = 0;
 
657
                if (!m_dy)
 
658
                    snapDirection &= HorizontalSnap;
 
659
            }
 
660
        }
 
661
        // If we are no longer snapping in any direction, reset snapped
 
662
        if (!snapGeometry.isEmpty () && !snapDirection)
 
663
            snapGeometry = CompWindow::Geometry ();
 
664
    }
 
665
 
 
666
    // If we don't already snap vertically and horizontally,
 
667
    // check edges status
 
668
    if (snapDirection != (VerticalSnap | HorizontalSnap))
 
669
        resizeCheckEdges (dx, dy, dwidth, dheight);
 
670
}
 
671
 
 
672
void
 
673
SnapWindow::moveNotify (int dx, int dy, bool immediate)
 
674
{
 
675
    SNAP_SCREEN (screen);
 
676
 
 
677
    window->moveNotify (dx, dy, immediate);
 
678
 
 
679
    // avoid-infinite-notify-loop mode/not grabbed
 
680
    if (skipNotify || !(grabbed & MoveGrab))
 
681
        return;
 
682
 
 
683
    // we have to avoid snapping but there's still some buffered moving
 
684
    if (!ss->snapping && (m_dx || m_dy))
 
685
    {
 
686
        move (m_dx, m_dy);
 
687
        m_dx = m_dy = 0;
 
688
        return;
 
689
    }
 
690
 
 
691
    // don't snap maximized windows
 
692
    if (window->state () & CompWindowStateMaximizedHorzMask)
 
693
        dx = 0;
 
694
 
 
695
    if (window->state () & CompWindowStateMaximizedVertMask)
 
696
        dy = 0;
 
697
 
 
698
    // avoiding snap, nothing buffered
 
699
    if (!ss->snapping)
 
700
        return;
 
701
 
 
702
    // apply edge resistance
 
703
    if (ss->optionGetSnapTypeMask () & SnapTypeEdgeResistanceMask)
 
704
    {
 
705
        // If there's horizontal snapping, add dx to current buffered
 
706
        // dx and resist (move by -dx) or release the window and move
 
707
        // by buffered dx - dx
 
708
        if (!snapGeometry.isEmpty () && snapDirection & HorizontalSnap)
 
709
        {
 
710
            m_dx += dx;
 
711
            if (m_dx < ss->optionGetResistanceDistance ()
 
712
                && m_dx > -ss->optionGetResistanceDistance ())
 
713
            {
 
714
                dx = snapGeometry.x () - window->geometry ().x ();
 
715
                move (dx, 0);
 
716
            }
 
717
            else
 
718
            {
 
719
                move (m_dx - dx, 0);
 
720
                m_dx = 0;
 
721
                snapDirection &= VerticalSnap;
 
722
            }
 
723
        }
 
724
        // Same for vertical snapping and dy
 
725
        if (!snapGeometry.isEmpty () && snapDirection & VerticalSnap)
 
726
        {
 
727
            m_dy += dy;
 
728
            if (m_dy < ss->optionGetResistanceDistance ()
 
729
                && m_dy > -ss->optionGetResistanceDistance ())
 
730
            {
 
731
                dy = snapGeometry.y () - window->geometry ().y ();
 
732
                move (0, dy);
 
733
            }
 
734
            else
 
735
            {
 
736
                move (0, m_dy - dy);
 
737
                m_dy = 0;
 
738
                snapDirection &= HorizontalSnap;
 
739
            }
 
740
        }
 
741
        // If we are no longer snapping in any direction, reset snapped
 
742
        if (!snapGeometry.isEmpty () && !snapDirection)
 
743
            snapGeometry = CompWindow::Geometry ();
 
744
    }
 
745
    // If we don't already snap vertically and horizontally,
 
746
    // check edges status
 
747
    if (snapDirection != (VerticalSnap | HorizontalSnap))
 
748
        moveCheckEdges ();
 
749
}
 
750
 
 
751
/*
 
752
 * Initiate snap, get edges
 
753
 */
 
754
void
 
755
SnapWindow::grabNotify (int x, int y, unsigned int state, unsigned int mask)
 
756
{
 
757
    grabbed = (mask & CompWindowGrabResizeMask) ? ResizeGrab : MoveGrab;
 
758
    updateEdges ();
 
759
 
 
760
    window->grabNotify (x, y, state, mask);
 
761
}
 
762
 
 
763
/*
 
764
 * Clean edges data, reset dx/dy to avoid buggy moves
 
765
 * when snap will be triggered again.
 
766
 */
 
767
void
 
768
SnapWindow::ungrabNotify ()
 
769
{
 
770
    edges.clear ();
 
771
 
 
772
    snapGeometry = CompWindow::Geometry ();
 
773
    snapDirection = 0;
 
774
    grabbed = 0;
 
775
    m_dx = m_dy = m_dwidth = m_dheight = 0;
 
776
 
 
777
    window->ungrabNotify ();
 
778
}
 
779
 
 
780
// Internal stuff --------------------------------------------------------------
 
781
 
 
782
void
 
783
SnapScreen::optionChanged (CompOption *opt, SnapOptions::Options num)
 
784
{
 
785
    switch (num)
 
786
    {
 
787
    case SnapOptions::AvoidSnap:
 
788
    {
 
789
        unsigned int mask = optionGetAvoidSnapMask ();
 
790
        avoidSnapMask = 0;
 
791
        if (mask & AvoidSnapShiftMask)
 
792
            avoidSnapMask |= ShiftMask;
 
793
        if (mask & AvoidSnapAltMask)
 
794
            avoidSnapMask |= CompAltMask;
 
795
        if (mask & AvoidSnapControlMask)
 
796
            avoidSnapMask |= ControlMask;
 
797
        if (mask & AvoidSnapMetaMask)
 
798
            avoidSnapMask |= CompMetaMask;
 
799
    }
 
800
 
 
801
    default:
 
802
        break;
 
803
    }
 
804
}
 
805
 
 
806
SnapScreen::SnapScreen (CompScreen *screen) :
 
807
    PluginClassHandler <SnapScreen, CompScreen> (screen),
 
808
    SnapOptions (),
 
809
    snapping (true),
 
810
    avoidSnapMask (0)
 
811
{
 
812
    ScreenInterface::setHandler (screen);
 
813
 
 
814
#define setNotify(func) \
 
815
    optionSet##func##Notify (boost::bind (&SnapScreen::optionChanged, this, _1, _2))
 
816
 
 
817
    setNotify (AvoidSnap);
 
818
}
 
819
 
 
820
SnapWindow::SnapWindow (CompWindow *window) :
 
821
    PluginClassHandler <SnapWindow, CompWindow> (window),
 
822
    window (window),
 
823
    snapDirection (0),
 
824
    m_dx (0),
 
825
    m_dy (0),
 
826
    m_dwidth (0),
 
827
    m_dheight (0),
 
828
    snapGeometry (0, 0, 0, 0, 0),
 
829
    grabbed (0),
 
830
    skipNotify (false)
 
831
{
 
832
    WindowInterface::setHandler (window);
 
833
}
 
834
 
 
835
SnapWindow::~SnapWindow ()
 
836
{
 
837
}
 
838
 
 
839
bool
 
840
SnapPluginVTable::init ()
 
841
{
 
842
    if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))
 
843
        return false;
 
844
 
 
845
    return true;
 
846
}
 
847