~muktupavels/metacity/adwaita-icon-theme-lp-1414613

« back to all changes in this revision

Viewing changes to src/constraints.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2005-10-03 22:44:28 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20051003224428-ft31gkmz12qpzohj
Tags: 1:2.12.1-0ubuntu1
* New upstream release:
  - Thanks to Ray Strode, Havoc Pennington, and Elijah Newren for
    improvements in this release.
  - Truncate ridiculously long titles to avoid crashing or letting the
    pager crash (Ray, Havoc, Elijah) [#315070] (Ubuntu: #15995)
  - Get the tabbing window outline to work with gtk+ 2.8.4 again
    (Elijah) [#317528] (Ubuntu: #16589)
  - Translations: Mahay Alam Khan (bn), Francisco Javier F. Serrador (es), 
    Ivar Smolin (et), I\uffffaki Larra\uffffaga Murgoitio (eu), Luca 
    Ferretti (it), Christian Rose (sv), Clytie Siddall (vi), Funda 
    Wang (zh_CN)
* debian/control.in:
  - Bumped Standards-Version.
* debian/patches/003_bordersdrawingfix.patch:
  - dropped, fixed upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Metacity size/position constraints */
 
2
 
 
3
/*
 
4
 * Copyright (C) 2002, 2003 Red Hat, Inc.
 
5
 * Copyright (C) 2003, 2004 Rob Adams
 
6
 *
 
7
 * This program is free software; you can redistribute it and/or
 
8
 * modify it under the terms of the GNU General Public License as
 
9
 * published by the Free Software Foundation; either version 2 of the
 
10
 * License, or (at your option) any later version.
 
11
 *
 
12
 * This program is distributed in the hope that it will be useful, but
 
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
 * General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU General Public License
 
18
 * along with this program; if not, write to the Free Software
 
19
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
20
 * 02111-1307, USA.
 
21
 */
 
22
 
 
23
#include <config.h>
 
24
#include "constraints.h"
 
25
#include "window.h"
 
26
#include "workspace.h"
 
27
#include "place.h"
 
28
 
 
29
/* The way this code works was suggested by Owen Taylor.
 
30
 *
 
31
 * For any move_resize, we determine which variables are "free
 
32
 * variables" and apply constraints in terms of those. During the move
 
33
 * resize, we only want to modify those variables; otherwise the
 
34
 * constraint process can have peculiar side effects when the size and
 
35
 * position constraints interact. For example, resizing a window from
 
36
 * the top might go wrong when position constraints apply to the top
 
37
 * edge, and result in the bottom edge moving downward while the top
 
38
 * stays fixed.
 
39
 *
 
40
 * After selecting the variables we plan to vary, we define
 
41
 * each constraint on the window in terms of those variables.
 
42
 *
 
43
 * Trivial example, say we are resizing vertically from the top of the
 
44
 * window. In that case we are applying the user's mouse motion delta
 
45
 * to an original size and position, note that dy is positive to
 
46
 * resize downward:
 
47
 *
 
48
 *   new_height = orig_height - dy;
 
49
 *   new_y      = orig_y + dy;
 
50
 *
 
51
 * A constraint that the position can't go above the top panel would
 
52
 * look like this:
 
53
 *
 
54
 *   new_y >= screen_top_bound
 
55
 *
 
56
 * Substitute:
 
57
 *
 
58
 *   orig_y + dy >= screen_top_bound
 
59
 *
 
60
 * Find the "boundary point" by changing to an equality:
 
61
 *
 
62
 *   orig_y + dy = screen_top_bound
 
63
 *
 
64
 * Solve:
 
65
 *
 
66
 *   dy = screen_top_bound - orig_y
 
67
 *
 
68
 * This dy is now the _maximum_ dy and you constrain dy with that
 
69
 * value, applying it to both the move and the resize:
 
70
 *
 
71
 *   new_height = orig_height - dy;
 
72
 *   new_y      = orig_y + dy;
 
73
 *
 
74
 * This way the constraint is applied simultaneously to size/position,
 
75
 * so you aren't running the risk of constraining one but still
 
76
 * changing the other. i.e. we've converted an operation that may
 
77
 * modify both the Y position and the height of the window into an
 
78
 * operation that modifies a single variable, dy.  That variable is
 
79
 * then constrained, rather than the constraining the Y pos and height
 
80
 * separately. This is a rather complicated fix for an obscure bug
 
81
 * that happened when resizing a window and encountering a constraint
 
82
 * such as the top edge of the screen.
 
83
 *
 
84
 */
 
85
 
 
86
 
 
87
/* To adjust for window gravity, such as a client moving itself to the
 
88
 * southeast corner, we want to compute the gravity reference point
 
89
 * - (screen_width,screen_height) in the SE corner case - using the
 
90
 * size the client had in its configure request. But then we want
 
91
 * to compute the actual position we intend to land on using
 
92
 * the real constrained dimensions of the window.
 
93
 *
 
94
 * So for a window being placed in the SE corner and simultaneously
 
95
 * resized, we get the gravity reference point, then compute where the
 
96
 * window should go to maintain that ref. point at its current size
 
97
 * instead of at the requested size, and conceptually move the window
 
98
 * to the requested ref. point but at its current size, without
 
99
 * applying any constraints. Then we constrain it with the top and
 
100
 * left edges as the edges that vary, with a dx/dy that are the delta
 
101
 * from the current size to the requested size.
 
102
 *
 
103
 * This method applies to any ConfigureRequest that does a simultaneous
 
104
 * move/resize.
 
105
 *
 
106
 * We use the same method to e.g. maximize a window; if the window is
 
107
 * maximized, we want to MOVE_VERTICAL/MOVE_HORIZONTAL to the top
 
108
 * center of the screen, then RESIZE_BOTTOM and
 
109
 * RESIZE_HORIZONTAL_CENTER. i.e. essentially NorthGravity.
 
110
 *
 
111
 */
 
112
 
 
113
#define FLOOR(value, base)      ( ((int) ((value) / (base))) * (base) )
 
114
 
 
115
typedef struct
 
116
{
 
117
  MetaWindow *window;
 
118
  MetaFrameGeometry fgeom;
 
119
  const MetaXineramaScreenInfo *xinerama;
 
120
  MetaRectangle work_area_xinerama;
 
121
  MetaRectangle work_area_screen;
 
122
  int nw_x, nw_y, se_x, se_y; /* these are whole-screen not xinerama */
 
123
} ConstraintInfo;
 
124
 
 
125
/* (FIXME instead of TITLEBAR_LENGTH_ONSCREEN, get the actual
 
126
 * size of the menu control?).
 
127
 */
 
128
 
 
129
#define TITLEBAR_LENGTH_ONSCREEN 75
 
130
 
 
131
typedef gboolean (* MetaConstraintAppliesFunc) (MetaWindow *window);
 
132
 
 
133
/* There's a function for each case with a different "free variable" */
 
134
typedef void (* MetaConstrainTopFunc)     (MetaWindow           *window,
 
135
                                           const ConstraintInfo *info,
 
136
                                           const MetaRectangle  *orig,
 
137
                                           int                  *y_delta);
 
138
typedef void (* MetaConstrainBottomFunc)  (MetaWindow           *window,
 
139
                                           const ConstraintInfo *info,
 
140
                                           const MetaRectangle  *orig,
 
141
                                           int                  *y_delta);
 
142
typedef void (* MetaConstrainVCenterFunc) (MetaWindow           *window,
 
143
                                           const ConstraintInfo *info,
 
144
                                           const MetaRectangle  *orig,
 
145
                                           int                  *y_delta);
 
146
typedef void (* MetaConstrainLeftFunc)    (MetaWindow           *window,
 
147
                                           const ConstraintInfo *info,
 
148
                                           const MetaRectangle  *orig,
 
149
                                           int                  *x_delta);
 
150
typedef void (* MetaConstrainRightFunc)   (MetaWindow           *window,
 
151
                                           const ConstraintInfo *info,
 
152
                                           const MetaRectangle  *orig,
 
153
                                           int                  *x_delta);
 
154
typedef void (* MetaConstrainHCenterFunc) (MetaWindow           *window,
 
155
                                           const ConstraintInfo *info,
 
156
                                           const MetaRectangle  *orig,
 
157
                                           int                  *x_delta);
 
158
typedef void (* MetaConstrainMoveFunc)    (MetaWindow           *window,
 
159
                                           const ConstraintInfo *info,
 
160
                                           const MetaRectangle  *orig,
 
161
                                           int                  *x_delta,
 
162
                                           int                  *y_delta);
 
163
 
 
164
typedef struct
 
165
{
 
166
  const char               *name;
 
167
  MetaConstraintAppliesFunc applies_func;
 
168
  MetaConstrainTopFunc      top_func;
 
169
  MetaConstrainBottomFunc   bottom_func;
 
170
  MetaConstrainVCenterFunc  vcenter_func;
 
171
  MetaConstrainLeftFunc     left_func;
 
172
  MetaConstrainRightFunc    right_func;
 
173
  MetaConstrainHCenterFunc  hcenter_func;
 
174
  MetaConstrainMoveFunc     move_func;
 
175
} Constraint;
 
176
 
 
177
/* "Is the desktop window" constraint:
 
178
 *
 
179
 *  new_x = 0;
 
180
 *  new_y = 0;
 
181
 *  new_w = orig_width;
 
182
 *  new_h = orig_height;
 
183
 *
 
184
 * Note that if we are applying a resize constraint,
 
185
 * e.g. constraint_desktop_top_func, this is kind of broken since we
 
186
 * end up resizing the window in order to get its position right. But
 
187
 * that case shouldn't happen in practice.
 
188
 */
 
189
static gboolean
 
190
constraint_desktop_applies_func (MetaWindow *window)
 
191
{
 
192
  return window->type == META_WINDOW_DESKTOP;
 
193
}
 
194
 
 
195
static void
 
196
constraint_desktop_top_func     (MetaWindow           *window,
 
197
                                 const ConstraintInfo *info,
 
198
                                 const MetaRectangle  *orig,
 
199
                                 int                  *y_delta)
 
200
{
 
201
  *y_delta = 0 - orig->y;
 
202
}
 
203
 
 
204
static void
 
205
constraint_desktop_bottom_func  (MetaWindow           *window,
 
206
                                 const ConstraintInfo *info,
 
207
                                 const MetaRectangle  *orig,
 
208
                                 int                  *y_delta)
 
209
{
 
210
  /* nothing */
 
211
}
 
212
 
 
213
static void
 
214
constraint_desktop_vcenter_func (MetaWindow           *window,
 
215
                                 const ConstraintInfo *info,
 
216
                                 const MetaRectangle  *orig,
 
217
                                 int                  *y_delta)
 
218
{
 
219
  *y_delta = 0 - orig->y;
 
220
}
 
221
 
 
222
static void
 
223
constraint_desktop_left_func    (MetaWindow           *window,
 
224
                                 const ConstraintInfo *info,
 
225
                                 const MetaRectangle  *orig,
 
226
                                 int                  *x_delta)
 
227
{
 
228
  *x_delta = 0 - orig->x;
 
229
}
 
230
 
 
231
static void
 
232
constraint_desktop_right_func   (MetaWindow           *window,
 
233
                                 const ConstraintInfo *info,
 
234
                                 const MetaRectangle  *orig,
 
235
                                 int                  *x_delta)
 
236
{
 
237
  /* nothing */
 
238
}
 
239
 
 
240
static void
 
241
constraint_desktop_hcenter_func (MetaWindow           *window,
 
242
                                 const ConstraintInfo *info,
 
243
                                 const MetaRectangle  *orig,
 
244
                                 int                  *x_delta)
 
245
{
 
246
  *x_delta = 0 - orig->x;
 
247
}
 
248
 
 
249
static void
 
250
constraint_desktop_move_func    (MetaWindow           *window,
 
251
                                 const ConstraintInfo *info,
 
252
                                 const MetaRectangle  *orig,
 
253
                                 int                  *x_delta,
 
254
                                 int                  *y_delta)
 
255
{
 
256
  *x_delta = 0 - orig->x;
 
257
  *y_delta = 0 - orig->y;
 
258
}
 
259
 
 
260
static const Constraint constraint_desktop = {
 
261
  "Desktop",
 
262
  constraint_desktop_applies_func,
 
263
  constraint_desktop_top_func,
 
264
  constraint_desktop_bottom_func,
 
265
  constraint_desktop_vcenter_func,
 
266
  constraint_desktop_left_func,
 
267
  constraint_desktop_right_func,
 
268
  constraint_desktop_hcenter_func,
 
269
  constraint_desktop_move_func
 
270
};
 
271
 
 
272
/* Titlebar is onscreen constraint:
 
273
 *
 
274
 * Constants:
 
275
 *  titlebar_width_onscreen = amount of titlebar width that has to be onscreen
 
276
 *  nw_x, nw_y = left/top edges that titlebar can't go outside
 
277
 *  se_x, se_y = right/bottom edges
 
278
 *
 
279
 * NW limit has priority over SE, since titlebar is on NW
 
280
 *
 
281
 * Left resize
 
282
 * ===
 
283
 *
 
284
 *   new_width = orig_width - dx
 
285
 *   new_x = orig_x + dx
 
286
 *
 
287
 * Amount of window+frame that doesn't fit in the work area:
 
288
 *
 
289
 *   offscreen_width = left_width + new_width + right_width - (se_x - nw_x)
 
290
 *
 
291
 * If we keep the old metacity rule where a window can be offscreen by
 
292
 * offscreen_width, then the math works out that left/top resizes are not
 
293
 * constrained. If we instead have a rule where the window can never be offscreen,
 
294
 * you get the following:
 
295
 *
 
296
 *  new_x >= nw_x + left_width + titlebar_width_offscreen
 
297
 *  orig_x + dx >= nw_x + left_width + titlebar_width_onscreen
 
298
 *  dx >= nw_x + left_width + titlebar_width_onscreen - orig_x
 
299
 *
 
300
 * i.e. the minimum dx is: nw_x + left_width + titlebar_width_onscreen - orig_x
 
301
 *
 
302
 * We could have a more complicated rule that constrains only if the current
 
303
 * offscreen width is positive, thus allowing something more like the old
 
304
 * behavior, but not doing that for now.
 
305
 *
 
306
 * Top resize works the same as left resize. Right/bottom resize don't have a limit
 
307
 * because the constraint is designed to keep the top left corner of the
 
308
 * window or its titlebar on the screen, and right/bottom resize will never move that
 
309
 * area. Center resize is almost like left/top but dx has the opposite sign
 
310
 * and new_width = orig_width + 2dx.
 
311
 *
 
312
 * For right/bottom we can try to handle windows that aren't in a valid
 
313
 * location to begin with:
 
314
 *
 
315
 *  new_x <= se_x - titlebar_width_onscreen
 
316
 *  dx <= se_x - titlebar_width_onscreen - orig_x
 
317
 *
 
318
 * but in principle this constraint is never triggered.
 
319
 *
 
320
 * Vertical move
 
321
 * ===
 
322
 *
 
323
 *  new_height = orig_height
 
324
 *  new_y = orig_y + dy
 
325
 *
 
326
 *  new_y >= nw_y + top_height
 
327
 *
 
328
 *  Min negative dy (nw_y + top_height - orig_y) just as with top resize.
 
329
 *  Max positive dy has to be computed from se_y and given less priority than the
 
330
 *  min negative:
 
331
 *
 
332
 *   new_y < se_y
 
333
 *   orig_y + dy = se_y
 
334
 *   so max dy is (se_y - orig_y)
 
335
 *
 
336
 * Horizontal move is equivalent to vertical.
 
337
 *
 
338
 */
 
339
 
 
340
static gboolean
 
341
constraint_onscreen_applies_func (MetaWindow *window)
 
342
{
 
343
  return
 
344
    !window->fullscreen &&
 
345
    window->type != META_WINDOW_DESKTOP &&
 
346
    window->type != META_WINDOW_DOCK;
 
347
}
 
348
 
 
349
static void
 
350
get_outermost_onscreen_positions (MetaWindow           *window,
 
351
                                  const ConstraintInfo *info,
 
352
                                  const MetaRectangle  *orig,
 
353
                                  int                  delta_x,
 
354
                                  int                  delta_y,
 
355
                                  int                  *leftmost_x_p,
 
356
                                  int                  *rightmost_x_p,
 
357
                                  int                  *topmost_y_p,
 
358
                                  int                  *bottommost_y_p)
 
359
{
 
360
  GList *workspaces;
 
361
  GList *tmp;
 
362
  GSList *stmp;
 
363
  MetaRectangle current;
 
364
  int bottommost_y;
 
365
 
 
366
  /* to handle struts, we get the list of workspaces for the window
 
367
   * and traverse all the struts in each of the cached strut lists for
 
368
   * the workspaces.  Note that because the workarea has already been
 
369
   * computed, these strut lists should already be up to date. This function
 
370
   * should have good performance since we call it a lot.
 
371
   */
 
372
 
 
373
  current = *orig;
 
374
  current.x += delta_x;
 
375
  current.y += delta_y;
 
376
 
 
377
  workspaces = meta_window_get_workspaces (window);
 
378
  tmp = workspaces;
 
379
 
 
380
  if (leftmost_x_p)
 
381
    {
 
382
      *leftmost_x_p = info->nw_x;
 
383
      while (tmp)
 
384
        {
 
385
          stmp = ((MetaWorkspace*) tmp->data)->left_struts;
 
386
          while (stmp)
 
387
            {
 
388
              MetaRectangle *rect = (MetaRectangle*) stmp->data;
 
389
              /* the strut only matters if the title bar is
 
390
               * overlapping the strut rect.
 
391
               */
 
392
              if (((current.y - info->fgeom.top_height >= rect->y) &&
 
393
                   (current.y - info->fgeom.top_height < rect->y + rect->height)) ||
 
394
                  ((current.y >= rect->y) &&
 
395
                   (current.y < rect->y + rect->height)))
 
396
                {
 
397
                  *leftmost_x_p = MAX (*leftmost_x_p, rect->width);
 
398
                }
 
399
              
 
400
              stmp = stmp->next;
 
401
            }
 
402
          
 
403
          tmp = tmp->next;
 
404
        }
 
405
      
 
406
      *leftmost_x_p = *leftmost_x_p - current.width + 
 
407
        MIN (TITLEBAR_LENGTH_ONSCREEN, current.width);
 
408
    }
 
409
  
 
410
  tmp = workspaces;
 
411
  if (rightmost_x_p)
 
412
    {
 
413
      *rightmost_x_p = info->se_x;
 
414
      while (tmp)
 
415
        {
 
416
          stmp = ((MetaWorkspace*) tmp->data)->right_struts;
 
417
          while (stmp)
 
418
            {
 
419
              MetaRectangle *rect = (MetaRectangle*) stmp->data;
 
420
              /* the strut only matters if the title bar is
 
421
               * overlapping the strut rect.
 
422
               */
 
423
              if (((current.y - info->fgeom.top_height >= rect->y) &&
 
424
                   (current.y - info->fgeom.top_height < rect->y + rect->height)) ||
 
425
                  ((current.y >= rect->y) &&
 
426
                   (current.y < rect->y + rect->height)))
 
427
                {
 
428
                  *rightmost_x_p = MIN (*rightmost_x_p, rect->x);
 
429
                }
 
430
              
 
431
              stmp = stmp->next;
 
432
            }
 
433
          
 
434
          tmp = tmp->next;
 
435
        }
 
436
      
 
437
      *rightmost_x_p = *rightmost_x_p - 
 
438
        MIN (TITLEBAR_LENGTH_ONSCREEN, current.width);
 
439
    }
 
440
 
 
441
  tmp = workspaces;
 
442
  if (topmost_y_p)
 
443
    {
 
444
      *topmost_y_p = info->nw_y;
 
445
      while (tmp)
 
446
        {
 
447
          stmp = ((MetaWorkspace*) tmp->data)->top_struts;
 
448
          while (stmp)
 
449
            {
 
450
              MetaRectangle *rect = (MetaRectangle*) stmp->data;
 
451
              /* here the strut matters if the titlebar is overlapping
 
452
               * the window horizontally
 
453
               */
 
454
              if ((current.x < rect->x + rect->width) &&
 
455
                  (current.x + current.width > rect->x))
 
456
                {
 
457
                  *topmost_y_p = MAX (*topmost_y_p, rect->height);
 
458
                }
 
459
              
 
460
              stmp = stmp->next;
 
461
            }
 
462
          
 
463
          tmp = tmp->next;
 
464
        }
 
465
      
 
466
      *topmost_y_p = *topmost_y_p + info->fgeom.top_height;
 
467
    }
 
468
 
 
469
  tmp = workspaces;
 
470
  bottommost_y = G_MAXUSHORT;
 
471
  if (bottommost_y_p || topmost_y_p)
 
472
    {
 
473
      bottommost_y = info->se_y;
 
474
      while (tmp)
 
475
        {
 
476
          stmp = ((MetaWorkspace*) tmp->data)->bottom_struts;
 
477
          while (stmp)
 
478
            {
 
479
              MetaRectangle *rect = (MetaRectangle*) stmp->data;
 
480
              /* here the strut matters if the titlebar is overlapping
 
481
               * the window horizontally
 
482
               */
 
483
              if ((current.x < rect->x + rect->width) &&
 
484
                  (current.x + current.width > rect->x))
 
485
                {
 
486
                  bottommost_y = MIN (bottommost_y, rect->y);
 
487
                }
 
488
              
 
489
              stmp = stmp->next;
 
490
            }
 
491
          
 
492
          tmp = tmp->next;
 
493
        }
 
494
    }
 
495
 
 
496
  if (bottommost_y_p)
 
497
    {
 
498
      *bottommost_y_p = bottommost_y;
 
499
 
 
500
      /* If no frame, keep random TITLEBAR_LENGTH_ONSCREEN pixels on the
 
501
       * screen.
 
502
       */
 
503
      if (!window->frame)
 
504
        *bottommost_y_p = *bottommost_y_p -
 
505
          MIN (TITLEBAR_LENGTH_ONSCREEN, current.height);
 
506
    }
 
507
 
 
508
  /* if the window has a minimum size too big for the "effective" work
 
509
   * area let it "cheat" a little by allowing a user to move it up so
 
510
   * that you can see the bottom of the window.
 
511
   */
 
512
  if (topmost_y_p)
 
513
    {
 
514
      int minheight;
 
515
      
 
516
      if (window->frame)
 
517
        {
 
518
          /* this is the "normal" case of, e.g. a dialog that's
 
519
           * just too big for the work area
 
520
           */
 
521
          minheight = window->frame->bottom_height +
 
522
            window->size_hints.min_height;
 
523
        }
 
524
      else
 
525
        {
 
526
          /* let frameless windows move offscreen is too large for the
 
527
           * effective work area.  This may include windows that try
 
528
           * to make themselves full screen by removing the
 
529
           * decorations and repositioning themselves.
 
530
           */
 
531
          minheight = orig->height;
 
532
        }
 
533
      
 
534
      if (minheight > (bottommost_y - *topmost_y_p))
 
535
          *topmost_y_p = bottommost_y - minheight;
 
536
    }
 
537
}
 
538
 
 
539
static void
 
540
constraint_onscreen_top_func     (MetaWindow           *window,
 
541
                                  const ConstraintInfo *info,
 
542
                                  const MetaRectangle  *orig,
 
543
                                  int                  *y_delta)
 
544
{
 
545
  int min_dy;
 
546
  int topmost_y;
 
547
 
 
548
  get_outermost_onscreen_positions (window, info, orig, 0, *y_delta,
 
549
                                    NULL, NULL, &topmost_y, NULL);
 
550
 
 
551
  min_dy = topmost_y - orig->y;
 
552
 
 
553
  if (*y_delta < min_dy)
 
554
    *y_delta = min_dy;
 
555
}
 
556
 
 
557
static void
 
558
constraint_onscreen_bottom_func  (MetaWindow           *window,
 
559
                                  const ConstraintInfo *info,
 
560
                                  const MetaRectangle  *orig,
 
561
                                  int                  *y_delta)
 
562
{
 
563
  /* no way to resize off the bottom so that constraints are
 
564
     violated */
 
565
  return;
 
566
}
 
567
 
 
568
static void
 
569
constraint_onscreen_vcenter_func (MetaWindow           *window,
 
570
                                  const ConstraintInfo *info,
 
571
                                  const MetaRectangle  *orig,
 
572
                                  int                  *y_delta)
 
573
{
 
574
  int max_dy;
 
575
  int topmost_y;
 
576
 
 
577
  get_outermost_onscreen_positions (window, info, orig, 0, *y_delta,
 
578
                                    NULL, NULL, &topmost_y, NULL);
 
579
 
 
580
  max_dy = orig->y - topmost_y;
 
581
 
 
582
  if (*y_delta > max_dy)
 
583
    *y_delta = max_dy;
 
584
}
 
585
 
 
586
static void
 
587
constraint_onscreen_left_func    (MetaWindow           *window,
 
588
                                  const ConstraintInfo *info,
 
589
                                  const MetaRectangle  *orig,
 
590
                                  int                  *x_delta)
 
591
{
 
592
  /* no way to resize off the sides so that constraints are violated
 
593
   */
 
594
  return;
 
595
}
 
596
 
 
597
static void
 
598
constraint_onscreen_right_func   (MetaWindow           *window,
 
599
                                  const ConstraintInfo *info,
 
600
                                  const MetaRectangle  *orig,
 
601
                                  int                  *x_delta)
 
602
{
 
603
  /* no way to resize off the sides so that constraints are violated
 
604
   */
 
605
  return;
 
606
}
 
607
 
 
608
static void
 
609
constraint_onscreen_hcenter_func (MetaWindow           *window,
 
610
                                  const ConstraintInfo *info,
 
611
                                  const MetaRectangle  *orig,
 
612
                                  int                  *x_delta)
 
613
{
 
614
  /* no way to resize off the sides so that constraints are violated
 
615
   */
 
616
  return;
 
617
}
 
618
 
 
619
static void
 
620
constraint_onscreen_move_func    (MetaWindow           *window,
 
621
                                  const ConstraintInfo *info,
 
622
                                  const MetaRectangle  *orig,
 
623
                                  int                  *x_delta,
 
624
                                  int                  *y_delta)
 
625
{
 
626
  int min_delta;
 
627
  int max_delta;
 
628
  int leftmost_x, rightmost_x, topmost_y, bottommost_y;
 
629
 
 
630
  get_outermost_onscreen_positions (window, info, orig, *x_delta, *y_delta,
 
631
                                    &leftmost_x, &rightmost_x,
 
632
                                    &topmost_y, &bottommost_y);
 
633
 
 
634
  min_delta = topmost_y - orig->y;
 
635
  max_delta = bottommost_y - orig->y;
 
636
 
 
637
  /* Note that min delta (top left) has priority over
 
638
   * max delta (bottom right) to facilitate keeping
 
639
   * titlebar on the screen
 
640
   */
 
641
  if (*y_delta > max_delta)
 
642
    *y_delta = max_delta;
 
643
  if (*y_delta < min_delta)
 
644
    *y_delta = min_delta;
 
645
 
 
646
  min_delta = leftmost_x - orig->x;
 
647
  max_delta = rightmost_x - orig->x;
 
648
 
 
649
  if (*x_delta > max_delta)
 
650
    *x_delta = max_delta;
 
651
  if (*x_delta < min_delta)
 
652
    *x_delta = min_delta;
 
653
}
 
654
 
 
655
static const Constraint constraint_onscreen = {
 
656
  "Onscreen",
 
657
  constraint_onscreen_applies_func,
 
658
  constraint_onscreen_top_func,
 
659
  constraint_onscreen_bottom_func,
 
660
  constraint_onscreen_vcenter_func,
 
661
  constraint_onscreen_left_func,
 
662
  constraint_onscreen_right_func,
 
663
  constraint_onscreen_hcenter_func,
 
664
  constraint_onscreen_move_func
 
665
};
 
666
 
 
667
 
 
668
/* Size hints constraints:
 
669
 *
 
670
 * For min/max size we just clamp to those, and for resize increment
 
671
 * we clamp to the one at or below the requested place.
 
672
 *
 
673
 * For aspect ratio, we special-case it at the end of
 
674
 * meta_window_constrain, because it involves both dimensions, and
 
675
 * thus messes up our generic framework.
 
676
 *
 
677
 * Left resize can be solved for dx like this:
 
678
 *   new_width = orig_width - dx
 
679
 *   new_x = orig_x + dx
 
680
 *
 
681
 *   new_width >= min_width
 
682
 *   orig_width - dx >= min_width
 
683
 *   - dx >= min_width - orig_width
 
684
 *   dx <= orig_width - min_width
 
685
 *
 
686
 *   new_width <= max_width
 
687
 *   orig_width - dx <= max_width
 
688
 *   - dx <= max_width - orig_width
 
689
 *   dx >= orig_width - max_width
 
690
 *
 
691
 */
 
692
 
 
693
#define USE_HINTS_FOR_WINDOW_STATE(window) (!((window)->fullscreen || (window)->maximized))
 
694
 
 
695
static gboolean
 
696
constraint_hints_applies_func (MetaWindow *window)
 
697
{
 
698
  return USE_HINTS_FOR_WINDOW_STATE (window);
 
699
}
 
700
 
 
701
static void
 
702
constraint_hints_top_func     (MetaWindow           *window,
 
703
                               const ConstraintInfo *info,
 
704
                               const MetaRectangle  *orig,
 
705
                               int                  *y_delta)
 
706
{
 
707
  int min_dy;
 
708
  int max_dy;
 
709
  int height;
 
710
 
 
711
  max_dy = orig->height - window->size_hints.min_height;
 
712
  min_dy = orig->height - window->size_hints.max_height;
 
713
 
 
714
  g_assert (max_dy >= min_dy);
 
715
 
 
716
  if (*y_delta > max_dy)
 
717
    *y_delta = max_dy;
 
718
  if (*y_delta < min_dy)
 
719
    *y_delta = min_dy;
 
720
 
 
721
  /* shrink to base + N * inc
 
722
   */
 
723
  height = orig->height - *y_delta;
 
724
  height = window->size_hints.base_height +
 
725
    FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
 
726
 
 
727
  *y_delta = orig->height - height;
 
728
}
 
729
 
 
730
static void
 
731
constraint_hints_bottom_func  (MetaWindow           *window,
 
732
                               const ConstraintInfo *info,
 
733
                               const MetaRectangle  *orig,
 
734
                               int                  *y_delta)
 
735
{
 
736
  int min_dy;
 
737
  int max_dy;
 
738
  int height;
 
739
 
 
740
  min_dy = window->size_hints.min_height - orig->height;
 
741
  max_dy = window->size_hints.max_height - orig->height;
 
742
 
 
743
  g_assert (max_dy >= min_dy);
 
744
 
 
745
  if (*y_delta > max_dy)
 
746
    *y_delta = max_dy;
 
747
  if (*y_delta < min_dy)
 
748
    *y_delta = min_dy;
 
749
 
 
750
  /* shrink to base + N * inc
 
751
   */
 
752
  height = orig->height + *y_delta;
 
753
  height = window->size_hints.base_height +
 
754
    FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
 
755
 
 
756
  *y_delta = height - orig->height;
 
757
}
 
758
 
 
759
static void
 
760
constraint_hints_vcenter_func (MetaWindow           *window,
 
761
                               const ConstraintInfo *info,
 
762
                               const MetaRectangle  *orig,
 
763
                               int                  *y_delta)
 
764
{
 
765
  int min_dy;
 
766
  int max_dy;
 
767
  int height;
 
768
 
 
769
  /* Remember our delta is negative to shrink window, positive to
 
770
   * grow it, and the actual resize is y_delta * 2 (which is broken,
 
771
   * but that's how it currently is)
 
772
   */
 
773
 
 
774
  min_dy = (window->size_hints.min_height - orig->height) / 2;
 
775
  max_dy = (window->size_hints.max_height - orig->height) / 2;
 
776
 
 
777
  g_assert (max_dy >= min_dy);
 
778
 
 
779
  if (*y_delta > max_dy)
 
780
    *y_delta = max_dy;
 
781
  if (*y_delta < min_dy)
 
782
    *y_delta = min_dy;
 
783
 
 
784
  /* shrink to base + N * inc
 
785
   */
 
786
  height = orig->height + *y_delta * 2;
 
787
  height = window->size_hints.base_height +
 
788
    FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
 
789
 
 
790
  *y_delta = (height - orig->height) / 2;
 
791
}
 
792
 
 
793
static void
 
794
constraint_hints_left_func    (MetaWindow           *window,
 
795
                               const ConstraintInfo *info,
 
796
                               const MetaRectangle  *orig,
 
797
                               int                  *x_delta)
 
798
{
 
799
  int min_dx;
 
800
  int max_dx;
 
801
  int width;
 
802
 
 
803
  max_dx = orig->width - window->size_hints.min_width;
 
804
  min_dx = orig->width - window->size_hints.max_width;
 
805
 
 
806
  g_assert (max_dx >= min_dx);
 
807
 
 
808
  if (*x_delta > max_dx)
 
809
    *x_delta = max_dx;
 
810
  if (*x_delta < min_dx)
 
811
    *x_delta = min_dx;
 
812
 
 
813
  /* shrink to base + N * inc
 
814
   */
 
815
  width = orig->width - *x_delta;
 
816
  width = window->size_hints.base_width +
 
817
    FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
 
818
 
 
819
  *x_delta = orig->width - width;
 
820
}
 
821
 
 
822
static void
 
823
constraint_hints_right_func   (MetaWindow           *window,
 
824
                               const ConstraintInfo *info,
 
825
                               const MetaRectangle  *orig,
 
826
                               int                  *x_delta)
 
827
{
 
828
  int min_dx;
 
829
  int max_dx;
 
830
  int width;
 
831
 
 
832
  min_dx = window->size_hints.min_width - orig->width;
 
833
  max_dx = window->size_hints.max_width - orig->width;
 
834
 
 
835
  g_assert (max_dx >= min_dx);
 
836
 
 
837
  if (*x_delta > max_dx)
 
838
    *x_delta = max_dx;
 
839
  if (*x_delta < min_dx)
 
840
    *x_delta = min_dx;
 
841
 
 
842
  /* shrink to base + N * inc
 
843
   */
 
844
  width = orig->width + *x_delta;
 
845
  width = window->size_hints.base_width +
 
846
    FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
 
847
 
 
848
  *x_delta = width - orig->width;
 
849
}
 
850
 
 
851
static void
 
852
constraint_hints_hcenter_func (MetaWindow           *window,
 
853
                               const ConstraintInfo *info,
 
854
                               const MetaRectangle  *orig,
 
855
                               int                  *x_delta)
 
856
{
 
857
  int min_dx;
 
858
  int max_dx;
 
859
  int width;
 
860
  
 
861
  /* Remember our delta is negative to shrink window, positive to
 
862
   * grow it, and the actual resize is x_delta * 2 (which is broken,
 
863
   * but that's how it currently is)
 
864
   */
 
865
 
 
866
  min_dx = (window->size_hints.min_width - orig->width) / 2;
 
867
  max_dx = (window->size_hints.max_width - orig->width) / 2;
 
868
 
 
869
  g_assert (max_dx >= min_dx);
 
870
 
 
871
  if (*x_delta > max_dx)
 
872
    *x_delta = max_dx;
 
873
  if (*x_delta < min_dx)
 
874
    *x_delta = min_dx;
 
875
 
 
876
  /* shrink to base + N * inc
 
877
   */
 
878
  width = orig->width + *x_delta * 2;
 
879
  width = window->size_hints.base_width +
 
880
    FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
 
881
 
 
882
  *x_delta = (width - orig->width) / 2;
 
883
}
 
884
 
 
885
static void
 
886
constraint_hints_move_func    (MetaWindow           *window,
 
887
                               const ConstraintInfo *info,
 
888
                               const MetaRectangle  *orig,
 
889
                               int                  *x_delta,
 
890
                               int                  *y_delta)
 
891
{
 
892
  /* nothing */
 
893
}
 
894
 
 
895
static const Constraint constraint_hints = {
 
896
  "Hints",
 
897
  constraint_hints_applies_func,
 
898
  constraint_hints_top_func,
 
899
  constraint_hints_bottom_func,
 
900
  constraint_hints_vcenter_func,
 
901
  constraint_hints_left_func,
 
902
  constraint_hints_right_func,
 
903
  constraint_hints_hcenter_func,
 
904
  constraint_hints_move_func
 
905
};
 
906
 
 
907
/* Array of all constraints at once */
 
908
static const Constraint *all_constraints[] = {
 
909
  &constraint_desktop,
 
910
  &constraint_onscreen,
 
911
  &constraint_hints,
 
912
  NULL
 
913
};
 
914
 
 
915
/* Move with no accompanying change to window size */
 
916
static void
 
917
constrain_move (MetaWindow           *window,
 
918
                const ConstraintInfo *info,
 
919
                const MetaRectangle  *orig,
 
920
                int                   x_delta,
 
921
                int                   y_delta,
 
922
                MetaRectangle        *new)
 
923
{
 
924
  const Constraint **cp;
 
925
  int old_x, old_y;
 
926
  int paranoia;
 
927
 
 
928
  /* Evidence that we can't actually prove this algorithm is right */
 
929
#define MAX_ITERATIONS 10
 
930
  paranoia = 0;
 
931
  
 
932
  do {
 
933
    old_x = x_delta;
 
934
    old_y = y_delta;
 
935
    cp = &all_constraints[0];
 
936
 
 
937
    while (*cp)
 
938
      {
 
939
        meta_topic (META_DEBUG_GEOMETRY,
 
940
                    "Before: %d %d (Move constraint '%s')\n",
 
941
                    x_delta, y_delta, (*cp)->name);
 
942
 
 
943
        if ((* (*cp)->applies_func) (window))
 
944
          (* (*cp)->move_func) (window, info, orig,
 
945
                                &x_delta, &y_delta);
 
946
 
 
947
        meta_topic (META_DEBUG_GEOMETRY,
 
948
                    "After:  %d %d (Move constraint '%s')\n",
 
949
                    x_delta, y_delta, (*cp)->name);
 
950
      
 
951
        ++cp;
 
952
      }
 
953
 
 
954
    ++paranoia;
 
955
  } while (((old_x != x_delta) || (old_y != y_delta)) && paranoia < MAX_ITERATIONS);
 
956
 
 
957
  new->x = orig->x + x_delta;
 
958
  new->y = orig->y + y_delta;
 
959
 
 
960
  if (paranoia >= MAX_ITERATIONS)
 
961
    meta_topic (META_DEBUG_GEOMETRY,
 
962
                "Constraints were never satisfied for window %s\n",
 
963
                window->desc);
 
964
}
 
965
 
 
966
static void
 
967
constrain_resize_left (MetaWindow           *window,
 
968
                       const ConstraintInfo *info,
 
969
                       const MetaRectangle  *orig,
 
970
                       int                   x_delta,
 
971
                       MetaRectangle        *new)
 
972
{
 
973
  const Constraint **cp;
 
974
 
 
975
  cp = &all_constraints[0];
 
976
 
 
977
  while (*cp)
 
978
    {
 
979
      meta_topic (META_DEBUG_GEOMETRY,
 
980
                  "Before: %d (Left constraint '%s')\n",
 
981
                  x_delta, (*cp)->name);
 
982
      
 
983
      if ((* (*cp)->applies_func) (window))
 
984
        (* (*cp)->left_func) (window, info, orig,
 
985
                              &x_delta);
 
986
 
 
987
      meta_topic (META_DEBUG_GEOMETRY,
 
988
                  "After:  %d (Left constraint '%s')\n",
 
989
                  x_delta, (*cp)->name);
 
990
      
 
991
      ++cp;
 
992
    }
 
993
 
 
994
  /* Moving mouse from 10 to 5 means current - orig means 5 - 10 means
 
995
   * a delta of -5
 
996
   */
 
997
  new->x = orig->x + x_delta;
 
998
  new->width = orig->width - x_delta;
 
999
}
 
1000
 
 
1001
static void
 
1002
constrain_resize_hcenter (MetaWindow           *window,
 
1003
                          const ConstraintInfo *info,
 
1004
                          const MetaRectangle  *orig,
 
1005
                          int                   x_delta,
 
1006
                          MetaRectangle        *new)
 
1007
{
 
1008
  const Constraint **cp;
 
1009
  
 
1010
  cp = &all_constraints[0];
 
1011
 
 
1012
  while (*cp)
 
1013
    {
 
1014
      meta_topic (META_DEBUG_GEOMETRY,
 
1015
                  "Before: %d (HCenter constraint '%s')\n",
 
1016
                  x_delta, (*cp)->name);
 
1017
      
 
1018
      if ((* (*cp)->applies_func) (window))
 
1019
        (* (*cp)->hcenter_func) (window, info, orig,
 
1020
                                 &x_delta);
 
1021
 
 
1022
      meta_topic (META_DEBUG_GEOMETRY,
 
1023
                  "After:  %d (HCenter constraint '%s')\n",
 
1024
                  x_delta, (*cp)->name);
 
1025
      
 
1026
      ++cp;
 
1027
    }
 
1028
  
 
1029
  /* center deltas are positive to grow the window and negative to
 
1030
   * shrink it.
 
1031
   */
 
1032
  new->x = orig->x - x_delta;
 
1033
  new->width = orig->width + x_delta * 2;
 
1034
  /* FIXME above implies that with center gravity you have to grow
 
1035
   * in increments of two
 
1036
   */
 
1037
}
 
1038
 
 
1039
static void
 
1040
constrain_resize_right (MetaWindow           *window,
 
1041
                        const ConstraintInfo *info,
 
1042
                        const MetaRectangle  *orig,
 
1043
                        int                   x_delta,
 
1044
                        MetaRectangle        *new)
 
1045
{
 
1046
  const Constraint **cp;
 
1047
 
 
1048
  cp = &all_constraints[0];
 
1049
 
 
1050
  while (*cp)
 
1051
    {
 
1052
      meta_topic (META_DEBUG_GEOMETRY,
 
1053
                  "Before: %d (Right constraint '%s')\n",
 
1054
                  x_delta, (*cp)->name);
 
1055
      
 
1056
      if ((* (*cp)->applies_func) (window))
 
1057
        (* (*cp)->right_func) (window, info, orig,
 
1058
                               &x_delta);
 
1059
 
 
1060
      meta_topic (META_DEBUG_GEOMETRY,
 
1061
                  "After:  %d (Right constraint '%s')\n",
 
1062
                  x_delta, (*cp)->name);
 
1063
      
 
1064
      ++cp;
 
1065
    }
 
1066
 
 
1067
  new->width = orig->width + x_delta;
 
1068
}
 
1069
 
 
1070
static void
 
1071
constrain_resize_top (MetaWindow           *window,
 
1072
                      const ConstraintInfo *info,
 
1073
                      const MetaRectangle  *orig,
 
1074
                      int                   y_delta,
 
1075
                      MetaRectangle        *new)
 
1076
{
 
1077
  const Constraint **cp;
 
1078
 
 
1079
  cp = &all_constraints[0];
 
1080
 
 
1081
  while (*cp)
 
1082
    {
 
1083
      meta_topic (META_DEBUG_GEOMETRY,
 
1084
                  "Before: %d (Top constraint '%s')\n",
 
1085
                  y_delta, (*cp)->name);
 
1086
      
 
1087
      if ((* (*cp)->applies_func) (window))
 
1088
        (* (*cp)->top_func) (window, info, orig,
 
1089
                             &y_delta);
 
1090
 
 
1091
      meta_topic (META_DEBUG_GEOMETRY,
 
1092
                  "After:  %d (Top constraint '%s')\n",
 
1093
                  y_delta, (*cp)->name);
 
1094
      
 
1095
      ++cp;
 
1096
    }
 
1097
 
 
1098
  new->y = orig->y + y_delta;
 
1099
  new->height = orig->height - y_delta;
 
1100
}
 
1101
 
 
1102
static void
 
1103
constrain_resize_vcenter (MetaWindow           *window,
 
1104
                          const ConstraintInfo *info,
 
1105
                          const MetaRectangle  *orig,
 
1106
                          int                   y_delta,
 
1107
                          MetaRectangle        *new)
 
1108
{
 
1109
  const Constraint **cp;
 
1110
 
 
1111
  cp = &all_constraints[0];
 
1112
 
 
1113
  while (*cp)
 
1114
    {
 
1115
      meta_topic (META_DEBUG_GEOMETRY,
 
1116
                  "Before: %d (VCenter constraint '%s')\n",
 
1117
                  y_delta, (*cp)->name);
 
1118
      
 
1119
      if ((* (*cp)->applies_func) (window))
 
1120
        (* (*cp)->vcenter_func) (window, info, orig,
 
1121
                                 &y_delta);
 
1122
 
 
1123
      meta_topic (META_DEBUG_GEOMETRY,
 
1124
                  "After:  %d (VCenter constraint '%s')\n",
 
1125
                  y_delta, (*cp)->name);
 
1126
      
 
1127
      ++cp;
 
1128
    }
 
1129
 
 
1130
  /* center deltas are positive to grow the window and negative to
 
1131
   * shrink it.
 
1132
   */
 
1133
  new->y = orig->y - y_delta;
 
1134
  new->height = orig->height + y_delta * 2;
 
1135
  /* FIXME above implies that with center gravity you have to grow
 
1136
   * in increments of two
 
1137
   */
 
1138
}
 
1139
 
 
1140
static void
 
1141
constrain_resize_bottom (MetaWindow           *window,
 
1142
                         const ConstraintInfo *info,
 
1143
                         const MetaRectangle  *orig,
 
1144
                         int                   y_delta,
 
1145
                         MetaRectangle        *new)
 
1146
{
 
1147
  const Constraint **cp;
 
1148
 
 
1149
  cp = &all_constraints[0];
 
1150
 
 
1151
  while (*cp)
 
1152
    {
 
1153
      meta_topic (META_DEBUG_GEOMETRY,
 
1154
                  "Before: %d (Bottom constraint '%s')\n",
 
1155
                  y_delta, (*cp)->name);
 
1156
      
 
1157
      if ((* (*cp)->applies_func) (window))
 
1158
        (* (*cp)->bottom_func) (window, info, orig,
 
1159
                                &y_delta);
 
1160
 
 
1161
      meta_topic (META_DEBUG_GEOMETRY,
 
1162
                  "After:  %d (Bottom constraint '%s')\n",
 
1163
                  y_delta, (*cp)->name);
 
1164
      
 
1165
      ++cp;
 
1166
    }
 
1167
 
 
1168
  new->height = orig->height + y_delta;
 
1169
}
 
1170
 
 
1171
static void
 
1172
update_position_limits (MetaWindow          *window,
 
1173
                        ConstraintInfo      *info)
 
1174
{
 
1175
  int nw_x, nw_y;
 
1176
  int se_x, se_y;
 
1177
 
 
1178
  /* For maximized windows the limits are the work area, for
 
1179
   * other windows we see which struts apply based on the
 
1180
   * window's position later on
 
1181
   */
 
1182
  if (window->maximized)
 
1183
    {
 
1184
      nw_x = MIN (info->work_area_xinerama.x, info->work_area_screen.x);
 
1185
      nw_y = MIN (info->work_area_xinerama.y, info->work_area_screen.y);
 
1186
 
 
1187
      /* find bottom-right corner of workarea */
 
1188
      se_x = MAX (info->work_area_xinerama.x + info->work_area_xinerama.width,
 
1189
                  info->work_area_screen.x + info->work_area_screen.width);
 
1190
      se_y = MAX (info->work_area_xinerama.y + info->work_area_xinerama.height,
 
1191
                  info->work_area_screen.y + info->work_area_screen.height);
 
1192
    }
 
1193
  else
 
1194
    {
 
1195
      nw_x = 0;
 
1196
      nw_y = 0;
 
1197
      se_x = window->screen->width;
 
1198
      se_y = window->screen->height;
 
1199
   }
 
1200
 
 
1201
  /* If we have a micro-screen or huge frames maybe nw/se got
 
1202
   * swapped
 
1203
   */
 
1204
  if (nw_x > se_x)
 
1205
    {
 
1206
      int tmp = nw_x;
 
1207
      nw_x = se_x;
 
1208
      se_x = tmp;
 
1209
    }
 
1210
 
 
1211
  if (nw_y > se_y)
 
1212
    {
 
1213
      int tmp = nw_y;
 
1214
      nw_y = se_y;
 
1215
      se_y = tmp;
 
1216
    }
 
1217
 
 
1218
  info->nw_x = nw_x;
 
1219
  info->nw_y = nw_y;
 
1220
  info->se_x = se_x;
 
1221
  info->se_y = se_y;
 
1222
}
 
1223
 
 
1224
/* The delta values are the mouse motion distance deltas,
 
1225
 * i.e. mouse_current_pos - mouse_orig_pos, for resizing on
 
1226
 * the sides, or moving. For center resize, the delta
 
1227
 * value is positive to grow the window and negative to
 
1228
 * shrink it (while the sign of the mouse delta
 
1229
 * depends on which side of the window you are center resizing
 
1230
 * from)
 
1231
 */
 
1232
void
 
1233
meta_window_constrain (MetaWindow          *window,
 
1234
                       MetaFrameGeometry   *orig_fgeom,
 
1235
                       const MetaRectangle *orig,
 
1236
                       int                  x_move_delta,
 
1237
                       int                  y_move_delta,
 
1238
                       MetaResizeDirection  x_direction,
 
1239
                       int                  x_delta,
 
1240
                       MetaResizeDirection  y_direction,
 
1241
                       int                  y_delta,
 
1242
                       MetaRectangle       *new)
 
1243
{
 
1244
  ConstraintInfo info;
 
1245
  MetaRectangle current;
 
1246
  gboolean did_placement;
 
1247
 
 
1248
#define OUTER_WIDTH(rect) ((rect).width + info.fgeom.left_width + info.fgeom.right_width)
 
1249
#define OUTER_HEIGHT(rect) ((rect).height + info.fgeom.top_height + info.fgeom.bottom_height)
 
1250
 
 
1251
  meta_topic (META_DEBUG_GEOMETRY,
 
1252
              "Constraining %s x_move_delta = %d y_move_delta = %d x_direction = %d y_direction = %d x_delta = %d y_delta = %d orig %d,%d %dx%d\n",
 
1253
              window->desc, x_move_delta, y_move_delta,
 
1254
              x_direction, y_direction, x_delta, y_delta,
 
1255
              orig->x, orig->y, orig->width, orig->height);
 
1256
 
 
1257
  /* Create a fake frame geometry if none really exists */
 
1258
  if (orig_fgeom && !window->fullscreen)
 
1259
    info.fgeom = *orig_fgeom;
 
1260
  else
 
1261
    {
 
1262
      info.fgeom.top_height = 0;
 
1263
      info.fgeom.bottom_height = 0;
 
1264
      info.fgeom.left_width = 0;
 
1265
      info.fgeom.right_width = 0;
 
1266
    }
 
1267
 
 
1268
  meta_window_get_work_area_current_xinerama (window, &info.work_area_xinerama);
 
1269
  meta_window_get_work_area_all_xineramas (window, &info.work_area_screen);
 
1270
 
 
1271
  info.window = window;
 
1272
  info.xinerama = meta_screen_get_xinerama_for_window (window->screen,
 
1273
                                                       window);
 
1274
  /* Init info->nw_x etc. */
 
1275
  update_position_limits (window, &info);
 
1276
 
 
1277
  current = *orig;
 
1278
  *new = current;
 
1279
 
 
1280
  /* Do placement if any, so we go ahead and apply position
 
1281
   * constraints in a move-only context. Don't place
 
1282
   * maximized/fullscreen windows until they are unmaximized
 
1283
   * and unfullscreened
 
1284
   */
 
1285
  did_placement = FALSE;
 
1286
  if (!window->placed &&
 
1287
      window->calc_placement &&
 
1288
      !window->maximized &&
 
1289
      !window->fullscreen)
 
1290
    {
 
1291
      MetaRectangle placed_rect = current;
 
1292
 
 
1293
      meta_window_place (window, orig_fgeom, current.x, current.y,
 
1294
                         &placed_rect.x, &placed_rect.y);
 
1295
      did_placement = TRUE;
 
1296
 
 
1297
      /* placing the window may have changed the xinerama.  Find the
 
1298
       * new xinerama and update the ConstraintInfo
 
1299
       */
 
1300
      info.xinerama = meta_screen_get_xinerama_for_rect (window->screen,
 
1301
                                              &placed_rect);
 
1302
      meta_window_get_work_area_for_xinerama (window,
 
1303
                                              info.xinerama->number,
 
1304
                                              &info.work_area_xinerama);
 
1305
      update_position_limits (window, &info);
 
1306
 
 
1307
      constrain_move (window, &info, &current,
 
1308
                      placed_rect.x - current.x,
 
1309
                      placed_rect.y - current.y,
 
1310
                      new);
 
1311
      current = *new;
 
1312
 
 
1313
      /* Ignore any non-placement movement */
 
1314
      x_move_delta = 0;
 
1315
      y_move_delta = 0;
 
1316
 
 
1317
    }
 
1318
 
 
1319
  if (window->maximize_after_placement &&
 
1320
      (window->placed || did_placement))
 
1321
    {
 
1322
      window->maximize_after_placement = FALSE;
 
1323
 
 
1324
      if (OUTER_WIDTH (*new) >= info.work_area_xinerama.width &&
 
1325
          OUTER_HEIGHT (*new) >= info.work_area_xinerama.height)
 
1326
        {
 
1327
          /* define a sane saved_rect so that the user can unmaximize
 
1328
           * to something reasonable.
 
1329
           */
 
1330
          new->width = .75 * info.work_area_xinerama.width;
 
1331
          new->height = .75 * info.work_area_xinerama.height;
 
1332
          new->x = info.work_area_xinerama.x + .125 * info.work_area_xinerama.width;
 
1333
          new->y = info.work_area_xinerama.y + .083 * info.work_area_xinerama.height;
 
1334
        }
 
1335
 
 
1336
      meta_window_maximize_internal (window, new);
 
1337
 
 
1338
      /* maximization may have changed frame geometry */
 
1339
      if (orig_fgeom && !window->fullscreen)
 
1340
        {
 
1341
          meta_frame_calc_geometry (window->frame,
 
1342
                                    orig_fgeom);
 
1343
          info.fgeom = *orig_fgeom;
 
1344
        }
 
1345
    }
 
1346
 
 
1347
  /* Maximization, fullscreen, etc. are defined as a resize followed by
 
1348
   * a move, as explained in one of the big comments at the top of
 
1349
   * this file.
 
1350
   */
 
1351
  if (window->fullscreen)
 
1352
    {
 
1353
      current = *new;
 
1354
      constrain_resize_bottom (window, &info, &current,
 
1355
                               (info.xinerama->height - OUTER_HEIGHT (current)),
 
1356
                               new);
 
1357
 
 
1358
      current = *new;
 
1359
 
 
1360
      constrain_resize_right (window, &info, &current,
 
1361
                              info.xinerama->width - OUTER_WIDTH (current),
 
1362
                              new);
 
1363
      current = *new;
 
1364
 
 
1365
      constrain_move (window, &info, &current,
 
1366
                      info.xinerama->x_origin - current.x + info.fgeom.left_width,
 
1367
                      info.xinerama->y_origin - current.y + info.fgeom.top_height,
 
1368
                      new);
 
1369
    }
 
1370
  else if (window->maximized)
 
1371
    {
 
1372
      constrain_resize_bottom (window, &info, &current,
 
1373
                               (info.work_area_xinerama.height - OUTER_HEIGHT (current)),
 
1374
                               new);
 
1375
 
 
1376
      current = *new;
 
1377
 
 
1378
      constrain_resize_right (window, &info, &current,
 
1379
                              info.work_area_xinerama.width - OUTER_WIDTH (current),
 
1380
                              new);
 
1381
      current = *new;
 
1382
 
 
1383
      constrain_move (window, &info, &current,
 
1384
                      info.work_area_xinerama.x - current.x + info.fgeom.left_width,
 
1385
                      info.work_area_xinerama.y - current.y + info.fgeom.top_height,
 
1386
                      new);
 
1387
 
 
1388
      current = *new;
 
1389
    }
 
1390
  else
 
1391
    {
 
1392
      switch (x_direction)
 
1393
        {
 
1394
        case META_RESIZE_LEFT_OR_TOP:
 
1395
          constrain_resize_left (window, &info, &current,
 
1396
                                 x_delta, new);
 
1397
          break;
 
1398
        case META_RESIZE_CENTER:
 
1399
          constrain_resize_hcenter (window, &info, &current,
 
1400
                                    x_delta, new);
 
1401
          break;
 
1402
        case META_RESIZE_RIGHT_OR_BOTTOM:
 
1403
          constrain_resize_right (window, &info, &current,
 
1404
                                  x_delta, new);
 
1405
          break;
 
1406
        }
 
1407
 
 
1408
      switch (y_direction)
 
1409
        {
 
1410
        case META_RESIZE_LEFT_OR_TOP:
 
1411
          constrain_resize_top (window, &info, &current,
 
1412
                                y_delta, new);
 
1413
          break;
 
1414
        case META_RESIZE_CENTER:
 
1415
          constrain_resize_vcenter (window, &info, &current,
 
1416
                                    y_delta, new);
 
1417
          break;
 
1418
        case META_RESIZE_RIGHT_OR_BOTTOM:
 
1419
          constrain_resize_bottom (window, &info, &current,
 
1420
                                   y_delta, new);
 
1421
          break;
 
1422
        }
 
1423
 
 
1424
      current = *new;
 
1425
 
 
1426
      constrain_move (window, &info, &current,
 
1427
                      x_move_delta, y_move_delta,
 
1428
                      new);
 
1429
 
 
1430
      current = *new;
 
1431
    }
 
1432
 
 
1433
  /* Now we have to sort out the aspect ratio */
 
1434
  if (!window->fullscreen)
 
1435
    {
 
1436
      /*
 
1437
       *                width
 
1438
       * min_aspect <= -------- <= max_aspect
 
1439
       *                height
 
1440
       */
 
1441
      double min_aspect, max_aspect;
 
1442
      int width, height;
 
1443
      
 
1444
      min_aspect = window->size_hints.min_aspect.x / (double) window->size_hints.min_aspect.y;
 
1445
      max_aspect = window->size_hints.max_aspect.x / (double) window->size_hints.max_aspect.y;
 
1446
      
 
1447
      width = current.width;
 
1448
      height = current.height;
 
1449
 
 
1450
      if (min_aspect * height > width)
 
1451
        {
 
1452
          int delta;
 
1453
          
 
1454
          if (y_direction == META_RESIZE_CENTER)
 
1455
            {
 
1456
              delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc);
 
1457
              if (width + delta <= window->size_hints.max_width)
 
1458
                width += delta;
 
1459
              else
 
1460
                {
 
1461
                  delta = FLOOR (height - width / min_aspect, window->size_hints.height_inc);
 
1462
                  if (height - delta >= window->size_hints.min_height)
 
1463
                    height -= delta;
 
1464
                }
 
1465
            }
 
1466
          else
 
1467
            {
 
1468
              delta = FLOOR (height - width / min_aspect, window->size_hints.height_inc);
 
1469
              if (height - delta >= window->size_hints.min_height)
 
1470
                height -= delta;
 
1471
              else
 
1472
                {
 
1473
                  delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc);
 
1474
                  if (width + delta <= window->size_hints.max_width)
 
1475
                    width += delta;
 
1476
                }
 
1477
            }
 
1478
        }
 
1479
      
 
1480
      if (max_aspect * height < width)
 
1481
        {
 
1482
          int delta;
 
1483
          
 
1484
          if (x_direction == META_RESIZE_CENTER)
 
1485
            {
 
1486
              delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc);
 
1487
              if (height + delta <= window->size_hints.max_height)
 
1488
                height += delta;
 
1489
              else
 
1490
                {
 
1491
                  delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc);
 
1492
                  if (width - delta >= window->size_hints.min_width)
 
1493
                    width -= delta;
 
1494
                }
 
1495
            }
 
1496
          else
 
1497
            {
 
1498
              delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc);
 
1499
              if (width - delta >= window->size_hints.min_width)
 
1500
                width -= delta;
 
1501
              else
 
1502
                {
 
1503
                  delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc);
 
1504
                  if (height + delta <= window->size_hints.max_height)
 
1505
                    height += delta;
 
1506
                }
 
