constraints.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity size/position constraints */
00004 
00005 /*
00006  * Copyright (C) 2002, 2003 Red Hat, Inc.
00007  * Copyright (C) 2003, 2004 Rob Adams
00008  * Copyright (C) 2005, 2006 Elijah Newren
00009  *
00010  * This program is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU General Public License as
00012  * published by the Free Software Foundation; either version 2 of the
00013  * License, or (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00023  * 02111-1307, USA.
00024  */
00025 
00026 #include <config.h>
00027 #include "constraints.h"
00028 #include "workspace.h"
00029 #include "place.h"
00030 
00031 #include <stdlib.h>
00032 #include <math.h>
00033 
00034 #if 0
00035  // This is the short and sweet version of how to hack on this file; see
00036  // doc/how-constraints-works.txt for the gory details.  The basics of
00037  // understanding this file can be shown by the steps needed to add a new
00038  // constraint, which are:
00039  //   1) Add a new entry in the ConstraintPriority enum; higher values
00040  //      have higher priority
00041  //   2) Write a new function following the format of the example below,
00042  //      "constrain_whatever".
00043  //   3) Add your function to the all_constraints and all_constraint_names
00044  //      arrays (the latter of which is for debugging purposes)
00045  // 
00046  // An example constraint function, constrain_whatever:
00047  //
00048  // /* constrain_whatever does the following:
00049  //  *   Quits (returning true) if priority is higher than PRIORITY_WHATEVER
00050  //  *   If check_only is TRUE
00051  //  *     Returns whether the constraint is satisfied or not
00052  //  *   otherwise
00053  //  *     Enforces the constraint
00054  //  * Note that the value of PRIORITY_WHATEVER is centralized with the
00055  //  * priorities of other constraints in the definition of ConstrainPriority
00056  //  * for easier maintenance and shuffling of priorities.
00057  //  */
00058  // static gboolean
00059  // constrain_whatever (MetaWindow         *window,
00060  //                     ConstraintInfo     *info,
00061  //                     ConstraintPriority  priority,
00062  //                     gboolean            check_only)
00063  // {
00064  //   if (priority > PRIORITY_WHATEVER)
00065  //     return TRUE;
00066  //
00067  //   /* Determine whether constraint applies; note that if the constraint
00068  //    * cannot possibly be satisfied, constraint_applies should be set to
00069  //    * false.  If we don't do this, all constraints with a lesser priority
00070  //    * will be dropped along with this one, and we'd rather apply as many as
00071  //    * possible.
00072  //    */
00073  //   if (!constraint_applies)
00074  //     return TRUE;
00075  //
00076  //   /* Determine whether constraint is already satisfied; if we're only
00077  //    * checking the status of whether the constraint is satisfied, we end
00078  //    * here.
00079  //    */
00080  //   if (check_only || constraint_already_satisfied)
00081  //     return constraint_already_satisfied;
00082  //
00083  //   /* Enforce constraints */
00084  //   return TRUE;  /* Note that we exited early if check_only is FALSE; also,
00085  //                  * we know we can return TRUE here because we exited early
00086  //                  * if the constraint could not be satisfied; not that the
00087  //                  * return value is heeded in this case...
00088  //                  */
00089  // }
00090 #endif
00091 
00092 typedef enum
00093 {
00094   PRIORITY_MINIMUM = 0, /* Dummy value used for loop start = min(all priorities) */
00095   PRIORITY_ASPECT_RATIO = 0,
00096   PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA = 0,
00097   PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
00098   PRIORITY_SIZE_HINTS_INCREMENTS = 1,
00099   PRIORITY_MAXIMIZATION = 2,
00100   PRIORITY_FULLSCREEN = 2,
00101   PRIORITY_SIZE_HINTS_LIMITS = 3,
00102   PRIORITY_TITLEBAR_VISIBLE = 4,
00103   PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4,
00104   PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */
00105 } ConstraintPriority;
00106 
00107 typedef enum
00108 {
00109   ACTION_MOVE,
00110   ACTION_RESIZE,
00111   ACTION_MOVE_AND_RESIZE
00112 } ActionType;
00113 
00114 typedef struct
00115 {
00116   MetaRectangle        orig;
00117   MetaRectangle        current;
00118   MetaFrameGeometry   *fgeom;
00119   ActionType           action_type;
00120   gboolean             is_user_action;
00121 
00122   /* I know that these two things probably look similar at first, but they
00123    * have much different uses.  See doc/how-constraints-works.txt for for
00124    * explanation of the differences and similarity between resize_gravity
00125    * and fixed_directions
00126    */
00127   int                  resize_gravity;
00128   FixedDirections      fixed_directions;
00129 
00130   /* work_area_xinerama - current xinerama region minus struts
00131    * entire_xinerama    - current xienrama, including strut regions
00132    */
00133   MetaRectangle        work_area_xinerama;
00134   MetaRectangle        entire_xinerama;
00135 
00136   /* Spanning rectangles for the non-covered (by struts) region of the
00137    * screen and also for just the current xinerama
00138    */
00139   GList  *usable_screen_region;
00140   GList  *usable_xinerama_region;
00141 } ConstraintInfo;
00142 
00143 static gboolean constrain_maximization       (MetaWindow         *window,
00144                                               ConstraintInfo     *info,
00145                                               ConstraintPriority  priority,
00146                                               gboolean            check_only);
00147 static gboolean constrain_fullscreen         (MetaWindow         *window,
00148                                               ConstraintInfo     *info,
00149                                               ConstraintPriority  priority,
00150                                               gboolean            check_only);
00151 static gboolean constrain_size_increments    (MetaWindow         *window,
00152                                               ConstraintInfo     *info,
00153                                               ConstraintPriority  priority,
00154                                               gboolean            check_only);
00155 static gboolean constrain_size_limits        (MetaWindow         *window,
00156                                               ConstraintInfo     *info,
00157                                               ConstraintPriority  priority,
00158                                               gboolean            check_only);
00159 static gboolean constrain_aspect_ratio       (MetaWindow         *window,
00160                                               ConstraintInfo     *info,
00161                                               ConstraintPriority  priority,
00162                                               gboolean            check_only);
00163 static gboolean constrain_to_single_xinerama (MetaWindow         *window,
00164                                               ConstraintInfo     *info,
00165                                               ConstraintPriority  priority,
00166                                               gboolean            check_only);
00167 static gboolean constrain_fully_onscreen     (MetaWindow         *window,
00168                                               ConstraintInfo     *info,
00169                                               ConstraintPriority  priority,
00170                                               gboolean            check_only);
00171 static gboolean constrain_titlebar_visible   (MetaWindow         *window,
00172                                               ConstraintInfo     *info,
00173                                               ConstraintPriority  priority,
00174                                               gboolean            check_only);
00175 static gboolean constrain_partially_onscreen (MetaWindow         *window,
00176                                               ConstraintInfo     *info,
00177                                               ConstraintPriority  priority,
00178                                               gboolean            check_only);
00179 
00180 static void setup_constraint_info        (ConstraintInfo      *info,
00181                                           MetaWindow          *window,
00182                                           MetaFrameGeometry   *orig_fgeom,
00183                                           MetaMoveResizeFlags  flags,
00184                                           int                  resize_gravity,
00185                                           const MetaRectangle *orig,
00186                                           MetaRectangle       *new);
00187 static void place_window_if_needed       (MetaWindow     *window,
00188                                           ConstraintInfo *info);
00189 static void update_onscreen_requirements (MetaWindow     *window,
00190                                           ConstraintInfo *info);
00191 static void extend_by_frame              (MetaRectangle           *rect,
00192                                           const MetaFrameGeometry *fgeom);
00193 static void unextend_by_frame            (MetaRectangle           *rect,
00194                                           const MetaFrameGeometry *fgeom);
00195 static inline void get_size_limits       (const MetaWindow        *window,
00196                                           const MetaFrameGeometry *fgeom,
00197                                           gboolean include_frame,
00198                                           MetaRectangle *min_size,
00199                                           MetaRectangle *max_size);
00200 
00201 typedef gboolean (* ConstraintFunc) (MetaWindow         *window,
00202                                      ConstraintInfo     *info,
00203                                      ConstraintPriority  priority,
00204                                      gboolean            check_only);
00205 
00206 typedef struct {
00207   ConstraintFunc func;
00208   const char* name;
00209 } Constraint;
00210 
00211 static const Constraint all_constraints[] = {
00212   {constrain_maximization,       "constrain_maximization"},
00213   {constrain_fullscreen,         "constrain_fullscreen"},
00214   {constrain_size_increments,    "constrain_size_increments"},
00215   {constrain_size_limits,        "constrain_size_limits"},
00216   {constrain_aspect_ratio,       "constrain_aspect_ratio"},
00217   {constrain_to_single_xinerama, "constrain_to_single_xinerama"},
00218   {constrain_fully_onscreen,     "constrain_fully_onscreen"},
00219   {constrain_titlebar_visible,   "constrain_titlebar_visible"},
00220   {constrain_partially_onscreen, "constrain_partially_onscreen"},
00221   {NULL,                         NULL}
00222 };
00223 
00224 static gboolean
00225 do_all_constraints (MetaWindow         *window,
00226                     ConstraintInfo     *info,
00227                     ConstraintPriority  priority,
00228                     gboolean            check_only)
00229 {
00230   const Constraint *constraint;
00231   gboolean          satisfied;
00232 
00233   constraint = &all_constraints[0];
00234   satisfied = TRUE;
00235   while (constraint->func != NULL)
00236     {
00237       satisfied = satisfied &&
00238                   (*constraint->func) (window, info, priority, check_only);
00239 
00240       if (!check_only)
00241         {
00242           /* Log how the constraint modified the position */
00243           meta_topic (META_DEBUG_GEOMETRY,
00244                       "info->current is %d,%d +%d,%d after %s\n",
00245                       info->current.x, info->current.y, 
00246                       info->current.width, info->current.height,
00247                       constraint->name);
00248         }
00249       else if (!satisfied)
00250         {
00251           /* Log which constraint was not satisfied */
00252           meta_topic (META_DEBUG_GEOMETRY,
00253                       "constraint %s not satisfied.\n",
00254                       constraint->name);
00255           return FALSE;
00256         }
00257       ++constraint;
00258     }
00259 
00260   return TRUE;
00261 }
00262 
00263 void
00264 meta_window_constrain (MetaWindow          *window,
00265                        MetaFrameGeometry   *orig_fgeom,
00266                        MetaMoveResizeFlags  flags,
00267                        int                  resize_gravity,
00268                        const MetaRectangle *orig,
00269                        MetaRectangle       *new)
00270 {
00271   ConstraintInfo info;
00272   ConstraintPriority priority = PRIORITY_MINIMUM;
00273   gboolean satisfied = FALSE;
00274 
00275   /* WARNING: orig and new specify positions and sizes of the inner window,
00276    * not the outer.  This is a common gotcha since half the constraints
00277    * deal with inner window position/size and half deal with outer.  See
00278    * doc/how-constraints-works.txt for more information.
00279    */
00280   meta_topic (META_DEBUG_GEOMETRY,
00281               "Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d\n",
00282               window->desc,
00283               orig->x, orig->y, orig->width, orig->height,
00284               new->x,  new->y,  new->width,  new->height);
00285 
00286   setup_constraint_info (&info,
00287                          window, 
00288                          orig_fgeom, 
00289                          flags,
00290                          resize_gravity,
00291                          orig,
00292                          new);
00293   place_window_if_needed (window, &info);
00294 
00295   while (!satisfied && priority <= PRIORITY_MAXIMUM) {
00296     gboolean check_only = TRUE;
00297 
00298     /* Individually enforce all the high-enough priority constraints */
00299     do_all_constraints (window, &info, priority, !check_only);
00300 
00301     /* Check if all high-enough priority constraints are simultaneously 
00302      * satisfied
00303      */
00304     satisfied = do_all_constraints (window, &info, priority, check_only);
00305 
00306     /* Drop the least important constraints if we can't satisfy them all */
00307     priority++;
00308   }
00309 
00310   /* Make sure we use the constrained position */
00311   *new = info.current;
00312 
00313   /* We may need to update window->require_fully_onscreen,
00314    * window->require_on_single_xinerama, and perhaps other quantities
00315    * if this was a user move or user move-and-resize operation.
00316    */
00317   update_onscreen_requirements (window, &info);
00318 
00319   /* Ew, what an ugly way to do things.  Destructors (in a real OOP language,
00320    * not gobject-style--gobject would be more pain than it's worth) or
00321    * smart pointers would be so much nicer here.  *shrug*
00322    */
00323   if (!orig_fgeom)
00324     g_free (info.fgeom);
00325 }
00326 
00327 static void
00328 setup_constraint_info (ConstraintInfo      *info,
00329                        MetaWindow          *window,
00330                        MetaFrameGeometry   *orig_fgeom,
00331                        MetaMoveResizeFlags  flags,
00332                        int                  resize_gravity,
00333                        const MetaRectangle *orig,
00334                        MetaRectangle       *new)
00335 {
00336   const MetaXineramaScreenInfo *xinerama_info;
00337   MetaWorkspace *cur_workspace;
00338 
00339   info->orig    = *orig;
00340   info->current = *new;
00341 
00342   /* Create a fake frame geometry if none really exists */
00343   if (orig_fgeom && !window->fullscreen)
00344     info->fgeom = orig_fgeom;
00345   else
00346     info->fgeom = g_new0 (MetaFrameGeometry, 1);
00347 
00348   if (flags & META_IS_MOVE_ACTION && flags & META_IS_RESIZE_ACTION)
00349     info->action_type = ACTION_MOVE_AND_RESIZE;
00350   else if (flags & META_IS_RESIZE_ACTION)
00351     info->action_type = ACTION_RESIZE;
00352   else if (flags & META_IS_MOVE_ACTION)
00353     info->action_type = ACTION_MOVE;
00354   else
00355     g_error ("BAD, BAD developer!  No treat for you!  (Fix your calls to "
00356              "meta_window_move_resize_internal()).\n");
00357 
00358   info->is_user_action = (flags & META_IS_USER_ACTION);
00359 
00360   info->resize_gravity = resize_gravity;
00361 
00362   /* FIXME: fixed_directions might be more sane if we (a) made it
00363    * depend on the grab_op type instead of current amount of movement
00364    * (thus implying that it only has effect when user_action is true,
00365    * and (b) ignored it for aspect ratio windows -- at least in those
00366    * cases where both directions do actually change size.
00367    */
00368   info->fixed_directions = FIXED_DIRECTION_NONE;
00369   /* If x directions don't change but either y direction does */
00370   if ( orig->x == new->x && orig->x + orig->width  == new->x + new->width   &&
00371       (orig->y != new->y || orig->y + orig->height != new->y + new->height))
00372     {
00373       info->fixed_directions = FIXED_DIRECTION_X;
00374     }
00375   /* If y directions don't change but either x direction does */
00376   if ( orig->y == new->y && orig->y + orig->height == new->y + new->height  &&
00377       (orig->x != new->x || orig->x + orig->width  != new->x + new->width ))
00378     {
00379       info->fixed_directions = FIXED_DIRECTION_Y;
00380     }
00381   /* The point of fixed directions is just that "move to nearest valid
00382    * position" is sometimes a poorer choice than "move to nearest
00383    * valid position but only change this coordinate" for windows the
00384    * user is explicitly moving.  This isn't ever true for things that
00385    * aren't explicit user interaction, though, so just clear it out.
00386    */
00387   if (!info->is_user_action)
00388     info->fixed_directions = FIXED_DIRECTION_NONE;
00389 
00390   xinerama_info =
00391     meta_screen_get_xinerama_for_rect (window->screen, &info->current);
00392   meta_window_get_work_area_for_xinerama (window,
00393                                           xinerama_info->number,
00394                                           &info->work_area_xinerama);
00395   info->entire_xinerama = xinerama_info->rect;
00396 
00397   cur_workspace = window->screen->active_workspace;
00398   info->usable_screen_region   = 
00399     meta_workspace_get_onscreen_region (cur_workspace);
00400   info->usable_xinerama_region = 
00401     meta_workspace_get_onxinerama_region (cur_workspace, 
00402                                           xinerama_info->number);
00403 
00404   /* Workaround braindead legacy apps that don't know how to
00405    * fullscreen themselves properly.
00406    */
00407   if (meta_rectangle_equal (new, &xinerama_info->rect) &&
00408       window->has_fullscreen_func &&
00409       !window->fullscreen)
00410     {
00411       /*
00412       meta_topic (META_DEBUG_GEOMETRY,
00413       */
00414       meta_warning (
00415                   "Treating resize request of legacy application %s as a "
00416                   "fullscreen request\n",
00417                   window->desc);
00418       meta_window_make_fullscreen_internal (window);
00419     }
00420 
00421   /* Log all this information for debugging */
00422   meta_topic (META_DEBUG_GEOMETRY,
00423               "Setting up constraint info:\n"
00424               "  orig: %d,%d +%d,%d\n"
00425               "  new : %d,%d +%d,%d\n"
00426               "  fgeom: %d,%d,%d,%d\n"
00427               "  action_type     : %s\n"
00428               "  is_user_action  : %s\n"
00429               "  resize_gravity  : %s\n"
00430               "  fixed_directions: %s\n"
00431               "  work_area_xinerama: %d,%d +%d,%d\n"
00432               "  entire_xinerama   : %d,%d +%d,%d\n",
00433               info->orig.x, info->orig.y, info->orig.width, info->orig.height,
00434               info->current.x, info->current.y, 
00435                 info->current.width, info->current.height,
00436               info->fgeom->left_width, info->fgeom->right_width,
00437                 info->fgeom->top_height, info->fgeom->bottom_height,
00438               (info->action_type == ACTION_MOVE) ? "Move" :
00439                 (info->action_type == ACTION_RESIZE) ? "Resize" :
00440                 (info->action_type == ACTION_MOVE_AND_RESIZE) ? "Move&Resize" :
00441                 "Freakin' Invalid Stupid",
00442               (info->is_user_action) ? "true" : "false",
00443               meta_gravity_to_string (info->resize_gravity),
00444               (info->fixed_directions == FIXED_DIRECTION_NONE) ? "None" :
00445                 (info->fixed_directions == FIXED_DIRECTION_X) ? "X fixed" :
00446                 (info->fixed_directions == FIXED_DIRECTION_Y) ? "Y fixed" :
00447                 "Freakin' Invalid Stupid",
00448               info->work_area_xinerama.x, info->work_area_xinerama.y,
00449                 info->work_area_xinerama.width, 
00450                 info->work_area_xinerama.height,
00451               info->entire_xinerama.x, info->entire_xinerama.y,
00452                 info->entire_xinerama.width, info->entire_xinerama.height);
00453 }
00454 
00455 static void
00456 place_window_if_needed(MetaWindow     *window,
00457                        ConstraintInfo *info)
00458 {
00459   gboolean did_placement;
00460 
00461   /* Do placement if any, so we go ahead and apply position
00462    * constraints in a move-only context. Don't place
00463    * maximized/minimized/fullscreen windows until they are
00464    * unmaximized, unminimized and unfullscreened.
00465    */
00466   did_placement = FALSE;
00467   if (!window->placed &&
00468       window->calc_placement &&
00469       !(window->maximized_horizontally ||
00470         window->maximized_vertically) &&
00471       !window->minimized &&
00472       !window->fullscreen)
00473     {
00474       MetaRectangle placed_rect = info->orig;
00475       MetaWorkspace *cur_workspace;
00476       const MetaXineramaScreenInfo *xinerama_info;
00477 
00478       meta_window_place (window, info->fgeom, info->orig.x, info->orig.y,
00479                          &placed_rect.x, &placed_rect.y);
00480       did_placement = TRUE;
00481 
00482       /* placing the window may have changed the xinerama.  Find the
00483        * new xinerama and update the ConstraintInfo
00484        */
00485       xinerama_info =
00486         meta_screen_get_xinerama_for_rect (window->screen, &placed_rect);
00487       info->entire_xinerama = xinerama_info->rect;
00488       meta_window_get_work_area_for_xinerama (window,
00489                                               xinerama_info->number,
00490                                               &info->work_area_xinerama);
00491       cur_workspace = window->screen->active_workspace;
00492       info->usable_xinerama_region = 
00493         meta_workspace_get_onxinerama_region (cur_workspace, 
00494                                               xinerama_info->number);
00495 
00496 
00497       info->current.x = placed_rect.x;
00498       info->current.y = placed_rect.y;
00499 
00500       /* Since we just barely placed the window, there's no reason to
00501        * consider any of the directions fixed.
00502        */
00503       info->fixed_directions = FIXED_DIRECTION_NONE;
00504     }
00505 
00506   if (window->placed || did_placement)
00507     {
00508       if (window->maximize_horizontally_after_placement ||
00509           window->maximize_vertically_after_placement)
00510         {
00511           /* define a sane saved_rect so that the user can unmaximize to
00512            * something reasonable.
00513            */
00514           if (info->current.width >= info->work_area_xinerama.width)
00515             {
00516               info->current.width = .75 * info->work_area_xinerama.width;
00517               info->current.x = info->work_area_xinerama.x +
00518                        .125 * info->work_area_xinerama.width;
00519             }
00520           if (info->current.height >= info->work_area_xinerama.height)
00521             {
00522               info->current.height = .75 * info->work_area_xinerama.height;
00523               info->current.y = info->work_area_xinerama.y +
00524                        .083 * info->work_area_xinerama.height;
00525             }
00526 
00527           if (window->maximize_horizontally_after_placement ||
00528               window->maximize_vertically_after_placement)
00529             meta_window_maximize_internal (window,   
00530                 (window->maximize_horizontally_after_placement ?
00531                  META_MAXIMIZE_HORIZONTAL : 0 ) |
00532                 (window->maximize_vertically_after_placement ?
00533                  META_MAXIMIZE_VERTICAL : 0), &info->current);
00534 
00535           /* maximization may have changed frame geometry */
00536           if (window->frame && !window->fullscreen)
00537             meta_frame_calc_geometry (window->frame, info->fgeom);
00538 
00539           window->maximize_horizontally_after_placement = FALSE;
00540           window->maximize_vertically_after_placement = FALSE;
00541         }
00542       if (window->minimize_after_placement)
00543         {
00544           meta_window_minimize (window);
00545           window->minimize_after_placement = FALSE;
00546         }
00547     }
00548 }
00549 
00550 static void
00551 update_onscreen_requirements (MetaWindow     *window,
00552                               ConstraintInfo *info)
00553 {
00554   gboolean old;
00555 
00556   /* We only apply the various onscreen requirements to normal windows */
00557   if (window->type == META_WINDOW_DESKTOP ||
00558       window->type == META_WINDOW_DOCK)
00559     return;
00560 
00561   /* We don't want to update the requirements for fullscreen windows;
00562    * fullscreen windows are specially handled anyway, and it updating
00563    * the requirements when windows enter fullscreen mode mess up the
00564    * handling of the window when it leaves that mode (especially when
00565    * the application sends a bunch of configurerequest events).  See
00566    * #353699.
00567    */
00568   if (window->fullscreen)
00569     return;
00570 
00571   /* USABILITY NOTE: Naturally, I only want the require_fully_onscreen,
00572    * require_on_single_xinerama, and require_titlebar_visible flags to
00573    * *become false* due to user interactions (which is allowed since
00574    * certain constraints are ignored for user interactions regardless of
00575    * the setting of these flags).  However, whether to make these flags
00576    * *become true* due to just an application interaction is a little
00577    * trickier.  It's possible that users may find not doing that strange
00578    * since two application interactions that resize in opposite ways don't
00579    * necessarily end up cancelling--but it may also be strange for the user
00580    * to have an application resize the window so that it's onscreen, the
00581    * user forgets about it, and then later the app is able to resize itself
00582    * off the screen.  Anyway, for now, I think the latter is the more
00583    * problematic case but this may need to be revisited.
00584    */
00585 
00586   /* The require onscreen/on-single-xinerama and titlebar_visible
00587    * stuff is relative to the outer window, not the inner
00588    */
00589   extend_by_frame (&info->current, info->fgeom);
00590 
00591   /* Update whether we want future constraint runs to require the
00592    * window to be on fully onscreen.
00593    */
00594   old = window->require_fully_onscreen;
00595   window->require_fully_onscreen =
00596     meta_rectangle_contained_in_region (info->usable_screen_region,
00597                                         &info->current);
00598   if (old ^ window->require_fully_onscreen)
00599     meta_topic (META_DEBUG_GEOMETRY,
00600                 "require_fully_onscreen for %s toggled to %s\n",
00601                 window->desc,
00602                 window->require_fully_onscreen ? "TRUE" : "FALSE");
00603 
00604   /* Update whether we want future constraint runs to require the
00605    * window to be on a single xinerama.
00606    */
00607   old = window->require_on_single_xinerama;
00608   window->require_on_single_xinerama =
00609     meta_rectangle_contained_in_region (info->usable_xinerama_region,
00610                                         &info->current);
00611   if (old ^ window->require_on_single_xinerama)
00612     meta_topic (META_DEBUG_GEOMETRY,
00613                 "require_on_single_xinerama for %s toggled to %s\n",
00614                 window->desc, 
00615                 window->require_on_single_xinerama ? "TRUE" : "FALSE");
00616 
00617   /* Update whether we want future constraint runs to require the
00618    * titlebar to be visible.
00619    */
00620   if (window->frame && window->decorated)
00621     {
00622       MetaRectangle titlebar_rect;
00623 
00624       titlebar_rect = info->current;
00625       titlebar_rect.height = info->fgeom->top_height;
00626       old = window->require_titlebar_visible;
00627       window->require_titlebar_visible =
00628         meta_rectangle_overlaps_with_region (info->usable_screen_region,
00629                                              &titlebar_rect);
00630       if (old ^ window->require_titlebar_visible)
00631         meta_topic (META_DEBUG_GEOMETRY,
00632                     "require_titlebar_visible for %s toggled to %s\n",
00633                     window->desc,
00634                     window->require_titlebar_visible ? "TRUE" : "FALSE");
00635     }
00636 
00637   /* Don't forget to restore the position of the window */
00638   unextend_by_frame (&info->current, info->fgeom);
00639 }
00640 
00641 static void
00642 extend_by_frame (MetaRectangle           *rect,
00643                  const MetaFrameGeometry *fgeom)
00644 {
00645   rect->x -= fgeom->left_width;
00646   rect->y -= fgeom->top_height;
00647   rect->width  += fgeom->left_width + fgeom->right_width;
00648   rect->height += fgeom->top_height + fgeom->bottom_height;
00649 }
00650 
00651 static void
00652 unextend_by_frame (MetaRectangle           *rect,
00653                    const MetaFrameGeometry *fgeom)
00654 {
00655   rect->x += fgeom->left_width;
00656   rect->y += fgeom->top_height;
00657   rect->width  -= fgeom->left_width + fgeom->right_width;
00658   rect->height -= fgeom->top_height + fgeom->bottom_height;
00659 }
00660 
00661 static inline void
00662 get_size_limits (const MetaWindow        *window,
00663                  const MetaFrameGeometry *fgeom,
00664                  gboolean                 include_frame,
00665                  MetaRectangle *min_size,
00666                  MetaRectangle *max_size)
00667 {
00668   /* We pack the results into MetaRectangle structs just for convienience; we
00669    * don't actually use the position of those rects.
00670    */
00671   min_size->width  = window->size_hints.min_width;
00672   min_size->height = window->size_hints.min_height;
00673   max_size->width  = window->size_hints.max_width;
00674   max_size->height = window->size_hints.max_height;
00675 
00676   if (include_frame)
00677     {
00678       int fw = fgeom->left_width + fgeom->right_width;
00679       int fh = fgeom->top_height + fgeom->bottom_height;
00680 
00681       min_size->width  += fw;
00682       min_size->height += fh;
00683       max_size->width  += fw;
00684       max_size->height += fh;
00685     }
00686 }
00687 
00688 static gboolean
00689 constrain_maximization (MetaWindow         *window,
00690                         ConstraintInfo     *info,
00691                         ConstraintPriority  priority,
00692                         gboolean            check_only)
00693 {
00694   MetaRectangle target_size;
00695   MetaRectangle min_size, max_size;
00696   gboolean hminbad, vminbad;
00697   gboolean horiz_equal, vert_equal;
00698   gboolean constraint_already_satisfied;
00699 
00700   if (priority > PRIORITY_MAXIMIZATION)
00701     return TRUE;
00702 
00703   /* Determine whether constraint applies; exit if it doesn't */
00704   if (!window->maximized_horizontally && !window->maximized_vertically)
00705     return TRUE;
00706 
00707   /* Calculate target_size = maximized size of (window + frame) */
00708   if (window->maximized_horizontally && window->maximized_vertically)
00709     target_size = info->work_area_xinerama;
00710   else
00711     {
00712       /* Amount of maximization possible in a single direction depends
00713        * on which struts could occlude the window given its current
00714        * position.  For example, a vertical partial strut on the right
00715        * is only relevant for a horizontally maximized window when the
00716        * window is at a vertical position where it could be occluded
00717        * by that partial strut.
00718        */
00719       MetaDirection  direction;
00720       GSList        *active_workspace_struts;
00721 
00722       if (window->maximized_horizontally)
00723         direction = META_DIRECTION_HORIZONTAL;
00724       else
00725         direction = META_DIRECTION_VERTICAL;
00726       active_workspace_struts = window->screen->active_workspace->all_struts;
00727 
00728       target_size = info->current;
00729       extend_by_frame (&target_size, info->fgeom);
00730       meta_rectangle_expand_to_avoiding_struts (&target_size,
00731                                                 &info->entire_xinerama,
00732                                                 direction,
00733                                                 active_workspace_struts);
00734    }
00735   /* Now make target_size = maximized size of client window */
00736   unextend_by_frame (&target_size, info->fgeom);
00737 
00738   /* Check min size constraints; max size constraints are ignored for maximized
00739    * windows, as per bug 327543.
00740    */
00741   get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
00742   hminbad = target_size.width < min_size.width && window->maximized_horizontally;
00743   vminbad = target_size.height < min_size.height && window->maximized_vertically;
00744   if (hminbad || vminbad)
00745     return TRUE;
00746 
00747   /* Determine whether constraint is already satisfied; exit if it is */
00748   horiz_equal = target_size.x      == info->current.x &&
00749                 target_size.width  == info->current.width;
00750   vert_equal  = target_size.y      == info->current.y &&
00751                 target_size.height == info->current.height;
00752   constraint_already_satisfied =
00753     (horiz_equal || !window->maximized_horizontally) &&
00754     (vert_equal  || !window->maximized_vertically);
00755   if (check_only || constraint_already_satisfied)
00756     return constraint_already_satisfied;
00757 
00758   /*** Enforce constraint ***/
00759   if (window->maximized_horizontally)
00760     {
00761       info->current.x      = target_size.x;
00762       info->current.width  = target_size.width;
00763     }
00764   if (window->maximized_vertically)
00765     {
00766       info->current.y      = target_size.y;
00767       info->current.height = target_size.height;
00768     }
00769   return TRUE;
00770 }
00771 
00772 static gboolean
00773 constrain_fullscreen (MetaWindow         *window,
00774                       ConstraintInfo     *info,
00775                       ConstraintPriority  priority,
00776                       gboolean            check_only)
00777 {
00778   MetaRectangle min_size, max_size, xinerama;
00779   gboolean too_big, too_small, constraint_already_satisfied;
00780 
00781   if (priority > PRIORITY_FULLSCREEN)
00782     return TRUE;
00783 
00784   /* Determine whether constraint applies; exit if it doesn't */
00785   if (!window->fullscreen)
00786     return TRUE;
00787   xinerama = info->entire_xinerama;
00788   get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
00789   too_big =   !meta_rectangle_could_fit_rect (&xinerama, &min_size);
00790   too_small = !meta_rectangle_could_fit_rect (&max_size, &xinerama);
00791   if (too_big || too_small)
00792     return TRUE;
00793 
00794   /* Determine whether constraint is already satisfied; exit if it is */
00795   constraint_already_satisfied =
00796     meta_rectangle_equal (&info->current, &xinerama);
00797   if (check_only || constraint_already_satisfied)
00798     return constraint_already_satisfied;
00799 
00800   /*** Enforce constraint ***/
00801   info->current = xinerama;
00802   return TRUE;
00803 }
00804 
00805 static gboolean
00806 constrain_size_increments (MetaWindow         *window,
00807                            ConstraintInfo     *info,
00808                            ConstraintPriority  priority,
00809                            gboolean            check_only)
00810 {
00811   int bh, hi, bw, wi, extra_height, extra_width;
00812   int new_width, new_height;
00813   gboolean constraint_already_satisfied;
00814   MetaRectangle *start_rect;
00815 
00816   if (priority > PRIORITY_SIZE_HINTS_INCREMENTS)
00817     return TRUE;
00818 
00819   /* Determine whether constraint applies; exit if it doesn't */
00820   if (META_WINDOW_MAXIMIZED (window) || window->fullscreen || 
00821       info->action_type == ACTION_MOVE)
00822     return TRUE;
00823 
00824   /* Determine whether constraint is already satisfied; exit if it is */
00825   bh = window->size_hints.base_height;
00826   hi = window->size_hints.height_inc;
00827   bw = window->size_hints.base_width;
00828   wi = window->size_hints.width_inc;
00829   extra_height = (info->current.height - bh) % hi;
00830   extra_width  = (info->current.width  - bw) % wi;
00831   /* ignore size increments for maximized windows */
00832   if (window->maximized_horizontally)
00833     extra_width *= 0;
00834   if (window->maximized_vertically)
00835     extra_height *= 0;
00836   /* constraint is satisfied iff there is no extra height or width */
00837   constraint_already_satisfied = 
00838     (extra_height == 0 && extra_width == 0);
00839 
00840   if (check_only || constraint_already_satisfied)
00841     return constraint_already_satisfied;
00842 
00843   /*** Enforce constraint ***/
00844   new_width  = info->current.width  - extra_width;
00845   new_height = info->current.height - extra_height;
00846 
00847   /* Adjusting down instead of up (as done in the above two lines) may
00848    * violate minimum size constraints; fix the adjustment if this
00849    * happens.
00850    */
00851   if (new_width  < window->size_hints.min_width)
00852     new_width  += ((window->size_hints.min_width  - new_width)/wi  + 1)*wi;
00853   if (new_height < window->size_hints.min_height)
00854     new_height += ((window->size_hints.min_height - new_height)/hi + 1)*hi;
00855 
00856   /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
00857    * See bug 448183
00858    */
00859   if (info->action_type == ACTION_MOVE_AND_RESIZE)
00860     start_rect = &info->current;
00861   else
00862     start_rect = &info->orig;
00863     
00864   /* Resize to the new size */
00865   meta_rectangle_resize_with_gravity (start_rect,
00866                                       &info->current, 
00867                                       info->resize_gravity,
00868                                       new_width,
00869                                       new_height);
00870   return TRUE;
00871 }
00872 
00873 static gboolean
00874 constrain_size_limits (MetaWindow         *window,
00875                        ConstraintInfo     *info,
00876                        ConstraintPriority  priority,
00877                        gboolean            check_only)
00878 {
00879   MetaRectangle min_size, max_size;
00880   gboolean too_big, too_small, constraint_already_satisfied;
00881   int new_width, new_height;
00882   MetaRectangle *start_rect;
00883 
00884   if (priority > PRIORITY_SIZE_HINTS_LIMITS)
00885     return TRUE;
00886 
00887   /* Determine whether constraint applies; exit if it doesn't.
00888    *
00889    * Note: The old code didn't apply this constraint for fullscreen or
00890    * maximized windows--but that seems odd to me.  *shrug*
00891    */
00892   if (info->action_type == ACTION_MOVE)
00893     return TRUE;
00894 
00895   /* Determine whether constraint is already satisfied; exit if it is */
00896   get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
00897   /* We ignore max-size limits for maximized windows; see #327543 */
00898   if (window->maximized_horizontally)
00899     max_size.width = MAX (max_size.width, info->current.width);
00900   if (window->maximized_vertically)
00901     max_size.height = MAX (max_size.height, info->current.height);
00902   too_small = !meta_rectangle_could_fit_rect (&info->current, &min_size);
00903   too_big   = !meta_rectangle_could_fit_rect (&max_size, &info->current);
00904   constraint_already_satisfied = !too_big && !too_small;
00905   if (check_only || constraint_already_satisfied)
00906     return constraint_already_satisfied;
00907 
00908   /*** Enforce constraint ***/
00909   new_width  = CLAMP (info->current.width,  min_size.width,  max_size.width);
00910   new_height = CLAMP (info->current.height, min_size.height, max_size.height);
00911   
00912   /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
00913    * See bug 448183
00914    */
00915   if (info->action_type == ACTION_MOVE_AND_RESIZE)
00916     start_rect = &info->current;
00917   else
00918     start_rect = &info->orig;
00919   
00920   meta_rectangle_resize_with_gravity (start_rect,
00921                                       &info->current, 
00922                                       info->resize_gravity,
00923                                       new_width,
00924                                       new_height);
00925   return TRUE;
00926 }
00927 
00928 static gboolean
00929 constrain_aspect_ratio (MetaWindow         *window,
00930                         ConstraintInfo     *info,
00931                         ConstraintPriority  priority,
00932                         gboolean            check_only)
00933 {
00934   double minr, maxr;
00935   gboolean constraints_are_inconsistent, constraint_already_satisfied;
00936   int fudge, new_width, new_height;
00937   double best_width, best_height;
00938   double alt_width, alt_height;
00939   MetaRectangle *start_rect;
00940 
00941   if (priority > PRIORITY_ASPECT_RATIO)
00942     return TRUE;
00943 
00944   /* Determine whether constraint applies; exit if it doesn't. */
00945   minr =         window->size_hints.min_aspect.x /
00946          (double)window->size_hints.min_aspect.y;
00947   maxr =         window->size_hints.max_aspect.x /
00948          (double)window->size_hints.max_aspect.y;
00949   constraints_are_inconsistent = minr > maxr;
00950   if (constraints_are_inconsistent ||
00951       META_WINDOW_MAXIMIZED (window) || window->fullscreen || 
00952       info->action_type == ACTION_MOVE)
00953     return TRUE;
00954 
00955   /* Determine whether constraint is already satisfied; exit if it is.  We
00956    * need the following to hold:
00957    *
00958    *                 width
00959    *         minr <= ------ <= maxr
00960    *                 height
00961    *
00962    * But we need to allow for some slight fudging since width and height
00963    * are integers instead of floating point numbers (this is particularly
00964    * important when minr == maxr), so we allow width and height to be off
00965    * a little bit from strictly satisfying these equations.  For just one
00966    * sided resizing, we have to make the fudge factor a little bigger
00967    * because of how meta_rectangle_resize_with_gravity treats those as
00968    * being a resize increment (FIXME: I should handle real resize
00969    * increments better here...)
00970    */
00971   switch (info->resize_gravity)
00972     {
00973     case WestGravity:
00974     case NorthGravity:
00975     case SouthGravity:
00976     case EastGravity:
00977       fudge = 2;
00978       break;
00979 
00980     case NorthWestGravity:
00981     case SouthWestGravity:
00982     case CenterGravity:
00983     case NorthEastGravity:
00984     case SouthEastGravity:
00985     case StaticGravity:
00986     default:
00987       fudge = 1;
00988       break;
00989     }
00990   constraint_already_satisfied = 
00991     info->current.width - (info->current.height * minr ) > -minr*fudge &&
00992     info->current.width - (info->current.height * maxr ) <  maxr*fudge;
00993   if (check_only || constraint_already_satisfied)
00994     return constraint_already_satisfied;
00995 
00996   /*** Enforce constraint ***/
00997   new_width = info->current.width;
00998   new_height = info->current.height;
00999 
01000   switch (info->resize_gravity)
01001     {
01002     case WestGravity:
01003     case EastGravity:
01004       /* Yeah, I suck for doing implicit rounding -- sue me */
01005       new_height = CLAMP (new_height, new_width / maxr,  new_width / minr);
01006       break;
01007 
01008     case NorthGravity:
01009     case SouthGravity:
01010       /* Yeah, I suck for doing implicit rounding -- sue me */
01011       new_width  = CLAMP (new_width,  new_height * minr, new_height * maxr);
01012       break;
01013 
01014     case NorthWestGravity:
01015     case SouthWestGravity:
01016     case CenterGravity:
01017     case NorthEastGravity:
01018     case SouthEastGravity:
01019     case StaticGravity:
01020     default:
01021       /* Find what width would correspond to new_height, and what height would
01022        * correspond to new_width */
01023       alt_width  = CLAMP (new_width,  new_height * minr, new_height * maxr);
01024       alt_height = CLAMP (new_height, new_width / maxr,  new_width / minr);
01025 
01026       /* The line connecting the points (alt_width, new_height) and
01027        * (new_width, alt_height) provide a range of
01028        * valid-for-the-aspect-ratio-constraint sizes.  We want the
01029        * size in that range closest to the value requested, i.e. the
01030        * point on the line which is closest to the point (new_width,
01031        * new_height)
01032        */
01033       meta_rectangle_find_linepoint_closest_to_point (alt_width, new_height,
01034                                                       new_width, alt_height,
01035                                                       new_width, new_height,
01036                                                       &best_width, &best_height);
01037 
01038       /* Yeah, I suck for doing implicit rounding -- sue me */
01039       new_width  = best_width;
01040       new_height = best_height;
01041 
01042       break;
01043     }
01044 
01045   /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
01046    * See bug 448183
01047    */
01048   if (info->action_type == ACTION_MOVE_AND_RESIZE)
01049     start_rect = &info->current;
01050   else
01051     start_rect = &info->orig;
01052 
01053   meta_rectangle_resize_with_gravity (start_rect,
01054                                       &info->current, 
01055                                       info->resize_gravity,
01056                                       new_width,
01057                                       new_height);
01058 
01059   return TRUE;
01060 }
01061 
01062 static gboolean
01063 do_screen_and_xinerama_relative_constraints (
01064   MetaWindow     *window,
01065   GList          *region_spanning_rectangles,
01066   ConstraintInfo *info,
01067   gboolean        check_only)
01068 {
01069   gboolean exit_early = FALSE, constraint_satisfied;
01070   MetaRectangle how_far_it_can_be_smushed, min_size, max_size;
01071 
01072 #ifdef WITH_VERBOSE_MODE
01073   if (meta_is_verbose ())
01074     {
01075       /* First, log some debugging information */
01076       char spanning_region[1 + 28 * g_list_length (region_spanning_rectangles)];
01077 
01078       meta_topic (META_DEBUG_GEOMETRY,
01079              "screen/xinerama constraint; region_spanning_rectangles: %s\n",
01080              meta_rectangle_region_to_string (region_spanning_rectangles, ", ",
01081                                               spanning_region));
01082     }
01083 #endif
01084 
01085   /* Determine whether constraint applies; exit if it doesn't */
01086   how_far_it_can_be_smushed = info->current;
01087   get_size_limits (window, info->fgeom, TRUE, &min_size, &max_size);
01088   extend_by_frame (&info->current, info->fgeom);
01089 
01090   if (info->action_type != ACTION_MOVE)
01091     {
01092       if (!(info->fixed_directions & FIXED_DIRECTION_X))
01093         how_far_it_can_be_smushed.width = min_size.width;
01094 
01095       if (!(info->fixed_directions & FIXED_DIRECTION_Y))
01096         how_far_it_can_be_smushed.height = min_size.height;
01097     }
01098   if (!meta_rectangle_could_fit_in_region (region_spanning_rectangles,
01099                                            &how_far_it_can_be_smushed))
01100     exit_early = TRUE;
01101 
01102   /* Determine whether constraint is already satisfied; exit if it is */
01103   constraint_satisfied = 
01104     meta_rectangle_contained_in_region (region_spanning_rectangles,
01105                                         &info->current);
01106   if (exit_early || constraint_satisfied || check_only)
01107     {
01108       unextend_by_frame (&info->current, info->fgeom);
01109       return constraint_satisfied;
01110     }
01111 
01112   /* Enforce constraint */
01113 
01114   /* Clamp rectangle size for resize or move+resize actions */
01115   if (info->action_type != ACTION_MOVE)
01116     meta_rectangle_clamp_to_fit_into_region (region_spanning_rectangles,
01117                                              info->fixed_directions,
01118                                              &info->current,
01119                                              &min_size);
01120 
01121   if (info->is_user_action && info->action_type == ACTION_RESIZE)
01122     /* For user resize, clip to the relevant region */
01123     meta_rectangle_clip_to_region (region_spanning_rectangles,
01124                                    info->fixed_directions,
01125                                    &info->current);
01126   else
01127     /* For everything else, shove the rectangle into the relevant region */
01128     meta_rectangle_shove_into_region (region_spanning_rectangles,
01129                                       info->fixed_directions,
01130                                       &info->current);
01131 
01132   unextend_by_frame (&info->current, info->fgeom);
01133   return TRUE;
01134 }
01135 
01136 static gboolean
01137 constrain_to_single_xinerama (MetaWindow         *window,
01138                               ConstraintInfo     *info,
01139                               ConstraintPriority  priority,
01140                               gboolean            check_only)
01141 {
01142   if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA)
01143     return TRUE;
01144 
01145   /* Exit early if we know the constraint won't apply--note that this constraint
01146    * is only meant for normal windows (e.g. we don't want docks to be shoved 
01147    * "onscreen" by their own strut) and we can't apply it to frameless windows
01148    * or else users will be unable to move windows such as XMMS across xineramas.
01149    */
01150   if (window->type == META_WINDOW_DESKTOP   ||
01151       window->type == META_WINDOW_DOCK      ||
01152       window->screen->n_xinerama_infos == 1 ||
01153       !window->require_on_single_xinerama   ||
01154       !window->frame                        ||
01155       info->is_user_action)
01156     return TRUE;
01157 
01158   /* Have a helper function handle the constraint for us */
01159   return do_screen_and_xinerama_relative_constraints (window, 
01160                                                  info->usable_xinerama_region,
01161                                                  info,
01162                                                  check_only);
01163 }
01164 
01165 static gboolean
01166 constrain_fully_onscreen (MetaWindow         *window,
01167                           ConstraintInfo     *info,
01168                           ConstraintPriority  priority,
01169                           gboolean            check_only)
01170 {
01171   if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA)
01172     return TRUE;
01173 
01174   /* Exit early if we know the constraint won't apply--note that this constraint
01175    * is only meant for normal windows (e.g. we don't want docks to be shoved 
01176    * "onscreen" by their own strut).
01177    */
01178   if (window->type == META_WINDOW_DESKTOP ||
01179       window->type == META_WINDOW_DOCK    ||
01180       window->fullscreen                  ||
01181       !window->require_fully_onscreen     || 
01182       info->is_user_action)
01183     return TRUE;
01184 
01185   /* Have a helper function handle the constraint for us */
01186   return do_screen_and_xinerama_relative_constraints (window, 
01187                                                  info->usable_screen_region,
01188                                                  info,
01189                                                  check_only);
01190 }
01191 
01192 static gboolean
01193 constrain_titlebar_visible (MetaWindow         *window,
01194                             ConstraintInfo     *info,
01195                             ConstraintPriority  priority,
01196                             gboolean            check_only)
01197 {
01198   gboolean unconstrained_user_action;
01199   gboolean retval;
01200   int bottom_amount;
01201   int horiz_amount_offscreen, vert_amount_offscreen;
01202   int horiz_amount_onscreen,  vert_amount_onscreen;
01203 
01204   if (priority > PRIORITY_TITLEBAR_VISIBLE)
01205     return TRUE;
01206 
01207   /* Allow the titlebar beyond the top of the screen only if the user wasn't
01208    * clicking on the frame to start the move.
01209    */
01210   unconstrained_user_action =
01211     info->is_user_action && !window->display->grab_frame_action;
01212 
01213   /* Exit early if we know the constraint won't apply--note that this constraint
01214    * is only meant for normal windows (e.g. we don't want docks to be shoved 
01215    * "onscreen" by their own strut).
01216    */
01217   if (window->type == META_WINDOW_DESKTOP ||
01218       window->type == META_WINDOW_DOCK    ||
01219       window->fullscreen                  ||
01220       !window->require_titlebar_visible   ||
01221       !window->decorated                  ||
01222       unconstrained_user_action)
01223     return TRUE;
01224 
01225   /* Determine how much offscreen things are allowed.  We first need to
01226    * figure out how much must remain on the screen.  For that, we use 25%
01227    * window width/height but clamp to the range of (10,75) pixels.  This is
01228    * somewhat of a seat of my pants random guess at what might look good.
01229    * Then, the amount that is allowed off is just the window size minus
01230    * this amount (but no less than 0 for tiny windows).
01231    */
01232   horiz_amount_onscreen = info->current.width  / 4;
01233   vert_amount_onscreen  = info->current.height / 4;
01234   horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
01235   vert_amount_onscreen  = CLAMP (vert_amount_onscreen,  10, 75);
01236   horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
01237   vert_amount_offscreen  = info->current.height - vert_amount_onscreen;
01238   horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
01239   vert_amount_offscreen  = MAX (vert_amount_offscreen,  0);
01240   /* Allow the titlebar to touch the bottom panel;  If there is no titlebar,
01241    * require vert_amount to remain on the screen.
01242    */
01243   if (window->frame)
01244     {
01245       bottom_amount = info->current.height + info->fgeom->bottom_height;
01246       vert_amount_onscreen = info->fgeom->top_height;
01247     }
01248   else
01249     bottom_amount = vert_amount_offscreen;
01250 
01251   /* Extend the region, have a helper function handle the constraint,
01252    * then return the region to its original size.
01253    */
01254   meta_rectangle_expand_region_conditionally (info->usable_screen_region,
01255                                               horiz_amount_offscreen,
01256                                               horiz_amount_offscreen, 
01257                                               0, /* Don't let titlebar off */
01258                                               bottom_amount,
01259                                               horiz_amount_onscreen,
01260                                               vert_amount_onscreen);
01261   retval =
01262     do_screen_and_xinerama_relative_constraints (window, 
01263                                                  info->usable_screen_region,
01264                                                  info,
01265                                                  check_only);
01266   meta_rectangle_expand_region_conditionally (info->usable_screen_region,
01267                                               -horiz_amount_offscreen,
01268                                               -horiz_amount_offscreen,
01269                                               0, /* Don't let titlebar off */
01270                                               -bottom_amount,
01271                                               horiz_amount_onscreen,
01272                                               vert_amount_onscreen);
01273 
01274   return retval;
01275 }
01276 
01277 static gboolean
01278 constrain_partially_onscreen (MetaWindow         *window,
01279                               ConstraintInfo     *info,
01280                               ConstraintPriority  priority,
01281                               gboolean            check_only)
01282 {
01283   gboolean retval;
01284   int top_amount, bottom_amount;
01285   int horiz_amount_offscreen, vert_amount_offscreen;
01286   int horiz_amount_onscreen,  vert_amount_onscreen;
01287 
01288   if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA)
01289     return TRUE;
01290 
01291   /* Exit early if we know the constraint won't apply--note that this constraint
01292    * is only meant for normal windows (e.g. we don't want docks to be shoved 
01293    * "onscreen" by their own strut).
01294    */
01295   if (window->type == META_WINDOW_DESKTOP ||
01296       window->type == META_WINDOW_DOCK)
01297     return TRUE;
01298 
01299   /* Determine how much offscreen things are allowed.  We first need to
01300    * figure out how much must remain on the screen.  For that, we use 25%
01301    * window width/height but clamp to the range of (10,75) pixels.  This is
01302    * somewhat of a seat of my pants random guess at what might look good.
01303    * Then, the amount that is allowed off is just the window size minus
01304    * this amount (but no less than 0 for tiny windows).
01305    */
01306   horiz_amount_onscreen = info->current.width  / 4;
01307   vert_amount_onscreen  = info->current.height / 4;
01308   horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
01309   vert_amount_onscreen  = CLAMP (vert_amount_onscreen,  10, 75);
01310   horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
01311   vert_amount_offscreen  = info->current.height - vert_amount_onscreen;
01312   horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
01313   vert_amount_offscreen  = MAX (vert_amount_offscreen,  0);
01314   top_amount = vert_amount_offscreen;
01315   /* Allow the titlebar to touch the bottom panel;  If there is no titlebar,
01316    * require vert_amount to remain on the screen.
01317    */
01318   if (window->frame)
01319     {
01320       bottom_amount = info->current.height + info->fgeom->bottom_height;
01321       vert_amount_onscreen = info->fgeom->top_height;
01322     }
01323   else
01324     bottom_amount = vert_amount_offscreen;
01325 
01326   /* Extend the region, have a helper function handle the constraint,
01327    * then return the region to its original size.
01328    */
01329   meta_rectangle_expand_region_conditionally (info->usable_screen_region,
01330                                               horiz_amount_offscreen,
01331                                               horiz_amount_offscreen, 
01332                                               top_amount,
01333                                               bottom_amount,
01334                                               horiz_amount_onscreen,
01335                                               vert_amount_onscreen);
01336   retval =
01337     do_screen_and_xinerama_relative_constraints (window, 
01338                                                  info->usable_screen_region,
01339                                                  info,
01340                                                  check_only);
01341   meta_rectangle_expand_region_conditionally (info->usable_screen_region,
01342                                               -horiz_amount_offscreen,
01343                                               -horiz_amount_offscreen,
01344                                               -top_amount,
01345                                               -bottom_amount,
01346                                               horiz_amount_onscreen,
01347                                               vert_amount_onscreen);
01348 
01349   return retval;
01350 }

Generated on Sat Aug 23 22:04:16 2008 for metacity by  doxygen 1.5.5