1abef24154
And drop the clutter helper Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3128>
1966 lines
65 KiB
C
1966 lines
65 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2005, 2006 Elijah Newren
|
|
* [mtk_rectangle_intersect() is copyright the GTK+ Team according to Havoc,
|
|
* see gdkrectangle.c. As far as Havoc knows, he probably wrote
|
|
* mtk_rectangle_equal(), and I'm guessing it's (C) Red Hat. So...]
|
|
* Copyright (C) 1995-2000 GTK+ Team
|
|
* Copyright (C) 2002 Red Hat, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/meta-monitor-transform.h"
|
|
#include "core/boxes-private.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include "meta/util.h"
|
|
|
|
char*
|
|
meta_rectangle_to_string (const MtkRectangle *rect,
|
|
char *output)
|
|
{
|
|
/* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit.
|
|
* Should be more than enough space. Note that of this space, the
|
|
* trailing \0 will be overwritten for all but the last rectangle.
|
|
*/
|
|
g_snprintf (output, RECT_LENGTH, "%d,%d +%d,%d",
|
|
rect->x, rect->y, rect->width, rect->height);
|
|
|
|
return output;
|
|
}
|
|
|
|
char*
|
|
meta_rectangle_region_to_string (GList *region,
|
|
const char *separator_string,
|
|
char *output)
|
|
{
|
|
/* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5
|
|
* for each digit. Should be more than enough space. Note that of this
|
|
* space, the trailing \0 will be overwritten for all but the last
|
|
* rectangle.
|
|
*/
|
|
char rect_string[RECT_LENGTH];
|
|
|
|
GList *tmp = region;
|
|
char *cur = output;
|
|
|
|
if (region == NULL)
|
|
g_snprintf (output, 10, "(EMPTY)");
|
|
|
|
while (tmp)
|
|
{
|
|
MtkRectangle *rect = tmp->data;
|
|
g_snprintf (rect_string, RECT_LENGTH, "[%d,%d +%d,%d]",
|
|
rect->x, rect->y, rect->width, rect->height);
|
|
cur = g_stpcpy (cur, rect_string);
|
|
tmp = tmp->next;
|
|
if (tmp)
|
|
cur = g_stpcpy (cur, separator_string);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
char*
|
|
meta_rectangle_edge_list_to_string (GList *edge_list,
|
|
const char *separator_string,
|
|
char *output)
|
|
{
|
|
/* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5 for
|
|
* each digit. Should be more than enough space. Note that of this
|
|
* space, the trailing \0 will be overwritten for all but the last
|
|
* rectangle.
|
|
*
|
|
* Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and
|
|
* 2 more spaces, for a total of 10 more.
|
|
*/
|
|
char rect_string[EDGE_LENGTH];
|
|
|
|
char *cur = output;
|
|
GList *tmp = edge_list;
|
|
|
|
if (edge_list == NULL)
|
|
g_snprintf (output, 10, "(EMPTY)");
|
|
|
|
while (tmp)
|
|
{
|
|
MetaEdge *edge = tmp->data;
|
|
MtkRectangle *rect = &edge->rect;
|
|
g_snprintf (rect_string, EDGE_LENGTH, "([%d,%d +%d,%d], %2d, %2d)",
|
|
rect->x, rect->y, rect->width, rect->height,
|
|
edge->side_type, edge->edge_type);
|
|
cur = g_stpcpy (cur, rect_string);
|
|
tmp = tmp->next;
|
|
if (tmp)
|
|
cur = g_stpcpy (cur, separator_string);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
void
|
|
meta_rectangle_resize_with_gravity (const MtkRectangle *old_rect,
|
|
MtkRectangle *rect,
|
|
MetaGravity gravity,
|
|
int new_width,
|
|
int new_height)
|
|
{
|
|
/* FIXME: I'm too deep into this to know whether the below comment is
|
|
* still clear or not now that I've moved it out of constraints.c.
|
|
* boxes.h has a good comment, but I'm not sure if the below info is also
|
|
* helpful on top of that (or whether it has superfluous info).
|
|
*/
|
|
|
|
/* These formulas may look overly simplistic at first but you can work
|
|
* everything out with a left_frame_with, right_frame_width,
|
|
* border_width, and old and new client area widths (instead of old total
|
|
* width and new total width) and you come up with the same formulas.
|
|
*
|
|
* Also, note that the reason we can treat META_GRAVITY_NORTH_WEST and
|
|
* META_GRAVITY_STATIC the same is because we're not given a location at
|
|
* which to place the window--the window was already placed
|
|
* appropriately before. So, META_GRAVITY_NORTH_WEST for this function
|
|
* means to just leave the upper left corner of the outer window
|
|
* where it already is, and META_GRAVITY_STATIC for this function means to
|
|
* just leave the upper left corner of the inner window where it
|
|
* already is. But leaving either of those two corners where they
|
|
* already are will ensure that the other corner is fixed as well
|
|
* (since frame size doesn't change)--thus making the two
|
|
* equivalent.
|
|
*/
|
|
|
|
/* First, the x direction */
|
|
switch (gravity)
|
|
{
|
|
case META_GRAVITY_NORTH_WEST:
|
|
case META_GRAVITY_WEST:
|
|
case META_GRAVITY_SOUTH_WEST:
|
|
rect->x = old_rect->x;
|
|
break;
|
|
|
|
case META_GRAVITY_NORTH:
|
|
case META_GRAVITY_CENTER:
|
|
case META_GRAVITY_SOUTH:
|
|
/* FIXME: Needing to adjust new_width kind of sucks, but not doing so
|
|
* would cause drift.
|
|
*/
|
|
new_width -= (old_rect->width - new_width) % 2;
|
|
rect->x = old_rect->x + (old_rect->width - new_width)/2;
|
|
break;
|
|
|
|
case META_GRAVITY_NORTH_EAST:
|
|
case META_GRAVITY_EAST:
|
|
case META_GRAVITY_SOUTH_EAST:
|
|
rect->x = old_rect->x + (old_rect->width - new_width);
|
|
break;
|
|
|
|
case META_GRAVITY_STATIC:
|
|
default:
|
|
rect->x = old_rect->x;
|
|
break;
|
|
}
|
|
rect->width = new_width;
|
|
|
|
/* Next, the y direction */
|
|
switch (gravity)
|
|
{
|
|
case META_GRAVITY_NORTH_WEST:
|
|
case META_GRAVITY_NORTH:
|
|
case META_GRAVITY_NORTH_EAST:
|
|
rect->y = old_rect->y;
|
|
break;
|
|
|
|
case META_GRAVITY_WEST:
|
|
case META_GRAVITY_CENTER:
|
|
case META_GRAVITY_EAST:
|
|
/* FIXME: Needing to adjust new_height kind of sucks, but not doing so
|
|
* would cause drift.
|
|
*/
|
|
new_height -= (old_rect->height - new_height) % 2;
|
|
rect->y = old_rect->y + (old_rect->height - new_height)/2;
|
|
break;
|
|
|
|
case META_GRAVITY_SOUTH_WEST:
|
|
case META_GRAVITY_SOUTH:
|
|
case META_GRAVITY_SOUTH_EAST:
|
|
rect->y = old_rect->y + (old_rect->height - new_height);
|
|
break;
|
|
|
|
case META_GRAVITY_STATIC:
|
|
default:
|
|
rect->y = old_rect->y;
|
|
break;
|
|
}
|
|
rect->height = new_height;
|
|
}
|
|
|
|
/* Not so simple helper function for get_minimal_spanning_set_for_region() */
|
|
static GList*
|
|
merge_spanning_rects_in_region (GList *region)
|
|
{
|
|
/* NOTE FOR ANY OPTIMIZATION PEOPLE OUT THERE: Please see the
|
|
* documentation of get_minimal_spanning_set_for_region() for performance
|
|
* considerations that also apply to this function.
|
|
*/
|
|
|
|
GList *compare;
|
|
compare = region;
|
|
|
|
if (region == NULL)
|
|
{
|
|
g_warning ("Region to merge was empty! Either you have some "
|
|
"pathological STRUT list or there's a bug somewhere!");
|
|
return NULL;
|
|
}
|
|
|
|
while (compare && compare->next)
|
|
{
|
|
MtkRectangle *a = compare->data;
|
|
GList *other = compare->next;
|
|
|
|
g_assert (a->width > 0 && a->height > 0);
|
|
|
|
while (other)
|
|
{
|
|
MtkRectangle *b = other->data;
|
|
GList *delete_me = NULL;
|
|
|
|
g_assert (b->width > 0 && b->height > 0);
|
|
|
|
/* If a contains b, just remove b */
|
|
if (mtk_rectangle_contains_rect (a, b))
|
|
{
|
|
delete_me = other;
|
|
}
|
|
/* If b contains a, just remove a */
|
|
else if (mtk_rectangle_contains_rect (b, a))
|
|
{
|
|
delete_me = compare;
|
|
}
|
|
/* If a and b might be mergeable horizontally */
|
|
else if (a->y == b->y && a->height == b->height)
|
|
{
|
|
/* If a and b overlap */
|
|
if (mtk_rectangle_overlap (a, b))
|
|
{
|
|
int new_x = MIN (a->x, b->x);
|
|
a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
|
|
a->x = new_x;
|
|
delete_me = other;
|
|
}
|
|
/* If a and b are adjacent */
|
|
else if (a->x + a->width == b->x || a->x == b->x + b->width)
|
|
{
|
|
int new_x = MIN (a->x, b->x);
|
|
a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
|
|
a->x = new_x;
|
|
delete_me = other;
|
|
}
|
|
}
|
|
/* If a and b might be mergeable vertically */
|
|
else if (a->x == b->x && a->width == b->width)
|
|
{
|
|
/* If a and b overlap */
|
|
if (mtk_rectangle_overlap (a, b))
|
|
{
|
|
int new_y = MIN (a->y, b->y);
|
|
a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
|
|
a->y = new_y;
|
|
delete_me = other;
|
|
}
|
|
/* If a and b are adjacent */
|
|
else if (a->y + a->height == b->y || a->y == b->y + b->height)
|
|
{
|
|
int new_y = MIN (a->y, b->y);
|
|
a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
|
|
a->y = new_y;
|
|
delete_me = other;
|
|
}
|
|
}
|
|
|
|
other = other->next;
|
|
|
|
/* Delete any rectangle in the list that is no longer wanted */
|
|
if (delete_me != NULL)
|
|
{
|
|
/* Deleting the rect we compare others to is a little tricker */
|
|
if (compare == delete_me)
|
|
{
|
|
compare = compare->next;
|
|
other = compare->next;
|
|
a = compare->data;
|
|
}
|
|
|
|
/* Okay, we can free it now */
|
|
g_free (delete_me->data);
|
|
region = g_list_delete_link (region, delete_me);
|
|
}
|
|
|
|
}
|
|
|
|
compare = compare->next;
|
|
}
|
|
|
|
return region;
|
|
}
|
|
|
|
/* Simple helper function for get_minimal_spanning_set_for_region()... */
|
|
static gint
|
|
compare_rect_areas (gconstpointer a, gconstpointer b)
|
|
{
|
|
const MtkRectangle *a_rect = (gconstpointer) a;
|
|
const MtkRectangle *b_rect = (gconstpointer) b;
|
|
|
|
int a_area = mtk_rectangle_area (a_rect);
|
|
int b_area = mtk_rectangle_area (b_rect);
|
|
|
|
return b_area - a_area; /* positive ret value denotes b > a, ... */
|
|
}
|
|
|
|
/* ... and another helper for get_minimal_spanning_set_for_region()... */
|
|
static gboolean
|
|
check_strut_align (MetaStrut *strut,
|
|
const MtkRectangle *rect)
|
|
{
|
|
/* Check whether @strut actually aligns to the side of @rect it claims */
|
|
switch (strut->side)
|
|
{
|
|
case META_SIDE_TOP:
|
|
return BOX_TOP (strut->rect) <= BOX_TOP (*rect);
|
|
case META_SIDE_BOTTOM:
|
|
return BOX_BOTTOM (strut->rect) >= BOX_BOTTOM (*rect);
|
|
case META_SIDE_LEFT:
|
|
return BOX_LEFT (strut->rect) <= BOX_LEFT (*rect);
|
|
case META_SIDE_RIGHT:
|
|
return BOX_RIGHT (strut->rect) >= BOX_RIGHT (*rect);
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* meta_rectangle_get_minimal_spanning_set_for_region:
|
|
* @basic_rect: Input rectangle
|
|
* @all_struts: (element-type Meta.Rectangle): List of struts
|
|
*
|
|
* This function is trying to find a "minimal spanning set (of rectangles)"
|
|
* for a given region.
|
|
*
|
|
* The region is given by taking basic_rect, then removing the areas
|
|
* covered by all the rectangles in the all_struts list, and then expanding
|
|
* the resulting region by the given number of pixels in each direction.
|
|
*
|
|
* A "minimal spanning set (of rectangles)" is the best name I could come
|
|
* up with for the concept I had in mind. Basically, for a given region, I
|
|
* want a set of rectangles with the property that a window is contained in
|
|
* the region if and only if it is contained within at least one of the
|
|
* rectangles.
|
|
*
|
|
* Returns: (transfer full) (element-type Meta.Rectangle): Minimal spanning set
|
|
*/
|
|
GList*
|
|
meta_rectangle_get_minimal_spanning_set_for_region (
|
|
const MtkRectangle *basic_rect,
|
|
const GSList *all_struts)
|
|
{
|
|
/* NOTE FOR OPTIMIZERS: This function *might* be somewhat slow,
|
|
* especially due to the call to merge_spanning_rects_in_region() (which
|
|
* is O(n^2) where n is the size of the list generated in this function).
|
|
* This is made more onerous due to the fact that it involves a fair
|
|
* number of memory allocation and deallocation calls. However, n is 1
|
|
* for default installations of Gnome (because partial struts aren't used
|
|
* 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. 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 monitor 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
|
|
* the list and sorting them appropriately, I believe it might be.
|
|
* (2) only call merge_spanning_rects_in_region() with a subset of the
|
|
* full list of rectangles. I believe from some of my preliminary
|
|
* debugging and thinking about it that it is possible to figure out
|
|
* apriori groups of rectangles which are only merge candidates with
|
|
* each other. (See testboxes.c:get_screen_region() when which==2
|
|
* and track the steps of this function carefully to see what gave
|
|
* me the hint that this might work)
|
|
* (3) figure out how to avoid merge_spanning_rects_in_region(). I think
|
|
* 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) 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;
|
|
GList *tmp_list;
|
|
const GSList *strut_iter;
|
|
MtkRectangle *temp_rect;
|
|
|
|
/* The algorithm is basically as follows:
|
|
* Initialize rectangle_set to basic_rect
|
|
* Foreach strut:
|
|
* Foreach rectangle in rectangle_set:
|
|
* - Split the rectangle into new rectangles that don't overlap the
|
|
* strut (but which are as big as possible otherwise)
|
|
* - Remove the old (pre-split) rectangle from the rectangle_set,
|
|
* and replace it with the new rectangles generated from the
|
|
* splitting
|
|
*/
|
|
|
|
temp_rect = g_new (MtkRectangle, 1);
|
|
*temp_rect = *basic_rect;
|
|
ret = g_list_prepend (NULL, temp_rect);
|
|
|
|
for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next)
|
|
{
|
|
GList *rect_iter;
|
|
MetaStrut *strut = (MetaStrut*)strut_iter->data;
|
|
MtkRectangle *strut_rect = &strut->rect;
|
|
|
|
tmp_list = ret;
|
|
ret = NULL;
|
|
rect_iter = tmp_list;
|
|
while (rect_iter)
|
|
{
|
|
MtkRectangle *rect = (MtkRectangle*) rect_iter->data;
|
|
|
|
if (!mtk_rectangle_overlap (strut_rect, rect) ||
|
|
!check_strut_align (strut, basic_rect))
|
|
ret = g_list_prepend (ret, rect);
|
|
else
|
|
{
|
|
/* If there is area in rect left of strut */
|
|
if (BOX_LEFT (*rect) < BOX_LEFT (*strut_rect))
|
|
{
|
|
temp_rect = g_new (MtkRectangle, 1);
|
|
*temp_rect = *rect;
|
|
temp_rect->width = BOX_LEFT (*strut_rect) - BOX_LEFT (*rect);
|
|
ret = g_list_prepend (ret, temp_rect);
|
|
}
|
|
/* If there is area in rect right of strut */
|
|
if (BOX_RIGHT (*rect) > BOX_RIGHT (*strut_rect))
|
|
{
|
|
int new_x;
|
|
temp_rect = g_new (MtkRectangle, 1);
|
|
*temp_rect = *rect;
|
|
new_x = BOX_RIGHT (*strut_rect);
|
|
temp_rect->width = BOX_RIGHT (*rect) - new_x;
|
|
temp_rect->x = new_x;
|
|
ret = g_list_prepend (ret, temp_rect);
|
|
}
|
|
/* If there is area in rect above strut */
|
|
if (BOX_TOP (*rect) < BOX_TOP (*strut_rect))
|
|
{
|
|
temp_rect = g_new (MtkRectangle, 1);
|
|
*temp_rect = *rect;
|
|
temp_rect->height = BOX_TOP (*strut_rect) - BOX_TOP (*rect);
|
|
ret = g_list_prepend (ret, temp_rect);
|
|
}
|
|
/* If there is area in rect below strut */
|
|
if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*strut_rect))
|
|
{
|
|
int new_y;
|
|
temp_rect = g_new (MtkRectangle, 1);
|
|
*temp_rect = *rect;
|
|
new_y = BOX_BOTTOM (*strut_rect);
|
|
temp_rect->height = BOX_BOTTOM (*rect) - new_y;
|
|
temp_rect->y = new_y;
|
|
ret = g_list_prepend (ret, temp_rect);
|
|
}
|
|
g_free (rect);
|
|
}
|
|
rect_iter = rect_iter->next;
|
|
}
|
|
g_list_free (tmp_list);
|
|
}
|
|
|
|
/* Sort by maximal area, just because I feel like it... */
|
|
ret = g_list_sort (ret, compare_rect_areas);
|
|
|
|
/* Merge rectangles if possible so that the list really is minimal */
|
|
ret = merge_spanning_rects_in_region (ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* meta_rectangle_expand_region: (skip)
|
|
*
|
|
*/
|
|
GList*
|
|
meta_rectangle_expand_region (GList *region,
|
|
const int left_expand,
|
|
const int right_expand,
|
|
const int top_expand,
|
|
const int bottom_expand)
|
|
{
|
|
return meta_rectangle_expand_region_conditionally (region,
|
|
left_expand,
|
|
right_expand,
|
|
top_expand,
|
|
bottom_expand,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
/**
|
|
* meta_rectangle_expand_region_conditionally: (skip)
|
|
*
|
|
*/
|
|
GList*
|
|
meta_rectangle_expand_region_conditionally (GList *region,
|
|
const int left_expand,
|
|
const int right_expand,
|
|
const int top_expand,
|
|
const int bottom_expand,
|
|
const int min_x,
|
|
const int min_y)
|
|
{
|
|
GList *tmp_list = region;
|
|
while (tmp_list)
|
|
{
|
|
MtkRectangle *rect = (MtkRectangle*) tmp_list->data;
|
|
if (rect->width >= min_x)
|
|
{
|
|
rect->x -= left_expand;
|
|
rect->width += (left_expand + right_expand);
|
|
}
|
|
if (rect->height >= min_y)
|
|
{
|
|
rect->y -= top_expand;
|
|
rect->height += (top_expand + bottom_expand);
|
|
}
|
|
tmp_list = tmp_list->next;
|
|
}
|
|
|
|
return region;
|
|
}
|
|
|
|
void
|
|
meta_rectangle_expand_to_avoiding_struts (MtkRectangle *rect,
|
|
const MtkRectangle *expand_to,
|
|
const MetaDirection direction,
|
|
const GSList *all_struts)
|
|
{
|
|
const GSList *strut_iter;
|
|
|
|
/* If someone wants this function to handle more fine-grained
|
|
* direction expanding in the future (e.g. only left, or fully
|
|
* horizontal plus upward), feel free. But I'm hard-coding for both
|
|
* horizontal directions (exclusive-)or both vertical directions.
|
|
*/
|
|
g_assert ((direction == META_DIRECTION_HORIZONTAL) ^
|
|
(direction == META_DIRECTION_VERTICAL ));
|
|
|
|
if (direction == META_DIRECTION_HORIZONTAL)
|
|
{
|
|
rect->x = expand_to->x;
|
|
rect->width = expand_to->width;
|
|
}
|
|
else
|
|
{
|
|
rect->y = expand_to->y;
|
|
rect->height = expand_to->height;
|
|
}
|
|
|
|
|
|
/* Run over all struts */
|
|
for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next)
|
|
{
|
|
MetaStrut *strut = (MetaStrut*) strut_iter->data;
|
|
|
|
/* Skip struts that don't overlap */
|
|
if (!mtk_rectangle_overlap (&strut->rect, rect))
|
|
continue;
|
|
|
|
if (direction == META_DIRECTION_HORIZONTAL)
|
|
{
|
|
if (strut->side == META_SIDE_LEFT)
|
|
{
|
|
int offset = BOX_RIGHT (strut->rect) - BOX_LEFT (*rect);
|
|
rect->x += offset;
|
|
rect->width -= offset;
|
|
}
|
|
else if (strut->side == META_SIDE_RIGHT)
|
|
{
|
|
int offset = BOX_RIGHT (*rect) - BOX_LEFT (strut->rect);
|
|
rect->width -= offset;
|
|
}
|
|
/* else ignore the strut */
|
|
}
|
|
else /* direction == META_DIRECTION_VERTICAL */
|
|
{
|
|
if (strut->side == META_SIDE_TOP)
|
|
{
|
|
int offset = BOX_BOTTOM (strut->rect) - BOX_TOP (*rect);
|
|
rect->y += offset;
|
|
rect->height -= offset;
|
|
}
|
|
else if (strut->side == META_SIDE_BOTTOM)
|
|
{
|
|
int offset = BOX_BOTTOM (*rect) - BOX_TOP (strut->rect);
|
|
rect->height -= offset;
|
|
}
|
|
/* else ignore the strut */
|
|
}
|
|
} /* end loop over struts */
|
|
} /* end meta_rectangle_expand_to_avoiding_struts */
|
|
|
|
void
|
|
meta_rectangle_free_list_and_elements (GList *filled_list)
|
|
{
|
|
g_list_free_full (filled_list, g_free);
|
|
}
|
|
|
|
gboolean
|
|
meta_rectangle_could_fit_in_region (const GList *spanning_rects,
|
|
const MtkRectangle *rect)
|
|
{
|
|
const GList *temp;
|
|
gboolean could_fit;
|
|
|
|
temp = spanning_rects;
|
|
could_fit = FALSE;
|
|
while (!could_fit && temp != NULL)
|
|
{
|
|
could_fit = could_fit || mtk_rectangle_could_fit_rect (temp->data, rect);
|
|
temp = temp->next;
|
|
}
|
|
|
|
return could_fit;
|
|
}
|
|
|
|
gboolean
|
|
meta_rectangle_contained_in_region (const GList * spanning_rects,
|
|
const MtkRectangle * rect)
|
|
{
|
|
const GList *temp;
|
|
gboolean contained;
|
|
|
|
temp = spanning_rects;
|
|
contained = FALSE;
|
|
while (!contained && temp != NULL)
|
|
{
|
|
contained = contained || mtk_rectangle_contains_rect (temp->data, rect);
|
|
temp = temp->next;
|
|
}
|
|
|
|
return contained;
|
|
}
|
|
|
|
gboolean
|
|
meta_rectangle_overlaps_with_region (const GList * spanning_rects,
|
|
const MtkRectangle * rect)
|
|
{
|
|
const GList *temp;
|
|
gboolean overlaps;
|
|
|
|
temp = spanning_rects;
|
|
overlaps = FALSE;
|
|
while (!overlaps && temp != NULL)
|
|
{
|
|
overlaps = overlaps || mtk_rectangle_overlap (temp->data, rect);
|
|
temp = temp->next;
|
|
}
|
|
|
|
return overlaps;
|
|
}
|
|
|
|
gboolean
|
|
meta_rectangle_is_adjacent_to_any_in_region (const GList *spanning_rects,
|
|
MtkRectangle *rect)
|
|
{
|
|
const GList *l;
|
|
|
|
for (l = spanning_rects; l; l = l->next)
|
|
{
|
|
MtkRectangle *other = (MtkRectangle *) l->data;
|
|
|
|
if (rect == other || mtk_rectangle_equal (rect, other))
|
|
continue;
|
|
|
|
if (meta_rectangle_is_adjacent_to (rect, other))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
meta_rectangle_clamp_to_fit_into_region (const GList *spanning_rects,
|
|
FixedDirections fixed_directions,
|
|
MtkRectangle *rect,
|
|
const MtkRectangle *min_size)
|
|
{
|
|
const GList *temp;
|
|
const MtkRectangle *best_rect = NULL;
|
|
int best_overlap = 0;
|
|
|
|
/* First, find best rectangle from spanning_rects to which we can clamp
|
|
* rect to fit into.
|
|
*/
|
|
for (temp = spanning_rects; temp; temp = temp->next)
|
|
{
|
|
MtkRectangle *compare_rect = temp->data;
|
|
int maximal_overlap_amount_for_compare;
|
|
|
|
/* If x is fixed and the entire width of rect doesn't fit in compare,
|
|
* skip this rectangle.
|
|
*/
|
|
if ((fixed_directions & FIXED_DIRECTION_X) &&
|
|
(compare_rect->x > rect->x ||
|
|
compare_rect->x + compare_rect->width < rect->x + rect->width))
|
|
continue;
|
|
|
|
/* If y is fixed and the entire height of rect doesn't fit in compare,
|
|
* skip this rectangle.
|
|
*/
|
|
if ((fixed_directions & FIXED_DIRECTION_Y) &&
|
|
(compare_rect->y > rect->y ||
|
|
compare_rect->y + compare_rect->height < rect->y + rect->height))
|
|
continue;
|
|
|
|
/* If compare can't hold the min_size window, skip this rectangle. */
|
|
if (compare_rect->width < min_size->width ||
|
|
compare_rect->height < min_size->height)
|
|
continue;
|
|
|
|
/* Determine maximal overlap amount */
|
|
maximal_overlap_amount_for_compare =
|
|
MIN (rect->width, compare_rect->width) *
|
|
MIN (rect->height, compare_rect->height);
|
|
|
|
/* See if this is the best rect so far */
|
|
if (maximal_overlap_amount_for_compare > best_overlap)
|
|
{
|
|
best_rect = compare_rect;
|
|
best_overlap = maximal_overlap_amount_for_compare;
|
|
}
|
|
}
|
|
|
|
/* Clamp rect appropriately */
|
|
if (best_rect == NULL)
|
|
{
|
|
g_warning ("No rect whose size to clamp to found!");
|
|
|
|
/* If it doesn't fit, at least make it no bigger than it has to be */
|
|
if (!(fixed_directions & FIXED_DIRECTION_X))
|
|
rect->width = min_size->width;
|
|
if (!(fixed_directions & FIXED_DIRECTION_Y))
|
|
rect->height = min_size->height;
|
|
}
|
|
else
|
|
{
|
|
rect->width = MIN (rect->width, best_rect->width);
|
|
rect->height = MIN (rect->height, best_rect->height);
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_rectangle_clip_to_region (const GList *spanning_rects,
|
|
FixedDirections fixed_directions,
|
|
MtkRectangle *rect)
|
|
{
|
|
const GList *temp;
|
|
const MtkRectangle *best_rect = NULL;
|
|
int best_overlap = 0;
|
|
|
|
/* First, find best rectangle from spanning_rects to which we will clip
|
|
* rect into.
|
|
*/
|
|
for (temp = spanning_rects; temp; temp = temp->next)
|
|
{
|
|
MtkRectangle *compare_rect = temp->data;
|
|
MtkRectangle overlap;
|
|
int maximal_overlap_amount_for_compare;
|
|
|
|
/* If x is fixed and the entire width of rect doesn't fit in compare,
|
|
* skip the rectangle.
|
|
*/
|
|
if ((fixed_directions & FIXED_DIRECTION_X) &&
|
|
(compare_rect->x > rect->x ||
|
|
compare_rect->x + compare_rect->width < rect->x + rect->width))
|
|
continue;
|
|
|
|
/* If y is fixed and the entire height of rect doesn't fit in compare,
|
|
* skip the rectangle.
|
|
*/
|
|
if ((fixed_directions & FIXED_DIRECTION_Y) &&
|
|
(compare_rect->y > rect->y ||
|
|
compare_rect->y + compare_rect->height < rect->y + rect->height))
|
|
continue;
|
|
|
|
/* Determine maximal overlap amount */
|
|
mtk_rectangle_intersect (rect, compare_rect, &overlap);
|
|
maximal_overlap_amount_for_compare = mtk_rectangle_area (&overlap);
|
|
|
|
/* See if this is the best rect so far */
|
|
if (maximal_overlap_amount_for_compare > best_overlap)
|
|
{
|
|
best_rect = compare_rect;
|
|
best_overlap = maximal_overlap_amount_for_compare;
|
|
}
|
|
}
|
|
|
|
/* Clip rect appropriately */
|
|
if (best_rect == NULL)
|
|
{
|
|
g_warning ("No rect to clip to found!");
|
|
}
|
|
else
|
|
{
|
|
/* Extra precaution with checking fixed direction shouldn't be needed
|
|
* due to logic above, but it shouldn't hurt either.
|
|
*/
|
|
if (!(fixed_directions & FIXED_DIRECTION_X))
|
|
{
|
|
/* Find the new left and right */
|
|
int new_x = MAX (rect->x, best_rect->x);
|
|
rect->width = MIN ((rect->x + rect->width) - new_x,
|
|
(best_rect->x + best_rect->width) - new_x);
|
|
rect->x = new_x;
|
|
}
|
|
|
|
/* Extra precaution with checking fixed direction shouldn't be needed
|
|
* due to logic above, but it shouldn't hurt either.
|
|
*/
|
|
if (!(fixed_directions & FIXED_DIRECTION_Y))
|
|
{
|
|
/* Clip the top, if needed */
|
|
int new_y = MAX (rect->y, best_rect->y);
|
|
rect->height = MIN ((rect->y + rect->height) - new_y,
|
|
(best_rect->y + best_rect->height) - new_y);
|
|
rect->y = new_y;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_rectangle_shove_into_region (const GList *spanning_rects,
|
|
FixedDirections fixed_directions,
|
|
MtkRectangle *rect)
|
|
{
|
|
const GList *temp;
|
|
const MtkRectangle *best_rect = NULL;
|
|
int best_overlap = 0;
|
|
int shortest_distance = G_MAXINT;
|
|
|
|
/* First, find best rectangle from spanning_rects to which we will shove
|
|
* rect into.
|
|
*/
|
|
|
|
for (temp = spanning_rects; temp; temp = temp->next)
|
|
{
|
|
MtkRectangle *compare_rect = temp->data;
|
|
int maximal_overlap_amount_for_compare;
|
|
int dist_to_compare;
|
|
|
|
/* If x is fixed and the entire width of rect doesn't fit in compare,
|
|
* skip this rectangle.
|
|
*/
|
|
if ((fixed_directions & FIXED_DIRECTION_X) &&
|
|
(compare_rect->x > rect->x ||
|
|
compare_rect->x + compare_rect->width < rect->x + rect->width))
|
|
continue;
|
|
|
|
/* If y is fixed and the entire height of rect doesn't fit in compare,
|
|
* skip this rectangle.
|
|
*/
|
|
if ((fixed_directions & FIXED_DIRECTION_Y) &&
|
|
(compare_rect->y > rect->y ||
|
|
compare_rect->y + compare_rect->height < rect->y + rect->height))
|
|
continue;
|
|
|
|
/* Determine maximal overlap amount between rect & compare_rect */
|
|
maximal_overlap_amount_for_compare =
|
|
MIN (rect->width, compare_rect->width) *
|
|
MIN (rect->height, compare_rect->height);
|
|
|
|
/* Determine distance necessary to put rect into compare_rect */
|
|
dist_to_compare = 0;
|
|
if (compare_rect->x > rect->x)
|
|
dist_to_compare += compare_rect->x - rect->x;
|
|
if (compare_rect->x + compare_rect->width < rect->x + rect->width)
|
|
dist_to_compare += (rect->x + rect->width) -
|
|
(compare_rect->x + compare_rect->width);
|
|
if (compare_rect->y > rect->y)
|
|
dist_to_compare += compare_rect->y - rect->y;
|
|
if (compare_rect->y + compare_rect->height < rect->y + rect->height)
|
|
dist_to_compare += (rect->y + rect->height) -
|
|
(compare_rect->y + compare_rect->height);
|
|
|
|
/* See if this is the best rect so far */
|
|
if ((maximal_overlap_amount_for_compare > best_overlap) ||
|
|
(maximal_overlap_amount_for_compare == best_overlap &&
|
|
dist_to_compare < shortest_distance))
|
|
{
|
|
best_rect = compare_rect;
|
|
best_overlap = maximal_overlap_amount_for_compare;
|
|
shortest_distance = dist_to_compare;
|
|
}
|
|
}
|
|
|
|
/* Shove rect appropriately */
|
|
if (best_rect == NULL)
|
|
{
|
|
g_warning ("No rect to shove into found!");
|
|
}
|
|
else
|
|
{
|
|
/* Extra precaution with checking fixed direction shouldn't be needed
|
|
* due to logic above, but it shouldn't hurt either.
|
|
*/
|
|
if (!(fixed_directions & FIXED_DIRECTION_X))
|
|
{
|
|
/* Shove to the right, if needed */
|
|
if (best_rect->x > rect->x)
|
|
rect->x = best_rect->x;
|
|
|
|
/* Shove to the left, if needed */
|
|
if (best_rect->x + best_rect->width < rect->x + rect->width)
|
|
rect->x = (best_rect->x + best_rect->width) - rect->width;
|
|
}
|
|
|
|
/* Extra precaution with checking fixed direction shouldn't be needed
|
|
* due to logic above, but it shouldn't hurt either.
|
|
*/
|
|
if (!(fixed_directions & FIXED_DIRECTION_Y))
|
|
{
|
|
/* Shove down, if needed */
|
|
if (best_rect->y > rect->y)
|
|
rect->y = best_rect->y;
|
|
|
|
/* Shove up, if needed */
|
|
if (best_rect->y + best_rect->height < rect->y + rect->height)
|
|
rect->y = (best_rect->y + best_rect->height) - rect->height;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_rectangle_find_linepoint_closest_to_point (double x1,
|
|
double y1,
|
|
double x2,
|
|
double y2,
|
|
double px,
|
|
double py,
|
|
double *valx,
|
|
double *valy)
|
|
{
|
|
/* I'll use the shorthand rx, ry for the return values, valx & valy.
|
|
* Now, we need (rx,ry) to be on the line between (x1,y1) and (x2,y2).
|
|
* For that to happen, we first need the slope of the line from (x1,y1)
|
|
* to (rx,ry) must match the slope of (x1,y1) to (x2,y2), i.e.:
|
|
* (ry-y1) (y2-y1)
|
|
* ------- = -------
|
|
* (rx-x1) (x2-x1)
|
|
* If x1==x2, though, this gives divide by zero errors, so we want to
|
|
* 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 be equal to both x1 and
|
|
* x2)
|
|
*
|
|
* The other requirement that we have is that the line from (rx,ry) to
|
|
* (px,py) must be perpendicular to the line from (x1,y1) to (x2,y2). So
|
|
* we just need to get a vector in the direction of each line, take the
|
|
* dot product of the two, and ensure that the result is 0:
|
|
* (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
|
|
*
|
|
* This gives us two equations and two unknowns:
|
|
*
|
|
* (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
|
|
* (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
|
|
*
|
|
* This particular pair of equations is always solvable so long as
|
|
* (x1,y1) and (x2,y2) are not the same point (and note that anyone who
|
|
* calls this function that way is braindead because it means that they
|
|
* really didn't specify a line after all). However, the caller should
|
|
* be careful to avoid making (x1,y1) and (x2,y2) too close (e.g. like
|
|
* 10^{-8} apart in each coordinate), otherwise roundoff error could
|
|
* cause issues. Solving these equations by hand (or using Maple(TM) or
|
|
* Mathematica(TM) or whatever) results in slightly messy expressions,
|
|
* but that's all the below few lines do.
|
|
*/
|
|
|
|
double diffx, diffy, den;
|
|
diffx = x2 - x1;
|
|
diffy = y2 - y1;
|
|
den = diffx * diffx + diffy * diffy;
|
|
|
|
*valx = (py * diffx * diffy + px * diffx * diffx +
|
|
y2 * x1 * diffy - y1 * x2 * diffy) / den;
|
|
*valy = (px * diffx * diffy + py * diffy * diffy +
|
|
x2 * y1 * diffx - x1 * y2 * diffx) / den;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/* */
|
|
/* Switching gears to code for edges instead of just rectangles */
|
|
/* */
|
|
/***************************************************************************/
|
|
|
|
gboolean
|
|
meta_rectangle_edge_aligns (const MtkRectangle *rect,
|
|
const MetaEdge *edge)
|
|
{
|
|
/* The reason for the usage of <= below instead of < is because we are
|
|
* interested in in-the-way-or-adject'ness. So, a left (i.e. vertical
|
|
* edge) occupying y positions 0-9 (which has a y of 0 and a height of
|
|
* 10) and a rectangle with top at y=10 would be considered to "align" by
|
|
* this function.
|
|
*/
|
|
switch (edge->side_type)
|
|
{
|
|
case META_SIDE_LEFT:
|
|
case META_SIDE_RIGHT:
|
|
return BOX_TOP (*rect) <= BOX_BOTTOM (edge->rect) &&
|
|
BOX_TOP (edge->rect) <= BOX_BOTTOM (*rect);
|
|
case META_SIDE_TOP:
|
|
case META_SIDE_BOTTOM:
|
|
return BOX_LEFT (*rect) <= BOX_RIGHT (edge->rect) &&
|
|
BOX_LEFT (edge->rect) <= BOX_RIGHT (*rect);
|
|
default:
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static GList*
|
|
get_rect_minus_overlap (const GList *rect_in_list,
|
|
MtkRectangle *overlap)
|
|
{
|
|
MtkRectangle *temp;
|
|
MtkRectangle *rect = rect_in_list->data;
|
|
GList *ret = NULL;
|
|
|
|
if (BOX_LEFT (*rect) < BOX_LEFT (*overlap))
|
|
{
|
|
temp = g_new (MtkRectangle, 1);
|
|
*temp = *rect;
|
|
temp->width = BOX_LEFT (*overlap) - BOX_LEFT (*rect);
|
|
ret = g_list_prepend (ret, temp);
|
|
}
|
|
if (BOX_RIGHT (*rect) > BOX_RIGHT (*overlap))
|
|
{
|
|
temp = g_new (MtkRectangle, 1);
|
|
*temp = *rect;
|
|
temp->x = BOX_RIGHT (*overlap);
|
|
temp->width = BOX_RIGHT (*rect) - BOX_RIGHT (*overlap);
|
|
ret = g_list_prepend (ret, temp);
|
|
}
|
|
if (BOX_TOP (*rect) < BOX_TOP (*overlap))
|
|
{
|
|
temp = g_new (MtkRectangle, 1);
|
|
temp->x = overlap->x;
|
|
temp->width = overlap->width;
|
|
temp->y = BOX_TOP (*rect);
|
|
temp->height = BOX_TOP (*overlap) - BOX_TOP (*rect);
|
|
ret = g_list_prepend (ret, temp);
|
|
}
|
|
if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*overlap))
|
|
{
|
|
temp = g_new (MtkRectangle, 1);
|
|
temp->x = overlap->x;
|
|
temp->width = overlap->width;
|
|
temp->y = BOX_BOTTOM (*overlap);
|
|
temp->height = BOX_BOTTOM (*rect) - BOX_BOTTOM (*overlap);
|
|
ret = g_list_prepend (ret, temp);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GList*
|
|
replace_rect_with_list (GList *old_element,
|
|
GList *new_list)
|
|
{
|
|
GList *ret;
|
|
g_assert (old_element != NULL);
|
|
|
|
if (!new_list)
|
|
{
|
|
/* If there is no new list, just remove the old_element */
|
|
ret = g_list_remove_link (old_element, old_element);
|
|
}
|
|
else
|
|
{
|
|
/* Fix up the prev and next pointers everywhere */
|
|
ret = new_list;
|
|
if (old_element->prev)
|
|
{
|
|
old_element->prev->next = new_list;
|
|
new_list->prev = old_element->prev;
|
|
}
|
|
if (old_element->next)
|
|
{
|
|
GList *tmp = g_list_last (new_list);
|
|
old_element->next->prev = tmp;
|
|
tmp->next = old_element->next;
|
|
}
|
|
}
|
|
|
|
/* Free the old_element and return the appropriate "next" point */
|
|
g_free (old_element->data);
|
|
g_list_free_1 (old_element);
|
|
return ret;
|
|
}
|
|
|
|
/* Make a copy of the strut list, make sure that copy only contains parts
|
|
* of the old_struts that intersect with the region rect, and then do some
|
|
* magic to make all the new struts disjoint (okay, we we break up struts
|
|
* that aren't disjoint in a way that the overlapping part is only included
|
|
* once, so it's not really magic...).
|
|
*/
|
|
static GList*
|
|
get_disjoint_strut_rect_list_in_region (const GSList *old_struts,
|
|
const MtkRectangle *region)
|
|
{
|
|
GList *strut_rects;
|
|
GList *tmp;
|
|
|
|
/* First, copy the list */
|
|
strut_rects = NULL;
|
|
while (old_struts)
|
|
{
|
|
MtkRectangle *cur = &((MetaStrut*)old_struts->data)->rect;
|
|
MtkRectangle *copy = g_new (MtkRectangle, 1);
|
|
*copy = *cur;
|
|
if (mtk_rectangle_intersect (copy, region, copy))
|
|
strut_rects = g_list_prepend (strut_rects, copy);
|
|
else
|
|
g_free (copy);
|
|
|
|
old_struts = old_struts->next;
|
|
}
|
|
|
|
/* Now, loop over the list and check for intersections, fixing things up
|
|
* where they do intersect.
|
|
*/
|
|
tmp = strut_rects;
|
|
while (tmp)
|
|
{
|
|
GList *compare;
|
|
|
|
MtkRectangle *cur = tmp->data;
|
|
|
|
compare = tmp->next;
|
|
while (compare)
|
|
{
|
|
MtkRectangle *comp = compare->data;
|
|
MtkRectangle overlap;
|
|
|
|
if (mtk_rectangle_intersect (cur, comp, &overlap))
|
|
{
|
|
/* Get a list of rectangles for each strut that don't overlap
|
|
* the intersection region.
|
|
*/
|
|
GList *cur_leftover = get_rect_minus_overlap (tmp, &overlap);
|
|
GList *comp_leftover = get_rect_minus_overlap (compare, &overlap);
|
|
|
|
/* Add the intersection region to cur_leftover */
|
|
MtkRectangle *overlap_allocated = g_new (MtkRectangle, 1);
|
|
*overlap_allocated = overlap;
|
|
cur_leftover = g_list_prepend (cur_leftover, overlap_allocated);
|
|
|
|
/* Fix up tmp, compare, and cur -- maybe struts too */
|
|
if (strut_rects == tmp)
|
|
{
|
|
strut_rects = replace_rect_with_list (tmp, cur_leftover);
|
|
tmp = strut_rects;
|
|
}
|
|
else
|
|
tmp = replace_rect_with_list (tmp, cur_leftover);
|
|
compare = replace_rect_with_list (compare, comp_leftover);
|
|
|
|
if (compare == NULL)
|
|
break;
|
|
|
|
cur = tmp->data;
|
|
}
|
|
|
|
compare = compare->next;
|
|
}
|
|
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
return strut_rects;
|
|
}
|
|
|
|
gint
|
|
meta_rectangle_edge_cmp_ignore_type (gconstpointer a, gconstpointer b)
|
|
{
|
|
const MetaEdge *a_edge_rect = (gconstpointer) a;
|
|
const MetaEdge *b_edge_rect = (gconstpointer) b;
|
|
int a_compare, b_compare;
|
|
|
|
/* Edges must be both vertical or both horizontal, or it doesn't make
|
|
* sense to compare them.
|
|
*/
|
|
g_assert ((a_edge_rect->rect.width == 0 && b_edge_rect->rect.width == 0) ||
|
|
(a_edge_rect->rect.height == 0 && b_edge_rect->rect.height == 0));
|
|
|
|
a_compare = b_compare = 0; /* gcc-3.4.2 sucks at figuring initialized'ness */
|
|
|
|
if (a_edge_rect->side_type == META_SIDE_LEFT ||
|
|
a_edge_rect->side_type == META_SIDE_RIGHT)
|
|
{
|
|
a_compare = a_edge_rect->rect.x;
|
|
b_compare = b_edge_rect->rect.x;
|
|
if (a_compare == b_compare)
|
|
{
|
|
a_compare = a_edge_rect->rect.y;
|
|
b_compare = b_edge_rect->rect.y;
|
|
}
|
|
}
|
|
else if (a_edge_rect->side_type == META_SIDE_TOP ||
|
|
a_edge_rect->side_type == META_SIDE_BOTTOM)
|
|
{
|
|
a_compare = a_edge_rect->rect.y;
|
|
b_compare = b_edge_rect->rect.y;
|
|
if (a_compare == b_compare)
|
|
{
|
|
a_compare = a_edge_rect->rect.x;
|
|
b_compare = b_edge_rect->rect.x;
|
|
}
|
|
}
|
|
else
|
|
g_assert ("Some idiot wanted to sort sides of different types.");
|
|
|
|
return a_compare - b_compare; /* positive value denotes a > b ... */
|
|
}
|
|
|
|
/* To make things easily testable, provide a nice way of sorting edges */
|
|
gint
|
|
meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b)
|
|
{
|
|
const MetaEdge *a_edge_rect = (gconstpointer) a;
|
|
const MetaEdge *b_edge_rect = (gconstpointer) b;
|
|
|
|
int a_compare, b_compare;
|
|
|
|
a_compare = a_edge_rect->side_type;
|
|
b_compare = b_edge_rect->side_type;
|
|
|
|
if (a_compare == b_compare)
|
|
return meta_rectangle_edge_cmp_ignore_type (a, b);
|
|
|
|
return a_compare - b_compare; /* positive value denotes a > b ... */
|
|
}
|
|
|
|
/* Determine whether two given edges overlap */
|
|
static gboolean
|
|
edges_overlap (const MetaEdge *edge1,
|
|
const MetaEdge *edge2)
|
|
{
|
|
if (edge1->rect.width == 0 && edge2->rect.width == 0)
|
|
{
|
|
return mtk_rectangle_vert_overlap (&edge1->rect, &edge2->rect) &&
|
|
edge1->rect.x == edge2->rect.x;
|
|
}
|
|
else if (edge1->rect.height == 0 && edge2->rect.height == 0)
|
|
{
|
|
return mtk_rectangle_horiz_overlap (&edge1->rect, &edge2->rect) &&
|
|
edge1->rect.y == edge2->rect.y;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
rectangle_and_edge_intersection (const MtkRectangle *rect,
|
|
const MetaEdge *edge,
|
|
MetaEdge *overlap,
|
|
int *handle_type)
|
|
{
|
|
const MtkRectangle *rect2 = &edge->rect;
|
|
MtkRectangle *result = &overlap->rect;
|
|
gboolean intersect = TRUE;
|
|
|
|
/* We don't know how to set these, so set them to invalid values */
|
|
overlap->edge_type = -1;
|
|
overlap->side_type = -1;
|
|
|
|
/* Figure out what the intersection is */
|
|
result->x = MAX (rect->x, rect2->x);
|
|
result->y = MAX (rect->y, rect2->y);
|
|
result->width = MIN (BOX_RIGHT (*rect), BOX_RIGHT (*rect2)) - result->x;
|
|
result->height = MIN (BOX_BOTTOM (*rect), BOX_BOTTOM (*rect2)) - result->y;
|
|
|
|
/* Find out if the intersection is empty; have to do it this way since
|
|
* edges have a thickness of 0
|
|
*/
|
|
if ((result->width < 0 || result->height < 0) ||
|
|
(result->width == 0 && result->height == 0))
|
|
{
|
|
result->width = 0;
|
|
result->height = 0;
|
|
intersect = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* Need to figure out the handle_type, a somewhat weird quantity:
|
|
* 0 - overlap is in middle of rect
|
|
* -1 - overlap is at the side of rect, and is on the opposite side
|
|
* of rect than the edge->side_type side
|
|
* 1 - overlap is at the side of rect, and the side of rect it is
|
|
* on is the edge->side_type side
|
|
*/
|
|
switch (edge->side_type)
|
|
{
|
|
case META_SIDE_LEFT:
|
|
if (result->x == rect->x)
|
|
*handle_type = 1;
|
|
else if (result->x == BOX_RIGHT (*rect))
|
|
*handle_type = -1;
|
|
else
|
|
*handle_type = 0;
|
|
break;
|
|
case META_SIDE_RIGHT:
|
|
if (result->x == rect->x)
|
|
*handle_type = -1;
|
|
else if (result->x == BOX_RIGHT (*rect))
|
|
*handle_type = 1;
|
|
else
|
|
*handle_type = 0;
|
|
break;
|
|
case META_SIDE_TOP:
|
|
if (result->y == rect->y)
|
|
*handle_type = 1;
|
|
else if (result->y == BOX_BOTTOM (*rect))
|
|
*handle_type = -1;
|
|
else
|
|
*handle_type = 0;
|
|
break;
|
|
case META_SIDE_BOTTOM:
|
|
if (result->y == rect->y)
|
|
*handle_type = -1;
|
|
else if (result->y == BOX_BOTTOM (*rect))
|
|
*handle_type = 1;
|
|
else
|
|
*handle_type = 0;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
return intersect;
|
|
}
|
|
|
|
/* Add all edges of the given rect to cur_edges and return the result. If
|
|
* rect_is_internal is false, the side types are switched (LEFT<->RIGHT and
|
|
* TOP<->BOTTOM).
|
|
*/
|
|
static GList*
|
|
add_edges (GList *cur_edges,
|
|
const MtkRectangle *rect,
|
|
gboolean rect_is_internal)
|
|
{
|
|
MetaEdge *temp_edge;
|
|
int i;
|
|
|
|
for (i=0; i<4; i++)
|
|
{
|
|
temp_edge = g_new (MetaEdge, 1);
|
|
temp_edge->rect = *rect;
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
temp_edge->side_type =
|
|
rect_is_internal ? META_SIDE_LEFT : META_SIDE_RIGHT;
|
|
temp_edge->rect.width = 0;
|
|
break;
|
|
case 1:
|
|
temp_edge->side_type =
|
|
rect_is_internal ? META_SIDE_RIGHT : META_SIDE_LEFT;
|
|
temp_edge->rect.x += temp_edge->rect.width;
|
|
temp_edge->rect.width = 0;
|
|
break;
|
|
case 2:
|
|
temp_edge->side_type =
|
|
rect_is_internal ? META_SIDE_TOP : META_SIDE_BOTTOM;
|
|
temp_edge->rect.height = 0;
|
|
break;
|
|
case 3:
|
|
temp_edge->side_type =
|
|
rect_is_internal ? META_SIDE_BOTTOM : META_SIDE_TOP;
|
|
temp_edge->rect.y += temp_edge->rect.height;
|
|
temp_edge->rect.height = 0;
|
|
break;
|
|
}
|
|
temp_edge->edge_type = META_EDGE_SCREEN;
|
|
cur_edges = g_list_prepend (cur_edges, temp_edge);
|
|
}
|
|
|
|
return cur_edges;
|
|
}
|
|
|
|
/* Remove any part of old_edge that intersects remove and add any resulting
|
|
* edges to cur_list. Return cur_list when finished.
|
|
*/
|
|
static GList*
|
|
split_edge (GList *cur_list,
|
|
const MetaEdge *old_edge,
|
|
const MetaEdge *remove)
|
|
{
|
|
MetaEdge *temp_edge;
|
|
switch (old_edge->side_type)
|
|
{
|
|
case META_SIDE_LEFT:
|
|
case META_SIDE_RIGHT:
|
|
g_assert (mtk_rectangle_vert_overlap (&old_edge->rect, &remove->rect));
|
|
if (BOX_TOP (old_edge->rect) < BOX_TOP (remove->rect))
|
|
{
|
|
temp_edge = g_new (MetaEdge, 1);
|
|
*temp_edge = *old_edge;
|
|
temp_edge->rect.height = BOX_TOP (remove->rect)
|
|
- BOX_TOP (old_edge->rect);
|
|
cur_list = g_list_prepend (cur_list, temp_edge);
|
|
}
|
|
if (BOX_BOTTOM (old_edge->rect) > BOX_BOTTOM (remove->rect))
|
|
{
|
|
temp_edge = g_new (MetaEdge, 1);
|
|
*temp_edge = *old_edge;
|
|
temp_edge->rect.y = BOX_BOTTOM (remove->rect);
|
|
temp_edge->rect.height = BOX_BOTTOM (old_edge->rect)
|
|
- BOX_BOTTOM (remove->rect);
|
|
cur_list = g_list_prepend (cur_list, temp_edge);
|
|
}
|
|
break;
|
|
case META_SIDE_TOP:
|
|
case META_SIDE_BOTTOM:
|
|
g_assert (mtk_rectangle_horiz_overlap (&old_edge->rect, &remove->rect));
|
|
if (BOX_LEFT (old_edge->rect) < BOX_LEFT (remove->rect))
|
|
{
|
|
temp_edge = g_new (MetaEdge, 1);
|
|
*temp_edge = *old_edge;
|
|
temp_edge->rect.width = BOX_LEFT (remove->rect)
|
|
- BOX_LEFT (old_edge->rect);
|
|
cur_list = g_list_prepend (cur_list, temp_edge);
|
|
}
|
|
if (BOX_RIGHT (old_edge->rect) > BOX_RIGHT (remove->rect))
|
|
{
|
|
temp_edge = g_new (MetaEdge, 1);
|
|
*temp_edge = *old_edge;
|
|
temp_edge->rect.x = BOX_RIGHT (remove->rect);
|
|
temp_edge->rect.width = BOX_RIGHT (old_edge->rect)
|
|
- BOX_RIGHT (remove->rect);
|
|
cur_list = g_list_prepend (cur_list, temp_edge);
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
return cur_list;
|
|
}
|
|
|
|
/* Split up edge and remove preliminary edges from strut_edges depending on
|
|
* if and how rect and edge intersect.
|
|
*/
|
|
static void
|
|
fix_up_edges (MtkRectangle *rect,
|
|
MetaEdge *edge,
|
|
GList **strut_edges,
|
|
GList **edge_splits,
|
|
gboolean *edge_needs_removal)
|
|
{
|
|
MetaEdge overlap;
|
|
int handle_type;
|
|
|
|
if (!rectangle_and_edge_intersection (rect, edge, &overlap, &handle_type))
|
|
return;
|
|
|
|
if (handle_type == 0 || handle_type == 1)
|
|
{
|
|
/* Put the result of removing overlap from edge into edge_splits */
|
|
*edge_splits = split_edge (*edge_splits, edge, &overlap);
|
|
*edge_needs_removal = TRUE;
|
|
}
|
|
|
|
if (handle_type == -1 || handle_type == 1)
|
|
{
|
|
/* Remove the overlap from strut_edges */
|
|
/* First, loop over the edges of the strut */
|
|
GList *tmp = *strut_edges;
|
|
while (tmp)
|
|
{
|
|
MetaEdge *cur = tmp->data;
|
|
/* If this is the edge that overlaps, then we need to split it */
|
|
if (edges_overlap (cur, &overlap))
|
|
{
|
|
GList *delete_me = tmp;
|
|
|
|
/* Split this edge into some new ones */
|
|
*strut_edges = split_edge (*strut_edges, cur, &overlap);
|
|
|
|
/* Delete the old one */
|
|
tmp = tmp->next;
|
|
g_free (cur);
|
|
*strut_edges = g_list_delete_link (*strut_edges, delete_me);
|
|
}
|
|
else
|
|
tmp = tmp->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* meta_rectangle_remove_intersections_with_boxes_from_edges: (skip)
|
|
*
|
|
* This function removes intersections of edges with the rectangles from the
|
|
* list of edges.
|
|
*/
|
|
GList*
|
|
meta_rectangle_remove_intersections_with_boxes_from_edges (
|
|
GList *edges,
|
|
const GSList *rectangles)
|
|
{
|
|
const GSList *rect_iter;
|
|
const int opposing = 1;
|
|
|
|
/* Now remove all intersections of rectangles with the edge list */
|
|
rect_iter = rectangles;
|
|
while (rect_iter)
|
|
{
|
|
MtkRectangle *rect = rect_iter->data;
|
|
GList *edge_iter = edges;
|
|
while (edge_iter)
|
|
{
|
|
MetaEdge *edge = edge_iter->data;
|
|
MetaEdge overlap;
|
|
int handle;
|
|
gboolean edge_iter_advanced = FALSE;
|
|
|
|
/* If this edge overlaps with this rect... */
|
|
if (rectangle_and_edge_intersection (rect, edge, &overlap, &handle))
|
|
{
|
|
|
|
/* "Intersections" where the edges touch but are opposite
|
|
* sides (e.g. a left edge against the right edge) should not
|
|
* be split. Note that the comments in
|
|
* rectangle_and_edge_intersection() say that opposing edges
|
|
* occur when handle is -1, BUT you need to remember that we
|
|
* treat the left side of a window as a right edge because
|
|
* it's what the right side of the window being moved should
|
|
* be-resisted-by/snap-to. So opposing is really 1. Anyway,
|
|
* we just keep track of it in the opposing constant set up
|
|
* above and if handle isn't equal to that, then we know the
|
|
* edge should be split.
|
|
*/
|
|
if (handle != opposing)
|
|
{
|
|
/* Keep track of this edge so we can delete it below */
|
|
GList *delete_me = edge_iter;
|
|
edge_iter = edge_iter->next;
|
|
edge_iter_advanced = TRUE;
|
|
|
|
/* Split the edge and add the result to beginning of edges */
|
|
edges = split_edge (edges, edge, &overlap);
|
|
|
|
/* Now free the edge... */
|
|
g_free (edge);
|
|
edges = g_list_delete_link (edges, delete_me);
|
|
}
|
|
}
|
|
|
|
if (!edge_iter_advanced)
|
|
edge_iter = edge_iter->next;
|
|
}
|
|
|
|
rect_iter = rect_iter->next;
|
|
}
|
|
|
|
return edges;
|
|
}
|
|
|
|
/**
|
|
* meta_rectangle_find_onscreen_edges: (skip)
|
|
*
|
|
* This function is trying to find all the edges of an onscreen region.
|
|
*/
|
|
GList*
|
|
meta_rectangle_find_onscreen_edges (const MtkRectangle *basic_rect,
|
|
const GSList *all_struts)
|
|
{
|
|
GList *ret;
|
|
GList *fixed_strut_rects;
|
|
GList *edge_iter;
|
|
const GList *strut_rect_iter;
|
|
|
|
/* The algorithm is basically as follows:
|
|
* Make sure the struts are disjoint
|
|
* Initialize the edge_set to the edges of basic_rect
|
|
* Foreach strut:
|
|
* Put together a preliminary new edge from the edges of the strut
|
|
* Foreach edge in edge_set:
|
|
* - Split the edge if it is partially contained inside the strut
|
|
* - If the edge matches an edge of the strut (i.e. a strut just
|
|
* against the edge of the screen or a not-next-to-edge-of-screen
|
|
* strut adjacent to another), then both the edge from the
|
|
* edge_set and the preliminary edge for the strut will need to
|
|
* be split
|
|
* Add any remaining "preliminary" strut edges to the edge_set
|
|
*/
|
|
|
|
/* Make sure the struts are disjoint */
|
|
fixed_strut_rects =
|
|
get_disjoint_strut_rect_list_in_region (all_struts, basic_rect);
|
|
|
|
/* Start off the list with the edges of basic_rect */
|
|
ret = add_edges (NULL, basic_rect, TRUE);
|
|
|
|
strut_rect_iter = fixed_strut_rects;
|
|
while (strut_rect_iter)
|
|
{
|
|
MtkRectangle *strut_rect = (MtkRectangle*) strut_rect_iter->data;
|
|
|
|
/* Get the new possible edges we may need to add from the strut */
|
|
GList *new_strut_edges = add_edges (NULL, strut_rect, FALSE);
|
|
|
|
edge_iter = ret;
|
|
while (edge_iter)
|
|
{
|
|
MetaEdge *cur_edge = edge_iter->data;
|
|
GList *splits_of_cur_edge = NULL;
|
|
gboolean edge_needs_removal = FALSE;
|
|
|
|
fix_up_edges (strut_rect, cur_edge,
|
|
&new_strut_edges, &splits_of_cur_edge,
|
|
&edge_needs_removal);
|
|
|
|
if (edge_needs_removal)
|
|
{
|
|
/* Delete the old edge */
|
|
GList *delete_me = edge_iter;
|
|
edge_iter = edge_iter->next;
|
|
g_free (cur_edge);
|
|
ret = g_list_delete_link (ret, delete_me);
|
|
|
|
/* Add the new split parts of the edge */
|
|
ret = g_list_concat (splits_of_cur_edge, ret);
|
|
}
|
|
else
|
|
{
|
|
edge_iter = edge_iter->next;
|
|
}
|
|
|
|
/* edge_iter was already advanced above */
|
|
}
|
|
|
|
ret = g_list_concat (new_strut_edges, ret);
|
|
strut_rect_iter = strut_rect_iter->next;
|
|
}
|
|
|
|
/* Sort the list */
|
|
ret = g_list_sort (ret, meta_rectangle_edge_cmp);
|
|
|
|
/* Free the fixed struts list */
|
|
meta_rectangle_free_list_and_elements (fixed_strut_rects);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* meta_rectangle_find_nonintersected_monitor_edges: (skip)
|
|
*
|
|
*/
|
|
GList*
|
|
meta_rectangle_find_nonintersected_monitor_edges (
|
|
const GList *monitor_rects,
|
|
const GSList *all_struts)
|
|
{
|
|
/* This function cannot easily be merged with
|
|
* meta_rectangle_find_onscreen_edges() because real screen edges
|
|
* and strut edges both are of the type "there ain't anything
|
|
* immediately on the other side"; monitor edges are different.
|
|
*/
|
|
GList *ret;
|
|
const GList *cur;
|
|
GSList *temp_rects;
|
|
|
|
/* Initialize the return list to be empty */
|
|
ret = NULL;
|
|
|
|
/* start of ret with all the edges of monitors that are adjacent to
|
|
* another monitor.
|
|
*/
|
|
cur = monitor_rects;
|
|
while (cur)
|
|
{
|
|
MtkRectangle *cur_rect = cur->data;
|
|
const GList *compare = monitor_rects;
|
|
while (compare)
|
|
{
|
|
MtkRectangle *compare_rect = compare->data;
|
|
|
|
/* Check if cur might be horizontally adjacent to compare */
|
|
if (mtk_rectangle_vert_overlap (cur_rect, compare_rect))
|
|
{
|
|
MetaSide side_type;
|
|
int y = MAX (cur_rect->y, compare_rect->y);
|
|
int height = MIN (BOX_BOTTOM (*cur_rect) - y,
|
|
BOX_BOTTOM (*compare_rect) - y);
|
|
int width = 0;
|
|
int x;
|
|
|
|
if (BOX_LEFT (*cur_rect) == BOX_RIGHT (*compare_rect))
|
|
{
|
|
/* compare_rect is to the left of cur_rect */
|
|
x = BOX_LEFT (*cur_rect);
|
|
side_type = META_SIDE_LEFT;
|
|
}
|
|
else if (BOX_RIGHT (*cur_rect) == BOX_LEFT (*compare_rect))
|
|
{
|
|
/* compare_rect is to the right of cur_rect */
|
|
x = BOX_RIGHT (*cur_rect);
|
|
side_type = META_SIDE_RIGHT;
|
|
}
|
|
else
|
|
/* These rectangles aren't adjacent after all */
|
|
x = INT_MIN;
|
|
|
|
/* If the rectangles really are adjacent */
|
|
if (x != INT_MIN)
|
|
{
|
|
/* We need a left edge for the monitor on the right, and
|
|
* a right edge for the monitor on the left. Just fill
|
|
* up the edges and stick 'em on the list.
|
|
*/
|
|
MetaEdge *new_edge = g_new (MetaEdge, 1);
|
|
|
|
new_edge->rect = MTK_RECTANGLE_INIT (x, y, width, height);
|
|
new_edge->side_type = side_type;
|
|
new_edge->edge_type = META_EDGE_MONITOR;
|
|
|
|
ret = g_list_prepend (ret, new_edge);
|
|
}
|
|
}
|
|
|
|
/* Check if cur might be vertically adjacent to compare */
|
|
if (mtk_rectangle_horiz_overlap (cur_rect, compare_rect))
|
|
{
|
|
MetaSide side_type;
|
|
int x = MAX (cur_rect->x, compare_rect->x);
|
|
int width = MIN (BOX_RIGHT (*cur_rect) - x,
|
|
BOX_RIGHT (*compare_rect) - x);
|
|
int height = 0;
|
|
int y;
|
|
|
|
if (BOX_TOP (*cur_rect) == BOX_BOTTOM (*compare_rect))
|
|
{
|
|
/* compare_rect is to the top of cur_rect */
|
|
y = BOX_TOP (*cur_rect);
|
|
side_type = META_SIDE_TOP;
|
|
}
|
|
else if (BOX_BOTTOM (*cur_rect) == BOX_TOP (*compare_rect))
|
|
{
|
|
/* compare_rect is to the bottom of cur_rect */
|
|
y = BOX_BOTTOM (*cur_rect);
|
|
side_type = META_SIDE_BOTTOM;
|
|
}
|
|
else
|
|
/* These rectangles aren't adjacent after all */
|
|
y = INT_MIN;
|
|
|
|
/* If the rectangles really are adjacent */
|
|
if (y != INT_MIN)
|
|
{
|
|
/* We need a top edge for the monitor on the bottom, and
|
|
* a bottom edge for the monitor on the top. Just fill
|
|
* up the edges and stick 'em on the list.
|
|
*/
|
|
MetaEdge *new_edge = g_new (MetaEdge, 1);
|
|
|
|
new_edge->rect = MTK_RECTANGLE_INIT (x, y, width, height);
|
|
new_edge->side_type = side_type;
|
|
new_edge->edge_type = META_EDGE_MONITOR;
|
|
|
|
ret = g_list_prepend (ret, new_edge);
|
|
}
|
|
}
|
|
|
|
compare = compare->next;
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
|
|
temp_rects = NULL;
|
|
for (; all_struts; all_struts = all_struts->next)
|
|
temp_rects = g_slist_prepend (temp_rects,
|
|
&((MetaStrut*)all_struts->data)->rect);
|
|
ret = meta_rectangle_remove_intersections_with_boxes_from_edges (ret,
|
|
temp_rects);
|
|
g_slist_free (temp_rects);
|
|
|
|
/* Sort the list */
|
|
ret = g_list_sort (ret, meta_rectangle_edge_cmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
meta_rectangle_is_adjacent_to (MtkRectangle *rect,
|
|
MtkRectangle *other)
|
|
{
|
|
int rect_x1 = rect->x;
|
|
int rect_y1 = rect->y;
|
|
int rect_x2 = rect->x + rect->width;
|
|
int rect_y2 = rect->y + rect->height;
|
|
int other_x1 = other->x;
|
|
int other_y1 = other->y;
|
|
int other_x2 = other->x + other->width;
|
|
int other_y2 = other->y + other->height;
|
|
|
|
if ((rect_x1 == other_x2 || rect_x2 == other_x1) &&
|
|
!(rect_y2 <= other_y1 || rect_y1 >= other_y2))
|
|
return TRUE;
|
|
else if ((rect_y1 == other_y2 || rect_y2 == other_y1) &&
|
|
!(rect_x2 <= other_x1 || rect_x1 >= other_x2))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
meta_rectangle_scale_double (const MtkRectangle *rect,
|
|
double scale,
|
|
MtkRoundingStrategy rounding_strategy,
|
|
MtkRectangle *dest)
|
|
{
|
|
graphene_rect_t tmp = GRAPHENE_RECT_INIT (rect->x, rect->y,
|
|
rect->width, rect->height);
|
|
|
|
graphene_rect_scale (&tmp, scale, scale, &tmp);
|
|
mtk_rectangle_from_graphene_rect (&tmp, rounding_strategy, dest);
|
|
}
|
|
|
|
/**
|
|
* meta_rectangle_transform:
|
|
* @rect: the #MtkRectangle to be transformed
|
|
* @transform: the #MetaMonitorTransform
|
|
* @width: the width of the target space
|
|
* @height: the height of the target space
|
|
* @dest: the transformed #MtkRectangle
|
|
*
|
|
* This function transforms the values in @rect in order to compensate for
|
|
* @transform applied to a #MetaMonitor, making them match the viewport. Note
|
|
* that compensating implies that for a clockwise rotation of the #MetaMonitor
|
|
* an anti-clockwise rotation has to be applied to @rect.
|
|
*/
|
|
void
|
|
meta_rectangle_transform (const MtkRectangle *rect,
|
|
MetaMonitorTransform transform,
|
|
int width,
|
|
int height,
|
|
MtkRectangle *dest)
|
|
{
|
|
switch (transform)
|
|
{
|
|
case META_MONITOR_TRANSFORM_NORMAL:
|
|
*dest = *rect;
|
|
break;
|
|
case META_MONITOR_TRANSFORM_90:
|
|
*dest = (MtkRectangle) {
|
|
.x = rect->y,
|
|
.y = height - (rect->x + rect->width),
|
|
.width = rect->height,
|
|
.height = rect->width,
|
|
};
|
|
break;
|
|
case META_MONITOR_TRANSFORM_180:
|
|
*dest = (MtkRectangle) {
|
|
.x = width - (rect->x + rect->width),
|
|
.y = height - (rect->y + rect->height),
|
|
.width = rect->width,
|
|
.height = rect->height,
|
|
};
|
|
break;
|
|
case META_MONITOR_TRANSFORM_270:
|
|
*dest = (MtkRectangle) {
|
|
.x = width - (rect->y + rect->height),
|
|
.y = rect->x,
|
|
.width = rect->height,
|
|
.height = rect->width,
|
|
};
|
|
break;
|
|
case META_MONITOR_TRANSFORM_FLIPPED:
|
|
*dest = (MtkRectangle) {
|
|
.x = width - (rect->x + rect->width),
|
|
.y = rect->y,
|
|
.width = rect->width,
|
|
.height = rect->height,
|
|
};
|
|
break;
|
|
case META_MONITOR_TRANSFORM_FLIPPED_90:
|
|
*dest = (MtkRectangle) {
|
|
.x = rect->y,
|
|
.y = rect->x,
|
|
.width = rect->height,
|
|
.height = rect->width,
|
|
};
|
|
break;
|
|
case META_MONITOR_TRANSFORM_FLIPPED_180:
|
|
*dest = (MtkRectangle) {
|
|
.x = rect->x,
|
|
.y = height - (rect->y + rect->height),
|
|
.width = rect->width,
|
|
.height = rect->height,
|
|
};
|
|
break;
|
|
case META_MONITOR_TRANSFORM_FLIPPED_270:
|
|
*dest = (MtkRectangle) {
|
|
.x = width - (rect->y + rect->height),
|
|
.y = height - (rect->x + rect->width),
|
|
.width = rect->height,
|
|
.height = rect->width,
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_rectangle_crop_and_scale (const MtkRectangle *rect,
|
|
graphene_rect_t *src_rect,
|
|
int dst_width,
|
|
int dst_height,
|
|
MtkRectangle *dest)
|
|
{
|
|
graphene_rect_t tmp = GRAPHENE_RECT_INIT (rect->x, rect->y,
|
|
rect->width, rect->height);
|
|
|
|
graphene_rect_scale (&tmp,
|
|
src_rect->size.width / dst_width,
|
|
src_rect->size.height / dst_height,
|
|
&tmp);
|
|
graphene_rect_offset (&tmp, src_rect->origin.x, src_rect->origin.y);
|
|
|
|
mtk_rectangle_from_graphene_rect (&tmp, MTK_ROUNDING_STRATEGY_GROW, dest);
|
|
}
|