diff options
author | Elijah Newren <newren gmail com> | 2007-04-04 21:54:56 +0000 |
---|---|---|
committer | Elijah Newren <newren@src.gnome.org> | 2007-04-04 21:54:56 +0000 |
commit | 921661e91d8b4602a2903dee805a5505da052da9 (patch) | |
tree | f98f9073740bfbb415d3009da8636f51c1b3ee47 | |
parent | 7a799b3a6356be3b44dc249786fe31b7a4fb713d (diff) | |
download | metacity-921661e91d8b4602a2903dee805a5505da052da9.tar.gz |
Fix lots of little issues with min/max constraints and size increment
2004-04-04 Elijah Newren <newren gmail com>
Fix lots of little issues with min/max constraints and size
increment constraints. Fixes #329152, #418395, and possibly
others.
* src/window-props.c (meta_set_normal_hints):
Do more checking to make sure application specified constraints
are self-consistent, modifying the size_hints as necessary to
achieve self-consistency.
* src/constraints.c (setup_constraint_info): remove ugly
copy-pasto, (constrain_size_increments): be careful that fixing
violation of the constraints doesn't cause a violation of the
minimum size constraints.
* src/window.c (ensure_size_hints_satisfied): new function,
(meta_window_unmaximize, meta_window_unmake_fullscreen): the
saved_rect may no longer be valid (as in the case of #329152) so
call ensure_size_hints_satisfied to fix it up.
* doc/how-to-get-focus-right.txt: Some minor spacing and wording
fixes completely unrelated to the rest of this commit
svn path=/trunk/; revision=3155
-rw-r--r-- | ChangeLog | 24 | ||||
-rw-r--r-- | doc/how-to-get-focus-right.txt | 12 | ||||
-rw-r--r-- | src/constraints.c | 23 | ||||
-rw-r--r-- | src/window-props.c | 291 | ||||
-rw-r--r-- | src/window.c | 54 |
5 files changed, 311 insertions, 93 deletions
@@ -1,3 +1,27 @@ +2004-04-04 Elijah Newren <newren gmail com> + + Fix lots of little issues with min/max constraints and size + increment constraints. Fixes #329152, #418395, and possibly + others. + + * src/window-props.c (meta_set_normal_hints): + Do more checking to make sure application specified constraints + are self-consistent, modifying the size_hints as necessary to + achieve self-consistency. + + * src/constraints.c (setup_constraint_info): remove ugly + copy-pasto, (constrain_size_increments): be careful that fixing + violation of the constraints doesn't cause a violation of the + minimum size constraints. + + * src/window.c (ensure_size_hints_satisfied): new function, + (meta_window_unmaximize, meta_window_unmake_fullscreen): the + saved_rect may no longer be valid (as in the case of #329152) so + call ensure_size_hints_satisfied to fix it up. + + * doc/how-to-get-focus-right.txt: Some minor spacing and wording + fixes completely unrelated to the rest of this commit + 2007-04-03 Elijah Newren <newren gmail com> * src/window.c (meta_window_unmaximize): diff --git a/doc/how-to-get-focus-right.txt b/doc/how-to-get-focus-right.txt index c7d807be..53cfa524 100644 --- a/doc/how-to-get-focus-right.txt +++ b/doc/how-to-get-focus-right.txt @@ -21,7 +21,6 @@ Focus method Invariant sloppy If the mouse is in a window, then it is focused; if the mouse is not in a window, then the most recently used window is focused. - mouse If the mouse is in a non-DESKTOP window, then it is focused; otherwise, the designated "no_focus_window" is focused @@ -36,7 +35,6 @@ Focus method Behavior on top) sloppy Focus the window containing the pointer if there is such a window, otherwise focus the most recently used window. - mouse Focus the non-DESKTOP window containing the pointer if there is one, otherwise focus the designated "no_focus_window". @@ -66,9 +64,9 @@ cases in which a new window shouldn't be focused: To handle these cases, Metacity compares timestamps of the event that caused the launch and the timestamp of the last interaction with the -focused window. (Case 2 is handled by providing a special timestamp -of 0 for the launch time, which ensures that the window that appears -doesn't get focus) +focused window. (Case 2 is handled by the application providing a +special timestamp of 0 for the launch time, which ensures that the +window that appears doesn't get focus) If the newly launched window isn't focused, some things should be done to alert the user that there is a window to work with: @@ -88,10 +86,10 @@ attempt to handle the INHERENTLY CONFLICTING CONSTRAINTS. Metacity does this by having a mouse_mode boolean used to determine which of the two sets of invariants holds. This mode is set according to which method was most recently used to choose a focus window: - 1) When receiving EnterNotify/LeaveNotify events from mouse movement, set + 1) When receiving EnterNotify events from mouse movement, set mouse_mode to TRUE. 2) When using keynav to choose a focus window (e.g. alt-tab, alt-esc, - move-window-to-workspace keybindings), set mouse_mode to FALSE. + alt-f2, move-window-to-workspace keybindings), set mouse_mode to FALSE. 3) When handling events that don't choose a focus window but rather need a focus_window chosen for them (e.g. switch-to-workspace keybindings), don't change the mouse_mode and just use the current value. diff --git a/src/constraints.c b/src/constraints.c index 250ca7c5..8cba3bfb 100644 --- a/src/constraints.c +++ b/src/constraints.c @@ -350,7 +350,7 @@ setup_constraint_info (ConstraintInfo *info, else if (flags & META_IS_RESIZE_ACTION) info->action_type = ACTION_RESIZE; else if (flags & META_IS_MOVE_ACTION) - info->action_type = ACTION_MOVE_AND_RESIZE; + info->action_type = ACTION_MOVE; else g_error ("BAD, BAD developer! No treat for you! (Fix your calls to " "meta_window_move_resize_internal()).\n"); @@ -809,6 +809,7 @@ constrain_size_increments (MetaWindow *window, gboolean check_only) { int bh, hi, bw, wi, extra_height, extra_width; + int new_width, new_height; gboolean constraint_already_satisfied; if (priority > PRIORITY_SIZE_HINTS_INCREMENTS) @@ -826,10 +827,12 @@ constrain_size_increments (MetaWindow *window, wi = window->size_hints.width_inc; extra_height = (info->current.height - bh) % hi; extra_width = (info->current.width - bw) % wi; + /* ignore size increments for maximized windows */ if (window->maximized_horizontally) extra_width *= 0; if (window->maximized_vertically) extra_height *= 0; + /* constraint is satisfied iff there is no extra height or width */ constraint_already_satisfied = (extra_height == 0 && extra_width == 0); @@ -837,12 +840,24 @@ constrain_size_increments (MetaWindow *window, return constraint_already_satisfied; /*** Enforce constraint ***/ - /* Shrink to base + N * inc */ + new_width = info->current.width - extra_width; + new_height = info->current.height - extra_height; + + /* Adjusting down instead of up (as done in the above two lines) may + * violate minimum size constraints; fix the adjustment if this + * happens. + */ + if (new_width < window->size_hints.min_width) + new_width += ((window->size_hints.min_width - new_width)/wi + 1)*wi; + if (new_height < window->size_hints.min_height) + new_height += ((window->size_hints.min_height - new_height)/hi + 1)*hi; + + /* Resize to the new size */ meta_rectangle_resize_with_gravity (&info->orig, &info->current, info->resize_gravity, - info->current.width - extra_width, - info->current.height - extra_height); + new_width, + new_height); return TRUE; } diff --git a/src/window-props.c b/src/window-props.c index ff087340..8e2d1b0f 100644 --- a/src/window-props.c +++ b/src/window-props.c @@ -909,9 +909,14 @@ spew_size_hints_differences (const XSizeHints *old, void meta_set_normal_hints (MetaWindow *window, - XSizeHints *hints) + XSizeHints *hints) { int x, y, w, h; + double minr, maxr; + /* Some convenience vars */ + int minw, minh, maxw, maxh; /* min/max width/height */ + int basew, baseh, winc, hinc; /* base width/height, width/height increment */ + /* Save the last ConfigureRequest, which we put here. * Values here set in the hints are supposed to * be ignored. @@ -944,12 +949,13 @@ meta_set_normal_hints (MetaWindow *window, window->size_hints.width = w; window->size_hints.height = h; + /* Get base size hints */ if (window->size_hints.flags & PBaseSize) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets base size %d x %d\n", - window->desc, - window->size_hints.base_width, - window->size_hints.base_height); + window->desc, + window->size_hints.base_width, + window->size_hints.base_height); } else if (window->size_hints.flags & PMinSize) { @@ -963,12 +969,13 @@ meta_set_normal_hints (MetaWindow *window, } window->size_hints.flags |= PBaseSize; + /* Get min size hints */ if (window->size_hints.flags & PMinSize) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min size %d x %d\n", - window->desc, - window->size_hints.min_width, - window->size_hints.min_height); + window->desc, + window->size_hints.min_width, + window->size_hints.min_height); } else if (window->size_hints.flags & PBaseSize) { @@ -982,12 +989,13 @@ meta_set_normal_hints (MetaWindow *window, } window->size_hints.flags |= PMinSize; + /* Get max size hints */ if (window->size_hints.flags & PMaxSize) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max size %d x %d\n", - window->desc, - window->size_hints.max_width, - window->size_hints.max_height); + window->desc, + window->size_hints.max_width, + window->size_hints.max_height); } else { @@ -996,121 +1004,248 @@ meta_set_normal_hints (MetaWindow *window, window->size_hints.flags |= PMaxSize; } - if (window->size_hints.max_width < window->size_hints.min_width) + /* Get resize increment hints */ + if (window->size_hints.flags & PResizeInc) { - /* someone is on crack */ meta_topic (META_DEBUG_GEOMETRY, - "Window %s sets max width %d less than min width %d, disabling resize\n", - window->desc, - window->size_hints.max_width, - window->size_hints.min_width); - window->size_hints.max_width = window->size_hints.min_width; + "Window %s sets resize width inc: %d height inc: %d\n", + window->desc, + window->size_hints.width_inc, + window->size_hints.height_inc); + } + else + { + window->size_hints.width_inc = 1; + window->size_hints.height_inc = 1; + window->size_hints.flags |= PResizeInc; } - if (window->size_hints.max_height < window->size_hints.min_height) + /* Get aspect ratio hints */ + if (window->size_hints.flags & PAspect) { - /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, - "Window %s sets max height %d less than min height %d, disabling resize\n", - window->desc, - window->size_hints.max_height, - window->size_hints.min_height); - window->size_hints.max_height = window->size_hints.min_height; + "Window %s sets min_aspect: %d/%d max_aspect: %d/%d\n", + window->desc, + window->size_hints.min_aspect.x, + window->size_hints.min_aspect.y, + window->size_hints.max_aspect.x, + window->size_hints.max_aspect.y); + } + else + { + window->size_hints.min_aspect.x = 1; + window->size_hints.min_aspect.y = G_MAXINT; + window->size_hints.max_aspect.x = G_MAXINT; + window->size_hints.max_aspect.y = 1; + window->size_hints.flags |= PAspect; } + /* Get gravity hint */ + if (window->size_hints.flags & PWinGravity) + { + meta_topic (META_DEBUG_GEOMETRY, "Window %s sets gravity %d\n", + window->desc, + window->size_hints.win_gravity); + } + else + { + meta_topic (META_DEBUG_GEOMETRY, + "Window %s doesn't set gravity, using NW\n", + window->desc); + window->size_hints.win_gravity = NorthWestGravity; + window->size_hints.flags |= PWinGravity; + } + + /*** Lots of sanity checking ***/ + + /* Verify all min & max hints are at least 1 pixel */ if (window->size_hints.min_width < 1) { - /* another cracksmoker */ + /* someone is on crack */ meta_topic (META_DEBUG_GEOMETRY, - "Window %s sets min width to 0, which makes no sense\n", - window->desc); + "Window %s sets min width to 0, which makes no sense\n", + window->desc); window->size_hints.min_width = 1; } if (window->size_hints.max_width < 1) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, - "Window %s sets max width to 0, which makes no sense\n", - window->desc); + "Window %s sets max width to 0, which makes no sense\n", + window->desc); window->size_hints.max_width = 1; } if (window->size_hints.min_height < 1) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, - "Window %s sets min height to 0, which makes no sense\n", - window->desc); + "Window %s sets min height to 0, which makes no sense\n", + window->desc); window->size_hints.min_height = 1; } if (window->size_hints.max_height < 1) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, - "Window %s sets max height to 0, which makes no sense\n", - window->desc); + "Window %s sets max height to 0, which makes no sense\n", + window->desc); window->size_hints.max_height = 1; } - if (window->size_hints.flags & PResizeInc) + /* Verify size increment hints are at least 1 pixel */ + if (window->size_hints.width_inc < 1) { - meta_topic (META_DEBUG_GEOMETRY, "Window %s sets resize width inc: %d height inc: %d\n", - window->desc, - window->size_hints.width_inc, - window->size_hints.height_inc); - if (window->size_hints.width_inc == 0) - { - window->size_hints.width_inc = 1; - meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 width_inc to 1\n"); - } - if (window->size_hints.height_inc == 0) - { - window->size_hints.height_inc = 1; - meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 height_inc to 1\n"); - } + /* app authors find so many ways to smoke crack */ + window->size_hints.width_inc = 1; + meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 width_inc to 1\n"); } - else + if (window->size_hints.height_inc < 1) { - window->size_hints.width_inc = 1; + /* another cracksmoker */ window->size_hints.height_inc = 1; - window->size_hints.flags |= PResizeInc; + meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 height_inc to 1\n"); + } + /* divide by 0 cracksmokers; note that x & y in (min|max)_aspect are + * numerator & denominator + */ + if (window->size_hints.min_aspect.y < 1) + window->size_hints.min_aspect.y = 1; + if (window->size_hints.max_aspect.y < 1) + window->size_hints.max_aspect.y = 1; + + minw = window->size_hints.min_width; minh = window->size_hints.min_height; + maxw = window->size_hints.max_width; maxh = window->size_hints.max_height; + basew = window->size_hints.base_width; baseh = window->size_hints.base_height; + winc = window->size_hints.width_inc; hinc = window->size_hints.height_inc; + + /* Make sure min and max size hints are consistent with the base + increment + * size hints. If they're not, it's not a real big deal, but it means the + * effective min and max size are more restrictive than the application + * specified values. + */ + if ((minw - basew) % winc != 0) + { + /* Take advantage of integer division throwing away the remainder... */ + window->size_hints.min_width = basew + ((minw - basew)/winc + 1)*winc; + + meta_topic (META_DEBUG_GEOMETRY, + "Window %s has width_inc (%d) that does not evenly divide " + "min_width - base_width (%d - %d); thus effective " + "min_width is really %d\n", + window->desc, + winc, minw, basew, window->size_hints.min_width); + minw = window->size_hints.min_width; } + if (maxw != G_MAXINT && (maxw - basew) % winc != 0) + { + /* Take advantage of integer division throwing away the remainder... */ + window->size_hints.max_width = basew + ((maxw - basew)/winc)*winc; - if (window->size_hints.flags & PAspect) + meta_topic (META_DEBUG_GEOMETRY, + "Window %s has width_inc (%d) that does not evenly divide " + "max_width - base_width (%d - %d); thus effective " + "max_width is really %d\n", + window->desc, + winc, maxw, basew, window->size_hints.max_width); + maxw = window->size_hints.max_width; + } + if ((minh - baseh) % hinc != 0) { - meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min_aspect: %d/%d max_aspect: %d/%d\n", - window->desc, - window->size_hints.min_aspect.x, - window->size_hints.min_aspect.y, - window->size_hints.max_aspect.x, - window->size_hints.max_aspect.y); - - /* don't divide by 0 */ - if (window->size_hints.min_aspect.y < 1) - window->size_hints.min_aspect.y = 1; - if (window->size_hints.max_aspect.y < 1) - window->size_hints.max_aspect.y = 1; + /* Take advantage of integer division throwing away the remainder... */ + window->size_hints.min_height = baseh + ((minh - baseh)/hinc + 1)*hinc; + + meta_topic (META_DEBUG_GEOMETRY, + "Window %s has height_inc (%d) that does not evenly divide " + "min_height - base_height (%d - %d); thus effective " + "min_height is really %d\n", + window->desc, + hinc, minh, baseh, window->size_hints.min_height); + minh = window->size_hints.min_height; } - else + if (maxh != G_MAXINT && (maxh - baseh) % hinc != 0) { + /* Take advantage of integer division throwing away the remainder... */ + window->size_hints.max_height = baseh + ((maxh - baseh)/hinc)*hinc; + + meta_topic (META_DEBUG_GEOMETRY, + "Window %s has height_inc (%d) that does not evenly divide " + "max_height - base_height (%d - %d); thus effective " + "max_height is really %d\n", + window->desc, + hinc, maxh, baseh, window->size_hints.max_height); + maxh = window->size_hints.max_height; + } + + /* make sure maximum size hints are compatible with minimum size hints; min + * size hints take precedence. + */ + if (window->size_hints.max_width < window->size_hints.min_width) + { + /* another cracksmoker */ + meta_topic (META_DEBUG_GEOMETRY, + "Window %s sets max width %d less than min width %d, " + "disabling resize\n", + window->desc, + window->size_hints.max_width, + window->size_hints.min_width); + maxw = window->size_hints.max_width = window->size_hints.min_width; + } + if (window->size_hints.max_height < window->size_hints.min_height) + { + /* another cracksmoker */ + meta_topic (META_DEBUG_GEOMETRY, + "Window %s sets max height %d less than min height %d, " + "disabling resize\n", + window->desc, + window->size_hints.max_height, + window->size_hints.min_height); + maxh = window->size_hints.max_height = window->size_hints.min_height; + } + + /* Make sure the aspect ratio hints are sane. */ + minr = window->size_hints.min_aspect.x / + (double)window->size_hints.min_aspect.y; + maxr = window->size_hints.max_aspect.x / + (double)window->size_hints.max_aspect.y; + if (minr > maxr) + { + /* another cracksmoker; not even minimally (self) consistent */ + meta_topic (META_DEBUG_GEOMETRY, + "Window %s sets min aspect ratio larger than max aspect " + "ratio; disabling aspect ratio constraints.\n", + window->desc); window->size_hints.min_aspect.x = 1; window->size_hints.min_aspect.y = G_MAXINT; window->size_hints.max_aspect.x = G_MAXINT; window->size_hints.max_aspect.y = 1; - window->size_hints.flags |= PAspect; } - - if (window->size_hints.flags & PWinGravity) + else /* check consistency of aspect ratio hints with other hints */ { - meta_topic (META_DEBUG_GEOMETRY, "Window %s sets gravity %d\n", - window->desc, - window->size_hints.win_gravity); - } - else - { - meta_topic (META_DEBUG_GEOMETRY, "Window %s doesn't set gravity, using NW\n", - window->desc); - window->size_hints.win_gravity = NorthWestGravity; - window->size_hints.flags |= PWinGravity; + if (minh > 0 && minr > (maxw / (double)minh)) + { + /* another cracksmoker */ + meta_topic (META_DEBUG_GEOMETRY, + "Window %s sets min aspect ratio larger than largest " + "aspect ratio possible given min/max size constraints; " + "disabling min aspect ratio constraint.\n", + window->desc); + window->size_hints.min_aspect.x = 1; + window->size_hints.min_aspect.y = G_MAXINT; + } + if (maxr < (minw / (double)maxh)) + { + /* another cracksmoker */ + meta_topic (META_DEBUG_GEOMETRY, + "Window %s sets max aspect ratio smaller than smallest " + "aspect ratio possible given min/max size constraints; " + "disabling max aspect ratio constraint.\n", + window->desc); + window->size_hints.max_aspect.x = G_MAXINT; + window->size_hints.max_aspect.y = 1; + } + /* FIXME: Would be nice to check that aspect ratios are + * consistent with base and size increment constraints. + */ } } diff --git a/src/window.c b/src/window.c index d670b31d..c00a6982 100644 --- a/src/window.c +++ b/src/window.c @@ -2233,6 +2233,39 @@ meta_window_unminimize (MetaWindow *window) } static void +ensure_size_hints_satisfied (MetaRectangle *rect, + const XSizeHints *size_hints) +{ + int minw, minh, maxw, maxh; /* min/max width/height */ + int basew, baseh, winc, hinc; /* base width/height, width/height increment */ + int extra_width, extra_height; + + minw = size_hints->min_width; minh = size_hints->min_height; + maxw = size_hints->max_width; maxh = size_hints->max_height; + basew = size_hints->base_width; baseh = size_hints->base_height; + winc = size_hints->width_inc; hinc = size_hints->height_inc; + + /* First, enforce min/max size constraints */ + rect->width = CLAMP (rect->width, minw, maxw); + rect->height = CLAMP (rect->height, minh, maxh); + + /* Now, verify size increment constraints are satisfied, or make them be */ + extra_width = (rect->width - basew) % winc; + extra_height = (rect->height - baseh) % hinc; + + rect->width -= extra_width; + rect->height -= extra_height; + + /* Adjusting width/height down, as done above, may violate minimum size + * constraints, so one last fix. + */ + if (rect->width < minw) + rect->width += ((minw - rect->width)/winc + 1)*winc; + if (rect->height < minh) + rect->height += ((minh - rect->height)/hinc + 1)*hinc; +} + +static void meta_window_save_rect (MetaWindow *window) { if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen)) @@ -2424,6 +2457,11 @@ meta_window_unmaximize (MetaWindow *window, target_rect.height = window->saved_rect.height; } + /* Window's size hints may have changed while maximized, making + * saved_rect invalid. #329152 + */ + ensure_size_hints_satisfied (&target_rect, &window->size_hints); + /* When we unmaximize, if we're doing a mouse move also we could * get the window suddenly jumping to the upper left corner of * the workspace, since that's where it was when the grab op @@ -2520,17 +2558,25 @@ meta_window_unmake_fullscreen (MetaWindow *window) { if (window->fullscreen) { + MetaRectangle target_rect; + meta_topic (META_DEBUG_WINDOW_OPS, "Unfullscreening %s\n", window->desc); window->fullscreen = FALSE; + target_rect = window->saved_rect; + + /* Window's size hints may have changed while maximized, making + * saved_rect invalid. #329152 + */ + ensure_size_hints_satisfied (&target_rect, &window->size_hints); meta_window_move_resize (window, FALSE, - window->saved_rect.x, - window->saved_rect.y, - window->saved_rect.width, - window->saved_rect.height); + target_rect.x, + target_rect.y, + target_rect.width, + target_rect.height); meta_window_update_layer (window); |