~ubuntu-branches/ubuntu/vivid/marco/vivid

« back to all changes in this revision

Viewing changes to src/core/constraints.c

  • Committer: Package Import Robot
  • Author(s): Mike Gabriel
  • Date: 2014-01-21 19:52:39 UTC
  • Revision ID: package-import@ubuntu.com-20140121195239-c4k1v8umdw1u4n29
Tags: upstream-1.6.2
ImportĀ upstreamĀ versionĀ 1.6.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
2
 
 
3
/* Marco size/position constraints */
 
4
 
 
5
/*
 
6
 * Copyright (C) 2002, 2003 Red Hat, Inc.
 
7
 * Copyright (C) 2003, 2004 Rob Adams
 
8
 * Copyright (C) 2005, 2006 Elijah Newren
 
9
 *
 
10
 * This program is free software; you can redistribute it and/or
 
11
 * modify it under the terms of the GNU General Public License as
 
12
 * published by the Free Software Foundation; either version 2 of the
 
13
 * License, or (at your option) any later version.
 
14
 *
 
15
 * This program is distributed in the hope that it will be useful, but
 
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 * General Public License for more details.
 
19
 *
 
20
 * You should have received a copy of the GNU General Public License
 
21
 * along with this program; if not, write to the Free Software
 
22
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 
23
 * 02110-1301, USA.
 
24
 */
 
25
 
 
26
#include <config.h>
 
27
#include "constraints.h"
 
28
#include "workspace.h"
 
29
#include "place.h"
 
30
#include "prefs.h"
 
31
 
 
32
#include <stdlib.h>
 
33
#include <math.h>
 
34
 
 
35
#if 0
 
36
 // This is the short and sweet version of how to hack on this file; see
 
37
 // doc/how-constraints-works.txt for the gory details.  The basics of
 
38
 // understanding this file can be shown by the steps needed to add a new
 
39
 // constraint, which are:
 
40
 //   1) Add a new entry in the ConstraintPriority enum; higher values
 
41
 //      have higher priority
 
42
 //   2) Write a new function following the format of the example below,
 
43
 //      "constrain_whatever".
 
44
 //   3) Add your function to the all_constraints and all_constraint_names
 
45
 //      arrays (the latter of which is for debugging purposes)
 
46
 // 
 
47
 // An example constraint function, constrain_whatever:
 
48
 //
 
49
 // /* constrain_whatever does the following:
 
50
 //  *   Quits (returning true) if priority is higher than PRIORITY_WHATEVER
 
51
 //  *   If check_only is TRUE
 
52
 //  *     Returns whether the constraint is satisfied or not
 
53
 //  *   otherwise
 
54
 //  *     Enforces the constraint
 
55
 //  * Note that the value of PRIORITY_WHATEVER is centralized with the
 
56
 //  * priorities of other constraints in the definition of ConstrainPriority
 
57
 //  * for easier maintenance and shuffling of priorities.
 
58
 //  */
 
59
 // static gboolean
 
60
 // constrain_whatever (MetaWindow         *window,
 
61
 //                     ConstraintInfo     *info,
 
62
 //                     ConstraintPriority  priority,
 
63
 //                     gboolean            check_only)
 
64
 // {
 
65
 //   if (priority > PRIORITY_WHATEVER)
 
66
 //     return TRUE;
 
67
 //
 
68
 //   /* Determine whether constraint applies; note that if the constraint
 
69
 //    * cannot possibly be satisfied, constraint_applies should be set to
 
70
 //    * false.  If we don't do this, all constraints with a lesser priority
 
71
 //    * will be dropped along with this one, and we'd rather apply as many as
 
72
 //    * possible.
 
73
 //    */
 
74
 //   if (!constraint_applies)
 
75
 //     return TRUE;
 
76
 //
 
77
 //   /* Determine whether constraint is already satisfied; if we're only
 
78
 //    * checking the status of whether the constraint is satisfied, we end
 
79
 //    * here.
 
80
 //    */
 
81
 //   if (check_only || constraint_already_satisfied)
 
82
 //     return constraint_already_satisfied;
 
83
 //
 
84
 //   /* Enforce constraints */
 
85
 //   return TRUE;  /* Note that we exited early if check_only is FALSE; also,
 
86
 //                  * we know we can return TRUE here because we exited early
 
87
 //                  * if the constraint could not be satisfied; not that the
 
88
 //                  * return value is heeded in this case...
 
89
 //                  */
 
90
 // }
 
91
#endif
 
92
 
 
93
typedef enum
 
94
{
 
95
  PRIORITY_MINIMUM = 0, /* Dummy value used for loop start = min(all priorities) */
 
96
  PRIORITY_ASPECT_RATIO = 0,
 
97
  PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA = 0,
 
98
  PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
 
99
  PRIORITY_SIZE_HINTS_INCREMENTS = 1,
 
100
  PRIORITY_MAXIMIZATION = 2,
 
101
  PRIORITY_FULLSCREEN = 2,
 
102
  PRIORITY_SIZE_HINTS_LIMITS = 3,
 
103
  PRIORITY_TITLEBAR_VISIBLE = 4,
 
104
  PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4,
 
105
  PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */
 
106
} ConstraintPriority;
 
107
 
 
108
typedef enum
 
109
{
 
110
  ACTION_MOVE,
 
111
  ACTION_RESIZE,
 
112
  ACTION_MOVE_AND_RESIZE
 
113
} ActionType;
 
114
 
 
115
typedef struct
 
116
{
 
117
  MetaRectangle        orig;
 
118
  MetaRectangle        current;
 
119
  MetaFrameGeometry   *fgeom;
 
120
  ActionType           action_type;
 
121
  gboolean             is_user_action;
 
122
 
 
123
  /* I know that these two things probably look similar at first, but they
 
124
   * have much different uses.  See doc/how-constraints-works.txt for for
 
125
   * explanation of the differences and similarity between resize_gravity
 
126
   * and fixed_directions
 
127
   */
 
128
  int                  resize_gravity;
 
129
  FixedDirections      fixed_directions;
 
130
 
 
131
  /* work_area_xinerama - current xinerama region minus struts
 
132
   * entire_xinerama    - current xienrama, including strut regions
 
133
   */
 
134
  MetaRectangle        work_area_xinerama;
 
135
  MetaRectangle        entire_xinerama;
 
136
 
 
137
  /* Spanning rectangles for the non-covered (by struts) region of the
 
138
   * screen and also for just the current xinerama
 
139
   */
 
140
  GList  *usable_screen_region;
 
141
  GList  *usable_xinerama_region;
 
142
} ConstraintInfo;
 
143
 
 
144
static gboolean constrain_maximization       (MetaWindow         *window,
 
145
                                              ConstraintInfo     *info,
 
146
                                              ConstraintPriority  priority,
 
147
                                              gboolean            check_only);
 
148
static gboolean constrain_fullscreen         (MetaWindow         *window,
 
149
                                              ConstraintInfo     *info,
 
150
                                              ConstraintPriority  priority,
 
151
                                              gboolean            check_only);
 
152
static gboolean constrain_size_increments    (MetaWindow         *window,
 
153
                                              ConstraintInfo     *info,
 
154
                                              ConstraintPriority  priority,
 
155
                                              gboolean            check_only);
 
156
static gboolean constrain_size_limits        (MetaWindow         *window,
 
157
                                              ConstraintInfo     *info,
 
158
                                              ConstraintPriority  priority,
 
159
                                              gboolean            check_only);
 
160
static gboolean constrain_aspect_ratio       (MetaWindow         *window,
 
161
                                              ConstraintInfo     *info,
 
162
                                              ConstraintPriority  priority,
 
163
                                              gboolean            check_only);
 
164
static gboolean constrain_to_single_xinerama (MetaWindow         *window,
 
165
                                              ConstraintInfo     *info,
 
166
                                              ConstraintPriority  priority,
 
167
                                              gboolean            check_only);
 
168
static gboolean constrain_fully_onscreen     (MetaWindow         *window,
 
169
                                              ConstraintInfo     *info,
 
170
                                              ConstraintPriority  priority,
 
171
                                              gboolean            check_only);
 
