1
/* Metacity size/position constraints */
4
* Copyright (C) 2002, 2003 Red Hat, Inc.
5
* Copyright (C) 2003, 2004 Rob Adams
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.
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.
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
24
#include "constraints.h"
26
#include "workspace.h"
29
/* The way this code works was suggested by Owen Taylor.
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
40
* After selecting the variables we plan to vary, we define
41
* each constraint on the window in terms of those variables.
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
48
* new_height = orig_height - dy;
49
* new_y = orig_y + dy;
51
* A constraint that the position can't go above the top panel would
54
* new_y >= screen_top_bound
58
* orig_y + dy >= screen_top_bound
60
* Find the "boundary point" by changing to an equality:
62
* orig_y + dy = screen_top_bound
66
* dy = screen_top_bound - orig_y
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:
71
* new_height = orig_height - dy;
72
* new_y = orig_y + dy;
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.
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.
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.
103
* This method applies to any ConfigureRequest that does a simultaneous
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.
113
#define FLOOR(value, base) ( ((int) ((value) / (base))) * (base) )
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 */
125
/* (FIXME instead of TITLEBAR_LENGTH_ONSCREEN, get the actual
126
* size of the menu control?).
129
#define TITLEBAR_LENGTH_ONSCREEN 75
131
typedef gboolean (* MetaConstraintAppliesFunc) (MetaWindow *window);
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,
138
typedef void (* MetaConstrainBottomFunc) (MetaWindow *window,
139
const ConstraintInfo *info,
140
const MetaRectangle *orig,
142
typedef void (* MetaConstrainVCenterFunc) (MetaWindow *window,
143
const ConstraintInfo *info,
144
const MetaRectangle *orig,
146
typedef void (* MetaConstrainLeftFunc) (MetaWindow *window,
147
const ConstraintInfo *info,
148
const MetaRectangle *orig,
150
typedef void (* MetaConstrainRightFunc) (MetaWindow *window,
151
const ConstraintInfo *info,
152
const MetaRectangle *orig,
154
typedef void (* MetaConstrainHCenterFunc) (MetaWindow *window,
155
const ConstraintInfo *info,
156
const MetaRectangle *orig,
158
typedef void (* MetaConstrainMoveFunc) (MetaWindow *window,
159
const ConstraintInfo *info,
160
const MetaRectangle *orig,
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;
177
/* "Is the desktop window" constraint:
181
* new_w = orig_width;
182
* new_h = orig_height;
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.
190
constraint_desktop_applies_func (MetaWindow *window)
192
return window->type == META_WINDOW_DESKTOP;
196
constraint_desktop_top_func (MetaWindow *window,
197
const ConstraintInfo *info,
198
const MetaRectangle *orig,
201
*y_delta = 0 - orig->y;
205
constraint_desktop_bottom_func (MetaWindow *window,
206
const ConstraintInfo *info,
207
const MetaRectangle *orig,
214
constraint_desktop_vcenter_func (MetaWindow *window,
215
const ConstraintInfo *info,
216
const MetaRectangle *orig,
219
*y_delta = 0 - orig->y;
223
constraint_desktop_left_func (MetaWindow *window,
224
const ConstraintInfo *info,
225
const MetaRectangle *orig,
228
*x_delta = 0 - orig->x;
232
constraint_desktop_right_func (MetaWindow *window,
233
const ConstraintInfo *info,
234
const MetaRectangle *orig,
241
constraint_desktop_hcenter_func (MetaWindow *window,
242
const ConstraintInfo *info,
243
const MetaRectangle *orig,
246
*x_delta = 0 - orig->x;
250
constraint_desktop_move_func (MetaWindow *window,
251
const ConstraintInfo *info,
252
const MetaRectangle *orig,
256
*x_delta = 0 - orig->x;
257
*y_delta = 0 - orig->y;
260
static const Constraint constraint_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
272
/* Titlebar is onscreen constraint:
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
279
* NW limit has priority over SE, since titlebar is on NW
284
* new_width = orig_width - dx
285
* new_x = orig_x + dx
287
* Amount of window+frame that doesn't fit in the work area:
289
* offscreen_width = left_width + new_width + right_width - (se_x - nw_x)
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:
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
300
* i.e. the minimum dx is: nw_x + left_width + titlebar_width_onscreen - orig_x
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.
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.
312
* For right/bottom we can try to handle windows that aren't in a valid
313
* location to begin with:
315
* new_x <= se_x - titlebar_width_onscreen
316
* dx <= se_x - titlebar_width_onscreen - orig_x
318
* but in principle this constraint is never triggered.
323
* new_height = orig_height
324
* new_y = orig_y + dy
326
* new_y >= nw_y + top_height
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
334
* so max dy is (se_y - orig_y)
336
* Horizontal move is equivalent to vertical.
341
constraint_onscreen_applies_func (MetaWindow *window)
344
!window->fullscreen &&
345
window->type != META_WINDOW_DESKTOP &&
346
window->type != META_WINDOW_DOCK;
350
get_outermost_onscreen_positions (MetaWindow *window,
351
const ConstraintInfo *info,
352
const MetaRectangle *orig,
363
MetaRectangle current;
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.
374
current.x += delta_x;
375
current.y += delta_y;
377
workspaces = meta_window_get_workspaces (window);
382
*leftmost_x_p = info->nw_x;
385
stmp = ((MetaWorkspace*) tmp->data)->left_struts;
388
MetaRectangle *rect = (MetaRectangle*) stmp->data;
389
/* the strut only matters if the title bar is
390
* overlapping the strut rect.
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)))
397
*leftmost_x_p = MAX (*leftmost_x_p, rect->width);
406
*leftmost_x_p = *leftmost_x_p - current.width +
407
MIN (TITLEBAR_LENGTH_ONSCREEN, current.width);
413
*rightmost_x_p = info->se_x;
416
stmp = ((MetaWorkspace*) tmp->data)->right_struts;
419
MetaRectangle *rect = (MetaRectangle*) stmp->data;
420
/* the strut only matters if the title bar is
421
* overlapping the strut rect.
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)))
428
*rightmost_x_p = MIN (*rightmost_x_p, rect->x);
437
*rightmost_x_p = *rightmost_x_p -
438
MIN (TITLEBAR_LENGTH_ONSCREEN, current.width);
444
*topmost_y_p = info->nw_y;
447
stmp = ((MetaWorkspace*) tmp->data)->top_struts;
450
MetaRectangle *rect = (MetaRectangle*) stmp->data;
451
/* here the strut matters if the titlebar is overlapping
452
* the window horizontally
454
if ((current.x < rect->x + rect->width) &&
455
(current.x + current.width > rect->x))
457
*topmost_y_p = MAX (*topmost_y_p, rect->height);
466
*topmost_y_p = *topmost_y_p + info->fgeom.top_height;
470
bottommost_y = G_MAXUSHORT;
471
if (bottommost_y_p || topmost_y_p)
473
bottommost_y = info->se_y;
476
stmp = ((MetaWorkspace*) tmp->data)->bottom_struts;
479
MetaRectangle *rect = (MetaRectangle*) stmp->data;
480
/* here the strut matters if the titlebar is overlapping
481
* the window horizontally
483
if ((current.x < rect->x + rect->width) &&
484
(current.x + current.width > rect->x))
486
bottommost_y = MIN (bottommost_y, rect->y);
498
*bottommost_y_p = bottommost_y;
500
/* If no frame, keep random TITLEBAR_LENGTH_ONSCREEN pixels on the
504
*bottommost_y_p = *bottommost_y_p -
505
MIN (TITLEBAR_LENGTH_ONSCREEN, current.height);
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.
518
/* this is the "normal" case of, e.g. a dialog that's
519
* just too big for the work area
521
minheight = window->frame->bottom_height +
522
window->size_hints.min_height;
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.
531
minheight = orig->height;
534
if (minheight > (bottommost_y - *topmost_y_p))
535
*topmost_y_p = bottommost_y - minheight;
540
constraint_onscreen_top_func (MetaWindow *window,
541
const ConstraintInfo *info,
542
const MetaRectangle *orig,
548
get_outermost_onscreen_positions (window, info, orig, 0, *y_delta,
549
NULL, NULL, &topmost_y, NULL);
551
min_dy = topmost_y - orig->y;
553
if (*y_delta < min_dy)
558
constraint_onscreen_bottom_func (MetaWindow *window,
559
const ConstraintInfo *info,
560
const MetaRectangle *orig,
563
/* no way to resize off the bottom so that constraints are
569
constraint_onscreen_vcenter_func (MetaWindow *window,
570
const ConstraintInfo *info,
571
const MetaRectangle *orig,
577
get_outermost_onscreen_positions (window, info, orig, 0, *y_delta,
578
NULL, NULL, &topmost_y, NULL);
580
max_dy = orig->y - topmost_y;
582
if (*y_delta > max_dy)
587
constraint_onscreen_left_func (MetaWindow *window,
588
const ConstraintInfo *info,
589
const MetaRectangle *orig,
592
/* no way to resize off the sides so that constraints are violated
598
constraint_onscreen_right_func (MetaWindow *window,
599
const ConstraintInfo *info,
600
const MetaRectangle *orig,
603
/* no way to resize off the sides so that constraints are violated
609
constraint_onscreen_hcenter_func (MetaWindow *window,
610
const ConstraintInfo *info,
611
const MetaRectangle *orig,
614
/* no way to resize off the sides so that constraints are violated
620
constraint_onscreen_move_func (MetaWindow *window,
621
const ConstraintInfo *info,
622
const MetaRectangle *orig,
628
int leftmost_x, rightmost_x, topmost_y, bottommost_y;
630
get_outermost_onscreen_positions (window, info, orig, *x_delta, *y_delta,
631
&leftmost_x, &rightmost_x,
632
&topmost_y, &bottommost_y);
634
min_delta = topmost_y - orig->y;
635
max_delta = bottommost_y - orig->y;
637
/* Note that min delta (top left) has priority over
638
* max delta (bottom right) to facilitate keeping
639
* titlebar on the screen
641
if (*y_delta > max_delta)
642
*y_delta = max_delta;
643
if (*y_delta < min_delta)
644
*y_delta = min_delta;
646
min_delta = leftmost_x - orig->x;
647
max_delta = rightmost_x - orig->x;
649
if (*x_delta > max_delta)
650
*x_delta = max_delta;
651
if (*x_delta < min_delta)
652
*x_delta = min_delta;
655
static const Constraint constraint_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
668
/* Size hints constraints:
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.
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.
677
* Left resize can be solved for dx like this:
678
* new_width = orig_width - dx
679
* new_x = orig_x + dx
681
* new_width >= min_width
682
* orig_width - dx >= min_width
683
* - dx >= min_width - orig_width
684
* dx <= orig_width - min_width
686
* new_width <= max_width
687
* orig_width - dx <= max_width
688
* - dx <= max_width - orig_width
689
* dx >= orig_width - max_width
693
#define USE_HINTS_FOR_WINDOW_STATE(window) (!((window)->fullscreen || (window)->maximized))
696
constraint_hints_applies_func (MetaWindow *window)
698
return USE_HINTS_FOR_WINDOW_STATE (window);
702
constraint_hints_top_func (MetaWindow *window,
703
const ConstraintInfo *info,
704
const MetaRectangle *orig,
711
max_dy = orig->height - window->size_hints.min_height;
712
min_dy = orig->height - window->size_hints.max_height;
714
g_assert (max_dy >= min_dy);
716
if (*y_delta > max_dy)
718
if (*y_delta < min_dy)
721
/* shrink to base + N * inc
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);
727
*y_delta = orig->height - height;
731
constraint_hints_bottom_func (MetaWindow *window,
732
const ConstraintInfo *info,
733
const MetaRectangle *orig,
740
min_dy = window->size_hints.min_height - orig->height;
741
max_dy = window->size_hints.max_height - orig->height;
743
g_assert (max_dy >= min_dy);
745
if (*y_delta > max_dy)
747
if (*y_delta < min_dy)
750
/* shrink to base + N * inc
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);
756
*y_delta = height - orig->height;
760
constraint_hints_vcenter_func (MetaWindow *window,
761
const ConstraintInfo *info,
762
const MetaRectangle *orig,
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)
774
min_dy = (window->size_hints.min_height - orig->height) / 2;
775
max_dy = (window->size_hints.max_height - orig->height) / 2;
777
g_assert (max_dy >= min_dy);
779
if (*y_delta > max_dy)
781
if (*y_delta < min_dy)
784
/* shrink to base + N * inc
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);
790
*y_delta = (height - orig->height) / 2;
794
constraint_hints_left_func (MetaWindow *window,
795
const ConstraintInfo *info,
796
const MetaRectangle *orig,
803
max_dx = orig->width - window->size_hints.min_width;
804
min_dx = orig->width - window->size_hints.max_width;
806
g_assert (max_dx >= min_dx);
808
if (*x_delta > max_dx)
810
if (*x_delta < min_dx)
813
/* shrink to base + N * inc
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);
819
*x_delta = orig->width - width;
823
constraint_hints_right_func (MetaWindow *window,
824
const ConstraintInfo *info,
825
const MetaRectangle *orig,
832
min_dx = window->size_hints.min_width - orig->width;
833
max_dx = window->size_hints.max_width - orig->width;
835
g_assert (max_dx >= min_dx);
837
if (*x_delta > max_dx)
839
if (*x_delta < min_dx)
842
/* shrink to base + N * inc
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);
848
*x_delta = width - orig->width;
852
constraint_hints_hcenter_func (MetaWindow *window,
853
const ConstraintInfo *info,
854
const MetaRectangle *orig,
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)
866
min_dx = (window->size_hints.min_width - orig->width) / 2;
867
max_dx = (window->size_hints.max_width - orig->width) / 2;
869
g_assert (max_dx >= min_dx);
871
if (*x_delta > max_dx)
873
if (*x_delta < min_dx)
876
/* shrink to base + N * inc
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);
882
*x_delta = (width - orig->width) / 2;
886
constraint_hints_move_func (MetaWindow *window,
887
const ConstraintInfo *info,
888
const MetaRectangle *orig,
895
static const Constraint constraint_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
907
/* Array of all constraints at once */
908
static const Constraint *all_constraints[] = {
910
&constraint_onscreen,
915
/* Move with no accompanying change to window size */
917
constrain_move (MetaWindow *window,
918
const ConstraintInfo *info,
919
const MetaRectangle *orig,
924
const Constraint **cp;
928
/* Evidence that we can't actually prove this algorithm is right */
929
#define MAX_ITERATIONS 10
935
cp = &all_constraints[0];
939
meta_topic (META_DEBUG_GEOMETRY,
940
"Before: %d %d (Move constraint '%s')\n",
941
x_delta, y_delta, (*cp)->name);
943
if ((* (*cp)->applies_func) (window))
944
(* (*cp)->move_func) (window, info, orig,
947
meta_topic (META_DEBUG_GEOMETRY,
948
"After: %d %d (Move constraint '%s')\n",
949
x_delta, y_delta, (*cp)->name);
955
} while (((old_x != x_delta) || (old_y != y_delta)) && paranoia < MAX_ITERATIONS);
957
new->x = orig->x + x_delta;
958
new->y = orig->y + y_delta;
960
if (paranoia >= MAX_ITERATIONS)
961
meta_topic (META_DEBUG_GEOMETRY,
962
"Constraints were never satisfied for window %s\n",
967
constrain_resize_left (MetaWindow *window,
968
const ConstraintInfo *info,
969
const MetaRectangle *orig,
973
const Constraint **cp;
975
cp = &all_constraints[0];
979
meta_topic (META_DEBUG_GEOMETRY,
980
"Before: %d (Left constraint '%s')\n",
981
x_delta, (*cp)->name);
983
if ((* (*cp)->applies_func) (window))
984
(* (*cp)->left_func) (window, info, orig,
987
meta_topic (META_DEBUG_GEOMETRY,
988
"After: %d (Left constraint '%s')\n",
989
x_delta, (*cp)->name);
994
/* Moving mouse from 10 to 5 means current - orig means 5 - 10 means
997
new->x = orig->x + x_delta;
998
new->width = orig->width - x_delta;
1002
constrain_resize_hcenter (MetaWindow *window,
1003
const ConstraintInfo *info,
1004
const MetaRectangle *orig,
1008
const Constraint **cp;
1010
cp = &all_constraints[0];
1014
meta_topic (META_DEBUG_GEOMETRY,
1015
"Before: %d (HCenter constraint '%s')\n",
1016
x_delta, (*cp)->name);
1018
if ((* (*cp)->applies_func) (window))
1019
(* (*cp)->hcenter_func) (window, info, orig,
1022
meta_topic (META_DEBUG_GEOMETRY,
1023
"After: %d (HCenter constraint '%s')\n",
1024
x_delta, (*cp)->name);
1029
/* center deltas are positive to grow the window and negative to
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
1040
constrain_resize_right (MetaWindow *window,
1041
const ConstraintInfo *info,
1042
const MetaRectangle *orig,
1046
const Constraint **cp;
1048
cp = &all_constraints[0];
1052
meta_topic (META_DEBUG_GEOMETRY,
1053
"Before: %d (Right constraint '%s')\n",
1054
x_delta, (*cp)->name);
1056
if ((* (*cp)->applies_func) (window))
1057
(* (*cp)->right_func) (window, info, orig,
1060
meta_topic (META_DEBUG_GEOMETRY,
1061
"After: %d (Right constraint '%s')\n",
1062
x_delta, (*cp)->name);
1067
new->width = orig->width + x_delta;
1071
constrain_resize_top (MetaWindow *window,
1072
const ConstraintInfo *info,
1073
const MetaRectangle *orig,
1077
const Constraint **cp;
1079
cp = &all_constraints[0];
1083
meta_topic (META_DEBUG_GEOMETRY,
1084
"Before: %d (Top constraint '%s')\n",
1085
y_delta, (*cp)->name);
1087
if ((* (*cp)->applies_func) (window))
1088
(* (*cp)->top_func) (window, info, orig,
1091
meta_topic (META_DEBUG_GEOMETRY,
1092
"After: %d (Top constraint '%s')\n",
1093
y_delta, (*cp)->name);
1098
new->y = orig->y + y_delta;
1099
new->height = orig->height - y_delta;
1103
constrain_resize_vcenter (MetaWindow *window,
1104
const ConstraintInfo *info,
1105
const MetaRectangle *orig,
1109
const Constraint **cp;
1111
cp = &all_constraints[0];
1115
meta_topic (META_DEBUG_GEOMETRY,
1116
"Before: %d (VCenter constraint '%s')\n",
1117
y_delta, (*cp)->name);
1119
if ((* (*cp)->applies_func) (window))
1120
(* (*cp)->vcenter_func) (window, info, orig,
1123
meta_topic (META_DEBUG_GEOMETRY,
1124
"After: %d (VCenter constraint '%s')\n",
1125
y_delta, (*cp)->name);
1130
/* center deltas are positive to grow the window and negative to
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
1141
constrain_resize_bottom (MetaWindow *window,
1142
const ConstraintInfo *info,
1143
const MetaRectangle *orig,
1147
const Constraint **cp;
1149
cp = &all_constraints[0];
1153
meta_topic (META_DEBUG_GEOMETRY,
1154
"Before: %d (Bottom constraint '%s')\n",
1155
y_delta, (*cp)->name);
1157
if ((* (*cp)->applies_func) (window))
1158
(* (*cp)->bottom_func) (window, info, orig,
1161
meta_topic (META_DEBUG_GEOMETRY,
1162
"After: %d (Bottom constraint '%s')\n",
1163
y_delta, (*cp)->name);
1168
new->height = orig->height + y_delta;
1172
update_position_limits (MetaWindow *window,
1173
ConstraintInfo *info)
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
1182
if (window->maximized)
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);
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);
1197
se_x = window->screen->width;
1198
se_y = window->screen->height;
1201
/* If we have a micro-screen or huge frames maybe nw/se got
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
1233
meta_window_constrain (MetaWindow *window,
1234
MetaFrameGeometry *orig_fgeom,
1235
const MetaRectangle *orig,
1238
MetaResizeDirection x_direction,
1240
MetaResizeDirection y_direction,
1244
ConstraintInfo info;
1245
MetaRectangle current;
1246
gboolean did_placement;
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)
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);
1257
/* Create a fake frame geometry if none really exists */
1258
if (orig_fgeom && !window->fullscreen)
1259
info.fgeom = *orig_fgeom;
1262
info.fgeom.top_height = 0;
1263
info.fgeom.bottom_height = 0;
1264
info.fgeom.left_width = 0;
1265
info.fgeom.right_width = 0;
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);
1271
info.window = window;
1272
info.xinerama = meta_screen_get_xinerama_for_window (window->screen,
1274
/* Init info->nw_x etc. */
1275
update_position_limits (window, &info);
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
1285
did_placement = FALSE;
1286
if (!window->placed &&
1287
window->calc_placement &&
1288
!window->maximized &&
1289
!window->fullscreen)
1291
MetaRectangle placed_rect = current;
1293
meta_window_place (window, orig_fgeom, current.x, current.y,
1294
&placed_rect.x, &placed_rect.y);
1295
did_placement = TRUE;
1297
/* placing the window may have changed the xinerama. Find the
1298
* new xinerama and update the ConstraintInfo
1300
info.xinerama = meta_screen_get_xinerama_for_rect (window->screen,
1302
meta_window_get_work_area_for_xinerama (window,
1303
info.xinerama->number,
1304
&info.work_area_xinerama);
1305
update_position_limits (window, &info);
1307
constrain_move (window, &info, ¤t,
1308
placed_rect.x - current.x,
1309
placed_rect.y - current.y,
1313
/* Ignore any non-placement movement */
1319
if (window->maximize_after_placement &&
1320
(window->placed || did_placement))
1322
window->maximize_after_placement = FALSE;
1324
if (OUTER_WIDTH (*new) >= info.work_area_xinerama.width &&
1325
OUTER_HEIGHT (*new) >= info.work_area_xinerama.height)
1327
/* define a sane saved_rect so that the user can unmaximize
1328
* to something reasonable.
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;
1336
meta_window_maximize_internal (window, new);
1338
/* maximization may have changed frame geometry */
1339
if (orig_fgeom && !window->fullscreen)
1341
meta_frame_calc_geometry (window->frame,
1343
info.fgeom = *orig_fgeom;
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
1351
if (window->fullscreen)
1354
constrain_resize_bottom (window, &info, ¤t,
1355
(info.xinerama->height - OUTER_HEIGHT (current)),
1360
constrain_resize_right (window, &info, ¤t,
1361
info.xinerama->width - OUTER_WIDTH (current),
1365
constrain_move (window, &info, ¤t,
1366
info.xinerama->x_origin - current.x + info.fgeom.left_width,
1367
info.xinerama->y_origin - current.y + info.fgeom.top_height,
1370
else if (window->maximized)
1372
constrain_resize_bottom (window, &info, ¤t,
1373
(info.work_area_xinerama.height - OUTER_HEIGHT (current)),
1378
constrain_resize_right (window, &info, ¤t,
1379
info.work_area_xinerama.width - OUTER_WIDTH (current),
1383
constrain_move (window, &info, ¤t,
1384
info.work_area_xinerama.x - current.x + info.fgeom.left_width,
1385
info.work_area_xinerama.y - current.y + info.fgeom.top_height,
1392
switch (x_direction)
1394
case META_RESIZE_LEFT_OR_TOP:
1395
constrain_resize_left (window, &info, ¤t,
1398
case META_RESIZE_CENTER:
1399
constrain_resize_hcenter (window, &info, ¤t,
1402
case META_RESIZE_RIGHT_OR_BOTTOM:
1403
constrain_resize_right (window, &info, ¤t,
1408
switch (y_direction)
1410
case META_RESIZE_LEFT_OR_TOP:
1411
constrain_resize_top (window, &info, ¤t,
1414
case META_RESIZE_CENTER:
1415
constrain_resize_vcenter (window, &info, ¤t,
1418
case META_RESIZE_RIGHT_OR_BOTTOM:
1419
constrain_resize_bottom (window, &info, ¤t,
1426
constrain_move (window, &info, ¤t,
1427
x_move_delta, y_move_delta,
1433
/* Now we have to sort out the aspect ratio */
1434
if (!window->fullscreen)
1438
* min_aspect <= -------- <= max_aspect
1441
double min_aspect, max_aspect;
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;
1447
width = current.width;
1448
height = current.height;
1450
if (min_aspect * height > width)
1454
if (y_direction == META_RESIZE_CENTER)
1456
delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc);
1457
if (width + delta <= window->size_hints.max_width)
1461
delta = FLOOR (height - width / min_aspect, window->size_hints.height_inc);
1462
if (height - delta >= window->size_hints.min_height)
1468
delta = FLOOR (height - width / min_aspect, window->size_hints.height_inc);
1469
if (height - delta >= window->size_hints.min_height)
1473
delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc);
1474
if (width + delta <= window->size_hints.max_width)
1480
if (max_aspect * height < width)
1484
if (x_direction == META_RESIZE_CENTER)
1486
delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc);
1487
if (height + delta <= window->size_hints.max_height)
1491
delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc);
1492
if (width - delta >= window->size_hints.min_width)
1498
delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc);
1499
if (width - delta >= window->size_hints.min_width)
1503
delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc);
1504
if (height + delta <= window->size_hints.max_height)
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.
1516
if (current.width != width)
1518
x_delta = width - current.width; /* positive delta to increase width */
1519
switch (x_direction)
1521
case META_RESIZE_LEFT_OR_TOP:
1522
constrain_resize_left (window, &info, ¤t,
1525
case META_RESIZE_CENTER:
1526
constrain_resize_hcenter (window, &info, ¤t,
1529
case META_RESIZE_RIGHT_OR_BOTTOM:
1530
constrain_resize_right (window, &info, ¤t,
1536
if (current.height != height)
1538
y_delta = height - current.height; /* positive to increase height */
1540
switch (y_direction)
1542
case META_RESIZE_LEFT_OR_TOP:
1543
constrain_resize_top (window, &info, ¤t,
1546
case META_RESIZE_CENTER:
1547
constrain_resize_vcenter (window, &info, ¤t,
1550
case META_RESIZE_RIGHT_OR_BOTTOM:
1551
constrain_resize_bottom (window, &info, ¤t,
1560
meta_topic (META_DEBUG_GEOMETRY,
1561
"Constrained %s new %d,%d %dx%d old %d,%d %dx%d\n",
1563
new->x, new->y, new->width, new->height,
1564
orig->x, orig->y, orig->width, orig->height);
1568
meta_x_direction_from_gravity (int gravity)
1573
case NorthEastGravity:
1574
case SouthEastGravity:
1575
return META_RESIZE_LEFT_OR_TOP;
1579
case NorthWestGravity:
1580
case SouthWestGravity:
1582
return META_RESIZE_RIGHT_OR_BOTTOM;
1586
return META_RESIZE_CENTER;
1592
meta_y_direction_from_gravity (int gravity)
1597
case SouthWestGravity:
1598
case SouthEastGravity:
1599
return META_RESIZE_LEFT_OR_TOP;
1603
case NorthWestGravity:
1604
case NorthEastGravity:
1606
return META_RESIZE_RIGHT_OR_BOTTOM;
1610
return META_RESIZE_CENTER;