summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElijah Newren <newren@gmail.com>2005-09-06 21:51:06 +0000
committerElijah Newren <newren@src.gnome.org>2005-09-06 21:51:06 +0000
commit544a4ca7787a4ccd70d80ba73ddeecf66ee606e0 (patch)
treefbec0e0e04fa2728621d186660b4abc9664841dd
parentb4f123c332db32c9998ea21c2dcbcd0a02b058fc (diff)
downloadmetacity-544a4ca7787a4ccd70d80ba73ddeecf66ee606e0.tar.gz
Start of new constraints_experiments branch.
2005-09-06 Elijah Newren <newren@gmail.com> Start of new constraints_experiments branch. * README: Note that this branch is for trying out some ideas and doesn't even build yet. * constraints-ideas.txt: Random notes about the rewrite. * src/boxes.[ch]: An initial idea that was way over engineered (move_rectangle_into_region() & clip_rectangle_into_region() and their helper functions are the crux of those files, and both are much more complicated than necessary). Basic ideas are fine, but the implementation can be much simpler. Code was never tested, but might still be useful (for the ideas if nothing else) * src/display.[ch]: Move MetaRectangle, meta_rectangle_intersect(), and meta_rectangle_equal() into boxes.[ch] * src/window.c: Remove get_mouse_deltas_for_resize(), comment out use of adjust_for_gravity(), modify meta_window_move_resize_internal() for new form of meta_window_constrain including the fact that I think it's more intuitive to work with the whole window (client + decorations) than just the client part * src/constraints.[ch]: Lots of comments, new API for meta_window_constrain() (not quite correct yet since I need more info to move adjust_for_gravity() into constraints.c from window.c), some code salvaged from the old constraints.c, lots of stub functions or random comments, totally does not compile or anything.
-rw-r--r--ChangeLog37
-rw-r--r--README11
-rw-r--r--constraints-ideas.txt177
-rw-r--r--src/boxes.c834
-rw-r--r--src/boxes.h65
-rw-r--r--src/constraints.c1916
-rw-r--r--src/constraints.h22
-rw-r--r--src/display.c47
-rw-r--r--src/display.h17
-rw-r--r--src/window.c105
10 files changed, 1563 insertions, 1668 deletions
diff --git a/ChangeLog b/ChangeLog
index 339e7f0a..7df72125 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,40 @@
+2005-09-06 Elijah Newren <newren@gmail.com>
+
+ Start of new constraints_experiments branch.
+
+ * README:
+ Note that this branch is for trying out some ideas and doesn't
+ even build yet.
+
+ * constraints-ideas.txt:
+ Random notes about the rewrite.
+
+ * src/boxes.[ch]:
+ An initial idea that was way over engineered
+ (move_rectangle_into_region() & clip_rectangle_into_region() and
+ their helper functions are the crux of those files, and both are
+ much more complicated than necessary). Basic ideas are fine, but
+ the implementation can be much simpler. Code was never tested,
+ but might still be useful (for the ideas if nothing else)
+
+ * src/display.[ch]:
+ Move MetaRectangle, meta_rectangle_intersect(), and
+ meta_rectangle_equal() into boxes.[ch]
+
+ * src/window.c:
+ Remove get_mouse_deltas_for_resize(), comment out use of
+ adjust_for_gravity(), modify meta_window_move_resize_internal()
+ for new form of meta_window_constrain including the fact that I
+ think it's more intuitive to work with the whole window (client +
+ decorations) than just the client part
+
+ * src/constraints.[ch]:
+ Lots of comments, new API for meta_window_constrain() (not quite
+ correct yet since I need more info to move adjust_for_gravity()
+ into constraints.c from window.c), some code salvaged from the old
+ constraints.c, lots of stub functions or random comments, totally
+ does not compile or anything.
+
2005-09-05 Elijah Newren <newren@gmail.com>
* configure.in: post-release version bump to 2.12.1
diff --git a/README b/README
index db9fb1dd..55b737fa 100644
--- a/README
+++ b/README
@@ -1,3 +1,14 @@
+This is the 'constraints-experiments' branch of metacity. It is intended to try
+out some new ideas for implementing constraints.c If you expect it to compile,
+let alone work, I will laugh in your face.
+
+To actually use this branch of metacity, you need to finish my implementation
+and then fix all the bugs. I'll update this file when it's at least buildable.
+
+Old README file:
+
+
+
Metacity is not a meta-City as in an urban center, but rather
Meta-ness as in the state of being meta. i.e. metacity : meta as
opacity : opaque. Also it may have something to do with the Meta key
diff --git a/constraints-ideas.txt b/constraints-ideas.txt
new file mode 100644
index 00000000..f9112f8b
--- /dev/null
+++ b/constraints-ideas.txt
@@ -0,0 +1,177 @@
+Bugs to fix:
+ unfiled - constraints.c is overly complicated
+ unfiled - constraints.c is not robust when all constraints cannot be met
+ unfiled - document our treating of gravities with center as a
+ size-increment constraint (or perhaps actually store the
+ reference point and don't require the
+ even-number-of-pixels in resize)
+ unfiled - get_outermost_onscreen_positions is decoration-unaware
+ unfiled - constraints.c documentation is difficult to understand
+ 109553 - gravity w/ simultaneous move & resize doesn't work
+ 113601 - maximize vert and horiz should toggle & be constrained
+ 122196 - windows show up under vertical panels
+ 143145 - clamp to screensize
+ 143145? - make appear on screen
+ 136307 - don't allow app resize off screen
+ (unless minimum size hints? No...too big windows are fully clamped)
+ 156699 - avoid struts when placing windows, if possible
+ 194867 - fixed aspect ratio windows are difficult to resize
+ WARNING: nasty interaction with e.g. onscreen constraints in 2 dim's!
+ 312007 - snap-resize moves windows with minimium size constraint
+ 312104 - resize top causes bottom to grow
+
+ Maybe:
+ 152898 - no way to move a window off the top of the screen
+ (warning: requires cleverness as user can accidentally
+ trigger alt+click moving; probably needs edge resistance
+ and other work first)
+ 154706 - bouncing weirdness at screen edge w/ keyboard move or resize
+ 124582 - shift-arrow erroneously moves window multi-dimensionally
+ 122670 - jerky/random resizing of windows via keyboard
+ (drop extra events -- not really needed)
+ 81704 - edge magnetism/resistance/snapping/etc.
+
+Some ideas:
+ - move_rectangle_into_region & clip_rectangle_into_region in
+ boxes.[ch] are way over-engineered; only need workarea &
+ best-window-area (via modified form of
+ get_outermost_onscreen_positions())
+ - update_position_limits looks like stupidity without current crufty
+ framework
+ - get_mouse_deltas_for_resize() shouldn't be necessary (window.c)
+ - adjust_for_gravity() ought to move to constraints.c, IMO
+
+ - onscreen cases:
+ - user resize: clip into valid region
+ - user move: move window into (fully extended) valid region
+ - user move & resize: someone lied about what's happening or who's doing it
+ <All three below can be handled via the resize algorithm; extras are no-ops>
+ - app resize: gravity adjust, clamp to workarea size, move into it
+ - app move: move into valid region
+ - app move & resize: clamp to workarea size, then move into valid region
+
+ - offscreen differences:
+ - user resize: don't allow size increase of offscreen sides
+ - user move: N/A
+ - user move & resize: N/A
+ <All three below can be handled via the resize algorithm; extras are no-ops>
+ - app resize, move, move&resize: same but with bigger region
+
+Constraints in constraints.c:
+ - place the window (huh?), including maximization after placement
+ - maximization constraints (both horiz & vert; window-size == workarea size)
+ - fullscreen constraints (window-size == xinerama size)
+ - desktop window constraints
+ - titlebar onscreen constraints
+ - min & max window size constraints (restore towards previous size)
+ - resize increment constraints (no need for restoration here; but adjust imp.)
+ - aspect ratio (geometric average of rects defined by x & y?)
+
+ <should also add>
+ - gravity
+ - really onscreen
+ - clamp to screen size
+
+Add a rectangle calculus packages
+ MetaRectangle rectangle;
+ GList* region; // list of rectangles
+
+ gboolean rectangles_intersect
+ gboolean region_contains_rectangle
+ gboolean region_larger_than_rectangle
+ void move_rectangle_into_region
+ void clip_rectangle_into_region
+ region_expand // takes screen region, add 75 -> have
+ // partially onscreen region contraint
+
+ ** NOTE: Trying to resize at left should not move bigger window onscreen **
+ ** NOTE: Resizing aspect ratio windows should resize to rect closest in
+ size to the previous one **
+ ** NOTE: Trying to resize minimum size window should attempt to restore
+ window to previous position, not just resize to minimum size or
+ else it could result in the window moving **
+ ** NOTE: Might be good to consider gravity somehow; Havoc has test program **
+ ** NOTE: Would probably be good to store optimal points, largest
+ rectangle(s) (area-wise?) that will fit, and some other stuff
+ that only changes when docks are added or removed... **
+ ** NOTE: How does application specified placement interact with
+ onscreen constraints? **
+
+Enumeration:
+ A simple enumeration with explicitly defined values, e.g.
+ typedef enum
+ {
+ PRIORITY_MINIMUM=0,
+ PRIORITY_ASPECT_RATIO=0,
+ PRIORITY_CLAMP_TO_WORKAREA=1,
+ PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA=1,
+ PRIORITY_MEET_MINIMIMUM_SIZE_HINTS=2,
+ PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA=3,
+ PRIORITY_TITLEBAR_PARTIALLY_VISIBLE_ON_WORKAREA=3
+ PRIORITY_MAXIMUM=3
+ }
+
+ Others: (large windows are auto-maximized,
+ maximized windows follow struts,
+ fullscreen windows are screensize,
+ desktop window is screensize,...)
+
+Function form:
+ gboolean
+ meta_constrain_whatever (constraint_info info,
+ int priority,
+ gboolean check_only)
+ {
+ if (priority > PRIORITY_WHATEVER)
+ return TRUE;
+
+ if (check_only)
+ {
+ /* If constraints already satisfied return TRUE, otherwise FALSE */
+ }
+
+ /* Enforce constraints */
+
+ return TRUE; /* Though this is ignored when check_only is FALSE */
+ }
+
+Extra sanity checking:
+ It can be useful to define variables
+ <some>_corner_fixed (where <some> = ne, nw, se, or sw)
+ window_size_fixed
+ To verify that size or aspect ratio or placement constraints affect
+ the correct variables
+
+Advantages:
+ - Easier to understand (can easily add/remove/reprioritize constraints)
+ - Can handle multi-dimensional constraints such as aspect ratio
+ - Can determine which constraints will be violated when there are too many
+ - We can even log the fact that constraints will be violated
+
+Minor disadvantage
+ - We don't get enforced sanity, but sanity checking is fairly easy
+
+Disadvantages:
+ - Major code overhaul
+
+
+
+Extra rationale:
+ - fully-onscreen trumping PPosition hints
+ Advantages:
+ - users are annoyed when they can't see the whole window they are
+ interacting with (note that this is why we raise on click too)
+ - needed to fully fix placement with vertical panels (bug 122196)
+ - needed to fully fix placement for accessibility with docks (bug 156699)
+ Disadvantages:
+ - apps may be trying to place dialogs relative to some other UI in
+ their main window, and it may annoy the users to have the
+ windows not align
+ Other notes:
+ - disable_workarounds totally ignores PPosition anyway
+ - PPosition is already overridden for totally offscreen or
+ titlebar offscreen cases (though, in these cases, the window is
+ placed as close as possible to the specified position)
+ - users should easily recognize why things don't align as they
+ might expect, and the fully-onscreen constraint should place the
+ window near the specified position
diff --git a/src/boxes.c b/src/boxes.c
new file mode 100644
index 00000000..7dedfc3b
--- /dev/null
+++ b/src/boxes.c
@@ -0,0 +1,834 @@
+/* Simple box operations */
+
+/*
+ * Copyright (C) 2005 Elijah Newren
+ * [According to the ChangeLog, Anders, Havoc, and Rob were responsible
+ * for the meta_rectangle_intersect() and meta_rectangle_equal()
+ * functions that I copied from display.c]
+ * Copyright (C) 2002 Anders Carlsson
+ * Copyright (C) 2002 Red Hat, Inc.
+ * Copyright (C) 2003 Rob Adams
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "boxes.h"
+
+static void free_the_list (GList *list);
+static GList* get_optimal_locations (GList *region);
+static GList* remove_rectangle_from_region (const MetaRectangle *rect,
+ GList *region);
+static void clip_rectangle_away_from_spot(MetaRectangle *rect,
+ const MetaRectangle *bad_rect);
+static void move_rectangle_away_from_spot(MetaRectangle *rect,
+ const MetaRectangle *bad_rect,
+ gboolean shortest_path);
+
+int
+meta_rectangle_area (const MetaRectangle *rect)
+{
+ g_return_val_if_fail (rect != NULL, 0);
+ return rect->width * rect->height;
+}
+
+gboolean
+meta_rectangle_intersect (const MetaRectangle *src1,
+ const MetaRectangle *src2,
+ MetaRectangle *dest)
+{
+ int dest_x, dest_y;
+ int dest_w, dest_h;
+ int return_val;
+
+ g_return_val_if_fail (src1 != NULL, FALSE);
+ g_return_val_if_fail (src2 != NULL, FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+
+ return_val = FALSE;
+
+ dest_x = MAX (src1->x, src2->x);
+ dest_y = MAX (src1->y, src2->y);
+ dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x;
+ dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y;
+
+ if (dest_w > 0 && dest_h > 0)
+ {
+ dest->x = dest_x;
+ dest->y = dest_y;
+ dest->width = dest_w;
+ dest->height = dest_h;
+ return_val = TRUE;
+ }
+ else
+ {
+ dest->width = 0;
+ dest->height = 0;
+ }
+
+ return return_val;
+}
+
+gboolean
+meta_rectangle_equal (const MetaRectangle *src1,
+ const MetaRectangle *src2)
+{
+ return ((src1->x == src2->x) &&
+ (src1->y == src2->y) &&
+ (src1->width == src2->width) &&
+ (src1->height == src2->height));
+}
+
+#if 0
+gboolean
+rectangles_intersect (const MetaRectangle *rect1,
+ const MetaRectangle *rect2)
+{
+ g_return_val_if_fail (rect1 != NULL, FALSE);
+ g_return_val_if_fail (rect2 != NULL, FALSE);
+
+ return (!(rect1->x + rect1->width < rect2.x) ||
+ (rect2->x + rect2->width < rect1.x) ||
+ (rect1->y + rect1->height < rect2.y) ||
+ (rect2->y + rect2->height < rect1.y));
+}
+#endif
+
+gboolean
+region_contains_rectangle (const GList *region,
+ const MetaRectangle *rect)
+{
+ /* The whole trick to this function, is that since region contains
+ * non-overlapping rectangles, we just need to add up the area of
+ * intersections between rect and various rectangles of region; if it
+ * adds up to the area of rect then region contains rect.
+ */
+ GList *tmp;
+ int area;
+ int compare_area;
+
+ area = meta_rectangle_area (rect);
+ tmp = region;
+ while (tmp != NULL)
+ {
+ MetaRectangle *compare_rect = tmp->data;
+ MetaRectangle overlap;
+
+ meta_rectangle_intersect (rect, compare_rect, &overlap);
+ compare_area += meta_rectangle_area (&overlap);
+
+ tmp = tmp->next;
+ }
+
+ return compare_area == area;
+}
+
+gboolean
+region_could_contain_rectangle (const GList *region,
+ const MetaRectangle *rect)
+{
+ GList *tmp;
+ GList *optimal_locations;
+ int area;
+ MetaRectangle temp;
+ gboolean retval;
+
+ /* If rect fits in any one of the rectangles in region then it could fit;
+ * if the total area of the rectangles in region is less than that of rect,
+ * then it can't fit. Try these easy two checks first.
+ */
+ area = 0;
+ tmp = region;
+ while (tmp != NULL)
+ {
+ MetaRectangle *compare_rect = tmp->data;
+ if (compare_rect->height >= rect->height &&
+ compare_rect->width >= rect->width)
+ return TRUE;
+ area += meta_rectangle_area (compare_rect);
+
+ tmp = tmp->next;
+ }
+
+ if (area < meta_rectangle_area (rect))
+ return FALSE;
+
+ /* Well, that didn't work, unfortunately. That means that the only
+ * way that rect could fit in region is if it spans multiple
+ * rectangles from region...
+ */
+ optimal_locations = get_optimal_locations (region);
+ tmp = optimal_locations;
+ retval = FALSE:
+ while (tmp != NULL &&
+ retval != TRUE)
+ {
+ MetaRectangle *current = optimal_locations->data;
+ temp.x = current->x;
+ temp.y = current->y;
+ temp.width = rect->width;
+ temp.height = rect->height;
+
+ if (region_contains_rectangle (region, temp))
+ retval = TRUE;
+
+ tmp = tmp->next;
+ }
+
+ /* Free the data */
+ free_the_list (optimal_locations);
+ return retval;
+}
+
+gboolean
+move_rectangle_into_region (MetaRectangle *rect,
+ const GList *region)
+{
+ /* The basic idea here is similar to clip_rectangle_into_region, except
+ * that instead of clipping rect away from individual rectangles R, it is
+ * shifted away from them. This function returns whether such a strategy
+ * was successful in placing rect within region. If it fails, an
+ * approach similar to that found in region_could_contain_rectangle could
+ * be used to try to place rect in region.
+ */
+ GList *tmp;
+ GList *bad_region
+ MetaRectangle copy;
+ MetaRectangle *temp;
+ gboolean retval;
+
+ if (region_contains_rectangle (region, rect))
+ return TRUE;
+
+ temp = g_new (MetaRectangle, 1);
+ *temp = *rect;
+ bad_region = g_list_prepend(NULL, temp);
+ tmp = region;
+ while (tmp != NULL)
+ {
+ MetaRectangle *compare_rect = tmp->data;
+ bad_region = remove_rectangle_from_region (compare_rect, bad_region)
+ tmp = tmp->next;
+ }
+
+ copy = *rect;
+ tmp = bad_region;
+ while (tmp != NULL)
+ {
+ MetaRectange *bad_rect = tmp->data;
+ move_rectangle_away_from_spot(rect, bad_rect, TRUE);
+ tmp = tmp->next;
+ }
+
+ if (region_contains_rectangle (region, rect))
+ {
+ retval = TRUE;
+ goto done;
+ }
+
+ /* Initial effort failed; this can happen, for example, with two
+ * panels that don't span the width of the screen, both of which are
+ * at the bottom of the screen and which have some open space
+ * between them. If a window almost fits between them but just
+ * barely overlaps one of the panels, the algorithm above will shove
+ * the window over the other panel. So we repeat the algorithm
+ * above but use a different flag to move_rectangle_away_from_spot()
+ * to try to avoid this kind of problem.
+ */
+ *rect = copy;
+ tmp = bad_region;
+ while (tmp != NULL)
+ {
+ MetaRectange *bad_rect = tmp->data;
+ move_rectangle_away_from_spot(rect, bad_rect, FALSE);
+ tmp = tmp->next;
+ }
+ retval = region_contains_rectangle (region, rect);
+
+done:
+ free_the_list (bad_region);
+
+ return retval;
+}
+
+void
+clip_rectangle_into_region (MetaRectangle *rect,
+ const GList *region)
+{
+ /* The basic idea here is to progressively remove each rectangle in
+ * region from rect; then take the resulting bad region and for each
+ * rectangle R that makes it (the bad region) up, clip rect away from R.
+ */
+ GList *tmp;
+ GList *bad_region
+
+ MetaRectangle *temp = g_new (MetaRectangle, 1);
+ *temp = *rect;
+ bad_region = g_list_prepend(NULL, temp);
+ tmp = region;
+ while (tmp != NULL)
+ {
+ MetaRectangle *compare_rect = tmp->data;
+ bad_region = remove_rectangle_from_region (compare_rect, bad_region)
+ tmp = tmp->next;
+ }
+ tmp = bad_region;
+ while (tmp != NULL)
+ {
+ MetaRectange *bad_rect = tmp->data;
+ clip_rectangle_away_from_spot(rect, bad_rect);
+ tmp = tmp->next;
+ }
+ free_the_list (bad_region);
+}
+
+#if 0
+/* Old, buggy implementation... */
+void
+clip_rectangle_into_region (MetaRectangle *rect,
+ const GList *region)
+{
+ g_assert (0==1);
+ GList *tmp;
+ GList *bad_region
+
+ MetaRectangle *temp = new MetaRectangle;
+ *temp = *rect;
+ bad_region = g_list_prepend(NULL, temp);
+ tmp = region;
+ while (tmp != NULL)
+ {
+ MetaRectangle *compare_rect = tmp->data;
+ bad_region = remove_rectangle_from_region (compare_rect, bad_region)
+ tmp = tmp->next;
+ }
+ tmp = bad_region;
+ if (bad_region != NULL)
+ {
+ /* Get the minimum and maximum x and y coordinates that are bad */
+ int minx, miny, maxx, maxy;
+ minx = miny = INT_MAX;
+ maxx = maxy = INT_MIN;
+
+ tmp = region;
+ while (tmp != NULL)
+ {
+ MetaRectangle *compare_rect = tmp->data;
+ if (compare_rect.x < minx)
+ minx = compare_rect.x;
+ if (compare_rect.x + compare_rect.width > maxx)
+ maxx = compare_rect.x + compare_rect.width;
+ if (compare_rect.y < miny)
+ miny = compare_rect.y;
+ if (compare_rect.y + compare_rect.height > maxy)
+ maxy = compare_rect.y + compare_rect.height;
+ tmp = tmp->next;
+ }
+
+ /* See which direction(s) would be best to clip in (preserve as
+ * much area as possible)
+ */
+
+ }
+
+ g_list_free (bad_region);
+}
+#endif
+
+void
+region_expand (GList *region,
+ int expand_amount,
+ MetaRectDirection directions)
+{
+ GList *tmp;
+ tmp = region;
+ while (tmp != NULL)
+ {
+ MetaRectangle *rect;
+ rect = tmp->data;
+
+ if (directions & META_RECTANGLE_LEFT)
+ {
+ tmp->x -= expand_amount;
+ tmp->width += expand_amount;
+ }
+ if (directions & META_RECTANGLE_RIGHT)
+ {
+ tmp->width += expand_amount;
+ }
+ if (directions & META_RECTANGLE_UP)
+ {
+ tmp->y -= expand_amount;
+ tmp->height += expand_amount;
+ }
+ if (directions & META_RECTANGLE_DOWN)
+ {
+ tmp->height += expand_amount;
+ }
+
+ tmp = tmp->next;
+ }
+}
+
+static void
+free_the_list (GList *list)
+{
+ GList *tmp;
+ tmp = list;
+ while (tmp != NULL)
+ {
+ g_free (tmp->data);
+ tmp = tmp->next;
+ }
+
+ g_list_free (list);
+}
+
+static gint
+sort_by_leftmost (const MetaRectangle *a, const MetaRectangle *b)
+{
+ return a->x - b->x;
+}
+
+static gint
+sort_by_topmost (const MetaRectangle *a, const MetaRectangle *b)
+{
+ return a->y - b->y;
+}
+
+static gint
+sort_by_left_then_by_top (const MetaRectangle *a, const MetaRectangle *b)
+{
+ if (a->x == b->x)
+ return a->y - b->y;
+ return a->x - b->x;
+}
+
+static GList*
+get_optimal_locations (GList *region)
+{
+ /*
+ * We'd like to be able to determine if a rectangle can fit within a
+ * region, where a region is the union of N adjacent, nonoverlapping
+ * rectangles. Note that the rectangles are not necessarily aligned
+ * nicely. We'd like to do so with a minimal set of points where
+ * the test for fitting in the region will be that it does so if and
+ * only if it will fit in the region when its upper left corner is
+ * placed at one of the given points. This could be easy if we just
+ * specified every point in the domain, but the trick is trying to
+ * get a minimal set of points (typically O(N) of them though it can
+ * be O(N^2) in pathological cases)
+ *
+ * To understand what this function tries to do, draw some
+ * qualifying regions and ask yourself, "If I'm given any old
+ * rectangle, what are the best locations to stick it so that it's
+ * most likely to fit in this region?" That's what this algorithm
+ * is trying to find.
+ *
+ * The basic idea is that we want the upper left corner of each
+ * rectangle. Call these locations UL[i]. The difference is that
+ * if there is open space to the left of or above any given UL[i] in
+ * a different rectangle then we may want to also add (or replace
+ * UL[i] with) a point that is horizontally or vertically aligned
+ * with UL[i] at the edge of the adjacent rectangle.
+ *
+ * The sorted_left_to_right and sorted_top_to_bottom arrays should
+ * help us with the following of a given UL[i] to the real edge of
+ * the domain in the left and above directions.
+ */
+ GList *locations;
+ GList *tmp;
+ GArray *sorted_left_to_right, *sorted_top_to_bottom;
+ locations = NULL;
+
+ int len = g_list_length (region);
+ sorted_left_to_right = g_array_sized_new (FALSE, FALSE, sizeof (GList*), len);
+ sorted_top_to_bottom = g_array_sized_new (FALSE, FALSE, sizeof (GList*), len);
+ tmp = region;
+ int i = 0;
+ do
+ {
+ g_array_index (sorted_left_to_right, i, GList*) =
+ g_array_index (sorted_top_to_bottom, i, GList*) = tmp;
+
+ tmp = tmp->next;
+ i++;
+ }
+ while (tmp != NULL);
+
+ g_array_sort (sorted_left_to_right, sort_by_leftmost);
+ g_array_sort (sorted_top_to_bottom, sort_by_topmost);
+
+ for (int i=0; i<len; i++)
+ {
+ MetaRectangle *orig_compare_rect;
+ MetaRectangle *new_point;
+
+ orig_compare_rect = g_array_index (sorted_left_to_right, i, GList*)->data;
+
+ /************************************/
+ /* FIRST: SEARCH POINTS TO THE LEFT */
+ /************************************/
+ new_point = g_new (MetaRectangle, 1);
+ new_point->x = orig_compare_rect->x;
+ new_point->y = orig_compare_rect->y;
+
+ /* I need to know the width and height of the rectangle I'm
+ * looking at because it affects whether there's more than one
+ * "optimal point" for each rectangle when smaller rectangles
+ * are adjacent.
+ */
+ new_point->width = orig_compare_rect->width;
+ new_point->height = orig_compare_rect->height;
+
+ /* loop over rectangles that could be to the left of orig_compare_rect */
+ for (int j=i-1; j>=0; j--)
+ {
+ MetaRectangle *compare_rect;
+ compare_rect = g_array_index (sorted_left_to_right, j, GList*)->data;
+
+ /* If this compare_rect's right touches the other's left... */
+ if (compare_rect->x + compare_rect->width == new_rectangle->x)
+ {
+ /* Get the one pixel past the bottoms of both rectangles */
+ int compare_end = compare_rect->y + compare_rect->height;
+ int new_end = new_rectangle->y + new_rectangle->height;
+
+ /* If compare_rect contains something to the left of the
+ * specified point then we have more work to do
+ */
+ if (compare_rect->y <= new_rectangle->y &&
+ compare_end > new_rectangle->y)
+ {
+ /* If compare_rect covers the full height of the relevant
+ * rectangle, then we can just use the corresponding left
+ * point of compare_rect; otherwise, we want both
+ * points.
+ */
+ if (compare_rect->y <= new_rectangle->y &&
+ compare_end >= new_end)
+ {
+ new_rectangle->x = compare_rect->x;
+ }
+ else
+ {
+ MetaRectangle temp;
+
+ /* Record point stored in new_rectangle */
+ locations = g_list_prepend (locations, new_rectangle);
+
+ /* Create a new point, copied from the old
+ temp = *new_rectangle;
+ new_rectangle = g_new (MetaRectangle, 1);
+ *new_rectangle = temp;
+
+ /* Initialize the values appropriately */
+ new_rectangle->x = compare_rect->x;
+ new_rectangle->height = compare_end - new_rectangle->y;
+ }
+ } // if compare_rect is vertically aligned okay too...
+ } // if compare_rect ends to the left
+ } // looping over rectangles that could be to the left
+
+ /* Record the final point found and stored in new_rectangle */
+ locations = g_list_prepend (locations, new_rectangle);
+
+ /************************************/
+ /* SECOND: SEARCH POINTS ABOVE */
+ /************************************/
+ new_point = g_new (MetaRectangle, 1);
+ new_point->x = orig_compare_rect->x;
+ new_point->y = orig_compare_rect->y;
+
+ /* I need to know the width and height of the rectangle I'm
+ * looking at because it affects whether there's more than one
+ * "optimal point" for each rectangle when smaller rectangles
+ * are adjacent.
+ */
+ new_point->width = orig_compare_rect->width;
+ new_point->height = orig_compare_rect->height;
+
+ /* loop over rectangles that could be to above orig_compare_rect */
+ for (int j=i-1; j>=0; j--)
+ {
+ MetaRectangle *compare_rect;
+ compare_rect = g_array_index (sorted_top_to_bottom, j, GList*)->data;
+
+ /* If this compare_rect's bottom touches the other's top... */
+ if (compare_rect->y + compare_rect->height == new_rectangle->y)
+ {
+ /* Get the one pixel past the rights of both rectangles */
+ int compare_end = compare_rect->x + compare_rect->width;
+ int new_end = new_rectangle->x + new_rectangle->width;
+
+ /* If compare_rect contains something to above the
+ * specified point then we have more work to do
+ */
+ if (compare_rect->x <= new_rectangle->x &&
+ compare_end > new_rectangle->x)
+ {
+ /* If compare_rect covers the full width of the relevant
+ * rectangle, then we can just use the corresponding top
+ * point of compare_rect; otherwise, we want both
+ * points.
+ */
+ if (compare_rect->x <= new_rectangle->x &&
+ compare_end >= new_end)
+ {
+ new_rectangle->y = compare_rect->y;
+ }
+ else
+ {
+ MetaRectangle temp;
+
+ /* Record point stored in new_rectangle */
+ locations = g_list_prepend (locations, new_rectangle);
+
+ /* Create a new point, copied from the old
+ temp = *new_rectangle;
+ new_rectangle = g_new (MetaRectangle, 1);
+ *new_rectangle = temp;
+
+ /* Initialize the values appropriately */
+ new_rectangle->y = compare_rect->y;
+ new_rectangle->width = compare_end - new_rectangle->x;
+ }
+ } // if compare_rect is horizontally aligned okay too...
+ } // if compare_rect ends to the top
+ } // looping over rectangles that could be above
+
+ /* Record the final point found and stored in new_rectangle */
+ locations = g_list_prepend (locations, new_rectangle);
+ }
+
+ g_array_free (sorted_left_to_right, TRUE);
+ g_array_free (sorted_top_to_bottom, TRUE);
+
+ /* The above code will result in a lot of duplicates; e.g. the same point
+ * can be added for both going left and going above--further, in some
+ * cases such as when rectangles align nicely, the same point may be
+ * added for a later rectangle that was added for a previous one. So we
+ * should remove duplicates from the locations list.
+ */
+ locations = g_list_sort (locations, sort_by_left_then_by_top);
+ tmp = locations;
+ while (tmp != NULL && tmp->next != NULL)
+ {
+ GList *next;
+ next = tmp->next;
+ if (sort_by_left_then_by_top (tmp->data, next->data) == 0)
+ {
+ g_free (tmp->data);
+ locations = g_list_delete_link (locations, tmp);
+ }
+ tmp = next;
+ }
+
+ return locations;
+}
+
+/* Returns a new region resulting from removing rect from region. NOTE:
+ * region is freed (because it is effectively replaced by the return list).
+ */
+static GList*
+remove_rectangle_from_region (const MetaRectangle *rect, GList *region)
+{
+ GList *tmp;
+ GList *new;
+
+ new = NULL;
+ tmp = region;
+ while (tmp != NULL)
+ {
+ MetaRectangle *compare_rect = tmp->data;
+ MetaRectangle overlap;
+
+ if (meta_rectangle_intersect (rect, compare_rect, &overlap))
+ {
+ MetaRectangle *new_rectangle;
+
+ /* If compare_rect is totally overlaped, just don't add any of it to
+ * the new GList.
+ */
+ if (overlap.width == compare_rect.width &&
+ overlap.height == compare_rect.height)
+ {
+ continue;
+ }
+
+ /* Look for remaining rectangles above, below, to the left,
+ * then to the right of the overlap area
+ */
+ if (overlap.y > compare_rect.y)
+ {
+ new_rectangle = g_new (MetaRectangle, 1);
+ new_rectangle.x = compare_rect.x;
+ new_rectangle.width = compare_rect.width;
+ new_rectangle.y = compare_rect.y;
+ new_rectangle.height = overlap.y - compare_rect.y;
+ new = g_list_prepend (new, new_rectangle);
+ }
+ if (overlap.y + overlap.height < compare_rect.y + compare_rect.height)
+ {
+ new_rectangle = g_new (MetaRectangle, 1);
+ new_rectangle.x = compare_rect.x;
+ new_rectangle.width = compare_rect.width;
+ new_rectangle.y = overlap.y + overlap.height;
+ new_rectangle.height = compare_rect.y + compare_rect.height
+ - new_rectangle.y;
+ new = g_list_prepend (new, new_rectangle);
+ }
+ if (overlap.x > compare_rect.x)
+ {
+ new_rectangle = g_new (MetaRectangle, 1);
+ new_rectangle.x = compare_rect.x;
+ new_rectangle.width = overlap.x - compare_rect.x;
+ new_rectangle.y = overlap.y;
+ new_rectangle.height = overlap.height;
+ new = g_list_prepend (new, new_rectangle);
+ }
+ if (overlap.x + overlap.width < compare_rect.x + compare_rect.width)
+ {
+ new_rectangle = g_new (MetaRectangle, 1);
+ new_rectangle.x = overlap.x + overlap.width;
+ new_rectangle.width = compare_rect.x + compare_rect.width
+ - new_rectangle.x;
+ new_rectangle.y = overlap.y;
+ new_rectangle.height = overlap.height;
+ new = g_list_prepend (new, new_rectangle);
+ }
+ g_free (tmp->data);
+ }
+ else
+ {
+ new = g_list_prepend (new, tmp->data);
+ }
+ tmp = tmp->next;
+ }
+
+ g_list_free (region);
+ return new;
+}
+
+static void
+clip_rectangle_away_from_spot(MetaRectangle *rect,
+ const MetaRectangle *bad_rect)
+{
+ /* No need to do anything if these rectangles don't overlap */
+ MetaRectangle overlap;
+ if (!meta_rectangle_intersect (rect, bad_rect, &overlap))
+ return;
+
+ /* Determine direction to clip the rectangle in; try to minimize amount */
+ gboolean clip_height;
+ if (overlap->width == rect->width)
+ clip_height = TRUE;
+ else if (overlap->height == rect->height)
+ clip_height = FALSE;
+ else if (overlap->width > overlap->height)
+ clip_height = TRUE;
+ else
+ clip_height = FALSE;
+
+ if (clip_height)
+ {
+ /* If clipping from the bottom removes less area than clipping
+ from the top */
+ if (rect->y + rect->height - overlap->x <
+ overlap->y + overlap->height - rect->x)
+ {
+ rect->height = overlap.y - rect->y;
+ }
+ else
+ {
+ int newy = overlap->y + overlap->height;
+ rect->height -= (newy - rect->y);
+ rect->y = newy;
+ }
+ }
+ else
+ {
+ /* If clipping from the right removes less area than clipping
+ from the left */
+ if (rect->x + rect->width - overlap->x <
+ overlap->x + overlap->width - rect->x)
+ {
+ rect->width = overlap.x - rect->x;
+ }
+ else
+ {
+ int newx = overlap->x + overlap->width;
+ rect->width -= (newx - rect->x);
+ rect->x = newx;
+ }
+ }
+}
+
+static void
+move_rectangle_away_from_spot(MetaRectangle *rect,
+ const MetaRectangle *bad_rect,
+ gboolean shortest_path)
+{
+ /* No need to do anything if these rectangles don't overlap */
+ MetaRectangle overlap;
+ if (!meta_rectangle_intersect (rect, bad_rect, &overlap))
+ return;
+
+ /* Determine direction to move the rectangle in; try to minimize amount */
+ gboolean move_height;
+ if (overlap->width == rect->width)
+ move_height = TRUE;
+ else if (overlap->height == rect->height)
+ move_height = FALSE;
+ else if (overlap->width > overlap->height)
+ move_height = TRUE;
+ else
+ move_height = FALSE;
+
+ /* Er, actually we maximize the amount if shortest_path is false */
+ if (!shortest_path)
+ move_height = !move_height;
+
+ if (move_height)
+ {
+ /* If moving away from the bottom removes less area than moving
+ * away from the top
+ */
+ if (rect->y + rect->height - overlap->x <
+ overlap->y + overlap->height - rect->x)
+ {
+ rect->y = overlap->y - rect->height;
+ }
+ else
+ {
+ rect->y = overlap->y + overlap->height;
+ }
+ }
+ else
+ {
+ /* If moving away from the right removes less area than moving
+ * away from the left
+ */
+ if (rect->x + rect->width - overlap->x <
+ overlap->x + overlap->width - rect->x)
+ {
+ rect->x = overlap.x - rect->width;
+ }
+ else
+ {
+ rect->x = overlap->x + overlap->width;
+ }
+ }
+}
diff --git a/src/boxes.h b/src/boxes.h
new file mode 100644
index 00000000..2b8e937e
--- /dev/null
+++ b/src/boxes.h
@@ -0,0 +1,65 @@
+/* Simple box operations */
+
+/*
+ * Copyright (C) 2005 Elijah Newren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_BOXES_H
+#define META_BOXES_H
+
+typedef struct _MetaRectangle MetaRectangle;
+
+struct _MetaRectangle
+{
+ int x;
+ int y;
+ int width;
+ int height;
+};
+
+typedef enum
+{
+ META_RECTANGLE_LEFT = 1 << 0,
+ META_RECTANGLE_RIGHT = 1 << 1,
+ META_RECTANGLE_UP = 1 << 2,
+ META_RECTANGLE_DOWN = 1 << 3
+} MetaRectDirection;
+
+int meta_rectangle_area (const MetaRectangle *rect);
+gboolean meta_rectangle_intersect (const MetaRectangle *src1,
+ const MetaRectangle *src2,
+ MetaRectangle *dest);
+gboolean meta_rectangle_equal (const MetaRectangle *src1,
+ const MetaRectangle *src2);
+#if 0
+gboolean rectangles_intersect (const MetaRectangle *rect1,
+ const MetaRectangle *rect2);
+#endif
+gboolean region_contains_rectangle (const GList *region,
+ const MetaRectangle *rect);
+gboolean region_could_contain_rectangle (const GList *region,
+ const MetaRectangle *rect)
+gboolean move_rectangle_into_region (MetaRectangle *rect,
+ const GList *region);
+void clip_rectangle_into_region (MetaRectangle *rect,
+ const GList *region);
+void region_expand (GList *region,
+ int expand_amount,
+ MetaRectDirection directions);
+
+#endif /* META_BOXES_H */
diff --git a/src/constraints.c b/src/constraints.c
index ca36522d..32f41c8a 100644
--- a/src/constraints.c
+++ b/src/constraints.c
@@ -3,6 +3,7 @@
/*
* Copyright (C) 2002, 2003 Red Hat, Inc.
* Copyright (C) 2003, 2004 Rob Adams
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -20,1262 +21,360 @@
* 02111-1307, USA.
*/
-#include <config.h>
#include "constraints.h"
-#include "window.h"
#include "workspace.h"
#include "place.h"
-/* The way this code works was suggested by Owen Taylor.
- *
- * For any move_resize, we determine which variables are "free
- * variables" and apply constraints in terms of those. During the move
- * resize, we only want to modify those variables; otherwise the
- * constraint process can have peculiar side effects when the size and
- * position constraints interact. For example, resizing a window from
- * the top might go wrong when position constraints apply to the top
- * edge, and result in the bottom edge moving downward while the top
- * stays fixed.
- *
- * After selecting the variables we plan to vary, we define
- * each constraint on the window in terms of those variables.
- *
- * Trivial example, say we are resizing vertically from the top of the
- * window. In that case we are applying the user's mouse motion delta
- * to an original size and position, note that dy is positive to
- * resize downward:
- *
- * new_height = orig_height - dy;
- * new_y = orig_y + dy;
- *
- * A constraint that the position can't go above the top panel would
- * look like this:
- *
- * new_y >= screen_top_bound
- *
- * Substitute:
- *
- * orig_y + dy >= screen_top_bound
- *
- * Find the "boundary point" by changing to an equality:
- *
- * orig_y + dy = screen_top_bound
- *
- * Solve:
- *
- * dy = screen_top_bound - orig_y
- *
- * This dy is now the _maximum_ dy and you constrain dy with that
- * value, applying it to both the move and the resize:
- *
- * new_height = orig_height - dy;
- * new_y = orig_y + dy;
- *
- * This way the constraint is applied simultaneously to size/position,
- * so you aren't running the risk of constraining one but still
- * changing the other. i.e. we've converted an operation that may
- * modify both the Y position and the height of the window into an
- * operation that modifies a single variable, dy. That variable is
- * then constrained, rather than the constraining the Y pos and height
- * separately. This is a rather complicated fix for an obscure bug
- * that happened when resizing a window and encountering a constraint
- * such as the top edge of the screen.
- *
+/* Stupid disallowing of nested C comments makes a #if 0 mandatory... */
+#if 0
+/* There are a couple basic ideas behind how this code works:
+ *
+ * 1) Rely heavily on "workarea" (though we complicate it's meaning
+ * with overloading, unfortunately)
+ * 2) Clipping a window or shoving a window to a work_area are easy
+ * operations for people to understand and maintain
+ * 3) A uni-dimensional view of constraints doesn't work
+ * 4) Robustness can be added via constraint priorities
+ *
+ * In just a little more detail:
+ *
+ * 1) Relying on "workarea"
+ *
+ * The previous code had two workareas, work_area_xinerama (remember,
+ * in a xinerama setup "monitor" == xinerama and "all monitors" ==
+ * screen), and work_area_screen. These were mostly used for
+ * maximization constraints and determining "outermost position
+ * limits" (partially onscreen constraints). In this code they are
+ * used more heavily. They are an important part of the
+ * clip-to-workarea and shove-into-workarea methods that are part of
+ * (2). Also, I want to use them to help force fully-onscreen
+ * constraints (clamp to screensize -- bug 143145, make appear
+ * onscreen -- bug 143145 & 156699, avoid struts when placing new
+ * windows -- bug 156699 & bug 122196, don't let app resize itself off
+ * the screen -- bug 136307; see also http://xrl.us/FullyOnscreenRants
+ * which had a number of d-d-l emails from Feb 2005 about this)
+ *
+ * Two little wrinkles: (a) fully-onscreen should not be enforced in
+ * all cases (e.g. the user should be able to manually move a window
+ * offscreen, and once offscreen the fully-onscreen constraints should
+ * no longer apply until manually moved back onscreen. Application
+ * specified placement may override as well (haven't decided, but it
+ * would mean setting window->onscreen to FALSE in place.c somewhere).
+ * Also, minimum size hints might be bigger than screen size). Thus,
+ * we use a method of "growing" the workarea so that the extended
+ * region provides the constraint. (b) docks can remove otherwise
+ * valid space from the workarea. This doesn't pose much problem for
+ * docks that either span the width or the height of the screen. It
+ * does cause problems when they only span part of the width or height
+ * ("partial struts"), because then the workarea (the area used by
+ * e.g. maximized windows) leaves out some available holes that
+ * smaller windows could use. So we have an auxiliary workarea that
+ * takes these into account using a combination of
+ * get_outermost_onscreen_constraints() from the old code plus
+ * possible workarea expansion as noted in (a). Things will probably
+ * be kind of hosed for docks that appear in some small rectangle in
+ * the middle of the screen; I don't know if I even want to worry
+ * about that special case, though.
+ *
+ * 2 & 3) Clip-to-workarea and shove-into-workarea are easier methods
+ *
+ * The previous code tried to reform the constraints into terms of a
+ * single variable. This made the code rather difficult to
+ * understand. ("This is a rather complicated fix for an obscure bug
+ * that happened when resizing a window and encountering a constraint
+ * such as the top edge of the screen.") It also failed, even on the
+ * very example for which it used as justification for the complexity
+ * (bug 312104 -- when keyboard resizing the top of the window,
+ * Metacity extends the bottom once the titlebar hits the top panel),
+ * though the reason why it failed is somewhat mysterious as it should
+ * have worked. Further, it didn't really reform the constraints in
+ * terms of a single variable -- there was both an x_move_delta and an
+ * x_resize_delta, and the existence of both caused bug 109553
+ * (gravity with simultaneous move and resize doesn't work)
+ *
+ * We can reuse the example used to justify the old method in order to
+ * elucidate the problem that the old method attempted to fix, and in
+ * so doing motivate our new method: Windows are described by un upper
+ * left coordinate, a height, and a width. If a user is resizing the
+ * window from the top, then they are both changing the position of
+ * the top of the window and the height of the window simultaneously.
+ * Now, if they move the window above the top of the screen and we
+ * have a titlebar-must-be-onscreen constraint, then the resize should
+ * be stopped at the screen edge. However, the straightforward
+ * approach to trying to do this, setting upper position to 0, fails
+ * because without the window height also being decreased, the actual
+ * result is that the window grows from the bottom. The old solution
+ * was to reformulate the resize in terms of a single variable,
+ * constrain that variable, and then do the adjustments to position
+ * and height after the constraint. What would be much simpler,
+ * though, is just clipping the window to the appropriate workarea.
+ *
+ * We can't always just clip, though. If the user moves the window
+ * without resizing it so that it is offscreen, then clipping is
+ * nasty. We instead need to shove the window onscreen. The old
+ * method knew about this too and thus had separate constraints for
+ * moving and resizing, which we also obviously need.
+ *
+ * There is a little wrinkle, though. If an application does the
+ * resize instead of the user, the window should be shoved onscreen
+ * instead of clipped (see bug 136307 if you don't understand why).
+ * Also, we need to apply gravity constraints (user resizes specify
+ * both position and size implicitly, whereas app resizes only specify
+ * the size and the app can want the resize to be done relative to a
+ * different corner or side than the top left corner). The old code
+ * did not account for differences between user and application
+ * actions like this, but we need to do so to fix outstanding bugs.
+ *
+ * A small summary of how clipping-to-workarea and
+ * shoving-onto-workarea nicely solve the constrain-multiple-variables
+ * problem conceptually:
+ * user resize: clip-to-workarea
+ * user move: shove-into-(expanded)-workarea (see (1))
+ * user move & resize: who lied about what's happening or who's doing it?
+ * app resize: clamp size, gravity adjust, shove-into-workarea
+ * app move: shove-into-workarea
+ * app move & resize: clamp size, shove-into-workarea
+ *
+ * 4) Constraint priorities
+ *
+ * In the old code, if each and every constraint could not be
+ * simultaneously satisfied, then it would result in some
+ * difficult-to-predict set of constraints being violated. This was
+ * because constraints were applied in order, with the possibility for
+ * each making changes that violated previous constraints, with no
+ * checking done at the end. There may be cases where it failed even
+ * when all constraints could be satisfied (because the constraints
+ * were highly segmented).
+ *
+ * I suggest fixing that by adding priorities to all the constraints.
+ * Then, in a loop, apply all the constraints, check them all, if
+ * they're not all satisfied, increase the priority and repeat. That
+ * way we make sure the most important constraints are satisfied.
+ * Also, note that if any one given constraint is impossible to apply
+ * (e.g. if minimum size hints specify a larger screen than the real
+ * workarea making fully-onscreen impossible) then we treat the
+ * constraint as being satisfied. This sounds counter-intuitive, but
+ * the idea is that we want to satisfy as many constraints as possible
+ * and treating it as a violation means that all constraints with a
+ * lesser priority also get dropped along with the impossible one if
+ * we don't do this.
+ *
+ * Now that you understand the basic ideas behind this code, the crux of
+ * adding/modifying/removing constraints is to look at the functions called
+ * in the loop inside meta_window_constrain(). All these functions are of
+ * the following form:
+ *
+ * /* constrain_whatever does the following:
+ * * Quits (returning true) if priority is higher than PRIORITY_WHATEVER
+ * * If check_only is TRUE
+ * * Returns whether the constraint is satisfied or not
+ * * otherwise
+ * * Enforces the constraint
+ * * Note that the value of PRIORITY_WHATEVER is centralized with the
+ * * priorities of other constraints in the definition of ConstraintInfo
+ * * for easier maintenance and shuffling of priorities.
+ * */
+ * gboolean
+ * constrain_whatever (MetaWindow *window,
+ * ConstraintInfo *info,
+ * ConstraintPriority priority,
+ * gboolean check_only)
+ * {
+ * if (priority > PRIORITY_WHATEVER)
+ * return TRUE;
+ *
+ * /* Determine whether constraint applies; note that if the constraint
+ * * cannot possibly be satisfied, constraint_applies should be set to
+ * * false. If we don't do this, all constraints with a lesser priority
+ * * will be dropped along with this one, and we'd rather apply as many as
+ * * possible.
+ * */
+ * if (!constraint_applies)
+ * return TRUE;
+ *
+ * /* Determine whether constraint is already satisfied; if we're only
+ * * checking the status of whether the constraint is satisfied, we end
+ * * here.
+ * */
+ * if (check_only || constraint_already_satisfied)
+ * return constraint_already_satisfied;
+ *
+ * /* Enforce constraints */
+ * return TRUE; /* Note that we exited early if check_only is FALSE; also,
+ * * we know we can return TRUE here because we exited early
+ * * if the constraint could not be satisfied. */
+ * }
*/
-
-
-/* To adjust for window gravity, such as a client moving itself to the
- * southeast corner, we want to compute the gravity reference point
- * - (screen_width,screen_height) in the SE corner case - using the
- * size the client had in its configure request. But then we want
- * to compute the actual position we intend to land on using
- * the real constrained dimensions of the window.
- *
- * So for a window being placed in the SE corner and simultaneously
- * resized, we get the gravity reference point, then compute where the
- * window should go to maintain that ref. point at its current size
- * instead of at the requested size, and conceptually move the window
- * to the requested ref. point but at its current size, without
- * applying any constraints. Then we constrain it with the top and
- * left edges as the edges that vary, with a dx/dy that are the delta
- * from the current size to the requested size.
- *
- * This method applies to any ConfigureRequest that does a simultaneous
- * move/resize.
- *
- * We use the same method to e.g. maximize a window; if the window is
- * maximized, we want to MOVE_VERTICAL/MOVE_HORIZONTAL to the top
- * center of the screen, then RESIZE_BOTTOM and
- * RESIZE_HORIZONTAL_CENTER. i.e. essentially NorthGravity.
- *
- */
-
-#define FLOOR(value, base) ( ((int) ((value) / (base))) * (base) )
+#endif
+
+typedef enum FIXME--there's more than this...
+{
+ PRIORITY_MINIMUM=0, # Dummy value used for loop start = min(all priorities)
+ PRIORITY_ASPECT_RATIO=0,
+ PRIORITY_CLAMP_TO_WORKAREA=1,
+ PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA=1,
+ PRIORITY_GRAVITY_ADJUST=1,
+ PRIORITY_SIZE_HINTS_INCREMENTS=1,
+ PRIORITY_MAXIMIZATION=2,
+ PRIORITY_FULLSCREEN=2
+ PRIORITY_SIZE_HINTS_LIMITS=3,
+ PRIORITY_TITLEBAR_PARTIALLY_VISIBLE_ON_WORKAREA=4
+ PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA=4,
+ PRIORITY_MAXIMUM=4 # Dummy value used for loop end = max(all priorities)
+} ConstraintPriority;
typedef struct
{
- MetaWindow *window;
+ MetaRectangle orig;
+ MetaRectangle current;
MetaFrameGeometry fgeom;
- const MetaXineramaScreenInfo *xinerama;
- MetaRectangle work_area_xinerama;
- MetaRectangle work_area_screen;
- int nw_x, nw_y, se_x, se_y; /* these are whole-screen not xinerama */
+ int gravity;
+ MetaRectangle work_area_xinerama; /* current xinerama only */
+ MetaRectangle work_area_screen; /* all xineramas */
+ const MetaXineramaScreenInfo *xinerama_info;
} ConstraintInfo;
-/* (FIXME instead of TITLEBAR_LENGTH_ONSCREEN, get the actual
- * size of the menu control?).
- */
-
-#define TITLEBAR_LENGTH_ONSCREEN 75
-
-typedef gboolean (* MetaConstraintAppliesFunc) (MetaWindow *window);
-
-/* There's a function for each case with a different "free variable" */
-typedef void (* MetaConstrainTopFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta);
-typedef void (* MetaConstrainBottomFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta);
-typedef void (* MetaConstrainVCenterFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta);
-typedef void (* MetaConstrainLeftFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta);
-typedef void (* MetaConstrainRightFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta);
-typedef void (* MetaConstrainHCenterFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta);
-typedef void (* MetaConstrainMoveFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta,
- int *y_delta);
-
-typedef struct
-{
- const char *name;
- MetaConstraintAppliesFunc applies_func;
- MetaConstrainTopFunc top_func;
- MetaConstrainBottomFunc bottom_func;
- MetaConstrainVCenterFunc vcenter_func;
- MetaConstrainLeftFunc left_func;
- MetaConstrainRightFunc right_func;
- MetaConstrainHCenterFunc hcenter_func;
- MetaConstrainMoveFunc move_func;
-} Constraint;
-
-/* "Is the desktop window" constraint:
- *
- * new_x = 0;
- * new_y = 0;
- * new_w = orig_width;
- * new_h = orig_height;
- *
- * Note that if we are applying a resize constraint,
- * e.g. constraint_desktop_top_func, this is kind of broken since we
- * end up resizing the window in order to get its position right. But
- * that case shouldn't happen in practice.
- */
-static gboolean
-constraint_desktop_applies_func (MetaWindow *window)
-{
- return window->type == META_WINDOW_DESKTOP;
-}
-
-static void
-constraint_desktop_top_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- *y_delta = 0 - orig->y;
-}
-
-static void
-constraint_desktop_bottom_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- /* nothing */
-}
-
-static void
-constraint_desktop_vcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- *y_delta = 0 - orig->y;
-}
-
-static void
-constraint_desktop_left_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- *x_delta = 0 - orig->x;
-}
-
-static void
-constraint_desktop_right_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- /* nothing */
-}
-
-static void
-constraint_desktop_hcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- *x_delta = 0 - orig->x;
-}
-
-static void
-constraint_desktop_move_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta,
- int *y_delta)
-{
- *x_delta = 0 - orig->x;
- *y_delta = 0 - orig->y;
-}
-
-static const Constraint constraint_desktop = {
- "Desktop",
- constraint_desktop_applies_func,
- constraint_desktop_top_func,
- constraint_desktop_bottom_func,
- constraint_desktop_vcenter_func,
- constraint_desktop_left_func,
- constraint_desktop_right_func,
- constraint_desktop_hcenter_func,
- constraint_desktop_move_func
-};
-
-/* Titlebar is onscreen constraint:
- *
- * Constants:
- * titlebar_width_onscreen = amount of titlebar width that has to be onscreen
- * nw_x, nw_y = left/top edges that titlebar can't go outside
- * se_x, se_y = right/bottom edges
- *
- * NW limit has priority over SE, since titlebar is on NW
- *
- * Left resize
- * ===
- *
- * new_width = orig_width - dx
- * new_x = orig_x + dx
- *
- * Amount of window+frame that doesn't fit in the work area:
- *
- * offscreen_width = left_width + new_width + right_width - (se_x - nw_x)
- *
- * If we keep the old metacity rule where a window can be offscreen by
- * offscreen_width, then the math works out that left/top resizes are not
- * constrained. If we instead have a rule where the window can never be offscreen,
- * you get the following:
- *
- * new_x >= nw_x + left_width + titlebar_width_offscreen
- * orig_x + dx >= nw_x + left_width + titlebar_width_onscreen
- * dx >= nw_x + left_width + titlebar_width_onscreen - orig_x
- *
- * i.e. the minimum dx is: nw_x + left_width + titlebar_width_onscreen - orig_x
- *
- * We could have a more complicated rule that constrains only if the current
- * offscreen width is positive, thus allowing something more like the old
- * behavior, but not doing that for now.
- *
- * Top resize works the same as left resize. Right/bottom resize don't have a limit
- * because the constraint is designed to keep the top left corner of the
- * window or its titlebar on the screen, and right/bottom resize will never move that
- * area. Center resize is almost like left/top but dx has the opposite sign
- * and new_width = orig_width + 2dx.
- *
- * For right/bottom we can try to handle windows that aren't in a valid
- * location to begin with:
- *
- * new_x <= se_x - titlebar_width_onscreen
- * dx <= se_x - titlebar_width_onscreen - orig_x
- *
- * but in principle this constraint is never triggered.
- *
- * Vertical move
- * ===
- *
- * new_height = orig_height
- * new_y = orig_y + dy
- *
- * new_y >= nw_y + top_height
- *
- * Min negative dy (nw_y + top_height - orig_y) just as with top resize.
- * Max positive dy has to be computed from se_y and given less priority than the
- * min negative:
- *
- * new_y < se_y
- * orig_y + dy = se_y
- * so max dy is (se_y - orig_y)
- *
- * Horizontal move is equivalent to vertical.
- *
- */
-
-static gboolean
-constraint_onscreen_applies_func (MetaWindow *window)
-{
- return
- !window->fullscreen &&
- window->type != META_WINDOW_DESKTOP &&
- window->type != META_WINDOW_DOCK;
-}
-
-static void
-get_outermost_onscreen_positions (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int delta_x,
- int delta_y,
- int *leftmost_x_p,
- int *rightmost_x_p,
- int *topmost_y_p,
- int *bottommost_y_p)
-{
- GList *workspaces;
- GList *tmp;
- GSList *stmp;
- MetaRectangle current;
- int bottommost_y;
-
- /* to handle struts, we get the list of workspaces for the window
- * and traverse all the struts in each of the cached strut lists for
- * the workspaces. Note that because the workarea has already been
- * computed, these strut lists should already be up to date. This function
- * should have good performance since we call it a lot.
- */
-
- current = *orig;
- current.x += delta_x;
- current.y += delta_y;
-
- workspaces = meta_window_get_workspaces (window);
- tmp = workspaces;
-
- if (leftmost_x_p)
- {
- *leftmost_x_p = info->nw_x;
- while (tmp)
- {
- stmp = ((MetaWorkspace*) tmp->data)->left_struts;
- while (stmp)
- {
- MetaRectangle *rect = (MetaRectangle*) stmp->data;
- /* the strut only matters if the title bar is
- * overlapping the strut rect.
- */
- if (((current.y - info->fgeom.top_height >= rect->y) &&
- (current.y - info->fgeom.top_height < rect->y + rect->height)) ||
- ((current.y >= rect->y) &&
- (current.y < rect->y + rect->height)))
- {
- *leftmost_x_p = MAX (*leftmost_x_p, rect->width);
- }
-
- stmp = stmp->next;
- }
-
- tmp = tmp->next;
- }
-
- *leftmost_x_p = *leftmost_x_p - current.width +
- MIN (TITLEBAR_LENGTH_ONSCREEN, current.width);
- }
-
- tmp = workspaces;
- if (rightmost_x_p)
- {
- *rightmost_x_p = info->se_x;
- while (tmp)
- {
- stmp = ((MetaWorkspace*) tmp->data)->right_struts;
- while (stmp)
- {
- MetaRectangle *rect = (MetaRectangle*) stmp->data;
- /* the strut only matters if the title bar is
- * overlapping the strut rect.
- */
- if (((current.y - info->fgeom.top_height >= rect->y) &&
- (current.y - info->fgeom.top_height < rect->y + rect->height)) ||
- ((current.y >= rect->y) &&
- (current.y < rect->y + rect->height)))
- {
- *rightmost_x_p = MIN (*rightmost_x_p, rect->x);
- }
-
- stmp = stmp->next;
- }
-
- tmp = tmp->next;
- }
-
- *rightmost_x_p = *rightmost_x_p -
- MIN (TITLEBAR_LENGTH_ONSCREEN, current.width);
- }
-
- tmp = workspaces;
- if (topmost_y_p)
- {
- *topmost_y_p = info->nw_y;
- while (tmp)
- {
- stmp = ((MetaWorkspace*) tmp->data)->top_struts;
- while (stmp)
- {
- MetaRectangle *rect = (MetaRectangle*) stmp->data;
- /* here the strut matters if the titlebar is overlapping
- * the window horizontally
- */
- if ((current.x < rect->x + rect->width) &&
- (current.x + current.width > rect->x))
- {
- *topmost_y_p = MAX (*topmost_y_p, rect->height);
- }
-
- stmp = stmp->next;
- }
-
- tmp = tmp->next;
- }
-
- *topmost_y_p = *topmost_y_p + info->fgeom.top_height;
- }
-
- tmp = workspaces;
- bottommost_y = G_MAXUSHORT;
- if (bottommost_y_p || topmost_y_p)
- {
- bottommost_y = info->se_y;
- while (tmp)
- {
- stmp = ((MetaWorkspace*) tmp->data)->bottom_struts;
- while (stmp)
- {
- MetaRectangle *rect = (MetaRectangle*) stmp->data;
- /* here the strut matters if the titlebar is overlapping
- * the window horizontally
- */
- if ((current.x < rect->x + rect->width) &&
- (current.x + current.width > rect->x))
- {
- bottommost_y = MIN (bottommost_y, rect->y);
- }
-
- stmp = stmp->next;
- }
-
- tmp = tmp->next;
- }
- }
-
- if (bottommost_y_p)
- {
- *bottommost_y_p = bottommost_y;
-
- /* If no frame, keep random TITLEBAR_LENGTH_ONSCREEN pixels on the
- * screen.
- */
- if (!window->frame)
- *bottommost_y_p = *bottommost_y_p -
- MIN (TITLEBAR_LENGTH_ONSCREEN, current.height);
- }
-
- /* if the window has a minimum size too big for the "effective" work
- * area let it "cheat" a little by allowing a user to move it up so
- * that you can see the bottom of the window.
- */
- if (topmost_y_p)
- {
- int minheight;
-
- if (window->frame)
- {
- /* this is the "normal" case of, e.g. a dialog that's
- * just too big for the work area
- */
- minheight = window->frame->bottom_height +
- window->size_hints.min_height;
- }
- else
- {
- /* let frameless windows move offscreen is too large for the
- * effective work area. This may include windows that try
- * to make themselves full screen by removing the
- * decorations and repositioning themselves.
- */
- minheight = orig->height;
- }
-
- if (minheight > (bottommost_y - *topmost_y_p))
- *topmost_y_p = bottommost_y - minheight;
- }
-}
-
-static void
-constraint_onscreen_top_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- int min_dy;
- int topmost_y;
-
- get_outermost_onscreen_positions (window, info, orig, 0, *y_delta,
- NULL, NULL, &topmost_y, NULL);
-
- min_dy = topmost_y - orig->y;
-
- if (*y_delta < min_dy)
- *y_delta = min_dy;
-}
-
-static void
-constraint_onscreen_bottom_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- /* no way to resize off the bottom so that constraints are
- violated */
- return;
-}
-
-static void
-constraint_onscreen_vcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- int max_dy;
- int topmost_y;
-
- get_outermost_onscreen_positions (window, info, orig, 0, *y_delta,
- NULL, NULL, &topmost_y, NULL);
-
- max_dy = orig->y - topmost_y;
-
- if (*y_delta > max_dy)
- *y_delta = max_dy;
-}
-
-static void
-constraint_onscreen_left_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- /* no way to resize off the sides so that constraints are violated
- */
- return;
-}
-
-static void
-constraint_onscreen_right_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- /* no way to resize off the sides so that constraints are violated
- */
- return;
-}
-
-static void
-constraint_onscreen_hcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- /* no way to resize off the sides so that constraints are violated
- */
- return;
-}
-
-static void
-constraint_onscreen_move_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta,
- int *y_delta)
-{
- int min_delta;
- int max_delta;
- int leftmost_x, rightmost_x, topmost_y, bottommost_y;
-
- get_outermost_onscreen_positions (window, info, orig, *x_delta, *y_delta,
- &leftmost_x, &rightmost_x,
- &topmost_y, &bottommost_y);
-
- min_delta = topmost_y - orig->y;
- max_delta = bottommost_y - orig->y;
-
- /* Note that min delta (top left) has priority over
- * max delta (bottom right) to facilitate keeping
- * titlebar on the screen
- */
- if (*y_delta > max_delta)
- *y_delta = max_delta;
- if (*y_delta < min_delta)
- *y_delta = min_delta;
-
- min_delta = leftmost_x - orig->x;
- max_delta = rightmost_x - orig->x;
-
- if (*x_delta > max_delta)
- *x_delta = max_delta;
- if (*x_delta < min_delta)
- *x_delta = min_delta;
-}
-
-static const Constraint constraint_onscreen = {
- "Onscreen",
- constraint_onscreen_applies_func,
- constraint_onscreen_top_func,
- constraint_onscreen_bottom_func,
- constraint_onscreen_vcenter_func,
- constraint_onscreen_left_func,
- constraint_onscreen_right_func,
- constraint_onscreen_hcenter_func,
- constraint_onscreen_move_func
-};
-
-
-/* Size hints constraints:
- *
- * For min/max size we just clamp to those, and for resize increment
- * we clamp to the one at or below the requested place.
- *
- * For aspect ratio, we special-case it at the end of
- * meta_window_constrain, because it involves both dimensions, and
- * thus messes up our generic framework.
- *
- * Left resize can be solved for dx like this:
- * new_width = orig_width - dx
- * new_x = orig_x + dx
- *
- * new_width >= min_width
- * orig_width - dx >= min_width
- * - dx >= min_width - orig_width
- * dx <= orig_width - min_width
- *
- * new_width <= max_width
- * orig_width - dx <= max_width
- * - dx <= max_width - orig_width
- * dx >= orig_width - max_width
- *
- */
-
-#define USE_HINTS_FOR_WINDOW_STATE(window) (!((window)->fullscreen || (window)->maximized))
-
-static gboolean
-constraint_hints_applies_func (MetaWindow *window)
-{
- return USE_HINTS_FOR_WINDOW_STATE (window);
-}
-
-static void
-constraint_hints_top_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- int min_dy;
- int max_dy;
- int height;
-
- max_dy = orig->height - window->size_hints.min_height;
- min_dy = orig->height - window->size_hints.max_height;
-
- g_assert (max_dy >= min_dy);
-
- if (*y_delta > max_dy)
- *y_delta = max_dy;
- if (*y_delta < min_dy)
- *y_delta = min_dy;
-
- /* shrink to base + N * inc
- */
- height = orig->height - *y_delta;
- height = window->size_hints.base_height +
- FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
-
- *y_delta = orig->height - height;
-}
-
-static void
-constraint_hints_bottom_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- int min_dy;
- int max_dy;
- int height;
-
- min_dy = window->size_hints.min_height - orig->height;
- max_dy = window->size_hints.max_height - orig->height;
-
- g_assert (max_dy >= min_dy);
-
- if (*y_delta > max_dy)
- *y_delta = max_dy;
- if (*y_delta < min_dy)
- *y_delta = min_dy;
-
- /* shrink to base + N * inc
- */
- height = orig->height + *y_delta;
- height = window->size_hints.base_height +
- FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
-
- *y_delta = height - orig->height;
-}
-
-static void
-constraint_hints_vcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- int min_dy;
- int max_dy;
- int height;
-
- /* Remember our delta is negative to shrink window, positive to
- * grow it, and the actual resize is y_delta * 2 (which is broken,
- * but that's how it currently is)
- */
-
- min_dy = (window->size_hints.min_height - orig->height) / 2;
- max_dy = (window->size_hints.max_height - orig->height) / 2;
-
- g_assert (max_dy >= min_dy);
-
- if (*y_delta > max_dy)
- *y_delta = max_dy;
- if (*y_delta < min_dy)
- *y_delta = min_dy;
-
- /* shrink to base + N * inc
- */
- height = orig->height + *y_delta * 2;
- height = window->size_hints.base_height +
- FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
-
- *y_delta = (height - orig->height) / 2;
-}
-
-static void
-constraint_hints_left_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- int min_dx;
- int max_dx;
- int width;
-
- max_dx = orig->width - window->size_hints.min_width;
- min_dx = orig->width - window->size_hints.max_width;
-
- g_assert (max_dx >= min_dx);
-
- if (*x_delta > max_dx)
- *x_delta = max_dx;
- if (*x_delta < min_dx)
- *x_delta = min_dx;
-
- /* shrink to base + N * inc
- */
- width = orig->width - *x_delta;
- width = window->size_hints.base_width +
- FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
-
- *x_delta = orig->width - width;
-}
-
-static void
-constraint_hints_right_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- int min_dx;
- int max_dx;
- int width;
-
- min_dx = window->size_hints.min_width - orig->width;
- max_dx = window->size_hints.max_width - orig->width;
-
- g_assert (max_dx >= min_dx);
-
- if (*x_delta > max_dx)
- *x_delta = max_dx;
- if (*x_delta < min_dx)
- *x_delta = min_dx;
-
- /* shrink to base + N * inc
- */
- width = orig->width + *x_delta;
- width = window->size_hints.base_width +
- FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
-
- *x_delta = width - orig->width;
-}
-
-static void
-constraint_hints_hcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- int min_dx;
- int max_dx;
- int width;
-
- /* Remember our delta is negative to shrink window, positive to
- * grow it, and the actual resize is x_delta * 2 (which is broken,
- * but that's how it currently is)
- */
-
- min_dx = (window->size_hints.min_width - orig->width) / 2;
- max_dx = (window->size_hints.max_width - orig->width) / 2;
-
- g_assert (max_dx >= min_dx);
-
- if (*x_delta > max_dx)
- *x_delta = max_dx;
- if (*x_delta < min_dx)
- *x_delta = min_dx;
-
- /* shrink to base + N * inc
- */
- width = orig->width + *x_delta * 2;
- width = window->size_hints.base_width +
- FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
-
- *x_delta = (width - orig->width) / 2;
-}
-
-static void
-constraint_hints_move_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta,
- int *y_delta)
-{
- /* nothing */
-}
-
-static const Constraint constraint_hints = {
- "Hints",
- constraint_hints_applies_func,
- constraint_hints_top_func,
- constraint_hints_bottom_func,
- constraint_hints_vcenter_func,
- constraint_hints_left_func,
- constraint_hints_right_func,
- constraint_hints_hcenter_func,
- constraint_hints_move_func
-};
-
-/* Array of all constraints at once */
-static const Constraint *all_constraints[] = {
- &constraint_desktop,
- &constraint_onscreen,
- &constraint_hints,
- NULL
-};
-
-/* Move with no accompanying change to window size */
-static void
-constrain_move (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int x_delta,
- int y_delta,
- MetaRectangle *new)
-{
- const Constraint **cp;
- int old_x, old_y;
- int paranoia;
-
- /* Evidence that we can't actually prove this algorithm is right */
-#define MAX_ITERATIONS 10
- paranoia = 0;
-
- do {
- old_x = x_delta;
- old_y = y_delta;
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d %d (Move constraint '%s')\n",
- x_delta, y_delta, (*cp)->name);
+static gboolean constrain_gravity_adjust (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_maximization (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_fullscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_clamp_size (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_1d_size_hints (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_aspect_ratio (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_fully_onscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_titlebar_onscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+
+static void setup_constraint_info (ConstraintInfo *info,
+ MetaWindow *window,
+ MetaFrameGeometry *orig_fgeom,
+ gboolean adjust_for_gravity,
+ int gravity,
+ gboolean is_user_interaction,
+ const MetaRectangle *orig,
+ MetaRectangle *new);
+static void place_window_if_needed (MetaWindow *window,
+ ConstraintInfo info)
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->move_func) (window, info, orig,
- &x_delta, &y_delta);
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d %d (Move constraint '%s')\n",
- x_delta, y_delta, (*cp)->name);
-
- ++cp;
- }
- ++paranoia;
- } while (((old_x != x_delta) || (old_y != y_delta)) && paranoia < MAX_ITERATIONS);
-
- new->x = orig->x + x_delta;
- new->y = orig->y + y_delta;
-
- if (paranoia >= MAX_ITERATIONS)
- meta_topic (META_DEBUG_GEOMETRY,
- "Constraints were never satisfied for window %s\n",
- window->desc);
-}
-
-static void
-constrain_resize_left (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int x_delta,
- MetaRectangle *new)
-{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (Left constraint '%s')\n",
- x_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->left_func) (window, info, orig,
- &x_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (Left constraint '%s')\n",
- x_delta, (*cp)->name);
-
- ++cp;
- }
-
- /* Moving mouse from 10 to 5 means current - orig means 5 - 10 means
- * a delta of -5
- */
- new->x = orig->x + x_delta;
- new->width = orig->width - x_delta;
-}
-
-static void
-constrain_resize_hcenter (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int x_delta,
- MetaRectangle *new)
-{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (HCenter constraint '%s')\n",
- x_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->hcenter_func) (window, info, orig,
- &x_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (HCenter constraint '%s')\n",
- x_delta, (*cp)->name);
-
- ++cp;
- }
-
- /* center deltas are positive to grow the window and negative to
- * shrink it.
- */
- new->x = orig->x - x_delta;
- new->width = orig->width + x_delta * 2;
- /* FIXME above implies that with center gravity you have to grow
- * in increments of two
- */
-}
-
-static void
-constrain_resize_right (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int x_delta,
- MetaRectangle *new)
-{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (Right constraint '%s')\n",
- x_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->right_func) (window, info, orig,
- &x_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (Right constraint '%s')\n",
- x_delta, (*cp)->name);
-
- ++cp;
- }
-
- new->width = orig->width + x_delta;
-}
-
-static void
-constrain_resize_top (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int y_delta,
- MetaRectangle *new)
-{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (Top constraint '%s')\n",
- y_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->top_func) (window, info, orig,
- &y_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (Top constraint '%s')\n",
- y_delta, (*cp)->name);
-
- ++cp;
- }
-
- new->y = orig->y + y_delta;
- new->height = orig->height - y_delta;
-}
-
-static void
-constrain_resize_vcenter (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int y_delta,
- MetaRectangle *new)
-{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (VCenter constraint '%s')\n",
- y_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->vcenter_func) (window, info, orig,
- &y_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (VCenter constraint '%s')\n",
- y_delta, (*cp)->name);
-
- ++cp;
- }
-
- /* center deltas are positive to grow the window and negative to
- * shrink it.
- */
- new->y = orig->y - y_delta;
- new->height = orig->height + y_delta * 2;
- /* FIXME above implies that with center gravity you have to grow
- * in increments of two
- */
-}
-
-static void
-constrain_resize_bottom (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int y_delta,
- MetaRectangle *new)
-{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (Bottom constraint '%s')\n",
- y_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->bottom_func) (window, info, orig,
- &y_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (Bottom constraint '%s')\n",
- y_delta, (*cp)->name);
-
- ++cp;
- }
-
- new->height = orig->height + y_delta;
-}
-
-static void
-update_position_limits (MetaWindow *window,
- ConstraintInfo *info)
-{
- int nw_x, nw_y;
- int se_x, se_y;
-
- /* For maximized windows the limits are the work area, for
- * other windows we see which struts apply based on the
- * window's position later on
- */
- if (window->maximized)
- {
- nw_x = MIN (info->work_area_xinerama.x, info->work_area_screen.x);
- nw_y = MIN (info->work_area_xinerama.y, info->work_area_screen.y);
-
- /* find bottom-right corner of workarea */
- se_x = MAX (info->work_area_xinerama.x + info->work_area_xinerama.width,
- info->work_area_screen.x + info->work_area_screen.width);
- se_y = MAX (info->work_area_xinerama.y + info->work_area_xinerama.height,
- info->work_area_screen.y + info->work_area_screen.height);
- }
- else
- {
- nw_x = 0;
- nw_y = 0;
- se_x = window->screen->width;
- se_y = window->screen->height;
- }
-
- /* If we have a micro-screen or huge frames maybe nw/se got
- * swapped
- */
- if (nw_x > se_x)
- {
- int tmp = nw_x;
- nw_x = se_x;
- se_x = tmp;
- }
-
- if (nw_y > se_y)
- {
- int tmp = nw_y;
- nw_y = se_y;
- se_y = tmp;
- }
-
- info->nw_x = nw_x;
- info->nw_y = nw_y;
- info->se_x = se_x;
- info->se_y = se_y;
-}
-
-/* The delta values are the mouse motion distance deltas,
- * i.e. mouse_current_pos - mouse_orig_pos, for resizing on
- * the sides, or moving. For center resize, the delta
- * value is positive to grow the window and negative to
- * shrink it (while the sign of the mouse delta
- * depends on which side of the window you are center resizing
- * from)
- */
void
meta_window_constrain (MetaWindow *window,
MetaFrameGeometry *orig_fgeom,
+ gboolean adjust_for_gravity,
+ int gravity,
+ gboolean is_user_interaction,
const MetaRectangle *orig,
- int x_move_delta,
- int y_move_delta,
- MetaResizeDirection x_direction,
- int x_delta,
- MetaResizeDirection y_direction,
- int y_delta,
MetaRectangle *new)
{
ConstraintInfo info;
MetaRectangle current;
- gboolean did_placement;
-
-#define OUTER_WIDTH(rect) ((rect).width + info.fgeom.left_width + info.fgeom.right_width)
-#define OUTER_HEIGHT(rect) ((rect).height + info.fgeom.top_height + info.fgeom.bottom_height)
meta_topic (META_DEBUG_GEOMETRY,
- "Constraining %s x_move_delta = %d y_move_delta = %d x_direction = %d y_direction = %d x_delta = %d y_delta = %d orig %d,%d %dx%d\n",
- window->desc, x_move_delta, y_move_delta,
- x_direction, y_direction, x_delta, y_delta,
- orig->x, orig->y, orig->width, orig->height);
+ "Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d\n",
+ window->desc,
+ orig->x, orig->y, orig->width, orig->height,
+ new->x, new->y, new->width, new->height);
+
+ setup_constraint_info (&info,
+ window,
+ orig_fgeom,
+ adjust_for_gravity,
+ gravity,
+ is_user_interaction,
+ orig,
+ new);
+ place_window_if_needed (window, info);
+
+ ConstraintPriority priority = PRIORITY_MINIMUM;
+ gboolean satisfied = false;
+ while (!satisfied && priority <= PRIORITY_MAXIMUM) {
+ gboolean check_only = FALSE;
+ constrain_gravity_adjust (window, &info, priority, check_only);
+ constrain_maximization (window, &info, priority, check_only);
+ constrain_fullscreen (window, &info, priority, check_only);
+ constrain_clamp_size (window, &info, priority, check_only);
+ constrain_1d_size_hints (window, &info, priority, check_only);
+ constrain_aspect_ratio (window, &info, priority, check_only);
+ constrain_fully_onscreen (window, &info, priority, check_only);
+ constrain_titlebar_onscreen (window, &info, priority, check_only);
+
+ check_only = TRUE;
+ satisfied =
+ constrain_gravity_adjust (window, &info, priority, check_only) &&
+ constrain_maximization (window, &info, priority, check_only) &&
+ constrain_fullscreen (window, &info, priority, check_only) &&
+ constrain_clamp_size (window, &info, priority, check_only) &&
+ constrain_1d_size_hints (window, &info, priority, check_only) &&
+ constrain_aspect_ratio (window, &info, priority, check_only) &&
+ constrain_fully_onscreen (window, &info, priority, check_only) &&
+ constrain_titlebar_onscreen (window, &info, priority, check_only);
+
+ priority++;
+ }
+}
+
+static void
+setup_constraint_info (ConstraintInfo *info,
+ MetaWindow *window,
+ MetaFrameGeometry *orig_fgeom,
+ gboolean adjust_for_gravity,
+ int gravity,
+ gboolean is_user_interaction,
+ const MetaRectangle *orig,
+ MetaRectangle *new)
+{
+ info->orig = *orig;
+ info->current = *new;
/* Create a fake frame geometry if none really exists */
if (orig_fgeom && !window->fullscreen)
- info.fgeom = *orig_fgeom;
+ info->fgeom = *orig_fgeom;
else
{
- info.fgeom.top_height = 0;
- info.fgeom.bottom_height = 0;
- info.fgeom.left_width = 0;
- info.fgeom.right_width = 0;
+ info->fgeom.top_height = 0;
+ info->fgeom.bottom_height = 0;
+ info->fgeom.left_width = 0;
+ info->fgeom.right_width = 0;
}
- meta_window_get_work_area_current_xinerama (window, &info.work_area_xinerama);
- meta_window_get_work_area_all_xineramas (window, &info.work_area_screen);
+ info->adjust_for_gravity = adjust_for_gravity;
+ info->gravity = gravity;
- info.window = window;
- info.xinerama = meta_screen_get_xinerama_for_window (window->screen,
- window);
- /* Init info->nw_x etc. */
- update_position_limits (window, &info);
+ meta_window_get_work_area_current_xinerama (window, &info->work_area_xinerama);
+ meta_window_get_work_area_all_xineramas (window, &info->work_area_screen);
+
+ info->xinerama_info =
+ meta_screen_get_xinerama_for_window (window->screen, window);
+}
- current = *orig;
- *new = current;
+static void
+place_window_if_needed(MetaWindow *window,
+ ConstraintInfo info)
+{
+ gboolean did_placement;
/* Do placement if any, so we go ahead and apply position
* constraints in a move-only context. Don't place
@@ -1288,9 +387,9 @@ meta_window_constrain (MetaWindow *window,
!window->maximized &&
!window->fullscreen)
{
- MetaRectangle placed_rect = current;
+ MetaRectangle placed_rect = info.orig;
- meta_window_place (window, orig_fgeom, current.x, current.y,
+ meta_window_place (window, orig_fgeom, info.orig.x, info.orig.y,
&placed_rect.x, &placed_rect.y);
did_placement = TRUE;
@@ -1298,22 +397,13 @@ meta_window_constrain (MetaWindow *window,
* new xinerama and update the ConstraintInfo
*/
info.xinerama = meta_screen_get_xinerama_for_rect (window->screen,
- &placed_rect);
+ &placed_rect);
meta_window_get_work_area_for_xinerama (window,
info.xinerama->number,
&info.work_area_xinerama);
- update_position_limits (window, &info);
-
- constrain_move (window, &info, &current,
- placed_rect.x - current.x,
- placed_rect.y - current.y,
- new);
- current = *new;
-
- /* Ignore any non-placement movement */
- x_move_delta = 0;
- y_move_delta = 0;
+ info.current.x = placed_rect.x;
+ info.current.y = placed_rect.y;
}
if (window->maximize_after_placement &&
@@ -1321,292 +411,98 @@ meta_window_constrain (MetaWindow *window,
{
window->maximize_after_placement = FALSE;
- if (OUTER_WIDTH (*new) >= info.work_area_xinerama.width &&
- OUTER_HEIGHT (*new) >= info.work_area_xinerama.height)
- {
- /* define a sane saved_rect so that the user can unmaximize
- * to something reasonable.
- */
- new->width = .75 * info.work_area_xinerama.width;
- new->height = .75 * info.work_area_xinerama.height;
- new->x = info.work_area_xinerama.x + .125 * info.work_area_xinerama.width;
- new->y = info.work_area_xinerama.y + .083 * info.work_area_xinerama.height;
- }
+ if (info.current.width >= info.work_area_xinerama.width &&
+ info.current.height >= info.work_area_xinerama.height)
+ {
+ /* define a sane saved_rect so that the user can unmaximize
+ * to something reasonable.
+ */
+ new->width = .75 * info.work_area_xinerama.width;
+ new->height = .75 * info.work_area_xinerama.height;
+ new->x = info.work_area_xinerama.x +
+ .125 * info.work_area_xinerama.width;
+ new->y = info.work_area_xinerama.y +
+ .083 * info.work_area_xinerama.height;
+ }
meta_window_maximize_internal (window, new);
/* maximization may have changed frame geometry */
- if (orig_fgeom && !window->fullscreen)
- {
- meta_frame_calc_geometry (window->frame,
- orig_fgeom);
- info.fgeom = *orig_fgeom;
- }
+ if (window->frame && !window->fullscreen)
+ meta_frame_calc_geometry (window->frame,
+ &info.fgeom);
}
+}
- /* Maximization, fullscreen, etc. are defined as a resize followed by
- * a move, as explained in one of the big comments at the top of
- * this file.
+static gboolean
+constrain_gravity_adjust (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
+{
+ if (priority > PRIORITY_GRAVITY_ADJUST)
+ return TRUE;
+
+ /* Exit if constraint doesn't apply */
+ if (!info->adjust_for_gravity)
+ return TRUE;
+
+ /* The only way you could have a situation that could be interpreted as
+ * violating this constraint is if the window is near the screen edge
+ * (but is fully onscreen) and adjusting according to gravity would send
+ * it off--but the onscreen constraints shove it back on. I guess I
+ * could check old positions of the window versus new positions, but I
+ * don't care that much--I'll just say it always is.
*/
- if (window->fullscreen)
- {
- current = *new;
- constrain_resize_bottom (window, &info, &current,
- (info.xinerama->height - OUTER_HEIGHT (current)),
- new);
-
- current = *new;
-
- constrain_resize_right (window, &info, &current,
- info.xinerama->width - OUTER_WIDTH (current),
- new);
- current = *new;
-
- constrain_move (window, &info, &current,
- info.xinerama->x_origin - current.x + info.fgeom.left_width,
- info.xinerama->y_origin - current.y + info.fgeom.top_height,
- new);
- }
- else if (window->maximized)
- {
- constrain_resize_bottom (window, &info, &current,
- (info.work_area_xinerama.height - OUTER_HEIGHT (current)),
- new);
-
- current = *new;
-
- constrain_resize_right (window, &info, &current,
- info.work_area_xinerama.width - OUTER_WIDTH (current),
- new);
- current = *new;
+ if (check_only)
+ return TRUE;
- constrain_move (window, &info, &current,
- info.work_area_xinerama.x - current.x + info.fgeom.left_width,
- info.work_area_xinerama.y - current.y + info.fgeom.top_height,
- new);
-
- current = *new;
- }
- else
- {
- switch (x_direction)
- {
- case META_RESIZE_LEFT_OR_TOP:
- constrain_resize_left (window, &info, &current,
- x_delta, new);
- break;
- case META_RESIZE_CENTER:
- constrain_resize_hcenter (window, &info, &current,
- x_delta, new);
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- constrain_resize_right (window, &info, &current,
- x_delta, new);
- break;
- }
-
- switch (y_direction)
- {
- case META_RESIZE_LEFT_OR_TOP:
- constrain_resize_top (window, &info, &current,
- y_delta, new);
- break;
- case META_RESIZE_CENTER:
- constrain_resize_vcenter (window, &info, &current,
- y_delta, new);
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- constrain_resize_bottom (window, &info, &current,
- y_delta, new);
- break;
- }
-
- current = *new;
-
- constrain_move (window, &info, &current,
- x_move_delta, y_move_delta,
- new);
-
- current = *new;
- }
-
- /* Now we have to sort out the aspect ratio */
- if (!window->fullscreen)
- {
- /*
- * width
- * min_aspect <= -------- <= max_aspect
- * height
- */
- double min_aspect, max_aspect;
- int width, height;
-
- min_aspect = window->size_hints.min_aspect.x / (double) window->size_hints.min_aspect.y;
- max_aspect = window->size_hints.max_aspect.x / (double) window->size_hints.max_aspect.y;
-
- width = current.width;
- height = current.height;
+ /* Do the gravity adjustment */
- if (min_aspect * height > width)
- {
- int delta;
-
- if (y_direction == META_RESIZE_CENTER)
- {
- delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc);
- if (width + delta <= window->size_hints.max_width)
- width += delta;
- else
- {
- delta = FLOOR (height - width / min_aspect, window->size_hints.height_inc);
- if (height - delta >= window->size_hints.min_height)
- height -= delta;
- }
- }
- else
- {
- delta = FLOOR (height - width / min_aspect, window->size_hints.height_inc);
- if (height - delta >= window->size_hints.min_height)
- height -= delta;
- else
- {
- delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc);
- if (width + delta <= window->size_hints.max_width)
- width += delta;
- }
- }
- }
-
- if (max_aspect * height < width)
- {
- int delta;
-
- if (x_direction == META_RESIZE_CENTER)
- {
- delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc);
- if (height + delta <= window->size_hints.max_height)
- height += delta;
- else
- {
- delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc);
- if (width - delta >= window->size_hints.min_width)
- width -= delta;
- }
- }
- else
- {
- delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc);
- if (width - delta >= window->size_hints.min_width)
- width -= delta;
- else
- {
- delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc);
- if (height + delta <= window->size_hints.max_height)
- height += delta;
- }
- }
- }
-
- /* Convert into terms of the direction of resize and reapply the
- * earlier constraints; this means aspect ratio becomes the
- * least-important of the constraints. If we wanted aspect to be
- * the most important, we could just not do this next bit.
- */
-
- if (current.width != width)
- {
- x_delta = width - current.width; /* positive delta to increase width */
- switch (x_direction)
- {
- case META_RESIZE_LEFT_OR_TOP:
- constrain_resize_left (window, &info, &current,
- - x_delta, new);
- break;
- case META_RESIZE_CENTER:
- constrain_resize_hcenter (window, &info, &current,
- x_delta, new);
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- constrain_resize_right (window, &info, &current,
- x_delta, new);
- break;
- }
- }
-
- if (current.height != height)
- {
- y_delta = height - current.height; /* positive to increase height */
-
- switch (y_direction)
- {
- case META_RESIZE_LEFT_OR_TOP:
- constrain_resize_top (window, &info, &current,
- - y_delta, new);
- break;
- case META_RESIZE_CENTER:
- constrain_resize_vcenter (window, &info, &current,
- y_delta, new);
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- constrain_resize_bottom (window, &info, &current,
- y_delta, new);
- break;
- }
- }
-
- current = *new;
- }
-
- meta_topic (META_DEBUG_GEOMETRY,
- "Constrained %s new %d,%d %dx%d old %d,%d %dx%d\n",
- window->desc,
- new->x, new->y, new->width, new->height,
- orig->x, orig->y, orig->width, orig->height);
+ return TRUE;
}
-MetaResizeDirection
-meta_x_direction_from_gravity (int gravity)
-{
- switch (gravity)
- {
- case EastGravity:
- case NorthEastGravity:
- case SouthEastGravity:
- return META_RESIZE_LEFT_OR_TOP;
- break;
-
- case WestGravity:
- case NorthWestGravity:
- case SouthWestGravity:
- case StaticGravity:
- return META_RESIZE_RIGHT_OR_BOTTOM;
- break;
+static gboolean
+constrain_gravity_adjust (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
+ constrain_gravity_adjust (window, &info, priority, check_only);
+ constrain_maximization (window, &info, priority, check_only);
+ constrain_fullscreen (window, &info, priority, check_only);
+ constrain_clamp_size (window, &info, priority, check_only);
+ constrain_1d_size_hints (window, &info, priority, check_only);
+ constrain_aspect_ratio (window, &info, priority, check_only);
+ constrain_fully_onscreen (window, &info, priority, check_only);
+ constrain_titlebar_onscreen (window, &info, priority, check_only);
- default:
- return META_RESIZE_CENTER;
- break;
- }
-}
+static gboolean
+constrain_whatever (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
+{
+ if (priority > PRIORITY_WHATEVER)
+ return TRUE;
+
+ /* Determine whether constraint applies; note that if the constraint
+ * cannot possibly be satisfied, constraint_applies should be set to
+ * false. If we don't do this, all constraints with a lesser priority
+ * will be dropped along with this one, and we'd rather apply as many as
+ * possible.
+ */
+ if (!constraint_applies)
+ return TRUE;
-MetaResizeDirection
-meta_y_direction_from_gravity (int gravity)
-{
- switch (gravity)
- {
- case SouthGravity:
- case SouthWestGravity:
- case SouthEastGravity:
- return META_RESIZE_LEFT_OR_TOP;
- break;
+ /* Determine whether constraint is already satisfied; if we're only
+ * checking the status of whether the constraint is satisfied, we end
+ * here.
+ */
+ if (check_only || constraint_already_satisfied)
+ return constraint_already_satisfied;
- case NorthGravity:
- case NorthWestGravity:
- case NorthEastGravity:
- case StaticGravity:
- return META_RESIZE_RIGHT_OR_BOTTOM;
- break;
+ /* Enforce constraints */
- default:
- return META_RESIZE_CENTER;
- }
+ return TRUE;
}
+
diff --git a/src/constraints.h b/src/constraints.h
index de8555b2..fa4677de 100644
--- a/src/constraints.h
+++ b/src/constraints.h
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2002 Red Hat, Inc.
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -26,26 +27,13 @@
#include "window.h"
#include "frame.h"
-typedef enum
-{
- META_RESIZE_LEFT_OR_TOP,
- META_RESIZE_CENTER,
- META_RESIZE_RIGHT_OR_BOTTOM
-} MetaResizeDirection;
-
void meta_window_constrain (MetaWindow *window,
MetaFrameGeometry *fgeom,
+ gboolean adjust_for_gravity,
+ int gravity,
+ gboolean is_user_interaction,
const MetaRectangle *orig,
- int x_move_delta,
- int y_move_delta,
- MetaResizeDirection x_direction,
- int x_delta,
- MetaResizeDirection y_direction,
- int y_delta,
- MetaRectangle *new);
-
-MetaResizeDirection meta_x_direction_from_gravity (int gravity);
-MetaResizeDirection meta_y_direction_from_gravity (int gravity);
+ MetaRectangle* new);
#endif /* META_CONSTRAINTS_H */
diff --git a/src/display.c b/src/display.c
index 99f0d72e..40faee05 100644
--- a/src/display.c
+++ b/src/display.c
@@ -4285,53 +4285,6 @@ meta_resize_gravity_from_grab_op (MetaGrabOp op)
return gravity;
}
-gboolean
-meta_rectangle_intersect (MetaRectangle *src1,
- MetaRectangle *src2,
- MetaRectangle *dest)
-{
- int dest_x, dest_y;
- int dest_w, dest_h;
- int return_val;
-
- g_return_val_if_fail (src1 != NULL, FALSE);
- g_return_val_if_fail (src2 != NULL, FALSE);
- g_return_val_if_fail (dest != NULL, FALSE);
-
- return_val = FALSE;
-
- dest_x = MAX (src1->x, src2->x);
- dest_y = MAX (src1->y, src2->y);
- dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x;
- dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y;
-
- if (dest_w > 0 && dest_h > 0)
- {
- dest->x = dest_x;
- dest->y = dest_y;
- dest->width = dest_w;
- dest->height = dest_h;
- return_val = TRUE;
- }
- else
- {
- dest->width = 0;
- dest->height = 0;
- }
-
- return return_val;
-}
-
-gboolean
-meta_rectangle_equal (const MetaRectangle *src1,
- const MetaRectangle *src2)
-{
- return ((src1->x == src2->x) &&
- (src1->y == src2->y) &&
- (src1->width == src2->width) &&
- (src1->height == src2->height));
-}
-
static MetaScreen*
find_screen_for_selection (MetaDisplay *display,
Window owner,
diff --git a/src/display.h b/src/display.h
index 9c8c2f5e..f45486c2 100644
--- a/src/display.h
+++ b/src/display.h
@@ -43,17 +43,6 @@
#define meta_XFree(p) do { if ((p)) XFree ((p)); } while (0)
-/* this doesn't really belong here, oh well. */
-typedef struct _MetaRectangle MetaRectangle;
-
-struct _MetaRectangle
-{
- int x;
- int y;
- int width;
- int height;
-};
-
typedef struct MetaCompositor MetaCompositor;
typedef struct _MetaDisplay MetaDisplay;
typedef struct _MetaFrame MetaFrame;
@@ -513,12 +502,6 @@ int meta_resize_gravity_from_grab_op (MetaGrabOp op);
gboolean meta_grab_op_is_moving (MetaGrabOp op);
gboolean meta_grab_op_is_resizing (MetaGrabOp op);
-gboolean meta_rectangle_intersect (MetaRectangle *src1,
- MetaRectangle *src2,
- MetaRectangle *dest);
-gboolean meta_rectangle_equal (const MetaRectangle *src1,
- const MetaRectangle *src2);
-
void meta_display_devirtualize_modifiers (MetaDisplay *display,
MetaVirtualModifier modifiers,
unsigned int *mask);
diff --git a/src/window.c b/src/window.c
index c6b67690..d7dd18dc 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2424,47 +2424,6 @@ static_gravity_works (MetaDisplay *display)
return display->static_gravity_works;
}
-static void
-get_mouse_deltas_for_resize (MetaWindow *window,
- int resize_gravity,
- int w,
- int h,
- int *x_delta,
- int *y_delta)
-{
- switch (meta_x_direction_from_gravity (resize_gravity))
- {
- case META_RESIZE_LEFT_OR_TOP:
- *x_delta = window->rect.width - w;
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- *x_delta = w - window->rect.width;
- break;
- case META_RESIZE_CENTER:
- /* FIXME this implies that with center gravity you have to grow
- * in increments of two
- */
- *x_delta = (w - window->rect.width) / 2;
- break;
- }
-
- switch (meta_y_direction_from_gravity (resize_gravity))
- {
- case META_RESIZE_LEFT_OR_TOP:
- *y_delta = window->rect.height - h;
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- *y_delta = h - window->rect.height;
- break;
- case META_RESIZE_CENTER:
- /* FIXME this implies that with center gravity you have to grow
- * in increments of two
- */
- *y_delta = (h - window->rect.height) / 2;
- break;
- }
-}
-
#ifdef HAVE_XSYNC
static void
send_sync_request (MetaWindow *window)
@@ -2539,7 +2498,6 @@ meta_window_move_resize_internal (MetaWindow *window,
meta_window_unqueue_move_resize (window);
old_rect = window->rect;
- meta_window_get_position (window, &old_rect.x, &old_rect.y);
meta_topic (META_DEBUG_GEOMETRY,
"Move/resize %s to %d,%d %dx%d%s%s from %d,%d %dx%d\n",
@@ -2551,7 +2509,8 @@ meta_window_move_resize_internal (MetaWindow *window,
if (window->frame)
meta_frame_calc_geometry (window->frame,
&fgeom);
-
+
+#if 0
if (is_configure_request || do_gravity_adjust)
{
adjust_for_gravity (window,
@@ -2571,42 +2530,38 @@ meta_window_move_resize_internal (MetaWindow *window,
root_x_nw, root_y_nw);
}
- get_mouse_deltas_for_resize (window, resize_gravity, w, h,
- &x_delta, &y_delta);
-
+#endif
+
+ new_rect.x = root_x_nw;
+ new_rect.y = root_y_nw;
+ new_rect.width = w;
+ new_rect.height = h;
+
meta_window_constrain (window,
window->frame ? &fgeom : NULL,
+ is_configure_request || do_gravity_adjust,
+ resize_gravity,
+ is_user_action,
&old_rect,
- root_x_nw - old_rect.x,
- root_y_nw - old_rect.y,
- meta_x_direction_from_gravity (resize_gravity),
- x_delta,
- meta_y_direction_from_gravity (resize_gravity),
- y_delta,
- &new_rect);
-
- w = new_rect.width;
- h = new_rect.height;
- root_x_nw = new_rect.x;
- root_y_nw = new_rect.y;
-
- if (w != window->rect.width ||
- h != window->rect.height)
+ &new_rect)
+
+ if (new_rect.width != window->rect.width ||
+ new_rect.height != window->rect.height)
need_resize_client = TRUE;
- window->rect.width = w;
- window->rect.height = h;
+ window->rect.width = new_rect.width;
+ window->rect.height = new_rect.height;
if (window->frame)
{
int new_w, new_h;
- new_w = window->rect.width + fgeom.left_width + fgeom.right_width;
+ new_w = window->rect.width;
if (window->shaded)
new_h = fgeom.top_height;
else
- new_h = window->rect.height + fgeom.top_height + fgeom.bottom_height;
+ new_h = window->rect.height;
frame_size_dx = new_w - window->frame->rect.width;
frame_size_dy = new_h - window->frame->rect.height;
@@ -2647,17 +2602,13 @@ meta_window_move_resize_internal (MetaWindow *window,
int new_x, new_y;
int frame_pos_dx, frame_pos_dy;
- /* Compute new frame coords */
- new_x = root_x_nw - fgeom.left_width;
- new_y = root_y_nw - fgeom.top_height;
-
- frame_pos_dx = new_x - window->frame->rect.x;
- frame_pos_dy = new_y - window->frame->rect.y;
+ frame_pos_dx = new_rect.x - window->frame->rect.x;
+ frame_pos_dy = new_rect.y - window->frame->rect.y;
need_move_frame = (frame_pos_dx != 0 || frame_pos_dy != 0);
- window->frame->rect.x = new_x;
- window->frame->rect.y = new_y;
+ window->frame->rect.x = new_rect.x;
+ window->frame->rect.y = new_rect.y;
/* If frame will both move and resize, then StaticGravity
* on the child window will kick in and implicitly move
@@ -2728,12 +2679,12 @@ meta_window_move_resize_internal (MetaWindow *window,
}
else
{
- if (root_x_nw != window->rect.x ||
- root_y_nw != window->rect.y)
+ if (new_rect.x != window->rect.x ||
+ new_rect.y != window->rect.y)
need_move_client = TRUE;
- window->rect.x = root_x_nw;
- window->rect.y = root_y_nw;
+ window->rect.x = new_rect.x;
+ window->rect.y = new_rect.y;
client_move_x = window->rect.x;
client_move_y = window->rect.y;