1
/* Edge resistance for move/resize operations */
4
* Copyright (C) 2005 Elijah Newren
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.
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.
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
22
#include "edge-resistance.h"
25
#include "workspace.h"
27
/* A simple macro for whether a given window's edges are potentially
28
* relevant for resistance/snapping during a move/resize operation
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
38
struct ResistanceDataForAnEdge
40
gboolean timeout_setup;
43
gboolean timeout_over;
44
GSourceFunc timeout_func;
47
gboolean allow_past_screen_edge;
49
typedef struct ResistanceDataForAnEdge ResistanceDataForAnEdge;
51
struct MetaEdgeResistanceData
58
ResistanceDataForAnEdge left_data;
59
ResistanceDataForAnEdge right_data;
60
ResistanceDataForAnEdge top_data;
61
ResistanceDataForAnEdge bottom_data;
65
find_index_of_edge_near_position (const GArray *edges,
67
gboolean want_interval_min,
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
89
/* Initialize mid, edge, & compare in the off change that the array only
93
edge = g_array_index (edges, MetaEdge*, mid);
94
compare = horizontal ? edge->rect.x : edge->rect.y;
96
/* Begin the search... */
98
high = edges->len - 1;
101
mid = low + (high - low)/2;
102
edge = g_array_index (edges, MetaEdge*, mid);
103
compare = horizontal ? edge->rect.x : edge->rect.y;
105
if (compare == position)
108
if (compare > position)
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.
121
if (want_interval_min)
123
while (compare >= position && mid > 0)
126
edge = g_array_index (edges, MetaEdge*, mid);
127
compare = horizontal ? edge->rect.x : edge->rect.y;
129
while (compare < position && mid < (int)edges->len - 1)
132
edge = g_array_index (edges, MetaEdge*, mid);
133
compare = horizontal ? edge->rect.x : edge->rect.y;
136
/* Special case for no values in array big enough */
137
if (compare < position)
140
/* Return the found value */
145
while (compare <= position && mid < (int)edges->len - 1)
148
edge = g_array_index (edges, MetaEdge*, mid);
149
compare = horizontal ? edge->rect.x : edge->rect.y;
151
while (compare > position && mid > 0)
154
edge = g_array_index (edges, MetaEdge*, mid);
155
compare = horizontal ? edge->rect.x : edge->rect.y;
158
/* Special case for no values in array small enough */
159
if (compare > position)
162
/* Return the found value */
168
points_on_same_side (int ref, int pt1, int pt2)
170
return (pt1 - ref) * (pt2 - ref) > 0;
174
find_nearest_position (const GArray *edges,
177
const MetaRectangle *new_rect,
179
gboolean only_forward)
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.
189
int best, best_dist, i;
191
/* Initialize mid, edge, & compare in the off change that the array only
195
edge = g_array_index (edges, MetaEdge*, mid);
196
compare = horizontal ? edge->rect.x : edge->rect.y;
198
/* Begin the search... */
200
high = edges->len - 1;
203
mid = low + (high - low)/2;
204
edge = g_array_index (edges, MetaEdge*, mid);
205
compare = horizontal ? edge->rect.x : edge->rect.y;
207
if (compare == position)
210
if (compare > position)
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.
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);
230
(!only_forward || !points_on_same_side (position, compare, old_position)))
232
int dist = ABS (compare - position);
233
if (dist < best_dist)
240
/* Now start searching higher than mid */
241
for (i = mid + 1; i < (int)edges->len; i++)
243
edge = g_array_index (edges, MetaEdge*, i);
244
compare = horizontal ? edge->rect.x : edge->rect.y;
246
gboolean edges_align = horizontal ?
247
meta_rectangle_vert_overlap (&edge->rect, new_rect) :
248
meta_rectangle_horiz_overlap (&edge->rect, new_rect);
252
!points_on_same_side (position, compare, old_position)))
254
int dist = ABS (compare - position);
255
if (dist < best_dist)
264
/* Now start searching lower than mid */
265
for (i = mid-1; i >= 0; i--)
267
edge = g_array_index (edges, MetaEdge*, i);
268
compare = horizontal ? edge->rect.x : edge->rect.y;
270
gboolean edges_align = horizontal ?
271
meta_rectangle_vert_overlap (&edge->rect, new_rect) :
272
meta_rectangle_horiz_overlap (&edge->rect, new_rect);
276
!points_on_same_side (position, compare, old_position)))
278
int dist = ABS (compare - position);
279
if (dist < best_dist)
288
/* Return the best one found */
293
movement_towards_edge (MetaDirection side, int increment)
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;
305
g_assert_not_reached ();
309
edge_resistance_timeout (gpointer data)
311
ResistanceDataForAnEdge *resistance_data = data;
313
resistance_data->timeout_over = TRUE;
314
resistance_data->timeout_id = 0;
315
(*resistance_data->timeout_func)(resistance_data->window);
321
apply_edge_resistance (MetaWindow *window,
324
const MetaRectangle *new_rect,
326
ResistanceDataForAnEdge *resistance_data,
327
GSourceFunc timeout_func,
329
gboolean keyboard_op)
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;
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;
353
/* Quit if no movement was specified */
354
if (old_pos == new_pos)
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)))
364
resistance_data->timeout_setup = FALSE;
365
if (resistance_data->timeout_id != 0)
367
g_source_remove (resistance_data->timeout_id);
368
resistance_data->timeout_id = 0;
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);
376
/* Loop over all these edges we're moving past/to. */
378
while ((increasing && i <= end) ||
379
(!increasing && i >= end))
381
gboolean edges_align;
382
MetaEdge *edge = g_array_index (edges, MetaEdge*, i);
383
int compare = xdir ? edge->rect.x : edge->rect.y;
385
/* Find out if this edge is relevant */
387
meta_rectangle_vert_overlap (&edge->rect, new_rect) :
388
meta_rectangle_horiz_overlap (&edge->rect, new_rect);
390
/* Nothing to do unless the edges align */
393
/* Go to the next edge in the range */
398
/* Rest is easier to read if we split on keyboard vs. mouse op */
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.
408
/* First, determine the amount of the resistance */
410
switch (edge->edge_type)
412
case META_EDGE_WINDOW:
413
if (movement_towards_edge (edge->side_type, increment))
414
resistance = KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_WINDOW;
416
resistance = KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_WINDOW;
418
case META_EDGE_XINERAMA:
419
if (movement_towards_edge (edge->side_type, increment))
420
resistance = KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_XINERAMA;
422
resistance = KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_XINERAMA;
424
case META_EDGE_SCREEN:
425
if (movement_towards_edge (edge->side_type, increment))
426
resistance = KEYBOARD_BUILDUP_THRESHOLD_TOWARDS_SCREEN;
428
resistance = KEYBOARD_BUILDUP_THRESHOLD_AWAYFROM_SCREEN;
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.
436
if (okay_to_clear_keyboard_buildup &&
437
compare != keyboard_buildup_edge)
439
okay_to_clear_keyboard_buildup = FALSE;
440
resistance_data->keyboard_buildup = 0;
443
/* Determine the threshold */
444
int threshold = resistance - resistance_data->keyboard_buildup;
446
/* See if threshold hasn't been met yet or not */
447
if (ABS (compare - new_pos) < threshold)
449
if (resistance_data->keyboard_buildup != 0)
450
resistance_data->keyboard_buildup += ABS (new_pos - compare);
452
resistance_data->keyboard_buildup = 1; /* 0 causes stuckage */
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
463
okay_to_clear_keyboard_buildup = TRUE;
464
keyboard_buildup_edge = compare;
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.
474
if (edge->edge_type == META_EDGE_SCREEN &&
475
!resistance_data->allow_past_screen_edge &&
476
movement_towards_edge (edge->side_type, increment))
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.
485
if (movement_towards_edge (edge->side_type, increment))
487
/* First, determine the length of time for the resistance */
488
int timeout_length_ms = 0;
489
switch (edge->edge_type)
491
case META_EDGE_WINDOW:
492
timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW;
494
case META_EDGE_XINERAMA:
495
if (window->require_on_single_xinerama)
496
timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_XINERAMA;
498
case META_EDGE_SCREEN:
499
if (window->require_fully_onscreen)
500
timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN;
504
if (!resistance_data->timeout_setup &&
505
timeout_length_ms != 0)
507
resistance_data->timeout_id =
508
g_timeout_add (timeout_length_ms,
509
edge_resistance_timeout,
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;
517
if (!resistance_data->timeout_over &&
518
timeout_length_ms != 0)
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)
531
/* First, determine the threshold */
533
switch (edge->edge_type)
535
case META_EDGE_WINDOW:
536
if (movement_towards_edge (edge->side_type, increment))
537
threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW;
539
threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW;
541
case META_EDGE_XINERAMA:
542
if (movement_towards_edge (edge->side_type, increment))
543
threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_XINERAMA;
545
threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_XINERAMA;
547
case META_EDGE_SCREEN:
548
if (movement_towards_edge (edge->side_type, increment))
549
threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN;
551
threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN;
555
if (ABS (compare - new_pos) < threshold)
559
/* Go to the next edge in the range */
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
566
if (okay_to_clear_keyboard_buildup && new_pos != keyboard_buildup_edge)
567
resistance_data->keyboard_buildup = 0;
573
apply_edge_snapping (int old_pos,
575
const MetaRectangle *new_rect,
579
gboolean keyboard_op)
584
if (old_pos == new_pos)
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.
591
pos1 = find_nearest_position (edges1,
597
pos2 = find_nearest_position (edges2,
604
/* For keyboard snapping, ignore either pos1 or pos2 if they aren't in the
609
if (!points_on_same_side (old_pos, pos1, new_pos))
611
if (!points_on_same_side (old_pos, pos2, new_pos))
615
/* Find the better of pos1 and pos2 and return it */
616
if (ABS (pos1 - new_pos) < ABS (pos2 - new_pos))
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
627
ABS (best - old_pos) >= 8 &&
628
ABS (new_pos - old_pos) < 8)
631
/* Otherwise, return the best of the snapping positions found */
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.
640
* display->grab_edge_resistance_data MUST already be setup or calling this
641
* function will cause a crash.
644
apply_edge_resistance_to_each_side (MetaDisplay *display,
646
const MetaRectangle *old_outer,
647
MetaRectangle *new_outer,
648
GSourceFunc timeout_func,
650
gboolean keyboard_op)
652
MetaEdgeResistanceData *edge_data;
653
MetaRectangle modified_rect;
655
int new_left, new_right, new_top, new_bottom;
657
g_assert (display->grab_edge_resistance_data != NULL);
658
edge_data = display->grab_edge_resistance_data;
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.
667
new_left = apply_edge_snapping (BOX_LEFT (*old_outer),
668
BOX_LEFT (*new_outer),
670
edge_data->left_edges,
671
edge_data->right_edges,
675
new_right = apply_edge_snapping (BOX_RIGHT (*old_outer),
676
BOX_RIGHT (*new_outer),
678
edge_data->left_edges,
679
edge_data->right_edges,
683
new_top = apply_edge_snapping (BOX_TOP (*old_outer),
684
BOX_TOP (*new_outer),
686
edge_data->top_edges,
687
edge_data->bottom_edges,
691
new_bottom = apply_edge_snapping (BOX_BOTTOM (*old_outer),
692
BOX_BOTTOM (*new_outer),
694
edge_data->top_edges,
695
edge_data->bottom_edges,
701
/* Now, apply the normal edge resistance */
702
new_left = apply_edge_resistance (window,
703
BOX_LEFT (*old_outer),
704
BOX_LEFT (*new_outer),
706
edge_data->left_edges,
707
&edge_data->left_data,
711
new_right = apply_edge_resistance (window,
712
BOX_RIGHT (*old_outer),
713
BOX_RIGHT (*new_outer),
715
edge_data->right_edges,
716
&edge_data->right_data,
720
new_top = apply_edge_resistance (window,
721
BOX_TOP (*old_outer),
722
BOX_TOP (*new_outer),
724
edge_data->top_edges,
725
&edge_data->top_data,
729
new_bottom = apply_edge_resistance (window,
730
BOX_BOTTOM (*old_outer),
731
BOX_BOTTOM (*new_outer),
733
edge_data->bottom_edges,
734
&edge_data->bottom_data,
740
/* Determine whether anything changed, and save the changes */
741
modified_rect = meta_rect (new_left,
743
new_right - new_left,
744
new_bottom - new_top);
745
modified = !meta_rectangle_equal (new_outer, &modified_rect);
746
*new_outer = modified_rect;
751
meta_display_cleanup_edges (MetaDisplay *display)
753
MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
754
g_assert (edge_data != NULL);
757
/* We first need to clean out any window edges */
758
for (i = 0; i < 4; i++)
764
tmp = edge_data->left_edges;
767
tmp = edge_data->right_edges;
770
tmp = edge_data->top_edges;
773
tmp = edge_data->bottom_edges;
776
g_assert_not_reached ();
779
for (j = 0; j < tmp->len; j++)
781
MetaEdge *edge = g_array_index (tmp, MetaEdge*, j);
782
if (edge->edge_type == META_EDGE_WINDOW)
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);
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);
807
g_free (display->grab_edge_resistance_data);
808
display->grab_edge_resistance_data = NULL;
812
stupid_sort_requiring_extra_pointer_dereference (gconstpointer a,
815
const MetaEdge * const *a_edge = a;
816
const MetaEdge * const *b_edge = b;
817
return meta_rectangle_edge_cmp (*a_edge, *b_edge);
821
cache_edges (MetaDisplay *display,
823
GList *xinerama_edges,
826
MetaEdgeResistanceData *edge_data;
828
int num_left, num_right, num_top, num_bottom;
832
* 1st: Get the total number of each kind of edge
834
num_left = num_right = num_top = num_bottom = 0;
835
for (i = 0; i < 3; i++)
844
tmp = xinerama_edges;
850
g_assert_not_reached ();
855
MetaEdge *edge = tmp->data;
856
switch (edge->side_type)
858
case META_DIRECTION_LEFT:
861
case META_DIRECTION_RIGHT:
864
case META_DIRECTION_TOP:
867
case META_DIRECTION_BOTTOM:
871
g_assert_not_reached ();
878
* 2nd: Allocate the edges
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,
887
edge_data->right_edges = g_array_sized_new (FALSE,
891
edge_data->top_edges = g_array_sized_new (FALSE,
895
edge_data->bottom_edges = g_array_sized_new (FALSE,
901
* 3rd: Add the edges to the arrays
903
num_left = num_right = num_top = num_bottom = 0;
904
for (i = 0; i < 3; i++)
913
tmp = xinerama_edges;
919
g_assert_not_reached ();
924
MetaEdge *edge = tmp->data;
925
switch (edge->side_type)
927
case META_DIRECTION_LEFT:
928
g_array_append_val (edge_data->left_edges, edge);
930
case META_DIRECTION_RIGHT:
931
g_array_append_val (edge_data->right_edges, edge);
933
case META_DIRECTION_TOP:
934
g_array_append_val (edge_data->top_edges, edge);
936
case META_DIRECTION_BOTTOM:
937
g_array_append_val (edge_data->bottom_edges, edge);
940
g_assert_not_reached ();
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).
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);
963
initialize_grab_edge_resistance_data (MetaDisplay *display)
965
MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
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;
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;
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;
985
meta_display_compute_resistance_and_snapping_edges (MetaDisplay *display)
987
GList *stacked_windows;
988
GList *cur_window_iter;
990
/* Lists of window positions (rects) and their relative stacking positions */
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
996
GSList *rem_windows, *rem_win_stacking;
999
* 1st: Get the list of relevant windows, from bottom to top
1002
meta_stack_list_windows (display->grab_screen->stack,
1003
display->grab_screen->active_workspace);
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.
1011
obscuring_windows = window_stacking = NULL;
1012
cur_window_iter = stacked_windows;
1014
while (cur_window_iter != NULL)
1016
MetaWindow *cur_window = cur_window_iter->data;
1017
if (WINDOW_EDGES_RELEVANT (cur_window, display))
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);
1024
g_slist_prepend (window_stacking, GINT_TO_POINTER (stack_position));
1028
cur_window_iter = cur_window_iter->next;
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);
1035
* 3rd: loop over the windows again, this time getting the edges from
1036
* them and removing intersections with the relevant obscuring_windows &
1041
cur_window_iter = stacked_windows;
1042
while (cur_window_iter != NULL)
1044
MetaRectangle cur_rect;
1045
MetaWindow *cur_window = cur_window_iter->data;
1046
meta_window_get_outer_rect (cur_window, &cur_rect);
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
1052
if (WINDOW_EDGES_RELEVANT (cur_window, display) &&
1053
cur_window->type != META_WINDOW_DOCK)
1057
MetaRectangle reduced;
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).
1063
meta_rectangle_intersect (&cur_rect,
1064
&display->grab_screen->rect,
1069
/* Left side of this window is resistance for the right edge of
1070
* the window being moved.
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);
1079
/* Right side of this window is resistance for the left edge of
1080
* the window being moved.
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);
1090
/* Top side of this window is resistance for the bottom edge of
1091
* the window being moved.
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);
1100
/* Top side of this window is resistance for the bottom edge of
1101
* the window being moved.
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);
1111
/* Update the remaining windows to only those at a higher
1112
* stacking position than this one.
1114
while (rem_win_stacking &&
1115
stack_position >= GPOINTER_TO_INT (rem_win_stacking->data))
1117
rem_windows = rem_windows->next;
1118
rem_win_stacking = rem_win_stacking->next;
1121
/* Remove edge portions overlapped by rem_windows and rem_docks */
1123
meta_rectangle_remove_intersections_with_boxes_from_edges (
1127
/* Save the new edges */
1128
edges = g_list_concat (new_edges, edges);
1132
cur_window_iter = cur_window_iter->next;
1136
* 4th: Free the extra memory not needed and sort the list
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?
1143
g_slist_foreach (obscuring_windows,
1144
(void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */
1146
g_slist_free (obscuring_windows);
1148
/* Sort the list. FIXME: Should I bother with this sorting? I just
1149
* sort again later in cache_edges() anyway...
1151
edges = g_list_sort (edges, meta_rectangle_edge_cmp);
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.
1158
cache_edges (display,
1160
display->grab_screen->active_workspace->xinerama_edges,
1161
display->grab_screen->active_workspace->screen_edges);
1162
g_list_free (edges);
1165
* 6th: Initialize the resistance timeouts and buildups
1167
initialize_grab_edge_resistance_data (display);
1170
/* Note that old_[xy] and new_[xy] are with respect to inner positions of
1174
meta_window_edge_resistance_for_move (MetaWindow *window,
1179
GSourceFunc timeout_func,
1181
gboolean is_keyboard_op)
1183
MetaRectangle old_outer, proposed_outer, new_outer;
1185
if (window == window->display->grab_window &&
1186
window->display->grab_wireframe_active)
1188
meta_window_get_xor_rect (window,
1189
&window->display->grab_wireframe_rect,
1194
meta_window_get_outer_rect (window, &old_outer);
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;
1201
window->display->grab_last_user_action_was_snap = snap;
1202
if (apply_edge_resistance_to_each_side (window->display,
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.
1216
MetaRectangle *reference;
1217
int left_change, right_change, smaller_x_change;
1218
int top_change, bottom_change, smaller_y_change;
1220
if (snap && !is_keyboard_op)
1221
reference = &proposed_outer;
1223
reference = &old_outer;
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;
1234
smaller_x_change = right_change;
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;
1245
smaller_y_change = bottom_change;
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));
1254
/* Note that old_(width|height) and new_(width|height) are with respect to
1255
* sizes of the inner window.
1258
meta_window_edge_resistance_for_resize (MetaWindow *window,
1264
GSourceFunc timeout_func,
1266
gboolean is_keyboard_op)
1268
MetaRectangle old_outer, new_outer;
1269
int new_outer_width, new_outer_height;
1271
if (window == window->display->grab_window &&
1272
window->display->grab_wireframe_active)
1274
meta_window_get_xor_rect (window,
1275
&window->display->grab_wireframe_rect,
1280
meta_window_get_outer_rect (window, &old_outer);
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,
1290
window->display->grab_last_user_action_was_snap = snap;
1291
if (apply_edge_resistance_to_each_side (window->display,
1299
*new_width = old_width + (new_outer.width - old_outer.width);
1300
*new_height = old_height + (new_outer.height - old_outer.height);