Commit f36a6273 authored by Owen W. Taylor's avatar Owen W. Taylor

Use utility functions to convert between frame and client rectangles

There are extensive places in the code where we convert between the client
rectangle and the frame rectangle. Instead of manually doing it use
new helper functions on MetaWindow and the existing meta_window_get_outer_rect().

This fixes a number of bugs where the computation was being done incorrectly,
most of these bugs are with the recently added custom frame extents, but
some relate to invisible borders or even simply to confusion between the
window and frame rectangle.

Switch the placement code to place the frame rectangle rather
than the client window - this simplifies things considerably.

https://bugzilla.gnome.org/show_bug.cgi?id=707194
parent a1087c3f
......@@ -542,6 +542,8 @@ meta_window_is_skip_taskbar
meta_window_get_rect
meta_window_get_input_rect
meta_window_get_outer_rect
meta_window_client_rect_to_frame_rect
meta_window_frame_rect_to_client_rect
meta_window_get_screen
meta_window_get_display
meta_window_get_xwindow
......
......@@ -204,10 +204,10 @@ static void place_window_if_needed (MetaWindow *window,
ConstraintInfo *info);
static void update_onscreen_requirements (MetaWindow *window,
ConstraintInfo *info);
static void extend_by_frame (MetaRectangle *rect,
const MetaFrameBorders *borders);
static void unextend_by_frame (MetaRectangle *rect,
const MetaFrameBorders *borders);
static void extend_by_frame (MetaWindow *window,
MetaRectangle *rect);
static void unextend_by_frame (MetaWindow *window,
MetaRectangle *rect);
static inline void get_size_limits (const MetaWindow *window,
const MetaFrameBorders *borders,
gboolean include_frame,
......@@ -519,10 +519,11 @@ place_window_if_needed(MetaWindow *window,
!window->minimized &&
!window->fullscreen)
{
MetaRectangle placed_rect = info->orig;
MetaRectangle placed_rect;
MetaWorkspace *cur_workspace;
const MetaMonitorInfo *monitor_info;
meta_window_get_outer_rect (window, &placed_rect);
meta_window_place (window, info->borders, info->orig.x, info->orig.y,
&placed_rect.x, &placed_rect.y);
did_placement = TRUE;
......@@ -541,6 +542,7 @@ place_window_if_needed(MetaWindow *window,
meta_workspace_get_onmonitor_region (cur_workspace,
monitor_info->number);
meta_window_frame_rect_to_client_rect (window, &placed_rect, &placed_rect);
info->current.x = placed_rect.x;
info->current.y = placed_rect.y;
......@@ -649,7 +651,7 @@ update_onscreen_requirements (MetaWindow *window,
/* The require onscreen/on-single-monitor and titlebar_visible
* stuff is relative to the outer window, not the inner
*/
extend_by_frame (&info->current, info->borders);
extend_by_frame (window, &info->current);
/* Update whether we want future constraint runs to require the
* window to be on fully onscreen.
......@@ -698,27 +700,21 @@ update_onscreen_requirements (MetaWindow *window,
}
/* Don't forget to restore the position of the window */
unextend_by_frame (&info->current, info->borders);
unextend_by_frame (window, &info->current);
}
static void
extend_by_frame (MetaRectangle *rect,
const MetaFrameBorders *borders)
extend_by_frame (MetaWindow *window,
MetaRectangle *rect)
{
rect->x -= borders->visible.left;
rect->y -= borders->visible.top;
rect->width += borders->visible.left + borders->visible.right;
rect->height += borders->visible.top + borders->visible.bottom;
meta_window_client_rect_to_frame_rect (window, rect, rect);
}
static void
unextend_by_frame (MetaRectangle *rect,
const MetaFrameBorders *borders)
unextend_by_frame (MetaWindow *window,
MetaRectangle *rect)
{
rect->x += borders->visible.left;
rect->y += borders->visible.top;
rect->width -= borders->visible.left + borders->visible.right;
rect->height -= borders->visible.top + borders->visible.bottom;
meta_window_frame_rect_to_client_rect (window, rect, rect);
}
static inline void
......@@ -778,13 +774,13 @@ constrain_modal_dialog (MetaWindow *window,
*/
child_rect = info->current;
extend_by_frame (&child_rect, info->borders);
extend_by_frame (window, &child_rect);
meta_window_get_outer_rect (parent, &parent_rect);
child_rect.x = parent_rect.x + (parent_rect.width / 2 - child_rect.width / 2);
child_rect.y = parent_rect.y + (parent_rect.height / 2 - child_rect.height / 2);
unextend_by_frame (&child_rect, info->borders);
unextend_by_frame (window, &child_rect);
x = child_rect.x;
y = child_rect.y;
......@@ -851,14 +847,14 @@ constrain_maximization (MetaWindow *window,
active_workspace_struts = window->screen->active_workspace->all_struts;
target_size = info->current;
extend_by_frame (&target_size, info->borders);
extend_by_frame (window, &target_size);
meta_rectangle_expand_to_avoiding_struts (&target_size,
&info->entire_monitor,
direction,
active_workspace_struts);
}
/* Now make target_size = maximized size of client window */
unextend_by_frame (&target_size, info->borders);
unextend_by_frame (window, &target_size);
/* Check min size constraints; max size constraints are ignored for maximized
* windows, as per bug 327543.
......@@ -917,7 +913,7 @@ constrain_tiling (MetaWindow *window,
* use an external function for the actual calculation
*/
meta_window_get_current_tile_area (window, &target_size);
unextend_by_frame (&target_size, info->borders);
unextend_by_frame (window, &target_size);
/* Check min size constraints; max size constraints are ignored as for
* maximized windows.
......@@ -1267,7 +1263,7 @@ do_screen_and_monitor_relative_constraints (
/* Determine whether constraint applies; exit if it doesn't */
how_far_it_can_be_smushed = info->current;
get_size_limits (window, info->borders, TRUE, &min_size, &max_size);
extend_by_frame (&info->current, info->borders);
extend_by_frame (window, &info->current);
if (info->action_type != ACTION_MOVE)
{
......@@ -1287,7 +1283,7 @@ do_screen_and_monitor_relative_constraints (
&info->current);
if (exit_early || constraint_satisfied || check_only)
{
unextend_by_frame (&info->current, info->borders);
unextend_by_frame (window, &info->current);
return constraint_satisfied;
}
......@@ -1311,7 +1307,7 @@ do_screen_and_monitor_relative_constraints (
info->fixed_directions,
&info->current);
unextend_by_frame (&info->current, info->borders);
unextend_by_frame (window, &info->current);
return TRUE;
}
......
......@@ -1607,6 +1607,7 @@ window_raise_with_delay_callback (void *data)
{
int x, y, root_x, root_y;
Window root, child;
MetaRectangle frame_rect;
unsigned int mask;
gboolean same_screen;
gboolean point_in_window;
......@@ -1618,9 +1619,8 @@ window_raise_with_delay_callback (void *data)
&root_x, &root_y, &x, &y, &mask);
meta_error_trap_pop (window->display);
point_in_window =
(window->frame && POINT_IN_RECT (root_x, root_y, window->frame->rect)) ||
(window->frame == NULL && POINT_IN_RECT (root_x, root_y, window->rect));
meta_window_get_outer_rect (window, &frame_rect);
point_in_window = POINT_IN_RECT (root_x, root_y, frame_rect);
if (same_screen && point_in_window)
meta_window_raise (window);
else
......@@ -2173,15 +2173,16 @@ meta_display_handle_event (MetaDisplay *display,
{
gboolean north, south;
gboolean west, east;
MetaRectangle frame_rect;
int root_x, root_y;
MetaGrabOp op;
meta_window_get_position (window, &root_x, &root_y);
meta_window_get_outer_rect (window, &frame_rect);
west = event->button.x < (root_x + 1 * window->rect.width / 3);
east = event->button.x > (root_x + 2 * window->rect.width / 3);
north = event->button.y < (root_y + 1 * window->rect.height / 3);
south = event->button.y > (root_y + 2 * window->rect.height / 3);
west = event->button.x < (frame_rect.x + 1 * frame_rect.width / 3);
east = event->button.x > (frame_rect.x + 2 * frame_rect.width / 3);
north = event->button.y < (frame_rect.y + 1 * frame_rect.height / 3);
south = event->button.y > (frame_rect.y + 2 * frame_rect.height / 3);
if (north && west)
op = META_GRAB_OP_RESIZING_NW;
......
......@@ -47,34 +47,18 @@ northwestcmp (gconstpointer a, gconstpointer b)
{
MetaWindow *aw = (gpointer) a;
MetaWindow *bw = (gpointer) b;
MetaRectangle a_frame;
MetaRectangle b_frame;
int from_origin_a;
int from_origin_b;
int ax, ay, bx, by;
/* we're interested in the frame position for cascading,
* not meta_window_get_position()
*/
if (aw->frame)
{
ax = aw->frame->rect.x;
ay = aw->frame->rect.y;
}
else
{
ax = aw->rect.x;
ay = aw->rect.y;
}
if (bw->frame)
{
bx = bw->frame->rect.x;
by = bw->frame->rect.y;
}
else
{
bx = bw->rect.x;
by = bw->rect.y;
}
meta_window_get_outer_rect (aw, &a_frame);
meta_window_get_outer_rect (bw, &b_frame);
ax = a_frame.x;
ay = a_frame.y;
bx = b_frame.x;
by = b_frame.y;
/* probably there's a fast good-enough-guess we could use here. */
from_origin_a = sqrt (ax * ax + ay * ay);
......@@ -102,6 +86,7 @@ find_next_cascade (MetaWindow *window,
GList *sorted;
int cascade_x, cascade_y;
int x_threshold, y_threshold;
MetaRectangle frame_rect;
int window_width, window_height;
int cascade_stage;
MetaRectangle work_area;
......@@ -143,30 +128,25 @@ find_next_cascade (MetaWindow *window,
cascade_y = MAX (0, work_area.y);
/* Find first cascade position that's not used. */
window_width = window->frame ? window->frame->rect.width : window->rect.width;
window_height = window->frame ? window->frame->rect.height : window->rect.height;
meta_window_get_outer_rect (window, &frame_rect);
window_width = frame_rect.width;
window_height = frame_rect.height;
cascade_stage = 0;
tmp = sorted;
while (tmp != NULL)
{
MetaWindow *w;
MetaRectangle w_frame_rect;
int wx, wy;
w = tmp->data;
/* we want frame position, not window position */
if (w->frame)
{
wx = w->frame->rect.x;
wy = w->frame->rect.y;
}
else
{
wx = w->rect.x;
wy = w->rect.y;
}
meta_window_get_outer_rect (w, &w_frame_rect);
wx = w_frame_rect.x;
wy = w_frame_rect.y;
if (ABS (wx - cascade_x) < x_threshold &&
ABS (wy - cascade_y) < y_threshold)
......@@ -223,17 +203,8 @@ find_next_cascade (MetaWindow *window,
g_list_free (sorted);
/* Convert coords to position of window, not position of frame. */
if (borders == NULL)
{
*new_x = cascade_x;
*new_y = cascade_y;
}
else
{
*new_x = cascade_x + borders->visible.left;
*new_y = cascade_y + borders->visible.top;
}
*new_x = cascade_x;
*new_y = cascade_y;
}
static void
......@@ -250,14 +221,10 @@ find_most_freespace (MetaWindow *window,
int max_area;
int max_width, max_height, left, right, top, bottom;
int left_space, right_space, top_space, bottom_space;
int frame_size_left, frame_size_top;
MetaRectangle work_area;
MetaRectangle avoid;
MetaRectangle outer;
frame_size_left = borders ? borders->visible.left : 0;
frame_size_top = borders ? borders->visible.top : 0;
meta_window_get_work_area_current_monitor (focus_window, &work_area);
meta_window_get_outer_rect (focus_window, &avoid);
meta_window_get_outer_rect (window, &outer);
......@@ -304,36 +271,54 @@ find_most_freespace (MetaWindow *window,
switch (side)
{
case META_LEFT:
*new_y = avoid.y + frame_size_top;
*new_y = avoid.y;
if (left_space > outer.width)
*new_x = avoid.x - outer.width + frame_size_left;
*new_x = avoid.x - outer.width;
else
*new_x = work_area.x + frame_size_left;
*new_x = work_area.x;
break;
case META_RIGHT:
*new_y = avoid.y + frame_size_top;
*new_y = avoid.y;
if (right_space > outer.width)
*new_x = avoid.x + avoid.width + frame_size_left;
*new_x = avoid.x + avoid.width;
else
*new_x = work_area.x + work_area.width - outer.width + frame_size_left;
*new_x = work_area.x + work_area.width - outer.width;
break;
case META_TOP:
*new_x = avoid.x + frame_size_left;
*new_x = avoid.x;
if (top_space > outer.height)
*new_y = avoid.y - outer.height + frame_size_top;
*new_y = avoid.y - outer.height;
else
*new_y = work_area.y + frame_size_top;
*new_y = work_area.y;
break;
case META_BOTTOM:
*new_x = avoid.x + frame_size_left;
*new_x = avoid.x;
if (bottom_space > outer.height)
*new_y = avoid.y + avoid.height + frame_size_top;
*new_y = avoid.y + avoid.height;
else
*new_y = work_area.y + work_area.height - outer.height + frame_size_top;
*new_y = work_area.y + work_area.height - outer.height;
break;
}
}
static gboolean
window_overlaps_focus_window (MetaWindow *window)
{
MetaWindow *focus_window;
MetaRectangle window_frame, focus_frame, overlap;
focus_window = window->display->focus_window;
if (focus_window == NULL)
return FALSE;
meta_window_get_outer_rect (window, &window_frame);
meta_window_get_outer_rect (focus_window, &focus_frame);
return meta_rectangle_intersect (&window_frame,
&focus_frame,
&overlap);
}
static void
avoid_being_obscured_as_second_modal_dialog (MetaWindow *window,
MetaFrameBorders *borders,
......@@ -355,16 +340,15 @@ avoid_being_obscured_as_second_modal_dialog (MetaWindow *window,
*/
MetaWindow *focus_window;
MetaRectangle overlap;
focus_window = window->display->focus_window;
/* denied_focus_and_not_transient is only set when focus_window != NULL */
if (window->denied_focus_and_not_transient &&
window->wm_state_modal && /* FIXME: Maybe do this for all transients? */
meta_window_same_application (window, focus_window) &&
meta_rectangle_intersect (&window->rect,
&focus_window->rect,
&overlap))
window_overlaps_focus_window (window))
{
find_most_freespace (window, borders, focus_window, *x, *y, x, y);
meta_topic (META_DEBUG_PLACEMENT,
......@@ -427,20 +411,14 @@ leftmost_cmp (gconstpointer a, gconstpointer b)
{
MetaWindow *aw = (gpointer) a;
MetaWindow *bw = (gpointer) b;
MetaRectangle a_frame;
MetaRectangle b_frame;
int ax, bx;
/* we're interested in the frame position for cascading,
* not meta_window_get_position()
*/
if (aw->frame)
ax = aw->frame->rect.x;
else
ax = aw->rect.x;
if (bw->frame)
bx = bw->frame->rect.x;
else
bx = bw->rect.x;
meta_window_get_outer_rect (aw, &a_frame);
meta_window_get_outer_rect (bw, &b_frame);
ax = a_frame.x;
bx = b_frame.x;
if (ax < bx)
return -1;
......@@ -455,20 +433,14 @@ topmost_cmp (gconstpointer a, gconstpointer b)
{
MetaWindow *aw = (gpointer) a;
MetaWindow *bw = (gpointer) b;
MetaRectangle a_frame;
MetaRectangle b_frame;
int ay, by;
/* we're interested in the frame position for cascading,
* not meta_window_get_position()
*/
if (aw->frame)
ay = aw->frame->rect.y;
else
ay = aw->rect.y;
if (bw->frame)
by = bw->frame->rect.y;
else
by = bw->rect.y;
meta_window_get_outer_rect (aw, &a_frame);
meta_window_get_outer_rect (bw, &b_frame);
ay = a_frame.y;
by = b_frame.y;
if (ay < by)
return -1;
......@@ -540,15 +512,8 @@ find_first_fit (MetaWindow *window,
right_sorted = g_list_copy (windows);
right_sorted = g_list_sort (right_sorted, topmost_cmp);
right_sorted = g_list_sort (right_sorted, leftmost_cmp);
rect.width = window->rect.width;
rect.height = window->rect.height;
if (borders)
{
rect.width += borders->visible.left + borders->visible.right;
rect.height += borders->visible.top + borders->visible.bottom;
}
meta_window_get_outer_rect (window, &rect);
#ifdef WITH_VERBOSE_MODE
{
......@@ -570,11 +535,6 @@ find_first_fit (MetaWindow *window,
{
*new_x = rect.x;
*new_y = rect.y;
if (borders)
{
*new_x += borders->visible.left;
*new_y += borders->visible.top;
}
retval = TRUE;
......@@ -598,11 +558,6 @@ find_first_fit (MetaWindow *window,
{
*new_x = rect.x;
*new_y = rect.y;
if (borders)
{
*new_x += borders->visible.left;
*new_y += borders->visible.top;
}
retval = TRUE;
......@@ -629,11 +584,6 @@ find_first_fit (MetaWindow *window,
{
*new_x = rect.x;
*new_y = rect.y;
if (borders)
{
*new_x += borders->visible.left;
*new_y += borders->visible.top;
}
retval = TRUE;
......@@ -775,24 +725,22 @@ meta_window_place (MetaWindow *window,
if (parent)
{
int w;
MetaRectangle frame_rect, parent_frame_rect;
meta_window_get_position (parent, &x, &y);
w = parent->rect.width;
meta_window_get_outer_rect (window, &frame_rect);
meta_window_get_outer_rect (parent, &parent_frame_rect);
y = parent_frame_rect.y;
/* center of parent */
x = x + w / 2;
x = parent_frame_rect.x + parent_frame_rect.width / 2;
/* center of child over center of parent */
x -= window->rect.width / 2;
x -= frame_rect.width / 2;
/* "visually" center window over parent, leaving twice as
* much space below as on top.
*/
y += (parent->rect.height - window->rect.height)/3;
/* put top of child's frame, not top of child's client */
if (borders)
y += borders->visible.top;
y += (parent_frame_rect.height - frame_rect.height)/3;
meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent\n",
window->desc);
......@@ -813,6 +761,9 @@ meta_window_place (MetaWindow *window,
{
/* Center on current monitor */
int w, h;
MetaRectangle frame_rect;
meta_window_get_outer_rect (window, &frame_rect);
/* Warning, this function is a round trip! */
xi = meta_screen_get_current_monitor_info (window->screen);
......@@ -820,8 +771,8 @@ meta_window_place (MetaWindow *window,
w = xi->rect.width;
h = xi->rect.height;
x = (w - window->rect.width) / 2;
y = (h - window->rect.height) / 2;
x = (w - frame_rect.width) / 2;
y = (h - frame_rect.height) / 2;
x += xi->rect.x;
y += xi->rect.y;
......@@ -909,17 +860,14 @@ meta_window_place (MetaWindow *window,
*/
if (window->denied_focus_and_not_transient)
{
gboolean found_fit;
MetaWindow *focus_window;
MetaRectangle overlap;
gboolean found_fit;
focus_window = window->display->focus_window;
g_assert (focus_window != NULL);
/* No need to do anything if the window doesn't overlap at all */
found_fit = !meta_rectangle_intersect (&window->rect,
&focus_window->rect,
&overlap);
found_fit = !window_overlaps_focus_window (window);
/* Try to do a first fit again, this time only taking into account the
* focus window.
......
......@@ -3959,7 +3959,7 @@ meta_window_can_tile_side_by_side (MetaWindow *window)
{
int monitor;
MetaRectangle tile_area;
MetaFrameBorders borders;
MetaRectangle client_rect;
if (!meta_window_can_tile_maximized (window))
return FALSE;
......@@ -3973,13 +3973,10 @@ meta_window_can_tile_side_by_side (MetaWindow *window)
tile_area.width /= 2;
meta_frame_calc_borders (window->frame, &borders);
tile_area.width -= (borders.visible.left + borders.visible.right);
tile_area.height -= (borders.visible.top + borders.visible.bottom);
meta_window_frame_rect_to_client_rect (window, &tile_area, &client_rect);
return tile_area.width >= window->size_hints.min_width &&
tile_area.height >= window->size_hints.min_height;
return client_rect.width >= window->size_hints.min_width &&
client_rect.height >= window->size_hints.min_height;
}
static void
......@@ -5670,23 +5667,10 @@ meta_window_move_frame (MetaWindow *window,
int root_x_nw,
int root_y_nw)
{
int x = root_x_nw;
int y = root_y_nw;
MetaRectangle rect = { root_x_nw, root_y_nw, 0, 0 };
if (window->frame)
{
MetaFrameBorders borders;
meta_frame_calc_borders (window->frame, &borders);
/* root_x_nw and root_y_nw correspond to where the top of
* the visible frame should be. Offset by the distance between
* the origin of the window and the origin of the enclosing
* window decorations.
*/
x += window->frame->child_x - borders.invisible.left;
y += window->frame->child_y - borders.invisible.top;
}
meta_window_move (window, user_op, x, y);
meta_window_frame_rect_to_client_rect (window, &rect, &rect);
meta_window_move (window, user_op, rect.x, rect.y);
}
static void
......@@ -5734,18 +5718,10 @@ meta_window_move_resize_frame (MetaWindow *window,
int w,
int h)
{
MetaFrameBorders borders;
meta_frame_calc_borders (window->frame, &borders);
/* offset by the distance between the origin of the window
* and the origin of the enclosing window decorations ( + border)
*/
root_x_nw += borders.visible.left;
root_y_nw += borders.visible.top;
w -= borders.visible.left + borders.visible.right;
h -= borders.visible.top + borders.visible.bottom;
MetaRectangle rect = { root_x_nw, root_y_nw, w, h };
meta_window_frame_rect_to_client_rect (window, &rect, &rect);
meta_window_move_resize (window, user_op, root_x_nw, root_y_nw, w, h);
meta_window_move_resize (window, user_op, rect.x, rect.y, rect.width, rect.height);
}
/**
......@@ -6057,6 +6033,91 @@ meta_window_get_input_rect (const MetaWindow *window,
*rect = window->rect;
}
/**
* meta_window_client_rect_to_frame_rect:
* @window: a #MetaWindow
* @frame_rect: client rectangle in root coordinates
* @client_rect: (out): location to store the computed corresponding frame bounds.
*
* Converts a desired bounds of the client window - what is passed to meta_window_move_resize() -
* into the corresponding bounds of the window frame (excluding invisible borders
* and client side shadows.)
*/
void
meta_window_client_rect_to_frame_rect (MetaWindow *window,
MetaRectangle *frame_rect,
MetaRectangle *client_rect)
{
if (!client_rect)
return;
*client_rect = *frame_rect;
if (window->frame)
{
MetaFrameBorders borders;
meta_frame_calc_borders (window->frame, &borders);
client_rect->x -= borders.visible.left;
client_rect->y -= borders.visible.top;
client_rect->width += borders.visible.left + borders.visible.right;
client_rect->height += borders.visible.top + borders.visible.bottom;
}
else
{
if (window->has_custom_frame_extents)
{
const GtkBorder *extents = &window->custom_frame_extents;
client_rect->x += extents->left;
client_rect->y += extents->top;
client_rect->width -= extents->left + extents->right;
client_rect->height -= extents->top + extents->bottom;
}
}
}
/**
* meta_window_frame_rect_to_client_rect:
* @window: a #MetaWindow
* @frame_rect: desired frame bounds for the window
* @client_rect: (out): location to store the computed corresponding client rectangle.
*
* Converts a desired frame bounds for a window into the bounds of the client
* window - what is passed to meta_window_move_resize().
*/
void
meta_window_frame_rect_to_client_rect (MetaWindow *window,
MetaRectangle *frame_rect,
MetaRectangle *client_rect)
{
if (!client_rect)
return;
*client_rect = *frame_rect;
if (window->frame)
{
MetaFrameBorders borders;
meta_frame_calc_borders (window->frame, &borders);
client_rect->x += borders.visible.left;
client_rect->y += borders.visible.top;
client_rect->width -= borders.visible.left + borders.visible.right;
client_rect->height -= borders.visible.top + borders.visible.bottom;
}