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> 2003-04-05 Rob Adams <robadams@ucla.edu>
* src/place.c (center_tile_rect_in_area): Fix a minor off-by-one * 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 static gboolean
fit_rect_in_xinerama (MetaWindow *window, rect_fits_in_work_area (MetaRectangle *work_area,
MetaRectangle *rect) MetaRectangle *rect)
{ {
int i; return ((rect->x >= work_area->x) &&
int best_index; (rect->y >= work_area->y) &&
int best_overlap; (rect->x + rect->width <= work_area->x + work_area->width) &&
MetaScreen *screen; (rect->y + rect->height <= work_area->y + work_area->height));
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));
} }
/* Find the leftmost, then topmost, empty area on the workspace /* 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. * existing window in each of those cases.
*/ */
int retval; int retval;
GList *sorted; GList *below_sorted;
GList *right_sorted;
GList *tmp; GList *tmp;
MetaRectangle rect; MetaRectangle rect;
MetaRectangle work_area; MetaRectangle work_area;
int i; int i;
int* xineramas_list;
int n_xineramas;
retval = FALSE; 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.width = window->rect.width;
rect.height = window->rect.height; rect.height = window->rect.height;
@ -490,110 +449,29 @@ find_first_fit (MetaWindow *window,
rect.height += fgeom->top_height + fgeom->bottom_height; rect.height += fgeom->top_height + fgeom->bottom_height;
} }
/* Try center-tiling on first xinerama */ meta_screen_get_natural_xinerama_list (window->screen,
/* FIXME should use xinerama with mouse pointer &xineramas_list,
* (or better, xinerama where window was launched &n_xineramas);
* determined via startup notification) for (i = 0; i < n_xineramas; i++)
*/
meta_window_get_work_area_for_xinerama (window, 0, &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; meta_topic (META_DEBUG_XINERAMA,
*new_y = rect.y; "Natural xinerama %d is %d,%d %dx%d\n",
if (fgeom) i,
{ window->screen->xinerama_infos[xineramas_list[i]].x_origin,
*new_x += fgeom->left_width; window->screen->xinerama_infos[xineramas_list[i]].y_origin,
*new_y += fgeom->top_height; window->screen->xinerama_infos[xineramas_list[i]].width,
} window->screen->xinerama_infos[xineramas_list[i]].height);
retval = TRUE;
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;
while (tmp != NULL)
{
MetaWindow *w = tmp->data;
MetaRectangle outer_rect;
meta_window_get_outer_rect (w, &outer_rect);
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))
{
*new_x = rect.x;
*new_y = rect.y;
if (fgeom)
{
*new_x += fgeom->left_width;
*new_y += fgeom->top_height;
}
retval = TRUE;
goto out;
}
tmp = tmp->next;
} }
/* To the right of each window */ /* try each xinerama in the natural ordering in turn */
sorted = g_list_sort (sorted, topmost_cmp); i = 0;
sorted = g_list_sort (sorted, leftmost_cmp); while (i < n_xineramas)
tmp = sorted;
while (tmp != NULL)
{ {
MetaWindow *w = tmp->data; meta_window_get_work_area_for_xinerama (window, xineramas_list[i], &work_area);
MetaRectangle outer_rect;
meta_window_get_outer_rect (w, &outer_rect);
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))
{
*new_x = rect.x;
*new_y = rect.y;
if (fgeom)
{
*new_x += fgeom->left_width;
*new_y += fgeom->top_height;
}
retval = TRUE;
goto out;
}
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); 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)) !rectangle_overlaps_some_window (&rect, windows))
{ {
*new_x = rect.x; *new_x = rect.x;
@ -608,12 +486,77 @@ find_first_fit (MetaWindow *window,
goto out; goto out;
} }
/* try below each window */
tmp = below_sorted;
while (tmp != NULL)
{
MetaWindow *w = tmp->data;
MetaRectangle outer_rect;
meta_window_get_outer_rect (w, &outer_rect);
rect.x = outer_rect.x;
rect.y = outer_rect.y + outer_rect.height;
if (rect_fits_in_work_area (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, below_sorted))
{
*new_x = rect.x;
*new_y = rect.y;
if (fgeom)
{
*new_x += fgeom->left_width;
*new_y += fgeom->top_height;
}
retval = TRUE;
goto out;
}
tmp = tmp->next;
}
/* try to the right of each window */
tmp = right_sorted;
while (tmp != NULL)
{
MetaWindow *w = tmp->data;
MetaRectangle outer_rect;
meta_window_get_outer_rect (w, &outer_rect);
rect.x = outer_rect.x + outer_rect.width;
rect.y = outer_rect.y;
if (rect_fits_in_work_area (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, right_sorted))
{
*new_x = rect.x;
*new_y = rect.y;
if (fgeom)
{
*new_x += fgeom->left_width;
*new_y += fgeom->top_height;
}
retval = TRUE;
goto out;
}
tmp = tmp->next;
}
++i; ++i;
} }
out: out:
g_list_free (sorted);
g_free (xineramas_list);
g_list_free (below_sorted);
g_list_free (right_sorted);
return retval; return retval;
} }
@ -975,7 +918,9 @@ get_vertical_edges (MetaWindow *window,
windows = get_windows_on_same_workspace (window, &n_windows); windows = get_windows_on_same_workspace (window, &n_windows);
i = 0; 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); edges = g_new (int, n_edges);
/* workspace/screen edges */ /* workspace/screen edges */
@ -1236,7 +1181,6 @@ meta_window_find_next_horizontal_edge (MetaWindow *window,
return retval; return retval;
} }
int int
meta_window_find_nearest_vertical_edge (MetaWindow *window, meta_window_find_nearest_vertical_edge (MetaWindow *window,
int x_pos) 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); 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 gboolean
meta_screen_window_intersects_xinerama (MetaScreen *screen, meta_screen_window_intersects_xinerama (MetaScreen *screen,
MetaWindow *window, MetaWindow *window,

View File

@ -48,6 +48,14 @@ typedef enum
META_SCREEN_BOTTOMRIGHT META_SCREEN_BOTTOMRIGHT
} MetaScreenCorner; } MetaScreenCorner;
typedef enum
{
META_SCREEN_UP,
META_SCREEN_DOWN,
META_SCREEN_LEFT,
META_SCREEN_RIGHT
} MetaScreenDirection;
struct _MetaScreen struct _MetaScreen
{ {
MetaDisplay *display; MetaDisplay *display;
@ -140,10 +148,16 @@ const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_rect (MetaScreen
const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_window (MetaScreen *screen, const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_window (MetaScreen *screen,
MetaWindow *window); MetaWindow *window);
gboolean meta_screen_window_intersects_xinerama (MetaScreen *screen,
MetaWindow *window,
int which_xinerama);
gboolean meta_screen_window_intersects_xinerama (MetaScreen *screen, const MetaXineramaScreenInfo* meta_screen_get_xinerama_neighbor (MetaScreen *screen,
MetaWindow *window, int which_xinerama,
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_layout (MetaScreen *screen);
void meta_screen_update_workspace_names (MetaScreen *screen); void meta_screen_update_workspace_names (MetaScreen *screen);