path: root/doc
diff options
Diffstat (limited to 'doc')
1 files changed, 283 insertions, 0 deletions
diff --git a/doc/how-constraints-works.txt b/doc/how-constraints-works.txt
new file mode 100644
index 0000000..327e5fe
--- /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
+ 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.