diff options
Diffstat (limited to 'src/core/constraints.c')
-rw-r--r-- | src/core/constraints.c | 1878 |
1 files changed, 0 insertions, 1878 deletions
diff --git a/src/core/constraints.c b/src/core/constraints.c deleted file mode 100644 index 4b1d95338..000000000 --- a/src/core/constraints.c +++ /dev/null @@ -1,1878 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Mutter size/position constraints */ - -/* - * Copyright (C) 2002, 2003 Red Hat, Inc. - * Copyright (C) 2003, 2004 Rob Adams - * Copyright (C) 2005, 2006 Elijah Newren - * - * 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, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/constraints.h" - -#include <stdlib.h> -#include <math.h> - -#include "backends/meta-backend-private.h" -#include "backends/meta-logical-monitor.h" -#include "backends/meta-monitor-manager-private.h" -#include "core/boxes-private.h" -#include "core/meta-workspace-manager-private.h" -#include "core/place.h" -#include "core/workspace-private.h" -#include "meta/prefs.h" - -#if 0 - // This is the short and sweet version of how to hack on this file; see - // doc/how-constraints-works.txt for the gory details. The basics of - // understanding this file can be shown by the steps needed to add a new - // constraint, which are: - // 1) Add a new entry in the ConstraintPriority enum; higher values - // have higher priority - // 2) Write a new function following the format of the example below, - // "constrain_whatever". - // 3) Add your function to the all_constraints and all_constraint_names - // arrays (the latter of which is for debugging purposes) - // - // An example constraint function, constrain_whatever: - // - // /* constrain_whatever does the following: - // * Quits (returning true) if priority is higher than PRIORITY_WHATEVER - // * If check_only is TRUE - // * Returns whether the constraint is satisfied or not - // * otherwise - // * Enforces the constraint - // * Note that the value of PRIORITY_WHATEVER is centralized with the - // * priorities of other constraints in the definition of ConstrainPriority - // * for easier maintenance and shuffling of priorities. - // */ - // static gboolean - // constrain_whatever (MetaWindow *window, - // ConstraintInfo *info, - // ConstraintPriority priority, - // gboolean check_only) - // { - // if (priority > PRIORITY_WHATEVER) - // return TRUE; - // - // /* Determine whether constraint applies; note that if the constraint - // * cannot possibly be satisfied, constraint_applies should be set to - // * false. If we don't do this, all constraints with a lesser priority - // * will be dropped along with this one, and we'd rather apply as many as - // * possible. - // */ - // if (!constraint_applies) - // return TRUE; - // - // /* Determine whether constraint is already satisfied; if we're only - // * checking the status of whether the constraint is satisfied, we end - // * here. - // */ - // if (check_only || constraint_already_satisfied) - // return constraint_already_satisfied; - // - // /* Enforce constraints */ - // return TRUE; /* Note that we exited early if check_only is FALSE; also, - // * we know we can return TRUE here because we exited early - // * if the constraint could not be satisfied; not that the - // * return value is heeded in this case... - // */ - // } -#endif - -typedef enum -{ - PRIORITY_MINIMUM = 0, /* Dummy value used for loop start = min(all priorities) */ - PRIORITY_ASPECT_RATIO = 0, - PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_MONITOR = 0, - PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1, - PRIORITY_SIZE_HINTS_INCREMENTS = 1, - PRIORITY_MAXIMIZATION = 2, - PRIORITY_TILING = 2, - PRIORITY_FULLSCREEN = 2, - PRIORITY_SIZE_HINTS_LIMITS = 3, - PRIORITY_TITLEBAR_VISIBLE = 4, - PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4, - PRIORITY_CUSTOM_RULE = 4, - PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */ -} ConstraintPriority; - -typedef enum -{ - ACTION_MOVE, - ACTION_RESIZE, - ACTION_MOVE_AND_RESIZE -} ActionType; - -typedef struct -{ - MetaRectangle orig; - MetaRectangle current; - MetaRectangle temporary; - int rel_x; - int rel_y; - ActionType action_type; - gboolean is_user_action; - - /* I know that these two things probably look similar at first, but they - * have much different uses. See doc/how-constraints-works.txt for for - * explanation of the differences and similarity between resize_gravity - * and fixed_directions - */ - MetaGravity resize_gravity; - FixedDirections fixed_directions; - - /* work_area_monitor - current monitor region minus struts - * entire_monitor - current monitor, including strut regions - */ - MetaRectangle work_area_monitor; - MetaRectangle entire_monitor; - - /* Spanning rectangles for the non-covered (by struts) region of the - * screen and also for just the current monitor - */ - GList *usable_screen_region; - GList *usable_monitor_region; - - MetaMoveResizeFlags flags; -} ConstraintInfo; - -static gboolean do_screen_and_monitor_relative_constraints (MetaWindow *window, - GList *region_spanning_rectangles, - ConstraintInfo *info, - gboolean check_only); -static gboolean constrain_custom_rule (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_modal_dialog (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_maximization (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_tiling (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_fullscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_size_increments (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_size_limits (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_aspect_ratio (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_to_single_monitor (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_fully_onscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_titlebar_visible (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); -static gboolean constrain_partially_onscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); - -static void setup_constraint_info (ConstraintInfo *info, - MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity resize_gravity, - const MetaRectangle *orig, - MetaRectangle *new); -static void place_window_if_needed (MetaWindow *window, - ConstraintInfo *info); -static void update_onscreen_requirements (MetaWindow *window, - ConstraintInfo *info); - -typedef gboolean (* ConstraintFunc) (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only); - -typedef struct { - ConstraintFunc func; - const char* name; -} Constraint; - -static const Constraint all_constraints[] = { - {constrain_custom_rule, "constrain_custom_rule"}, - {constrain_modal_dialog, "constrain_modal_dialog"}, - {constrain_maximization, "constrain_maximization"}, - {constrain_tiling, "constrain_tiling"}, - {constrain_fullscreen, "constrain_fullscreen"}, - {constrain_size_increments, "constrain_size_increments"}, - {constrain_size_limits, "constrain_size_limits"}, - {constrain_aspect_ratio, "constrain_aspect_ratio"}, - {constrain_to_single_monitor, "constrain_to_single_monitor"}, - {constrain_fully_onscreen, "constrain_fully_onscreen"}, - {constrain_titlebar_visible, "constrain_titlebar_visible"}, - {constrain_partially_onscreen, "constrain_partially_onscreen"}, - {NULL, NULL} -}; - -static gboolean -do_all_constraints (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - const Constraint *constraint; - gboolean satisfied; - - constraint = &all_constraints[0]; - satisfied = TRUE; - while (constraint->func != NULL) - { - satisfied = satisfied && - (*constraint->func) (window, info, priority, check_only); - - if (!check_only) - { - /* Log how the constraint modified the position */ - meta_topic (META_DEBUG_GEOMETRY, - "info->current is %d,%d +%d,%d after %s", - info->current.x, info->current.y, - info->current.width, info->current.height, - constraint->name); - } - else if (!satisfied) - { - /* Log which constraint was not satisfied */ - meta_topic (META_DEBUG_GEOMETRY, - "constraint %s not satisfied.", - constraint->name); - return FALSE; - } - ++constraint; - } - - return TRUE; -} - -void -meta_window_constrain (MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity resize_gravity, - const MetaRectangle *orig, - MetaRectangle *new, - MetaRectangle *temporary, - int *rel_x, - int *rel_y) -{ - ConstraintInfo info; - ConstraintPriority priority = PRIORITY_MINIMUM; - gboolean satisfied = FALSE; - - meta_topic (META_DEBUG_GEOMETRY, - "Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d", - window->desc, - orig->x, orig->y, orig->width, orig->height, - new->x, new->y, new->width, new->height); - - setup_constraint_info (&info, - window, - flags, - resize_gravity, - orig, - new); - place_window_if_needed (window, &info); - - while (!satisfied && priority <= PRIORITY_MAXIMUM) { - gboolean check_only = TRUE; - - /* Individually enforce all the high-enough priority constraints */ - do_all_constraints (window, &info, priority, !check_only); - - /* Check if all high-enough priority constraints are simultaneously - * satisfied - */ - satisfied = do_all_constraints (window, &info, priority, check_only); - - /* Drop the least important constraints if we can't satisfy them all */ - priority++; - } - - /* Make sure we use the constrained position */ - *new = info.current; - *temporary = info.temporary; - *rel_x = info.rel_x; - *rel_y = info.rel_y; - - /* We may need to update window->require_fully_onscreen, - * window->require_on_single_monitor, and perhaps other quantities - * if this was a user move or user move-and-resize operation. - */ - update_onscreen_requirements (window, &info); -} - -static void -setup_constraint_info (ConstraintInfo *info, - MetaWindow *window, - MetaMoveResizeFlags flags, - MetaGravity resize_gravity, - const MetaRectangle *orig, - MetaRectangle *new) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaLogicalMonitor *logical_monitor; - MetaWorkspace *cur_workspace; - - info->orig = *orig; - info->current = *new; - info->temporary = *orig; - info->rel_x = 0; - info->rel_y = 0; - info->flags = flags; - - if (info->current.width < 1) - info->current.width = 1; - if (info->current.height < 1) - info->current.height = 1; - - if (flags & META_MOVE_RESIZE_MOVE_ACTION && flags & META_MOVE_RESIZE_RESIZE_ACTION) - info->action_type = ACTION_MOVE_AND_RESIZE; - else if (flags & META_MOVE_RESIZE_RESIZE_ACTION) - info->action_type = ACTION_RESIZE; - else if (flags & META_MOVE_RESIZE_MOVE_ACTION) - info->action_type = ACTION_MOVE; - else - g_error ("BAD, BAD developer! No treat for you! (Fix your calls to " - "meta_window_move_resize_internal())."); - - info->is_user_action = (flags & META_MOVE_RESIZE_USER_ACTION); - - info->resize_gravity = resize_gravity; - - /* FIXME: fixed_directions might be more sane if we (a) made it - * depend on the grab_op type instead of current amount of movement - * (thus implying that it only has effect when user_action is true, - * and (b) ignored it for aspect ratio windows -- at least in those - * cases where both directions do actually change size. - */ - info->fixed_directions = FIXED_DIRECTION_NONE; - /* If x directions don't change but either y direction does */ - if ( orig->x == new->x && orig->x + orig->width == new->x + new->width && - (orig->y != new->y || orig->y + orig->height != new->y + new->height)) - { - info->fixed_directions = FIXED_DIRECTION_X; - } - /* If y directions don't change but either x direction does */ - if ( orig->y == new->y && orig->y + orig->height == new->y + new->height && - (orig->x != new->x || orig->x + orig->width != new->x + new->width )) - { - info->fixed_directions = FIXED_DIRECTION_Y; - } - /* The point of fixed directions is just that "move to nearest valid - * position" is sometimes a poorer choice than "move to nearest - * valid position but only change this coordinate" for windows the - * user is explicitly moving. This isn't ever true for things that - * aren't explicit user interaction, though, so just clear it out. - */ - if (!info->is_user_action) - info->fixed_directions = FIXED_DIRECTION_NONE; - - logical_monitor = - meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, - &info->current); - meta_window_get_work_area_for_logical_monitor (window, - logical_monitor, - &info->work_area_monitor); - - if (window->fullscreen && meta_window_has_fullscreen_monitors (window)) - { - info->entire_monitor = window->fullscreen_monitors.top->rect; - meta_rectangle_union (&info->entire_monitor, - &window->fullscreen_monitors.bottom->rect, - &info->entire_monitor); - meta_rectangle_union (&info->entire_monitor, - &window->fullscreen_monitors.left->rect, - &info->entire_monitor); - meta_rectangle_union (&info->entire_monitor, - &window->fullscreen_monitors.right->rect, - &info->entire_monitor); - } - else - { - info->entire_monitor = logical_monitor->rect; - if (window->fullscreen) - meta_window_adjust_fullscreen_monitor_rect (window, &info->entire_monitor); - } - - cur_workspace = window->display->workspace_manager->active_workspace; - info->usable_screen_region = - meta_workspace_get_onscreen_region (cur_workspace); - info->usable_monitor_region = - meta_workspace_get_onmonitor_region (cur_workspace, logical_monitor); - - /* Log all this information for debugging */ - meta_topic (META_DEBUG_GEOMETRY, - "Setting up constraint info:\n" - " orig: %d,%d +%d,%d\n" - " new : %d,%d +%d,%d\n" - " action_type : %s\n" - " is_user_action : %s\n" - " resize_gravity : %s\n" - " fixed_directions: %s\n" - " work_area_monitor: %d,%d +%d,%d\n" - " entire_monitor : %d,%d +%d,%d", - info->orig.x, info->orig.y, info->orig.width, info->orig.height, - info->current.x, info->current.y, - info->current.width, info->current.height, - (info->action_type == ACTION_MOVE) ? "Move" : - (info->action_type == ACTION_RESIZE) ? "Resize" : - (info->action_type == ACTION_MOVE_AND_RESIZE) ? "Move&Resize" : - "Freakin' Invalid Stupid", - (info->is_user_action) ? "true" : "false", - meta_gravity_to_string (info->resize_gravity), - (info->fixed_directions == FIXED_DIRECTION_NONE) ? "None" : - (info->fixed_directions == FIXED_DIRECTION_X) ? "X fixed" : - (info->fixed_directions == FIXED_DIRECTION_Y) ? "Y fixed" : - "Freakin' Invalid Stupid", - info->work_area_monitor.x, info->work_area_monitor.y, - info->work_area_monitor.width, - info->work_area_monitor.height, - info->entire_monitor.x, info->entire_monitor.y, - info->entire_monitor.width, info->entire_monitor.height); -} - -static MetaRectangle * -get_start_rect_for_resize (MetaWindow *window, - ConstraintInfo *info) -{ - if (!info->is_user_action && info->action_type == ACTION_MOVE_AND_RESIZE) - return &info->current; - else - return &info->orig; -} - -static void -place_window_if_needed(MetaWindow *window, - ConstraintInfo *info) -{ - gboolean did_placement; - - /* Do placement if any, so we go ahead and apply position - * constraints in a move-only context. Don't place - * maximized/minimized/fullscreen windows until they are - * unmaximized, unminimized and unfullscreened. - */ - did_placement = FALSE; - if (!window->placed && - window->calc_placement && - !(window->maximized_horizontally || - window->maximized_vertically) && - !window->minimized && - !window->fullscreen) - { - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - MetaRectangle orig_rect; - MetaRectangle placed_rect; - MetaWorkspace *cur_workspace; - MetaLogicalMonitor *logical_monitor; - - placed_rect = (MetaRectangle) { - .x = window->rect.x, - .y = window->rect.y, - .width = info->current.width, - .height = info->current.height - }; - - orig_rect = info->orig; - - if (window->placement.rule) - { - meta_window_process_placement (window, - window->placement.rule, - &info->rel_x, &info->rel_y); - placed_rect.x = window->placement.rule->parent_rect.x + info->rel_x; - placed_rect.y = window->placement.rule->parent_rect.y + info->rel_y; - } - else - { - meta_window_place (window, orig_rect.x, orig_rect.y, - &placed_rect.x, &placed_rect.y); - } - did_placement = TRUE; - - /* placing the window may have changed the monitor. Find the - * new monitor and update the ConstraintInfo - */ - logical_monitor = - meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, - &placed_rect); - info->entire_monitor = logical_monitor->rect; - meta_window_get_work_area_for_logical_monitor (window, - logical_monitor, - &info->work_area_monitor); - cur_workspace = window->display->workspace_manager->active_workspace; - info->usable_monitor_region = - meta_workspace_get_onmonitor_region (cur_workspace, logical_monitor); - - info->current.x = placed_rect.x; - info->current.y = placed_rect.y; - - /* Since we just barely placed the window, there's no reason to - * consider any of the directions fixed. - */ - info->fixed_directions = FIXED_DIRECTION_NONE; - } - - if (window->placed || did_placement) - { - if (window->maximize_horizontally_after_placement || - window->maximize_vertically_after_placement) - { - /* define a sane saved_rect so that the user can unmaximize to - * something reasonable. - */ - if (info->current.width >= info->work_area_monitor.width) - { - info->current.width = .75 * info->work_area_monitor.width; - info->current.x = info->work_area_monitor.x + - .125 * info->work_area_monitor.width; - } - if (info->current.height >= info->work_area_monitor.height) - { - info->current.height = .75 * info->work_area_monitor.height; - info->current.y = info->work_area_monitor.y + - .083 * info->work_area_monitor.height; - } - - /* idle_move_resize() uses the unconstrained_rect, so make sure it - * uses the placed coordinates (bug #556696). - */ - window->unconstrained_rect = info->current; - - if (window->maximize_horizontally_after_placement || - window->maximize_vertically_after_placement) - meta_window_maximize_internal (window, - (window->maximize_horizontally_after_placement ? - META_MAXIMIZE_HORIZONTAL : 0 ) | - (window->maximize_vertically_after_placement ? - META_MAXIMIZE_VERTICAL : 0), &info->current); - - window->maximize_horizontally_after_placement = FALSE; - window->maximize_vertically_after_placement = FALSE; - } - if (window->minimize_after_placement) - { - meta_window_minimize (window); - window->minimize_after_placement = FALSE; - } - } -} - -static void -update_onscreen_requirements (MetaWindow *window, - ConstraintInfo *info) -{ - gboolean old; - - /* We only apply the various onscreen requirements to normal windows */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK) - return; - - /* We don't want to update the requirements for fullscreen windows; - * fullscreen windows are specially handled anyway, and it updating - * the requirements when windows enter fullscreen mode mess up the - * handling of the window when it leaves that mode (especially when - * the application sends a bunch of configurerequest events). See - * #353699. - */ - if (window->fullscreen) - return; - - /* USABILITY NOTE: Naturally, I only want the require_fully_onscreen, - * require_on_single_monitor, and require_titlebar_visible flags to - * *become false* due to user interactions (which is allowed since - * certain constraints are ignored for user interactions regardless of - * the setting of these flags). However, whether to make these flags - * *become true* due to just an application interaction is a little - * trickier. It's possible that users may find not doing that strange - * since two application interactions that resize in opposite ways don't - * necessarily end up cancelling--but it may also be strange for the user - * to have an application resize the window so that it's onscreen, the - * user forgets about it, and then later the app is able to resize itself - * off the screen. Anyway, for now, I think the latter is the more - * problematic case but this may need to be revisited. - */ - - /* Update whether we want future constraint runs to require the - * window to be on fully onscreen. - */ - old = window->require_fully_onscreen; - window->require_fully_onscreen = - meta_rectangle_contained_in_region (info->usable_screen_region, - &info->current); - if (old != window->require_fully_onscreen) - meta_topic (META_DEBUG_GEOMETRY, - "require_fully_onscreen for %s toggled to %s", - window->desc, - window->require_fully_onscreen ? "TRUE" : "FALSE"); - - /* Update whether we want future constraint runs to require the - * window to be on a single monitor. - */ - old = window->require_on_single_monitor; - window->require_on_single_monitor = - meta_rectangle_contained_in_region (info->usable_monitor_region, - &info->current); - if (old != window->require_on_single_monitor) - meta_topic (META_DEBUG_GEOMETRY, - "require_on_single_monitor for %s toggled to %s", - window->desc, - window->require_on_single_monitor ? "TRUE" : "FALSE"); - - /* Update whether we want future constraint runs to require the - * titlebar to be visible. - */ - if (window->frame && window->decorated) - { - MetaRectangle titlebar_rect, frame_rect; - - meta_window_get_titlebar_rect (window, &titlebar_rect); - meta_window_get_frame_rect (window, &frame_rect); - - /* translate into screen coordinates */ - titlebar_rect.x = frame_rect.x; - titlebar_rect.y = frame_rect.y; - - old = window->require_titlebar_visible; - window->require_titlebar_visible = - meta_rectangle_overlaps_with_region (info->usable_screen_region, - &titlebar_rect); - if (old != window->require_titlebar_visible) - meta_topic (META_DEBUG_GEOMETRY, - "require_titlebar_visible for %s toggled to %s", - window->desc, - window->require_titlebar_visible ? "TRUE" : "FALSE"); - } -} - -static inline void -get_size_limits (MetaWindow *window, - MetaRectangle *min_size, - MetaRectangle *max_size) -{ - /* We pack the results into MetaRectangle structs just for convenience; we - * don't actually use the position of those rects. - */ - min_size->x = min_size->y = max_size->x = max_size->y = 0; - min_size->width = window->size_hints.min_width; - min_size->height = window->size_hints.min_height; - max_size->width = window->size_hints.max_width; - max_size->height = window->size_hints.max_height; - - meta_window_client_rect_to_frame_rect (window, min_size, min_size); - meta_window_client_rect_to_frame_rect (window, max_size, max_size); -} - -static void -placement_rule_flip_horizontally (MetaPlacementRule *placement_rule) -{ - if (placement_rule->anchor & META_PLACEMENT_ANCHOR_LEFT) - { - placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_LEFT; - placement_rule->anchor |= META_PLACEMENT_ANCHOR_RIGHT; - } - else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_RIGHT) - { - placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_RIGHT; - placement_rule->anchor |= META_PLACEMENT_ANCHOR_LEFT; - } - - if (placement_rule->gravity & META_PLACEMENT_GRAVITY_LEFT) - { - placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_LEFT; - placement_rule->gravity |= META_PLACEMENT_GRAVITY_RIGHT; - } - else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_RIGHT) - { - placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_RIGHT; - placement_rule->gravity |= META_PLACEMENT_GRAVITY_LEFT; - } -} - -static void -placement_rule_flip_vertically (MetaPlacementRule *placement_rule) -{ - if (placement_rule->anchor & META_PLACEMENT_ANCHOR_TOP) - { - placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_TOP; - placement_rule->anchor |= META_PLACEMENT_ANCHOR_BOTTOM; - } - else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_BOTTOM) - { - placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_BOTTOM; - placement_rule->anchor |= META_PLACEMENT_ANCHOR_TOP; - } - - if (placement_rule->gravity & META_PLACEMENT_GRAVITY_TOP) - { - placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_TOP; - placement_rule->gravity |= META_PLACEMENT_GRAVITY_BOTTOM; - } - else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_BOTTOM) - { - placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_BOTTOM; - placement_rule->gravity |= META_PLACEMENT_GRAVITY_TOP; - } -} - -static void -try_flip_window_position (MetaWindow *window, - ConstraintInfo *info, - MetaPlacementRule *placement_rule, - MetaPlacementConstraintAdjustment constraint_adjustment, - int parent_x, - int parent_y, - MetaRectangle *rect, - int *rel_x, - int *rel_y, - MetaRectangle *intersection) -{ - MetaPlacementRule flipped_rule = *placement_rule; - MetaRectangle flipped_rect; - MetaRectangle flipped_intersection; - int flipped_rel_x; - int flipped_rel_y; - - switch (constraint_adjustment) - { - case META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X: - placement_rule_flip_horizontally (&flipped_rule); - break; - case META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y: - placement_rule_flip_vertically (&flipped_rule); - break; - - default: - g_assert_not_reached (); - } - - flipped_rect = info->current; - meta_window_process_placement (window, &flipped_rule, - &flipped_rel_x, &flipped_rel_y); - flipped_rect.x = parent_x + flipped_rel_x; - flipped_rect.y = parent_y + flipped_rel_y; - meta_rectangle_intersect (&flipped_rect, &info->work_area_monitor, - &flipped_intersection); - - if ((constraint_adjustment == META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X && - flipped_intersection.width == flipped_rect.width) || - (constraint_adjustment == META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y && - flipped_intersection.height == flipped_rect.height)) - { - *placement_rule = flipped_rule; - *rect = flipped_rect; - *rel_x = flipped_rel_x; - *rel_y = flipped_rel_y; - *intersection = flipped_intersection; - } -} - -static gboolean -is_custom_rule_satisfied (MetaRectangle *rect, - MetaPlacementRule *placement_rule, - MetaRectangle *intersection) -{ - uint32_t x_constrain_actions, y_constrain_actions; - - x_constrain_actions = (META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X | - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X); - y_constrain_actions = (META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y | - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y); - if ((placement_rule->constraint_adjustment & x_constrain_actions && - rect->width != intersection->width) || - (placement_rule->constraint_adjustment & y_constrain_actions && - rect->height != intersection->height)) - return FALSE; - else - return TRUE; -} - -static gboolean -constrain_custom_rule (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaPlacementRule *placement_rule; - MetaRectangle intersection; - gboolean constraint_satisfied; - MetaRectangle temporary_rect; - MetaRectangle adjusted_unconstrained; - int adjusted_rel_x; - int adjusted_rel_y; - MetaPlacementRule current_rule; - MetaWindow *parent; - int parent_x, parent_y; - - if (priority > PRIORITY_CUSTOM_RULE) - return TRUE; - - placement_rule = meta_window_get_placement_rule (window); - if (!placement_rule) - return TRUE; - - parent = meta_window_get_transient_for (window); - if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED) - { - placement_rule->parent_rect.x = parent->rect.x; - placement_rule->parent_rect.y = parent->rect.y; - } - parent_x = placement_rule->parent_rect.x; - parent_y = placement_rule->parent_rect.y; - - /* - * Calculate the temporary position, meaning a position that will be - * applied if the new constrained position requires asynchronous - * configuration of the window. This happens for example when the parent - * moves, causing this window to change relative position, meaning it can - * only have its newly constrained position applied when the configuration is - * acknowledged. - */ - - switch (window->placement.state) - { - case META_PLACEMENT_STATE_UNCONSTRAINED: - temporary_rect = info->current; - break; - case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: - case META_PLACEMENT_STATE_CONSTRAINED_PENDING: - case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: - case META_PLACEMENT_STATE_INVALIDATED: - temporary_rect = (MetaRectangle) { - .x = parent->rect.x + window->placement.current.rel_x, - .y = parent->rect.y + window->placement.current.rel_y, - .width = info->current.width, - .height = info->current.height, - }; - break; - } - - /* - * Calculate an adjusted current position. Depending on the rule - * configuration and placement state, this may result in window being - * reconstrained. - */ - - adjusted_unconstrained = temporary_rect; - - if (window->placement.state == META_PLACEMENT_STATE_INVALIDATED || - window->placement.state == META_PLACEMENT_STATE_UNCONSTRAINED || - (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED && - placement_rule->is_reactive)) - { - meta_window_process_placement (window, placement_rule, - &adjusted_rel_x, - &adjusted_rel_y); - adjusted_unconstrained.x = parent_x + adjusted_rel_x; - adjusted_unconstrained.y = parent_y + adjusted_rel_y; - } - else if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_PENDING) - { - adjusted_rel_x = window->placement.pending.rel_x; - adjusted_rel_y = window->placement.pending.rel_y; - adjusted_unconstrained.x = window->placement.pending.x; - adjusted_unconstrained.y = window->placement.pending.y; - } - else - { - adjusted_rel_x = window->placement.current.rel_x; - adjusted_rel_y = window->placement.current.rel_y; - } - - meta_rectangle_intersect (&adjusted_unconstrained, &info->work_area_monitor, - &intersection); - - constraint_satisfied = (meta_rectangle_equal (&info->current, - &adjusted_unconstrained) && - is_custom_rule_satisfied (&adjusted_unconstrained, - placement_rule, - &intersection)); - - if (check_only) - return constraint_satisfied; - - info->current = adjusted_unconstrained; - info->rel_x = adjusted_rel_x; - info->rel_y = adjusted_rel_y; - info->temporary = temporary_rect; - - switch (window->placement.state) - { - case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: - if (!placement_rule->is_reactive) - return TRUE; - break; - case META_PLACEMENT_STATE_CONSTRAINED_PENDING: - case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: - return TRUE; - case META_PLACEMENT_STATE_UNCONSTRAINED: - case META_PLACEMENT_STATE_INVALIDATED: - break; - } - - if (constraint_satisfied) - goto done; - - /* - * Process the placement rule in order either until constraints are - * satisfied, or there are no more rules to process. - */ - - current_rule = *placement_rule; - - if (info->current.width != intersection.width && - (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X)) - { - try_flip_window_position (window, info, ¤t_rule, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X, - parent_x, - parent_y, - &info->current, - &info->rel_x, - &info->rel_y, - &intersection); - } - if (info->current.height != intersection.height && - (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y)) - { - try_flip_window_position (window, info, ¤t_rule, - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y, - parent_x, - parent_y, - &info->current, - &info->rel_x, - &info->rel_y, - &intersection); - } - - meta_rectangle_intersect (&info->current, &info->work_area_monitor, - &intersection); - constraint_satisfied = is_custom_rule_satisfied (&info->current, - placement_rule, - &intersection); - - if (constraint_satisfied) - goto done; - - if (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X) - { - int current_x2; - int work_area_monitor_x2; - int new_x; - - current_x2 = info->current.x + info->current.width; - work_area_monitor_x2 = (info->work_area_monitor.x + - info->work_area_monitor.width); - - if (current_x2 > work_area_monitor_x2) - { - new_x = MAX (info->work_area_monitor.x, - work_area_monitor_x2 - info->current.width); - } - else if (info->current.x < info->work_area_monitor.x) - { - new_x = info->work_area_monitor.x; - } - else - { - new_x = info->current.x; - } - - info->rel_x += new_x - info->current.x; - info->current.x = new_x; - } - if (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y) - { - int current_y2; - int work_area_monitor_y2; - int new_y; - - current_y2 = info->current.y + info->current.height; - work_area_monitor_y2 = (info->work_area_monitor.y + - info->work_area_monitor.height); - - if (current_y2 > work_area_monitor_y2) - { - new_y = MAX (info->work_area_monitor.y, - work_area_monitor_y2 - info->current.height); - } - else if (info->current.y < info->work_area_monitor.y) - { - new_y = info->work_area_monitor.y; - } - else - { - new_y = info->current.y; - } - - info->rel_y += new_y - info->current.y; - info->current.y = new_y; - } - - meta_rectangle_intersect (&info->current, &info->work_area_monitor, - &intersection); - constraint_satisfied = is_custom_rule_satisfied (&info->current, - placement_rule, - &intersection); - - if (constraint_satisfied) - goto done; - - if (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X) - { - int new_x; - new_x = intersection.x; - info->current.width = intersection.width; - info->rel_x += new_x - info->current.x; - info->current.x = new_x; - } - if (current_rule.constraint_adjustment & - META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y) - { - int new_y; - new_y = intersection.y; - info->current.height = intersection.height; - info->rel_y += new_y - info->current.y; - info->current.y = new_y; - } - -done: - window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_PENDING; - - window->placement.pending.rel_x = info->rel_x; - window->placement.pending.rel_y = info->rel_y; - window->placement.pending.x = info->current.x; - window->placement.pending.y = info->current.y; - - return TRUE; -} - -static gboolean -constrain_modal_dialog (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - int x, y; - MetaWindow *parent = meta_window_get_transient_for (window); - MetaRectangle child_rect, parent_rect; - gboolean constraint_already_satisfied; - - if (!parent || - !meta_window_is_attached_dialog (window) || - meta_window_get_placement_rule (window)) - return TRUE; - - /* We want to center the dialog on the parent, including the decorations - for both of them. info->current is in client X window coordinates, so we need - to convert them to frame coordinates, apply the centering and then - convert back to client. - */ - - child_rect = info->current; - - meta_window_get_frame_rect (parent, &parent_rect); - - child_rect.x = parent_rect.x + (parent_rect.width / 2 - child_rect.width / 2); - child_rect.y = parent_rect.y + (parent_rect.height / 2 - child_rect.height / 2); - x = child_rect.x; - y = child_rect.y; - - constraint_already_satisfied = (x == info->current.x) && (y == info->current.y); - - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - info->current.y = y; - info->current.x = x; - /* The calculated position above may need adjustment to make sure the - * dialog does not end up partially off-screen */ - return do_screen_and_monitor_relative_constraints (window, - info->usable_screen_region, - info, - check_only); -} - -static gboolean -constrain_maximization (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; - MetaRectangle target_size; - MetaRectangle min_size, max_size; - gboolean hminbad, vminbad; - gboolean horiz_equal, vert_equal; - gboolean constraint_already_satisfied; - - if (priority > PRIORITY_MAXIMIZATION) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't */ - if ((!window->maximized_horizontally && !window->maximized_vertically) || - META_WINDOW_TILED_SIDE_BY_SIDE (window)) - return TRUE; - - /* Calculate target_size = maximized size of (window + frame) */ - if (META_WINDOW_TILED_MAXIMIZED (window)) - { - meta_window_get_tile_area (window, window->tile_mode, &target_size); - } - else if (META_WINDOW_MAXIMIZED (window)) - { - target_size = info->work_area_monitor; - } - else - { - /* Amount of maximization possible in a single direction depends - * on which struts could occlude the window given its current - * position. For example, a vertical partial strut on the right - * is only relevant for a horizontally maximized window when the - * window is at a vertical position where it could be occluded - * by that partial strut. - */ - MetaDirection direction; - GSList *active_workspace_struts; - - if (window->maximized_horizontally) - direction = META_DIRECTION_HORIZONTAL; - else - direction = META_DIRECTION_VERTICAL; - active_workspace_struts = workspace_manager->active_workspace->all_struts; - - target_size = info->current; - meta_rectangle_expand_to_avoiding_struts (&target_size, - &info->entire_monitor, - direction, - active_workspace_struts); - } - - /* Check min size constraints; max size constraints are ignored for maximized - * windows, as per bug 327543. - */ - get_size_limits (window, &min_size, &max_size); - hminbad = target_size.width < min_size.width && window->maximized_horizontally; - vminbad = target_size.height < min_size.height && window->maximized_vertically; - if (hminbad || vminbad) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - horiz_equal = target_size.x == info->current.x && - target_size.width == info->current.width; - vert_equal = target_size.y == info->current.y && - target_size.height == info->current.height; - constraint_already_satisfied = - (horiz_equal || !window->maximized_horizontally) && - (vert_equal || !window->maximized_vertically); - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - if (window->maximized_horizontally) - { - info->current.x = target_size.x; - info->current.width = target_size.width; - } - if (window->maximized_vertically) - { - info->current.y = target_size.y; - info->current.height = target_size.height; - } - return TRUE; -} - -static gboolean -constrain_tiling (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaRectangle target_size; - MetaRectangle min_size, max_size; - gboolean hminbad, vminbad; - gboolean horiz_equal, vert_equal; - gboolean constraint_already_satisfied; - - if (priority > PRIORITY_TILING) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't */ - if (!META_WINDOW_TILED_SIDE_BY_SIDE (window)) - return TRUE; - - /* Calculate target_size - as the tile previews need this as well, we - * use an external function for the actual calculation - */ - meta_window_get_tile_area (window, window->tile_mode, &target_size); - - /* Check min size constraints; max size constraints are ignored as for - * maximized windows. - */ - get_size_limits (window, &min_size, &max_size); - hminbad = target_size.width < min_size.width; - vminbad = target_size.height < min_size.height; - if (hminbad || vminbad) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - horiz_equal = target_size.x == info->current.x && - target_size.width == info->current.width; - vert_equal = target_size.y == info->current.y && - target_size.height == info->current.height; - constraint_already_satisfied = horiz_equal && vert_equal; - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - info->current.x = target_size.x; - info->current.width = target_size.width; - info->current.y = target_size.y; - info->current.height = target_size.height; - - return TRUE; -} - - -static gboolean -constrain_fullscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaRectangle min_size, max_size, monitor; - gboolean too_big, too_small, constraint_already_satisfied; - - if (priority > PRIORITY_FULLSCREEN) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't */ - if (!window->fullscreen) - return TRUE; - - monitor = info->entire_monitor; - - get_size_limits (window, &min_size, &max_size); - too_big = !meta_rectangle_could_fit_rect (&monitor, &min_size); - too_small = !meta_rectangle_could_fit_rect (&max_size, &monitor); - if (too_big || too_small) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - constraint_already_satisfied = - meta_rectangle_equal (&info->current, &monitor); - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - info->current = monitor; - return TRUE; -} - -static gboolean -constrain_size_increments (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - int bh, hi, bw, wi, extra_height, extra_width; - int new_width, new_height; - gboolean constraint_already_satisfied; - MetaRectangle *start_rect; - MetaRectangle client_rect; - - if (priority > PRIORITY_SIZE_HINTS_INCREMENTS) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't */ - if (META_WINDOW_MAXIMIZED (window) || window->fullscreen || - META_WINDOW_TILED_SIDE_BY_SIDE (window) || - info->action_type == ACTION_MOVE) - return TRUE; - - meta_window_frame_rect_to_client_rect (window, &info->current, &client_rect); - - /* Determine whether constraint is already satisfied; exit if it is */ - bh = window->size_hints.base_height; - hi = window->size_hints.height_inc; - bw = window->size_hints.base_width; - wi = window->size_hints.width_inc; - extra_height = (client_rect.height - bh) % hi; - extra_width = (client_rect.width - bw) % wi; - /* ignore size increments for maximized windows */ - if (window->maximized_horizontally) - extra_width *= 0; - if (window->maximized_vertically) - extra_height *= 0; - /* constraint is satisfied iff there is no extra height or width */ - constraint_already_satisfied = - (extra_height == 0 && extra_width == 0); - - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - new_width = client_rect.width - extra_width; - new_height = client_rect.height - extra_height; - - /* Adjusting down instead of up (as done in the above two lines) may - * violate minimum size constraints; fix the adjustment if this - * happens. - */ - if (new_width < window->size_hints.min_width) - new_width += ((window->size_hints.min_width - new_width)/wi + 1)*wi; - if (new_height < window->size_hints.min_height) - new_height += ((window->size_hints.min_height - new_height)/hi + 1)*hi; - - { - client_rect.width = new_width; - client_rect.height = new_height; - meta_window_client_rect_to_frame_rect (window, &client_rect, &client_rect); - new_width = client_rect.width; - new_height = client_rect.height; - } - - start_rect = get_start_rect_for_resize (window, info); - - /* Resize to the new size */ - meta_rectangle_resize_with_gravity (start_rect, - &info->current, - info->resize_gravity, - new_width, - new_height); - return TRUE; -} - -static gboolean -constrain_size_limits (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaRectangle min_size, max_size; - gboolean too_big, too_small, constraint_already_satisfied; - int new_width, new_height; - MetaRectangle *start_rect; - - if (priority > PRIORITY_SIZE_HINTS_LIMITS) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't. - * - * Note: The old code didn't apply this constraint for fullscreen or - * maximized windows--but that seems odd to me. *shrug* - */ - if (info->action_type == ACTION_MOVE) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - get_size_limits (window, &min_size, &max_size); - /* We ignore max-size limits for maximized windows; see #327543 */ - if (window->maximized_horizontally) - max_size.width = MAX (max_size.width, info->current.width); - if (window->maximized_vertically) - max_size.height = MAX (max_size.height, info->current.height); - too_small = !meta_rectangle_could_fit_rect (&info->current, &min_size); - too_big = !meta_rectangle_could_fit_rect (&max_size, &info->current); - constraint_already_satisfied = !too_big && !too_small; - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - new_width = CLAMP (info->current.width, min_size.width, max_size.width); - new_height = CLAMP (info->current.height, min_size.height, max_size.height); - - start_rect = get_start_rect_for_resize (window, info); - - meta_rectangle_resize_with_gravity (start_rect, - &info->current, - info->resize_gravity, - new_width, - new_height); - return TRUE; -} - -static gboolean -constrain_aspect_ratio (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - double minr, maxr; - gboolean constraints_are_inconsistent, constraint_already_satisfied; - int fudge, new_width, new_height; - double best_width, best_height; - double alt_width, alt_height; - MetaRectangle *start_rect; - MetaRectangle client_rect; - - if (priority > PRIORITY_ASPECT_RATIO) - return TRUE; - - /* Determine whether constraint applies; exit if it doesn't. */ - minr = window->size_hints.min_aspect.x / - (double)window->size_hints.min_aspect.y; - maxr = window->size_hints.max_aspect.x / - (double)window->size_hints.max_aspect.y; - constraints_are_inconsistent = minr > maxr; - if (constraints_are_inconsistent || - META_WINDOW_MAXIMIZED (window) || window->fullscreen || - META_WINDOW_TILED_SIDE_BY_SIDE (window) || - info->action_type == ACTION_MOVE) - return TRUE; - - /* Determine whether constraint is already satisfied; exit if it is. We - * need the following to hold: - * - * width - * minr <= ------ <= maxr - * height - * - * But we need to allow for some slight fudging since width and height - * are integers instead of floating point numbers (this is particularly - * important when minr == maxr), so we allow width and height to be off - * a little bit from strictly satisfying these equations. For just one - * sided resizing, we have to make the fudge factor a little bigger - * because of how meta_rectangle_resize_with_gravity treats those as - * being a resize increment (FIXME: I should handle real resize - * increments better here...) - */ - switch (info->resize_gravity) - { - case META_GRAVITY_WEST: - case META_GRAVITY_NORTH: - case META_GRAVITY_SOUTH: - case META_GRAVITY_EAST: - fudge = 2; - break; - - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_CENTER: - case META_GRAVITY_NORTH_EAST: - case META_GRAVITY_SOUTH_EAST: - case META_GRAVITY_STATIC: - default: - fudge = 1; - break; - } - - meta_window_frame_rect_to_client_rect (window, &info->current, &client_rect); - - constraint_already_satisfied = - client_rect.width - (client_rect.height * minr ) > -minr*fudge && - client_rect.width - (client_rect.height * maxr ) < maxr*fudge; - if (check_only || constraint_already_satisfied) - return constraint_already_satisfied; - - /*** Enforce constraint ***/ - new_width = client_rect.width; - new_height = client_rect.height; - - switch (info->resize_gravity) - { - case META_GRAVITY_WEST: - case META_GRAVITY_EAST: - /* Yeah, I suck for doing implicit rounding -- sue me */ - new_height = CLAMP (new_height, new_width / maxr, new_width / minr); - break; - - case META_GRAVITY_NORTH: - case META_GRAVITY_SOUTH: - /* Yeah, I suck for doing implicit rounding -- sue me */ - new_width = CLAMP (new_width, new_height * minr, new_height * maxr); - break; - - case META_GRAVITY_NORTH_WEST: - case META_GRAVITY_SOUTH_WEST: - case META_GRAVITY_CENTER: - case META_GRAVITY_NORTH_EAST: - case META_GRAVITY_SOUTH_EAST: - case META_GRAVITY_STATIC: - default: - /* Find what width would correspond to new_height, and what height would - * correspond to new_width */ - alt_width = CLAMP (new_width, new_height * minr, new_height * maxr); - alt_height = CLAMP (new_height, new_width / maxr, new_width / minr); - - /* The line connecting the points (alt_width, new_height) and - * (new_width, alt_height) provide a range of - * valid-for-the-aspect-ratio-constraint sizes. We want the - * size in that range closest to the value requested, i.e. the - * point on the line which is closest to the point (new_width, - * new_height) - */ - meta_rectangle_find_linepoint_closest_to_point (alt_width, new_height, - new_width, alt_height, - new_width, new_height, - &best_width, &best_height); - - /* Yeah, I suck for doing implicit rounding -- sue me */ - new_width = best_width; - new_height = best_height; - - break; - } - - { - client_rect.width = new_width; - client_rect.height = new_height; - meta_window_client_rect_to_frame_rect (window, &client_rect, &client_rect); - new_width = client_rect.width; - new_height = client_rect.height; - } - - start_rect = get_start_rect_for_resize (window, info); - - meta_rectangle_resize_with_gravity (start_rect, - &info->current, - info->resize_gravity, - new_width, - new_height); - - return TRUE; -} - -static gboolean -do_screen_and_monitor_relative_constraints ( - MetaWindow *window, - GList *region_spanning_rectangles, - ConstraintInfo *info, - gboolean check_only) -{ - gboolean exit_early = FALSE, constraint_satisfied; - MetaRectangle how_far_it_can_be_smushed, min_size, max_size; - -#ifdef WITH_VERBOSE_MODE - if (meta_is_verbose ()) - { - /* First, log some debugging information */ - char spanning_region[1 + 28 * g_list_length (region_spanning_rectangles)]; - - meta_topic (META_DEBUG_GEOMETRY, - "screen/monitor constraint; region_spanning_rectangles: %s", - meta_rectangle_region_to_string (region_spanning_rectangles, ", ", - spanning_region)); - } -#endif - - /* Determine whether constraint applies; exit if it doesn't */ - how_far_it_can_be_smushed = info->current; - get_size_limits (window, &min_size, &max_size); - - if (info->action_type != ACTION_MOVE) - { - if (!(info->fixed_directions & FIXED_DIRECTION_X)) - how_far_it_can_be_smushed.width = min_size.width; - - if (!(info->fixed_directions & FIXED_DIRECTION_Y)) - how_far_it_can_be_smushed.height = min_size.height; - } - if (!meta_rectangle_could_fit_in_region (region_spanning_rectangles, - &how_far_it_can_be_smushed)) - exit_early = TRUE; - - /* Determine whether constraint is already satisfied; exit if it is */ - constraint_satisfied = - meta_rectangle_contained_in_region (region_spanning_rectangles, - &info->current); - if (exit_early || constraint_satisfied || check_only) - return constraint_satisfied; - - /* Enforce constraint */ - - /* Clamp rectangle size for resize or move+resize actions */ - if (info->action_type != ACTION_MOVE) - meta_rectangle_clamp_to_fit_into_region (region_spanning_rectangles, - info->fixed_directions, - &info->current, - &min_size); - - if (info->is_user_action && info->action_type == ACTION_RESIZE) - /* For user resize, clip to the relevant region */ - meta_rectangle_clip_to_region (region_spanning_rectangles, - info->fixed_directions, - &info->current); - else - /* For everything else, shove the rectangle into the relevant region */ - meta_rectangle_shove_into_region (region_spanning_rectangles, - info->fixed_directions, - &info->current); - - return TRUE; -} - -static gboolean -constrain_to_single_monitor (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - MetaBackend *backend = meta_get_backend (); - MetaMonitorManager *monitor_manager = - meta_backend_get_monitor_manager (backend); - - if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_MONITOR) - return TRUE; - - /* Exit early if we know the constraint won't apply--note that this constraint - * is only meant for normal windows (e.g. we don't want docks to be shoved - * "onscreen" by their own strut) and we can't apply it to frameless windows - * or else users will be unable to move windows such as XMMS across monitors. - */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - meta_monitor_manager_get_num_logical_monitors (monitor_manager) == 1 || - !window->require_on_single_monitor || - !window->frame || - info->is_user_action || - meta_window_get_placement_rule (window)) - return TRUE; - - /* Have a helper function handle the constraint for us */ - return do_screen_and_monitor_relative_constraints (window, - info->usable_monitor_region, - info, - check_only); -} - -static gboolean -constrain_fully_onscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA) - return TRUE; - - /* Exit early if we know the constraint won't apply--note that this constraint - * is only meant for normal windows (e.g. we don't want docks to be shoved - * "onscreen" by their own strut). - */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - window->fullscreen || - !window->require_fully_onscreen || - info->is_user_action || - meta_window_get_placement_rule (window)) - return TRUE; - - /* Have a helper function handle the constraint for us */ - return do_screen_and_monitor_relative_constraints (window, - info->usable_screen_region, - info, - check_only); -} - -static gboolean -constrain_titlebar_visible (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - gboolean unconstrained_user_action; - gboolean retval; - int bottom_amount; - int horiz_amount_offscreen, vert_amount_offscreen; - int horiz_amount_onscreen, vert_amount_onscreen; - - if (priority > PRIORITY_TITLEBAR_VISIBLE) - return TRUE; - - /* Allow the titlebar beyond the top of the screen only if the user wasn't - * clicking on the frame to start the move. - */ - unconstrained_user_action = - info->is_user_action && !window->display->grab_frame_action; - - /* Exit early if we know the constraint won't apply--note that this constraint - * is only meant for normal windows (e.g. we don't want docks to be shoved - * "onscreen" by their own strut). - */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - window->fullscreen || - !window->require_titlebar_visible || - unconstrained_user_action || - meta_window_get_placement_rule (window)) - return TRUE; - - /* Determine how much offscreen things are allowed. We first need to - * figure out how much must remain on the screen. For that, we use 25% - * window width/height but clamp to the range of (10,75) pixels. This is - * somewhat of a seat of my pants random guess at what might look good. - * Then, the amount that is allowed off is just the window size minus - * this amount (but no less than 0 for tiny windows). - */ - horiz_amount_onscreen = info->current.width / 4; - vert_amount_onscreen = info->current.height / 4; - horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75); - vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75); - horiz_amount_offscreen = info->current.width - horiz_amount_onscreen; - vert_amount_offscreen = info->current.height - vert_amount_onscreen; - horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0); - vert_amount_offscreen = MAX (vert_amount_offscreen, 0); - /* Allow the titlebar to touch the bottom panel; If there is no titlebar, - * require vert_amount to remain on the screen. - */ - if (window->frame) - { - MetaFrameBorders borders; - meta_frame_calc_borders (window->frame, &borders); - - bottom_amount = info->current.height - borders.visible.top; - vert_amount_onscreen = borders.visible.top; - } - else - bottom_amount = vert_amount_offscreen; - - /* Extend the region, have a helper function handle the constraint, - * then return the region to its original size. - */ - meta_rectangle_expand_region_conditionally (info->usable_screen_region, - horiz_amount_offscreen, - horiz_amount_offscreen, - 0, /* Don't let titlebar off */ - bottom_amount, - horiz_amount_onscreen, - vert_amount_onscreen); - retval = - do_screen_and_monitor_relative_constraints (window, - info->usable_screen_region, - info, - check_only); - meta_rectangle_expand_region_conditionally (info->usable_screen_region, - -horiz_amount_offscreen, - -horiz_amount_offscreen, - 0, /* Don't let titlebar off */ - -bottom_amount, - horiz_amount_onscreen, - vert_amount_onscreen); - - return retval; -} - -static gboolean -constrain_partially_onscreen (MetaWindow *window, - ConstraintInfo *info, - ConstraintPriority priority, - gboolean check_only) -{ - gboolean retval; - int top_amount, bottom_amount; - int horiz_amount_offscreen, vert_amount_offscreen; - int horiz_amount_onscreen, vert_amount_onscreen; - - if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA) - return TRUE; - - /* Exit early if we know the constraint won't apply--note that this constraint - * is only meant for normal windows (e.g. we don't want docks to be shoved - * "onscreen" by their own strut). - */ - if (window->type == META_WINDOW_DESKTOP || - window->type == META_WINDOW_DOCK || - meta_window_get_placement_rule (window)) - return TRUE; - - /* Determine how much offscreen things are allowed. We first need to - * figure out how much must remain on the screen. For that, we use 25% - * window width/height but clamp to the range of (10,75) pixels. This is - * somewhat of a seat of my pants random guess at what might look good. - * Then, the amount that is allowed off is just the window size minus - * this amount (but no less than 0 for tiny windows). - */ - horiz_amount_onscreen = info->current.width / 4; - vert_amount_onscreen = info->current.height / 4; - horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75); - vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75); - horiz_amount_offscreen = info->current.width - horiz_amount_onscreen; - vert_amount_offscreen = info->current.height - vert_amount_onscreen; - horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0); - vert_amount_offscreen = MAX (vert_amount_offscreen, 0); - top_amount = vert_amount_offscreen; - /* Allow the titlebar to touch the bottom panel; If there is no titlebar, - * require vert_amount to remain on the screen. - */ - if (window->frame) - { - MetaFrameBorders borders; - meta_frame_calc_borders (window->frame, &borders); - - bottom_amount = info->current.height - borders.visible.top; - vert_amount_onscreen = borders.visible.top; - } - else - bottom_amount = vert_amount_offscreen; - - /* Extend the region, have a helper function handle the constraint, - * then return the region to its original size. - */ - meta_rectangle_expand_region_conditionally (info->usable_screen_region, - horiz_amount_offscreen, - horiz_amount_offscreen, - top_amount, - bottom_amount, - horiz_amount_onscreen, - vert_amount_onscreen); - retval = - do_screen_and_monitor_relative_constraints (window, - info->usable_screen_region, - info, - check_only); - meta_rectangle_expand_region_conditionally (info->usable_screen_region, - -horiz_amount_offscreen, - -horiz_amount_offscreen, - -top_amount, - -bottom_amount, - horiz_amount_onscreen, - vert_amount_onscreen); - - return retval; -} |