00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <config.h>
00027 #include "constraints.h"
00028 #include "workspace.h"
00029 #include "place.h"
00030
00031 #include <stdlib.h>
00032 #include <math.h>
00033
00034 #if 0
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090 #endif
00091
00092 typedef enum
00093 {
00094 PRIORITY_MINIMUM = 0,
00095 PRIORITY_ASPECT_RATIO = 0,
00096 PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA = 0,
00097 PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
00098 PRIORITY_SIZE_HINTS_INCREMENTS = 1,
00099 PRIORITY_MAXIMIZATION = 2,
00100 PRIORITY_FULLSCREEN = 2,
00101 PRIORITY_SIZE_HINTS_LIMITS = 3,
00102 PRIORITY_TITLEBAR_VISIBLE = 4,
00103 PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4,
00104 PRIORITY_MAXIMUM = 4
00105 } ConstraintPriority;
00106
00107 typedef enum
00108 {
00109 ACTION_MOVE,
00110 ACTION_RESIZE,
00111 ACTION_MOVE_AND_RESIZE
00112 } ActionType;
00113
00114 typedef struct
00115 {
00116 MetaRectangle orig;
00117 MetaRectangle current;
00118 MetaFrameGeometry *fgeom;
00119 ActionType action_type;
00120 gboolean is_user_action;
00121
00122
00123
00124
00125
00126
00127 int resize_gravity;
00128 FixedDirections fixed_directions;
00129
00130
00131
00132
00133 MetaRectangle work_area_xinerama;
00134 MetaRectangle entire_xinerama;
00135
00136
00137
00138
00139 GList *usable_screen_region;
00140 GList *usable_xinerama_region;
00141 } ConstraintInfo;
00142
00143 static gboolean constrain_maximization (MetaWindow *window,
00144 ConstraintInfo *info,
00145 ConstraintPriority priority,
00146 gboolean check_only);
00147 static gboolean constrain_fullscreen (MetaWindow *window,
00148 ConstraintInfo *info,
00149 ConstraintPriority priority,
00150 gboolean check_only);
00151 static gboolean constrain_size_increments (MetaWindow *window,
00152 ConstraintInfo *info,
00153 ConstraintPriority priority,
00154 gboolean check_only);
00155 static gboolean constrain_size_limits (MetaWindow *window,
00156 ConstraintInfo *info,
00157 ConstraintPriority priority,
00158 gboolean check_only);
00159 static gboolean constrain_aspect_ratio (MetaWindow *window,
00160 ConstraintInfo *info,
00161 ConstraintPriority priority,
00162 gboolean check_only);
00163 static gboolean constrain_to_single_xinerama (MetaWindow *window,
00164 ConstraintInfo *info,
00165 ConstraintPriority priority,
00166 gboolean check_only);
00167 static gboolean constrain_fully_onscreen (MetaWindow *window,
00168 ConstraintInfo *info,
00169 ConstraintPriority priority,
00170 gboolean check_only);
00171 static gboolean constrain_titlebar_visible (MetaWindow *window,
00172 ConstraintInfo *info,
00173 ConstraintPriority priority,
00174 gboolean check_only);
00175 static gboolean constrain_partially_onscreen (MetaWindow *window,
00176 ConstraintInfo *info,
00177 ConstraintPriority priority,
00178 gboolean check_only);
00179
00180 static void setup_constraint_info (ConstraintInfo *info,
00181 MetaWindow *window,
00182 MetaFrameGeometry *orig_fgeom,
00183 MetaMoveResizeFlags flags,
00184 int resize_gravity,
00185 const MetaRectangle *orig,
00186 MetaRectangle *new);
00187 static void place_window_if_needed (MetaWindow *window,
00188 ConstraintInfo *info);
00189 static void update_onscreen_requirements (MetaWindow *window,
00190 ConstraintInfo *info);
00191 static void extend_by_frame (MetaRectangle *rect,
00192 const MetaFrameGeometry *fgeom);
00193 static void unextend_by_frame (MetaRectangle *rect,
00194 const MetaFrameGeometry *fgeom);
00195 static inline void get_size_limits (const MetaWindow *window,
00196 const MetaFrameGeometry *fgeom,
00197 gboolean include_frame,
00198 MetaRectangle *min_size,
00199 MetaRectangle *max_size);
00200
00201 typedef gboolean (* ConstraintFunc) (MetaWindow *window,
00202 ConstraintInfo *info,
00203 ConstraintPriority priority,
00204 gboolean check_only);
00205
00206 typedef struct {
00207 ConstraintFunc func;
00208 const char* name;
00209 } Constraint;
00210
00211 static const Constraint all_constraints[] = {
00212 {constrain_maximization, "constrain_maximization"},
00213 {constrain_fullscreen, "constrain_fullscreen"},
00214 {constrain_size_increments, "constrain_size_increments"},
00215 {constrain_size_limits, "constrain_size_limits"},
00216 {constrain_aspect_ratio, "constrain_aspect_ratio"},
00217 {constrain_to_single_xinerama, "constrain_to_single_xinerama"},
00218 {constrain_fully_onscreen, "constrain_fully_onscreen"},
00219 {constrain_titlebar_visible, "constrain_titlebar_visible"},
00220 {constrain_partially_onscreen, "constrain_partially_onscreen"},
00221 {NULL, NULL}
00222 };
00223
00224 static gboolean
00225 do_all_constraints (MetaWindow *window,
00226 ConstraintInfo *info,
00227 ConstraintPriority priority,
00228 gboolean check_only)
00229 {
00230 const Constraint *constraint;
00231 gboolean satisfied;
00232
00233 constraint = &all_constraints[0];
00234 satisfied = TRUE;
00235 while (constraint->func != NULL)
00236 {
00237 satisfied = satisfied &&
00238 (*constraint->func) (window, info, priority, check_only);
00239
00240 if (!check_only)
00241 {
00242
00243 meta_topic (META_DEBUG_GEOMETRY,
00244 "info->current is %d,%d +%d,%d after %s\n",
00245 info->current.x, info->current.y,
00246 info->current.width, info->current.height,
00247 constraint->name);
00248 }
00249 else if (!satisfied)
00250 {
00251
00252 meta_topic (META_DEBUG_GEOMETRY,
00253 "constraint %s not satisfied.\n",
00254 constraint->name);
00255 return FALSE;
00256 }
00257 ++constraint;
00258 }
00259
00260 return TRUE;
00261 }
00262
00263 void
00264 meta_window_constrain (MetaWindow *window,
00265 MetaFrameGeometry *orig_fgeom,
00266 MetaMoveResizeFlags flags,
00267 int resize_gravity,
00268 const MetaRectangle *orig,
00269 MetaRectangle *new)
00270 {
00271 ConstraintInfo info;
00272 ConstraintPriority priority = PRIORITY_MINIMUM;
00273 gboolean satisfied = FALSE;
00274
00275
00276
00277
00278
00279
00280 meta_topic (META_DEBUG_GEOMETRY,
00281 "Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d\n",
00282 window->desc,
00283 orig->x, orig->y, orig->width, orig->height,
00284 new->x, new->y, new->width, new->height);
00285
00286 setup_constraint_info (&info,
00287 window,
00288 orig_fgeom,
00289 flags,
00290 resize_gravity,
00291 orig,
00292 new);
00293 place_window_if_needed (window, &info);
00294
00295 while (!satisfied && priority <= PRIORITY_MAXIMUM) {
00296 gboolean check_only = TRUE;
00297
00298
00299 do_all_constraints (window, &info, priority, !check_only);
00300
00301
00302
00303
00304 satisfied = do_all_constraints (window, &info, priority, check_only);
00305
00306
00307 priority++;
00308 }
00309
00310
00311 *new = info.current;
00312
00313
00314
00315
00316
00317 update_onscreen_requirements (window, &info);
00318
00319
00320
00321
00322
00323 if (!orig_fgeom)
00324 g_free (info.fgeom);
00325 }
00326
00327 static void
00328 setup_constraint_info (ConstraintInfo *info,
00329 MetaWindow *window,
00330 MetaFrameGeometry *orig_fgeom,
00331 MetaMoveResizeFlags flags,
00332 int resize_gravity,
00333 const MetaRectangle *orig,
00334 MetaRectangle *new)
00335 {
00336 const MetaXineramaScreenInfo *xinerama_info;
00337 MetaWorkspace *cur_workspace;
00338
00339 info->orig = *orig;
00340 info->current = *new;
00341
00342
00343 if (orig_fgeom && !window->fullscreen)
00344 info->fgeom = orig_fgeom;
00345 else
00346 info->fgeom = g_new0 (MetaFrameGeometry, 1);
00347
00348 if (flags & META_IS_MOVE_ACTION && flags & META_IS_RESIZE_ACTION)
00349 info->action_type = ACTION_MOVE_AND_RESIZE;
00350 else if (flags & META_IS_RESIZE_ACTION)
00351 info->action_type = ACTION_RESIZE;
00352 else if (flags & META_IS_MOVE_ACTION)
00353 info->action_type = ACTION_MOVE;
00354 else
00355 g_error ("BAD, BAD developer! No treat for you! (Fix your calls to "
00356 "meta_window_move_resize_internal()).\n");
00357
00358 info->is_user_action = (flags & META_IS_USER_ACTION);
00359
00360 info->resize_gravity = resize_gravity;
00361
00362
00363
00364
00365
00366
00367
00368 info->fixed_directions = FIXED_DIRECTION_NONE;
00369
00370 if ( orig->x == new->x && orig->x + orig->width == new->x + new->width &&
00371 (orig->y != new->y || orig->y + orig->height != new->y + new->height))
00372 {
00373 info->fixed_directions = FIXED_DIRECTION_X;
00374 }
00375
00376 if ( orig->y == new->y && orig->y + orig->height == new->y + new->height &&
00377 (orig->x != new->x || orig->x + orig->width != new->x + new->width ))
00378 {
00379 info->fixed_directions = FIXED_DIRECTION_Y;
00380 }
00381
00382
00383
00384
00385
00386
00387 if (!info->is_user_action)
00388 info->fixed_directions = FIXED_DIRECTION_NONE;
00389
00390 xinerama_info =
00391 meta_screen_get_xinerama_for_rect (window->screen, &info->current);
00392 meta_window_get_work_area_for_xinerama (window,
00393 xinerama_info->number,
00394 &info->work_area_xinerama);
00395 info->entire_xinerama = xinerama_info->rect;
00396
00397 cur_workspace = window->screen->active_workspace;
00398 info->usable_screen_region =
00399 meta_workspace_get_onscreen_region (cur_workspace);
00400 info->usable_xinerama_region =
00401 meta_workspace_get_onxinerama_region (cur_workspace,
00402 xinerama_info->number);
00403
00404
00405
00406
00407 if (meta_rectangle_equal (new, &xinerama_info->rect) &&
00408 window->has_fullscreen_func &&
00409 !window->fullscreen)
00410 {
00411
00412
00413
00414 meta_warning (
00415 "Treating resize request of legacy application %s as a "
00416 "fullscreen request\n",
00417 window->desc);
00418 meta_window_make_fullscreen_internal (window);
00419 }
00420
00421
00422 meta_topic (META_DEBUG_GEOMETRY,
00423 "Setting up constraint info:\n"
00424 " orig: %d,%d +%d,%d\n"
00425 " new : %d,%d +%d,%d\n"
00426 " fgeom: %d,%d,%d,%d\n"
00427 " action_type : %s\n"
00428 " is_user_action : %s\n"
00429 " resize_gravity : %s\n"
00430 " fixed_directions: %s\n"
00431 " work_area_xinerama: %d,%d +%d,%d\n"
00432 " entire_xinerama : %d,%d +%d,%d\n",
00433 info->orig.x, info->orig.y, info->orig.width, info->orig.height,
00434 info->current.x, info->current.y,
00435 info->current.width, info->current.height,
00436 info->fgeom->left_width, info->fgeom->right_width,
00437 info->fgeom->top_height, info->fgeom->bottom_height,
00438 (info->action_type == ACTION_MOVE) ? "Move" :
00439 (info->action_type == ACTION_RESIZE) ? "Resize" :
00440 (info->action_type == ACTION_MOVE_AND_RESIZE) ? "Move&Resize" :
00441 "Freakin' Invalid Stupid",
00442 (info->is_user_action) ? "true" : "false",
00443 meta_gravity_to_string (info->resize_gravity),
00444 (info->fixed_directions == FIXED_DIRECTION_NONE) ? "None" :
00445 (info->fixed_directions == FIXED_DIRECTION_X) ? "X fixed" :
00446 (info->fixed_directions == FIXED_DIRECTION_Y) ? "Y fixed" :
00447 "Freakin' Invalid Stupid",
00448 info->work_area_xinerama.x, info->work_area_xinerama.y,
00449 info->work_area_xinerama.width,
00450 info->work_area_xinerama.height,
00451 info->entire_xinerama.x, info->entire_xinerama.y,
00452 info->entire_xinerama.width, info->entire_xinerama.height);
00453 }
00454
00455 static void
00456 place_window_if_needed(MetaWindow *window,
00457 ConstraintInfo *info)
00458 {
00459 gboolean did_placement;
00460
00461
00462
00463
00464
00465
00466 did_placement = FALSE;
00467 if (!window->placed &&
00468 window->calc_placement &&
00469 !(window->maximized_horizontally ||
00470 window->maximized_vertically) &&
00471 !window->minimized &&
00472 !window->fullscreen)
00473 {
00474 MetaRectangle placed_rect = info->orig;
00475 MetaWorkspace *cur_workspace;
00476 const MetaXineramaScreenInfo *xinerama_info;
00477
00478 meta_window_place (window, info->fgeom, info->orig.x, info->orig.y,
00479 &placed_rect.x, &placed_rect.y);
00480 did_placement = TRUE;
00481
00482
00483
00484
00485 xinerama_info =
00486 meta_screen_get_xinerama_for_rect (window->screen, &placed_rect);
00487 info->entire_xinerama = xinerama_info->rect;
00488 meta_window_get_work_area_for_xinerama (window,
00489 xinerama_info->number,
00490 &info->work_area_xinerama);
00491 cur_workspace = window->screen->active_workspace;
00492 info->usable_xinerama_region =
00493 meta_workspace_get_onxinerama_region (cur_workspace,
00494 xinerama_info->number);
00495
00496
00497 info->current.x = placed_rect.x;
00498 info->current.y = placed_rect.y;
00499
00500
00501
00502
00503 info->fixed_directions = FIXED_DIRECTION_NONE;
00504 }
00505
00506 if (window->placed || did_placement)
00507 {
00508 if (window->maximize_horizontally_after_placement ||
00509 window->maximize_vertically_after_placement)
00510 {
00511
00512
00513
00514 if (info->current.width >= info->work_area_xinerama.width)
00515 {
00516 info->current.width = .75 * info->work_area_xinerama.width;
00517 info->current.x = info->work_area_xinerama.x +
00518 .125 * info->work_area_xinerama.width;
00519 }
00520 if (info->current.height >= info->work_area_xinerama.height)
00521 {
00522 info->current.height = .75 * info->work_area_xinerama.height;
00523 info->current.y = info->work_area_xinerama.y +
00524 .083 * info->work_area_xinerama.height;
00525 }
00526
00527 if (window->maximize_horizontally_after_placement ||
00528 window->maximize_vertically_after_placement)
00529 meta_window_maximize_internal (window,
00530 (window->maximize_horizontally_after_placement ?
00531 META_MAXIMIZE_HORIZONTAL : 0 ) |
00532 (window->maximize_vertically_after_placement ?
00533 META_MAXIMIZE_VERTICAL : 0), &info->current);
00534
00535
00536 if (window->frame && !window->fullscreen)
00537 meta_frame_calc_geometry (window->frame, info->fgeom);
00538
00539 window->maximize_horizontally_after_placement = FALSE;
00540 window->maximize_vertically_after_placement = FALSE;
00541 }
00542 if (window->minimize_after_placement)
00543 {
00544 meta_window_minimize (window);
00545 window->minimize_after_placement = FALSE;
00546 }
00547 }
00548 }
00549
00550 static void
00551 update_onscreen_requirements (MetaWindow *window,
00552 ConstraintInfo *info)
00553 {
00554 gboolean old;
00555
00556
00557 if (window->type == META_WINDOW_DESKTOP ||
00558 window->type == META_WINDOW_DOCK)
00559 return;
00560
00561
00562
00563
00564
00565
00566
00567
00568 if (window->fullscreen)
00569 return;
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589 extend_by_frame (&info->current, info->fgeom);
00590
00591
00592
00593
00594 old = window->require_fully_onscreen;
00595 window->require_fully_onscreen =
00596 meta_rectangle_contained_in_region (info->usable_screen_region,
00597 &info->current);
00598 if (old ^ window->require_fully_onscreen)
00599 meta_topic (META_DEBUG_GEOMETRY,
00600 "require_fully_onscreen for %s toggled to %s\n",
00601 window->desc,
00602 window->require_fully_onscreen ? "TRUE" : "FALSE");
00603
00604
00605
00606
00607 old = window->require_on_single_xinerama;
00608 window->require_on_single_xinerama =
00609 meta_rectangle_contained_in_region (info->usable_xinerama_region,
00610 &info->current);
00611 if (old ^ window->require_on_single_xinerama)
00612 meta_topic (META_DEBUG_GEOMETRY,
00613 "require_on_single_xinerama for %s toggled to %s\n",
00614 window->desc,
00615 window->require_on_single_xinerama ? "TRUE" : "FALSE");
00616
00617
00618
00619
00620 if (window->frame && window->decorated)
00621 {
00622 MetaRectangle titlebar_rect;
00623
00624 titlebar_rect = info->current;
00625 titlebar_rect.height = info->fgeom->top_height;
00626 old = window->require_titlebar_visible;
00627 window->require_titlebar_visible =
00628 meta_rectangle_overlaps_with_region (info->usable_screen_region,
00629 &titlebar_rect);
00630 if (old ^ window->require_titlebar_visible)
00631 meta_topic (META_DEBUG_GEOMETRY,
00632 "require_titlebar_visible for %s toggled to %s\n",
00633 window->desc,
00634 window->require_titlebar_visible ? "TRUE" : "FALSE");
00635 }
00636
00637
00638 unextend_by_frame (&info->current, info->fgeom);
00639 }
00640
00641 static void
00642 extend_by_frame (MetaRectangle *rect,
00643 const MetaFrameGeometry *fgeom)
00644 {
00645 rect->x -= fgeom->left_width;
00646 rect->y -= fgeom->top_height;
00647 rect->width += fgeom->left_width + fgeom->right_width;
00648 rect->height += fgeom->top_height + fgeom->bottom_height;
00649 }
00650
00651 static void
00652 unextend_by_frame (MetaRectangle *rect,
00653 const MetaFrameGeometry *fgeom)
00654 {
00655 rect->x += fgeom->left_width;
00656 rect->y += fgeom->top_height;
00657 rect->width -= fgeom->left_width + fgeom->right_width;
00658 rect->height -= fgeom->top_height + fgeom->bottom_height;
00659 }
00660
00661 static inline void
00662 get_size_limits (const MetaWindow *window,
00663 const MetaFrameGeometry *fgeom,
00664 gboolean include_frame,
00665 MetaRectangle *min_size,
00666 MetaRectangle *max_size)
00667 {
00668
00669
00670
00671 min_size->width = window->size_hints.min_width;
00672 min_size->height = window->size_hints.min_height;
00673 max_size->width = window->size_hints.max_width;
00674 max_size->height = window->size_hints.max_height;
00675
00676 if (include_frame)
00677 {
00678 int fw = fgeom->left_width + fgeom->right_width;
00679 int fh = fgeom->top_height + fgeom->bottom_height;
00680
00681 min_size->width += fw;
00682 min_size->height += fh;
00683 max_size->width += fw;
00684 max_size->height += fh;
00685 }
00686 }
00687
00688 static gboolean
00689 constrain_maximization (MetaWindow *window,
00690 ConstraintInfo *info,
00691 ConstraintPriority priority,
00692 gboolean check_only)
00693 {
00694 MetaRectangle target_size;
00695 MetaRectangle min_size, max_size;
00696 gboolean hminbad, vminbad;
00697 gboolean horiz_equal, vert_equal;
00698 gboolean constraint_already_satisfied;
00699
00700 if (priority > PRIORITY_MAXIMIZATION)
00701 return TRUE;
00702
00703
00704 if (!window->maximized_horizontally && !window->maximized_vertically)
00705 return TRUE;
00706
00707
00708 if (window->maximized_horizontally && window->maximized_vertically)
00709 target_size = info->work_area_xinerama;
00710 else
00711 {
00712
00713
00714
00715
00716
00717
00718
00719 MetaDirection direction;
00720 GSList *active_workspace_struts;
00721
00722 if (window->maximized_horizontally)
00723 direction = META_DIRECTION_HORIZONTAL;
00724 else
00725 direction = META_DIRECTION_VERTICAL;
00726 active_workspace_struts = window->screen->active_workspace->all_struts;
00727
00728 target_size = info->current;
00729 extend_by_frame (&target_size, info->fgeom);
00730 meta_rectangle_expand_to_avoiding_struts (&target_size,
00731 &info->entire_xinerama,
00732 direction,
00733 active_workspace_struts);
00734 }
00735
00736 unextend_by_frame (&target_size, info->fgeom);
00737
00738
00739
00740
00741 get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
00742 hminbad = target_size.width < min_size.width && window->maximized_horizontally;
00743 vminbad = target_size.height < min_size.height && window->maximized_vertically;
00744 if (hminbad || vminbad)
00745 return TRUE;
00746
00747
00748 horiz_equal = target_size.x == info->current.x &&
00749 target_size.width == info->current.width;
00750 vert_equal = target_size.y == info->current.y &&
00751 target_size.height == info->current.height;
00752 constraint_already_satisfied =
00753 (horiz_equal || !window->maximized_horizontally) &&
00754 (vert_equal || !window->maximized_vertically);
00755 if (check_only || constraint_already_satisfied)
00756 return constraint_already_satisfied;
00757
00758
00759 if (window->maximized_horizontally)
00760 {
00761 info->current.x = target_size.x;
00762 info->current.width = target_size.width;
00763 }
00764 if (window->maximized_vertically)
00765 {
00766 info->current.y = target_size.y;
00767 info->current.height = target_size.height;
00768 }
00769 return TRUE;
00770 }
00771
00772 static gboolean
00773 constrain_fullscreen (MetaWindow *window,
00774 ConstraintInfo *info,
00775 ConstraintPriority priority,
00776 gboolean check_only)
00777 {
00778 MetaRectangle min_size, max_size, xinerama;
00779 gboolean too_big, too_small, constraint_already_satisfied;
00780
00781 if (priority > PRIORITY_FULLSCREEN)
00782 return TRUE;
00783
00784
00785 if (!window->fullscreen)
00786 return TRUE;
00787 xinerama = info->entire_xinerama;
00788 get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
00789 too_big = !meta_rectangle_could_fit_rect (&xinerama, &min_size);
00790 too_small = !meta_rectangle_could_fit_rect (&max_size, &xinerama);
00791 if (too_big || too_small)
00792 return TRUE;
00793
00794
00795 constraint_already_satisfied =
00796 meta_rectangle_equal (&info->current, &xinerama);
00797 if (check_only || constraint_already_satisfied)
00798 return constraint_already_satisfied;
00799
00800
00801 info->current = xinerama;
00802 return TRUE;
00803 }
00804
00805 static gboolean
00806 constrain_size_increments (MetaWindow *window,
00807 ConstraintInfo *info,
00808 ConstraintPriority priority,
00809 gboolean check_only)
00810 {
00811 int bh, hi, bw, wi, extra_height, extra_width;
00812 int new_width, new_height;
00813 gboolean constraint_already_satisfied;
00814 MetaRectangle *start_rect;
00815
00816 if (priority > PRIORITY_SIZE_HINTS_INCREMENTS)
00817 return TRUE;
00818
00819
00820 if (META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
00821 info->action_type == ACTION_MOVE)
00822 return TRUE;
00823
00824
00825 bh = window->size_hints.base_height;
00826 hi = window->size_hints.height_inc;
00827 bw = window->size_hints.base_width;
00828 wi = window->size_hints.width_inc;
00829 extra_height = (info->current.height - bh) % hi;
00830 extra_width = (info->current.width - bw) % wi;
00831
00832 if (window->maximized_horizontally)
00833 extra_width *= 0;
00834 if (window->maximized_vertically)
00835 extra_height *= 0;
00836
00837 constraint_already_satisfied =
00838 (extra_height == 0 && extra_width == 0);
00839
00840 if (check_only || constraint_already_satisfied)
00841 return constraint_already_satisfied;
00842
00843
00844 new_width = info->current.width - extra_width;
00845 new_height = info->current.height - extra_height;
00846
00847
00848
00849
00850
00851 if (new_width < window->size_hints.min_width)
00852 new_width += ((window->size_hints.min_width - new_width)/wi + 1)*wi;
00853 if (new_height < window->size_hints.min_height)
00854 new_height += ((window->size_hints.min_height - new_height)/hi + 1)*hi;
00855
00856
00857
00858
00859 if (info->action_type == ACTION_MOVE_AND_RESIZE)
00860 start_rect = &info->current;
00861 else
00862 start_rect = &info->orig;
00863
00864
00865 meta_rectangle_resize_with_gravity (start_rect,
00866 &info->current,
00867 info->resize_gravity,
00868 new_width,
00869 new_height);
00870 return TRUE;
00871 }
00872
00873 static gboolean
00874 constrain_size_limits (MetaWindow *window,
00875 ConstraintInfo *info,
00876 ConstraintPriority priority,
00877 gboolean check_only)
00878 {
00879 MetaRectangle min_size, max_size;
00880 gboolean too_big, too_small, constraint_already_satisfied;
00881 int new_width, new_height;
00882 MetaRectangle *start_rect;
00883
00884 if (priority > PRIORITY_SIZE_HINTS_LIMITS)
00885 return TRUE;
00886
00887
00888
00889
00890
00891
00892 if (info->action_type == ACTION_MOVE)
00893 return TRUE;
00894
00895
00896 get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
00897
00898 if (window->maximized_horizontally)
00899 max_size.width = MAX (max_size.width, info->current.width);
00900 if (window->maximized_vertically)
00901 max_size.height = MAX (max_size.height, info->current.height);
00902 too_small = !meta_rectangle_could_fit_rect (&info->current, &min_size);
00903 too_big = !meta_rectangle_could_fit_rect (&max_size, &info->current);
00904 constraint_already_satisfied = !too_big && !too_small;
00905 if (check_only || constraint_already_satisfied)
00906 return constraint_already_satisfied;
00907
00908
00909 new_width = CLAMP (info->current.width, min_size.width, max_size.width);
00910 new_height = CLAMP (info->current.height, min_size.height, max_size.height);
00911
00912
00913
00914
00915 if (info->action_type == ACTION_MOVE_AND_RESIZE)
00916 start_rect = &info->current;
00917 else
00918 start_rect = &info->orig;
00919
00920 meta_rectangle_resize_with_gravity (start_rect,
00921 &info->current,
00922 info->resize_gravity,
00923 new_width,
00924 new_height);
00925 return TRUE;
00926 }
00927
00928 static gboolean
00929 constrain_aspect_ratio (MetaWindow *window,
00930 ConstraintInfo *info,
00931 ConstraintPriority priority,
00932 gboolean check_only)
00933 {
00934 double minr, maxr;
00935 gboolean constraints_are_inconsistent, constraint_already_satisfied;
00936 int fudge, new_width, new_height;
00937 double best_width, best_height;
00938 double alt_width, alt_height;
00939 MetaRectangle *start_rect;
00940
00941 if (priority > PRIORITY_ASPECT_RATIO)
00942 return TRUE;
00943
00944
00945 minr = window->size_hints.min_aspect.x /
00946 (double)window->size_hints.min_aspect.y;
00947 maxr = window->size_hints.max_aspect.x /
00948 (double)window->size_hints.max_aspect.y;
00949 constraints_are_inconsistent = minr > maxr;
00950 if (constraints_are_inconsistent ||
00951 META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
00952 info->action_type == ACTION_MOVE)
00953 return TRUE;
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971 switch (info->resize_gravity)
00972 {
00973 case WestGravity:
00974 case NorthGravity:
00975 case SouthGravity:
00976 case EastGravity:
00977 fudge = 2;
00978 break;
00979
00980 case NorthWestGravity:
00981 case SouthWestGravity:
00982 case CenterGravity:
00983 case NorthEastGravity:
00984 case SouthEastGravity:
00985 case StaticGravity:
00986 default:
00987 fudge = 1;
00988 break;
00989 }
00990 constraint_already_satisfied =
00991 info->current.width - (info->current.height * minr ) > -minr*fudge &&
00992 info->current.width - (info->current.height * maxr ) < maxr*fudge;
00993 if (check_only || constraint_already_satisfied)
00994 return constraint_already_satisfied;
00995
00996
00997 new_width = info->current.width;
00998 new_height = info->current.height;
00999
01000 switch (info->resize_gravity)
01001 {
01002 case WestGravity:
01003 case EastGravity:
01004
01005 new_height = CLAMP (new_height, new_width / maxr, new_width / minr);
01006 break;
01007
01008 case NorthGravity:
01009 case SouthGravity:
01010
01011 new_width = CLAMP (new_width, new_height * minr, new_height * maxr);
01012 break;
01013
01014 case NorthWestGravity:
01015 case SouthWestGravity:
01016 case CenterGravity:
01017 case NorthEastGravity:
01018 case SouthEastGravity:
01019 case StaticGravity:
01020 default:
01021
01022
01023 alt_width = CLAMP (new_width, new_height * minr, new_height * maxr);
01024 alt_height = CLAMP (new_height, new_width / maxr, new_width / minr);
01025
01026
01027
01028
01029
01030
01031
01032
01033 meta_rectangle_find_linepoint_closest_to_point (alt_width, new_height,
01034 new_width, alt_height,
01035 new_width, new_height,
01036 &best_width, &best_height);
01037
01038
01039 new_width = best_width;
01040 new_height = best_height;
01041
01042 break;
01043 }
01044
01045
01046
01047
01048 if (info->action_type == ACTION_MOVE_AND_RESIZE)
01049 start_rect = &info->current;
01050 else
01051 start_rect = &info->orig;
01052
01053 meta_rectangle_resize_with_gravity (start_rect,
01054 &info->current,
01055 info->resize_gravity,
01056 new_width,
01057 new_height);
01058
01059 return TRUE;
01060 }
01061
01062 static gboolean
01063 do_screen_and_xinerama_relative_constraints (
01064 MetaWindow *window,
01065 GList *region_spanning_rectangles,
01066 ConstraintInfo *info,
01067 gboolean check_only)
01068 {
01069 gboolean exit_early = FALSE, constraint_satisfied;
01070 MetaRectangle how_far_it_can_be_smushed, min_size, max_size;
01071
01072 #ifdef WITH_VERBOSE_MODE
01073 if (meta_is_verbose ())
01074 {
01075
01076 char spanning_region[1 + 28 * g_list_length (region_spanning_rectangles)];
01077
01078 meta_topic (META_DEBUG_GEOMETRY,
01079 "screen/xinerama constraint; region_spanning_rectangles: %s\n",
01080 meta_rectangle_region_to_string (region_spanning_rectangles, ", ",
01081 spanning_region));
01082 }
01083 #endif
01084
01085
01086 how_far_it_can_be_smushed = info->current;
01087 get_size_limits (window, info->fgeom, TRUE, &min_size, &max_size);
01088 extend_by_frame (&info->current, info->fgeom);
01089
01090 if (info->action_type != ACTION_MOVE)
01091 {
01092 if (!(info->fixed_directions & FIXED_DIRECTION_X))
01093 how_far_it_can_be_smushed.width = min_size.width;
01094
01095 if (!(info->fixed_directions & FIXED_DIRECTION_Y))
01096 how_far_it_can_be_smushed.height = min_size.height;
01097 }
01098 if (!meta_rectangle_could_fit_in_region (region_spanning_rectangles,
01099 &how_far_it_can_be_smushed))
01100 exit_early = TRUE;
01101
01102
01103 constraint_satisfied =
01104 meta_rectangle_contained_in_region (region_spanning_rectangles,
01105 &info->current);
01106 if (exit_early || constraint_satisfied || check_only)
01107 {
01108 unextend_by_frame (&info->current, info->fgeom);
01109 return constraint_satisfied;
01110 }
01111
01112
01113
01114
01115 if (info->action_type != ACTION_MOVE)
01116 meta_rectangle_clamp_to_fit_into_region (region_spanning_rectangles,
01117 info->fixed_directions,
01118 &info->current,
01119 &min_size);
01120
01121 if (info->is_user_action && info->action_type == ACTION_RESIZE)
01122
01123 meta_rectangle_clip_to_region (region_spanning_rectangles,
01124 info->fixed_directions,
01125 &info->current);
01126 else
01127
01128 meta_rectangle_shove_into_region (region_spanning_rectangles,
01129 info->fixed_directions,
01130 &info->current);
01131
01132 unextend_by_frame (&info->current, info->fgeom);
01133 return TRUE;
01134 }
01135
01136 static gboolean
01137 constrain_to_single_xinerama (MetaWindow *window,
01138 ConstraintInfo *info,
01139 ConstraintPriority priority,
01140 gboolean check_only)
01141 {
01142 if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA)
01143 return TRUE;
01144
01145
01146
01147
01148
01149
01150 if (window->type == META_WINDOW_DESKTOP ||
01151 window->type == META_WINDOW_DOCK ||
01152 window->screen->n_xinerama_infos == 1 ||
01153 !window->require_on_single_xinerama ||
01154 !window->frame ||
01155 info->is_user_action)
01156 return TRUE;
01157
01158
01159 return do_screen_and_xinerama_relative_constraints (window,
01160 info->usable_xinerama_region,
01161 info,
01162 check_only);
01163 }
01164
01165 static gboolean
01166 constrain_fully_onscreen (MetaWindow *window,
01167 ConstraintInfo *info,
01168 ConstraintPriority priority,
01169 gboolean check_only)
01170 {
01171 if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA)
01172 return TRUE;
01173
01174
01175
01176
01177
01178 if (window->type == META_WINDOW_DESKTOP ||
01179 window->type == META_WINDOW_DOCK ||
01180 window->fullscreen ||
01181 !window->require_fully_onscreen ||
01182 info->is_user_action)
01183 return TRUE;
01184
01185
01186 return do_screen_and_xinerama_relative_constraints (window,
01187 info->usable_screen_region,
01188 info,
01189 check_only);
01190 }
01191
01192 static gboolean
01193 constrain_titlebar_visible (MetaWindow *window,
01194 ConstraintInfo *info,
01195 ConstraintPriority priority,
01196 gboolean check_only)
01197 {
01198 gboolean unconstrained_user_action;
01199 gboolean retval;
01200 int bottom_amount;
01201 int horiz_amount_offscreen, vert_amount_offscreen;
01202 int horiz_amount_onscreen, vert_amount_onscreen;
01203
01204 if (priority > PRIORITY_TITLEBAR_VISIBLE)
01205 return TRUE;
01206
01207
01208
01209
01210 unconstrained_user_action =
01211 info->is_user_action && !window->display->grab_frame_action;
01212
01213
01214
01215
01216
01217 if (window->type == META_WINDOW_DESKTOP ||
01218 window->type == META_WINDOW_DOCK ||
01219 window->fullscreen ||
01220 !window->require_titlebar_visible ||
01221 !window->decorated ||
01222 unconstrained_user_action)
01223 return TRUE;
01224
01225
01226
01227
01228
01229
01230
01231
01232 horiz_amount_onscreen = info->current.width / 4;
01233 vert_amount_onscreen = info->current.height / 4;
01234 horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
01235 vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75);
01236 horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
01237 vert_amount_offscreen = info->current.height - vert_amount_onscreen;
01238 horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
01239 vert_amount_offscreen = MAX (vert_amount_offscreen, 0);
01240
01241
01242
01243 if (window->frame)
01244 {
01245 bottom_amount = info->current.height + info->fgeom->bottom_height;
01246 vert_amount_onscreen = info->fgeom->top_height;
01247 }
01248 else
01249 bottom_amount = vert_amount_offscreen;
01250
01251
01252
01253
01254 meta_rectangle_expand_region_conditionally (info->usable_screen_region,
01255 horiz_amount_offscreen,
01256 horiz_amount_offscreen,
01257 0,
01258 bottom_amount,
01259 horiz_amount_onscreen,
01260 vert_amount_onscreen);
01261 retval =
01262 do_screen_and_xinerama_relative_constraints (window,
01263 info->usable_screen_region,
01264 info,
01265 check_only);
01266 meta_rectangle_expand_region_conditionally (info->usable_screen_region,
01267 -horiz_amount_offscreen,
01268 -horiz_amount_offscreen,
01269 0,
01270 -bottom_amount,
01271 horiz_amount_onscreen,
01272 vert_amount_onscreen);
01273
01274 return retval;
01275 }
01276
01277 static gboolean
01278 constrain_partially_onscreen (MetaWindow *window,
01279 ConstraintInfo *info,
01280 ConstraintPriority priority,
01281 gboolean check_only)
01282 {
01283 gboolean retval;
01284 int top_amount, bottom_amount;
01285 int horiz_amount_offscreen, vert_amount_offscreen;
01286 int horiz_amount_onscreen, vert_amount_onscreen;
01287
01288 if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA)
01289 return TRUE;
01290
01291
01292
01293
01294
01295 if (window->type == META_WINDOW_DESKTOP ||
01296 window->type == META_WINDOW_DOCK)
01297 return TRUE;
01298
01299
01300
01301
01302
01303
01304
01305
01306 horiz_amount_onscreen = info->current.width / 4;
01307 vert_amount_onscreen = info->current.height / 4;
01308 horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
01309 vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75);
01310 horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
01311 vert_amount_offscreen = info->current.height - vert_amount_onscreen;
01312 horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
01313 vert_amount_offscreen = MAX (vert_amount_offscreen, 0);
01314 top_amount = vert_amount_offscreen;
01315
01316
01317
01318 if (window->frame)
01319 {
01320 bottom_amount = info->current.height + info->fgeom->bottom_height;
01321 vert_amount_onscreen = info->fgeom->top_height;
01322 }
01323 else
01324 bottom_amount = vert_amount_offscreen;
01325
01326
01327
01328
01329 meta_rectangle_expand_region_conditionally (info->usable_screen_region,
01330 horiz_amount_offscreen,
01331 horiz_amount_offscreen,
01332 top_amount,
01333 bottom_amount,
01334 horiz_amount_onscreen,
01335 vert_amount_onscreen);
01336 retval =
01337 do_screen_and_xinerama_relative_constraints (window,
01338 info->usable_screen_region,
01339 info,
01340 check_only);
01341 meta_rectangle_expand_region_conditionally (info->usable_screen_region,
01342 -horiz_amount_offscreen,
01343 -horiz_amount_offscreen,
01344 -top_amount,
01345 -bottom_amount,
01346 horiz_amount_onscreen,
01347 vert_amount_onscreen);
01348
01349 return retval;
01350 }