- Check whether libXft is at least version 2.1.2 else refuse to compile.
[wmaker-crm.git] / src / placement.c
blob47a1af6a500d03a01e65ca624defefca7b982dba
1 /* placement.c - window and icon placement on screen
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 *
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,
20 * USA.
23 #include "wconfig.h"
25 #include <X11/Xlib.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <limits.h>
31 #include "WindowMaker.h"
32 #include "wcore.h"
33 #include "framewin.h"
34 #include "window.h"
35 #include "icon.h"
36 #include "appicon.h"
37 #include "actions.h"
38 #include "funcs.h"
39 #include "application.h"
40 #include "appicon.h"
41 #include "dock.h"
42 #include "xinerama.h"
45 extern WPreferences wPreferences;
48 #define X_ORIGIN(scr) WMAX(usableArea.x1,\
49 wPreferences.window_place_origin.x)
51 #define Y_ORIGIN(scr) WMAX(usableArea.y1,\
52 wPreferences.window_place_origin.y)
56 * interactive window placement is in moveres.c
59 extern void
60 InteractivePlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
61 unsigned width, unsigned height);
65 * Returns True if it is an icon and is in this workspace.
67 static Bool
68 iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2,
69 int workspace, int *retX, int *retY)
71 void *parent;
72 int ok = 0;
74 parent = wcore->descriptor.parent;
76 /* if it is an application icon */
77 if (wcore->descriptor.parent_type == WCLASS_APPICON
78 && !((WAppIcon*)parent)->docked) {
79 *retX = ((WAppIcon*)parent)->x_pos;
80 *retY = ((WAppIcon*)parent)->y_pos;
82 ok = 1;
83 } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
84 (((WIcon*)parent)->owner->frame->workspace == workspace
85 || IS_OMNIPRESENT(((WIcon*)parent)->owner)
86 || wPreferences.sticky_icons)
87 && ((WIcon*)parent)->mapped) {
89 *retX = ((WIcon*)parent)->owner->icon_x;
90 *retY = ((WIcon*)parent)->owner->icon_y;
92 ok = 1;
93 } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
94 && ((WWindow*)parent)->flags.icon_moved
95 && (((WWindow*)parent)->frame->workspace == workspace
96 || IS_OMNIPRESENT((WWindow*)parent)
97 || wPreferences.sticky_icons)) {
98 *retX = ((WWindow*)parent)->icon_x;
99 *retY = ((WWindow*)parent)->icon_y;
101 ok = 1;
106 * Check if it is inside the screen.
108 if (ok) {
109 if (*retX < sx1-wPreferences.icon_size)
110 ok = 0;
111 else if (*retX > sx2)
112 ok = 0;
114 if (*retY < sy1-wPreferences.icon_size)
115 ok = 0;
116 else if (*retY > sy2)
117 ok = 0;
120 return ok;
126 void
127 PlaceIcon(WScreen *scr, int *x_ret, int *y_ret, int head)
129 int pf; /* primary axis */
130 int sf; /* secondary axis */
131 int fullW;
132 int fullH;
133 char *map;
134 int pi, si;
135 WCoreWindow *obj;
136 int sx1, sx2, sy1, sy2; /* screen boundary */
137 int sw, sh;
138 int xo, yo;
139 int xs, ys;
140 int x, y;
141 int isize = wPreferences.icon_size;
142 int done = 0;
143 WMBagIterator iter;
144 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
146 /* Find out screen boundaries. */
148 /* Allows each head to have miniwindows */
150 sx1 = area.x1;
151 sy1 = area.y1;
152 sx2 = area.x2;
153 sy2 = area.y2;
154 sw = sx2-sx1;
155 sh = sy2-sy1;
157 #if 0
158 if (scr->dock) {
159 if (scr->dock->on_right_side)
160 sx2 -= isize + DOCK_EXTRA_SPACE;
161 else
162 sx1 += isize + DOCK_EXTRA_SPACE;
164 #endif
166 sw = isize * (sw/isize);
167 sh = isize * (sh/isize);
168 fullW = (sx2-sx1)/isize;
169 fullH = (sy2-sy1)/isize;
171 /* icon yard boundaries */
172 if (wPreferences.icon_yard & IY_VERT) {
173 pf = fullH;
174 sf = fullW;
175 } else {
176 pf = fullW;
177 sf = fullH;
179 if (wPreferences.icon_yard & IY_RIGHT) {
180 xo = sx2 - isize;
181 xs = -1;
182 } else {
183 xo = sx1;
184 xs = 1;
186 if (wPreferences.icon_yard & IY_TOP) {
187 yo = sy1;
188 ys = 1;
189 } else {
190 yo = sy2 - isize;
191 ys = -1;
195 * Create a map with the occupied slots. 1 means the slot is used
196 * or at least partially used.
197 * The slot usage can be optimized by only marking fully used slots
198 * or slots that have most of it covered.
199 * Space usage is worse than the fvwm algorithm (used in the old version)
200 * but complexity is much better (faster) than it.
202 map = wmalloc((sw+2) * (sh+2));
203 memset(map, 0, (sw+2) * (sh+2));
205 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
207 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
209 while (obj) {
210 int x, y;
212 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
213 &x, &y)) {
214 int xdi, ydi; /* rounded down */
215 int xui, yui; /* rounded up */
217 xdi = x/isize;
218 ydi = y/isize;
219 xui = (x+isize/2)/isize;
220 yui = (y+isize/2)/isize;
221 map[INDEX(xdi,ydi)] = 1;
222 map[INDEX(xdi,yui)] = 1;
223 map[INDEX(xui,ydi)] = 1;
224 map[INDEX(xui,yui)] = 1;
226 obj = obj->stacking->under;
230 * Default position
232 *x_ret = 0;
233 *y_ret = 0;
236 * Look for an empty slot
238 for (si=0; si<sf; si++) {
239 for (pi=0; pi<pf; pi++) {
240 if (wPreferences.icon_yard & IY_VERT) {
241 x = xo + xs*(si*isize);
242 y = yo + ys*(pi*isize);
243 } else {
244 x = xo + xs*(pi*isize);
245 y = yo + ys*(si*isize);
247 if (!map[INDEX(x/isize, y/isize)]) {
248 *x_ret = x;
249 *y_ret = y;
250 done = 1;
251 break;
254 if (done)
255 break;
258 wfree(map);
263 * This function calculates the length of the intersection of two
264 * line sections. (Hey, is that english?)
266 static int
267 calcIntersectionLength(int p1, int l1, int p2, int l2)
269 int isect;
270 int tmp;
272 if (p1 > p2) {
273 tmp = p1;
274 p1 = p2;
275 p2 = tmp;
276 tmp = l1;
277 l1 = l2;
278 l2 = tmp;
281 if (p1 + l1 < p2)
282 isect = 0;
283 else if (p2 + l2 < p1 + l1)
284 isect = l2;
285 else
286 isect = p1 + l1 - p2;
288 return isect;
293 * This function calculates the area of the intersection of two rectangles.
297 calcIntersectionArea(int x1, int y1, int w1, int h1,
298 int x2, int y2, int w2, int h2)
300 return calcIntersectionLength(x1, w1, x2, w2)
301 * calcIntersectionLength(y1, h1, y2, h2);
305 static int
306 calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
308 int sum_isect = 0;
309 WWindow *test_window;
310 int tw,tx,ty,th;
312 test_window = wwin->screen_ptr->focused_window;
313 for(;test_window != NULL && test_window->prev != NULL;)
314 test_window = test_window->prev;
316 for(; test_window != NULL; test_window = test_window->next) {
317 if (test_window->frame->core->stacking->window_level
318 < WMNormalLevel) {
319 continue;
322 #if 0
323 tw = test_window->client.width;
324 if (test_window->flags.shaded)
325 th = test_window->frame->top_width;
326 else
327 th = test_window->client.height + extra_height;
328 #else
329 tw = test_window->frame->core->width;
330 th = test_window->frame->core->height;
331 #endif
332 tx = test_window->frame_x;
333 ty = test_window->frame_y;
335 if (test_window->flags.mapped ||
336 (test_window->flags.shaded &&
337 !(test_window->flags.miniaturized ||
338 test_window->flags.hidden))) {
339 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
343 return sum_isect;
347 static void
348 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
349 unsigned int width, unsigned int height,
350 WArea usableArea)
352 //WScreen *scr = wwin->screen_ptr;
353 int test_x = 0, test_y = Y_ORIGIN(scr);
354 int from_x, to_x, from_y, to_y;
355 int sx;
356 int min_isect, min_isect_x, min_isect_y;
357 int sum_isect;
359 if (wwin->frame) {
360 height += wwin->frame->top_width + wwin->frame->bottom_width;
361 } else {
362 if (HAS_TITLEBAR(wwin)) height += 18;
363 if (HAS_RESIZEBAR(wwin)) height += 8;
365 if (HAS_BORDER(wwin)) {
366 height += 2;
367 width += 2;
369 sx = X_ORIGIN(scr);
371 min_isect = INT_MAX;
372 min_isect_x = sx;
373 min_isect_y = test_y;
375 while (((test_y + height) < usableArea.y2)) {
376 test_x = sx;
377 while ((test_x + width) < usableArea.x2) {
378 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
379 width, height);
381 if (sum_isect < min_isect) {
382 min_isect = sum_isect;
383 min_isect_x = test_x;
384 min_isect_y = test_y;
387 test_x += PLACETEST_HSTEP;
389 test_y += PLACETEST_VSTEP;
392 from_x = min_isect_x - PLACETEST_HSTEP + 1;
393 from_x = WMAX(from_x, X_ORIGIN(scr));
394 to_x = min_isect_x + PLACETEST_HSTEP;
395 if (to_x + width > usableArea.x2)
396 to_x = usableArea.x2 - width;
398 from_y = min_isect_y - PLACETEST_VSTEP + 1;
399 from_y = WMAX(from_y, Y_ORIGIN(scr));
400 to_y = min_isect_y + PLACETEST_VSTEP;
401 if (to_y + height > usableArea.y2)
402 to_y = usableArea.y2 - height;
404 for (test_x = from_x; test_x < to_x; test_x++) {
405 for (test_y = from_y; test_y < to_y; test_y++) {
406 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
407 width, height);
409 if (sum_isect < min_isect) {
410 min_isect = sum_isect;
411 min_isect_x = test_x;
412 min_isect_y = test_y;
417 *x_ret = min_isect_x;
418 *y_ret = min_isect_y;
422 static Bool
423 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
424 unsigned int width, unsigned int height, int tryCount,
425 WArea usableArea)
427 WScreen *scr = wwin->screen_ptr;
428 int test_x = 0, test_y = Y_ORIGIN(scr);
429 int loc_ok = False, tw,tx,ty,th;
430 int swidth, sx;
431 WWindow *test_window;
433 if (wwin->frame) {
434 height += wwin->frame->top_width + wwin->frame->bottom_width;
435 } else {
436 if (HAS_TITLEBAR(wwin)) height += 18;
437 if (HAS_RESIZEBAR(wwin)) height += 8;
439 if (HAS_BORDER(wwin)) {
440 height += 2;
441 width += 2;
444 swidth = usableArea.x2-usableArea.x1;
445 sx = X_ORIGIN(scr);
447 /* this was based on fvwm2's smart placement */
449 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
450 test_x = sx;
452 while (((test_x + width) < swidth) && (!loc_ok)) {
454 loc_ok = True;
455 test_window = scr->focused_window;
457 while ((test_window != NULL) && (loc_ok == True)) {
459 if (test_window->frame->core->stacking->window_level
460 < WMNormalLevel && tryCount > 0) {
461 test_window = test_window->next;
462 continue;
464 #if 0
465 tw = test_window->client.width;
466 if (test_window->flags.shaded)
467 th = test_window->frame->top_width;
468 else
469 th = test_window->client.height + extra_height;
470 #else
471 tw = test_window->frame->core->width;
472 th = test_window->frame->core->height;
473 #endif
474 tx = test_window->frame_x;
475 ty = test_window->frame_y;
477 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
478 (ty < (test_y + height)) && ((ty + th) > test_y) &&
479 (test_window->flags.mapped ||
480 (test_window->flags.shaded &&
481 test_window->frame->workspace==scr->current_workspace &&
482 !(test_window->flags.miniaturized ||
483 test_window->flags.hidden)))) {
485 loc_ok = False;
487 test_window = test_window->next;
490 test_window = scr->focused_window;
492 while ((test_window != NULL) && (loc_ok == True)) {
494 if (test_window->frame->core->stacking->window_level
495 < WMNormalLevel && tryCount > 0) {
496 test_window = test_window->prev;
497 continue;
499 #if 0
500 tw = test_window->client.width;
501 if (test_window->flags.shaded)
502 th = test_window->frame->top_width;
503 else
504 th = test_window->client.height + extra_height;
505 #else
506 tw = test_window->frame->core->width;
507 th = test_window->frame->core->height;
508 #endif
509 tx = test_window->frame_x;
510 ty = test_window->frame_y;
512 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
513 (ty < (test_y + height)) && ((ty + th) > test_y) &&
514 (test_window->flags.mapped ||
515 (test_window->flags.shaded &&
516 test_window->frame->workspace==scr->current_workspace &&
517 !(test_window->flags.miniaturized ||
518 test_window->flags.hidden)))) {
520 loc_ok = False;
522 test_window = test_window->prev;
524 if (loc_ok == True) {
525 *x_ret = test_x;
526 *y_ret = test_y;
527 break;
529 test_x += PLACETEST_HSTEP;
531 test_y += PLACETEST_VSTEP;
534 return loc_ok;
538 static void
539 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
540 unsigned int width, unsigned int height, int h,
541 WArea usableArea)
543 if (wwin->frame) {
544 height += wwin->frame->top_width + wwin->frame->bottom_width;
545 } else {
546 if (HAS_TITLEBAR(wwin)) height += 18;
547 if (HAS_RESIZEBAR(wwin)) height += 8;
549 if (HAS_BORDER(wwin)) {
550 height += 2;
551 width += 2;
554 *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
555 *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
557 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
558 scr->cascade_index = 0;
559 *x_ret = X_ORIGIN(scr);
560 *y_ret = Y_ORIGIN(scr);
565 static void
566 randomPlaceWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
567 unsigned int width, unsigned int height,
568 WArea usableArea)
570 int w, h;
572 if (wwin->frame) {
573 height += wwin->frame->top_width + wwin->frame->bottom_width;
574 } else {
575 if (HAS_TITLEBAR(wwin)) height += 18;
576 if (HAS_RESIZEBAR(wwin)) height += 8;
578 if (HAS_BORDER(wwin)) {
579 height += 2;
580 width += 2;
583 w = ((usableArea.x2-X_ORIGIN(scr)) - width);
584 h = ((usableArea.y2-Y_ORIGIN(scr)) - height);
585 if (w<1) w = 1;
586 if (h<1) h = 1;
587 *x_ret = X_ORIGIN(scr) + rand()%w;
588 *y_ret = Y_ORIGIN(scr) + rand()%h;
593 void
594 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
595 unsigned width, unsigned height)
597 WScreen *scr = wwin->screen_ptr;
598 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
599 WArea usableArea = wGetUsableAreaForHead(scr,
600 wGetHeadForPointerLocation(scr),
601 NULL, True);
603 switch (wPreferences.window_placement) {
604 case WPM_MANUAL:
605 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
606 break;
608 case WPM_SMART:
609 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
610 break;
612 case WPM_AUTO:
613 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0,
614 usableArea)) {
615 break;
616 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1,
617 usableArea)) {
618 break;
620 /* there isn't a break here, because if we fail, it should fall
621 through to cascade placement, as people who want tiling want
622 automagicness aren't going to want to place their window */
624 case WPM_CASCADE:
625 if (wPreferences.window_placement == WPM_AUTO)
626 scr->cascade_index++;
628 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
630 if (wPreferences.window_placement == WPM_CASCADE)
631 scr->cascade_index++;
632 break;
634 case WPM_RANDOM:
635 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
636 break;
638 #ifdef DEBUG
639 default:
640 puts("Invalid window placement!!!");
641 *x_ret = 0;
642 *y_ret = 0;
643 #endif
647 * clip to usableArea instead of full screen
648 * this will also take dock/clip etc.. into account
649 * aswell as being xinerama friendly
651 if (*x_ret + width > usableArea.x2)
652 *x_ret = usableArea.x2 - width;
653 if (*x_ret < usableArea.x1)
654 *x_ret = usableArea.x1;
656 if (*y_ret + height > usableArea.y2)
657 *y_ret = usableArea.y2 - height;
658 if (*y_ret < usableArea.y1)
659 *y_ret = usableArea.y1;