172
static gboolean constrain_titlebar_visible   (MetaWindow         *window,
 
173
                                              ConstraintInfo     *info,
 
174
                                              ConstraintPriority  priority,
 
175
                                              gboolean            check_only);
 
176
static gboolean constrain_partially_onscreen (MetaWindow         *window,
 
177
                                              ConstraintInfo     *info,
 
178
                                              ConstraintPriority  priority,
 
179
                                              gboolean            check_only);
 
180
 
 
181
static void setup_constraint_info        (ConstraintInfo      *info,
 
182
                                          MetaWindow          *window,
 
183
                                          MetaFrameGeometry   *orig_fgeom,
 
184
                                          MetaMoveResizeFlags  flags,
 
185
                                          int                  resize_gravity,
 
186
                                          const MetaRectangle *orig,
 
187
                                          MetaRectangle       *new);
 
188
static void place_window_if_needed       (MetaWindow     *window,
 
189
                                          ConstraintInfo *info);
 
190
static void update_onscreen_requirements (MetaWindow     *window,
 
191
                                          ConstraintInfo *info);
 
192
static void extend_by_frame              (MetaRectangle           *rect,
 
193
                                          const MetaFrameGeometry *fgeom);
 
194
static void unextend_by_frame            (MetaRectangle           *rect,
 
195
                                          const MetaFrameGeometry *fgeom);
 
196
static inline void get_size_limits       (const MetaWindow        *window,
 
197
                                          const MetaFrameGeometry *fgeom,
 
198
                                          gboolean include_frame,
 
199
                                          MetaRectangle *min_size,
 
200
                                          MetaRectangle *max_size);
 
201
 
 
202
typedef gboolean (* ConstraintFunc) (MetaWindow         *window,
 
203
                                     ConstraintInfo     *info,
 
204
                                     ConstraintPriority  priority,
 
205
                                     gboolean            check_only);
 
206
 
 
207
typedef struct {
 
208
  ConstraintFunc func;
 
209
  const char* name;
 
210
} Constraint;
 
211
 
 
212
static const Constraint all_constraints[] = {
 
213
  {constrain_maximization,       "constrain_maximization"},
 
214
  {constrain_fullscreen,         "constrain_fullscreen"},
 
215
  {constrain_size_increments,    "constrain_size_increments"},
 
216
  {constrain_size_limits,        "constrain_size_limits"},
 
217
  {constrain_aspect_ratio,       "constrain_aspect_ratio"},
 
218
  {constrain_to_single_xinerama, "constrain_to_single_xinerama"},
 
219
  {constrain_fully_onscreen,     "constrain_fully_onscreen"},
 
220
  {constrain_titlebar_visible,   "constrain_titlebar_visible"},
 
221
  {constrain_partially_onscreen, "constrain_partially_onscreen"},
 
222
  {NULL,                         NULL}
 
223
};
 
224
 
 
225
static gboolean
 
226
do_all_constraints (MetaWindow         *window,
 
227
                    ConstraintInfo     *info,
 
228
                    ConstraintPriority  priority,
 
229
                    gboolean            check_only)
 
230
{
 
231
  const Constraint *constraint;
 
232
  gboolean          satisfied;
 
233
 
 
234
  constraint = &all_constraints[0];
 
235
  satisfied = TRUE;
 
236
  while (constraint->func != NULL)
 
237
    {
 
238
      satisfied = satisfied &&
 
239
                  (*constraint->func) (window, info, priority, check_only);
 
240
 
 
241
      if (!check_only)
 
242
        {
 
243
          /* Log how the constraint modified the position */
 
244
          meta_topic (META_DEBUG_GEOMETRY,
 
245
                      "info->current is %d,%d +%d,%d after %s\n",
 
246
                      info->current.x, info->current.y, 
 
247
                      info->current.width, info->current.height,
 
248
                      constraint->name);
 
249
        }
 
250
      else if (!satisfied)
 
251
        {
 
252
          /* Log which constraint was not satisfied */
 
253
          meta_topic (META_DEBUG_GEOMETRY,
 
254
                      "constraint %s not satisfied.\n",
 
255
                      constraint->name);
 
256
          return FALSE;
 
257
        }
 
258
      ++constraint;
 
259
    }
 
260
 
 
261
  return TRUE;
 
262
}
 
263
 
 
264
void
 
265
meta_window_constrain (MetaWindow          *window,
 
266
                       MetaFrameGeometry   *orig_fgeom,
 
267
                       MetaMoveResizeFlags  flags,
 
268
                       int                  resize_gravity,
 
269
                       const MetaRectangle *orig,
 
270
                       MetaRectangle       *new)
 
271
{
 
272
  ConstraintInfo info;
 
273
  ConstraintPriority priority = PRIORITY_MINIMUM;
 
274
  gboolean satisfied = FALSE;
 
275
 
 
276
  /* WARNING: orig and new specify positions and sizes of the inner window,
 
277
   * not the outer.  This is a common gotcha since half the constraints
 
278
   * deal with inner window position/size and half deal with outer.  See
 
279
   * doc/how-constraints-works.txt for more information.
 
280
   */
 
281
  meta_topic (META_DEBUG_GEOMETRY,
 
282
              "Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d\n",
 
283
              window->desc,
 
284
              orig->x, orig->y, orig->width, orig->height,
 
285
              new->x,  new->y,  new->width,  new->height);
 
286
 
 
287
  setup_constraint_info (&info,
 
288
                         window, 
 
289
                         orig_fgeom, 
 
290
                         flags,
 
291
                         resize_gravity,
 
292
                         orig,
 
293
                         new);
 
294
  place_window_if_needed (window, &info);
 
295
 
 
296
  while (!satisfied && priority <= PRIORITY_MAXIMUM) {
 
297
    gboolean check_only = TRUE;
 
298
 
 
299
    /* Individually enforce all the high-enough priority constraints */
 
300
    do_all_constraints (window, &info, priority, !check_only);
 
301
 
 
302
    /* Check if all high-enough priority constraints are simultaneously 
 
303
     * satisfied
 
304
     */
 
305
    satisfied = do_all_constraints (window, &info, priority, check_only);
 
306
 
 
307
    /* Drop the least important constraints if we can't satisfy them all */
 
308
    priority++;
 
309
  }
 
310
 
 
311
  /* Make sure we use the constrained position */
 
312
  *new = info.current;
 
313
 
 
314
  /* We may need to update window->require_fully_onscreen,
 
315
   * window->require_on_single_xinerama, and perhaps other quantities
 
316
   * if this was a user move or user move-and-resize operation.
 
317
   */
 
318
  update_onscreen_requirements (window, &info);
 
319
 
 
320
  /* Ew, what an ugly way to do things.  Destructors (in a real OOP language,
 
321
   * not gobject-style--gobject would be more pain than it's worth) or
 
322
   * smart pointers would be so much nicer here.  *shrug*
 
323
   */
 
324
  if (!orig_fgeom)
 
325
    g_free (info.fgeom);
 
326
}
 
327
 
 
328
static void
 
329
setup_constraint_info (ConstraintInfo      *info,
 
330
                       MetaWindow          *window,
 
331
                       MetaFrameGeometry   *orig_fgeom,
 
332
                       MetaMoveResizeFlags  flags,
 
333
                       int                  resize_gravity,
 
334
                       const MetaRectangle *orig,
 
335
                       MetaRectangle       *new)
 
