~muktupavels/metacity/adwaita-icon-theme-lp-1414613

« back to all changes in this revision

Viewing changes to src/edge-resistance.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2005-12-13 23:03:47 UTC
  • mto: (2.2.1 sid) (1.4.2)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20051213230347-8dnaprp18n18dz1y
Tags: upstream-2.13.5
ImportĀ upstreamĀ versionĀ 2.13.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Edge resistance for move/resize operations */
 
2
 
 
3
/* 
 
4
 * Copyright (C) 2005 Elijah Newren
 
5
 * 
 
6
 * This program is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU General Public License as
 
8
 * published by the Free Software Foundation; either version 2 of the
 
9
 * License, or (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful, but
 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * General Public License for more details.
 
15
 * 
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
19
 * 02111-1307, USA.
 
20
 */
 
21
 
 
22
#include "edge-resistance.h"
 
23
#include "boxes.h"
 
24
#include "display.h"
 
25
#include "workspace.h"
 
26
 
 
27
/* A simple macro for whether a given window's edges are potentially
 
28
 * relevant for resistance/snapping during a move/resize operation
 
29
 */
 
30
#define WINDOW_EDGES_RELEVANT(window, display) \
 
31
  meta_window_should_be_showing (window) &&    \
 
32
  window->screen == display->grab_screen &&    \
 
33
  window         != display->grab_window &&    \
 
34
  window->type   != META_WINDOW_DESKTOP &&     \
 
35
  window->type   != META_WINDOW_MENU    &&     \
 
36
  window->type   != META_WINDOW_SPLASHSCREEN
 
37
 
 
38
struct ResistanceDataForAnEdge
 
39
{
 
40
  gboolean     timeout_setup;
 
41
  guint        timeout_id;
 
42
  int          timeout_edge_pos;
 
43
  gboolean     timeout_over;
 
44
  GSourceFunc  timeout_func;
 
45
  MetaWindow  *window;
 
46
  int          keyboard_buildup;
 
47
  gboolean     allow_past_screen_edge;
 
48
};
 
49
typedef struct ResistanceDataForAnEdge ResistanceDataForAnEdge;
 
50
 
 
51
struct MetaEdgeResistanceData
 
52
{
 
53
  GArray *left_edges;
 
54
  GArray *right_edges;
 
55
  GArray *top_edges;
 
56
  GArray *bottom_edges;
 
57
 
 
58
  ResistanceDataForAnEdge left_data;
 
59
  ResistanceDataForAnEdge right_data;
 
60
  ResistanceDataForAnEdge top_data;
 
61
  ResistanceDataForAnEdge bottom_data;
 
62
};
 
63
 
 
64
static int
 
65
find_index_of_edge_near_position (const GArray *edges,
 
66
                                  int           position,
 
67
                                  gboolean      want_interval_min,
 
68
                                  gboolean      horizontal)
 
69
{
 
70
  /* This is basically like a binary search, except that we're trying to
 
71
   * find a range instead of an exact value.  So, if we have in our array
 
72
   *   Value: 3  27 316 316 316 505 522 800 1213
 
73
   *   Index: 0   1   2   3   4   5   6   7    8
 
74
   * and we call this function with position=500 & want_interval_min=TRUE
 
75
   * then we should get 5 (because 505 is the first value bigger than 500).
 
76
   * If we call this function with position=805 and want_interval_min=FALSE
 
77
   * then we should get 7 (because 800 is the last value smaller than 800).
 
78
   * A couple more, to make things clear:
 
79
   *    position  want_interval_min  correct_answer
 
80
   *         316               TRUE               2
 
81
   *         316              FALSE               4
 
82
   *           2              FALSE              -1
 
83
   *        2000               TRUE               9
 
84
   */
 
85
  int low, high, mid;
 
86
  int compare;
 
87
  MetaEdge *edge;
 
88
 
 
89
  /* Initialize mid, edge, & compare in the off change that the array only
 
90
   * has one element.
 
91
   */
 
92
  mid  = 0;
 
93
  edge = g_array_index (edges, MetaEdge*, mid);
 
94
  compare = horizontal ? edge->rect.x : edge->rect.y;
 
95
 
 
96
  /* Begin the search... */
 
97
  low  = 0;
 
98
  high = edges->len - 1;
 
99
  while (low < high)
 
100
    {
 
101
      mid = low + (high - low)/2;
 
102
      edge = g_array_index (edges, MetaEdge*, mid);
 
103
      compare = horizontal ? edge->rect.x : edge->rect.y;
 
104
 
 
105
      if (compare == position)
 
106
        break;
 
107
 
 
108
      if (compare > position)
 
109
        high = mid - 1;
 
110
      else
 
111
        low = mid + 1;
 
112
    }
 
113
 
 
114
  /* mid should now be _really_ close to the index we want, so we start
 
115
   * linearly searching.  However, note that we don't know if mid is less
 
116
   * than or greater than what we need and it's possible that there are
 
117
   * several equal values equal to what we were searching for and we ended
 
118
   * up in the middle of them instead of at the end.  So we may need to
 
119
   * move mid multiple locations over.
 
120
   */
 
121
  if (want_interval_min)
 
122
    {
 
123
      while (compare >= position && mid > 0)
 
124
        {
 
125
          mid--;
 
126
          edge = g_array_index (edges, MetaEdge*, mid);
 
127
          compare = horizontal ? edge->rect.x : edge->rect.y;
 
128
        }
 
129
      while (compare < position && mid < (int)edges->len - 1)
 
130
        {
 
131
          mid++;
 
132
          edge = g_array_index (edges, MetaEdge*, mid);
 
133
          compare = horizontal ? edge->rect.x : edge->rect.y;
 
134
        }
 
135
 
 
136
      /* Special case for no values in array big enough */
 
137
      if (compare < position)
 
138
        return edges->len;
 
139
 
 
140
      /* Return the found value */
 
141
      return mid;
 
142
    }
 
143
  else
 
144
    {
 
145
      while (compare <= position && mid < (int)edges->len - 1)
 
146
        {
 
147
          mid++;
 
148
          edge = g_array_index (edges, MetaEdge*, mid);
 
149
          compare = horizontal ? edge->rect.x : edge->rect.y;
 
150
        }
 
151
      while (compare > position && mid > 0)
 
152
        {
 
153
          mid--;
 
154
          edge = g_array_index (edges, MetaEdge*, mid);
 
155
          compare = horizontal ? edge->rect.x : edge->rect.y;
 
156
        }
 
157
 
 
158
      /* Special case for no values in array small enough */
 
159
      if (compare > position)
 
160
        return -1;
 
161
 
 
162
      /* Return the found value */
 
163
      return mid;
 
164
    }
 
165
}
 
166
 
 
167
static gboolean
 
168
points_on_same_side (int ref, int pt1, int pt2)
 
169
{
 
170
  return (pt1 - ref) * (pt2 - ref) > 0;
 
171
}
 
