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 /*
17  *
18  * code for moving and resizing windows
19  *
20  */
21 
22 #include "config.h"
23 
24 #include <stdio.h>
25 #include <math.h>
26 #include <stdbool.h>
27 
28 #include "libs/fvwmlib.h"
29 #include "libs/Picture.h"
30 #include "libs/Grab.h"
31 #include "libs/Parse.h"
32 #include "libs/Graphics.h"
33 #include "libs/FEvent.h"
34 #include "fvwm.h"
35 #include "externs.h"
36 #include "cursor.h"
37 #include "execcontext.h"
38 #include "commands.h"
39 #include "misc.h"
40 #include "screen.h"
41 #include "menus.h"
42 #include "menuparameters.h"
43 #include "module_list.h"
44 #include "module_interface.h"
45 #include "focus.h"
46 #include "borders.h"
47 #include "frame.h"
48 #include "geometry.h"
49 #include "ewmh.h"
50 #include "virtual.h"
51 #include "decorations.h"
52 #include "events.h"
53 #include "eventhandler.h"
54 #include "eventmask.h"
55 #include "colormaps.h"
56 #include "update.h"
57 #include "stack.h"
58 #include "move_resize.h"
59 #include "functions.h"
60 #include "style.h"
61 #include "externs.h"
62 
63 /* ----- move globals ----- */
64 
65 #define MOVE_NORMAL  0x00
66 #define MOVE_PAGE    0x01
67 #define MOVE_SCREEN  0x02
68 
69 /* Animated move stuff added by Greg J. Badros, gjb@cs.washington.edu */
70 
71 float rgpctMovementDefault[32] =
72 {
73 	-.01, 0, .01, .03,.08,.18,.3,.45,.60,.75,.85,.90,.94,.97,.99,1.0
74 	/* must end in 1.0 */
75 };
76 int cmsDelayDefault = 10; /* milliseconds */
77 
78 /* current geometry of size window */
79 static rectangle sizew_g =
80 {
81 	-1,
82 	-1,
83 	-1,
84 	-1
85 };
86 
87 static int move_interactive_finish_button_mask =
88 	((1<<(NUMBER_OF_EXTENDED_MOUSE_BUTTONS))-1) & ~0x2;
89 static int move_drag_finish_button_mask =
90 	((1<<(NUMBER_OF_EXTENDED_MOUSE_BUTTONS))-1) & ~0x3;
91 
92 /* ----- end of move globals ----- */
93 
94 /* ----- resize globals ----- */
95 
96 /* DO NOT USE (STATIC) GLOBALS IN THIS MODULE!
97  * Since some functions are called from other modules unwanted side effects
98  * (i.e. bugs.) would be created */
99 
100 extern Window PressedW;
101 
102 static void draw_move_resize_grid(int x, int  y, int  width, int height);
103 static void grow_to_closest_type(FvwmWindow *, rectangle *, rectangle, int *,
104     int, bool);
105 static void set_geom_win_visible_val(char *, bool);
106 static bool set_geom_win_position_val(char *, int *, bool *, bool *);
107 
108 /* ----- end of resize globals ----- */
109 
110 /*
111  *
112  *  Procedure:
113  *      draw_move_resize_grid - move a window outline
114  *
115  *  Inputs:
116  *      root        - the window we are outlining
117  *      x           - upper left x coordinate
118  *      y           - upper left y coordinate
119  *      width       - the width of the rectangle
120  *      height      - the height of the rectangle
121  *
122  */
get_outline_rects(XRectangle * rects,int x,int y,int width,int height)123 static int get_outline_rects(
124 	XRectangle *rects, int x, int y, int width, int height)
125 {
126 	int i;
127 	int n;
128 	int m;
129 
130 	n = 3;
131 	m = (width - 5) / 2;
132 	if (m < n)
133 	{
134 		n = m;
135 	}
136 	m = (height - 5) / 2;
137 	if (m < n)
138 	{
139 		n = m;
140 	}
141 	if (n < 1)
142 	{
143 		n = 1;
144 	}
145 
146 	for (i = 0; i < n; i++)
147 	{
148 		rects[i].x = x + i;
149 		rects[i].y = y + i;
150 		rects[i].width = width - (i << 1);
151 		rects[i].height = height - (i << 1);
152 	}
153 	if (width - (n << 1) >= 5 && height - (n << 1) >= 5)
154 	{
155 		if (width - (n << 1) >= 10)
156 		{
157 			int off = (width - (n << 1)) / 3 + n;
158 			rects[i].x = x + off;
159 			rects[i].y = y + n;
160 			rects[i].width = width - (off << 1);
161 			rects[i].height = height - (n << 1);
162 			i++;
163 		}
164 		if (height - (n << 1) >= 10)
165 		{
166 			int off = (height - (n << 1)) / 3 + n;
167 			rects[i].x = x + n;
168 			rects[i].y = y + off;
169 			rects[i].width = width - (n << 1);
170 			rects[i].height = height - (off << 1);
171 			i++;
172 		}
173 	}
174 
175 	return i;
176 }
177 
178 struct
179 {
180 	rectangle geom;
181 	struct
182 	{
183 		unsigned is_enabled : 1;
184 	} flags;
185 } move_resize_grid =
186 {
187 	{ 0, 0, 0, 0 },
188 	{ 0 }
189 };
190 
draw_move_resize_grid(int x,int y,int width,int height)191 static void draw_move_resize_grid(int x, int  y, int  width, int height)
192 {
193 	int nrects = 0;
194 	XRectangle rects[10];
195 
196 	if (move_resize_grid.flags.is_enabled &&
197 	    x == move_resize_grid.geom.x &&
198 	    y == move_resize_grid.geom.y &&
199 	    width == move_resize_grid.geom.width &&
200 	    height == move_resize_grid.geom.height)
201 	{
202 		return;
203 	}
204 
205 	memset(rects, 0, 10 * sizeof(XRectangle));
206 	/* place the resize rectangle into the array of rectangles */
207 	/* interleave them for best visual look */
208 	/* draw the new one, if any */
209 	if (move_resize_grid.flags.is_enabled
210 	    /*move_resize_grid.geom.width && move_resize_grid.geom.height*/)
211 	{
212 		move_resize_grid.flags.is_enabled = 0;
213 		nrects +=
214 			get_outline_rects(
215 				&(rects[0]), move_resize_grid.geom.x,
216 				move_resize_grid.geom.y,
217 				move_resize_grid.geom.width,
218 				move_resize_grid.geom.height);
219 	}
220 	if (width && height)
221 	{
222 		move_resize_grid.flags.is_enabled = 1;
223 		move_resize_grid.geom.x = x;
224 		move_resize_grid.geom.y = y;
225 		move_resize_grid.geom.width = width;
226 		move_resize_grid.geom.height = height;
227 		nrects += get_outline_rects(
228 			&(rects[nrects]), x, y, width, height);
229 	}
230 	if (nrects > 0)
231 	{
232 		XDrawRectangles(dpy, Scr.Root, Scr.XorGC, rects, nrects);
233 		XFlush(dpy);
234 	}
235 
236 	return;
237 }
238 
switch_move_resize_grid(Bool state)239 void switch_move_resize_grid(Bool state)
240 {
241 	if (state == False)
242 	{
243 		if (move_resize_grid.flags.is_enabled)
244 		{
245 			draw_move_resize_grid(0, 0, 0, 0);
246 		}
247 		else
248 		{
249 			move_resize_grid.geom.x = 0;
250 			move_resize_grid.geom.y = 0;
251 			move_resize_grid.geom.width = 0;
252 			move_resize_grid.geom.height = 0;
253 		}
254 	}
255 	else if (!move_resize_grid.flags.is_enabled)
256 	{
257 		if (move_resize_grid.geom.width &&
258 		    move_resize_grid.geom.height)
259 		{
260 			draw_move_resize_grid(
261 				move_resize_grid.geom.x,
262 				move_resize_grid.geom.y,
263 				move_resize_grid.geom.width,
264 				move_resize_grid.geom.height);
265 		}
266 	}
267 
268 	return;
269 }
270 
ParsePositionArgumentSuffix(float * ret_factor,char * suffix,float wfactor,float sfactor)271 static int ParsePositionArgumentSuffix(
272 	float *ret_factor, char *suffix, float wfactor, float sfactor)
273 {
274 	int n;
275 
276 	switch (*suffix)
277 	{
278 	case 'p':
279 	case 'P':
280 		*ret_factor = 1.0;
281 		n = 1;
282 		break;
283 	case 'w':
284 	case 'W':
285 		*ret_factor = wfactor;
286 		n = 1;
287 		break;
288 	default:
289 		*ret_factor = sfactor;
290 		n = 0;
291 		break;
292 	}
293 
294 	return n;
295 }
296 
297 /* Functions to shuffle windows to the closest boundary. */
grow_bound_to_next_monitor(FvwmWindow * fw,rectangle * bound,rectangle win_r,direction_t dir)298 static void grow_bound_to_next_monitor(
299 	FvwmWindow *fw, rectangle *bound, rectangle win_r, direction_t dir)
300 {
301 	int page_x, page_y;
302 	struct monitor *m;
303 
304 	get_page_offset_check_visible(&page_x, &page_y, fw);
305 	bound->x -= page_x;
306 	bound->y -= page_y;
307 	win_r.x -= page_x;
308 	win_r.y -= page_y;
309 
310 	TAILQ_FOREACH(m, &monitor_q, entry)
311 	{
312 		if (fw->m == m)
313 			continue;
314 
315 		if (dir == DIR_N && m->si->y + m->si->h == fw->m->si->y &&
316 			win_r.x < m->si->x + m->si->w &&
317 			win_r.x + win_r.width > m->si->x)
318 		{
319 			bound->y = m->si->y + m->ewmhc.BaseStrut.top;
320 			bound->height = win_r.y + win_r.height - bound->y;
321 		}
322 		else if (dir == DIR_E && m->si->x == fw->m->si->x +
323 			fw->m->si->w &&	win_r.y < m->si->y + m->si->h &&
324 				win_r.y + win_r.height > m->si->y)
325 		{
326 			bound->width = m->si->x + m->si->w - bound->x -
327 				m->ewmhc.BaseStrut.right;
328 		}
329 		else if (dir == DIR_S && m->si->y == fw->m->si->y +
330 			fw->m->si->h &&	win_r.x < m->si->x + m->si->w &&
331 			win_r.x + win_r.width > m->si->x)
332 		{
333 			bound->height = m->si->y + m->si->h - bound->y -
334 				m->ewmhc.BaseStrut.bottom;
335 		}
336 		else if (dir == DIR_W && m->si->x + m->si->w == fw->m->si->x &&
337 			win_r.y < m->si->y + m->si->h &&
338 			win_r.y + win_r.height > m->si->y)
339 		{
340 			bound->x = m->si->x + m->ewmhc.BaseStrut.left;
341 			bound->width = win_r.x + win_r.width - bound->x;
342 		}
343 	}
344 	bound->x += page_x;
345 	bound->y += page_y;
346 }
shuffle_win_to_closest(FvwmWindow * fw,char ** action,int * pFinalX,int * pFinalY,Bool * fWarp)347 static void shuffle_win_to_closest(
348 	FvwmWindow *fw, char **action, int *pFinalX, int *pFinalY, Bool *fWarp)
349 {
350 	direction_t dir;
351 	rectangle cwin, bound;
352 	char *naction, *token = NULL;
353 	int page_x, page_y, n;
354 	int snap = SNAP_NONE;
355 	int layers[2] = { -1, -1 };
356 
357 	cwin = fw->g.frame;
358 	get_page_offset_check_visible(&page_x, &page_y, fw);
359 
360 	token = PeekToken(*action, &naction);
361 	/* Get flags */
362 	while (token)
363 	{
364 		if (StrEquals(token, "snap"))
365 		{
366 			*action = naction;
367 			token = PeekToken(*action, &naction);
368 			if (StrEquals(token, "windows"))
369 				snap = SNAP_WINDOWS;
370 			else if (StrEquals(token, "icons"))
371 				snap = SNAP_ICONS;
372 			else if (StrEquals(token, "same"))
373 				snap = SNAP_SAME;
374 		}
375 		else if (StrEquals(token, "layers"))
376 		{
377 			*action = naction;
378 			n = GetIntegerArguments(*action, &naction, layers, 2);
379 			if (n != 2)
380 			{
381 				layers[0] = -1;
382 				layers[1] = -1;
383 			}
384 		}
385 		else if (StrEquals(token, "Warp") && fWarp != NULL)
386 		{
387 			*fWarp = true;
388 		}
389 		else
390 		{
391 			break;
392 		}
393 		*action = naction;
394 		token = PeekToken(*action, &naction);
395 
396 	}
397 
398 	/* Get direction(s) */
399 	while (token)
400 	{
401 		dir = gravity_parse_dir_argument(
402 				*action, &naction, DIR_NONE);
403 
404 		switch (dir)
405 		{
406 		case DIR_N:
407 			bound.x = cwin.x;
408 			bound.y = fw->m->si->y + page_y;
409 			if (cwin.y - bound.y > fw->m->ewmhc.BaseStrut.top)
410 				bound.y += fw->m->ewmhc.BaseStrut.top;
411 			bound.width = cwin.width;
412 			bound.height = cwin.y + cwin.height - bound.y;
413 			if (cwin.y <= bound.y)
414 			{
415 				grow_bound_to_next_monitor(
416 					fw, &bound, cwin, DIR_N);
417 			}
418 			grow_to_closest_type(fw, &cwin, bound, layers,
419 				snap, false);
420 			cwin.height = fw->g.frame.height;
421 			break;
422 		case DIR_E:
423 			bound.x = cwin.x;
424 			bound.y = cwin.y;
425 			bound.width = fw->m->si->x + fw->m->si->w -
426 				bound.x + page_x;
427 			if (bound.x + bound.width - cwin.x - cwin.width >
428 				fw->m->ewmhc.BaseStrut.right)
429 			{
430 				bound.width -= fw->m->ewmhc.BaseStrut.right;
431 			}
432 			bound.height = cwin.height;
433 			if (cwin.x + cwin.width >= bound.x + bound.width)
434 			{
435 				grow_bound_to_next_monitor(
436 					fw, &bound, cwin, DIR_E);
437 			}
438 			grow_to_closest_type(fw, &cwin, bound, layers,
439 				snap, false);
440 			cwin.x = cwin.x + cwin.width - fw->g.frame.width;
441 			cwin.width = fw->g.frame.width;
442 			break;
443 		case DIR_S:
444 			bound.x = cwin.x;
445 			bound.y = cwin.y;
446 			bound.width = cwin.width;
447 			bound.height = fw->m->si->y + fw->m->si->h -
448 				bound.y + page_y;
449 			if (bound.y + bound.height - cwin.y - cwin.height >
450 				fw->m->ewmhc.BaseStrut.bottom)
451 			{
452 				bound.height -= fw->m->ewmhc.BaseStrut.bottom;
453 			}
454 			if (cwin.y + cwin.height >= bound.y + bound.height)
455 			{
456 				grow_bound_to_next_monitor(
457 					fw, &bound, cwin, DIR_S);
458 			}
459 			grow_to_closest_type(fw, &cwin, bound, layers,
460 				snap, false);
461 			cwin.y = cwin.y + cwin.height -	fw->g.frame.height;
462 			cwin.height = fw->g.frame.height;
463 			break;
464 		case DIR_W:
465 			bound.x = fw->m->si->x + page_x;
466 			if (cwin.x - bound.x > fw->m->ewmhc.BaseStrut.left)
467 				bound.x += fw->m->ewmhc.BaseStrut.left;
468 			bound.y = cwin.y;
469 			bound.width = cwin.y + cwin.width - bound.x;
470 			bound.height = cwin.height;
471 			bound.height = cwin.y + cwin.height - bound.y;
472 			if (cwin.x <= bound.x)
473 			{
474 				grow_bound_to_next_monitor(
475 					fw, &bound, cwin, DIR_W);
476 			}
477 			grow_to_closest_type(fw, &cwin, bound, layers,
478 				snap, false);
479 			cwin.width = fw->g.frame.width;
480 			break;
481 		case DIR_NONE:
482 			/* No direction found, need to move to next token */
483 			token = PeekToken(*action, &naction);
484 			break;
485 		default:
486 			break;
487 		}
488 		*action = naction;
489 		token = PeekToken(*action, &naction);
490 	}
491 	*pFinalX = cwin.x;
492 	*pFinalY = cwin.y;
493 }
494 
__get_shift(int val,float factor)495 static int __get_shift(int val, float factor)
496 {
497 	int shift;
498 
499 	if (val >= 0)
500 	{
501 		shift = (int)(val * factor + 0.5);
502 	}
503 	else
504 	{
505 		shift = (int)(val * factor - 0.5);
506 	}
507 
508 	return shift;
509 }
510 
511 /* The vars are named for the x-direction, but this is used for both x and y */
GetOnePositionArgument(char * s1,int window_pos,int window_size,int * pFinalPos,float sfactor,int screen_size,int screen_pos,Bool is_x)512 static int GetOnePositionArgument(
513 	char *s1, int window_pos, int window_size, int *pFinalPos,
514 	float sfactor, int screen_size, int screen_pos, Bool is_x)
515 {
516 	int final_pos;
517 	float wfactor;
518 
519 	if (s1 == 0 || *s1 == 0)
520 	{
521 		return 0;
522 	}
523 	wfactor = (float)window_size / 100;
524 	/* get start position */
525 	switch (*s1)
526 	{
527 	case 'w':
528 	case 'W':
529 		final_pos = window_pos;
530 		s1++;
531 		break;
532 	case 'm':
533 	case 'M':
534 	{
535 	        int x;
536 		int y;
537 
538 		if (
539 			FQueryPointer(
540 			    dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX,
541 			    &JunkY, &x, &y, &JunkMask) == False)
542 		{
543 			/* pointer is on a different screen - that's okay here
544 			 */
545 			final_pos = 0;
546 		}
547 		else
548 		{
549 			final_pos = (is_x) ? x : y;
550 		}
551 		s1++;
552 		break;
553 	}
554 	default:
555 		final_pos = screen_pos;
556 		if (*s1 != 0)
557 		{
558 			int val;
559 			int n;
560 			float f;
561 
562 			/* parse value */
563 			if (sscanf(s1, "-%d%n", &val, &n) >= 1)
564 			{
565 				/* i.e. -1, -+1 or --1 */
566 				final_pos += (screen_size - window_size);
567 				val = -val;
568 			}
569 			else if (
570 				sscanf(s1, "+%d%n", &val, &n) >= 1 ||
571 				sscanf(s1, "%d%n", &val, &n) >= 1)
572 			{
573 				/* i.e. 1, +1, ++1 or +-1 */
574 			}
575 			else
576 			{
577 				/* syntax error, ignore rest of string */
578 				break;
579 			}
580 			s1 += n;
581 			/* parse suffix */
582 			n = ParsePositionArgumentSuffix(
583 				&f, s1, wfactor, sfactor);
584 			s1 += n;
585 			final_pos += __get_shift(val, f);
586 		}
587 		break;
588 	}
589 	/* loop over shift arguments */
590 	while (*s1 != 0)
591 	{
592 		int val;
593 		int n;
594 		float f;
595 
596 		/* parse value */
597 		if (sscanf(s1, "-%d%n", &val, &n) >= 1)
598 		{
599 			/* i.e. -1, -+1 or --1 */
600 			val = -val;
601 		}
602 		else if (
603 			sscanf(s1, "+%d%n", &val, &n) >= 1 ||
604 			sscanf(s1, "%d%n", &val, &n) >= 1)
605 		{
606 			/* i.e. 1, +1, ++1 or +-1 */
607 		}
608 		else
609 		{
610 			/* syntax error, ignore rest of string */
611 			break;
612 		}
613 		s1 += n;
614 		/* parse suffix */
615 		n = ParsePositionArgumentSuffix(&f, s1, wfactor, sfactor);
616 		s1 += n;
617 		final_pos += __get_shift(val, f);
618 	}
619 	*pFinalPos = final_pos;
620 
621 	return 1;
622 }
623 
624 /* GetMoveArguments is used for Move & AnimatedMove
625  * It lets you specify in all the following ways
626  *   20  30          Absolute percent position, from left edge and top
627  *  -50  50          Absolute percent position, from right edge and top
628  *   10p 5p          Absolute pixel position
629  *   10p -0p         Absolute pixel position, from bottom
630  *  w+5  w-10p       Relative position, right 5%, up ten pixels
631  *  m+5  m-10p       Pointer relative position, right 5%, up ten pixels
632  * Returns 2 when x & y have parsed without error, 0 otherwise
633  */
GetMoveArguments(FvwmWindow * fw,char ** paction,int w,int h,int * pFinalX,int * pFinalY,Bool * fWarp,Bool * fPointer,Bool fKeep)634 int GetMoveArguments(FvwmWindow *fw,
635 	char **paction, int w, int h, int *pFinalX, int *pFinalY,
636 	Bool *fWarp, Bool *fPointer, Bool fKeep)
637 {
638 	char *s1 = NULL;
639 	char *s2 = NULL;
640 	char *token = NULL;
641 	char *action;
642 	char *naction;
643 	int scr_x = 0;
644 	int scr_y = 0;
645 	int scr_w = monitor_get_all_widths();
646 	int scr_h = monitor_get_all_heights();
647 	Bool use_working_area = True;
648 	Bool global_flag_parsed = False;
649 	int retval = 0;
650 
651 	if (!paction)
652 	{
653 		return 0;
654 	}
655 	action = *paction;
656 	action = GetNextToken(action, &s1);
657 	if (s1 && fPointer && StrEquals(s1, "pointer"))
658 	{
659 		*fPointer = True;
660 		*paction = action;
661 		free(s1);
662 		return 0;
663 	}
664 	if (s1 && StrEquals(s1, "shuffle"))
665 	{
666 		free(s1);
667 		shuffle_win_to_closest(fw, &action, pFinalX, pFinalY, fWarp);
668 		*paction = action;
669 		return 2;
670 
671 	}
672 	if (s1 && StrEquals(s1, "screen"))
673 	{
674 		fscreen_scr_arg parg;
675 		parg.mouse_ev = NULL;
676 
677 		free(s1);
678 		token = PeekToken(action, &action);
679 		parg.name = token;
680 
681 		/* When being asked to move a window to coordinates which are
682 		 * relative to a given screen, don't assume to use the
683 		 * screen's working area, as the coordinates given are not
684 		 * relative to that.
685 		 */
686 		use_working_area = False;
687 
688 		FScreenGetScrRect(&parg, FSCREEN_BY_NAME, &scr_x, &scr_y,
689 				  &scr_w, &scr_h);
690 		action = GetNextToken(action, &s1);
691 	}
692 	action = GetNextToken(action, &s2);
693 	while (!global_flag_parsed)
694 	{
695 		token = PeekToken(action, &naction);
696 		if (!token)
697 		{
698 			global_flag_parsed = True;
699 			break;
700 		}
701 
702 		if (StrEquals(token, "Warp"))
703 		{
704 			action = naction;
705 			if (fWarp)
706 			{
707 				*fWarp = True;
708 			}
709 		}
710 		else if (StrEquals(token, "ewmhiwa"))
711 		{
712 			use_working_area = False;
713 			action = naction;
714 		}
715 		else
716 		{
717 			global_flag_parsed = True;
718 		}
719 	}
720 
721 	if (use_working_area)
722 	{
723 		EWMH_GetWorkAreaIntersection(
724 			NULL, &scr_x, &scr_y, &scr_w, &scr_h,
725 			EWMH_USE_WORKING_AREA);
726 	}
727 
728 	if (s1 != NULL && s2 != NULL)
729 	{
730 		retval = 0;
731 		if (fKeep == True && StrEquals(s1, "keep"))
732 		{
733 			retval++;
734 		}
735 		else if (
736 			GetOnePositionArgument(
737 				s1, *pFinalX, w, pFinalX, (float)scr_w / 100,
738 				scr_w, scr_x, True))
739 		{
740 			retval++;
741 		}
742 		if (fKeep == True && StrEquals(s2, "keep"))
743 		{
744 			retval++;
745 		}
746 		else if (
747 			GetOnePositionArgument(
748 				s2, *pFinalY, h, pFinalY, (float)scr_h / 100,
749 				scr_h, scr_y, False))
750 		{
751 			retval++;
752 		}
753 		if (retval == 0)
754 		{
755 			/* make sure warping is off for interactive moves */
756 			*fWarp = False;
757 		}
758 	}
759 	else
760 	{
761 		/* not enough arguments, switch to current page. */
762 		while (*pFinalX < 0)
763 		{
764 			*pFinalX = monitor_get_all_widths() + *pFinalX;
765 		}
766 		while (*pFinalY < 0)
767 		{
768 			*pFinalY = monitor_get_all_heights() + *pFinalY;
769 		}
770 	}
771 
772 	if (s1)
773 	{
774 		free(s1);
775 	}
776 	if (s2)
777 	{
778 		free(s2);
779 	}
780 	*paction = action;
781 
782 	return retval;
783 }
784 
ParseOneResizeArgument(char * arg,int scr_size,int wa_size,int dwa_size,int base_size,int size_inc,int add_size,int * ret_size)785 static int ParseOneResizeArgument(
786 	char *arg, int scr_size, int wa_size, int dwa_size, int base_size,
787 	int size_inc, int add_size, int *ret_size)
788 {
789 	float factor;
790 	int val;
791 	int add_base_size = 0;
792 	int cch = strlen(arg);
793 	int tmp_size;
794 
795 	if (cch == 0)
796 	{
797 		return 0;
798 	}
799 	if (StrEquals(arg, "keep"))
800 	{
801 		/* do not change size */
802 		return 1;
803 	}
804 	if (cch > 1 && arg[cch-2] == 'w' && arg[cch-1] == 'a')
805 	{
806 		/* ewmh working area */
807 		factor = (float)wa_size / 100.0;
808 		arg[cch-1] = '\0';
809 	}
810 	else if (cch > 1 && arg[cch-2] == 'd' && arg[cch-1] == 'a')
811 	{
812 		/* ewmh dynamic working area */
813 		factor = (float)dwa_size / 100.0;
814 		arg[cch-1] = '\0';
815 	}
816 	else if (arg[cch-1] == 'p')
817 	{
818 		factor = 1;
819 		arg[cch-1] = '\0';
820 	}
821 	else if (arg[cch-1] == 'c')
822 	{
823 		factor = size_inc;
824 		add_base_size = base_size;
825 		arg[cch-1] = '\0';
826 	}
827 	else
828 	{
829 		factor = (float)scr_size / 100.0;
830 	}
831 	if (strcmp(arg,"w") == 0)
832 	{
833 		/* do not change size */
834 	}
835 	else if (sscanf(arg,"w-%d",&val) == 1)
836 	{
837 		tmp_size = (int)(val * factor + 0.5);
838 		if (tmp_size < *ret_size)
839 		{
840 			*ret_size -= tmp_size;
841 		}
842 		else
843 		{
844 			*ret_size = 0;
845 		}
846 	}
847 	else if (sscanf(arg,"w+%d",&val) == 1 || sscanf(arg,"w%d",&val) == 1)
848 	{
849 		tmp_size = (int)(val * factor + 0.5);
850 		if (-tmp_size < *ret_size)
851 		{
852 			*ret_size += tmp_size;
853 		}
854 		else
855 		{
856 			*ret_size = 0;
857 		}
858 	}
859 	else if (sscanf(arg,"-%d",&val) == 1)
860 	{
861 		tmp_size = (int)(val * factor + 0.5);
862 		if (tmp_size < scr_size + add_size)
863 		{
864 			*ret_size = scr_size - tmp_size + add_size;
865 		}
866 		else
867 		{
868 			*ret_size = 0;
869 		}
870 	}
871 	else if (sscanf(arg,"+%d",&val) == 1 || sscanf(arg,"%d",&val) == 1)
872 	{
873 		tmp_size = (int)(val * factor + 0.5);
874 		if (-tmp_size < add_size + add_base_size)
875 		{
876 			*ret_size = tmp_size + add_size + add_base_size;
877 		}
878 		else
879 		{
880 			*ret_size = 0;
881 		}
882 	}
883 	else
884 	{
885 		return 0;
886 	}
887 
888 	return 1;
889 }
890 
GetResizeArguments(FvwmWindow * fw,char ** paction,int x,int y,int w_base,int h_base,int w_inc,int h_inc,size_borders * sb,int * pFinalW,int * pFinalH,direction_t * ret_dir,Bool * is_direction_fixed,Bool * do_warp_to_border,Bool * automatic_border_direction,Bool * detect_automatic_direction)891 static int GetResizeArguments(FvwmWindow *fw,
892 	char **paction, int x, int y, int w_base, int h_base, int w_inc,
893 	int h_inc, size_borders *sb, int *pFinalW, int *pFinalH,
894 	direction_t *ret_dir, Bool *is_direction_fixed,
895 	Bool *do_warp_to_border, Bool *automatic_border_direction,
896 	Bool *detect_automatic_direction)
897 {
898 	int n;
899 	char *naction;
900 	char *token;
901 	char *tmp_token;
902 	char *s1;
903 	char *s2;
904 	int w_add;
905 	int h_add;
906 	int has_frame_option;
907 	struct monitor	*m = NULL;
908 
909 	*ret_dir = DIR_NONE;
910 	*is_direction_fixed = False;
911 	*do_warp_to_border = False;
912 	*automatic_border_direction = False;
913 	*detect_automatic_direction = False;
914 
915 	if (!paction)
916 	{
917 		return 0;
918 	}
919 	token = PeekToken(*paction, &naction);
920 	if (!token)
921 	{
922 		return 0;
923 	}
924 	if (StrEquals(token, "bottomright") || StrEquals(token, "br"))
925 	{
926 		int nx = x + *pFinalW - 1;
927 		int ny = y + *pFinalH - 1;
928 
929 		n = GetMoveArguments(fw,
930 			&naction, 0, 0, &nx, &ny, NULL, NULL, True);
931 		if (n < 2)
932 		{
933 			return 0;
934 		}
935 		*pFinalW = nx - x + 1;
936 		*pFinalH = ny - y + 1;
937 		*paction = naction;
938 
939 		return n;
940 	}
941 	has_frame_option = 0;
942 	for ( ; ; token = PeekToken(naction, &naction))
943 	{
944 		if (StrEquals(token, "frame"))
945 		{
946 			has_frame_option = 1;
947 		}
948 		else if (StrEquals(token, "direction"))
949 		{
950 			*ret_dir = gravity_parse_dir_argument(
951 					naction, &naction, DIR_NONE);
952 			if (*ret_dir != DIR_NONE)
953 			{
954 				*is_direction_fixed = True;
955 			}
956 			else if (*ret_dir == DIR_NONE)
957 			{
958 				tmp_token = PeekToken(naction, &naction);
959 				if (tmp_token != NULL &&
960 						StrEquals(tmp_token, "automatic"))
961 				{
962 					*detect_automatic_direction = True;
963 					*is_direction_fixed = True;
964 				}
965 			}
966 		}
967 		else if (StrEquals(token, "fixeddirection"))
968 		{
969 			*is_direction_fixed = True;
970 		}
971 		else if (StrEquals(token, "warptoborder"))
972 		{
973 			tmp_token = PeekToken(naction, &naction);
974 
975 			if (tmp_token != NULL &&
976 				StrEquals(tmp_token, "automatic"))
977 			{
978 				*automatic_border_direction = True;
979 			}
980 			*do_warp_to_border = True;
981 		}
982 		else
983 		{
984 			break;
985 		}
986 	}
987 	if (has_frame_option)
988 	{
989 		w_add = 0;
990 		h_add = 0;
991 	}
992 	else
993 	{
994 		w_add = sb->total_size.width;
995 		h_add = sb->total_size.height;
996 	}
997 	s1 = NULL;
998 	if (token != NULL)
999 	{
1000 		s1 = fxstrdup(token);
1001 	}
1002 	naction = GetNextToken(naction, &s2);
1003 	if (!s2)
1004 	{
1005 		free(s1);
1006 		return 0;
1007 	}
1008 	*paction = naction;
1009 
1010 	m = fw->m;
1011 	n = 0;
1012 	n += ParseOneResizeArgument(
1013 		s1, monitor_get_all_widths(),
1014 		m->Desktops->ewmh_working_area.width,
1015 		m->Desktops->ewmh_dyn_working_area.width, w_base, w_inc,
1016 		w_add, pFinalW);
1017 	n += ParseOneResizeArgument(
1018 		s2, monitor_get_all_heights(),
1019 		m->Desktops->ewmh_working_area.height,
1020 		m->Desktops->ewmh_dyn_working_area.height, h_base, h_inc,
1021 		h_add, pFinalH);
1022 
1023 	free(s1);
1024 	free(s2);
1025 
1026 	if (n < 2)
1027 	{
1028 		n = 0;
1029 	}
1030 
1031 	return n;
1032 }
1033 
GetResizeMoveArguments(FvwmWindow * fw,char ** paction,int w_base,int h_base,int w_inc,int h_inc,size_borders * sb,int * pFinalX,int * pFinalY,int * pFinalW,int * pFinalH,Bool * fWarp,Bool * fPointer)1034 static int GetResizeMoveArguments(FvwmWindow *fw,
1035 	char **paction, int w_base, int h_base, int w_inc, int h_inc,
1036 	size_borders *sb, int *pFinalX, int *pFinalY,
1037 	int *pFinalW, int *pFinalH, Bool *fWarp, Bool *fPointer)
1038 {
1039 	char *action = *paction;
1040 	direction_t dir;
1041 	Bool dummy;
1042 
1043 	if (!paction)
1044 	{
1045 		return 0;
1046 	}
1047 	if (GetResizeArguments(fw,
1048 		    &action, *pFinalX, *pFinalY, w_base, h_base, w_inc, h_inc,
1049 		    sb, pFinalW, pFinalH, &dir, &dummy, &dummy, &dummy,
1050 		    &dummy) < 2)
1051 	{
1052 		return 0;
1053 	}
1054 	if (GetMoveArguments(fw,
1055 		    &action, *pFinalW, *pFinalH, pFinalX, pFinalY, fWarp,
1056 		    NULL, True) < 2)
1057 	{
1058 		return 0;
1059 	}
1060 	*paction = action;
1061 
1062 	return 4;
1063 }
1064 
1065 /* Positions the SizeWindow */
position_geometry_window(const XEvent * eventp)1066 static void position_geometry_window(const XEvent *eventp)
1067 {
1068 	int x, y, sgnx = 1, sgny = 1;
1069 	fscreen_scr_t screen = FSCREEN_CURRENT;
1070 	fscreen_scr_arg fscr;
1071 
1072 	/* If we're being called without having been told which screen to use
1073 	 * explicitly, then use the current screen.  This emulates the
1074 	 * behaviour whereby the geometry window follows the pointer.
1075 	 */
1076 	if (Scr.SizeWindow.m == NULL) {
1077 		fscr.mouse_ev = (XEvent *)eventp;
1078 		screen = FSCREEN_CURRENT;
1079 		fscr.name = NULL;
1080 	} else {
1081 		fscr.mouse_ev = (XEvent *)NULL;
1082 		screen = FSCREEN_BY_NAME;
1083 		fscr.name = Scr.SizeWindow.m->si->name;
1084 	}
1085 
1086 	if (Scr.SizeWindow.is_configured) {
1087 		rectangle scr_g;
1088 		FScreenGetScrRect(&fscr, screen, &scr_g.x, &scr_g.y, &scr_g.width,
1089 				&scr_g.height);
1090 
1091 		if (Scr.SizeWindow.xneg)
1092 			sgnx = -1;
1093 		x = scr_g.x + sgnx * (Scr.SizeWindow.x * (scr_g.width -
1094 			sizew_g.width)) / 100;
1095 		if (!Scr.SizeWindow.xrel)
1096 			x = scr_g.x + sgnx * Scr.SizeWindow.x;
1097 		if (Scr.SizeWindow.xneg)
1098 			x += scr_g.width - sizew_g.width;
1099 
1100 		if (Scr.SizeWindow.yneg)
1101 			sgny = -1;
1102 		y = scr_g.y + sgny * (Scr.SizeWindow.y * (scr_g.height -
1103 			sizew_g.height)) / 100;
1104 		if (!Scr.SizeWindow.yrel)
1105 			y = scr_g.y + sgny * Scr.SizeWindow.y;
1106 		if (Scr.SizeWindow.yneg)
1107 			y += scr_g.height - sizew_g.height;
1108 	} else if (Scr.gs.do_emulate_mwm) {
1109 		FScreenCenterOnScreen(
1110 			&fscr, screen, &x, &y, sizew_g.width,
1111 			sizew_g.height);
1112 	} else {
1113 		FScreenGetScrRect(&fscr, screen, &x, &y, NULL, NULL);
1114 	}
1115 
1116 	if (x != sizew_g.x || y != sizew_g.y)
1117 	{
1118 		switch_move_resize_grid(False);
1119 		XMoveWindow(dpy, Scr.SizeWindow.win, x, y);
1120 		switch_move_resize_grid(True);
1121 		sizew_g.x = x;
1122 		sizew_g.y = y;
1123 	}
1124 }
1125 
resize_geometry_window(void)1126 void resize_geometry_window(void)
1127 {
1128 	int w;
1129 	int h;
1130 	int cset = Scr.DefaultColorset;
1131 
1132 	if (Scr.SizeWindow.cset >= 0)
1133 		cset = Scr.SizeWindow.cset;
1134 
1135 	Scr.SizeWindow.StringWidth =
1136 		FlocaleTextWidth(Scr.DefaultFont, GEOMETRY_WINDOW_STRING,
1137 				 sizeof(GEOMETRY_WINDOW_STRING) - 1);
1138 	w = Scr.SizeWindow.StringWidth + 2 * GEOMETRY_WINDOW_BW;
1139 	h = Scr.DefaultFont->height + 2 * GEOMETRY_WINDOW_BW;
1140 	if (w != sizew_g.width || h != sizew_g.height)
1141 	{
1142 		XResizeWindow(dpy, Scr.SizeWindow.win, w, h);
1143 		sizew_g.width = w;
1144 		sizew_g.height = h;
1145 	}
1146 	if (cset >= 0)
1147 	{
1148 		SetWindowBackground(
1149 			dpy, Scr.SizeWindow.win, w, h, &Colorset[cset], Pdepth,
1150 			Scr.StdGC, False);
1151 	}
1152 	else
1153 	{
1154 		XSetWindowBackground(dpy, Scr.SizeWindow.win, Scr.StdBack);
1155 	}
1156 
1157 	return;
1158 }
1159 
1160 /*
1161  *
1162  *  Procedure:
1163  *      DisplayPosition - display the position in the dimensions window
1164  *
1165  *  Inputs:
1166  *      tmp_win - the current fvwm window
1167  *      x, y    - position of the window
1168  *
1169  */
1170 
DisplayPosition(const FvwmWindow * tmp_win,const XEvent * eventp,int x,int y,int Init)1171 static void DisplayPosition(
1172 	const FvwmWindow *tmp_win, const XEvent *eventp, int x, int y,int Init)
1173 {
1174 	char str[100];
1175 	int offset;
1176 	GC reliefGC, shadowGC;
1177 	FlocaleWinString fstr;
1178 	fscreen_scr_arg fscr;
1179 
1180 	if (Scr.gs.do_hide_position_window)
1181 	{
1182 		return;
1183 	}
1184 	position_geometry_window(eventp);
1185 	/* Translate x,y into local screen coordinates,
1186 	 * in case Xinerama is used. */
1187 	fscr.xypos.x = x;
1188 	fscr.xypos.y = y;
1189 	FScreenTranslateCoordinates(NULL, FSCREEN_GLOBAL, &fscr,
1190 			FSCREEN_XYPOS, &x, &y);
1191 	(void)sprintf(str, GEOMETRY_WINDOW_POS_STRING, x, y);
1192 	if (Init)
1193 	{
1194 		XClearWindow(dpy, Scr.SizeWindow.win);
1195 	}
1196 	else
1197 	{
1198 		/* just clear indside the relief lines to reduce flicker */
1199 		XClearArea(dpy, Scr.SizeWindow.win,
1200 			   GEOMETRY_WINDOW_BW, GEOMETRY_WINDOW_BW,
1201 			   Scr.SizeWindow.StringWidth,
1202 			   Scr.DefaultFont->height, False);
1203 	}
1204 
1205 	memset(&fstr, 0, sizeof(fstr));
1206 	if (Scr.SizeWindow.cset >= 0)
1207 	{
1208 		fstr.colorset = &Colorset[Scr.SizeWindow.cset];
1209 		fstr.flags.has_colorset = True;
1210 	}
1211 	else if (Scr.DefaultColorset >= 0)
1212 	{
1213 		fstr.colorset = &Colorset[Scr.DefaultColorset];
1214 		fstr.flags.has_colorset = True;
1215 	}
1216 	if (fstr.flags.has_colorset)
1217 	{
1218 		XGCValues gcv;
1219 
1220 		gcv.foreground = fstr.colorset->hilite;
1221 		reliefGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin,
1222 			GCForeground, &gcv);
1223 		gcv.foreground = fstr.colorset->shadow;
1224 		shadowGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin,
1225 			GCForeground, &gcv);
1226 	}
1227 	else
1228 	{
1229 		reliefGC = Scr.StdReliefGC;
1230 		shadowGC = Scr.StdShadowGC;
1231 	}
1232 
1233 	if (Pdepth >= 2)
1234 	{
1235 		RelieveRectangle(
1236 			dpy, Scr.SizeWindow.win, 0, 0,
1237 			Scr.SizeWindow.StringWidth + GEOMETRY_WINDOW_BW * 2 - 1,
1238 			Scr.DefaultFont->height + GEOMETRY_WINDOW_BW * 2 - 1,
1239 			reliefGC, shadowGC, GEOMETRY_WINDOW_BW);
1240 	}
1241 	offset = (Scr.SizeWindow.StringWidth -
1242 		  FlocaleTextWidth(Scr.DefaultFont, str, strlen(str))) / 2;
1243 	offset += GEOMETRY_WINDOW_BW;
1244 
1245 	fstr.str = str;
1246 	fstr.win = Scr.SizeWindow.win;
1247 	fstr.gc = Scr.StdGC;
1248 	fstr.x = offset;
1249 	fstr.y = Scr.DefaultFont->ascent + GEOMETRY_WINDOW_BW;
1250 	FlocaleDrawString(dpy, Scr.DefaultFont, &fstr, 0);
1251 
1252 	return;
1253 }
1254 
1255 
1256 /*
1257  *
1258  *  Procedure:
1259  *      DisplaySize - display the size in the dimensions window
1260  *
1261  *  Inputs:
1262  *      tmp_win - the current fvwm window
1263  *      width   - the width of the rubber band
1264  *      height  - the height of the rubber band
1265  *
1266  */
DisplaySize(const FvwmWindow * tmp_win,const XEvent * eventp,int width,int height,Bool Init,Bool resetLast)1267 static void DisplaySize(
1268 	const FvwmWindow *tmp_win, const XEvent *eventp, int width,
1269 	int height, Bool Init, Bool resetLast)
1270 {
1271 	char str[100];
1272 	int dwidth,dheight,offset;
1273 	size_borders b;
1274 	static int last_width = 0;
1275 	static int last_height = 0;
1276 	GC reliefGC, shadowGC;
1277 	FlocaleWinString fstr;
1278 
1279 	if (Scr.gs.do_hide_resize_window)
1280 	{
1281 		return;
1282 	}
1283 	position_geometry_window(eventp);
1284 	if (resetLast)
1285 	{
1286 		last_width = 0;
1287 		last_height = 0;
1288 	}
1289 	if (last_width == width && last_height == height)
1290 	{
1291 		return;
1292 	}
1293 	last_width = width;
1294 	last_height = height;
1295 
1296 	get_window_borders(tmp_win, &b);
1297 	dheight = height - b.total_size.height;
1298 	dwidth = width - b.total_size.width;
1299 	dwidth -= tmp_win->hints.base_width;
1300 	dheight -= tmp_win->hints.base_height;
1301 	dwidth /= tmp_win->hints.width_inc;
1302 	dheight /= tmp_win->hints.height_inc;
1303 
1304 	(void)sprintf(str, GEOMETRY_WINDOW_SIZE_STRING, dwidth, dheight);
1305 	if (Init)
1306 	{
1307 		XClearWindow(dpy,Scr.SizeWindow.win);
1308 	}
1309 	else
1310 	{
1311 		/* just clear indside the relief lines to reduce flicker */
1312 		XClearArea(
1313 			dpy, Scr.SizeWindow.win, GEOMETRY_WINDOW_BW,
1314 			GEOMETRY_WINDOW_BW, Scr.SizeWindow.StringWidth,
1315 			Scr.DefaultFont->height, False);
1316 	}
1317 
1318 	memset(&fstr, 0, sizeof(fstr));
1319 	if (Scr.SizeWindow.cset >= 0)
1320 	{
1321 		fstr.colorset = &Colorset[Scr.SizeWindow.cset];
1322 		fstr.flags.has_colorset = True;
1323 	}
1324 	else if (Scr.DefaultColorset >= 0)
1325 	{
1326 		fstr.colorset = &Colorset[Scr.DefaultColorset];
1327 		fstr.flags.has_colorset = True;
1328 	}
1329 	if (fstr.flags.has_colorset)
1330 	{
1331 		XGCValues gcv;
1332 
1333 
1334 		gcv.foreground = fstr.colorset->hilite;
1335 		reliefGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin,
1336 			GCForeground, &gcv);
1337 		gcv.foreground = fstr.colorset->shadow;
1338 		shadowGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin,
1339 			GCForeground, &gcv);
1340 	}
1341 	else
1342 	{
1343 		reliefGC = Scr.StdReliefGC;
1344 		shadowGC = Scr.StdShadowGC;
1345 	}
1346 
1347 	if (Pdepth >= 2)
1348 	{
1349 		RelieveRectangle(
1350 			dpy, Scr.SizeWindow.win, 0, 0,
1351 			Scr.SizeWindow.StringWidth + GEOMETRY_WINDOW_BW * 2 - 1,
1352 			Scr.DefaultFont->height + GEOMETRY_WINDOW_BW*2 - 1,
1353 			reliefGC, shadowGC, GEOMETRY_WINDOW_BW);
1354 	}
1355 	offset = (Scr.SizeWindow.StringWidth -
1356 		  FlocaleTextWidth(Scr.DefaultFont, str, strlen(str))) / 2;
1357 	offset += GEOMETRY_WINDOW_BW;
1358 
1359 	fstr.str = str;
1360 	fstr.win = Scr.SizeWindow.win;
1361 	fstr.gc = Scr.StdGC;
1362 	fstr.x = offset;
1363 	fstr.y = Scr.DefaultFont->ascent + GEOMETRY_WINDOW_BW;
1364 	FlocaleDrawString(dpy, Scr.DefaultFont, &fstr, 0);
1365 
1366 	return;
1367 }
1368 
resize_move_window(F_CMD_ARGS)1369 static Bool resize_move_window(F_CMD_ARGS)
1370 {
1371 	int FinalX = 0;
1372 	int FinalY = 0;
1373 	int FinalW = 0;
1374 	int FinalH = 0;
1375 	int n;
1376 	int x,y;
1377 	Bool fWarp = False;
1378 	Bool fPointer = False;
1379 	int dx;
1380 	int dy;
1381 	size_borders b;
1382 	FvwmWindow *fw = exc->w.fw;
1383 	Window w = exc->w.w;
1384 
1385 	if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
1386 	{
1387 		return False;
1388 	}
1389 	if (!is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True))
1390 	{
1391 		return False;
1392 	}
1393 
1394 	/* gotta have a window */
1395 	w = FW_W_FRAME(fw);
1396 	if (!XGetGeometry(
1397 		    dpy, w, &JunkRoot, &x, &y, (unsigned int*)&FinalW,
1398 		    (unsigned int*)&FinalH, (unsigned int*)&JunkBW,
1399 		    (unsigned int*)&JunkDepth))
1400 	{
1401 		XBell(dpy, 0);
1402 		return False;
1403 	}
1404 
1405 	FinalX = x;
1406 	FinalY = y;
1407 
1408 	get_window_borders(fw, &b);
1409 	n = GetResizeMoveArguments(
1410 		fw,
1411 		&action,
1412 		fw->hints.base_width, fw->hints.base_height,
1413 		fw->hints.width_inc, fw->hints.height_inc,
1414 		&b, &FinalX, &FinalY, &FinalW, &FinalH, &fWarp, &fPointer);
1415 	if (n < 4)
1416 	{
1417 		return False;
1418 	}
1419 
1420 	if (IS_MAXIMIZED(fw))
1421 	{
1422 		/* must redraw the buttons now so that the 'maximize' button
1423 		 * does not stay depressed. */
1424 		SET_MAXIMIZED(fw, 0);
1425 		border_draw_decorations(
1426 			fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
1427 			NULL, NULL);
1428 	}
1429 	dx = FinalX - fw->g.frame.x;
1430 	dy = FinalY - fw->g.frame.y;
1431 	/* size will be less or equal to requested */
1432 	constrain_size(fw, NULL, &FinalW, &FinalH, 0, 0, 0);
1433 	if (IS_SHADED(fw))
1434 	{
1435 		frame_setup_window(
1436 			fw, FinalX, FinalY, FinalW, fw->g.frame.height, False);
1437 	}
1438 	else
1439 	{
1440 		frame_setup_window(fw, FinalX, FinalY, FinalW, FinalH, True);
1441 	}
1442 	if (fWarp)
1443 	{
1444 		FWarpPointer(
1445 			dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y);
1446 	}
1447 	if (IS_MAXIMIZED(fw))
1448 	{
1449 		fw->g.max.x += dx;
1450 		fw->g.max.y += dy;
1451 	}
1452 	else
1453 	{
1454 		fw->g.normal.x += dx;
1455 		fw->g.normal.y += dy;
1456 	}
1457 	update_absolute_geometry(fw);
1458 	maximize_adjust_offset(fw);
1459 	XFlush(dpy);
1460 
1461 	return True;
1462 }
1463 
CMD_ResizeMove(F_CMD_ARGS)1464 void CMD_ResizeMove(F_CMD_ARGS)
1465 {
1466 	FvwmWindow *fw = exc->w.fw;
1467 
1468 	if (IS_EWMH_FULLSCREEN(fw))
1469 	{
1470 		/* do not unmaximize ! */
1471 		CMD_ResizeMoveMaximize(F_PASS_ARGS);
1472 		return;
1473 	}
1474 	resize_move_window(F_PASS_ARGS);
1475 
1476 	return;
1477 }
1478 
InteractiveMove(Window * win,const exec_context_t * exc,int * FinalX,int * FinalY,Bool do_start_at_pointer)1479 static void InteractiveMove(
1480 	Window *win, const exec_context_t *exc, int *FinalX, int *FinalY,
1481 	Bool do_start_at_pointer)
1482 {
1483 	int origDragX,origDragY,DragX, DragY, DragWidth, DragHeight;
1484 	int XOffset, YOffset;
1485 	Window w;
1486 	Bool do_move_opaque = False;
1487 
1488 	w = *win;
1489 
1490 	if (Scr.bo.do_install_root_cmap)
1491 	{
1492 		InstallRootColormap();
1493 	}
1494 	else
1495 	{
1496 		InstallFvwmColormap();
1497 	}
1498 	/* warp the pointer to the cursor position from before menu appeared */
1499 	/* domivogt (17-May-1999): an XFlush should not hurt anyway, so do it
1500 	 * unconditionally to remove the external */
1501 	XFlush(dpy);
1502 
1503 	if (do_start_at_pointer)
1504 	{
1505 		if (FQueryPointer(
1506 			    dpy, Scr.Root, &JunkRoot, &JunkChild, &DragX,
1507 			    &DragY, &JunkX, &JunkY, &JunkMask) == False)
1508 		{
1509 			/* pointer is on a different screen */
1510 			DragX = 0;
1511 			DragY = 0;
1512 		}
1513 	}
1514 	else
1515 	{
1516 		/* Although a move is usually done with a button depressed we
1517 		 * have to check for ButtonRelease too since the event may be
1518 		 * faked. */
1519 		fev_get_evpos_or_query(
1520 			dpy, Scr.Root, exc->x.elast, &DragX, &DragY);
1521 	}
1522 
1523 	MyXGrabServer(dpy);
1524 	if (!XGetGeometry(
1525 		    dpy, w, &JunkRoot, &origDragX, &origDragY,
1526 		    (unsigned int*)&DragWidth, (unsigned int*)&DragHeight,
1527 		    (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1528 	{
1529 		MyXUngrabServer(dpy);
1530 		return;
1531 	}
1532 	MyXGrabKeyboard(dpy);
1533 	if (do_start_at_pointer)
1534 	{
1535 		origDragX = DragX;
1536 		origDragY = DragY;
1537 	}
1538 
1539 	if (IS_ICONIFIED(exc->w.fw))
1540 	{
1541 		do_move_opaque = True;
1542 	}
1543 	else if (IS_MAPPED(exc->w.fw))
1544 	{
1545 		float areapct;
1546 
1547 		areapct = 100.0;
1548 		areapct *= ((float)DragWidth / (float) monitor_get_all_widths());
1549 		areapct *= ((float)DragHeight / (float) monitor_get_all_heights());
1550 		/* round up */
1551 		areapct += 0.1;
1552 		if (Scr.OpaqueSize < 0 ||
1553 		    (float)areapct <= (float)Scr.OpaqueSize)
1554 		{
1555 			do_move_opaque = True;
1556 		}
1557 	}
1558 	if (do_move_opaque)
1559 	{
1560 		MyXUngrabServer(dpy);
1561 	}
1562 	else
1563 	{
1564 		Scr.flags.is_wire_frame_displayed = True;
1565 	}
1566 
1567 	if (!do_move_opaque && IS_ICONIFIED(exc->w.fw))
1568 	{
1569 		XUnmapWindow(dpy,w);
1570 	}
1571 
1572 	XOffset = origDragX - DragX;
1573 	YOffset = origDragY - DragY;
1574 	if (!Scr.gs.do_hide_position_window)
1575 	{
1576 		resize_geometry_window();
1577 		position_geometry_window(NULL);
1578 		XMapRaised(dpy,Scr.SizeWindow.win);
1579 	}
1580 	__move_loop(
1581 		exc, XOffset, YOffset, DragWidth, DragHeight, FinalX, FinalY,
1582 		do_move_opaque, CRS_MOVE);
1583 	if (!Scr.gs.do_hide_position_window)
1584 	{
1585 		XUnmapWindow(dpy,Scr.SizeWindow.win);
1586 	}
1587 	if (Scr.bo.do_install_root_cmap)
1588 	{
1589 		UninstallRootColormap();
1590 	}
1591 	else
1592 	{
1593 		UninstallFvwmColormap();
1594 	}
1595 
1596 	if (!do_move_opaque)
1597 	{
1598 		int event_types[2] = { EnterNotify, LeaveNotify };
1599 
1600 		/* Throw away some events that dont interest us right now. */
1601 		discard_typed_events(2, event_types);
1602 		Scr.flags.is_wire_frame_displayed = False;
1603 		MyXUngrabServer(dpy);
1604 	}
1605 	MyXUngrabKeyboard(dpy);
1606 
1607 	return;
1608 }
1609 
1610 /* Perform the movement of the window. ppctMovement *must* have a 1.0 entry
1611  * somewhere in ins list of floats, and movement will stop when it hits a 1.0
1612  * entry */
AnimatedMoveAnyWindow(FvwmWindow * fw,Window w,int startX,int startY,int endX,int endY,Bool fWarpPointerToo,int cmsDelay,float * ppctMovement,MenuRepaintTransparentParameters * pmrtp)1613 static void AnimatedMoveAnyWindow(
1614 	FvwmWindow *fw, Window w, int startX, int startY, int endX,
1615 	int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement,
1616 	MenuRepaintTransparentParameters *pmrtp)
1617 {
1618 	int pointerX, pointerY;
1619 	int currentX, currentY;
1620 	int lastX, lastY;
1621 	int deltaX, deltaY;
1622 	Bool first = True;
1623 	XEvent evdummy;
1624 	unsigned int draw_parts = PART_NONE;
1625 
1626 	if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
1627 	{
1628 		return;
1629 	}
1630 
1631 	/* set our defaults */
1632 	if (ppctMovement == NULL)
1633 	{
1634 		ppctMovement = rgpctMovementDefault;
1635 	}
1636 	if (cmsDelay < 0)
1637 	{
1638 		cmsDelay = cmsDelayDefault;
1639 	}
1640 
1641 	if (startX < 0 || startY < 0)
1642 	{
1643 		if (
1644 			!XGetGeometry(
1645 				dpy, w, &JunkRoot, &currentX, &currentY,
1646 				(unsigned int*)&JunkWidth,
1647 				(unsigned int*)&JunkHeight,
1648 				(unsigned int*)&JunkBW,
1649 				(unsigned int*)&JunkDepth))
1650 		{
1651 			XBell(dpy, 0);
1652 			return;
1653 		}
1654 		if (startX < 0)
1655 		{
1656 			startX = currentX;
1657 		}
1658 		if (startY < 0)
1659 		{
1660 			startY = currentY;
1661 		}
1662 	}
1663 
1664 	deltaX = endX - startX;
1665 	deltaY = endY - startY;
1666 	lastX = startX;
1667 	lastY = startY;
1668 
1669 	if (deltaX == 0 && deltaY == 0)
1670 	{
1671 		/* go nowhere fast */
1672 		return;
1673 	}
1674 
1675 	if (fw && w == FW_W_FRAME(fw))
1676 	{
1677 		draw_parts = border_get_transparent_decorations_part(fw);
1678 	}
1679 
1680 	/* Needed for aborting */
1681 	MyXGrabKeyboard(dpy);
1682 	do
1683 	{
1684 		currentX = startX + deltaX * (*ppctMovement);
1685 		currentY = startY + deltaY * (*ppctMovement);
1686 		if (lastX == currentX && lastY == currentY)
1687 		{
1688 			/* don't waste time in the same spot */
1689 			continue;
1690 		}
1691 		if (pmrtp != NULL)
1692 		{
1693 			update_transparent_menu_bg(
1694 				pmrtp, lastX, lastY, currentX, currentY,
1695 				endX, endY);
1696 		}
1697 		XMoveWindow(dpy,w,currentX,currentY);
1698 		if (pmrtp != NULL)
1699 		{
1700 			repaint_transparent_menu(
1701 				pmrtp, first,
1702 				currentX, currentY, endX, endY, True);
1703 		}
1704 		else if (draw_parts != PART_NONE)
1705 		{
1706 			border_draw_decorations(
1707 				fw, draw_parts,
1708 				((fw == get_focus_window())) ?
1709 				True : False,
1710 				True, CLEAR_ALL, NULL, NULL);
1711 		}
1712 		if (fw && pmrtp == NULL && IS_TEAR_OFF_MENU(fw))
1713 		{
1714 			menu_redraw_transparent_tear_off_menu(fw, False);
1715 		}
1716 		if (fWarpPointerToo == True)
1717 		{
1718 			if (FQueryPointer(
1719 				    dpy, Scr.Root, &JunkRoot, &JunkChild,
1720 				    &JunkX, &JunkY, &pointerX, &pointerY,
1721 				    &JunkMask) == False)
1722 			{
1723 				/* pointer is on a different screen */
1724 				pointerX = currentX;
1725 				pointerY = currentY;
1726 			}
1727 			else
1728 			{
1729 				pointerX += currentX - lastX;
1730 				pointerY += currentY - lastY;
1731 			}
1732 			FWarpPointer(
1733 				dpy, None, Scr.Root, 0, 0, 0, 0, pointerX,
1734 				pointerY);
1735 		}
1736 		if (fw && !IS_SHADED(fw) && !Scr.bo.do_disable_configure_notify)
1737 		{
1738 			/* send configure notify event for windows that care
1739 			 * about their location */
1740 			SendConfigureNotify(
1741 				fw, currentX, currentY,
1742 				fw->g.frame.width,
1743 				fw->g.frame.height, 0, False);
1744 #ifdef FVWM_DEBUG_MSGS
1745 			fvwm_debug(__func__,
1746 				   "Sent ConfigureNotify (w == %d, h == %d)",
1747 				   fw->g.frame.width,
1748 				   fw->g.frame.height);
1749 #endif
1750 		}
1751 		XFlush(dpy);
1752 		if (fw)
1753 		{
1754 			fw->g.frame.x = currentX;
1755 			fw->g.frame.y = currentY;
1756 			update_absolute_geometry(fw);
1757 			maximize_adjust_offset(fw);
1758 			BroadcastConfig(M_CONFIGURE_WINDOW, fw);
1759 			FlushAllMessageQueues();
1760 		}
1761 
1762 		usleep(cmsDelay * 1000); /* usleep takes microseconds */
1763 		/* this didn't work for me -- maybe no longer necessary since
1764 		 * we warn the user when they use > .5 seconds as a
1765 		 * between-frame delay time.
1766 		 *
1767 		 * domivogt (28-apr-1999): That is because the keyboard was not
1768 		 * grabbed. works nicely now.
1769 		 */
1770 		if (FCheckMaskEvent(
1771 			    dpy, ButtonPressMask|ButtonReleaseMask|KeyPressMask,
1772 			    &evdummy))
1773 		{
1774 			/* finish the move immediately */
1775 			if (pmrtp != NULL)
1776 			{
1777 				update_transparent_menu_bg(
1778 					pmrtp, lastX, lastY,
1779 					currentX, currentY, endX, endY);
1780 			}
1781 			XMoveWindow(dpy,w,endX,endY);
1782 			if (pmrtp != NULL)
1783 			{
1784 				repaint_transparent_menu(
1785 					pmrtp, first,
1786 					endX, endY, endX, endY, True);
1787 			}
1788 			break;
1789 		}
1790 		lastX = currentX;
1791 		lastY = currentY;
1792 		first = False;
1793 	} while (*ppctMovement != 1.0 && ppctMovement++);
1794 	MyXUngrabKeyboard(dpy);
1795 	XFlush(dpy);
1796 
1797 	return;
1798 }
1799 
1800 /* used for moving menus, not a client window */
AnimatedMoveOfWindow(Window w,int startX,int startY,int endX,int endY,Bool fWarpPointerToo,int cmsDelay,float * ppctMovement,MenuRepaintTransparentParameters * pmrtp)1801 void AnimatedMoveOfWindow(
1802 	Window w, int startX, int startY, int endX, int endY,
1803 	Bool fWarpPointerToo, int cmsDelay, float *ppctMovement,
1804 	MenuRepaintTransparentParameters *pmrtp)
1805 {
1806 	AnimatedMoveAnyWindow(
1807 		NULL, w, startX, startY, endX, endY, fWarpPointerToo,
1808 		cmsDelay, ppctMovement, pmrtp);
1809 
1810 	return;
1811 }
1812 
1813 /* used for moving client windows */
AnimatedMoveFvwmWindow(FvwmWindow * fw,Window w,int startX,int startY,int endX,int endY,Bool fWarpPointerToo,int cmsDelay,float * ppctMovement)1814 void AnimatedMoveFvwmWindow(
1815 	FvwmWindow *fw, Window w, int startX, int startY, int endX,
1816 	int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement)
1817 {
1818 	AnimatedMoveAnyWindow(
1819 		fw, w, startX, startY, endX, endY, fWarpPointerToo,
1820 		cmsDelay, ppctMovement, NULL);
1821 
1822 	return;
1823 }
1824 
placement_binding(int button,KeySym keysym,int modifier,char * action)1825 int placement_binding(int button, KeySym keysym, int modifier, char *action)
1826 {
1827 	if (keysym != 0)
1828 	{
1829 		/* fixme */
1830 		fvwm_debug(__func__,
1831 			   "sorry, placement keybindings not allowed. yet.");
1832 		return 1;
1833 	}
1834 	if (modifier != 0)
1835 	{
1836 		/* fixme */
1837 		fvwm_debug(__func__,
1838 			   "sorry, placement binding modifiers not allowed. yet.");
1839 		return 1;
1840 	}
1841 	if (strcmp(action,"-") == 0 ||
1842 		strcasecmp(action,"CancelPlacement") == 0)
1843 	{
1844 		if (keysym == 0) /* must be button binding */
1845 		{
1846 			if (button == 0)
1847 			{
1848 				move_drag_finish_button_mask = 0;
1849 				move_interactive_finish_button_mask = 0;
1850 			}
1851 			else if (button > 0 && button <=
1852 				 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1853 			{
1854 				move_drag_finish_button_mask &=
1855 					~(1<<(button-1));
1856 				move_interactive_finish_button_mask &=
1857 					~(1<<(button-1));
1858 			}
1859 		}
1860 	}
1861 	else if (strcasecmp(action,"CancelPlacementDrag") == 0)
1862 	{
1863 		if (keysym == 0) /* must be button binding */
1864 		{
1865 			if (button == 0)
1866 			{
1867 				move_drag_finish_button_mask = 0;
1868 			}
1869 			else if (button > 0 && button <=
1870 				 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1871 			{
1872 				move_drag_finish_button_mask &=
1873 					~(1<<(button-1));
1874 			}
1875 		}
1876 	}
1877 	else if (strcasecmp(action,"CancelPlacementInteractive") == 0)
1878 	{
1879 		if (keysym == 0) /* must be button binding */
1880 		{
1881 			if (button == 0)
1882 			{
1883 				move_interactive_finish_button_mask = 0;
1884 			}
1885 			else if (button > 0 && button <=
1886 				 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1887 			{
1888 				move_interactive_finish_button_mask &=
1889 					~(1<<(button-1));
1890 			}
1891 		}
1892 	}
1893 	else if (strcasecmp(action,"PlaceWindow") == 0)
1894 	{
1895 		if (keysym == 0) /* must be button binding */
1896 		{
1897 			if (button == 0)
1898 			{
1899 				move_interactive_finish_button_mask =
1900 				move_drag_finish_button_mask =
1901 					(1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1902 			}
1903 			else if (button > 0 && button <=
1904 				 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1905 			{
1906 				move_drag_finish_button_mask |= (1<<(button-1));
1907 				move_interactive_finish_button_mask |=
1908 					(1<<(button-1));
1909 			}
1910 		}
1911 	}
1912 	else if (strcasecmp(action,"PlaceWindowDrag") == 0)
1913 	{
1914 		if (keysym == 0) /* must be button binding */
1915 		{
1916 			if (button == 0)
1917 			{
1918 				move_drag_finish_button_mask =
1919 					(1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1920 			}
1921 			else if (button > 0 && button <=
1922 				 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1923 			{
1924 				move_drag_finish_button_mask |= (1<<(button-1));
1925 			}
1926 		}
1927 	}
1928 	else if (strcasecmp(action,"PlaceWindowInteractive") == 0)
1929 	{
1930 		if (keysym == 0) /* must be button binding */
1931 		{
1932 			if (button == 0)
1933 			{
1934 				move_interactive_finish_button_mask =
1935 					(1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1936 			}
1937 			else if (button > 0 && button <=
1938 				 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1939 			{
1940 				move_interactive_finish_button_mask |=
1941 					(1<<(button-1));
1942 			}
1943 		}
1944 	}
1945 	else
1946 	{
1947 		fvwm_debug(__func__,
1948 			   "invalid action %s", action);
1949 	}
1950 
1951 	return 0;
1952 }
1953 
1954 
1955 /*
1956  *
1957  * Start a window move operation
1958  *
1959  */
__move_icon(FvwmWindow * fw,int x,int y,int old_x,int old_y,Bool do_move_animated,Bool do_warp_pointer)1960 void __move_icon(
1961 	FvwmWindow *fw, int x, int y, int old_x, int old_y,
1962 	Bool do_move_animated, Bool do_warp_pointer)
1963 {
1964 	rectangle gt;
1965 	rectangle gp;
1966 	Bool has_icon_title;
1967 	Bool has_icon_picture;
1968 	Window tw;
1969 	int tx;
1970 	int ty;
1971 
1972 	set_icon_position(fw, x, y);
1973 	broadcast_icon_geometry(fw, False);
1974 	has_icon_title = get_visible_icon_title_geometry(fw, &gt);
1975 	has_icon_picture = get_visible_icon_picture_geometry(fw, &gp);
1976 	if (has_icon_picture)
1977 	{
1978 		tw = FW_W_ICON_PIXMAP(fw);
1979 		tx = gp.x;
1980 		ty = gp.y;
1981 	}
1982 	else if (has_icon_title)
1983 	{
1984 		tw = FW_W_ICON_TITLE(fw);
1985 		tx = gt.x;
1986 		ty = gt.y;
1987 	}
1988 	else
1989 	{
1990 		return;
1991 	}
1992 	if (do_move_animated)
1993 	{
1994 		AnimatedMoveOfWindow(
1995 			tw, -1, -1, tx, ty, do_warp_pointer, -1, NULL, NULL);
1996 		do_warp_pointer = 0;
1997 	}
1998 	if (has_icon_title)
1999 	{
2000 		XMoveWindow(dpy, FW_W_ICON_TITLE(fw), gt.x, gt.y);
2001 	}
2002 	if (has_icon_picture)
2003 	{
2004 		struct monitor	*m = fw->m;
2005 		XMoveWindow(dpy, FW_W_ICON_PIXMAP(fw), gp.x, gp.y);
2006 		if (fw->Desk == m->virtual_scr.CurrentDesk)
2007 		{
2008 			XMapWindow(dpy, FW_W_ICON_PIXMAP(fw));
2009 			if (has_icon_title)
2010 			{
2011 				XMapWindow(dpy, FW_W_ICON_TITLE(fw));
2012 			}
2013 		}
2014 	}
2015 	if (do_warp_pointer)
2016 	{
2017 		FWarpPointer(dpy, None, None, 0, 0, 0, 0, x - old_x, y - old_y);
2018 	}
2019 
2020 	return;
2021 }
2022 
__move_window(F_CMD_ARGS,Bool do_animate,int mode)2023 static void __move_window(F_CMD_ARGS, Bool do_animate, int mode)
2024 {
2025 	int FinalX = 0;
2026 	int FinalY = 0;
2027 	int n;
2028 	int x;
2029 	int y;
2030 	int width, height;
2031 	int page_x, page_y;
2032 	Bool fWarp = False;
2033 	Bool fPointer = False;
2034 	int dx;
2035 	int dy;
2036 	FvwmWindow *fw = exc->w.fw;
2037 	Window w;
2038 
2039 	if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
2040 	{
2041 		return;
2042 	}
2043 	/* gotta have a window */
2044 	w = FW_W_FRAME(fw);
2045 	if (IS_ICONIFIED(fw))
2046 	{
2047 		if (FW_W_ICON_PIXMAP(fw) != None)
2048 		{
2049 			w = FW_W_ICON_PIXMAP(fw);
2050 			XUnmapWindow(dpy,FW_W_ICON_TITLE(fw));
2051 		}
2052 		else
2053 		{
2054 			w = FW_W_ICON_TITLE(fw);
2055 		}
2056 		if (w == None && (mode == MOVE_PAGE || mode == MOVE_SCREEN))
2057 		{
2058 			w = FW_W_FRAME(fw);
2059 		}
2060 	}
2061 	if (
2062 		!XGetGeometry(
2063 			dpy, w, &JunkRoot, &x, &y, (unsigned int*)&width,
2064 			(unsigned int*)&height, (unsigned int*)&JunkBW,
2065 			(unsigned int*)&JunkDepth))
2066 	{
2067 		return;
2068 	}
2069 	if (mode == MOVE_PAGE && IS_STICKY_ACROSS_PAGES(fw))
2070 	{
2071 		return;
2072 	}
2073 	if (mode == MOVE_PAGE)
2074 	{
2075 		rectangle r;
2076 		rectangle s;
2077 		rectangle t;
2078 
2079 		struct monitor *m;
2080 
2081 		do_animate = False;
2082 		r.x = x;
2083 		r.y = y;
2084 		r.width = width;
2085 		r.height = height;
2086 		get_absolute_geometry(fw, &t, &r);
2087 		get_page_offset_rectangle(fw, &page_x, &page_y, &t);
2088 
2089 		if (!get_page_arguments(fw, action, &page_x, &page_y, &m))
2090 		{
2091 			m = fw->m;
2092 			page_x = m->virtual_scr.Vx;
2093 			page_y = m->virtual_scr.Vy;
2094 		}
2095 		/* It's possible that we've received a command such as:
2096 		 *
2097 		 * MoveToPage HDMI2 0 1
2098 		 *
2099 		 * In which case, move the window to the selected monitor,
2100 		 * first.
2101 		 */
2102 		if (m != NULL && m != fw->m) {
2103 			rectangle ms;
2104 
2105 			ms.x = m->si->x;
2106 			ms.y = m->si->y;
2107 			ms.width = m->si->w;
2108 			ms.height = m->si->h;
2109 
2110 			/* then move to screen */
2111 			fvwmrect_move_into_rectangle(&r, &ms);
2112 			UPDATE_FVWM_SCREEN(fw);
2113 		}
2114 
2115 		s.x = page_x - m->virtual_scr.Vx;
2116 		s.y = page_y - m->virtual_scr.Vy;
2117 		s.width = monitor_get_all_widths();
2118 		s.height = monitor_get_all_heights();
2119 		fvwmrect_move_into_rectangle(&r, &s);
2120 
2121 		FinalX = r.x;
2122 		FinalY = r.y;
2123 	}
2124 	else if (mode == MOVE_SCREEN)
2125 	{
2126 		rectangle r;
2127 		rectangle s;
2128 		rectangle p;
2129 		struct monitor	*m;
2130 		char		*token;
2131 
2132 		if (action == NULL)
2133 			m = monitor_get_current();
2134 		else {
2135 			token = PeekToken(action, &action);
2136 			m = monitor_resolve_name(token);
2137 		}
2138 
2139 		s.x = m->si->x;
2140 		s.y = m->si->y;
2141 		s.width = m->si->w;
2142 		s.height = m->si->h;
2143 
2144 		do_animate = False;
2145 		page_x = m->virtual_scr.Vx;
2146 		page_y = m->virtual_scr.Vy;
2147 		r.x = x;
2148 		r.y = y;
2149 		r.width = width;
2150 		r.height = height;
2151 		p.x = page_x - m->virtual_scr.Vx;
2152 		p.y = page_y - m->virtual_scr.Vy;
2153 		p.width = monitor_get_all_widths();
2154 		p.height = monitor_get_all_heights();
2155 		/* move to page first */
2156 		fvwmrect_move_into_rectangle(&r, &p);
2157 		/* then move to screen */
2158 		fvwmrect_move_into_rectangle(&r, &s);
2159 		FinalX = r.x;
2160 		FinalY = r.y;
2161 
2162 		fw->Desk = m->virtual_scr.CurrentDesk;
2163 		fw->m = m;
2164 	}
2165 	else
2166 	{
2167 		FinalX = x;
2168 		FinalY = y;
2169 		n = GetMoveArguments(fw,
2170 			&action, width, height, &FinalX, &FinalY, &fWarp,
2171 			&fPointer, True);
2172 
2173 		if (n != 2 || fPointer)
2174 		{
2175 			InteractiveMove(&w, exc, &FinalX, &FinalY, fPointer);
2176 		}
2177 		else if (IS_ICONIFIED(fw))
2178 		{
2179 			SET_ICON_MOVED(fw, 1);
2180 		}
2181 	}
2182 
2183 	if (w == FW_W_FRAME(fw))
2184 	{
2185 		dx = FinalX - fw->g.frame.x;
2186 		dy = FinalY - fw->g.frame.y;
2187 		if (do_animate)
2188 		{
2189 			AnimatedMoveFvwmWindow(
2190 				fw, w, -1, -1, FinalX, FinalY, fWarp, -1,
2191 				NULL);
2192 		}
2193 		frame_setup_window(
2194 			fw, FinalX, FinalY, fw->g.frame.width,
2195 			fw->g.frame.height, True);
2196 		if (fWarp & !do_animate)
2197 		{
2198 			char *cmd = "WarpToWindow 50 50";
2199 			execute_function_override_window(NULL, exc, cmd, 0, fw);
2200 		}
2201 		if (IS_MAXIMIZED(fw))
2202 		{
2203 			fw->g.max.x += dx;
2204 			fw->g.max.y += dy;
2205 		}
2206 		else
2207 		{
2208 			fw->g.normal.x += dx;
2209 			fw->g.normal.y += dy;
2210 		}
2211 		update_absolute_geometry(fw);
2212 		maximize_adjust_offset(fw);
2213 		XFlush(dpy);
2214 	}
2215 	else /* icon window */
2216 	{
2217 		__move_icon(fw, FinalX, FinalY, x, y, do_animate, fWarp);
2218 		XFlush(dpy);
2219 	}
2220 	focus_grab_buttons_on_layer(fw->layer);
2221 
2222 	return;
2223 }
2224 
CMD_Move(F_CMD_ARGS)2225 void CMD_Move(F_CMD_ARGS)
2226 {
2227 	__move_window(F_PASS_ARGS, False, MOVE_NORMAL);
2228 	UPDATE_FVWM_SCREEN(exc->w.fw);
2229 }
2230 
CMD_AnimatedMove(F_CMD_ARGS)2231 void CMD_AnimatedMove(F_CMD_ARGS)
2232 {
2233 	__move_window(F_PASS_ARGS, True, MOVE_NORMAL);
2234 	UPDATE_FVWM_SCREEN(exc->w.fw);
2235 }
2236 
CMD_MoveToPage(F_CMD_ARGS)2237 void CMD_MoveToPage(F_CMD_ARGS)
2238 {
2239 	__move_window(F_PASS_ARGS, False, MOVE_PAGE);
2240 	UPDATE_FVWM_SCREEN(exc->w.fw);
2241 }
2242 
CMD_MoveToScreen(F_CMD_ARGS)2243 void CMD_MoveToScreen(F_CMD_ARGS)
2244 {
2245 	__move_window(F_PASS_ARGS, False, MOVE_SCREEN);
2246 	UPDATE_FVWM_SCREEN(exc->w.fw);
2247 }
2248 
update_pos(int * dest_score,int * dest_pos,int current_pos,int requested_pos)2249 static void update_pos(
2250 	int *dest_score, int *dest_pos, int current_pos, int requested_pos)
2251 {
2252 	int requested_score;
2253 
2254 	requested_score = abs(current_pos - requested_pos);
2255 	if (requested_score < *dest_score)
2256 	{
2257 		*dest_pos = requested_pos;
2258 		*dest_score = requested_score;
2259 	}
2260 
2261 	return;
2262 }
2263 
2264 /* This function does the SnapAttraction stuff. It takes x and y coordinates
2265  * (*px and *py) and returns the snapped values. */
DoSnapAttract(FvwmWindow * fw,int Width,int Height,int * px,int * py)2266 static void DoSnapAttract(
2267 	FvwmWindow *fw, int Width, int Height, int *px, int *py)
2268 {
2269 	int nyt,nxl;
2270 	rectangle self;
2271 	int score_x;
2272 	int score_y;
2273 	int scr_w, scr_h;
2274 	int mon_w, mon_h, mon_x, mon_y;
2275 	struct monitor *m;
2276 
2277 	nxl = -99999;
2278 	nyt = -99999;
2279 	score_x = 99999999;
2280 	score_y = 99999999;
2281 	self.x = *px;
2282 	self.y = *py;
2283 	self.width = Width;
2284 	self.height = Height;
2285 	{
2286 		rectangle g;
2287 		Bool rc;
2288 
2289 		rc = get_visible_icon_title_geometry(fw, &g);
2290 		if (rc == True)
2291 		{
2292 			self.height += g.height;
2293 		}
2294 	}
2295 
2296 	scr_w = monitor_get_all_widths();
2297 	scr_h = monitor_get_all_heights();
2298 
2299 	/*
2300 	 * Snap grid handling
2301 	 */
2302 	if (fw->snap_grid_x > 1 && nxl == -99999)
2303 	{
2304 		if (*px != *px / fw->snap_grid_x * fw->snap_grid_x)
2305 		{
2306 			nxl = (*px + ((*px >= 0) ?
2307 				      fw->snap_grid_x : -fw->snap_grid_x) /
2308 			       2) / fw->snap_grid_x * fw->snap_grid_x;
2309 		}
2310 	}
2311 	if (fw->snap_grid_y > 1 && nyt == -99999)
2312 	{
2313 		if (*py != *py / fw->snap_grid_y * fw->snap_grid_y)
2314 		{
2315 			nyt = (*py + ((*py >= 0) ?
2316 				      fw->snap_grid_y : -fw->snap_grid_y) /
2317 			       2) / fw->snap_grid_y * fw->snap_grid_y;
2318 		}
2319 	}
2320 
2321 	/*
2322 	 * snap attraction
2323 	 */
2324 	/* snap to other windows or icons*/
2325 	if (fw->snap_attraction.proximity > 0 &&
2326 		(fw->snap_attraction.mode & (SNAP_ICONS | SNAP_WINDOWS | SNAP_SAME)))
2327 	{
2328 		FvwmWindow *tmp;
2329 		int maskout = (SNAP_SCREEN | SNAP_SCREEN_WINDOWS |
2330 				SNAP_SCREEN_ICONS | SNAP_SCREEN_ALL);
2331 
2332 		for (tmp = Scr.FvwmRoot.next; tmp; tmp = tmp->next)
2333 		{
2334 			rectangle other;
2335 
2336 			if (fw->Desk != tmp->Desk || fw == tmp)
2337 			{
2338 				continue;
2339 			}
2340 			/* check snapping type */
2341 			switch (fw->snap_attraction.mode & ~(maskout))
2342 			{
2343 			case SNAP_WINDOWS:  /* we only snap windows */
2344 				if (IS_ICONIFIED(tmp) || IS_ICONIFIED(fw))
2345 				{
2346 					continue;
2347 				}
2348 				break;
2349 			case SNAP_ICONS:  /* we only snap icons */
2350 				if (!IS_ICONIFIED(tmp) || !IS_ICONIFIED(fw))
2351 				{
2352 					continue;
2353 				}
2354 				break;
2355 			case SNAP_SAME:  /* we don't snap unequal */
2356 				if (IS_ICONIFIED(tmp) != IS_ICONIFIED(fw))
2357 				{
2358 					continue;
2359 				}
2360 				break;
2361 			default:  /* All */
2362 				/* NOOP */
2363 				break;
2364 			}
2365 			/* get other window dimensions */
2366 			get_visible_window_or_icon_geometry(tmp, &other);
2367 			if (other.x >= scr_w ||
2368 			    other.x + other.width <= 0 ||
2369 			    other.y >= scr_h ||
2370 			    other.y + other.height <= 0)
2371 			{
2372 				/* do not snap to windows that are not currently
2373 				 * visible */
2374 				continue;
2375 			}
2376 			/* snap horizontally */
2377 			if (other.y + other.height > *py &&
2378 			    other.y < *py + self.height)
2379 			{
2380 				if (*px + self.width >= other.x -
2381 				    fw->snap_attraction.proximity &&
2382 				    *px + self.width <= other.x +
2383 				    fw->snap_attraction.proximity)
2384 				{
2385 					update_pos(&score_x, &nxl, *px,
2386 						other.x - self.width);
2387 				}
2388 				if (*px <= other.x + other.width +
2389 				    fw->snap_attraction.proximity &&
2390 				    *px >= other.x + other.width -
2391 				    fw->snap_attraction.proximity)
2392 				{
2393 					update_pos(&score_x, &nxl, *px,
2394 						other.x + other.width);
2395 				}
2396 			}
2397 			/* snap vertically */
2398 			if (other.x + other.width > *px &&
2399 			    other.x < *px + self.width)
2400 			{
2401 				if (*py + self.height >= other.y -
2402 				    fw->snap_attraction.proximity &&
2403 				    *py + self.height <= other.y +
2404 				    fw->snap_attraction.proximity)
2405 				{
2406 					update_pos(&score_y, &nyt, *py,
2407 						other.y - self.height);
2408 				}
2409 				if (*py <= other.y + other.height +
2410 				    fw->snap_attraction.proximity &&
2411 				    *py >= other.y + other.height -
2412 				    fw->snap_attraction.proximity)
2413 				{
2414 					update_pos(&score_y, &nyt, *py,
2415 						other.y + other.height);
2416 				}
2417 			}
2418 		} /* for */
2419 	} /* snap to other windows */
2420 
2421 	/* snap to monitor edges */
2422 	if (fw->snap_attraction.proximity > 0 && (
2423 			( fw->snap_attraction.mode & SNAP_SCREEN && (
2424 				fw->snap_attraction.mode & SNAP_SAME ||
2425 			( IS_ICONIFIED(fw) &&
2426 				fw->snap_attraction.mode & SNAP_ICONS ) ||
2427 			( !IS_ICONIFIED(fw) &&
2428 				fw->snap_attraction.mode & SNAP_WINDOWS ))) ||
2429 			( !IS_ICONIFIED(fw) &&
2430 				fw->snap_attraction.mode & SNAP_SCREEN_WINDOWS ) ||
2431 			( IS_ICONIFIED(fw) &&
2432 				fw->snap_attraction.mode & SNAP_SCREEN_ICONS ) ||
2433 			fw->snap_attraction.mode & SNAP_SCREEN_ALL ))
2434 	{
2435 		/* Loop over each monitor */
2436 		TAILQ_FOREACH(m, &monitor_q, entry)
2437 		{
2438 			mon_x = m->si->x;
2439 			mon_y = m->si->y;
2440 			mon_w = m->si->w;
2441 			mon_h = m->si->h;
2442 
2443 			/* vertically */
2444 			if (!(*px + self.width < mon_x || *px > mon_x + mon_w))
2445 			{
2446 				if (*py + self.height >=
2447 					mon_h + mon_y - fw->snap_attraction.proximity &&
2448 					*py + self.height <=
2449 				 	mon_h + mon_y + fw->snap_attraction.proximity)
2450 				{
2451 					update_pos(&score_y, &nyt, *py,
2452 						mon_h + mon_y - self.height);
2453 				}
2454 				if (*py <= mon_y + fw->snap_attraction.proximity &&
2455 					*py > mon_y - fw->snap_attraction.proximity)
2456 				{
2457 					update_pos(&score_y, &nyt, *py, mon_y);
2458 				}
2459 			}
2460 			/* horizontally */
2461 			if (!(*py + self.height < mon_y || *py > mon_y + mon_h))
2462 			{
2463 				if (*px + self.width >=
2464 					mon_w + mon_x - fw->snap_attraction.proximity &&
2465 					*px + self.width <=
2466 					mon_w + mon_x + fw->snap_attraction.proximity)
2467 				{
2468 					update_pos(&score_x, &nxl, *px,
2469 						mon_w + mon_x - self.width);
2470 				}
2471 				if ((*px <= mon_x + fw->snap_attraction.proximity) &&
2472 					(*px >= mon_x - fw->snap_attraction.proximity))
2473 				{
2474 					update_pos(&score_x, &nxl, *px, mon_x);
2475 				}
2476 			}
2477 		} /* monitor loop */
2478 	} /* snap to monitor edges */
2479 
2480 	if (nxl != -99999)
2481 	{
2482 		*px = nxl;
2483 	}
2484 	if (nyt != -99999)
2485 	{
2486 		*py = nyt;
2487 	}
2488 
2489 	/*
2490 	 * Resist moving windows beyond the edge of the screen
2491 	 */
2492 	if (fw->edge_resistance_move > 0)
2493 	{
2494 		/* snap to right edge */
2495 		if (
2496 			*px + Width > scr_w &&
2497 			*px + Width < scr_w +
2498 			fw->edge_resistance_move)
2499 		{
2500 			*px = scr_w - Width;
2501 		}
2502 		/* snap to left edge */
2503 		else if ((*px < 0) && (*px > -fw->edge_resistance_move))
2504 		{
2505 			*px = 0;
2506 		}
2507 		/* snap to bottom edge */
2508 		if (
2509 			*py + Height > scr_h &&
2510 			*py + Height < scr_h +
2511 			fw->edge_resistance_move)
2512 		{
2513 			*py = scr_h - Height;
2514 		}
2515 		/* snap to top edge */
2516 		else if (*py < 0 && *py > -fw->edge_resistance_move)
2517 		{
2518 			*py = 0;
2519 		}
2520 	}
2521 	/* Resist moving windows between xineramascreens */
2522 	if (fw->edge_resistance_xinerama_move)
2523 	{
2524 		int scr_x0, scr_y0;
2525 		int scr_x1, scr_y1;
2526 		Bool do_recalc_rectangle = False;
2527 
2528 		FScreenGetResistanceRect(
2529 			*px, *py, Width, Height, &scr_x0, &scr_y0, &scr_x1,
2530 			&scr_y1);
2531 
2532 		/* snap to right edge */
2533 		if (scr_x1 < scr_w &&
2534 		    *px + Width >= scr_x1 && *px + Width <
2535 		    scr_x1 + fw->edge_resistance_xinerama_move)
2536 		{
2537 			*px = scr_x1 - Width;
2538 			do_recalc_rectangle = True;
2539 		}
2540 		/* snap to left edge */
2541 		else if (
2542 			scr_x0 > 0 &&
2543 			*px <= scr_x0 && scr_x0 - *px <
2544 			fw->edge_resistance_xinerama_move)
2545 		{
2546 			*px = scr_x0;
2547 			do_recalc_rectangle = True;
2548 		}
2549 		if (do_recalc_rectangle)
2550 		{
2551 			/* Snapping in X direction can move the window off a
2552 			 * screen.  Thus, it may no longer be necessary to snap
2553 			 * in Y direction. */
2554 			FScreenGetResistanceRect(
2555 				*px, *py, Width, Height, &scr_x0, &scr_y0,
2556 				&scr_x1, &scr_y1);
2557 		}
2558 		/* snap to bottom edge */
2559 		if (scr_y1 < scr_h &&
2560 		    *py + Height >= scr_y1 && *py + Height <
2561 		    scr_y1 + fw->edge_resistance_xinerama_move)
2562 		{
2563 			*py = scr_y1 - Height;
2564 		}
2565 		/* snap to top edge */
2566 		else if (
2567 			scr_y0 > 0 &&
2568 			*py <= scr_y0 && scr_y0 - *py <
2569 			fw->edge_resistance_xinerama_move)
2570 		{
2571 			*py = scr_y0;
2572 		}
2573 	}
2574 
2575 	return;
2576 }
2577 
2578 /*
2579  *
2580  * Move the rubberband around, return with the new window location
2581  *
2582  * Returns True if the window has to be resized after the move.
2583  *
2584  */
__move_loop(const exec_context_t * exc,int XOffset,int YOffset,int Width,int Height,int * FinalX,int * FinalY,Bool do_move_opaque,int cursor)2585 Bool __move_loop(
2586 	const exec_context_t *exc, int XOffset, int YOffset, int Width,
2587 	int Height, int *FinalX, int *FinalY, Bool do_move_opaque, int cursor)
2588 {
2589 	extern Window bad_window;
2590 	Bool is_finished = False;
2591 	Bool is_aborted = False;
2592 	int xl,xl2,yt,yt2,delta_x,delta_y,paged;
2593 	unsigned int button_mask = 0;
2594 	FvwmWindow fw_copy;
2595 	int dx;
2596 	int dy;
2597 	int vx;
2598 	int vy;
2599 	int xl_orig = 0;
2600 	int yt_orig = 0;
2601 	int cnx = 0;
2602 	int cny = 0;
2603 	int x_virtual_offset = 0;
2604 	int y_virtual_offset = 0;
2605 	Bool sent_cn = False;
2606 	Bool do_resize_too = False;
2607 	int x_bak;
2608 	int y_bak;
2609 	Window move_w = None;
2610 	int orig_icon_x = 0;
2611 	int orig_icon_y = 0;
2612 	Bool do_snap = True;
2613 	Bool was_snapped = False;
2614 	/* if Alt is initially pressed don't enable no-snap until Alt is
2615 	 * released */
2616 	Bool nosnap_enabled = False;
2617 	/* Must not set placed by button if the event is a modified KeyEvent */
2618 	Bool is_fake_event;
2619 	FvwmWindow *fw = exc->w.fw;
2620 	unsigned int draw_parts = PART_NONE;
2621 	XEvent e;
2622 	struct monitor	*m = NULL;
2623 
2624 	if (fw->m == NULL)
2625 		UPDATE_FVWM_SCREEN(fw);
2626 	m = fw->m;
2627 	vx = m->virtual_scr.Vx;
2628 	vy = m->virtual_scr.Vy;
2629 	dx = m->virtual_scr.EdgeScrollX ? m->virtual_scr.EdgeScrollX : monitor_get_all_widths();
2630 	dy = m->virtual_scr.EdgeScrollY ? m->virtual_scr.EdgeScrollY : monitor_get_all_heights();
2631 
2632 	if (!GrabEm(cursor, GRAB_NORMAL))
2633 	{
2634 		XBell(dpy, 0);
2635 		return False;
2636 	}
2637 	if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
2638 	{
2639 		do_move_opaque = False;
2640 	}
2641 	bad_window = None;
2642 	if (IS_ICONIFIED(fw))
2643 	{
2644 		if (FW_W_ICON_PIXMAP(fw) != None)
2645 		{
2646 			move_w = FW_W_ICON_PIXMAP(fw);
2647 		}
2648 		else if (FW_W_ICON_TITLE(fw) != None)
2649 		{
2650 			move_w = FW_W_ICON_TITLE(fw);
2651 		}
2652 	}
2653 	else
2654 	{
2655 		move_w = FW_W_FRAME(fw);
2656 	}
2657 	if (
2658 		!XGetGeometry(
2659 			dpy, move_w, &JunkRoot, &x_bak, &y_bak,
2660 			(unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
2661 			(unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
2662 	{
2663 		/* This is allright here since the window may not be mapped
2664 		 * yet. */
2665 	}
2666 
2667 	if (IS_ICONIFIED(fw))
2668 	{
2669 		rectangle g;
2670 
2671 		get_visible_icon_geometry(fw, &g);
2672 		orig_icon_x = g.x;
2673 		orig_icon_y = g.y;
2674 	}
2675 
2676 	/* make a copy of the fw structure for sending to the pager */
2677 	memcpy(&fw_copy, fw, sizeof(FvwmWindow));
2678 	/* prevent flicker when paging */
2679 	SET_WINDOW_BEING_MOVED_OPAQUE(fw, do_move_opaque);
2680 
2681 	if (FQueryPointer(
2682 		    dpy, Scr.Root, &JunkRoot, &JunkChild, &xl, &yt,
2683 		    &JunkX, &JunkY, &button_mask) == False)
2684 	{
2685 		/* pointer is on a different screen */
2686 		xl = 0;
2687 		yt = 0;
2688 	}
2689 	else
2690 	{
2691 		xl += XOffset;
2692 		yt += YOffset;
2693 	}
2694 	button_mask &= DEFAULT_ALL_BUTTONS_MASK;
2695 	xl_orig = xl;
2696 	yt_orig = yt;
2697 
2698 	/* draw initial outline */
2699 	if (!IS_ICONIFIED(fw) &&
2700 	    ((!do_move_opaque && !Scr.gs.do_emulate_mwm) || !IS_MAPPED(fw)))
2701 	{
2702 		draw_move_resize_grid(xl, yt, Width - 1, Height - 1);
2703 	}
2704 
2705 	if (move_w == FW_W_FRAME(fw) && do_move_opaque)
2706 	{
2707 		draw_parts = border_get_transparent_decorations_part(fw);
2708 	}
2709 	DisplayPosition(fw, exc->x.elast, xl, yt, True);
2710 
2711 	memset(&e, 0, sizeof(e));
2712 
2713 	/* Unset the placed by button mask.
2714 	 * If the move is canceled this will remain as zero.
2715 	 */
2716 	fw->placed_by_button = 0;
2717 	while (!is_finished && bad_window != FW_W(fw))
2718 	{
2719 		int rc = 0;
2720 		int old_xl;
2721 		int old_yt;
2722 
2723 		old_xl = xl;
2724 		old_yt = yt;
2725 		/* wait until there is an interesting event */
2726 		while (rc != -1 &&
2727 		       (!FPending(dpy) ||
2728 			!FCheckMaskEvent(
2729 				dpy, ButtonPressMask | ButtonReleaseMask |
2730 				KeyPressMask | PointerMotionMask |
2731 				ButtonMotionMask | ExposureMask, &e)))
2732 		{
2733 			XEvent le;
2734 			int x;
2735 			int y;
2736 
2737 			UPDATE_FVWM_SCREEN(fw);
2738 
2739 			fev_get_last_event(&le);
2740 
2741 			xl -= XOffset;
2742 			yt -= YOffset;
2743 
2744 			rc = HandlePaging(
2745 				&le, dx, dy, &xl, &yt, &delta_x, &delta_y,
2746 				False, False, True, fw->edge_delay_ms_move);
2747 
2748 			/* Fake an event to force window reposition */
2749 			if (delta_x)
2750 			{
2751 				x_virtual_offset = 0;
2752 			}
2753 			xl += XOffset;
2754 			if (delta_y)
2755 			{
2756 				y_virtual_offset = 0;
2757 			}
2758 			yt += YOffset;
2759 			if (do_snap)
2760 			{
2761 				DoSnapAttract(fw, Width, Height, &xl, &yt);
2762 				was_snapped = True;
2763 			}
2764 			fev_make_null_event(&e, dpy);
2765 			e.type = MotionNotify;
2766 			e.xmotion.time = fev_get_evtime();
2767 			if (FQueryPointer(
2768 				    dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
2769 				    &y, &JunkX, &JunkY, &JunkMask) == True)
2770 			{
2771 				e.xmotion.x_root = x;
2772 				e.xmotion.y_root = y;
2773 			}
2774 			else
2775 			{
2776 				/* pointer is on a different screen */
2777 				e.xmotion.x_root = 0;
2778 				e.xmotion.y_root = 0;
2779 			}
2780 			e.xmotion.state = JunkMask;
2781 			e.xmotion.same_screen = True;
2782 			break;
2783 		}
2784 		if (rc == -1)
2785 		{
2786 			/* block until an event arrives */
2787 			/* dv (2004-07-01): With XFree 4.1.0.1, some Mouse
2788 			 * events are not reported to fvwm when the pointer
2789 			 * moves very fast and suddenly stops in the corner of
2790 			 * the screen.  Handle EnterNotify/LeaveNotify events
2791 			 * too to get an idea where the pointer might be. */
2792 			FMaskEvent(
2793 				dpy, ButtonPressMask | ButtonReleaseMask |
2794 				KeyPressMask | PointerMotionMask |
2795 				ButtonMotionMask | ExposureMask |
2796 				EnterWindowMask | LeaveWindowMask, &e);
2797 		}
2798 
2799 		/* discard extra events before a logical release */
2800 		if (e.type == MotionNotify ||
2801 		    e.type == EnterNotify || e.type == LeaveNotify)
2802 		{
2803 			while (FPending(dpy) > 0 &&
2804 			       FCheckMaskEvent(
2805 				       dpy, ButtonMotionMask |
2806 				       PointerMotionMask | ButtonPressMask |
2807 				       ButtonRelease | KeyPressMask |
2808 				       EnterWindowMask | LeaveWindowMask, &e))
2809 			{
2810 				if (e.type == ButtonPress ||
2811 				    e.type == ButtonRelease ||
2812 				    e.type == KeyPress)
2813 				{
2814 					break;
2815 				}
2816 			}
2817 		}
2818 		if (e.type == EnterNotify || e.type == LeaveNotify)
2819 		{
2820 			XEvent e2;
2821 			int x;
2822 			int y;
2823 
2824 			/* Query the pointer to catch the latest information.
2825 			 * This *is* necessary. */
2826 			if (FQueryPointer(
2827 				    dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
2828 				    &y, &JunkX, &JunkY, &JunkMask) == True)
2829 			{
2830 				fev_make_null_event(&e2, dpy);
2831 				e2.type = MotionNotify;
2832 				e2.xmotion.time = fev_get_evtime();
2833 				e2.xmotion.x_root = x;
2834 				e2.xmotion.y_root = y;
2835 				e2.xmotion.state = JunkMask;
2836 				e2.xmotion.same_screen = True;
2837 				e = e2;
2838 				fev_fake_event(&e);
2839 			}
2840 			else
2841 			{
2842 				/* pointer is on a different screen,
2843 				 * ignore event */
2844 			}
2845 		}
2846 		is_fake_event = False;
2847 		/* Handle a limited number of key press events to allow
2848 		 * mouseless operation */
2849 		if (e.type == KeyPress)
2850 		{
2851 			Keyboard_shortcuts(
2852 				&e, fw, &x_virtual_offset,
2853 				&y_virtual_offset, ButtonRelease);
2854 
2855 			is_fake_event = (e.type != KeyPress);
2856 		}
2857 		switch (e.type)
2858 		{
2859 		case KeyPress:
2860 			if (!(e.xkey.state & Mod1Mask))
2861 			{
2862 				nosnap_enabled = True;
2863 			}
2864 			do_snap = nosnap_enabled &&
2865 				(e.xkey.state & Mod1Mask) ? False : True;
2866 
2867 			/* simple code to bag out of move - CKH */
2868 			if (XLookupKeysym(&(e.xkey), 0) == XK_Escape)
2869 			{
2870 				if (!do_move_opaque)
2871 				{
2872 					switch_move_resize_grid(False);
2873 				}
2874 				if (!IS_ICONIFIED(fw))
2875 				{
2876 					if (do_move_opaque)
2877 					{
2878 						*FinalX = fw->g.frame.x;
2879 						*FinalY = fw->g.frame.y;
2880 					}
2881 				}
2882 				else
2883 				{
2884 					*FinalX = orig_icon_x;
2885 					*FinalY = orig_icon_y;
2886 				}
2887 				is_aborted = True;
2888 				is_finished = True;
2889 			}
2890 			break;
2891 		case ButtonPress:
2892 			if (e.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
2893 			    ((Button1Mask << (e.xbutton.button - 1)) &
2894 			     button_mask))
2895 			{
2896 				/* No new button was pressed, just a delayed
2897 				 * event */
2898 				break;
2899 			}
2900 			if (!IS_MAPPED(fw) &&
2901 			    ((e.xbutton.button == 2 && !Scr.gs.do_emulate_mwm)
2902 			     ||
2903 			    (e.xbutton.button == 1 && Scr.gs.do_emulate_mwm &&
2904 			     (e.xbutton.state & ShiftMask))))
2905 			{
2906 				do_resize_too = True;
2907 				/* Fallthrough to button-release */
2908 			}
2909 			else if (!button_mask && e.xbutton.button <=
2910 				 NUMBER_OF_EXTENDED_MOUSE_BUTTONS &&
2911 				 e.xbutton.button > 0 &&
2912 				 (move_interactive_finish_button_mask &
2913 				  (1<<(e.xbutton.button-1))))
2914 			{
2915 				do_resize_too = False;
2916 				break;
2917 			}
2918 			else if (button_mask &&  e.xbutton.button <=
2919 				 NUMBER_OF_EXTENDED_MOUSE_BUTTONS &&
2920 				 e.xbutton.button > 0 &&
2921 				 (move_drag_finish_button_mask &
2922 				  (1<<(e.xbutton.button-1))))
2923 			{
2924 				do_resize_too = False;
2925 				/* Fallthrough to button-release */
2926 			}
2927 			else
2928 			{
2929 				/* Abort the move if
2930 				 *  - the move started with a pressed button
2931 				 *    and another button was pressed during the
2932 				 *    operation
2933 				 *  - Any button not in the
2934 				 *    move_finish_button_mask is pressed
2935 				 */
2936 			  /*	if (button_mask) */
2937 			  	/* - button_mask will always be set here.
2938 				 *   only add an if if we want to be able to
2939 				 *   place windows dragged by other means
2940 				 *   than releasing the initial button.
2941 				 */
2942 				{
2943 					if (!do_move_opaque)
2944 					{
2945 						switch_move_resize_grid(False);
2946 					}
2947 					if (!IS_ICONIFIED(fw))
2948 					{
2949 						*FinalX = fw->g.frame.x;
2950 						*FinalY = fw->g.frame.y;
2951 					}
2952 					else
2953 					{
2954 						*FinalX = orig_icon_x;
2955 						*FinalY = orig_icon_y;
2956 					}
2957 					is_aborted = True;
2958 					is_finished = True;
2959 				}
2960 				break;
2961 			}
2962 		case ButtonRelease:
2963 			if (!is_fake_event)
2964 			{
2965 				fw->placed_by_button = e.xbutton.button;
2966 			}
2967 			if (!do_move_opaque)
2968 			{
2969 				switch_move_resize_grid(False);
2970 			}
2971 			xl2 = e.xbutton.x_root + XOffset + x_virtual_offset;
2972 			yt2 = e.xbutton.y_root + YOffset + y_virtual_offset;
2973 			/* ignore the position of the button release if it was
2974 			 * on a different page. */
2975 			if (!(((xl <  0 && xl2 >= 0) ||
2976 			       (xl >= 0 && xl2 <  0) ||
2977 			       (yt <  0 && yt2 >= 0) ||
2978 			       (yt >= 0 && yt2 <  0)) &&
2979 			      (abs(xl - xl2) > monitor_get_all_widths() / 2 ||
2980 			       abs(yt - yt2) > monitor_get_all_heights() / 2)))
2981 			{
2982 				xl = xl2;
2983 				yt = yt2;
2984 			}
2985 			if (xl != xl_orig || yt != yt_orig || vx != m->virtual_scr.Vx ||
2986 			    vy != m->virtual_scr.Vy || was_snapped)
2987 			{
2988 				/* only snap if the window actually moved! */
2989 				if (do_snap)
2990 				{
2991 					DoSnapAttract(
2992 						fw, Width, Height, &xl, &yt);
2993 					was_snapped = True;
2994 				}
2995 			}
2996 
2997 			*FinalX = xl;
2998 			*FinalY = yt;
2999 
3000 			is_finished = True;
3001 			break;
3002 
3003 		case MotionNotify:
3004 			if (e.xmotion.same_screen == False)
3005 			{
3006 				continue;
3007 			}
3008 			if (!(e.xmotion.state & Mod1Mask))
3009 			{
3010 				nosnap_enabled = True;
3011 			}
3012 			do_snap = nosnap_enabled &&
3013 				(e.xmotion.state & Mod1Mask) ? False : True;
3014 			xl = e.xmotion.x_root;
3015 			yt = e.xmotion.y_root;
3016 			if (xl > 0 && xl < monitor_get_all_widths() - 1)
3017 			{
3018 				/* pointer was moved away from the left/right
3019 				 * border with the mouse, reset the virtual x
3020 				 * offset */
3021 				x_virtual_offset = 0;
3022 			}
3023 			if (yt > 0 && yt < monitor_get_all_heights() - 1)
3024 			{
3025 				/* pointer was moved away from the top/bottom
3026 				 * border with the mouse, reset the virtual y
3027 				 * offset */
3028 				y_virtual_offset = 0;
3029 			}
3030 			xl += XOffset + x_virtual_offset;
3031 			yt += YOffset + y_virtual_offset;
3032 
3033 			if (do_snap)
3034 			{
3035 				DoSnapAttract(fw, Width, Height, &xl, &yt);
3036 				was_snapped = True;
3037 			}
3038 
3039 			/* check Paging request once and only once after
3040 			 * outline redrawn redraw after paging if needed
3041 			 * - mab */
3042 			for (paged = 0; paged <= 1; paged++)
3043 			{
3044 				if (!do_move_opaque)
3045 				{
3046 					draw_move_resize_grid(
3047 						xl, yt, Width - 1, Height - 1);
3048 				}
3049 				else
3050 				{
3051 					if (IS_ICONIFIED(fw))
3052 					{
3053 						set_icon_position(fw, xl, yt);
3054 						move_icon_to_position(fw);
3055 						broadcast_icon_geometry(
3056 							fw, False);
3057 					}
3058 					else
3059 					{
3060 						XMoveWindow(
3061 							dpy, FW_W_FRAME(fw),
3062 							xl, yt);
3063 					}
3064 				}
3065 				DisplayPosition(fw, &e, xl, yt, False);
3066 
3067 				/* prevent window from lagging behind mouse
3068 				 * when paging - mab */
3069 				if (paged == 0)
3070 				{
3071 					XEvent le;
3072 
3073 					xl = e.xmotion.x_root;
3074 					yt = e.xmotion.y_root;
3075 					fev_get_last_event(&le);
3076 					HandlePaging(
3077 						&le, dx, dy, &xl, &yt,
3078 						&delta_x, &delta_y, False,
3079 						False, False,
3080 						fw->edge_delay_ms_move);
3081 					if (delta_x)
3082 					{
3083 						x_virtual_offset = 0;
3084 					}
3085 					xl += XOffset;
3086 					if (delta_y)
3087 					{
3088 						y_virtual_offset = 0;
3089 					}
3090 					yt += YOffset;
3091 					if (do_snap)
3092 					{
3093 						DoSnapAttract(
3094 							fw, Width, Height,
3095 							&xl, &yt);
3096 						was_snapped = True;
3097 					}
3098 					if (!delta_x && !delta_y)
3099 					{
3100 						/* break from while
3101 						 * (paged <= 1) */
3102 						break;
3103 					}
3104 				}
3105 			}
3106 			/* dv (13-Jan-2014): Without this XFlush the modules
3107 			 * (and probably other windows too) sometimes get their
3108 			 * expose only after the next motion step.  */
3109 			XFlush(dpy);
3110 			break;
3111 
3112 		case Expose:
3113 			if (!do_move_opaque)
3114 			{
3115 				/* must undraw the rubber band in case the
3116 				 * event causes some drawing */
3117 				switch_move_resize_grid(False);
3118 			}
3119 			dispatch_event(&e);
3120 			if (!do_move_opaque)
3121 			{
3122 				draw_move_resize_grid(
3123 					xl, yt, Width - 1, Height - 1);
3124 			}
3125 			break;
3126 
3127 		default:
3128 			/* cannot happen */
3129 			break;
3130 		} /* switch */
3131 		xl += x_virtual_offset;
3132 		yt += y_virtual_offset;
3133 		if (do_move_opaque && !IS_ICONIFIED(fw) &&
3134 		    !IS_SHADED(fw) && !Scr.bo.do_disable_configure_notify)
3135 		{
3136 			/* send configure notify event for windows that care
3137 			 * about their location; don't send anything if
3138 			 * position didn't change */
3139 			if (!sent_cn || cnx != xl || cny != yt)
3140 			{
3141 				cnx = xl;
3142 				cny = yt;
3143 				sent_cn = True;
3144 				SendConfigureNotify(
3145 					fw, xl, yt, Width, Height, 0,
3146 					False);
3147 #ifdef FVWM_DEBUG_MSGS
3148 				fvwm_debug(__func__,
3149 					   "Sent ConfigureNotify (w %d, h %d)",
3150 					   Width, Height);
3151 #endif
3152 			}
3153 		}
3154 		if (do_move_opaque)
3155 		{
3156 			if (!IS_ICONIFIED(fw))
3157 			{
3158 				fw_copy.g.frame.x = xl;
3159 				fw_copy.g.frame.y = yt;
3160 			}
3161 			if (xl != old_xl || yt != old_yt)
3162 			{
3163 				/* only do this with opaque moves, (i.e. the
3164 				 * server is not grabbed) */
3165 				if (draw_parts != PART_NONE)
3166 				{
3167 					border_draw_decorations(
3168 						fw, draw_parts,
3169 						((fw == get_focus_window())) ?
3170 						True : False,
3171 						True, CLEAR_ALL, NULL, NULL);
3172 				}
3173 				if (IS_TEAR_OFF_MENU(fw))
3174 				{
3175 					menu_redraw_transparent_tear_off_menu(
3176 						fw, False);
3177 				}
3178 				BroadcastConfig(M_CONFIGURE_WINDOW, &fw_copy);
3179 				FlushAllMessageQueues();
3180 			}
3181 		}
3182 	} /* while (!is_finished) */
3183 
3184 	if (!Scr.gs.do_hide_position_window)
3185 	{
3186 		XUnmapWindow(dpy,Scr.SizeWindow.win);
3187 	}
3188 	if (is_aborted || bad_window == FW_W(fw))
3189 	{
3190 		if (vx != m->virtual_scr.Vx || vy != m->virtual_scr.Vy)
3191 		{
3192 			MoveViewport(m, vx, vy, False);
3193 		}
3194 		if (is_aborted && do_move_opaque)
3195 		{
3196 			XMoveWindow(dpy, move_w, x_bak, y_bak);
3197 			if (draw_parts != PART_NONE)
3198 			{
3199 				border_draw_decorations(
3200 					fw, draw_parts,
3201 					((fw == get_focus_window())) ?
3202 					True : False,
3203 					True, CLEAR_ALL, NULL, NULL);
3204 			}
3205 			menu_redraw_transparent_tear_off_menu(fw, False);
3206 		}
3207 		if (bad_window == FW_W(fw))
3208 		{
3209 			XUnmapWindow(dpy, move_w);
3210 			border_undraw_decorations(fw);
3211 			XBell(dpy, 0);
3212 		}
3213 	}
3214 	if (!is_aborted && bad_window != FW_W(fw) && IS_ICONIFIED(fw))
3215 	{
3216 		SET_ICON_MOVED(fw, 1);
3217 	}
3218 	UngrabEm(GRAB_NORMAL);
3219 	if (!do_resize_too)
3220 	{
3221 		/* Don't wait for buttons to come up when user is placing a new
3222 		 * window and wants to resize it. */
3223 		WaitForButtonsUp(True);
3224 	}
3225 	SET_WINDOW_BEING_MOVED_OPAQUE(fw, 0);
3226 	bad_window = None;
3227 
3228 	return do_resize_too;
3229 }
3230 
CMD_MoveThreshold(F_CMD_ARGS)3231 void CMD_MoveThreshold(F_CMD_ARGS)
3232 {
3233 	int val = 0;
3234 
3235 	if (GetIntegerArguments(action, NULL, &val, 1) < 1 || val < 0)
3236 	{
3237 		Scr.MoveThreshold = DEFAULT_MOVE_THRESHOLD;
3238 	}
3239 	else
3240 	{
3241 		Scr.MoveThreshold = val;
3242 	}
3243 
3244 	return;
3245 }
3246 
CMD_OpaqueMoveSize(F_CMD_ARGS)3247 void CMD_OpaqueMoveSize(F_CMD_ARGS)
3248 {
3249 	int val;
3250 
3251 	if (GetIntegerArguments(action, NULL, &val, 1) < 1)
3252 	{
3253 		if (strncasecmp(action, "unlimited", 9) == 0)
3254 		{
3255 			Scr.OpaqueSize = -1;
3256 		}
3257 		else
3258 		{
3259 			Scr.OpaqueSize = DEFAULT_OPAQUE_MOVE_SIZE;
3260 		}
3261 	}
3262 	else
3263 	{
3264 		Scr.OpaqueSize = val;
3265 	}
3266 
3267 	return;
3268 }
3269 
set_geom_win_visible_val(char * token,bool val)3270 static void set_geom_win_visible_val(char *token, bool val)
3271 {
3272 	if (token == NULL)
3273 		return;
3274 
3275 	Scr.gs.do_hide_position_window = !val;
3276 	Scr.gs.do_hide_resize_window = !val;
3277 
3278 	if (StrEquals(token, "never"))
3279 	{
3280 		Scr.gs.do_hide_position_window = val;
3281 		Scr.gs.do_hide_resize_window = val;
3282 	}
3283 	else if (StrEquals(token, "move"))
3284 	{
3285 		Scr.gs.do_hide_resize_window = val;
3286 	}
3287 	else if (StrEquals(token, "resize"))
3288 	{
3289 		Scr.gs.do_hide_position_window = val;
3290 	}
3291 }
3292 
set_geom_win_position_val(char * s,int * coord,bool * neg,bool * rel)3293 static bool set_geom_win_position_val(char *s, int *coord, bool *neg, bool *rel)
3294 {
3295 	int val, n;
3296 
3297 	if (sscanf(s, "-%d%n", &val, &n) >= 1)
3298 	{
3299 		*coord = val;
3300 		*neg = true;
3301 	}
3302 	else if (sscanf(s, "+%d%n", &val, &n) >= 1 ||
3303 			sscanf(s, "%d%n", &val, &n) >= 1)
3304 	{
3305 		*coord = val;
3306 		*neg = false;
3307 	}
3308 	else
3309 	{
3310 		/* syntax error */
3311 		return false;
3312 	}
3313 	s += n;
3314 	*rel = true;
3315 	if (*s == 'p')
3316 		*rel = false;
3317 
3318 	return true;
3319 }
3320 
CMD_GeometryWindow(F_CMD_ARGS)3321 void CMD_GeometryWindow(F_CMD_ARGS)
3322 {
3323 	int val;
3324 	char *token = NULL, *s = NULL;
3325 
3326 	while ((token = PeekToken(action, &action)) != NULL) {
3327 		if (StrEquals(token, "hide")) {
3328 			set_geom_win_visible_val(PeekToken(action, &action), false);
3329 		}
3330 		if (StrEquals(token, "show")) {
3331 			set_geom_win_visible_val(PeekToken(action, &action), true);
3332 		}
3333 		if (StrEquals(token, "colorset")) {
3334 			if (GetIntegerArguments(action, &action, &val, 1) != 1)
3335 			{
3336 				val = -1;
3337 			}
3338 			Scr.SizeWindow.cset = val;
3339 		}
3340 		if (StrEquals(token, "position")) {
3341 			Scr.SizeWindow.is_configured = false;
3342 
3343 			/* x-coordinate */
3344 			if ((s = PeekToken(action, &action)) != NULL &&
3345 				!set_geom_win_position_val(s, &Scr.SizeWindow.x,
3346 				&Scr.SizeWindow.xneg, &Scr.SizeWindow.xrel))
3347 			{
3348 				continue;
3349 			}
3350 			/* y-coordinate */
3351 			if ((s = PeekToken(action, &action)) != NULL &&
3352 				!set_geom_win_position_val(s, &Scr.SizeWindow.y,
3353 				&Scr.SizeWindow.yneg, &Scr.SizeWindow.yrel))
3354 			{
3355 				continue;
3356 			}
3357 
3358 			if (s != NULL)
3359 				Scr.SizeWindow.is_configured = true;
3360 		}
3361 		if (StrEquals(token, "screen")) {
3362 			token = PeekToken(action, &action);
3363 			if (token != NULL)
3364 			{
3365 				Scr.SizeWindow.m = monitor_resolve_name(token);
3366 				if (strcasecmp(Scr.SizeWindow.m->si->name, token) != 0) {
3367 					/* Incorrect RandR screen found. */
3368 					Scr.SizeWindow.m = NULL;
3369 				}
3370 			}
3371 		}
3372 	}
3373 }
3374 
CMD_HideGeometryWindow(F_CMD_ARGS)3375 void CMD_HideGeometryWindow(F_CMD_ARGS)
3376 {
3377 	char *cmd;
3378 
3379 	fvwm_debug(__func__, "HideGeometryWindow is deprecated.  "
3380 	    "Converting to use: GeometryWindow hide %s", action);
3381 
3382 	xasprintf(&cmd, "GeometryWindow hide %s", action);
3383 	execute_function_override_window(NULL, NULL, cmd, 0, NULL);
3384 	free(cmd);
3385 }
3386 
CMD_SnapAttraction(F_CMD_ARGS)3387 void CMD_SnapAttraction(F_CMD_ARGS)
3388 {
3389 	char *cmd;
3390 	size_t len;
3391 
3392 	/* TA:  FIXME  xasprintf() */
3393 	len = strlen(action);
3394 	len += 99;
3395 	cmd = fxmalloc(len);
3396 	sprintf(cmd, "Style * SnapAttraction %s", action);
3397 	fvwm_debug(__func__,
3398 		   "The command SnapAttraction is obsolete. Please use the"
3399 		   " following command instead:\n\n%s", cmd);
3400 	execute_function(
3401 		cond_rc, exc, cmd,
3402 		FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
3403 	free(cmd);
3404 
3405 	return;
3406 }
3407 
CMD_SnapGrid(F_CMD_ARGS)3408 void CMD_SnapGrid(F_CMD_ARGS)
3409 {
3410 	char *cmd;
3411 	size_t len;
3412 
3413 	/* TA:  FIXME xasprintf() */
3414 	len = strlen(action);
3415 	len += 99;
3416 	cmd = fxmalloc(len);
3417 	sprintf(cmd, "Style * SnapGrid %s", action);
3418 	fvwm_debug(__func__,
3419 		   "The command SnapGrid is obsolete. Please use the following"
3420 		   " command instead:\n\n%s", cmd);
3421 	execute_function(
3422 		cond_rc, exc, cmd,
3423 		FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
3424 	free(cmd);
3425 
3426 	return;
3427 }
3428 
3429 static Pixmap XorPixmap = None;
3430 
CMD_XorValue(F_CMD_ARGS)3431 void CMD_XorValue(F_CMD_ARGS)
3432 {
3433 	int val;
3434 	XGCValues gcv;
3435 	unsigned long gcm;
3436 
3437 	if (GetIntegerArguments(action, NULL, &val, 1) != 1)
3438 	{
3439 		val = 0;
3440 	}
3441 
3442 	PictureUseDefaultVisual();
3443 	gcm = GCFunction|GCLineWidth|GCForeground|GCFillStyle|GCSubwindowMode;
3444 	gcv.subwindow_mode = IncludeInferiors;
3445 	gcv.function = GXxor;
3446 	gcv.line_width = 1;
3447 	/* use passed in value, or try to calculate appropriate value if 0 */
3448 	/* ctwm method: */
3449 	/*
3450 	  gcv.foreground = (val1)?(val1):((((unsigned long) 1) <<
3451 	  Scr.d_depth) - 1);
3452 	*/
3453 	/* Xlib programming manual suggestion: */
3454 	gcv.foreground = (val)?
3455 		(val):(PictureBlackPixel() ^ PictureWhitePixel());
3456 	gcv.fill_style = FillSolid;
3457 	gcv.subwindow_mode = IncludeInferiors;
3458 
3459 	/* modify XorGC, only create once */
3460 	if (Scr.XorGC)
3461 	{
3462 		XChangeGC(dpy, Scr.XorGC, gcm, &gcv);
3463 	}
3464 	else
3465 	{
3466 		Scr.XorGC = fvwmlib_XCreateGC(dpy, Scr.Root, gcm, &gcv);
3467 	}
3468 
3469 	/* free up XorPixmap if neccesary */
3470 	if (XorPixmap != None) {
3471 		XFreePixmap(dpy, XorPixmap);
3472 		XorPixmap = None;
3473 	}
3474 	PictureUseFvwmVisual();
3475 
3476 	return;
3477 }
3478 
3479 
CMD_XorPixmap(F_CMD_ARGS)3480 void CMD_XorPixmap(F_CMD_ARGS)
3481 {
3482 	char *PixmapName;
3483 	FvwmPicture *xp;
3484 	XGCValues gcv;
3485 	unsigned long gcm;
3486 	FvwmPictureAttributes fpa;
3487 
3488 	action = GetNextToken(action, &PixmapName);
3489 	if (PixmapName == NULL)
3490 	{
3491 		/* return to default value. */
3492 		action = "0";
3493 		CMD_XorValue(F_PASS_ARGS);
3494 		return;
3495 	}
3496 	/* get the picture in the root visual, colorlimit is ignored because the
3497 	 * pixels will be freed */
3498 	fpa.mask = FPAM_NO_COLOR_LIMIT | FPAM_NO_ALPHA;
3499 	PictureUseDefaultVisual();
3500 	xp = PGetFvwmPicture(dpy, Scr.Root, NULL, PixmapName, fpa);
3501 	if (xp == NULL)
3502 	{
3503 		fvwm_debug(__func__, "Can't find pixmap %s", PixmapName);
3504 		free(PixmapName);
3505 		PictureUseFvwmVisual();
3506 		return;
3507 	}
3508 	free(PixmapName);
3509 	/* free up old pixmap */
3510 	if (XorPixmap != None)
3511 	{
3512 		XFreePixmap(dpy, XorPixmap);
3513 	}
3514 
3515 	/* make a copy of the picture pixmap */
3516 	XorPixmap = XCreatePixmap(dpy, Scr.Root, xp->width, xp->height, Pdepth);
3517 	XCopyArea(dpy, xp->picture, XorPixmap, DefaultGC(dpy, Scr.screen), 0, 0,
3518 		  xp->width, xp->height, 0, 0);
3519 	/* destroy picture and free colors */
3520 	PDestroyFvwmPicture(dpy, xp);
3521 	PictureUseFvwmVisual();
3522 
3523 	/* create Graphics context */
3524 	gcm = GCFunction|GCLineWidth|GCTile|GCFillStyle|GCSubwindowMode;
3525 	gcv.subwindow_mode = IncludeInferiors;
3526 	gcv.function = GXxor;
3527 	/* line width of 1 is necessary for Exceed servers */
3528 	gcv.line_width = 1;
3529 	gcv.tile = XorPixmap;
3530 	gcv.fill_style = FillTiled;
3531 	gcv.subwindow_mode = IncludeInferiors;
3532 	/* modify XorGC, only create once */
3533 	if (Scr.XorGC)
3534 	{
3535 		XChangeGC(dpy, Scr.XorGC, gcm, &gcv);
3536 	}
3537 	else
3538 	{
3539 		Scr.XorGC = fvwmlib_XCreateGC(dpy, Scr.Root, gcm, &gcv);
3540 	}
3541 
3542 	return;
3543 }
3544 
3545 
3546 /* ----------------------------- resizing code ----------------------------- */
3547 
3548 /* Given a mouse location within a window context, return the direction the
3549  * window could be resized in, based on the window quadrant.  This is the same
3550  * as the quadrants drawn by the rubber-band, if "ResizeOpaque" has not been
3551  * set.
3552  */
__resize_get_dir_from_resize_quadrant(int x_off,int y_off,int px,int py)3553 static direction_t __resize_get_dir_from_resize_quadrant(
3554 		int x_off, int y_off, int px, int py)
3555 {
3556 	direction_t dir = DIR_NONE;
3557 	int tx;
3558 	int ty;
3559 	int dx;
3560 	int dy;
3561 
3562 
3563 	if (px < 0 || x_off < 0 || py < 0 || y_off < 0)
3564 	{
3565 		return dir;
3566 	}
3567 
3568 	/* Rough quadrants per window.  3x3. */
3569 	tx = (x_off / 3) - 1;
3570 	ty = (y_off / 3) - 1;
3571 
3572 	dx = x_off - px;
3573 	dy = y_off - py;
3574 
3575 	if (px < tx)
3576 	{
3577 		/* Far left of window.  Quadrants of NW, W, SW. */
3578 		if (py < ty)
3579 		{
3580 			/* North-West direction. */
3581 			dir = DIR_NW;
3582 		}
3583 		else if (dy < ty)
3584 		{
3585 			/* South-West direction. */
3586 			dir = DIR_SW;
3587 		}
3588 		else
3589 		{
3590 			/* West direction. */
3591 			dir = DIR_W;
3592 		}
3593 	}
3594 	else if (dx < tx)
3595 	{
3596 		/* Far right of window.  Quadrants NE, E, SE. */
3597 		if (py < ty)
3598 		{
3599 			/* North-East direction. */
3600 			dir = DIR_NE;
3601 		}
3602 		else if (dy < ty)
3603 		{
3604 			/* South-East direction */
3605 			dir = DIR_SE;
3606 		}
3607 		else
3608 		{
3609 			/* East direction. */
3610 			dir = DIR_E;
3611 		}
3612 	}
3613 	else
3614 	{
3615 		if (py < ty)
3616 		{
3617 			/* North direction. */
3618 			dir = DIR_N;
3619 		}
3620 		else if (dy < ty)
3621 		{
3622 			/* South direction. */
3623 			dir = DIR_S;
3624 		}
3625 	}
3626 
3627 	return dir;
3628 }
3629 
__resize_get_dir_from_window(int * ret_xmotion,int * ret_ymotion,FvwmWindow * fw,Window context_w)3630 static void __resize_get_dir_from_window(
3631 	int *ret_xmotion, int *ret_ymotion, FvwmWindow *fw, Window context_w)
3632 {
3633 	if (context_w != Scr.Root && context_w != None)
3634 	{
3635 		if (context_w == FW_W_SIDE(fw, 0))   /* top */
3636 		{
3637 			*ret_ymotion = 1;
3638 		}
3639 		else if (context_w == FW_W_SIDE(fw, 1))  /* right */
3640 		{
3641 			*ret_xmotion = -1;
3642 		}
3643 		else if (context_w == FW_W_SIDE(fw, 2))  /* bottom */
3644 		{
3645 			*ret_ymotion = -1;
3646 		}
3647 		else if (context_w == FW_W_SIDE(fw, 3))  /* left */
3648 		{
3649 			*ret_xmotion = 1;
3650 		}
3651 		else if (context_w == FW_W_CORNER(fw, 0))  /* upper-left */
3652 		{
3653 			*ret_xmotion = 1;
3654 			*ret_ymotion = 1;
3655 		}
3656 		else if (context_w == FW_W_CORNER(fw, 1))  /* upper-right */
3657 		{
3658 			*ret_xmotion = -1;
3659 			*ret_ymotion = 1;
3660 		}
3661 		else if (context_w == FW_W_CORNER(fw, 2)) /* lower left */
3662 		{
3663 			*ret_xmotion = 1;
3664 			*ret_ymotion = -1;
3665 		}
3666 		else if (context_w == FW_W_CORNER(fw, 3))  /* lower right */
3667 		{
3668 			*ret_xmotion = -1;
3669 			*ret_ymotion = -1;
3670 		}
3671 	}
3672 
3673 	return;
3674 }
3675 
__resize_get_dir_proximity(int * ret_xmotion,int * ret_ymotion,FvwmWindow * fw,int x_off,int y_off,int px,int py,int * warp_x,int * warp_y,Bool find_nearest_border)3676 static void __resize_get_dir_proximity(
3677 	int *ret_xmotion, int *ret_ymotion, FvwmWindow *fw, int x_off,
3678 	int y_off, int px, int py, int *warp_x, int *warp_y, Bool find_nearest_border)
3679 {
3680 	int tx;
3681 	int ty;
3682 	direction_t dir;
3683 
3684 	*warp_x = *warp_y = -1;
3685 
3686 	if (px < 0 || x_off < 0 || py < 0 || y_off < 0)
3687 	{
3688 		return;
3689 	}
3690 
3691 	if (find_nearest_border == False)
3692 	{
3693 		tx = 0;
3694 		ty = 0;
3695 
3696 		tx = max(fw->boundary_width, tx);
3697 		ty = max(fw->boundary_width, ty);
3698 
3699 		if (px < tx)
3700 		{
3701 			*ret_xmotion = 1;
3702 		}
3703 		else if (x_off < tx)
3704 		{
3705 			*ret_xmotion = -1;
3706 		}
3707 		if (py < ty)
3708 		{
3709 			*ret_ymotion = 1;
3710 		}
3711 		else if (y_off < ty)
3712 		{
3713 			*ret_ymotion = -1;
3714 		}
3715 
3716 		return;
3717 	}
3718 
3719 	/* Get the direction from the quadrant the pointer is in. */
3720 	dir = __resize_get_dir_from_resize_quadrant(
3721 		x_off, y_off, px, py);
3722 
3723 	switch (dir)
3724 	{
3725 	case DIR_NW:
3726 		*ret_xmotion = 1;
3727 		*ret_ymotion = 1;
3728 		*warp_x = 0;
3729 		*warp_y = 0;
3730 		break;
3731 	case DIR_SW:
3732 		*ret_xmotion = 1;
3733 		*ret_ymotion = -1;
3734 		*warp_x = 0;
3735 		*warp_y = (y_off - 1);
3736 		break;
3737 	case DIR_W:
3738 		*ret_xmotion = 1;
3739 		*warp_x = 0;
3740 		*warp_y = (y_off / 2);
3741 		break;
3742 	case DIR_NE:
3743 		*ret_xmotion = -1;
3744 		*ret_ymotion = 1;
3745 		*warp_x = (x_off - 1);
3746 		*warp_y = 0;
3747 		break;
3748 	case DIR_SE:
3749 		*ret_xmotion = -1;
3750 		*ret_ymotion = -1;
3751 		*warp_x = (x_off - 1);
3752 		*warp_y = (y_off - 1);
3753 		break;
3754 	case DIR_E:
3755 		*ret_xmotion = -1;
3756 		*warp_x = (x_off - 1);
3757 		*warp_y = (y_off / 2);
3758 		break;
3759 	case DIR_N:
3760 		*ret_ymotion = 1;
3761 		*warp_x = (x_off / 2);
3762 		*warp_y = 0;
3763 		break;
3764 	case DIR_S:
3765 		*ret_ymotion = -1;
3766 		*warp_x = (x_off / 2);
3767 		*warp_y = (y_off - 1);
3768 		break;
3769 	default:
3770 		break;
3771 	}
3772 
3773 	return;
3774 }
3775 
__resize_get_refpos(int * ret_x,int * ret_y,int xmotion,int ymotion,int w,int h,FvwmWindow * fw)3776 static void __resize_get_refpos(
3777 	int *ret_x, int *ret_y, int xmotion, int ymotion, int w, int h,
3778 	FvwmWindow *fw)
3779 {
3780 	if (xmotion > 0)
3781 	{
3782 		*ret_x = 0;
3783 	}
3784 	else if (xmotion < 0)
3785 	{
3786 		*ret_x = w - 1;
3787 	}
3788 	else
3789 	{
3790 		*ret_x = w / 2;
3791 	}
3792 	if (ymotion > 0)
3793 	{
3794 		*ret_y = 0;
3795 	}
3796 	else if (ymotion < 0)
3797 	{
3798 		*ret_y = h - 1;
3799 	}
3800 	else
3801 	{
3802 		*ret_y = h / 2;
3803 	}
3804 
3805 	return;
3806 }
3807 
3808 /* Procedure:
3809  *      __resize_step - move the rubberband around.  This is called for
3810  *                 each motion event when we are resizing
3811  *
3812  *  Inputs:
3813  *      x_root   - the X corrdinate in the root window
3814  *      y_root   - the Y corrdinate in the root window
3815  *      x_off    - x offset of pointer from border (input/output)
3816  *      y_off    - y offset of pointer from border (input/output)
3817  *      drag     - resize internal structure
3818  *      orig     - resize internal structure
3819  *      xmotionp - pointer to xmotion in resize_window
3820  *      ymotionp - pointer to ymotion in resize_window
3821  *
3822  */
__resize_step(const exec_context_t * exc,int x_root,int y_root,int * x_off,int * y_off,rectangle * drag,const rectangle * orig,int * xmotionp,int * ymotionp,Bool do_resize_opaque,Bool is_direction_fixed)3823 static void __resize_step(
3824 	const exec_context_t *exc, int x_root, int y_root, int *x_off,
3825 	int *y_off, rectangle *drag, const rectangle *orig, int *xmotionp,
3826 	int *ymotionp, Bool do_resize_opaque, Bool is_direction_fixed)
3827 {
3828 	int action = 0;
3829 	int x2;
3830 	int y2;
3831 	int xdir;
3832 	int ydir;
3833 
3834 	x2 = x_root - *x_off;
3835 	x_root += *x_off;
3836 	if (is_direction_fixed == True && (*xmotionp != 0 || *ymotionp != 0))
3837 	{
3838 		xdir = *xmotionp;
3839 	}
3840 	else if (x2 <= orig->x ||
3841 		 (*xmotionp == 1 && x2 < orig->x + orig->width - 1))
3842 	{
3843 		xdir = 1;
3844 	}
3845 	else if (x2 >= orig->x + orig->width - 1 ||
3846 		 (*xmotionp == -1 && x2 > orig->x))
3847 	{
3848 		xdir = -1;
3849 	}
3850 	else
3851 	{
3852 		xdir = 0;
3853 	}
3854 	switch (xdir)
3855 	{
3856 	case 1:
3857 		if (*xmotionp != 1)
3858 		{
3859 			*x_off = -*x_off;
3860 			x_root = x2;
3861 			*xmotionp = 1;
3862 		}
3863 		drag->x = x_root;
3864 		drag->width = orig->x + orig->width - x_root;
3865 		action = 1;
3866 		break;
3867 	case -1:
3868 		if (*xmotionp != -1)
3869 		{
3870 			*x_off = -*x_off;
3871 			x_root = x2;
3872 			*xmotionp = -1;
3873 		}
3874 		drag->x = orig->x;
3875 		drag->width = 1 + x_root - drag->x;
3876 		action = 1;
3877 		break;
3878 	default:
3879 		break;
3880 	}
3881 	y2 = y_root - *y_off;
3882 	y_root += *y_off;
3883 	if (is_direction_fixed == True && (*xmotionp != 0 || *ymotionp != 0))
3884 	{
3885 		ydir = *ymotionp;
3886 	}
3887 	else if (y2 <= orig->y ||
3888 		 (*ymotionp == 1 && y2 < orig->y + orig->height - 1))
3889 	{
3890 		ydir = 1;
3891 	}
3892 	else if (y2 >= orig->y + orig->height - 1 ||
3893 		 (*ymotionp == -1 && y2 > orig->y))
3894 	{
3895 		ydir = -1;
3896 	}
3897 	else
3898 	{
3899 		ydir = 0;
3900 	}
3901 	switch (ydir)
3902 	{
3903 	case 1:
3904 		if (*ymotionp != 1)
3905 		{
3906 			*y_off = -*y_off;
3907 			y_root = y2;
3908 			*ymotionp = 1;
3909 		}
3910 		drag->y = y_root;
3911 		drag->height = orig->y + orig->height - y_root;
3912 		action = 1;
3913 		break;
3914 	case -1:
3915 		if (*ymotionp != -1)
3916 		{
3917 			*y_off = -*y_off;
3918 			y_root = y2;
3919 			*ymotionp = -1;
3920 		}
3921 		drag->y = orig->y;
3922 		drag->height = 1 + y_root - drag->y;
3923 		action = 1;
3924 		break;
3925 	default:
3926 		break;
3927 	}
3928 
3929 	if (action)
3930 	{
3931 		/* round up to nearest OK size to keep pointer inside
3932 		 * rubberband */
3933 		constrain_size(
3934 			exc->w.fw, exc->x.elast, &drag->width, &drag->height,
3935 			*xmotionp, *ymotionp, CS_ROUND_UP);
3936 		if (*xmotionp == 1)
3937 		{
3938 			drag->x = orig->x + orig->width - drag->width;
3939 		}
3940 		if (*ymotionp == 1)
3941 		{
3942 			drag->y = orig->y + orig->height - drag->height;
3943 		}
3944 		if (!do_resize_opaque)
3945 		{
3946 			draw_move_resize_grid(
3947 				drag->x, drag->y, drag->width - 1,
3948 				drag->height - 1);
3949 		}
3950 		else
3951 		{
3952 			frame_setup_window(
3953 				exc->w.fw, drag->x, drag->y, drag->width,
3954 				drag->height, False);
3955 		}
3956 	}
3957 	DisplaySize(exc->w.fw, exc->x.elast, drag->width, drag->height, False, False);
3958 
3959 	return;
3960 }
3961 
3962 /* Starts a window resize operation */
__resize_window(F_CMD_ARGS)3963 static Bool __resize_window(F_CMD_ARGS)
3964 {
3965 	extern Window bad_window;
3966 	FvwmWindow *fw = exc->w.fw;
3967 	Bool is_finished = False, is_done = False, is_aborted = False;
3968 	Bool do_send_cn = False;
3969 	Bool do_resize_opaque;
3970 	Bool do_warp_to_border;
3971 	Bool is_direction_fixed;
3972 	Bool automatic_border_direction;
3973 	Bool detect_automatic_direction;
3974 	Bool fButtonAbort = False;
3975 	Bool fForceRedraw = False;
3976 	Bool called_from_title = False;
3977 	int x,y,delta_x,delta_y,stashed_x,stashed_y;
3978 	Window ResizeWindow;
3979 	struct monitor *mon = fw->m;
3980 	int dx;
3981 	int dy;
3982 	const int vx = mon->virtual_scr.Vx;
3983 	const int vy = mon->virtual_scr.Vy;
3984 	int n;
3985 	unsigned int button_mask = 0;
3986 	rectangle sdrag;
3987 	rectangle sorig;
3988 	rectangle *drag = &sdrag;
3989 	const rectangle *orig = &sorig;
3990 	const window_g g_backup = fw->g;
3991 	int ymotion = 0;
3992 	int xmotion = 0;
3993 	int was_maximized;
3994 	unsigned edge_wrap_x;
3995 	unsigned edge_wrap_y;
3996 	int px;
3997 	int py;
3998 	int i;
3999 	size_borders b;
4000 	frame_move_resize_args mr_args = NULL;
4001 	long evmask;
4002 	XEvent ev;
4003 	int ref_x;
4004 	int ref_y;
4005 	int x_off;
4006 	int y_off;
4007 	direction_t dir;
4008 	int warp_x = 0;
4009 	int warp_y = 0;
4010 
4011 	dx = mon->virtual_scr.EdgeScrollX ? mon->virtual_scr.EdgeScrollX : monitor_get_all_widths();
4012 	dy = mon->virtual_scr.EdgeScrollY ? mon->virtual_scr.EdgeScrollY : monitor_get_all_heights();
4013 
4014 	bad_window = False;
4015 	ResizeWindow = FW_W_FRAME(fw);
4016 	if (fev_get_evpos_or_query(dpy, Scr.Root, exc->x.etrigger, &px, &py) ==
4017 	    False ||
4018 	    XTranslateCoordinates(
4019 		    dpy, Scr.Root, ResizeWindow, px, py, &px, &py,
4020 		    &JunkChild) == False)
4021 	{
4022 		/* pointer is on a different screen - that's okay here */
4023 		px = 0;
4024 		py = 0;
4025 	}
4026 	button_mask &= DEFAULT_ALL_BUTTONS_MASK;
4027 
4028 	if (!is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True))
4029 	{
4030 		XBell(dpy, 0);
4031 		return False;
4032 	}
4033 
4034 	if (IS_SHADED(fw) || !IS_MAPPED(fw))
4035 	{
4036 		do_resize_opaque = False;
4037 		evmask = XEVMASK_RESIZE;
4038 	}
4039 	else
4040 	{
4041 		do_resize_opaque = DO_RESIZE_OPAQUE(fw);
4042 		evmask = XEVMASK_RESIZE_OPAQUE;
4043 	}
4044 
4045 	/* no suffix = % of screen, 'p' = pixels, 'c' = increment units */
4046 	if (IS_SHADED(fw))
4047 	{
4048 		get_unshaded_geometry(fw, drag);
4049 	}
4050 	else
4051 	{
4052 		drag->width = fw->g.frame.width;
4053 		drag->height = fw->g.frame.height;
4054 	}
4055 
4056 	get_window_borders(fw, &b);
4057 	n = GetResizeArguments(fw,
4058 		&action, fw->g.frame.x, fw->g.frame.y,
4059 		fw->hints.base_width, fw->hints.base_height,
4060 		fw->hints.width_inc, fw->hints.height_inc,
4061 		&b, &(drag->width), &(drag->height),
4062 		&dir, &is_direction_fixed, &do_warp_to_border,
4063 		&automatic_border_direction, &detect_automatic_direction);
4064 
4065 	if (n == 2)
4066 	{
4067 		rectangle new_g;
4068 
4069 		/* size will be less or equal to requested */
4070 		if (IS_SHADED(fw))
4071 		{
4072 			rectangle shaded_g;
4073 
4074 			get_unshaded_geometry(fw, &new_g);
4075 			SET_MAXIMIZED(fw, 0);
4076 			constrain_size(
4077 				fw, NULL, &drag->width, &drag->height, xmotion,
4078 				ymotion, 0);
4079 			gravity_resize(
4080 				fw->hints.win_gravity, &new_g,
4081 				drag->width - new_g.width,
4082 				drag->height - new_g.height);
4083 			fw->g.normal = new_g;
4084 			get_shaded_geometry(fw, &shaded_g, &new_g);
4085 			frame_setup_window(
4086 				fw, shaded_g.x, shaded_g.y, shaded_g.width,
4087 				shaded_g.height, False);
4088 		}
4089 		else
4090 		{
4091 			new_g = fw->g.frame;
4092 			SET_MAXIMIZED(fw, 0);
4093 			constrain_size(
4094 				fw, NULL, &drag->width, &drag->height, xmotion,
4095 				ymotion, 0);
4096 			gravity_resize(
4097 				fw->hints.win_gravity, &new_g,
4098 				drag->width - new_g.width,
4099 				drag->height - new_g.height);
4100 			frame_setup_window(
4101 				fw, new_g.x, new_g.y, drag->width,
4102 				drag->height, False);
4103 		}
4104 		update_absolute_geometry(fw);
4105 		maximize_adjust_offset(fw);
4106 		ResizeWindow = None;
4107 		return True;
4108 	}
4109 
4110 	was_maximized = IS_MAXIMIZED(fw);
4111 	SET_MAXIMIZED(fw, 0);
4112 	if (was_maximized)
4113 	{
4114 		/* must redraw the buttons now so that the 'maximize' button
4115 		 * does not stay depressed. */
4116 		border_draw_decorations(
4117 			fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
4118 			NULL, NULL);
4119 	}
4120 
4121 	if (Scr.bo.do_install_root_cmap)
4122 	{
4123 		InstallRootColormap();
4124 	}
4125 	else
4126 	{
4127 		InstallFvwmColormap();
4128 	}
4129 	if (!GrabEm(CRS_RESIZE, GRAB_NORMAL))
4130 	{
4131 		XBell(dpy, 0);
4132 		return False;
4133 	}
4134 
4135 	/* handle problems with edge-wrapping while resizing */
4136 	edge_wrap_x = Scr.flags.do_edge_wrap_x;
4137 	edge_wrap_y = Scr.flags.do_edge_wrap_y;
4138 	Scr.flags.do_edge_wrap_x = 0;
4139 	Scr.flags.do_edge_wrap_y = 0;
4140 
4141 	if (!do_resize_opaque)
4142 	{
4143 		MyXGrabServer(dpy);
4144 	}
4145 	if (!XGetGeometry(
4146 		    dpy, (Drawable)ResizeWindow, &JunkRoot, &drag->x, &drag->y,
4147 		    (unsigned int*)&drag->width, (unsigned int*)&drag->height,
4148 		    (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
4149 	{
4150 		UngrabEm(GRAB_NORMAL);
4151 		if (!do_resize_opaque)
4152 		{
4153 			MyXUngrabServer(dpy);
4154 		}
4155 		return False;
4156 	}
4157 	if (IS_SHADED(fw))
4158 	{
4159 		SET_MAXIMIZED(fw, was_maximized);
4160 		get_unshaded_geometry(fw, drag);
4161 		SET_MAXIMIZED(fw, 0);
4162 	}
4163 	if (do_resize_opaque)
4164 	{
4165 		mr_args = frame_create_move_resize_args(
4166 			fw, FRAME_MR_OPAQUE, &fw->g.frame, &fw->g.frame, 0,
4167 			DIR_NONE);
4168 	}
4169 	else
4170 	{
4171 		Scr.flags.is_wire_frame_displayed = True;
4172 	}
4173 	MyXGrabKeyboard(dpy);
4174 
4175 	sorig = *drag;
4176 	ymotion = 0;
4177 	xmotion = 0;
4178 
4179 	/* pop up a resize dimensions window */
4180 	if (!Scr.gs.do_hide_resize_window)
4181 	{
4182 		resize_geometry_window();
4183 		position_geometry_window(NULL);
4184 		XMapRaised(dpy, Scr.SizeWindow.win);
4185 	}
4186 	DisplaySize(fw, exc->x.elast, orig->width, orig->height, True, True);
4187 
4188 	if (dir == DIR_NONE && detect_automatic_direction == True)
4189 	{
4190 		dir = __resize_get_dir_from_resize_quadrant(
4191 				orig->width, orig->height, px, py);
4192 	}
4193 
4194 	if (dir != DIR_NONE)
4195 	{
4196 		int grav;
4197 
4198 		grav = gravity_dir_to_grav(dir);
4199 		gravity_get_offsets(grav, &xmotion, &ymotion);
4200 		xmotion = -xmotion;
4201 		ymotion = -ymotion;
4202 	}
4203 	if (xmotion == 0 && ymotion == 0)
4204 	{
4205 		__resize_get_dir_from_window(&xmotion, &ymotion, fw, PressedW);
4206 	}
4207 	if (FW_W_TITLE(fw) != None && PressedW == FW_W_TITLE(fw))
4208 	{
4209 		/* title was pressed to start the resize */
4210 		called_from_title = True;
4211 	}
4212 	else
4213 	{
4214 		for (i = NUMBER_OF_TITLE_BUTTONS; i--; )
4215 		{
4216 			/* see if the title button was pressed to that the
4217 			 * resize */
4218 			if (FW_W_BUTTON(fw, i) != None &&
4219 			    FW_W_BUTTON(fw, i) == PressedW)
4220 			{
4221 				/* yes */
4222 				called_from_title = True;
4223 			}
4224 		}
4225 	}
4226 	/* don't warp if the resize was triggered by a press somwhere on the
4227 	 * title bar */
4228 	if (PressedW != Scr.Root && xmotion == 0 && ymotion == 0 &&
4229 	    !called_from_title)
4230 	{
4231 		__resize_get_dir_proximity(
4232 			&xmotion, &ymotion, fw, orig->width,
4233 			orig->height, px, py, &warp_x, &warp_y,
4234 			automatic_border_direction);
4235 		if (xmotion != 0 || ymotion != 0)
4236 		{
4237 			do_warp_to_border = True;
4238 		}
4239 	}
4240 	if (!IS_SHADED(fw))
4241 	{
4242 		__resize_get_refpos(
4243 			&ref_x, &ref_y, xmotion, ymotion, orig->width,
4244 			orig->height, fw);
4245 	}
4246 	else
4247 	{
4248 		switch (SHADED_DIR(fw))
4249 		{
4250 		case DIR_N:
4251 		case DIR_NW:
4252 		case DIR_NE:
4253 			if (ymotion == -1)
4254 			{
4255 				ymotion = 0;
4256 			}
4257 			break;
4258 		case DIR_S:
4259 		case DIR_SW:
4260 		case DIR_SE:
4261 			if (ymotion == 1)
4262 			{
4263 				ymotion = 0;
4264 			}
4265 			break;
4266 		default:
4267 			break;
4268 		}
4269 		switch (SHADED_DIR(fw))
4270 		{
4271 		case DIR_E:
4272 		case DIR_NE:
4273 		case DIR_SE:
4274 			if (xmotion == 1)
4275 			{
4276 				xmotion = 0;
4277 			}
4278 			break;
4279 		case DIR_W:
4280 		case DIR_NW:
4281 		case DIR_SW:
4282 			if (xmotion == -1)
4283 			{
4284 				xmotion = 0;
4285 			}
4286 			break;
4287 		default:
4288 			break;
4289 		}
4290 		__resize_get_refpos(
4291 			&ref_x, &ref_y, xmotion, ymotion, fw->g.frame.width,
4292 			fw->g.frame.height, fw);
4293 	}
4294 	x_off = 0;
4295 	y_off = 0;
4296 	if (do_warp_to_border == True)
4297 	{
4298 		int dx;
4299 		int dy;
4300 
4301 		dx = (xmotion == 0) ? px : ref_x;
4302 		dy = (ymotion == 0) ? py : ref_y;
4303 
4304 		/* Warp the pointer to the closest border automatically? */
4305 		if (automatic_border_direction == True &&
4306 			(warp_x >=0 && warp_y >=0) &&
4307 			!IS_SHADED(fw))
4308 		{
4309 			dx = warp_x;
4310 			dy = warp_y;
4311 		}
4312 
4313 		/* warp the pointer to the border */
4314 		FWarpPointer(
4315 			dpy, None, ResizeWindow, 0, 0, 1, 1, dx, dy);
4316 		XFlush(dpy);
4317 	}
4318 	else if (xmotion != 0 || ymotion != 0)
4319 	{
4320 		/* keep the distance between pointer and border */
4321 		x_off = (xmotion == 0) ? 0 : ref_x - px;
4322 		y_off = (ymotion == 0) ? 0 : ref_y - py;
4323 	}
4324 	else
4325 	{
4326 		/* wait until the pointer hits a border before making a
4327 		 * decision about the resize direction */
4328 	}
4329 
4330 	/* draw the rubber-band window */
4331 	if (!do_resize_opaque)
4332 	{
4333 		draw_move_resize_grid(
4334 			drag->x, drag->y, drag->width - 1, drag->height - 1);
4335 	}
4336 	/* kick off resizing without requiring any motion if invoked with a key
4337 	 * press */
4338 	if (exc->x.elast->type == KeyPress)
4339 	{
4340 		int xo;
4341 		int yo;
4342 
4343 		if (FQueryPointer(
4344 			    dpy, Scr.Root, &JunkRoot, &JunkChild, &stashed_x,
4345 			    &stashed_y, &JunkX, &JunkY, &JunkMask) == False)
4346 		{
4347 			/* pointer is on a different screen */
4348 			stashed_x = 0;
4349 			stashed_y = 0;
4350 		}
4351 		xo = 0;
4352 		yo = 0;
4353 		__resize_step(
4354 			exc, stashed_x, stashed_y, &xo, &yo, drag, orig,
4355 			&xmotion, &ymotion, do_resize_opaque, True);
4356 	}
4357 	else
4358 	{
4359 		stashed_x = stashed_y = -1;
4360 	}
4361 
4362 	/* loop to resize */
4363 	memset(&ev, 0, sizeof(ev));
4364 	while (!is_finished && bad_window != FW_W(fw))
4365 	{
4366 		int rc = 0;
4367 		int is_resized = False;
4368 
4369 		/* block until there is an interesting event */
4370 		while (rc != -1 &&
4371 		       (!FPending(dpy) || !FCheckMaskEvent(dpy, evmask, &ev)))
4372 		{
4373 			rc = HandlePaging(
4374 				&ev, dx, dy, &x, &y, &delta_x, &delta_y, False,
4375 				False, True, fw->edge_delay_ms_resize);
4376 			if (rc == 1)
4377 			{
4378 				/* Fake an event to force window reposition */
4379 				ev.type = MotionNotify;
4380 				ev.xmotion.time = fev_get_evtime();
4381 				fForceRedraw = True;
4382 				break;
4383 			}
4384 		}
4385 		if (rc == -1)
4386 		{
4387 			FMaskEvent(
4388 				dpy,
4389 				evmask | EnterWindowMask | LeaveWindowMask,
4390 				&ev);
4391 		}
4392 		if (ev.type == MotionNotify ||
4393 		    ev.type == EnterNotify || ev.type == LeaveNotify)
4394 		{
4395 			/* discard any extra motion events before a release */
4396 			/* dv (2004-07-01): With XFree 4.1.0.1, some Mouse
4397 			 * events are not reported to fvwm when the pointer
4398 			 * moves very fast and suddenly stops in the corner of
4399 			 * the screen.  Handle EnterNotify/LeaveNotify events
4400 			 * too to get an idea where the pointer might be. */
4401 			while (
4402 				FCheckMaskEvent(
4403 					dpy, ButtonMotionMask |
4404 					PointerMotionMask | ButtonReleaseMask |
4405 					ButtonPressMask | EnterWindowMask |
4406 					LeaveWindowMask, &ev) == True)
4407 			{
4408 				if (ev.type == ButtonRelease ||
4409 				    ev.type == ButtonPress ||
4410 				    ev.type == KeyPress)
4411 				{
4412 					break;
4413 				}
4414 			}
4415 		}
4416 		if (ev.type == EnterNotify || ev.type == LeaveNotify)
4417 		{
4418 			XEvent e2;
4419 			int x;
4420 			int y;
4421 
4422 			/* Query the pointer to catch the latest information.
4423 			 * This *is* necessary. */
4424 			if (FQueryPointer(
4425 				    dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
4426 				    &y, &JunkX, &JunkY, &JunkMask) == True)
4427 			{
4428 				/* Must NOT use button_mask here, or resize
4429 				 * will not work with num lock */
4430 				fev_make_null_event(&e2, dpy);
4431 				e2.type = MotionNotify;
4432 				e2.xmotion.time = fev_get_evtime();
4433 				e2.xmotion.x_root = x;
4434 				e2.xmotion.y_root = y;
4435 				e2.xmotion.state = JunkMask;
4436 				e2.xmotion.same_screen = True;
4437 				ev = e2;
4438 				fev_fake_event(&ev);
4439 			}
4440 			else
4441 			{
4442 				/* pointer is on a different screen,
4443 				 * ignore event */
4444 			}
4445 		}
4446 
4447 		is_done = False;
4448 		/* Handle a limited number of key press events to allow
4449 		 * mouseless operation */
4450 		if (ev.type == KeyPress)
4451 		{
4452 			Keyboard_shortcuts(&ev, fw, NULL, NULL, ButtonRelease);
4453 			if (ev.type == ButtonRelease)
4454 			{
4455 				do_send_cn = True;
4456 			}
4457 		}
4458 		switch (ev.type)
4459 		{
4460 		case ButtonPress:
4461 			is_done = True;
4462 			if (ev.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
4463 			    ((Button1Mask << (ev.xbutton.button - 1)) &
4464 			     button_mask))
4465 			{
4466 				/* No new button was pressed, just a delayed
4467 				 * event */
4468 				break;
4469 			}
4470 			/* Abort the resize if
4471 			 *  - the move started with a pressed button and
4472 			 *    another button was pressed during the operation
4473 			 *  - no button was started at the beginning and any
4474 			 *    button except button 1 was pressed. */
4475 			if (button_mask || (ev.xbutton.button != 1))
4476 			{
4477 				fButtonAbort = True;
4478 				/* fall through */
4479 			}
4480 			else
4481 			{
4482 				is_finished = True;
4483 				do_send_cn = True;
4484 				break;
4485 			}
4486 		case KeyPress:
4487 			/* simple code to bag out of move - CKH */
4488 			if (fButtonAbort ||
4489 			    XLookupKeysym(&ev.xkey, 0) == XK_Escape)
4490 			{
4491 				is_aborted = True;
4492 				do_send_cn = True;
4493 				is_finished = True;
4494 			}
4495 			is_done = True;
4496 			break;
4497 
4498 		case ButtonRelease:
4499 			is_finished = True;
4500 			is_done = True;
4501 			break;
4502 
4503 		case MotionNotify:
4504 			if (ev.xmotion.same_screen == False)
4505 			{
4506 				continue;
4507 			}
4508 			if (!fForceRedraw)
4509 			{
4510 				x = ev.xmotion.x_root;
4511 				y = ev.xmotion.y_root;
4512 				/* resize before paging request to prevent
4513 				 * resize from lagging * mouse - mab */
4514 				__resize_step(
4515 					exc, x, y, &x_off, &y_off, drag, orig,
4516 					&xmotion, &ymotion, do_resize_opaque,
4517 					is_direction_fixed);
4518 				is_resized = True;
4519 				/* need to move the viewport */
4520 				HandlePaging(
4521 					&ev, dx, dy, &x, &y, &delta_x,
4522 					&delta_y, False, False, False,
4523 					fw->edge_delay_ms_resize);
4524 			}
4525 			/* redraw outline if we paged - mab */
4526 			if (delta_x != 0 || delta_y != 0)
4527 			{
4528 				sorig.x -= delta_x;
4529 				sorig.y -= delta_y;
4530 				drag->x -= delta_x;
4531 				drag->y -= delta_y;
4532 
4533 				__resize_step(
4534 					exc, x, y, &x_off, &y_off, drag, orig,
4535 					&xmotion, &ymotion, do_resize_opaque,
4536 					is_direction_fixed);
4537 				is_resized = True;
4538 			}
4539 			fForceRedraw = False;
4540 			is_done = True;
4541 			break;
4542 
4543 		case PropertyNotify:
4544 		{
4545 			evh_args_t ea;
4546 			exec_context_changes_t ecc;
4547 
4548 			ecc.x.etrigger = &ev;
4549 			ea.exc = exc_clone_context(exc, &ecc, ECC_ETRIGGER);
4550 			HandlePropertyNotify(&ea);
4551 			exc_destroy_context(ea.exc);
4552 			is_done = True;
4553 			break;
4554 		}
4555 
4556 		default:
4557 			break;
4558 		}
4559 		if (!is_done)
4560 		{
4561 			if (!do_resize_opaque)
4562 			{
4563 				/* must undraw the rubber band in case the
4564 				 * event causes some drawing */
4565 				switch_move_resize_grid(False);
4566 			}
4567 			dispatch_event(&ev);
4568 			if (!do_resize_opaque)
4569 			{
4570 				draw_move_resize_grid(
4571 					drag->x, drag->y, drag->width - 1,
4572 					drag->height - 1);
4573 			}
4574 		}
4575 		else
4576 		{
4577 			if (do_resize_opaque)
4578 			{
4579 				/* only do this with opaque resizes, (i.e. the
4580 				 * server is not grabbed) */
4581 				if (is_resized == False)
4582 				{
4583 					BroadcastConfig(
4584 						M_CONFIGURE_WINDOW, fw);
4585 				}
4586 				else
4587 				{
4588 					/* event already broadcast */
4589 				}
4590 				FlushAllMessageQueues();
4591 			}
4592 		}
4593 	}
4594 
4595 	/* erase the rubber-band */
4596 	if (!do_resize_opaque)
4597 	{
4598 		switch_move_resize_grid(False);
4599 	}
4600 	/* pop down the size window */
4601 	if (!Scr.gs.do_hide_resize_window)
4602 	{
4603 		XUnmapWindow(dpy, Scr.SizeWindow.win);
4604 	}
4605 	if (is_aborted || bad_window == FW_W(fw))
4606 	{
4607 		/* return pointer if aborted resize was invoked with key */
4608 		if (stashed_x >= 0)
4609 		{
4610 			FWarpPointer(
4611 				dpy, None, Scr.Root, 0, 0, 0, 0, stashed_x,
4612 				stashed_y);
4613 		}
4614 		if (was_maximized)
4615 		{
4616 			/* since we aborted the resize, the window is still
4617 			 * maximized */
4618 			SET_MAXIMIZED(fw, 1);
4619 		}
4620 		if (do_resize_opaque)
4621 		{
4622 			int xo;
4623 			int yo;
4624 			rectangle g;
4625 
4626 			xo = 0;
4627 			yo = 0;
4628 			xmotion = 1;
4629 			ymotion = 1;
4630 			g = sorig;
4631 			__resize_step(
4632 				exc, sorig.x, sorig.y, &xo, &yo, &g, orig,
4633 				&xmotion, &ymotion, do_resize_opaque, True);
4634 		}
4635 		if (vx != mon->virtual_scr.Vx || vy != mon->virtual_scr.Vy)
4636 		{
4637 			MoveViewport(mon, vx, vy, False);
4638 		}
4639 		/* restore all geometry-related info */
4640 		fw->g = g_backup;
4641 		if (bad_window == FW_W(fw))
4642 		{
4643 			XUnmapWindow(dpy, FW_W_FRAME(fw));
4644 			border_undraw_decorations(fw);
4645 			XBell(dpy, 0);
4646 		}
4647 	}
4648 	else if (!is_aborted && bad_window != FW_W(fw))
4649 	{
4650 		rectangle new_g;
4651 
4652 		/* size will be >= to requested */
4653 		constrain_size(
4654 			fw, exc->x.elast, &drag->width, &drag->height,
4655 			xmotion, ymotion, CS_ROUND_UP);
4656 		if (IS_SHADED(fw))
4657 		{
4658 			get_shaded_geometry(fw, &new_g, drag);
4659 		}
4660 		else
4661 		{
4662 			new_g = *drag;
4663 		}
4664 		if (do_resize_opaque)
4665 		{
4666 			frame_update_move_resize_args(mr_args, &new_g);
4667 		}
4668 		else
4669 		{
4670 			frame_setup_window(
4671 				fw, new_g.x, new_g.y, new_g.width,
4672 				new_g.height, False);
4673 		}
4674 		if (IS_SHADED(fw))
4675 		{
4676 			fw->g.normal.width = drag->width;
4677 			fw->g.normal.height = drag->height;
4678 		}
4679 	}
4680 	if (is_aborted && was_maximized)
4681 	{
4682 		/* force redraw */
4683 		border_draw_decorations(
4684 			fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
4685 			NULL, NULL);
4686 	}
4687 	if (Scr.bo.do_install_root_cmap)
4688 	{
4689 		UninstallRootColormap();
4690 	}
4691 	else
4692 	{
4693 		UninstallFvwmColormap();
4694 	}
4695 	ResizeWindow = None;
4696 	if (!do_resize_opaque)
4697 	{
4698 		int event_types[2] = { EnterNotify, LeaveNotify };
4699 
4700 		/* Throw away some events that dont interest us right now. */
4701 		discard_typed_events(2, event_types);
4702 		Scr.flags.is_wire_frame_displayed = False;
4703 		MyXUngrabServer(dpy);
4704 	}
4705 	if (mr_args != NULL)
4706 	{
4707 		frame_free_move_resize_args(fw, mr_args);
4708 	}
4709 	if (do_send_cn == True)
4710 	{
4711 		rectangle g;
4712 
4713 		if (is_aborted)
4714 		{
4715 			g = sorig;
4716 		}
4717 		else
4718 		{
4719 			g = *drag;
4720 		}
4721 		SendConfigureNotify(fw, g.x, g.y, g.width, g.height, 0, True);
4722 	}
4723 	MyXUngrabKeyboard(dpy);
4724 	WaitForButtonsUp(True);
4725 	UngrabEm(GRAB_NORMAL);
4726 	Scr.flags.do_edge_wrap_x = edge_wrap_x;
4727 	Scr.flags.do_edge_wrap_y = edge_wrap_y;
4728 	update_absolute_geometry(fw);
4729 	maximize_adjust_offset(fw);
4730 	if (is_aborted)
4731 	{
4732 		return False;
4733 	}
4734 
4735 	return True;
4736 }
4737 
CMD_Resize(F_CMD_ARGS)4738 void CMD_Resize(F_CMD_ARGS)
4739 {
4740 	FvwmWindow *fw = exc->w.fw;
4741 
4742 	if (IS_EWMH_FULLSCREEN(fw))
4743 	{
4744 		/* do not unmaximize ! */
4745 		CMD_ResizeMaximize(F_PASS_ARGS);
4746 		return;
4747 	}
4748 
4749 	__resize_window(F_PASS_ARGS);
4750 	UPDATE_FVWM_SCREEN(fw);
4751 
4752 	return;
4753 }
4754 
4755 /* ----------------------------- maximizing code --------------------------- */
4756 
is_window_sticky_across_pages(FvwmWindow * fw)4757 Bool is_window_sticky_across_pages(FvwmWindow *fw)
4758 {
4759 	if (IS_STICKY_ACROSS_PAGES(fw) ||
4760 	    (IS_ICONIFIED(fw) && IS_ICON_STICKY_ACROSS_PAGES(fw)))
4761 	{
4762 		return True;
4763 	}
4764 	else
4765 	{
4766 		return False;
4767 	}
4768 }
4769 
is_window_sticky_across_desks(FvwmWindow * fw)4770 Bool is_window_sticky_across_desks(FvwmWindow *fw)
4771 {
4772 	if (IS_STICKY_ACROSS_DESKS(fw) ||
4773 	    (IS_ICONIFIED(fw) && IS_ICON_STICKY_ACROSS_DESKS(fw)))
4774 	{
4775 		return True;
4776 	}
4777 	else
4778 	{
4779 		return False;
4780 	}
4781 }
4782 
move_sticky_window_to_same_page(FvwmWindow * fw,int * x11,int * x12,int * y11,int * y12)4783 static void move_sticky_window_to_same_page(FvwmWindow *fw,
4784 	int *x11, int *x12, int *y11, int *y12)
4785 {
4786 	int x21, x22, y21, y22, page_x, page_y;
4787 
4788 	get_page_offset_check_visible(&page_x, &page_y, fw);
4789 	x21 = page_x;
4790 	x22 = page_x + monitor_get_all_widths();
4791 	y21 = page_y;
4792 	y22 = page_y + monitor_get_all_heights();
4793 
4794 	/* make sure the x coordinate is on the same page as the reference
4795 	 * window */
4796 	if (*x11 >= x22)
4797 	{
4798 		while (*x11 >= x22)
4799 		{
4800 			*x11 -= monitor_get_all_widths();
4801 			*x12 -= monitor_get_all_widths();
4802 		}
4803 	}
4804 	else if (*x12 <= x21)
4805 	{
4806 		while (*x12 <= x21)
4807 		{
4808 			*x11 += monitor_get_all_widths();
4809 			*x12 += monitor_get_all_widths();
4810 		}
4811 	}
4812 	/* make sure the y coordinate is on the same page as the reference
4813 	 * window */
4814 	if (*y11 >= y22)
4815 	{
4816 		while (*y11 >= y22)
4817 		{
4818 			*y11 -= monitor_get_all_heights();
4819 			*y12 -= monitor_get_all_heights();
4820 		}
4821 	}
4822 	else if (*y12 <= y21)
4823 	{
4824 		while (*y12 <= y21)
4825 		{
4826 			*y11 += monitor_get_all_heights();
4827 			*y12 += monitor_get_all_heights();
4828 		}
4829 	}
4830 
4831 	return;
4832 }
4833 
4834 
4835 /*
4836  * Grows a window rectangle until its edges touch the closest window based
4837  * on snap type, layers = { min_layer, max_layer }, or the boundary rectangle.
4838  */
grow_to_closest_type(FvwmWindow * fw,rectangle * win_r,rectangle bound,int * layers,int type,bool consider_touching)4839 static void grow_to_closest_type(
4840 	FvwmWindow *fw, rectangle *win_r, rectangle bound, int *layers,
4841 	int type, bool consider_touching)
4842 {
4843 	FvwmWindow *twin;
4844 	rectangle other;
4845 	int maskout = (SNAP_SCREEN | SNAP_SCREEN_WINDOWS |
4846 			SNAP_SCREEN_ICONS | SNAP_SCREEN_ALL);
4847 
4848 	/* window coordinates for original window, other, and new */
4849 	int xw1, yw1, xw2, yw2;
4850 	int xo1, yo1, xo2, yo2;
4851 	int new_x1, new_x2, new_y1, new_y2;
4852 
4853 	xw1 = win_r->x;
4854 	xw2 = xw1 + win_r->width;
4855 	yw1 = win_r->y;
4856 	yw2 = yw1 + win_r->height;
4857 
4858 	new_x1 = bound.x;
4859 	new_x2 = new_x1 + bound.width;
4860 	new_y1 = bound.y;
4861 	new_y2 = new_y1 + bound.height;
4862 
4863 	/* Use other windows to shrink boundary to get closest */
4864 	for (twin = Scr.FvwmRoot.next; twin; twin = twin->next)
4865 	{
4866 		if (twin == fw || !IS_PARTIALLY_VISIBLE(twin) ||
4867 			(twin->Desk != fw->Desk &&
4868 			!is_window_sticky_across_desks(twin)))
4869 		{
4870 			continue;
4871 		}
4872 		switch (type & ~(maskout))
4873 		{
4874 		case SNAP_WINDOWS:  /* we only consider windows */
4875 			if (IS_ICONIFIED(twin))
4876 			{
4877 				continue;
4878 			}
4879 			break;
4880 		case SNAP_ICONS:  /* we only consider icons */
4881 			if (!IS_ICONIFIED(twin))
4882 			{
4883 				continue;
4884 			}
4885 			break;
4886 		case SNAP_SAME:  /* we don't consider unequal */
4887 			if (IS_ICONIFIED(twin) != IS_ICONIFIED(fw))
4888 			{
4889 				continue;
4890 			}
4891 			break;
4892 		default:
4893 			break;
4894 		}
4895 		if ((layers[0] >= 0 && twin->layer < layers[0]) ||
4896 			(layers[1] >= 0 && twin->layer > layers[1]))
4897 		{
4898 			continue;
4899 		}
4900 		if (!get_visible_window_or_icon_geometry(twin, &other))
4901 		{
4902 			continue;
4903 		}
4904 		xo1 = other.x;
4905 		xo2 = xo1 + other.width;
4906 		yo1 = other.y;
4907 		yo2 = yo1 + other.height;
4908 
4909 		if (is_window_sticky_across_pages(twin))
4910                 {
4911                         move_sticky_window_to_same_page(fw,
4912                                 &xo1, &xo2, &yo1, &yo2);
4913                 }
4914 
4915 		/* Shrink left/right edges */
4916 		if (yo1 < yw2 && yo2 > yw1)
4917 		{
4918 			if (new_x1 < xo2 && (xw1 > xo2 ||
4919 				(consider_touching && xw1 == xo2)))
4920 			{
4921 				new_x1 = xo2;
4922 			}
4923 			if (new_x2 > xo1 && (xw2 < xo1 ||
4924 				(consider_touching && xw2 == xo1)))
4925 			{
4926 				new_x2 = xo1;
4927 			}
4928 		}
4929 		/* Shrink top/bottom edges */
4930 		if (xo1 < xw2 && xo2 > xw1)
4931 		{
4932 			if (new_y1 < yo2 && (yw1 > yo2 ||
4933 				(consider_touching && yw1 == yo2)))
4934 			{
4935 				new_y1 = yo2;
4936 			}
4937 			if (new_y2 > yo1 && (yw2 < yo1 ||
4938 				(consider_touching && yw2 == yo1)))
4939 			{
4940 				new_y2 = yo1;
4941 			}
4942 		}
4943 	}
4944 	win_r->x = new_x1;
4945 	win_r->width = new_x2 - new_x1;
4946 	win_r->y = new_y1;
4947 	win_r->height = new_y2 - new_y1;
4948 }
4949 
unmaximize_fvwm_window(FvwmWindow * fw)4950 static void unmaximize_fvwm_window(
4951 	FvwmWindow *fw)
4952 {
4953 	char	*cmd;
4954 	rectangle new_g;
4955 
4956 	SET_MAXIMIZED(fw, 0);
4957 
4958 	if (IS_SHADED(fw))
4959 	{
4960 		get_shaded_geometry(fw, &new_g, &new_g);
4961 	}
4962 
4963 	/* We might be restoring a window's geometry coming out of fullscreen,
4964 	 * which might be a maximized window, so ensure we use the correct
4965 	 * geometry reference.
4966 	 *
4967 	 * If the window was not maximized, then we use the window's normal
4968 	 * geometry.
4969 	 */
4970 	get_relative_geometry(fw, &new_g, fw->fullscreen.was_maximized ?
4971 			&fw->fullscreen.g.max : &fw->g.normal);
4972 
4973 	if (fw->fullscreen.was_maximized)
4974 	{
4975 		/* If MWMButtons is in use, set the style of the button as
4976 		 * marked as maximized.
4977 		 */
4978 		SET_MAXIMIZED(fw, 1);
4979 	}
4980 
4981 	if (IS_EWMH_FULLSCREEN(fw))
4982 	{
4983 		SET_EWMH_FULLSCREEN(fw, False);
4984 		if (DO_EWMH_USE_STACKING_HINTS(fw))
4985 		{
4986 			new_layer(fw, fw->ewmh_normal_layer);
4987 		}
4988 	}
4989 
4990 	/* TA:  Apply the decor change now, if the window we've just restored
4991 	 * was maximized; the client frame and geometry will be updated as a
4992 	 * result of this, so we correctly restore the window at this point.
4993 	 */
4994 	if (fw->fullscreen.was_maximized) {
4995 		fw->fullscreen.was_maximized = 0;
4996 		apply_decor_change(fw);
4997 	}
4998 
4999 	frame_setup_window(
5000 		fw, new_g.x, new_g.y, new_g.width, new_g.height, True);
5001 
5002 	xasprintf(&cmd, "MoveToScreen %s", fw->m->si->name);
5003 	execute_function_override_window(NULL, NULL, cmd, 0, fw);
5004 	free(cmd);
5005 
5006 	border_draw_decorations(
5007 		fw, PART_ALL, (Scr.Hilite == fw), True, CLEAR_ALL, NULL, NULL);
5008 
5009 	if (fw->fullscreen.is_shaded)
5010 	{
5011 		execute_function_override_window(
5012 			NULL, NULL, "WindowShade on", 0, fw);
5013 
5014 		fw->fullscreen.is_shaded = 0;
5015 	}
5016 
5017 	if (fw->fullscreen.is_iconified) {
5018 		execute_function_override_window(
5019 			NULL, NULL, "Iconify on", 0, fw);
5020 		fw->fullscreen.is_iconified = 0;
5021 	}
5022 
5023 	/* Since the window's geometry will have been constrained already when
5024 	 * coming out of fullscreen, we can always call this; either it's a
5025 	 * noop or the window will be correctly decorated in the case of the
5026 	 * window being restored from fullscreen to a non-maximized state.
5027 	 */
5028 	apply_decor_change(fw);
5029 	EWMH_SetWMState(fw, True);
5030 	return;
5031 }
5032 
maximize_fvwm_window(FvwmWindow * fw,rectangle * geometry)5033 static void maximize_fvwm_window(
5034 	FvwmWindow *fw, rectangle *geometry)
5035 {
5036 	SET_MAXIMIZED(fw, 1);
5037 	fw->g.max_defect.width = 0;
5038 	fw->g.max_defect.height = 0;
5039 	constrain_size(
5040 		fw, NULL, &geometry->width, &geometry->height, 0, 0,
5041 		CS_UPDATE_MAX_DEFECT);
5042 	fw->g.max = *geometry;
5043 	if (IS_SHADED(fw))
5044 	{
5045 		get_shaded_geometry(fw, geometry, &fw->g.max);
5046 	}
5047 	frame_setup_window(
5048 		fw, geometry->x, geometry->y, geometry->width,
5049 		geometry->height, True);
5050 	border_draw_decorations(
5051 		fw, PART_ALL, (Scr.Hilite == fw), True, CLEAR_ALL, NULL, NULL);
5052 	update_absolute_geometry(fw);
5053 	/* remember the offset between old and new position in case the
5054 	 * maximized  window is moved more than the screen width/height. */
5055 	fw->g.max_offset.x = fw->g.normal.x - fw->g.max.x;
5056 	fw->g.max_offset.y = fw->g.normal.y - fw->g.max.y;
5057 #if 0
5058 fprintf(stderr,"%d %d %d %d, g.max_offset.x = %d, g.max_offset.y = %d, %d %d %d %d\n", fw->g.max.x, fw->g.max.y, fw->g.max.width, fw->g.max.height, fw->g.max_offset.x, fw->g.max_offset.y, fw->g.normal.x, fw->g.normal.y, fw->g.normal.width, fw->g.normal.height);
5059 #endif
5060 
5061     return;
5062 }
5063 
5064 /*
5065  *
5066  *  Procedure:
5067  *      (Un)Maximize a window.
5068  *
5069  */
CMD_Maximize(F_CMD_ARGS)5070 void CMD_Maximize(F_CMD_ARGS)
5071 {
5072 	int page_x, page_y;
5073 	int val1, val2, val1_unit, val2_unit;
5074 	int toggle;
5075 	char *token;
5076 	char *taction;
5077 	Bool grow_up = False;
5078 	Bool grow_down = False;
5079 	Bool grow_left = False;
5080 	Bool grow_right = False;
5081 	Bool do_force_maximize = False;
5082 	Bool do_forget = False;
5083 	Bool is_screen_given = False;
5084 	Bool ignore_working_area = False;
5085 	Bool do_fullscreen = False;
5086 	int layers[2] = { -1, -1 };
5087 	Bool global_flag_parsed = False;
5088 	int  scr_x, scr_y;
5089 	int scr_w, scr_h;
5090 	rectangle new_g, bound;
5091 	FvwmWindow *fw = exc->w.fw;
5092 
5093 	if (
5094 		!is_function_allowed(
5095 			F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
5096 	{
5097 		XBell(dpy, 0);
5098 		return;
5099 	}
5100 	/* Check for "global" flag ("absolute" is for compatibility with E) */
5101 	while (!global_flag_parsed)
5102 	{
5103 		token = PeekToken(action, &taction);
5104 		if (!token)
5105 		{
5106 			global_flag_parsed = True;
5107 		}
5108 		else
5109 		{
5110 			if (StrEquals(token, "screen"))
5111 			{
5112 				fscreen_scr_arg arg;
5113 				arg.mouse_ev = NULL;
5114 				is_screen_given = True;
5115 				token = PeekToken(taction, &action);
5116 
5117 				arg.name = token;
5118 
5119 				FScreenGetScrRect(&arg, FSCREEN_BY_NAME,
5120 					&scr_x, &scr_y, &scr_w, &scr_h);
5121 			}
5122 			else if (StrEquals(token, "ewmhiwa"))
5123 			{
5124 				ignore_working_area = True;
5125 				action = taction;
5126 			}
5127 			else if (StrEquals(token, "growonwindowlayer"))
5128 			{
5129 				layers[0] = fw->layer;
5130 				layers[1] = fw->layer;
5131 				action = taction;
5132 			}
5133 			else if (StrEquals(token, "growonlayers"))
5134 			{
5135 				int n;
5136 
5137 				n = GetIntegerArguments(
5138 					taction, &action, layers, 2);
5139 				if (n != 2)
5140 				{
5141 					layers[0] = -1;
5142 					layers[1] = -1;
5143 				}
5144 			}
5145 			else if (StrEquals(token, "fullscreen"))
5146 			{
5147 				do_fullscreen = True;
5148 				action = taction;
5149 			}
5150 			else
5151 			{
5152 				global_flag_parsed = True;
5153 			}
5154 		}
5155 	}
5156 	toggle = ParseToggleArgument(action, &action, -1, 0);
5157 	if (do_fullscreen) {
5158 		if (toggle == -1) {
5159 			/* Flip-flop between fullscreen or not, if no toggle
5160 			 * argument is given.
5161 			 */
5162 			toggle = (IS_EWMH_FULLSCREEN(fw) ? 0 : 1);
5163 		}
5164 		if (toggle == 1 && !IS_EWMH_FULLSCREEN(fw)) {
5165 			EWMH_fullscreen(fw);
5166 			return;
5167 		}
5168 
5169 		if (toggle == 0 && IS_EWMH_FULLSCREEN(fw)) {
5170 			unmaximize_fvwm_window(fw);
5171 			return;
5172 		}
5173 		return;
5174 	} else {
5175 		if (toggle == 0 && !IS_MAXIMIZED(fw))
5176 		{
5177 			return;
5178 		}
5179 
5180 		if (toggle == 1 && IS_MAXIMIZED(fw))
5181 		{
5182 			/* Fake that the window is not maximized. */
5183 			do_force_maximize = True;
5184 		}
5185 	}
5186 
5187 	/* find the new page and geometry */
5188 	new_g.x = fw->g.frame.x;
5189 	new_g.y = fw->g.frame.y;
5190 	new_g.width = fw->g.frame.width;
5191 	new_g.height = fw->g.frame.height;
5192 	get_page_offset_check_visible(&page_x, &page_y, fw);
5193 
5194 	if (!is_screen_given)
5195 	{
5196 		fscreen_scr_arg fscr;
5197 
5198 		fscr.xypos.x = fw->g.frame.x + fw->g.frame.width  / 2 - page_x;
5199 		fscr.xypos.y = fw->g.frame.y + fw->g.frame.height / 2 - page_y;
5200 		FScreenGetScrRect(&fscr, FSCREEN_XYPOS, &scr_x, &scr_y, &scr_w,
5201 		    &scr_h);
5202 	}
5203 
5204 	if (!ignore_working_area)
5205 	{
5206 		EWMH_GetWorkAreaIntersection(
5207 			fw, &scr_x, &scr_y, &scr_w, &scr_h,
5208 			EWMH_MAXIMIZE_MODE(fw));
5209 	}
5210 #if 0
5211 	fprintf(stderr, "%s: page=(%d,%d), scr=(%d,%d, %dx%d)\n", __FUNCTION__,
5212 		page_x, page_y, scr_x, scr_y, scr_w, scr_h);
5213 #endif
5214 
5215 	/* parse first parameter */
5216 	val1_unit = scr_w;
5217 	token = PeekToken(action, &taction);
5218 	if (token && StrEquals(token, "forget"))
5219 	{
5220 		if (!IS_MAXIMIZED(fw))
5221 		{
5222 			return;
5223 		}
5224 		do_forget = True;
5225 		do_force_maximize = True;
5226 	}
5227 	else if (token && StrEquals(token, "grow"))
5228 	{
5229 		grow_left = True;
5230 		grow_right = True;
5231 		val1 = 100;
5232 		val1_unit = scr_w;
5233 	}
5234 	else if (token && StrEquals(token, "growleft"))
5235 	{
5236 		grow_left = True;
5237 		val1 = 100;
5238 		val1_unit = scr_w;
5239 	}
5240 	else if (token && StrEquals(token, "growright"))
5241 	{
5242 		grow_right = True;
5243 		val1 = 100;
5244 		val1_unit = scr_w;
5245 	}
5246 	else
5247 	{
5248 		if (GetOnePercentArgument(token, &val1, &val1_unit) == 0)
5249 		{
5250 			val1 = 100;
5251 			val1_unit = scr_w;
5252 		}
5253 		else if (val1 < 0)
5254 		{
5255 			/* handle negative offsets */
5256 			if (val1_unit == scr_w)
5257 			{
5258 				val1 = 100 + val1;
5259 			}
5260 			else
5261 			{
5262 				val1 = scr_w + val1;
5263 			}
5264 		}
5265 	}
5266 
5267 	/* parse second parameter */
5268 	val2_unit = scr_h;
5269 	token = PeekToken(taction, NULL);
5270 	if (do_forget == True)
5271 	{
5272 		/* nop */
5273 	}
5274 	else if (token && StrEquals(token, "grow"))
5275 	{
5276 		grow_up = True;
5277 		grow_down = True;
5278 		val2 = 100;
5279 		val2_unit = scr_h;
5280 	}
5281 	else if (token && StrEquals(token, "growup"))
5282 	{
5283 		grow_up = True;
5284 		val2 = 100;
5285 		val2_unit = scr_h;
5286 	}
5287 	else if (token && StrEquals(token, "growdown"))
5288 	{
5289 		grow_down = True;
5290 		val2 = 100;
5291 		val2_unit = scr_h;
5292 	}
5293 	else
5294 	{
5295 		if (GetOnePercentArgument(token, &val2, &val2_unit) == 0)
5296 		{
5297 			val2 = 100;
5298 			val2_unit = scr_h;
5299 		}
5300 		else if (val2 < 0)
5301 		{
5302 			/* handle negative offsets */
5303 			if (val2_unit == scr_h)
5304 			{
5305 				val2 = 100 + val2;
5306 			}
5307 			else
5308 			{
5309 				val2 = scr_h + val2;
5310 			}
5311 		}
5312 	}
5313 
5314 #if 0
5315 	fprintf(stderr, "%s: page=(%d,%d), scr=(%d,%d, %dx%d)\n", __FUNCTION__,
5316 		page_x, page_y, scr_x, scr_y, scr_w, scr_h);
5317 #endif
5318 
5319 	if (do_forget == True)
5320 	{
5321 		fw->g.normal = fw->g.max;
5322 		unmaximize_fvwm_window(fw);
5323 	}
5324 	else if (IS_MAXIMIZED(fw) && !do_force_maximize)
5325 	{
5326 		unmaximize_fvwm_window(fw);
5327 	}
5328 	else /* maximize */
5329 	{
5330 		/* handle command line arguments */
5331 		if (grow_up || grow_down)
5332 		{
5333 			bound = new_g;
5334 			if (grow_up)
5335 			{
5336 				bound.y = page_y + scr_y;
5337 				bound.height = new_g.y + new_g.height - bound.y;
5338 			}
5339 			if (grow_down)
5340 			{
5341 				bound.height = page_y + scr_y + scr_h - bound.y;
5342 			}
5343 			grow_to_closest_type(fw, &new_g, bound, layers,
5344 				SNAP_NONE, true);
5345 		}
5346 		else if (val2 > 0)
5347 		{
5348 			new_g.height = val2 * val2_unit / 100;
5349 			new_g.y = page_y + scr_y;
5350 		}
5351 		if (grow_left || grow_right)
5352 		{
5353 			bound = new_g;
5354 			if (grow_left)
5355 			{
5356 				bound.x = page_x + scr_x;
5357 				bound.width = new_g.x + new_g.width - bound.x;
5358 			}
5359 			if (grow_right)
5360 			{
5361 				bound.width = page_x + scr_x + scr_w - bound.x;
5362 			}
5363 			grow_to_closest_type(fw, &new_g, bound, layers,
5364 				SNAP_NONE, true);
5365 		}
5366 		else if (val1 >0)
5367 		{
5368 			new_g.width = val1 * val1_unit / 100;
5369 			new_g.x = page_x + scr_x;
5370 		}
5371 		if (val1 == 0 && val2 == 0)
5372 		{
5373 			new_g.x = page_x + scr_x;
5374 			new_g.y = page_y + scr_y;
5375 			new_g.height = scr_h;
5376 			new_g.width = scr_w;
5377 		}
5378 		/* now maximize it */
5379 		maximize_fvwm_window(fw, &new_g);
5380 	}
5381 	EWMH_SetWMState(fw, False);
5382 
5383 	return;
5384 }
5385 
5386 /*
5387  *
5388  * Same as CMD_Resize and CMD_ResizeMove, but the window ends up maximized
5389  * without touching the normal geometry.
5390  *
5391  */
CMD_ResizeMaximize(F_CMD_ARGS)5392 void CMD_ResizeMaximize(F_CMD_ARGS)
5393 {
5394 	rectangle normal_g;
5395 	rectangle max_g;
5396 	Bool was_resized;
5397 	FvwmWindow *fw = exc->w.fw;
5398 	struct monitor	*m = (fw && fw->m) ? fw->m : monitor_get_current();
5399 
5400 	/* keep a copy of the old geometry */
5401 	normal_g = fw->g.normal;
5402 	/* resize the window normally */
5403 	was_resized = __resize_window(F_PASS_ARGS);
5404 	if (was_resized == True)
5405 	{
5406 		/* set the new geometry as the maximized geometry and restore
5407 		 * the old normal geometry */
5408 		max_g = fw->g.normal;
5409 		max_g.x -= m->virtual_scr.Vx;
5410 		max_g.y -= m->virtual_scr.Vy;
5411 		fw->g.normal = normal_g;
5412 		/* and mark it as maximized */
5413 		maximize_fvwm_window(fw, &max_g);
5414 	}
5415 	EWMH_SetWMState(fw, False);
5416 	UPDATE_FVWM_SCREEN(fw);
5417 
5418 	return;
5419 }
5420 
CMD_ResizeMoveMaximize(F_CMD_ARGS)5421 void CMD_ResizeMoveMaximize(F_CMD_ARGS)
5422 {
5423 	rectangle normal_g;
5424 	rectangle max_g;
5425 	Bool was_resized;
5426 	FvwmWindow *fw = exc->w.fw;
5427 	struct monitor	*m = (fw && fw->m) ? fw->m : monitor_get_current();
5428 
5429 	/* keep a copy of the old geometry */
5430 	normal_g = fw->g.normal;
5431 	/* resize the window normally */
5432 	was_resized = resize_move_window(F_PASS_ARGS);
5433 	if (was_resized == True)
5434 	{
5435 		/* set the new geometry as the maximized geometry and restore
5436 		 * the old normal geometry */
5437 		max_g = fw->g.normal;
5438 		max_g.x -= m->virtual_scr.Vx;
5439 		max_g.y -= m->virtual_scr.Vy;
5440 		fw->g.normal = normal_g;
5441 		/* and mark it as maximized */
5442 		maximize_fvwm_window(fw, &max_g);
5443 	}
5444 	EWMH_SetWMState(fw, False);
5445 	UPDATE_FVWM_SCREEN(fw);
5446 
5447 	return;
5448 }
5449 
5450 /* ----------------------------- stick code -------------------------------- */
5451 
stick_across_pages(F_CMD_ARGS,int toggle)5452 int stick_across_pages(F_CMD_ARGS, int toggle)
5453 {
5454 	FvwmWindow *fw = exc->w.fw;
5455 
5456 	if ((toggle == 1 && IS_STICKY_ACROSS_PAGES(fw)) ||
5457 	    (toggle == 0 && !IS_STICKY_ACROSS_PAGES(fw)))
5458 	{
5459 		return 0;
5460 	}
5461 	if (IS_STICKY_ACROSS_PAGES(fw))
5462 	{
5463 		SET_STICKY_ACROSS_PAGES(fw, 0);
5464 	}
5465 	else
5466 	{
5467 		if (!IsRectangleOnThisPage(fw->m, &fw->g.frame,
5468 		    fw->m->virtual_scr.CurrentDesk))
5469 		{
5470 			action = "";
5471 			__move_window(F_PASS_ARGS, False, MOVE_PAGE);
5472 			UPDATE_FVWM_SCREEN(fw);
5473 		}
5474 		SET_STICKY_ACROSS_PAGES(fw, 1);
5475 	}
5476 
5477 	return 1;
5478 }
5479 
stick_across_desks(F_CMD_ARGS,int toggle)5480 int stick_across_desks(F_CMD_ARGS, int toggle)
5481 {
5482 	FvwmWindow *fw = exc->w.fw;
5483 
5484 	if ((toggle == 1 && IS_STICKY_ACROSS_DESKS(fw)) ||
5485 	    (toggle == 0 && !IS_STICKY_ACROSS_DESKS(fw)))
5486 	{
5487 		return 0;
5488 	}
5489 
5490 	if (IS_STICKY_ACROSS_DESKS(fw))
5491 	{
5492 		SET_STICKY_ACROSS_DESKS(fw, 0);
5493 		fw->Desk = fw->m->virtual_scr.CurrentDesk;
5494 	}
5495 	else
5496 	{
5497 		if (fw->Desk != fw->m->virtual_scr.CurrentDesk)
5498 		{
5499 			do_move_window_to_desk(fw, fw->m->virtual_scr.CurrentDesk);
5500 		}
5501 		SET_STICKY_ACROSS_DESKS(fw, 1);
5502 	}
5503 
5504 	return 1;
5505 }
5506 
__handle_stick_exit(FvwmWindow * fw,int do_not_draw,int do_silently)5507 static void __handle_stick_exit(
5508 	FvwmWindow *fw, int do_not_draw, int do_silently)
5509 {
5510 	if (do_not_draw == 0)
5511 	{
5512 		border_draw_decorations(
5513 			fw, PART_TITLE | PART_BUTTONS, (Scr.Hilite==fw), True,
5514 			CLEAR_ALL, NULL, NULL);
5515 	}
5516 	if (!do_silently)
5517 	{
5518 		BroadcastConfig(M_CONFIGURE_WINDOW,fw);
5519 		EWMH_SetWMState(fw, False);
5520 		EWMH_SetWMDesktop(fw);
5521 
5522 		desk_add_fw(fw);
5523 	}
5524 
5525 	return;
5526 }
5527 
handle_stick_across_pages(F_CMD_ARGS,int toggle,int do_not_draw,int do_silently)5528 void handle_stick_across_pages(
5529 	F_CMD_ARGS, int toggle, int do_not_draw, int do_silently)
5530 {
5531 	FvwmWindow *fw = exc->w.fw;
5532 	int did_change;
5533 
5534 	did_change = stick_across_pages(F_PASS_ARGS, toggle);
5535 	if (did_change)
5536 	{
5537 		__handle_stick_exit(fw, do_not_draw, do_silently);
5538 	}
5539 
5540 	return;
5541 }
5542 
handle_stick_across_desks(F_CMD_ARGS,int toggle,int do_not_draw,int do_silently)5543 void handle_stick_across_desks(
5544 	F_CMD_ARGS, int toggle, int do_not_draw, int do_silently)
5545 {
5546 	FvwmWindow *fw = exc->w.fw;
5547 	int did_change;
5548 
5549 	did_change = stick_across_desks(F_PASS_ARGS, toggle);
5550 	if (did_change)
5551 	{
5552 		__handle_stick_exit(fw, do_not_draw, do_silently);
5553 	}
5554 
5555 	return;
5556 }
5557 
handle_stick(F_CMD_ARGS,int toggle_page,int toggle_desk,int do_not_draw,int do_silently)5558 void handle_stick(
5559 	F_CMD_ARGS, int toggle_page, int toggle_desk, int do_not_draw,
5560 	int do_silently)
5561 {
5562 	FvwmWindow *fw = exc->w.fw;
5563 	int did_change;
5564 
5565 	did_change = 0;
5566 	did_change |= stick_across_desks(F_PASS_ARGS, toggle_desk);
5567 	did_change |= stick_across_pages(F_PASS_ARGS, toggle_page);
5568 	if (did_change)
5569 	{
5570 		__handle_stick_exit(fw, do_not_draw, do_silently);
5571 	}
5572 
5573 	return;
5574 }
5575 
CMD_Stick(F_CMD_ARGS)5576 void CMD_Stick(F_CMD_ARGS)
5577 {
5578 	int toggle;
5579 
5580 	toggle = ParseToggleArgument(action, &action, -1, 0);
5581 	if (toggle == -1 && IS_STICKY_ACROSS_DESKS(exc->w.fw) !=
5582 	    IS_STICKY_ACROSS_PAGES(exc->w.fw))
5583 	{
5584 		/* don't switch between only stickypage and only stickydesk.
5585 		 * rather switch it off completely */
5586 		toggle = 0;
5587 	}
5588 	handle_stick(F_PASS_ARGS, toggle, toggle, 0, 0);
5589 
5590 	return;
5591 }
5592 
CMD_StickAcrossPages(F_CMD_ARGS)5593 void CMD_StickAcrossPages(F_CMD_ARGS)
5594 {
5595 	int toggle;
5596 
5597 	toggle = ParseToggleArgument(action, &action, -1, 0);
5598 	handle_stick_across_pages(F_PASS_ARGS, toggle, 0, 0);
5599 
5600 	return;
5601 }
5602 
CMD_StickAcrossDesks(F_CMD_ARGS)5603 void CMD_StickAcrossDesks(F_CMD_ARGS)
5604 {
5605 	int toggle;
5606 
5607 	toggle = ParseToggleArgument(action, &action, -1, 0);
5608 	handle_stick_across_desks(F_PASS_ARGS, toggle, 0, 0);
5609 
5610 	return;
5611 }
5612