diff options
-rw-r--r-- | ChangeLog | 15 | ||||
-rw-r--r-- | constraints-ideas.txt | 22 | ||||
-rw-r--r-- | doc/how-constraints-works.txt | 283 | ||||
-rw-r--r-- | src/boxes.c | 31 | ||||
-rw-r--r-- | src/constraints.c | 368 |
5 files changed, 406 insertions, 313 deletions
@@ -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. */ |