172
 
 
173
static int
 
174
find_nearest_position (const GArray        *edges,
 
175
                       int                  position,
 
176
                       int                  old_position,
 
177
                       const MetaRectangle *new_rect,
 
178
                       gboolean             horizontal,
 
179
                       gboolean             only_forward)
 
180
{
 
181
  /* This is basically just a binary search except that we're looking
 
182
   * for the value closest to position, rather than finding that
 
183
   * actual value.  Also, we ignore any edges that aren't relevant
 
184
   * given the horizontal/vertical position of new_rect.
 
185
   */
 
186
  int low, high, mid;
 
187
  int compare;
 
188
  MetaEdge *edge;
 
189
  int best, best_dist, i;
 
190
 
 
191
  /* Initialize mid, edge, & compare in the off change that the array only
 
192
   * has one element.
 
193
   */
 
194
  mid  = 0;
 
195
  edge = g_array_index (edges, MetaEdge*, mid);
 
196
  compare = horizontal ? edge->rect.x : edge->rect.y;
 
197
 
 
198
  /* Begin the search... */
 
199
  low  = 0;
 
200
  high = edges->len - 1;
 
201
  while (low < high)
 
202
    {
 
203
      mid = low + (high - low)/2;
 
204
      edge = g_array_index (edges, MetaEdge*, mid);
 
205
      compare = horizontal ? edge->rect.x : edge->rect.y;
 
206
 
 
207
      if (compare == position)
 
208
        break;
 
209
 
 
210
      if (compare > position)
 
211
        high = mid - 1;
 
212
      else
 
213
        low = mid + 1;
 
214
    }
 
215
 
 
216
  /* mid should now be _really_ close to the index we want, so we
 
217
   * start searching nearby for something that overlaps and is closer
 
218
   * than the original position.
 
219
   */
 
220
  best = old_position;
 
221
  best_dist = INT_MAX;
 
222
 
 
223
  /* Start the search at mid */
 
224
  edge = g_array_index (edges, MetaEdge*, mid);
 
225
  compare = horizontal ? edge->rect.x : edge->rect.y;
 
226
  gboolean edges_align = horizontal ? 
 
227
    meta_rectangle_vert_overlap (&edge->rect, new_rect) :
 
228
    meta_rectangle_horiz_overlap (&edge->rect, new_rect);
 
229
  if (edges_align &&
 
230
      (!only_forward || !points_on_same_side (position, compare, old_position)))
 
231
    {
 
232
      int dist = ABS (compare - position);
 
233
      if (dist < best_dist)
 
234
        {
 
235
          best = compare;
 
236
          best_dist = dist;
 
237
        }
 
238
    }
 
239
 
 
240
  /* Now start searching higher than mid */
 
241
  for (i = mid + 1; i < (int)edges->len; i++)
 
242
    {
 
243
      edge = g_array_index (edges, MetaEdge*, i);
 
244
      compare = horizontal ? edge->rect.x : edge->rect.y;
 
245
  
 
246
      gboolean edges_align = horizontal ? 
 
247
        meta_rectangle_vert_overlap (&edge->rect, new_rect) :
 
248
        meta_rectangle_horiz_overlap (&edge->rect, new_rect);
 
249
 
 
250
      if (edges_align &&
 
251
          (!only_forward ||
 
252
           !points_on_same_side (position, compare, old_position)))
 
253
        {
 
254
          int dist = ABS (compare - position);
 
255
          if (dist < best_dist)
 
256
            {
 
257
              best = compare;
 
258
              best_dist = dist;
 
259
            }
 
260
          break;
 
261
        }
 
262
    }
 
263
 
 
264
  /* Now start searching lower than mid */
 
265
  for (i = mid-1; i >= 0; i--)
 
266
    {
 
267
      edge = g_array_index (edges, MetaEdge*, i);
 
268
      compare = horizontal ? edge->rect.x : edge->rect.y;
 
269
  
 
270
      gboolean edges_align = horizontal ? 
 
271
        meta_rectangle_vert_overlap (&edge->rect, new_rect) :
 
272
        meta_rectangle_horiz_overlap (&edge->rect, new_rect);
 
273
 
 
274
      if (edges_align &&
 
275
          (!only_forward ||
 
276
           !points_on_same_side (position, compare, old_position)))
 
277
        {
 
278
          int dist = ABS (compare - position);
 
279
          if (dist < best_dist)
 
280
            {
 
281
              best = compare;
 
282
              best_dist = dist;
 
283
            }
 
284
          break;
 
285
        }
 
286
    }
 
287
 
 
288
  /* Return the best one found */
 
289
  return best;
 
290
}
 
291
 
 
292
static gboolean
 
293
movement_towards_edge (MetaDirection side, int increment)
 
294
{
 
295
  switch (side)
 
296
    {
 
297
    case META_DIRECTION_LEFT:
 
298
    case META_DIRECTION_TOP:
 
299
      return increment < 0;
 
300
    case META_DIRECTION_RIGHT:
 
301
    case META_DIRECTION_BOTTOM:
 
302
      return increment > 0;
 
303
    }
 
304
 
 
305
  g_assert_not_reached ();
 
306
}
 
307
 
 
308
static gboolean
 
309
edge_resistance_timeout (gpointer data)
 
310
{
 
311
  ResistanceDataForAnEdge *resistance_data = data;
 
312
 
 
313
  resistance_data->timeout_over = TRUE;
 
314
  resistance_data->timeout_id = 0;
 
315
  (*resistance_data->timeout_func)(resistance_data->window);
 
316
 
 
317
  return FALSE;
 
318
}
 
319
 
 
320
static int
 
321
apply_edge_resistance (MetaWindow                *window,
 
322
                       int                        old_pos,
 
323
                       int                        new_pos,
 
324
                       const MetaRectangle       *new_rect,
 
325
                       GArray                    *edges,
 
326
                       ResistanceDataForAnEdge   *resistance_data,
 
327
                       GSourceFunc                timeout_func,
 
328
                       gboolean                   xdir,
 
329
                       gboolean                   keyboard_op)
 