336
{
 
337
  const MetaXineramaScreenInfo *xinerama_info;
 
338
  MetaWorkspace *cur_workspace;
 
339
 
 
340
  info->orig    = *orig;
 
341
  info->current = *new;
 
342
 
 
343
  /* Create a fake frame geometry if none really exists */
 
344
  if (orig_fgeom && !window->fullscreen)
 
345
    info->fgeom = orig_fgeom;
 
346
  else
 
347
    info->fgeom = g_new0 (MetaFrameGeometry, 1);
 
348
 
 
349
  if (flags & META_IS_MOVE_ACTION && flags & META_IS_RESIZE_ACTION)
 
350
    info->action_type = ACTION_MOVE_AND_RESIZE;
 
351
  else if (flags & META_IS_RESIZE_ACTION)
 
352
    info->action_type = ACTION_RESIZE;
 
353
  else if (flags & META_IS_MOVE_ACTION)
 
354
    info->action_type = ACTION_MOVE;
 
355
  else
 
356
    g_error ("BAD, BAD developer!  No treat for you!  (Fix your calls to "
 
357
             "meta_window_move_resize_internal()).\n");
 
358
 
 
359
  info->is_user_action = (flags & META_IS_USER_ACTION);
 
360
 
 
361
  info->resize_gravity = resize_gravity;
 
362
 
 
363
  /* FIXME: fixed_directions might be more sane if we (a) made it
 
364
   * depend on the grab_op type instead of current amount of movement
 
365
   * (thus implying that it only has effect when user_action is true,
 
366
   * and (b) ignored it for aspect ratio windows -- at least in those
 
367
   * cases where both directions do actually change size.
 
368
   */
 
369
  info->fixed_directions = FIXED_DIRECTION_NONE;
 
370
  /* If x directions don't change but either y direction does */
 
371
  if ( orig->x == new->x && orig->x + orig->width  == new->x + new->width   &&
 
372
      (orig->y != new->y || orig->y + orig->height != new->y + new->height))
 
373
    {
 
374
      info->fixed_directions = FIXED_DIRECTION_X;
 
375
    }
 
376
  /* If y directions don't change but either x direction does */
 
377
  if ( orig->y == new->y && orig->y + orig->height == new->y + new->height  &&
 
378
      (orig->x != new->x || orig->x + orig->width  != new->x + new->width ))
 
379
    {
 
380
      info->fixed_directions = FIXED_DIRECTION_Y;
 
381
    }
 
382
  /* The point of fixed directions is just that "move to nearest valid
 
383
   * position" is sometimes a poorer choice than "move to nearest
 
384
   * valid position but only change this coordinate" for windows the
 
385
   * user is explicitly moving.  This isn't ever true for things that
 
386
   * aren't explicit user interaction, though, so just clear it out.
 
387
   */
 
388
  if (!info->is_user_action)
 
389
    info->fixed_directions = FIXED_DIRECTION_NONE;
 
390
 
 
391
  xinerama_info =
 
392
    meta_screen_get_xinerama_for_rect (window->screen, &info->current);
 
393
  meta_window_get_work_area_for_xinerama (window,
 
394
                                          xinerama_info->number,
 
395
                                          &info->work_area_xinerama);
 
396
 
 
397
  if (!window->fullscreen || window->fullscreen_monitors[0] == -1)
 
398
    {
 
399
      info->entire_xinerama = xinerama_info->rect;
 
400
    }
 
401
  else
 
402
    {
 
403
      int i = 0;
 
404
      long monitor;
 
405
 
 
406
      monitor = window->fullscreen_monitors[i];
 
407
      info->entire_xinerama =
 
408
        window->screen->xinerama_infos[monitor].rect;
 
409
      for (i = 1; i <= 3; i++)
 
410
        {
 
411
          monitor = window->fullscreen_monitors[i];
 
412
          meta_rectangle_union (&info->entire_xinerama,
 
413
                                &window->screen->xinerama_infos[monitor].rect,
 
414
                                &info->entire_xinerama);
 
415
        }
 
416
    }
 
417
 
 
418
  cur_workspace = window->screen->active_workspace;
 
419
  info->usable_screen_region   = 
 
420
    meta_workspace_get_onscreen_region (cur_workspace);
 
421
  info->usable_xinerama_region = 
 
422
    meta_workspace_get_onxinerama_region (cur_workspace, 
 
423
                                          xinerama_info->number);
 
424
 
 
425
  /* Workaround braindead legacy apps that don't know how to
 
426
   * fullscreen themselves properly.
 
427
   */
 
428
  if (meta_prefs_get_force_fullscreen() &&
 
429
      meta_rectangle_equal (new, &xinerama_info->rect) &&
 
430
      window->has_fullscreen_func &&
 
431
      !window->fullscreen)
 
432
    {
 
433
      /*
 
434
      meta_topic (META_DEBUG_GEOMETRY,
 
435
      */
 
436
      meta_warning (
 
437
                  "Treating resize request of legacy application %s as a "
 
438
                  "fullscreen request\n",
 
439
                  window->desc);
 
440
      meta_window_make_fullscreen_internal (window);
 
441
    }
 
442
 
 
443
  /* Log all this information for debugging */
 
444
  meta_topic (META_DEBUG_GEOMETRY,
 
445
              "Setting up constraint info:\n"
 
446
              "  orig: %d,%d +%d,%d\n"
 
447
              "  new : %d,%d +%d,%d\n"
 
448
              "  fgeom: %d,%d,%d,%d\n"
 
449
              "  action_type     : %s\n"
 
450
              "  is_user_action  : %s\n"
 
451
              "  resize_gravity  : %s\n"
 
452
              "  fixed_directions: %s\n"
 
453
              "  work_area_xinerama: %d,%d +%d,%d\n"
 
454
              "  entire_xinerama   : %d,%d +%d,%d\n",
 
455
              info->orig.x, info->orig.y, info->orig.width, info->orig.height,
 
456
              info->current.x, info->current.y, 
 
457
                info->current.width, info->current.height,
 
458
              info->fgeom->left_width, info->fgeom->right_width,
 
459
                info->fgeom->top_height, info->fgeom->bottom_height,
 
460
              (info->action_type == ACTION_MOVE) ? "Move" :
 
461
                (info->action_type == ACTION_RESIZE) ? "Resize" :
 
462
                (info->action_type == ACTION_MOVE_AND_RESIZE) ? "Move&Resize" :
 
463
                "Freakin' Invalid Stupid",
 
464
              (info->is_user_action) ? "true" : "false",
 
465
              meta_gravity_to_string (info->resize_gravity),
 
466
              (info->fixed_directions == FIXED_DIRECTION_NONE) ? "None" :
 
467
                (info->fixed_directions == FIXED_DIRECTION_X) ? "X fixed" :
 
468
                (info->fixed_directions == FIXED_DIRECTION_Y) ? "Y fixed" :
 
469
                "Freakin' Invalid Stupid",
 
470
              info->work_area_xinerama.x, info->work_area_xinerama.y,
 
471
                info->work_area_xinerama.width, 
 
472
                info->work_area_xinerama.height,
 
473
              info->entire_xinerama.x, info->entire_xinerama.y,
 
474
                info->entire_xinerama.width, info->entire_xinerama.height);
 
475
}
 
476
 
 
477
static void
 
478
place_window_if_needed(MetaWindow     *window,
 
479
                       ConstraintInfo *info)
 
