1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3
/* Marco size/position constraints */
6
* Copyright (C) 2002, 2003 Red Hat, Inc.
7
* Copyright (C) 2003, 2004 Rob Adams
8
* Copyright (C) 2005, 2006 Elijah Newren
10
* This program is free software; you can redistribute it and/or
11
* modify it under the terms of the GNU General Public License as
12
* published by the Free Software Foundation; either version 2 of the
13
* License, or (at your option) any later version.
15
* This program is distributed in the hope that it will be useful, but
16
* WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program; if not, write to the Free Software
22
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27
#include "constraints.h"
28
#include "workspace.h"
36
// This is the short and sweet version of how to hack on this file; see
37
// doc/how-constraints-works.txt for the gory details. The basics of
38
// understanding this file can be shown by the steps needed to add a new
39
// constraint, which are:
40
// 1) Add a new entry in the ConstraintPriority enum; higher values
41
// have higher priority
42
// 2) Write a new function following the format of the example below,
43
// "constrain_whatever".
44
// 3) Add your function to the all_constraints and all_constraint_names
45
// arrays (the latter of which is for debugging purposes)
47
// An example constraint function, constrain_whatever:
49
// /* constrain_whatever does the following:
50
// * Quits (returning true) if priority is higher than PRIORITY_WHATEVER
51
// * If check_only is TRUE
52
// * Returns whether the constraint is satisfied or not
54
// * Enforces the constraint
55
// * Note that the value of PRIORITY_WHATEVER is centralized with the
56
// * priorities of other constraints in the definition of ConstrainPriority
57
// * for easier maintenance and shuffling of priorities.
60
// constrain_whatever (MetaWindow *window,
61
// ConstraintInfo *info,
62
// ConstraintPriority priority,
63
// gboolean check_only)
65
// if (priority > PRIORITY_WHATEVER)
68
// /* Determine whether constraint applies; note that if the constraint
69
// * cannot possibly be satisfied, constraint_applies should be set to
70
// * false. If we don't do this, all constraints with a lesser priority
71
// * will be dropped along with this one, and we'd rather apply as many as
74
// if (!constraint_applies)
77
// /* Determine whether constraint is already satisfied; if we're only
78
// * checking the status of whether the constraint is satisfied, we end
81
// if (check_only || constraint_already_satisfied)
82
// return constraint_already_satisfied;
84
// /* Enforce constraints */
85
// return TRUE; /* Note that we exited early if check_only is FALSE; also,
86
// * we know we can return TRUE here because we exited early
87
// * if the constraint could not be satisfied; not that the
88
// * return value is heeded in this case...
95
PRIORITY_MINIMUM = 0, /* Dummy value used for loop start = min(all priorities) */
96
PRIORITY_ASPECT_RATIO = 0,
97
PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA = 0,
98
PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
99
PRIORITY_SIZE_HINTS_INCREMENTS = 1,
100
PRIORITY_MAXIMIZATION = 2,
101
PRIORITY_FULLSCREEN = 2,
102
PRIORITY_SIZE_HINTS_LIMITS = 3,
103
PRIORITY_TITLEBAR_VISIBLE = 4,
104
PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4,
105
PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */
106
} ConstraintPriority;
112
ACTION_MOVE_AND_RESIZE
118
MetaRectangle current;
119
MetaFrameGeometry *fgeom;
120
ActionType action_type;
121
gboolean is_user_action;
123
/* I know that these two things probably look similar at first, but they
124
* have much different uses. See doc/how-constraints-works.txt for for
125
* explanation of the differences and similarity between resize_gravity
126
* and fixed_directions
129
FixedDirections fixed_directions;
131
/* work_area_xinerama - current xinerama region minus struts
132
* entire_xinerama - current xienrama, including strut regions
134
MetaRectangle work_area_xinerama;
135
MetaRectangle entire_xinerama;
137
/* Spanning rectangles for the non-covered (by struts) region of the
138
* screen and also for just the current xinerama
140
GList *usable_screen_region;
141
GList *usable_xinerama_region;
144
static gboolean constrain_maximization (MetaWindow *window,
145
ConstraintInfo *info,
146
ConstraintPriority priority,
147
gboolean check_only);
148
static gboolean constrain_fullscreen (MetaWindow *window,
149
ConstraintInfo *info,
150
ConstraintPriority priority,
151
gboolean check_only);
152
static gboolean constrain_size_increments (MetaWindow *window,
153
ConstraintInfo *info,
154
ConstraintPriority priority,
155
gboolean check_only);
156
static gboolean constrain_size_limits (MetaWindow *window,
157
ConstraintInfo *info,
158
ConstraintPriority priority,
159
gboolean check_only);
160
static gboolean constrain_aspect_ratio (MetaWindow *window,
161
ConstraintInfo *info,
162
ConstraintPriority priority,
163
gboolean check_only);
164
static gboolean constrain_to_single_xinerama (MetaWindow *window,
165
ConstraintInfo *info,
166
ConstraintPriority priority,
167
gboolean check_only);
168
static gboolean constrain_fully_onscreen (MetaWindow *window,
169
ConstraintInfo *info,
170
ConstraintPriority priority,
171
gboolean check_only);
172
static gboolean constrain_titlebar_visible (MetaWindow *window,
173
ConstraintInfo *info,
174
ConstraintPriority priority,
175
gboolean check_only);
176
static gboolean constrain_partially_onscreen (MetaWindow *window,
177
ConstraintInfo *info,
178
ConstraintPriority priority,
179
gboolean check_only);
181
static void setup_constraint_info (ConstraintInfo *info,
183
MetaFrameGeometry *orig_fgeom,
184
MetaMoveResizeFlags flags,
186
const MetaRectangle *orig,
188
static void place_window_if_needed (MetaWindow *window,
189
ConstraintInfo *info);
190
static void update_onscreen_requirements (MetaWindow *window,
191
ConstraintInfo *info);
192
static void extend_by_frame (MetaRectangle *rect,
193
const MetaFrameGeometry *fgeom);
194
static void unextend_by_frame (MetaRectangle *rect,
195
const MetaFrameGeometry *fgeom);
196
static inline void get_size_limits (const MetaWindow *window,
197
const MetaFrameGeometry *fgeom,
198
gboolean include_frame,
199
MetaRectangle *min_size,
200
MetaRectangle *max_size);
202
typedef gboolean (* ConstraintFunc) (MetaWindow *window,
203
ConstraintInfo *info,
204
ConstraintPriority priority,
205
gboolean check_only);
212
static const Constraint all_constraints[] = {
213
{constrain_maximization, "constrain_maximization"},
214
{constrain_fullscreen, "constrain_fullscreen"},
215
{constrain_size_increments, "constrain_size_increments"},
216
{constrain_size_limits, "constrain_size_limits"},
217
{constrain_aspect_ratio, "constrain_aspect_ratio"},
218
{constrain_to_single_xinerama, "constrain_to_single_xinerama"},
219
{constrain_fully_onscreen, "constrain_fully_onscreen"},
220
{constrain_titlebar_visible, "constrain_titlebar_visible"},
221
{constrain_partially_onscreen, "constrain_partially_onscreen"},
226
do_all_constraints (MetaWindow *window,
227
ConstraintInfo *info,
228
ConstraintPriority priority,
231
const Constraint *constraint;
234
constraint = &all_constraints[0];
236
while (constraint->func != NULL)
238
satisfied = satisfied &&
239
(*constraint->func) (window, info, priority, check_only);
243
/* Log how the constraint modified the position */
244
meta_topic (META_DEBUG_GEOMETRY,
245
"info->current is %d,%d +%d,%d after %s\n",
246
info->current.x, info->current.y,
247
info->current.width, info->current.height,
252
/* Log which constraint was not satisfied */
253
meta_topic (META_DEBUG_GEOMETRY,
254
"constraint %s not satisfied.\n",
265
meta_window_constrain (MetaWindow *window,
266
MetaFrameGeometry *orig_fgeom,
267
MetaMoveResizeFlags flags,
269
const MetaRectangle *orig,
273
ConstraintPriority priority = PRIORITY_MINIMUM;
274
gboolean satisfied = FALSE;
276
/* WARNING: orig and new specify positions and sizes of the inner window,
277
* not the outer. This is a common gotcha since half the constraints
278
* deal with inner window position/size and half deal with outer. See
279
* doc/how-constraints-works.txt for more information.
281
meta_topic (META_DEBUG_GEOMETRY,
282
"Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d\n",
284
orig->x, orig->y, orig->width, orig->height,
285
new->x, new->y, new->width, new->height);
287
setup_constraint_info (&info,
294
place_window_if_needed (window, &info);
296
while (!satisfied && priority <= PRIORITY_MAXIMUM) {
297
gboolean check_only = TRUE;
299
/* Individually enforce all the high-enough priority constraints */
300
do_all_constraints (window, &info, priority, !check_only);
302
/* Check if all high-enough priority constraints are simultaneously
305
satisfied = do_all_constraints (window, &info, priority, check_only);
307
/* Drop the least important constraints if we can't satisfy them all */
311
/* Make sure we use the constrained position */
314
/* We may need to update window->require_fully_onscreen,
315
* window->require_on_single_xinerama, and perhaps other quantities
316
* if this was a user move or user move-and-resize operation.
318
update_onscreen_requirements (window, &info);
320
/* Ew, what an ugly way to do things. Destructors (in a real OOP language,
321
* not gobject-style--gobject would be more pain than it's worth) or
322
* smart pointers would be so much nicer here. *shrug*
329
setup_constraint_info (ConstraintInfo *info,
331
MetaFrameGeometry *orig_fgeom,
332
MetaMoveResizeFlags flags,
334
const MetaRectangle *orig,
337
const MetaXineramaScreenInfo *xinerama_info;
338
MetaWorkspace *cur_workspace;
341
info->current = *new;
343
/* Create a fake frame geometry if none really exists */
344
if (orig_fgeom && !window->fullscreen)
345
info->fgeom = orig_fgeom;
347
info->fgeom = g_new0 (MetaFrameGeometry, 1);
349
if (flags & META_IS_MOVE_ACTION && flags & META_IS_RESIZE_ACTION)
350
info->action_type = ACTION_MOVE_AND_RESIZE;
351
else if (flags & META_IS_RESIZE_ACTION)
352
info->action_type = ACTION_RESIZE;
353
else if (flags & META_IS_MOVE_ACTION)
354
info->action_type = ACTION_MOVE;
356
g_error ("BAD, BAD developer! No treat for you! (Fix your calls to "
357
"meta_window_move_resize_internal()).\n");
359
info->is_user_action = (flags & META_IS_USER_ACTION);
361
info->resize_gravity = resize_gravity;
363
/* FIXME: fixed_directions might be more sane if we (a) made it
364
* depend on the grab_op type instead of current amount of movement
365
* (thus implying that it only has effect when user_action is true,
366
* and (b) ignored it for aspect ratio windows -- at least in those
367
* cases where both directions do actually change size.
369
info->fixed_directions = FIXED_DIRECTION_NONE;
370
/* If x directions don't change but either y direction does */
371
if ( orig->x == new->x && orig->x + orig->width == new->x + new->width &&
372
(orig->y != new->y || orig->y + orig->height != new->y + new->height))
374
info->fixed_directions = FIXED_DIRECTION_X;
376
/* If y directions don't change but either x direction does */
377
if ( orig->y == new->y && orig->y + orig->height == new->y + new->height &&
378
(orig->x != new->x || orig->x + orig->width != new->x + new->width ))
380
info->fixed_directions = FIXED_DIRECTION_Y;
382
/* The point of fixed directions is just that "move to nearest valid
383
* position" is sometimes a poorer choice than "move to nearest
384
* valid position but only change this coordinate" for windows the
385
* user is explicitly moving. This isn't ever true for things that
386
* aren't explicit user interaction, though, so just clear it out.
388
if (!info->is_user_action)
389
info->fixed_directions = FIXED_DIRECTION_NONE;
392
meta_screen_get_xinerama_for_rect (window->screen, &info->current);
393
meta_window_get_work_area_for_xinerama (window,
394
xinerama_info->number,
395
&info->work_area_xinerama);
397
if (!window->fullscreen || window->fullscreen_monitors[0] == -1)
399
info->entire_xinerama = xinerama_info->rect;
406
monitor = window->fullscreen_monitors[i];
407
info->entire_xinerama =
408
window->screen->xinerama_infos[monitor].rect;
409
for (i = 1; i <= 3; i++)
411
monitor = window->fullscreen_monitors[i];
412
meta_rectangle_union (&info->entire_xinerama,
413
&window->screen->xinerama_infos[monitor].rect,
414
&info->entire_xinerama);
418
cur_workspace = window->screen->active_workspace;
419
info->usable_screen_region =
420
meta_workspace_get_onscreen_region (cur_workspace);
421
info->usable_xinerama_region =
422
meta_workspace_get_onxinerama_region (cur_workspace,
423
xinerama_info->number);
425
/* Workaround braindead legacy apps that don't know how to
426
* fullscreen themselves properly.
428
if (meta_prefs_get_force_fullscreen() &&
429
meta_rectangle_equal (new, &xinerama_info->rect) &&
430
window->has_fullscreen_func &&
434
meta_topic (META_DEBUG_GEOMETRY,
437
"Treating resize request of legacy application %s as a "
438
"fullscreen request\n",
440
meta_window_make_fullscreen_internal (window);
443
/* Log all this information for debugging */
444
meta_topic (META_DEBUG_GEOMETRY,
445
"Setting up constraint info:\n"
446
" orig: %d,%d +%d,%d\n"
447
" new : %d,%d +%d,%d\n"
448
" fgeom: %d,%d,%d,%d\n"
449
" action_type : %s\n"
450
" is_user_action : %s\n"
451
" resize_gravity : %s\n"
452
" fixed_directions: %s\n"
453
" work_area_xinerama: %d,%d +%d,%d\n"
454
" entire_xinerama : %d,%d +%d,%d\n",
455
info->orig.x, info->orig.y, info->orig.width, info->orig.height,
456
info->current.x, info->current.y,
457
info->current.width, info->current.height,
458
info->fgeom->left_width, info->fgeom->right_width,
459
info->fgeom->top_height, info->fgeom->bottom_height,
460
(info->action_type == ACTION_MOVE) ? "Move" :
461
(info->action_type == ACTION_RESIZE) ? "Resize" :
462
(info->action_type == ACTION_MOVE_AND_RESIZE) ? "Move&Resize" :
463
"Freakin' Invalid Stupid",
464
(info->is_user_action) ? "true" : "false",
465
meta_gravity_to_string (info->resize_gravity),
466
(info->fixed_directions == FIXED_DIRECTION_NONE) ? "None" :
467
(info->fixed_directions == FIXED_DIRECTION_X) ? "X fixed" :
468
(info->fixed_directions == FIXED_DIRECTION_Y) ? "Y fixed" :
469
"Freakin' Invalid Stupid",
470
info->work_area_xinerama.x, info->work_area_xinerama.y,
471
info->work_area_xinerama.width,
472
info->work_area_xinerama.height,
473
info->entire_xinerama.x, info->entire_xinerama.y,
474
info->entire_xinerama.width, info->entire_xinerama.height);
478
place_window_if_needed(MetaWindow *window,
479
ConstraintInfo *info)
481
gboolean did_placement;
483
/* Do placement if any, so we go ahead and apply position
484
* constraints in a move-only context. Don't place
485
* maximized/minimized/fullscreen windows until they are
486
* unmaximized, unminimized and unfullscreened.
488
did_placement = FALSE;
489
if (!window->placed &&
490
window->calc_placement &&
491
!(window->maximized_horizontally ||
492
window->maximized_vertically) &&
493
!window->minimized &&
496
MetaRectangle placed_rect = info->orig;
497
MetaWorkspace *cur_workspace;
498
const MetaXineramaScreenInfo *xinerama_info;
500
meta_window_place (window, info->fgeom, info->orig.x, info->orig.y,
501
&placed_rect.x, &placed_rect.y);
502
did_placement = TRUE;
504
/* placing the window may have changed the xinerama. Find the
505
* new xinerama and update the ConstraintInfo
508
meta_screen_get_xinerama_for_rect (window->screen, &placed_rect);
509
info->entire_xinerama = xinerama_info->rect;
510
meta_window_get_work_area_for_xinerama (window,
511
xinerama_info->number,
512
&info->work_area_xinerama);
513
cur_workspace = window->screen->active_workspace;
514
info->usable_xinerama_region =
515
meta_workspace_get_onxinerama_region (cur_workspace,
516
xinerama_info->number);
519
info->current.x = placed_rect.x;
520
info->current.y = placed_rect.y;
522
/* Since we just barely placed the window, there's no reason to
523
* consider any of the directions fixed.
525
info->fixed_directions = FIXED_DIRECTION_NONE;
528
if (window->placed || did_placement)
530
if (window->maximize_horizontally_after_placement ||
531
window->maximize_vertically_after_placement ||
532
window->fullscreen_after_placement)
534
/* define a sane saved_rect so that the user can unmaximize or
535
* make unfullscreen to something reasonable.
537
if (info->current.width >= info->work_area_xinerama.width)
539
info->current.width = .75 * info->work_area_xinerama.width;
540
info->current.x = info->work_area_xinerama.x +
541
.125 * info->work_area_xinerama.width;
543
if (info->current.height >= info->work_area_xinerama.height)
545
info->current.height = .75 * info->work_area_xinerama.height;
546
info->current.y = info->work_area_xinerama.y +
547
.083 * info->work_area_xinerama.height;
550
if (window->maximize_horizontally_after_placement ||
551
window->maximize_vertically_after_placement)
552
meta_window_maximize_internal (window,
553
(window->maximize_horizontally_after_placement ?
554
META_MAXIMIZE_HORIZONTAL : 0 ) |
555
(window->maximize_vertically_after_placement ?
556
META_MAXIMIZE_VERTICAL : 0), &info->current);
558
/* maximization may have changed frame geometry */
559
if (window->frame && !window->fullscreen)
560
meta_frame_calc_geometry (window->frame, info->fgeom);
562
if (window->fullscreen_after_placement)
564
window->saved_rect = info->current;
565
window->fullscreen = TRUE;
566
window->fullscreen_after_placement = FALSE;
569
window->maximize_horizontally_after_placement = FALSE;
570
window->maximize_vertically_after_placement = FALSE;
572
if (window->minimize_after_placement)
574
meta_window_minimize (window);
575
window->minimize_after_placement = FALSE;
581
update_onscreen_requirements (MetaWindow *window,
582
ConstraintInfo *info)
586
/* We only apply the various onscreen requirements to normal windows */
587
if (window->type == META_WINDOW_DESKTOP ||
588
window->type == META_WINDOW_DOCK)
591
/* We don't want to update the requirements for fullscreen windows;
592
* fullscreen windows are specially handled anyway, and it updating
593
* the requirements when windows enter fullscreen mode mess up the
594
* handling of the window when it leaves that mode (especially when
595
* the application sends a bunch of configurerequest events). See
598
if (window->fullscreen)
601
/* USABILITY NOTE: Naturally, I only want the require_fully_onscreen,
602
* require_on_single_xinerama, and require_titlebar_visible flags to
603
* *become false* due to user interactions (which is allowed since
604
* certain constraints are ignored for user interactions regardless of
605
* the setting of these flags). However, whether to make these flags
606
* *become true* due to just an application interaction is a little
607
* trickier. It's possible that users may find not doing that strange
608
* since two application interactions that resize in opposite ways don't
609
* necessarily end up cancelling--but it may also be strange for the user
610
* to have an application resize the window so that it's onscreen, the
611
* user forgets about it, and then later the app is able to resize itself
612
* off the screen. Anyway, for now, I think the latter is the more
613
* problematic case but this may need to be revisited.
616
/* The require onscreen/on-single-xinerama and titlebar_visible
617
* stuff is relative to the outer window, not the inner
619
extend_by_frame (&info->current, info->fgeom);
621
/* Update whether we want future constraint runs to require the
622
* window to be on fully onscreen.
624
old = window->require_fully_onscreen;
625
window->require_fully_onscreen =
626
meta_rectangle_contained_in_region (info->usable_screen_region,
628
if (old ^ window->require_fully_onscreen)
629
meta_topic (META_DEBUG_GEOMETRY,
630
"require_fully_onscreen for %s toggled to %s\n",
632
window->require_fully_onscreen ? "TRUE" : "FALSE");
634
/* Update whether we want future constraint runs to require the
635
* window to be on a single xinerama.
637
old = window->require_on_single_xinerama;
638
window->require_on_single_xinerama =
639
meta_rectangle_contained_in_region (info->usable_xinerama_region,
641
if (old ^ window->require_on_single_xinerama)
642
meta_topic (META_DEBUG_GEOMETRY,
643
"require_on_single_xinerama for %s toggled to %s\n",
645
window->require_on_single_xinerama ? "TRUE" : "FALSE");
647
/* Update whether we want future constraint runs to require the
648
* titlebar to be visible.
650
if (window->frame && window->decorated)
652
MetaRectangle titlebar_rect;
654
titlebar_rect = info->current;
655
titlebar_rect.height = info->fgeom->top_height;
656
old = window->require_titlebar_visible;
657
window->require_titlebar_visible =
658
meta_rectangle_overlaps_with_region (info->usable_screen_region,
660
if (old ^ window->require_titlebar_visible)
661
meta_topic (META_DEBUG_GEOMETRY,
662
"require_titlebar_visible for %s toggled to %s\n",
664
window->require_titlebar_visible ? "TRUE" : "FALSE");
667
/* Don't forget to restore the position of the window */
668
unextend_by_frame (&info->current, info->fgeom);
672
extend_by_frame (MetaRectangle *rect,
673
const MetaFrameGeometry *fgeom)
675
rect->x -= fgeom->left_width;
676
rect->y -= fgeom->top_height;
677
rect->width += fgeom->left_width + fgeom->right_width;
678
rect->height += fgeom->top_height + fgeom->bottom_height;
682
unextend_by_frame (MetaRectangle *rect,
683
const MetaFrameGeometry *fgeom)
685
rect->x += fgeom->left_width;
686
rect->y += fgeom->top_height;
687
rect->width -= fgeom->left_width + fgeom->right_width;
688
rect->height -= fgeom->top_height + fgeom->bottom_height;
692
get_size_limits (const MetaWindow *window,
693
const MetaFrameGeometry *fgeom,
694
gboolean include_frame,
695
MetaRectangle *min_size,
696
MetaRectangle *max_size)
698
/* We pack the results into MetaRectangle structs just for convienience; we
699
* don't actually use the position of those rects.
701
min_size->width = window->size_hints.min_width;
702
min_size->height = window->size_hints.min_height;
703
max_size->width = window->size_hints.max_width;
704
max_size->height = window->size_hints.max_height;
708
int fw = fgeom->left_width + fgeom->right_width;
709
int fh = fgeom->top_height + fgeom->bottom_height;
711
min_size->width += fw;
712
min_size->height += fh;
713
max_size->width += fw;
714
max_size->height += fh;
719
constrain_maximization (MetaWindow *window,
720
ConstraintInfo *info,
721
ConstraintPriority priority,
724
MetaRectangle target_size;
725
MetaRectangle min_size, max_size;
726
gboolean hminbad, vminbad;
727
gboolean horiz_equal, vert_equal;
728
gboolean constraint_already_satisfied;
730
if (priority > PRIORITY_MAXIMIZATION)
733
/* Determine whether constraint applies; exit if it doesn't */
734
if (!window->maximized_horizontally && !window->maximized_vertically)
737
/* Calculate target_size = maximized size of (window + frame) */
738
if (window->maximized_horizontally && window->maximized_vertically)
739
target_size = info->work_area_xinerama;
742
/* Amount of maximization possible in a single direction depends
743
* on which struts could occlude the window given its current
744
* position. For example, a vertical partial strut on the right
745
* is only relevant for a horizontally maximized window when the
746
* window is at a vertical position where it could be occluded
747
* by that partial strut.
749
MetaDirection direction;
750
GSList *active_workspace_struts;
752
if (window->maximized_horizontally)
753
direction = META_DIRECTION_HORIZONTAL;
755
direction = META_DIRECTION_VERTICAL;
756
active_workspace_struts = window->screen->active_workspace->all_struts;
758
target_size = info->current;
759
extend_by_frame (&target_size, info->fgeom);
760
meta_rectangle_expand_to_avoiding_struts (&target_size,
761
&info->entire_xinerama,
763
active_workspace_struts);
765
/* Now make target_size = maximized size of client window */
766
unextend_by_frame (&target_size, info->fgeom);
768
/* Check min size constraints; max size constraints are ignored for maximized
769
* windows, as per bug 327543.
771
get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
772
hminbad = target_size.width < min_size.width && window->maximized_horizontally;
773
vminbad = target_size.height < min_size.height && window->maximized_vertically;
774
if (hminbad || vminbad)
777
/* Determine whether constraint is already satisfied; exit if it is */
778
horiz_equal = target_size.x == info->current.x &&
779
target_size.width == info->current.width;
780
vert_equal = target_size.y == info->current.y &&
781
target_size.height == info->current.height;
782
constraint_already_satisfied =
783
(horiz_equal || !window->maximized_horizontally) &&
784
(vert_equal || !window->maximized_vertically);
785
if (check_only || constraint_already_satisfied)
786
return constraint_already_satisfied;
788
/*** Enforce constraint ***/
789
if (window->maximized_horizontally)
791
info->current.x = target_size.x;
792
info->current.width = target_size.width;
794
if (window->maximized_vertically)
796
info->current.y = target_size.y;
797
info->current.height = target_size.height;
803
constrain_fullscreen (MetaWindow *window,
804
ConstraintInfo *info,
805
ConstraintPriority priority,
808
MetaRectangle min_size, max_size, xinerama;
809
gboolean too_big, too_small, constraint_already_satisfied;
811
if (priority > PRIORITY_FULLSCREEN)
814
/* Determine whether constraint applies; exit if it doesn't */
815
if (!window->fullscreen)
818
xinerama = info->entire_xinerama;
820
get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
821
too_big = !meta_rectangle_could_fit_rect (&xinerama, &min_size);
822
too_small = !meta_rectangle_could_fit_rect (&max_size, &xinerama);
823
if (too_big || too_small)
826
/* Determine whether constraint is already satisfied; exit if it is */
827
constraint_already_satisfied =
828
meta_rectangle_equal (&info->current, &xinerama);
829
if (check_only || constraint_already_satisfied)
830
return constraint_already_satisfied;
832
/*** Enforce constraint ***/
833
info->current = xinerama;
838
constrain_size_increments (MetaWindow *window,
839
ConstraintInfo *info,
840
ConstraintPriority priority,
843
int bh, hi, bw, wi, extra_height, extra_width;
844
int new_width, new_height;
845
gboolean constraint_already_satisfied;
846
MetaRectangle *start_rect;
848
if (priority > PRIORITY_SIZE_HINTS_INCREMENTS)
851
/* Determine whether constraint applies; exit if it doesn't */
852
if (META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
853
info->action_type == ACTION_MOVE)
856
/* Determine whether constraint is already satisfied; exit if it is */
857
bh = window->size_hints.base_height;
858
hi = window->size_hints.height_inc;
859
bw = window->size_hints.base_width;
860
wi = window->size_hints.width_inc;
861
extra_height = (info->current.height - bh) % hi;
862
extra_width = (info->current.width - bw) % wi;
863
/* ignore size increments for maximized windows */
864
if (window->maximized_horizontally)
866
if (window->maximized_vertically)
868
/* constraint is satisfied iff there is no extra height or width */
869
constraint_already_satisfied =
870
(extra_height == 0 && extra_width == 0);
872
if (check_only || constraint_already_satisfied)
873
return constraint_already_satisfied;
875
/*** Enforce constraint ***/
876
new_width = info->current.width - extra_width;
877
new_height = info->current.height - extra_height;
879
/* Adjusting down instead of up (as done in the above two lines) may
880
* violate minimum size constraints; fix the adjustment if this
883
if (new_width < window->size_hints.min_width)
884
new_width += ((window->size_hints.min_width - new_width)/wi + 1)*wi;
885
if (new_height < window->size_hints.min_height)
886
new_height += ((window->size_hints.min_height - new_height)/hi + 1)*hi;
888
/* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
891
if (info->action_type == ACTION_MOVE_AND_RESIZE)
892
start_rect = &info->current;
894
start_rect = &info->orig;
896
/* Resize to the new size */
897
meta_rectangle_resize_with_gravity (start_rect,
899
info->resize_gravity,
906
constrain_size_limits (MetaWindow *window,
907
ConstraintInfo *info,
908
ConstraintPriority priority,
911
MetaRectangle min_size, max_size;
912
gboolean too_big, too_small, constraint_already_satisfied;
913
int new_width, new_height;
914
MetaRectangle *start_rect;
916
if (priority > PRIORITY_SIZE_HINTS_LIMITS)
919
/* Determine whether constraint applies; exit if it doesn't.
921
* Note: The old code didn't apply this constraint for fullscreen or
922
* maximized windows--but that seems odd to me. *shrug*
924
if (info->action_type == ACTION_MOVE)
927
/* Determine whether constraint is already satisfied; exit if it is */
928
get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
929
/* We ignore max-size limits for maximized windows; see #327543 */
930
if (window->maximized_horizontally)
931
max_size.width = MAX (max_size.width, info->current.width);
932
if (window->maximized_vertically)
933
max_size.height = MAX (max_size.height, info->current.height);
934
too_small = !meta_rectangle_could_fit_rect (&info->current, &min_size);
935
too_big = !meta_rectangle_could_fit_rect (&max_size, &info->current);
936
constraint_already_satisfied = !too_big && !too_small;
937
if (check_only || constraint_already_satisfied)
938
return constraint_already_satisfied;
940
/*** Enforce constraint ***/
941
new_width = CLAMP (info->current.width, min_size.width, max_size.width);
942
new_height = CLAMP (info->current.height, min_size.height, max_size.height);
944
/* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
947
if (info->action_type == ACTION_MOVE_AND_RESIZE)
948
start_rect = &info->current;
950
start_rect = &info->orig;
952
meta_rectangle_resize_with_gravity (start_rect,
954
info->resize_gravity,
961
constrain_aspect_ratio (MetaWindow *window,
962
ConstraintInfo *info,
963
ConstraintPriority priority,
967
gboolean constraints_are_inconsistent, constraint_already_satisfied;
968
int fudge, new_width, new_height;
969
double best_width, best_height;
970
double alt_width, alt_height;
971
MetaRectangle *start_rect;
973
if (priority > PRIORITY_ASPECT_RATIO)
976
/* Determine whether constraint applies; exit if it doesn't. */
977
minr = window->size_hints.min_aspect.x /
978
(double)window->size_hints.min_aspect.y;
979
maxr = window->size_hints.max_aspect.x /
980
(double)window->size_hints.max_aspect.y;
981
constraints_are_inconsistent = minr > maxr;
982
if (constraints_are_inconsistent ||
983
META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
984
info->action_type == ACTION_MOVE)
987
/* Determine whether constraint is already satisfied; exit if it is. We
988
* need the following to hold:
991
* minr <= ------ <= maxr
994
* But we need to allow for some slight fudging since width and height
995
* are integers instead of floating point numbers (this is particularly
996
* important when minr == maxr), so we allow width and height to be off
997
* a little bit from strictly satisfying these equations. For just one
998
* sided resizing, we have to make the fudge factor a little bigger
999
* because of how meta_rectangle_resize_with_gravity treats those as
1000
* being a resize increment (FIXME: I should handle real resize
1001
* increments better here...)
1003
switch (info->resize_gravity)
1012
case NorthWestGravity:
1013
case SouthWestGravity:
1015
case NorthEastGravity:
1016
case SouthEastGravity:
1022
constraint_already_satisfied =
1023
info->current.width - (info->current.height * minr ) > -minr*fudge &&
1024
info->current.width - (info->current.height * maxr ) < maxr*fudge;
1025
if (check_only || constraint_already_satisfied)
1026
return constraint_already_satisfied;
1028
/*** Enforce constraint ***/
1029
new_width = info->current.width;
1030
new_height = info->current.height;
1032
switch (info->resize_gravity)
1036
/* Yeah, I suck for doing implicit rounding -- sue me */
1037
new_height = CLAMP (new_height, new_width / maxr, new_width / minr);
1042
/* Yeah, I suck for doing implicit rounding -- sue me */
1043
new_width = CLAMP (new_width, new_height * minr, new_height * maxr);
1046
case NorthWestGravity:
1047
case SouthWestGravity:
1049
case NorthEastGravity:
1050
case SouthEastGravity:
1053
/* Find what width would correspond to new_height, and what height would
1054
* correspond to new_width */
1055
alt_width = CLAMP (new_width, new_height * minr, new_height * maxr);
1056
alt_height = CLAMP (new_height, new_width / maxr, new_width / minr);
1058
/* The line connecting the points (alt_width, new_height) and
1059
* (new_width, alt_height) provide a range of
1060
* valid-for-the-aspect-ratio-constraint sizes. We want the
1061
* size in that range closest to the value requested, i.e. the
1062
* point on the line which is closest to the point (new_width,
1065
meta_rectangle_find_linepoint_closest_to_point (alt_width, new_height,
1066
new_width, alt_height,
1067
new_width, new_height,
1068
&best_width, &best_height);
1070
/* Yeah, I suck for doing implicit rounding -- sue me */
1071
new_width = best_width;
1072
new_height = best_height;
1077
/* Figure out what original rect to pass to meta_rectangle_resize_with_gravity
1080
if (info->action_type == ACTION_MOVE_AND_RESIZE)
1081
start_rect = &info->current;
1083
start_rect = &info->orig;
1085
meta_rectangle_resize_with_gravity (start_rect,
1087
info->resize_gravity,
1095
do_screen_and_xinerama_relative_constraints (
1097
GList *region_spanning_rectangles,
1098
ConstraintInfo *info,
1099
gboolean check_only)
1101
gboolean exit_early = FALSE, constraint_satisfied;
1102
MetaRectangle how_far_it_can_be_smushed, min_size, max_size;
1104
#ifdef WITH_VERBOSE_MODE
1105
if (meta_is_verbose ())
1107
/* First, log some debugging information */
1108
char spanning_region[1 + 28 * g_list_length (region_spanning_rectangles)];
1110
meta_topic (META_DEBUG_GEOMETRY,
1111
"screen/xinerama constraint; region_spanning_rectangles: %s\n",
1112
meta_rectangle_region_to_string (region_spanning_rectangles, ", ",
1117
/* Determine whether constraint applies; exit if it doesn't */
1118
how_far_it_can_be_smushed = info->current;
1119
get_size_limits (window, info->fgeom, TRUE, &min_size, &max_size);
1120
extend_by_frame (&info->current, info->fgeom);
1122
if (info->action_type != ACTION_MOVE)
1124
if (!(info->fixed_directions & FIXED_DIRECTION_X))
1125
how_far_it_can_be_smushed.width = min_size.width;
1127
if (!(info->fixed_directions & FIXED_DIRECTION_Y))
1128
how_far_it_can_be_smushed.height = min_size.height;
1130
if (!meta_rectangle_could_fit_in_region (region_spanning_rectangles,
1131
&how_far_it_can_be_smushed))
1134
/* Determine whether constraint is already satisfied; exit if it is */
1135
constraint_satisfied =
1136
meta_rectangle_contained_in_region (region_spanning_rectangles,
1138
if (exit_early || constraint_satisfied || check_only)
1140
unextend_by_frame (&info->current, info->fgeom);
1141
return constraint_satisfied;
1144
/* Enforce constraint */
1146
/* Clamp rectangle size for resize or move+resize actions */
1147
if (info->action_type != ACTION_MOVE)
1148
meta_rectangle_clamp_to_fit_into_region (region_spanning_rectangles,
1149
info->fixed_directions,
1153
if (info->is_user_action && info->action_type == ACTION_RESIZE)
1154
/* For user resize, clip to the relevant region */
1155
meta_rectangle_clip_to_region (region_spanning_rectangles,
1156
info->fixed_directions,
1159
/* For everything else, shove the rectangle into the relevant region */
1160
meta_rectangle_shove_into_region (region_spanning_rectangles,
1161
info->fixed_directions,
1164
unextend_by_frame (&info->current, info->fgeom);
1169
constrain_to_single_xinerama (MetaWindow *window,
1170
ConstraintInfo *info,
1171
ConstraintPriority priority,
1172
gboolean check_only)
1174
if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA)
1177
/* Exit early if we know the constraint won't apply--note that this constraint
1178
* is only meant for normal windows (e.g. we don't want docks to be shoved
1179
* "onscreen" by their own strut) and we can't apply it to frameless windows
1180
* or else users will be unable to move windows such as XMMS across xineramas.
1182
if (window->type == META_WINDOW_DESKTOP ||
1183
window->type == META_WINDOW_DOCK ||
1184
window->screen->n_xinerama_infos == 1 ||
1185
!window->require_on_single_xinerama ||
1187
info->is_user_action)
1190
/* Have a helper function handle the constraint for us */
1191
return do_screen_and_xinerama_relative_constraints (window,
1192
info->usable_xinerama_region,
1198
constrain_fully_onscreen (MetaWindow *window,
1199
ConstraintInfo *info,
1200
ConstraintPriority priority,
1201
gboolean check_only)
1203
if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA)
1206
/* Exit early if we know the constraint won't apply--note that this constraint
1207
* is only meant for normal windows (e.g. we don't want docks to be shoved
1208
* "onscreen" by their own strut).
1210
if (window->type == META_WINDOW_DESKTOP ||
1211
window->type == META_WINDOW_DOCK ||
1212
window->fullscreen ||
1213
!window->require_fully_onscreen ||
1214
info->is_user_action)
1217
/* Have a helper function handle the constraint for us */
1218
return do_screen_and_xinerama_relative_constraints (window,
1219
info->usable_screen_region,
1225
constrain_titlebar_visible (MetaWindow *window,
1226
ConstraintInfo *info,
1227
ConstraintPriority priority,
1228
gboolean check_only)
1230
gboolean unconstrained_user_action;
1233
int horiz_amount_offscreen, vert_amount_offscreen;
1234
int horiz_amount_onscreen, vert_amount_onscreen;
1236
if (priority > PRIORITY_TITLEBAR_VISIBLE)
1239
/* Allow the titlebar beyond the top of the screen only if the user wasn't
1240
* clicking on the frame to start the move.
1242
unconstrained_user_action =
1243
info->is_user_action && !window->display->grab_frame_action;
1245
/* Exit early if we know the constraint won't apply--note that this constraint
1246
* is only meant for normal windows (e.g. we don't want docks to be shoved
1247
* "onscreen" by their own strut).
1249
if (window->type == META_WINDOW_DESKTOP ||
1250
window->type == META_WINDOW_DOCK ||
1251
window->fullscreen ||
1252
!window->require_titlebar_visible ||
1253
!window->decorated ||
1254
unconstrained_user_action)
1257
/* Determine how much offscreen things are allowed. We first need to
1258
* figure out how much must remain on the screen. For that, we use 25%
1259
* window width/height but clamp to the range of (10,75) pixels. This is
1260
* somewhat of a seat of my pants random guess at what might look good.
1261
* Then, the amount that is allowed off is just the window size minus
1262
* this amount (but no less than 0 for tiny windows).
1264
horiz_amount_onscreen = info->current.width / 4;
1265
vert_amount_onscreen = info->current.height / 4;
1266
horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
1267
vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75);
1268
horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
1269
vert_amount_offscreen = info->current.height - vert_amount_onscreen;
1270
horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
1271
vert_amount_offscreen = MAX (vert_amount_offscreen, 0);
1272
/* Allow the titlebar to touch the bottom panel; If there is no titlebar,
1273
* require vert_amount to remain on the screen.
1277
bottom_amount = info->current.height + info->fgeom->bottom_height;
1278
vert_amount_onscreen = info->fgeom->top_height;
1281
bottom_amount = vert_amount_offscreen;
1283
/* Extend the region, have a helper function handle the constraint,
1284
* then return the region to its original size.
1286
meta_rectangle_expand_region_conditionally (info->usable_screen_region,
1287
horiz_amount_offscreen,
1288
horiz_amount_offscreen,
1289
0, /* Don't let titlebar off */
1291
horiz_amount_onscreen,
1292
vert_amount_onscreen);
1294
do_screen_and_xinerama_relative_constraints (window,
1295
info->usable_screen_region,
1298
meta_rectangle_expand_region_conditionally (info->usable_screen_region,
1299
-horiz_amount_offscreen,
1300
-horiz_amount_offscreen,
1301
0, /* Don't let titlebar off */
1303
horiz_amount_onscreen,
1304
vert_amount_onscreen);
1310
constrain_partially_onscreen (MetaWindow *window,
1311
ConstraintInfo *info,
1312
ConstraintPriority priority,
1313
gboolean check_only)
1316
int top_amount, bottom_amount;
1317
int horiz_amount_offscreen, vert_amount_offscreen;
1318
int horiz_amount_onscreen, vert_amount_onscreen;
1320
if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA)
1323
/* Exit early if we know the constraint won't apply--note that this constraint
1324
* is only meant for normal windows (e.g. we don't want docks to be shoved
1325
* "onscreen" by their own strut).
1327
if (window->type == META_WINDOW_DESKTOP ||
1328
window->type == META_WINDOW_DOCK)
1331
/* Determine how much offscreen things are allowed. We first need to
1332
* figure out how much must remain on the screen. For that, we use 25%
1333
* window width/height but clamp to the range of (10,75) pixels. This is
1334
* somewhat of a seat of my pants random guess at what might look good.
1335
* Then, the amount that is allowed off is just the window size minus
1336
* this amount (but no less than 0 for tiny windows).
1338
horiz_amount_onscreen = info->current.width / 4;
1339
vert_amount_onscreen = info->current.height / 4;
1340
horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
1341
vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75);
1342
horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
1343
vert_amount_offscreen = info->current.height - vert_amount_onscreen;
1344
horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
1345
vert_amount_offscreen = MAX (vert_amount_offscreen, 0);
1346
top_amount = vert_amount_offscreen;
1347
/* Allow the titlebar to touch the bottom panel; If there is no titlebar,
1348
* require vert_amount to remain on the screen.
1352
bottom_amount = info->current.height + info->fgeom->bottom_height;
1353
vert_amount_onscreen = info->fgeom->top_height;
1356
bottom_amount = vert_amount_offscreen;
1358
/* Extend the region, have a helper function handle the constraint,
1359
* then return the region to its original size.
1361
meta_rectangle_expand_region_conditionally (info->usable_screen_region,
1362
horiz_amount_offscreen,
1363
horiz_amount_offscreen,
1366
horiz_amount_onscreen,
1367
vert_amount_onscreen);
1369
do_screen_and_xinerama_relative_constraints (window,
1370
info->usable_screen_region,
1373
meta_rectangle_expand_region_conditionally (info->usable_screen_region,
1374
-horiz_amount_offscreen,
1375
-horiz_amount_offscreen,
1378
horiz_amount_onscreen,
1379
vert_amount_onscreen);