diff --git a/ChangeLog b/ChangeLog index d25ed1886..3ac0bfb30 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2002-01-09 Havoc Pennington + + * src/window.c, src/window.h: store strut information, + update it on property changes, etc. etc. so we avoid panel + on maximize. + + * src/workspace.c (meta_workspace_get_work_area): add accessor for + work area so we can compute it lazily + + * src/display.h, src/display.c: add _NET_WM_STRUT atom + and _WIN_HINTS atom + 2002-01-08 Havoc Pennington * configure.in (ACLOCAL): add code to save ACLOCAL_FLAGS diff --git a/src/display.c b/src/display.c index 28a627e6d..9e77113c4 100644 --- a/src/display.c +++ b/src/display.c @@ -139,7 +139,9 @@ meta_display_open (const char *name) "_KWM_WIN_ICON", "_NET_WM_MOVERESIZE", "_NET_ACTIVE_WINDOW", - "_METACITY_RESTART_MESSAGE" + "_METACITY_RESTART_MESSAGE", + "_NET_WM_STRUT", + "_WIN_HINTS" }; Atom atoms[G_N_ELEMENTS(atom_names)]; @@ -230,6 +232,8 @@ meta_display_open (const char *name) display->atom_net_wm_moveresize = atoms[42]; display->atom_net_active_window = atoms[43]; display->atom_metacity_restart_message = atoms[44]; + display->atom_net_wm_strut = atoms[45]; + display->atom_win_hints = atoms[46]; /* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK, * created in screen_new diff --git a/src/display.h b/src/display.h index 51f4abada..ca7351bac 100644 --- a/src/display.h +++ b/src/display.h @@ -104,6 +104,8 @@ struct _MetaDisplay Atom atom_net_wm_moveresize; Atom atom_net_active_window; Atom atom_metacity_restart_message; + Atom atom_net_wm_strut; + Atom atom_win_hints; /* This is the actual window from focus events, * not the one we last set diff --git a/src/place.c b/src/place.c index b67af69db..6574d5098 100644 --- a/src/place.c +++ b/src/place.c @@ -85,6 +85,7 @@ find_next_cascade (MetaWindow *window, GList *sorted; int cascade_x, cascade_y; int x_threshold, y_threshold; + MetaRectangle work_area; sorted = g_list_copy (windows); sorted = g_list_sort (sorted, northwestcmp); @@ -99,18 +100,10 @@ find_next_cascade (MetaWindow *window, * 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; - } + meta_window_get_work_area (window, &work_area); + + cascade_x = MAX (0, work_area.x); + cascade_y = MAX (0, work_area.y); /* Find first cascade position that's not used. */ @@ -445,6 +438,7 @@ get_vertical_edges (MetaWindow *window, int i; int n_edges; MetaRectangle rect; + MetaRectangle work_area; windows = get_windows_on_same_workspace (window, &n_windows); @@ -453,11 +447,13 @@ get_vertical_edges (MetaWindow *window, edges = g_new (int, n_edges); /* workspace/screen edges */ - edges[i] = window->screen->active_workspace->workarea.x; + meta_window_get_work_area (window, &work_area); + + edges[i] = work_area.x; ++i; edges[i] = - window->screen->active_workspace->workarea.x + - window->screen->active_workspace->workarea.width; + work_area.x + + work_area.width; ++i; edges[i] = 0; ++i; @@ -508,6 +504,7 @@ get_horizontal_edges (MetaWindow *window, int i; int n_edges; MetaRectangle rect; + MetaRectangle work_area; windows = get_windows_on_same_workspace (window, &n_windows); @@ -516,11 +513,13 @@ get_horizontal_edges (MetaWindow *window, edges = g_new (int, n_edges); /* workspace/screen edges */ - edges[i] = window->screen->active_workspace->workarea.y; + meta_window_get_work_area (window, &work_area); + + edges[i] = work_area.y; ++i; edges[i] = - window->screen->active_workspace->workarea.y + - window->screen->active_workspace->workarea.height; + work_area.y + + work_area.height; ++i; edges[i] = 0; ++i; diff --git a/src/window.c b/src/window.c index 914a0c38d..b13f52f28 100644 --- a/src/window.c +++ b/src/window.c @@ -43,6 +43,16 @@ typedef enum META_USER_MOVE_RESIZE = 1 << 2 } MetaMoveResizeFlags; +typedef enum +{ + WIN_HINTS_SKIP_FOCUS = (1<<0), /* "alt-tab" skips this win */ + WIN_HINTS_SKIP_WINLIST = (1<<1), /* not in win list */ + WIN_HINTS_SKIP_TASKBAR = (1<<2), /* not on taskbar */ + WIN_HINTS_GROUP_TRANSIENT = (1<<3), /* ??????? */ + WIN_HINTS_FOCUS_ON_CLICK = (1<<4), /* app only accepts focus when clicked */ + WIN_HINTS_DO_NOT_COVER = (1<<5) /* attempt to not cover this window */ +} GnomeWinHints; + static void constrain_size (MetaWindow *window, MetaFrameGeometry *fgeom, int width, @@ -72,8 +82,11 @@ static int update_icon_name (MetaWindow *window); static int update_icon (MetaWindow *window, gboolean reread_rgb_icon); static int update_kwm_icon (MetaWindow *window); +static void update_struts (MetaWindow *window); static void recalc_window_type (MetaWindow *window); static void recalc_window_features (MetaWindow *window); +static void recalc_do_not_cover_struts(MetaWindow *window); +static void invalidate_work_areas (MetaWindow *window); static int set_wm_state (MetaWindow *window, int state); static int set_net_wm_state (MetaWindow *window); @@ -83,6 +96,8 @@ static gboolean process_property_notify (MetaWindow *window, static void meta_window_show (MetaWindow *window); static void meta_window_hide (MetaWindow *window); +static GList* meta_window_get_workspaces (MetaWindow *window); + static gboolean meta_window_get_icon_geometry (MetaWindow *window, MetaRectangle *rect); @@ -350,6 +365,12 @@ meta_window_new (MetaDisplay *display, Window xwindow, window->type = META_WINDOW_NORMAL; window->type_atom = None; + window->has_struts = FALSE; + window->left_strut = 0; + window->right_strut = 0; + window->top_strut = 0; + window->bottom_strut = 0; + window->layer = META_LAYER_NORMAL; window->stack_op = NULL; window->initial_workspace = 0; /* not used */ @@ -359,6 +380,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, update_title (window); update_protocols (window); update_wm_hints (window); + update_struts (window); update_net_wm_state (window); /* Initially maximize if window is fullscreen; FIXME @@ -1193,6 +1215,8 @@ meta_window_maximize (MetaWindow *window) { if (!window->maximized) { + meta_verbose ("Maximizing %s\n", window->desc); + window->maximized = TRUE; meta_window_raise (window); @@ -1218,6 +1242,8 @@ meta_window_unmaximize (MetaWindow *window) { if (window->maximized) { + meta_verbose ("Unmaximizing %s\n", window->desc); + window->maximized = FALSE; meta_window_move_resize (window, @@ -1660,7 +1686,7 @@ meta_window_move_resize_internal (MetaWindow *window, window->frame->right_width = fgeom.right_width; window->frame->bottom_height = fgeom.bottom_height; } - + /* See ICCCM 4.1.5 for when to send ConfigureNotify */ need_configure_notify = FALSE; @@ -1756,6 +1782,19 @@ meta_window_move_resize_internal (MetaWindow *window, { meta_verbose ("Size/position not modified\n"); } + + /* Update struts for new window size */ + if (window->do_not_cover && (need_resize_client || need_move_client)) + { + recalc_do_not_cover_struts (window); + + /* Does a resize on all windows on entire current workspace, + * would be an infinite loop except for need_resize_client + * above. + */ + + invalidate_work_areas (window); + } /* Invariants leaving this function are: * a) window->rect and frame->rect reflect the actual @@ -2636,6 +2675,44 @@ meta_window_client_message (MetaWindow *window, return TRUE; } + else if (event->xclient.message_type == + display->atom_win_hints) + { + /* gnome-winhints.c seems to indicate that the hints are + * in l[1], though god knows why it's like that + */ + gulong data[1]; + + meta_verbose ("_WIN_HINTS client message, hints: %ld\n", + event->xclient.data.l[1]); + + if (event->xclient.data.l[1] & WIN_HINTS_DO_NOT_COVER) + { + meta_verbose ("Setting WIN_HINTS_DO_NOT_COVER\n"); + + data[0] = WIN_HINTS_DO_NOT_COVER; + + meta_error_trap_push (window->display); + XChangeProperty (window->display->xdisplay, + window->xwindow, window->display->atom_win_hints, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)data, 1); + meta_error_trap_pop (window->display); + } + else + { + meta_verbose ("Unsetting WIN_HINTS_DO_NOT_COVER\n"); + + data[0] = 0; + + meta_error_trap_push (window->display); + XDeleteProperty (window->display->xdisplay, + window->xwindow, window->display->atom_win_hints); + meta_error_trap_pop (window->display); + } + + return TRUE; + } return FALSE; } @@ -2858,6 +2935,16 @@ process_property_notify (MetaWindow *window, update_kwm_icon (window); update_icon (window, FALSE); } + else if (event->atom == window->display->atom_net_wm_strut) + { + meta_verbose ("Property notify on %s for _NET_WM_STRUT\n", window->desc); + update_struts (window); + } + else if (event->atom == window->display->atom_win_hints) + { + meta_verbose ("Property notify on %s for _WIN_HINTS\n", window->desc); + update_struts (window); + } return TRUE; } @@ -4377,6 +4464,166 @@ update_kwm_icon (MetaWindow *window) return Success; } +static GList* +meta_window_get_workspaces (MetaWindow *window) +{ + if (window->on_all_workspaces) + return window->display->workspaces; + else + return window->workspaces; +} + +static void +invalidate_work_areas (MetaWindow *window) +{ + GList *tmp; + + tmp = meta_window_get_workspaces (window); + + while (tmp != NULL) + { + meta_workspace_invalidate_work_area (tmp->data); + tmp = tmp->next; + } +} + +static void +update_struts (MetaWindow *window) +{ + gulong *struts = NULL; + int nitems; + + meta_verbose ("Updating struts for %s\n", window->desc); + + window->has_struts = FALSE; + window->do_not_cover = FALSE; + window->left_strut = 0; + window->right_strut = 0; + window->top_strut = 0; + window->bottom_strut = 0; + + if (meta_prop_get_cardinal_list (window->display, + window->xwindow, + window->display->atom_net_wm_strut, + &struts, &nitems)) + { + if (nitems != 4) + { + meta_verbose ("_NET_WM_STRUT on %s has %d values instead of 4\n", + window->desc, nitems); + meta_XFree (struts); + } + + window->has_struts = TRUE; + window->left_strut = struts[0]; + window->right_strut = struts[1]; + window->top_strut = struts[2]; + window->bottom_strut = struts[3]; + + meta_verbose ("Using _NET_WM_STRUT struts %d %d %d %d for window %s\n", + window->left_strut, window->right_strut, + window->top_strut, window->bottom_strut, + window->desc); + + meta_XFree (struts); + } + else + { + meta_verbose ("No _NET_WM_STRUT property for %s\n", + window->desc); + } + + if (!window->has_struts) + { + /* Try _WIN_HINTS */ + gulong hints; + + if (meta_prop_get_cardinal (window->display, + window->xwindow, + window->display->atom_win_hints, + &hints)) + { + if (hints & WIN_HINTS_DO_NOT_COVER) + { + window->has_struts = TRUE; + window->do_not_cover = TRUE; + recalc_do_not_cover_struts (window); + + meta_verbose ("Using _WIN_HINTS struts %d %d %d %d for window %s\n", + window->left_strut, window->right_strut, + window->top_strut, window->bottom_strut, + window->desc); + } + else + { + meta_verbose ("DO_NOT_COVER hint not set in _WIN_HINTS\n"); + } + } + else + { + meta_verbose ("No _WIN_HINTS property on %s\n", + window->desc); + } + } + + invalidate_work_areas (window); +} + +static void +recalc_do_not_cover_struts (MetaWindow *window) +{ + if (window->do_not_cover) + { + /* We only understand windows that are aligned to + * a screen edge + */ + gboolean horizontal; + gboolean on_left_edge; + gboolean on_right_edge; + gboolean on_bottom_edge; + gboolean on_top_edge; + + window->left_strut = 0; + window->right_strut = 0; + window->top_strut = 0; + window->bottom_strut = 0; + + on_left_edge = window->rect.x == 0; + on_right_edge = (window->rect.x + window->rect.width) == + window->screen->width; + on_top_edge = window->rect.y == 0; + on_bottom_edge = (window->rect.y + window->rect.height) == + window->screen->height; + + /* cheesy heuristic to decide where the strut goes */ + if (on_left_edge && on_right_edge && on_bottom_edge) + horizontal = TRUE; + else if (on_left_edge && on_right_edge && on_top_edge) + horizontal = TRUE; + else if (on_top_edge && on_bottom_edge && on_left_edge) + horizontal = FALSE; + else if (on_top_edge && on_bottom_edge && on_right_edge) + horizontal = FALSE; + else + horizontal = window->rect.width > window->rect.height; + + if (horizontal) + { + if (on_top_edge) + window->top_strut = window->rect.height; + else if (on_bottom_edge) + window->bottom_strut = window->rect.height; + } + else + { + if (on_left_edge) + window->left_strut = window->rect.width; + else if (on_right_edge) + window->right_strut = window->rect.width; + } + } +} + static void recalc_window_type (MetaWindow *window) { @@ -4499,10 +4746,13 @@ constrain_size (MetaWindow *window, int delta; double min_aspect, max_aspect; int minw, minh, maxw, maxh, fullw, fullh; - + MetaRectangle work_area; + /* frame member variables should NEVER be used in here */ #define FLOOR(value, base) ( ((int) ((value) / (base))) * (base) ) + + meta_window_get_work_area (window, &work_area); /* Get the allowed size ranges, considering maximized, etc. */ if (window->type == META_WINDOW_DESKTOP || @@ -4513,8 +4763,8 @@ constrain_size (MetaWindow *window, } else { - fullw = window->screen->active_workspace->workarea.width; - fullh = window->screen->active_workspace->workarea.height; + fullw = work_area.width; + fullh = work_area.height; } if (window->frame) @@ -4620,6 +4870,10 @@ constrain_position (MetaWindow *window, int *new_x, int *new_y) { + MetaRectangle work_area; + + meta_window_get_work_area (window, &work_area); + /* frame member variables should NEVER be used in here, only * MetaFrameGeometry */ @@ -4644,8 +4898,8 @@ constrain_position (MetaWindow *window, /* find furthest northwest point the window can occupy, * to disallow moving titlebar off the top or left */ - nw_x = window->screen->active_workspace->workarea.x; - nw_y = window->screen->active_workspace->workarea.y; + nw_x = work_area.x; + nw_y = work_area.y; if (window->frame) { nw_x += fgeom->left_width; @@ -4653,10 +4907,8 @@ constrain_position (MetaWindow *window, } /* find bottom-right corner of workarea */ - se_x = window->screen->active_workspace->workarea.x + - window->screen->active_workspace->workarea.width; - se_y = window->screen->active_workspace->workarea.y + - window->screen->active_workspace->workarea.height; + se_x = work_area.x + work_area.width; + se_y = work_area.y + work_area.height; /* if the window's size exceeds the screen size, * we allow it to go off the top/left far enough @@ -5181,3 +5433,40 @@ meta_window_set_gravity (MetaWindow *window, meta_error_trap_pop (window->display); } + +void +meta_window_get_work_area (MetaWindow *window, + MetaRectangle *area) +{ + MetaRectangle space_area; + GList *tmp; + + int left_strut = 0; + int right_strut = 0; + int top_strut = 0; + int bottom_strut = 0; + + tmp = meta_window_get_workspaces (window); + + while (tmp != NULL) + { + meta_workspace_get_work_area (tmp->data, &space_area); + + left_strut = MAX (left_strut, space_area.x); + right_strut = MAX (right_strut, + (window->screen->width - space_area.x - space_area.width)); + top_strut = MAX (top_strut, space_area.y); + bottom_strut = MAX (bottom_strut, + (window->screen->height - space_area.y - space_area.height)); + + tmp = tmp->next; + } + + area->x = left_strut; + area->y = top_strut; + area->width = window->screen->width - left_strut - right_strut; + area->height = window->screen->height - top_strut - bottom_strut; + + meta_verbose ("Window %s has work area %d,%d %d x %d\n", + window->desc, area->x, area->y, area->width, area->height); +} diff --git a/src/window.h b/src/window.h index 974465678..4fbea570c 100644 --- a/src/window.h +++ b/src/window.h @@ -183,6 +183,11 @@ struct _MetaWindow * only relevant if !window->placed */ guint calc_placement : 1; + + /* Has nonzero struts */ + guint has_struts : 1; + /* Struts are from the _WIN_HINTS do not cover deal */ + guint do_not_cover : 1; /* Number of UnmapNotify that are caused by us, if * we get UnmapNotify with none pending then the client @@ -220,6 +225,12 @@ struct _MetaWindow /* x/y/w/h here get filled with ConfigureRequest values */ XSizeHints size_hints; + /* struts */ + int left_strut; + int right_strut; + int top_strut; + int bottom_strut; + /* Managed by stack.c */ MetaStackLayer layer; MetaStackOp *stack_op; @@ -342,4 +353,8 @@ void meta_window_handle_mouse_grab_op_event (MetaWindow *window, gboolean meta_window_visible_on_workspace (MetaWindow *window, MetaWorkspace *workspace); +/* Get minimum work area for all workspaces we're on */ +void meta_window_get_work_area (MetaWindow *window, + MetaRectangle *area); + #endif diff --git a/src/workspace.c b/src/workspace.c index 1b65915e7..77c02e5eb 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -40,14 +40,12 @@ meta_workspace_new (MetaScreen *screen) g_list_append (workspace->screen->display->workspaces, workspace); workspace->windows = NULL; - /* This may have something to do with the strut hints - * eventually - */ - workspace->workarea.x = 0; - workspace->workarea.y = 0; - workspace->workarea.width = screen->width; - workspace->workarea.height = screen->height; - + workspace->work_area.x = 0; + workspace->work_area.y = 0; + workspace->work_area.width = screen->width; + workspace->work_area.height = screen->height; + workspace->work_area_invalid = TRUE; + /* Update hint for current number of workspaces */ set_number_of_spaces_hint (screen); @@ -105,6 +103,13 @@ meta_workspace_add_window (MetaWorkspace *workspace, meta_window_set_current_workspace_hint (window); meta_window_queue_calc_showing (window); + if (window->has_struts) + meta_workspace_invalidate_work_area (workspace); + + /* queue a move_resize since changing workspaces may change + * the relevant struts + */ + meta_window_queue_move_resize (window); } void @@ -119,6 +124,14 @@ meta_workspace_remove_window (MetaWorkspace *workspace, meta_window_set_current_workspace_hint (window); meta_window_queue_calc_showing (window); + + if (window->has_struts) + meta_workspace_invalidate_work_area (workspace); + + /* queue a move_resize since changing workspaces may change + * the relevant struts + */ + meta_window_queue_move_resize (window); } void @@ -242,6 +255,37 @@ meta_workspace_screen_index (MetaWorkspace *workspace) meta_bug ("Workspace does not exist to index!\n"); } +/* get windows contained on workspace, including workspace->windows + * and also sticky windows. + */ +GList* +meta_workspace_list_windows (MetaWorkspace *workspace) +{ + GSList *display_windows; + GSList *tmp; + GList *workspace_windows; + + display_windows = meta_display_list_windows (workspace->screen->display); + + workspace_windows = NULL; + tmp = display_windows; + while (tmp != NULL) + { + MetaWindow *window = tmp->data; + + if (window->on_all_workspaces || + meta_workspace_contains_window (workspace, window)) + workspace_windows = g_list_prepend (workspace_windows, + window); + + tmp = tmp->next; + } + + g_slist_free (display_windows); + + return workspace_windows; +} + static int set_number_of_spaces_hint (MetaScreen *screen) { @@ -275,3 +319,90 @@ set_active_space_hint (MetaScreen *screen) 32, PropModeReplace, (guchar*) data, 1); return meta_error_trap_pop (screen->display); } + +void +meta_workspace_invalidate_work_area (MetaWorkspace *workspace) +{ + GList *tmp; + + if (workspace->work_area_invalid) + return; + + workspace->work_area_invalid = TRUE; + + /* redo the size/position constraints on all windows */ + tmp = workspace->windows; + while (tmp != NULL) + { + MetaWindow *w = tmp->data; + + meta_window_queue_move_resize (w); + + tmp = tmp->next; + } +} + +void +meta_workspace_get_work_area (MetaWorkspace *workspace, + MetaRectangle *area) +{ + if (workspace->work_area_invalid) + { + int left_strut = 0; + int right_strut = 0; + int top_strut = 0; + int bottom_strut = 0; + GList *tmp; + GList *windows; + + windows = meta_workspace_list_windows (workspace); + tmp = windows; + while (tmp != NULL) + { + MetaWindow *w = tmp->data; + + if (w->has_struts) + { + left_strut = MAX (left_strut, w->left_strut); + right_strut = MAX (right_strut, w->right_strut); + top_strut = MAX (top_strut, w->top_strut); + bottom_strut = MAX (bottom_strut, w->bottom_strut); + } + + tmp = tmp->next; + } + + g_list_free (windows); + + /* Some paranoid robustness */ +#define MIN_SANE_AREA 100 + + if ((left_strut + right_strut) > (workspace->screen->width - MIN_SANE_AREA)) + { + left_strut = (workspace->screen->width - MIN_SANE_AREA) / 2; + right_strut = left_strut; + } + + if ((top_strut + bottom_strut) > (workspace->screen->height - MIN_SANE_AREA)) + { + top_strut = (workspace->screen->height - MIN_SANE_AREA) / 2; + bottom_strut = top_strut; + } + + workspace->work_area.x = left_strut; + workspace->work_area.y = top_strut; + workspace->work_area.width = workspace->screen->width - left_strut - right_strut; + workspace->work_area.height = workspace->screen->height - top_strut - bottom_strut; + + workspace->work_area_invalid = FALSE; + + meta_verbose ("Workspace %d has work area %d,%d %d x %d\n", + meta_workspace_index (workspace), + workspace->work_area.x, + workspace->work_area.y, + workspace->work_area.width, + workspace->work_area.height); + } + + *area = workspace->work_area; +} diff --git a/src/workspace.h b/src/workspace.h index 97f12ec84..46439d245 100644 --- a/src/workspace.h +++ b/src/workspace.h @@ -30,7 +30,8 @@ struct _MetaWorkspace GList *windows; - MetaRectangle workarea; + MetaRectangle work_area; + guint work_area_invalid : 1; }; MetaWorkspace* meta_workspace_new (MetaScreen *screen); @@ -47,6 +48,12 @@ gboolean meta_workspace_contains_window (MetaWorkspace *workspace, void meta_workspace_activate (MetaWorkspace *workspace); int meta_workspace_index (MetaWorkspace *workspace); int meta_workspace_screen_index (MetaWorkspace *workspace); +GList* meta_workspace_list_windows (MetaWorkspace *workspace); + +void meta_workspace_invalidate_work_area (MetaWorkspace *workspace); +void meta_workspace_get_work_area (MetaWorkspace *workspace, + MetaRectangle *area); + #endif