480
{
 
481
  gboolean did_placement;
 
482
 
 
483
  /* Do placement if any, so we go ahead and apply position
 
484
   * constraints in a move-only context. Don't place
 
485
   * maximized/minimized/fullscreen windows until they are
 
486
   * unmaximized, unminimized and unfullscreened.
 
487
   */
 
488
  did_placement = FALSE;
 
489
  if (!window->placed &&
 
490
      window->calc_placement &&
 
491
      !(window->maximized_horizontally ||
 
492
        window->maximized_vertically) &&
 
493
      !window->minimized &&
 
494
      !window->fullscreen)
 
495
    {
 
496
      MetaRectangle placed_rect = info->orig;
 
497
      MetaWorkspace *cur_workspace;
 
498
      const MetaXineramaScreenInfo *xinerama_info;
 
499
 
 
500
      meta_window_place (window, info->fgeom, info->orig.x, info->orig.y,
 
501
                         &placed_rect.x, &placed_rect.y);
 
502
      did_placement = TRUE;
 
503
 
 
504
      /* placing the window may have changed the xinerama.  Find the
 
505
       * new xinerama and update the ConstraintInfo
 
506
       */
 
507
      xinerama_info =
 
508
        meta_screen_get_xinerama_for_rect (window->screen, &placed_rect);
 
509
      info->entire_xinerama = xinerama_info->rect;
 
510
      meta_window_get_work_area_for_xinerama (window,
 
511
                                              xinerama_info->number,
 
512
                                              &info->work_area_xinerama);
 
513
      cur_workspace = window->screen->active_workspace;
 
514
      info->usable_xinerama_region = 
 
515
        meta_workspace_get_onxinerama_region (cur_workspace, 
 
516
                                              xinerama_info->number);
 
517
 
 
518
 
 
519
      info->current.x = placed_rect.x;
 
520
      info->current.y = placed_rect.y;
 
521
 
 
522
      /* Since we just barely placed the window, there's no reason to
 
523
       * consider any of the directions fixed.
 
524
       */
 
525
      info->fixed_directions = FIXED_DIRECTION_NONE;
 
526
    }
 
527
 
 
528
  if (window->placed || did_placement)
 
529
    {
 
530
      if (window->maximize_horizontally_after_placement ||
 
531
          window->maximize_vertically_after_placement   ||
 
532
          window->fullscreen_after_placement)
 
533
        {
 
534
          /* define a sane saved_rect so that the user can unmaximize or
 
535
           * make unfullscreen to something reasonable.
 
536
           */
 
537
          if (info->current.width >= info->work_area_xinerama.width)
 
538
            {
 
539
              info->current.width = .75 * info->work_area_xinerama.width;
 
540
              info->current.x = info->work_area_xinerama.x +
 
541
                       .125 * info->work_area_xinerama.width;
 
542
            }
 
543
          if (info->current.height >= info->work_area_xinerama.height)
 
544
            {
 
545
              info->current.height = .75 * info->work_area_xinerama.height;
 
546
              info->current.y = info->work_area_xinerama.y +
 
547
                       .083 * info->work_area_xinerama.height;
 
548
            }
 
549
 
 
550
          if (window->maximize_horizontally_after_placement ||
 
551
              window->maximize_vertically_after_placement)
 
552
            meta_window_maximize_internal (window,   
 
553
                (window->maximize_horizontally_after_placement ?
 
554
                 META_MAXIMIZE_HORIZONTAL : 0 ) |
 
555
                (window->maximize_vertically_after_placement ?
 
556
                 META_MAXIMIZE_VERTICAL : 0), &info->current);
 
557
 
 
558
          /* maximization may have changed frame geometry */
 
559
          if (window->frame && !window->fullscreen)
 
560
            meta_frame_calc_geometry (window->frame, info->fgeom);
 
561
 
 
562
          if (window->fullscreen_after_placement)
 
563
            {
 
564
              window->saved_rect = info->current;
 
565
              window->fullscreen = TRUE;
 
566
              window->fullscreen_after_placement = FALSE;
 
567
            }
 
568
 
 
569
          window->maximize_horizontally_after_placement = FALSE;
 
570
          window->maximize_vertically_after_placement = FALSE;
 
571
        }
 
572
      if (window->minimize_after_placement)
 
573
        {
 
574
          meta_window_minimize (window);
 
575
          window->minimize_after_placement = FALSE;
 
576
        }
 
577
    }
 
578
}
 
579
 
 
580
static void
 
581
update_onscreen_requirements (MetaWindow     *window,
 
582
                              ConstraintInfo *info)
 
583
{
 
584
  gboolean old;
 
585
 
 
586
  /* We only apply the various onscreen requirements to normal windows */
 
587
  if (window->type == META_WINDOW_DESKTOP ||
 
588
      window->type == META_WINDOW_DOCK)
 
589
    return;
 
590
 
 
591
  /* We don't want to update the requirements for fullscreen windows;
 
592
   * fullscreen windows are specially handled anyway, and it updating
 
593
   * the requirements when windows enter fullscreen mode mess up the
 
594
   * handling of the window when it leaves that mode (especially when
 
595
   * the application sends a bunch of configurerequest events).  See
 
596
   * #353699.
 
597
   */
 
598
  if (window->fullscreen)
 
599
    return;
 
600
 
 
601
  /* USABILITY NOTE: Naturally, I only want the require_fully_onscreen,
 
602
   * require_on_single_xinerama, and require_titlebar_visible flags to
 
603
   * *become false* due to user interactions (which is allowed since
 
604
   * certain constraints are ignored for user interactions regardless of
 
605
   * the setting of these flags).  However, whether to make these flags
 
606
   * *become true* due to just an application interaction is a little
 
607
   * trickier.  It's possible that users may find not doing that strange
 
608
   * since two application interactions that resize in opposite ways don't
 
609
   * necessarily end up cancelling--but it may also be strange for the user
 
610
   * to have an application resize the window so that it's onscreen, the
 
611
   * user forgets about it, and then later the app is able to resize itself
 
612
   * off the screen.  Anyway, for now, I think the latter is the more
 
613
   * problematic case but this may need to be revisited.
 
614
   */
 
615
 
 
616
  /* The require onscreen/on-single-xinerama and titlebar_visible
 
617
   * stuff is relative to the outer window, not the inner
 
618
   */
 
619
  extend_by_frame (&info->current, info->fgeom);
 
620
 
 
621
  /* Update whether we want future constraint runs to require the
 
622
   * window to be on fully onscreen.
 
623
   */
 
624
  old = window->require_fully_onscreen;
 
625
  window->require_fully_onscreen =
 
626
    meta_rectangle_contained_in_region (info->usable_screen_region,
 
627
                                        &info->current);
 
628
  if (old ^ window->require_fully_onscreen)
 
629
    meta_topic (META_DEBUG_GEOMETRY,
 
630
                "require_fully_onscreen for %s toggled to %s\n",
 
631
                window->desc,
 
632
                window->require_fully_onscreen ? "TRUE" : "FALSE");
 
633
 
 
634
  /* Update whether we want future constraint runs to require the
 
635
   * window to be on a single xinerama.
 
636
   */
 
637
  old = window->require_on_single_xinerama;
 
638
  window->require_on_single_xinerama =
 
639
    meta_rectangle_contained_in_region (info->usable_xinerama_region,
 
640
                                        &info->current);
 
641
  if (old ^ window->require_on_single_xinerama)
 
642
    meta_topic (META_DEBUG_GEOMETRY,
 
643
                "require_on_single_xinerama for %s toggled to %s\n",
 
644
                window->desc, 
 
645
                window->require_on_single_xinerama ? "TRUE" : "FALSE");
 
646
 
 
647
  /* Update whether we want future constraint runs to require the
 
648
   * titlebar to be visible.
 
649
   */
 
650
  if (window->frame && window->decorated)
 
651
    {
 
652
      MetaRectangle titlebar_rect;
 
653
 
 
654
      titlebar_rect = info->current;
 
655
      titlebar_rect.height = info->fgeom->top_height;
 
656
      old = window->require_titlebar_visible;
 
657
      window->require_titlebar_visible =
 
658
        meta_rectangle_overlaps_with_region (info->usable_screen_region,
 
659
                                             &titlebar_rect);
 
660
      if (old ^ window->require_titlebar_visible)
 
661
        meta_topic (META_DEBUG_GEOMETRY,
 
662
                    "require_titlebar_visible for %s toggled to %s\n",
 
663
                    window->desc,
 
664
                    window->require_titlebar_visible ? "TRUE" : "FALSE");
 
665
    }
 
666
 
 
667
  /* Don't forget to restore the position of the window */
 
668
  unextend_by_frame (&info->current, info->fgeom);
 
669
}
 
670
 
 
671
static void
 
672
extend_by_frame (MetaRectangle           *rect,
 
673
                 const MetaFrameGeometry *fgeom)
 
674
{
 
675
  rect->x -= fgeom->left_width;
 
676
  rect->y -= fgeom->top_height;
 
677
  rect->width  += fgeom->left_width + fgeom->right_width;
 
678
  rect->height += fgeom->top_height + fgeom->bottom_height;
 
679
}
 
680
 
 
681
static void
 
