Change to the linux kernel coding style
[wmaker-crm.git] / src / placement.c
blob14aa501e8e8ecd4225826daadc6597b2b0c196a7
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,
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"
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.
61 static Bool
62 iconPosition(WCoreWindow * wcore, int sx1, int sy1, int sx2, int sy2, int workspace, int *retX, int *retY)
64 void *parent;
65 int ok = 0;
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;
74 ok = 1;
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;
84 ok = 1;
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;
92 ok = 1;
96 * Check if it is inside the screen.
98 if (ok) {
99 if (*retX < sx1 - wPreferences.icon_size)
100 ok = 0;
101 else if (*retX > sx2)
102 ok = 0;
104 if (*retY < sy1 - wPreferences.icon_size)
105 ok = 0;
106 else if (*retY > sy2)
107 ok = 0;
110 return ok;
113 void PlaceIcon(WScreen * scr, int *x_ret, int *y_ret, int head)
115 int pf; /* primary axis */
116 int sf; /* secondary axis */
117 int fullW;
118 int fullH;
119 char *map;
120 int pi, si;
121 WCoreWindow *obj;
122 int sx1, sx2, sy1, sy2; /* screen boundary */
123 int sw, sh;
124 int xo, yo;
125 int xs, ys;
126 int x, y;
127 int isize = wPreferences.icon_size;
128 int done = 0;
129 WMBagIterator iter;
130 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
132 /* Find out screen boundaries. */
134 /* Allows each head to have miniwindows */
136 sx1 = area.x1;
137 sy1 = area.y1;
138 sx2 = area.x2;
139 sy2 = area.y2;
140 sw = sx2 - sx1;
141 sh = sy2 - sy1;
143 #if 0
144 if (scr->dock) {
145 if (scr->dock->on_right_side)
146 sx2 -= isize + DOCK_EXTRA_SPACE;
147 else
148 sx1 += isize + DOCK_EXTRA_SPACE;
150 #endif
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) {
159 pf = fullH;
160 sf = fullW;
161 } else {
162 pf = fullW;
163 sf = fullH;
165 if (wPreferences.icon_yard & IY_RIGHT) {
166 xo = sx2 - isize;
167 xs = -1;
168 } else {
169 xo = sx1;
170 xs = 1;
172 if (wPreferences.icon_yard & IY_TOP) {
173 yo = sy1;
174 ys = 1;
175 } else {
176 yo = sy2 - isize;
177 ys = -1;
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) {
195 while (obj) {
196 int x, y;
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 */
202 xdi = x / isize;
203 ydi = y / isize;
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;
215 * Default position
217 *x_ret = 0;
218 *y_ret = 0;
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);
228 } else {
229 x = xo + xs * (pi * isize);
230 y = yo + ys * (si * isize);
232 if (!map[INDEX(x / isize, y / isize)]) {
233 *x_ret = x;
234 *y_ret = y;
235 done = 1;
236 break;
239 if (done)
240 break;
243 wfree(map);
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)
252 int isect;
253 int tmp;
255 if (p1 > p2) {
256 tmp = p1;
257 p1 = p2;
258 p2 = tmp;
259 tmp = l1;
260 l1 = l2;
261 l2 = tmp;
264 if (p1 + l1 < p2)
265 isect = 0;
266 else if (p2 + l2 < p1 + l1)
267 isect = l2;
268 else
269 isect = p1 + l1 - p2;
271 return isect;
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)
286 int sum_isect = 0;
287 WWindow *test_window;
288 int tw, tx, ty, th;
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) {
296 continue;
298 #if 0
299 tw = test_window->client.width;
300 if (test_window->flags.shaded)
301 th = test_window->frame->top_width;
302 else
303 th = test_window->client.height + extra_height;
304 #else
305 tw = test_window->frame->core->width;
306 th = test_window->frame->core->height;
307 #endif
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);
318 return sum_isect;
321 static void
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;
326 int sx;
327 int min_isect, min_isect_x, min_isect_y;
328 int sum_isect;
330 if (wwin->frame) {
331 height += wwin->frame->top_width + wwin->frame->bottom_width;
332 } else {
333 if (HAS_TITLEBAR(wwin))
334 height += 18;
335 if (HAS_RESIZEBAR(wwin))
336 height += 8;
338 if (HAS_BORDER(wwin)) {
339 height += 2;
340 width += 2;
342 sx = X_ORIGIN;
344 min_isect = INT_MAX;
345 min_isect_x = sx;
346 min_isect_y = test_y;
348 while (((test_y + height) < usableArea.y2)) {
349 test_x = sx;
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;
392 static Bool
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;
399 int swidth, sx;
400 WWindow *test_window;
402 if (wwin->frame) {
403 height += wwin->frame->top_width + wwin->frame->bottom_width;
404 } else {
405 if (HAS_TITLEBAR(wwin))
406 height += 18;
407 if (HAS_RESIZEBAR(wwin))
408 height += 8;
410 if (HAS_BORDER(wwin)) {
411 height += 2;
412 width += 2;
415 swidth = usableArea.x2 - usableArea.x1;
416 sx = X_ORIGIN;
418 /* this was based on fvwm2's smart placement */
420 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
421 test_x = sx;
423 while (((test_x + width) < swidth) && (!loc_ok)) {
425 loc_ok = True;
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;
433 continue;
435 #if 0
436 tw = test_window->client.width;
437 if (test_window->flags.shaded)
438 th = test_window->frame->top_width;
439 else
440 th = test_window->client.height + extra_height;
441 #else
442 tw = test_window->frame->core->width;
443 th = test_window->frame->core->height;
444 #endif
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)))) {
455 loc_ok = False;
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;
467 continue;
469 #if 0
470 tw = test_window->client.width;
471 if (test_window->flags.shaded)
472 th = test_window->frame->top_width;
473 else
474 th = test_window->client.height + extra_height;
475 #else
476 tw = test_window->frame->core->width;
477 th = test_window->frame->core->height;
478 #endif
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)))) {
489 loc_ok = False;
491 test_window = test_window->prev;
493 if (loc_ok == True) {
494 *x_ret = test_x;
495 *y_ret = test_y;
496 break;
498 test_x += PLACETEST_HSTEP;
500 test_y += PLACETEST_VSTEP;
503 return loc_ok;
506 static void
507 cascadeWindow(WScreen * scr, WWindow * wwin, int *x_ret, int *y_ret,
508 unsigned int width, unsigned int height, int h, WArea usableArea)
510 if (wwin->frame) {
511 height += wwin->frame->top_width + wwin->frame->bottom_width;
512 } else {
513 if (HAS_TITLEBAR(wwin))
514 height += 18;
515 if (HAS_RESIZEBAR(wwin))
516 height += 8;
518 if (HAS_BORDER(wwin)) {
519 height += 2;
520 width += 2;
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;
528 *x_ret = X_ORIGIN;
529 *y_ret = Y_ORIGIN;
533 static void
534 randomPlaceWindow(WScreen * scr, WWindow * wwin, int *x_ret, int *y_ret,
535 unsigned int width, unsigned int height, WArea usableArea)
537 int w, h;
539 if (wwin->frame) {
540 height += wwin->frame->top_width + wwin->frame->bottom_width;
541 } else {
542 if (HAS_TITLEBAR(wwin))
543 height += 18;
544 if (HAS_RESIZEBAR(wwin))
545 height += 8;
547 if (HAS_BORDER(wwin)) {
548 height += 2;
549 width += 2;
552 w = ((usableArea.x2 - X_ORIGIN) - width);
553 h = ((usableArea.y2 - Y_ORIGIN) - height);
554 if (w < 1)
555 w = 1;
556 if (h < 1)
557 h = 1;
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),
568 NULL, True);
570 switch (wPreferences.window_placement) {
571 case WPM_MANUAL:
572 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
573 break;
575 case WPM_SMART:
576 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
577 break;
579 case WPM_AUTO:
580 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0, usableArea)) {
581 break;
582 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1, usableArea)) {
583 break;
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 */
589 case WPM_CASCADE:
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++;
597 break;
599 case WPM_RANDOM:
600 randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
601 break;
603 #ifdef DEBUG
604 default:
605 puts("Invalid window placement!!!");
606 *x_ret = 0;
607 *y_ret = 0;
608 #endif
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;