1 /* placement.c - window and icon placement on screen
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
31 #include "WindowMaker.h"
39 #include "application.h"
44 extern WPreferences wPreferences;
46 #define X_ORIGIN WMAX(usableArea.x1,\
47 wPreferences.window_place_origin.x)
49 #define Y_ORIGIN WMAX(usableArea.y1,\
50 wPreferences.window_place_origin.y)
53 * interactive window placement is in moveres.c
56 extern void InteractivePlaceWindow(WWindow * wwin, int *x_ret, int *y_ret, unsigned width, unsigned height);
59 * Returns True if it is an icon and is in this workspace.
62 iconPosition(WCoreWindow * wcore, int sx1, int sy1, int sx2, int sy2, int workspace, int *retX, int *retY)
67 parent = wcore->descriptor.parent;
69 /* if it is an application icon */
70 if (wcore->descriptor.parent_type == WCLASS_APPICON && !((WAppIcon *) parent)->docked) {
71 *retX = ((WAppIcon *) parent)->x_pos;
72 *retY = ((WAppIcon *) parent)->y_pos;
75 } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
76 (((WIcon *) parent)->owner->frame->workspace == workspace
77 || IS_OMNIPRESENT(((WIcon *) parent)->owner)
78 || wPreferences.sticky_icons)
79 && ((WIcon *) parent)->mapped) {
81 *retX = ((WIcon *) parent)->owner->icon_x;
82 *retY = ((WIcon *) parent)->owner->icon_y;
85 } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
86 && ((WWindow *) parent)->flags.icon_moved
87 && (((WWindow *) parent)->frame->workspace == workspace || IS_OMNIPRESENT((WWindow *) parent)
88 || wPreferences.sticky_icons)) {
89 *retX = ((WWindow *) parent)->icon_x;
90 *retY = ((WWindow *) parent)->icon_y;
96 * Check if it is inside the screen.
99 if (*retX < sx1 - wPreferences.icon_size)
101 else if (*retX > sx2)
104 if (*retY < sy1 - wPreferences.icon_size)
106 else if (*retY > sy2)
113 void PlaceIcon(WScreen * scr, int *x_ret, int *y_ret, int head)
115 int pf; /* primary axis */
116 int sf; /* secondary axis */
122 int sx1, sx2, sy1, sy2; /* screen boundary */
127 int isize = wPreferences.icon_size;
130 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
132 /* Find out screen boundaries. */
134 /* Allows each head to have miniwindows */
145 if (scr->dock->on_right_side)
146 sx2 -= isize + DOCK_EXTRA_SPACE;
148 sx1 += isize + DOCK_EXTRA_SPACE;
152 sw = isize * (sw / isize);
153 sh = isize * (sh / isize);
154 fullW = (sx2 - sx1) / isize;
155 fullH = (sy2 - sy1) / isize;
157 /* icon yard boundaries */
158 if (wPreferences.icon_yard & IY_VERT) {
165 if (wPreferences.icon_yard & IY_RIGHT) {
172 if (wPreferences.icon_yard & IY_TOP) {
181 * Create a map with the occupied slots. 1 means the slot is used
182 * or at least partially used.
183 * The slot usage can be optimized by only marking fully used slots
184 * or slots that have most of it covered.
185 * Space usage is worse than the fvwm algorithm (used in the old version)
186 * but complexity is much better (faster) than it.
188 map = wmalloc((sw + 2) * (sh + 2));
189 memset(map, 0, (sw + 2) * (sh + 2));
191 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
193 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
198 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace, &x, &y)) {
199 int xdi, ydi; /* rounded down */
200 int xui, yui; /* rounded up */
204 xui = (x + isize / 2) / isize;
205 yui = (y + isize / 2) / isize;
206 map[INDEX(xdi, ydi)] = 1;
207 map[INDEX(xdi, yui)] = 1;
208 map[INDEX(xui, ydi)] = 1;
209 map[INDEX(xui, yui)] = 1;
211 obj = obj->stacking->under;
221 * Look for an empty slot
223 for (si = 0; si < sf; si++) {
224 for (pi = 0; pi < pf; pi++) {
225 if (wPreferences.icon_yard & IY_VERT) {
226 x = xo + xs * (si * isize);
227 y = yo + ys * (pi * isize);
229 x = xo + xs * (pi * isize);
230 y = yo + ys * (si * isize);
232 if (!map[INDEX(x / isize, y / isize)]) {
247 * This function calculates the length of the intersection of two
248 * line sections. (Hey, is that english?)
250 static int calcIntersectionLength(int p1, int l1, int p2, int l2)
266 else if (p2 + l2 < p1 + l1)
269 isect = p1 + l1 - p2;
275 * This function calculates the area of the intersection of two rectangles.
278 int calcIntersectionArea(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
280 return calcIntersectionLength(x1, w1, x2, w2)
281 * calcIntersectionLength(y1, h1, y2, h2);
284 static int calcSumOfCoveredAreas(WWindow * wwin, int x, int y, int w, int h)
287 WWindow *test_window;
290 test_window = wwin->screen_ptr->focused_window;
291 for (; test_window != NULL && test_window->prev != NULL;)
292 test_window = test_window->prev;
294 for (; test_window != NULL; test_window = test_window->next) {
295 if (test_window->frame->core->stacking->window_level < WMNormalLevel) {
299 tw = test_window->client.width;
300 if (test_window->flags.shaded)
301 th = test_window->frame->top_width;
303 th = test_window->client.height + extra_height;
305 tw = test_window->frame->core->width;
306 th = test_window->frame->core->height;
308 tx = test_window->frame_x;
309 ty = test_window->frame_y;
311 if (test_window->flags.mapped ||
312 (test_window->flags.shaded &&
313 !(test_window->flags.miniaturized || test_window->flags.hidden))) {
314 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
322 smartPlaceWindow(WWindow * wwin, int *x_ret, int *y_ret, unsigned int width, unsigned int height, WArea usableArea)
324 int test_x = 0, test_y = Y_ORIGIN;
325 int from_x, to_x, from_y, to_y;
327 int min_isect, min_isect_x, min_isect_y;
331 height += wwin->frame->top_width + wwin->frame->bottom_width;
333 if (HAS_TITLEBAR(wwin))
335 if (HAS_RESIZEBAR(wwin))
338 if (HAS_BORDER(wwin)) {
346 min_isect_y = test_y;
348 while (((test_y + height) < usableArea.y2)) {
350 while ((test_x + width) < usableArea.x2) {
351 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y, width, height);
353 if (sum_isect < min_isect) {
354 min_isect = sum_isect;
355 min_isect_x = test_x;
356 min_isect_y = test_y;
359 test_x += PLACETEST_HSTEP;
361 test_y += PLACETEST_VSTEP;
364 from_x = min_isect_x - PLACETEST_HSTEP + 1;
365 from_x = WMAX(from_x, X_ORIGIN);
366 to_x = min_isect_x + PLACETEST_HSTEP;
367 if (to_x + width > usableArea.x2)
368 to_x = usableArea.x2 - width;
370 from_y = min_isect_y - PLACETEST_VSTEP + 1;
371 from_y = WMAX(from_y, Y_ORIGIN);
372 to_y = min_isect_y + PLACETEST_VSTEP;
373 if (to_y + height > usableArea.y2)
374 to_y = usableArea.y2 - height;
376 for (test_x = from_x; test_x < to_x; test_x++) {
377 for (test_y = from_y; test_y < to_y; test_y++) {
378 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y, width, height);
380 if (sum_isect < min_isect) {
381 min_isect = sum_isect;
382 min_isect_x = test_x;
383 min_isect_y = test_y;
388 *x_ret = min_isect_x;
389 *y_ret = min_isect_y;
393 autoPlaceWindow(WWindow * wwin, int *x_ret, int *y_ret,
394 unsigned int width, unsigned int height, int tryCount, WArea usableArea)
396 WScreen *scr = wwin->screen_ptr;
397 int test_x = 0, test_y = Y_ORIGIN;
398 int loc_ok = False, tw, tx, ty, th;
400 WWindow *test_window;
403 height += wwin->frame->top_width + wwin->frame->bottom_width;
405 if (HAS_TITLEBAR(wwin))
407 if (HAS_RESIZEBAR(wwin))
410 if (HAS_BORDER(wwin)) {
415 swidth = usableArea.x2 - usableArea.x1;
418 /* this was based on fvwm2's smart placement */
420 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
423 while (((test_x + width) < swidth) && (!loc_ok)) {
426 test_window = scr->focused_window;
428 while ((test_window != NULL) && (loc_ok == True)) {
430 if (test_window->frame->core->stacking->window_level
431 < WMNormalLevel && tryCount > 0) {
432 test_window = test_window->next;
436 tw = test_window->client.width;
437 if (test_window->flags.shaded)
438 th = test_window->frame->top_width;
440 th = test_window->client.height + extra_height;
442 tw = test_window->frame->core->width;
443 th = test_window->frame->core->height;
445 tx = test_window->frame_x;
446 ty = test_window->frame_y;
448 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
449 (ty < (test_y + height)) && ((ty + th) > test_y) &&
450 (test_window->flags.mapped ||
451 (test_window->flags.shaded &&
452 test_window->frame->workspace == scr->current_workspace &&
453 !(test_window->flags.miniaturized || test_window->flags.hidden)))) {
457 test_window = test_window->next;
460 test_window = scr->focused_window;
462 while ((test_window != NULL) && (loc_ok == True)) {
464 if (test_window->frame->core->stacking->window_level
465 < WMNormalLevel && tryCount > 0) {
466 test_window = test_window->prev;
470 tw = test_window->client.width;
471 if (test_window->flags.shaded)
472 th = test_window->frame->top_width;
474 th = test_window->client.height + extra_height;
476 tw = test_window->frame->core->width;
477 th = test_window->frame->core->height;
479 tx = test_window->frame_x;
480 ty = test_window->frame_y;
482 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
483 (ty < (test_y + height)) && ((ty + th) > test_y) &&
484 (test_window->flags.mapped ||
485 (test_window->flags.shaded &&
486 test_window->frame->workspace == scr->current_workspace &&
487 !(test_window->flags.miniaturized || test_window->flags.hidden)))) {
491 test_window = test_window->prev;
493 if (loc_ok == True) {
498 test_x += PLACETEST_HSTEP;
500 test_y += PLACETEST_VSTEP;
507 cascadeWindow(WScreen * scr, WWindow * wwin, int *x_ret, int *y_ret,
508 unsigned int width, unsigned int height, int h, WArea usableArea)
511 height += wwin->frame->top_width + wwin->frame->bottom_width;
513 if (HAS_TITLEBAR(wwin))
515 if (HAS_RESIZEBAR(wwin))
518 if (HAS_BORDER(wwin)) {
523 *x_ret = h * scr->cascade_index + X_ORIGIN;
524 *y_ret = h * scr->cascade_index + Y_ORIGIN;
526 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
527 scr->cascade_index = 0;
534 randomPlaceWindow(WScreen * scr, WWindow * wwin, int *x_ret, int *y_ret,
535 unsigned int width, unsigned int height, WArea usableArea)
540 height += wwin->frame->top_width + wwin->frame->bottom_width;
542 if (HAS_TITLEBAR(wwin))
544 if (HAS_RESIZEBAR(wwin))
547 if (HAS_BORDER(wwin)) {
552 w = ((usableArea.x2 - X_ORIGIN) - width);
553 h = ((usableArea.y2 - Y_ORIGIN) - height);
558 *x_ret = X_ORIGIN + rand() % w;
559 *y_ret = Y_ORIGIN + rand() % h;
562 void PlaceWindow(WWindow * wwin, int *x_ret, int *y_ret, unsigned width, unsigned height)
564 WScreen *scr = wwin->screen_ptr;
565 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
566 WArea usableArea = wGetUsableAreaForHead(scr,
567 wGetHeadForPointerLocation(scr),
570 switch (wPreferences.window_placement) {
572 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
576 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
580 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0, usableArea)) {
582 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1, usableArea)) {
585 /* there isn't a break here, because if we fail, it should fall
586 through to cascade placement, as people who want tiling want
587 automagicness aren't going to want to place their window */
590 if (wPreferences.window_placement == WPM_AUTO)
591 scr->cascade_index++;
593 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
595 if (wPreferences.window_placement == WPM_CASCADE)
596 scr->cascade_index++;
600 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
605 puts("Invalid window placement!!!");
612 * clip to usableArea instead of full screen
613 * this will also take dock/clip etc.. into account
614 * aswell as being xinerama friendly
616 if (*x_ret + width > usableArea.x2)
617 *x_ret = usableArea.x2 - width;
618 if (*x_ret < usableArea.x1)
619 *x_ret = usableArea.x1;
621 if (*y_ret + height > usableArea.y2)
622 *y_ret = usableArea.y2 - height;
623 if (*y_ret < usableArea.y1)
624 *y_ret = usableArea.y1;