682
unextend_by_frame (MetaRectangle           *rect,
 
683
                   const MetaFrameGeometry *fgeom)
 
684
{
 
685
  rect->x += fgeom->left_width;
 
686
  rect->y += fgeom->top_height;
 
687
  rect->width  -= fgeom->left_width + fgeom->right_width;
 
688
  rect->height -= fgeom->top_height + fgeom->bottom_height;
 
689
}
 
690
 
 
691
static inline void
 
692
get_size_limits (const MetaWindow        *window,
 
693
                 const MetaFrameGeometry *fgeom,
 
694
                 gboolean                 include_frame,
 
695
                 MetaRectangle *min_size,
 
696
                 MetaRectangle *max_size)
 
697
{
 
698
  /* We pack the results into MetaRectangle structs just for convienience; we
 
699
   * don't actually use the position of those rects.
 
700
   */
 
701
  min_size->width  = window->size_hints.min_width;
 
702
  min_size->height = window->size_hints.min_height;
 
703
  max_size->width  = window->size_hints.max_width;
 
704
  max_size->height = window->size_hints.max_height;
 
705
 
 
706
  if (include_frame)
 
707
    {
 
708
      int fw = fgeom->left_width + fgeom->right_width;
 
709
      int fh = fgeom->top_height + fgeom->bottom_height;
 
710
 
 
711
      min_size->width  += fw;
 
712
      min_size->height += fh;
 
713
      max_size->width  += fw;
 
714
      max_size->height += fh;
 
715
    }
 
716
}
 
717
 
 
718
static gboolean
 
719
constrain_maximization (MetaWindow         *window,
 
720
                        ConstraintInfo     *info,
 
721
                        ConstraintPriority  priority,
 
722
                        gboolean            check_only)
 
723
{
 
724
  MetaRectangle target_size;
 
725
  MetaRectangle min_size, max_size;
 
726
  gboolean hminbad, vminbad;
 
727
  gboolean horiz_equal, vert_equal;
 
728
  gboolean constraint_already_satisfied;
 
729
 
 
730
  if (priority > PRIORITY_MAXIMIZATION)
 
731
    return TRUE;
 
732
 
 
733
  /* Determine whether constraint applies; exit if it doesn't */
 
734
  if (!window->maximized_horizontally && !window->maximized_vertically)
 
735
    return TRUE;
 
736
 
 
737
  /* Calculate target_size = maximized size of (window + frame) */
 
738
  if (window->maximized_horizontally && window->maximized_vertically)
 
739
    target_size = info->work_area_xinerama;
 
740
  else
 
741
    {
 
742
      /* Amount of maximization possible in a single direction depends
 
743
       * on which struts could occlude the window given its current
 
744
       * position.  For example, a vertical partial strut on the right
 
745
       * is only relevant for a horizontally maximized window when the
 
746
       * window is at a vertical position where it could be occluded
 
747
       * by that partial strut.
 
748
       */
 
749
      MetaDirection  direction;
 
750
      GSList        *active_workspace_struts;
 
751
 
 
752
      if (window->maximized_horizontally)
 
753
        direction = META_DIRECTION_HORIZONTAL;
 
754
      else
 
755
        direction = META_DIRECTION_VERTICAL;
 
756
      active_workspace_struts = window->screen->active_workspace->all_struts;
 
757
 
 
758
      target_size = info->current;
 
759
      extend_by_frame (&target_size, info->fgeom);
 
760
      meta_rectangle_expand_to_avoiding_struts (&target_size,
 
761
                                                &info->entire_xinerama,
 
762
                                                direction,
 
763
                                                active_workspace_struts);
 
764
   }
 
765
  /* Now make target_size = maximized size of client window */
 
766
  unextend_by_frame (&target_size, info->fgeom);
 
767
 
 
768
  /* Check min size constraints; max size constraints are ignored for maximized
 
769
   * windows, as per bug 327543.
 
770
   */
 
771
  get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
 
772
  hminbad = target_size.width < min_size.width && window->maximized_horizontally;
 
773
  vminbad = target_size.height < min_size.height && window->maximized_vertically;
 
774
  if (hminbad || vminbad)
 
775
    return TRUE;
 
776
 
 
777
  /* Determine whether constraint is already satisfied; exit if it is */
 
778
  horiz_equal = target_size.x      == info->current.x &&
 
779
                target_size.width  == info->current.width;
 
780
  vert_equal  = target_size.y      == info->current.y &&
 
781
                target_size.height == info->current.height;
 
782
  constraint_already_satisfied =
 
783
    (horiz_equal || !window->maximized_horizontally) &&
 
784
    (vert_equal  || !window->maximized_vertically);
 
785
  if (check_only || constraint_already_satisfied)
 
786
    return constraint_already_satisfied;
 
787
 
 
788
  /*** Enforce constraint ***/
 
789
  if (window->maximized_horizontally)
 
790
    {
 
791
      info->current.x      = target_size.x;
 
792
      info->current.width  = target_size.width;
 
793
    }
 
794
  if (window->maximized_vertically)
 
795
    {
 
796
      info->current.y      = target_size.y;
 
797
      info->current.height = target_size.height;
 
798
    }
 
799
  return TRUE;
 
800
}
 
801
 
 
802
static gboolean
 
803
constrain_fullscreen (MetaWindow         *window,
 
804
                      ConstraintInfo     *info,
 
805
                      ConstraintPriority  priority,
 
806
                      gboolean            check_only)
 
807
{
 
808
  MetaRectangle min_size, max_size, xinerama;
 
809
  gboolean too_big, too_small, constraint_already_satisfied;
 
810
 
 
811
  if (priority > PRIORITY_FULLSCREEN)
 
812
    return TRUE;
 
813
 
 
814
  /* Determine whether constraint applies; exit if it doesn't */
 
815
  if (!window->fullscreen)
 
816
    return TRUE;
 
817
 
 
818
  xinerama = info->entire_xinerama;
 
819
 
 
820
  get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
 
821
  too_big =   !meta_rectangle_could_fit_rect (&xinerama, &min_size);
 
822
  too_small = !meta_rectangle_could_fit_rect (&max_size, &xinerama);
 
823
  if (too_big || too_small)
 
824
    return TRUE;
 
825
 
 
826
  /* Determine whether constraint is already satisfied; exit if it is */
 
827
  constraint_already_satisfied =
 
828
    meta_rectangle_equal (&info->current, &xinerama);
 
829
  if (check_only || constraint_already_satisfied)
 
830
    return constraint_already_satisfied;
 
831
 
 
832
  /*** Enforce constraint ***/
 
833
  info->current = xinerama;
 
834
  return TRUE;
 
835
}
 
836
 
 
837
static gboolean
 
838
constrain_size_increments (MetaWindow         *window,
 
839
                           ConstraintInfo     *info,
 
840
                           ConstraintPriority  priority,
 
841
                           gboolean            check_only)
 
