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