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
This commit is contained in:
Elijah Newren 2007-04-04 21:54:56 +00:00 committed by Elijah Newren
parent 7a799b3a63
commit 921661e91d
5 changed files with 328 additions and 110 deletions

View File

@ -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> 2007-04-03 Elijah Newren <newren gmail com>
* src/window.c (meta_window_unmaximize): * src/window.c (meta_window_unmaximize):

View File

@ -21,7 +21,6 @@ Focus method Invariant
sloppy If the mouse is in a window, then it is focused; if the 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 mouse is not in a window, then the most recently used
window is focused. window is focused.
mouse If the mouse is in a non-DESKTOP window, then it is focused; mouse If the mouse is in a non-DESKTOP window, then it is focused;
otherwise, the designated "no_focus_window" is focused otherwise, the designated "no_focus_window" is focused
@ -36,7 +35,6 @@ Focus method Behavior
on top) on top)
sloppy Focus the window containing the pointer if there is such sloppy Focus the window containing the pointer if there is such
a window, otherwise focus the most recently used window. a window, otherwise focus the most recently used window.
mouse Focus the non-DESKTOP window containing the pointer if mouse Focus the non-DESKTOP window containing the pointer if
there is one, otherwise focus the designated there is one, otherwise focus the designated
"no_focus_window". "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 To handle these cases, Metacity compares timestamps of the event that
caused the launch and the timestamp of the last interaction with the caused the launch and the timestamp of the last interaction with the
focused window. (Case 2 is handled by providing a special timestamp focused window. (Case 2 is handled by the application providing a
of 0 for the launch time, which ensures that the window that appears special timestamp of 0 for the launch time, which ensures that the
doesn't get focus) window that appears doesn't get focus)
If the newly launched window isn't focused, some things should be done 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: 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 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 sets of invariants holds. This mode is set according to which method was
most recently used to choose a focus window: 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. mouse_mode to TRUE.
2) When using keynav to choose a focus window (e.g. alt-tab, alt-esc, 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 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), a focus_window chosen for them (e.g. switch-to-workspace keybindings),
don't change the mouse_mode and just use the current value. don't change the mouse_mode and just use the current value.

View File