842
{
 
843
  int bh, hi, bw, wi, extra_height, extra_width;
 
844
  int new_width, new_height;
 
845
  gboolean constraint_already_satisfied;
 
846
  MetaRectangle *start_rect;
 
847
 
 
848
  if (priority > PRIORITY_SIZE_HINTS_INCREMENTS)
 
849
    return TRUE;
 
850
 
 
851
  /* Determine whether constraint applies; exit if it doesn't */
 
852
  if (META_WINDOW_MAXIMIZED (window) || window->fullscreen || 
 
853
      info->action_type == ACTION_MOVE)
 
854
    return TRUE;
 
855
 
 
856
  /* Determine whether constraint is already satisfied; exit if it is */
 
857
  bh = window->size_hints.base_height;
 
858
  hi = window->size_hints.height_inc;
 
859
  bw = window->size_hints.base_width;
 
860
  wi = window->size_hints.width_inc;
 
861
  extra_height = (info->current.height - bh) % hi;
 
862
  extra_width  = (info->current.width  - bw) % wi;
 
863
  /* ignore size increments for maximized windows */
 
864
  if (window->maximized_horizontally)
 
865
    extra_width *= 0;
 
866
  if (window->maximized_vertically)
 
867
    extra_height *= 0;
 
868
  /* constraint is satisfied iff there is no extra height or width */
 
869
  constraint_already_satisfied = 
 
870
    (extra_height == 0 && extra_width == 0);
 
871
 
 
872
  if (check_only || constraint_already_satisfied)
 
873
    return constraint_already_satisfied;
 
874
 
 
875
  /*** Enforce constraint ***/
 
876
  new_width  = info->current.width  - extra_width;
 
877
  new_height = info->current.height - extra_height;
 
878
 
 
879
  /* Adjusting down instead of up (as done in the above two lines) may
 
880
   * violate minimum size constraints; fix the adjustment if this
 
881
   * happens.
 
882
   */
 
883
  if (new_width  < window->size_hints.min_width)
 
884
    new_width  += ((window->size_hints.min_width  - new_width)/wi  + 1)*wi;
 
885
  if (new_height < window->size_hints.min_height)
 
886
    new_height += ((window->size_hints.min_height - new_height)/hi + 1)*hi;
 
887
 
 
888
  /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
 
889
   * See bug 448183
 
890
   */
 
891
  if (info->action_type == ACTION_MOVE_AND_RESIZE)
 
892
    start_rect = &info->current;
 
893
  else
 
894
    start_rect = &info->orig;
 
895
    
 
896
  /* Resize to the new size */
 
897
  meta_rectangle_resize_with_gravity (start_rect,
 
898
                                      &info->current, 
 
899
                                      info->resize_gravity,
 
900
                                      new_width,
 
901
                                      new_height);
 
902
  return TRUE;
 
903
}
 
904
 
 
905
static gboolean
 
906
constrain_size_limits (MetaWindow         *window,
 
907
                       ConstraintInfo     *info,
 
908
                       ConstraintPriority  priority,
 
909
                       gboolean            check_only)
 
910
{
 
911
  MetaRectangle min_size, max_size;
 
912
  gboolean too_big, too_small, constraint_already_satisfied;
 
913
  int new_width, new_height;
 
914
  MetaRectangle *start_rect;
 
915
 
 
916
  if (priority > PRIORITY_SIZE_HINTS_LIMITS)
 
917
    return TRUE;
 
918
 
 
919
  /* Determine whether constraint applies; exit if it doesn't.
 
920
   *
 
921
   * Note: The old code didn't apply this constraint for fullscreen or
 
922
   * maximized windows--but that seems odd to me.  *shrug*
 
923
   */
 
924
  if (info->action_type == ACTION_MOVE)
 
925
    return TRUE;
 
926
 
 
927
  /* Determine whether constraint is already satisfied; exit if it is */
 
928
  get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
 
929
  /* We ignore max-size limits for maximized windows; see #327543 */
 
930
  if (window->maximized_horizontally)
 
931
    max_size.width = MAX (max_size.width, info->current.width);
 
932
  if (window->maximized_vertically)
 
933
    max_size.height = MAX (max_size.height, info->current.height);
 
934
  too_small = !meta_rectangle_could_fit_rect (&info->current, &min_size);
 
935
  too_big   = !meta_rectangle_could_fit_rect (&max_size, &info->current);
 
936
  constraint_already_satisfied = !too_big && !too_small;
 
937
  if (check_only || constraint_already_satisfied)
 
938
    return constraint_already_satisfied;
 
939
 
 
940
  /*** Enforce constraint ***/
 
941
  new_width  = CLAMP (info->current.width,  min_size.width,  max_size.width);
 
942
  new_height = CLAMP (info->current.height, min_size.height, max_size.height);
 
943
  
 
944
  /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
 
945
   * See bug 448183
 
946
   */
 
947
  if (info->action_type == ACTION_MOVE_AND_RESIZE)
 
948
    start_rect = &info->current;
 
949
  else
 
950
    start_rect = &info->orig;
 
951
  
 
952
  meta_rectangle_resize_with_gravity (start_rect,
 
953
                                      &info->current, 
 
954
                                      info->resize_gravity,
 
955
                                      new_width,
 
956
                                      new_height);
 
957
  return TRUE;
 
958
}
 
959
 
 
960
static gboolean
 
961
constrain_aspect_ratio (MetaWindow         *window,
 
962
                        ConstraintInfo     *info,
 
963
                        ConstraintPriority  priority,
 
964
                        gboolean            check_only)
 
965
{
 
966
  double minr, maxr;
 
967
  gboolean constraints_are_inconsistent, constraint_already_satisfied;
 
968
  int fudge, new_width, new_height;
 
969
  double best_width, best_height;
 
970
  double alt_width, alt_height;
 
971
  MetaRectangle *start_rect;
 
972
 
 
973
  if (priority > PRIORITY_ASPECT_RATIO)
 
974
    return TRUE;
 
975
 
 
976
  /* Determine whether constraint applies; exit if it doesn't. */
 
977
  minr =         window->size_hints.min_aspect.x /
 
978
         (double)window->size_hints.min_aspect.y;
 
979
  maxr =         window->size_hints.max_aspect.x /
 
980
         (double)window->size_hints.max_aspect.y;
 
981
  constraints_are_inconsistent = minr > maxr;
 
982
  if (constraints_are_inconsistent ||
 
983
      META_WINDOW_MAXIMIZED (window) || window->fullscreen || 
 
984
      info->action_type == ACTION_MOVE)
 
985
    return TRUE;
 
986
 
 
987
  /* Determine whether constraint is already satisfied; exit if it is.  We
 
988
   * need the following to hold:
 
989
   *
 
990
   *                 width
 
991
   *         minr <= ------ <= maxr
 
992
   *                 height
 
993
   *
 
994
   * But we need to allow for some slight fudging since width and height
 
995
   * are integers instead of floating point numbers (this is particularly
 
996
   * important when minr == maxr), so we allow width and height to be off
 
997
   * a little bit from strictly satisfying these equations.  For just one
 
998
   * sided resizing, we have to make the fudge factor a little bigger
 
999
   * because of how meta_rectangle_resize_with_gravity treats those as
 
1000
   * being a resize increment (FIXME: I should handle real resize
 
1001
   * increments better here...)
 
1002
   */
 
1003
  switch (info->resize_gravity)
 
1004
    {
 
1005
    case WestGravity:
 
1006
    case NorthGravity:
 
1007
    case SouthGravity:
 
1008
    case EastGravity:
 
1009
      fudge = 2;
 
1010
      break;
 
1011
 
 
1012
    case NorthWestGravity:
 
1013
    case SouthWestGravity:
 
1014
    case CenterGravity:
 
1015
    case NorthEastGravity:
 
1016
    case SouthEastGravity:
 
1017
    case StaticGravity:
 
1018
    default:
 
1019
      fudge = 1;
 
1020
      break;
 
1021
    }
 
1022
  constraint_already_satisfied = 
 
1023
    info->current.width - (info->current.height * minr ) > -minr*fudge &&
 
1024
    info->current.width - (info->current.height * maxr ) <  maxr*fudge;
 
1025
  if (check_only || constraint_already_satisfied)
 
1026
    return constraint_already_satisfied;
 
1027
 
 
1028
  /*** Enforce constraint ***/
 
1029
  new_width = info->current.width;
 
1030
  new_height = info->current.height;
 
1031
 
 
1032
  switch (info->resize_gravity)
 
1033
    {
 
1034
    case WestGravity:
 
1035
    case EastGravity:
 
1036
      /* Yeah, I suck for doing implicit rounding -- sue me */
 
1037
      new_height = CLAMP (new_height, new_width / maxr,  new_width / minr);
 
1038
      break;
 
1039
 
 
1040
    case NorthGravity:
 
1041
    case SouthGravity:
 
1042
      /* Yeah, I suck for doing implicit rounding -- sue me */
 
1043
      new_width  = CLAMP (new_width,  new_height * minr, new_height * maxr);
 
1044
      break;
 
1045
 
 
1046
    case NorthWestGravity:
 
1047
    case SouthWestGravity:
 
1048
    case CenterGravity:
 
1049
    case NorthEastGravity:
 
1050
    case SouthEastGravity:
 
1051
    case StaticGravity:
 
1052
    default:
 
1053
      /* Find what width would correspond to new_height, and what height would
 
1054
       * correspond to new_width */
 
1055
      alt_width  = CLAMP (new_width,  new_height * minr, new_height * maxr);
 
1056
      alt_height = CLAMP (new_height, new_width / maxr,  new_width / minr);
 
1057
 
 
1058
      /* The line connecting the points (alt_width, new_height) and
 
1059
       * (new_width, alt_height) provide a range of
 
1060
       * valid-for-the-aspect-ratio-constraint sizes.  We want the
 
1061
       * size in that range closest to the value requested, i.e. the
 
1062
       * point on the line which is closest to the point (new_width,
 
1063
       * new_height)
 
1064
       */
 
1065
      meta_rectangle_find_linepoint_closest_to_point (alt_width, new_height,
 
1066
                                                      new_width, alt_height,
 
1067
                                                      new_width, new_height,
 
1068
                                                      &best_width, &best_height);
 
1069
 
 
1070
      /* Yeah, I suck for doing implicit rounding -- sue me */
 
1071
      new_width  = best_width;
 
1072
      new_height = best_height;
 
1073
 
 
1074
      break;
 
1075
    }
 
1076
 
 
1077
  /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
 
1078
   * See bug 448183
 
1079
   */
 
1080
  if (info->action_type == ACTION_MOVE_AND_RESIZE)
 
1081
    start_rect = &info->current;
 
1082
  else
 
1083
    start_rect = &info->orig;
 
1084
 
 
1085
  meta_rectangle_resize_with_gravity (start_rect,
 
1086
                                      &info->current, 
 
1087
                                      info->resize_gravity,
 
1088
                                      new_width,
 
1089
                                      new_height);
 
1090
 
 
1091
  return TRUE;
 
1092
}
 
