Fix ExplainWindowPlacement when using "NoUSPosition" style.
[fvwm.git] / fvwm / geometry.c
blobf6eeeebeae9afda0014a615ff5688df1382463f5
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/charmap.h"
25 #include "libs/wcontext.h"
26 #include "fvwm.h"
27 #include "externs.h"
28 #include "execcontext.h"
29 #include "misc.h"
30 #include "screen.h"
31 #include "geometry.h"
32 #include "module_interface.h"
33 #include "borders.h"
34 #include "icons.h"
35 #include "add_window.h"
37 /* ---------------------------- local definitions -------------------------- */
39 /* ---------------------------- local macros ------------------------------- */
41 /* ---------------------------- imports ------------------------------------ */
43 /* ---------------------------- included code files ------------------------ */
45 /* ---------------------------- local types -------------------------------- */
47 /* ---------------------------- forward declarations ----------------------- */
49 /* ---------------------------- local variables ---------------------------- */
51 /* ---------------------------- exported variables (globals) --------------- */
53 /* ---------------------------- local functions ---------------------------- */
55 /* ---------------------------- interface functions ------------------------ */
57 /* Removes decorations from the source rectangle and moves it according to the
58 * gravity specification. */
59 void gravity_get_naked_geometry(
60 int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
62 int xoff;
63 int yoff;
64 size_borders b;
66 get_window_borders(t, &b);
67 gravity_get_offsets(gravity, &xoff, &yoff);
68 dest_g->x = orig_g->x + ((xoff + 1) * (orig_g->width - 1)) / 2;
69 dest_g->y = orig_g->y + ((yoff + 1) * (orig_g->height - 1)) / 2;
70 dest_g->width = orig_g->width - b.total_size.width;
71 dest_g->height = orig_g->height - b.total_size.height;
73 return;
76 /* Decorate the rectangle. Resize and shift it according to gravity. */
77 void gravity_add_decoration(
78 int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
80 size_borders b;
82 get_window_borders(t, &b);
83 *dest_g = *orig_g;
84 gravity_resize(
85 gravity, dest_g, b.total_size.width, b.total_size.height);
87 return;
90 void get_relative_geometry(rectangle *rel_g, rectangle *abs_g)
92 rel_g->x = abs_g->x - Scr.Vx;
93 rel_g->y = abs_g->y - Scr.Vy;
94 rel_g->width = abs_g->width;
95 rel_g->height = abs_g->height;
97 return;
100 void get_absolute_geometry(rectangle *abs_g, rectangle *rel_g)
102 abs_g->x = rel_g->x + Scr.Vx;
103 abs_g->y = rel_g->y + Scr.Vy;
104 abs_g->width = rel_g->width;
105 abs_g->height = rel_g->height;
107 return;
110 void gravity_translate_to_northwest_geometry(
111 int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
113 int xoff;
114 int yoff;
116 gravity_get_offsets(gravity, &xoff, &yoff);
117 dest_g->x = orig_g->x -
118 ((xoff + 1) * (orig_g->width - 1 +
119 2 * t->attr_backup.border_width)) / 2;
120 dest_g->y = orig_g->y -
121 ((yoff + 1) * (orig_g->height - 1 +
122 2 * t->attr_backup.border_width)) / 2;
123 dest_g->width = orig_g->width;
124 dest_g->height = orig_g->height;
126 return;
129 void gravity_translate_to_northwest_geometry_no_bw(
130 int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
132 int bw = t->attr_backup.border_width;
134 t->attr_backup.border_width = 0;
135 gravity_translate_to_northwest_geometry(gravity, t, dest_g, orig_g);
136 t->attr_backup.border_width = bw;
138 return;
141 void get_title_geometry(
142 FvwmWindow *fw, rectangle *ret_g)
144 size_borders b;
145 size_borders nt;
146 int w;
147 int h;
149 get_window_borders(fw, &b);
150 get_window_borders_no_title(fw, &nt);
151 w = (ret_g->width > 0) ? ret_g->width : fw->g.frame.width;
152 h = (ret_g->height > 0) ? ret_g->height : fw->g.frame.height;
153 ret_g->x = nt.top_left.width;
154 ret_g->y = nt.top_left.height;
155 switch (GET_TITLE_DIR(fw))
157 case DIR_S:
158 ret_g->y = h - b.bottom_right.height;
159 /* fall through */
160 case DIR_N:
161 ret_g->width = w - b.total_size.width;
162 ret_g->height = fw->title_thickness;
163 break;
164 case DIR_E:
165 ret_g->x = w - b.bottom_right.width;
166 /* fall through */
167 case DIR_W:
168 ret_g->width = fw->title_thickness;
169 ret_g->height = h - b.total_size.height;
170 break;
171 default:
172 break;
175 return;
178 void get_title_gravity_factors(
179 FvwmWindow *fw, int *ret_fx, int *ret_fy)
181 switch (GET_TITLE_DIR(fw))
183 case DIR_N:
184 *ret_fx = 0;
185 *ret_fy = 1;
186 break;
187 case DIR_S:
188 *ret_fx = 0;
189 *ret_fy = -1;
190 break;
191 case DIR_W:
192 *ret_fx = 1;
193 *ret_fy = 0;
194 break;
195 case DIR_E:
196 *ret_fx = -1;
197 *ret_fy = 0;
198 break;
201 return;
204 Bool get_title_button_geometry(
205 FvwmWindow *fw, rectangle *ret_g, int context)
207 int bnum;
209 if (context & C_TITLE)
211 ret_g->width = 0;
212 ret_g->height = 0;
213 get_title_geometry(fw, ret_g);
214 ret_g->x += fw->g.frame.x;
215 ret_g->y += fw->g.frame.y;
217 return True;
220 bnum = get_button_number(context);
221 if (bnum < 0 || FW_W_BUTTON(fw, bnum) == None)
223 return False;
225 if (XGetGeometry(
226 dpy, FW_W_BUTTON(fw, bnum), &JunkRoot, &ret_g->x, &ret_g->y,
227 (unsigned int*)&ret_g->width, (unsigned int*)&ret_g->height,
228 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
230 return False;
232 XTranslateCoordinates(
233 dpy, FW_W_FRAME(fw), Scr.Root, ret_g->x, ret_g->y, &ret_g->x,
234 &ret_g->y, &JunkChild);
236 return True;
239 void get_title_font_size_and_offset(
240 FvwmWindow *fw, direction_t title_dir,
241 Bool is_left_title_rotated_cw, Bool is_right_title_rotated_cw,
242 Bool is_top_title_rotated, Bool is_bottom_title_rotated,
243 int *size, int *offset)
245 int decor_size;
246 int extra_size;
247 int font_size;
248 int min_offset;
249 Bool is_rotated_cw, is_rotated;
250 rotation_t draw_rotation;
252 /* adjust font offset according to height specified in title style */
253 decor_size = fw->decor->title_height;
254 font_size = fw->title_font->height + EXTRA_TITLE_FONT_HEIGHT;
255 switch (title_dir)
257 case DIR_W:
258 case DIR_E:
259 is_rotated_cw = (title_dir == DIR_W) ?
260 is_left_title_rotated_cw : is_right_title_rotated_cw;
261 if (is_rotated_cw)
263 fw->title_text_rotation = ROTATION_90;
265 else
267 fw->title_text_rotation = ROTATION_270;
269 break;
270 case DIR_N:
271 case DIR_S:
272 default:
273 is_rotated = (title_dir == DIR_N) ?
274 is_top_title_rotated : is_bottom_title_rotated;
275 if (is_rotated)
277 fw->title_text_rotation = ROTATION_180;
279 else
281 fw->title_text_rotation = ROTATION_0;
283 break;
285 if (USE_TITLE_DECOR_ROTATION(fw))
287 draw_rotation = ROTATION_0;
289 else
291 draw_rotation = fw->title_text_rotation;
293 min_offset = FlocaleGetMinOffset(
294 fw->title_font, draw_rotation);
295 extra_size = (decor_size > 0) ? decor_size - font_size : 0;
296 *offset = min_offset;
297 if (fw->decor->min_title_height > 0 &&
298 font_size + extra_size < fw->decor->min_title_height)
300 extra_size = fw->decor->min_title_height - font_size;
302 if (extra_size > 0)
304 *offset += extra_size / 2;
306 *size = font_size + extra_size;
308 return;
311 void get_icon_corner(
312 FvwmWindow *fw, rectangle *ret_g)
314 switch (GET_TITLE_DIR(fw))
316 case DIR_N:
317 case DIR_W:
318 ret_g->x = fw->g.frame.x;
319 ret_g->y = fw->g.frame.y;
320 break;
321 case DIR_S:
322 ret_g->x = fw->g.frame.x;
323 ret_g->y = fw->g.frame.y + fw->g.frame.height -
324 ret_g->height;
325 break;
326 case DIR_E:
327 ret_g->x = fw->g.frame.x + fw->g.frame.width -
328 ret_g->width;
329 ret_g->y = fw->g.frame.y;
330 break;
333 return;
336 void get_shaded_geometry(
337 FvwmWindow *fw, rectangle *small_g, rectangle *big_g)
339 size_borders b;
340 /* this variable is necessary so the function can be called with
341 * small_g == big_g */
342 int big_width = big_g->width;
343 int big_height = big_g->height;
344 int d;
346 get_window_borders(fw, &b);
347 *small_g = *big_g;
348 d = 0;
349 switch (SHADED_DIR(fw))
351 case DIR_S:
352 case DIR_SW:
353 case DIR_SE:
354 small_g->y = big_g->y + big_height - b.total_size.height;
355 d = 1;
356 /* fall through */
357 case DIR_N:
358 case DIR_NW:
359 case DIR_NE:
360 small_g->height = b.total_size.height;
361 if (small_g->height == 0)
363 small_g->height = 1;
364 small_g->y -= d;
366 break;
367 default:
368 break;
370 d = 0;
371 switch (SHADED_DIR(fw))
373 case DIR_E:
374 case DIR_NE:
375 case DIR_SE:
376 small_g->x = big_g->x + big_width - b.total_size.width;
377 d = 1;
378 /* fall through */
379 case DIR_W:
380 case DIR_NW:
381 case DIR_SW:
382 small_g->width = b.total_size.width;
383 if (small_g->width == 0)
385 small_g->width = 1;
386 small_g->x -= d;
388 break;
389 default:
390 break;
393 return;
396 void get_shaded_geometry_with_dir(
397 FvwmWindow *fw, rectangle *small_g, rectangle *big_g,
398 direction_t shade_dir)
400 direction_t old_shade_dir;
402 old_shade_dir = SHADED_DIR(fw);
403 SET_SHADED_DIR(fw, shade_dir);
404 get_shaded_geometry(fw, small_g, big_g);
405 SET_SHADED_DIR(fw, old_shade_dir);
407 return;
410 void get_unshaded_geometry(
411 FvwmWindow *fw, rectangle *ret_g)
413 if (IS_SHADED(fw))
415 if (IS_MAXIMIZED(fw))
417 *ret_g = fw->g.max;
419 else
421 *ret_g = fw->g.normal;
423 get_relative_geometry(ret_g, ret_g);
425 else
427 *ret_g = fw->g.frame;
430 return;
433 void get_shaded_client_window_pos(
434 FvwmWindow *fw, rectangle *ret_g)
436 rectangle big_g;
437 size_borders b;
439 get_window_borders(fw, &b);
440 big_g = (IS_MAXIMIZED(fw)) ? fw->g.max : fw->g.normal;
441 get_relative_geometry(&big_g, &big_g);
442 switch (SHADED_DIR(fw))
444 case DIR_S:
445 case DIR_SW:
446 case DIR_SE:
447 ret_g->y = 1 - big_g.height + b.total_size.height;
448 break;
449 default:
450 ret_g->y = 0;
451 break;
453 switch (SHADED_DIR(fw))
455 case DIR_E:
456 case DIR_NE:
457 case DIR_SE:
458 ret_g->x = 1 - big_g.width + b.total_size.width;
459 break;
460 default:
461 ret_g->x = 0;
462 break;
465 return;
468 /* returns the dimensions of the borders */
469 void get_window_borders(
470 const FvwmWindow *fw, size_borders *borders)
472 borders->top_left.width = fw->boundary_width;
473 borders->bottom_right.width = fw->boundary_width;
474 borders->top_left.height = fw->boundary_width;
475 borders->bottom_right.height = fw->boundary_width;
476 switch (GET_TITLE_DIR(fw))
478 case DIR_N:
479 borders->top_left.height += fw->title_thickness;
480 break;
481 case DIR_S:
482 borders->bottom_right.height += fw->title_thickness;
483 break;
484 case DIR_W:
485 borders->top_left.width += fw->title_thickness;
486 break;
487 case DIR_E:
488 borders->bottom_right.width += fw->title_thickness;
489 break;
491 borders->total_size.width =
492 borders->top_left.width + borders->bottom_right.width;
493 borders->total_size.height =
494 borders->top_left.height + borders->bottom_right.height;
496 return;
499 /* returns the dimensions of the borders without the title */
500 void get_window_borders_no_title(
501 const FvwmWindow *fw, size_borders *borders)
503 borders->top_left.width = fw->boundary_width;
504 borders->bottom_right.width = fw->boundary_width;
505 borders->top_left.height = fw->boundary_width;
506 borders->bottom_right.height = fw->boundary_width;
507 borders->total_size.width =
508 borders->top_left.width + borders->bottom_right.width;
509 borders->total_size.height =
510 borders->top_left.height + borders->bottom_right.height;
512 return;
515 void set_window_border_size(
516 FvwmWindow *fw, int used_width)
518 if (used_width <= 0)
520 fw->boundary_width = 0;
521 fw->unshaped_boundary_width = 0;
523 else
525 fw->unshaped_boundary_width = used_width;
526 fw->boundary_width = (fw->wShaped) ? 0 : used_width;
529 return;
532 /* Returns True if all window borders are only 1 pixel thick (or less). */
533 Bool is_window_border_minimal(
534 FvwmWindow *fw)
536 size_borders nt;
538 get_window_borders_no_title(fw, &nt);
539 if (nt.top_left.width > 1 || nt.top_left.height > 1 ||
540 nt.bottom_right.width > 1 || nt.bottom_right.height > 1)
542 return False;
545 return True;
549 /* This function returns the geometry of the client window. If the window is
550 * shaded, the unshaded geometry is used instead. */
551 void get_client_geometry(
552 FvwmWindow *fw, rectangle *ret_g)
554 size_borders borders;
556 get_unshaded_geometry(fw, ret_g);
557 get_window_borders(fw, &borders);
558 ret_g->x += borders.top_left.width;
559 ret_g->y += borders.top_left.height;
560 ret_g->width -= borders.total_size.width;
561 ret_g->height -= borders.total_size.height;
563 return;
566 /* update the frame_g according to the window's g.normal or g.max and shaded
567 * state */
568 void update_relative_geometry(FvwmWindow *fw)
570 get_relative_geometry(
571 &fw->g.frame,
572 (IS_MAXIMIZED(fw)) ? &fw->g.max : &fw->g.normal);
573 if (IS_SHADED(fw))
575 get_shaded_geometry(
576 fw, &fw->g.frame, &fw->g.frame);
579 return;
582 /* update the g.normal or g.max according to the window's current position */
583 void update_absolute_geometry(FvwmWindow *fw)
585 rectangle *dest_g;
586 rectangle frame_g;
588 /* store orig values in absolute coords */
589 dest_g = (IS_MAXIMIZED(fw)) ? &fw->g.max : &fw->g.normal;
590 frame_g = *dest_g;
591 dest_g->x = fw->g.frame.x + Scr.Vx;
592 dest_g->y = fw->g.frame.y + Scr.Vy;
593 dest_g->width = fw->g.frame.width;
594 dest_g->height = fw->g.frame.height;
595 if (IS_SHADED(fw))
597 switch (SHADED_DIR(fw))
599 case DIR_SW:
600 case DIR_S:
601 case DIR_SE:
602 dest_g->y += fw->g.frame.height - frame_g.height;
603 /* fall through */
604 case DIR_NW:
605 case DIR_N:
606 case DIR_NE:
607 dest_g->height = frame_g.height;
608 break;
610 switch (SHADED_DIR(fw))
612 case DIR_NE:
613 case DIR_E:
614 case DIR_SE:
615 dest_g->x += fw->g.frame.width - frame_g.width;
616 /* fall through */
617 case DIR_NW:
618 case DIR_W:
619 case DIR_SW:
620 dest_g->width = frame_g.width;
621 break;
625 return;
628 /* make sure a maximized window and it's normal version are never a page or
629 * more apart. */
630 void maximize_adjust_offset(FvwmWindow *fw)
632 int off_x;
633 int off_y;
634 int dh;
635 int dw;
637 if (!IS_MAXIMIZED(fw))
639 /* otherwise we might corrupt the g.normal */
640 return;
642 off_x = fw->g.normal.x - fw->g.max.x - fw->g.max_offset.x;
643 off_y = fw->g.normal.y - fw->g.max.y - fw->g.max_offset.y;
644 dw = Scr.MyDisplayWidth;
645 dh = Scr.MyDisplayHeight;
646 if (off_x >= dw)
648 fw->g.normal.x -= (off_x / dw) * dw;
650 else if (off_x <= -dw)
652 fw->g.normal.x += (-off_x / dw) * dw;
654 if (off_y >= dh)
656 fw->g.normal.y -= (off_y / dh) * dh;
658 else if (off_y <= -dh)
660 fw->g.normal.y += (-off_y / dh) * dh;
663 return;
666 #define MAKEMULT(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )
667 static void __cs_handle_aspect_ratio(
668 size_rect *ret_s, FvwmWindow *fw, size_rect s, const size_rect base,
669 const size_rect inc, size_rect min, size_rect max, int xmotion,
670 int ymotion, int flags)
672 volatile double odefect;
673 volatile double defect;
674 volatile double rmax;
675 volatile double rmin;
676 volatile int delta;
677 volatile int ow;
678 volatile int oh;
680 if (fw->hints.flags & PBaseSize)
683 * ICCCM 2 demands that aspect ratio should apply to width -
684 * base_width. To prevent funny results, we reset PBaseSize in
685 * GetWindowSizeHints, if base is not smaller than min.
687 s.width -= base.width;
688 max.width -= base.width;
689 min.width -= base.width;
690 s.height -= base.height;
691 max.height -= base.height;
692 min.height -= base.height;
694 rmin = (double)fw->hints.min_aspect.x / (double)fw->hints.min_aspect.y;
695 rmax = (double)fw->hints.max_aspect.x / (double)fw->hints.max_aspect.y;
698 double r;
700 r = (double)s.width / (double)s.height;
701 ow = s.width;
702 oh = s.height;
703 odefect = 0;
704 if (r < rmin)
706 odefect = rmin - r;
708 else if (r > rmax)
710 odefect = r - rmax;
712 if (r < rmin && (flags & CS_ROUND_UP) && xmotion == 0)
714 /* change width to match */
715 delta = MAKEMULT(s.height * rmin - s.width, inc.width);
716 if (s.width + delta <= max.width)
718 s.width += delta;
720 r = (double)s.width / (double)s.height;
722 if (r < rmin)
724 /* change height to match */
725 delta = MAKEMULT(
726 s.height - s.width / rmin, inc.height);
727 if (s.height - delta >= min.height)
729 s.height -= delta;
731 else
733 delta = MAKEMULT(
734 s.height * rmin - s.width, inc.width);
735 if (s.width + delta <= max.width)
737 s.width += delta;
740 r = (double)s.width / (double)s.height;
743 if (r > rmax && (flags & CS_ROUND_UP) && ymotion == 0)
745 /* change height to match */
746 delta = MAKEMULT(s.width /rmax - s.height, inc.height);
747 if (s.height + delta <= max.height)
749 s.height += delta;
751 r = (double)s.width / (double)s.height;
753 if (r > rmax)
755 /* change width to match */
756 delta = MAKEMULT(s.width - s.height * rmax, inc.width);
757 if (s.width - delta >= min.width)
759 s.width -= delta;
761 else
763 delta = MAKEMULT(
764 s.width / rmax - s.height, inc.height);
765 if (s.height + delta <= max.height)
767 s.height += delta;
770 r = (double)s.width / (double)s.height;
772 defect = 0;
773 if (r < rmin)
775 defect = rmin - r;
777 else if (r > rmax)
779 defect = r - rmax;
781 } while (odefect > defect);
782 if (fw->hints.flags & PBaseSize)
784 ow += base.width;
785 oh += base.height;
787 ret_s->width = ow;
788 ret_s->height = oh;
790 return;
795 * Procedure:
796 * constrain_size - adjust the given width and height to account for the
797 * constraints imposed by size hints
799 void constrain_size(
800 FvwmWindow *fw, const XEvent *e, int *widthp, int *heightp,
801 int xmotion, int ymotion, int flags)
803 size_rect min;
804 size_rect max;
805 size_rect inc;
806 size_rect base;
807 size_rect round_up;
808 size_rect d;
809 size_rect old;
810 size_borders b;
812 if (DO_DISABLE_CONSTRAIN_SIZE_FULLSCREEN(fw) == 1)
814 return;
816 if (HAS_NEW_WM_NORMAL_HINTS(fw))
818 /* get the latest size hints */
819 XSync(dpy, 0);
820 GetWindowSizeHints(fw);
821 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
823 if (IS_MAXIMIZED(fw) && (flags & CS_UPDATE_MAX_DEFECT))
825 *widthp += fw->g.max_defect.width;
826 *heightp += fw->g.max_defect.height;
828 /* gcc 4.1.1 warns about these not being initialized at the end,
829 * but the conditions for the use are the same...*/
830 old.width = *widthp;
831 old.height = *heightp;
833 d.width = *widthp;
834 d.height = *heightp;
835 get_window_borders(fw, &b);
836 d.width -= b.total_size.width;
837 d.height -= b.total_size.height;
839 min.width = fw->hints.min_width;
840 min.height = fw->hints.min_height;
841 if (min.width < fw->min_window_width - b.total_size.width)
843 min.width = fw->min_window_width - b.total_size.width;
845 if (min.height < fw->min_window_height - b.total_size.height)
847 min.height =
848 fw->min_window_height - b.total_size.height;
851 max.width = fw->hints.max_width;
852 max.height = fw->hints.max_height;
853 if (max.width > fw->max_window_width - b.total_size.width)
855 max.width = fw->max_window_width - b.total_size.width;
857 if (max.height > fw->max_window_height - b.total_size.height)
859 max.height =
860 fw->max_window_height - b.total_size.height;
863 if (min.width > max.width)
865 min.width = max.width;
867 if (min.height > max.height)
869 min.height = max.height;
872 base.width = fw->hints.base_width;
873 base.height = fw->hints.base_height;
875 inc.width = fw->hints.width_inc;
876 inc.height = fw->hints.height_inc;
879 * First, clamp to min and max values
881 if (d.width < min.width)
883 d.width = min.width;
885 if (d.height < min.height)
887 d.height = min.height;
889 if (d.width > max.width)
891 d.width = max.width;
893 if (d.height > max.height)
895 d.height = max.height;
899 * Second, round to base + N * inc (up or down depending on resize
900 * type) if rounding up store amount
902 if (!(flags & CS_ROUND_UP))
904 d.width = ((d.width - base.width) / inc.width) *
905 inc.width + base.width;
906 d.height = ((d.height - base.height) / inc.height) *
907 inc.height + base.height;
909 else
911 round_up.width = d.width;
912 round_up.height = d.height;
913 d.width = ((d.width - base.width + inc.width - 1) /
914 inc.width) * inc.width + base.width;
915 d.height = ((d.height - base.height + inc.height - 1) /
916 inc.height) * inc.height + base.height;
917 round_up.width = d.width - round_up.width;
918 round_up.height = d.height - round_up.height;
922 * Step 2a: check we didn't move the edge off screen in interactive
923 * moves
925 if ((flags & CS_ROUND_UP) && e != NULL && e->type == MotionNotify)
927 if (xmotion > 0 && e->xmotion.x_root < round_up.width)
929 d.width -= inc.width;
931 else if (
932 xmotion < 0 && e->xmotion.x_root >=
933 Scr.MyDisplayWidth - round_up.width)
935 d.width -= inc.width;
937 if (ymotion > 0 && e->xmotion.y_root < round_up.height)
939 d.height -= inc.height;
941 else if (
942 ymotion < 0 && e->xmotion.y_root >=
943 Scr.MyDisplayHeight - round_up.height)
945 d.height -= inc.height;
950 * Step 2b: Check that we didn't violate min and max.
952 if (d.width < min.width)
954 d.width += inc.width;
956 if (d.height < min.height)
958 d.height += inc.height;
960 if (d.width > max.width)
962 d.width -= inc.width;
964 if (d.height > max.height)
966 d.height -= inc.height;
970 * Third, adjust for aspect ratio
972 if (fw->hints.flags & PAspect)
974 __cs_handle_aspect_ratio(
975 &d, fw, d, base, inc, min, max, xmotion, ymotion,
976 flags);
980 * Fourth, account for border width and title height
982 *widthp = d.width + b.total_size.width;
983 *heightp = d.height + b.total_size.height;
984 if (IS_MAXIMIZED(fw) && (flags & CS_UPDATE_MAX_DEFECT))
986 /* update size defect for maximized window */
987 fw->g.max_defect.width = old.width - *widthp;
988 fw->g.max_defect.height = old.height - *heightp;
991 return;
994 /* This function does roughly the same as constrain_size, but takes into account
995 * that the window shifts according to gravity if constrain_size actually
996 * changes the width or height. The frame_g of the window is not changed. The
997 * target geometry is expected to be in *rect and will be retured through rect.
999 void gravity_constrain_size(
1000 int gravity, FvwmWindow *t, rectangle *rect, int flags)
1002 int new_width = rect->width;
1003 int new_height = rect->height;
1005 if (IS_MAXIMIZED(t) && (flags & CS_UPDATE_MAX_DEFECT))
1007 gravity_resize(
1008 gravity, rect, t->g.max_defect.width,
1009 t->g.max_defect.height);
1010 t->g.max_defect.width = 0;
1011 t->g.max_defect.height = 0;
1012 new_width = rect->width;
1013 new_height = rect->height;
1015 constrain_size(
1016 t, NULL, &new_width, &new_height, 0, 0, flags);
1017 if (rect->width != new_width || rect->height != new_height)
1019 gravity_resize(
1020 gravity, rect, new_width - rect->width,
1021 new_height - rect->height);
1024 return;
1027 /* returns the icon title geometry if it is visible */
1028 Bool get_visible_icon_title_geometry(
1029 FvwmWindow *fw, rectangle *ret_g)
1031 if (HAS_NO_ICON_TITLE(fw) || IS_ICON_UNMAPPED(fw) ||
1032 !IS_ICONIFIED(fw))
1034 memset(ret_g, 0, sizeof(*ret_g));
1035 return False;
1037 *ret_g = fw->icon_g.title_w_g;
1039 return True;
1042 /* returns the icon title geometry if it the icon title window exists */
1043 Bool get_icon_title_geometry(
1044 FvwmWindow *fw, rectangle *ret_g)
1046 if (HAS_NO_ICON_TITLE(fw))
1048 memset(ret_g, 0, sizeof(*ret_g));
1049 return False;
1051 *ret_g = fw->icon_g.title_w_g;
1053 return True;
1056 /* returns the icon picture geometry if it is visible */
1057 Bool get_visible_icon_picture_geometry(
1058 FvwmWindow *fw, rectangle *ret_g)
1060 if (fw->icon_g.picture_w_g.width == 0 ||
1061 IS_ICON_UNMAPPED(fw) || !IS_ICONIFIED(fw))
1063 memset(ret_g, 0, sizeof(*ret_g));
1064 return False;
1066 *ret_g = fw->icon_g.picture_w_g;
1068 return True;
1071 /* returns the icon picture geometry if it is exists */
1072 Bool get_icon_picture_geometry(
1073 FvwmWindow *fw, rectangle *ret_g)
1075 if (fw->icon_g.picture_w_g.width == 0)
1077 memset(ret_g, 0, sizeof(*ret_g));
1078 return False;
1080 *ret_g = fw->icon_g.picture_w_g;
1082 return True;
1085 /* returns the icon geometry (unexpanded title plus pixmap) if it is visible */
1086 Bool get_visible_icon_geometry(
1087 FvwmWindow *fw, rectangle *ret_g)
1089 if (IS_ICON_UNMAPPED(fw) || !IS_ICONIFIED(fw))
1091 memset(ret_g, 0, sizeof(*ret_g));
1092 return False;
1094 if (fw->icon_g.picture_w_g.width > 0)
1096 *ret_g = fw->icon_g.picture_w_g;
1097 if (!HAS_NO_ICON_TITLE(fw))
1099 ret_g->height += fw->icon_g.title_w_g.height;
1102 else if (!HAS_NO_ICON_TITLE(fw))
1104 *ret_g = fw->icon_g.title_w_g;
1106 else
1108 memset(ret_g, 0, sizeof(*ret_g));
1109 return False;
1112 return True;
1115 /* returns the icon geometry (unexpanded title plus pixmap) if it exists */
1116 void get_icon_geometry(
1117 FvwmWindow *fw, rectangle *ret_g)
1119 /* valid geometry? */
1120 if (fw->icon_g.picture_w_g.width > 0)
1122 *ret_g = fw->icon_g.picture_w_g;
1123 if (!HAS_NO_ICON_TITLE(fw))
1125 ret_g->height += fw->icon_g.title_w_g.height;
1128 else if (fw->icon_g.title_w_g.width > 0)
1130 *ret_g = fw->icon_g.title_w_g;
1132 /* valid position? */
1133 else if (fw->icon_g.picture_w_g.x != 0 || fw->icon_g.picture_w_g.y != 0)
1135 *ret_g = fw->icon_g.picture_w_g;
1137 else if (fw->icon_g.title_w_g.x != 0 || fw->icon_g.title_w_g.y != 0)
1139 *ret_g = fw->icon_g.title_w_g;
1141 else
1143 memset(ret_g, 0, sizeof(*ret_g));
1146 return;
1149 /* Returns the visible geometry of a window or icon. This can be used to test
1150 * if this region overlaps other windows. */
1151 Bool get_visible_window_or_icon_geometry(
1152 FvwmWindow *fw, rectangle *ret_g)
1154 if (IS_ICONIFIED(fw))
1156 return get_visible_icon_geometry(fw, ret_g);
1158 *ret_g = fw->g.frame;
1160 return True;
1163 void move_icon_to_position(
1164 FvwmWindow *fw)
1166 Bool draw_picture_w = False;
1167 Bool draw_title_w = False;
1169 if (fw->icon_g.picture_w_g.width > 0)
1171 int cs;
1173 if (Scr.Hilite == fw)
1175 cs = fw->cs_hi;
1177 else
1179 cs = fw->cs;
1181 XMoveWindow(
1182 dpy, FW_W_ICON_PIXMAP(fw),
1183 fw->icon_g.picture_w_g.x,
1184 fw->icon_g.picture_w_g.y);
1185 if (fw->icon_alphaPixmap ||
1186 (cs >= 0 && Colorset[cs].icon_alpha_percent < 100) ||
1187 CSET_IS_TRANSPARENT(fw->icon_background_cs) ||
1188 (!IS_ICON_SHAPED(fw) && fw->icon_background_padding > 0))
1190 draw_picture_w = True;
1193 if (!HAS_NO_ICON_TITLE(fw))
1195 int cs;
1196 rectangle dummy;
1198 if (Scr.Hilite == fw)
1200 cs = fw->icon_title_cs_hi;
1202 else
1204 cs = fw->icon_title_cs;
1206 XMoveWindow(
1207 dpy, FW_W_ICON_TITLE(fw),
1208 fw->icon_g.title_w_g.x,
1209 fw->icon_g.title_w_g.y);
1210 if (CSET_IS_TRANSPARENT(cs) &&
1211 !get_visible_icon_picture_geometry(fw, &dummy) &&
1212 get_visible_icon_title_geometry(fw, &dummy))
1214 draw_title_w = True;
1218 if (draw_title_w || draw_picture_w)
1220 DrawIconWindow(
1221 fw, draw_title_w, draw_picture_w, False, draw_picture_w,
1222 NULL);
1225 return;
1228 void broadcast_icon_geometry(
1229 FvwmWindow *fw, Bool do_force)
1231 rectangle g;
1232 Bool rc;
1234 rc = get_visible_icon_geometry(fw, &g);
1235 if (rc == True && (!IS_ICON_UNMAPPED(fw) || do_force == True))
1237 BroadcastPacket(
1238 M_ICON_LOCATION, 7, (long)FW_W(fw),
1239 (long)FW_W_FRAME(fw), (unsigned long)fw,
1240 (long)g.x, (long)g.y, (long)g.width, (long)g.height);
1243 return;
1246 void modify_icon_position(
1247 FvwmWindow *fw, int dx, int dy)
1249 if (fw->icon_g.picture_w_g.width > 0 || HAS_NO_ICON_TITLE(fw))
1251 /* picture position is also valid if there is neither a picture
1252 * nor a title */
1253 fw->icon_g.picture_w_g.x += dx;
1254 fw->icon_g.picture_w_g.y += dy;
1256 if (!HAS_NO_ICON_TITLE(fw))
1258 fw->icon_g.title_w_g.x += dx;
1259 fw->icon_g.title_w_g.y += dy;
1262 return;
1265 /* set the icon position to the specified value. take care of the actual icon
1266 * layout */
1267 void set_icon_position(
1268 FvwmWindow *fw, int x, int y)
1270 if (fw->icon_g.picture_w_g.width > 0)
1272 fw->icon_g.picture_w_g.x = x;
1273 fw->icon_g.picture_w_g.y = y;
1275 else
1277 fw->icon_g.picture_w_g.x = 0;
1278 fw->icon_g.picture_w_g.y = 0;
1280 if (!HAS_NO_ICON_TITLE(fw))
1282 fw->icon_g.title_w_g.x = x;
1283 fw->icon_g.title_w_g.y = y;
1285 else
1287 fw->icon_g.title_w_g.x = 0;
1288 fw->icon_g.title_w_g.y = 0;
1290 if (fw->icon_g.picture_w_g.width > 0 &&
1291 !HAS_NO_ICON_TITLE(fw))
1293 fw->icon_g.title_w_g.x -=
1294 (fw->icon_g.title_w_g.width -
1295 fw->icon_g.picture_w_g.width) / 2;
1296 fw->icon_g.title_w_g.y +=
1297 fw->icon_g.picture_w_g.height;
1299 else if (fw->icon_g.picture_w_g.width <= 0 && HAS_NO_ICON_TITLE(fw))
1301 /* In case there is no icon, fake the icon position so the
1302 * modules know where its window was iconified. */
1303 fw->icon_g.picture_w_g.x = x;
1304 fw->icon_g.picture_w_g.y = y;
1307 return;
1310 void set_icon_picture_size(
1311 FvwmWindow *fw, int w, int h)
1313 if (fw->icon_g.picture_w_g.width > 0)
1315 fw->icon_g.picture_w_g.width = w;
1316 fw->icon_g.picture_w_g.height = h;
1318 else
1320 fw->icon_g.picture_w_g.width = 0;
1321 fw->icon_g.picture_w_g.height = 0;
1324 return;
1327 void resize_icon_title_height(FvwmWindow *fw, int dh)
1329 if (!HAS_NO_ICON_TITLE(fw))
1331 fw->icon_g.title_w_g.height += dh;
1334 return;
1337 void get_page_offset_rectangle(
1338 int *ret_page_x, int *ret_page_y, rectangle *r)
1340 int xoff = Scr.Vx % Scr.MyDisplayWidth;
1341 int yoff = Scr.Vy % Scr.MyDisplayHeight;
1343 /* maximize on the page where the center of the window is */
1344 *ret_page_x = truncate_to_multiple(
1345 r->x + r->width / 2 + xoff, Scr.MyDisplayWidth) - xoff;
1346 *ret_page_y = truncate_to_multiple(
1347 r->y + r->height / 2 + yoff, Scr.MyDisplayHeight) - yoff;
1349 return;
1352 void get_page_offset(
1353 int *ret_page_x, int *ret_page_y, FvwmWindow *fw)
1355 rectangle r;
1357 r.x = fw->g.frame.x;
1358 r.y = fw->g.frame.y;
1359 r.width = fw->g.frame.width;
1360 r.height = fw->g.frame.height;
1361 get_page_offset_rectangle(ret_page_x, ret_page_y, &r);
1363 return;
1366 void get_page_offset_check_visible(
1367 int *ret_page_x, int *ret_page_y, FvwmWindow *fw)
1369 if (IsRectangleOnThisPage(&fw->g.frame, fw->Desk))
1371 /* maximize on visible page if any part of the window is
1372 * visible */
1373 *ret_page_x = 0;
1374 *ret_page_y = 0;
1376 else
1378 get_page_offset(ret_page_x, ret_page_y, fw);
1381 return;
1384 /* ---------------------------- builtin commands --------------------------- */