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.
6 *
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.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see: <http://www.gnu.org/licenses/>
14 */
15
16 /* ---------------------------- included header files ---------------------- */
17
18 #include "config.h"
19
20 #include <stdio.h>
21
22 #include "libs/fvwmlib.h"
23 #include "libs/charmap.h"
24 #include "libs/wcontext.h"
25 #include "fvwm.h"
26 #include "externs.h"
27 #include "execcontext.h"
28 #include "misc.h"
29 #include "screen.h"
30 #include "geometry.h"
31 #include "module_interface.h"
32 #include "borders.h"
33 #include "icons.h"
34 #include "add_window.h"
35
36 /* ---------------------------- local definitions -------------------------- */
37
38 /* ---------------------------- local macros ------------------------------- */
39
40 /* ---------------------------- imports ------------------------------------ */
41
42 /* ---------------------------- included code files ------------------------ */
43
44 /* ---------------------------- local types -------------------------------- */
45
46 /* ---------------------------- forward declarations ----------------------- */
47
48 /* ---------------------------- local variables ---------------------------- */
49
50 /* ---------------------------- exported variables (globals) --------------- */
51
52 /* ---------------------------- local functions ---------------------------- */
53
54 /* ---------------------------- interface functions ------------------------ */
55
56 /* Removes decorations from the source rectangle and moves it according to the
57 * gravity specification. */
gravity_get_naked_geometry(int gravity,FvwmWindow * t,rectangle * dest_g,rectangle * orig_g)58 void gravity_get_naked_geometry(
59 int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
60 {
61 int xoff;
62 int yoff;
63 size_borders b;
64
65 get_window_borders(t, &b);
66 gravity_get_offsets(gravity, &xoff, &yoff);
67 dest_g->x = orig_g->x + ((xoff + 1) * (orig_g->width - 1)) / 2;
68 dest_g->y = orig_g->y + ((yoff + 1) * (orig_g->height - 1)) / 2;
69 dest_g->width = orig_g->width - b.total_size.width;
70 dest_g->height = orig_g->height - b.total_size.height;
71
72 return;
73 }
74
75 /* Decorate the rectangle. Resize and shift it according to gravity. */
gravity_add_decoration(int gravity,FvwmWindow * t,rectangle * dest_g,rectangle * orig_g)76 void gravity_add_decoration(
77 int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
78 {
79 size_borders b;
80
81 get_window_borders(t, &b);
82 *dest_g = *orig_g;
83 gravity_resize(
84 gravity, dest_g, b.total_size.width, b.total_size.height);
85
86 return;
87 }
88
get_relative_geometry(rectangle * rel_g,rectangle * abs_g)89 void get_relative_geometry(rectangle *rel_g, rectangle *abs_g)
90 {
91 rel_g->x = abs_g->x - Scr.Vx;
92 rel_g->y = abs_g->y - Scr.Vy;
93 rel_g->width = abs_g->width;
94 rel_g->height = abs_g->height;
95
96 return;
97 }
98
get_absolute_geometry(rectangle * abs_g,rectangle * rel_g)99 void get_absolute_geometry(rectangle *abs_g, rectangle *rel_g)
100 {
101 abs_g->x = rel_g->x + Scr.Vx;
102 abs_g->y = rel_g->y + Scr.Vy;
103 abs_g->width = rel_g->width;
104 abs_g->height = rel_g->height;
105
106 return;
107 }
108
gravity_translate_to_northwest_geometry(int gravity,FvwmWindow * t,rectangle * dest_g,rectangle * orig_g)109 void gravity_translate_to_northwest_geometry(
110 int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
111 {
112 int xoff;
113 int yoff;
114
115 gravity_get_offsets(gravity, &xoff, &yoff);
116 dest_g->x = orig_g->x -
117 ((xoff + 1) * (orig_g->width - 1 +
118 2 * t->attr_backup.border_width)) / 2;
119 dest_g->y = orig_g->y -
120 ((yoff + 1) * (orig_g->height - 1 +
121 2 * t->attr_backup.border_width)) / 2;
122 dest_g->width = orig_g->width;
123 dest_g->height = orig_g->height;
124
125 return;
126 }
127
gravity_translate_to_northwest_geometry_no_bw(int gravity,FvwmWindow * t,rectangle * dest_g,rectangle * orig_g)128 void gravity_translate_to_northwest_geometry_no_bw(
129 int gravity, FvwmWindow *t, rectangle *dest_g, rectangle *orig_g)
130 {
131 int bw = t->attr_backup.border_width;
132
133 t->attr_backup.border_width = 0;
134 gravity_translate_to_northwest_geometry(gravity, t, dest_g, orig_g);
135 t->attr_backup.border_width = bw;
136
137 return;
138 }
139
get_title_geometry(FvwmWindow * fw,rectangle * ret_g)140 void get_title_geometry(
141 FvwmWindow *fw, rectangle *ret_g)
142 {
143 size_borders b;
144 size_borders nt;
145 int w;
146 int h;
147
148 get_window_borders(fw, &b);
149 get_window_borders_no_title(fw, &nt);
150 w = (ret_g->width > 0) ? ret_g->width : fw->g.frame.width;
151 h = (ret_g->height > 0) ? ret_g->height : fw->g.frame.height;
152 ret_g->x = nt.top_left.width;
153 ret_g->y = nt.top_left.height;
154 switch (GET_TITLE_DIR(fw))
155 {
156 case DIR_S:
157 ret_g->y = h - b.bottom_right.height;
158 /* fall through */
159 case DIR_N:
160 ret_g->width = w - b.total_size.width;
161 ret_g->height = fw->title_thickness;
162 break;
163 case DIR_E:
164 ret_g->x = w - b.bottom_right.width;
165 /* fall through */
166 case DIR_W:
167 ret_g->width = fw->title_thickness;
168 ret_g->height = h - b.total_size.height;
169 break;
170 default:
171 break;
172 }
173
174 return;
175 }
176
get_title_gravity_factors(FvwmWindow * fw,int * ret_fx,int * ret_fy)177 void get_title_gravity_factors(
178 FvwmWindow *fw, int *ret_fx, int *ret_fy)
179 {
180 switch (GET_TITLE_DIR(fw))
181 {
182 case DIR_N:
183 *ret_fx = 0;
184 *ret_fy = 1;
185 break;
186 case DIR_S:
187 *ret_fx = 0;
188 *ret_fy = -1;
189 break;
190 case DIR_W:
191 *ret_fx = 1;
192 *ret_fy = 0;
193 break;
194 case DIR_E:
195 *ret_fx = -1;
196 *ret_fy = 0;
197 break;
198 }
199
200 return;
201 }
202
get_title_button_geometry(FvwmWindow * fw,rectangle * ret_g,int context)203 Bool get_title_button_geometry(
204 FvwmWindow *fw, rectangle *ret_g, int context)
205 {
206 int bnum;
207
208 if (context & C_TITLE)
209 {
210 ret_g->width = 0;
211 ret_g->height = 0;
212 get_title_geometry(fw, ret_g);
213 ret_g->x += fw->g.frame.x;
214 ret_g->y += fw->g.frame.y;
215
216 return True;
217
218 }
219 bnum = get_button_number(context);
220 if (bnum < 0 || FW_W_BUTTON(fw, bnum) == None)
221 {
222 return False;
223 }
224 if (XGetGeometry(
225 dpy, FW_W_BUTTON(fw, bnum), &JunkRoot, &ret_g->x, &ret_g->y,
226 (unsigned int*)&ret_g->width, (unsigned int*)&ret_g->height,
227 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
228 {
229 return False;
230 }
231 XTranslateCoordinates(
232 dpy, FW_W_FRAME(fw), Scr.Root, ret_g->x, ret_g->y, &ret_g->x,
233 &ret_g->y, &JunkChild);
234
235 return True;
236 }
237
get_title_font_size_and_offset(FvwmWindow * fw,direction_t title_dir,Bool is_left_title_rotated_cw,Bool is_right_title_rotated_cw,Bool is_top_title_rotated,Bool is_bottom_title_rotated,int * size,int * offset)238 void get_title_font_size_and_offset(
239 FvwmWindow *fw, direction_t title_dir,
240 Bool is_left_title_rotated_cw, Bool is_right_title_rotated_cw,
241 Bool is_top_title_rotated, Bool is_bottom_title_rotated,
242 int *size, int *offset)
243 {
244 int decor_size;
245 int extra_size;
246 int font_size;
247 int min_offset;
248 Bool is_rotated_cw, is_rotated;
249 rotation_t draw_rotation;
250
251 /* adjust font offset according to height specified in title style */
252 decor_size = fw->decor->title_height;
253 font_size = fw->title_font->height + EXTRA_TITLE_FONT_HEIGHT;
254 switch (title_dir)
255 {
256 case DIR_W:
257 case DIR_E:
258 is_rotated_cw = (title_dir == DIR_W) ?
259 is_left_title_rotated_cw : is_right_title_rotated_cw;
260 if (is_rotated_cw)
261 {
262 fw->title_text_rotation = ROTATION_90;
263 }
264 else
265 {
266 fw->title_text_rotation = ROTATION_270;
267 }
268 break;
269 case DIR_N:
270 case DIR_S:
271 default:
272 is_rotated = (title_dir == DIR_N) ?
273 is_top_title_rotated : is_bottom_title_rotated;
274 if (is_rotated)
275 {
276 fw->title_text_rotation = ROTATION_180;
277 }
278 else
279 {
280 fw->title_text_rotation = ROTATION_0;
281 }
282 break;
283 }
284 if (USE_TITLE_DECOR_ROTATION(fw))
285 {
286 draw_rotation = ROTATION_0;
287 }
288 else
289 {
290 draw_rotation = fw->title_text_rotation;
291 }
292 min_offset = FlocaleGetMinOffset(
293 fw->title_font, draw_rotation);
294 extra_size = (decor_size > 0) ? decor_size - font_size : 0;
295 *offset = min_offset;
296 if (fw->decor->min_title_height > 0 &&
297 font_size + extra_size < fw->decor->min_title_height)
298 {
299 extra_size = fw->decor->min_title_height - font_size;
300 }
301 if (extra_size > 0)
302 {
303 *offset += extra_size / 2;
304 }
305 *size = font_size + extra_size;
306
307 return;
308 }
309
get_icon_corner(FvwmWindow * fw,rectangle * ret_g)310 void get_icon_corner(
311 FvwmWindow *fw, rectangle *ret_g)
312 {
313 switch (GET_TITLE_DIR(fw))
314 {
315 case DIR_N:
316 case DIR_W:
317 ret_g->x = fw->g.frame.x;
318 ret_g->y = fw->g.frame.y;
319 break;
320 case DIR_S:
321 ret_g->x = fw->g.frame.x;
322 ret_g->y = fw->g.frame.y + fw->g.frame.height -
323 ret_g->height;
324 break;
325 case DIR_E:
326 ret_g->x = fw->g.frame.x + fw->g.frame.width -
327 ret_g->width;
328 ret_g->y = fw->g.frame.y;
329 break;
330 }
331
332 return;
333 }
334
get_shaded_geometry(FvwmWindow * fw,rectangle * small_g,rectangle * big_g)335 void get_shaded_geometry(
336 FvwmWindow *fw, rectangle *small_g, rectangle *big_g)
337 {
338 size_borders b;
339 /* this variable is necessary so the function can be called with
340 * small_g == big_g */
341 int big_width = big_g->width;
342 int big_height = big_g->height;
343 int d;
344
345 switch (SHADED_DIR(fw))
346 {
347 case DIR_SW:
348 case DIR_SE:
349 case DIR_NW:
350 case DIR_NE:
351 get_window_borders_no_title(fw, &b);
352 break;
353 default:
354 get_window_borders(fw, &b);
355 break;
356 }
357 *small_g = *big_g;
358 d = 0;
359 switch (SHADED_DIR(fw))
360 {
361 case DIR_S:
362 case DIR_SW:
363 case DIR_SE:
364 small_g->y = big_g->y + big_height - b.total_size.height;
365 d = 1;
366 /* fall through */
367 case DIR_N:
368 case DIR_NW:
369 case DIR_NE:
370 small_g->height = b.total_size.height;
371 if (small_g->height == 0)
372 {
373 small_g->height = 1;
374 small_g->y -= d;
375 }
376 break;
377 default:
378 break;
379 }
380 d = 0;
381 switch (SHADED_DIR(fw))
382 {
383 case DIR_E:
384 case DIR_NE:
385 case DIR_SE:
386 small_g->x = big_g->x + big_width - b.total_size.width;
387 d = 1;
388 /* fall through */
389 case DIR_W:
390 case DIR_NW:
391 case DIR_SW:
392 small_g->width = b.total_size.width;
393 if (small_g->width == 0)
394 {
395 small_g->width = 1;
396 small_g->x -= d;
397 }
398 break;
399 default:
400 break;
401 }
402
403 return;
404 }
405
get_shaded_geometry_with_dir(FvwmWindow * fw,rectangle * small_g,rectangle * big_g,direction_t shade_dir)406 void get_shaded_geometry_with_dir(
407 FvwmWindow *fw, rectangle *small_g, rectangle *big_g,
408 direction_t shade_dir)
409 {
410 direction_t old_shade_dir;
411
412 old_shade_dir = SHADED_DIR(fw);
413 SET_SHADED_DIR(fw, shade_dir);
414 get_shaded_geometry(fw, small_g, big_g);
415 SET_SHADED_DIR(fw, old_shade_dir);
416
417 return;
418 }
419
get_unshaded_geometry(FvwmWindow * fw,rectangle * ret_g)420 void get_unshaded_geometry(
421 FvwmWindow *fw, rectangle *ret_g)
422 {
423 if (IS_SHADED(fw))
424 {
425 if (IS_MAXIMIZED(fw))
426 {
427 *ret_g = fw->g.max;
428 }
429 else
430 {
431 *ret_g = fw->g.normal;
432 }
433 get_relative_geometry(ret_g, ret_g);
434 }
435 else
436 {
437 *ret_g = fw->g.frame;
438 }
439
440 return;
441 }
442
get_shaded_client_window_pos(FvwmWindow * fw,rectangle * ret_g)443 void get_shaded_client_window_pos(
444 FvwmWindow *fw, rectangle *ret_g)
445 {
446 rectangle big_g;
447 size_borders b;
448
449 get_window_borders(fw, &b);
450 big_g = (IS_MAXIMIZED(fw)) ? fw->g.max : fw->g.normal;
451 get_relative_geometry(&big_g, &big_g);
452 switch (SHADED_DIR(fw))
453 {
454 case DIR_S:
455 case DIR_SW:
456 case DIR_SE:
457 ret_g->y = 1 - big_g.height + b.total_size.height;
458 break;
459 default:
460 ret_g->y = 0;
461 break;
462 }
463 switch (SHADED_DIR(fw))
464 {
465 case DIR_E:
466 case DIR_NE:
467 case DIR_SE:
468 ret_g->x = 1 - big_g.width + b.total_size.width;
469 break;
470 default:
471 ret_g->x = 0;
472 break;
473 }
474
475 return;
476 }
477
478 /* returns the dimensions of the borders */
get_window_borders(const FvwmWindow * fw,size_borders * borders)479 void get_window_borders(
480 const FvwmWindow *fw, size_borders *borders)
481 {
482 borders->top_left.width = fw->boundary_width;
483 borders->bottom_right.width = fw->boundary_width;
484 borders->top_left.height = fw->boundary_width;
485 borders->bottom_right.height = fw->boundary_width;
486 switch (GET_TITLE_DIR(fw))
487 {
488 case DIR_N:
489 borders->top_left.height += fw->title_thickness;
490 break;
491 case DIR_S:
492 borders->bottom_right.height += fw->title_thickness;
493 break;
494 case DIR_W:
495 borders->top_left.width += fw->title_thickness;
496 break;
497 case DIR_E:
498 borders->bottom_right.width += fw->title_thickness;
499 break;
500 }
501 borders->total_size.width =
502 borders->top_left.width + borders->bottom_right.width;
503 borders->total_size.height =
504 borders->top_left.height + borders->bottom_right.height;
505
506 return;
507 }
508
509 /* returns the dimensions of the borders without the title */
get_window_borders_no_title(const FvwmWindow * fw,size_borders * borders)510 void get_window_borders_no_title(
511 const FvwmWindow *fw, size_borders *borders)
512 {
513 borders->top_left.width = fw->boundary_width;
514 borders->bottom_right.width = fw->boundary_width;
515 borders->top_left.height = fw->boundary_width;
516 borders->bottom_right.height = fw->boundary_width;
517 borders->total_size.width =
518 borders->top_left.width + borders->bottom_right.width;
519 borders->total_size.height =
520 borders->top_left.height + borders->bottom_right.height;
521
522 return;
523 }
524
set_window_border_size(FvwmWindow * fw,int used_width)525 void set_window_border_size(
526 FvwmWindow *fw, int used_width)
527 {
528 if (used_width <= 0)
529 {
530 fw->boundary_width = 0;
531 fw->unshaped_boundary_width = 0;
532 }
533 else
534 {
535 fw->unshaped_boundary_width = used_width;
536 fw->boundary_width = (fw->wShaped) ? 0 : used_width;
537 }
538
539 return;
540 }
541
542 /* Returns True if all window borders are only 1 pixel thick (or less). */
is_window_border_minimal(FvwmWindow * fw)543 Bool is_window_border_minimal(
544 FvwmWindow *fw)
545 {
546 size_borders nt;
547
548 get_window_borders_no_title(fw, &nt);
549 if (nt.top_left.width > 1 || nt.top_left.height > 1 ||
550 nt.bottom_right.width > 1 || nt.bottom_right.height > 1)
551 {
552 return False;
553 }
554
555 return True;
556 }
557
558
559 /* This function returns the geometry of the client window. If the window is
560 * shaded, the unshaded geometry is used instead. */
get_client_geometry(FvwmWindow * fw,rectangle * ret_g)561 void get_client_geometry(
562 FvwmWindow *fw, rectangle *ret_g)
563 {
564 size_borders borders;
565
566 get_unshaded_geometry(fw, ret_g);
567 get_window_borders(fw, &borders);
568 ret_g->x += borders.top_left.width;
569 ret_g->y += borders.top_left.height;
570 ret_g->width -= borders.total_size.width;
571 ret_g->height -= borders.total_size.height;
572
573 return;
574 }
575
576 /* update the frame_g according to the window's g.normal or g.max and shaded
577 * state */
update_relative_geometry(FvwmWindow * fw)578 void update_relative_geometry(FvwmWindow *fw)
579 {
580 get_relative_geometry(
581 &fw->g.frame,
582 (IS_MAXIMIZED(fw)) ? &fw->g.max : &fw->g.normal);
583 if (IS_SHADED(fw))
584 {
585 get_shaded_geometry(
586 fw, &fw->g.frame, &fw->g.frame);
587 }
588
589 return;
590 }
591
592 /* update the g.normal or g.max according to the window's current position */
update_absolute_geometry(FvwmWindow * fw)593 void update_absolute_geometry(FvwmWindow *fw)
594 {
595 rectangle *dest_g;
596 rectangle frame_g;
597
598 /* store orig values in absolute coords */
599 dest_g = (IS_MAXIMIZED(fw)) ? &fw->g.max : &fw->g.normal;
600 frame_g = *dest_g;
601 dest_g->x = fw->g.frame.x + Scr.Vx;
602 dest_g->y = fw->g.frame.y + Scr.Vy;
603 dest_g->width = fw->g.frame.width;
604 dest_g->height = fw->g.frame.height;
605 if (IS_SHADED(fw))
606 {
607 switch (SHADED_DIR(fw))
608 {
609 case DIR_SW:
610 case DIR_S:
611 case DIR_SE:
612 dest_g->y += fw->g.frame.height - frame_g.height;
613 /* fall through */
614 case DIR_NW:
615 case DIR_N:
616 case DIR_NE:
617 dest_g->height = frame_g.height;
618 break;
619 }
620 switch (SHADED_DIR(fw))
621 {
622 case DIR_NE:
623 case DIR_E:
624 case DIR_SE:
625 dest_g->x += fw->g.frame.width - frame_g.width;
626 /* fall through */
627 case DIR_NW:
628 case DIR_W:
629 case DIR_SW:
630 dest_g->width = frame_g.width;
631 break;
632 }
633 }
634
635 return;
636 }
637
638 /* make sure a maximized window and it's normal version are never a page or
639 * more apart. */
maximize_adjust_offset(FvwmWindow * fw)640 void maximize_adjust_offset(FvwmWindow *fw)
641 {
642 int off_x;
643 int off_y;
644 int dh;
645 int dw;
646
647 if (!IS_MAXIMIZED(fw))
648 {
649 /* otherwise we might corrupt the g.normal */
650 return;
651 }
652 off_x = fw->g.normal.x - fw->g.max.x - fw->g.max_offset.x;
653 off_y = fw->g.normal.y - fw->g.max.y - fw->g.max_offset.y;
654 dw = Scr.MyDisplayWidth;
655 dh = Scr.MyDisplayHeight;
656 if (off_x >= dw)
657 {
658 fw->g.normal.x -= (off_x / dw) * dw;
659 }
660 else if (off_x <= -dw)
661 {
662 fw->g.normal.x += (-off_x / dw) * dw;
663 }
664 if (off_y >= dh)
665 {
666 fw->g.normal.y -= (off_y / dh) * dh;
667 }
668 else if (off_y <= -dh)
669 {
670 fw->g.normal.y += (-off_y / dh) * dh;
671 }
672
673 return;
674 }
675
676 #define MAKEMULT(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )
__cs_handle_aspect_ratio(size_rect * ret_s,FvwmWindow * fw,size_rect s,const size_rect base,const size_rect inc,size_rect min,size_rect max,int xmotion,int ymotion,int flags)677 static void __cs_handle_aspect_ratio(
678 size_rect *ret_s, FvwmWindow *fw, size_rect s, const size_rect base,
679 const size_rect inc, size_rect min, size_rect max, int xmotion,
680 int ymotion, int flags)
681 {
682 volatile double odefect;
683 volatile double defect;
684 volatile double rmax;
685 volatile double rmin;
686 volatile int delta;
687 volatile int ow;
688 volatile int oh;
689
690 if (fw->hints.flags & PBaseSize)
691 {
692 /*
693 * ICCCM 2 demands that aspect ratio should apply to width -
694 * base_width. To prevent funny results, we reset PBaseSize in
695 * GetWindowSizeHints, if base is not smaller than min.
696 */
697 s.width -= base.width;
698 max.width -= base.width;
699 min.width -= base.width;
700 s.height -= base.height;
701 max.height -= base.height;
702 min.height -= base.height;
703 }
704 rmin = (double)fw->hints.min_aspect.x / (double)fw->hints.min_aspect.y;
705 rmax = (double)fw->hints.max_aspect.x / (double)fw->hints.max_aspect.y;
706 do
707 {
708 double r;
709
710 r = (double)s.width / (double)s.height;
711 ow = s.width;
712 oh = s.height;
713 odefect = 0;
714 if (r < rmin)
715 {
716 odefect = rmin - r;
717 }
718 else if (r > rmax)
719 {
720 odefect = r - rmax;
721 }
722 if (r < rmin && (flags & CS_ROUND_UP) && xmotion == 0)
723 {
724 /* change width to match */
725 delta = MAKEMULT(s.height * rmin - s.width, inc.width);
726 if (s.width + delta <= max.width)
727 {
728 s.width += delta;
729 }
730 r = (double)s.width / (double)s.height;
731 }
732 if (r < rmin)
733 {
734 /* change height to match */
735 delta = MAKEMULT(
736 s.height - s.width / rmin, inc.height);
737 if (s.height - delta >= min.height)
738 {
739 s.height -= delta;
740 }
741 else
742 {
743 delta = MAKEMULT(
744 s.height * rmin - s.width, inc.width);
745 if (s.width + delta <= max.width)
746 {
747 s.width += delta;
748 }
749 }
750 r = (double)s.width / (double)s.height;
751 }
752
753 if (r > rmax && (flags & CS_ROUND_UP) && ymotion == 0)
754 {
755 /* change height to match */
756 delta = MAKEMULT(s.width /rmax - s.height, inc.height);
757 if (s.height + delta <= max.height)
758 {
759 s.height += delta;
760 }
761 r = (double)s.width / (double)s.height;
762 }
763 if (r > rmax)
764 {
765 /* change width to match */
766 delta = MAKEMULT(s.width - s.height * rmax, inc.width);
767 if (s.width - delta >= min.width)
768 {
769 s.width -= delta;
770 }
771 else
772 {
773 delta = MAKEMULT(
774 s.width / rmax - s.height, inc.height);
775 if (s.height + delta <= max.height)
776 {
777 s.height += delta;
778 }
779 }
780 r = (double)s.width / (double)s.height;
781 }
782 defect = 0;
783 if (r < rmin)
784 {
785 defect = rmin - r;
786 }
787 else if (r > rmax)
788 {
789 defect = r - rmax;
790 }
791 } while (odefect > defect);
792 if (fw->hints.flags & PBaseSize)
793 {
794 ow += base.width;
795 oh += base.height;
796 }
797 ret_s->width = ow;
798 ret_s->height = oh;
799
800 return;
801 }
802
803 /*
804 *
805 * Procedure:
806 * constrain_size - adjust the given width and height to account for the
807 * constraints imposed by size hints
808 */
constrain_size(FvwmWindow * fw,const XEvent * e,int * widthp,int * heightp,int xmotion,int ymotion,int flags)809 void constrain_size(
810 FvwmWindow *fw, const XEvent *e, int *widthp, int *heightp,
811 int xmotion, int ymotion, int flags)
812 {
813 size_rect min;
814 size_rect max;
815 size_rect inc;
816 size_rect base;
817 size_rect round_up;
818 size_rect d;
819 size_rect old;
820 size_borders b;
821
822 if (DO_DISABLE_CONSTRAIN_SIZE_FULLSCREEN(fw) == 1)
823 {
824 return;
825 }
826 if (HAS_NEW_WM_NORMAL_HINTS(fw))
827 {
828 /* get the latest size hints */
829 XSync(dpy, 0);
830 GetWindowSizeHints(fw);
831 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
832 }
833 if (IS_MAXIMIZED(fw) && (flags & CS_UPDATE_MAX_DEFECT))
834 {
835 *widthp += fw->g.max_defect.width;
836 *heightp += fw->g.max_defect.height;
837 }
838 /* gcc 4.1.1 warns about these not being initialized at the end,
839 * but the conditions for the use are the same...*/
840 old.width = *widthp;
841 old.height = *heightp;
842
843 d.width = *widthp;
844 d.height = *heightp;
845 get_window_borders(fw, &b);
846 d.width -= b.total_size.width;
847 d.height -= b.total_size.height;
848
849 min.width = fw->hints.min_width;
850 min.height = fw->hints.min_height;
851 if (min.width < fw->min_window_width - b.total_size.width)
852 {
853 min.width = fw->min_window_width - b.total_size.width;
854 }
855 if (min.height < fw->min_window_height - b.total_size.height)
856 {
857 min.height =
858 fw->min_window_height - b.total_size.height;
859 }
860
861 max.width = fw->hints.max_width;
862 max.height = fw->hints.max_height;
863 if (max.width > fw->max_window_width - b.total_size.width)
864 {
865 max.width = fw->max_window_width - b.total_size.width;
866 }
867 if (max.height > fw->max_window_height - b.total_size.height)
868 {
869 max.height =
870 fw->max_window_height - b.total_size.height;
871 }
872
873 if (min.width > max.width)
874 {
875 min.width = max.width;
876 }
877 if (min.height > max.height)
878 {
879 min.height = max.height;
880 }
881
882 base.width = fw->hints.base_width;
883 base.height = fw->hints.base_height;
884
885 inc.width = fw->hints.width_inc;
886 inc.height = fw->hints.height_inc;
887
888 /*
889 * First, clamp to min and max values
890 */
891 if (d.width < min.width)
892 {
893 d.width = min.width;
894 }
895 if (d.height < min.height)
896 {
897 d.height = min.height;
898 }
899 if (d.width > max.width)
900 {
901 d.width = max.width;
902 }
903 if (d.height > max.height)
904 {
905 d.height = max.height;
906 }
907
908 /*
909 * Second, round to base + N * inc (up or down depending on resize
910 * type) if rounding up store amount
911 */
912 if (!(flags & CS_ROUND_UP))
913 {
914 d.width = ((d.width - base.width) / inc.width) *
915 inc.width + base.width;
916 d.height = ((d.height - base.height) / inc.height) *
917 inc.height + base.height;
918 }
919 else
920 {
921 round_up.width = d.width;
922 round_up.height = d.height;
923 d.width = ((d.width - base.width + inc.width - 1) /
924 inc.width) * inc.width + base.width;
925 d.height = ((d.height - base.height + inc.height - 1) /
926 inc.height) * inc.height + base.height;
927 round_up.width = d.width - round_up.width;
928 round_up.height = d.height - round_up.height;
929 }
930
931 /*
932 * Step 2a: check we didn't move the edge off screen in interactive
933 * moves
934 */
935 if ((flags & CS_ROUND_UP) && e != NULL && e->type == MotionNotify)
936 {
937 if (xmotion > 0 && e->xmotion.x_root < round_up.width)
938 {
939 d.width -= inc.width;
940 }
941 else if (
942 xmotion < 0 && e->xmotion.x_root >=
943 Scr.MyDisplayWidth - round_up.width)
944 {
945 d.width -= inc.width;
946 }
947 if (ymotion > 0 && e->xmotion.y_root < round_up.height)
948 {
949 d.height -= inc.height;
950 }
951 else if (
952 ymotion < 0 && e->xmotion.y_root >=
953 Scr.MyDisplayHeight - round_up.height)
954 {
955 d.height -= inc.height;
956 }
957 }
958
959 /*
960 * Step 2b: Check that we didn't violate min and max.
961 */
962 if (d.width < min.width)
963 {
964 d.width += inc.width;
965 }
966 if (d.height < min.height)
967 {
968 d.height += inc.height;
969 }
970 if (d.width > max.width)
971 {
972 d.width -= inc.width;
973 }
974 if (d.height > max.height)
975 {
976 d.height -= inc.height;
977 }
978
979 /*
980 * Third, adjust for aspect ratio
981 */
982 if (fw->hints.flags & PAspect)
983 {
984 __cs_handle_aspect_ratio(
985 &d, fw, d, base, inc, min, max, xmotion, ymotion,
986 flags);
987 }
988
989 /*
990 * Fourth, account for border width and title height
991 */
992 *widthp = d.width + b.total_size.width;
993 *heightp = d.height + b.total_size.height;
994 if (IS_MAXIMIZED(fw) && (flags & CS_UPDATE_MAX_DEFECT))
995 {
996 /* update size defect for maximized window */
997 fw->g.max_defect.width = old.width - *widthp;
998 fw->g.max_defect.height = old.height - *heightp;
999 }
1000
1001 return;
1002 }
1003
1004 /* This function does roughly the same as constrain_size, but takes into account
1005 * that the window shifts according to gravity if constrain_size actually
1006 * changes the width or height. The frame_g of the window is not changed. The
1007 * target geometry is expected to be in *rect and will be retured through rect.
1008 */
gravity_constrain_size(int gravity,FvwmWindow * t,rectangle * rect,int flags)1009 void gravity_constrain_size(
1010 int gravity, FvwmWindow *t, rectangle *rect, int flags)
1011 {
1012 int new_width = rect->width;
1013 int new_height = rect->height;
1014
1015 if (IS_MAXIMIZED(t) && (flags & CS_UPDATE_MAX_DEFECT))
1016 {
1017 gravity_resize(
1018 gravity, rect, t->g.max_defect.width,
1019 t->g.max_defect.height);
1020 t->g.max_defect.width = 0;
1021 t->g.max_defect.height = 0;
1022 new_width = rect->width;
1023 new_height = rect->height;
1024 }
1025 constrain_size(
1026 t, NULL, &new_width, &new_height, 0, 0, flags);
1027 if (rect->width != new_width || rect->height != new_height)
1028 {
1029 gravity_resize(
1030 gravity, rect, new_width - rect->width,
1031 new_height - rect->height);
1032 }
1033
1034 return;
1035 }
1036
1037 /* returns the icon title geometry if it is visible */
get_visible_icon_title_geometry(FvwmWindow * fw,rectangle * ret_g)1038 Bool get_visible_icon_title_geometry(
1039 FvwmWindow *fw, rectangle *ret_g)
1040 {
1041 if (HAS_NO_ICON_TITLE(fw) || IS_ICON_UNMAPPED(fw) ||
1042 !IS_ICONIFIED(fw))
1043 {
1044 memset(ret_g, 0, sizeof(*ret_g));
1045 return False;
1046 }
1047 *ret_g = fw->icon_g.title_w_g;
1048
1049 return True;
1050 }
1051
1052 /* returns the icon title geometry if it the icon title window exists */
get_icon_title_geometry(FvwmWindow * fw,rectangle * ret_g)1053 Bool get_icon_title_geometry(
1054 FvwmWindow *fw, rectangle *ret_g)
1055 {
1056 if (HAS_NO_ICON_TITLE(fw))
1057 {
1058 memset(ret_g, 0, sizeof(*ret_g));
1059 return False;
1060 }
1061 *ret_g = fw->icon_g.title_w_g;
1062
1063 return True;
1064 }
1065
1066 /* returns the icon picture geometry if it is visible */
get_visible_icon_picture_geometry(FvwmWindow * fw,rectangle * ret_g)1067 Bool get_visible_icon_picture_geometry(
1068 FvwmWindow *fw, rectangle *ret_g)
1069 {
1070 if (fw->icon_g.picture_w_g.width == 0 ||
1071 IS_ICON_UNMAPPED(fw) || !IS_ICONIFIED(fw))
1072 {
1073 memset(ret_g, 0, sizeof(*ret_g));
1074 return False;
1075 }
1076 *ret_g = fw->icon_g.picture_w_g;
1077
1078 return True;
1079 }
1080
1081 /* returns the icon picture geometry if it is exists */
get_icon_picture_geometry(FvwmWindow * fw,rectangle * ret_g)1082 Bool get_icon_picture_geometry(
1083 FvwmWindow *fw, rectangle *ret_g)
1084 {
1085 if (fw->icon_g.picture_w_g.width == 0)
1086 {
1087 memset(ret_g, 0, sizeof(*ret_g));
1088 return False;
1089 }
1090 *ret_g = fw->icon_g.picture_w_g;
1091
1092 return True;
1093 }
1094
1095 /* returns the icon geometry (unexpanded title plus pixmap) if it is visible */
get_visible_icon_geometry(FvwmWindow * fw,rectangle * ret_g)1096 Bool get_visible_icon_geometry(
1097 FvwmWindow *fw, rectangle *ret_g)
1098 {
1099 if (IS_ICON_UNMAPPED(fw) || !IS_ICONIFIED(fw))
1100 {
1101 memset(ret_g, 0, sizeof(*ret_g));
1102 return False;
1103 }
1104 if (fw->icon_g.picture_w_g.width > 0)
1105 {
1106 *ret_g = fw->icon_g.picture_w_g;
1107 if (!HAS_NO_ICON_TITLE(fw))
1108 {
1109 ret_g->height += fw->icon_g.title_w_g.height;
1110 }
1111 }
1112 else if (!HAS_NO_ICON_TITLE(fw))
1113 {
1114 *ret_g = fw->icon_g.title_w_g;
1115 }
1116 else
1117 {
1118 memset(ret_g, 0, sizeof(*ret_g));
1119 return False;
1120 }
1121
1122 return True;
1123 }
1124
1125 /* returns the icon geometry (unexpanded title plus pixmap) if it exists */
get_icon_geometry(FvwmWindow * fw,rectangle * ret_g)1126 void get_icon_geometry(
1127 FvwmWindow *fw, rectangle *ret_g)
1128 {
1129 /* valid geometry? */
1130 if (fw->icon_g.picture_w_g.width > 0)
1131 {
1132 *ret_g = fw->icon_g.picture_w_g;
1133 if (!HAS_NO_ICON_TITLE(fw))
1134 {
1135 ret_g->height += fw->icon_g.title_w_g.height;
1136 }
1137 }
1138 else if (fw->icon_g.title_w_g.width > 0)
1139 {
1140 *ret_g = fw->icon_g.title_w_g;
1141 }
1142 /* valid position? */
1143 else if (fw->icon_g.picture_w_g.x != 0 || fw->icon_g.picture_w_g.y != 0)
1144 {
1145 *ret_g = fw->icon_g.picture_w_g;
1146 }
1147 else if (fw->icon_g.title_w_g.x != 0 || fw->icon_g.title_w_g.y != 0)
1148 {
1149 *ret_g = fw->icon_g.title_w_g;
1150 }
1151 else
1152 {
1153 memset(ret_g, 0, sizeof(*ret_g));
1154 }
1155
1156 return;
1157 }
1158
1159 /* Returns the visible geometry of a window or icon. This can be used to test
1160 * if this region overlaps other windows. */
get_visible_window_or_icon_geometry(FvwmWindow * fw,rectangle * ret_g)1161 Bool get_visible_window_or_icon_geometry(
1162 FvwmWindow *fw, rectangle *ret_g)
1163 {
1164 if (IS_ICONIFIED(fw))
1165 {
1166 return get_visible_icon_geometry(fw, ret_g);
1167 }
1168 *ret_g = fw->g.frame;
1169
1170 return True;
1171 }
1172
move_icon_to_position(FvwmWindow * fw)1173 void move_icon_to_position(
1174 FvwmWindow *fw)
1175 {
1176 Bool draw_picture_w = False;
1177 Bool draw_title_w = False;
1178
1179 if (fw->icon_g.picture_w_g.width > 0)
1180 {
1181 int cs;
1182
1183 if (Scr.Hilite == fw)
1184 {
1185 cs = fw->cs_hi;
1186 }
1187 else
1188 {
1189 cs = fw->cs;
1190 }
1191 XMoveWindow(
1192 dpy, FW_W_ICON_PIXMAP(fw),
1193 fw->icon_g.picture_w_g.x,
1194 fw->icon_g.picture_w_g.y);
1195 if (fw->icon_alphaPixmap ||
1196 (cs >= 0 && Colorset[cs].icon_alpha_percent < 100) ||
1197 CSET_IS_TRANSPARENT(fw->icon_background_cs) ||
1198 (!IS_ICON_SHAPED(fw) && fw->icon_background_padding > 0))
1199 {
1200 draw_picture_w = True;
1201 }
1202 }
1203 if (!HAS_NO_ICON_TITLE(fw))
1204 {
1205 int cs;
1206 rectangle dummy;
1207
1208 if (Scr.Hilite == fw)
1209 {
1210 cs = fw->icon_title_cs_hi;
1211 }
1212 else
1213 {
1214 cs = fw->icon_title_cs;
1215 }
1216 XMoveWindow(
1217 dpy, FW_W_ICON_TITLE(fw),
1218 fw->icon_g.title_w_g.x,
1219 fw->icon_g.title_w_g.y);
1220 if (CSET_IS_TRANSPARENT(cs) &&
1221 !get_visible_icon_picture_geometry(fw, &dummy) &&
1222 get_visible_icon_title_geometry(fw, &dummy))
1223 {
1224 draw_title_w = True;
1225 }
1226 }
1227
1228 if (draw_title_w || draw_picture_w)
1229 {
1230 DrawIconWindow(
1231 fw, draw_title_w, draw_picture_w, False, draw_picture_w,
1232 NULL);
1233 }
1234
1235 return;
1236 }
1237
broadcast_icon_geometry(FvwmWindow * fw,Bool do_force)1238 void broadcast_icon_geometry(
1239 FvwmWindow *fw, Bool do_force)
1240 {
1241 rectangle g;
1242 Bool rc;
1243
1244 rc = get_visible_icon_geometry(fw, &g);
1245 if (rc == True && (!IS_ICON_UNMAPPED(fw) || do_force == True))
1246 {
1247 BroadcastPacket(
1248 M_ICON_LOCATION, 7, (long)FW_W(fw),
1249 (long)FW_W_FRAME(fw), (unsigned long)fw,
1250 (long)g.x, (long)g.y, (long)g.width, (long)g.height);
1251 }
1252
1253 return;
1254 }
1255
modify_icon_position(FvwmWindow * fw,int dx,int dy)1256 void modify_icon_position(
1257 FvwmWindow *fw, int dx, int dy)
1258 {
1259 if (fw->icon_g.picture_w_g.width > 0 || HAS_NO_ICON_TITLE(fw))
1260 {
1261 /* picture position is also valid if there is neither a picture
1262 * nor a title */
1263 fw->icon_g.picture_w_g.x += dx;
1264 fw->icon_g.picture_w_g.y += dy;
1265 }
1266 if (!HAS_NO_ICON_TITLE(fw))
1267 {
1268 fw->icon_g.title_w_g.x += dx;
1269 fw->icon_g.title_w_g.y += dy;
1270 }
1271
1272 return;
1273 }
1274
1275 /* set the icon position to the specified value. take care of the actual icon
1276 * layout */
set_icon_position(FvwmWindow * fw,int x,int y)1277 void set_icon_position(
1278 FvwmWindow *fw, int x, int y)
1279 {
1280 if (fw->icon_g.picture_w_g.width > 0)
1281 {
1282 fw->icon_g.picture_w_g.x = x;
1283 fw->icon_g.picture_w_g.y = y;
1284 }
1285 else
1286 {
1287 fw->icon_g.picture_w_g.x = 0;
1288 fw->icon_g.picture_w_g.y = 0;
1289 }
1290 if (!HAS_NO_ICON_TITLE(fw))
1291 {
1292 fw->icon_g.title_w_g.x = x;
1293 fw->icon_g.title_w_g.y = y;
1294 }
1295 else
1296 {
1297 fw->icon_g.title_w_g.x = 0;
1298 fw->icon_g.title_w_g.y = 0;
1299 }
1300 if (fw->icon_g.picture_w_g.width > 0 &&
1301 !HAS_NO_ICON_TITLE(fw))
1302 {
1303 fw->icon_g.title_w_g.x -=
1304 (fw->icon_g.title_w_g.width -
1305 fw->icon_g.picture_w_g.width) / 2;
1306 fw->icon_g.title_w_g.y +=
1307 fw->icon_g.picture_w_g.height;
1308 }
1309 else if (fw->icon_g.picture_w_g.width <= 0 && HAS_NO_ICON_TITLE(fw))
1310 {
1311 /* In case there is no icon, fake the icon position so the
1312 * modules know where its window was iconified. */
1313 fw->icon_g.picture_w_g.x = x;
1314 fw->icon_g.picture_w_g.y = y;
1315 }
1316
1317 return;
1318 }
1319
set_icon_picture_size(FvwmWindow * fw,int w,int h)1320 void set_icon_picture_size(
1321 FvwmWindow *fw, int w, int h)
1322 {
1323 if (fw->icon_g.picture_w_g.width > 0)
1324 {
1325 fw->icon_g.picture_w_g.width = w;
1326 fw->icon_g.picture_w_g.height = h;
1327 }
1328 else
1329 {
1330 fw->icon_g.picture_w_g.width = 0;
1331 fw->icon_g.picture_w_g.height = 0;
1332 }
1333
1334 return;
1335 }
1336
resize_icon_title_height(FvwmWindow * fw,int dh)1337 void resize_icon_title_height(FvwmWindow *fw, int dh)
1338 {
1339 if (!HAS_NO_ICON_TITLE(fw))
1340 {
1341 fw->icon_g.title_w_g.height += dh;
1342 }
1343
1344 return;
1345 }
1346
get_page_offset_rectangle(int * ret_page_x,int * ret_page_y,rectangle * r)1347 void get_page_offset_rectangle(
1348 int *ret_page_x, int *ret_page_y, rectangle *r)
1349 {
1350 int xoff = Scr.Vx % Scr.MyDisplayWidth;
1351 int yoff = Scr.Vy % Scr.MyDisplayHeight;
1352
1353 /* maximize on the page where the center of the window is */
1354 *ret_page_x = truncate_to_multiple(
1355 r->x + r->width / 2 + xoff, Scr.MyDisplayWidth) - xoff;
1356 *ret_page_y = truncate_to_multiple(
1357 r->y + r->height / 2 + yoff, Scr.MyDisplayHeight) - yoff;
1358
1359 return;
1360 }
1361
get_page_offset(int * ret_page_x,int * ret_page_y,FvwmWindow * fw)1362 void get_page_offset(
1363 int *ret_page_x, int *ret_page_y, FvwmWindow *fw)
1364 {
1365 rectangle r;
1366
1367 r.x = fw->g.frame.x;
1368 r.y = fw->g.frame.y;
1369 r.width = fw->g.frame.width;
1370 r.height = fw->g.frame.height;
1371 get_page_offset_rectangle(ret_page_x, ret_page_y, &r);
1372
1373 return;
1374 }
1375
get_page_offset_check_visible(int * ret_page_x,int * ret_page_y,FvwmWindow * fw)1376 void get_page_offset_check_visible(
1377 int *ret_page_x, int *ret_page_y, FvwmWindow *fw)
1378 {
1379 if (IsRectangleOnThisPage(&fw->g.frame, fw->Desk))
1380 {
1381 /* maximize on visible page if any part of the window is
1382 * visible */
1383 *ret_page_x = 0;
1384 *ret_page_y = 0;
1385 }
1386 else
1387 {
1388 get_page_offset(ret_page_x, ret_page_y, fw);
1389 }
1390
1391 return;
1392 }
1393
1394 /* ---------------------------- builtin commands --------------------------- */
1395