Fix for stupid Solaris (maybe other Sysv systems too).
[wmaker-crm.git] / src / placement.c
blob025ec9066e3fe64eaa99f02e179af347dde1b017
1 /* placement.c - window and icon placement on screen
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997, 1998 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"
44 extern WPreferences wPreferences;
47 #define X_ORIGIN(scr) WMAX((scr)->totalUsableArea.x1,\
48 wPreferences.window_place_origin.x)
50 #define Y_ORIGIN(scr) WMAX((scr)->totalUsableArea.y1,\
51 wPreferences.window_place_origin.y)
55 * interactive window placement is in moveres.c
58 extern void
59 InteractivePlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
60 unsigned width, unsigned height);
64 * Returns True if it is an icon and is in this workspace.
66 static Bool
67 iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2,
68 int workspace, int *retX, int *retY)
70 void *parent;
71 int ok = 0;
73 parent = wcore->descriptor.parent;
75 /* if it is an application icon */
76 if (wcore->descriptor.parent_type == WCLASS_APPICON
77 && !((WAppIcon*)parent)->docked) {
78 *retX = ((WAppIcon*)parent)->x_pos;
79 *retY = ((WAppIcon*)parent)->y_pos;
81 ok = 1;
82 } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
83 (((WIcon*)parent)->owner->frame->workspace == workspace
84 || IS_OMNIPRESENT(((WIcon*)parent)->owner)
85 || wPreferences.sticky_icons)
86 && ((WIcon*)parent)->mapped) {
88 *retX = ((WIcon*)parent)->owner->icon_x;
89 *retY = ((WIcon*)parent)->owner->icon_y;
91 ok = 1;
92 } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
93 && ((WWindow*)parent)->flags.icon_moved
94 && (((WWindow*)parent)->frame->workspace == workspace
95 || IS_OMNIPRESENT((WWindow*)parent)
96 || wPreferences.sticky_icons)) {
97 *retX = ((WWindow*)parent)->icon_x;
98 *retY = ((WWindow*)parent)->icon_y;
100 ok = 1;
105 * Check if it is inside the screen.
107 if (ok) {
108 if (*retX < sx1-wPreferences.icon_size)
109 ok = 0;
110 else if (*retX > sx2)
111 ok = 0;
113 if (*retY < sy1-wPreferences.icon_size)
114 ok = 0;
115 else if (*retY > sy2)
116 ok = 0;
119 return ok;
125 void
126 PlaceIcon(WScreen *scr, int *x_ret, int *y_ret)
128 int pf; /* primary axis */
129 int sf; /* secondary axis */
130 int fullW;
131 int fullH;
132 char *map;
133 int pi, si;
134 WCoreWindow *obj;
135 int sx1, sx2, sy1, sy2; /* screen boundary */
136 int sw, sh;
137 int xo, yo;
138 int xs, ys;
139 int x, y;
140 int isize = wPreferences.icon_size;
141 int done = 0;
142 WMBagIterator iter;
145 * Find out screen boundaries.
147 sx1 = 0;
148 sy1 = 0;
149 sx2 = scr->scr_width;
150 sy2 = scr->scr_height;
151 if (scr->dock) {
152 if (scr->dock->on_right_side)
153 sx2 -= isize + DOCK_EXTRA_SPACE;
154 else
155 sx1 += isize + DOCK_EXTRA_SPACE;
158 sw = isize * (scr->scr_width/isize);
159 sh = isize * (scr->scr_height/isize);
160 fullW = (sx2-sx1)/isize;
161 fullH = (sy2-sy1)/isize;
163 /* icon yard boundaries */
164 if (wPreferences.icon_yard & IY_VERT) {
165 pf = fullH;
166 sf = fullW;
167 } else {
168 pf = fullW;
169 sf = fullH;
171 if (wPreferences.icon_yard & IY_RIGHT) {
172 xo = sx2 - isize;
173 xs = -1;
174 } else {
175 xo = sx1;
176 xs = 1;
178 if (wPreferences.icon_yard & IY_TOP) {
179 yo = sy1;
180 ys = 1;
181 } else {
182 yo = sy2 - isize;
183 ys = -1;
187 * Create a map with the occupied slots. 1 means the slot is used
188 * or at least partially used.
189 * The slot usage can be optimized by only marking fully used slots
190 * or slots that have most of it covered.
191 * Space usage is worse than the fvwm algorithm (used in the old version)
192 * but complexity is much better (faster) than it.
194 map = wmalloc((sw+2) * (sh+2));
195 memset(map, 0, (sw+2) * (sh+2));
197 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
199 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
201 while (obj) {
202 int x, y;
204 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
205 &x, &y)) {
206 int xdi, ydi; /* rounded down */
207 int xui, yui; /* rounded up */
209 xdi = x/isize;
210 ydi = y/isize;
211 xui = (x+isize/2)/isize;
212 yui = (y+isize/2)/isize;
213 map[INDEX(xdi,ydi)] = 1;
214 map[INDEX(xdi,yui)] = 1;
215 map[INDEX(xui,ydi)] = 1;
216 map[INDEX(xui,yui)] = 1;
218 obj = obj->stacking->under;
222 * Default position
224 *x_ret = 0;
225 *y_ret = 0;
228 * Look for an empty slot
230 for (si=0; si<sf; si++) {
231 for (pi=0; pi<pf; pi++) {
232 if (wPreferences.icon_yard & IY_VERT) {
233 x = xo + xs*(si*isize);
234 y = yo + ys*(pi*isize);
235 } else {
236 x = xo + xs*(pi*isize);
237 y = yo + ys*(si*isize);
239 if (!map[INDEX(x/isize, y/isize)]) {
240 *x_ret = x;
241 *y_ret = y;
242 done = 1;
243 break;
246 if (done)
247 break;
250 free(map);
255 * This function calculates the length of the intersection of two
256 * line sections. (Hey, is that english?)
258 static int
259 calcIntersectionLength(int p1, int l1, int p2, int l2)
261 int isect;
262 int tmp;
264 if (p1 > p2) {
265 tmp = p1;
266 p1 = p2;
267 p2 = tmp;
268 tmp = l1;
269 l1 = l2;
270 l2 = tmp;
273 if (p1 + l1 < p2)
274 isect = 0;
275 else if (p2 + l2 < p1 + l1)
276 isect = l2;
277 else
278 isect = p1 + l1 - p2;
280 return isect;
285 * This function calculates the area of the intersection of two rectangles.
287 static int
288 calcIntersectionArea(int x1, int y1, int w1, int h1,
289 int x2, int y2, int w2, int h2)
291 return calcIntersectionLength(x1, w1, x2, w2)
292 * calcIntersectionLength(y1, h1, y2, h2);
296 static int
297 calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
299 int sum_isect = 0;
300 WWindow *test_window;
301 int tw,tx,ty,th;
303 test_window = wwin->screen_ptr->focused_window;
304 for(;test_window != NULL && test_window->prev != NULL;)
305 test_window = test_window->prev;
307 for(; test_window != NULL; test_window = test_window->next) {
308 if (test_window->frame->core->stacking->window_level
309 < WMNormalLevel) {
310 continue;
313 #if 0
314 tw = test_window->client.width;
315 if (test_window->flags.shaded)
316 th = test_window->frame->top_width;
317 else
318 th = test_window->client.height + extra_height;
319 #else
320 tw = test_window->frame->core->width;
321 th = test_window->frame->core->height;
322 #endif
323 tx = test_window->frame_x;
324 ty = test_window->frame_y;
326 if (test_window->flags.mapped ||
327 (test_window->flags.shaded &&
328 !(test_window->flags.miniaturized ||
329 test_window->flags.hidden))) {
330 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
334 return sum_isect;
338 static void
339 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
340 unsigned int width, unsigned int height)
342 WScreen *scr = wwin->screen_ptr;
343 int test_x = 0, test_y = Y_ORIGIN(scr);
344 int from_x, to_x, from_y, to_y;
345 int sx;
346 int min_isect, min_isect_x, min_isect_y;
347 int sum_isect;
348 int extra_height;
349 WArea usableArea = scr->totalUsableArea;
351 if (wwin->frame)
352 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
353 else
354 extra_height = 24; /* random value */
356 sx = X_ORIGIN(scr);
358 min_isect = INT_MAX;
359 min_isect_x = sx;
360 min_isect_y = test_y;
362 height += extra_height;
364 while (((test_y + height) < usableArea.y2)) {
366 test_x = sx;
368 while ((test_x + width) < usableArea.x2) {
370 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
371 width, height);
373 if ( sum_isect < min_isect ) {
374 min_isect = sum_isect;
375 min_isect_x = test_x;
376 min_isect_y = test_y;
379 test_x += PLACETEST_HSTEP;
381 test_y += PLACETEST_VSTEP;
384 from_x = min_isect_x - PLACETEST_HSTEP + 1;
385 from_x = WMAX(from_x, X_ORIGIN(scr));
386 to_x = min_isect_x + PLACETEST_HSTEP;
387 if (to_x + width > usableArea.x2)
388 to_x = usableArea.x2 - width;
390 from_y = min_isect_y - PLACETEST_VSTEP + 1;
391 from_y = WMAX(from_y, Y_ORIGIN(scr));
392 to_y = min_isect_y + PLACETEST_VSTEP;
393 if (to_y + height > usableArea.y2)
394 to_y = usableArea.y2 - height;
396 for (test_x = from_x; test_x < to_x; test_x++) {
397 for (test_y = from_y; test_y < to_y; test_y++) {
398 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
399 width, height);
401 if ( sum_isect < min_isect ) {
402 min_isect = sum_isect;
403 min_isect_x = test_x;
404 min_isect_y = test_y;
409 *x_ret = min_isect_x;
410 *y_ret = min_isect_y;
414 static Bool
415 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
416 unsigned int width, unsigned int height, int tryCount)
418 WScreen *scr = wwin->screen_ptr;
419 int test_x = 0, test_y = Y_ORIGIN(scr);
420 int loc_ok = False, tw,tx,ty,th;
421 int swidth, sx;
422 WWindow *test_window;
423 int extra_height;
424 WArea usableArea = scr->totalUsableArea;
426 if (wwin->frame)
427 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
428 else
429 extra_height = 24; /* random value */
431 swidth = usableArea.x2-usableArea.x1;
432 sx = X_ORIGIN(scr);
434 /* this was based on fvwm2's smart placement */
436 height += extra_height;
438 while (((test_y + height) < (scr->scr_height)) && (!loc_ok)) {
440 test_x = sx;
442 while (((test_x + width) < swidth) && (!loc_ok)) {
444 loc_ok = True;
445 test_window = scr->focused_window;
447 while ((test_window != NULL) && (loc_ok == True)) {
449 if (test_window->frame->core->stacking->window_level
450 < WMNormalLevel && tryCount > 0) {
451 test_window = test_window->next;
452 continue;
454 #if 0
455 tw = test_window->client.width;
456 if (test_window->flags.shaded)
457 th = test_window->frame->top_width;
458 else
459 th = test_window->client.height + extra_height;
460 #else
461 tw = test_window->frame->core->width;
462 th = test_window->frame->core->height;
463 #endif
464 tx = test_window->frame_x;
465 ty = test_window->frame_y;
467 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
468 (ty < (test_y + height)) && ((ty + th) > test_y) &&
469 (test_window->flags.mapped ||
470 (test_window->flags.shaded &&
471 !(test_window->flags.miniaturized ||
472 test_window->flags.hidden)))) {
474 loc_ok = False;
476 test_window = test_window->next;
479 test_window = scr->focused_window;
481 while ((test_window != NULL) && (loc_ok == True)) {
483 if (test_window->frame->core->stacking->window_level
484 < WMNormalLevel && tryCount > 0) {
485 test_window = test_window->prev;
486 continue;
488 #if 0
489 tw = test_window->client.width;
490 if (test_window->flags.shaded)
491 th = test_window->frame->top_width;
492 else
493 th = test_window->client.height + extra_height;
494 #else
495 tw = test_window->frame->core->width;
496 th = test_window->frame->core->height;
497 #endif
498 tx = test_window->frame_x;
499 ty = test_window->frame_y;
501 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
502 (ty < (test_y + height)) && ((ty + th) > test_y) &&
503 (test_window->flags.mapped ||
504 (test_window->flags.shaded &&
505 !(test_window->flags.miniaturized ||
506 test_window->flags.hidden)))) {
508 loc_ok = False;
510 test_window = test_window->prev;
512 if (loc_ok == True) {
513 *x_ret = test_x;
514 *y_ret = test_y;
515 break;
517 test_x += PLACETEST_HSTEP;
519 test_y += PLACETEST_VSTEP;
522 return loc_ok;
526 static void
527 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
528 unsigned int width, unsigned int height, int h)
530 unsigned int extra_height;
531 WArea usableArea = scr->totalUsableArea;
533 if (wwin->frame)
534 extra_height = wwin->frame->top_width + wwin->frame->bottom_width;
535 else
536 extra_height = 24; /* random value */
538 *x_ret = h * scr->cascade_index + X_ORIGIN(scr);
539 *y_ret = h * scr->cascade_index + Y_ORIGIN(scr);
540 height += extra_height;
542 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
543 scr->cascade_index = 0;
544 *x_ret = X_ORIGIN(scr);
545 *y_ret = Y_ORIGIN(scr);
550 void
551 PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
552 unsigned width, unsigned height)
554 WScreen *scr = wwin->screen_ptr;
555 int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
557 switch (wPreferences.window_placement) {
558 case WPM_MANUAL:
559 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
560 break;
562 case WPM_SMART:
563 smartPlaceWindow(wwin, x_ret, y_ret, width, height);
564 break;
566 case WPM_AUTO:
567 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0)) {
568 break;
569 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1)) {
570 break;
572 /* there isn't a break here, because if we fail, it should fall
573 through to cascade placement, as people who want tiling want
574 automagicness aren't going to want to place their window */
576 case WPM_CASCADE:
577 if (wPreferences.window_placement == WPM_AUTO)
578 scr->cascade_index++;
580 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h);
582 if (wPreferences.window_placement == WPM_CASCADE)
583 scr->cascade_index++;
584 break;
586 case WPM_RANDOM:
588 int w, h, extra_height;
589 WArea usableArea = scr->totalUsableArea;
591 if (wwin->frame)
592 extra_height = wwin->frame->top_width + wwin->frame->bottom_width + 2;
593 else
594 extra_height = 24; /* random value */
596 w = ((usableArea.x2-X_ORIGIN(scr)) - width);
597 h = ((usableArea.y2-Y_ORIGIN(scr)) - height - extra_height);
598 if (w<1) w = 1;
599 if (h<1) h = 1;
600 *x_ret = X_ORIGIN(scr) + rand()%w;
601 *y_ret = Y_ORIGIN(scr) + rand()%h;
603 break;
605 #ifdef DEBUG
606 default:
607 puts("Invalid window placement!!!");
608 *x_ret = 0;
609 *y_ret = 0;
610 #endif
613 if (*x_ret + width > scr->scr_width)
614 *x_ret = scr->scr_width - width;
615 if (*x_ret < 0)
616 *x_ret = 0;
618 if (*y_ret + height > scr->scr_height)
619 *y_ret = scr->scr_height - height;
620 if (*y_ret < 0)
621 *y_ret = 0;