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