1507
            }
 
1508
        }
 
1509
      
 
1510
      /* Convert into terms of the direction of resize and reapply the
 
1511
       * earlier constraints; this means aspect ratio becomes the
 
1512
       * least-important of the constraints. If we wanted aspect to be
 
1513
       * the most important, we could just not do this next bit.
 
1514
       */
 
1515
      
 
1516
      if (current.width != width)
 
1517
        {
 
1518
          x_delta = width - current.width; /* positive delta to increase width */
 
1519
          switch (x_direction)
 
1520
            {
 
1521
            case META_RESIZE_LEFT_OR_TOP:
 
1522
              constrain_resize_left (window, &info, &current,
 
1523
                                     - x_delta, new);
 
1524
              break;
 
1525
            case META_RESIZE_CENTER:
 
1526
              constrain_resize_hcenter (window, &info, &current,
 
1527
                                        x_delta, new);
 
1528
              break;
 
1529
            case META_RESIZE_RIGHT_OR_BOTTOM:
 
1530
              constrain_resize_right (window, &info, &current,
 
1531
                                      x_delta, new);
 
1532
              break;
 
1533
            }
 
1534
        }
 
1535
      
 
1536
      if (current.height != height)
 
1537
        {
 
1538
          y_delta = height - current.height; /* positive to increase height */
 
1539
          
 
1540
          switch (y_direction)
 
1541
            {
 
1542
            case META_RESIZE_LEFT_OR_TOP:
 
1543
              constrain_resize_top (window, &info, &current,
 
1544
                                    - y_delta, new);
 
1545
              break;
 
1546
            case META_RESIZE_CENTER:
 
1547
              constrain_resize_vcenter (window, &info, &current,
 
1548
                                        y_delta, new);
 
1549
              break;
 
1550
            case META_RESIZE_RIGHT_OR_BOTTOM:
 
1551
              constrain_resize_bottom (window, &info, &current,
 
1552
                                       y_delta, new);
 
1553
              break;
 
1554
            }
 
1555
        }
 
