diff --git a/src/display.c b/src/display.c index d422faf16..235e64b5c 100644 --- a/src/display.c +++ b/src/display.c @@ -328,6 +328,57 @@ ptrcmp (gconstpointer a, gconstpointer b) return 0; } +GSList* +meta_display_list_windows (MetaDisplay *display) +{ + GSList *winlist; + GSList *tmp; + GSList *prev; + + 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) { @@ -337,21 +388,14 @@ meta_display_close (MetaDisplay *display) 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); - - winlist = g_slist_sort (winlist, ptrcmp); - + 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 27c3b6fe5..5fa16899e 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 6f623708e..6dce2693b 100644 --- a/src/place.c +++ b/src/place.c @@ -20,6 +20,177 @@ */ #include "place.h" +#include "workspace.h" +#include +#include + +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 482a1e8a2..f1c6c8bc1 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 39cb973a7..0ed30cb49 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 8d706305c..e84e9e119 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); @@ -1246,6 +1285,13 @@ meta_window_queue_move_resize (MetaWindow *window) window->rect.width, window->rect.height); } +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, @@ -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 42ce95334..693a662a8 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