330
{
 
331
  int i, begin, end;
 
332
  gboolean okay_to_clear_keyboard_buildup = FALSE;
 
333
  int      keyboard_buildup_edge = G_MAXINT;
 
334
  gboolean increasing = new_pos > old_pos;
 
335
  int      increment = increasing ? 1 : -1;
 
336
 
 
337
  const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW    = 16;
 
338
  const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW   =  8;
 
339
  const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_XINERAMA  = 32;
 
340
  const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_XINERAMA =  8;
 
341
  const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN    = 32;
 
342
  const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN   =  8;
 
343
  const int TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW   =   0;
 
344
  const int TIMEOUT_RESISTANCE_LENGTH_MS_XINERAMA = 100;
 
345
  const int TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN   = 750;
 
346
  const int KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_WINDOW    = 16;
 
347
  const int KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_WINDOW   = 16;
 
348
  const int KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_XINERAMA  = 24;
 
349
  const int KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_XINERAMA = 16;
 
350
  const int KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_SCREEN    = 32;
 
351
  const int KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_SCREEN   = 16;
 
352
 
 
353
  /* Quit if no movement was specified */
 
354
  if (old_pos == new_pos)
 
355
    return new_pos;
 
356
 
 
357
  /* Remove the old timeout if it's no longer relevant */
 
358
  if (resistance_data->timeout_setup &&
 
359
      ((resistance_data->timeout_edge_pos > old_pos &&
 
360
        resistance_data->timeout_edge_pos > new_pos)  ||
 
361
       (resistance_data->timeout_edge_pos < old_pos &&
 
362
        resistance_data->timeout_edge_pos < new_pos)))
 
363
    {
 
364
      resistance_data->timeout_setup = FALSE;
 
365
      if (resistance_data->timeout_id != 0)
 
366
        {
 
367
          g_source_remove (resistance_data->timeout_id);
 
368
          resistance_data->timeout_id = 0;
 
369
        }
 
370
    }
 
371
 
 
372
  /* Get the range of indices in the edge array that we move past/to. */
 
373
  begin = find_index_of_edge_near_position (edges, old_pos,  increasing, xdir);
 
374
  end   = find_index_of_edge_near_position (edges, new_pos, !increasing, xdir);
 
375
 
 
376
  /* Loop over all these edges we're moving past/to. */
 
377
  i = begin;
 
378
  while ((increasing  && i <= end) ||
 
379
         (!increasing && i >= end))
 
380
    {
 
381
      gboolean  edges_align;
 
382
      MetaEdge *edge = g_array_index (edges, MetaEdge*, i);
 
383
      int       compare = xdir ? edge->rect.x : edge->rect.y;
 
384
 
 
385
      /* Find out if this edge is relevant */
 
386
      edges_align = xdir ? 
 
387
        meta_rectangle_vert_overlap (&edge->rect, new_rect) :
 
388
        meta_rectangle_horiz_overlap (&edge->rect, new_rect);
 
389
 
 
390
      /* Nothing to do unless the edges align */
 
391
      if (!edges_align)
 
392
        {
 
393
          /* Go to the next edge in the range */
 
394
          i += increment;
 
395
          continue;
 
396
        }
 
397
 
 
398
      /* Rest is easier to read if we split on keyboard vs. mouse op */
 
399
      if (keyboard_op)
 
400
        {
 
401
          /* KEYBOARD ENERGY BUILDUP RESISTANCE: If the user has is moving
 
402
           * fast enough or has already built up enough "energy", then let
 
403
           * the user past the edge, otherwise stop at this edge.  If the
 
404
           * user was previously stopped at this edge, add movement amount
 
405
           * to the built up energy.
 
406
           */
 
407
 
 
408
          /* First, determine the amount of the resistance */
 
409
          int resistance = 0;
 
410
          switch (edge->edge_type)
 
411
            {
 
412
            case META_EDGE_WINDOW:
 
413
              if (movement_towards_edge (edge->side_type, increment))
 
414
                resistance = KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_WINDOW;
 
415
              else
 
416
                resistance = KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_WINDOW;
 
417
              break;
 
418
            case META_EDGE_XINERAMA:
 
419
              if (movement_towards_edge (edge->side_type, increment))
 
420
                resistance = KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_XINERAMA;
 
421
              else
 
422
                resistance = KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_XINERAMA;
 
423
              break;
 
424
            case META_EDGE_SCREEN:
 
425
              if (movement_towards_edge (edge->side_type, increment))
 
426
                resistance = KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_SCREEN;
 
427
              else
 
428
                resistance = KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_SCREEN;
 
429
              break;
 
430
            }
 
431
 
 
432
          /* Clear any previous buildup if we've run into an edge at a
 
433
           * different location than what we were building up on before.
 
434
           * See below for more details where these get set.
 
435
           */
 
436
          if (okay_to_clear_keyboard_buildup &&
 
437
              compare != keyboard_buildup_edge)
 
438
            {
 
439
              okay_to_clear_keyboard_buildup = FALSE;
 
440
              resistance_data->keyboard_buildup = 0;
 
441
            }
 
442
 
 
443
          /* Determine the threshold */
 
444
          int threshold = resistance - resistance_data->keyboard_buildup;
 
445
 
 
446
          /* See if threshold hasn't been met yet or not */
 
447
          if (ABS (compare - new_pos) < threshold)
 
448
            {
 
449
              if (resistance_data->keyboard_buildup != 0)
 
450
                resistance_data->keyboard_buildup += ABS (new_pos - compare);
 
451
              else
 
452
                resistance_data->keyboard_buildup = 1; /* 0 causes stuckage */
 
453
              return compare;
 
454
            }
 
455
          else
 
456
            {
 
457
              /* It may be the case that there are two windows with edges
 
458
               * at the same location.  If so, the buildup ought to count
 
459
               * towards both edges.  So we just not that it's okay to
 
460
               * clear the buildup once we find an edge at a different
 
461
               * location.
 
462
               */
 
463
              okay_to_clear_keyboard_buildup = TRUE;
 
464
              keyboard_buildup_edge = compare;
 
465
            }
 
466
        }
 
467
      else /* mouse op */
 
468
        {
 
469
          /* INFINITE RESISTANCE for screen edges under certain cases; If
 
470
           * the edge is relevant and we're moving towards it and it's a
 
471
           * screen edge and infinite resistance has been requested for
 
472
           * this particular grab op then don't allow movement past it.
 
473
           */
 
474
          if (edge->edge_type == META_EDGE_SCREEN &&
 
475
              !resistance_data->allow_past_screen_edge &&
 
476
              movement_towards_edge (edge->side_type, increment))
 
477
            {
 
478
              return compare;
 
479
            }
 
480
 
 
481
          /* TIMEOUT RESISTANCE: If the edge is relevant and we're moving
 
482
           * towards it, then we may want to have some kind of time delay
 
483
           * before the user can move past this edge.
 
484
           */
 
485
          if (movement_towards_edge (edge->side_type, increment))
 
486
            {
 
487
              /* First, determine the length of time for the resistance */
 
488
              int timeout_length_ms = 0;
 
489
              switch (edge->edge_type)
 
490
                {
 
491
                case META_EDGE_WINDOW:
 
492
                  timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW;
 
493
                  break;
 
494
                case META_EDGE_XINERAMA:
 
495
                  if (window->require_on_single_xinerama)
 
496
                    timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_XINERAMA;
 
497
                  break;
 
498
                case META_EDGE_SCREEN:
 
499
                  if (window->require_fully_onscreen)
 
500
                    timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN;
 
501
                  break;
 
502
                }
 
503
 
 
504
              if (!resistance_data->timeout_setup &&
 
505
                  timeout_length_ms != 0)
 
506
                {
 
507
                  resistance_data->timeout_id = 
 
508
                    g_timeout_add (timeout_length_ms,
 
509
                                   edge_resistance_timeout,
 
510
                                   resistance_data);
 
511
                  resistance_data->timeout_setup = TRUE;
 
512
                  resistance_data->timeout_edge_pos = compare;
 
513
                  resistance_data->timeout_over = FALSE;
 
514
                  resistance_data->timeout_func = timeout_func;
 
515
                  resistance_data->window = window;
 
516
                }
 
517
              if (!resistance_data->timeout_over &&
 
518
                  timeout_length_ms != 0)
 
519
                return compare;
 
520
            }
 
521
 
 
522
          /* PIXEL DISTANCE MOUSE RESISTANCE: If the edge matters and the
 
523
           * user hasn't moved at least threshold pixels past this edge,
 
524
           * stop movement at this edge.  (Note that this is different from
 
525
           * keyboard resistance precisely because keyboard move ops are
 
526
           * relative to previous positions, whereas mouse move ops are
 
527
           * relative to differences in mouse position and mouse position
 
528
           * is an absolute quantity rather than a relative quantity)
 
529
           */
 
530
 
 
531
          /* First, determine the threshold */
 
532
          int threshold = 0;
 
533
          switch (edge->edge_type)
 
534
            {
 
535
            case META_EDGE_WINDOW:
 
536
              if (movement_towards_edge (edge->side_type, increment))
 
537
                threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW;
 
538
              else
 
539
                threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW;
 
540
              break;
 
541
            case META_EDGE_XINERAMA:
 
542
              if (movement_towards_edge (edge->side_type, increment))
 
543
                threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_XINERAMA;
 
544
              else
 
545
                threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_XINERAMA;
 
546
              break;
 
547
            case META_EDGE_SCREEN:
 
548
              if (movement_towards_edge (edge->side_type, increment))
 
549
                threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN;
 
550
              else
 
551
                threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN;
 
552
              break;
 
553
            }
 
554
 
 
555
          if (ABS (compare - new_pos) < threshold)
 
556
            return compare;
 
557
        }
 
558
 
 
559
      /* Go to the next edge in the range */
 
560
      i += increment;
 
561
    }
 
562
 
 
563
  /* If we didn't run into any new edges in keyboard buildup but had moved
 
564
   * far enough to get past the last one, clear the buildup
 
565
   */
 
566
  if (okay_to_clear_keyboard_buildup && new_pos != keyboard_buildup_edge)
 
567
    resistance_data->keyboard_buildup = 0;
 
568
 
 
569
  return new_pos;
 
570
}
 