1556
      
 
1557
      current = *new;
 
1558
    }
 
1559
 
 
1560
  meta_topic (META_DEBUG_GEOMETRY,
 
1561
              "Constrained %s new %d,%d %dx%d old %d,%d %dx%d\n",
 
1562
              window->desc,
 
1563
              new->x, new->y, new->width, new->height,
 
1564
              orig->x, orig->y, orig->width, orig->height);
 
1565
}
 
1566
 
 
1567
MetaResizeDirection
 
1568
meta_x_direction_from_gravity (int gravity)
 
1569
{
 
1570
  switch (gravity)
 
1571
    {
 
1572
    case EastGravity:
 
1573
    case NorthEastGravity:
 
1574
    case SouthEastGravity:
 
1575
      return META_RESIZE_LEFT_OR_TOP;
 
1576
      break;
 
1577
 
 
1578
    case WestGravity:
 
1579
    case NorthWestGravity:
 
1580
    case SouthWestGravity:
 
1581
    case StaticGravity:
 
1582
      return META_RESIZE_RIGHT_OR_BOTTOM;
 
1583
      break;
 
1584
 
 
1585
    default:
 
1586
      return META_RESIZE_CENTER;
 
1587
      break;
 
1588
    }
 
1589
}
 
1590
 
 
1591
MetaResizeDirection
 
1592
meta_y_direction_from_gravity (int gravity)
 
1593
{
 
1594
  switch (gravity)
 
1595
    {
 
1596
    case SouthGravity:
 
1597
    case SouthWestGravity:
 
1598
    case SouthEastGravity:
 
1599
      return META_RESIZE_LEFT_OR_TOP;
 
1600
      break;
 
1601
 
 
1602
    case NorthGravity:
 
1603
    case NorthWestGravity:
 
1604
    case NorthEastGravity:
 
1605
    case StaticGravity:
 
1606
      return META_RESIZE_RIGHT_OR_BOTTOM;
 
1607
      break;
 
1608
 
 
1609
    default:
 
1610
      return META_RESIZE_CENTER;
 
1611
    }
 
1612
}