1093
 
 
1094
static gboolean
 
1095
do_screen_and_xinerama_relative_constraints (
 
1096
  MetaWindow     *window,
 
1097
  GList          *region_spanning_rectangles,
 
1098
  ConstraintInfo *info,
 
1099
  gboolean        check_only)
 
1100
{
 
1101
  gboolean exit_early = FALSE, constraint_satisfied;
 
1102
  MetaRectangle how_far_it_can_be_smushed, min_size, max_size;
 
1103
 
 
1104
#ifdef WITH_VERBOSE_MODE
 
1105
  if (meta_is_verbose ())
 
1106
    {
 
1107
      /* First, log some debugging information */
 
1108
      char spanning_region[1 + 28 * g_list_length (region_spanning_rectangles)];
 
1109
 
 
1110
      meta_topic (META_DEBUG_GEOMETRY,
 
1111
             "screen/xinerama constraint; region_spanning_rectangles: %s\n",
 
1112
             meta_rectangle_region_to_string (region_spanning_rectangles, ", ",
 
1113
                                              spanning_region));
 
1114
    }
 
1115
#endif
 
1116
 
 
1117
  /* Determine whether constraint applies; exit if it doesn't */
 
1118
  how_far_it_can_be_smushed = info->current;
 
1119
  get_size_limits (window, info->fgeom, TRUE, &min_size, &max_size);
 
1120
  extend_by_frame (&info->current, info->fgeom);
 
1121
 
 
1122
  if (info->action_type != ACTION_MOVE)
 
1123
    {
 
1124
      if (!(info->fixed_directions & FIXED_DIRECTION_X))
 
1125
        how_far_it_can_be_smushed.width = min_size.width;
 
1126
 
 
1127
      if (!(info->fixed_directions & FIXED_DIRECTION_Y))
 
1128
        how_far_it_can_be_smushed.height = min_size.height;
 
1129
    }
 
1130
  if (!meta_rectangle_could_fit_in_region (region_spanning_rectangles,
 
1131
                                           &how_far_it_can_be_smushed))
 
1132
    exit_early = TRUE;
 
1133
 
 
1134
  /* Determine whether constraint is already satisfied; exit if it is */
 
1135
  constraint_satisfied = 
 
1136
    meta_rectangle_contained_in_region (region_spanning_rectangles,
 
1137
                                        &info->current);
 
1138
  if (exit_early || constraint_satisfied || check_only)
 
1139
    {
 
1140
      unextend_by_frame (&info->current, info->fgeom);
 
1141
      return constraint_satisfied;
 
1142
    }
 
1143
 
 
1144
  /* Enforce constraint */
 
1145
 
 
1146
  /* Clamp rectangle size for resize or move+resize actions */
 
1147
  if (info->action_type != ACTION_MOVE)
 
1148
    meta_rectangle_clamp_to_fit_into_region (region_spanning_rectangles,
 
1149
                                             info->fixed_directions,
 
1150
                                             &info->current,
 
1151
                                             &min_size);
 
1152
 
 
1153
  if (info->is_user_action && info->action_type == ACTION_RESIZE)
 
1154
    /* For user resize, clip to the relevant region */
 
1155
    meta_rectangle_clip_to_region (region_spanning_rectangles,
 
1156
                                   info->fixed_directions,
 
1157
                                   &info->current);
 
1158
  else
 
1159
    /* For everything else, shove the rectangle into the relevant region */
 
1160
    meta_rectangle_shove_into_region (region_spanning_rectangles,
 
1161
                                      info->fixed_directions,
 
1162
                                      &info->current);
 
1163
 
 
1164
  unextend_by_frame (&info->current, info->fgeom);
 
1165
  return TRUE;
 
1166
}
 
1167
 
 
1168
static gboolean
 
1169
constrain_to_single_xinerama (MetaWindow         *window,
 
1170
                              ConstraintInfo     *info,
 
1171
                              ConstraintPriority  priority,
 
1172
                              gboolean            check_only)
 
1173
{
 
1174
  if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA)
 
1175
    return TRUE;
 
1176
 
 
1177
  /* Exit early if we know the constraint won't apply--note that this constraint
 
1178
   * is only meant for normal windows (e.g. we don't want docks to be shoved 
 
1179
   * "onscreen" by their own strut) and we can't apply it to frameless windows
 
1180
   * or else users will be unable to move windows such as XMMS across xineramas.
 
1181
   */
 
1182
  if (window->type == META_WINDOW_DESKTOP   ||
 
1183
      window->type == META_WINDOW_DOCK      ||
 
1184
      window->screen->n_xinerama_infos == 1 ||
 
1185
      !window->require_on_single_xinerama   ||
 
1186
      !window->frame                        ||
 
1187
      info->is_user_action)
 
1188
    return TRUE;
 
1189
 
 
1190
  /* Have a helper function handle the constraint for us */
 
1191
  return do_screen_and_xinerama_relative_constraints (window, 
 
1192
                                                 info->usable_xinerama_region,
 
1193
                                                 info,
 
1194
                                                 check_only);
 
1195
}
 
1196
 
 
1197
static gboolean
 
1198
constrain_fully_onscreen (MetaWindow         *window,
 
1199
                          ConstraintInfo     *info,
 
1200
                          ConstraintPriority  priority,
 
1201
                          gboolean            check_only)
 
1202
{
 
1203
  if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA)
 
1204
    return TRUE;
 
1205
 
 
1206
  /* Exit early if we know the constraint won't apply--note that this constraint
 
1207
   * is only meant for normal windows (e.g. we don't want docks to be shoved 
 
1208
   * "onscreen" by their own strut).
 
1209
   */
 
1210
  if (window->type == META_WINDOW_DESKTOP ||
 
1211
      window->type == META_WINDOW_DOCK    ||
 
1212
      window->fullscreen                  ||
 
1213
      !window->require_fully_onscreen     || 
 
1214
      info->is_user_action)
 
1215
    return TRUE;
 
1216
 
 
1217
  /* Have a helper function handle the constraint for us */
 