571
 
 
572
static int
 
573
apply_edge_snapping (int                  old_pos,
 
574
                     int                  new_pos,
 
575
                     const MetaRectangle *new_rect,
 
576
                     GArray              *edges1,
 
577
                     GArray              *edges2,
 
578
                     gboolean             xdir,
 
579
                     gboolean             keyboard_op)
 
580
{
 
581
  int pos1, pos2;
 
582
  int best;
 
583
 
 
584
  if (old_pos == new_pos)
 
585
    return new_pos;
 
586
 
 
587
  /* We look at two sets of edges (e.g. left and right) individually
 
588
   * finding the nearest position among each set of edges and then later
 
589
   * finding the better of these two bests.
 
590
   */
 
591
  pos1 = find_nearest_position (edges1,
 
592
                                new_pos,
 
593
                                old_pos,
 
594
                                new_rect,
 
595
                                xdir,
 
596
                                keyboard_op);
 
597
  pos2 = find_nearest_position (edges2,
 
598
                                new_pos,
 
599
                                old_pos,
 
600
                                new_rect,
 
601
                                xdir,
 
602
                                keyboard_op);
 
603
 
 
604
  /* For keyboard snapping, ignore either pos1 or pos2 if they aren't in the
 
605
   * right direction.
 
606
   */
 
607
  if (keyboard_op)
 
608
    {
 
609
      if (!points_on_same_side (old_pos, pos1, new_pos))
 
610
        return pos2;
 
611
      if (!points_on_same_side (old_pos, pos2, new_pos))
 
612
        return pos1;
 
613
    }
 
614
 
 
615
  /* Find the better of pos1 and pos2 and return it */
 
616
  if (ABS (pos1 - new_pos) < ABS (pos2 - new_pos))
 
617
    best = pos1;
 
618
  else
 
619
    best = pos2;
 
620
 
 
621
  /* If mouse snap-moving, the user could easily accidentally move just a
 
622
   * couple pixels in a direction they didn't mean to move; so ignore snap
 
623
   * movement in those cases unless it's only a small number of pixels
 
624
   * anyway.
 
625
   */
 
626
  if (!keyboard_op &&
 
627
      ABS (best - old_pos) >= 8 &&
 
628
      ABS (new_pos - old_pos) < 8)
 
629
    return old_pos;
 
630
  else
 
631
    /* Otherwise, return the best of the snapping positions found */
 
632
    return best;
 
633
}
 
634
 
 
635
/* This function takes the position (including any frame) of the window and
 
636
 * a proposed new position (ignoring edge resistance/snapping), and then
 
637
 * applies edge resistance to EACH edge (separately) updating new_outer.
 
638
 * It returns true if new_outer is modified, false otherwise.
 
639
 *
 
640
 * display->grab_edge_resistance_data MUST already be setup or calling this
 
641
 * function will cause a crash.
 
642
 */
 
643
static gboolean 
 
644
apply_edge_resistance_to_each_side (MetaDisplay         *display,
 
645
                                    MetaWindow          *window,
 
646
                                    const MetaRectangle *old_outer,
 
647
                                    MetaRectangle       *new_outer,
 
648
                                    GSourceFunc          timeout_func,
 
649
                                    gboolean             auto_snap,
 
650
                                    gboolean             keyboard_op)
 
