Update placement policy for screen with multiple xineramas. Windows will

2003-04-05  Rob Adams  <robadams@ucla.edu>

	Update placement policy for screen with multiple xineramas.
	Windows will be placed preferentially on the xinerama with the
	pointer, and progressively further away as needed to find a place
	where the window does not overlap other windows.

	* src/place.c (rect_fits_in_work_area): function
	fit_rect_in_xinerama greatly simplified to work with new placement
	policy.
	(find_first_fit): implement new first first placement scheme

	* src/screen.c (meta_screen_get_xinerama_neighbor): look for an
	xinerama in the xinerama list that is adjacent to the specified
	xinerama.
	(meta_screen_get_natural_xinerama_list): return a list of
	xineramas in the order to be preferred by the placement algorithm
	as determined by the current location of the pointer.

	* src/screen.h: add function prototypes and an enum used by
	meta_screen_get_xinerama_neighbor.
This commit is contained in:
Rob Adams 2003-04-06 05:57:36 +00:00 committed by Rob Adams
parent 40ec58787f
commit 9bd17f4fae
4 changed files with 276 additions and 164 deletions

View File

@ -1,3 +1,25 @@
2003-04-05 Rob Adams <robadams@ucla.edu>
Update placement policy for screen with multiple xineramas.
Windows will be placed preferentially on the xinerama with the
pointer, and progressively further away as needed to find a place
where the window does not overlap other windows.
* src/place.c (rect_fits_in_work_area): function
fit_rect_in_xinerama greatly simplified to work with new placement
policy.
(find_first_fit): implement new first first placement scheme
* src/screen.c (meta_screen_get_xinerama_neighbor): look for an
xinerama in the xinerama list that is adjacent to the specified
xinerama.
(meta_screen_get_natural_xinerama_list): return a list of
xineramas in the order to be preferred by the placement algorithm
as determined by the current location of the pointer.
* src/screen.h: add function prototypes and an enum used by
meta_screen_get_xinerama_neighbor.
2003-04-05 Rob Adams <robadams@ucla.edu>
* src/place.c (center_tile_rect_in_area): Fix a minor off-by-one

View File

