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"
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.
62 iconPosition(WCoreWindow
* wcore
, int sx1
, int sy1
, int sx2
, int sy2
, int workspace
, int *retX
, int *retY
)
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
;
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
;
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
;
96 * Check if it is inside the screen.
99 if (*retX
< sx1
- wPreferences
.icon_size
)
101 else if (*retX
> sx2
)
104 if (*retY
< sy1
- wPreferences
.icon_size
)
106 else if (*retY
> sy2
)
113 void PlaceIcon(WScreen
* scr
, int *x_ret
, int *y_ret
, int head
)
115 int pf
; /* primary axis */
116 int sf
; /* secondary axis */
122 int sx1
, sx2
, sy1
, sy2
; /* screen boundary */
127 int isize
= wPreferences
.icon_size
;
130 WArea area
= wGetUsableAreaForHead(scr
, head
, NULL
, False
);
132 /* Find out screen boundaries. */
134 /* Allows each head to have miniwindows */
145 if (scr
->dock
->on_right_side
)
146 sx2
-= isize
+ DOCK_EXTRA_SPACE
;
148 sx1
+= isize
+ DOCK_EXTRA_SPACE
;
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
) {
165 if (wPreferences
.icon_yard
& IY_RIGHT
) {
172 if (wPreferences
.icon_yard
& IY_TOP
) {
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
) {
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 */
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
;
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
);
229 x
= xo
+ xs
* (pi
* isize
);
230 y
= yo
+ ys
* (si
* isize
);
232 if (!map
[INDEX(x
/ isize
, y
/ isize
)]) {
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
)
266 else if (p2
+ l2
< p1
+ l1
)
269 isect
= p1
+ l1
- p2
;
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
)
287 WWindow
*test_window
;
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
) {
299 tw
= test_window
->client
.width
;
300 if (test_window
->flags
.shaded
)
301 th
= test_window
->frame
->top_width
;
303 th
= test_window
->client
.height
+ extra_height
;
305 tw
= test_window
->frame
->core
->width
;
306 th
= test_window
->frame
->core
->height
;
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
);
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
;
327 int min_isect
, min_isect_x
, min_isect_y
;
331 height
+= wwin
->frame
->top_width
+ wwin
->frame
->bottom_width
;
333 if (HAS_TITLEBAR(wwin
))
335 if (HAS_RESIZEBAR(wwin
))
338 if (HAS_BORDER(wwin
)) {
346 min_isect_y
= test_y
;
348 while (((test_y
+ height
) < usableArea
.y2
)) {
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
;
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
;
400 WWindow
*test_window
;
403 height
+= wwin
->frame
->top_width
+ wwin
->frame
->bottom_width
;
405 if (HAS_TITLEBAR(wwin
))
407 if (HAS_RESIZEBAR(wwin
))
410 if (HAS_BORDER(wwin
)) {
415 swidth
= usableArea
.x2
- usableArea
.x1
;
418 /* this was based on fvwm2's smart placement */
420 while (((test_y
+ height
) < (usableArea
.y2
- usableArea
.y1
)) && !loc_ok
) {
423 while (((test_x
+ width
) < swidth
) && (!loc_ok
)) {
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
;
436 tw
= test_window
->client
.width
;
437 if (test_window
->flags
.shaded
)
438 th
= test_window
->frame
->top_width
;
440 th
= test_window
->client
.height
+ extra_height
;
442 tw
= test_window
->frame
->core
->width
;
443 th
= test_window
->frame
->core
->height
;
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
)))) {
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
;
470 tw
= test_window
->client
.width
;
471 if (test_window
->flags
.shaded
)
472 th
= test_window
->frame
->top_width
;
474 th
= test_window
->client
.height
+ extra_height
;
476 tw
= test_window
->frame
->core
->width
;
477 th
= test_window
->frame
->core
->height
;
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
)))) {
491 test_window
= test_window
->prev
;
493 if (loc_ok
== True
) {
498 test_x
+= PLACETEST_HSTEP
;
500 test_y
+= PLACETEST_VSTEP
;
507 cascadeWindow(WScreen
* scr
, WWindow
* wwin
, int *x_ret
, int *y_ret
,
508 unsigned int width
, unsigned int height
, int h
, WArea usableArea
)
511 height
+= wwin
->frame
->top_width
+ wwin
->frame
->bottom_width
;
513 if (HAS_TITLEBAR(wwin
))
515 if (HAS_RESIZEBAR(wwin
))
518 if (HAS_BORDER(wwin
)) {
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;
534 randomPlaceWindow(WScreen
* scr
, WWindow
* wwin
, int *x_ret
, int *y_ret
,
535 unsigned int width
, unsigned int height
, WArea usableArea
)
540 height
+= wwin
->frame
->top_width
+ wwin
->frame
->bottom_width
;
542 if (HAS_TITLEBAR(wwin
))
544 if (HAS_RESIZEBAR(wwin
))
547 if (HAS_BORDER(wwin
)) {
552 w
= ((usableArea
.x2
- X_ORIGIN
) - width
);
553 h
= ((usableArea
.y2
- Y_ORIGIN
) - height
);
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
),
570 switch (wPreferences
.window_placement
) {
572 InteractivePlaceWindow(wwin
, x_ret
, y_ret
, width
, height
);
576 smartPlaceWindow(wwin
, x_ret
, y_ret
, width
, height
, usableArea
);
580 if (autoPlaceWindow(wwin
, x_ret
, y_ret
, width
, height
, 0, usableArea
)) {
582 } else if (autoPlaceWindow(wwin
, x_ret
, y_ret
, width
, height
, 1, usableArea
)) {
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 */
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
++;
600 randomPlaceWindow(scr
, wwin
, x_ret
, y_ret
, width
, height
, usableArea
);
605 puts("Invalid window placement!!!");
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
;