1
/* Simple box operations */
4
* Copyright (C) 2005 Elijah Newren
5
* [meta_rectangle_intersect() is copyright the GTK+ Team according to Havoc,
6
* see gdkrectangle.c. As far as Havoc knows, he probably wrote
7
* meta_rectangle_equal(), and I'm guessing it's (C) Red Hat. So...]
8
* Copyright (C) 1995-2000 GTK+ Team
9
* Copyright (C) 2002 Red Hat, Inc.
11
* This program is free software; you can redistribute it and/or
12
* modify it under the terms of the GNU General Public License as
13
* published by the Free Software Foundation; either version 2 of the
14
* License, or (at your option) any later version.
16
* This program is distributed in the hope that it will be useful, but
17
* WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
* General Public License for more details.
21
* You should have received a copy of the GNU General Public License
22
* along with this program; if not, write to the Free Software
23
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
29
#include <X11/Xutil.h> /* Just for the definition of the various gravities */
30
#include <stdio.h> /* For snprintf */
33
meta_rectangle_to_string (const MetaRectangle *rect,
36
/* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit.
37
* Should be more than enough space. Note that of this space, the
38
* trailing \0 will be overwritten for all but the last rectangle.
40
g_snprintf (output, RECT_LENGTH, "%d,%d +%d,%d",
41
rect->x, rect->y, rect->width, rect->height);
47
meta_rectangle_region_to_string (GList *region,
48
const char *separator_string,
51
/* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5
52
* for each digit. Should be more than enough space. Note that of this
53
* space, the trailing \0 will be overwritten for all but the last
56
char rect_string[RECT_LENGTH];
59
snprintf (output, 10, "(EMPTY)");
65
MetaRectangle *rect = tmp->data;
66
g_snprintf (rect_string, RECT_LENGTH, "[%d,%d +%d,%d]",
67
rect->x, rect->y, rect->width, rect->height);
68
cur = g_stpcpy (cur, rect_string);
71
cur = g_stpcpy (cur, separator_string);
78
meta_rectangle_edge_to_string (const MetaEdge *edge,
81
/* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit.
82
* Should be more than enough space. Note that of this space, the
83
* trailing \0 will be overwritten for all but the last rectangle.
85
* Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and
86
* 2 more spaces, for a total of 10 more.
88
g_snprintf (output, EDGE_LENGTH, "[%d,%d +%d,%d], %2d, %2d",
89
edge->rect.x, edge->rect.y, edge->rect.width, edge->rect.height,
90
edge->side_type, edge->edge_type);
96
meta_rectangle_edge_list_to_string (GList *edge_list,
97
const char *separator_string,
100
/* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5 for
101
* each digit. Should be more than enough space. Note that of this
102
* space, the trailing \0 will be overwritten for all but the last
105
* Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and
106
* 2 more spaces, for a total of 10 more.
108
char rect_string[EDGE_LENGTH];
110
if (edge_list == NULL)
111
snprintf (output, 10, "(EMPTY)");
114
GList *tmp = edge_list;
117
MetaEdge *edge = tmp->data;
118
MetaRectangle *rect = &edge->rect;
119
g_snprintf (rect_string, EDGE_LENGTH, "([%d,%d +%d,%d], %2d, %2d)",
120
rect->x, rect->y, rect->width, rect->height,
121
edge->side_type, edge->edge_type);
122
cur = g_stpcpy (cur, rect_string);
125
cur = g_stpcpy (cur, separator_string);
132
meta_rect (int x, int y, int width, int height)
134
MetaRectangle temporary;
137
temporary.width = width;
138
temporary.height = height;
144
meta_rectangle_area (const MetaRectangle *rect)
146
g_return_val_if_fail (rect != NULL, 0);
147
return rect->width * rect->height;
151
meta_rectangle_intersect (const MetaRectangle *src1,
152
const MetaRectangle *src2,
159
g_return_val_if_fail (src1 != NULL, FALSE);
160
g_return_val_if_fail (src2 != NULL, FALSE);
161
g_return_val_if_fail (dest != NULL, FALSE);
165
dest_x = MAX (src1->x, src2->x);
166
dest_y = MAX (src1->y, src2->y);
167
dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x;
168
dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y;
170
if (dest_w > 0 && dest_h > 0)
174
dest->width = dest_w;
175
dest->height = dest_h;
188
meta_rectangle_equal (const MetaRectangle *src1,
189
const MetaRectangle *src2)
191
return ((src1->x == src2->x) &&
192
(src1->y == src2->y) &&
193
(src1->width == src2->width) &&
194
(src1->height == src2->height));
198
meta_rectangle_overlap (const MetaRectangle *rect1,
199
const MetaRectangle *rect2)
201
g_return_val_if_fail (rect1 != NULL, FALSE);
202
g_return_val_if_fail (rect2 != NULL, FALSE);
204
return !((rect1->x + rect1->width <= rect2->x) ||
205
(rect2->x + rect2->width <= rect1->x) ||
206
(rect1->y + rect1->height <= rect2->y) ||
207
(rect2->y + rect2->height <= rect1->y));
211
meta_rectangle_vert_overlap (const MetaRectangle *rect1,
212
const MetaRectangle *rect2)
214
return (rect1->y < rect2->y + rect2->height &&
215
rect2->y < rect1->y + rect1->height);
219
meta_rectangle_horiz_overlap (const MetaRectangle *rect1,
220
const MetaRectangle *rect2)
222
return (rect1->x < rect2->x + rect2->width &&
223
rect2->x < rect1->x + rect1->width);
227
meta_rectangle_could_fit_rect (const MetaRectangle *outer_rect,
228
const MetaRectangle *inner_rect)
230
return (outer_rect->width >= inner_rect->width &&
231
outer_rect->height >= inner_rect->height);
235
meta_rectangle_contains_rect (const MetaRectangle *outer_rect,
236
const MetaRectangle *inner_rect)
239
inner_rect->x >= outer_rect->x &&
240
inner_rect->y >= outer_rect->y &&
241
inner_rect->x + inner_rect->width <= outer_rect->x + outer_rect->width &&
242
inner_rect->y + inner_rect->height <= outer_rect->y + outer_rect->height;
246
meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect,
252
/* FIXME: I'm too deep into this to know whether the below comment is
253
* still clear or not now that I've moved it out of constraints.c.
254
* boxes.h has a good comment, but I'm not sure if the below info is also
255
* helpful on top of that (or whether it has superfluous info).
258
/* These formulas may look overly simplistic at first but you can work
259
* everything out with a left_frame_with, right_frame_width,
260
* border_width, and old and new client area widths (instead of old total
261
* width and new total width) and you come up with the same formulas.
263
* Also, note that the reason we can treat NorthWestGravity and
264
* StaticGravity the same is because we're not given a location at
265
* which to place the window--the window was already placed
266
* appropriately before. So, NorthWestGravity for this function
267
* means to just leave the upper left corner of the outer window
268
* where it already is, and StaticGravity for this function means to
269
* just leave the upper left corner of the inner window where it
270
* already is. But leaving either of those two corners where they
271
* already are will ensure that the other corner is fixed as well
272
* (since frame size doesn't change)--thus making the two
276
/* First, the x direction */
280
case NorthWestGravity:
282
case SouthWestGravity:
283
rect->x = old_rect->x;
289
/* FIXME: Needing to adjust new_width kind of sucks, but not doing so
292
new_width -= (old_rect->width - new_width) % 2;
293
rect->x = old_rect->x + (old_rect->width - new_width)/2;
296
case NorthEastGravity:
298
case SouthEastGravity:
299
rect->x = old_rect->x + (old_rect->width - new_width);
304
rect->x = old_rect->x;
307
rect->width = new_width;
309
/* Next, the y direction */
313
case NorthWestGravity:
315
case NorthEastGravity:
316
rect->y = old_rect->y;
322
/* FIXME: Needing to adjust new_height kind of sucks, but not doing so
325
new_height -= (old_rect->height - new_height) % 2;
326
rect->y = old_rect->y + (old_rect->height - new_height)/2;
329
case SouthWestGravity:
331
case SouthEastGravity:
332
rect->y = old_rect->y + (old_rect->height - new_height);
337
rect->y = old_rect->y;
340
rect->height = new_height;
343
/* Not so simple helper function for get_minimal_spanning_set_for_region() */
345
merge_spanning_rects_in_region (GList *region)
347
/* NOTE FOR ANY OPTIMIZATION PEOPLE OUT THERE: Please see the
348
* documentation of get_minimal_spanning_set_for_region() for performance
349
* considerations that also apply to this function.
357
meta_warning ("Region to merge was empty! Either you have a some "
358
"pathological STRUT list or there's a bug somewhere!\n");
362
while (compare && compare->next)
364
MetaRectangle *a = compare->data;
365
GList *other = compare->next;
367
g_assert (a->width > 0 && a->height > 0);
371
MetaRectangle *b = other->data;
372
GList *delete_me = NULL;
374
g_assert (b->width > 0 && b->height > 0);
376
/* If a contains b, just remove b */
377
if (meta_rectangle_contains_rect (a, b))
381
/* If b contains a, just remove a */
382
else if (meta_rectangle_contains_rect (a, b))
386
/* If a and b might be mergeable horizontally */
387
else if (a->y == b->y && a->height == b->height)
389
/* If a and b overlap */
390
if (meta_rectangle_overlap (a, b))
392
int new_x = MIN (a->x, b->x);
393
a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
397
/* If a and b are adjacent */
398
else if (a->x + a->width == b->x || a->x == b->x + b->width)
400
int new_x = MIN (a->x, b->x);
401
a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
406
/* If a and b might be mergeable vertically */
407
else if (a->x == b->x && a->width == b->width)
409
/* If a and b overlap */
410
if (meta_rectangle_overlap (a, b))
412
int new_y = MIN (a->y, b->y);
413
a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
417
/* If a and b are adjacent */
418
else if (a->y + a->height == b->y || a->y == b->y + b->height)
420
int new_y = MIN (a->y, b->y);
421
a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
429
/* Delete any rectangle in the list that is no longer wanted */
430
if (delete_me != NULL)
432
/* Deleting the rect we compare others to is a little tricker */
433
if (compare == delete_me)
435
compare = compare->next;
436
other = compare->next;
440
/* Okay, we can free it now */
441
g_free (delete_me->data);
442
region = g_list_delete_link (region, delete_me);
447
compare = compare->next;
453
/* Simple helper function for get_minimal_spanning_set_for_region()... */
455
compare_rect_areas (gconstpointer a, gconstpointer b)
457
const MetaRectangle *a_rect = (gconstpointer) a;
458
const MetaRectangle *b_rect = (gconstpointer) b;
460
int a_area = meta_rectangle_area (a_rect);
461
int b_area = meta_rectangle_area (b_rect);
463
return b_area - a_area; /* positive ret value denotes b > a, ... */
466
/* This function is trying to find a "minimal spanning set (of rectangles)"
467
* for a given region.
469
* The region is given by taking basic_rect, then removing the areas
470
* covered by all the rectangles in the all_struts list, and then expanding
471
* the resulting region by the given number of pixels in each direction.
473
* A "minimal spanning set (of rectangles)" is the best name I could come
474
* up with for the concept I had in mind. Basically, for a given region, I
475
* want a set of rectangles with the property that a window is contained in
476
* the region if and only if it is contained within at least one of the
479
* The GList* returned will be a list of (allocated) MetaRectangles.
480
* The list will need to be freed by calling
481
* meta_rectangle_free_spanning_set() on it (or by manually
482
* implementing that function...)
485
meta_rectangle_get_minimal_spanning_set_for_region (
486
const MetaRectangle *basic_rect,
487
const GSList *all_struts)
489
/* NOTE FOR OPTIMIZERS: This function *might* be somewhat slow,
490
* especially due to the call to merge_spanning_rects_in_region() (which
491
* is O(n^2) where n is the size of the list generated in this function).
492
* This is made more onerous due to the fact that it involves a fair
493
* number of memory allocation and deallocation calls. However, n is 1
494
* for default installations of Gnome (because partial struts aren't used
495
* by default and only partial struts increase the size of the spanning
496
* set generated). With one partial strut, n will be 2 or 3. With 2
497
* partial struts, n will probably be 4 or 5. So, n probably isn't large
498
* enough to make this worth bothering. Further, it is only called from
499
* workspace.c:ensure_work_areas_validated (at least as of the time of
500
* writing this comment), which in turn should only be called if the
501
* strut list changes or the screen or xinerama size changes. If it ever
502
* does show up on profiles (most likely because people start using
503
* ridiculously huge numbers of partial struts), possible optimizations
506
* (1) rewrite merge_spanning_rects_in_region() to be O(n) or O(nlogn).
507
* I'm not totally sure it's possible, but with a couple copies of
508
* the list and sorting them appropriately, I believe it might be.
509
* (2) only call merge_spanning_rects_in_region() with a subset of the
510
* full list of rectangles. I believe from some of my preliminary
511
* debugging and thinking about it that it is possible to figure out
512
* apriori groups of rectangles which are only merge candidates with
513
* each other. (See testboxes.c:get_screen_region() when which==2
514
* and track the steps of this function carefully to see what gave
515
* me the hint that this might work)
516
* (3) figure out how to avoid merge_spanning_rects_in_region(). I think
517
* it might be possible to modify this function to make that
518
* possible, and I spent just a little while thinking about it, but n
519
* wasn't large enough to convince me to care yet.
520
* (4) Some of the stuff Rob mentioned at http://mail.gnome.org/archives\
521
* /metacity-devel-list/2005-November/msg00028.html. (Sorry for the
527
const GSList *strut_iter;
528
MetaRectangle *temp_rect;
530
/* The algorithm is basically as follows:
531
* Initialize rectangle_set to basic_rect
533
* Foreach rectangle in rectangle_set:
534
* - Split the rectangle into new rectangles that don't overlap the
535
* strut (but which are as big as possible otherwise)
536
* - Remove the old (pre-split) rectangle from the rectangle_set,
537
* and replace it with the new rectangles generated from the
541
temp_rect = g_new (MetaRectangle, 1);
542
*temp_rect = *basic_rect;
543
ret = g_list_prepend (NULL, temp_rect);
545
strut_iter = all_struts;
549
MetaRectangle *strut = (MetaRectangle*) strut_iter->data;
553
rect_iter = tmp_list;
556
MetaRectangle *rect = (MetaRectangle*) rect_iter->data;
557
if (!meta_rectangle_overlap (rect, strut))
558
ret = g_list_prepend (ret, rect);
561
/* If there is area in rect left of strut */
562
if (rect->x < strut->x)
564
temp_rect = g_new (MetaRectangle, 1);
566
temp_rect->width = strut->x - rect->x;
567
ret = g_list_prepend (ret, temp_rect);
569
/* If there is area in rect right of strut */
570
if (rect->x + rect->width > strut->x + strut->width)
573
temp_rect = g_new (MetaRectangle, 1);
575
new_x = strut->x + strut->width;
576
temp_rect->width = rect->x + rect->width - new_x;
577
temp_rect->x = new_x;
578
ret = g_list_prepend (ret, temp_rect);
580
/* If there is area in rect above strut */
581
if (rect->y < strut->y)
583
temp_rect = g_new (MetaRectangle, 1);
585
temp_rect->height = strut->y - rect->y;
586
ret = g_list_prepend (ret, temp_rect);
588
/* If there is area in rect below strut */
589
if (rect->y + rect->height > strut->y + strut->height)
592
temp_rect = g_new (MetaRectangle, 1);
594
new_y = strut->y + strut->height;
595
temp_rect->height = rect->y + rect->height - new_y;
596
temp_rect->y = new_y;
597
ret = g_list_prepend (ret, temp_rect);
601
rect_iter = rect_iter->next;
603
g_list_free (tmp_list);
604
strut_iter = strut_iter->next;
607
/* Sort by maximal area, just because I feel like it... */
608
ret = g_list_sort (ret, compare_rect_areas);
610
/* Merge rectangles if possible so that the list really is minimal */
611
ret = merge_spanning_rects_in_region (ret);
617
meta_rectangle_expand_region (GList *region,
618
const int left_expand,
619
const int right_expand,
620
const int top_expand,
621
const int bottom_expand)
623
/* Now it's time to do the directional expansion */
624
GList *tmp_list = region;
627
MetaRectangle *rect = (MetaRectangle*) tmp_list->data;
628
rect->x -= left_expand;
629
rect->width += (left_expand + right_expand);
630
rect->y -= top_expand;
631
rect->height += (top_expand + bottom_expand);
632
tmp_list = tmp_list->next;
639
meta_rectangle_free_list_and_elements (GList *filled_list)
641
g_list_foreach (filled_list,
642
(void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */
644
g_list_free (filled_list);
648
meta_rectangle_could_fit_in_region (const GList *spanning_rects,
649
const MetaRectangle *rect)
654
temp = spanning_rects;
656
while (!could_fit && temp != NULL)
658
could_fit = could_fit || meta_rectangle_could_fit_rect (temp->data, rect);
666
meta_rectangle_contained_in_region (const GList *spanning_rects,
667
const MetaRectangle *rect)
672
temp = spanning_rects;
674
while (!contained && temp != NULL)
676
contained = contained || meta_rectangle_contains_rect (temp->data, rect);
684
meta_rectangle_clamp_to_fit_into_region (const GList *spanning_rects,
685
FixedDirections fixed_directions,
687
const MetaRectangle *min_size)
690
const MetaRectangle *best_rect = NULL;
691
int best_overlap = 0;
693
/* First, find best rectangle from spanning_rects to which we can clamp
696
temp = spanning_rects;
700
MetaRectangle *compare_rect = temp->data;
701
int maximal_overlap_amount_for_compare;
703
/* If x is fixed and the entire width of rect doesn't fit in compare, set
706
if ((fixed_directions & FIXED_DIRECTION_X) &&
707
(compare_rect->x > rect->x ||
708
compare_rect->x + compare_rect->width < rect->x + rect->width))
711
/* If y is fixed and the entire height of rect doesn't fit in compare, set
714
if ((fixed_directions & FIXED_DIRECTION_Y) &&
715
(compare_rect->y > rect->y ||
716
compare_rect->y + compare_rect->height < rect->y + rect->height))
719
/* If compare can't hold the min_size window, set factor to 0 */
720
if (compare_rect->width < min_size->width ||
721
compare_rect->height < min_size->height)
724
/* Determine maximal overlap amount */
725
maximal_overlap_amount_for_compare =
726
MIN (rect->width, compare_rect->width) *
727
MIN (rect->height, compare_rect->height);
728
maximal_overlap_amount_for_compare *= factor;
730
/* See if this is the best rect so far */
731
if (maximal_overlap_amount_for_compare > best_overlap)
733
best_rect = compare_rect;
734
best_overlap = maximal_overlap_amount_for_compare;
740
/* Clamp rect appropriately */
741
if (best_rect == NULL)
743
meta_warning ("No rect whose size to clamp to found!\n");
745
/* If it doesn't fit, at least make it no bigger than it has to be */
746
if (!(fixed_directions & FIXED_DIRECTION_X))
747
rect->width = min_size->width;
748
if (!(fixed_directions & FIXED_DIRECTION_Y))
749
rect->height = min_size->height;
753
rect->width = MIN (rect->width, best_rect->width);
754
rect->height = MIN (rect->height, best_rect->height);
759
meta_rectangle_clip_to_region (const GList *spanning_rects,
760
FixedDirections fixed_directions,
764
const MetaRectangle *best_rect = NULL;
765
int best_overlap = 0;
767
/* First, find best rectangle from spanning_rects to which we will clip
770
temp = spanning_rects;
774
MetaRectangle *compare_rect = temp->data;
775
MetaRectangle overlap;
776
int maximal_overlap_amount_for_compare;
778
/* If x is fixed and the entire width of rect doesn't fit in compare, set
781
if ((fixed_directions & FIXED_DIRECTION_X) &&
782
(compare_rect->x > rect->x ||
783
compare_rect->x + compare_rect->width < rect->x + rect->width))
786
/* If y is fixed and the entire height of rect doesn't fit in compare, set
789
if ((fixed_directions & FIXED_DIRECTION_Y) &&
790
(compare_rect->y > rect->y ||
791
compare_rect->y + compare_rect->height < rect->y + rect->height))
794
/* Determine maximal overlap amount */
795
meta_rectangle_intersect (rect, compare_rect, &overlap);
796
maximal_overlap_amount_for_compare = meta_rectangle_area (&overlap);
797
maximal_overlap_amount_for_compare *= factor;
799
/* See if this is the best rect so far */
800
if (maximal_overlap_amount_for_compare > best_overlap)
802
best_rect = compare_rect;
803
best_overlap = maximal_overlap_amount_for_compare;
809
/* Clip rect appropriately */
810
if (best_rect == NULL)
811
meta_warning ("No rect to clip to found!\n");
814
/* Extra precaution with checking fixed direction shouldn't be needed
815
* due to logic above, but it shouldn't hurt either.
817
if (!(fixed_directions & FIXED_DIRECTION_X))
819
/* Find the new left and right */
820
int new_x = MAX (rect->x, best_rect->x);
821
rect->width = MIN ((rect->x + rect->width) - new_x,
822
(best_rect->x + best_rect->width) - new_x);
826
/* Extra precaution with checking fixed direction shouldn't be needed
827
* due to logic above, but it shouldn't hurt either.
829
if (!(fixed_directions & FIXED_DIRECTION_Y))
831
/* Clip the top, if needed */
832
int new_y = MAX (rect->y, best_rect->y);
833
rect->height = MIN ((rect->y + rect->height) - new_y,
834
(best_rect->y + best_rect->height) - new_y);
841
meta_rectangle_shove_into_region (const GList *spanning_rects,
842
FixedDirections fixed_directions,
846
const MetaRectangle *best_rect = NULL;
847
int best_overlap = 0;
848
int shortest_distance = G_MAXINT;
850
/* First, find best rectangle from spanning_rects to which we will shove
853
temp = spanning_rects;
857
MetaRectangle *compare_rect = temp->data;
858
int maximal_overlap_amount_for_compare;
861
/* If x is fixed and the entire width of rect doesn't fit in compare, set
864
if ((fixed_directions & FIXED_DIRECTION_X) &&
865
(compare_rect->x > rect->x ||
866
compare_rect->x + compare_rect->width < rect->x + rect->width))
869
/* If y is fixed and the entire height of rect doesn't fit in compare, set
872
if ((fixed_directions & FIXED_DIRECTION_Y) &&
873
(compare_rect->y > rect->y ||
874
compare_rect->y + compare_rect->height < rect->y + rect->height))
877
/* Determine maximal overlap amount between rect & compare_rect */
878
maximal_overlap_amount_for_compare =
879
MIN (rect->width, compare_rect->width) *
880
MIN (rect->height, compare_rect->height);
882
/* Determine distance necessary to put rect into comapre_rect */
884
if (compare_rect->x > rect->x)
885
dist_to_compare += compare_rect->x - rect->x;
886
if (compare_rect->x + compare_rect->width < rect->x + rect->width)
887
dist_to_compare += (rect->x + rect->width) -
888
(compare_rect->x + compare_rect->width);
889
if (compare_rect->y > rect->y)
890
dist_to_compare += compare_rect->y - rect->y;
891
if (compare_rect->y + compare_rect->height < rect->y + rect->height)
892
dist_to_compare += (rect->y + rect->height) -
893
(compare_rect->y + compare_rect->height);
895
/* If we'd have to move in the wrong direction, disqualify compare_rect */
898
maximal_overlap_amount_for_compare = 0;
899
dist_to_compare = G_MAXINT;
902
/* See if this is the best rect so far */
903
if ((maximal_overlap_amount_for_compare > best_overlap) ||
904
(maximal_overlap_amount_for_compare == best_overlap &&
905
dist_to_compare < shortest_distance))
907
best_rect = compare_rect;
908
best_overlap = maximal_overlap_amount_for_compare;
909
shortest_distance = dist_to_compare;
915
/* Shove rect appropriately */
916
if (best_rect == NULL)
917
meta_warning ("No rect to shove into found!\n");
920
/* Extra precaution with checking fixed direction shouldn't be needed
921
* due to logic above, but it shouldn't hurt either.
923
if (!(fixed_directions & FIXED_DIRECTION_X))
925
/* Shove to the right, if needed */
926
if (best_rect->x > rect->x)
927
rect->x = best_rect->x;
929
/* Shove to the left, if needed */
930
if (best_rect->x + best_rect->width < rect->x + rect->width)
931
rect->x = (best_rect->x + best_rect->width) - rect->width;
934
/* Extra precaution with checking fixed direction shouldn't be needed
935
* due to logic above, but it shouldn't hurt either.
937
if (!(fixed_directions & FIXED_DIRECTION_Y))
939
/* Shove down, if needed */
940
if (best_rect->y > rect->y)
941
rect->y = best_rect->y;
943
/* Shove up, if needed */
944
if (best_rect->y + best_rect->height < rect->y + rect->height)
945
rect->y = (best_rect->y + best_rect->height) - rect->height;
951
meta_rectangle_find_linepoint_closest_to_point (double x1,
960
/* I'll use the shorthand rx, ry for the return values, valx & valy.
961
* Now, we need (rx,ry) to be on the line between (x1,y1) and (x2,y2).
962
* For that to happen, we first need the slope of the line from (x1,y1)
963
* to (rx,ry) must match the slope of (x1,y1) to (x2,y2), i.e.:
967
* If x1==x2, though, this gives divide by zero errors, so we want to
968
* rewrite the equation by multiplying both sides by (rx-x1)*(x2-x1):
969
* (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
970
* This is a valid requirement even when x1==x2 (when x1==x2, this latter
971
* equation will basically just mean that rx must be equal to both x1 and
974
* The other requirement that we have is that the line from (rx,ry) to
975
* (px,py) must be perpendicular to the line from (x1,y1) to (x2,y2). So
976
* we just need to get a vector in the direction of each line, take the
977
* dot product of the two, and ensure that the result is 0:
978
* (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
980
* This gives us two equations and two unknowns:
982
* (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
983
* (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
985
* This particular pair of equations is always solvable so long as
986
* (x1,y1) and (x2,y2) are not the same point (and note that anyone who
987
* calls this function that way is braindead because it means that they
988
* really didn't specify a line after all). However, the caller should
989
* be careful to avoid making (x1,y1) and (x2,y2) too close (e.g. like
990
* 10^{-8} apart in each coordinate), otherwise roundoff error could
991
* cause issues. Solving these equations by hand (or using Maple(TM) or
992
* Mathematica(TM) or whatever) results in slightly messy expressions,
993
* but that's all the below few lines do.
996
double diffx, diffy, den;
999
den = diffx * diffx + diffy * diffy;
1001
*valx = (py * diffx * diffy + px * diffx * diffx +
1002
y2 * x1 * diffy - y1 * x2 * diffy) / den;
1003
*valy = (px * diffx * diffy + py * diffy * diffy +
1004
x2 * y1 * diffx - x1 * y2 * diffx) / den;
1007
/***************************************************************************/
1009
/* Switching gears to code for edges instead of just rectangles */
1011
/***************************************************************************/
1014
get_rect_minus_overlap (const GList *rect_in_list,
1015
MetaRectangle *overlap)
1017
MetaRectangle *temp;
1018
MetaRectangle *rect = rect_in_list->data;
1021
if (BOX_LEFT (*rect) < BOX_LEFT (*overlap))
1023
temp = g_new (MetaRectangle, 1);
1025
temp->width = BOX_LEFT (*overlap) - BOX_LEFT (*rect);
1026
ret = g_list_prepend (ret, temp);
1028
if (BOX_RIGHT (*rect) > BOX_RIGHT (*overlap))
1030
temp = g_new (MetaRectangle, 1);
1032
temp->x = BOX_RIGHT (*overlap);
1033
temp->width = BOX_RIGHT (*rect) - BOX_RIGHT (*overlap);
1034
ret = g_list_prepend (ret, temp);
1036
if (BOX_TOP (*rect) < BOX_TOP (*overlap))
1038
temp = g_new (MetaRectangle, 1);
1039
temp->x = overlap->x;
1040
temp->width = overlap->width;
1041
temp->y = BOX_TOP (*rect);
1042
temp->height = BOX_TOP (*overlap) - BOX_TOP (*rect);
1043
ret = g_list_prepend (ret, temp);
1045
if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*overlap))
1047
temp = g_new (MetaRectangle, 1);
1048
temp->x = overlap->x;
1049
temp->width = overlap->width;
1050
temp->y = BOX_BOTTOM (*overlap);
1051
temp->height = BOX_BOTTOM (*rect) - BOX_BOTTOM (*overlap);
1052
ret = g_list_prepend (ret, temp);
1059
replace_rect_with_list (GList *old_element,
1063
g_assert (old_element != NULL);
1067
/* If there is no new list, just remove the old_element */
1068
ret = old_element->next;
1069
g_list_remove_link (old_element, old_element);
1073
/* Fix up the prev and next pointers everywhere */
1075
if (old_element->prev)
1077
old_element->prev->next = new_list;
1078
new_list->prev = old_element->prev;
1080
if (old_element->next)
1082
GList *tmp = g_list_last (new_list);
1083
old_element->next->prev = tmp;
1084
tmp->next = old_element->next;
1088
/* Free the old_element and return the appropriate "next" point */
1089
g_free (old_element->data);
1090
g_list_free_1 (old_element);
1094
/* Make a copy of the strut list, make sure that copy only contains parts
1095
* of the old_struts that intersect with the rection rect, and then do some
1096
* magic to make all the new struts disjoint (okay, we we break up struts
1097
* that aren't disjoint in a way that the overlapping part is only included
1098
* once, so it's not really magic...).
1101
get_disjoint_strut_list_in_region (const GSList *old_struts,
1102
const MetaRectangle *region)
1107
/* First, copy the list */
1111
MetaRectangle *cur = old_struts->data;
1112
MetaRectangle *copy = g_new (MetaRectangle, 1);
1114
if (meta_rectangle_intersect (copy, region, copy))
1115
struts = g_list_prepend (struts, copy);
1119
old_struts = old_struts->next;
1122
/* Now, loop over the list and check for intersections, fixing things up
1123
* where they do intersect.
1130
MetaRectangle *cur = tmp->data;
1132
compare = tmp->next;
1135
MetaRectangle *comp = compare->data;
1136
MetaRectangle overlap;
1138
if (meta_rectangle_intersect (cur, comp, &overlap))
1140
/* Get a list of rectangles for each strut that don't overlap
1141
* the intersection region.
1143
GList *cur_leftover = get_rect_minus_overlap (tmp, &overlap);
1144
GList *comp_leftover = get_rect_minus_overlap (compare, &overlap);
1146
/* Add the intersection region to cur_leftover */
1147
MetaRectangle *overlap_allocated = g_new (MetaRectangle, 1);
1148
*overlap_allocated = overlap;
1149
cur_leftover = g_list_prepend (cur_leftover, overlap_allocated);
1151
/* Fix up tmp, compare, and cur -- maybe struts too */
1154
struts = replace_rect_with_list (tmp, cur_leftover);
1158
tmp = replace_rect_with_list (tmp, cur_leftover);
1159
compare = replace_rect_with_list (compare, comp_leftover);
1161
if (compare == NULL)
1167
compare = compare->next;
1176
/* To make things easily testable, provide a nice way of sorting edges */
1178
meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b)
1180
const MetaEdge *a_edge_rect = (gconstpointer) a;
1181
const MetaEdge *b_edge_rect = (gconstpointer) b;
1183
int a_compare, b_compare;
1185
a_compare = a_edge_rect->side_type;
1186
b_compare = b_edge_rect->side_type;
1188
if (a_compare == b_compare)
1190
if (a_edge_rect->side_type == META_DIRECTION_LEFT ||
1191
a_edge_rect->side_type == META_DIRECTION_RIGHT)
1193
a_compare = a_edge_rect->rect.x;
1194
b_compare = b_edge_rect->rect.x;
1195
if (a_compare == b_compare)
1197
a_compare = a_edge_rect->rect.y;
1198
b_compare = b_edge_rect->rect.y;
1201
else if (a_edge_rect->side_type == META_DIRECTION_TOP ||
1202
a_edge_rect->side_type == META_DIRECTION_BOTTOM)
1204
a_compare = a_edge_rect->rect.y;
1205
b_compare = b_edge_rect->rect.y;
1206
if (a_compare == b_compare)
1208
a_compare = a_edge_rect->rect.x;
1209
b_compare = b_edge_rect->rect.x;
1213
g_assert ("Some idiot wanted to sort sides of different types.\n");
1216
return a_compare - b_compare; /* positive value denotes a > b ... */
1219
/* Determine whether two given edges overlap */
1221
edges_overlap (const MetaEdge *edge1,
1222
const MetaEdge *edge2)
1224
if (edge1->rect.width == 0 && edge2->rect.width == 0)
1226
return meta_rectangle_vert_overlap (&edge1->rect, &edge2->rect) &&
1227
edge1->rect.x == edge2->rect.x;
1229
else if (edge1->rect.height == 0 && edge2->rect.height == 0)
1231
return meta_rectangle_horiz_overlap (&edge1->rect, &edge2->rect) &&
1232
edge1->rect.y == edge2->rect.y;
1241
rectangle_and_edge_intersection (const MetaRectangle *rect,
1242
const MetaEdge *edge,
1246
const MetaRectangle *rect2 = &edge->rect;
1247
MetaRectangle *result = &overlap->rect;
1248
gboolean intersect = TRUE;
1250
/* We don't know how to set these, so set them to invalid values */
1251
overlap->edge_type = -1;
1252
overlap->side_type = -1;
1254
/* Figure out what the intersection is */
1255
result->x = MAX (rect->x, rect2->x);
1256
result->y = MAX (rect->y, rect2->y);
1257
result->width = MIN (BOX_RIGHT (*rect), BOX_RIGHT (*rect2)) - result->x;
1258
result->height = MIN (BOX_BOTTOM (*rect), BOX_BOTTOM (*rect2)) - result->y;
1260
/* Find out if the intersection is empty; have to do it this way since
1261
* edges have a thickness of 0
1263
if ((result->width < 0 || result->height < 0) ||
1264
(result->width == 0 && result->height == 0))
1272
/* Need to figure out the handle_type, a somewhat weird quantity:
1273
* 0 - overlap is in middle of rect
1274
* -1 - overlap is at the side of rect, and is on the opposite side
1275
* of rect than the edge->side_type side
1276
* 1 - overlap is at the side of rect, and the side of rect it is
1277
* on is the edge->side_type side
1279
switch (edge->side_type)
1281
case META_DIRECTION_LEFT:
1282
if (result->x == rect->x)
1284
else if (result->x == BOX_RIGHT (*rect))
1289
case META_DIRECTION_RIGHT:
1290
if (result->x == rect->x)
1292
else if (result->x == BOX_RIGHT (*rect))
1297
case META_DIRECTION_TOP:
1298
if (result->y == rect->y)
1300
else if (result->y == BOX_BOTTOM (*rect))
1305
case META_DIRECTION_BOTTOM:
1306
if (result->y == rect->y)
1308
else if (result->y == BOX_BOTTOM (*rect))
1318
/* Add all edges of the given rect to cur_edges and return the result. If
1319
* rect_is_internal is false, the side types are switched (LEFT<->RIGHT and
1323
add_edges (GList *cur_edges,
1324
const MetaRectangle *rect,
1325
gboolean rect_is_internal)
1327
MetaEdge *temp_edge;
1332
temp_edge = g_new (MetaEdge, 1);
1333
temp_edge->rect = *rect;
1337
temp_edge->side_type =
1338
rect_is_internal ? META_DIRECTION_LEFT : META_DIRECTION_RIGHT;
1339
temp_edge->rect.width = 0;
1342
temp_edge->side_type =
1343
rect_is_internal ? META_DIRECTION_RIGHT : META_DIRECTION_LEFT;
1344
temp_edge->rect.x += temp_edge->rect.width;
1345
temp_edge->rect.width = 0;
1348
temp_edge->side_type =
1349
rect_is_internal ? META_DIRECTION_TOP : META_DIRECTION_BOTTOM;
1350
temp_edge->rect.height = 0;
1353
temp_edge->side_type =
1354
rect_is_internal ? META_DIRECTION_BOTTOM : META_DIRECTION_TOP;
1355
temp_edge->rect.y += temp_edge->rect.height;
1356
temp_edge->rect.height = 0;
1359
temp_edge->edge_type = META_EDGE_SCREEN;
1360
cur_edges = g_list_prepend (cur_edges, temp_edge);
1366
/* Remove any part of old_edge that intersects remove and add any resulting
1367
* edges to cur_list. Return cur_list when finished.
1370
split_edge (GList *cur_list,
1371
const MetaEdge *old_edge,
1372
const MetaEdge *remove)
1374
MetaEdge *temp_edge;
1375
switch (old_edge->side_type)
1377
case META_DIRECTION_LEFT:
1378
case META_DIRECTION_RIGHT:
1379
g_assert (meta_rectangle_vert_overlap (&old_edge->rect, &remove->rect));
1380
if (BOX_TOP (old_edge->rect) < BOX_TOP (remove->rect))
1382
temp_edge = g_new (MetaEdge, 1);
1383
*temp_edge = *old_edge;
1384
temp_edge->rect.height = BOX_TOP (remove->rect)
1385
- BOX_TOP (old_edge->rect);
1386
cur_list = g_list_prepend (cur_list, temp_edge);
1388
if (BOX_BOTTOM (old_edge->rect) > BOX_BOTTOM (remove->rect))
1390
temp_edge = g_new (MetaEdge, 1);
1391
*temp_edge = *old_edge;
1392
temp_edge->rect.y = BOX_BOTTOM (remove->rect);
1393
temp_edge->rect.height = BOX_BOTTOM (old_edge->rect)
1394
- BOX_BOTTOM (remove->rect);
1395
cur_list = g_list_prepend (cur_list, temp_edge);
1398
case META_DIRECTION_TOP:
1399
case META_DIRECTION_BOTTOM:
1400
g_assert (meta_rectangle_horiz_overlap (&old_edge->rect, &remove->rect));
1401
if (BOX_LEFT (old_edge->rect) < BOX_LEFT (remove->rect))
1403
temp_edge = g_new (MetaEdge, 1);
1404
*temp_edge = *old_edge;
1405
temp_edge->rect.width = BOX_LEFT (remove->rect)
1406
- BOX_LEFT (old_edge->rect);
1407
cur_list = g_list_prepend (cur_list, temp_edge);
1409
if (BOX_RIGHT (old_edge->rect) > BOX_RIGHT (remove->rect))
1411
temp_edge = g_new (MetaEdge, 1);
1412
*temp_edge = *old_edge;
1413
temp_edge->rect.x = BOX_RIGHT (remove->rect);
1414
temp_edge->rect.width = BOX_RIGHT (old_edge->rect)
1415
- BOX_RIGHT (remove->rect);
1416
cur_list = g_list_prepend (cur_list, temp_edge);
1424
/* Split up edge and remove preliminary edges from strut_edges depending on
1425
* if and how strut and edge intersect.
1428
fix_up_edges (MetaRectangle *strut, MetaEdge *edge,
1429
GList **strut_edges, GList **edge_splits,
1430
gboolean *edge_needs_removal)
1435
if (!rectangle_and_edge_intersection (strut, edge, &overlap, &handle_type))
1438
if (handle_type == 0 || handle_type == 1)
1440
/* Put the result of removing overlap from edge into edge_splits */
1441
*edge_splits = split_edge (*edge_splits, edge, &overlap);
1442
*edge_needs_removal = TRUE;
1445
if (handle_type == -1 || handle_type == 1)
1447
/* Remove the overlap from strut_edges */
1448
/* First, loop over the edges of the strut */
1449
GList *tmp = *strut_edges;
1452
MetaEdge *cur = tmp->data;
1453
/* If this is the edge that overlaps, then we need to split it */
1454
if (edges_overlap (cur, &overlap))
1456
/* Split this edge into some new ones */
1457
*strut_edges = split_edge (*strut_edges, cur, &overlap);
1459
/* Delete the old one */
1460
GList *delete_me = tmp;
1463
*strut_edges = g_list_delete_link (*strut_edges, delete_me);
1471
/* This function removes intersections of edges with the rectangles from the
1475
meta_rectangle_remove_intersections_with_boxes_from_edges (
1477
const GSList *rectangles)
1479
const GSList *rect_iter;
1480
const int opposing = 1;
1482
/* Now remove all intersections of rectangles with the edge list */
1483
rect_iter = rectangles;
1486
MetaRectangle *rect = rect_iter->data;
1487
GList *edge_iter = edges;
1490
MetaEdge *edge = edge_iter->data;
1493
gboolean edge_iter_advanced = FALSE;
1495
/* If this edge overlaps with this rect... */
1496
if (rectangle_and_edge_intersection (rect, edge, &overlap, &handle))
1499
/* "Intersections" where the edges touch but are opposite
1500
* sides (e.g. a left edge against the right edge) should not
1501
* be split. Note that the comments in
1502
* rectangle_and_edge_intersection() say that opposing edges
1503
* occur when handle is -1, BUT you need to remember that we
1504
* treat the left side of a window as a right edge because
1505
* it's what the right side of the window being moved should
1506
* be-resisted-by/snap-to. So opposing is really 1. Anyway,
1507
* we just keep track of it in the opposing constant set up
1508
* above and if handle isn't equal to that, then we know the
1509
* edge should be split.
1511
if (handle != opposing)
1513
/* Keep track of this edge so we can delete it below */
1514
GList *delete_me = edge_iter;
1515
edge_iter = edge_iter->next;
1516
edge_iter_advanced = TRUE;
1518
/* Split the edge and add the result to beginning of edges */
1519
edges = split_edge (edges, edge, &overlap);
1521
/* Now free the edge... */
1523
edges = g_list_delete_link (edges, delete_me);
1527
if (!edge_iter_advanced)
1528
edge_iter = edge_iter->next;
1531
rect_iter = rect_iter->next;
1537
/* This function is trying to find all the edges of an onscreen region. */
1539
meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect,
1540
const GSList *all_struts)
1543
GList *fixed_struts;
1545
const GList *strut_iter;
1547
/* The algorithm is basically as follows:
1548
* Make sure the struts are disjoint
1549
* Initialize the edge_set to the edges of basic_rect
1551
* Put together a preliminary new edge from the edges of the strut
1552
* Foreach edge in edge_set:
1553
* - Split the edge if it is partially contained inside the strut
1554
* - If the edge matches an edge of the strut (i.e. a strut just
1555
* against the edge of the screen or a not-next-to-edge-of-screen
1556
* strut adjacent to another), then both the edge from the
1557
* edge_set and the preliminary edge for the strut will need to
1559
* Add any remaining "preliminary" strut edges to the edge_set
1562
/* Make sure the struts are disjoint */
1563
fixed_struts = get_disjoint_strut_list_in_region (all_struts, basic_rect);
1565
/* Start off the list with the edges of basic_rect */
1566
ret = add_edges (NULL, basic_rect, TRUE);
1568
strut_iter = fixed_struts;
1571
MetaRectangle *strut = (MetaRectangle*) strut_iter->data;
1573
/* Get the new possible edges we may need to add from the strut */
1574
GList *new_strut_edges = add_edges (NULL, strut, FALSE);
1579
MetaEdge *cur_edge = edge_iter->data;
1580
GList *splits_of_cur_edge = NULL;
1581
gboolean edge_needs_removal = FALSE;
1583
fix_up_edges (strut, cur_edge,
1584
&new_strut_edges, &splits_of_cur_edge,
1585
&edge_needs_removal);
1587
if (edge_needs_removal)
1589
/* Delete the old edge */
1590
GList *delete_me = edge_iter;
1591
edge_iter = edge_iter->next;
1593
ret = g_list_delete_link (ret, delete_me);
1595
/* Add the new split parts of the edge */
1596
ret = g_list_concat (splits_of_cur_edge, ret);
1600
edge_iter = edge_iter->next;
1603
/* edge_iter was already advanced above */
1606
ret = g_list_concat (new_strut_edges, ret);
1607
strut_iter = strut_iter->next;
1611
ret = g_list_sort (ret, meta_rectangle_edge_cmp);
1613
/* Free the fixed struts list */
1614
meta_rectangle_free_list_and_elements (fixed_struts);
1620
meta_rectangle_find_nonintersected_xinerama_edges (
1621
const GList *xinerama_rects,
1622
const GSList *all_struts)
1624
/* This function cannot easily be merged with
1625
* meta_rectangle_find_onscreen_edges() because real screen edges
1626
* and strut edges both are of the type "there ain't anything
1627
* immediately on the other side"; xinerama edges are different.
1632
/* Initialize the return list to be empty */
1635
/* start of ret with all the edges of xineramas that are adjacent to
1638
cur = xinerama_rects;
1641
MetaRectangle *cur_rect = cur->data;
1642
const GList *compare = xinerama_rects;
1645
MetaRectangle *compare_rect = compare->data;
1647
/* Check if cur might be horizontally adjacent to compare */
1648
if (meta_rectangle_vert_overlap(cur_rect, compare_rect))
1650
MetaDirection side_type;
1651
int y = MAX (cur_rect->y, compare_rect->y);
1652
int height = MIN (BOX_BOTTOM (*cur_rect) - y,
1653
BOX_BOTTOM (*compare_rect) - y);
1657
if (BOX_LEFT (*cur_rect) == BOX_RIGHT (*compare_rect))
1659
/* compare_rect is to the left of cur_rect */
1660
x = BOX_LEFT (*cur_rect);
1661
side_type = META_DIRECTION_LEFT;
1663
else if (BOX_RIGHT (*cur_rect) == BOX_LEFT (*compare_rect))
1665
/* compare_rect is to the right of cur_rect */
1666
x = BOX_RIGHT (*cur_rect);
1667
side_type = META_DIRECTION_RIGHT;
1670
/* These rectangles aren't adjacent after all */
1673
/* If the rectangles really are adjacent */
1676
/* We need a left edge for the xinerama on the right, and
1677
* a right edge for the xinerama on the left. Just fill
1678
* up the edges and stick 'em on the list.
1680
MetaEdge *new_edge = g_new (MetaEdge, 1);
1682
new_edge->rect = meta_rect (x, y, width, height);
1683
new_edge->side_type = side_type;
1684
new_edge->edge_type = META_EDGE_XINERAMA;
1686
ret = g_list_prepend (ret, new_edge);
1690
/* Check if cur might be vertically adjacent to compare */
1691
if (meta_rectangle_horiz_overlap(cur_rect, compare_rect))
1693
MetaDirection side_type;
1694
int x = MAX (cur_rect->x, compare_rect->x);
1695
int width = MIN (BOX_RIGHT (*cur_rect) - x,
1696
BOX_RIGHT (*compare_rect) - x);
1700
if (BOX_TOP (*cur_rect) == BOX_BOTTOM (*compare_rect))
1702
/* compare_rect is to the top of cur_rect */
1703
y = BOX_TOP (*cur_rect);
1704
side_type = META_DIRECTION_TOP;
1706
else if (BOX_BOTTOM (*cur_rect) == BOX_TOP (*compare_rect))
1708
/* compare_rect is to the bottom of cur_rect */
1709
y = BOX_BOTTOM (*cur_rect);
1710
side_type = META_DIRECTION_BOTTOM;
1713
/* These rectangles aren't adjacent after all */
1716
/* If the rectangles really are adjacent */
1719
/* We need a top edge for the xinerama on the bottom, and
1720
* a bottom edge for the xinerama on the top. Just fill
1721
* up the edges and stick 'em on the list.
1723
MetaEdge *new_edge = g_new (MetaEdge, 1);
1725
new_edge->rect = meta_rect (x, y, width, height);
1726
new_edge->side_type = side_type;
1727
new_edge->edge_type = META_EDGE_XINERAMA;
1729
ret = g_list_prepend (ret, new_edge);
1733
compare = compare->next;
1738
ret = meta_rectangle_remove_intersections_with_boxes_from_edges (ret,
1742
ret = g_list_sort (ret, meta_rectangle_edge_cmp);