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"
45
extern WPreferences wPreferences;
48
#define X_ORIGIN WMAX(usableArea.x1,\
49
wPreferences.window_place_origin.x)
51
#define Y_ORIGIN WMAX(usableArea.y1,\
52
wPreferences.window_place_origin.y)
56
* interactive window placement is in moveres.c
59
extern void 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.
67
iconPosition(WCoreWindow *wcore, int sx1, int sy1, int sx2, int sy2,
68
int workspace, int *retX, int *retY)
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;
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;
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;
105
* Check if it is inside the screen.
108
if (*retX < sx1-wPreferences.icon_size)
110
else if (*retX > sx2)
113
if (*retY < sy1-wPreferences.icon_size)
115
else if (*retY > sy2)
126
PlaceIcon(WScreen *scr, int *x_ret, int *y_ret, int head)
128
int pf; /* primary axis */
129
int sf; /* secondary axis */
135
int sx1, sx2, sy1, sy2; /* screen boundary */
140
int isize = wPreferences.icon_size;
143
WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
145
/* Find out screen boundaries. */
147
/* Allows each head to have miniwindows */
158
if (scr->dock->on_right_side)
159
sx2 -= isize + DOCK_EXTRA_SPACE;
161
sx1 += isize + DOCK_EXTRA_SPACE;
165
sw = isize * (sw/isize);
166
sh = isize * (sh/isize);
167
fullW = (sx2-sx1)/isize;
168
fullH = (sy2-sy1)/isize;
170
/* icon yard boundaries */
171
if (wPreferences.icon_yard & IY_VERT) {
178
if (wPreferences.icon_yard & IY_RIGHT) {
185
if (wPreferences.icon_yard & IY_TOP) {
194
* Create a map with the occupied slots. 1 means the slot is used
195
* or at least partially used.
196
* The slot usage can be optimized by only marking fully used slots
197
* or slots that have most of it covered.
198
* Space usage is worse than the fvwm algorithm (used in the old version)
199
* but complexity is much better (faster) than it.
201
map = wmalloc((sw+2) * (sh+2));
202
memset(map, 0, (sw+2) * (sh+2));
204
#define INDEX(x,y) (((y)+1)*(sw+2) + (x) + 1)
206
WM_ETARETI_BAG(scr->stacking_list, obj, iter) {
211
if (iconPosition(obj, sx1, sy1, sx2, sy2, scr->current_workspace,
213
int xdi, ydi; /* rounded down */
214
int xui, yui; /* rounded up */
218
xui = (x+isize/2)/isize;
219
yui = (y+isize/2)/isize;
220
map[INDEX(xdi,ydi)] = 1;
221
map[INDEX(xdi,yui)] = 1;
222
map[INDEX(xui,ydi)] = 1;
223
map[INDEX(xui,yui)] = 1;
225
obj = obj->stacking->under;
235
* Look for an empty slot
237
for (si=0; si<sf; si++) {
238
for (pi=0; pi<pf; pi++) {
239
if (wPreferences.icon_yard & IY_VERT) {
240
x = xo + xs*(si*isize);
241
y = yo + ys*(pi*isize);
243
x = xo + xs*(pi*isize);
244
y = yo + ys*(si*isize);
246
if (!map[INDEX(x/isize, y/isize)]) {
262
* This function calculates the length of the intersection of two
263
* line sections. (Hey, is that english?)
266
calcIntersectionLength(int p1, int l1, int p2, int l2)
282
else if (p2 + l2 < p1 + l1)
285
isect = p1 + l1 - p2;
292
* This function calculates the area of the intersection of two rectangles.
296
calcIntersectionArea(int x1, int y1, int w1, int h1,
297
int x2, int y2, int w2, int h2)
299
return calcIntersectionLength(x1, w1, x2, w2)
300
* calcIntersectionLength(y1, h1, y2, h2);
305
calcSumOfCoveredAreas(WWindow *wwin, int x, int y, int w, int h)
308
WWindow *test_window;
311
test_window = wwin->screen_ptr->focused_window;
312
for(;test_window != NULL && test_window->prev != NULL;)
313
test_window = test_window->prev;
315
for(; test_window != NULL; test_window = test_window->next) {
316
if (test_window->frame->core->stacking->window_level
322
tw = test_window->client.width;
323
if (test_window->flags.shaded)
324
th = test_window->frame->top_width;
326
th = test_window->client.height + extra_height;
328
tw = test_window->frame->core->width;
329
th = test_window->frame->core->height;
331
tx = test_window->frame_x;
332
ty = test_window->frame_y;
334
if (test_window->flags.mapped ||
335
(test_window->flags.shaded &&
336
!(test_window->flags.miniaturized ||
337
test_window->flags.hidden))) {
338
sum_isect += calcIntersectionArea(tx, ty, tw, th, x, y, w, h);
347
smartPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
348
unsigned int width, unsigned int height,
351
int test_x = 0, test_y = Y_ORIGIN;
352
int from_x, to_x, from_y, to_y;
354
int min_isect, min_isect_x, min_isect_y;
358
height += wwin->frame->top_width + wwin->frame->bottom_width;
360
if (HAS_TITLEBAR(wwin)) height += 18;
361
if (HAS_RESIZEBAR(wwin)) height += 8;
363
if (HAS_BORDER(wwin)) {
371
min_isect_y = test_y;
373
while (((test_y + height) < usableArea.y2)) {
375
while ((test_x + width) < usableArea.x2) {
376
sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
379
if (sum_isect < min_isect) {
380
min_isect = sum_isect;
381
min_isect_x = test_x;
382
min_isect_y = test_y;
385
test_x += PLACETEST_HSTEP;
387
test_y += PLACETEST_VSTEP;
390
from_x = min_isect_x - PLACETEST_HSTEP + 1;
391
from_x = WMAX(from_x, X_ORIGIN);
392
to_x = min_isect_x + PLACETEST_HSTEP;
393
if (to_x + width > usableArea.x2)
394
to_x = usableArea.x2 - width;
396
from_y = min_isect_y - PLACETEST_VSTEP + 1;
397
from_y = WMAX(from_y, Y_ORIGIN);
398
to_y = min_isect_y + PLACETEST_VSTEP;
399
if (to_y + height > usableArea.y2)
400
to_y = usableArea.y2 - height;
402
for (test_x = from_x; test_x < to_x; test_x++) {
403
for (test_y = from_y; test_y < to_y; test_y++) {
404
sum_isect = calcSumOfCoveredAreas(wwin, test_x, test_y,
407
if (sum_isect < min_isect) {
408
min_isect = sum_isect;
409
min_isect_x = test_x;
410
min_isect_y = test_y;
415
*x_ret = min_isect_x;
416
*y_ret = min_isect_y;
421
autoPlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
422
unsigned int width, unsigned int height, int tryCount,
425
WScreen *scr = wwin->screen_ptr;
426
int test_x = 0, test_y = Y_ORIGIN;
427
int loc_ok = False, tw,tx,ty,th;
429
WWindow *test_window;
432
height += wwin->frame->top_width + wwin->frame->bottom_width;
434
if (HAS_TITLEBAR(wwin)) height += 18;
435
if (HAS_RESIZEBAR(wwin)) height += 8;
437
if (HAS_BORDER(wwin)) {
442
swidth = usableArea.x2-usableArea.x1;
445
/* this was based on fvwm2's smart placement */
447
while (((test_y + height) < (usableArea.y2 - usableArea.y1)) && !loc_ok) {
450
while (((test_x + width) < swidth) && (!loc_ok)) {
453
test_window = scr->focused_window;
455
while ((test_window != NULL) && (loc_ok == True)) {
457
if (test_window->frame->core->stacking->window_level
458
< WMNormalLevel && tryCount > 0) {
459
test_window = test_window->next;
463
tw = test_window->client.width;
464
if (test_window->flags.shaded)
465
th = test_window->frame->top_width;
467
th = test_window->client.height + extra_height;
469
tw = test_window->frame->core->width;
470
th = test_window->frame->core->height;
472
tx = test_window->frame_x;
473
ty = test_window->frame_y;
475
if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
476
(ty < (test_y + height)) && ((ty + th) > test_y) &&
477
(test_window->flags.mapped ||
478
(test_window->flags.shaded &&
479
test_window->frame->workspace==scr->current_workspace &&
480
!(test_window->flags.miniaturized ||
481
test_window->flags.hidden)))) {
485
test_window = test_window->next;
488
test_window = scr->focused_window;
490
while ((test_window != NULL) && (loc_ok == True)) {
492
if (test_window->frame->core->stacking->window_level
493
< WMNormalLevel && tryCount > 0) {
494
test_window = test_window->prev;
498
tw = test_window->client.width;
499
if (test_window->flags.shaded)
500
th = test_window->frame->top_width;
502
th = test_window->client.height + extra_height;
504
tw = test_window->frame->core->width;
505
th = test_window->frame->core->height;
507
tx = test_window->frame_x;
508
ty = test_window->frame_y;
510
if ((tx < (test_x + width)) && ((tx + tw) > test_x) &&
511
(ty < (test_y + height)) && ((ty + th) > test_y) &&
512
(test_window->flags.mapped ||
513
(test_window->flags.shaded &&
514
test_window->frame->workspace==scr->current_workspace &&
515
!(test_window->flags.miniaturized ||
516
test_window->flags.hidden)))) {
520
test_window = test_window->prev;
522
if (loc_ok == True) {
527
test_x += PLACETEST_HSTEP;
529
test_y += PLACETEST_VSTEP;
537
cascadeWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
538
unsigned int width, unsigned int height, int h,
542
height += wwin->frame->top_width + wwin->frame->bottom_width;
544
if (HAS_TITLEBAR(wwin)) height += 18;
545
if (HAS_RESIZEBAR(wwin)) height += 8;
547
if (HAS_BORDER(wwin)) {
552
*x_ret = h * scr->cascade_index + X_ORIGIN;
553
*y_ret = h * scr->cascade_index + Y_ORIGIN;
555
if (width + *x_ret > usableArea.x2 || height + *y_ret > usableArea.y2) {
556
scr->cascade_index = 0;
564
randomPlaceWindow(WScreen *scr, WWindow *wwin, int *x_ret, int *y_ret,
565
unsigned int width, unsigned int height,
571
height += wwin->frame->top_width + wwin->frame->bottom_width;
573
if (HAS_TITLEBAR(wwin)) height += 18;
574
if (HAS_RESIZEBAR(wwin)) height += 8;
576
if (HAS_BORDER(wwin)) {
581
w = ((usableArea.x2-X_ORIGIN) - width);
582
h = ((usableArea.y2-Y_ORIGIN) - height);
585
*x_ret = X_ORIGIN + rand()%w;
586
*y_ret = Y_ORIGIN + rand()%h;
592
PlaceWindow(WWindow *wwin, int *x_ret, int *y_ret,
593
unsigned width, unsigned height)
595
WScreen *scr = wwin->screen_ptr;
596
int h = WMFontHeight(scr->title_font) + (wPreferences.window_title_clearance + TITLEBAR_EXTEND_SPACE) * 2;
597
WArea usableArea = wGetUsableAreaForHead(scr,
598
wGetHeadForPointerLocation(scr),
601
switch (wPreferences.window_placement) {
603
InteractivePlaceWindow(wwin, x_ret, y_ret, width, height);
607
smartPlaceWindow(wwin, x_ret, y_ret, width, height, usableArea);
611
if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 0,
614
} else if (autoPlaceWindow(wwin, x_ret, y_ret, width, height, 1,
618
/* there isn't a break here, because if we fail, it should fall
619
through to cascade placement, as people who want tiling want
620
automagicness aren't going to want to place their window */
623
if (wPreferences.window_placement == WPM_AUTO)
624
scr->cascade_index++;
626
cascadeWindow(scr, wwin, x_ret, y_ret, width, height, h, usableArea);
628
if (wPreferences.window_placement == WPM_CASCADE)
629
scr->cascade_index++;
633
randomPlaceWindow(scr, wwin, x_ret, y_ret, width, height, usableArea);
638
puts("Invalid window placement!!!");
645
* clip to usableArea instead of full screen
646
* this will also take dock/clip etc.. into account
647
* aswell as being xinerama friendly
649
if (*x_ret + width > usableArea.x2)
650
*x_ret = usableArea.x2 - width;
651
if (*x_ret < usableArea.x1)
652
*x_ret = usableArea.x1;
654
if (*y_ret + height > usableArea.y2)
655
*y_ret = usableArea.y2 - height;
656
if (*y_ret < usableArea.y1)
657
*y_ret = usableArea.y1;