651
{
 
652
  MetaEdgeResistanceData *edge_data;
 
653
  MetaRectangle           modified_rect;
 
654
  gboolean                modified;
 
655
  int new_left, new_right, new_top, new_bottom;
 
656
 
 
657
  g_assert (display->grab_edge_resistance_data != NULL);
 
658
  edge_data = display->grab_edge_resistance_data;
 
659
 
 
660
  if (auto_snap)
 
661
    {
 
662
      /* Do the auto snapping instead of normal edge resistance; in all
 
663
       * cases, we allow snapping to opposite kinds of edges (e.g. left
 
664
       * sides of windows to both left and right edges.
 
665
       */
 
666
 
 
667
      new_left   = apply_edge_snapping (BOX_LEFT (*old_outer),
 
668
                                        BOX_LEFT (*new_outer),
 
669
                                        new_outer,
 
670
                                        edge_data->left_edges,
 
671
                                        edge_data->right_edges,
 
672
                                        TRUE,
 
673
                                        keyboard_op);
 
674
 
 
675
      new_right  = apply_edge_snapping (BOX_RIGHT (*old_outer),
 
676
                                        BOX_RIGHT (*new_outer),
 
677
                                        new_outer,
 
678
                                        edge_data->left_edges,
 
679
                                        edge_data->right_edges,
 
680
                                        TRUE,
 
681
                                        keyboard_op);
 
682
 
 
683
      new_top    = apply_edge_snapping (BOX_TOP (*old_outer),
 
684
                                        BOX_TOP (*new_outer),
 
685
                                        new_outer,
 
686
                                        edge_data->top_edges,
 
687
                                        edge_data->bottom_edges,
 
688
                                        FALSE,
 
689
                                        keyboard_op);
 
690
 
 
691
      new_bottom = apply_edge_snapping (BOX_BOTTOM (*old_outer),
 
692
                                        BOX_BOTTOM (*new_outer),
 
693
                                        new_outer,
 
694
                                        edge_data->top_edges,
 
695
                                        edge_data->bottom_edges,
 
696
                                        FALSE,
 
697
                                        keyboard_op);
 
698
    }
 
699
  else
 
700
    {
 
701
      /* Now, apply the normal edge resistance */
 
702
      new_left   = apply_edge_resistance (window,
 
703
                                          BOX_LEFT (*old_outer),
 
704
                                          BOX_LEFT (*new_outer),
 
705
                                          new_outer,
 
706
                                          edge_data->left_edges,
 
707
                                          &edge_data->left_data,
 
708
                                          timeout_func,
 
709
                                          TRUE,
 
710
                                          keyboard_op);
 
711
      new_right  = apply_edge_resistance (window,
 
712
                                          BOX_RIGHT (*old_outer),
 
713
                                          BOX_RIGHT (*new_outer),
 
714
                                          new_outer,
 
715
                                          edge_data->right_edges,
 
716
                                          &edge_data->right_data,
 
717
                                          timeout_func,
 
718
                                          TRUE,
 
719
                                          keyboard_op);
 
720
      new_top    = apply_edge_resistance (window,
 
721
                                          BOX_TOP (*old_outer),
 
722
                                          BOX_TOP (*new_outer),
 
723
                                          new_outer,
 
724
                                          edge_data->top_edges,
 
725
                                          &edge_data->top_data,
 
726
                                          timeout_func,
 
727
                                          FALSE,
 
728
                                          keyboard_op);
 
729
      new_bottom = apply_edge_resistance (window,
 
730
                                          BOX_BOTTOM (*old_outer),
 
731
                                          BOX_BOTTOM (*new_outer),
 
732
                                          new_outer,
 
733
                                          edge_data->bottom_edges,
 
734
                                          &edge_data->bottom_data,
 
735
                                          timeout_func,
 
736
                                          FALSE,
 
737
                                          keyboard_op);
 
738
    }
 
739
 
 
740
  /* Determine whether anything changed, and save the changes */
 
741
  modified_rect = meta_rect (new_left, 
 
742
                             new_top,
 
743
                             new_right - new_left,
 
744
                             new_bottom - new_top);
 
745
  modified = !meta_rectangle_equal (new_outer, &modified_rect);
 
746
  *new_outer = modified_rect;
 
747
  return modified;
 
748
}
 
749
 
 
750
void
 
751
meta_display_cleanup_edges (MetaDisplay *display)
 
752
{
 
753
  MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
 
754
  g_assert (edge_data != NULL);
 
755
  guint i,j;
 
756
 
 
757
  /* We first need to clean out any window edges */
 
758
  for (i = 0; i < 4; i++)
 
759
    {
 
760
      GArray *tmp = NULL;
 
761
      switch (i)
 
762
        {
 
763
        case 0:
 
764
          tmp = edge_data->left_edges;
 
765
          break;
 
766
        case 1:
 
767
          tmp = edge_data->right_edges;
 
768
          break;
 
769
        case 2:
 
770
          tmp = edge_data->top_edges;
 
771
          break;
 
772
        case 3:
 
773
          tmp = edge_data->bottom_edges;
 
774
          break;
 
775
        default:
 
776
          g_assert_not_reached ();
 
777
        }
 
778
 
 
779
      for (j = 0; j < tmp->len; j++)
 
780
        {
 
781
          MetaEdge *edge = g_array_index (tmp, MetaEdge*, j);
 
782
          if (edge->edge_type == META_EDGE_WINDOW)
 
783
            g_free (edge);
 
784
        }
 
785
    }
 
786
 
 
787
  /* Now free the arrays and data */
 
788
  g_array_free (edge_data->left_edges, TRUE);
 
789
  g_array_free (edge_data->right_edges, TRUE);
 
790
  g_array_free (edge_data->top_edges, TRUE);
 
791
  g_array_free (edge_data->bottom_edges, TRUE);
 
792
 
 
793
  /* Cleanup the timeouts */
 
794
  if (edge_data->left_data.timeout_setup   &&
 
795
      edge_data->left_data.timeout_id   != 0)
 
796
    g_source_remove (edge_data->left_data.timeout_id);
 
797
  if (edge_data->right_data.timeout_setup  &&
 
798
      edge_data->right_data.timeout_id  != 0)
 
799
    g_source_remove (edge_data->right_data.timeout_id);
 
800
  if (edge_data->top_data.timeout_setup    &&
 
801
      edge_data->top_data.timeout_id    != 0)
 
802
    g_source_remove (edge_data->top_data.timeout_id);
 
803
  if (edge_data->bottom_data.timeout_setup &&
 
804
      edge_data->bottom_data.timeout_id != 0)
 
805
    g_source_remove (edge_data->bottom_data.timeout_id);
 
806
 
 
807
  g_free (display->grab_edge_resistance_data);
 
808
  display->grab_edge_resistance_data = NULL;
 
809
}
 
810
 
 
811
static int
 
812
stupid_sort_requiring_extra_pointer_dereference (gconstpointer a, 
 
813
                                                 gconstpointer b)
 
814
{
 
815
  const MetaEdge * const *a_edge = a;
 
816
  const MetaEdge * const *b_edge = b;
 
817
  return meta_rectangle_edge_cmp (*a_edge, *b_edge);
 
818
}
 
819
 
 
820
static void
 
821
cache_edges (MetaDisplay *display,
 
822
             GList *window_edges,
 
823
             GList *xinerama_edges,
 
824
             GList *screen_edges)
 