@ -384,66 +384,13 @@ center_tile_rect_in_area (MetaRectangle *rect,
}
static gboolean
fit_rect_in_xinerama (MetaWindow *window,
rect_fits_in_work_area (MetaRectangle *work_area,
MetaRectangle *rect)
{
int i;
int best_index;
int best_overlap;
MetaScreen *screen;
MetaRectangle work_area;
const MetaXineramaScreenInfo *xsi;
screen = window->screen;
/* Find xinerama with best fit, then
* shift rect to be entirely within it.
*/
best_overlap = -1;
best_index = -1;
i = 0;
while (i < screen->n_xinerama_infos)
{
MetaRectangle xinerama_rect;
MetaRectangle intersect;
int overlap;
xsi = &screen->xinerama_infos[i];
xinerama_rect.x = xsi->x_origin;
xinerama_rect.y = xsi->y_origin;
xinerama_rect.width = xsi->width;
xinerama_rect.height = xsi->height;
if (meta_rectangle_intersect (rect, &xinerama_rect, &intersect))
overlap = intersect.width * intersect.height;
else
overlap = 0;
if (overlap > best_overlap)
{
best_index = i;
best_overlap = overlap;
}
++i;
}
/* some overlap had to be better than -1 */
g_assert (best_index >= 0);
meta_window_get_work_area_for_xinerama (window, best_index, &work_area);
if ((rect->x < work_area.x) || (rect->y < work_area.y) ||
(rect->x >= work_area.x + work_area.width) ||
(rect->y >= work_area.y + work_area.height))
center_tile_rect_in_area (rect, &work_area);
/* Now return whether we are entirely within the work area */
return
((rect->x + rect->width) < (work_area.x + work_area.width)) &&
((rect->y + rect->height) < (work_area.y + work_area.height));
return ((rect->x >= work_area->x) &&
(rect->y >= work_area->y) &&
(rect->x + rect->width <= work_area->x + work_area->width) &&
(rect->y + rect->height <= work_area->y + work_area->height));
}
/* Find the leftmost, then topmost, empty area on the workspace
@ -472,14 +419,26 @@ find_first_fit (MetaWindow *window,
* existing window in each of those cases.
*/
int retval;
GList *sorted;
GList *below_sorted;
GList *right_sorted;
GList *tmp;
MetaRectangle rect;
MetaRectangle work_area;
int i;
int* xineramas_list;
int n_xineramas;
retval = FALSE;
sorted = NULL;
/* Below each window */
below_sorted = g_list_copy (windows);
below_sorted = g_list_sort (below_sorted, leftmost_cmp);
below_sorted = g_list_sort (below_sorted, topmost_cmp);
/* To the right of each 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;
@ -490,16 +449,29 @@ find_first_fit (MetaWindow *window,
rect.height += fgeom->top_height + fgeom->bottom_height;
}
/* Try center-tiling on first xinerama */
/* FIXME should use xinerama with mouse pointer
* (or better, xinerama where window was launched
* determined via startup notification)
*/
meta_window_get_work_area_for_xinerama (window, 0, &work_area);
meta_screen_get_natural_xinerama_list (window->screen,
&xineramas_list,
&n_xineramas);
for (i = 0; i < n_xineramas; i++)
{
meta_topic (META_DEBUG_XINERAMA,
"Natural xinerama %d is %d,%d %dx%d\n",
i,
window->screen->xinerama_infos[xineramas_list[i]].x_origin,
window->screen->xinerama_infos[xineramas_list[i]].y_origin,
window->screen->xinerama_infos[xineramas_list[i]].width,
window->screen->xinerama_infos[xineramas_list[i]].height);
}
/* try each xinerama in the natural ordering in turn */
i = 0;
while (i < n_xineramas)
{
meta_window_get_work_area_for_xinerama (window, xineramas_list[i], &work_area);
center_tile_rect_in_area (&rect, &work_area);
if (fit_rect_in_xinerama (window, &rect) &&
if (rect_fits_in_work_area (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, windows))
{
*new_x = rect.x;
@ -515,13 +487,8 @@ find_first_fit (MetaWindow *window,
goto out;
}
sorted = g_list_copy (windows);
/* Below each window */
sorted = g_list_sort (sorted, leftmost_cmp);
sorted = g_list_sort (sorted, topmost_cmp);
tmp = sorted;
/* try below each window */
tmp = below_sorted;
while (tmp != NULL)
{
MetaWindow *w = tmp->data;
@ -532,8 +499,8 @@ find_first_fit (MetaWindow *window,
rect.x = outer_rect.x;
rect.y = outer_rect.y + outer_rect.height;
if (fit_rect_in_xinerama (window, &rect) &&
!rectangle_overlaps_some_window (&rect, sorted))
if (rect_fits_in_work_area (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, below_sorted))
{
*new_x = rect.x;
*new_y = rect.y;
@ -551,11 +518,8 @@ find_first_fit (MetaWindow *window,
tmp = tmp->next;
}
/* To the right of each window */
sorted = g_list_sort (sorted, topmost_cmp);
sorted = g_list_sort (sorted, leftmost_cmp);
tmp = sorted;
/* try to the right of each window */
tmp = right_sorted;
while (tmp != NULL)
{
MetaWindow *w = tmp->data;
@ -566,8 +530,8 @@ find_first_fit (MetaWindow *window,
rect.x = outer_rect.x + outer_rect.width;
rect.y = outer_rect.y;
if (fit_rect_in_xinerama (window, &rect) &&
!rectangle_overlaps_some_window (&rect, sorted))
if (rect_fits_in_work_area (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, right_sorted))
{
*new_x = rect.x;
*new_y = rect.y;
@ -585,35 +549,14 @@ find_first_fit (MetaWindow *window,
tmp = tmp->next;
}
/* Try center-tile on each Xinerama screen which isn't the first */
i = 1;
while (i < window->screen->n_xinerama_infos)
{
meta_window_get_work_area_for_xinerama (window, i, &work_area);
center_tile_rect_in_area (&rect, &work_area);
if (fit_rect_in_xinerama (window, &rect) &&
!rectangle_overlaps_some_window (&rect, windows))
{
*new_x = rect.x;
*new_y = rect.y;
if (fgeom)
{
*new_x += fgeom->left_width;
*new_y += fgeom->top_height;
}
retval = TRUE;
goto out;
}
++i;
}
out:
g_list_free (sorted);
g_free (xineramas_list);
g_list_free (below_sorted);
g_list_free (right_sorted);
return retval;
}
@ -975,7 +918,9 @@ get_vertical_edges (MetaWindow *window,
windows = get_windows_on_same_workspace (window, &n_windows);
i = 0;
n_edges = n_windows * 2 + 4 + window->screen->n_xinerama_infos - 1; /* 4 = workspace/screen edges */
/* 4 = workspace/screen edges */
n_edges = n_windows * 2 + 4 + window->screen->n_xinerama_infos - 1;
edges = g_new (int, n_edges);
/* workspace/screen edges */
@ -1236,7 +1181,6 @@ meta_window_find_next_horizontal_edge (MetaWindow *window,
return retval;
}
int
meta_window_find_nearest_vertical_edge (MetaWindow *window,
int x_pos)

View File

@ -1346,6 +1346,138 @@ meta_screen_get_xinerama_for_window (MetaScreen *screen,
return meta_screen_get_xinerama_for_rect (screen, &window_rect);
}
const MetaXineramaScreenInfo*
meta_screen_get_xinerama_neighbor (MetaScreen *screen,
int which_xinerama,
MetaScreenDirection direction)
{
MetaXineramaScreenInfo* input = screen->xinerama_infos + which_xinerama;
MetaXineramaScreenInfo* current;
int i;
for (i = 0; i < screen->n_xinerama_infos; i++)
{
current = screen->xinerama_infos + i;
if (((direction == META_SCREEN_RIGHT) &&
(current->x_origin == input->x_origin + input->width) &&
(current->y_origin >= input->y_origin) &&
(current->y_origin <= input->y_origin+input->height)) ||
((direction == META_SCREEN_LEFT) &&
(input->x_origin == current->x_origin + current->width) &&
(current->y_origin >= input->y_origin) &&
(current->y_origin <= input->y_origin + input->height)) ||
((direction == META_SCREEN_UP) &&
(input->y_origin == current->y_origin + current->height) &&
(current->x_origin >= input->x_origin) &&
(current->x_origin <= input->x_origin + input->width)) ||
((direction == META_SCREEN_DOWN) &&
(current->y_origin == input->y_origin + input->height) &&
(current->x_origin >= input->x_origin) &&
(current->x_origin <= input->x_origin + input->width)))
{
return current;
}
}
return NULL;
}
void
meta_screen_get_natural_xinerama_list (MetaScreen *screen,
int** xineramas_list,
int* n_xineramas)
{
const MetaXineramaScreenInfo* current;
const MetaXineramaScreenInfo* tmp;
GQueue* xinerama_queue;
int* visited;
int cur = 0;
int i;
*n_xineramas = screen->n_xinerama_infos;
*xineramas_list = g_new (int, screen->n_xinerama_infos);
/* we calculate a natural ordering by which to choose xineramas for
* window placement. We start at the current xinerama, and perform
* a breadth-first search of the xineramas starting from that
* xinerama. We choose preferentially left, then right, then down,
* then up. The visitation order produced by this traversal is the
* natural xinerama ordering.
*/
visited = g_new (int, screen->n_xinerama_infos);
for (i = 0; i < screen->n_xinerama_infos; i++)
{
visited[i] = FALSE;
}
current = meta_screen_get_current_xinerama (screen);
xinerama_queue = g_queue_new ();
g_queue_push_tail (xinerama_queue, (gpointer) current);
visited[current->number] = TRUE;
while (!g_queue_is_empty (xinerama_queue))
{
current = (const MetaXineramaScreenInfo*)
g_queue_pop_head (xinerama_queue);
(*xineramas_list)[cur++] = current->number;
/* enqueue each of the directions */
tmp = meta_screen_get_xinerama_neighbor (screen,
current->number,
META_SCREEN_LEFT);
if (tmp && !visited[tmp->number])
{
g_queue_push_tail (xinerama_queue,
(MetaXineramaScreenInfo*) tmp);
visited[tmp->number] = TRUE;
}
tmp = meta_screen_get_xinerama_neighbor (screen,
current->number,
META_SCREEN_RIGHT);
if (tmp && !visited[tmp->number])
{
g_queue_push_tail (xinerama_queue,
(MetaXineramaScreenInfo*) tmp);
visited[tmp->number] = TRUE;
}
tmp = meta_screen_get_xinerama_neighbor (screen,
current->number,
META_SCREEN_UP);
if (tmp && !visited[tmp->number])
{
g_queue_push_tail (xinerama_queue,
(MetaXineramaScreenInfo*) tmp);
visited[tmp->number] = TRUE;
}
tmp = meta_screen_get_xinerama_neighbor (screen,
current->number,
META_SCREEN_DOWN);
if (tmp && !visited[tmp->number])
{
g_queue_push_tail (xinerama_queue,
(MetaXineramaScreenInfo*) tmp);
visited[tmp->number] = TRUE;
}
}
/* in case we somehow missed some set of xineramas, go through the
* visited list and add in any xineramas that were missed
*/
for (i = 0; i < screen->n_xinerama_infos; i++)
{
if (visited[i] == FALSE)
{
(*xineramas_list)[cur++] = i;
}
}
g_free (visited);
g_queue_free (xinerama_queue);
}
gboolean
meta_screen_window_intersects_xinerama (MetaScreen *screen,
MetaWindow *window,

View File

@ -48,6 +48,14 @@ typedef enum
META_SCREEN_BOTTOMRIGHT
} MetaScreenCorner;
typedef enum
{
META_SCREEN_UP,
META_SCREEN_DOWN,
META_SCREEN_LEFT,
META_SCREEN_RIGHT
} MetaScreenDirection;
struct _MetaScreen
{
MetaDisplay *display;
@ -140,11 +148,17 @@ const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_rect (MetaScreen
const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_window (MetaScreen *screen,
MetaWindow *window);
gboolean meta_screen_window_intersects_xinerama (MetaScreen *screen,
MetaWindow *window,
int which_xinerama);
const MetaXineramaScreenInfo* meta_screen_get_xinerama_neighbor (MetaScreen *screen,
int which_xinerama,
MetaScreenDirection dir);
void meta_screen_get_natural_xinerama_list (MetaScreen *screen,
int** xineramas_list,
int* n_xineramas);
void meta_screen_update_workspace_layout (MetaScreen *screen);
void meta_screen_update_workspace_names (MetaScreen *screen);
void meta_screen_queue_workarea_recalc (MetaScreen *screen);