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)
52 /* interactive window placement is in moveres.c */
53 extern void InteractivePlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
54 unsigned width, unsigned height);
56 /* Returns True if it is an icon and is in this workspace */
58 iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2,
59 int workspace, int *retX, int *retY)
64 parent = wcore->descriptor.parent;
66 /* if it is an application icon */
67 if (wcore->descriptor.parent_type == WCLASS_APPICON && !((WAppIcon *) parent)->docked) {
68 *retX = ((WAppIcon *) parent)->x_pos;
69 *retY = ((WAppIcon *) parent)->y_pos;
72 } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
73 (((WIcon *) parent)->owner->frame->workspace == workspace
74 || IS_OMNIPRESENT(((WIcon *) parent)->owner)
75 || wPreferences.sticky_icons)
76 && ((WIcon *) parent)->mapped) {
78 *retX = ((WIcon *) parent)->owner->icon_x;
79 *retY = ((WIcon *) parent)->owner->icon_y;
82 } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
83 && ((WWindow *) parent)->flags.icon_moved
84 && (((WWindow *) parent)->frame->workspace == workspace || IS_OMNIPRESENT((WWindow *) parent)
85 || wPreferences.sticky_icons)) {
86 *retX = ((WWindow *) parent)->icon_x;
87 *retY = ((WWindow *) parent)->icon_y;
92 /* Check if it is inside the screen */
94 if (*retX < sx1 - wPreferences.icon_size)
99 if (*retY < sy1 - wPreferences.icon_size)
101 else if (*retY > sy2)
108 void PlaceIcon(WScreen *scr, int *x_ret, int *y_ret, int head)
110 int pf; /* primary axis */
111 int sf; /* secondary axis */
117 int sx1, sx2, sy1, sy2; /* screen boundary */
122 int isize = wPreferences.icon_size;
125 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
127 /* Find out screen boundaries. */
129 /* Allows each head to have miniwindows */
139 if (scr->dock->on_right_side)
140 sx2 -= isize + DOCK_EXTRA_SPACE;
142 sx1 += isize + DOCK_EXTRA_SPACE;
146 sw = isize * (sw / isize);
147 sh = isize * (sh / isize);
148 fullW = (sx2 - sx1) / isize;
149 fullH = (sy2 - sy1) / isize;
151 /* icon yard boundaries */
152 if (wPreferences.icon_yard & IY_VERT) {
159 if (wPreferences.icon_yard & IY_RIGHT) {
166 if (wPreferences.icon_yard & IY_TOP) {
175 * Create a map with the occupied slots. 1 means the slot is used
176 * or at least partially used.
177 * The slot usage can be optimized by only marking fully used slots
178 * or slots that have most of it covered.
179 * Space usage is worse than the fvwm algorithm (used in the old version)
180 * but complexity is much better (faster) than it.
182 map = wmalloc((sw + 2) * (sh + 2));
183 memset(map, 0, (sw + 2) * (sh + 2));
185 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
187 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
192 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace, &x, &y)) {
193 int xdi, ydi; /* rounded down */
194 int xui, yui; /* rounded up */
198 xui = (x + isize / 2) / isize;
199 yui = (y + isize / 2) / isize;
200 map[INDEX(xdi, ydi)] = 1;
201 map[INDEX(xdi, yui)] = 1;
202 map[INDEX(xui, ydi)] = 1;
203 map[INDEX(xui, yui)] = 1;
205 obj = obj->stacking->under;
208 /* Default position */
212 /* Look for an empty slot */
213 for (si = 0; si < sf; si++) {
214 for (pi = 0; pi < pf; pi++) {
215 if (wPreferences.icon_yard & IY_VERT) {
216 x = xo + xs * (si * isize);
217 y = yo + ys * (pi * isize);
219 x = xo + xs * (pi * isize);
220 y = yo + ys * (si * isize);
222 if (!map[INDEX(x / isize, y / isize)]) {
236 /* Computes the intersecting length of two line sections */
237 int calcIntersectionLength(int p1, int l1, int p2, int l2)
253 else if (p2 + l2 < p1 + l1)
256 isect = p1 + l1 - p2;
261 /* Computes the intersecting area of two rectangles */
262 int calcIntersectionArea(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
264 return calcIntersectionLength(x1, w1, x2, w2)
265 * calcIntersectionLength(y1, h1, y2, h2);
268 static int calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
271 WWindow *test_window;
274 test_window = wwin->screen_ptr->focused_window;
275 for (; test_window != NULL && test_window->prev != NULL;)
276 test_window = test_window->prev;
278 for (; test_window != NULL; test_window = test_window->next) {
279 if (test_window->frame->core->stacking->window_level < WMNormalLevel) {
283 tw = test_window->client.width;
284 if (test_window->flags.shaded)
285 th = test_window->frame->top_width;
287 th = test_window->client.height + extra_height;
289 tw = test_window->frame->core->width;
290 th = test_window->frame->core->height;
292 tx = test_window->frame_x;
293 ty = test_window->frame_y;
295 if (test_window->flags.mapped || (test_window->flags.shaded &&
296 test_window->frame->workspace == wwin->screen_ptr->current_workspace &&
297 !(test_window->flags.miniaturized || test_window->flags.hidden))) {
298 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
305 static void set_width_height(WWindow *wwin, unsigned int *width, unsigned int *height)
308 *height += wwin->frame->top_width + wwin->frame->bottom_width;
310 if (HAS_TITLEBAR(wwin))
311 *height += TITLEBAR_HEIGHT;
312 if (HAS_RESIZEBAR(wwin))
313 *height += RESIZEBAR_HEIGHT;
315 if (HAS_BORDER(wwin)) {
316 *height += 2 * FRAME_BORDER_WIDTH;
317 *width += 2 * FRAME_BORDER_WIDTH;
322 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret, unsigned int width,
323 unsigned int height, WArea usableArea)
325 int test_x = 0, test_y = Y_ORIGIN;
326 int from_x, to_x, from_y, to_y;
328 int min_isect, min_isect_x, min_isect_y;
331 set_width_height(wwin, &width, &height);
336 min_isect_y = test_y;
338 while (((test_y + height) < usableArea.y2)) {
340 while ((test_x + width) < usableArea.x2) {
341 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y, width, height);
343 if (sum_isect < min_isect) {
344 min_isect = sum_isect;
345 min_isect_x = test_x;
346 min_isect_y = test_y;
349 test_x += PLACETEST_HSTEP;
351 test_y += PLACETEST_VSTEP;
354 from_x = min_isect_x - PLACETEST_HSTEP + 1;
355 from_x = WMAX(from_x, X_ORIGIN);
356 to_x = min_isect_x + PLACETEST_HSTEP;
357 if (to_x + width > usableArea.x2)
358 to_x = usableArea.x2 - width;
360 from_y = min_isect_y - PLACETEST_VSTEP + 1;
361 from_y = WMAX(from_y, Y_ORIGIN);
362 to_y = min_isect_y + PLACETEST_VSTEP;
363 if (to_y + height > usableArea.y2)
364 to_y = usableArea.y2 - height;
366 for (test_x = from_x; test_x < to_x; test_x++) {
367 for (test_y = from_y; test_y < to_y; test_y++) {
368 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y, width, height);
370 if (sum_isect < min_isect) {
371 min_isect = sum_isect;
372 min_isect_x = test_x;
373 min_isect_y = test_y;
378 *x_ret = min_isect_x;
379 *y_ret = min_isect_y;
383 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
384 unsigned int width, unsigned int height, int tryCount, WArea usableArea)
386 WScreen *scr = wwin->screen_ptr;
387 int test_x = 0, test_y = Y_ORIGIN;
388 int loc_ok = False, tw, tx, ty, th;
390 WWindow *test_window;
392 set_width_height(wwin, &width, &height);
393 swidth = usableArea.x2 - usableArea.x1;
396 /* this was based on fvwm2's smart placement */
397 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
400 while (((test_x + width) < swidth) && (!loc_ok)) {
403 test_window = scr->focused_window;
405 while ((test_window != NULL) && (loc_ok == True)) {
407 if (test_window->frame->core->stacking->window_level
408 < WMNormalLevel && tryCount > 0) {
409 test_window = test_window->next;
413 tw = test_window->client.width;
414 if (test_window->flags.shaded)
415 th = test_window->frame->top_width;
417 th = test_window->client.height + extra_height;
419 tw = test_window->frame->core->width;
420 th = test_window->frame->core->height;
422 tx = test_window->frame_x;
423 ty = test_window->frame_y;
425 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
426 (ty < (test_y + height)) && ((ty + th) > test_y) &&
427 (test_window->flags.mapped ||
428 (test_window->flags.shaded &&
429 test_window->frame->workspace == scr->current_workspace &&
430 !(test_window->flags.miniaturized || test_window->flags.hidden)))) {
434 test_window = test_window->next;
437 test_window = scr->focused_window;
439 while ((test_window != NULL) && (loc_ok == True)) {
441 if (test_window->frame->core->stacking->window_level
442 < WMNormalLevel && tryCount > 0) {
443 test_window = test_window->prev;
447 tw = test_window->client.width;
448 if (test_window->flags.shaded)
449 th = test_window->frame->top_width;
451 th = test_window->client.height + extra_height;
453 tw = test_window->frame->core->width;
454 th = test_window->frame->core->height;
456 tx = test_window->frame_x;
457 ty = test_window->frame_y;
459 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
460 (ty < (test_y + height)) && ((ty + th) > test_y) &&
461 (test_window->flags.mapped ||
462 (test_window->flags.shaded &&
463 test_window->frame->workspace == scr->current_workspace &&
464 !(test_window->flags.miniaturized || test_window->flags.hidden)))) {
468 test_window = test_window->prev;
470 if (loc_ok == True) {
475 test_x += PLACETEST_HSTEP;
477 test_y += PLACETEST_VSTEP;
484 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
485 unsigned int width, unsigned int height, int h, WArea usableArea)
487 set_width_height(wwin, &width, &height);
489 *x_ret = h * scr->cascade_index + X_ORIGIN;
490 *y_ret = h * scr->cascade_index + Y_ORIGIN;
492 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
493 scr->cascade_index = 0;
500 randomPlaceWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
501 unsigned int width, unsigned int height, WArea usableArea)
505 set_width_height(wwin, &width, &height);
507 w = ((usableArea.x2 - X_ORIGIN) - width);
508 h = ((usableArea.y2 - Y_ORIGIN) - height);
513 *x_ret = X_ORIGIN + rand() % w;
514 *y_ret = Y_ORIGIN + rand() % h;
517 void PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret, unsigned width, unsigned height)
519 WScreen *scr = wwin->screen_ptr;
520 int h = WMFontHeight(scr->title_font)
521 + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
522 WArea usableArea = wGetUsableAreaForHead(scr, wGetHeadForPointerLocation(scr),
525 switch (wPreferences.window_placement) {
527 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
531 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
535 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0, usableArea)) {
537 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1, usableArea)) {
540 /* there isn't a break here, because if we fail, it should fall
541 through to cascade placement, as people who want tiling want
542 automagicness aren't going to want to place their window */
545 if (wPreferences.window_placement == WPM_AUTO)
546 scr->cascade_index++;
548 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
550 if (wPreferences.window_placement == WPM_CASCADE)
551 scr->cascade_index++;
555 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
560 puts("Invalid window placement!!!");
567 * clip to usableArea instead of full screen
568 * this will also take dock/clip etc.. into account
569 * aswell as being xinerama friendly
571 if (*x_ret + width > usableArea.x2)
572 *x_ret = usableArea.x2 - width;
573 if (*x_ret < usableArea.x1)
574 *x_ret = usableArea.x1;
576 if (*y_ret + height > usableArea.y2)
577 *y_ret = usableArea.y2 - height;
578 if (*y_ret < usableArea.y1)
579 *y_ret = usableArea.y1;