summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElijah Newren <newren@gmail.com>2005-11-16 00:08:26 +0000
committerElijah Newren <newren@src.gnome.org>2005-11-16 00:08:26 +0000
commitfd279e3d61836e38f5ce65f20e455760e16594bc (patch)
tree41ed8c52dc8c10a6167beb33bfca6fcd780d9ee4
parentf47247c4fb93b2b034199ca96c7a87b8eecb1716 (diff)
downloadmetacity-fd279e3d61836e38f5ce65f20e455760e16594bc.tar.gz
Update, fix, and extend all the documentation on constraints.
2005-11-15 Elijah Newren <newren@gmail.com> Update, fix, and extend all the documentation on constraints. * src/constraints-ideas.txt: Update * doc/how-constraints-works.txt: New file * src/boxes.c: Update documentation (I've done the main optimization now) * src/constraints.c: Rip lots of the documentation out and stick it in doc/how-constraints-works.txt while simultaneously removing the old and incorrect stuff and extending other parts
-rw-r--r--ChangeLog15
-rw-r--r--constraints-ideas.txt22
-rw-r--r--doc/how-constraints-works.txt283
-rw-r--r--src/boxes.c31
-rw-r--r--src/constraints.c368
5 files changed, 406 insertions, 313 deletions
diff --git a/ChangeLog b/ChangeLog
index 7af728c8..2bfc8949 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
2005-11-15 Elijah Newren <newren@gmail.com>
+ Update, fix, and extend all the documentation on constraints.
+
+ * src/constraints-ideas.txt: Update
+
+ * doc/how-constraints-works.txt: New file
+
+ * src/boxes.c: Update documentation (I've done the main
+ optimization now)
+
+ * src/constraints.c: Rip lots of the documentation out and stick
+ it in doc/how-constraints-works.txt while simultaneously removing
+ the old and incorrect stuff and extending other parts
+
+2005-11-15 Elijah Newren <newren@gmail.com>
+
Make meta_rectangle_find_onscreen_edges() robust against
overlapping struts instead of just checking and throwing an error
if they aren't.
diff --git a/constraints-ideas.txt b/constraints-ideas.txt
index 6491e93c..8606af32 100644
--- a/constraints-ideas.txt
+++ b/constraints-ideas.txt
@@ -1,9 +1,19 @@
+WARNING: This file is basically just a running TODO list for Elijah.
+WARNING: It is NOT meant to make sense to anyone else. It probably
+WARNING: won't help much either, because it has remnants of old ideas
+WARNING: and thoughts which occasionally contradict stuff elsewhere in
+WARNING: this file or in the other files on this branch.
+
Short-term (I hope...) TODO list/reminders:
- - Need to update the documentation; the huge comment is out of date and
+ - Need to add my copyright info to all the modified files
+
+ - Need to verify XRandR stuff appropriately invalidates the workarea
+
+ X Need to update the documentation; the huge comment is out of date and
misleading now
- - Quicklist that I thought of:
+ X Quicklist that I thought of:
X Titlebar onscreen func should use CLAMP(percent*width, 10, 75) for
width and 2-3 pixels for height for checking if the titlebar is
partially onscreen or not
@@ -15,7 +25,7 @@ Short-term (I hope...) TODO list/reminders:
X There may be a bug with gravity an onscreen constraints--it seems
that creating a tab then removing it and repeating this a lot made
the window drift northwest. Very weird.
- - Get rid of compiling warnings for constraints.c even if they're dumb
+ X Get rid of compiling warnings for constraints.c even if they're dumb
- Should we turn off fully-onscreen & on-single-xinerama constraints
for decorationless windows? Otherwise, most users (those that
@@ -64,11 +74,11 @@ Short-term (I hope...) TODO list/reminders:
timeout-resistance be much larger for the screen edges, (e) it may be
difficult determining where the screen edges are
- - Various extra random ideas
+ X Various extra random ideas
X Could make edge resistance infinite if move/resize was
click-on-titlebar-area activated (or perhaps just if the
grab_anchor_root_(x|y) is in the titlebar area?)
- - Could get rid of the big comment at the beginning of
+ X Could get rid of the big comment at the beginning of
meta_rectangle_get_minimal_spanning_set_for_region()
X checking for obscured edges could make use of boxes.c:split_edge()
X when timeout fires, edge-resistance for _any_ edge should be disabled
@@ -90,7 +100,7 @@ Short-term (I hope...) TODO list/reminders:
- cache edges
- put all this functionality into one common location
- - Important documentation points
+ X Important documentation points
- All fields of ConstraintsInfo struct
- Inner vs Outer window
- meta_window_move_resize_internal() and other changes
diff --git a/doc/how-constraints-works.txt b/doc/how-constraints-works.txt
new file mode 100644
index 00000000..327e5fe8
--- /dev/null
+++ b/doc/how-constraints-works.txt
@@ -0,0 +1,283 @@
+File contents:
+ Basic Ideas
+ Important points to remember
+ Explanation of fields in the ConstraintInfo struct
+ Gory details of resize_gravity vs. fixed_directions
+
+IMPORTANT NOTE: There's a big comment at the top of constraints.c
+explaining how to add extra constraints or tweak others. Read it. I put
+that information there because it may be enough information by itself for
+people to hack on constraints.c. I won't duplicate that information in
+this file; this file is for deeper details.
+
+
+---------------------------------------------------------------------------
+Basic Ideas
+---------------------------------------------------------------------------
+There are a couple basic ideas behind how this constraints.c code works and
+why it works that way:
+
+ 1) Split the low-level error-prone operations into a special file
+ 2) Add robustness by prioritizing constraints
+ 3) Make use of a minimal spanning set of rectangles for the
+ "onscreen region" (screen minus struts).
+ 4) Constraints can be user-action vs app-action oriented
+ 5) Avoid over-complification ;-)
+
+Some more details explaining these basic ideas:
+
+ 1) Split tedious operations out
+
+ boxes.[ch] have been added which contain many common, tedious, and
+ error-prone operations. I find that this separation helps a lot for
+ managing the complexity and ensuring that things work correctly.
+ Also, note that testboxes.c thoroughly tests all functionality in
+ boxes.[ch] and a testboxes program is automatically compiled.
+
+ Note that functions have also been added to this file to handle some
+ of the tedium necessary for edge resistance as well.
+
+ 2) Prioritize constraints
+
+ 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.
+
+ Now, all constraints have an associated priority, defined in the
+ ConstraintPriority enum near the top of constraints.c. The
+ constraints are all applied, and then are all checked; if not all are
+ satisfied then the least important constraints are dropped and the
+ process is repeated. This ensures that the most important constraints
+ are satisfied.
+
+ A special note to make here is that if any one given constraint is
+ impossible to satisfy even individually (e.g. if minimum size hints
+ specify a larger window than the screen size, making the
+ fully-onscreen constraint impossible to satisfy) 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 if
+ we treat it as a violation then all constraints with a lesser priority
+ also get dropped along with the impossible to satisfy one.
+
+ 3) Using maximal/spanning rectangles
+
+ The constraints rely heavily on something I call spanning rectangles
+ (which Soeren referred to as maximal rectangles, a name which I think
+ I like better but I don't want to go change all the code now). These
+ spanning rectangles have the property that a window will fit on the
+ screen if and only if it fits within at least one of the rectangles.
+ Soeren had an alternative way of describing these rectangles, namely
+ that they were rectangles with the property that if you made any of
+ them larger in any direction, they would overlap with struts or be
+ offscreen (with the implicit assumption that there are enough of these
+ rectangles that combined they cover all relevant parts of the screen).
+ Note that, by necessity, these spanning/maximal rectangles will often
+ overlap each other.
+
+ Such a list makes it relatively easy to define operations like
+ window-is-onscreen or clamp-window-to-region or
+ shove-window-into-region. Since we have a on-single-xinerama
+ constraint in addition to the onscreen constraint(s), we cache
+ number_xineramas + 1 of these lists in the workspace. These lists
+ then only need to be updated whenever the workarea is (e.g. when strut
+ list change or screen or xinerama size changes).
+
+ 4) Constraints can be user-action vs app-action oriented
+
+ Such differentiation requires special care for the constraints to be
+ consistent; e.g. if the user does something and one constraint
+ applies, then the app does something you have to be careful that the
+ constraint on the app action doesn't result in some jarring motion.
+
+ In particular, the constraints currently allow offscreen movement or
+ resizing for user actions only. The way consistency is handled is
+ that at the end of the constraints, update_onscreen_requirements()
+ checks to see if the window is offscreen or split across xineramas and
+ updates window->require_fully_onscreen and
+ window->require_on_single_xinerama appropriately.
+
+ 5) Avoid over-complification
+
+ 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)
+
+
+---------------------------------------------------------------------------
+Important points to remember
+---------------------------------------------------------------------------
+
+ - Inner vs Outer window
+
+ Note that because of how configure requests work and
+ meta_window_move_resize_internal() and friends are set up, that the
+ rectangles passed to meta_window_constrain() are with respect to inner
+ window positions instead of outer window positions (meaning that window
+ manager decorations are not included in the position/size). For the
+ constraints that need to be enforced with respect to outer window
+ positions, you'll need to make use of the extend_by_frame() and
+ unextend_by_frame() functions.
+
+ - meta_window_move_resize_internal() accepts a really hairy set of
+ inputs. See the huge comment at the beginning of that function.
+ constraints gets screwed up if that function can't sanitize the input,
+ so be very careful about that. It used to be pretty busted.
+
+
+---------------------------------------------------------------------------
+Explanation of fields in the ConstraintInfo strut
+---------------------------------------------------------------------------
+
+As of the time of this writing, ConstraintInfo had the following fields:
+ orig
+ current
+ fgeom
+ action_type
+ is_user_action
+ resize_gravity
+ fixed_directions
+ work_area_xinerama
+ entire_xinerama
+ usable_screen_region
+ usable_xinerama_region
+
+A brief description of each and/or pointers to more information are found
+below:
+ orig
+ The previous position and size of the window, ignoring any window
+ decorations
+ current
+ The requested position and size of the window, ignoring any window
+ decorations. This rectangle gets modified by the various constraints
+ to specify the allowed position closest to the requested position.
+ fgeom
+ The geometry of the window frame (i.e. "decorations"), if it exists.
+ Otherwise, it's a dummy 0-size frame for convenience (i.e. this pointer
+ is guaranteed to be non-NULL so you don't have to do the stupid check).
+ action_type
+ Whether the action being constrained is a move, resize, or a combined
+ move and resize. Some constraints can run faster with this information
+ (e.g. constraining size increment hints or min size hints don't need to
+ do anything for pure move operations). This may also be used for
+ providing slightly different behavior (e.g. clip-to-region instead of
+ shove-into-region for resize vs. moving operations), but doesn't
+ currently have a lot of use for this.
+ is_user_action
+ Used to determine whether the action being constrained is a user
+ action. If so, certain parts of the constraint may be relaxed. Note
+ that this requires care to get right; see item 4 of the basic ideas
+ section for more details.
+ resize_gravity
+ The gravity used in the resize operation, used in order to make sure
+ windows are resized correctly if constraints specify that their size
+ must be modified. Explained further in the resize_gravity
+ vs. fixed_directions section.
+ fixed_directions
+ There may be multiple solutions to shoving a window back onscreen.
+ Typically, the shortest distance used is the solution picked, but if
+ e.g. an application only moved its window in a single direction, it's
+ more desirable that the window is shoved back in that direction than in
+ a different one. fixed_directions facilitates that. Explained further
+ in the resize_gravity vs. fixed_directions section.
+ work_area_xinerama
+ This region is defined in the workspace and just cached here for
+ convenience. It is basically the area obtained by taking the current
+ xinerama, treating all partial struts as full struts, and then
+ subtracting all struts from the current xinerama region. Useful
+ e.g. for enforcing maximization constraints.
+ entire_xinerama
+ Just a cache of the rectangle corresponding to the entire current
+ xinerama, including struts. Useful e.g. for enforcing fullscreen
+ constraints.
+ usable_screen_region
+ The set of maximal/spanning rectangles for the entire screen; this
+ region doesn't overlap with any struts and helps to enforce
+ e.g. onscreen constraints.
+ usable_xinerama_region
+ The set of maximal/spanning rectangles for the current xinerama; this
+ region doesn't overlap with any struts on the xinerama and helps to
+ enforce e.g. the on-single-xinerama constraint.
+
+
+---------------------------------------------------------------------------
+Gory details of resize_gravity vs. fixed_directions
+---------------------------------------------------------------------------
+
+Note that although resize_gravity and fixed_directions look similar, they
+are used for different purposes:
+
+ - resize_gravity is only for resize operations and is used for
+ constraints unrelated to keeping a window within a certain region
+ - fixed_directions is for both move and resize operations and is
+ specifically for keeping a window within a specified region.
+
+Examples of where each are used:
+
+ - If a window is simultaneously moved and resized to the southeast corner
+ with SouthEastGravity, but it turns out that the window was sized to
+ something smaller than the minimum size hint, then the size_hints
+ constraint should resize the window using the resize_gravity to ensure
+ that the southeast corner doesn't move.
+ - If an application resizes itself so that it grows downward only (which
+ I note could be using any of three different gravities, most likely
+ NorthWest), and happens to put the southeast part of the window under a
+ partial strut, then the window needs to be forced back on screen.
+ (Yes, shoved onscreen and not clipped; see bug 136307). It may be the
+ case that moving the window to the left results in less movement of the
+ window than moving the window up, which, in the absence of fixed
+ directions would cause us to chose moving to the left. But since the
+ user knows that only the height of the window is changing, they would
+ find moving to the left weird (especially if this were a dialog that
+ had been centered on its parent). It'd be better to shove the window
+ upwards so we make sure to keep the left and right sides fixed in this
+ case. Note that moving the window upwards (or leftwards) is probably
+ totally against the gravity in this case; but that's okay because
+ gravity typically assumes there's more than enough onscreen space for
+ the resize and we only override the gravity when that assumption is
+ wrong.
+
+For the paranoid, a fixed directions might give an impossible to fulfill
+constraint (I don't think that's true currently in the code, but I haven't
+thought it through in a while). If this ever becomes a problem, it should
+be relatively simple to throw out the fixed directions when this happens
+and rerun the constraint. Of course, it might be better to rethink things
+to just avoid such a problem.
+
+The nitty gritty of what gets fixed:
+ User move:
+ in x direction - y direction fixed
+ in y direction - x direction fixed
+ in both dirs. - neither direction fixed
+ User resize: (note that for clipping, only 1 side ever changed)
+ in x direction - y direction fixed (technically opposite x side fixed too)
+ in y direction - x direction fixed (technically opposite y side fixed too)
+ in both dirs. - neither direction fixed
+ App move:
+ in x direction - y direction fixed
+ in y direction - x direction fixed
+ in both dirs. - neither direction fixed
+ App resize
+ in x direction - y direction fixed
+ in y direction - x direction fixed
+ in 2 parallel directions (center side gravity) - other dir. fixed
+ in 2 orthogonal directions (corner gravity) - neither dir. fixed
+ in 3 or 4 directions (a center-like gravity) - neither dir. fixed
+ Move & resize
+ Treat like resize case though this will usually mean all four sides
+ change and result in neither direction being fixed
+ Note that in all cases, if neither direction moves it is likely do to a
+ change in struts and thus neither direction should be fixed despite the
+ lack of movement.
diff --git a/src/boxes.c b/src/boxes.c
index 358591c0..d11c3967 100644
--- a/src/boxes.c
+++ b/src/boxes.c
@@ -576,9 +576,13 @@ meta_rectangle_get_minimal_spanning_set_for_region (
* by default and only partial struts increase the size of the spanning
* set generated). With one partial strut, n will be 2 or 3. With 2
* partial struts, n will probably be 4 or 5. So, n probably isn't large
- * enough to make this worth bothering. If it ever does show up on
- * profiles (most likely because people start using large numbers of
- * partial struts), possible optimizations include:
+ * enough to make this worth bothering. Further, it is only called from
+ * workspace.c:ensure_work_areas_validated (at least as of the time of
+ * writing this comment), which in turn should only be called if the
+ * strut list changes or the screen or xinerama size changes. If it ever
+ * does show up on profiles (most likely because people start using
+ * ridiculously huge numbers of partial struts), possible optimizations
+ * include:
*
* (1) rewrite merge_spanning_rects_in_region() to be O(n) or O(nlogn).
* I'm not totally sure it's possible, but with a couple copies of
@@ -594,22 +598,9 @@ meta_rectangle_get_minimal_spanning_set_for_region (
* it might be possible to modify this function to make that
* possible, and I spent just a little while thinking about it, but n
* wasn't large enough to convince me to care yet.
- * (4) just don't call this function that much. Currently, it's called
- * from a few places in constraints.c, and thus is called multiple
- * times for every meta_window_constrain() call, which itself is
- * called an awful lot. However, the answer we provide is always the
- * same unless the screen size, number of xineramas, or list of
- * struts has changed. I'm not aware of any case where screen size
- * or number of xineramas changes without logging out. struts change
- * very rarely. So we should be able to just save the appropriate
- * info in the MetaWorkspace (or maybe MetaScreen), update it when
- * the struts change, and then just use those precomputed values
- * instead of calling this function so much.
- *
- * In terms of work, 1-3 would be hard (and I'm not entirely certain that
- * they would work) and 4 would be relatively easy. 4 would also provide
- * the greatest benefit. Therefore, do 4 first. Don't even think about
- * 1-3 or other micro-optimizations until you've done that one.
+ * (4) Some of the stuff Rob mentioned at http://mail.gnome.org/archives\
+ * /metacity-devel-list/2005-November/msg00028.html. (Sorry for the
+ * URL splitting.)
*/
GList *ret;
@@ -1088,7 +1079,7 @@ meta_rectangle_find_linepoint_closest_to_point (double x1, double y1,
* rewrite the equation by multiplying both sides by (rx-x1)*(x2-x1):
* (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
* This is a valid requirement even when x1==x2 (when x1==x2, this latter
- * equation will basically just mean that rx must also be equal to x1 and
+ * equation will basically just mean that rx must be equal to both x1 and
* x2)
*
* The other requirement that we have is that the line from (rx,ry) to
diff --git a/src/constraints.c b/src/constraints.c
index 362a6056..174078a6 100644
--- a/src/constraints.c
+++ b/src/constraints.c
@@ -28,202 +28,66 @@
#include <stdlib.h>
#include <math.h>
-/* Stupid disallowing of nested C comments makes a #if 0 mandatory... */
-#if 0
-/* This is a huge comment with a brief overview of how to hack on it as
- * quickly as possible, followed by much more in depth details of how it
- * works, why it works that way, the ideas behind it, and comparisons to
- * the previous way of doing things.
- *
- * BRIEF OVERVIEW OF HOW TO HACK ON THIS FILE
- *
- * This can basically be explained by showing how to add a new
- * constraint, the steps of which are:
- * 1) Add a new entry in the ConstraintPriority enum; higher values
- * have higher priority
- * 2) Write a new function following the format of the example below,
- * "constrain_whatever".
- * 3) Add your function to the loop in meta_window_constrain() in both
- * places.
- *
- * An example constraint function, constrain_whatever:
- *
- * /* 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.
- * */
- * 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;
- *
- * /* 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. */
- * }
- *
- * THE NASTY DETAILS (cue ominous music)
- *
- * There are a couple basic ideas behind how this code works and why
- * it works that way:
- *
- * 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->require_fully_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, 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.
+/* Stupid disallowing of nested C comments makes a #if 0 and the use of C++
+ * looking comments mandatory. Of course, if C weren't so stupid it'd just
+ * allow C++ style comments...
*/
+#if 0
+ // This is the short and sweet version of how to hack on this file; see
+ // doc/how-constraints-works.txt for the gory details. The basics of
+ // understanding this file can be shown by the steps needed to add a new
+ // constraint, which are:
+ // 1) Add a new entry in the ConstraintPriority enum; higher values
+ // have higher priority
+ // 2) Write a new function following the format of the example below,
+ // "constrain_whatever".
+ // 3) Add your function to the all_constraints and all_constraint_names
+ // arrays (the latter of which is for debugging purposes)
+ //
+ // An example constraint function, constrain_whatever:
+ //
+ // /* 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 ConstrainPriority
+ // * for easier maintenance and shuffling of priorities.
+ // */
+ // 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;
+ //
+ // /* 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; not that the
+ // * return value is heeded in this case...
+ // */
+ // }
#endif
typedef enum
@@ -255,8 +119,10 @@ typedef struct
ActionType action_type;
gboolean is_user_action;
- /* See setup_constraint_info for explanation of the differences and
- * similarity between resize_gravity and fixed_directions
+ /* I know that these two things probably look similar at first, but they
+ * have much different uses. See doc/how-constraints-works.txt for for
+ * explanation of the differences and similarity between resize_gravity
+ * and fixed_directions
*/
int resize_gravity;
FixedDirections fixed_directions;
@@ -412,9 +278,8 @@ meta_window_constrain (MetaWindow *window,
/* WARNING: orig and new specify positions and sizes of the inner window,
* not the outer. This is a common gotcha since half the constraints
- * deal with inner window position/size and half deal with outer. Take a
- * look at extend_by_frame() and unextend_by_frame() for some simple
- * helper functions to help deal with the differences.
+ * deal with inner window position/size and half deal with outer. See
+ * doc/how-constraints-works.txt for more information.
*/
meta_topic (META_DEBUG_GEOMETRY,
"Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d\n",
@@ -497,74 +362,6 @@ setup_constraint_info (ConstraintInfo *info,
info->resize_gravity = resize_gravity;
- /* Note that although resize_gravity and fixed_directions look similar,
- * they are used for different purposes and I believe it helps code
- * clarity to keep them separate in those sections where each is used.
- *
- * - resize_gravity is only for resize operations and is used for
- * constraints unrelated to keeping a window within a certain region
- * - fixed_directions is for both move and resize operations and is
- * specifically for keeping a window within a specified region.
- *
- * Examples of where each are used:
- *
- * - If a window is simultaneously moved and resized to the
- * southeast corner with SouthEastGravity, but it turns out that
- * the window was sized to something smaller than the minimum
- * size hint, then the size_hints constraint should resize the
- * window using the resize_gravity to ensure that the southeast
- * corner doesn't move.
- * - If an application resizes itself so that it grows downward only
- * (which I note could be using any of three different gravities,
- * most likely NorthWest), and happens to put the southeast part of
- * the window under a partial strut, then the window needs to be
- * forced back on screen. (Yes, shoved onscreen and not clipped; see
- * bug 136307). It may be the case that moving the window to the
- * left results in less movement of the window than moving the window
- * up, which, in the absence of fixed directions would cause us to
- * chose moving to the left. But since the user knows that only the
- * height of the window is changing, they would find moving to the
- * left weird (especially if this were a dialog that had been
- * centered on its parent). It'd be better to shove the window
- * upwards so we make sure to keep the left and right sides fixed in
- * this case. Note that moving the window upwards (or even if we
- * were to go left) is totally against the gravity in this case; but
- * that's okay because gravity typically assumes there's more than
- * enough onscreen space for the resize and we only override the
- * gravity when that assumption is wrong.
- *
- * Note that fixed directions might (though I haven't thought it
- * completely through) give an impossible to fulfill constraint; if they
- * do, then we could temporarily throw them out and retry the constraint
- * again.
- *
- * Now, some nasty details:
- *
- * This should be fixed directions (as opposed to fixed sides), because
- * I'm only interested in shoving/clipping in x versus y. The nitty
- * gritty of what gets fixed:
- * User move:
- * in x direction - y direction fixed
- * in y direction - x direction fixed
- * in both dirs. - neither direction fixed
- * User resize: (note that for clipping, only 1 side ever changed)
- * in x direction - y direction fixed (technically other x side fixed too)
- * in y direction - x direction fixed (technically other y side fixed too)
- * in both dirs. - neither direction fixed
- * App move:
- * in x direction - y direction fixed
- * in y direction - x direction fixed
- * in both dirs. - neither direction fixed
- * App resize
- * in x direction - y direction fixed
- * in y direction - x direction fixed
- * in 2 parallel directions (center side gravity) - other dir. fixed
- * in 2 orthogonal directions (corner gravity) - neither dir. fixed
- * in 3 or 4 directions (a center-like gravity) - neither dir. fixed
- * Move & resize
- * Treat like resize case though this will usually mean all four sides
- * change and result in neither direction being fixed
- */
info->fixed_directions = 0;
/* If x directions don't change but either y direction does */
if ( orig->x == new->x && orig->x + orig->width == new->x + new->width &&
@@ -710,18 +507,19 @@ update_onscreen_requirements (MetaWindow *window,
window->type == META_WINDOW_DOCK)
return;
- /* FIXME: Naturally, I only want these flags to become *false* due to
- * user interactions (which is allowed since certain constraints are
- * ignored for user interactions regardless of the setting of these
- * flags). However, do I want these flags to become *true* due to
- * just an application interaction? It's possible that users may
- * find that strange since two application interactions that resize
- * in opposite ways don't end up cancelling--but it may also be
- * strange for the user to have an application resize the window so
- * that it's onscreen, the user forgets about it, and then later the
- * app is able to resize itself off the screen. Anyway, for now,
- * I'm think the latter is the more problematic case but this may
- * need to be revisited.
+ /* USABILITY NOTE: Naturally, I only want the require_fully_onscreen and
+ * require_on_single_xinerama flags to *become false* due to user
+ * interactions (which is allowed since certain constraints are ignored
+ * for user interactions regardless of the setting of these flags).
+ * However, whether to make these flags *become true* due to just an
+ * application interaction is a little trickier. It's possible that
+ * users may find not doing that strange since two application
+ * interactions that resize in opposite ways don't necessarily end up
+ * cancelling--but it may also be strange for the user to have an
+ * application resize the window so that it's onscreen, the user forgets
+ * about it, and then later the app is able to resize itself off the
+ * screen. Anyway, for now, I'm think the latter is the more problematic
+ * case but this may need to be revisited.
*/
/* The require onscreen/on-single-xinerama stuff is relative to the
@@ -779,9 +577,6 @@ unextend_by_frame (MetaRectangle *rect,
rect->height -= fgeom->top_height + fgeom->bottom_height;
}
-/* We pack the results into MetaRectangle structs just for convienience; we
- * don't actually use the position of those rects.
- */
static inline void
get_size_limits (const MetaWindow *window,
const MetaFrameGeometry *fgeom,
@@ -789,6 +584,9 @@ get_size_limits (const MetaWindow *window,
MetaRectangle *min_size,
MetaRectangle *max_size)
{
+ /* We pack the results into MetaRectangle structs just for convienience; we
+ * don't actually use the position of those rects.
+ */
min_size->width = window->size_hints.min_width;
min_size->height = window->size_hints.min_height;
max_size->width = window->size_hints.max_width;
@@ -1265,10 +1063,6 @@ constrain_partially_onscreen (MetaWindow *window,
horiz_amount = info->current.width - horiz_amount;
vert_amount = info->current.height - vert_amount;
- /* FIXME!!!! I need to change amounts for user resize so that user
- * doesn't move the window further offscreen than it already is.
- */
-
/* Extend the region, have a helper function handle the constraint,
* then return the region to its original size.
*/