diff --git a/src/Makefile.am b/src/Makefile.am index 2fe9129f1..d86915a0e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,8 @@ metacity_SOURCES= \ main.h \ menu.c \ menu.h \ + place.c \ + place.h \ screen.c \ screen.h \ session.c \ diff --git a/src/keybindings.c b/src/keybindings.c index 09c6109b8..bdaa563d9 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -84,6 +84,10 @@ static MetaKeyBinding screen_bindings[] = { { XK_4, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (3), 0 }, { XK_5, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (4), 0 }, { XK_6, Mod1Mask, KeyPress, handle_activate_workspace, GINT_TO_POINTER (5), 0 }, + { XK_Tab, Mod1Mask, KeyPress, handle_tab_forward, NULL, 0 }, + { XK_ISO_Left_Tab, ShiftMask | Mod1Mask, KeyPress, handle_tab_backward, NULL, 0 }, + { XK_Tab, ShiftMask | Mod1Mask, KeyPress, handle_tab_backward, NULL, 0 }, + { XK_Escape, Mod1Mask, KeyPress, handle_focus_previous, NULL, 0 }, { None, 0, 0, NULL, NULL, 0 } }; diff --git a/src/place.c b/src/place.c new file mode 100644 index 000000000..6f623708e --- /dev/null +++ b/src/place.c @@ -0,0 +1,95 @@ +/* Metacity window placement */ + +/* + * Copyright (C) 2001 Havoc Pennington + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "place.h" + +void +meta_window_place (MetaWindow *window, + MetaFrameGeometry *fgeom, + int x, + int y, + int *new_x, + int *new_y) +{ + /* frame member variables should NEVER be used in here, only + * MetaFrameGeometry + */ + + meta_verbose ("Placing window %s\n", window->desc); + + if (window->xtransient_for != None) + { + /* Center horizontally, at top of parent vertically */ + + MetaWindow *parent; + + parent = + meta_display_lookup_x_window (window->display, + window->xtransient_for); + + if (parent) + { + int w; + + meta_window_get_position (parent, &x, &y); + w = parent->rect.width; + + /* center of parent */ + x = x + w / 2; + /* center of child over center of parent */ + x -= window->rect.width / 2; + + y += fgeom->top_height; + + meta_verbose ("Centered window %s over transient parent\n", + window->desc); + + goto done; + } + } + + if (window->type == META_WINDOW_DIALOG || + window->type == META_WINDOW_MODAL_DIALOG) + { + /* Center on screen */ + int w, h; + + /* I think whole screen will look nicer than workarea */ + w = WidthOfScreen (window->screen->xscreen); + h = HeightOfScreen (window->screen->xscreen); + + x = (w - window->rect.width) / 2; + y = (y - window->rect.height) / 2; + + meta_verbose ("Centered window %s on screen\n", + window->desc); + + goto done; + } + + /* "Origin" placement algorithm */ + x = 0; + y = 0; + + done: + *new_x = x; + *new_y = y; +} diff --git a/src/place.h b/src/place.h new file mode 100644 index 000000000..6b79ecdc9 --- /dev/null +++ b/src/place.h @@ -0,0 +1,39 @@ +/* Metacity window placement */ + +/* + * Copyright (C) 2001 Havoc Pennington + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_PLACE_H +#define META_PLACE_H + +#include "window.h" +#include "frame.h" + +void meta_window_place (MetaWindow *window, + MetaFrameGeometry *fgeom, + int x, + int y, + int *new_x, + int *new_y); + +#endif + + + + diff --git a/src/stack.c b/src/stack.c index 632e7fcc5..482a1e8a2 100644 --- a/src/stack.c +++ b/src/stack.c @@ -23,6 +23,7 @@ #include "window.h" #include "errors.h" #include "frame.h" +#include "workspace.h" #include @@ -761,6 +762,101 @@ meta_stack_get_below (MetaStack *stack, return find_prev_below_layer (stack, window->layer); } +#define IN_TAB_CHAIN(w) ((w)->layer != META_LAYER_DOCK && (w)->layer != META_LAYER_DESKTOP) +#define GET_XWINDOW(stack, i) (g_array_index ((stack)->windows, \ + Window, (i))) + +static MetaWindow* +find_tab_forward (MetaStack *stack, + MetaWorkspace *workspace, + int start) +{ + int i; + + /* start may be -1 to find any tab window at all */ + + i = start + 1; + while (i < stack->windows->len) + { + MetaWindow *window; + + window = meta_display_lookup_x_window (stack->screen->display, + GET_XWINDOW (stack, i)); + + if (window && IN_TAB_CHAIN (window) && + (workspace == NULL || + meta_workspace_contains_window (workspace, window))) + return window; + + ++i; + } + + i = 0; + while (i < start) + { + MetaWindow *window; + + window = meta_display_lookup_x_window (stack->screen->display, + GET_XWINDOW (stack, i)); + + if (window && IN_TAB_CHAIN (window) && + (workspace == NULL || + meta_workspace_contains_window (workspace, window))) + return window; + + ++i; + } + + /* no window other than the start window is in the tab chain */ + return NULL; +} + +static MetaWindow* +find_tab_backward (MetaStack *stack, + MetaWorkspace *workspace, + int start) +{ + int i; + + /* start may be stack->windows->len to find any tab window at all */ + + i = start - 1; + while (i >= 0) + { + MetaWindow *window; + + window = meta_display_lookup_x_window (stack->screen->display, + GET_XWINDOW (stack, i)); + + if (window && IN_TAB_CHAIN (window) && + (workspace == NULL || + meta_workspace_contains_window (workspace, window))) + return window; + + --i; + } + + i = stack->windows->len - 1; + while (i > start) + { + MetaWindow *window; + + window = meta_display_lookup_x_window (stack->screen->display, + GET_XWINDOW (stack, i)); + + if (window && IN_TAB_CHAIN (window) && + (workspace == NULL || + meta_workspace_contains_window (workspace, window))) + return window; + + --i; + } + + /* no window other than the start window is in the tab chain */ + return NULL; +} + +/* This ignores the dock/desktop layers */ MetaWindow* meta_stack_get_tab_next (MetaStack *stack, MetaWindow *window, @@ -782,41 +878,27 @@ meta_stack_get_tab_next (MetaStack *stack, if (w == window->xwindow) { - if (backward && i == 0) - goto out; - else if (!backward && i == (stack->windows->len - 1)) - goto out; - else - { - if (backward) - --i; - else - ++i; + MetaWorkspace *workspace; + + workspace = window->screen->active_workspace; - return meta_display_lookup_x_window (stack->screen->display, - g_array_index (stack->windows, - Window, - i)); - } + if (backward) + return find_tab_backward (stack, workspace, i); + else + return find_tab_forward (stack, workspace, i); } ++i; - } + } } - - out: /* window may be NULL, or maybe the origin window was already the last/first * window and we need to wrap around */ if (backward) - return meta_display_lookup_x_window (stack->screen->display, - g_array_index (stack->windows, - Window, - stack->windows->len - 1)); + return find_tab_backward (stack, NULL, + stack->windows->len); else - return meta_display_lookup_x_window (stack->screen->display, - g_array_index (stack->windows, - Window, - 0)); + return find_tab_forward (stack, NULL, -1); } + diff --git a/src/window.c b/src/window.c index b243f0bd3..8d706305c 100644 --- a/src/window.c +++ b/src/window.c @@ -27,6 +27,7 @@ #include "stack.h" #include "keybindings.h" #include "ui.h" +#include "place.h" #include @@ -97,6 +98,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, XWindowAttributes attrs; GSList *tmp; MetaWorkspace *space; + gulong existing_wm_state; meta_verbose ("Attempting to manage 0x%lx\n", xwindow); @@ -123,6 +125,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, return NULL; } + existing_wm_state = WithdrawnState; if (must_be_viewable && attrs.map_state != IsViewable) { /* Only manage if WM_STATE is IconicState or NormalState */ @@ -138,7 +141,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, return NULL; } - /* FIXME should honor WM_STATE probably */ + existing_wm_state = state; } meta_error_trap_push (display); @@ -237,6 +240,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, window->keys_grabbed = FALSE; window->grab_on_frame = FALSE; window->withdrawn = FALSE; + window->initial_workspace_set = FALSE; window->unmaps_pending = 0; @@ -277,8 +281,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, window->layer = META_LAYER_NORMAL; window->stack_op = NULL; - window->initial_workspace = - meta_workspace_screen_index (window->screen->active_workspace); + window->initial_workspace = 0; /* not used */ meta_display_register_x_window (display, &window->xwindow, window); update_size_hints (window); @@ -302,6 +305,19 @@ meta_window_new (MetaDisplay *display, Window xwindow, window->minimized = TRUE; meta_verbose ("Window %s asked to start out minimized\n", window->desc); } + + if (existing_wm_state == IconicState) + { + /* WM_STATE said minimized */ + window->minimized = TRUE; + meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n", + window->desc); + + /* Assume window was previously placed, though perhaps it's + * been iconic its whole life, we have no way of knowing. + */ + window->placed = TRUE; + } /* FIXME we have a tendency to set this then immediately * change it again. @@ -314,14 +330,54 @@ meta_window_new (MetaDisplay *display, Window xwindow, meta_window_grab_keys (window); - space = - meta_display_get_workspace_by_screen_index (window->display, - window->screen, - window->initial_workspace); - if (space == NULL) - space = window->screen->active_workspace; + /* For the workspace, first honor hints, + * if that fails put transients with parents, + * otherwise put window on active space + */ - meta_workspace_add_window (space, window); + if (window->initial_workspace_set) + { + space = + meta_display_get_workspace_by_screen_index (window->display, + window->screen, + window->initial_workspace); + + if (space) + meta_workspace_add_window (space, window); + } + + if (window->workspaces == NULL && + window->xtransient_for != None) + { + /* Try putting dialog on parent's workspace */ + MetaWindow *parent; + + parent = meta_display_lookup_x_window (window->display, + window->xtransient_for); + + if (parent) + { + GList *tmp; + + if (parent->on_all_workspaces) + window->on_all_workspaces = TRUE; + + tmp = parent->workspaces; + while (tmp != NULL) + { + meta_workspace_add_window (tmp->data, window); + + tmp = tmp->next; + } + } + } + + if (window->workspaces == NULL) + { + space = window->screen->active_workspace; + + meta_workspace_add_window (space, window); + } /* Only accept USPosition on normal windows because the app is full * of shit claiming the user set -geometry for a dialog or dock @@ -334,16 +390,23 @@ meta_window_new (MetaDisplay *display, Window xwindow, meta_verbose ("Honoring USPosition for %s instead of using placement algorithm\n", window->desc); } - if (window->type != META_WINDOW_NORMAL) + /* Assume the app knows best how to place these. */ + if (window->type == META_WINDOW_DESKTOP || + window->type == META_WINDOW_DOCK || + window->type == META_WINDOW_TOOLBAR || + window->type == META_WINDOW_MENU) { window->placed = TRUE; - meta_verbose ("Not placing non-normal-type window\n"); + meta_verbose ("Not placing non-normal non-dialog window\n"); } if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK) { - /* Change the default */ + /* Change the default, but don't enforce this if + * the user focuses the dock/desktop and unsticks it + * using key shortcuts + */ window->on_all_workspaces = TRUE; } @@ -625,7 +688,10 @@ meta_window_show (MetaWindow *window) meta_verbose ("Showing window %s, shaded: %d iconic: %d\n", window->desc, window->shaded, window->iconic); - /* don't ever do the initial position constraint thing again */ + /* 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 */ @@ -2708,6 +2774,8 @@ update_initial_workspace (MetaWindow *window) { gulong val = 0; + window->initial_workspace_set = FALSE; + /* Fall back to old WM spec hint if net_wm_desktop is missing, this * is just to be nice when restarting from old Sawfish basically, * should nuke it eventually @@ -2716,12 +2784,18 @@ update_initial_workspace (MetaWindow *window) window->xwindow, window->display->atom_net_wm_desktop, &val)) - window->initial_workspace = val; + { + window->initial_workspace_set = TRUE; + window->initial_workspace = val; + } else if (get_cardinal (window->display, window->xwindow, window->display->atom_win_workspace, &val)) - window->initial_workspace = val; + { + window->initial_workspace_set = TRUE; + window->initial_workspace = val; + } return Success; } @@ -3048,64 +3122,7 @@ constrain_position (MetaWindow *window, */ if (!window->placed) - { - gboolean found_transient = FALSE; - - meta_verbose ("Placing window %s\n", window->desc); - - /* "Origin" placement algorithm */ - x = 0; - y = 0; - - if (window->xtransient_for != None) - { - /* Center horizontally, at top of parent vertically */ - - MetaWindow *parent; - - parent = - meta_display_lookup_x_window (window->display, - window->xtransient_for); - - if (parent) - { - int w; - - meta_window_get_position (parent, &x, &y); - w = parent->rect.width; - - /* center of parent */ - x = x + w / 2; - /* center of child over center of parent */ - x -= window->rect.width / 2; - - y += fgeom->top_height; - - found_transient = TRUE; - - meta_verbose ("Centered window %s over transient parent\n", - window->desc); - } - } - - if (!found_transient && - (window->type == META_WINDOW_DIALOG || - window->type == META_WINDOW_MODAL_DIALOG)) - { - /* Center on screen */ - int w, h; - - /* I think whole screen will look nicer than workarea */ - w = WidthOfScreen (window->screen->xscreen); - h = HeightOfScreen (window->screen->xscreen); - - x = (w - window->rect.width) / 2; - y = (y - window->rect.height) / 2; - - meta_verbose ("Centered window %s on screen\n", - window->desc); - } - } + meta_window_place (window, fgeom, x, y, &x, &y); if (window->type != META_WINDOW_DESKTOP && window->type != META_WINDOW_DOCK) diff --git a/src/window.h b/src/window.h index 41df9fe29..42ce95334 100644 --- a/src/window.h +++ b/src/window.h @@ -104,6 +104,9 @@ struct _MetaWindow * the window. It's taken to mean initially minimized. */ guint initially_iconic : 1; + + /* whether an initial workspace was explicitly set */ + guint initial_workspace_set : 1; /* These are the two flags from WM_PROTOCOLS */ guint take_focus : 1;