1218
  return do_screen_and_xinerama_relative_constraints (window, 
 
1219
                                                 info->usable_screen_region,
 
1220
                                                 info,
 
1221
                                                 check_only);
 
1222
}
 
1223
 
 
1224
static gboolean
 
1225
constrain_titlebar_visible (MetaWindow         *window,
 
1226
                            ConstraintInfo     *info,
 
1227
                            ConstraintPriority  priority,
 
1228
                            gboolean            check_only)
 
1229
{
 
1230
  gboolean unconstrained_user_action;
 
1231
  gboolean retval;
 
1232
  int bottom_amount;
 
1233
  int horiz_amount_offscreen, vert_amount_offscreen;
 
1234
  int horiz_amount_onscreen,  vert_amount_onscreen;
 
1235
 
 
1236
  if (priority > PRIORITY_TITLEBAR_VISIBLE)
 
1237
    return TRUE;
 
1238
 
 
1239
  /* Allow the titlebar beyond the top of the screen only if the user wasn't
 
1240
   * clicking on the frame to start the move.
 
1241
   */
 
1242
  unconstrained_user_action =
 
1243
    info->is_user_action && !window->display->grab_frame_action;
 
1244
 
 
1245
  /* Exit early if we know the constraint won't apply--note that this constraint
 
1246
   * is only meant for normal windows (e.g. we don't want docks to be shoved 
 
1247
   * "onscreen" by their own strut).
 
1248
   */
 
1249
  if (window->type == META_WINDOW_DESKTOP ||
 
1250
      window->type == META_WINDOW_DOCK    ||
 
1251
      window->fullscreen                  ||
 
1252
      !window->require_titlebar_visible   ||
 
1253
      !window->decorated                  ||
 
1254
      unconstrained_user_action)
 
1255
    return TRUE;
 
1256
 
 
1257
  /* Determine how much offscreen things are allowed.  We first need to
 
1258
   * figure out how much must remain on the screen.  For that, we use 25%
 
1259
   * window width/height but clamp to the range of (10,75) pixels.  This is
 
1260
   * somewhat of a seat of my pants random guess at what might look good.
 
1261
   * Then, the amount that is allowed off is just the window size minus
 
1262
   * this amount (but no less than 0 for tiny windows).
 
1263
   */
 
1264
  horiz_amount_onscreen = info->current.width  / 4;
 
1265
  vert_amount_onscreen  = info->current.height / 4;
 
1266
  horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
 
1267
  vert_amount_onscreen  = CLAMP (vert_amount_onscreen,  10, 75);
 
1268
  horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
 
1269
  vert_amount_offscreen  = info->current.height - vert_amount_onscreen;
 
1270
  horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
 
1271
  vert_amount_offscreen  = MAX (vert_amount_offscreen,  0);
 
1272
  /* Allow the titlebar to touch the bottom panel;  If there is no titlebar,
 
1273
   * require vert_amount to remain on the screen.
 
1274
   */
 
1275
  if (window->frame)
 
1276
    {
 
1277
      bottom_amount = info->current.height + info->fgeom->bottom_height;
 
1278
      vert_amount_onscreen = info->fgeom->top_height;
 
1279
    }
 
1280
  else
 
1281
    bottom_amount = vert_amount_offscreen;
 
1282
 
 
1283
  /* Extend the region, have a helper function handle the constraint,
 
1284
   * then return the region to its original size.
 
1285
   */
 
1286
  meta_rectangle_expand_region_conditionally (info->usable_screen_region,
 
1287
                                              horiz_amount_offscreen,
 
1288
                                              horiz_amount_offscreen, 
 
1289
                                              0, /* Don't let titlebar off */
 
1290
                                              bottom_amount,
 
1291
                                              horiz_amount_onscreen,
 
1292
                                              vert_amount_onscreen);
 
1293
  retval =
 
1294
    do_screen_and_xinerama_relative_constraints (window, 
 
1295
                                                 info->usable_screen_region,
 
1296
                                                 info,
 
1297
                                                 check_only);
 
1298
  meta_rectangle_expand_region_conditionally (info->usable_screen_region,
 
1299
                                              -horiz_amount_offscreen,
 
1300
                                              -horiz_amount_offscreen,
 
1301
                                              0, /* Don't let titlebar off */
 
1302
                                              -bottom_amount,
 
1303
                                              horiz_amount_onscreen,
 
1304
                                              vert_amount_onscreen);
 
1305
 
 
1306
  return retval;
 
1307
}
 
1308
 
 
1309
static gboolean
 
1310
constrain_partially_onscreen (MetaWindow         *window,
 
1311
                              ConstraintInfo     *info,
 
1312
                              ConstraintPriority  priority,
 
1313
                              gboolean            check_only)
 
1314
{
 
1315
  gboolean retval;
 
1316
  int top_amount, bottom_amount;
 
1317
  int horiz_amount_offscreen, vert_amount_offscreen;
 
1318
  int horiz_amount_onscreen,  vert_amount_onscreen;
 
1319
 
 
1320
  if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA)
 
1321
    return TRUE;
 
1322
 
 
1323
  /* Exit early if we know the constraint won't apply--note that this constraint
 
1324
   * is only meant for normal windows (e.g. we don't want docks to be shoved 
 
1325
   * "onscreen" by their own strut).
 
1326
   */
 
1327
  if (window->type == META_WINDOW_DESKTOP ||
 
1328
      window->type == META_WINDOW_DOCK)
 
1329
    return TRUE;
 
1330
 
 
1331
  /* Determine how much offscreen things are allowed.  We first need to
 
1332
   * figure out how much must remain on the screen.  For that, we use 25%
 
1333
   * window width/height but clamp to the range of (10,75) pixels.  This is
 
1334
   * somewhat of a seat of my pants random guess at what might look good.
 
1335
   * Then, the amount that is allowed off is just the window size minus
 
1336
   * this amount (but no less than 0 for tiny windows).
 
1337
   */
 
1338
  horiz_amount_onscreen = info->current.width  / 4;
 
1339
  vert_amount_onscreen  = info->current.height / 4;
 
1340
  horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
 
1341
  vert_amount_onscreen  = CLAMP (vert_amount_onscreen,  10, 75);
 
1342
  horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
 
1343
  vert_amount_offscreen  = info->current.height - vert_amount_onscreen;
 
1344
  horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
 
1345
  vert_amount_offscreen  = MAX (vert_amount_offscreen,  0);
 
1346
  top_amount = vert_amount_offscreen;
 
1347
  /* Allow the titlebar to touch the bottom panel;  If there is no titlebar,
 
1348
   * require vert_amount to remain on the screen.
 
1349
   */
 
1350
  if (window->frame)
 
1351
    {
 
1352
      bottom_amount = info->current.height + info->fgeom->bottom_height;
 
1353
      vert_amount_onscreen = info->fgeom->top_height;
 
1354
    }
 
1355
  else
 
1356
    bottom_amount = vert_amount_offscreen;
 
1357
 
 
1358
  /* Extend the region, have a helper function handle the constraint,
 
1359
   * then return the region to its original size.
 
1360
   */
 
1361
  meta_rectangle_expand_region_conditionally (info->usable_screen_region,
 
1362
                                              horiz_amount_offscreen,
 
1363
                                              horiz_amount_offscreen, 
 
1364
                                              top_amount,
 
1365
                                              bottom_amount,
 
1366
                                              horiz_amount_onscreen,
 
1367
                                              vert_amount_onscreen);
 
1368
  retval =
 
1369
    do_screen_and_xinerama_relative_constraints (window, 
 
1370
                                                 info->usable_screen_region,
 
1371
                                                 info,
 
1372
                                                 check_only);
 
1373
  meta_rectangle_expand_region_conditionally (info->usable_screen_region,
 
1374
                                              -horiz_amount_offscreen,
 
1375
                                              -horiz_amount_offscreen,
 
1376
                                              -top_amount,
 
1377
                                              -bottom_amount,
 
1378
                                              horiz_amount_onscreen,
 
1379
                                              vert_amount_onscreen);
 
1380
 
 
1381
  return retval;
 
1382
}