2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 /* ---------------------------- included header files ---------------------- */
23 #include "libs/fvwmlib.h"
24 #include "libs/FScreen.h"
25 #include "libs/Grab.h"
26 #include "libs/Parse.h"
27 #include "libs/XResource.h"
30 #include "execcontext.h"
35 #include "placement.h"
39 #include "move_resize.h"
44 #include "add_window.h"
46 /* ---------------------------- local definitions -------------------------- */
48 #define MAX_NUM_PLACEMENT_ALGOS 31
49 #define CP_GET_NEXT_STEP 5
51 /* ---------------------------- local macros ------------------------------- */
54 #define MIN(A,B) ((A)<(B)? (A):(B))
57 #define MAX(A,B) ((A)>(B)? (A):(B))
60 #define NORMAL_PLACEMENT_PENALTY(p) (p->normal)
61 #define ONTOP_PLACEMENT_PENALTY(p) (p->ontop)
62 #define ICON_PLACEMENT_PENALTY(p) (p->icon)
63 #define STICKY_PLACEMENT_PENALTY(p) (p->sticky)
64 #define BELOW_PLACEMENT_PENALTY(p) (p->below)
65 #define EWMH_STRUT_PLACEMENT_PENALTY(p) (p->strut)
67 #define PERCENTAGE_99_PENALTY(p) (p->p99)
68 #define PERCENTAGE_95_PENALTY(p) (p->p95)
69 #define PERCENTAGE_85_PENALTY(p) (p->p85)
70 #define PERCENTAGE_75_PENALTY(p) (p->p75)
72 /* ---------------------------- imports ------------------------------------ */
74 /* ---------------------------- included code files ------------------------ */
76 /* ---------------------------- local types -------------------------------- */
87 PR_POS_USPOS_OVERRIDE_SOS
92 PR_SCREEN_CURRENT
= 0,
94 PR_SCREEN_X_RESOURCE_FVWMSCREEN
,
95 PR_SCREEN_IGNORE_CAPTURE
102 PR_PAGE_X_RESOURCE_PAGE
,
103 PR_PAGE_IGNORE_CAPTURE
,
104 PR_PAGE_IGNORE_INVALID
,
112 PR_DESK_X_RESOURCE_DESK
,
113 PR_DESK_X_RESOURCE_PAGE
,
116 PR_DESK_WINDOW_GROUP_LEADER
,
117 PR_DESK_WINDOW_GROUP_MEMBER
,
119 PR_DESK_XPROP_XA_WM_DESKTOP
126 preason_pos_t reason
;
130 char *pl_position_string
;
131 unsigned do_not_manual_icon_placement
: 1;
132 unsigned do_adjust_off_screen
: 1;
133 unsigned do_adjust_off_page
: 1;
134 unsigned is_pl_position_string_invalid
: 1;
135 unsigned has_tile_failed
: 1;
136 unsigned has_manual_failed
: 1;
137 unsigned has_placement_failed
: 1;
141 preason_screen_t reason
;
144 unsigned was_modified_by_ewmh_workingarea
: 1;
148 preason_page_t reason
;
151 unsigned do_switch_page
: 1;
152 unsigned do_honor_starts_on_page
: 1;
153 unsigned do_ignore_starts_on_page
: 1;
157 preason_desk_t reason
;
158 preason_desk_t sod_reason
;
160 unsigned do_switch_desk
: 1;
174 unsigned do_forbid_manual_placement
: 1;
175 unsigned do_honor_starts_on_page
: 1;
176 unsigned do_honor_starts_on_screen
: 1;
177 unsigned do_not_use_wm_placement
: 1;
180 typedef float pl_penalty_t
;
193 /* If this funtion pointer is not NULL, use this function to return
194 * the desired position in a single call */
195 pl_penalty_t (*get_pos_simple
)(
196 position
*ret_p
, struct pl_ret_t
*ret
,
197 const struct pl_arg_t
*arg
);
198 /* otherwise use these three in a loop */
199 pl_loop_rc_t (*get_first_pos
)(
200 position
*ret_p
, struct pl_ret_t
*ret
,
201 const struct pl_arg_t
*arg
);
202 pl_loop_rc_t (*get_next_pos
)(
203 position
*ret_p
, struct pl_ret_t
*ret
,
204 const struct pl_arg_t
*arg
, position hint_p
);
205 pl_penalty_t (*get_pos_penalty
)(
206 position
*ret_hint_p
, struct pl_ret_t
*ret
,
207 const struct pl_arg_t
*arg
);
210 typedef struct pl_scratch_t
212 const pl_penalty_struct
*pp
;
213 const pl_percent_penalty_struct
*ppp
;
216 typedef struct pl_arg_t
218 const pl_algo_t
*algo
;
219 const exec_context_t
*exc
;
220 const window_style
*style
;
222 FvwmWindow
*place_fw
;
223 pl_scratch_t
*scratch
;
232 unsigned use_percent
: 1;
233 unsigned use_ewmh_dynamic_working_areapercent
: 1;
234 unsigned do_honor_starts_on_page
: 1;
238 typedef struct pl_ret_t
241 pl_penalty_t best_penalty
;
244 unsigned do_resize_too
: 1;
248 /* ---------------------------- forward declarations ----------------------- */
250 static pl_loop_rc_t
__pl_minoverlap_get_first_pos(
251 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
);
252 static pl_loop_rc_t
__pl_minoverlap_get_next_pos(
253 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
,
255 static pl_penalty_t
__pl_minoverlap_get_pos_penalty(
256 position
*ret_hint_p
, struct pl_ret_t
*ret
, const pl_arg_t
*arg
);
258 static pl_penalty_t
__pl_smart_get_pos_penalty(
259 position
*ret_hint_p
, struct pl_ret_t
*ret
, const pl_arg_t
*arg
);
261 static pl_penalty_t
__pl_position_get_pos_simple(
262 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
);
264 static pl_penalty_t
__pl_cascade_get_pos_simple(
265 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
);
267 static pl_penalty_t
__pl_manual_get_pos_simple(
268 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
);
270 /* ---------------------------- local variables ---------------------------- */
272 const pl_algo_t minoverlap_placement_algo
=
275 __pl_minoverlap_get_first_pos
,
276 __pl_minoverlap_get_next_pos
,
277 __pl_minoverlap_get_pos_penalty
280 const pl_algo_t smart_placement_algo
=
283 __pl_minoverlap_get_first_pos
,
284 __pl_minoverlap_get_next_pos
,
285 __pl_smart_get_pos_penalty
288 const pl_algo_t position_placement_algo
=
290 __pl_position_get_pos_simple
293 const pl_algo_t cascade_placement_algo
=
295 __pl_cascade_get_pos_simple
298 const pl_algo_t manual_placement_algo
=
300 __pl_manual_get_pos_simple
303 /* ---------------------------- exported variables (globals) --------------- */
305 const pl_penalty_struct default_pl_penalty
=
308 PLACEMENT_AVOID_ONTOP
,
309 PLACEMENT_AVOID_ICON
,
310 PLACEMENT_AVOID_STICKY
,
311 PLACEMENT_AVOID_BELOW
,
312 PLACEMENT_AVOID_EWMH_STRUT
315 const pl_percent_penalty_struct default_pl_percent_penalty
=
317 PLACEMENT_AVOID_COVER_99
,
318 PLACEMENT_AVOID_COVER_95
,
319 PLACEMENT_AVOID_COVER_85
,
320 PLACEMENT_AVOID_COVER_75
323 /* ---------------------------- local functions (PositionPlacement) -------- */
325 static pl_penalty_t
__pl_position_get_pos_simple(
326 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
)
334 is_under_mouse
= False
;
335 spos
= SGET_PLACEMENT_POSITION_STRING(*arg
->style
);
336 if (spos
== NULL
|| *spos
== 0)
338 spos
= DEFAULT_PLACEMENT_POSITION_STRING
;
341 else if (StrEquals(spos
, "Center"))
343 spos
= DEFAULT_PLACEMENT_POS_CENTER_STRING
;
346 else if (StrEquals(spos
, "UnderMouse"))
348 spos
= DEFAULT_PLACEMENT_POS_MOUSE_STRING
;
350 is_under_mouse
= True
;
356 arg
->reason
->pos
.pl_position_string
= spos
;
357 for (n
= -1; i
< 2 && n
< 2; i
++)
362 n
= GetMoveArguments(
363 &spos
, arg
->place_g
.width
, arg
->place_g
.height
,
364 &ret_p
->x
, &ret_p
->y
, NULL
, &fPointer
, False
);
365 spos
= DEFAULT_PLACEMENT_POSITION_STRING
;
368 arg
->reason
->pos
.is_pl_position_string_invalid
= 1;
378 /* TA: 20090218: Try and keep the window on-screen if we
381 if (ret_p
->x
+ arg
->place_fw
->g
.frame
.width
> arg
->screen_g
.x
382 + arg
->screen_g
.width
)
384 ret_p
->x
= (arg
->screen_g
.x
+ arg
->screen_g
.width
) -
385 arg
->place_fw
->g
.frame
.width
;
387 if (ret_p
->y
+ arg
->place_fw
->g
.frame
.height
> arg
->screen_g
.y
388 + arg
->screen_g
.height
)
390 ret_p
->y
= (arg
->screen_g
.y
+ arg
->screen_g
.height
) -
391 arg
->place_fw
->g
.frame
.height
;
394 /* Don't let the upper left corner be offscreen. */
395 if (ret_p
->x
< arg
->screen_g
.x
)
397 ret_p
->x
= arg
->screen_g
.x
;
399 if (ret_p
->y
< arg
->screen_g
.y
)
401 ret_p
->y
= arg
->screen_g
.y
;
403 if (arg
->flags
.do_honor_starts_on_page
)
405 ret_p
->x
-= arg
->pdelta_p
.x
;
406 ret_p
->y
-= arg
->pdelta_p
.y
;
412 /* ---------------------------- local functions (CascadePlacement)---------- */
414 static pl_penalty_t
__pl_cascade_get_pos_simple(
415 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
)
421 t
= (Scr
.cascade_window
!= NULL
) ? Scr
.cascade_window
: arg
->place_fw
;
422 w
= t
->title_thickness
;
425 w
= t
->boundary_width
;
429 w
= PLACEMENT_FALLBACK_CASCADE_STEP
;
432 if (Scr
.cascade_window
!= NULL
)
437 switch (GET_TITLE_DIR(t
))
451 Scr
.cascade_window
= arg
->place_fw
;
452 if (Scr
.cascade_x
> arg
->screen_g
.width
/ 2)
454 Scr
.cascade_x
= arg
->place_fw
->title_thickness
;
456 if (Scr
.cascade_y
> arg
->screen_g
.height
/ 2)
458 Scr
.cascade_y
= 2 * arg
->place_fw
->title_thickness
;
460 ret_p
->x
= Scr
.cascade_x
+ arg
->page_p1
.x
;
461 ret_p
->y
= Scr
.cascade_y
+ arg
->page_p1
.y
;
462 /* try to keep the window on the screen */
463 get_window_borders(arg
->place_fw
, &b
);
464 if (ret_p
->x
+ arg
->place_g
.width
>= arg
->page_p2
.x
)
466 ret_p
->x
= arg
->page_p2
.x
- arg
->place_g
.width
-
468 Scr
.cascade_x
= arg
->place_fw
->title_thickness
;
469 switch (GET_TITLE_DIR(t
))
473 Scr
.cascade_x
+= arg
->place_fw
->title_thickness
;
478 if (ret_p
->y
+ arg
->place_g
.height
>= arg
->page_p2
.y
)
480 ret_p
->y
= arg
->page_p2
.y
- arg
->place_g
.height
-
482 Scr
.cascade_y
= arg
->place_fw
->title_thickness
;
483 switch (GET_TITLE_DIR(t
))
487 Scr
.cascade_y
+= arg
->place_fw
->title_thickness
;
493 /* the left and top sides are more important in huge windows */
494 if (ret_p
->x
< arg
->page_p1
.x
)
496 ret_p
->x
= arg
->page_p1
.x
;
498 if (ret_p
->y
< arg
->page_p1
.y
)
500 ret_p
->y
= arg
->page_p1
.y
;
506 /* ---------------------------- local functions (ManualPlacement)----------- */
508 static pl_penalty_t
__pl_manual_get_pos_simple(
509 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
)
513 if (GrabEm(CRS_POSITION
, GRAB_NORMAL
))
520 /* Grabbed the pointer - continue */
524 dpy
, FW_W(arg
->place_fw
), &JunkRoot
, &JunkX
,
525 &JunkY
, (unsigned int*)&DragWidth
,
526 (unsigned int*)&DragHeight
,
527 (unsigned int*)&JunkBW
,
528 (unsigned int*)&JunkDepth
) == 0)
530 MyXUngrabServer(dpy
);
531 UngrabEm(GRAB_NORMAL
);
535 SET_PLACED_BY_FVWM(arg
->place_fw
, 0);
536 MyXGrabKeyboard(dpy
);
537 DragWidth
= arg
->place_g
.width
;
538 DragHeight
= arg
->place_g
.height
;
540 if (Scr
.SizeWindow
!= None
)
542 XMapRaised(dpy
, Scr
.SizeWindow
);
545 NULL
, FSCREEN_GLOBAL
, &mx
, &my
, NULL
, NULL
);
547 arg
->exc
, mx
, my
, DragWidth
, DragHeight
, &ret_p
->x
,
548 &ret_p
->y
, False
, CRS_POSITION
))
550 ret
->flags
.do_resize_too
= 1;
552 if (Scr
.SizeWindow
!= None
)
554 XUnmapWindow(dpy
, Scr
.SizeWindow
);
556 MyXUngrabKeyboard(dpy
);
557 MyXUngrabServer(dpy
);
558 UngrabEm(GRAB_NORMAL
);
562 /* couldn't grab the pointer - better do something */
566 arg
->reason
->pos
.has_manual_failed
= 1;
568 if (arg
->flags
.do_honor_starts_on_page
)
570 ret_p
->x
-= arg
->pdelta_p
.x
;
571 ret_p
->y
-= arg
->pdelta_p
.y
;
577 /* ---------------------------- local functions (MinoverlapPlacement) ------ */
579 /* MinoverlapPlacement by Anthony Martin <amartin@engr.csulb.edu>
580 * This algorithm places a new window such that there is a minimum amount of
581 * interference with other windows. If it can place a window without any
582 * interference, fine. Otherwise, it places it so that the area of of
583 * interference between the new window and the other windows is minimized */
585 static int __pl_minoverlap_get_next_x(const pl_arg_t
*arg
)
587 FvwmWindow
*other_fw
;
601 if (arg
->flags
.use_percent
== 1)
607 start
= CP_GET_NEXT_STEP
;
610 /* Test window at far right of screen */
611 xnew
= arg
->page_p2
.x
;
612 xtest
= arg
->page_p2
.x
- arg
->place_g
.width
;
617 /* test the borders of the working area */
618 xtest
= arg
->page_p1
.x
+ Scr
.Desktops
->ewmh_working_area
.x
;
621 xnew
= MIN(xnew
, xtest
);
623 xtest
= arg
->page_p1
.x
+
624 (Scr
.Desktops
->ewmh_working_area
.x
+
625 Scr
.Desktops
->ewmh_working_area
.width
) -
629 xnew
= MIN(xnew
, xtest
);
631 /* Test the values of the right edges of every window */
633 other_fw
= Scr
.FvwmRoot
.next
; other_fw
!= NULL
;
634 other_fw
= other_fw
->next
)
637 other_fw
== arg
->place_fw
||
638 (other_fw
->Desk
!= arg
->place_fw
->Desk
&&
639 !IS_STICKY_ACROSS_DESKS(other_fw
)) ||
640 IS_EWMH_DESKTOP(FW_W(other_fw
)))
644 if (IS_STICKY_ACROSS_PAGES(other_fw
))
646 stickyx
= arg
->pdelta_p
.x
;
647 stickyy
= arg
->pdelta_p
.y
;
654 if (IS_ICONIFIED(other_fw
))
656 rc
= get_visible_icon_geometry(other_fw
, &g
);
657 if (rc
== True
&& y
< g
.y
+ g
.height
- stickyy
&&
658 g
.y
- stickyy
< arg
->place_g
.height
+ y
)
660 win_left
= arg
->page_p1
.x
+ g
.x
- stickyx
-
662 for (i
= start
; i
<= CP_GET_NEXT_STEP
; i
++)
664 xtest
= win_left
+ g
.width
*
665 (CP_GET_NEXT_STEP
- i
) /
669 xnew
= MIN(xnew
, xtest
);
672 win_left
= arg
->page_p1
.x
+ g
.x
- stickyx
;
673 for (i
= start
; i
<= CP_GET_NEXT_STEP
; i
++)
675 xtest
= (win_left
) + g
.width
* i
/
679 xnew
= MIN(xnew
, xtest
);
685 y
< other_fw
->g
.frame
.height
+ other_fw
->g
.frame
.y
-
687 other_fw
->g
.frame
.y
- stickyy
<
688 arg
->place_g
.height
+ y
&&
689 arg
->page_p1
.x
< other_fw
->g
.frame
.width
+
690 other_fw
->g
.frame
.x
- stickyx
&&
691 other_fw
->g
.frame
.x
- stickyx
< arg
->page_p2
.x
)
694 other_fw
->g
.frame
.x
- stickyx
-
696 for (i
= start
; i
<= CP_GET_NEXT_STEP
; i
++)
698 xtest
= win_left
+ other_fw
->g
.frame
.width
*
699 (CP_GET_NEXT_STEP
- i
) /
703 xnew
= MIN(xnew
, xtest
);
706 win_left
= other_fw
->g
.frame
.x
- stickyx
;
707 for (i
= start
; i
<= CP_GET_NEXT_STEP
; i
++)
709 xtest
= win_left
+ other_fw
->g
.frame
.width
*
710 i
/ CP_GET_NEXT_STEP
;
713 xnew
= MIN(xnew
, xtest
);
722 static int __pl_minoverlap_get_next_y(const pl_arg_t
*arg
)
724 FvwmWindow
*other_fw
;
735 if (arg
->flags
.use_percent
== 1)
741 start
= CP_GET_NEXT_STEP
;
744 /* Test window at far bottom of screen */
745 ynew
= arg
->page_p2
.y
;
746 ytest
= arg
->page_p2
.y
- arg
->place_g
.height
;
751 /* test the borders of the working area */
752 ytest
= arg
->page_p1
.y
+ Scr
.Desktops
->ewmh_working_area
.y
;
755 ynew
= MIN(ynew
, ytest
);
757 ytest
= arg
->screen_g
.y
+
758 (Scr
.Desktops
->ewmh_working_area
.y
+
759 Scr
.Desktops
->ewmh_working_area
.height
) -
763 ynew
= MIN(ynew
, ytest
);
765 /* Test the values of the bottom edge of every window */
767 other_fw
= Scr
.FvwmRoot
.next
; other_fw
!= NULL
;
768 other_fw
= other_fw
->next
)
771 other_fw
== arg
->place_fw
||
773 other_fw
->Desk
!= arg
->place_fw
->Desk
&&
774 !IS_STICKY_ACROSS_DESKS(other_fw
)) ||
775 IS_EWMH_DESKTOP(FW_W(other_fw
)))
780 if (IS_STICKY_ACROSS_PAGES(other_fw
))
782 stickyy
= arg
->pdelta_p
.y
;
789 if (IS_ICONIFIED(other_fw
))
791 get_visible_icon_geometry(other_fw
, &g
);
792 win_top
= g
.y
- stickyy
;
793 for (i
= start
; i
<= CP_GET_NEXT_STEP
; i
++)
796 win_top
+ g
.height
* i
/
800 ynew
= MIN(ynew
, ytest
);
803 win_top
= g
.y
- stickyy
- arg
->place_g
.height
;
804 for (i
= start
; i
<= CP_GET_NEXT_STEP
; i
++)
808 (CP_GET_NEXT_STEP
- i
) /
812 ynew
= MIN(ynew
, ytest
);
818 win_top
= other_fw
->g
.frame
.y
- stickyy
;
819 for (i
= start
; i
<= CP_GET_NEXT_STEP
; i
++)
822 win_top
+ other_fw
->g
.frame
.height
*
823 i
/ CP_GET_NEXT_STEP
;
826 ynew
= MIN(ynew
, ytest
);
829 win_top
= other_fw
->g
.frame
.y
- stickyy
-
831 for (i
= start
; i
<= CP_GET_NEXT_STEP
; i
++)
834 win_top
+ other_fw
->g
.frame
.height
*
835 (CP_GET_NEXT_STEP
- i
) /
839 ynew
= MIN(ynew
, ytest
);
848 static pl_loop_rc_t
__pl_minoverlap_get_first_pos(
849 position
*ret_p
, struct pl_ret_t
*ret
, const pl_arg_t
*arg
)
851 /* top left corner of page */
852 ret_p
->x
= arg
->page_p1
.x
;
853 ret_p
->y
= arg
->page_p1
.y
;
858 static pl_loop_rc_t
__pl_minoverlap_get_next_pos(
859 position
*ret_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
,
862 ret_p
->x
= arg
->place_g
.x
;
863 ret_p
->y
= arg
->place_g
.y
;
864 if (ret_p
->x
+ arg
->place_g
.width
<= arg
->page_p2
.x
)
867 ret_p
->x
= __pl_minoverlap_get_next_x(arg
);
868 ret_p
->y
= arg
->place_g
.y
;
870 if (ret_p
->x
+ arg
->place_g
.width
> arg
->page_p2
.x
)
872 /* out of room in x direction. Try next y. Reset x.*/
873 ret_p
->x
= arg
->page_p1
.x
;
874 ret_p
->y
= __pl_minoverlap_get_next_y(arg
);
876 if (ret_p
->y
+ arg
->place_g
.height
> arg
->page_p2
.y
)
885 static pl_penalty_t
__pl_minoverlap_get_avoidance_penalty(
886 const pl_arg_t
*arg
, FvwmWindow
*other_fw
, const rectangle
*other_g
)
889 pl_penalty_t avoidance_factor
;
891 const pl_penalty_struct
*opp
;
892 const pl_percent_penalty_struct
*oppp
;
894 opp
= (arg
->scratch
->pp
!= 0 && 0) ? arg
->scratch
->pp
:
895 &other_fw
->pl_penalty
;
896 oppp
= (arg
->scratch
->ppp
!= 0 && 0) ? arg
->scratch
->ppp
:
897 &other_fw
->pl_percent_penalty
;
898 other_p2
.x
= other_g
->x
+ other_g
->width
;
899 other_p2
.y
= other_g
->y
+ other_g
->height
;
901 long x1
= MAX(arg
->place_g
.x
, other_g
->x
);
902 long x2
= MIN(arg
->place_p2
.x
, other_p2
.x
);
903 long y1
= MAX(arg
->place_g
.y
, other_g
->y
);
904 long y2
= MIN(arg
->place_p2
.y
, other_p2
.y
);
906 /* overlapping area in pixels (windows are guaranteed to
907 * overlap when this function is called) */
908 anew
= (x2
- x1
) * (y2
- y1
);
910 if (IS_ICONIFIED(other_fw
))
912 avoidance_factor
= ICON_PLACEMENT_PENALTY(opp
);
914 else if (compare_window_layers(other_fw
, arg
->place_fw
) > 0)
916 avoidance_factor
= ONTOP_PLACEMENT_PENALTY(opp
);
918 else if (compare_window_layers(other_fw
, arg
->place_fw
) < 0)
920 avoidance_factor
= BELOW_PLACEMENT_PENALTY(opp
);
923 IS_STICKY_ACROSS_PAGES(other_fw
) ||
924 IS_STICKY_ACROSS_DESKS(other_fw
))
926 avoidance_factor
= STICKY_PLACEMENT_PENALTY(opp
);
930 avoidance_factor
= NORMAL_PLACEMENT_PENALTY(opp
);
932 if (arg
->flags
.use_percent
== 1)
934 pl_penalty_t cover_factor
;
938 other_area
= other_g
->width
* other_g
->height
;
939 place_area
= arg
->place_g
.width
* arg
->place_g
.height
;
941 if (other_area
!= 0 && place_area
!= 0)
943 anew
= 100 * MAX(anew
/ other_area
, anew
/ place_area
);
946 cover_factor
= PERCENTAGE_99_PENALTY(oppp
);
950 cover_factor
= PERCENTAGE_95_PENALTY(oppp
);
954 cover_factor
= PERCENTAGE_85_PENALTY(oppp
);
958 cover_factor
= PERCENTAGE_75_PENALTY(oppp
);
961 if (avoidance_factor
>= 1)
963 avoidance_factor
+= cover_factor
;
967 arg
->flags
.use_ewmh_dynamic_working_areapercent
== 1 &&
968 DO_EWMH_IGNORE_STRUT_HINTS(other_fw
) == 0 &&
970 other_fw
->dyn_strut
.left
> 0 ||
971 other_fw
->dyn_strut
.right
> 0 ||
972 other_fw
->dyn_strut
.top
> 0 ||
973 other_fw
->dyn_strut
.bottom
> 0))
975 const pl_penalty_struct
*mypp
;
977 mypp
= (arg
->scratch
->pp
!= 0 && 0) ? arg
->scratch
->pp
:
978 &arg
->place_fw
->pl_penalty
;
979 /* if we intersect a window which reserves space */
980 avoidance_factor
+= (avoidance_factor
>= 1) ?
981 EWMH_STRUT_PLACEMENT_PENALTY(mypp
) : 0;
983 anew
*= avoidance_factor
;
988 static pl_penalty_t
__pl_minoverlap_get_pos_penalty(
989 position
*ret_hint_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
)
991 FvwmWindow
*other_fw
;
992 pl_penalty_t penalty
;
997 other_fw
= Scr
.FvwmRoot
.next
; other_fw
!= NULL
;
998 other_fw
= other_fw
->next
)
1002 get_window_borders(other_fw
, &b
);
1005 arg
->place_fw
== other_fw
||
1006 IS_EWMH_DESKTOP(FW_W(other_fw
)))
1010 /* RBW - account for sticky windows... */
1012 other_fw
->Desk
!= arg
->place_fw
->Desk
&&
1013 IS_STICKY_ACROSS_DESKS(other_fw
) == 0)
1017 rc
= get_visible_window_or_icon_geometry(other_fw
, &other_g
);
1018 if (IS_STICKY_ACROSS_PAGES(other_fw
))
1020 other_g
.x
-= arg
->pdelta_p
.x
;
1021 other_g
.y
-= arg
->pdelta_p
.y
;
1024 arg
->place_g
.x
< other_g
.x
+ other_g
.width
&&
1025 arg
->place_p2
.x
> other_g
.x
&&
1026 arg
->place_g
.y
< other_g
.y
+ other_g
.height
&&
1027 arg
->place_p2
.y
> other_g
.y
)
1031 anew
= __pl_minoverlap_get_avoidance_penalty(
1032 arg
, other_fw
, &other_g
);
1035 penalty
> ret
->best_penalty
&&
1036 ret
->best_penalty
!= -1)
1038 /* TA: 20091230: Fix over-zealous penalties
1039 * by explicitly forcing the window on-screen
1040 * here. The y-axis is only affected here,
1041 * due to how the xoffset calculations happen
1042 * prior to setting the x-axis. When we get
1043 * penalties which are "over-zealous" -- and
1044 * by not taking into account the size of the
1045 * window borders, the window was being placed
1048 if (ret
->best_p
.y
+ arg
->place_g
.height
> arg
->page_p2
.y
)
1052 arg
->place_g
.height
-
1053 b
.total_size
.height
);
1055 ret
->best_penalty
= 0;
1059 /* stop looking; the penalty is too high */
1064 /* now handle the working area */
1066 const pl_penalty_struct
*mypp
;
1068 mypp
= (arg
->scratch
->pp
!= 0 && 0) ? arg
->scratch
->pp
:
1069 &arg
->place_fw
->pl_penalty
;
1070 if (arg
->flags
.use_ewmh_dynamic_working_areapercent
== 1)
1072 penalty
+= EWMH_STRUT_PLACEMENT_PENALTY(mypp
) *
1073 EWMH_GetStrutIntersection(
1074 arg
->place_g
.x
, arg
->place_g
.y
,
1075 arg
->place_p2
.x
, arg
->place_p2
.y
,
1076 arg
->flags
.use_percent
);
1080 /* EWMH_USE_DYNAMIC_WORKING_AREA, count the base strut
1083 EWMH_STRUT_PLACEMENT_PENALTY(mypp
) *
1084 EWMH_GetBaseStrutIntersection(
1085 arg
->place_g
.x
, arg
->place_g
.y
,
1086 arg
->place_p2
.x
, arg
->place_p2
.y
,
1087 arg
->flags
.use_percent
);
1094 /* ---------------------------- local functions (SmartPlacement) ----------- */
1096 static pl_penalty_t
__pl_smart_get_pos_penalty(
1097 position
*ret_hint_p
, struct pl_ret_t
*ret
, const struct pl_arg_t
*arg
)
1101 arg
->scratch
->pp
= &default_pl_penalty
;
1102 arg
->scratch
->ppp
= &default_pl_percent_penalty
;
1103 p
= __pl_minoverlap_get_pos_penalty(ret_hint_p
, ret
, arg
);
1112 /* ---------------------------- local functions ---------------------------- */
1114 static int placement_loop(pl_ret_t
*ret
, pl_arg_t
*arg
)
1117 pl_penalty_t penalty
;
1118 pl_loop_rc_t loop_rc
;
1120 if (arg
->algo
->get_pos_simple
!= NULL
)
1124 penalty
= arg
->algo
->get_pos_simple(&pos
, ret
, arg
);
1125 arg
->place_g
.x
= pos
.x
;
1126 arg
->place_g
.y
= pos
.y
;
1127 ret
->best_penalty
= penalty
;
1128 ret
->best_p
.x
= pos
.x
;
1129 ret
->best_p
.y
= pos
.y
;
1130 loop_rc
= PL_LOOP_END
;
1134 loop_rc
= arg
->algo
->get_first_pos(&next_p
, ret
, arg
);
1135 arg
->place_g
.x
= next_p
.x
;
1136 arg
->place_g
.y
= next_p
.y
;
1137 ret
->best_p
.x
= next_p
.x
;
1138 ret
->best_p
.y
= next_p
.y
;
1140 while (loop_rc
!= PL_LOOP_END
)
1143 pl_scratch_t scratch
;
1145 memset(&scratch
, 0, sizeof(scratch
));
1146 arg
->scratch
= &scratch
;
1147 arg
->place_p2
.x
= arg
->place_g
.x
+ arg
->place_g
.width
;
1148 arg
->place_p2
.y
= arg
->place_g
.y
+ arg
->place_g
.height
;
1149 hint_p
.x
= arg
->place_g
.x
;
1150 hint_p
.y
= arg
->place_g
.y
;
1151 penalty
= arg
->algo
->get_pos_penalty(&hint_p
, ret
, arg
);
1152 /* I've added +0.0001 because with my machine the < test fail
1153 * with certain *equal* float numbers! */
1157 ret
->best_penalty
< 0 ||
1158 penalty
+ 0.0001 < ret
->best_penalty
))
1160 ret
->best_p
.x
= arg
->place_g
.x
;
1161 ret
->best_p
.y
= arg
->place_g
.y
;
1162 ret
->best_penalty
= penalty
;
1168 loop_rc
= arg
->algo
->get_next_pos(&next_p
, ret
, arg
, hint_p
);
1169 arg
->place_g
.x
= next_p
.x
;
1170 arg
->place_g
.y
= next_p
.y
;
1172 if (ret
->best_penalty
< 0)
1174 ret
->best_penalty
= -1;
1177 return (ret
->best_penalty
== -1) ? -1 : 0;
1180 static void __place_get_placement_flags(
1181 pl_flags_t
*ret_flags
, FvwmWindow
*fw
, window_style
*pstyle
,
1182 initial_window_options_t
*win_opts
, int mode
, pl_reason_t
*reason
)
1185 Bool override_uspos
;
1186 Bool has_ppos
= False
;
1187 Bool has_uspos
= False
;
1189 /* Windows use the position hint if these conditions are met:
1191 * The program specified a USPosition hint and it is not overridden
1192 * with the No(Transient)USPosition style.
1196 * The program specified a PPosition hint and it is not overridden
1197 * with the No(Transient)PPosition style.
1199 * Windows without a position hint are placed using wm placement.
1201 if (IS_TRANSIENT(fw
))
1203 override_ppos
= SUSE_NO_TRANSIENT_PPOSITION(&pstyle
->flags
);
1204 override_uspos
= SUSE_NO_TRANSIENT_USPOSITION(&pstyle
->flags
);
1208 override_ppos
= SUSE_NO_PPOSITION(&pstyle
->flags
);
1209 override_uspos
= SUSE_NO_USPOSITION(&pstyle
->flags
);
1211 if (fw
->hints
.flags
& PPosition
)
1216 reason
->pos
.reason
= PR_POS_USE_PPOS
;
1220 reason
->pos
.reason
= PR_POS_IGNORE_PPOS
;
1223 if (fw
->hints
.flags
& USPosition
)
1225 if (!override_uspos
)
1228 reason
->pos
.reason
= PR_POS_USE_USPOS
;
1230 else if (reason
->pos
.reason
!= PR_POS_USE_PPOS
)
1232 reason
->pos
.reason
= PR_POS_USE_USPOS
;
1235 if (mode
== PLACE_AGAIN
)
1237 ret_flags
->do_not_use_wm_placement
= 0;
1238 reason
->pos
.reason
= PR_POS_PLACE_AGAIN
;
1240 else if (has_ppos
|| has_uspos
)
1242 ret_flags
->do_not_use_wm_placement
= 1;
1244 else if (win_opts
->flags
.do_override_ppos
)
1246 ret_flags
->do_not_use_wm_placement
= 1;
1247 reason
->pos
.reason
= PR_POS_CAPTURE
;
1249 else if (!ret_flags
->do_honor_starts_on_page
&&
1250 fw
->wmhints
&& (fw
->wmhints
->flags
& StateHint
) &&
1251 fw
->wmhints
->initial_state
== IconicState
)
1253 ret_flags
->do_forbid_manual_placement
= 1;
1254 reason
->pos
.do_not_manual_icon_placement
= 1;
1260 static int __add_algo(
1261 const pl_algo_t
**algos
, int num_algos
, const pl_algo_t
*new_algo
)
1263 if (num_algos
>= MAX_NUM_PLACEMENT_ALGOS
)
1265 return MAX_NUM_PLACEMENT_ALGOS
;
1267 algos
[num_algos
] = new_algo
;
1273 static int __place_get_wm_pos(
1274 const exec_context_t
*exc
, window_style
*pstyle
, rectangle
*attr_g
,
1275 pl_flags_t flags
, rectangle screen_g
, pl_start_style_t start_style
,
1276 int mode
, initial_window_options_t
*win_opts
, pl_reason_t
*reason
,
1277 int pdeltax
, int pdeltay
)
1279 const pl_algo_t
*algos
[MAX_NUM_PLACEMENT_ALGOS
+ 1];
1281 unsigned int placement_mode
= SPLACEMENT_MODE(&pstyle
->flags
);
1286 /* BEGIN init placement agrs and ret */
1287 memset(&arg
, 0, sizeof(arg
));
1290 arg
.reason
= reason
;
1291 arg
.place_fw
= exc
->w
.fw
;
1292 arg
.place_g
= arg
.place_fw
->g
.frame
;
1293 arg
.screen_g
= screen_g
;
1294 arg
.page_p1
.x
= arg
.screen_g
.x
- pdeltax
;
1295 arg
.page_p1
.y
= arg
.screen_g
.y
- pdeltay
;
1296 arg
.page_p2
.x
= arg
.page_p1
.x
+ screen_g
.width
;
1297 arg
.page_p2
.y
= arg
.page_p1
.y
+ screen_g
.height
;
1298 arg
.pdelta_p
.x
= pdeltax
;
1299 arg
.pdelta_p
.y
= pdeltay
;
1300 arg
.flags
.use_percent
= 0;
1301 arg
.flags
.do_honor_starts_on_page
= flags
.do_honor_starts_on_page
;
1302 if (SEWMH_PLACEMENT_MODE(&pstyle
->flags
) == EWMH_USE_WORKING_AREA
)
1304 arg
.flags
.use_ewmh_dynamic_working_areapercent
= 1;
1306 memset(&ret
, 0, sizeof(ret
));
1307 ret
.best_penalty
= -1.0;
1308 /* END init placement agrs and ret */
1309 /* override if manual placement happens */
1310 SET_PLACED_BY_FVWM(arg
.place_fw
, 1);
1311 if (flags
.do_forbid_manual_placement
)
1313 switch (placement_mode
)
1316 case PLACE_MANUAL_B
:
1317 placement_mode
= PLACE_CASCADE
;
1319 case PLACE_TILEMANUAL
:
1320 placement_mode
= PLACE_TILECASCADE
;
1326 reason
->pos
.algo
= placement_mode
;
1327 /* first, try various "smart" placement */
1329 switch (placement_mode
)
1331 case PLACE_POSITION
:
1332 num_algos
= __add_algo(
1333 algos
, num_algos
, &position_placement_algo
);
1335 case PLACE_TILEMANUAL
:
1336 num_algos
= __add_algo(
1337 algos
, num_algos
, &smart_placement_algo
);
1338 num_algos
= __add_algo(
1339 algos
, num_algos
, &manual_placement_algo
);
1341 case PLACE_MINOVERLAPPERCENT
:
1342 arg
.flags
.use_percent
= 1;
1344 case PLACE_MINOVERLAP
:
1345 num_algos
= __add_algo(
1346 algos
, num_algos
, &minoverlap_placement_algo
);
1348 case PLACE_TILECASCADE
:
1349 num_algos
= __add_algo(
1350 algos
, num_algos
, &smart_placement_algo
);
1351 num_algos
= __add_algo(
1352 algos
, num_algos
, &cascade_placement_algo
);
1355 case PLACE_MANUAL_B
:
1356 num_algos
= __add_algo(
1357 algos
, num_algos
, &manual_placement_algo
);
1360 case PLACE_CASCADE_B
:
1361 num_algos
= __add_algo(
1362 algos
, num_algos
, &cascade_placement_algo
);
1368 /* try all the placement algorithms */
1369 for (i
= 0 ; ret
.best_penalty
< 0 && i
< num_algos
; i
++)
1371 arg
.algo
= algos
[i
];
1372 placement_loop(&ret
, &arg
);
1374 if (ret
.best_penalty
>= 0)
1376 /* placement succed */
1377 attr_g
->x
= ret
.best_p
.x
;
1378 attr_g
->y
= ret
.best_p
.y
;
1382 /* fall back to default position */
1385 reason
->pos
.has_placement_failed
= 1;
1388 return ret
.flags
.do_resize_too
;
1391 static int __place_get_nowm_pos(
1392 const exec_context_t
*exc
, window_style
*pstyle
, rectangle
*attr_g
,
1393 pl_flags_t flags
, rectangle screen_g
, pl_start_style_t start_style
,
1394 int mode
, initial_window_options_t
*win_opts
, pl_reason_t
*reason
,
1395 int pdeltax
, int pdeltay
)
1397 FvwmWindow
*fw
= exc
->w
.fw
;
1400 if (!win_opts
->flags
.do_override_ppos
)
1402 SET_PLACED_BY_FVWM(fw
, False
);
1404 /* the USPosition was specified, or the window is a transient, or it
1405 * starts iconic so place it automatically */
1407 SUSE_START_ON_SCREEN(&pstyle
->flags
) &&
1408 flags
.do_honor_starts_on_screen
)
1410 fscreen_scr_t mangle_screen
;
1412 /* If StartsOnScreen has been given for a window, translate its
1413 * USPosition so that it is relative to that particular screen.
1414 * If we don't do this, then a geometry would completely
1415 * cancel the effect of the StartsOnScreen style. However, some
1416 * applications try to remember their position. This would
1417 * break if these were translated to screen coordinates. There
1418 * is no reliable way to do it. Currently, if the desired
1419 * place does not intersect the target screen, we assume the
1420 * window position must be adjusted to the screen origin. So
1421 * there are two ways to get a window to pop up on a particular
1422 * Xinerama screen. 1: The intuitive way giving a geometry
1423 * hint relative to the desired screen's 0,0 along with the
1424 * appropriate StartsOnScreen style (or *wmscreen resource), or
1425 * 2: Do NOT specify a Xinerama screen (or specify it to be
1426 * 'g') and give the geometry hint in terms of the global
1428 mangle_screen
= FScreenFetchMangledScreenFromUSPosHints(
1430 if (mangle_screen
!= FSCREEN_GLOBAL
)
1432 /* whoever set this hint knew exactly what he was
1433 * doing; so ignore the StartsOnScreen style */
1434 flags
.do_honor_starts_on_screen
= 0;
1435 reason
->pos
.reason
= PR_POS_USPOS_OVERRIDE_SOS
;
1437 else if (attr_g
->x
+ attr_g
->width
< screen_g
.x
||
1438 attr_g
->x
>= screen_g
.x
+ screen_g
.width
||
1439 attr_g
->y
+ attr_g
->height
< screen_g
.y
||
1440 attr_g
->y
>= screen_g
.y
+ screen_g
.height
)
1442 /* desired coordinates do not intersect the target
1443 * screen. Let's assume the application specified
1444 * global coordinates and translate them to the screen.
1446 FScreenTranslateCoordinates(
1447 NULL
, start_style
.screen
, NULL
, FSCREEN_GLOBAL
,
1448 &attr_g
->x
, &attr_g
->y
);
1449 reason
->pos
.do_adjust_off_screen
= 1;
1452 /* If SkipMapping, and other legalities are observed, adjust for
1454 if (DO_NOT_SHOW_ON_MAP(fw
) && flags
.do_honor_starts_on_page
&&
1455 (!IS_TRANSIENT(fw
) ||
1456 SUSE_START_ON_PAGE_FOR_TRANSIENT(&pstyle
->flags
))
1458 /* dv 08-Jul-2003: Do not use this. Instead, force the window on
1459 * the requested page even if the application requested a different
1461 && (SUSE_NO_PPOSITION(&pstyle
->flags
) ||
1462 !(fw
->hints
.flags
& PPosition
))
1463 /* dv 08-Jul-2003: This condition is always true because we
1464 * already checked for flags.do_honor_starts_on_page above. */
1465 /* RBW - allow StartsOnPage to go through, even if iconic. */
1466 && ((!(fw
->wmhints
&& (fw
->wmhints
->flags
& StateHint
) &&
1467 fw
->wmhints
->initial_state
== IconicState
))
1468 || flags
.do_honor_starts_on_page
)
1477 /* We're placing a SkipMapping window - either capturing one
1478 * that's previously been mapped, or overriding USPosition - so
1479 * what we have here is its actual untouched coordinates. In
1480 * case it was a StartsOnPage window, we have to 1) convert the
1481 * existing x,y offsets relative to the requested page (i.e.,
1482 * as though there were only one page, no virtual desktop),
1483 * then 2) readjust relative to the current page. */
1486 attr_g
->x
+= Scr
.MyDisplayWidth
;
1488 attr_g
->x
%= Scr
.MyDisplayWidth
;
1489 attr_g
->x
-= pdeltax
;
1490 /* Noticed a quirk here. With some apps (e.g., xman), we find
1491 * the placement has moved 1 pixel away from where we
1492 * originally put it when we come through here. Why is this
1493 * happening? Probably attr_backup.border_width, try xclock
1494 * -borderwidth 100 */
1497 attr_g
->y
+= Scr
.MyDisplayHeight
;
1499 attr_g
->y
%= Scr
.MyDisplayHeight
;
1500 attr_g
->y
-= pdeltay
;
1501 if (attr_g
->x
!= old_x
|| attr_g
->y
!= old_y
)
1503 reason
->pos
.do_adjust_off_page
= 1;
1506 /* put it where asked, mod title bar */
1507 /* if the gravity is towards the top, move it by the title height */
1513 gravity_get_offsets(fw
->hints
.win_gravity
, &gravx
, &gravy
);
1514 final_g
.x
= attr_g
->x
+ gravx
* fw
->attr_backup
.border_width
;
1515 final_g
.y
= attr_g
->y
+ gravy
* fw
->attr_backup
.border_width
;
1516 /* Virtually all applications seem to share a common bug: they
1517 * request the top left pixel of their *border* as their origin
1518 * instead of the top left pixel of their client window, e.g.
1519 * 'xterm -g +0+0' creates an xterm that tries to map at (0 0)
1520 * although its border (width 1) would not be visible if it ran
1521 * under plain X. It should have tried to map at (1 1)
1522 * instead. This clearly violates the ICCCM, but trying to
1523 * change this is like tilting at windmills. So we have to add
1524 * the border width here. */
1525 final_g
.x
+= fw
->attr_backup
.border_width
;
1526 final_g
.y
+= fw
->attr_backup
.border_width
;
1529 if (mode
== PLACE_INITIAL
)
1531 get_window_borders(fw
, &b
);
1533 fw
->hints
.win_gravity
, &final_g
,
1534 b
.total_size
.width
, b
.total_size
.height
);
1536 attr_g
->x
= final_g
.x
;
1537 attr_g
->y
= final_g
.y
;
1543 /* Handles initial placement and sizing of a new window
1549 * 2 = OK, window must be resized too */
1550 static int __place_window(
1551 const exec_context_t
*exc
, window_style
*pstyle
, rectangle
*attr_g
,
1552 pl_start_style_t start_style
, int mode
,
1553 initial_window_options_t
*win_opts
, pl_reason_t
*reason
)
1556 int is_skipmapping_forbidden
;
1564 extern Bool Restarting
;
1565 FvwmWindow
*fw
= exc
->w
.fw
;
1567 memset(&flags
, 0, sizeof(flags
));
1569 /* Select a desk to put the window on (in list of priority):
1570 * 1. Sticky Windows stay on the current desk.
1571 * 2. Windows specified with StartsOnDesk go where specified
1572 * 3. Put it on the desk it was on before the restart.
1573 * 4. Transients go on the same desk as their parents.
1574 * 5. Window groups stay together (if the KeepWindowGroupsOnDesk style
1577 /* Let's get the StartsOnDesk/Page tests out of the way first. */
1579 SUSE_START_ON_DESK(&pstyle
->flags
) ||
1580 SUSE_START_ON_SCREEN(&pstyle
->flags
))
1582 flags
.do_honor_starts_on_page
= 1;
1583 flags
.do_honor_starts_on_screen
= 1;
1585 * Honor the flag unless...
1586 * it's a restart or recapture, and that option's disallowed...
1588 if (win_opts
->flags
.do_override_ppos
&&
1589 (Restarting
|| (Scr
.flags
.are_windows_captured
)) &&
1590 !SRECAPTURE_HONORS_STARTS_ON_PAGE(&pstyle
->flags
))
1592 flags
.do_honor_starts_on_page
= 0;
1593 flags
.do_honor_starts_on_screen
= 0;
1594 reason
->page
.reason
= PR_PAGE_IGNORE_CAPTURE
;
1595 reason
->page
.do_ignore_starts_on_page
= 1;
1596 reason
->screen
.reason
= PR_PAGE_IGNORE_CAPTURE
;
1599 * it's a cold start window capture, and that's disallowed...
1601 if (win_opts
->flags
.do_override_ppos
&&
1602 (!Restarting
&& !(Scr
.flags
.are_windows_captured
)) &&
1603 !SCAPTURE_HONORS_STARTS_ON_PAGE(&pstyle
->flags
))
1605 flags
.do_honor_starts_on_page
= 0;
1606 flags
.do_honor_starts_on_screen
= 0;
1607 reason
->page
.reason
= PR_PAGE_IGNORE_CAPTURE
;
1608 reason
->page
.do_ignore_starts_on_page
= 1;
1609 reason
->screen
.reason
= PR_PAGE_IGNORE_CAPTURE
;
1612 * it's ActivePlacement and SkipMapping, and that's disallowed.
1614 switch (SPLACEMENT_MODE(&pstyle
->flags
))
1617 case PLACE_MANUAL_B
:
1618 case PLACE_TILEMANUAL
:
1619 is_skipmapping_forbidden
=
1620 !SMANUAL_PLACEMENT_HONORS_STARTS_ON_PAGE(
1624 is_skipmapping_forbidden
= 0;
1627 if (win_opts
->flags
.do_override_ppos
||
1628 !DO_NOT_SHOW_ON_MAP(fw
))
1630 is_skipmapping_forbidden
= 0;
1632 if (is_skipmapping_forbidden
== 1)
1634 flags
.do_honor_starts_on_page
= 0;
1635 reason
->page
.reason
= PR_PAGE_IGNORE_INVALID
;
1636 reason
->page
.do_ignore_starts_on_page
= 1;
1638 WARN
, "__place_window",
1639 "invalid style combination used: StartsOnPage"
1640 "/StartsOnDesk and SkipMapping don't work with"
1641 " ManualPlacement and TileManualPlacement."
1642 " Putting window on current page, please use"
1643 " another placement style or"
1644 " ActivePlacementHonorsStartsOnPage.");
1647 /* get the screen coordinates to place window on */
1648 if (SUSE_START_ON_SCREEN(&pstyle
->flags
))
1650 if (flags
.do_honor_starts_on_screen
)
1652 /* use screen from style */
1654 NULL
, start_style
.screen
, &screen_g
.x
,
1655 &screen_g
.y
, &screen_g
.width
,
1657 reason
->screen
.screen
= start_style
.screen
;
1661 /* use global screen */
1663 NULL
, FSCREEN_GLOBAL
, &screen_g
.x
, &screen_g
.y
,
1664 &screen_g
.width
, &screen_g
.height
);
1665 reason
->screen
.screen
= FSCREEN_GLOBAL
;
1670 /* use current screen */
1672 NULL
, FSCREEN_CURRENT
, &screen_g
.x
, &screen_g
.y
,
1673 &screen_g
.width
, &screen_g
.height
);
1674 reason
->screen
.screen
= FSCREEN_CURRENT
;
1677 if (SPLACEMENT_MODE(&pstyle
->flags
) != PLACE_MINOVERLAPPERCENT
&&
1678 SPLACEMENT_MODE(&pstyle
->flags
) != PLACE_MINOVERLAP
)
1680 EWMH_GetWorkAreaIntersection(
1681 fw
, &screen_g
.x
, &screen_g
.y
, &screen_g
.width
,
1683 SEWMH_PLACEMENT_MODE(&pstyle
->flags
));
1684 reason
->screen
.was_modified_by_ewmh_workingarea
= 1;
1686 reason
->screen
.g
= screen_g
;
1687 /* Don't alter the existing desk location during Capture/Recapture. */
1688 if (!win_opts
->flags
.do_override_ppos
)
1690 fw
->Desk
= Scr
.CurrentDesk
;
1691 reason
->desk
.reason
= PR_DESK_CURRENT
;
1695 reason
->desk
.reason
= PR_DESK_CAPTURE
;
1697 if (S_IS_STICKY_ACROSS_DESKS(SFC(pstyle
->flags
)))
1699 fw
->Desk
= Scr
.CurrentDesk
;
1700 reason
->desk
.reason
= PR_DESK_STICKY
;
1702 else if (SUSE_START_ON_DESK(&pstyle
->flags
) && start_style
.desk
&&
1703 flags
.do_honor_starts_on_page
)
1705 fw
->Desk
= (start_style
.desk
> -1) ?
1706 start_style
.desk
- 1 : start_style
.desk
;
1707 reason
->desk
.reason
= reason
->desk
.sod_reason
;
1711 if ((DO_USE_WINDOW_GROUP_HINT(fw
)) &&
1712 (fw
->wmhints
) && (fw
->wmhints
->flags
& WindowGroupHint
)&&
1713 (fw
->wmhints
->window_group
!= None
) &&
1714 (fw
->wmhints
->window_group
!= Scr
.Root
))
1716 /* Try to find the group leader or another window in
1718 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
1720 if (FW_W(t
) == fw
->wmhints
->window_group
)
1722 /* found the group leader, break out */
1724 reason
->desk
.reason
=
1725 PR_DESK_WINDOW_GROUP_LEADER
;
1728 else if (t
->wmhints
&&
1729 (t
->wmhints
->flags
&
1731 (t
->wmhints
->window_group
==
1732 fw
->wmhints
->window_group
))
1734 /* found a window from the same group,
1735 * but keep looking for the group
1738 reason
->desk
.reason
=
1739 PR_DESK_WINDOW_GROUP_MEMBER
;
1743 if ((IS_TRANSIENT(fw
))&&(FW_W_TRANSIENTFOR(fw
)!=None
)&&
1744 (FW_W_TRANSIENTFOR(fw
) != Scr
.Root
))
1746 /* Try to find the parent's desktop */
1747 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
1749 if (FW_W(t
) == FW_W_TRANSIENTFOR(fw
))
1752 reason
->desk
.reason
=
1760 /* migo - I am not sure this block is ever needed */
1764 unsigned long nitems
, bytes_remain
;
1765 unsigned char *prop
;
1769 dpy
, FW_W(fw
), _XA_WM_DESKTOP
, 0L, 1L,
1770 True
, _XA_WM_DESKTOP
, &atype
, &aformat
,
1771 &nitems
, &bytes_remain
, &prop
) ==
1776 fw
->Desk
= *(unsigned long *)prop
;
1778 reason
->desk
.reason
=
1779 PR_DESK_XPROP_XA_WM_DESKTOP
;
1784 reason
->desk
.desk
= fw
->Desk
;
1785 /* I think it would be good to switch to the selected desk
1786 * whenever a new window pops up, except during initialization */
1787 /* RBW - 11/02/1998 -- I dont. */
1788 if (!win_opts
->flags
.do_override_ppos
&& !DO_NOT_SHOW_ON_MAP(fw
))
1790 if (Scr
.CurrentDesk
!= fw
->Desk
)
1792 reason
->desk
.do_switch_desk
= 1;
1794 goto_desk(fw
->Desk
);
1796 /* Don't move viewport if SkipMapping, or if recapturing the window,
1797 * adjust the coordinates later. Otherwise, just switch to the target
1798 * page - it's ever so much simpler. */
1799 if (S_IS_STICKY_ACROSS_PAGES(SFC(pstyle
->flags
)))
1801 reason
->page
.reason
= PR_PAGE_STICKY
;
1803 else if (SUSE_START_ON_DESK(&pstyle
->flags
))
1805 if (start_style
.page_x
!= 0 && start_style
.page_y
!= 0)
1807 px
= start_style
.page_x
- 1;
1808 py
= start_style
.page_y
- 1;
1809 reason
->page
.reason
= PR_PAGE_STYLE
;
1810 px
*= Scr
.MyDisplayWidth
;
1811 py
*= Scr
.MyDisplayHeight
;
1812 if (!win_opts
->flags
.do_override_ppos
&&
1813 !DO_NOT_SHOW_ON_MAP(fw
))
1815 MoveViewport(px
,py
,True
);
1816 reason
->page
.do_switch_page
= 1;
1818 else if (flags
.do_honor_starts_on_page
)
1820 /* Save the delta from current page */
1821 pdeltax
= Scr
.Vx
- px
;
1822 pdeltay
= Scr
.Vy
- py
;
1823 reason
->page
.do_honor_starts_on_page
= 1;
1828 /* pick a location for the window. */
1829 __place_get_placement_flags(
1830 &flags
, fw
, pstyle
, win_opts
, mode
, reason
);
1831 if (flags
.do_not_use_wm_placement
)
1833 rc
= __place_get_nowm_pos(
1834 exc
, pstyle
, attr_g
, flags
, screen_g
, start_style
,
1835 mode
, win_opts
, reason
, pdeltax
, pdeltay
);
1839 rc
= __place_get_wm_pos(
1840 exc
, pstyle
, attr_g
, flags
, screen_g
, start_style
,
1841 mode
, win_opts
, reason
, pdeltax
, pdeltay
);
1843 reason
->pos
.x
= attr_g
->x
;
1844 reason
->pos
.y
= attr_g
->y
;
1849 static void __place_handle_x_resources(
1850 FvwmWindow
*fw
, window_style
*pstyle
, pl_reason_t
*reason
)
1852 int client_argc
= 0;
1853 char **client_argv
= NULL
;
1855 /* Used to parse command line of clients for specific desk requests. */
1856 /* Todo: check for multiple desks. */
1857 XrmDatabase db
= NULL
;
1858 static XrmOptionDescRec table
[] = {
1859 /* Want to accept "-workspace N" or -xrm "fvwm*desk:N" as
1860 * options to specify the desktop. I have to include dummy
1861 * options that are meaningless since Xrm seems to allow -w to
1862 * match -workspace if there would be no ambiguity. */
1863 {"-workspacf", "*junk", XrmoptionSepArg
, (caddr_t
) NULL
},
1864 {"-workspace", "*desk", XrmoptionSepArg
, (caddr_t
) NULL
},
1865 {"-xrn", NULL
, XrmoptionResArg
, (caddr_t
) NULL
},
1866 {"-xrm", NULL
, XrmoptionResArg
, (caddr_t
) NULL
},
1868 int t1
= -1, t2
= -1, t3
= -1, spargs
= 0;
1870 /* Find out if the client requested a specific desk on the command
1872 * RBW - 11/20/1998 - allow a desk of -1 to work. */
1873 if (XGetCommand(dpy
, FW_W(fw
), &client_argv
, &client_argc
) == 0)
1877 if (client_argc
<= 0 || client_argv
== NULL
)
1881 /* Get global X resources */
1882 MergeXResources(dpy
, &db
, False
);
1883 /* command line takes precedence over all */
1884 MergeCmdLineResources(
1885 &db
, table
, 4, client_argv
[0], &client_argc
, client_argv
,
1887 /* parse the database values */
1888 if (GetResourceString(db
, "desk", client_argv
[0], &rm_value
) &&
1891 SGET_START_DESK(*pstyle
) = atoi(rm_value
.addr
);
1892 /* RBW - 11/20/1998 */
1893 if (SGET_START_DESK(*pstyle
) > -1)
1896 *pstyle
, SGET_START_DESK(*pstyle
) + 1);
1898 reason
->desk
.sod_reason
= PR_DESK_X_RESOURCE_DESK
;
1899 pstyle
->flags
.use_start_on_desk
= 1;
1901 if (GetResourceString(db
, "fvwmscreen", client_argv
[0], &rm_value
) &&
1905 *pstyle
, FScreenGetScreenArgument(rm_value
.addr
, 'c'));
1906 reason
->screen
.reason
= PR_SCREEN_X_RESOURCE_FVWMSCREEN
;
1907 reason
->screen
.screen
= SGET_START_SCREEN(*pstyle
);
1908 pstyle
->flags
.use_start_on_screen
= 1;
1910 if (GetResourceString(db
, "page", client_argv
[0], &rm_value
) &&
1914 rm_value
.addr
, "%d %d %d", &t1
, &t2
, &t3
);
1918 pstyle
->flags
.use_start_on_desk
= 1;
1919 SSET_START_DESK(*pstyle
, (t1
> -1) ? t1
+ 1 : t1
);
1920 reason
->desk
.sod_reason
= PR_DESK_X_RESOURCE_PAGE
;
1923 pstyle
->flags
.use_start_on_desk
= 1;
1924 SSET_START_PAGE_X(*pstyle
, (t1
> -1) ? t1
+ 1 : t1
);
1925 SSET_START_PAGE_Y(*pstyle
, (t2
> -1) ? t2
+ 1 : t2
);
1926 reason
->page
.reason
= PR_PAGE_X_RESOURCE_PAGE
;
1927 reason
->page
.px
= SGET_START_PAGE_X(*pstyle
);
1928 reason
->page
.py
= SGET_START_PAGE_Y(*pstyle
);
1931 pstyle
->flags
.use_start_on_desk
= 1;
1932 SSET_START_DESK(*pstyle
, (t1
> -1) ? t1
+ 1 : t1
);
1933 reason
->desk
.sod_reason
=
1934 PR_DESK_X_RESOURCE_PAGE
;
1935 SSET_START_PAGE_X(*pstyle
, (t2
> -1) ? t2
+ 1 : t2
);
1936 SSET_START_PAGE_Y(*pstyle
, (t3
> -1) ? t3
+ 1 : t3
);
1937 reason
->page
.reason
= PR_PAGE_X_RESOURCE_PAGE
;
1938 reason
->page
.px
= SGET_START_PAGE_X(*pstyle
);
1939 reason
->page
.py
= SGET_START_PAGE_Y(*pstyle
);
1945 XFreeStringList(client_argv
);
1946 XrmDestroyDatabase(db
);
1951 static void __explain_placement(FvwmWindow
*fw
, pl_reason_t
*reason
)
1953 char explanation
[2048];
1958 int is_placed_by_algo
;
1962 strcat(s
, "placed new window 0x%x '%s':\n");
1965 s
, " initial size %dx%d\n", fw
->g
.frame
.width
,
1966 fw
->g
.frame
.height
);
1968 switch (reason
->desk
.reason
)
1970 case PR_DESK_CURRENT
:
1974 r
= "specified by style";
1976 case PR_DESK_X_RESOURCE_DESK
:
1977 r
= "specified by 'desk' X resource";
1979 case PR_DESK_X_RESOURCE_PAGE
:
1980 r
= "specified by 'page' X resource";
1982 case PR_DESK_CAPTURE
:
1983 r
= "window was (re)captured";
1985 case PR_DESK_STICKY
:
1986 r
= "window is sticky";
1988 case PR_DESK_WINDOW_GROUP_LEADER
:
1989 r
= "same desk as window group leader";
1991 case PR_DESK_WINDOW_GROUP_MEMBER
:
1992 r
= "same desk as window group member";
1994 case PR_DESK_TRANSIENT
:
1995 r
= "transient window placed on same desk as parent";
1997 case PR_DESK_XPROP_XA_WM_DESKTOP
:
1998 r
= "specified by _XA_WM_DESKTOP property";
2004 sprintf(s
, " desk %d (%s)\n", reason
->desk
.desk
, r
);
2006 if (reason
->desk
.do_switch_desk
== 1)
2008 sprintf(s
, " (switched to desk)\n");
2013 switch (reason
->page
.reason
)
2015 case PR_PAGE_CURRENT
:
2020 r
= "specified by style";
2022 case PR_PAGE_X_RESOURCE_PAGE
:
2023 r
= "specified by 'page' X resource";
2025 case PR_PAGE_IGNORE_CAPTURE
:
2026 r
= "window was (re)captured";
2028 case PR_PAGE_IGNORE_INVALID
:
2029 r
= "requested page ignored because of invalid style"
2032 case PR_PAGE_STICKY
:
2034 r
= "current page (window is sticky)";
2040 if (do_show_page
== 0)
2042 sprintf(s
, " %s\n", r
);
2047 s
, " page %d %d (%s)\n", reason
->page
.px
- 1,
2048 reason
->page
.py
- 1, r
);
2051 if (reason
->page
.do_switch_page
== 1)
2053 sprintf(s
, " (switched to page)\n");
2056 if (reason
->page
.do_ignore_starts_on_page
== 1)
2058 sprintf(s
, " (possibly ignored StartsOnPage)\n");
2062 if (FScreenIsEnabled() == True
|| FScreenIsSLSEnabled() == True
)
2064 switch (reason
->screen
.reason
)
2066 case PR_SCREEN_CURRENT
:
2067 r
= "current screen";
2069 case PR_SCREEN_STYLE
:
2070 r
= "specified by style";
2072 case PR_SCREEN_X_RESOURCE_FVWMSCREEN
:
2073 r
= "specified by 'fvwmscreen' X resource";
2075 case PR_SCREEN_IGNORE_CAPTURE
:
2076 r
= "window was (re)captured";
2082 FScreenSpecToString(t
, 32, reason
->screen
.screen
);
2084 s
, " screen: %s: %d %d %dx%d (%s)\n",
2085 t
, reason
->screen
.g
.x
, reason
->screen
.g
.y
,
2086 reason
->screen
.g
.width
, reason
->screen
.g
.height
, r
);
2088 if (reason
->screen
.was_modified_by_ewmh_workingarea
== 1)
2091 s
, " (screen area modified by EWMH working"
2097 is_placed_by_algo
= 0;
2098 switch (reason
->pos
.reason
)
2101 is_placed_by_algo
= 1;
2102 r
= "normal placement";
2104 case PR_POS_IGNORE_PPOS
:
2105 is_placed_by_algo
= 1;
2106 r
= "ignored program specified position";
2108 case PR_POS_USE_PPOS
:
2109 r
= "used program specified position";
2111 case PR_POS_IGNORE_USPOS
:
2112 is_placed_by_algo
= 1;
2113 r
= "ignored user specified position";
2115 case PR_POS_USE_USPOS
:
2116 r
= "used user specified position";
2118 case PR_POS_PLACE_AGAIN
:
2119 is_placed_by_algo
= 1;
2120 r
= "by PlaceAgain command";
2122 case PR_POS_CAPTURE
:
2123 r
= "window was (re)captured";
2125 case PR_POS_USPOS_OVERRIDE_SOS
:
2126 r
= "StartsOnPage style overridden by application via USPos";
2132 sprintf(s
, " position %d %d", reason
->pos
.x
, reason
->pos
.y
);
2134 if (is_placed_by_algo
== 1)
2140 switch (reason
->pos
.algo
)
2142 case PLACE_POSITION
:
2143 a
= "Position args: ";
2144 b
= reason
->pos
.pl_position_string
;
2146 case PLACE_TILEMANUAL
:
2150 case PLACE_MANUAL_B
:
2153 case PLACE_MINOVERLAPPERCENT
:
2154 a
= "MinOverlapPercent";
2156 case PLACE_TILECASCADE
:
2160 case PLACE_CASCADE_B
:
2163 case PLACE_MINOVERLAP
:
2170 sprintf(s
, ", placed by fvwm (%s)\n", r
);
2172 sprintf(s
, " placement method: %s%s\n", a
, b
);
2174 if (reason
->pos
.do_not_manual_icon_placement
== 1)
2176 sprintf(s
, " (icon not placed manually)\n");
2179 if (reason
->pos
.is_pl_position_string_invalid
== 1)
2181 sprintf(s
, " (invalid position string)\n");
2184 if (reason
->pos
.has_tile_failed
== 1)
2186 sprintf(s
, " (tile placement failed)\n");
2189 if (reason
->pos
.has_manual_failed
== 1)
2191 sprintf(s
, " (manual placement failed)\n");
2194 if (reason
->pos
.has_placement_failed
== 1)
2196 sprintf(s
, " (placement failed default pos 0 0)\n");
2202 sprintf(s
, " (%s)\n", r
);
2205 if (reason
->pos
.do_adjust_off_screen
== 1)
2207 sprintf(s
, " (adjusted to force window on screen)\n");
2210 if (reason
->pos
.do_adjust_off_page
== 1)
2212 sprintf(s
, " (adjusted to force window on page)\n");
2216 INFO
, "__explain_placement", explanation
, (int)FW_W(fw
),
2222 /* ---------------------------- interface functions ------------------------ */
2224 Bool
setup_window_placement(
2225 FvwmWindow
*fw
, window_style
*pstyle
, rectangle
*attr_g
,
2226 initial_window_options_t
*win_opts
, placement_mode_t mode
)
2229 const exec_context_t
*exc
;
2230 exec_context_changes_t ecc
;
2232 pl_start_style_t start_style
;
2234 memset(&reason
, 0, sizeof(reason
));
2235 if (pstyle
->flags
.use_start_on_desk
)
2237 reason
.desk
.sod_reason
= PR_DESK_STYLE
;
2238 reason
.page
.px
= SGET_START_PAGE_X(*pstyle
);
2239 reason
.page
.py
= SGET_START_PAGE_Y(*pstyle
);
2241 if (pstyle
->flags
.use_start_on_screen
)
2243 reason
.screen
.reason
= PR_SCREEN_STYLE
;
2244 reason
.screen
.screen
= SGET_START_SCREEN(*pstyle
);
2246 __place_handle_x_resources(fw
, pstyle
, &reason
);
2247 if (pstyle
->flags
.do_start_iconic
)
2249 win_opts
->initial_state
= IconicState
;
2251 ecc
.type
= EXCT_NULL
;
2253 exc
= exc_create_context(&ecc
, ECC_TYPE
| ECC_FW
);
2254 start_style
.desk
= SGET_START_DESK(*pstyle
);
2255 start_style
.page_x
= SGET_START_PAGE_X(*pstyle
);
2256 start_style
.page_y
= SGET_START_PAGE_Y(*pstyle
);
2257 start_style
.screen
= SGET_START_SCREEN(*pstyle
);
2258 rc
= __place_window(
2259 exc
, pstyle
, attr_g
, start_style
, mode
, win_opts
, &reason
);
2260 exc_destroy_context(exc
);
2261 if (Scr
.bo
.do_explain_window_placement
== 1)
2263 __explain_placement(fw
, &reason
);
2266 return (rc
== 0) ? False
: True
;
2269 /* ---------------------------- builtin commands --------------------------- */
2271 void CMD_PlaceAgain(F_CMD_ARGS
)
2275 float noMovement
[1] = {1.0};
2276 float *ppctMovement
= noMovement
;
2278 XWindowAttributes attr
;
2279 Bool do_move_animated
= False
;
2280 Bool do_place_icon
= False
;
2281 FvwmWindow
* const fw
= exc
->w
.fw
;
2283 if (!XGetWindowAttributes(dpy
, FW_W(fw
), &attr
))
2287 while ((token
= PeekToken(action
, &action
)) != NULL
)
2289 if (StrEquals("Anim", token
))
2291 ppctMovement
= NULL
;
2292 do_move_animated
= True
;
2294 else if (StrEquals("icon", token
))
2296 do_place_icon
= True
;
2299 old_desk
= fw
->Desk
;
2300 if (IS_ICONIFIED(fw
) && !do_place_icon
)
2304 if (IS_ICONIFIED(fw
) && do_place_icon
)
2309 if (IS_ICON_SUPPRESSED(fw
))
2313 fw
->Desk
= Scr
.CurrentDesk
;
2314 get_icon_geometry(fw
, &old_g
);
2315 SET_ICON_MOVED(fw
, 0);
2316 AutoPlaceIcon(fw
, NULL
, False
);
2317 get_icon_geometry(fw
, &new_g
);
2319 fw
, new_g
.x
, new_g
.y
, old_g
.x
, old_g
.y
,
2320 do_move_animated
, False
);
2325 initial_window_options_t win_opts
;
2327 memset(&win_opts
, 0, sizeof(win_opts
));
2328 lookup_style(fw
, &style
);
2331 attr_g
.width
= attr
.width
;
2332 attr_g
.height
= attr
.height
;
2334 setup_window_placement(
2335 exc
->w
.fw
, &style
, &attr_g
, &win_opts
, PLACE_AGAIN
);
2336 AnimatedMoveFvwmWindow(
2337 fw
, FW_W_FRAME(fw
), -1, -1, attr_g
.x
, attr_g
.y
, False
,
2340 if (fw
->Desk
!= old_desk
)
2342 int new_desk
= fw
->Desk
;
2344 fw
->Desk
= old_desk
;
2345 do_move_window_to_desk(fw
, new_desk
);