From 00dcef82e32456c6202f2ebb509ce411b5825eb8 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Tue, 6 Aug 2002 04:11:23 +0000 Subject: [PATCH] disable animation when shading windows, just doesn't really convey the 2002-07-28 Havoc Pennington * src/window.c (meta_window_shade): disable animation when shading windows, just doesn't really convey the idea anyway. * src/effects.c: Move to using a shaped window instead of IncludeInferiors to do the animations, looks a lot better because we don't have to grab the server. * src/window.c (meta_window_change_workspace): remove bogus assertion that was causing a crash (meta_window_new): auto-fullscreen huge undecorated windows. * src/keybindings.c (switch_to_workspace): use meta_window_change_workspace() to avoid same bug in cut-and-paste code from there --- ChangeLog | 17 + autogen.sh | 4 + src/constraints.c | 1039 +++++++++++++++++++++++++++++++++++++++++---- src/constraints.h | 17 +- src/display.c | 2 + src/effects.c | 140 +++++- src/keybindings.c | 10 +- src/screen.c | 1 + src/window.c | 50 ++- 9 files changed, 1162 insertions(+), 118 deletions(-) diff --git a/ChangeLog b/ChangeLog index 74e203968..bde18b952 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2002-07-28 Havoc Pennington + + * src/window.c (meta_window_shade): disable animation when shading + windows, just doesn't really convey the idea anyway. + + * src/effects.c: Move to using a shaped window instead of + IncludeInferiors to do the animations, looks a lot better + because we don't have to grab the server. + + * src/window.c (meta_window_change_workspace): remove bogus + assertion that was causing a crash + (meta_window_new): auto-fullscreen huge undecorated windows. + + * src/keybindings.c (switch_to_workspace): use + meta_window_change_workspace() to avoid same bug in cut-and-paste + code from there + 2002-08-06 He Qiangqiang * configure.in: Added "zh_CN" to ALL_LINGUAS. diff --git a/autogen.sh b/autogen.sh index ac91e6467..8b444007f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -112,14 +112,18 @@ do echo "Running libtoolize..." libtoolize --force --copy fi + echo "Running $ACLOCAL $aclocalinclude ..." $ACLOCAL $aclocalinclude + if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then echo "Running autoheader..." autoheader fi + echo "Running $AUTOMAKE --gnu $am_opt ..." $AUTOMAKE --add-missing --gnu $am_opt + echo "Running autoconf ..." autoconf ) diff --git a/src/constraints.c b/src/constraints.c index 796d19b2a..c98262484 100644 --- a/src/constraints.c +++ b/src/constraints.c @@ -19,7 +19,8 @@ * 02111-1307, USA. */ -#include +#include "constraints.h" +#include "place.h" /* The way this code works was suggested by Owen Taylor. * @@ -72,7 +73,9 @@ * modify both the Y position and the height of the window into an * operation that modifies a single variable, dy. That variable is * then constrained, rather than the constraining the Y pos and height - * separately. + * separately. This is a rather complicated fix for an obscure bug + * that happened when resizing a window and encountering a constraint + * such as the top edge of the screen. * */ @@ -103,55 +106,160 @@ * */ -/* Clamps the delta to nearest permitted. - * delta is the "free variable" in any given operation. - */ -typedef void (* MetaConstraintFunc) (MetaWindow *window, - MetaFrameGeometry *fgeom, - const MetaRectangle *orig, - int *x_delta, - int *y_delta); - - -/* This enum marks what x_delta and y_delta mean, i.e. it - * identifies the current free variables. - */ -enum +typedef struct { - RESIZE_TOP, - RESIZE_BOTTOM, - RESIZE_LEFT, - RESIZE_RIGHT, - RESIZE_VERTICAL_CENTER, - RESIZE_HORIZONTAL_CENTER, - MOVE_VERTICAL, - MOVE_HORIZONTAL, - LAST_MOVE_CONTEXT -} MoveContext; + MetaWindow *window; + MetaFrameGeometry fgeom; + const MetaXineramaScreenInfo *xinerama; + MetaRectangle work_area_xinerama; + MetaRectangle work_area_screen; + int nw_x, nw_y, se_x, se_y; +} ConstraintInfo; - -/* Maximization constraint: - * - * new_x = workarea_x + frame_left - * new_y = workarea_y + frame_top - * new_w = workarea_w - frame_left - frame_right - * new_h = workarea_h - frame_top - frame_bottom - * - * No need to do anything hard because it just locks specific - * size/pos. - * - * The min/max size constraints override maximization. +/* (FIXME instead of TITLEBAR_LENGTH_ONSCREEN, get the actual + * size of the menu control?). */ -/* Full screen constraint: +#define TITLEBAR_LENGTH_ONSCREEN 36 + +typedef gboolean (* MetaConstraintAppliesFunc) (MetaWindow *window); + +/* There's a function for each case with a different "free variable" */ +typedef void (* MetaConstrainTopFunc) (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta); +typedef void (* MetaConstrainBottomFunc) (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta); +typedef void (* MetaConstrainVCenterFunc) (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta); +typedef void (* MetaConstrainLeftFunc) (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta); +typedef void (* MetaConstrainRightFunc) (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta); +typedef void (* MetaConstrainHCenterFunc) (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta); +typedef void (* MetaConstrainMoveFunc) (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta, + int *y_delta); + +typedef struct +{ + MetaConstraintAppliesFunc applies_func; + MetaConstrainTopFunc top_func; + MetaConstrainBottomFunc bottom_func; + MetaConstrainVCenterFunc vcenter_func; + MetaConstrainLeftFunc left_func; + MetaConstrainRightFunc right_func; + MetaConstrainHCenterFunc hcenter_func; + MetaConstrainMoveFunc move_func; +} Constraint; + +/* "Is the desktop window" constraint: * - * new_x = 0; - * new_y = 0; - * new_w = xinerama_width; - * new_h = xinerama_height; + * new_x = 0; + * new_y = 0; + * new_w = orig_width; + * new_h = orig_height; * - * The min/max size constraints override fullscreen. + * Note that if we are applying a resize constraint, + * e.g. constraint_desktop_top_func, this is kind of broken since we + * end up resizing the window in order to get its position right. But + * that case shouldn't happen in practice. */ +static gboolean +constraint_desktop_applies_func (MetaWindow *window) +{ + return window->type == META_WINDOW_DESKTOP; +} + +static void +constraint_desktop_top_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ + *y_delta = 0 - orig->y; +} + +static void +constraint_desktop_bottom_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ + /* nothing */ +} + +static void +constraint_desktop_vcenter_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ + *y_delta = 0 - orig->y; +} + +static void +constraint_desktop_left_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ + *x_delta = 0 - orig->x; +} + +static void +constraint_desktop_right_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ + /* nothing */ +} + +static void +constraint_desktop_hcenter_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ + *x_delta = 0 - orig->x; +} + +static void +constraint_desktop_move_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta, + int *y_delta) +{ + *x_delta = 0 - orig->x; + *y_delta = 0 - orig->y; +} + +static const Constraint constraint_desktop = { + constraint_desktop_applies_func, + constraint_desktop_top_func, + constraint_desktop_bottom_func, + constraint_desktop_vcenter_func, + constraint_desktop_left_func, + constraint_desktop_right_func, + constraint_desktop_hcenter_func, + constraint_desktop_move_func +}; /* Titlebar is onscreen constraint: * @@ -162,45 +270,54 @@ enum * * NW limit has priority over SE, since titlebar is on NW * - * RESIZE_LEFT: - * new_width = orig_width + dx - * new_x = orig_x - dx + * Left resize + * === * - * new_x >= nw_x - (left_width + new_width + right_width - titlebar_width_onscreen) + * new_width = orig_width - dx + * new_x = orig_x + dx * - * orig_x - dx >= nw_x - (left_width + orig_width + dx + right_width - titlebar_width_onscreen) - * 0 >= nw_x - left_width - orig_width - right_width + titlebar_width_onscreen - orig_x + * Amount of window+frame that doesn't fit in the work area: * - * i.e. dx drops out so there is no constraint at all when moving left edge. + * offscreen_width = left_width + new_width + right_width - (se_x - nw_x) * - * RESIZE_RIGHT and RESIZE_BOTTOM are the same, cannot break this constraint - * by moving in those directions. + * If we keep the old metacity rule where a window can be offscreen by + * offscreen_width, then the math works out that left/top resizes are not + * constrained. If we instead have a rule where the window can never be offscreen, + * you get the following: * - * RESIZE_TOP: + * new_x >= nw_x + left_width + titlebar_width_offscreen + * orig_x + dx >= nw_x + left_width + titlebar_width_onscreen + * dx >= nw_x + left_width + titlebar_width_onscreen - orig_x * - * new_height = orig_height - dy - * new_y = orig_y + dy + * i.e. the minimum dx is: nw_x + left_width + titlebar_width_onscreen - orig_x * - * Can't move titlebar off the top at all regardless of height: - * new_y >= nw_y + top_height + * We could have a more complicated rule that constrains only if the current + * offscreen width is positive, thus allowing something more like the old + * behavior, but not doing that for now. * - * orig_y + dy = nw_y + top_height - * dy = nw_y + top_height - orig_y + * Top resize works the same as left resize. Right/bottom resize don't have a limit + * because the constraint is designed to keep the top right corner of the + * window or its titlebar on the screen, and right/bottom resize will never move that + * area. Center resize is almost like left/top but dx has the opposite sign + * and new_width = orig_width + 2dx. * - * Max dy is thus (nw_y + top_height - orig_y) + * For right/bottom we can try to handle windows that aren't in a valid + * location to begin with: * - * RESIZE_VERTICAL_CENTER: + * new_x <= se_x - titlebar_width_onscreen + * dx <= se_x - titlebar_width_onscreen - orig_x * - * RESIZE_HORIZONTAL_CENTER is like vertical + * but in principle this constraint is never triggered. + * + * Vertical move + * === * - * - * MOVE_VERTICAL: * new_height = orig_height * new_y = orig_y + dy * * new_y >= nw_y + top_height * - * Min negative dy (nw_y + top_height - orig_y) just as with RESIZE_TOP + * Min negative dy (nw_y + top_height - orig_y) just as with top resize. * Max positive dy has to be computed from se_y and given less priority than the * min negative: * @@ -208,45 +325,789 @@ enum * orig_y + dy = se_y * so max dy is (se_y - orig_y) * - * - * MOVE_HORIZONTAL: - * works out same as vertical in the other direction - * + * Horizontal move is equivalent to vertical. * */ - - -void -meta_window_constrain (MetaWindow *window, - MetaFrameGeometry *orig_fgeom, - int resize_gravity, - const MetaRectangle *orig, - MetaRectangle *new) +static gboolean +constraint_onscreen_applies_func (MetaWindow *window) { - MetaFrameGeometry fgeom; + return + window->type != META_WINDOW_DESKTOP && + window->type != META_WINDOW_DOCK; +} - /* Create a fake frame geometry if none really exists */ - if (orig_fgeom) - fgeom = *orig_fgeom; - else - { - fgeom.top_height = 0; - fgeom.bottom_height = 0; - fgeom.left_width = 0; - fgeom.right_width = 0; - } +static void +constraint_onscreen_top_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ + int min_dy; + + min_dy = info->nw_y + info->fgeom.top_height - orig->y; + + if (*y_delta < min_dy) + *y_delta = min_dy; +} + +static void +constraint_onscreen_bottom_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ + int max_dy; + + max_dy = info->se_y - info->fgeom.top_height - orig->y; + + if (*y_delta > max_dy) + *y_delta = max_dy; +} + +static void +constraint_onscreen_vcenter_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ + int max_dy; + + max_dy = info->nw_y + info->fgeom.top_height - orig->y; + max_dy = ABS (max_dy); + + if (*y_delta > max_dy) + *y_delta = max_dy; +} + +static void +constraint_onscreen_left_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ + int min_dx; + + min_dx = info->nw_x + info->fgeom.left_width + TITLEBAR_WIDTH_ONSCREEN - orig->x; + + if (*x_delta < min_dx) + *x_delta = min_dx; +} + +static void +constraint_onscreen_right_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ + int max_dx; + + max_dx = info->se_x - TITLEBAR_WIDTH_ONSCREEN - orig->x; + + if (*x_delta > max_dx) + *x_delta = max_dx; +} + +static void +constraint_onscreen_hcenter_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ + int max_dx; + + max_dx = info->nw_x + info->fgeom.left_width + TITLEBAR_WIDTH_ONSCREEN - orig->x; + max_dx = ABS (max_dx); + + if (*x_delta > max_dx) + *x_delta = max_dx; +} + +static void +constraint_onscreen_move_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta, + int *y_delta) +{ + int min_delta; + int max_delta; + + min_delta = info->nw_y + info->fgeom.top_height - orig->y; + + if (window->frame) /* if frame, the titlebar is always above the y pos */ + max_delta = info->se_y - orig->y; + else /* else keep some client area pixels on the screen */ + max_delta = info->se_y - orig->y - TITLEBAR_WIDTH_ONSCREEN; + /* Note that min delta (top left) has priority over + * max delta (bottom right) to facilitate keeping + * titlebar on the screen + */ + if (*y_delta > max_delta) + *y_delta = max_delta; + if (*y_delta < min_delta) + *y_delta = min_delta; + + min_delta = info->nw_x + TITLEBAR_WIDTH_ONSCREEN - orig->x; + max_delta = info->se_x - orig->x - TITLEBAR_WIDTH_ONSCREEN; + + if (*x_delta > max_delta) + *x_delta = max_delta; + if (*x_delta < min_delta) + *x_delta = min_delta; +} + +static const Constraint constraint_onscreen = { + constraint_onscreen_applies_func, + constraint_onscreen_top_func, + constraint_onscreen_bottom_func, + constraint_onscreen_vcenter_func, + constraint_onscreen_left_func, + constraint_onscreen_right_func, + constraint_onscreen_hcenter_func, + constraint_onscreen_move_func +}; + + +/* Size hints constraints: + * + * For min/max size we just clamp to those, and for resize increment + * we clamp to the one at or below the requested place. + * + * For aspect ratio, we special-case it at the end of + * meta_window_constrain, because it involves both dimensions, and + * thus messes up our generic framework. + * + * Left resize: + * new_width = orig_width - dx + * new_x = orig_x + dx + * + * new_width >= min_width + * orig_width - dx >= min_width + * - dx >= min_width - orig_width + * dx <= orig_width - min_width + * + * new_width <= max_width + * orig_width - dx <= max_width + * - dx <= max_width - orig_width + * dx >= orig_width - max_width + * + */ + +static gboolean +constraint_hints_applies_func (MetaWindow *window) +{ + return TRUE; +} + +static void +constraint_hints_top_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ +} + +static void +constraint_hints_bottom_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ } +static void +constraint_hints_vcenter_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *y_delta) +{ +} +static void +constraint_hints_left_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ + int min_dx; + int max_dx; + int width; + + max_dx = orig->width - window->size_hints.min_width; + min_dx = orig->width - window->size_hints.max_width; + if (*x_delta > max_dx) + *x_delta = max_dx; + if (*x_delta < min_dx) + *x_delta = min_dx; + /* shrink to base + N * inc + */ + width = orig->width - *x_delta; + width = window->size_hints.base_width + + FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc); + + *x_delta = orig->width - width; +} +static void +constraint_hints_right_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ +} +static void +constraint_hints_hcenter_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta) +{ +} + +static void +constraint_hints_move_func (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int *x_delta, + int *y_delta) +{ + /* nothing */ +} + +static const Constraint constraint_hints = { + constraint_hints_applies_func, + constraint_hints_top_func, + constraint_hints_bottom_func, + constraint_hints_vcenter_func, + constraint_hints_left_func, + constraint_hints_right_func, + constraint_hints_hcenter_func, + constraint_hints_move_func +}; + +/* Array of all constraints at once */ +static const Constraint *all_constraints[] = { + &constraint_desktop, + &constraint_onscreen, + &constraint_hints, + NULL +}; + +/* Move with no accompanying change to window size */ +static void +constrain_move (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int x_delta, + int y_delta, + MetaRectangle *new) +{ + const Constraint **cp; + + cp = &all_constraints[0]; + + while (*cp) + { + if ((* (*cp)->applies_func) (window)) + (* (*cp)->move_func) (window, info, orig, + &x_delta, &y_delta); + + ++cp; + } + + new->x = orig->x + x_delta; + new->y = orig->y + y_delta; +} + +static void +constrain_resize_left (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int x_delta, + MetaRectangle *new) +{ + const Constraint **cp; + + cp = &all_constraints[0]; + + while (*cp) + { + if ((* (*cp)->applies_func) (window)) + (* (*cp)->left_func) (window, info, orig, + &x_delta); + + ++cp; + } + + /* Moving mouse from 10 to 5 means current - orig means 5 - 10 means + * a delta of -5 + */ + new->x = orig->x - x_delta; + new->width = orig->width - x_delta; +} + +static void +constrain_resize_hcenter (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int x_delta, + MetaRectangle *new) +{ + const Constraint **cp; + + cp = &all_constraints[0]; + + while (*cp) + { + if ((* (*cp)->applies_func) (window)) + (* (*cp)->hcenter_func) (window, info, orig, + &x_delta); + + ++cp; + } + + /* center deltas are positive to grow the window and negative to + * shrink it. + */ + new->x = orig->x - x_delta; + new->width = orig->width + x_delta * 2; +} + +static void +constrain_resize_right (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int x_delta, + MetaRectangle *new) +{ + const Constraint **cp; + + cp = &all_constraints[0]; + + while (*cp) + { + if ((* (*cp)->applies_func) (window)) + (* (*cp)->right_func) (window, info, orig, + &x_delta); + + ++cp; + } + + new->width = orig->width + x_delta; +} + +static void +constrain_resize_top (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int y_delta, + MetaRectangle *new) +{ + const Constraint **cp; + + cp = &all_constraints[0]; + + while (*cp) + { + if ((* (*cp)->applies_func) (window)) + (* (*cp)->top_func) (window, info, orig, + &y_delta); + + ++cp; + } + + new->y = orig->y - y_delta; + new->height = orig->height - y_delta; +} + +static void +constrain_resize_vcenter (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int y_delta, + MetaRectangle *new) +{ + const Constraint **cp; + + cp = &all_constraints[0]; + + while (*cp) + { + if ((* (*cp)->applies_func) (window)) + (* (*cp)->vcenter_func) (window, info, orig, + &y_delta); + + ++cp; + } + + /* center deltas are positive to grow the window and negative to + * shrink it. + */ + new->y = orig->y - y_delta; + new->height = orig->height + y_delta * 2; +} + +static void +constrain_resize_bottom (MetaWindow *window, + const ConstraintInfo *info, + const MetaRectangle *orig, + int y_delta, + MetaRectangle *new) +{ + const Constraint **cp; + + cp = &all_constraints[0]; + + while (*cp) + { + if ((* (*cp)->applies_func) (window)) + (* (*cp)->bottom_func) (window, info, orig, + &y_delta); + + ++cp; + } + + new->height = orig->y + y_delta; +} + +static void +update_position_limits (MetaWindow *window, + ConstraintInfo *info) +{ + int nw_x, nw_y; + int se_x, se_y; + int offscreen_w, offscreen_h; + + nw_x = info->work_area_screen.x; + nw_y = info->work_area_screen.y; + + /* find bottom-right corner of workarea */ + se_x = info->work_area_screen.x + info->work_area_screen.width; + se_y = info->work_area_screen.y + info->work_area_screen.height; + + /* If we have a micro-screen or huge frames maybe nw/se got + * swapped + */ + if (nw_x > se_x) + { + int tmp = nw_x; + nw_x = se_x; + se_x = tmp; + } + + if (nw_y > se_y) + { + int tmp = nw_y; + nw_y = se_y; + se_y = tmp; + } + + info->nw_x = nw_x; + info->nw_y = nw_y; + info->se_x = se_x; + info->se_y = se_y; +} + +/* The delta values are the mouse motion distance deltas, + * i.e. mouse_current_pos - mouse_orig_pos, for resizing on + * the sides, or moving. For center resize, the delta + * value is positive to grow the window and negative to + * shrink it (while the sign of the mouse delta + * depends on which side of the window you are center resizing + * from) + */ +void +meta_window_constrain (MetaWindow *window, + MetaFrameGeometry *orig_fgeom, + const MetaRectangle *orig, + int x_move_delta, + int y_move_delta, + MetaResizeDirection x_direction, + int x_delta, + MetaResizeDirection y_direction, + int y_delta, + MetaRectangle *new) +{ + ConstraintInfo info; + MetaRectangle current; + + /* Create a fake frame geometry if none really exists */ + if (orig_fgeom && !window->fullscreen) + info.fgeom = *orig_fgeom; + else + { + info.fgeom.top_height = 0; + info.fgeom.bottom_height = 0; + info.fgeom.left_width = 0; + info.fgeom.right_width = 0; + } + + meta_window_get_work_area (window, TRUE, &info.work_area_xinerama); + meta_window_get_work_area (window, FALSE, &info.work_area_screen); + + info.window = window; + info.xinerama = meta_screen_get_xinerama_for_window (window->screen, + window); + + /* Init info->nw_x etc. */ + update_position_limits (window, &info); + + current = *orig; + *new = current; + + /* Do placement if any, so we go ahead and apply position + * constraints in a move-only context. Don't place + * maximized/fullscreen windows until they are unmaximized + * and unfullscreened + */ + if (!window->placed && + window->calc_placement && + !window->maximized && + !window->fullscreen) + { + int x, y; + + meta_window_place (window, orig_fgeom, x, y, &x, &y); + + constrain_move (window, &info, ¤t, + x - current.x, + y - current.y, + new); + } + + /* Maximization, fullscreen, etc. are defined as a move followed by + * a resize, as explained in one of the big comments at the top of + * this file. + */ + if (window->fullscreen) + { + constrain_move (window, &info, ¤t, + info.xinerama->x_origin - current.x, + info.xinerama->y_origin - current.y, + new); + + current = *new; + + constrain_resize_bottom (window, &info, ¤t, + info.xinerama->height - current.height, + new); + + current = *new; + + constrain_resize_hcenter (window, &info, ¤t, + (info.xinerama->width - current.width) / 2, + new); + } + else if (window->maximized) + { + constrain_move (window, &info, ¤t, + info.work_area_xinerama.x - current.x, + info.work_area_xinerama.y - current.y, + new); + + current = *new; + + constrain_resize_bottom (window, &info, ¤t, + info.work_area_xinerama.height - current.height, + new); + + current = *new; + + constrain_resize_hcenter (window, &info, ¤t, + (info.work_area_xinerama.width - current.width) / 2, + new); + } + else + { + constrain_move (window, &info, ¤t, + x_move_delta, y_move_delta, + new); + + current = *new; + + switch (x_direction) + { + case META_RESIZE_LEFT_OR_TOP: + constrain_resize_left (window, &info, ¤t, + x_delta, new); + break; + case META_RESIZE_CENTER: + constrain_resize_hcenter (window, &info, ¤t, + x_delta, new); + break; + case META_RESIZE_RIGHT_OR_BOTTOM: + constrain_resize_right (window, &info, ¤t, + x_delta, new); + break; + } + + switch (y_direction) + { + case META_RESIZE_LEFT_OR_TOP: + constrain_resize_top (window, &info, ¤t, + y_delta, new); + break; + case META_RESIZE_CENTER: + constrain_resize_vcenter (window, &info, ¤t, + y_delta, new); + break; + case META_RESIZE_RIGHT_OR_BOTTOM: + constrain_resize_bottom (window, &info, ¤t, + y_delta, new); + break; + } + } + + current = *new; + + /* Now we have to sort out the aspect ratio */ +#define FLOOR(value, base) ( ((int) ((value) / (base))) * (base) ) + { + /* + * width + * min_aspect <= -------- <= max_aspect + * height + */ + double min_aspect, max_aspect; + int width, height; + + min_aspect = window->size_hints.min_aspect.x / (double) window->size_hints.min_aspect.y; + max_aspect = window->size_hints.max_aspect.x / (double) window->size_hints.max_aspect.y; + + width = current.width; + height = current.height; + + /* Use the standard cut-and-pasted-between-every-WM code: */ + if (min_aspect * height > width) + { + delta = FLOOR (height - width * min_aspect, window->size_hints.height_inc); + if (height - delta >= window->size_hints.min_height) + height -= delta; + else + { + delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc); + if (width + delta <= window->size_hints.max_width) + width += delta; + } + } + + if (max_aspect * height < width) + { + delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc); + if (width - delta >= window->size_hints.min_width) + width -= delta; + else + { + delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc); + if (height + delta <= window->size_hints.max_height) + height += delta; + } + } + + /* Convert into terms of the direction of resize and reapply the + * earlier constraints; this means aspect ratio becomes the + * least-important of the constraints. If we wanted aspect to be + * the most important, we could just not do this next bit. + */ + + if (current.width != width) + { + x_delta = width - current.width; /* positive delta to increase width */ + switch (x_direction) + { + case META_RESIZE_LEFT_OR_TOP: + constrain_resize_left (window, &info, ¤t, + - x_delta, new); + break; + case META_RESIZE_CENTER: + constrain_resize_hcenter (window, &info, ¤t, + x_delta, new); + break; + case META_RESIZE_RIGHT_OR_BOTTOM: + constrain_resize_right (window, &info, ¤t, + x_delta, new); + break; + } + } + + if (current.height != height) + { + y_delta = height - current.height; /* positive to increase height */ + + switch (y_direction) + { + case META_RESIZE_LEFT_OR_TOP: + constrain_resize_top (window, &info, ¤t, + - y_delta, new); + break; + case META_RESIZE_CENTER: + constrain_resize_vcenter (window, &info, ¤t, + y_delta, new); + break; + case META_RESIZE_RIGHT_OR_BOTTOM: + constrain_resize_bottom (window, &info, ¤t, + y_delta, new); + break; + } + } + } +#undef FLOOR + +} + +MetaResizeDirection +meta_x_direction_from_gravity (int gravity) +{ + switch (gravity) + { + case EastGravity: + case NorthEastGravity: + case SouthEastGravity: + return META_RESIZE_LEFT_OR_TOP; + break; + + case WestGravity: + case NorthWestGravity: + case SouthWestGravity: + case StaticGravity: + return META_RESIZE_RIGHT_OR_BOTTOM; + break; + + default: + return META_RESIZE_CENTER; + break; + } +} + +MetaResizeDirection +meta_y_direction_from_gravity (int gravity) +{ + switch (gravity) + { + case SouthGravity: + case SouthWestGravity: + case SouthEastGravity: + return META_RESIZE_LEFT_OR_TOP; + break; + + case NorthGravity: + case NorthWestGravity: + case NorthEastGravity: + case StaticGravity: + return META_RESIZE_RIGHT_OR_BOTTOM; + break; + + default: + return META_RESIZE_CENTER; + } +} diff --git a/src/constraints.h b/src/constraints.h index e901cf251..de8555b27 100644 --- a/src/constraints.h +++ b/src/constraints.h @@ -24,13 +24,28 @@ #include "util.h" #include "window.h" +#include "frame.h" + +typedef enum +{ + META_RESIZE_LEFT_OR_TOP, + META_RESIZE_CENTER, + META_RESIZE_RIGHT_OR_BOTTOM +} MetaResizeDirection; void meta_window_constrain (MetaWindow *window, MetaFrameGeometry *fgeom, - int resize_gravity, const MetaRectangle *orig, + int x_move_delta, + int y_move_delta, + MetaResizeDirection x_direction, + int x_delta, + MetaResizeDirection y_direction, + int y_delta, MetaRectangle *new); +MetaResizeDirection meta_x_direction_from_gravity (int gravity); +MetaResizeDirection meta_y_direction_from_gravity (int gravity); #endif /* META_CONSTRAINTS_H */ diff --git a/src/display.c b/src/display.c index 3c6b7a474..78ade79ca 100644 --- a/src/display.c +++ b/src/display.c @@ -633,6 +633,8 @@ meta_display_close (MetaDisplay *display) if (display->leader_window != None) XDestroyWindow (display->xdisplay, display->leader_window); + XFlush (display->xdisplay); + #ifndef USE_GDK_DISPLAY meta_event_queue_free (display->events); XCloseDisplay (display->xdisplay); diff --git a/src/effects.c b/src/effects.c index e486edbe7..4b73e4d5f 100644 --- a/src/effects.c +++ b/src/effects.c @@ -19,10 +19,23 @@ * 02111-1307, USA. */ +#include #include "effects.h" #include "display.h" #include "ui.h" +#ifdef HAVE_SHAPE +#include +#endif + +typedef enum +{ + META_ANIMATION_DRAW_ROOT, + META_ANIMATION_WINDOW_WIREFRAME, + META_ANIMATION_WINDOW_OPAQUE + +} MetaAnimationStyle; + typedef struct { MetaScreen *screen; @@ -41,11 +54,14 @@ typedef struct /* used instead of the global flag, since * we don't want to change midstream. */ - gboolean use_opaque; + MetaAnimationStyle style; - /* For wireframe */ + /* For wireframe drawn on root window */ GC gc; + /* For wireframe window */ + Window wireframe_xwindow; + /* For opaque */ MetaImageWindow *image_window; GdkPixbuf *orig_pixbuf; @@ -54,6 +70,61 @@ typedef struct } BoxAnimationContext; +static void +update_wireframe_window (MetaDisplay *display, + Window xwindow, + const MetaRectangle *rect) +{ + XMoveResizeWindow (display->xdisplay, + xwindow, + rect->x, rect->y, + rect->width, rect->height); + +#ifdef HAVE_SHAPE + +#define OUTLINE_WIDTH 3 + + if (rect->width > OUTLINE_WIDTH * 2 && + rect->height > OUTLINE_WIDTH * 2) + { + XRectangle xrect; + Region inner_xregion; + Region outer_xregion; + + inner_xregion = XCreateRegion (); + outer_xregion = XCreateRegion (); + + xrect.x = 0; + xrect.y = 0; + xrect.width = rect->width; + xrect.height = rect->height; + + XUnionRectWithRegion (&xrect, outer_xregion, outer_xregion); + + xrect.x += OUTLINE_WIDTH; + xrect.y += OUTLINE_WIDTH; + xrect.width -= OUTLINE_WIDTH * 2; + xrect.height -= OUTLINE_WIDTH * 2; + + XUnionRectWithRegion (&xrect, inner_xregion, inner_xregion); + + XSubtractRegion (outer_xregion, inner_xregion, outer_xregion); + + XShapeCombineRegion (display->xdisplay, xwindow, + ShapeBounding, 0, 0, outer_xregion, ShapeSet); + + XDestroyRegion (outer_xregion); + XDestroyRegion (inner_xregion); + } + else + { + /* Unset the shape */ + XShapeCombineMask (display->xdisplay, xwindow, + ShapeBounding, 0, 0, None, ShapeSet); + } +#endif +} + static gboolean effects_draw_box_animation_timeout (BoxAnimationContext *context) { @@ -64,7 +135,7 @@ effects_draw_box_animation_timeout (BoxAnimationContext *context) if (!context->first_time) { - if (!context->use_opaque) + if (context->style == META_ANIMATION_DRAW_ROOT) { /* Restore the previously drawn background */ XDrawRectangle (context->screen->display->xdisplay, @@ -94,18 +165,23 @@ effects_draw_box_animation_timeout (BoxAnimationContext *context) if (elapsed > context->millisecs_duration) { /* All done */ - if (context->use_opaque) + if (context->style == META_ANIMATION_WINDOW_OPAQUE) { g_object_unref (G_OBJECT (context->orig_pixbuf)); meta_image_window_free (context->image_window); } - else + else if (context->style == META_ANIMATION_DRAW_ROOT) { meta_display_ungrab (context->screen->display); meta_ui_pop_delay_exposes (context->screen->ui); XFreeGC (context->screen->display->xdisplay, context->gc); } + else if (context->style == META_ANIMATION_WINDOW_WIREFRAME) + { + XDestroyWindow (context->screen->display->xdisplay, + context->wireframe_xwindow); + } g_free (context); return FALSE; @@ -130,7 +206,7 @@ effects_draw_box_animation_timeout (BoxAnimationContext *context) context->last_rect = draw_rect; - if (context->use_opaque) + if (context->style == META_ANIMATION_WINDOW_OPAQUE) { GdkPixbuf *scaled; @@ -174,7 +250,7 @@ effects_draw_box_animation_timeout (BoxAnimationContext *context) g_object_unref (G_OBJECT (scaled)); } } - else + else if (context->style == META_ANIMATION_DRAW_ROOT) { /* Draw the rectangle */ XDrawRectangle (context->screen->display->xdisplay, @@ -183,6 +259,12 @@ effects_draw_box_animation_timeout (BoxAnimationContext *context) draw_rect.x, draw_rect.y, draw_rect.width, draw_rect.height); } + else if (context->style == META_ANIMATION_WINDOW_WIREFRAME) + { + update_wireframe_window (context->screen->display, + context->wireframe_xwindow, + &draw_rect); + } /* kick changes onto the server */ XFlush (context->screen->display->xdisplay); @@ -198,7 +280,7 @@ effects_draw_box_animation_timeout (BoxAnimationContext *context) * and unmapping of windows that's going on. */ -static gboolean use_opaque_animations = FALSE; +static MetaAnimationStyle animation_style = META_ANIMATION_WINDOW_WIREFRAME; void meta_effects_draw_box_animation (MetaScreen *screen, @@ -225,9 +307,14 @@ meta_effects_draw_box_animation (MetaScreen *screen, context->end_rect = *destination_rect; context->anim_type = anim_type; - context->use_opaque = use_opaque_animations; + context->style = animation_style; - if (context->use_opaque) +#ifndef HAVE_SHAPE + if (context->style == META_ANIMATION_WINDOW_WIREFRAME) + context->style = META_ANIMATION_DRAW_ROOT; +#endif + + if (context->style == META_ANIMATION_WINDOW_OPAQUE) { GdkPixbuf *pix; @@ -242,7 +329,7 @@ meta_effects_draw_box_animation (MetaScreen *screen, if (pix == NULL) { /* Fall back to wireframe */ - context->use_opaque = FALSE; + context->style = META_ANIMATION_WINDOW_WIREFRAME; } else { @@ -258,7 +345,36 @@ meta_effects_draw_box_animation (MetaScreen *screen, } /* Not an else, so that fallback works */ - if (!context->use_opaque) + if (context->style == META_ANIMATION_WINDOW_WIREFRAME) + { + XSetWindowAttributes attrs; + + attrs.override_redirect = True; + attrs.background_pixel = BlackPixel (screen->display->xdisplay, + screen->number); + + context->wireframe_xwindow = XCreateWindow (screen->display->xdisplay, + screen->xroot, + initial_rect->x, + initial_rect->y, + initial_rect->width, + initial_rect->height, + 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + CWOverrideRedirect | CWBackPixel, + &attrs); + + update_wireframe_window (screen->display, + context->wireframe_xwindow, + initial_rect); + + XMapWindow (screen->display->xdisplay, + context->wireframe_xwindow); + } + + if (context->style == META_ANIMATION_DRAW_ROOT) { XGCValues gc_values; diff --git a/src/keybindings.c b/src/keybindings.c index 5d365858b..bb8b44c15 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -729,6 +729,8 @@ meta_display_init_keys (MetaDisplay *display) void meta_display_shutdown_keys (MetaDisplay *display) { + /* Note that display->xdisplay is invalid in this function */ + meta_prefs_remove_listener (bindings_changed_callback, display); if (display->keymap) @@ -1995,12 +1997,8 @@ switch_to_workspace (MetaDisplay *display, if (move_window) { - /* Lamely rely on prepend */ - g_assert (move_window->workspaces->data == workspace); - - while (move_window->workspaces->next) /* while list size > 1 */ - meta_workspace_remove_window (move_window->workspaces->next->data, - move_window); + /* Removes window from other spaces */ + meta_window_change_workspace (move_window, workspace); } } diff --git a/src/screen.c b/src/screen.c index c2b74970e..999d3dbda 100644 --- a/src/screen.c +++ b/src/screen.c @@ -535,6 +535,7 @@ meta_screen_free (MetaScreen *screen) g_free (screen->screen_name); g_free (screen); + XFlush (display->xdisplay); meta_display_ungrab (display); } diff --git a/src/window.c b/src/window.c index e02d6beea..c446591eb 100644 --- a/src/window.c +++ b/src/window.c @@ -627,6 +627,24 @@ meta_window_new (MetaDisplay *display, Window xwindow, outer.height >= workarea.height) meta_window_maximize (window); } + /* Assume screen-size undecorated windows are intended to be + * fullscreen + */ + else if (window->has_fullscreen_func && !window->decorated && + !window->maximized) + { + const MetaXineramaScreenInfo *xinerama; + MetaRectangle outer; + + xinerama = meta_screen_get_xinerama_for_window (window->screen, + window); + + meta_window_get_outer_rect (window, &outer); + + if (outer.width >= xinerama->width && + outer.height >= xinerama->height) + meta_window_make_fullscreen (window); + } /* Sync stack changes */ meta_stack_thaw (window->screen->stack); @@ -1661,6 +1679,7 @@ meta_window_shade (MetaWindow *window) "Shading %s\n", window->desc); if (!window->shaded) { +#if 0 if (window->mapped) { /* Animation */ @@ -1682,7 +1701,8 @@ meta_window_shade (MetaWindow *window) META_SHADE_ANIMATION_LENGTH, META_BOX_ANIM_SLIDE_UP); } - +#endif + window->shaded = TRUE; meta_window_queue_move_resize (window); @@ -2785,25 +2805,35 @@ meta_window_focus (MetaWindow *window, void meta_window_change_workspace (MetaWindow *window, MetaWorkspace *workspace) -{ +{ + GList *next; + meta_verbose ("Changing window %s to workspace %d\n", window->desc, meta_workspace_index (workspace)); /* See if we're already on this space. If not, make sure we are */ if (g_list_find (window->workspaces, workspace) == NULL) - { - meta_workspace_add_window (workspace, window); - } + meta_workspace_add_window (workspace, window); + /* unstick if stuck */ if (window->on_all_workspaces) meta_window_unstick (window); - /* Lamely rely on prepend */ - g_assert (window->workspaces->data == workspace); - /* Remove from all other spaces */ - while (window->workspaces->next) /* while list size > 1 */ - meta_workspace_remove_window (window->workspaces->next->data, window); + next = window->workspaces; + while (next != NULL) + { + MetaWorkspace *remove; + remove = next->data; + next = next->next; + + if (remove != workspace) + meta_workspace_remove_window (remove, window); + } + + /* list size == 1 */ + g_assert (window->workspaces != NULL); + g_assert (window->workspaces->next == NULL); } void