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
00027 #include <config.h>
00028
00029 #include "place.h"
00030 #include "workspace.h"
00031 #include "prefs.h"
00032 #include <gdk/gdkregion.h>
00033 #include <math.h>
00034 #include <stdlib.h>
00035
00036 typedef enum
00037 {
00038 META_LEFT,
00039 META_RIGHT,
00040 META_TOP,
00041 META_BOTTOM
00042 } MetaWindowDirection;
00043
00044 static gint
00045 northwestcmp (gconstpointer a, gconstpointer b)
00046 {
00047 MetaWindow *aw = (gpointer) a;
00048 MetaWindow *bw = (gpointer) b;
00049 int from_origin_a;
00050 int from_origin_b;
00051 int ax, ay, bx, by;
00052
00053
00054
00055
00056 if (aw->frame)
00057 {
00058 ax = aw->frame->rect.x;
00059 ay = aw->frame->rect.y;
00060 }
00061 else
00062 {
00063 ax = aw->rect.x;
00064 ay = aw->rect.y;
00065 }
00066
00067 if (bw->frame)
00068 {
00069 bx = bw->frame->rect.x;
00070 by = bw->frame->rect.y;
00071 }
00072 else
00073 {
00074 bx = bw->rect.x;
00075 by = bw->rect.y;
00076 }
00077
00078
00079 from_origin_a = sqrt (ax * ax + ay * ay);
00080 from_origin_b = sqrt (bx * bx + by * by);
00081
00082 if (from_origin_a < from_origin_b)
00083 return -1;
00084 else if (from_origin_a > from_origin_b)
00085 return 1;
00086 else
00087 return 0;
00088 }
00089
00090 static void
00091 find_next_cascade (MetaWindow *window,
00092 MetaFrameGeometry *fgeom,
00093
00094 GList *windows,
00095 int x,
00096 int y,
00097 int *new_x,
00098 int *new_y)
00099 {
00100 GList *tmp;
00101 GList *sorted;
00102 int cascade_x, cascade_y;
00103 int x_threshold, y_threshold;
00104 int window_width, window_height;
00105 int cascade_stage;
00106 MetaRectangle work_area;
00107 const MetaXineramaScreenInfo* current;
00108
00109 sorted = g_list_copy (windows);
00110 sorted = g_list_sort (sorted, northwestcmp);
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121 #define CASCADE_FUZZ 15
00122 if (fgeom)
00123 {
00124 x_threshold = MAX (fgeom->left_width, CASCADE_FUZZ);
00125 y_threshold = MAX (fgeom->top_height, CASCADE_FUZZ);
00126 }
00127 else
00128 {
00129 x_threshold = CASCADE_FUZZ;
00130 y_threshold = CASCADE_FUZZ;
00131 }
00132
00133
00134
00135
00136
00137
00138 current = meta_screen_get_current_xinerama (window->screen);
00139 meta_window_get_work_area_for_xinerama (window, current->number, &work_area);
00140
00141 cascade_x = MAX (0, work_area.x);
00142 cascade_y = MAX (0, work_area.y);
00143
00144
00145
00146 window_width = window->frame ? window->frame->rect.width : window->rect.width;
00147 window_height = window->frame ? window->frame->rect.height : window->rect.height;
00148
00149 cascade_stage = 0;
00150 tmp = sorted;
00151 while (tmp != NULL)
00152 {
00153 MetaWindow *w;
00154 int wx, wy;
00155
00156 w = tmp->data;
00157
00158
00159 if (w->frame)
00160 {
00161 wx = w->frame->rect.x;
00162 wy = w->frame->rect.y;
00163 }
00164 else
00165 {
00166 wx = w->rect.x;
00167 wy = w->rect.y;
00168 }
00169
00170 if (ABS (wx - cascade_x) < x_threshold &&
00171 ABS (wy - cascade_y) < y_threshold)
00172 {
00173
00174
00175
00176
00177 meta_window_get_position (w, &wx, &wy);
00178 cascade_x = wx;
00179 cascade_y = wy;
00180
00181
00182 if (((cascade_x + window_width) >
00183 (work_area.x + work_area.width)) ||
00184 ((cascade_y + window_height) >
00185 (work_area.y + work_area.height)))
00186 {
00187 cascade_x = MAX (0, work_area.x);
00188 cascade_y = MAX (0, work_area.y);
00189
00190 #define CASCADE_INTERVAL 50
00191 cascade_stage += 1;
00192 cascade_x += CASCADE_INTERVAL * cascade_stage;
00193
00194
00195
00196
00197 if ((cascade_x + window_width) <
00198 (work_area.x + work_area.width))
00199 {
00200 tmp = sorted;
00201 continue;
00202 }
00203 else
00204 {
00205
00206 cascade_x = MAX (0, work_area.x);
00207 break;
00208 }
00209 }
00210 }
00211 else
00212 {
00213
00214 }
00215
00216 tmp = tmp->next;
00217 }
00218
00219
00220
00221
00222
00223 g_list_free (sorted);
00224
00225
00226 if (fgeom == NULL)
00227 {
00228 *new_x = cascade_x;
00229 *new_y = cascade_y;
00230 }
00231 else
00232 {
00233 *new_x = cascade_x + fgeom->left_width;
00234 *new_y = cascade_y + fgeom->top_height;
00235 }
00236 }
00237
00238 static void
00239 find_most_freespace (MetaWindow *window,
00240 MetaFrameGeometry *fgeom,
00241
00242 MetaWindow *focus_window,
00243 int x,
00244 int y,
00245 int *new_x,
00246 int *new_y)
00247 {
00248 MetaWindowDirection side;
00249 int max_area;
00250 int max_width, max_height, left, right, top, bottom;
00251 int left_space, right_space, top_space, bottom_space;
00252 int frame_size_left, frame_size_top;
00253 MetaRectangle work_area;
00254 MetaRectangle avoid;
00255 MetaRectangle outer;
00256
00257 frame_size_left = fgeom ? fgeom->left_width : 0;
00258 frame_size_top = fgeom ? fgeom->top_height : 0;
00259
00260 meta_window_get_work_area_current_xinerama (focus_window, &work_area);
00261 meta_window_get_outer_rect (focus_window, &avoid);
00262 meta_window_get_outer_rect (window, &outer);
00263
00264
00265 max_width = MIN (avoid.width, outer.width);
00266 max_height = MIN (avoid.height, outer.height);
00267 left_space = avoid.x - work_area.x;
00268 right_space = work_area.width - (avoid.x + avoid.width - work_area.x);
00269 top_space = avoid.y - work_area.y;
00270 bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y);
00271 left = MIN (left_space, outer.width);
00272 right = MIN (right_space, outer.width);
00273 top = MIN (top_space, outer.height);
00274 bottom = MIN (bottom_space, outer.height);
00275
00276
00277 side = META_LEFT;
00278 max_area = left*max_height;
00279 if (right*max_height > max_area)
00280 {
00281 side = META_RIGHT;
00282 max_area = right*max_height;
00283 }
00284 if (top*max_width > max_area)
00285 {
00286 side = META_TOP;
00287 max_area = top*max_width;
00288 }
00289 if (bottom*max_width > max_area)
00290 {
00291 side = META_BOTTOM;
00292 max_area = bottom*max_width;
00293 }
00294
00295
00296 if (max_area == 0)
00297 return;
00298
00299
00300
00301
00302
00303 switch (side)
00304 {
00305 case META_LEFT:
00306 *new_y = avoid.y + frame_size_top;
00307 if (left_space > outer.width)
00308 *new_x = avoid.x - outer.width + frame_size_left;
00309 else
00310 *new_x = work_area.x + frame_size_left;
00311 break;
00312 case META_RIGHT:
00313 *new_y = avoid.y + frame_size_top;
00314 if (right_space > outer.width)
00315 *new_x = avoid.x + avoid.width + frame_size_left;
00316 else
00317 *new_x = work_area.x + work_area.width - outer.width + frame_size_left;
00318 break;
00319 case META_TOP:
00320 *new_x = avoid.x + frame_size_left;
00321 if (top_space > outer.height)
00322 *new_y = avoid.y - outer.height + frame_size_top;
00323 else
00324 *new_y = work_area.y + frame_size_top;
00325 break;
00326 case META_BOTTOM:
00327 *new_x = avoid.x + frame_size_left;
00328 if (bottom_space > outer.height)
00329 *new_y = avoid.y + avoid.height + frame_size_top;
00330 else
00331 *new_y = work_area.y + work_area.height - outer.height + frame_size_top;
00332 break;
00333 }
00334 }
00335
00336 static void
00337 avoid_being_obscured_as_second_modal_dialog (MetaWindow *window,
00338 MetaFrameGeometry *fgeom,
00339 int *x,
00340 int *y)
00341 {
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356 MetaWindow *focus_window;
00357 MetaRectangle overlap;
00358
00359 focus_window = window->display->focus_window;
00360
00361 if (window->denied_focus_and_not_transient &&
00362 window->wm_state_modal &&
00363 meta_window_same_application (window, focus_window) &&
00364 meta_rectangle_intersect (&window->rect,
00365 &focus_window->rect,
00366 &overlap))
00367 {
00368 find_most_freespace (window, fgeom, focus_window, *x, *y, x, y);
00369 meta_topic (META_DEBUG_PLACEMENT,
00370 "Dialog window %s was denied focus but may be modal "
00371 "to the focus window; had to move it to avoid the "
00372 "focus window\n",
00373 window->desc);
00374 }
00375 }
00376
00377 static gboolean
00378 rectangle_overlaps_some_window (MetaRectangle *rect,
00379 GList *windows)
00380 {
00381 GList *tmp;
00382 MetaRectangle dest;
00383
00384 tmp = windows;
00385 while (tmp != NULL)
00386 {
00387 MetaWindow *other = tmp->data;
00388 MetaRectangle other_rect;
00389
00390 switch (other->type)
00391 {
00392 case META_WINDOW_DOCK:
00393 case META_WINDOW_SPLASHSCREEN:
00394 case META_WINDOW_DESKTOP:
00395 case META_WINDOW_DIALOG:
00396 case META_WINDOW_MODAL_DIALOG:
00397 break;
00398
00399 case META_WINDOW_NORMAL:
00400 case META_WINDOW_UTILITY:
00401 case META_WINDOW_TOOLBAR:
00402 case META_WINDOW_MENU:
00403 meta_window_get_outer_rect (other, &other_rect);
00404
00405 if (meta_rectangle_intersect (rect, &other_rect, &dest))
00406 return TRUE;
00407 break;
00408 }
00409
00410 tmp = tmp->next;
00411 }
00412
00413 return FALSE;
00414 }
00415
00416 static gint
00417 leftmost_cmp (gconstpointer a, gconstpointer b)
00418 {
00419 MetaWindow *aw = (gpointer) a;
00420 MetaWindow *bw = (gpointer) b;
00421 int ax, bx;
00422
00423
00424
00425
00426 if (aw->frame)
00427 ax = aw->frame->rect.x;
00428 else
00429 ax = aw->rect.x;
00430
00431 if (bw->frame)
00432 bx = bw->frame->rect.x;
00433 else
00434 bx = bw->rect.x;
00435
00436 if (ax < bx)
00437 return -1;
00438 else if (ax > bx)
00439 return 1;
00440 else
00441 return 0;
00442 }
00443
00444 static gint
00445 topmost_cmp (gconstpointer a, gconstpointer b)
00446 {
00447 MetaWindow *aw = (gpointer) a;
00448 MetaWindow *bw = (gpointer) b;
00449 int ay, by;
00450
00451
00452
00453
00454 if (aw->frame)
00455 ay = aw->frame->rect.y;
00456 else
00457 ay = aw->rect.y;
00458
00459 if (bw->frame)
00460 by = bw->frame->rect.y;
00461 else
00462 by = bw->rect.y;
00463
00464 if (ay < by)
00465 return -1;
00466 else if (ay > by)
00467 return 1;
00468 else
00469 return 0;
00470 }
00471
00472 static void
00473 center_tile_rect_in_area (MetaRectangle *rect,
00474 MetaRectangle *work_area)
00475 {
00476 int fluff;
00477
00478
00479
00480
00481
00482
00483
00484 fluff = (work_area->width % (rect->width+1)) / 2;
00485 rect->x = work_area->x + fluff;
00486 fluff = (work_area->height % (rect->height+1)) / 3;
00487 rect->y = work_area->y + fluff;
00488 }
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498 static gboolean
00499 find_first_fit (MetaWindow *window,
00500 MetaFrameGeometry *fgeom,
00501
00502 GList *windows,
00503 int xinerama,
00504 int x,
00505 int y,
00506 int *new_x,
00507 int *new_y)
00508 {
00509
00510
00511
00512
00513
00514
00515
00516 int retval;
00517 GList *below_sorted;
00518 GList *right_sorted;
00519 GList *tmp;
00520 MetaRectangle rect;
00521 MetaRectangle work_area;
00522
00523 retval = FALSE;
00524
00525
00526 below_sorted = g_list_copy (windows);
00527 below_sorted = g_list_sort (below_sorted, leftmost_cmp);
00528 below_sorted = g_list_sort (below_sorted, topmost_cmp);
00529
00530
00531 right_sorted = g_list_copy (windows);
00532 right_sorted = g_list_sort (right_sorted, topmost_cmp);
00533 right_sorted = g_list_sort (right_sorted, leftmost_cmp);
00534
00535 rect.width = window->rect.width;
00536 rect.height = window->rect.height;
00537
00538 if (fgeom)
00539 {
00540 rect.width += fgeom->left_width + fgeom->right_width;
00541 rect.height += fgeom->top_height + fgeom->bottom_height;
00542 }
00543
00544 #ifdef WITH_VERBOSE_MODE
00545 {
00546 char xinerama_location_string[RECT_LENGTH];
00547 meta_rectangle_to_string (&window->screen->xinerama_infos[xinerama].rect,
00548 xinerama_location_string);
00549 meta_topic (META_DEBUG_XINERAMA,
00550 "Natural xinerama is %s\n",
00551 xinerama_location_string);
00552 }
00553 #endif
00554
00555 meta_window_get_work_area_for_xinerama (window, xinerama, &work_area);
00556
00557 center_tile_rect_in_area (&rect, &work_area);
00558
00559 if (meta_rectangle_contains_rect (&work_area, &rect) &&
00560 !rectangle_overlaps_some_window (&rect, windows))
00561 {
00562 *new_x = rect.x;
00563 *new_y = rect.y;
00564 if (fgeom)
00565 {
00566 *new_x += fgeom->left_width;
00567 *new_y += fgeom->top_height;
00568 }
00569
00570 retval = TRUE;
00571
00572 goto out;
00573 }
00574
00575
00576 tmp = below_sorted;
00577 while (tmp != NULL)
00578 {
00579 MetaWindow *w = tmp->data;
00580 MetaRectangle outer_rect;
00581
00582 meta_window_get_outer_rect (w, &outer_rect);
00583
00584 rect.x = outer_rect.x;
00585 rect.y = outer_rect.y + outer_rect.height;
00586
00587 if (meta_rectangle_contains_rect (&work_area, &rect) &&
00588 !rectangle_overlaps_some_window (&rect, below_sorted))
00589 {
00590 *new_x = rect.x;
00591 *new_y = rect.y;
00592 if (fgeom)
00593 {
00594 *new_x += fgeom->left_width;
00595 *new_y += fgeom->top_height;
00596 }
00597
00598 retval = TRUE;
00599
00600 goto out;
00601 }
00602
00603 tmp = tmp->next;
00604 }
00605
00606
00607 tmp = right_sorted;
00608 while (tmp != NULL)
00609 {
00610 MetaWindow *w = tmp->data;
00611 MetaRectangle outer_rect;
00612
00613 meta_window_get_outer_rect (w, &outer_rect);
00614
00615 rect.x = outer_rect.x + outer_rect.width;
00616 rect.y = outer_rect.y;
00617
00618 if (meta_rectangle_contains_rect (&work_area, &rect) &&
00619 !rectangle_overlaps_some_window (&rect, right_sorted))
00620 {
00621 *new_x = rect.x;
00622 *new_y = rect.y;
00623 if (fgeom)
00624 {
00625 *new_x += fgeom->left_width;
00626 *new_y += fgeom->top_height;
00627 }
00628
00629 retval = TRUE;
00630
00631 goto out;
00632 }
00633
00634 tmp = tmp->next;
00635 }
00636
00637 out:
00638
00639 g_list_free (below_sorted);
00640 g_list_free (right_sorted);
00641 return retval;
00642 }
00643
00644 void
00645 meta_window_place (MetaWindow *window,
00646 MetaFrameGeometry *fgeom,
00647 int x,
00648 int y,
00649 int *new_x,
00650 int *new_y)
00651 {
00652 GList *windows;
00653 const MetaXineramaScreenInfo *xi;
00654
00655
00656
00657
00658
00659
00660
00661
00662 meta_topic (META_DEBUG_PLACEMENT, "Placing window %s\n", window->desc);
00663
00664 windows = NULL;
00665
00666 switch (window->type)
00667 {
00668
00669 case META_WINDOW_NORMAL:
00670 case META_WINDOW_DIALOG:
00671 case META_WINDOW_MODAL_DIALOG:
00672 case META_WINDOW_SPLASHSCREEN:
00673 break;
00674
00675
00676
00677
00678 case META_WINDOW_DESKTOP:
00679 case META_WINDOW_DOCK:
00680 case META_WINDOW_TOOLBAR:
00681 case META_WINDOW_MENU:
00682 case META_WINDOW_UTILITY:
00683 goto done_no_constraints;
00684 }
00685
00686 if (meta_prefs_get_disable_workarounds ())
00687 {
00688 switch (window->type)
00689 {
00690
00691
00692
00693 case META_WINDOW_NORMAL:
00694 if (window->size_hints.flags & USPosition)
00695 {
00696
00697 meta_topic (META_DEBUG_PLACEMENT,
00698 "Honoring USPosition for %s instead of using placement algorithm\n", window->desc);
00699
00700 goto done;
00701 }
00702 break;
00703
00704
00705 case META_WINDOW_DIALOG:
00706 case META_WINDOW_MODAL_DIALOG:
00707 case META_WINDOW_SPLASHSCREEN:
00708 break;
00709
00710
00711 case META_WINDOW_DESKTOP:
00712 case META_WINDOW_DOCK:
00713 case META_WINDOW_TOOLBAR:
00714 case META_WINDOW_MENU:
00715 case META_WINDOW_UTILITY:
00716 if (window->size_hints.flags & PPosition)
00717 {
00718 meta_topic (META_DEBUG_PLACEMENT,
00719 "Not placing non-normal non-dialog window with PPosition set\n");
00720 goto done_no_constraints;
00721 }
00722 break;
00723 }
00724 }
00725 else
00726 {
00727
00728
00729 if ((window->size_hints.flags & PPosition) ||
00730 (window->size_hints.flags & USPosition))
00731 {
00732 meta_topic (META_DEBUG_PLACEMENT,
00733 "Not placing window with PPosition or USPosition set\n");
00734 avoid_being_obscured_as_second_modal_dialog (window, fgeom, &x, &y);
00735 goto done_no_constraints;
00736 }
00737 }
00738
00739 if ((window->type == META_WINDOW_DIALOG ||
00740 window->type == META_WINDOW_MODAL_DIALOG) &&
00741 window->xtransient_for != None)
00742 {
00743
00744
00745 MetaWindow *parent;
00746
00747 parent =
00748 meta_display_lookup_x_window (window->display,
00749 window->xtransient_for);
00750
00751 if (parent)
00752 {
00753 int w;
00754
00755 meta_window_get_position (parent, &x, &y);
00756 w = parent->rect.width;
00757
00758
00759 x = x + w / 2;
00760
00761 x -= window->rect.width / 2;
00762
00763
00764
00765
00766 y += (parent->rect.height - window->rect.height)/3;
00767
00768
00769 if (fgeom)
00770 y += fgeom->top_height;
00771
00772 meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent\n",
00773 window->desc);
00774
00775 avoid_being_obscured_as_second_modal_dialog (window, fgeom, &x, &y);
00776
00777 goto done;
00778 }
00779 }
00780
00781
00782
00783
00784
00785 if (window->type == META_WINDOW_DIALOG ||
00786 window->type == META_WINDOW_MODAL_DIALOG ||
00787 window->type == META_WINDOW_SPLASHSCREEN)
00788 {
00789
00790 int w, h;
00791
00792
00793 xi = meta_screen_get_current_xinerama (window->screen);
00794
00795 w = xi->rect.width;
00796 h = xi->rect.height;
00797
00798 x = (w - window->rect.width) / 2;
00799 y = (h - window->rect.height) / 2;
00800
00801 x += xi->rect.x;
00802 y += xi->rect.y;
00803
00804 meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on screen %d xinerama %d\n",
00805 window->desc, window->screen->number, xi->number);
00806
00807 goto done_check_denied_focus;
00808 }
00809
00810
00811
00812
00813
00814 {
00815 GSList *all_windows;
00816 GSList *tmp;
00817
00818 all_windows = meta_display_list_windows (window->display);
00819
00820 tmp = all_windows;
00821 while (tmp != NULL)
00822 {
00823 MetaWindow *w = tmp->data;
00824
00825 if (meta_window_showing_on_its_workspace (w) &&
00826 w != window &&
00827 (window->workspace == w->workspace ||
00828 window->on_all_workspaces || w->on_all_workspaces))
00829 windows = g_list_prepend (windows, w);
00830
00831 tmp = tmp->next;
00832 }
00833
00834 g_slist_free (all_windows);
00835 }
00836
00837
00838 xi = meta_screen_get_current_xinerama (window->screen);
00839
00840
00841 x = xi->rect.x;
00842 y = xi->rect.y;
00843
00844 if (find_first_fit (window, fgeom, windows,
00845 xi->number,
00846 x, y, &x, &y))
00847 goto done_check_denied_focus;
00848
00849
00850
00851
00852
00853 if (window->has_maximize_func && window->decorated &&
00854 !window->fullscreen)
00855 {
00856 MetaRectangle workarea;
00857 MetaRectangle outer;
00858
00859 meta_window_get_work_area_for_xinerama (window,
00860 xi->number,
00861 &workarea);
00862 meta_window_get_outer_rect (window, &outer);
00863
00864
00865
00866
00867 if (outer.width >= workarea.width && outer.height >= workarea.height)
00868 {
00869 window->maximize_horizontally_after_placement = TRUE;
00870 window->maximize_vertically_after_placement = TRUE;
00871 }
00872 }
00873
00874
00875
00876
00877 if (x == xi->rect.x && y == xi->rect.y)
00878 find_next_cascade (window, fgeom, windows, x, y, &x, &y);
00879
00880 done_check_denied_focus:
00881
00882
00883
00884
00885
00886 if (window->denied_focus_and_not_transient)
00887 {
00888 gboolean found_fit;
00889 MetaWindow *focus_window;
00890 MetaRectangle overlap;
00891
00892 focus_window = window->display->focus_window;
00893 g_assert (focus_window != NULL);
00894
00895
00896 found_fit = !meta_rectangle_intersect (&window->rect,
00897 &focus_window->rect,
00898 &overlap);
00899
00900
00901
00902
00903 if (!found_fit)
00904 {
00905 GList *focus_window_list;
00906 focus_window_list = g_list_prepend (NULL, focus_window);
00907
00908
00909 x = xi->rect.x;
00910 y = xi->rect.y;
00911
00912 found_fit = find_first_fit (window, fgeom, focus_window_list,
00913 xi->number,
00914 x, y, &x, &y);
00915 g_list_free (focus_window_list);
00916 }
00917
00918
00919
00920
00921 if (!found_fit)
00922 find_most_freespace (window, fgeom, focus_window, x, y, &x, &y);
00923 }
00924
00925 done:
00926 g_list_free (windows);
00927
00928 done_no_constraints:
00929
00930 *new_x = x;
00931 *new_y = y;
00932 }