825
{
 
826
  MetaEdgeResistanceData *edge_data;
 
827
  GList *tmp;
 
828
  int num_left, num_right, num_top, num_bottom;
 
829
  int i;
 
830
 
 
831
  /*
 
832
   * 1st: Get the total number of each kind of edge
 
833
   */
 
834
  num_left = num_right = num_top = num_bottom = 0;
 
835
  for (i = 0; i < 3; i++)
 
836
    {
 
837
      tmp = NULL;
 
838
      switch (i)
 
839
        {
 
840
        case 0:
 
841
          tmp = window_edges;
 
842
          break;
 
843
        case 1:
 
844
          tmp = xinerama_edges;
 
845
          break;
 
846
        case 2:
 
847
          tmp = screen_edges;
 
848
          break;
 
849
        default:
 
850
          g_assert_not_reached ();
 
851
        }
 
852
 
 
853
      while (tmp)
 
854
        {
 
855
          MetaEdge *edge = tmp->data;
 
856
          switch (edge->side_type)
 
857
            {
 
858
            case META_DIRECTION_LEFT:
 
859
              num_left++;
 
860
              break;
 
861
            case META_DIRECTION_RIGHT:
 
862
              num_right++;
 
863
              break;
 
864
            case META_DIRECTION_TOP:
 
865
              num_top++;
 
866
              break;
 
867
            case META_DIRECTION_BOTTOM:
 
868
              num_bottom++;
 
869
              break;
 
870
            default:
 
871
              g_assert_not_reached ();
 
872
            }
 
873
          tmp = tmp->next;
 
874
        }
 
875
    }
 
876
 
 
877
  /*
 
878
   * 2nd: Allocate the edges
 
879
   */
 
880
  g_assert (display->grab_edge_resistance_data == NULL);
 
881
  display->grab_edge_resistance_data = g_new (MetaEdgeResistanceData, 1);
 
882
  edge_data = display->grab_edge_resistance_data;
 
883
  edge_data->left_edges   = g_array_sized_new (FALSE,
 
884
                                               FALSE,
 
885
                                               sizeof(MetaEdge*),
 
886
                                               num_left);
 
887
  edge_data->right_edges  = g_array_sized_new (FALSE,
 
888
                                               FALSE,
 
889
                                               sizeof(MetaEdge*),
 
890
                                               num_right);
 
891
  edge_data->top_edges    = g_array_sized_new (FALSE,
 
892
                                               FALSE,
 
893
                                               sizeof(MetaEdge*),
 
894
                                               num_top);
 
895
  edge_data->bottom_edges = g_array_sized_new (FALSE,
 
896
                                               FALSE,
 
897
                                               sizeof(MetaEdge*),
 
898
                                               num_bottom);
 
899
 
 
900
  /*
 
901
   * 3rd: Add the edges to the arrays
 
902
   */
 
903
  num_left = num_right = num_top = num_bottom = 0;
 
904
  for (i = 0; i < 3; i++)
 
905
    {
 
906
      tmp = NULL;
 
907
      switch (i)
 
908
        {
 
909
        case 0:
 
910
          tmp = window_edges;
 
911
          break;
 
912
        case 1:
 
913
          tmp = xinerama_edges;
 
914
          break;
 
915
        case 2:
 
916
          tmp = screen_edges;
 
917
          break;
 
918
        default:
 
919
          g_assert_not_reached ();
 
920
        }
 
921
 
 
922
      while (tmp)
 
923
        {
 
924
          MetaEdge *edge = tmp->data;
 
925
          switch (edge->side_type)
 
926
            {
 
927
            case META_DIRECTION_LEFT:
 
928
              g_array_append_val (edge_data->left_edges, edge);
 
929
              break;
 
930
            case META_DIRECTION_RIGHT:
 
931
              g_array_append_val (edge_data->right_edges, edge);
 
932
              break;
 
933
            case META_DIRECTION_TOP:
 
934
              g_array_append_val (edge_data->top_edges, edge);
 
935
              break;
 
936
            case META_DIRECTION_BOTTOM:
 
937
              g_array_append_val (edge_data->bottom_edges, edge);
 
938
              break;
 
939
            default:
 
940
              g_assert_not_reached ();
 
941
            }
 
942
          tmp = tmp->next;
 
943
        }
 
944
    }
 
945
 
 
946
  /*
 
947
   * 4th: Sort the arrays (FIXME: This is kinda dumb since the arrays were
 
948
   * individually sorted earlier and we could have done this faster and
 
949
   * avoided this sort by sticking them into the array with some simple
 
950
   * merging of the lists).
 
951
   */
 
952
  g_array_sort (display->grab_edge_resistance_data->left_edges, 
 
953
                stupid_sort_requiring_extra_pointer_dereference);
 
954
  g_array_sort (display->grab_edge_resistance_data->right_edges, 
 
955
                stupid_sort_requiring_extra_pointer_dereference);
 
956
  g_array_sort (display->grab_edge_resistance_data->top_edges, 
 
957
                stupid_sort_requiring_extra_pointer_dereference);
 
958
  g_array_sort (display->grab_edge_resistance_data->bottom_edges, 
 
959
                stupid_sort_requiring_extra_pointer_dereference);
 
960
}
 
961
 
 
962
static void
 
963
initialize_grab_edge_resistance_data (MetaDisplay *display)
 
964
{
 
965
  MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
 
966
 
 
967
  edge_data->left_data.timeout_setup   = FALSE;
 
968
  edge_data->right_data.timeout_setup  = FALSE;
 
969
  edge_data->top_data.timeout_setup    = FALSE;
 
970
  edge_data->bottom_data.timeout_setup = FALSE;
 
971
 
 
972
  edge_data->left_data.keyboard_buildup   = 0;
 
973
  edge_data->right_data.keyboard_buildup  = 0;
 
974
  edge_data->top_data.keyboard_buildup    = 0;
 
975
  edge_data->bottom_data.keyboard_buildup = 0;
 
976
 
 
977
  edge_data->left_data.allow_past_screen_edge   = TRUE;
 
978
  edge_data->right_data.allow_past_screen_edge  = TRUE;
 
979
  edge_data->bottom_data.allow_past_screen_edge = TRUE;
 
980
  edge_data->top_data.allow_past_screen_edge    = 
 
981
    display->grab_anchor_root_y >= display->grab_initial_window_pos.y;
 
982
}
 
983
 
 
984
void
 
985
meta_display_compute_resistance_and_snapping_edges (MetaDisplay *display)
 
