Fix some warnings
[wmaker-crm.git] / src / placement.c
blob5938661e4bcb62235ac2049f1a27b5a35a4c27db
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 along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "wconfig.h"
24 #include <X11/Xlib.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <limits.h>
30 #include "WindowMaker.h"
31 #include "wcore.h"
32 #include "framewin.h"
33 #include "window.h"
34 #include "icon.h"
35 #include "appicon.h"
36 #include "actions.h"
37 #include "funcs.h"
38 #include "application.h"
39 #include "appicon.h"
40 #include "dock.h"
41 #include "xinerama.h"
43 extern WPreferences wPreferences;
45 #define X_ORIGIN WMAX(usableArea.x1,\
46 wPreferences.window_place_origin.x)
48 #define Y_ORIGIN WMAX(usableArea.y1,\
49 wPreferences.window_place_origin.y)
51 /* interactive window placement is in moveres.c */
52 extern void InteractivePlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
53 unsigned width, unsigned height);
55 /* Returns True if it is an icon and is in this workspace */
56 static Bool
57 iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2,
58 int workspace, int *retX, int *retY)
60 void *parent;
61 int ok = 0;
63 parent = wcore->descriptor.parent;
65 /* if it is an application icon */
66 if (wcore->descriptor.parent_type == WCLASS_APPICON && !((WAppIcon *) parent)->docked) {
67 *retX = ((WAppIcon *) parent)->x_pos;
68 *retY = ((WAppIcon *) parent)->y_pos;
70 ok = 1;
71 } else if (wcore->descriptor.parent_type == WCLASS_MINIWINDOW &&
72 (((WIcon *) parent)->owner->frame->workspace == workspace
73 || IS_OMNIPRESENT(((WIcon *) parent)->owner)
74 || wPreferences.sticky_icons)
75 && ((WIcon *) parent)->mapped) {
77 *retX = ((WIcon *) parent)->owner->icon_x;
78 *retY = ((WIcon *) parent)->owner->icon_y;
80 ok = 1;
81 } else if (wcore->descriptor.parent_type == WCLASS_WINDOW
82 && ((WWindow *) parent)->flags.icon_moved
83 && (((WWindow *) parent)->frame->workspace == workspace || IS_OMNIPRESENT((WWindow *) parent)
84 || wPreferences.sticky_icons)) {
85 *retX = ((WWindow *) parent)->icon_x;
86 *retY = ((WWindow *) parent)->icon_y;
88 ok = 1;
91 /* Check if it is inside the screen */
92 if (ok) {
93 if (*retX < sx1 - wPreferences.icon_size)
94 ok = 0;
95 else if (*retX > sx2)
96 ok = 0;
98 if (*retY < sy1 - wPreferences.icon_size)
99 ok = 0;
100 else if (*retY > sy2)
101 ok = 0;
104 return ok;
107 void PlaceIcon(WScreen *scr, int *x_ret, int *y_ret, int head)
109 int pf; /* primary axis */
110 int sf; /* secondary axis */
111 int fullW;
112 int fullH;
113 char *map;
114 int pi, si;
115 WCoreWindow *obj;
116 int sx1, sx2, sy1, sy2; /* screen boundary */
117 int sw, sh;
118 int xo, yo;
119 int xs, ys;
120 int x, y;
121 int isize = wPreferences.icon_size;
122 int done = 0;
123 WMBagIterator iter;
124 WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
126 /* Find out screen boundaries. */
128 /* Allows each head to have miniwindows */
129 sx1 = area.x1;
130 sy1 = area.y1;
131 sx2 = area.x2;
132 sy2 = area.y2;
133 sw = sx2 - sx1;
134 sh = sy2 - sy1;
136 sw = isize * (sw / isize);
137 sh = isize * (sh / isize);
138 fullW = (sx2 - sx1) / isize;
139 fullH = (sy2 - sy1) / isize;
141 /* icon yard boundaries */
142 if (wPreferences.icon_yard & IY_VERT) {
143 pf = fullH;
144 sf = fullW;
145 } else {
146 pf = fullW;
147 sf = fullH;
149 if (wPreferences.icon_yard & IY_RIGHT) {
150 xo = sx2 - isize;
151 xs = -1;
152 } else {
153 xo = sx1;
154 xs = 1;
156 if (wPreferences.icon_yard & IY_TOP) {
157 yo = sy1;
158 ys = 1;
159 } else {
160 yo = sy2 - isize;
161 ys = -1;
165 * Create a map with the occupied slots. 1 means the slot is used
166 * or at least partially used.
167 * The slot usage can be optimized by only marking fully used slots
168 * or slots that have most of it covered.
169 * Space usage is worse than the fvwm algorithm (used in the old version)
170 * but complexity is much better (faster) than it.
172 map = wmalloc((sw + 2) * (sh + 2));
173 memset(map, 0, (sw + 2) * (sh + 2));
175 #define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
177 WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
179 while (obj) {
180 int x, y;
182 if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace, &x, &y)) {
183 int xdi, ydi; /* rounded down */
184 int xui, yui; /* rounded up */
186 xdi = x / isize;
187 ydi = y / isize;
188 xui = (x + isize / 2) / isize;
189 yui = (y + isize / 2) / isize;
190 map[INDEX(xdi, ydi)] = 1;
191 map[INDEX(xdi, yui)] = 1;
192 map[INDEX(xui, ydi)] = 1;
193 map[INDEX(xui, yui)] = 1;
195 obj = obj->stacking->under;
198 /* Default position */
199 *x_ret = 0;
200 *y_ret = 0;
202 /* Look for an empty slot */
203 for (si = 0; si < sf; si++) {
204 for (pi = 0; pi < pf; pi++) {
205 if (wPreferences.icon_yard & IY_VERT) {
206 x = xo + xs * (si * isize);
207 y = yo + ys * (pi * isize);
208 } else {
209 x = xo + xs * (pi * isize);
210 y = yo + ys * (si * isize);
212 if (!map[INDEX(x / isize, y / isize)]) {
213 *x_ret = x;
214 *y_ret = y;
215 done = 1;
216 break;
219 if (done)
220 break;
223 wfree(map);
226 /* Computes the intersecting length of two line sections */
227 int calcIntersectionLength(int p1, int l1, int p2, int l2)
229 int isect;
230 int tmp;
232 if (p1 > p2) {
233 tmp = p1;
234 p1 = p2;
235 p2 = tmp;
236 tmp = l1;
237 l1 = l2;
238 l2 = tmp;
241 if (p1 + l1 < p2)
242 isect = 0;
243 else if (p2 + l2 < p1 + l1)
244 isect = l2;
245 else
246 isect = p1 + l1 - p2;
248 return isect;
251 /* Computes the intersecting area of two rectangles */
252 int calcIntersectionArea(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
254 return calcIntersectionLength(x1, w1, x2, w2)
255 * calcIntersectionLength(y1, h1, y2, h2);
258 static int calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
260 int sum_isect = 0;
261 WWindow *test_window;
262 int tw, tx, ty, th;
264 test_window = wwin->screen_ptr->focused_window;
265 for (; test_window != NULL && test_window->prev != NULL;)
266 test_window = test_window->prev;
268 for (; test_window != NULL; test_window = test_window->next) {
269 if (test_window->frame->core->stacking->window_level < WMNormalLevel) {
270 continue;
273 tw = test_window->frame->core->width;
274 th = test_window->frame->core->height;
275 tx = test_window->frame_x;
276 ty = test_window->frame_y;
278 if (test_window->flags.mapped || (test_window->flags.shaded &&
279 test_window->frame->workspace == wwin->screen_ptr->current_workspace &&
280 !(test_window->flags.miniaturized || test_window->flags.hidden))) {
281 sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
285 return sum_isect;
288 static void set_width_height(WWindow *wwin, unsigned int *width, unsigned int *height)
290 if (wwin->frame) {
291 *height += wwin->frame->top_width + wwin->frame->bottom_width;
292 } else {
293 if (HAS_TITLEBAR(wwin))
294 *height += TITLEBAR_HEIGHT;
295 if (HAS_RESIZEBAR(wwin))
296 *height += RESIZEBAR_HEIGHT;
298 if (HAS_BORDER(wwin)) {
299 *height += 2 * FRAME_BORDER_WIDTH;
300 *width += 2 * FRAME_BORDER_WIDTH;
304 static void
305 smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret, unsigned int width,
306 unsigned int height, WArea usableArea)
308 int test_x = 0, test_y = Y_ORIGIN;
309 int from_x, to_x, from_y, to_y;
310 int sx;
311 int min_isect, min_isect_x, min_isect_y;
312 int sum_isect;
314 set_width_height(wwin, &width, &height);
316 sx = X_ORIGIN;
317 min_isect = INT_MAX;
318 min_isect_x = sx;
319 min_isect_y = test_y;
321 while (((test_y + height) < usableArea.y2)) {
322 test_x = sx;
323 while ((test_x + width) < usableArea.x2) {
324 sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y, width, height);
326 if (sum_isect < min_isect) {
327 min_isect = sum_isect;
328 min_isect_x = test_x;
329 min_isect_y = test_y;
332 test_x += PLACETEST_HSTEP;
334 test_y += PLACETEST_VSTEP;
337 from_x = min_isect_x - PLACETEST_HSTEP + 1;
338 from_x = WMAX(from_x, X_ORIGIN);
339 to_x = min_isect_x + PLACETEST_HSTEP;
340 if (to_x + width > usableArea.x2)
341 to_x = usableArea.x2 - width;
343 from_y = min_isect_y - PLACETEST_VSTEP + 1;
344 from_y = WMAX(from_y, Y_ORIGIN);
345 to_y = min_isect_y + PLACETEST_VSTEP;
346 if (to_y + height > usableArea.y2)
347 to_y = usableArea.y2 - height;
349 for (test_x = from_x; test_x < to_x; test_x++) {
350 for (test_y = from_y; test_y < to_y; test_y++) {
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;
361 *x_ret = min_isect_x;
362 *y_ret = min_isect_y;
365 static Bool
366 autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
367 unsigned int width, unsigned int height, int tryCount, WArea usableArea)
369 WScreen *scr = wwin->screen_ptr;
370 int test_x = 0, test_y = Y_ORIGIN;
371 int loc_ok = False, tw, tx, ty, th;
372 int swidth, sx;
373 WWindow *test_window;
375 set_width_height(wwin, &width, &height);
376 swidth = usableArea.x2 - usableArea.x1;
377 sx = X_ORIGIN;
379 /* this was based on fvwm2's smart placement */
380 while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
381 test_x = sx;
383 while (((test_x + width) < swidth) && (!loc_ok)) {
385 loc_ok = True;
386 test_window = scr->focused_window;
388 while ((test_window != NULL) && (loc_ok == True)) {
390 if (test_window->frame->core->stacking->window_level
391 < WMNormalLevel && tryCount > 0) {
392 test_window = test_window->next;
393 continue;
395 tw = test_window->frame->core->width;
396 th = test_window->frame->core->height;
397 tx = test_window->frame_x;
398 ty = test_window->frame_y;
400 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
401 (ty < (test_y + height)) && ((ty + th) > test_y) &&
402 (test_window->flags.mapped ||
403 (test_window->flags.shaded &&
404 test_window->frame->workspace == scr->current_workspace &&
405 !(test_window->flags.miniaturized || test_window->flags.hidden)))) {
407 loc_ok = False;
409 test_window = test_window->next;
412 test_window = scr->focused_window;
414 while ((test_window != NULL) && (loc_ok == True)) {
416 if (test_window->frame->core->stacking->window_level
417 < WMNormalLevel && tryCount > 0) {
418 test_window = test_window->prev;
419 continue;
421 tw = test_window->frame->core->width;
422 th = test_window->frame->core->height;
423 tx = test_window->frame_x;
424 ty = test_window->frame_y;
426 if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
427 (ty < (test_y + height)) && ((ty + th) > test_y) &&
428 (test_window->flags.mapped ||
429 (test_window->flags.shaded &&
430 test_window->frame->workspace == scr->current_workspace &&
431 !(test_window->flags.miniaturized || test_window->flags.hidden)))) {
433 loc_ok = False;
435 test_window = test_window->prev;
437 if (loc_ok == True) {
438 *x_ret = test_x;
439 *y_ret = test_y;
440 break;
442 test_x += PLACETEST_HSTEP;
444 test_y += PLACETEST_VSTEP;
447 return loc_ok;
450 static void
451 cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
452 unsigned int width, unsigned int height, int h, WArea usableArea)
454 set_width_height(wwin, &width, &height);
456 *x_ret = h * scr->cascade_index + X_ORIGIN;
457 *y_ret = h * scr->cascade_index + Y_ORIGIN;
459 if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
460 scr->cascade_index = 0;
461 *x_ret = X_ORIGIN;
462 *y_ret = Y_ORIGIN;
466 static void randomPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
467 unsigned int width, unsigned int height, WArea usableArea)
469 int w, h;
471 set_width_height(wwin, &width, &height);
473 w = ((usableArea.x2 - X_ORIGIN) - width);
474 h = ((usableArea.y2 - Y_ORIGIN) - height);
475 if (w < 1)
476 w = 1;
477 if (h < 1)
478 h = 1;
479 *x_ret = X_ORIGIN + rand() % w;
480 *y_ret = Y_ORIGIN + rand() % h;
483 void PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret, unsigned width, unsigned height)
485 WScreen *scr = wwin->screen_ptr;
486 int h = WMFontHeight(scr->title_font)
487 + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
488 WArea usableArea = wGetUsableAreaForHead(scr, wGetHeadForPointerLocation(scr),
489 NULL, True);
491 switch (wPreferences.window_placement) {
492 case WPM_MANUAL:
493 InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
494 break;
496 case WPM_SMART:
497 smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
498 break;
500 case WPM_AUTO:
501 if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0, usableArea)) {
502 break;
503 } else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1, usableArea)) {
504 break;
506 /* there isn't a break here, because if we fail, it should fall
507 through to cascade placement, as people who want tiling want
508 automagicness aren't going to want to place their window */
510 case WPM_CASCADE:
511 if (wPreferences.window_placement == WPM_AUTO)
512 scr->cascade_index++;
514 cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
516 if (wPreferences.window_placement == WPM_CASCADE)
517 scr->cascade_index++;
518 break;
520 case WPM_RANDOM:
521 randomPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
522 break;
526 * clip to usableArea instead of full screen
527 * this will also take dock/clip etc.. into account
528 * aswell as being xinerama friendly
530 if (*x_ret + width > usableArea.x2)
531 *x_ret = usableArea.x2 - width;
532 if (*x_ret < usableArea.x1)
533 *x_ret = usableArea.x1;
535 if (*y_ret + height > usableArea.y2)
536 *y_ret = usableArea.y2 - height;
537 if (*y_ret < usableArea.y1)
538 *y_ret = usableArea.y1;