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, ¤tX, ¤tY,
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, >);
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