NEWS/Changelog for previous commit.
[fvwm.git] / fvwm / placement.c
blob527f0d54a2c90f2187efc73ed7c9495cc84ec9d5
1 /* -*-c-*- */
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 ---------------------- */
19 #include "config.h"
21 #include <stdio.h>
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"
28 #include "fvwm.h"
29 #include "externs.h"
30 #include "execcontext.h"
31 #include "cursor.h"
32 #include "bindings.h"
33 #include "misc.h"
34 #include "screen.h"
35 #include "placement.h"
36 #include "geometry.h"
37 #include "update.h"
38 #include "style.h"
39 #include "move_resize.h"
40 #include "virtual.h"
41 #include "stack.h"
42 #include "ewmh.h"
43 #include "icons.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 ------------------------------- */
53 #ifndef MIN
54 #define MIN(A,B) ((A)<(B)? (A):(B))
55 #endif
56 #ifndef MAX
57 #define MAX(A,B) ((A)>(B)? (A):(B))
58 #endif
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 -------------------------------- */
78 typedef enum
80 PR_POS_NORMAL = 0,
81 PR_POS_IGNORE_PPOS,
82 PR_POS_USE_PPOS,
83 PR_POS_IGNORE_USPOS,
84 PR_POS_USE_USPOS,
85 PR_POS_PLACE_AGAIN,
86 PR_POS_CAPTURE,
87 PR_POS_USPOS_OVERRIDE_SOS
88 } preason_pos_t;
90 typedef enum
92 PR_SCREEN_CURRENT = 0,
93 PR_SCREEN_STYLE,
94 PR_SCREEN_X_RESOURCE_FVWMSCREEN,
95 PR_SCREEN_IGNORE_CAPTURE
96 } preason_screen_t;
98 typedef enum
100 PR_PAGE_CURRENT = 0,
101 PR_PAGE_STYLE,
102 PR_PAGE_X_RESOURCE_PAGE,
103 PR_PAGE_IGNORE_CAPTURE,
104 PR_PAGE_IGNORE_INVALID,
105 PR_PAGE_STICKY
106 } preason_page_t;
108 typedef enum
110 PR_DESK_CURRENT = 0,
111 PR_DESK_STYLE,
112 PR_DESK_X_RESOURCE_DESK,
113 PR_DESK_X_RESOURCE_PAGE,
114 PR_DESK_CAPTURE,
115 PR_DESK_STICKY,
116 PR_DESK_WINDOW_GROUP_LEADER,
117 PR_DESK_WINDOW_GROUP_MEMBER,
118 PR_DESK_TRANSIENT,
119 PR_DESK_XPROP_XA_WM_DESKTOP
120 } preason_desk_t;
122 typedef struct
124 struct
126 preason_pos_t reason;
127 int x;
128 int y;
129 int algo;
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;
138 } pos;
139 struct
141 preason_screen_t reason;
142 int screen;
143 rectangle g;
144 unsigned was_modified_by_ewmh_workingarea : 1;
145 } screen;
146 struct
148 preason_page_t reason;
149 int px;
150 int py;
151 unsigned do_switch_page : 1;
152 unsigned do_honor_starts_on_page : 1;
153 unsigned do_ignore_starts_on_page : 1;
154 } page;
155 struct
157 preason_desk_t reason;
158 preason_desk_t sod_reason;
159 int desk;
160 unsigned do_switch_desk : 1;
161 } desk;
162 } pl_reason_t;
164 typedef struct
166 int desk;
167 int page_x;
168 int page_y;
169 int screen;
170 } pl_start_style_t;
172 typedef struct
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;
178 } pl_flags_t;
180 typedef float pl_penalty_t;
182 typedef enum
184 PL_LOOP_END,
185 PL_LOOP_CONT
186 } pl_loop_rc_t;
188 struct pl_arg_t;
189 struct pl_ret_t;
191 typedef struct
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);
208 } pl_algo_t;
210 typedef struct pl_scratch_t
212 const pl_penalty_struct *pp;
213 const pl_percent_penalty_struct *ppp;
214 } pl_scratch_t;
216 typedef struct pl_arg_t
218 const pl_algo_t *algo;
219 const exec_context_t *exc;
220 const window_style *style;
221 pl_reason_t *reason;
222 FvwmWindow *place_fw;
223 pl_scratch_t *scratch;
224 rectangle place_g;
225 position place_p2;
226 rectangle screen_g;
227 position page_p1;
228 position page_p2;
229 position pdelta_p;
230 struct
232 unsigned use_percent : 1;
233 unsigned use_ewmh_dynamic_working_areapercent : 1;
234 unsigned do_honor_starts_on_page : 1;
235 } flags;
236 } pl_arg_t;
238 typedef struct pl_ret_t
240 position best_p;
241 pl_penalty_t best_penalty;
242 struct
244 unsigned do_resize_too : 1;
245 } flags;
246 } pl_ret_t;
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,
254 position hint_p);
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 =
274 NULL,
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 =
282 NULL,
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)
328 char *spos;
329 Bool fPointer;
330 int n;
331 int i;
332 Bool is_under_mouse;
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;
339 i = 1;
341 else if (StrEquals(spos, "Center"))
343 spos = DEFAULT_PLACEMENT_POS_CENTER_STRING;
344 i = 1;
346 else if (StrEquals(spos, "UnderMouse"))
348 spos = DEFAULT_PLACEMENT_POS_MOUSE_STRING;
349 i = 1;
350 is_under_mouse = True;
352 else
354 i = 0;
356 arg->reason->pos.pl_position_string = spos;
357 for (n = -1; i < 2 && n < 2; i++)
359 fPointer = False;
360 ret_p->x = 0;
361 ret_p->y = 0;
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;
366 if (n < 2)
368 arg->reason->pos.is_pl_position_string_invalid = 1;
371 if (n < 2)
373 /* bug */
374 abort();
376 if (is_under_mouse)
378 /* TA: 20090218: Try and keep the window on-screen if we
379 * can.
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;
409 return 0;
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)
417 size_borders b;
418 int w;
419 FvwmWindow *t;
421 t = (Scr.cascade_window != NULL) ? Scr.cascade_window : arg->place_fw;
422 w = t->title_thickness;
423 if (w == 0)
425 w = t->boundary_width;
427 if (w == 0)
429 w = PLACEMENT_FALLBACK_CASCADE_STEP;
432 if (Scr.cascade_window != NULL)
435 Scr.cascade_x += w;
436 Scr.cascade_y += w;
437 switch (GET_TITLE_DIR(t))
439 case DIR_S:
440 case DIR_N:
441 Scr.cascade_y += w;
442 break;
443 case DIR_E:
444 case DIR_W:
445 Scr.cascade_x += w;
446 break;
447 default:
448 break;
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 -
467 b.total_size.width;
468 Scr.cascade_x = arg->place_fw->title_thickness;
469 switch (GET_TITLE_DIR(t))
471 case DIR_E:
472 case DIR_W:
473 Scr.cascade_x += arg->place_fw->title_thickness;
474 default:
475 break;
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 -
481 b.total_size.height;
482 Scr.cascade_y = arg->place_fw->title_thickness;
483 switch (GET_TITLE_DIR(t))
485 case DIR_N:
486 case DIR_S:
487 Scr.cascade_y += arg->place_fw->title_thickness;
488 default:
489 break;
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;
503 return 0;
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)
511 ret_p->x = 0;
512 ret_p->y = 0;
513 if (GrabEm(CRS_POSITION, GRAB_NORMAL))
515 int DragWidth;
516 int DragHeight;
517 int mx;
518 int my;
520 /* Grabbed the pointer - continue */
521 MyXGrabServer(dpy);
522 if (
523 XGetGeometry(
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);
533 return -1;
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);
544 FScreenGetScrRect(
545 NULL, FSCREEN_GLOBAL, &mx, &my, NULL, NULL);
546 if (__move_loop(
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);
560 else
562 /* couldn't grab the pointer - better do something */
563 XBell(dpy, 0);
564 ret_p->x = 0;
565 ret_p->y = 0;
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;
574 return 0;
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;
588 int xnew;
589 int xtest;
590 int stickyx;
591 int stickyy;
592 int start,i;
593 int win_left;
594 rectangle g;
595 Bool rc;
596 int x;
597 int y;
599 x = arg->place_g.x;
600 y = arg->place_g.y;
601 if (arg->flags.use_percent == 1)
603 start = 0;
605 else
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;
613 if (xtest > x)
615 xnew = xtest;
617 /* test the borders of the working area */
618 xtest = arg->page_p1.x + Scr.Desktops->ewmh_working_area.x;
619 if (xtest > 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) -
626 arg->place_g.width;
627 if (xtest > x)
629 xnew = MIN(xnew, xtest);
631 /* Test the values of the right edges of every window */
632 for (
633 other_fw = Scr.FvwmRoot.next; other_fw != NULL;
634 other_fw = other_fw->next)
636 if (
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)))
642 continue;
644 if (IS_STICKY_ACROSS_PAGES(other_fw))
646 stickyx = arg->pdelta_p.x;
647 stickyy = arg->pdelta_p.y;
649 else
651 stickyx = 0;
652 stickyy = 0;
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 -
661 arg->place_g.width;
662 for (i = start; i <= CP_GET_NEXT_STEP; i++)
664 xtest = win_left + g.width *
665 (CP_GET_NEXT_STEP - i) /
666 CP_GET_NEXT_STEP;
667 if (xtest > x)
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 /
676 CP_GET_NEXT_STEP;
677 if (xtest > x)
679 xnew = MIN(xnew, xtest);
684 else if (
685 y < other_fw->g.frame.height + other_fw->g.frame.y -
686 stickyy &&
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)
693 win_left =
694 other_fw->g.frame.x - stickyx -
695 arg->place_g.width;
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) /
700 CP_GET_NEXT_STEP;
701 if (xtest > x)
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;
711 if (xtest > x)
713 xnew = MIN(xnew, xtest);
719 return xnew;
722 static int __pl_minoverlap_get_next_y(const pl_arg_t *arg)
724 FvwmWindow *other_fw;
725 int ynew;
726 int ytest;
727 int stickyy;
728 int win_top;
729 int start;
730 int i;
731 rectangle g;
732 int y;
734 y = arg->place_g.y;
735 if (arg->flags.use_percent == 1)
737 start = 0;
739 else
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;
747 if (ytest > y)
749 ynew = ytest;
751 /* test the borders of the working area */
752 ytest = arg->page_p1.y + Scr.Desktops->ewmh_working_area.y;
753 if (ytest > 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) -
760 arg->place_g.height;
761 if (ytest > y)
763 ynew = MIN(ynew, ytest);
765 /* Test the values of the bottom edge of every window */
766 for (
767 other_fw = Scr.FvwmRoot.next; other_fw != NULL;
768 other_fw = other_fw->next)
770 if (
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)))
777 continue;
780 if (IS_STICKY_ACROSS_PAGES(other_fw))
782 stickyy = arg->pdelta_p.y;
784 else
786 stickyy = 0;
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++)
795 ytest =
796 win_top + g.height * i /
797 CP_GET_NEXT_STEP;
798 if (ytest > y)
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++)
806 ytest =
807 win_top + g.height *
808 (CP_GET_NEXT_STEP - i) /
809 CP_GET_NEXT_STEP;
810 if (ytest > y)
812 ynew = MIN(ynew, ytest);
816 else
818 win_top = other_fw->g.frame.y - stickyy;
819 for (i = start; i <= CP_GET_NEXT_STEP; i++)
821 ytest =
822 win_top + other_fw->g.frame.height *
823 i / CP_GET_NEXT_STEP;
824 if (ytest > y)
826 ynew = MIN(ynew, ytest);
829 win_top = other_fw->g.frame.y - stickyy -
830 arg->place_g.height;
831 for (i = start; i <= CP_GET_NEXT_STEP; i++)
833 ytest =
834 win_top + other_fw->g.frame.height *
835 (CP_GET_NEXT_STEP - i) /
836 CP_GET_NEXT_STEP;
837 if (ytest > y)
839 ynew = MIN(ynew, ytest);
845 return ynew;
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;
855 return PL_LOOP_CONT;
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,
860 position hint_p)
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)
866 /* try next 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)
878 /* PageBottom */
879 return PL_LOOP_END;
882 return PL_LOOP_CONT;
885 static pl_penalty_t __pl_minoverlap_get_avoidance_penalty(
886 const pl_arg_t *arg, FvwmWindow *other_fw, const rectangle *other_g)
888 pl_penalty_t anew;
889 pl_penalty_t avoidance_factor;
890 position other_p2;
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);
922 else if (
923 IS_STICKY_ACROSS_PAGES(other_fw) ||
924 IS_STICKY_ACROSS_DESKS(other_fw))
926 avoidance_factor = STICKY_PLACEMENT_PENALTY(opp);
928 else
930 avoidance_factor = NORMAL_PLACEMENT_PENALTY(opp);
932 if (arg->flags.use_percent == 1)
934 pl_penalty_t cover_factor;
935 long other_area;
936 long place_area;
938 other_area = other_g->width * other_g->height;
939 place_area = arg->place_g.width * arg->place_g.height;
940 cover_factor = 0;
941 if (other_area != 0 && place_area != 0)
943 anew = 100 * MAX(anew / other_area, anew / place_area);
944 if (anew >= 99)
946 cover_factor = PERCENTAGE_99_PENALTY(oppp);
948 else if (anew > 94)
950 cover_factor = PERCENTAGE_95_PENALTY(oppp);
952 else if (anew > 84)
954 cover_factor = PERCENTAGE_85_PENALTY(oppp);
956 else if (anew > 74)
958 cover_factor = PERCENTAGE_75_PENALTY(oppp);
961 if (avoidance_factor >= 1)
963 avoidance_factor += cover_factor;
966 if (
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;
985 return anew;
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;
993 size_borders b;
995 penalty = 0;
996 for (
997 other_fw = Scr.FvwmRoot.next; other_fw != NULL;
998 other_fw = other_fw->next)
1000 rectangle other_g;
1001 Bool rc;
1002 get_window_borders(other_fw, &b);
1004 if (
1005 arg->place_fw == other_fw ||
1006 IS_EWMH_DESKTOP(FW_W(other_fw)))
1008 continue;
1010 /* RBW - account for sticky windows... */
1011 if (
1012 other_fw->Desk != arg->place_fw->Desk &&
1013 IS_STICKY_ACROSS_DESKS(other_fw) == 0)
1015 continue;
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;
1023 if (
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)
1029 pl_penalty_t anew;
1031 anew = __pl_minoverlap_get_avoidance_penalty(
1032 arg, other_fw, &other_g);
1033 penalty += anew;
1034 if (
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
1046 * off screen.
1048 if (ret->best_p.y + arg->place_g.height > arg->page_p2.y)
1050 ret->best_p.y =
1051 (arg->page_p2.y -
1052 arg->place_g.height -
1053 b.total_size.height);
1055 ret->best_penalty = 0;
1056 penalty = 0;
1059 /* stop looking; the penalty is too high */
1060 return penalty;
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);
1078 else
1080 /* EWMH_USE_DYNAMIC_WORKING_AREA, count the base strut
1082 penalty +=
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);
1091 return penalty;
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)
1099 pl_penalty_t p;
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);
1104 if (p != 0)
1106 p = -1;
1109 return p;
1112 /* ---------------------------- local functions ---------------------------- */
1114 static int placement_loop(pl_ret_t *ret, pl_arg_t *arg)
1116 position next_p;
1117 pl_penalty_t penalty;
1118 pl_loop_rc_t loop_rc;
1120 if (arg->algo->get_pos_simple != NULL)
1122 position pos;
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;
1132 else
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)
1142 position hint_p;
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! */
1154 if (
1155 penalty >= 0 &&
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;
1164 if (penalty == 0)
1166 break;
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)
1184 Bool override_ppos;
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.
1194 * OR
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);
1206 else
1208 override_ppos = SUSE_NO_PPOSITION(&pstyle->flags);
1209 override_uspos = SUSE_NO_USPOSITION(&pstyle->flags);
1211 if (fw->hints.flags & PPosition)
1213 if (!override_ppos)
1215 has_ppos = True;
1216 reason->pos.reason = PR_POS_USE_PPOS;
1218 else
1220 reason->pos.reason = PR_POS_IGNORE_PPOS;
1223 if (fw->hints.flags & USPosition)
1225 if (!override_uspos)
1227 has_uspos = True;
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;
1257 return;
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;
1268 num_algos++;
1270 return num_algos;
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];
1280 int num_algos;
1281 unsigned int placement_mode = SPLACEMENT_MODE(&pstyle->flags);
1282 pl_arg_t arg;
1283 pl_ret_t ret;
1284 int i;
1286 /* BEGIN init placement agrs and ret */
1287 memset(&arg, 0, sizeof(arg));
1288 arg.exc = exc;
1289 arg.style = pstyle;
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)
1315 case PLACE_MANUAL:
1316 case PLACE_MANUAL_B:
1317 placement_mode = PLACE_CASCADE;
1318 break;
1319 case PLACE_TILEMANUAL:
1320 placement_mode = PLACE_TILECASCADE;
1321 break;
1322 default:
1323 break;
1326 reason->pos.algo = placement_mode;
1327 /* first, try various "smart" placement */
1328 num_algos = 0;
1329 switch (placement_mode)
1331 case PLACE_POSITION:
1332 num_algos = __add_algo(
1333 algos, num_algos, &position_placement_algo);
1334 break;
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);
1340 break;
1341 case PLACE_MINOVERLAPPERCENT:
1342 arg.flags.use_percent = 1;
1343 /* fall through */
1344 case PLACE_MINOVERLAP:
1345 num_algos = __add_algo(
1346 algos, num_algos, &minoverlap_placement_algo);
1347 break;
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);
1353 break;
1354 case PLACE_MANUAL:
1355 case PLACE_MANUAL_B:
1356 num_algos = __add_algo(
1357 algos, num_algos, &manual_placement_algo);
1358 break;
1359 case PLACE_CASCADE:
1360 case PLACE_CASCADE_B:
1361 num_algos = __add_algo(
1362 algos, num_algos, &cascade_placement_algo);
1363 break;
1364 default:
1365 /* can't happen */
1366 break;
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;
1380 else
1382 /* fall back to default position */
1383 attr_g->x = 0;
1384 attr_g->y = 0;
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;
1398 size_borders b;
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 */
1406 if (
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
1427 * screen. */
1428 mangle_screen = FScreenFetchMangledScreenFromUSPosHints(
1429 &(fw->hints));
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
1453 * StartsOnPage. */
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))
1457 #if 0
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
1460 * position. */
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)
1469 #endif
1472 int old_x;
1473 int old_y;
1475 old_x = attr_g->x;
1476 old_y = attr_g->y;
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. */
1484 if (attr_g->x < 0)
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 */
1495 if (attr_g->y < 0)
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 */
1509 rectangle final_g;
1510 int gravx;
1511 int gravy;
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;
1527 final_g.width = 0;
1528 final_g.height = 0;
1529 if (mode == PLACE_INITIAL)
1531 get_window_borders(fw, &b);
1532 gravity_resize(
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;
1540 return 0;
1543 /* Handles initial placement and sizing of a new window
1545 * Return value:
1547 * 0 = window lost
1548 * 1 = OK
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)
1555 FvwmWindow *t;
1556 int is_skipmapping_forbidden;
1557 int px = 0;
1558 int py = 0;
1559 int pdeltax = 0;
1560 int pdeltay = 0;
1561 rectangle screen_g;
1562 int rc = 0;
1563 pl_flags_t flags;
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
1575 * is used). */
1577 /* Let's get the StartsOnDesk/Page tests out of the way first. */
1578 if (
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))
1616 case PLACE_MANUAL:
1617 case PLACE_MANUAL_B:
1618 case PLACE_TILEMANUAL:
1619 is_skipmapping_forbidden =
1620 !SMANUAL_PLACEMENT_HONORS_STARTS_ON_PAGE(
1621 &pstyle->flags);
1622 break;
1623 default:
1624 is_skipmapping_forbidden = 0;
1625 break;
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;
1637 fvwm_msg(
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 */
1653 FScreenGetScrRect(
1654 NULL, start_style.screen, &screen_g.x,
1655 &screen_g.y, &screen_g.width,
1656 &screen_g.height);
1657 reason->screen.screen = start_style.screen;
1659 else
1661 /* use global screen */
1662 FScreenGetScrRect(
1663 NULL, FSCREEN_GLOBAL, &screen_g.x, &screen_g.y,
1664 &screen_g.width, &screen_g.height);
1665 reason->screen.screen = FSCREEN_GLOBAL;
1668 else
1670 /* use current screen */
1671 FScreenGetScrRect(
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,
1682 &screen_g.height,
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;
1693 else
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;
1709 else
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
1717 * the group */
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 */
1723 fw->Desk = t->Desk;
1724 reason->desk.reason =
1725 PR_DESK_WINDOW_GROUP_LEADER;
1726 break;
1728 else if (t->wmhints &&
1729 (t->wmhints->flags &
1730 WindowGroupHint) &&
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
1736 * leader */
1737 fw->Desk = t->Desk;
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))
1751 fw->Desk = t->Desk;
1752 reason->desk.reason =
1753 PR_DESK_TRANSIENT;
1754 break;
1760 /* migo - I am not sure this block is ever needed */
1762 Atom atype;
1763 int aformat;
1764 unsigned long nitems, bytes_remain;
1765 unsigned char *prop;
1767 if (
1768 XGetWindowProperty(
1769 dpy, FW_W(fw), _XA_WM_DESKTOP, 0L, 1L,
1770 True, _XA_WM_DESKTOP, &atype, &aformat,
1771 &nitems, &bytes_remain, &prop) ==
1772 Success)
1774 if (prop != NULL)
1776 fw->Desk = *(unsigned long *)prop;
1777 XFree(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);
1837 else
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;
1846 return rc;
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;
1854 XrmValue rm_value;
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
1871 * line.
1872 * RBW - 11/20/1998 - allow a desk of -1 to work. */
1873 if (XGetCommand(dpy, FW_W(fw), &client_argv, &client_argc) == 0)
1875 return;
1877 if (client_argc <= 0 || client_argv == NULL)
1879 return;
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,
1886 True);
1887 /* parse the database values */
1888 if (GetResourceString(db, "desk", client_argv[0], &rm_value) &&
1889 rm_value.size != 0)
1891 SGET_START_DESK(*pstyle) = atoi(rm_value.addr);
1892 /* RBW - 11/20/1998 */
1893 if (SGET_START_DESK(*pstyle) > -1)
1895 SSET_START_DESK(
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) &&
1902 rm_value.size != 0)
1904 SSET_START_SCREEN(
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) &&
1911 rm_value.size != 0)
1913 spargs = sscanf(
1914 rm_value.addr, "%d %d %d", &t1, &t2, &t3);
1915 switch (spargs)
1917 case 1:
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;
1921 break;
1922 case 2:
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);
1929 break;
1930 case 3:
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);
1940 break;
1941 default:
1942 break;
1945 XFreeStringList(client_argv);
1946 XrmDestroyDatabase(db);
1948 return;
1951 static void __explain_placement(FvwmWindow *fw, pl_reason_t *reason)
1953 char explanation[2048];
1954 char *r;
1955 char *s;
1956 char t[32];
1957 int do_show_page;
1958 int is_placed_by_algo;
1960 *explanation = 0;
1961 s = explanation;
1962 strcat(s, "placed new window 0x%x '%s':\n");
1963 s += strlen(s);
1964 sprintf(
1965 s, " initial size %dx%d\n", fw->g.frame.width,
1966 fw->g.frame.height);
1967 s += strlen(s);
1968 switch (reason->desk.reason)
1970 case PR_DESK_CURRENT:
1971 r = "current desk";
1972 break;
1973 case PR_DESK_STYLE:
1974 r = "specified by style";
1975 break;
1976 case PR_DESK_X_RESOURCE_DESK:
1977 r = "specified by 'desk' X resource";
1978 break;
1979 case PR_DESK_X_RESOURCE_PAGE:
1980 r = "specified by 'page' X resource";
1981 break;
1982 case PR_DESK_CAPTURE:
1983 r = "window was (re)captured";
1984 break;
1985 case PR_DESK_STICKY:
1986 r = "window is sticky";
1987 break;
1988 case PR_DESK_WINDOW_GROUP_LEADER:
1989 r = "same desk as window group leader";
1990 break;
1991 case PR_DESK_WINDOW_GROUP_MEMBER:
1992 r = "same desk as window group member";
1993 break;
1994 case PR_DESK_TRANSIENT:
1995 r = "transient window placed on same desk as parent";
1996 break;
1997 case PR_DESK_XPROP_XA_WM_DESKTOP:
1998 r = "specified by _XA_WM_DESKTOP property";
1999 break;
2000 default:
2001 r = "bug";
2002 break;
2004 sprintf(s, " desk %d (%s)\n", reason->desk.desk, r);
2005 s += strlen(s);
2006 if (reason->desk.do_switch_desk == 1)
2008 sprintf(s, " (switched to desk)\n");
2009 s += strlen(s);
2011 /* page */
2012 do_show_page = 1;
2013 switch (reason->page.reason)
2015 case PR_PAGE_CURRENT:
2016 do_show_page = 0;
2017 r = "current page";
2018 break;
2019 case PR_PAGE_STYLE:
2020 r = "specified by style";
2021 break;
2022 case PR_PAGE_X_RESOURCE_PAGE:
2023 r = "specified by 'page' X resource";
2024 break;
2025 case PR_PAGE_IGNORE_CAPTURE:
2026 r = "window was (re)captured";
2027 break;
2028 case PR_PAGE_IGNORE_INVALID:
2029 r = "requested page ignored because of invalid style"
2030 " combination";
2031 break;
2032 case PR_PAGE_STICKY:
2033 do_show_page = 0;
2034 r = "current page (window is sticky)";
2035 break;
2036 default:
2037 r = "bug";
2038 break;
2040 if (do_show_page == 0)
2042 sprintf(s, " %s\n", r);
2044 else
2046 sprintf(
2047 s, " page %d %d (%s)\n", reason->page.px - 1,
2048 reason->page.py - 1, r);
2050 s += strlen(s);
2051 if (reason->page.do_switch_page == 1)
2053 sprintf(s, " (switched to page)\n");
2054 s += strlen(s);
2056 if (reason->page.do_ignore_starts_on_page == 1)
2058 sprintf(s, " (possibly ignored StartsOnPage)\n");
2059 s += strlen(s);
2061 /* screen */
2062 if (FScreenIsEnabled() == True || FScreenIsSLSEnabled() == True)
2064 switch (reason->screen.reason)
2066 case PR_SCREEN_CURRENT:
2067 r = "current screen";
2068 break;
2069 case PR_SCREEN_STYLE:
2070 r = "specified by style";
2071 break;
2072 case PR_SCREEN_X_RESOURCE_FVWMSCREEN:
2073 r = "specified by 'fvwmscreen' X resource";
2074 break;
2075 case PR_SCREEN_IGNORE_CAPTURE:
2076 r = "window was (re)captured";
2077 break;
2078 default:
2079 r = "bug";
2080 break;
2082 FScreenSpecToString(t, 32, reason->screen.screen);
2083 sprintf(
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);
2087 s += strlen(s);
2088 if (reason->screen.was_modified_by_ewmh_workingarea == 1)
2090 sprintf(
2091 s, " (screen area modified by EWMH working"
2092 " area)\n");
2093 s += strlen(s);
2096 /* position */
2097 is_placed_by_algo = 0;
2098 switch (reason->pos.reason)
2100 case PR_POS_NORMAL:
2101 is_placed_by_algo = 1;
2102 r = "normal placement";
2103 break;
2104 case PR_POS_IGNORE_PPOS:
2105 is_placed_by_algo = 1;
2106 r = "ignored program specified position";
2107 break;
2108 case PR_POS_USE_PPOS:
2109 r = "used program specified position";
2110 break;
2111 case PR_POS_IGNORE_USPOS:
2112 is_placed_by_algo = 1;
2113 r = "ignored user specified position";
2114 break;
2115 case PR_POS_USE_USPOS:
2116 r = "used user specified position";
2117 break;
2118 case PR_POS_PLACE_AGAIN:
2119 is_placed_by_algo = 1;
2120 r = "by PlaceAgain command";
2121 break;
2122 case PR_POS_CAPTURE:
2123 r = "window was (re)captured";
2124 break;
2125 case PR_POS_USPOS_OVERRIDE_SOS:
2126 r = "StartsOnPage style overridden by application via USPos";
2127 break;
2128 default:
2129 r = "bug";
2130 break;
2132 sprintf(s, " position %d %d", reason->pos.x, reason->pos.y);
2133 s += strlen(s);
2134 if (is_placed_by_algo == 1)
2136 char *a;
2137 char *b;
2139 b = "";
2140 switch (reason->pos.algo)
2142 case PLACE_POSITION:
2143 a = "Position args: ";
2144 b = reason->pos.pl_position_string;
2145 break;
2146 case PLACE_TILEMANUAL:
2147 a = "TileManual";
2148 break;
2149 case PLACE_MANUAL:
2150 case PLACE_MANUAL_B:
2151 a = "Manual";
2152 break;
2153 case PLACE_MINOVERLAPPERCENT:
2154 a = "MinOverlapPercent";
2155 break;
2156 case PLACE_TILECASCADE:
2157 a = "TileCascade";
2158 break;
2159 case PLACE_CASCADE:
2160 case PLACE_CASCADE_B:
2161 a = "Cascade";
2162 break;
2163 case PLACE_MINOVERLAP:
2164 a = "MinOverlap";
2165 break;
2166 default:
2167 a = "bug";
2168 break;
2170 sprintf(s, ", placed by fvwm (%s)\n", r);
2171 s += strlen(s);
2172 sprintf(s, " placement method: %s%s\n", a, b);
2173 s += strlen(s);
2174 if (reason->pos.do_not_manual_icon_placement == 1)
2176 sprintf(s, " (icon not placed manually)\n");
2177 s += strlen(s);
2179 if (reason->pos.is_pl_position_string_invalid == 1)
2181 sprintf(s, " (invalid position string)\n");
2182 s += strlen(s);
2184 if (reason->pos.has_tile_failed == 1)
2186 sprintf(s, " (tile placement failed)\n");
2187 s += strlen(s);
2189 if (reason->pos.has_manual_failed == 1)
2191 sprintf(s, " (manual placement failed)\n");
2192 s += strlen(s);
2194 if (reason->pos.has_placement_failed == 1)
2196 sprintf(s, " (placement failed default pos 0 0)\n");
2197 s += strlen(s);
2200 else
2202 sprintf(s, " (%s)\n", r);
2203 s += strlen(s);
2205 if (reason->pos.do_adjust_off_screen == 1)
2207 sprintf(s, " (adjusted to force window on screen)\n");
2208 s += strlen(s);
2210 if (reason->pos.do_adjust_off_page == 1)
2212 sprintf(s, " (adjusted to force window on page)\n");
2213 s += strlen(s);
2215 fvwm_msg(
2216 INFO, "__explain_placement", explanation, (int)FW_W(fw),
2217 fw->name.name);
2219 return;
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)
2228 int rc;
2229 const exec_context_t *exc;
2230 exec_context_changes_t ecc;
2231 pl_reason_t reason;
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;
2252 ecc.w.fw = fw;
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)
2273 int old_desk;
2274 char *token;
2275 float noMovement[1] = {1.0};
2276 float *ppctMovement = noMovement;
2277 rectangle attr_g;
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))
2285 return;
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)
2302 return;
2304 if (IS_ICONIFIED(fw) && do_place_icon)
2306 rectangle new_g;
2307 rectangle old_g;
2309 if (IS_ICON_SUPPRESSED(fw))
2311 return;
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);
2318 __move_icon(
2319 fw, new_g.x, new_g.y, old_g.x, old_g.y,
2320 do_move_animated, False);
2322 else
2324 window_style style;
2325 initial_window_options_t win_opts;
2327 memset(&win_opts, 0, sizeof(win_opts));
2328 lookup_style(fw, &style);
2329 attr_g.x = attr.x;
2330 attr_g.y = attr.y;
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,
2338 -1, ppctMovement);
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);
2348 return;