986
{
 
987
  GList *stacked_windows;
 
988
  GList *cur_window_iter;
 
989
  GList *edges;
 
990
  /* Lists of window positions (rects) and their relative stacking positions */
 
991
  int stack_position;
 
992
  GSList *obscuring_windows, *window_stacking;
 
993
  /* The portions of the above lists that still remain at the stacking position
 
994
   * in the layer that we are working on
 
995
   */
 
996
  GSList *rem_windows, *rem_win_stacking;
 
997
 
 
998
  /*
 
999
   * 1st: Get the list of relevant windows, from bottom to top
 
1000
   */
 
1001
  stacked_windows = 
 
1002
    meta_stack_list_windows (display->grab_screen->stack,
 
1003
                             display->grab_screen->active_workspace);
 
1004
 
 
1005
  /*
 
1006
   * 2nd: we need to separate that stacked list into a list of windows that
 
1007
   * can obscure other edges.  To make sure we only have windows obscuring
 
1008
   * those below it instead of going both ways, we also need to keep a
 
1009
   * counter list.  Messy, I know.
 
1010
   */
 
1011
  obscuring_windows = window_stacking = NULL;
 
1012
  cur_window_iter = stacked_windows;
 
1013
  stack_position = 0;
 
1014
  while (cur_window_iter != NULL)
 
1015
    {
 
1016
      MetaWindow *cur_window = cur_window_iter->data;
 
1017
      if (WINDOW_EDGES_RELEVANT (cur_window, display))
 
1018
        {
 
1019
          MetaRectangle *new_rect;
 
1020
          new_rect = g_new (MetaRectangle, 1);
 
1021
          meta_window_get_outer_rect (cur_window, new_rect);
 
1022
          obscuring_windows = g_slist_prepend (obscuring_windows, new_rect);
 
1023
          window_stacking = 
 
1024
            g_slist_prepend (window_stacking, GINT_TO_POINTER (stack_position));
 
1025
        }
 
1026
 
 
1027
      stack_position++;
 
1028
      cur_window_iter = cur_window_iter->next;
 
1029
    }
 
1030
  /* Put 'em in bottom to top order */
 
1031
  rem_windows       = g_slist_reverse (obscuring_windows);
 
1032
  rem_win_stacking  = g_slist_reverse (window_stacking);
 
1033
 
 
1034
  /*
 
1035
   * 3rd: loop over the windows again, this time getting the edges from
 
1036
   * them and removing intersections with the relevant obscuring_windows &
 
1037
   * obscuring_docks.
 
1038
   */
 
1039
  edges = NULL;
 
1040
  stack_position = 0;
 
1041
  cur_window_iter = stacked_windows;
 
1042
  while (cur_window_iter != NULL)
 
1043
    {
 
1044
      MetaRectangle  cur_rect;
 
1045
      MetaWindow    *cur_window = cur_window_iter->data;
 
1046
      meta_window_get_outer_rect (cur_window, &cur_rect);
 
1047
 
 
1048
      /* Check if we want to use this window's edges for edge
 
1049
       * resistance (note that dock edges are considered screen edges
 
1050
       * which are handled separately
 
1051
       */
 
1052
      if (WINDOW_EDGES_RELEVANT (cur_window, display) &&
 
1053
          cur_window->type != META_WINDOW_DOCK)
 
1054
        {
 
1055
          GList *new_edges;
 
1056
          MetaEdge *new_edge;
 
1057
          MetaRectangle reduced;
 
1058
 
 
1059
          /* We don't care about snapping to any portion of the window that
 
1060
           * is offscreen (we also don't care about parts of edges covered
 
1061
           * by other windows or DOCKS, but that's handled below).
 
1062
           */
 
1063
          meta_rectangle_intersect (&cur_rect, 
 
1064
                                    &display->grab_screen->rect,
 
1065
                                    &reduced);
 
1066
 
 
1067
          new_edges = NULL;
 
1068
 
 
1069
          /* Left side of this window is resistance for the right edge of
 
1070
           * the window being moved.
 
1071
           */
 
1072
          new_edge = g_new (MetaEdge, 1);
 
1073
          new_edge->rect = reduced;
 
1074
          new_edge->rect.width = 0;
 
1075
          new_edge->side_type = META_DIRECTION_RIGHT;
 
1076
          new_edge->edge_type = META_EDGE_WINDOW;
 
1077
          new_edges = g_list_prepend (new_edges, new_edge);
 
1078
 
 
1079
          /* Right side of this window is resistance for the left edge of
 
1080
           * the window being moved.
 
1081
           */
 
1082
          new_edge = g_new (MetaEdge, 1);
 
1083
          new_edge->rect = reduced;
 
1084
          new_edge->rect.x += new_edge->rect.width;
 
1085
          new_edge->rect.width = 0;
 
1086
          new_edge->side_type = META_DIRECTION_LEFT;
 
1087
          new_edge->edge_type = META_EDGE_WINDOW;
 
1088
          new_edges = g_list_prepend (new_edges, new_edge);
 
1089
          
 
1090
          /* Top side of this window is resistance for the bottom edge of
 
1091
           * the window being moved.
 
1092
           */
 
1093
          new_edge = g_new (MetaEdge, 1);
 
1094
          new_edge->rect = reduced;
 
1095
          new_edge->rect.height = 0;
 
1096
          new_edge->side_type = META_DIRECTION_BOTTOM;
 
1097
          new_edge->edge_type = META_EDGE_WINDOW;
 
1098
          new_edges = g_list_prepend (new_edges, new_edge);
 
1099
 
 
1100
          /* Top side of this window is resistance for the bottom edge of
 
1101
           * the window being moved.
 
1102
           */
 
1103
          new_edge = g_new (MetaEdge, 1);
 
1104
          new_edge->rect = reduced;
 
1105
          new_edge->rect.y += new_edge->rect.height;
 
1106
          new_edge->rect.height = 0;
 
1107
          new_edge->side_type = META_DIRECTION_TOP;
 
1108
          new_edge->edge_type = META_EDGE_WINDOW;
 
1109
          new_edges = g_list_prepend (new_edges, new_edge);
 
1110
 
 
1111
          /* Update the remaining windows to only those at a higher
 
1112
           * stacking position than this one.
 
1113
           */
 
1114
          while (rem_win_stacking && 
 
1115
                 stack_position >= GPOINTER_TO_INT (rem_win_stacking->data))
 
1116
            {
 
1117
              rem_windows      = rem_windows->next;
 
1118
              rem_win_stacking = rem_win_stacking->next;
 
1119
            }
 
1120
 
 
1121
          /* Remove edge portions overlapped by rem_windows and rem_docks */
 
1122
          new_edges = 
 
1123
            meta_rectangle_remove_intersections_with_boxes_from_edges (
 
1124
              new_edges,
 
1125
              rem_windows);
 
1126
 
 
1127
          /* Save the new edges */
 
1128
          edges = g_list_concat (new_edges, edges);
 
1129
        }
 
1130
 
 
1131
      stack_position++;
 
1132
      cur_window_iter = cur_window_iter->next;
 
1133
    }
 
1134
 
 
1135
  /*
 
1136
   * 4th: Free the extra memory not needed and sort the list
 
1137
   */
 
1138
  /* Free the memory used by the obscuring windows/docks lists */
 
1139
  g_slist_free (window_stacking);
 
1140
  /* FIXME: Shouldn't there be a helper function to make this one line of code
 
1141
   * to free a list instead of four ugly ones?
 
1142
   */
 
1143
  g_slist_foreach (obscuring_windows, 
 
1144
                   (void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */
 
1145
                   NULL);
 
1146
  g_slist_free (obscuring_windows);
 
1147
 
 
1148
  /* Sort the list.  FIXME: Should I bother with this sorting?  I just
 
1149
   * sort again later in cache_edges() anyway...
 
1150
   */
 
1151
  edges = g_list_sort (edges, meta_rectangle_edge_cmp);
 
1152
 
 
1153
  /*
 
1154
   * 5th: Cache the combination of these edges with the onscreen and
 
1155
   * xinerama edges in an array for quick access.  Free the edges since
 
1156
   * they've been cached elsewhere.
 
1157
   */
 
1158
  cache_edges (display,
 
1159
               edges,
 
1160
               display->grab_screen->active_workspace->xinerama_edges,
 
1161
               display->grab_screen->active_workspace->screen_edges);
 
1162
  g_list_free (edges);
 
1163
 
 
1164
  /*
 
1165
   * 6th: Initialize the resistance timeouts and buildups
 
1166
   */
 
1167
  initialize_grab_edge_resistance_data (display);
 
1168
}
 
1169
 
 
1170
/* Note that old_[xy] and new_[xy] are with respect to inner positions of
 
1171
 * the window.
 
1172
 */
 
1173
void
 
1174
meta_window_edge_resistance_for_move (MetaWindow  *window,
 
1175
                                      int          old_x,
 
1176
                                      int          old_y,
 
1177
                                      int         *new_x,
 
1178
                                      int         *new_y,
 
1179
                                      GSourceFunc  timeout_func,
 
1180
                                      gboolean     snap,
 
1181
                                      gboolean     is_keyboard_op)
 
1182
{
 
1183
  MetaRectangle old_outer, proposed_outer, new_outer;
 
1184
 
 
1185
  if (window == window->display->grab_window &&
 
1186
      window->display->grab_wireframe_active)
 
1187
    {
 
1188
      meta_window_get_xor_rect (window,
 
1189
                                &window->display->grab_wireframe_rect,
 
1190
                                &old_outer);
 
1191
    }
 
1192
  else
 
1193
    {
 
1194
      meta_window_get_outer_rect (window, &old_outer);
 
1195
    }
 
1196
  proposed_outer = old_outer;
 
1197
  proposed_outer.x += (*new_x - old_x);
 
1198
  proposed_outer.y += (*new_y - old_y);
 
1199
  new_outer = proposed_outer;
 
1200
 
 
1201
  window->display->grab_last_user_action_was_snap = snap;
 
1202
  if (apply_edge_resistance_to_each_side (window->display,
 
1203
                                          window,
 
1204
                                          &old_outer,
 
1205
                                          &new_outer,
 
1206
                                          timeout_func,
 
1207
                                          snap,
 
1208
                                          is_keyboard_op))
 
1209
    {
 
1210
      /* apply_edge_resistance_to_each_side independently applies
 
1211
       * resistance to both the right and left edges of new_outer as both
 
1212
       * could meet areas of resistance.  But we don't want a resize, so we
 
1213
       * just have both edges move according to the stricter of the
 
1214
       * resistances.  Same thing goes for top & bottom edges.
 
1215
       */
 
1216
      MetaRectangle *reference;
 
1217
      int left_change, right_change, smaller_x_change;
 
1218
      int top_change, bottom_change, smaller_y_change;
 
1219
 
 
1220
      if (snap && !is_keyboard_op)
 
1221
        reference = &proposed_outer;
 
1222
      else
 
1223
        reference = &old_outer;
 
1224
 
 
1225
      left_change  = BOX_LEFT (new_outer)  - BOX_LEFT (*reference);
 
1226
      right_change = BOX_RIGHT (new_outer) - BOX_RIGHT (*reference);
 
1227
      if (     snap && is_keyboard_op && left_change == 0)
 
1228
        smaller_x_change = right_change;
 
1229
      else if (snap && is_keyboard_op && right_change == 0)
 
1230
        smaller_x_change = left_change;
 
1231
      else if (ABS (left_change) < ABS (right_change))
 
1232
        smaller_x_change = left_change;
 
1233
      else
 
1234
        smaller_x_change = right_change;
 
1235
 
 
1236
      top_change    = BOX_TOP (new_outer)    - BOX_TOP (*reference);
 
1237
      bottom_change = BOX_BOTTOM (new_outer) - BOX_BOTTOM (*reference);
 
1238
      if (     snap && is_keyboard_op && top_change == 0)
 
1239
        smaller_y_change = bottom_change;
 
1240
      else if (snap && is_keyboard_op && bottom_change == 0)
 
1241
        smaller_y_change = top_change;
 
1242
      else if (ABS (top_change) < ABS (bottom_change))
 
1243
        smaller_y_change = top_change;
 
1244
      else
 
1245
        smaller_y_change = bottom_change;
 
1246
 
 
1247
      *new_x = old_x + smaller_x_change + 
 
1248
              (BOX_LEFT (*reference) - BOX_LEFT (old_outer));
 
1249
      *new_y = old_y + smaller_y_change +
 
1250
              (BOX_TOP (*reference) - BOX_TOP (old_outer));
 
1251
    }
 
1252
}
 
1253
 
 
1254
/* Note that old_(width|height) and new_(width|height) are with respect to
 
1255
 * sizes of the inner window.
 
1256
 */
 
1257
void
 
1258
meta_window_edge_resistance_for_resize (MetaWindow  *window,
 
1259
                                        int          old_width,
 
1260
                                        int          old_height,
 
1261
                                        int         *new_width,
 
1262
                                        int         *new_height,
 
1263
                                        int          gravity,
 
1264
                                        GSourceFunc  timeout_func,
 
1265
                                        gboolean     snap,
 
1266
                                        gboolean     is_keyboard_op)
 
1267
{
 
1268
  MetaRectangle old_outer, new_outer;
 
1269
  int new_outer_width, new_outer_height;
 
1270
 
 
1271
  if (window == window->display->grab_window &&
 
1272
      window->display->grab_wireframe_active)
 
1273
    {
 
1274
      meta_window_get_xor_rect (window,
 
1275
                                &window->display->grab_wireframe_rect,
 
1276
                                &old_outer);
 
1277
    }
 
1278
  else
 
1279
    {
 
1280
      meta_window_get_outer_rect (window, &old_outer);
 
1281
    }
 
1282
  new_outer_width  = old_outer.width  + (*new_width  - old_width);
 
1283
  new_outer_height = old_outer.height + (*new_height - old_height);
 
1284
  meta_rectangle_resize_with_gravity (&old_outer, 
 
1285
                                      &new_outer,
 
1286
                                      gravity,
 
1287
                                      new_outer_width,
 
1288
                                      new_outer_height);
 
1289
 
 
1290
  window->display->grab_last_user_action_was_snap = snap;
 
1291
  if (apply_edge_resistance_to_each_side (window->display,
 
1292
                                          window,
 
1293
                                          &old_outer,
 
1294
                                          &new_outer,
 
1295
                                          timeout_func,
 
1296
                                          snap,
 
1297
                                          is_keyboard_op))
 
1298
    {
 
1299
      *new_width  = old_width  + (new_outer.width  - old_outer.width);
 
1300
      *new_height = old_height + (new_outer.height - old_outer.height);
 
1301
    }
 
1302
}