summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrhp <rhp>2001-06-24 08:09:10 +0000
committerrhp <rhp>2001-06-24 08:09:10 +0000
commitbca589280cd2acf9ee6ca3863e071b4d7c8496f4 (patch)
tree6b5a6fc2c438f0808e45ede86c126f104a3f71b8
parent8025ecea02c9723e3a72795e4d5913ccc1e74785 (diff)
downloadmetacity-bca589280cd2acf9ee6ca3863e071b4d7c8496f4.tar.gz
...
-rw-r--r--src/display.c60
-rw-r--r--src/display.h2
-rw-r--r--src/place.c209
-rw-r--r--src/stack.c37
-rw-r--r--src/stack.h4
-rw-r--r--src/window.c90
-rw-r--r--src/window.h8
7 files changed, 388 insertions, 22 deletions
diff --git a/src/display.c b/src/display.c
index d422faf1..235e64b5 100644
--- a/src/display.c
+++ b/src/display.c
@@ -328,30 +328,74 @@ ptrcmp (gconstpointer a, gconstpointer b)
return 0;
}
-void
-meta_display_close (MetaDisplay *display)
+GSList*
+meta_display_list_windows (MetaDisplay *display)
{
GSList *winlist;
GSList *tmp;
+ GSList *prev;
- if (display->error_traps)
- meta_bug ("Display closed with error traps pending\n");
-
winlist = NULL;
g_hash_table_foreach (display->window_ids,
listify_func,
&winlist);
+ /* Uniquify the list, since both frame windows and plain
+ * windows are in the hash
+ */
winlist = g_slist_sort (winlist, ptrcmp);
+ prev = NULL;
+ tmp = winlist;
+ while (tmp != NULL)
+ {
+ GSList *next;
+
+ next = tmp->next;
+
+ if (next &&
+ next->data == tmp->data)
+ {
+ /* Delete tmp from list */
+
+ if (prev)
+ prev->next = next;
+
+ if (tmp == winlist)
+ winlist = next;
+
+ g_slist_free_1 (tmp);
+
+ /* leave prev unchanged */
+ }
+ else
+ {
+ prev = tmp;
+ }
+
+ tmp = next;
+ }
+
+ return winlist;
+}
+
+void
+meta_display_close (MetaDisplay *display)
+{
+ GSList *winlist;
+ GSList *tmp;
+
+ if (display->error_traps)
+ meta_bug ("Display closed with error traps pending\n");
+
+ winlist = meta_display_list_windows (display);
+
/* Unmanage all windows */
meta_display_grab (display);
tmp = winlist;
while (tmp != NULL)
{
- if (tmp->next == NULL ||
- (tmp->next && tmp->next->data != tmp->data))
- meta_window_free (tmp->data);
+ meta_window_free (tmp->data);
tmp = tmp->next;
}
diff --git a/src/display.h b/src/display.h
index 27c3b6fe..5fa16899 100644
--- a/src/display.h
+++ b/src/display.h
@@ -141,7 +141,7 @@ void meta_display_register_x_window (MetaDisplay *display,
void meta_display_unregister_x_window (MetaDisplay *display,
Window xwindow);
-
+GSList* meta_display_list_windows (MetaDisplay *display);
MetaDisplay* meta_display_for_x_display (Display *xdisplay);
GSList* meta_displays_list (void);
diff --git a/src/place.c b/src/place.c
index 6f623708..6dce2693 100644
--- a/src/place.c
+++ b/src/place.c
@@ -20,6 +20,177 @@
*/
#include "place.h"
+#include "workspace.h"
+#include <gdk/gdkregion.h>
+#include <math.h>
+
+static gint
+northwestcmp (gconstpointer a, gconstpointer b)
+{
+ MetaWindow *aw = (gpointer) a;
+ MetaWindow *bw = (gpointer) b;
+ 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;
+ }
+
+ /* probably there's a fast good-enough-guess we could use here. */
+ from_origin_a = sqrt (ax * ax + ay * ay);
+ from_origin_b = sqrt (bx * bx + by * by);
+
+ if (from_origin_a < from_origin_b)
+ return -1;
+ else if (from_origin_a > from_origin_b)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+find_next_cascade (MetaWindow *window,
+ MetaFrameGeometry *fgeom,
+ /* visible windows on relevant workspaces */
+ GList *windows,
+ int x,
+ int y,
+ int *new_x,
+ int *new_y)
+{
+ GList *tmp;
+ GList *sorted;
+ int cascade_x, cascade_y;
+ int x_threshold, y_threshold;
+
+ sorted = g_list_copy (windows);
+ sorted = g_list_sort (sorted, northwestcmp);
+
+ /* This is a "fuzzy" cascade algorithm.
+ * For each window in the list, we find where we'd cascade a
+ * new window after it. If a window is already nearly at that
+ * position, we move on.
+ */
+
+ /* Find furthest-SE origin of all workspaces.
+ * cascade_x, cascade_y are the target position
+ * of NW corner of window frame.
+ */
+ cascade_x = 0;
+ cascade_y = 0;
+ tmp = window->workspaces;
+ while (tmp != NULL)
+ {
+ MetaWorkspace *space = tmp->data;
+
+ cascade_x = MAX (cascade_x, space->workarea.x);
+ cascade_y = MAX (cascade_y, space->workarea.y);
+
+ tmp = tmp->next;
+ }
+
+ /* Find first cascade position that's not used. */
+
+ /* arbitrary-ish threshold, honors user attempts to
+ * manually cascade.
+ */
+ x_threshold = MAX (fgeom->left_width, 10);
+ y_threshold = MAX (fgeom->top_height, 10);
+
+ tmp = sorted;
+ while (tmp != NULL)
+ {
+ MetaWindow *w;
+ 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;
+ }
+
+ if (ABS (wx - cascade_x) < x_threshold &&
+ ABS (wy - cascade_y) < y_threshold)
+ {
+ /* This window is "in the way", move to next cascade
+ * point. The new window frame should go at the origin
+ * of the client window we're stacking above.
+ */
+ meta_window_get_position (w, &wx, &wy);
+ cascade_x = wx;
+ cascade_y = wy;
+ }
+ else
+ goto found; /* no window at this cascade point. */
+
+ tmp = tmp->next;
+ }
+
+ /* cascade_x and cascade_y will match the last window in the list. */
+
+ found:
+ g_list_free (sorted);
+
+ /* Convert coords to position of window, not position of frame. */
+ if (fgeom == NULL)
+ {
+ *new_x = cascade_x;
+ *new_y = cascade_y;
+ }
+ else
+ {
+ *new_x = cascade_x + fgeom->left_width;
+ *new_y = cascade_y + fgeom->top_height;
+ }
+}
+
+/* Find the leftmost, then topmost, empty area on the workspace
+ * that can contain the new window.
+ */
+static gboolean
+find_first_fit (MetaWindow *window,
+ MetaFrameGeometry *fgeom,
+ /* visible windows on relevant workspaces */
+ GList *windows,
+ int x,
+ int y,
+ int *new_x,
+ int *new_y)
+{
+
+
+
+}
void
meta_window_place (MetaWindow *window,
@@ -29,8 +200,11 @@ meta_window_place (MetaWindow *window,
int *new_x,
int *new_y)
{
+ GList *windows;
+
/* frame member variables should NEVER be used in here, only
- * MetaFrameGeometry
+ * MetaFrameGeometry. But remember fgeom == NULL
+ * for undecorated windows.
*/
meta_verbose ("Placing window %s\n", window->desc);
@@ -57,7 +231,8 @@ meta_window_place (MetaWindow *window,
/* center of child over center of parent */
x -= window->rect.width / 2;
- y += fgeom->top_height;
+ if (fgeom)
+ y += fgeom->top_height;
meta_verbose ("Centered window %s over transient parent\n",
window->desc);
@@ -85,10 +260,40 @@ meta_window_place (MetaWindow *window,
goto done;
}
+ /* Find windows that matter (not minimized, on same workspace
+ * as placed window, may be shaded - if shaded we pretend it isn't
+ * for placement purposes)
+ */
+ windows = NULL;
+ {
+ GSList *all_windows;
+ GSList *tmp;
+
+ all_windows = meta_display_list_windows (window->display);
+
+ tmp = all_windows;
+ while (tmp != NULL)
+ {
+ MetaWindow *w = tmp->data;
+
+ if (!w->minimized &&
+ w != window &&
+ meta_window_shares_some_workspace (window, w))
+ windows = g_list_prepend (windows, w);
+
+ tmp = tmp->next;
+ }
+ }
+
/* "Origin" placement algorithm */
x = 0;
y = 0;
+ /* Cascade */
+ find_next_cascade (window, fgeom, windows, x, y, &x, &y);
+
+ g_list_free (windows);
+
done:
*new_x = x;
*new_y = y;
diff --git a/src/stack.c b/src/stack.c
index 482a1e8a..f1c6c8bc 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -902,3 +902,40 @@ meta_stack_get_tab_next (MetaStack *stack,
return find_tab_forward (stack, NULL, -1);
}
+int
+meta_stack_windows_cmp (MetaStack *stack,
+ MetaWindow *window_a,
+ MetaWindow *window_b)
+{
+ g_return_val_if_fail (window_a->screen == window_b->screen, 0);
+
+ /* -1 means a below b */
+
+ if (window_a->layer < window_b->layer)
+ return -1;
+ else if (window_a->layer > window_b->layer)
+ return 1;
+ else
+ {
+ GList *tmp;
+
+ g_assert (window_a->layer == window_b->layer);
+
+ tmp = stack->layers[window_a->layer];
+ while (tmp != NULL)
+ {
+ /* earlier in list is higher in stack */
+ if (tmp->data == window_a)
+ return 1;
+ else if (tmp->data == window_b)
+ return -1;
+
+ tmp = tmp->next;
+ }
+
+ meta_bug ("Didn't find windows in layer in meta_stack_windows_cmp()\n");
+ }
+
+ /* not reached */
+ return 0;
+}
diff --git a/src/stack.h b/src/stack.h
index 39cb973a..0ed30cb4 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -95,6 +95,10 @@ MetaWindow* meta_stack_get_below (MetaStack *stack,
MetaWindow* meta_stack_get_tab_next (MetaStack *stack,
MetaWindow *window,
gboolean backward);
+/* -1 if a < b, etc. */
+int meta_stack_windows_cmp (MetaStack *stack,
+ MetaWindow *window_a,
+ MetaWindow *window_b);
#endif
diff --git a/src/window.c b/src/window.c
index 8d706305..e84e9e11 100644
--- a/src/window.c
+++ b/src/window.c
@@ -82,6 +82,7 @@ static void meta_window_move_resize_internal (MetaWindow *window,
int w,
int h);
+void meta_window_move_resize_now (MetaWindow *window);
static gboolean get_cardinal (MetaDisplay *display,
Window xwindow,
@@ -235,12 +236,16 @@ meta_window_new (MetaDisplay *display, Window xwindow,
window->mapped = attrs.map_state != IsUnmapped;
/* if already mapped we don't want to do the placement thing */
window->placed = window->mapped;
+ if (window->placed)
+ meta_verbose ("Not placing window 0x%lx since it's already mapped\n",
+ xwindow);
window->unmanaging = FALSE;
window->calc_showing_queued = FALSE;
window->keys_grabbed = FALSE;
window->grab_on_frame = FALSE;
window->withdrawn = FALSE;
window->initial_workspace_set = FALSE;
+ window->calc_placement = FALSE;
window->unmaps_pending = 0;
@@ -617,11 +622,31 @@ meta_window_calc_showing (MetaWindow *window)
static guint calc_showing_idle = 0;
static GSList *calc_showing_pending = NULL;
+static int
+stackcmp (gconstpointer a, gconstpointer b)
+{
+ MetaWindow *aw = (gpointer) a;
+ MetaWindow *bw = (gpointer) b;
+
+ if (aw->screen != bw->screen)
+ return 0; /* don't care how they sort with respect to each other */
+ else
+ return meta_stack_windows_cmp (aw->screen->stack,
+ aw, bw);
+}
+
static gboolean
idle_calc_showing (gpointer data)
{
GSList *tmp;
+ /* sort them from bottom to top, so we map the
+ * bottom windows first, so that placement (e.g. cascading)
+ * works properly
+ */
+ calc_showing_pending = g_slist_sort (calc_showing_pending,
+ stackcmp);
+
tmp = calc_showing_pending;
while (tmp != NULL)
{
@@ -685,14 +710,29 @@ meta_window_queue_calc_showing (MetaWindow *window)
void
meta_window_show (MetaWindow *window)
{
- meta_verbose ("Showing window %s, shaded: %d iconic: %d\n",
- window->desc, window->shaded, window->iconic);
+ meta_verbose ("Showing window %s, shaded: %d iconic: %d placed: %d\n",
+ window->desc, window->shaded, window->iconic, window->placed);
- /* don't ever do the initial position constraint thing again.
- * This is toggled here so that initially-iconified windows
- * still get placed when they are ultimately shown.
- */
- window->placed = TRUE;
+ if (!window->placed)
+ {
+ /* We have to recalc the placement here since other windows may
+ * have been mapped/placed since we last did constrain_position
+ */
+
+ /* calc_placement is an efficiency hack to avoid
+ * multiple placement calculations before we finally
+ * show the window.
+ */
+ window->calc_placement = TRUE;
+ meta_window_move_resize_now (window);
+ window->calc_placement = FALSE;
+
+ /* don't ever do the initial position constraint thing again.
+ * This is toggled here so that initially-iconified windows
+ * still get placed when they are ultimately shown.
+ */
+ window->placed = TRUE;
+ }
/* Shaded means the frame is mapped but the window is not */
@@ -1235,9 +1275,8 @@ meta_window_move_resize (MetaWindow *window,
}
void
-meta_window_queue_move_resize (MetaWindow *window)
+meta_window_move_resize_now (MetaWindow *window)
{
- /* FIXME actually queue, don't do it immediately */
int x, y;
meta_window_get_position (window, &x, &y);
@@ -1247,6 +1286,13 @@ meta_window_queue_move_resize (MetaWindow *window)
}
void
+meta_window_queue_move_resize (MetaWindow *window)
+{
+ /* FIXME actually queue, don't do it immediately */
+ meta_window_move_resize_now (window);
+}
+
+void
meta_window_get_position (MetaWindow *window,
int *x,
int *y)
@@ -3116,12 +3162,12 @@ constrain_position (MetaWindow *window,
int y,
int *new_x,
int *new_y)
-{
+{
/* frame member variables should NEVER be used in here, only
* MetaFrameGeometry
*/
- if (!window->placed)
+ if (!window->placed && window->calc_placement)
meta_window_place (window, fgeom, x, y, &x, &y);
if (window->type != META_WINDOW_DESKTOP &&
@@ -3319,3 +3365,25 @@ meta_window_show_menu (MetaWindow *window,
meta_verbose ("Popping up window menu for %s\n", window->desc);
meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp);
}
+
+gboolean
+meta_window_shares_some_workspace (MetaWindow *window,
+ MetaWindow *with)
+{
+ GList *tmp;
+
+ if (window->on_all_workspaces ||
+ with->on_all_workspaces)
+ return TRUE;
+
+ tmp = window->workspaces;
+ while (tmp != NULL)
+ {
+ if (g_list_find (with->workspaces, tmp->data) != NULL)
+ return TRUE;
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
diff --git a/src/window.h b/src/window.h
index 42ce9533..693a662a 100644
--- a/src/window.h
+++ b/src/window.h
@@ -169,6 +169,11 @@ struct _MetaWindow
* it was withdrawn
*/
guint withdrawn : 1;
+
+ /* TRUE if constrain_position should calc placement.
+ * only relevant if !window->placed
+ */
+ guint calc_placement : 1;
/* Number of UnmapNotify that are caused by us, if
* we get UnmapNotify with none pending then the client
@@ -270,4 +275,7 @@ void meta_window_show_menu (MetaWindow *window,
int button,
Time timestamp);
+gboolean meta_window_shares_some_workspace (MetaWindow *window,
+ MetaWindow *with);
+
#endif