store strut information, update it on property changes, etc. etc. so we

2002-01-09  Havoc Pennington  <hp@pobox.com>

	* 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
This commit is contained in:
Havoc Pennington 2002-01-10 06:31:31 +00:00 committed by Havoc Pennington
parent da6ded6f3f
commit b2bbb306f4
8 changed files with 497 additions and 38 deletions

View File

@ -1,3 +1,15 @@
2002-01-09 Havoc Pennington <hp@pobox.com>
* 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 <hp@pobox.com>
* configure.in (ACLOCAL): add code to save ACLOCAL_FLAGS

View File

@ -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

View File

@ -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

View File

@ -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;
meta_window_get_work_area (window, &work_area);
cascade_x = MAX (cascade_x, space->workarea.x);
cascade_y = MAX (cascade_y, space->workarea.y);
tmp = tmp->next;
}
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;

View File

@ -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,
@ -1757,6 +1783,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
* server-side size/pos of window->xwindow and frame->xwindow
@ -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,11 +4746,14 @@ 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 ||
window->type == META_WINDOW_DOCK)
@ -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);
}

View File

@ -184,6 +184,11 @@ struct _MetaWindow
*/
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
* is withdrawing the window.
@ -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

View File

@ -40,13 +40,11 @@ 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;
}

View File

@ -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