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(scr) WMAX(usableArea.x1,\
49 wPreferences.window_place_origin.x)
51 #define Y_ORIGIN(scr) 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 //WScreen *scr = wwin->screen_ptr;
352 int test_x
= 0, test_y
= Y_ORIGIN(scr
);
353 int from_x
, to_x
, from_y
, to_y
;
355 int min_isect
, min_isect_x
, min_isect_y
;
359 height
+= wwin
->frame
->top_width
+ wwin
->frame
->bottom_width
;
361 if (HAS_TITLEBAR(wwin
)) height
+= 18;
362 if (HAS_RESIZEBAR(wwin
)) height
+= 8;
364 if (HAS_BORDER(wwin
)) {
372 min_isect_y
= test_y
;
374 while (((test_y
+ height
) < usableArea
.y2
)) {
376 while ((test_x
+ width
) < usableArea
.x2
) {
377 sum_isect
= calcSumOfCoveredAreas(wwin
, test_x
, test_y
,
380 if (sum_isect
< min_isect
) {
381 min_isect
= sum_isect
;
382 min_isect_x
= test_x
;
383 min_isect_y
= test_y
;
386 test_x
+= PLACETEST_HSTEP
;
388 test_y
+= PLACETEST_VSTEP
;
391 from_x
= min_isect_x
- PLACETEST_HSTEP
+ 1;
392 from_x
= WMAX(from_x
, X_ORIGIN(scr
));
393 to_x
= min_isect_x
+ PLACETEST_HSTEP
;
394 if (to_x
+ width
> usableArea
.x2
)
395 to_x
= usableArea
.x2
- width
;
397 from_y
= min_isect_y
- PLACETEST_VSTEP
+ 1;
398 from_y
= WMAX(from_y
, Y_ORIGIN(scr
));
399 to_y
= min_isect_y
+ PLACETEST_VSTEP
;
400 if (to_y
+ height
> usableArea
.y2
)
401 to_y
= usableArea
.y2
- height
;
403 for (test_x
= from_x
; test_x
< to_x
; test_x
++) {
404 for (test_y
= from_y
; test_y
< to_y
; test_y
++) {
405 sum_isect
= calcSumOfCoveredAreas(wwin
, test_x
, test_y
,
408 if (sum_isect
< min_isect
) {
409 min_isect
= sum_isect
;
410 min_isect_x
= test_x
;
411 min_isect_y
= test_y
;
416 *x_ret
= min_isect_x
;
417 *y_ret
= min_isect_y
;
422 autoPlaceWindow(WWindow
*wwin
, int *x_ret
, int *y_ret
,
423 unsigned int width
, unsigned int height
, int tryCount
,
426 WScreen
*scr
= wwin
->screen_ptr
;
427 int test_x
= 0, test_y
= Y_ORIGIN(scr
);
428 int loc_ok
= False
, tw
,tx
,ty
,th
;
430 WWindow
*test_window
;
433 height
+= wwin
->frame
->top_width
+ wwin
->frame
->bottom_width
;
435 if (HAS_TITLEBAR(wwin
)) height
+= 18;
436 if (HAS_RESIZEBAR(wwin
)) height
+= 8;
438 if (HAS_BORDER(wwin
)) {
443 swidth
= usableArea
.x2
-usableArea
.x1
;
446 /* this was based on fvwm2's smart placement */
448 while (((test_y
+ height
) < (usableArea
.y2
- usableArea
.y1
)) && !loc_ok
) {
451 while (((test_x
+ width
) < swidth
) && (!loc_ok
)) {
454 test_window
= scr
->focused_window
;
456 while ((test_window
!= NULL
) && (loc_ok
== True
)) {
458 if (test_window
->frame
->core
->stacking
->window_level
459 < WMNormalLevel
&& tryCount
> 0) {
460 test_window
= test_window
->next
;
464 tw
= test_window
->client
.width
;
465 if (test_window
->flags
.shaded
)
466 th
= test_window
->frame
->top_width
;
468 th
= test_window
->client
.height
+ extra_height
;
470 tw
= test_window
->frame
->core
->width
;
471 th
= test_window
->frame
->core
->height
;
473 tx
= test_window
->frame_x
;
474 ty
= test_window
->frame_y
;
476 if ((tx
< (test_x
+ width
)) && ((tx
+ tw
) > test_x
) &&
477 (ty
< (test_y
+ height
)) && ((ty
+ th
) > test_y
) &&
478 (test_window
->flags
.mapped
||
479 (test_window
->flags
.shaded
&&
480 test_window
->frame
->workspace
==scr
->current_workspace
&&
481 !(test_window
->flags
.miniaturized
||
482 test_window
->flags
.hidden
)))) {
486 test_window
= test_window
->next
;
489 test_window
= scr
->focused_window
;
491 while ((test_window
!= NULL
) && (loc_ok
== True
)) {
493 if (test_window
->frame
->core
->stacking
->window_level
494 < WMNormalLevel
&& tryCount
> 0) {
495 test_window
= test_window
->prev
;
499 tw
= test_window
->client
.width
;
500 if (test_window
->flags
.shaded
)
501 th
= test_window
->frame
->top_width
;
503 th
= test_window
->client
.height
+ extra_height
;
505 tw
= test_window
->frame
->core
->width
;
506 th
= test_window
->frame
->core
->height
;
508 tx
= test_window
->frame_x
;
509 ty
= test_window
->frame_y
;
511 if ((tx
< (test_x
+ width
)) && ((tx
+ tw
) > test_x
) &&
512 (ty
< (test_y
+ height
)) && ((ty
+ th
) > test_y
) &&
513 (test_window
->flags
.mapped
||
514 (test_window
->flags
.shaded
&&
515 test_window
->frame
->workspace
==scr
->current_workspace
&&
516 !(test_window
->flags
.miniaturized
||
517 test_window
->flags
.hidden
)))) {
521 test_window
= test_window
->prev
;
523 if (loc_ok
== True
) {
528 test_x
+= PLACETEST_HSTEP
;
530 test_y
+= PLACETEST_VSTEP
;
538 cascadeWindow(WScreen
*scr
, WWindow
*wwin
, int *x_ret
, int *y_ret
,
539 unsigned int width
, unsigned int height
, int h
,
543 height
+= wwin
->frame
->top_width
+ wwin
->frame
->bottom_width
;
545 if (HAS_TITLEBAR(wwin
)) height
+= 18;
546 if (HAS_RESIZEBAR(wwin
)) height
+= 8;
548 if (HAS_BORDER(wwin
)) {
553 *x_ret
= h
* scr
->cascade_index
+ X_ORIGIN(scr
);
554 *y_ret
= h
* scr
->cascade_index
+ Y_ORIGIN(scr
);
556 if (width
+ *x_ret
> usableArea
.x2
|| height
+ *y_ret
> usableArea
.y2
) {
557 scr
->cascade_index
= 0;
558 *x_ret
= X_ORIGIN(scr
);
559 *y_ret
= Y_ORIGIN(scr
);
565 randomPlaceWindow(WScreen
*scr
, WWindow
*wwin
, int *x_ret
, int *y_ret
,
566 unsigned int width
, unsigned int height
,
572 height
+= wwin
->frame
->top_width
+ wwin
->frame
->bottom_width
;
574 if (HAS_TITLEBAR(wwin
)) height
+= 18;
575 if (HAS_RESIZEBAR(wwin
)) height
+= 8;
577 if (HAS_BORDER(wwin
)) {
582 w
= ((usableArea
.x2
-X_ORIGIN(scr
)) - width
);
583 h
= ((usableArea
.y2
-Y_ORIGIN(scr
)) - height
);
586 *x_ret
= X_ORIGIN(scr
) + rand()%w
;
587 *y_ret
= Y_ORIGIN(scr
) + rand()%h
;
593 PlaceWindow(WWindow
*wwin
, int *x_ret
, int *y_ret
,
594 unsigned width
, unsigned height
)
596 WScreen
*scr
= wwin
->screen_ptr
;
597 int h
= WMFontHeight(scr
->title_font
) + (wPreferences
.window_title_clearance
+ TITLEBAR_EXTEND_SPACE
) * 2;
598 WArea usableArea
= wGetUsableAreaForHead(scr
,
599 wGetHeadForPointerLocation(scr
),
602 switch (wPreferences
.window_placement
) {
604 InteractivePlaceWindow(wwin
, x_ret
, y_ret
, width
, height
);
608 smartPlaceWindow(wwin
, x_ret
, y_ret
, width
, height
, usableArea
);
612 if (autoPlaceWindow(wwin
, x_ret
, y_ret
, width
, height
, 0,
615 } else if (autoPlaceWindow(wwin
, x_ret
, y_ret
, width
, height
, 1,
619 /* there isn't a break here, because if we fail, it should fall
620 through to cascade placement, as people who want tiling want
621 automagicness aren't going to want to place their window */
624 if (wPreferences
.window_placement
== WPM_AUTO
)
625 scr
->cascade_index
++;
627 cascadeWindow(scr
, wwin
, x_ret
, y_ret
, width
, height
, h
, usableArea
);
629 if (wPreferences
.window_placement
== WPM_CASCADE
)
630 scr
->cascade_index
++;
634 randomPlaceWindow(scr
, wwin
, x_ret
, y_ret
, width
, height
, usableArea
);
639 puts("Invalid window placement!!!");
646 * clip to usableArea instead of full screen
647 * this will also take dock/clip etc.. into account
648 * aswell as being xinerama friendly
650 if (*x_ret
+ width
> usableArea
.x2
)
651 *x_ret
= usableArea
.x2
- width
;
652 if (*x_ret
< usableArea
.x1
)
653 *x_ret
= usableArea
.x1
;
655 if (*y_ret
+ height
> usableArea
.y2
)
656 *y_ret
= usableArea
.y2
- height
;
657 if (*y_ret
< usableArea
.y1
)
658 *y_ret
= usableArea
.y1
;