@ -350,7 +350,7 @@ setup_constraint_info (ConstraintInfo *info,
else if (flags & META_IS_RESIZE_ACTION) else if (flags & META_IS_RESIZE_ACTION)
info->action_type = ACTION_RESIZE; info->action_type = ACTION_RESIZE;
else if (flags & META_IS_MOVE_ACTION) else if (flags & META_IS_MOVE_ACTION)
info->action_type = ACTION_MOVE_AND_RESIZE; info->action_type = ACTION_MOVE;
else else
g_error ("BAD, BAD developer! No treat for you! (Fix your calls to " g_error ("BAD, BAD developer! No treat for you! (Fix your calls to "
"meta_window_move_resize_internal()).\n"); "meta_window_move_resize_internal()).\n");
@ -809,6 +809,7 @@ constrain_size_increments (MetaWindow *window,
gboolean check_only) gboolean check_only)
{ {
int bh, hi, bw, wi, extra_height, extra_width; int bh, hi, bw, wi, extra_height, extra_width;
int new_width, new_height;
gboolean constraint_already_satisfied; gboolean constraint_already_satisfied;
if (priority > PRIORITY_SIZE_HINTS_INCREMENTS) if (priority > PRIORITY_SIZE_HINTS_INCREMENTS)
@ -826,10 +827,12 @@ constrain_size_increments (MetaWindow *window,
wi = window->size_hints.width_inc; wi = window->size_hints.width_inc;
extra_height = (info->current.height - bh) % hi; extra_height = (info->current.height - bh) % hi;
extra_width = (info->current.width - bw) % wi; extra_width = (info->current.width - bw) % wi;
/* ignore size increments for maximized windows */
if (window->maximized_horizontally) if (window->maximized_horizontally)
extra_width *= 0; extra_width *= 0;
if (window->maximized_vertically) if (window->maximized_vertically)
extra_height *= 0; extra_height *= 0;
/* constraint is satisfied iff there is no extra height or width */
constraint_already_satisfied = constraint_already_satisfied =
(extra_height == 0 && extra_width == 0); (extra_height == 0 && extra_width == 0);
@ -837,12 +840,24 @@ constrain_size_increments (MetaWindow *window,
return constraint_already_satisfied; return constraint_already_satisfied;
/*** Enforce constraint ***/ /*** 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, meta_rectangle_resize_with_gravity (&info->orig,
&info->current, &info->current,
info->resize_gravity, info->resize_gravity,
info->current.width - extra_width, new_width,
info->current.height - extra_height); new_height);
return TRUE; return TRUE;
} }

View File

@ -912,6 +912,11 @@ meta_set_normal_hints (MetaWindow *window,
XSizeHints *hints) XSizeHints *hints)
{ {
int x, y, w, h; 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. /* Save the last ConfigureRequest, which we put here.
* Values here set in the hints are supposed to * Values here set in the hints are supposed to
* be ignored. * be ignored.
@ -944,6 +949,7 @@ meta_set_normal_hints (MetaWindow *window,
window->size_hints.width = w; window->size_hints.width = w;
window->size_hints.height = h; window->size_hints.height = h;
/* Get base size hints */
if (window->size_hints.flags & PBaseSize) if (window->size_hints.flags & PBaseSize)
{ {
meta_topic (META_DEBUG_GEOMETRY, "Window %s sets base size %d x %d\n", meta_topic (META_DEBUG_GEOMETRY, "Window %s sets base size %d x %d\n",
@ -963,6 +969,7 @@ meta_set_normal_hints (MetaWindow *window,
} }
window->size_hints.flags |= PBaseSize; window->size_hints.flags |= PBaseSize;
/* Get min size hints */
if (window->size_hints.flags & PMinSize) if (window->size_hints.flags & PMinSize)
{ {
meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min size %d x %d\n", meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min size %d x %d\n",
@ -982,6 +989,7 @@ meta_set_normal_hints (MetaWindow *window,
} }
window->size_hints.flags |= PMinSize; window->size_hints.flags |= PMinSize;
/* Get max size hints */
if (window->size_hints.flags & PMaxSize) if (window->size_hints.flags & PMaxSize)
{ {
meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max size %d x %d\n", meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max size %d x %d\n",
@ -996,31 +1004,64 @@ meta_set_normal_hints (MetaWindow *window,
window->size_hints.flags |= PMaxSize; 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, meta_topic (META_DEBUG_GEOMETRY,
"Window %s sets max width %d less than min width %d, disabling resize\n", "Window %s sets resize width inc: %d height inc: %d\n",
window->desc, window->desc,
window->size_hints.max_width, window->size_hints.width_inc,
window->size_hints.min_width); window->size_hints.height_inc);
window->size_hints.max_width = window->size_hints.min_width; }
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, meta_topic (META_DEBUG_GEOMETRY,
"Window %s sets max height %d less than min height %d, disabling resize\n", "Window %s sets min_aspect: %d/%d max_aspect: %d/%d\n",
window->desc, window->desc,
window->size_hints.max_height, window->size_hints.min_aspect.x,
window->size_hints.min_height); window->size_hints.min_aspect.y,
window->size_hints.max_height = window->size_hints.min_height; 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) if (window->size_hints.min_width < 1)
{ {
/* another cracksmoker */ /* someone is on crack */
meta_topic (META_DEBUG_GEOMETRY, meta_topic (META_DEBUG_GEOMETRY,
"Window %s sets min width to 0, which makes no sense\n", "Window %s sets min width to 0, which makes no sense\n",
window->desc); window->desc);
@ -1051,66 +1092,160 @@ meta_set_normal_hints (MetaWindow *window,
window->size_hints.max_height = 1; 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)
{ {
/* app authors find so many ways to smoke crack */
window->size_hints.width_inc = 1; window->size_hints.width_inc = 1;
meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 width_inc to 1\n"); meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 width_inc to 1\n");
} }
if (window->size_hints.height_inc == 0) if (window->size_hints.height_inc < 1)
{ {
/* another cracksmoker */
window->size_hints.height_inc = 1; window->size_hints.height_inc = 1;
meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 height_inc to 1\n"); 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
else * numerator & denominator
{ */
window->size_hints.width_inc = 1;
window->size_hints.height_inc = 1;
window->size_hints.flags |= PResizeInc;
}
if (window->size_hints.flags & PAspect)
{
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) if (window->size_hints.min_aspect.y < 1)
window->size_hints.min_aspect.y = 1; window->size_hints.min_aspect.y = 1;
if (window->size_hints.max_aspect.y < 1) if (window->size_hints.max_aspect.y < 1)
window->size_hints.max_aspect.y = 1; window->size_hints.max_aspect.y = 1;
}
else 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;
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)
{
/* 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;
}
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.x = 1;
window->size_hints.min_aspect.y = G_MAXINT; window->size_hints.min_aspect.y = G_MAXINT;
window->size_hints.max_aspect.x = G_MAXINT; window->size_hints.max_aspect.x = G_MAXINT;
window->size_hints.max_aspect.y = 1; window->size_hints.max_aspect.y = 1;
window->size_hints.flags |= PAspect;
} }
else /* check consistency of aspect ratio hints with other hints */
if (window->size_hints.flags & PWinGravity)
{ {
meta_topic (META_DEBUG_GEOMETRY, "Window %s sets gravity %d\n", if (minh > 0 && minr > (maxw / (double)minh))
window->desc,
window->size_hints.win_gravity);
}
else
{ {
meta_topic (META_DEBUG_GEOMETRY, "Window %s doesn't set gravity, using NW\n", /* 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->desc);
window->size_hints.win_gravity = NorthWestGravity; window->size_hints.min_aspect.x = 1;
window->size_hints.flags |= PWinGravity; 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.
*/
} }
} }

View File

@ -2232,6 +2232,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 static void
meta_window_save_rect (MetaWindow *window) meta_window_save_rect (MetaWindow *window)
{ {
@ -2424,6 +2457,11 @@ meta_window_unmaximize (MetaWindow *window,
target_rect.height = window->saved_rect.height; 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 /* When we unmaximize, if we're doing a mouse move also we could
* get the window suddenly jumping to the upper left corner of * get the window suddenly jumping to the upper left corner of
* the workspace, since that's where it was when the grab op * 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) if (window->fullscreen)
{ {
MetaRectangle target_rect;
meta_topic (META_DEBUG_WINDOW_OPS, meta_topic (META_DEBUG_WINDOW_OPS,
"Unfullscreening %s\n", window->desc); "Unfullscreening %s\n", window->desc);
window->fullscreen = FALSE; 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, meta_window_move_resize (window,
FALSE, FALSE,
window->saved_rect.x, target_rect.x,
window->saved_rect.y, target_rect.y,
window->saved_rect.width, target_rect.width,
window->saved_rect.height); target_rect.height);
meta_window_update_layer (window); meta_window_update_layer (window);