Really fix move parameter parsing.
[fvwm.git] / fvwm / move_resize.c
blob2acb49314496d7d85608539a334e3e43b3c909ee
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.
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.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * This module is all original code
19 * by Rob Nation
20 * Copyright 1993, Robert Nation
21 * You may use this code for any purpose, as long as the original
22 * copyright remains in the source code and all documentation
27 * code for moving and resizing windows
31 #include "config.h"
33 #include <stdio.h>
34 #include <math.h>
35 #include <X11/keysym.h>
37 #include "libs/fvwmlib.h"
38 #include "libs/FScreen.h"
39 #include "libs/Picture.h"
40 #include "libs/Grab.h"
41 #include "libs/Parse.h"
42 #include "libs/Graphics.h"
43 #include "fvwm.h"
44 #include "externs.h"
45 #include "cursor.h"
46 #include "execcontext.h"
47 #include "commands.h"
48 #include "misc.h"
49 #include "screen.h"
50 #include "menus.h"
51 #include "menuparameters.h"
52 #include "module_list.h"
53 #include "module_interface.h"
54 #include "focus.h"
55 #include "borders.h"
56 #include "frame.h"
57 #include "geometry.h"
58 #include "gnome.h"
59 #include "ewmh.h"
60 #include "virtual.h"
61 #include "decorations.h"
62 #include "events.h"
63 #include "eventhandler.h"
64 #include "eventmask.h"
65 #include "colormaps.h"
66 #include "update.h"
67 #include "stack.h"
68 #include "move_resize.h"
69 #include "functions.h"
70 #include "style.h"
72 /* ----- move globals ----- */
74 #define MOVE_NORMAL 0x00
75 #define MOVE_PAGE 0x01
76 #define MOVE_SCREEN 0x02
78 /* Animated move stuff added by Greg J. Badros, gjb@cs.washington.edu */
80 float rgpctMovementDefault[32] =
82 -.01, 0, .01, .03,.08,.18,.3,.45,.60,.75,.85,.90,.94,.97,.99,1.0
83 /* must end in 1.0 */
85 int cmsDelayDefault = 10; /* milliseconds */
87 /* current geometry of size window */
88 static rectangle sizew_g =
90 -1,
91 -1,
92 -1,
96 static int move_interactive_finish_button_mask =
97 ((1<<(NUMBER_OF_EXTENDED_MOUSE_BUTTONS))-1) & ~0x2;
98 static int move_drag_finish_button_mask =
99 ((1<<(NUMBER_OF_EXTENDED_MOUSE_BUTTONS))-1) & ~0x3;
101 /* ----- end of move globals ----- */
103 /* ----- resize globals ----- */
105 /* DO NOT USE (STATIC) GLOBALS IN THIS MODULE!
106 * Since some functions are called from other modules unwanted side effects
107 * (i.e. bugs.) would be created */
109 extern Window PressedW;
111 static void draw_move_resize_grid(int x, int y, int width, int height);
113 /* ----- end of resize globals ----- */
117 * Procedure:
118 * draw_move_resize_grid - move a window outline
120 * Inputs:
121 * root - the window we are outlining
122 * x - upper left x coordinate
123 * y - upper left y coordinate
124 * width - the width of the rectangle
125 * height - the height of the rectangle
128 static int get_outline_rects(
129 XRectangle *rects, int x, int y, int width, int height)
131 int i;
132 int n;
133 int m;
135 n = 3;
136 m = (width - 5) / 2;
137 if (m < n)
139 n = m;
141 m = (height - 5) / 2;
142 if (m < n)
144 n = m;
146 if (n < 1)
148 n = 1;
151 for (i = 0; i < n; i++)
153 rects[i].x = x + i;
154 rects[i].y = y + i;
155 rects[i].width = width - (i << 1);
156 rects[i].height = height - (i << 1);
158 if (width - (n << 1) >= 5 && height - (n << 1) >= 5)
160 if (width - (n << 1) >= 10)
162 int off = (width - (n << 1)) / 3 + n;
163 rects[i].x = x + off;
164 rects[i].y = y + n;
165 rects[i].width = width - (off << 1);
166 rects[i].height = height - (n << 1);
167 i++;
169 if (height - (n << 1) >= 10)
171 int off = (height - (n << 1)) / 3 + n;
172 rects[i].x = x + n;
173 rects[i].y = y + off;
174 rects[i].width = width - (n << 1);
175 rects[i].height = height - (off << 1);
176 i++;
180 return i;
183 struct
185 rectangle geom;
186 struct
188 unsigned is_enabled : 1;
189 } flags;
190 } move_resize_grid =
192 { 0, 0, 0, 0 },
193 { 0 }
196 static void draw_move_resize_grid(int x, int y, int width, int height)
198 int nrects = 0;
199 XRectangle rects[10];
201 if (move_resize_grid.flags.is_enabled &&
202 x == move_resize_grid.geom.x &&
203 y == move_resize_grid.geom.y &&
204 width == move_resize_grid.geom.width &&
205 height == move_resize_grid.geom.height)
207 return;
210 memset(rects, 0, 10 * sizeof(XRectangle));
211 /* place the resize rectangle into the array of rectangles */
212 /* interleave them for best visual look */
213 /* draw the new one, if any */
214 if (move_resize_grid.flags.is_enabled
215 /*move_resize_grid.geom.width && move_resize_grid.geom.height*/)
217 move_resize_grid.flags.is_enabled = 0;
218 nrects +=
219 get_outline_rects(
220 &(rects[0]), move_resize_grid.geom.x,
221 move_resize_grid.geom.y,
222 move_resize_grid.geom.width,
223 move_resize_grid.geom.height);
225 if (width && height)
227 move_resize_grid.flags.is_enabled = 1;
228 move_resize_grid.geom.x = x;
229 move_resize_grid.geom.y = y;
230 move_resize_grid.geom.width = width;
231 move_resize_grid.geom.height = height;
232 nrects += get_outline_rects(
233 &(rects[nrects]), x, y, width, height);
235 if (nrects > 0)
237 XDrawRectangles(dpy, Scr.Root, Scr.XorGC, rects, nrects);
238 XFlush(dpy);
241 return;
244 void switch_move_resize_grid(Bool state)
246 if (state == False)
248 if (move_resize_grid.flags.is_enabled)
250 draw_move_resize_grid(0, 0, 0, 0);
252 else
254 move_resize_grid.geom.x = 0;
255 move_resize_grid.geom.y = 0;
256 move_resize_grid.geom.width = 0;
257 move_resize_grid.geom.height = 0;
260 else if (!move_resize_grid.flags.is_enabled)
262 if (move_resize_grid.geom.width &&
263 move_resize_grid.geom.height)
265 draw_move_resize_grid(
266 move_resize_grid.geom.x,
267 move_resize_grid.geom.y,
268 move_resize_grid.geom.width,
269 move_resize_grid.geom.height);
273 return;
276 static int ParsePositionArgumentSuffix(
277 float *ret_factor, char *suffix, float wfactor, float sfactor)
279 int n;
281 switch (*suffix)
283 case 'p':
284 case 'P':
285 *ret_factor = 1.0;
286 n = 1;
287 break;
288 case 'w':
289 case 'W':
290 *ret_factor = wfactor;
291 n = 1;
292 break;
293 default:
294 *ret_factor = sfactor;
295 n = 0;
296 break;
299 return n;
302 static int __get_shift(int val, float factor)
304 int shift;
306 if (val >= 0)
308 shift = (int)(val * factor + 0.5);
310 else
312 shift = (int)(val * factor - 0.5);
315 return shift;
318 /* The vars are named for the x-direction, but this is used for both x and y */
319 static int GetOnePositionArgument(
320 char *s1, int window_pos, int window_size, int *pFinalPos,
321 float sfactor, int screen_size, int screen_pos, Bool is_x)
323 int final_pos;
324 float wfactor;
326 if (s1 == 0 || *s1 == 0)
328 return 0;
330 wfactor = (float)window_size / 100;
331 /* get start position */
332 switch (*s1)
334 case 'w':
335 case 'W':
336 final_pos = window_pos;
337 s1++;
338 break;
339 case 'm':
340 case 'M':
342 int x;
343 int y;
345 if (
346 FQueryPointer(
347 dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX,
348 &JunkY, &x, &y, &JunkMask) == False)
350 /* pointer is on a different screen - that's okay here
352 final_pos = 0;
354 else
356 final_pos = (is_x) ? x : y;
358 s1++;
359 break;
361 default:
362 final_pos = screen_pos;
363 if (*s1 != 0)
365 int val;
366 int n;
367 float f;
369 /* parse value */
370 if (sscanf(s1, "-%d%n", &val, &n) >= 1)
372 /* i.e. -1, -+1 or --1 */
373 final_pos += (screen_size - window_size);
374 val = -val;
376 else if (
377 sscanf(s1, "+%d%n", &val, &n) >= 1 ||
378 sscanf(s1, "%d%n", &val, &n) >= 1)
380 /* i.e. 1, +1, ++1 or +-1 */
382 else
384 /* syntax error, ignore rest of string */
385 break;
387 s1 += n;
388 /* parse suffix */
389 n = ParsePositionArgumentSuffix(
390 &f, s1, wfactor, sfactor);
391 s1 += n;
392 final_pos += __get_shift(val, f);
394 break;
396 /* loop over shift arguments */
397 while (*s1 != 0)
399 int val;
400 int n;
401 float f;
403 /* parse value */
404 if (sscanf(s1, "-%d%n", &val, &n) >= 1)
406 /* i.e. -1, -+1 or --1 */
407 val = -val;
409 else if (
410 sscanf(s1, "+%d%n", &val, &n) >= 1 ||
411 sscanf(s1, "%d%n", &val, &n) >= 1)
413 /* i.e. 1, +1, ++1 or +-1 */
415 else
417 /* syntax error, ignore rest of string */
418 break;
420 s1 += n;
421 /* parse suffix */
422 n = ParsePositionArgumentSuffix(&f, s1, wfactor, sfactor);
423 s1 += n;
424 final_pos += __get_shift(val, f);
426 *pFinalPos = final_pos;
428 return 1;
431 /* GetMoveArguments is used for Move & AnimatedMove
432 * It lets you specify in all the following ways
433 * 20 30 Absolute percent position, from left edge and top
434 * -50 50 Absolute percent position, from right edge and top
435 * 10p 5p Absolute pixel position
436 * 10p -0p Absolute pixel position, from bottom
437 * w+5 w-10p Relative position, right 5%, up ten pixels
438 * m+5 m-10p Pointer relative position, right 5%, up ten pixels
439 * Returns 2 when x & y have parsed without error, 0 otherwise
441 int GetMoveArguments(
442 char **paction, int w, int h, int *pFinalX, int *pFinalY,
443 Bool *fWarp, Bool *fPointer, Bool fKeep)
445 char *s1 = NULL;
446 char *s2 = NULL;
447 char *warp = NULL;
448 char *action;
449 char *naction;
450 int scr_x = 0;
451 int scr_y = 0;
452 int scr_w = Scr.MyDisplayWidth;
453 int scr_h = Scr.MyDisplayHeight;
454 int retval = 0;
456 if (!paction)
458 return 0;
460 action = *paction;
461 action = GetNextToken(action, &s1);
462 if (s1 && fPointer && StrEquals(s1, "pointer"))
464 *fPointer = True;
465 *paction = action;
466 free(s1);
467 return 0;
469 if (s1 && StrEquals(s1, "screen"))
471 char *token;
472 int scr;
473 fscreen_scr_arg arg;
474 fscreen_scr_arg* parg;
476 free(s1);
477 token = PeekToken(action, &action);
478 scr = FScreenGetScreenArgument(token, FSCREEN_SPEC_PRIMARY);
479 if (scr == FSCREEN_XYPOS)
481 arg.xypos.x = *pFinalX;
482 arg.xypos.y = *pFinalY;
483 parg = &arg;
485 else
487 parg = NULL;
489 FScreenGetScrRect(parg, scr, &scr_x, &scr_y, &scr_w, &scr_h);
490 action = GetNextToken(action, &s1);
492 action = GetNextToken(action, &s2);
493 if (fWarp)
495 warp = PeekToken(action, &naction);
496 if (StrEquals(warp, "Warp"))
498 *fWarp = True;
499 action = naction;
503 if (s1 != NULL && s2 != NULL)
505 retval = 0;
506 if (fKeep == True && StrEquals(s1, "keep"))
508 retval++;
510 else if (
511 GetOnePositionArgument(
512 s1, *pFinalX, w, pFinalX, (float)scr_w / 100,
513 scr_w, scr_x, True))
515 retval++;
517 if (fKeep == True && StrEquals(s2, "keep"))
519 retval++;
521 else if (
522 GetOnePositionArgument(
523 s2, *pFinalY, h, pFinalY, (float)scr_h / 100,
524 scr_h, scr_y, False))
526 retval++;
528 if (retval == 0)
530 /* make sure warping is off for interactive moves */
531 *fWarp = False;
534 else
536 /* not enough arguments, switch to current page. */
537 while (*pFinalX < 0)
539 *pFinalX = Scr.MyDisplayWidth + *pFinalX;
541 while (*pFinalY < 0)
543 *pFinalY = Scr.MyDisplayHeight + *pFinalY;
547 if (s1)
549 free(s1);
551 if (s2)
553 free(s2);
555 *paction = action;
557 return retval;
560 static int ParseOneResizeArgument(
561 char *arg, int scr_size, int base_size, int size_inc, int add_size,
562 int *ret_size)
564 float factor;
565 int val;
566 int add_base_size = 0;
567 int cch = strlen(arg);
568 int tmp_size;
570 if (cch == 0)
572 return 0;
574 if (StrEquals(arg, "keep"))
576 /* do not change size */
577 return 1;
579 if (arg[cch-1] == 'p')
581 factor = 1;
582 arg[cch-1] = '\0';
584 else if (arg[cch-1] == 'c')
586 factor = size_inc;
587 add_base_size = base_size;
588 arg[cch-1] = '\0';
590 else
592 factor = (float)scr_size / 100.0;
594 if (strcmp(arg,"w") == 0)
596 /* do not change size */
598 else if (sscanf(arg,"w-%d",&val) == 1)
600 tmp_size = (int)(val * factor + 0.5);
601 if (tmp_size < *ret_size)
603 *ret_size -= tmp_size;
605 else
607 *ret_size = 0;
610 else if (sscanf(arg,"w+%d",&val) == 1 || sscanf(arg,"w%d",&val) == 1)
612 tmp_size = (int)(val * factor + 0.5);
613 if (-tmp_size < *ret_size)
615 *ret_size += tmp_size;
617 else
619 *ret_size = 0;
622 else if (sscanf(arg,"-%d",&val) == 1)
624 tmp_size = (int)(val * factor + 0.5);
625 if (tmp_size < scr_size + add_size)
627 *ret_size = scr_size - tmp_size + add_size;
629 else
631 *ret_size = 0;
634 else if (sscanf(arg,"+%d",&val) == 1 || sscanf(arg,"%d",&val) == 1)
636 tmp_size = (int)(val * factor + 0.5);
637 if (-tmp_size < add_size + add_base_size)
639 *ret_size = tmp_size + add_size + add_base_size;
641 else
643 *ret_size = 0;
646 else
648 return 0;
651 return 1;
654 static int GetResizeArguments(
655 char **paction, int x, int y, int w_base, int h_base, int w_inc,
656 int h_inc, size_borders *sb, int *pFinalW, int *pFinalH,
657 direction_t *ret_dir, Bool *is_direction_fixed,
658 Bool *do_warp_to_border)
660 int n;
661 char *naction;
662 char *token;
663 char *s1;
664 char *s2;
665 int w_add;
666 int h_add;
667 int has_frame_option;
669 *ret_dir = DIR_NONE;
670 *is_direction_fixed = False;
671 *do_warp_to_border = False;
672 if (!paction)
674 return 0;
676 token = PeekToken(*paction, &naction);
677 if (!token)
679 return 0;
681 if (StrEquals(token, "bottomright") || StrEquals(token, "br"))
683 int nx = x + *pFinalW - 1;
684 int ny = y + *pFinalH - 1;
686 n = GetMoveArguments(
687 &naction, 0, 0, &nx, &ny, NULL, NULL, True);
688 if (n < 2)
690 return 0;
692 *pFinalW = nx - x + 1;
693 *pFinalH = ny - y + 1;
694 *paction = naction;
696 return n;
698 has_frame_option = 0;
699 for ( ; ; token = PeekToken(naction, &naction))
701 if (StrEquals(token, "frame"))
703 has_frame_option = 1;
705 else if (StrEquals(token, "direction"))
707 if (token == NULL)
709 return 0;
711 *ret_dir = gravity_parse_dir_argument(
712 naction, &naction, DIR_NONE);
713 if (*ret_dir != DIR_NONE)
715 *is_direction_fixed = True;
718 else if (StrEquals(token, "fixeddirection"))
720 *is_direction_fixed = True;
722 else if (StrEquals(token, "warptoborder"))
724 *do_warp_to_border = True;
726 else
728 break;
731 if (has_frame_option)
733 w_add = 0;
734 h_add = 0;
736 else
738 w_add = sb->total_size.width;
739 h_add = sb->total_size.height;
741 s1 = NULL;
742 if (token != NULL)
744 s1 = safestrdup(token);
746 naction = GetNextToken(naction, &s2);
747 if (!s2)
749 if (s1 != NULL)
751 free(s1);
753 return 0;
755 *paction = naction;
757 n = 0;
758 n += ParseOneResizeArgument(
759 s1, Scr.MyDisplayWidth, w_base, w_inc, w_add, pFinalW);
760 n += ParseOneResizeArgument(
761 s2, Scr.MyDisplayHeight, h_base, h_inc, h_add, pFinalH);
762 if (s1 != NULL)
764 free(s1);
766 if (s2 != NULL)
768 free(s2);
770 if (n < 2)
772 n = 0;
775 return n;
778 static int GetResizeMoveArguments(
779 char **paction, int w_base, int h_base, int w_inc, int h_inc,
780 size_borders *sb, int *pFinalX, int *pFinalY,
781 int *pFinalW, int *pFinalH, Bool *fWarp, Bool *fPointer)
783 char *action = *paction;
784 direction_t dir;
785 Bool dummy;
787 if (!paction)
789 return 0;
791 if (GetResizeArguments(
792 &action, *pFinalX, *pFinalY, w_base, h_base, w_inc, h_inc,
793 sb, pFinalW, pFinalH, &dir, &dummy, &dummy) < 2)
795 return 0;
797 if (GetMoveArguments(
798 &action, *pFinalW, *pFinalH, pFinalX, pFinalY, fWarp,
799 NULL, True) < 2)
801 return 0;
803 *paction = action;
805 return 4;
808 /* Positions the SizeWindow on the current ("moused") xinerama-screen */
809 static void position_geometry_window(const XEvent *eventp)
811 int x;
812 int y;
813 fscreen_scr_arg fscr;
815 fscr.mouse_ev = (XEvent *)eventp;
816 /* Probably should remove this positioning code from {builtins,fvwm}.c?
818 if (Scr.gs.do_emulate_mwm)
820 FScreenCenterOnScreen(
821 &fscr, FSCREEN_CURRENT, &x, &y, sizew_g.width,
822 sizew_g.height);
824 else
826 FScreenGetScrRect(&fscr, FSCREEN_CURRENT, &x, &y, NULL, NULL);
828 if (x != sizew_g.x || y != sizew_g.y)
830 switch_move_resize_grid(False);
831 XMoveWindow(dpy, Scr.SizeWindow, x, y);
832 switch_move_resize_grid(True);
833 sizew_g.x = x;
834 sizew_g.y = y;
837 return;
840 void resize_geometry_window(void)
842 int w;
843 int h;
844 int cset = Scr.DefaultColorset;
846 Scr.SizeStringWidth =
847 FlocaleTextWidth(Scr.DefaultFont, GEOMETRY_WINDOW_STRING,
848 sizeof(GEOMETRY_WINDOW_STRING) - 1);
849 w = Scr.SizeStringWidth + 2 * GEOMETRY_WINDOW_BW;
850 h = Scr.DefaultFont->height + 2 * GEOMETRY_WINDOW_BW;
851 if (w != sizew_g.width || h != sizew_g.height)
853 XResizeWindow(dpy, Scr.SizeWindow, w, h);
854 sizew_g.width = w;
855 sizew_g.height = h;
857 if (cset >= 0)
859 SetWindowBackground(
860 dpy, Scr.SizeWindow, w, h, &Colorset[cset], Pdepth,
861 Scr.StdGC, False);
863 else
865 XSetWindowBackground(dpy, Scr.SizeWindow, Scr.StdBack);
868 return;
873 * Procedure:
874 * DisplayPosition - display the position in the dimensions window
876 * Inputs:
877 * tmp_win - the current fvwm window
878 * x, y - position of the window
882 static void DisplayPosition(
883 const FvwmWindow *tmp_win, const XEvent *eventp, int x, int y,int Init)
885 char str[100];
886 int offset;
887 fscreen_scr_arg fscr;
888 FlocaleWinString fstr;
890 if (Scr.gs.do_hide_position_window)
892 return;
894 position_geometry_window(eventp);
895 /* Translate x,y into local screen coordinates,
896 * in case Xinerama is used. */
897 fscr.xypos.x = x;
898 fscr.xypos.y = y;
899 FScreenTranslateCoordinates(
900 NULL, FSCREEN_GLOBAL, &fscr, FSCREEN_XYPOS, &x, &y);
901 (void)sprintf(str, GEOMETRY_WINDOW_POS_STRING, x, y);
902 if (Init)
904 XClearWindow(dpy, Scr.SizeWindow);
906 else
908 /* just clear indside the relief lines to reduce flicker */
909 XClearArea(dpy, Scr.SizeWindow,
910 GEOMETRY_WINDOW_BW, GEOMETRY_WINDOW_BW,
911 Scr.SizeStringWidth, Scr.DefaultFont->height, False);
914 if (Pdepth >= 2)
916 RelieveRectangle(
917 dpy, Scr.SizeWindow, 0, 0,
918 Scr.SizeStringWidth + GEOMETRY_WINDOW_BW * 2 - 1,
919 Scr.DefaultFont->height + GEOMETRY_WINDOW_BW * 2 - 1,
920 Scr.StdReliefGC, Scr.StdShadowGC, GEOMETRY_WINDOW_BW);
922 offset = (Scr.SizeStringWidth -
923 FlocaleTextWidth(Scr.DefaultFont, str, strlen(str))) / 2;
924 offset += GEOMETRY_WINDOW_BW;
926 memset(&fstr, 0, sizeof(fstr));
927 if (Scr.DefaultColorset >= 0)
929 fstr.colorset = &Colorset[Scr.DefaultColorset];
930 fstr.flags.has_colorset = True;
932 fstr.str = str;
933 fstr.win = Scr.SizeWindow;
934 fstr.gc = Scr.StdGC;
935 fstr.x = offset;
936 fstr.y = Scr.DefaultFont->ascent + GEOMETRY_WINDOW_BW;
937 FlocaleDrawString(dpy, Scr.DefaultFont, &fstr, 0);
939 return;
945 * Procedure:
946 * DisplaySize - display the size in the dimensions window
948 * Inputs:
949 * tmp_win - the current fvwm window
950 * width - the width of the rubber band
951 * height - the height of the rubber band
954 static void DisplaySize(
955 const FvwmWindow *tmp_win, const XEvent *eventp, int width,
956 int height, Bool Init, Bool resetLast)
958 char str[100];
959 int dwidth,dheight,offset;
960 size_borders b;
961 static int last_width = 0;
962 static int last_height = 0;
963 FlocaleWinString fstr;
965 if (Scr.gs.do_hide_resize_window)
967 return;
969 position_geometry_window(eventp);
970 if (resetLast)
972 last_width = 0;
973 last_height = 0;
975 if (last_width == width && last_height == height)
977 return;
979 last_width = width;
980 last_height = height;
982 get_window_borders(tmp_win, &b);
983 dheight = height - b.total_size.height;
984 dwidth = width - b.total_size.width;
985 dwidth -= tmp_win->hints.base_width;
986 dheight -= tmp_win->hints.base_height;
987 dwidth /= tmp_win->hints.width_inc;
988 dheight /= tmp_win->hints.height_inc;
990 (void)sprintf(str, GEOMETRY_WINDOW_SIZE_STRING, dwidth, dheight);
991 if (Init)
993 XClearWindow(dpy,Scr.SizeWindow);
995 else
997 /* just clear indside the relief lines to reduce flicker */
998 XClearArea(
999 dpy, Scr.SizeWindow, GEOMETRY_WINDOW_BW,
1000 GEOMETRY_WINDOW_BW, Scr.SizeStringWidth,
1001 Scr.DefaultFont->height, False);
1004 if (Pdepth >= 2)
1006 RelieveRectangle(
1007 dpy, Scr.SizeWindow, 0, 0,
1008 Scr.SizeStringWidth + GEOMETRY_WINDOW_BW * 2 - 1,
1009 Scr.DefaultFont->height + GEOMETRY_WINDOW_BW*2 - 1,
1010 Scr.StdReliefGC, Scr.StdShadowGC, GEOMETRY_WINDOW_BW);
1012 offset = (Scr.SizeStringWidth -
1013 FlocaleTextWidth(Scr.DefaultFont, str, strlen(str))) / 2;
1014 offset += GEOMETRY_WINDOW_BW;
1015 memset(&fstr, 0, sizeof(fstr));
1016 if (Scr.DefaultColorset >= 0)
1018 fstr.colorset = &Colorset[Scr.DefaultColorset];
1019 fstr.flags.has_colorset = True;
1021 fstr.str = str;
1022 fstr.win = Scr.SizeWindow;
1023 fstr.gc = Scr.StdGC;
1024 fstr.x = offset;
1025 fstr.y = Scr.DefaultFont->ascent + GEOMETRY_WINDOW_BW;
1026 FlocaleDrawString(dpy, Scr.DefaultFont, &fstr, 0);
1028 return;
1031 static Bool resize_move_window(F_CMD_ARGS)
1033 int FinalX = 0;
1034 int FinalY = 0;
1035 int FinalW = 0;
1036 int FinalH = 0;
1037 int n;
1038 int x,y;
1039 Bool fWarp = False;
1040 Bool fPointer = False;
1041 Bool has_focus;
1042 int dx;
1043 int dy;
1044 size_borders b;
1045 FvwmWindow *fw = exc->w.fw;
1046 Window w = exc->w.w;
1048 if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
1050 return False;
1052 if (!is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True))
1054 return False;
1057 /* gotta have a window */
1058 w = FW_W_FRAME(fw);
1059 if (!XGetGeometry(
1060 dpy, w, &JunkRoot, &x, &y, (unsigned int*)&FinalW,
1061 (unsigned int*)&FinalH, (unsigned int*)&JunkBW,
1062 (unsigned int*)&JunkDepth))
1064 XBell(dpy, 0);
1065 return False;
1068 FinalX = x;
1069 FinalY = y;
1071 get_window_borders(fw, &b);
1072 n = GetResizeMoveArguments(
1073 &action,
1074 fw->hints.base_width, fw->hints.base_height,
1075 fw->hints.width_inc, fw->hints.height_inc,
1076 &b, &FinalX, &FinalY, &FinalW, &FinalH, &fWarp, &fPointer);
1077 if (n < 4)
1079 return False;
1082 if (IS_MAXIMIZED(fw))
1084 /* must redraw the buttons now so that the 'maximize' button
1085 * does not stay depressed. */
1086 SET_MAXIMIZED(fw, 0);
1087 border_draw_decorations(
1088 fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
1089 NULL, NULL);
1091 dx = FinalX - fw->g.frame.x;
1092 dy = FinalY - fw->g.frame.y;
1093 /* size will be less or equal to requested */
1094 constrain_size(fw, NULL, &FinalW, &FinalH, 0, 0, 0);
1095 if (IS_SHADED(fw))
1097 frame_setup_window(
1098 fw, FinalX, FinalY, FinalW, fw->g.frame.height, False);
1100 else
1102 frame_setup_window(fw, FinalX, FinalY, FinalW, FinalH, True);
1104 if (fWarp)
1106 FWarpPointer(
1107 dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y);
1109 if (IS_MAXIMIZED(fw))
1111 fw->g.max.x += dx;
1112 fw->g.max.y += dy;
1114 else
1116 fw->g.normal.x += dx;
1117 fw->g.normal.y += dy;
1119 has_focus = (fw == get_focus_window())? True : False;
1120 update_absolute_geometry(fw);
1121 maximize_adjust_offset(fw);
1122 XFlush(dpy);
1123 GNOME_SetWinArea(fw);
1125 return True;
1128 void CMD_ResizeMove(F_CMD_ARGS)
1130 FvwmWindow *fw = exc->w.fw;
1132 if (IS_EWMH_FULLSCREEN(fw))
1134 /* do not unmaximize ! */
1135 CMD_ResizeMoveMaximize(F_PASS_ARGS);
1136 return;
1138 resize_move_window(F_PASS_ARGS);
1140 return;
1143 static void InteractiveMove(
1144 Window *win, const exec_context_t *exc, int *FinalX, int *FinalY,
1145 Bool do_start_at_pointer)
1147 int origDragX,origDragY,DragX, DragY, DragWidth, DragHeight;
1148 int XOffset, YOffset;
1149 Window w;
1150 Bool do_move_opaque = False;
1152 w = *win;
1154 if (Scr.bo.do_install_root_cmap)
1156 InstallRootColormap();
1158 else
1160 InstallFvwmColormap();
1162 /* warp the pointer to the cursor position from before menu appeared */
1163 /* domivogt (17-May-1999): an XFlush should not hurt anyway, so do it
1164 * unconditionally to remove the external */
1165 XFlush(dpy);
1167 if (do_start_at_pointer)
1169 if (FQueryPointer(
1170 dpy, Scr.Root, &JunkRoot, &JunkChild, &DragX,
1171 &DragY, &JunkX, &JunkY, &JunkMask) == False)
1173 /* pointer is on a different screen */
1174 DragX = 0;
1175 DragY = 0;
1178 else
1180 /* Although a move is usually done with a button depressed we
1181 * have to check for ButtonRelease too since the event may be
1182 * faked. */
1183 fev_get_evpos_or_query(
1184 dpy, Scr.Root, exc->x.elast, &DragX, &DragY);
1187 MyXGrabServer(dpy);
1188 if (!XGetGeometry(
1189 dpy, w, &JunkRoot, &origDragX, &origDragY,
1190 (unsigned int*)&DragWidth, (unsigned int*)&DragHeight,
1191 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1193 MyXUngrabServer(dpy);
1194 return;
1196 MyXGrabKeyboard(dpy);
1197 if (do_start_at_pointer)
1199 origDragX = DragX;
1200 origDragY = DragY;
1203 if (IS_ICONIFIED(exc->w.fw))
1205 do_move_opaque = True;
1207 else if (IS_MAPPED(exc->w.fw))
1209 float areapct;
1211 areapct = 100.0;
1212 areapct *= ((float)DragWidth / (float)Scr.MyDisplayWidth);
1213 areapct *= ((float)DragHeight / (float)Scr.MyDisplayHeight);
1214 /* round up */
1215 areapct += 0.1;
1216 if (Scr.OpaqueSize < 0 ||
1217 (float)areapct <= (float)Scr.OpaqueSize)
1219 do_move_opaque = True;
1222 if (do_move_opaque)
1224 MyXUngrabServer(dpy);
1226 else
1228 Scr.flags.is_wire_frame_displayed = True;
1231 if (!do_move_opaque && IS_ICONIFIED(exc->w.fw))
1233 XUnmapWindow(dpy,w);
1236 XOffset = origDragX - DragX;
1237 YOffset = origDragY - DragY;
1238 if (!Scr.gs.do_hide_position_window)
1240 position_geometry_window(NULL);
1241 XMapRaised(dpy,Scr.SizeWindow);
1243 __move_loop(
1244 exc, XOffset, YOffset, DragWidth, DragHeight, FinalX, FinalY,
1245 do_move_opaque, CRS_MOVE);
1246 if (!Scr.gs.do_hide_position_window)
1248 XUnmapWindow(dpy,Scr.SizeWindow);
1250 if (Scr.bo.do_install_root_cmap)
1252 UninstallRootColormap();
1254 else
1256 UninstallFvwmColormap();
1259 if (!do_move_opaque)
1261 /* Throw away some events that dont interest us right now. */
1262 discard_events(EnterWindowMask|LeaveWindowMask);
1263 Scr.flags.is_wire_frame_displayed = False;
1264 MyXUngrabServer(dpy);
1266 MyXUngrabKeyboard(dpy);
1268 return;
1271 /* Perform the movement of the window. ppctMovement *must* have a 1.0 entry
1272 * somewhere in ins list of floats, and movement will stop when it hits a 1.0
1273 * entry */
1274 static void AnimatedMoveAnyWindow(
1275 FvwmWindow *fw, Window w, int startX, int startY, int endX,
1276 int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement,
1277 MenuRepaintTransparentParameters *pmrtp)
1279 int pointerX, pointerY;
1280 int currentX, currentY;
1281 int lastX, lastY;
1282 int deltaX, deltaY;
1283 Bool first = True;
1284 XEvent evdummy;
1285 unsigned int draw_parts = PART_NONE;
1287 if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
1289 return;
1292 /* set our defaults */
1293 if (ppctMovement == NULL)
1295 ppctMovement = rgpctMovementDefault;
1297 if (cmsDelay < 0)
1299 cmsDelay = cmsDelayDefault;
1302 if (startX < 0 || startY < 0)
1304 if (
1305 !XGetGeometry(
1306 dpy, w, &JunkRoot, &currentX, &currentY,
1307 (unsigned int*)&JunkWidth,
1308 (unsigned int*)&JunkHeight,
1309 (unsigned int*)&JunkBW,
1310 (unsigned int*)&JunkDepth))
1312 XBell(dpy, 0);
1313 return;
1315 if (startX < 0)
1317 startX = currentX;
1319 if (startY < 0)
1321 startY = currentY;
1325 deltaX = endX - startX;
1326 deltaY = endY - startY;
1327 lastX = startX;
1328 lastY = startY;
1330 if (deltaX == 0 && deltaY == 0)
1332 /* go nowhere fast */
1333 return;
1336 if (fw && w == FW_W_FRAME(fw))
1338 draw_parts = border_get_transparent_decorations_part(fw);
1341 /* Needed for aborting */
1342 MyXGrabKeyboard(dpy);
1345 currentX = startX + deltaX * (*ppctMovement);
1346 currentY = startY + deltaY * (*ppctMovement);
1347 if (lastX == currentX && lastY == currentY)
1349 /* don't waste time in the same spot */
1350 continue;
1352 if (pmrtp != NULL)
1354 update_transparent_menu_bg(
1355 pmrtp, lastX, lastY, currentX, currentY,
1356 endX, endY);
1358 XMoveWindow(dpy,w,currentX,currentY);
1359 if (pmrtp != NULL)
1361 repaint_transparent_menu(
1362 pmrtp, first,
1363 currentX, currentY, endX, endY, True);
1365 else if (draw_parts != PART_NONE)
1367 border_draw_decorations(
1368 fw, draw_parts,
1369 ((fw == get_focus_window())) ?
1370 True : False,
1371 True, CLEAR_ALL, NULL, NULL);
1373 if (fw && pmrtp == NULL && IS_TEAR_OFF_MENU(fw))
1375 menu_redraw_transparent_tear_off_menu(fw, False);
1377 if (fWarpPointerToo == True)
1379 if (FQueryPointer(
1380 dpy, Scr.Root, &JunkRoot, &JunkChild,
1381 &JunkX, &JunkY, &pointerX, &pointerY,
1382 &JunkMask) == False)
1384 /* pointer is on a different screen */
1385 pointerX = currentX;
1386 pointerY = currentY;
1388 else
1390 pointerX += currentX - lastX;
1391 pointerY += currentY - lastY;
1393 FWarpPointer(
1394 dpy, None, Scr.Root, 0, 0, 0, 0, pointerX,
1395 pointerY);
1397 if (fw && !IS_SHADED(fw) && !Scr.bo.do_disable_configure_notify)
1399 /* send configure notify event for windows that care
1400 * about their location */
1401 SendConfigureNotify(
1402 fw, currentX, currentY,
1403 fw->g.frame.width,
1404 fw->g.frame.height, 0, False);
1405 #ifdef FVWM_DEBUG_MSGS
1406 fvwm_msg(DBG,"AnimatedMoveAnyWindow",
1407 "Sent ConfigureNotify (w == %d, h == %d)",
1408 fw->g.frame.width,
1409 fw->g.frame.height);
1410 #endif
1412 XFlush(dpy);
1413 if (fw)
1415 fw->g.frame.x = currentX;
1416 fw->g.frame.y = currentY;
1417 update_absolute_geometry(fw);
1418 maximize_adjust_offset(fw);
1419 BroadcastConfig(M_CONFIGURE_WINDOW, fw);
1420 FlushAllMessageQueues();
1423 usleep(cmsDelay * 1000); /* usleep takes microseconds */
1424 /* this didn't work for me -- maybe no longer necessary since
1425 * we warn the user when they use > .5 seconds as a
1426 * between-frame delay time.
1428 * domivogt (28-apr-1999): That is because the keyboard was not
1429 * grabbed. works nicely now.
1431 if (FCheckMaskEvent(
1432 dpy, ButtonPressMask|ButtonReleaseMask|KeyPressMask,
1433 &evdummy))
1435 /* finish the move immediately */
1436 if (pmrtp != NULL)
1438 update_transparent_menu_bg(
1439 pmrtp, lastX, lastY,
1440 currentX, currentY, endX, endY);
1442 XMoveWindow(dpy,w,endX,endY);
1443 if (pmrtp != NULL)
1445 repaint_transparent_menu(
1446 pmrtp, first,
1447 endX, endY, endX, endY, True);
1449 break;
1451 lastX = currentX;
1452 lastY = currentY;
1453 first = False;
1454 } while (*ppctMovement != 1.0 && ppctMovement++);
1455 MyXUngrabKeyboard(dpy);
1456 XFlush(dpy);
1457 if (fw)
1459 GNOME_SetWinArea(fw);
1462 return;
1465 /* used for moving menus, not a client window */
1466 void AnimatedMoveOfWindow(
1467 Window w, int startX, int startY, int endX, int endY,
1468 Bool fWarpPointerToo, int cmsDelay, float *ppctMovement,
1469 MenuRepaintTransparentParameters *pmrtp)
1471 AnimatedMoveAnyWindow(
1472 NULL, w, startX, startY, endX, endY, fWarpPointerToo,
1473 cmsDelay, ppctMovement, pmrtp);
1475 return;
1478 /* used for moving client windows */
1479 void AnimatedMoveFvwmWindow(
1480 FvwmWindow *fw, Window w, int startX, int startY, int endX,
1481 int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement)
1483 AnimatedMoveAnyWindow(
1484 fw, w, startX, startY, endX, endY, fWarpPointerToo,
1485 cmsDelay, ppctMovement, NULL);
1487 return;
1490 int placement_binding(int button, KeySym keysym, int modifier, char *action)
1492 if (keysym != 0)
1494 /* fixme */
1495 fvwm_msg(
1496 ERR, "placement_binding",
1497 "sorry, placement keybindings not allowed. yet.");
1498 return 1;
1500 if (modifier != 0)
1502 /* fixme */
1503 fvwm_msg(
1504 ERR, "placement_binding",
1505 "sorry, placement binding modifiers not allowed. yet.");
1506 return 1;
1508 if (strcmp(action,"-") == 0 ||
1509 strcasecmp(action,"CancelPlacement") == 0)
1511 if (keysym == 0) /* must be button binding */
1513 if (button == 0)
1515 move_drag_finish_button_mask = 0;
1516 move_interactive_finish_button_mask = 0;
1518 else if (button > 0 && button <=
1519 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1521 move_drag_finish_button_mask &=
1522 ~(1<<(button-1));
1523 move_interactive_finish_button_mask &=
1524 ~(1<<(button-1));
1528 else if (strcasecmp(action,"CancelPlacementDrag") == 0)
1530 if (keysym == 0) /* must be button binding */
1532 if (button == 0)
1534 move_drag_finish_button_mask = 0;
1536 else if (button > 0 && button <=
1537 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1539 move_drag_finish_button_mask &=
1540 ~(1<<(button-1));
1544 else if (strcasecmp(action,"CancelPlacementInteractive") == 0)
1546 if (keysym == 0) /* must be button binding */
1548 if (button == 0)
1550 move_interactive_finish_button_mask = 0;
1552 else if (button > 0 && button <=
1553 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1555 move_interactive_finish_button_mask &=
1556 ~(1<<(button-1));
1560 else if (strcasecmp(action,"PlaceWindow") == 0)
1562 if (keysym == 0) /* must be button binding */
1564 if (button == 0)
1566 move_interactive_finish_button_mask =
1567 move_drag_finish_button_mask =
1568 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1570 else if (button > 0 && button <=
1571 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1573 move_drag_finish_button_mask |= (1<<(button-1));
1574 move_interactive_finish_button_mask |=
1575 (1<<(button-1));
1579 else if (strcasecmp(action,"PlaceWindowDrag") == 0)
1581 if (keysym == 0) /* must be button binding */
1583 if (button == 0)
1585 move_drag_finish_button_mask =
1586 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1588 else if (button > 0 && button <=
1589 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1591 move_drag_finish_button_mask |= (1<<(button-1));
1595 else if (strcasecmp(action,"PlaceWindowInteractive") == 0)
1597 if (keysym == 0) /* must be button binding */
1599 if (button == 0)
1601 move_interactive_finish_button_mask =
1602 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1604 else if (button > 0 && button <=
1605 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1607 move_interactive_finish_button_mask |=
1608 (1<<(button-1));
1612 else
1614 fvwm_msg(
1615 ERR, "placement_binding",
1616 "invalid action %s", action);
1619 return 0;
1625 * Start a window move operation
1628 void __move_icon(
1629 FvwmWindow *fw, int x, int y, int old_x, int old_y,
1630 Bool do_move_animated, Bool do_warp_pointer)
1632 rectangle gt;
1633 rectangle gp;
1634 Bool has_icon_title;
1635 Bool has_icon_picture;
1636 Window tw;
1637 int tx;
1638 int ty;
1640 set_icon_position(fw, x, y);
1641 broadcast_icon_geometry(fw, False);
1642 has_icon_title = get_visible_icon_title_geometry(fw, &gt);
1643 has_icon_picture = get_visible_icon_picture_geometry(fw, &gp);
1644 if (has_icon_picture)
1646 tw = FW_W_ICON_PIXMAP(fw);
1647 tx = gp.x;
1648 ty = gp.y;
1650 else if (has_icon_title)
1652 tw = FW_W_ICON_TITLE(fw);
1653 tx = gt.x;
1654 ty = gt.y;
1656 else
1658 return;
1660 if (do_move_animated)
1662 AnimatedMoveOfWindow(
1663 tw, -1, -1, tx, ty, do_warp_pointer, -1, NULL, NULL);
1664 do_warp_pointer = 0;
1666 if (has_icon_title)
1668 XMoveWindow(dpy, FW_W_ICON_TITLE(fw), gt.x, gt.y);
1670 if (has_icon_picture)
1672 XMoveWindow(dpy, FW_W_ICON_PIXMAP(fw), gp.x, gp.y);
1673 if (fw->Desk == Scr.CurrentDesk)
1675 XMapWindow(dpy, FW_W_ICON_PIXMAP(fw));
1676 if (has_icon_title)
1678 XMapWindow(dpy, FW_W_ICON_TITLE(fw));
1682 if (do_warp_pointer)
1684 FWarpPointer(dpy, None, None, 0, 0, 0, 0, x - old_x, y - old_y);
1687 return;
1690 static void __move_window(F_CMD_ARGS, Bool do_animate, int mode)
1692 int FinalX = 0;
1693 int FinalY = 0;
1694 int n;
1695 int x;
1696 int y;
1697 int width, height;
1698 int page_x, page_y;
1699 Bool fWarp = False;
1700 Bool fPointer = False;
1701 int dx;
1702 int dy;
1703 FvwmWindow *fw = exc->w.fw;
1704 Window w;
1706 if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
1708 return;
1710 /* gotta have a window */
1711 w = FW_W_FRAME(fw);
1712 if (IS_ICONIFIED(fw))
1714 if (FW_W_ICON_PIXMAP(fw) != None)
1716 w = FW_W_ICON_PIXMAP(fw);
1717 XUnmapWindow(dpy,FW_W_ICON_TITLE(fw));
1719 else
1721 w = FW_W_ICON_TITLE(fw);
1723 if (w == None && (mode == MOVE_PAGE || mode == MOVE_SCREEN))
1725 w = FW_W_FRAME(fw);
1728 if (
1729 !XGetGeometry(
1730 dpy, w, &JunkRoot, &x, &y, (unsigned int*)&width,
1731 (unsigned int*)&height, (unsigned int*)&JunkBW,
1732 (unsigned int*)&JunkDepth))
1734 return;
1736 if (mode == MOVE_PAGE && IS_STICKY_ACROSS_PAGES(fw))
1738 return;
1740 if (mode == MOVE_PAGE)
1742 rectangle r;
1743 rectangle s;
1744 rectangle t;
1746 do_animate = False;
1747 r.x = x;
1748 r.y = y;
1749 r.width = width;
1750 r.height = height;
1751 get_absolute_geometry(&t, &r);
1752 get_page_offset_rectangle(&page_x, &page_y, &t);
1753 if (!get_page_arguments(action, &page_x, &page_y))
1755 page_x = Scr.Vx;
1756 page_y = Scr.Vy;
1758 s.x = page_x - Scr.Vx;
1759 s.y = page_y - Scr.Vy;
1760 s.width = Scr.MyDisplayWidth;
1761 s.height = Scr.MyDisplayHeight;
1762 fvwmrect_move_into_rectangle(&r, &s);
1763 FinalX = r.x;
1764 FinalY = r.y;
1766 else if (mode == MOVE_SCREEN)
1768 rectangle r;
1769 rectangle s;
1770 rectangle p;
1771 int fscreen;
1773 do_animate = False;
1774 fscreen = FScreenGetScreenArgument(
1775 action, FSCREEN_SPEC_CURRENT);
1776 FScreenGetScrRect(
1777 NULL, fscreen, &s.x, &s.y, &s.width, &s.height);
1778 page_x = Scr.Vx;
1779 page_y = Scr.Vy;
1780 r.x = x;
1781 r.y = y;
1782 r.width = width;
1783 r.height = height;
1784 p.x = page_x - Scr.Vx;
1785 p.y = page_y - Scr.Vy;
1786 p.width = Scr.MyDisplayWidth;
1787 p.height = Scr.MyDisplayHeight;
1788 /* move to page first */
1789 fvwmrect_move_into_rectangle(&r, &p);
1790 /* then move to screen */
1791 fvwmrect_move_into_rectangle(&r, &s);
1792 FinalX = r.x;
1793 FinalY = r.y;
1795 else
1797 FinalX = x;
1798 FinalY = y;
1799 n = GetMoveArguments(
1800 &action, width, height, &FinalX, &FinalY, &fWarp,
1801 &fPointer, True);
1803 if (n != 2 || fPointer)
1805 InteractiveMove(&w, exc, &FinalX, &FinalY, fPointer);
1807 else if (IS_ICONIFIED(fw))
1809 SET_ICON_MOVED(fw, 1);
1813 if (w == FW_W_FRAME(fw))
1815 dx = FinalX - fw->g.frame.x;
1816 dy = FinalY - fw->g.frame.y;
1817 if (do_animate)
1819 AnimatedMoveFvwmWindow(
1820 fw, w, -1, -1, FinalX, FinalY, fWarp, -1,
1821 NULL);
1823 frame_setup_window(
1824 fw, FinalX, FinalY, fw->g.frame.width,
1825 fw->g.frame.height, True);
1826 if (fWarp & !do_animate)
1828 FWarpPointer(
1829 dpy, None, None, 0, 0, 0, 0, FinalX - x,
1830 FinalY - y);
1832 if (IS_MAXIMIZED(fw))
1834 fw->g.max.x += dx;
1835 fw->g.max.y += dy;
1837 else
1839 fw->g.normal.x += dx;
1840 fw->g.normal.y += dy;
1842 update_absolute_geometry(fw);
1843 maximize_adjust_offset(fw);
1844 XFlush(dpy);
1845 GNOME_SetWinArea(fw);
1847 else /* icon window */
1849 __move_icon(fw, FinalX, FinalY, x, y, do_animate, fWarp);
1850 XFlush(dpy);
1852 focus_grab_buttons_on_layer(fw->layer);
1854 return;
1857 void CMD_Move(F_CMD_ARGS)
1859 __move_window(F_PASS_ARGS, False, MOVE_NORMAL);
1861 return;
1864 void CMD_AnimatedMove(F_CMD_ARGS)
1866 __move_window(F_PASS_ARGS, True, MOVE_NORMAL);
1868 return;
1871 void CMD_MoveToPage(F_CMD_ARGS)
1873 __move_window(F_PASS_ARGS, False, MOVE_PAGE);
1875 return;
1878 void CMD_MoveToScreen(F_CMD_ARGS)
1880 __move_window(F_PASS_ARGS, False, MOVE_SCREEN);
1882 return;
1885 /* This function does the SnapAttraction stuff. It takes x and y coordinates
1886 * (*px and *py) and returns the snapped values. */
1887 static void DoSnapAttract(
1888 FvwmWindow *fw, int Width, int Height, int *px, int *py)
1890 int nyt,nxl,dist,closestLeft,closestRight,closestBottom,closestTop;
1891 rectangle self;
1893 /* resist based on window edges */
1894 closestTop = fw->snap_attraction.proximity;
1895 closestBottom = fw->snap_attraction.proximity;
1896 closestRight = fw->snap_attraction.proximity;
1897 closestLeft = fw->snap_attraction.proximity;
1898 nxl = -99999;
1899 nyt = -99999;
1900 self.x = *px;
1901 self.y = *py;
1902 self.width = Width;
1903 self.height = Height;
1905 rectangle g;
1906 Bool rc;
1908 rc = get_visible_icon_title_geometry(fw, &g);
1909 if (rc == True)
1911 self.height += g.height;
1916 * Snap grid handling
1918 if (fw->snap_grid_x > 1 && nxl == -99999)
1920 if (*px != *px / fw->snap_grid_x * fw->snap_grid_x)
1922 *px = (*px + ((*px >= 0) ?
1923 fw->snap_grid_x : -fw->snap_grid_x) /
1924 2) / fw->snap_grid_x * fw->snap_grid_x;
1927 if (fw->snap_grid_y > 1 && nyt == -99999)
1929 if (*py != *py / fw->snap_grid_y * fw->snap_grid_y)
1931 *py = (*py + ((*py >= 0) ?
1932 fw->snap_grid_y : -fw->snap_grid_y) /
1933 2) / fw->snap_grid_y * fw->snap_grid_y;
1938 * snap attraction
1940 /* snap to other windows or icons*/
1941 if (fw->snap_attraction.proximity > 0 &&
1942 (fw->snap_attraction.mode & (SNAP_ICONS | SNAP_WINDOWS | SNAP_SAME)))
1944 FvwmWindow *tmp;
1945 int maskout = (SNAP_SCREEN | SNAP_SCREEN_WINDOWS |
1946 SNAP_SCREEN_ICONS | SNAP_SCREEN_ALL);
1948 for (tmp = Scr.FvwmRoot.next; tmp; tmp = tmp->next)
1950 rectangle other;
1952 if (fw->Desk != tmp->Desk || fw == tmp)
1954 continue;
1956 /* check snapping type */
1957 switch (fw->snap_attraction.mode & ~(maskout))
1959 case SNAP_WINDOWS: /* we only snap windows */
1960 if (IS_ICONIFIED(tmp) || IS_ICONIFIED(fw))
1962 continue;
1964 break;
1965 case SNAP_ICONS: /* we only snap icons */
1966 if (!IS_ICONIFIED(tmp) || !IS_ICONIFIED(fw))
1968 continue;
1970 break;
1971 case SNAP_SAME: /* we don't snap unequal */
1972 if (IS_ICONIFIED(tmp) != IS_ICONIFIED(fw))
1974 continue;
1976 break;
1977 default: /* All */
1978 /* NOOP */
1979 break;
1981 /* get other window dimensions */
1982 get_visible_window_or_icon_geometry(tmp, &other);
1983 /* prevent that window snaps off screen */
1984 if (other.x <= 0)
1986 other.x -= fw->snap_attraction.proximity + 10000;
1987 other.width += fw->snap_attraction.proximity + 10000;
1989 if (other.y <= 0)
1991 other.y -= fw->snap_attraction.proximity + 10000;
1992 other.height += fw->snap_attraction.proximity + 10000;
1994 if (other.x + other.width >= Scr.MyDisplayWidth)
1996 other.width += fw->snap_attraction.proximity + 10000;
1998 if (other.y + other.height >= Scr.MyDisplayHeight)
2000 other.height += fw->snap_attraction.proximity + 10000;
2003 /* snap horizontally */
2004 if (
2005 other.y + other.height > *py &&
2006 other.y < *py + self.height)
2008 dist = abs(other.x - (*px + self.width));
2009 if (dist < closestRight)
2011 closestRight = dist;
2012 if (*px + self.width >= other.x &&
2013 *px + self.width <
2014 other.x + fw->snap_attraction.proximity)
2016 nxl = other.x - self.width;
2018 if (*px + self.width >=
2019 other.x - fw->snap_attraction.proximity &&
2020 *px + self.width < other.x)
2022 nxl = other.x - self.width;
2025 dist = abs(other.x + other.width - *px);
2026 if (dist < closestLeft)
2028 closestLeft = dist;
2029 if (*px <= other.x + other.width &&
2030 *px > other.x + other.width -
2031 fw->snap_attraction.proximity)
2033 nxl = other.x + other.width;
2035 if (*px <= other.x + other.width +
2036 fw->snap_attraction.proximity &&
2037 *px > other.x + other.width)
2039 nxl = other.x + other.width;
2043 /* snap vertically */
2044 if (
2045 other.x + other.width > *px &&
2046 other.x < *px + self.width)
2048 dist = abs(other.y - (*py + self.height));
2049 if (dist < closestBottom)
2051 closestBottom = dist;
2052 if (*py + self.height >= other.y &&
2053 *py + self.height < other.y +
2054 fw->snap_attraction.proximity)
2056 nyt = other.y - self.height;
2058 if (*py + self.height >=
2059 other.y - fw->snap_attraction.proximity &&
2060 *py + self.height < other.y)
2062 nyt = other.y - self.height;
2065 dist = abs(other.y + other.height - *py);
2066 if (dist < closestTop)
2068 closestTop = dist;
2069 if (*py <=
2070 other.y + other.height &&
2071 *py > other.y + other.height -
2072 fw->snap_attraction.proximity)
2074 nyt = other.y + other.height;
2076 if (*py <= other.y + other.height +
2077 fw->snap_attraction.proximity &&
2078 *py > other.y + other.height)
2080 nyt = other.y + other.height;
2084 } /* for */
2085 } /* snap to other windows */
2087 /* snap to screen egdes */
2088 if (fw->snap_attraction.proximity > 0 && (
2089 ( fw->snap_attraction.mode & SNAP_SCREEN && (
2090 fw->snap_attraction.mode & SNAP_SAME ||
2091 ( IS_ICONIFIED(fw) &&
2092 fw->snap_attraction.mode & SNAP_ICONS ) ||
2093 ( !IS_ICONIFIED(fw) &&
2094 fw->snap_attraction.mode & SNAP_WINDOWS ))) ||
2095 ( !IS_ICONIFIED(fw) &&
2096 fw->snap_attraction.mode & SNAP_SCREEN_WINDOWS ) ||
2097 ( IS_ICONIFIED(fw) &&
2098 fw->snap_attraction.mode & SNAP_SCREEN_ICONS ) ||
2099 fw->snap_attraction.mode & SNAP_SCREEN_ALL ))
2101 /* horizontally */
2102 if (!(Scr.MyDisplayWidth < (*px) ||
2103 (*px + self.width) < 0))
2105 dist = abs(Scr.MyDisplayHeight - (*py + self.height));
2106 if (dist < closestBottom)
2108 closestBottom = dist;
2109 if (*py + self.height >=
2110 Scr.MyDisplayHeight &&
2111 *py + self.height <
2112 Scr.MyDisplayHeight + fw->snap_attraction.proximity)
2114 nyt = Scr.MyDisplayHeight -
2115 self.height;
2117 if (*py + self.height >=
2118 Scr.MyDisplayHeight - fw->snap_attraction.proximity &&
2119 *py + self.height < Scr.MyDisplayHeight)
2121 nyt = Scr.MyDisplayHeight -
2122 self.height;
2125 dist = abs(*py);
2126 if (dist < closestTop)
2128 closestTop = dist;
2129 if ((*py <= 0)&&(*py > - fw->snap_attraction.proximity))
2131 nyt = 0;
2133 if ((*py <= fw->snap_attraction.proximity)&&(*py > 0))
2135 nyt = 0;
2138 } /* horizontally */
2139 /* vertically */
2140 if (!(Scr.MyDisplayHeight < (*py) ||
2141 (*py + self.height) < 0))
2143 dist = abs(
2144 Scr.MyDisplayWidth - (*px + self.width));
2145 if (dist < closestRight)
2147 closestRight = dist;
2149 if (*px + self.width >= Scr.MyDisplayWidth &&
2150 *px + self.width <
2151 Scr.MyDisplayWidth + fw->snap_attraction.proximity)
2153 nxl = Scr.MyDisplayWidth - self.width;
2156 if (*px + self.width >=
2157 Scr.MyDisplayWidth - fw->snap_attraction.proximity &&
2158 *px + self.width < Scr.MyDisplayWidth)
2160 nxl = Scr.MyDisplayWidth - self.width;
2163 dist = abs(*px);
2164 if (dist < closestLeft)
2166 closestLeft = dist;
2168 if ((*px <= 0) &&
2169 (*px > - fw->snap_attraction.proximity))
2171 nxl = 0;
2173 if ((*px <= fw->snap_attraction.proximity) &&
2174 (*px > 0))
2176 nxl = 0;
2179 } /* vertically */
2180 } /* snap to screen edges */
2182 if (nxl != -99999)
2184 *px = nxl;
2186 if (nyt != -99999)
2188 *py = nyt;
2192 * Resist moving windows beyond the edge of the screen
2194 if (fw->edge_resistance_move > 0)
2196 /* snap to right edge */
2197 if (
2198 *px + Width >= Scr.MyDisplayWidth &&
2199 *px + Width < Scr.MyDisplayWidth +
2200 fw->edge_resistance_move)
2202 *px = Scr.MyDisplayWidth - Width;
2204 /* snap to left edge */
2205 else if ((*px <= 0) && (*px > -fw->edge_resistance_move))
2207 *px = 0;
2209 /* snap to bottom edge */
2210 if (
2211 *py + Height >= Scr.MyDisplayHeight &&
2212 *py + Height < Scr.MyDisplayHeight +
2213 fw->edge_resistance_move)
2215 *py = Scr.MyDisplayHeight - Height;
2217 /* snap to top edge */
2218 else if (*py <= 0 && *py > -fw->edge_resistance_move)
2220 *py = 0;
2223 /* Resist moving windows between xineramascreens */
2224 if (fw->edge_resistance_xinerama_move > 0 && FScreenIsEnabled())
2226 int scr_x0, scr_y0;
2227 int scr_x1, scr_y1;
2228 Bool do_recalc_rectangle = False;
2230 FScreenGetResistanceRect(
2231 *px, *py, Width, Height, &scr_x0, &scr_y0, &scr_x1,
2232 &scr_y1);
2234 /* snap to right edge */
2235 if (scr_x1 < Scr.MyDisplayWidth &&
2236 *px + Width >= scr_x1 && *px + Width <
2237 scr_x1 + fw->edge_resistance_xinerama_move)
2239 *px = scr_x1 - Width;
2240 do_recalc_rectangle = True;
2242 /* snap to left edge */
2243 else if (
2244 scr_x0 > 0 &&
2245 *px <= scr_x0 && scr_x0 - *px <
2246 fw->edge_resistance_xinerama_move)
2248 *px = scr_x0;
2249 do_recalc_rectangle = True;
2251 if (do_recalc_rectangle)
2253 /* Snapping in X direction can move the window off a
2254 * screen. Thus, it may no longer be necessary to snap
2255 * in Y direction. */
2256 FScreenGetResistanceRect(
2257 *px, *py, Width, Height, &scr_x0, &scr_y0,
2258 &scr_x1, &scr_y1);
2260 /* snap to bottom edge */
2261 if (scr_y1 < Scr.MyDisplayHeight &&
2262 *py + Height >= scr_y1 && *py + Height <
2263 scr_y1 + fw->edge_resistance_xinerama_move)
2265 *py = scr_y1 - Height;
2267 /* snap to top edge */
2268 else if (
2269 scr_y0 > 0 &&
2270 *py <= scr_y0 && scr_y0 - *py <
2271 fw->edge_resistance_xinerama_move)
2273 *py = scr_y0;
2277 return;
2282 * Move the rubberband around, return with the new window location
2284 * Returns True if the window has to be resized after the move.
2287 Bool __move_loop(
2288 const exec_context_t *exc, int XOffset, int YOffset, int Width,
2289 int Height, int *FinalX, int *FinalY, Bool do_move_opaque, int cursor)
2291 extern Window bad_window;
2292 Bool is_finished = False;
2293 Bool is_aborted = False;
2294 int xl,xl2,yt,yt2,delta_x,delta_y,paged;
2295 unsigned int button_mask = 0;
2296 FvwmWindow fw_copy;
2297 int dx = Scr.EdgeScrollX ? Scr.EdgeScrollX : Scr.MyDisplayWidth;
2298 int dy = Scr.EdgeScrollY ? Scr.EdgeScrollY : Scr.MyDisplayHeight;
2299 const int vx = Scr.Vx;
2300 const int vy = Scr.Vy;
2301 int xl_orig = 0;
2302 int yt_orig = 0;
2303 int cnx = 0;
2304 int cny = 0;
2305 int x_virtual_offset = 0;
2306 int y_virtual_offset = 0;
2307 Bool sent_cn = False;
2308 Bool do_resize_too = False;
2309 int x_bak;
2310 int y_bak;
2311 Window move_w = None;
2312 int orig_icon_x = 0;
2313 int orig_icon_y = 0;
2314 Bool do_snap = True;
2315 Bool was_snapped = False;
2316 /* if Alt is initially pressed don't enable no-snap until Alt is
2317 * released */
2318 Bool nosnap_enabled = False;
2319 /* Must not set placed by button if the event is a modified KeyEvent */
2320 Bool is_fake_event;
2321 FvwmWindow *fw = exc->w.fw;
2322 unsigned int draw_parts = PART_NONE;
2323 XEvent e;
2325 if (!GrabEm(cursor, GRAB_NORMAL))
2327 XBell(dpy, 0);
2328 return False;
2330 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
2332 do_move_opaque = False;
2334 bad_window = None;
2335 if (IS_ICONIFIED(fw))
2337 if (FW_W_ICON_PIXMAP(fw) != None)
2339 move_w = FW_W_ICON_PIXMAP(fw);
2341 else if (FW_W_ICON_TITLE(fw) != None)
2343 move_w = FW_W_ICON_TITLE(fw);
2346 else
2348 move_w = FW_W_FRAME(fw);
2350 if (
2351 !XGetGeometry(
2352 dpy, move_w, &JunkRoot, &x_bak, &y_bak,
2353 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
2354 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
2356 /* This is allright here since the window may not be mapped
2357 * yet. */
2360 if (IS_ICONIFIED(fw))
2362 rectangle g;
2364 get_visible_icon_geometry(fw, &g);
2365 orig_icon_x = g.x;
2366 orig_icon_y = g.y;
2369 /* make a copy of the fw structure for sending to the pager */
2370 memcpy(&fw_copy, fw, sizeof(FvwmWindow));
2371 /* prevent flicker when paging */
2372 SET_WINDOW_BEING_MOVED_OPAQUE(fw, do_move_opaque);
2374 if (FQueryPointer(
2375 dpy, Scr.Root, &JunkRoot, &JunkChild, &xl, &yt,
2376 &JunkX, &JunkY, &button_mask) == False)
2378 /* pointer is on a different screen */
2379 xl = 0;
2380 yt = 0;
2382 else
2384 xl += XOffset;
2385 yt += YOffset;
2387 button_mask &= DEFAULT_ALL_BUTTONS_MASK;
2388 xl_orig = xl;
2389 yt_orig = yt;
2391 /* draw initial outline */
2392 if (!IS_ICONIFIED(fw) &&
2393 ((!do_move_opaque && !Scr.gs.do_emulate_mwm) || !IS_MAPPED(fw)))
2395 draw_move_resize_grid(xl, yt, Width - 1, Height - 1);
2398 if (move_w == FW_W_FRAME(fw) && do_move_opaque)
2400 draw_parts = border_get_transparent_decorations_part(fw);
2402 DisplayPosition(fw, exc->x.elast, xl, yt, True);
2404 memset(&e, 0, sizeof(e));
2406 /* Unset the placed by button mask.
2407 * If the move is canceled this will remain as zero.
2409 fw->placed_by_button = 0;
2410 while (!is_finished && bad_window != FW_W(fw))
2412 int rc = 0;
2413 int old_xl;
2414 int old_yt;
2416 old_xl = xl;
2417 old_yt = yt;
2418 /* wait until there is an interesting event */
2419 while (rc != -1 &&
2420 (!FPending(dpy) ||
2421 !FCheckMaskEvent(
2422 dpy, ButtonPressMask | ButtonReleaseMask |
2423 KeyPressMask | PointerMotionMask |
2424 ButtonMotionMask | ExposureMask, &e)))
2426 XEvent le;
2428 fev_get_last_event(&le);
2430 xl -= XOffset;
2431 yt -= YOffset;
2433 rc = HandlePaging(
2434 &le, dx, dy, &xl, &yt, &delta_x, &delta_y,
2435 False, False, True, fw->edge_delay_ms_move);
2437 /* Fake an event to force window reposition */
2438 if (delta_x)
2440 x_virtual_offset = 0;
2442 xl += XOffset;
2443 if (delta_y)
2445 y_virtual_offset = 0;
2447 yt += YOffset;
2448 if (do_snap)
2450 DoSnapAttract(
2451 fw, Width, Height, &xl, &yt);
2452 was_snapped = True;
2454 fev_make_null_event(&e, dpy);
2455 e.type = MotionNotify;
2456 e.xmotion.time = fev_get_evtime();
2457 e.xmotion.x_root = xl - XOffset;
2458 e.xmotion.y_root = yt - YOffset;
2459 e.xmotion.same_screen = True;
2460 break;
2462 if (rc == -1)
2464 /* block until an event arrives */
2465 /* dv (2004-07-01): With XFree 4.1.0.1, some Mouse
2466 * events are not reported to fvwm when the pointer
2467 * moves very fast and suddenly stops in the corner of
2468 * the screen. Handle EnterNotify/LeaveNotify events
2469 * too to get an idea where the pointer might be. */
2470 FMaskEvent(
2471 dpy, ButtonPressMask | ButtonReleaseMask |
2472 KeyPressMask | PointerMotionMask |
2473 ButtonMotionMask | ExposureMask |
2474 EnterWindowMask | LeaveWindowMask, &e);
2477 /* discard extra events before a logical release */
2478 if (e.type == MotionNotify ||
2479 e.type == EnterNotify || e.type == LeaveNotify)
2481 while (FPending(dpy) > 0 &&
2482 FCheckMaskEvent(
2483 dpy, ButtonMotionMask |
2484 PointerMotionMask | ButtonPressMask |
2485 ButtonRelease | KeyPressMask |
2486 EnterWindowMask | LeaveWindowMask, &e))
2488 if (e.type == ButtonPress ||
2489 e.type == ButtonRelease ||
2490 e.type == KeyPress)
2492 break;
2496 if (e.type == EnterNotify || e.type == LeaveNotify)
2498 XEvent e2;
2499 int x;
2500 int y;
2502 /* Query the pointer to catch the latest information.
2503 * This *is* necessary. */
2504 if (FQueryPointer(
2505 dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
2506 &y, &JunkX, &JunkY, &JunkMask) == True)
2508 fev_make_null_event(&e2, dpy);
2509 e2.type = MotionNotify;
2510 e2.xmotion.time = fev_get_evtime();
2511 e2.xmotion.x_root = x;
2512 e2.xmotion.y_root = y;
2513 e2.xmotion.state = JunkMask;
2514 e2.xmotion.same_screen = True;
2515 e = e2;
2516 fev_fake_event(&e);
2518 else
2520 /* pointer is on a different screen,
2521 * ignore event */
2524 is_fake_event = False;
2525 /* Handle a limited number of key press events to allow
2526 * mouseless operation */
2527 if (e.type == KeyPress)
2529 Keyboard_shortcuts(
2530 &e, fw, &x_virtual_offset,
2531 &y_virtual_offset, ButtonRelease);
2533 is_fake_event = (e.type != KeyPress);
2535 switch (e.type)
2537 case KeyPress:
2538 if (!(e.xkey.state & Mod1Mask))
2540 nosnap_enabled = True;
2542 do_snap = nosnap_enabled &&
2543 (e.xkey.state & Mod1Mask) ? False : True;
2545 /* simple code to bag out of move - CKH */
2546 if (XLookupKeysym(&(e.xkey), 0) == XK_Escape)
2548 if (!do_move_opaque)
2550 switch_move_resize_grid(False);
2552 if (!IS_ICONIFIED(fw))
2554 if (do_move_opaque)
2556 *FinalX = fw->g.frame.x;
2557 *FinalY = fw->g.frame.y;
2560 else
2562 *FinalX = orig_icon_x;
2563 *FinalY = orig_icon_y;
2565 is_aborted = True;
2566 is_finished = True;
2568 break;
2569 case ButtonPress:
2570 if (e.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
2571 ((Button1Mask << (e.xbutton.button - 1)) &
2572 button_mask))
2574 /* No new button was pressed, just a delayed
2575 * event */
2576 break;
2578 if (!IS_MAPPED(fw) &&
2579 ((e.xbutton.button == 2 && !Scr.gs.do_emulate_mwm)
2581 (e.xbutton.button == 1 && Scr.gs.do_emulate_mwm &&
2582 (e.xbutton.state & ShiftMask))))
2584 do_resize_too = True;
2585 /* Fallthrough to button-release */
2587 else if (!button_mask && e.xbutton.button <=
2588 NUMBER_OF_EXTENDED_MOUSE_BUTTONS &&
2589 e.xbutton.button > 0 &&
2590 (move_interactive_finish_button_mask &
2591 (1<<(e.xbutton.button-1))))
2593 do_resize_too = False;
2594 break;
2596 else if (button_mask && e.xbutton.button <=
2597 NUMBER_OF_EXTENDED_MOUSE_BUTTONS &&
2598 e.xbutton.button > 0 &&
2599 (move_drag_finish_button_mask &
2600 (1<<(e.xbutton.button-1))))
2602 do_resize_too = False;
2603 /* Fallthrough to button-release */
2605 else
2607 /* Abort the move if
2608 * - the move started with a pressed button
2609 * and another button was pressed during the
2610 * operation
2611 * - Any button not in the
2612 * move_finish_button_mask is pressed
2614 /* if (button_mask) */
2615 /* - button_mask will always be set here.
2616 * only add an if if we want to be able to
2617 * place windows dragged by other means
2618 * than releasing the initial button.
2621 if (!do_move_opaque)
2623 switch_move_resize_grid(False);
2625 if (!IS_ICONIFIED(fw))
2627 *FinalX = fw->g.frame.x;
2628 *FinalY = fw->g.frame.y;
2630 else
2632 *FinalX = orig_icon_x;
2633 *FinalY = orig_icon_y;
2635 is_aborted = True;
2636 is_finished = True;
2638 break;
2640 case ButtonRelease:
2641 if (!is_fake_event)
2643 fw->placed_by_button = e.xbutton.button;
2645 if (!do_move_opaque)
2647 switch_move_resize_grid(False);
2649 xl2 = e.xbutton.x_root + XOffset + x_virtual_offset;
2650 yt2 = e.xbutton.y_root + YOffset + y_virtual_offset;
2651 /* ignore the position of the button release if it was
2652 * on a different page. */
2653 if (!(((xl < 0 && xl2 >= 0) ||
2654 (xl >= 0 && xl2 < 0) ||
2655 (yt < 0 && yt2 >= 0) ||
2656 (yt >= 0 && yt2 < 0)) &&
2657 (abs(xl - xl2) > Scr.MyDisplayWidth / 2 ||
2658 abs(yt - yt2) > Scr.MyDisplayHeight / 2)))
2660 xl = xl2;
2661 yt = yt2;
2663 if (xl != xl_orig || yt != yt_orig || vx != Scr.Vx ||
2664 vy != Scr.Vy || was_snapped)
2666 /* only snap if the window actually moved! */
2667 if (do_snap)
2669 DoSnapAttract(
2670 fw, Width, Height, &xl, &yt);
2671 was_snapped = True;
2675 *FinalX = xl;
2676 *FinalY = yt;
2678 is_finished = True;
2679 break;
2681 case MotionNotify:
2682 if (e.xmotion.same_screen == False)
2684 continue;
2686 if (!(e.xmotion.state & Mod1Mask))
2688 nosnap_enabled = True;
2690 do_snap = nosnap_enabled &&
2691 (e.xmotion.state & Mod1Mask) ? False : True;
2692 xl = e.xmotion.x_root;
2693 yt = e.xmotion.y_root;
2694 if (xl > 0 && xl < Scr.MyDisplayWidth - 1)
2696 /* pointer was moved away from the left/right
2697 * border with the mouse, reset the virtual x
2698 * offset */
2699 x_virtual_offset = 0;
2701 if (yt > 0 && yt < Scr.MyDisplayHeight - 1)
2703 /* pointer was moved away from the top/bottom
2704 * border with the mouse, reset the virtual y
2705 * offset */
2706 y_virtual_offset = 0;
2708 xl += XOffset + x_virtual_offset;
2709 yt += YOffset + y_virtual_offset;
2711 if (do_snap)
2713 DoSnapAttract(fw, Width, Height, &xl, &yt);
2714 was_snapped = True;
2717 /* check Paging request once and only once after
2718 * outline redrawn redraw after paging if needed
2719 * - mab */
2720 for (paged = 0; paged <= 1; paged++)
2722 if (!do_move_opaque)
2724 draw_move_resize_grid(
2725 xl, yt, Width - 1, Height - 1);
2727 else
2729 if (IS_ICONIFIED(fw))
2731 set_icon_position(fw, xl, yt);
2732 move_icon_to_position(fw);
2733 broadcast_icon_geometry(
2734 fw, False);
2736 else
2738 XMoveWindow(
2739 dpy, FW_W_FRAME(fw),
2740 xl, yt);
2743 DisplayPosition(fw, &e, xl, yt, False);
2745 /* prevent window from lagging behind mouse
2746 * when paging - mab */
2747 if (paged == 0)
2749 XEvent le;
2751 xl = e.xmotion.x_root;
2752 yt = e.xmotion.y_root;
2753 fev_get_last_event(&le);
2754 HandlePaging(
2755 &le, dx, dy, &xl, &yt,
2756 &delta_x, &delta_y, False,
2757 False, False,
2758 fw->edge_delay_ms_move);
2759 if (delta_x)
2761 x_virtual_offset = 0;
2763 xl += XOffset;
2764 if (delta_y)
2766 y_virtual_offset = 0;
2768 yt += YOffset;
2769 if (do_snap)
2771 DoSnapAttract(
2772 fw, Width, Height,
2773 &xl, &yt);
2774 was_snapped = True;
2776 if (!delta_x && !delta_y)
2778 /* break from while
2779 * (paged <= 1) */
2780 break;
2784 break;
2786 case Expose:
2787 if (!do_move_opaque)
2789 /* must undraw the rubber band in case the
2790 * event causes some drawing */
2791 switch_move_resize_grid(False);
2793 dispatch_event(&e);
2794 if (!do_move_opaque)
2796 draw_move_resize_grid(
2797 xl, yt, Width - 1, Height - 1);
2799 break;
2801 default:
2802 /* cannot happen */
2803 break;
2804 } /* switch */
2805 xl += x_virtual_offset;
2806 yt += y_virtual_offset;
2807 if (do_move_opaque && !IS_ICONIFIED(fw) &&
2808 !IS_SHADED(fw) && !Scr.bo.do_disable_configure_notify)
2810 /* send configure notify event for windows that care
2811 * about their location; don't send anything if
2812 * position didn't change */
2813 if (!sent_cn || cnx != xl || cny != yt)
2815 cnx = xl;
2816 cny = yt;
2817 sent_cn = True;
2818 SendConfigureNotify(
2819 fw, xl, yt, Width, Height, 0,
2820 False);
2821 #ifdef FVWM_DEBUG_MSGS
2822 fvwm_msg(
2823 DBG, "frame_setup_window",
2824 "Sent ConfigureNotify (w %d, h %d)",
2825 Width, Height);
2826 #endif
2829 if (do_move_opaque)
2831 if (!IS_ICONIFIED(fw))
2833 fw_copy.g.frame.x = xl;
2834 fw_copy.g.frame.y = yt;
2836 if (xl != old_xl || yt != old_yt)
2838 /* only do this with opaque moves, (i.e. the
2839 * server is not grabbed) */
2840 if (draw_parts != PART_NONE)
2842 border_draw_decorations(
2843 fw, draw_parts,
2844 ((fw == get_focus_window())) ?
2845 True : False,
2846 True, CLEAR_ALL, NULL, NULL);
2848 if (IS_TEAR_OFF_MENU(fw))
2850 menu_redraw_transparent_tear_off_menu(
2851 fw, False);
2853 BroadcastConfig(M_CONFIGURE_WINDOW, &fw_copy);
2854 FlushAllMessageQueues();
2857 } /* while (!is_finished) */
2859 if (!Scr.gs.do_hide_position_window)
2861 XUnmapWindow(dpy,Scr.SizeWindow);
2863 if (is_aborted || bad_window == FW_W(fw))
2865 if (vx != Scr.Vx || vy != Scr.Vy)
2867 MoveViewport(vx, vy, False);
2869 if (is_aborted && do_move_opaque)
2871 XMoveWindow(dpy, move_w, x_bak, y_bak);
2872 if (draw_parts != PART_NONE)
2874 border_draw_decorations(
2875 fw, draw_parts,
2876 ((fw == get_focus_window())) ?
2877 True : False,
2878 True, CLEAR_ALL, NULL, NULL);
2880 menu_redraw_transparent_tear_off_menu(fw, False);
2882 if (bad_window == FW_W(fw))
2884 XUnmapWindow(dpy, move_w);
2885 border_undraw_decorations(fw);
2886 XBell(dpy, 0);
2889 if (!is_aborted && bad_window != FW_W(fw) && IS_ICONIFIED(fw))
2891 SET_ICON_MOVED(fw, 1);
2893 UngrabEm(GRAB_NORMAL);
2894 if (!do_resize_too)
2896 /* Don't wait for buttons to come up when user is placing a new
2897 * window and wants to resize it. */
2898 WaitForButtonsUp(True);
2900 SET_WINDOW_BEING_MOVED_OPAQUE(fw, 0);
2901 bad_window = None;
2903 return do_resize_too;
2906 void CMD_MoveThreshold(F_CMD_ARGS)
2908 int val = 0;
2910 if (GetIntegerArguments(action, NULL, &val, 1) < 1 || val < 0)
2912 Scr.MoveThreshold = DEFAULT_MOVE_THRESHOLD;
2914 else
2916 Scr.MoveThreshold = val;
2919 return;
2922 void CMD_OpaqueMoveSize(F_CMD_ARGS)
2924 int val;
2926 if (GetIntegerArguments(action, NULL, &val, 1) < 1)
2928 if (strncasecmp(action, "unlimited", 9) == 0)
2930 Scr.OpaqueSize = -1;
2932 else
2934 Scr.OpaqueSize = DEFAULT_OPAQUE_MOVE_SIZE;
2937 else
2939 Scr.OpaqueSize = val;
2942 return;
2946 static char *hide_options[] =
2948 "never",
2949 "move",
2950 "resize",
2951 NULL
2954 void CMD_HideGeometryWindow(F_CMD_ARGS)
2956 char *token = PeekToken(action, NULL);
2958 Scr.gs.do_hide_position_window = 0;
2959 Scr.gs.do_hide_resize_window = 0;
2960 switch(GetTokenIndex(token, hide_options, 0, NULL))
2962 case 0:
2963 break;
2964 case 1:
2965 Scr.gs.do_hide_position_window = 1;
2966 break;
2967 case 2:
2968 Scr.gs.do_hide_resize_window = 1;
2969 break;
2970 default:
2971 Scr.gs.do_hide_position_window = 1;
2972 Scr.gs.do_hide_resize_window = 1;
2973 break;
2975 return;
2978 void CMD_SnapAttraction(F_CMD_ARGS)
2980 char *cmd;
2981 size_t len;
2983 len = strlen(action);
2984 len += 99;
2985 cmd = safemalloc(len);
2986 sprintf(cmd, "Style * SnapAttraction %s", action);
2987 fvwm_msg(
2988 OLD, "CMD_SnapAttraction",
2989 "The command SnapAttraction is obsolete. Please use the"
2990 " following command instead:");
2991 fvwm_msg(OLD, "", cmd);
2992 execute_function(
2993 cond_rc, exc, cmd,
2994 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
2995 free(cmd);
2997 return;
3000 void CMD_SnapGrid(F_CMD_ARGS)
3002 char *cmd;
3003 size_t len;
3005 len = strlen(action);
3006 len += 99;
3007 cmd = safemalloc(len);
3008 sprintf(cmd, "Style * SnapGrid %s", action);
3009 fvwm_msg(
3010 OLD, "CMD_SnapGrid",
3011 "The command SnapGrid is obsolete. Please use the following"
3012 " command instead:");
3013 fvwm_msg(OLD, "", cmd);
3014 execute_function(
3015 cond_rc, exc, cmd,
3016 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
3017 free(cmd);
3019 return;
3022 static Pixmap XorPixmap = None;
3024 void CMD_XorValue(F_CMD_ARGS)
3026 int val;
3027 XGCValues gcv;
3028 unsigned long gcm;
3030 if (GetIntegerArguments(action, NULL, &val, 1) != 1)
3032 val = 0;
3035 PictureUseDefaultVisual();
3036 gcm = GCFunction|GCLineWidth|GCForeground|GCFillStyle|GCSubwindowMode;
3037 gcv.subwindow_mode = IncludeInferiors;
3038 gcv.function = GXxor;
3039 gcv.line_width = 1;
3040 /* use passed in value, or try to calculate appropriate value if 0 */
3041 /* ctwm method: */
3043 gcv.foreground = (val1)?(val1):((((unsigned long) 1) <<
3044 Scr.d_depth) - 1);
3046 /* Xlib programming manual suggestion: */
3047 gcv.foreground = (val)?
3048 (val):(PictureBlackPixel() ^ PictureWhitePixel());
3049 gcv.fill_style = FillSolid;
3050 gcv.subwindow_mode = IncludeInferiors;
3052 /* modify XorGC, only create once */
3053 if (Scr.XorGC)
3055 XChangeGC(dpy, Scr.XorGC, gcm, &gcv);
3057 else
3059 Scr.XorGC = fvwmlib_XCreateGC(dpy, Scr.Root, gcm, &gcv);
3062 /* free up XorPixmap if neccesary */
3063 if (XorPixmap != None) {
3064 XFreePixmap(dpy, XorPixmap);
3065 XorPixmap = None;
3067 PictureUseFvwmVisual();
3069 return;
3073 void CMD_XorPixmap(F_CMD_ARGS)
3075 char *PixmapName;
3076 FvwmPicture *xp;
3077 XGCValues gcv;
3078 unsigned long gcm;
3079 FvwmPictureAttributes fpa;
3081 action = GetNextToken(action, &PixmapName);
3082 if (PixmapName == NULL)
3084 /* return to default value. */
3085 action = "0";
3086 CMD_XorValue(F_PASS_ARGS);
3087 return;
3089 /* get the picture in the root visual, colorlimit is ignored because the
3090 * pixels will be freed */
3091 fpa.mask = FPAM_NO_COLOR_LIMIT | FPAM_NO_ALPHA;
3092 PictureUseDefaultVisual();
3093 xp = PGetFvwmPicture(dpy, Scr.Root, NULL, PixmapName, fpa);
3094 if (xp == NULL)
3096 fvwm_msg(ERR,"SetXORPixmap","Can't find pixmap %s", PixmapName);
3097 free(PixmapName);
3098 PictureUseFvwmVisual();
3099 return;
3101 free(PixmapName);
3102 /* free up old pixmap */
3103 if (XorPixmap != None)
3105 XFreePixmap(dpy, XorPixmap);
3108 /* make a copy of the picture pixmap */
3109 XorPixmap = XCreatePixmap(dpy, Scr.Root, xp->width, xp->height, Pdepth);
3110 XCopyArea(dpy, xp->picture, XorPixmap, DefaultGC(dpy, Scr.screen), 0, 0,
3111 xp->width, xp->height, 0, 0);
3112 /* destroy picture and free colors */
3113 PDestroyFvwmPicture(dpy, xp);
3114 PictureUseFvwmVisual();
3116 /* create Graphics context */
3117 gcm = GCFunction|GCLineWidth|GCTile|GCFillStyle|GCSubwindowMode;
3118 gcv.subwindow_mode = IncludeInferiors;
3119 gcv.function = GXxor;
3120 /* line width of 1 is necessary for Exceed servers */
3121 gcv.line_width = 1;
3122 gcv.tile = XorPixmap;
3123 gcv.fill_style = FillTiled;
3124 gcv.subwindow_mode = IncludeInferiors;
3125 /* modify XorGC, only create once */
3126 if (Scr.XorGC)
3128 XChangeGC(dpy, Scr.XorGC, gcm, &gcv);
3130 else
3132 Scr.XorGC = fvwmlib_XCreateGC(dpy, Scr.Root, gcm, &gcv);
3135 return;
3139 /* ----------------------------- resizing code ----------------------------- */
3141 static void __resize_get_dir_from_window(
3142 int *ret_xmotion, int *ret_ymotion, FvwmWindow *fw, Window context_w)
3144 if (context_w != Scr.Root && context_w != None)
3146 if (context_w == FW_W_SIDE(fw, 0)) /* top */
3148 *ret_ymotion = 1;
3150 else if (context_w == FW_W_SIDE(fw, 1)) /* right */
3152 *ret_xmotion = -1;
3154 else if (context_w == FW_W_SIDE(fw, 2)) /* bottom */
3156 *ret_ymotion = -1;
3158 else if (context_w == FW_W_SIDE(fw, 3)) /* left */
3160 *ret_xmotion = 1;
3162 else if (context_w == FW_W_CORNER(fw, 0)) /* upper-left */
3164 *ret_xmotion = 1;
3165 *ret_ymotion = 1;
3167 else if (context_w == FW_W_CORNER(fw, 1)) /* upper-right */
3169 *ret_xmotion = -1;
3170 *ret_ymotion = 1;
3172 else if (context_w == FW_W_CORNER(fw, 2)) /* lower left */
3174 *ret_xmotion = 1;
3175 *ret_ymotion = -1;
3177 else if (context_w == FW_W_CORNER(fw, 3)) /* lower right */
3179 *ret_xmotion = -1;
3180 *ret_ymotion = -1;
3184 return;
3187 static void __resize_get_dir_proximity(
3188 int *ret_xmotion, int *ret_ymotion, FvwmWindow *fw, int x_off,
3189 int y_off, int px, int py)
3191 int tx;
3192 int ty;
3194 if (px < 0 || x_off < 0 || py < 0 || y_off < 0)
3196 return;
3198 /* Now find the place to warp to. We simply use the sectors
3199 * drawn when we start resizing the window. */
3200 #if 0
3201 tx = orig->width / 10 - 1;
3202 ty = orig->height / 10 - 1;
3203 #else
3204 tx = 0;
3205 ty = 0;
3206 #endif
3207 tx = max(fw->boundary_width, tx);
3208 ty = max(fw->boundary_width, ty);
3209 if (px < tx)
3211 *ret_xmotion = 1;
3213 else if (x_off < tx)
3215 *ret_xmotion = -1;
3217 if (py < ty)
3219 *ret_ymotion = 1;
3221 else if (y_off < ty)
3223 *ret_ymotion = -1;
3226 return;
3229 static void __resize_get_refpos(
3230 int *ret_x, int *ret_y, int xmotion, int ymotion, int w, int h,
3231 FvwmWindow *fw)
3233 if (xmotion > 0)
3235 *ret_x = 0;
3237 else if (xmotion < 0)
3239 *ret_x = w - 1;
3241 else
3243 *ret_x = w / 2;
3245 if (ymotion > 0)
3247 *ret_y = 0;
3249 else if (ymotion < 0)
3251 *ret_y = h - 1;
3253 else
3255 *ret_y = h / 2;
3258 return;
3261 /* Procedure:
3262 * __resize_step - move the rubberband around. This is called for
3263 * each motion event when we are resizing
3265 * Inputs:
3266 * x_root - the X corrdinate in the root window
3267 * y_root - the Y corrdinate in the root window
3268 * x_off - x offset of pointer from border (input/output)
3269 * y_off - y offset of pointer from border (input/output)
3270 * drag - resize internal structure
3271 * orig - resize internal structure
3272 * xmotionp - pointer to xmotion in resize_window
3273 * ymotionp - pointer to ymotion in resize_window
3276 static void __resize_step(
3277 const exec_context_t *exc, int x_root, int y_root, int *x_off,
3278 int *y_off, rectangle *drag, const rectangle *orig, int *xmotionp,
3279 int *ymotionp, Bool do_resize_opaque, Bool is_direction_fixed)
3281 int action = 0;
3282 int x2;
3283 int y2;
3284 int xdir;
3285 int ydir;
3287 x2 = x_root - *x_off;
3288 x_root += *x_off;
3289 if (is_direction_fixed == True && (*xmotionp != 0 || *ymotionp != 0))
3291 xdir = *xmotionp;
3293 else if (x2 <= orig->x ||
3294 (*xmotionp == 1 && x2 < orig->x + orig->width - 1))
3296 xdir = 1;
3298 else if (x2 >= orig->x + orig->width - 1 ||
3299 (*xmotionp == -1 && x2 > orig->x))
3301 xdir = -1;
3303 else
3305 xdir = 0;
3307 switch (xdir)
3309 case 1:
3310 if (*xmotionp != 1)
3312 *x_off = -*x_off;
3313 x_root = x2;
3314 *xmotionp = 1;
3316 drag->x = x_root;
3317 drag->width = orig->x + orig->width - x_root;
3318 action = 1;
3319 break;
3320 case -1:
3321 if (*xmotionp != -1)
3323 *x_off = -*x_off;
3324 x_root = x2;
3325 *xmotionp = -1;
3327 drag->x = orig->x;
3328 drag->width = 1 + x_root - drag->x;
3329 action = 1;
3330 break;
3331 default:
3332 break;
3334 y2 = y_root - *y_off;
3335 y_root += *y_off;
3336 if (is_direction_fixed == True && (*xmotionp != 0 || *ymotionp != 0))
3338 ydir = *ymotionp;
3340 else if (y2 <= orig->y ||
3341 (*ymotionp == 1 && y2 < orig->y + orig->height - 1))
3343 ydir = 1;
3345 else if (y2 >= orig->y + orig->height - 1 ||
3346 (*ymotionp == -1 && y2 > orig->y))
3348 ydir = -1;
3350 else
3352 ydir = 0;
3354 switch (ydir)
3356 case 1:
3357 if (*ymotionp != 1)
3359 *y_off = -*y_off;
3360 y_root = y2;
3361 *ymotionp = 1;
3363 drag->y = y_root;
3364 drag->height = orig->y + orig->height - y_root;
3365 action = 1;
3366 break;
3367 case -1:
3368 if (*ymotionp != -1)
3370 *y_off = -*y_off;
3371 y_root = y2;
3372 *ymotionp = -1;
3374 drag->y = orig->y;
3375 drag->height = 1 + y_root - drag->y;
3376 action = 1;
3377 break;
3378 default:
3379 break;
3382 if (action)
3384 /* round up to nearest OK size to keep pointer inside
3385 * rubberband */
3386 constrain_size(
3387 exc->w.fw, exc->x.elast, &drag->width, &drag->height,
3388 *xmotionp, *ymotionp, CS_ROUND_UP);
3389 if (*xmotionp == 1)
3391 drag->x = orig->x + orig->width - drag->width;
3393 if (*ymotionp == 1)
3395 drag->y = orig->y + orig->height - drag->height;
3397 if (!do_resize_opaque)
3399 draw_move_resize_grid(
3400 drag->x, drag->y, drag->width - 1,
3401 drag->height - 1);
3403 else
3405 frame_setup_window(
3406 exc->w.fw, drag->x, drag->y, drag->width,
3407 drag->height, False);
3410 DisplaySize(exc->w.fw, exc->x.elast, drag->width, drag->height, False, False);
3412 return;
3415 /* Starts a window resize operation */
3416 static Bool __resize_window(F_CMD_ARGS)
3418 extern Window bad_window;
3419 FvwmWindow *fw = exc->w.fw;
3420 Bool is_finished = False, is_done = False, is_aborted = False;
3421 Bool do_send_cn = False;
3422 Bool do_resize_opaque;
3423 Bool do_warp_to_border;
3424 Bool is_direction_fixed;
3425 Bool fButtonAbort = False;
3426 Bool fForceRedraw = False;
3427 Bool called_from_title = False;
3428 int x,y,delta_x,delta_y,stashed_x,stashed_y;
3429 Window ResizeWindow;
3430 int dx = Scr.EdgeScrollX ? Scr.EdgeScrollX : Scr.MyDisplayWidth;
3431 int dy = Scr.EdgeScrollY ? Scr.EdgeScrollY : Scr.MyDisplayHeight;
3432 const int vx = Scr.Vx;
3433 const int vy = Scr.Vy;
3434 int n;
3435 unsigned int button_mask = 0;
3436 rectangle sdrag;
3437 rectangle sorig;
3438 rectangle *drag = &sdrag;
3439 const rectangle *orig = &sorig;
3440 const window_g g_backup = fw->g;
3441 int ymotion = 0;
3442 int xmotion = 0;
3443 int was_maximized;
3444 unsigned edge_wrap_x;
3445 unsigned edge_wrap_y;
3446 int px;
3447 int py;
3448 int i;
3449 size_borders b;
3450 frame_move_resize_args mr_args = NULL;
3451 long evmask;
3452 XEvent ev;
3453 int ref_x;
3454 int ref_y;
3455 int x_off;
3456 int y_off;
3457 direction_t dir;
3459 bad_window = False;
3460 ResizeWindow = FW_W_FRAME(fw);
3461 if (fev_get_evpos_or_query(dpy, Scr.Root, exc->x.etrigger, &px, &py) ==
3462 False ||
3463 XTranslateCoordinates(
3464 dpy, Scr.Root, ResizeWindow, px, py, &px, &py,
3465 &JunkChild) == False)
3467 /* pointer is on a different screen - that's okay here */
3468 px = 0;
3469 py = 0;
3471 button_mask &= DEFAULT_ALL_BUTTONS_MASK;
3473 if (!is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True))
3475 XBell(dpy, 0);
3476 return False;
3479 if (IS_SHADED(fw) || !IS_MAPPED(fw))
3481 do_resize_opaque = False;
3482 evmask = XEVMASK_RESIZE;
3484 else
3486 do_resize_opaque = DO_RESIZE_OPAQUE(fw);
3487 evmask = XEVMASK_RESIZE_OPAQUE;
3490 /* no suffix = % of screen, 'p' = pixels, 'c' = increment units */
3491 if (IS_SHADED(fw))
3493 get_unshaded_geometry(fw, drag);
3495 else
3497 drag->width = fw->g.frame.width;
3498 drag->height = fw->g.frame.height;
3501 get_window_borders(fw, &b);
3502 n = GetResizeArguments(
3503 &action, fw->g.frame.x, fw->g.frame.y,
3504 fw->hints.base_width, fw->hints.base_height,
3505 fw->hints.width_inc, fw->hints.height_inc,
3506 &b, &(drag->width), &(drag->height),
3507 &dir, &is_direction_fixed, &do_warp_to_border);
3509 if (n == 2)
3511 rectangle new_g;
3513 /* size will be less or equal to requested */
3514 if (IS_SHADED(fw))
3516 rectangle shaded_g;
3518 get_unshaded_geometry(fw, &new_g);
3519 SET_MAXIMIZED(fw, 0);
3520 constrain_size(
3521 fw, NULL, &drag->width, &drag->height, xmotion,
3522 ymotion, 0);
3523 gravity_resize(
3524 fw->hints.win_gravity, &new_g,
3525 drag->width - new_g.width,
3526 drag->height - new_g.height);
3527 fw->g.normal = new_g;
3528 get_shaded_geometry(fw, &shaded_g, &new_g);
3529 frame_setup_window(
3530 fw, shaded_g.x, shaded_g.y, shaded_g.width,
3531 shaded_g.height, False);
3533 else
3535 new_g = fw->g.frame;
3536 SET_MAXIMIZED(fw, 0);
3537 constrain_size(
3538 fw, NULL, &drag->width, &drag->height, xmotion,
3539 ymotion, 0);
3540 gravity_resize(
3541 fw->hints.win_gravity, &new_g,
3542 drag->width - new_g.width,
3543 drag->height - new_g.height);
3544 frame_setup_window(
3545 fw, new_g.x, new_g.y, drag->width,
3546 drag->height, False);
3548 update_absolute_geometry(fw);
3549 maximize_adjust_offset(fw);
3550 GNOME_SetWinArea(fw);
3551 ResizeWindow = None;
3552 return True;
3555 was_maximized = IS_MAXIMIZED(fw);
3556 SET_MAXIMIZED(fw, 0);
3557 if (was_maximized)
3559 /* must redraw the buttons now so that the 'maximize' button
3560 * does not stay depressed. */
3561 border_draw_decorations(
3562 fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
3563 NULL, NULL);
3566 if (Scr.bo.do_install_root_cmap)
3568 InstallRootColormap();
3570 else
3572 InstallFvwmColormap();
3574 if (!GrabEm(CRS_RESIZE, GRAB_NORMAL))
3576 XBell(dpy, 0);
3577 return False;
3580 /* handle problems with edge-wrapping while resizing */
3581 edge_wrap_x = Scr.flags.do_edge_wrap_x;
3582 edge_wrap_y = Scr.flags.do_edge_wrap_y;
3583 Scr.flags.do_edge_wrap_x = 0;
3584 Scr.flags.do_edge_wrap_y = 0;
3586 if (!do_resize_opaque)
3588 MyXGrabServer(dpy);
3590 if (!XGetGeometry(
3591 dpy, (Drawable)ResizeWindow, &JunkRoot, &drag->x, &drag->y,
3592 (unsigned int*)&drag->width, (unsigned int*)&drag->height,
3593 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
3595 UngrabEm(GRAB_NORMAL);
3596 if (!do_resize_opaque)
3598 MyXUngrabServer(dpy);
3600 return False;
3602 if (IS_SHADED(fw))
3604 SET_MAXIMIZED(fw, was_maximized);
3605 get_unshaded_geometry(fw, drag);
3606 SET_MAXIMIZED(fw, 0);
3608 if (do_resize_opaque)
3610 mr_args = frame_create_move_resize_args(
3611 fw, FRAME_MR_OPAQUE, &fw->g.frame, &fw->g.frame, 0,
3612 DIR_NONE);
3614 else
3616 Scr.flags.is_wire_frame_displayed = True;
3618 MyXGrabKeyboard(dpy);
3620 sorig = *drag;
3621 ymotion = 0;
3622 xmotion = 0;
3624 /* pop up a resize dimensions window */
3625 if (!Scr.gs.do_hide_resize_window)
3627 position_geometry_window(NULL);
3628 XMapRaised(dpy, Scr.SizeWindow);
3630 DisplaySize(fw, exc->x.elast, orig->width, orig->height, True, True);
3632 if (dir != DIR_NONE)
3634 int grav;
3636 grav = gravity_dir_to_grav(dir);
3637 gravity_get_offsets(grav, &xmotion, &ymotion);
3638 xmotion = -xmotion;
3639 ymotion = -ymotion;
3641 if (xmotion == 0 && ymotion == 0)
3643 __resize_get_dir_from_window(&xmotion, &ymotion, fw, PressedW);
3645 if (FW_W_TITLE(fw) != None && PressedW == FW_W_TITLE(fw))
3647 /* title was pressed to start the resize */
3648 called_from_title = True;
3650 else
3652 for (i = NUMBER_OF_TITLE_BUTTONS; i--; )
3654 /* see if the title button was pressed to that the
3655 * resize */
3656 if (FW_W_BUTTON(fw, i) != None &&
3657 FW_W_BUTTON(fw, i) == PressedW)
3659 /* yes */
3660 called_from_title = True;
3664 /* don't warp if the resize was triggered by a press somwhere on the
3665 * title bar */
3666 if (PressedW != Scr.Root && xmotion == 0 && ymotion == 0 &&
3667 !called_from_title)
3669 __resize_get_dir_proximity(
3670 &xmotion, &ymotion, fw, orig->width - px,
3671 orig->height - py, px, py);
3672 if (xmotion != 0 || ymotion != 0)
3674 do_warp_to_border = True;
3677 if (!IS_SHADED(fw))
3679 __resize_get_refpos(
3680 &ref_x, &ref_y, xmotion, ymotion, orig->width,
3681 orig->height, fw);
3683 else
3685 switch (SHADED_DIR(fw))
3687 case DIR_N:
3688 case DIR_NW:
3689 case DIR_NE:
3690 if (ymotion == -1)
3692 ymotion = 0;
3694 break;
3695 case DIR_S:
3696 case DIR_SW:
3697 case DIR_SE:
3698 if (ymotion == 1)
3700 ymotion = 0;
3702 break;
3703 default:
3704 break;
3706 switch (SHADED_DIR(fw))
3708 case DIR_E:
3709 case DIR_NE:
3710 case DIR_SE:
3711 if (xmotion == 1)
3713 xmotion = 0;
3715 break;
3716 case DIR_W:
3717 case DIR_NW:
3718 case DIR_SW:
3719 if (xmotion == -1)
3721 xmotion = 0;
3723 break;
3724 default:
3725 break;
3727 __resize_get_refpos(
3728 &ref_x, &ref_y, xmotion, ymotion, fw->g.frame.width,
3729 fw->g.frame.height, fw);
3731 x_off = 0;
3732 y_off = 0;
3733 if (do_warp_to_border == True)
3735 int dx;
3736 int dy;
3738 dx = (xmotion == 0) ? px : ref_x;
3739 dy = (ymotion == 0) ? py : ref_y;
3740 /* warp the pointer to the border */
3741 FWarpPointer(
3742 dpy, None, ResizeWindow, 0, 0, 1, 1, dx, dy);
3743 XFlush(dpy);
3745 else if (xmotion != 0 || ymotion != 0)
3747 /* keep the distance between pointer and border */
3748 x_off = (xmotion == 0) ? 0 : ref_x - px;
3749 y_off = (ymotion == 0) ? 0 : ref_y - py;
3751 else
3753 /* wait until the pointer hits a border before making a
3754 * decision about the resize direction */
3757 /* draw the rubber-band window */
3758 if (!do_resize_opaque)
3760 draw_move_resize_grid(
3761 drag->x, drag->y, drag->width - 1, drag->height - 1);
3763 /* kick off resizing without requiring any motion if invoked with a key
3764 * press */
3765 if (exc->x.elast->type == KeyPress)
3767 int xo;
3768 int yo;
3770 if (FQueryPointer(
3771 dpy, Scr.Root, &JunkRoot, &JunkChild, &stashed_x,
3772 &stashed_y, &JunkX, &JunkY, &JunkMask) == False)
3774 /* pointer is on a different screen */
3775 stashed_x = 0;
3776 stashed_y = 0;
3778 xo = 0;
3779 yo = 0;
3780 __resize_step(
3781 exc, stashed_x, stashed_y, &xo, &yo, drag, orig,
3782 &xmotion, &ymotion, do_resize_opaque, True);
3784 else
3786 stashed_x = stashed_y = -1;
3789 /* loop to resize */
3790 memset(&ev, 0, sizeof(ev));
3791 while (!is_finished && bad_window != FW_W(fw))
3793 int rc = 0;
3795 /* block until there is an interesting event */
3796 while (rc != -1 &&
3797 (!FPending(dpy) || !FCheckMaskEvent(dpy, evmask, &ev)))
3799 rc = HandlePaging(
3800 &ev, dx, dy, &x, &y, &delta_x, &delta_y, False,
3801 False, True, fw->edge_delay_ms_resize);
3802 if (rc == 1)
3804 /* Fake an event to force window reposition */
3805 ev.type = MotionNotify;
3806 ev.xmotion.time = fev_get_evtime();
3807 fForceRedraw = True;
3808 break;
3811 if (rc == -1)
3813 FMaskEvent(
3814 dpy,
3815 evmask | EnterWindowMask | LeaveWindowMask,
3816 &ev);
3818 if (ev.type == MotionNotify ||
3819 ev.type == EnterNotify || ev.type == LeaveNotify)
3821 Bool is_motion;
3823 is_motion = (ev.type == MotionNotify) ? True : False;
3824 /* discard any extra motion events before a release */
3825 /* dv (2004-07-01): With XFree 4.1.0.1, some Mouse
3826 * events are not reported to fvwm when the pointer
3827 * moves very fast and suddenly stops in the corner of
3828 * the screen. Handle EnterNotify/LeaveNotify events
3829 * too to get an idea where the pointer might be. */
3830 while (
3831 FCheckMaskEvent(
3832 dpy, ButtonMotionMask |
3833 PointerMotionMask | ButtonReleaseMask |
3834 ButtonPressMask | EnterWindowMask |
3835 LeaveWindowMask, &ev) == True)
3837 if (ev.type == ButtonRelease ||
3838 ev.type == ButtonPress ||
3839 ev.type == KeyPress)
3841 break;
3845 if (ev.type == EnterNotify || ev.type == LeaveNotify)
3847 XEvent e2;
3848 int x;
3849 int y;
3851 /* Query the pointer to catch the latest information.
3852 * This *is* necessary. */
3853 if (FQueryPointer(
3854 dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
3855 &y, &JunkX, &JunkY, &JunkMask) == True)
3857 /* Must NOT use button_mask here, or resize
3858 * will not work with num lock */
3859 fev_make_null_event(&e2, dpy);
3860 e2.type = MotionNotify;
3861 e2.xmotion.time = fev_get_evtime();
3862 e2.xmotion.x_root = x;
3863 e2.xmotion.y_root = y;
3864 e2.xmotion.state = JunkMask;
3865 e2.xmotion.same_screen = True;
3866 ev = e2;
3867 fev_fake_event(&ev);
3869 else
3871 /* pointer is on a different screen,
3872 * ignore event */
3876 is_done = False;
3877 /* Handle a limited number of key press events to allow
3878 * mouseless operation */
3879 if (ev.type == KeyPress)
3881 Keyboard_shortcuts(&ev, fw, NULL, NULL, ButtonRelease);
3882 if (ev.type == ButtonRelease)
3884 do_send_cn = True;
3887 switch (ev.type)
3889 case ButtonPress:
3890 is_done = True;
3891 if (ev.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
3892 ((Button1Mask << (ev.xbutton.button - 1)) &
3893 button_mask))
3895 /* No new button was pressed, just a delayed
3896 * event */
3897 break;
3899 /* Abort the resize if
3900 * - the move started with a pressed button and
3901 * another button was pressed during the operation
3902 * - no button was started at the beginning and any
3903 * button except button 1 was pressed. */
3904 if (button_mask || (ev.xbutton.button != 1))
3906 fButtonAbort = True;
3907 /* fall through */
3909 else
3911 is_finished = True;
3912 do_send_cn = True;
3913 break;
3915 case KeyPress:
3916 /* simple code to bag out of move - CKH */
3917 if (fButtonAbort ||
3918 XLookupKeysym(&ev.xkey, 0) == XK_Escape)
3920 is_aborted = True;
3921 do_send_cn = True;
3922 is_finished = True;
3924 is_done = True;
3925 break;
3927 case ButtonRelease:
3928 is_finished = True;
3929 is_done = True;
3930 break;
3932 case MotionNotify:
3933 if (ev.xmotion.same_screen == False)
3935 continue;
3937 if (!fForceRedraw)
3939 x = ev.xmotion.x_root;
3940 y = ev.xmotion.y_root;
3941 /* resize before paging request to prevent
3942 * resize from lagging * mouse - mab */
3943 __resize_step(
3944 exc, x, y, &x_off, &y_off, drag, orig,
3945 &xmotion, &ymotion, do_resize_opaque,
3946 is_direction_fixed);
3947 /* need to move the viewport */
3948 HandlePaging(
3949 &ev, dx, dy, &x, &y, &delta_x,
3950 &delta_y, False, False, False,
3951 fw->edge_delay_ms_resize);
3953 /* redraw outline if we paged - mab */
3954 if (delta_x != 0 || delta_y != 0)
3956 sorig.x -= delta_x;
3957 sorig.y -= delta_y;
3958 drag->x -= delta_x;
3959 drag->y -= delta_y;
3961 __resize_step(
3962 exc, x, y, &x_off, &y_off, drag, orig,
3963 &xmotion, &ymotion, do_resize_opaque,
3964 is_direction_fixed);
3966 fForceRedraw = False;
3967 is_done = True;
3968 break;
3970 case PropertyNotify:
3972 evh_args_t ea;
3973 exec_context_changes_t ecc;
3975 ecc.x.etrigger = &ev;
3976 ea.exc = exc_clone_context(exc, &ecc, ECC_ETRIGGER);
3977 HandlePropertyNotify(&ea);
3978 exc_destroy_context(ea.exc);
3979 is_done = True;
3980 break;
3983 default:
3984 break;
3986 if (!is_done)
3988 if (!do_resize_opaque)
3990 /* must undraw the rubber band in case the
3991 * event causes some drawing */
3992 switch_move_resize_grid(False);
3994 dispatch_event(&ev);
3995 if (!do_resize_opaque)
3997 draw_move_resize_grid(
3998 drag->x, drag->y, drag->width - 1,
3999 drag->height - 1);
4002 else
4004 if (do_resize_opaque)
4006 /* only do this with opaque resizes, (i.e. the
4007 * server is not grabbed) */
4008 BroadcastConfig(M_CONFIGURE_WINDOW, fw);
4009 FlushAllMessageQueues();
4014 /* erase the rubber-band */
4015 if (!do_resize_opaque)
4017 switch_move_resize_grid(False);
4019 /* pop down the size window */
4020 if (!Scr.gs.do_hide_resize_window)
4022 XUnmapWindow(dpy, Scr.SizeWindow);
4024 if (is_aborted || bad_window == FW_W(fw))
4026 /* return pointer if aborted resize was invoked with key */
4027 if (stashed_x >= 0)
4029 FWarpPointer(
4030 dpy, None, Scr.Root, 0, 0, 0, 0, stashed_x,
4031 stashed_y);
4033 if (was_maximized)
4035 /* since we aborted the resize, the window is still
4036 * maximized */
4037 SET_MAXIMIZED(fw, 1);
4039 if (do_resize_opaque)
4041 int xo;
4042 int yo;
4043 rectangle g;
4045 xo = 0;
4046 yo = 0;
4047 xmotion = 1;
4048 ymotion = 1;
4049 g = sorig;
4050 __resize_step(
4051 exc, sorig.x, sorig.y, &xo, &yo, &g, orig,
4052 &xmotion, &ymotion, do_resize_opaque, True);
4054 if (vx != Scr.Vx || vy != Scr.Vy)
4056 MoveViewport(vx, vy, False);
4058 /* restore all geometry-related info */
4059 fw->g = g_backup;
4060 if (bad_window == FW_W(fw))
4062 XUnmapWindow(dpy, FW_W_FRAME(fw));
4063 border_undraw_decorations(fw);
4064 XBell(dpy, 0);
4067 else if (!is_aborted && bad_window != FW_W(fw))
4069 rectangle new_g;
4071 /* size will be >= to requested */
4072 constrain_size(
4073 fw, exc->x.elast, &drag->width, &drag->height,
4074 xmotion, ymotion, CS_ROUND_UP);
4075 if (IS_SHADED(fw))
4077 get_shaded_geometry(fw, &new_g, drag);
4079 else
4081 new_g = *drag;
4083 if (do_resize_opaque)
4085 frame_update_move_resize_args(mr_args, &new_g);
4087 else
4089 frame_setup_window(
4090 fw, new_g.x, new_g.y, new_g.width,
4091 new_g.height, False);
4093 if (IS_SHADED(fw))
4095 fw->g.normal.width = drag->width;
4096 fw->g.normal.height = drag->height;
4099 if (is_aborted && was_maximized)
4101 /* force redraw */
4102 border_draw_decorations(
4103 fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
4104 NULL, NULL);
4106 if (Scr.bo.do_install_root_cmap)
4108 UninstallRootColormap();
4110 else
4112 UninstallFvwmColormap();
4114 ResizeWindow = None;
4115 if (!do_resize_opaque)
4117 /* Throw away some events that dont interest us right now. */
4118 discard_events(EnterWindowMask|LeaveWindowMask);
4119 Scr.flags.is_wire_frame_displayed = False;
4120 MyXUngrabServer(dpy);
4122 if (mr_args != NULL)
4124 frame_free_move_resize_args(fw, mr_args);
4126 if (do_send_cn == True)
4128 rectangle g;
4130 if (is_aborted)
4132 g = sorig;
4134 else
4136 g = *drag;
4138 SendConfigureNotify(fw, g.x, g.y, g.width, g.height, 0, True);
4140 MyXUngrabKeyboard(dpy);
4141 WaitForButtonsUp(True);
4142 UngrabEm(GRAB_NORMAL);
4143 Scr.flags.do_edge_wrap_x = edge_wrap_x;
4144 Scr.flags.do_edge_wrap_y = edge_wrap_y;
4145 update_absolute_geometry(fw);
4146 maximize_adjust_offset(fw);
4147 GNOME_SetWinArea(fw);
4148 if (is_aborted)
4150 return False;
4153 return True;
4156 void CMD_Resize(F_CMD_ARGS)
4158 FvwmWindow *fw = exc->w.fw;
4160 if (IS_EWMH_FULLSCREEN(fw))
4162 /* do not unmaximize ! */
4163 CMD_ResizeMaximize(F_PASS_ARGS);
4164 return;
4167 __resize_window(F_PASS_ARGS);
4169 return;
4172 /* ----------------------------- maximizing code --------------------------- */
4174 Bool is_window_sticky_across_pages(FvwmWindow *fw)
4176 if (IS_STICKY_ACROSS_PAGES(fw) ||
4177 (IS_ICONIFIED(fw) && IS_ICON_STICKY_ACROSS_PAGES(fw)))
4179 return True;
4181 else
4183 return False;
4187 Bool is_window_sticky_across_desks(FvwmWindow *fw)
4189 if (IS_STICKY_ACROSS_DESKS(fw) ||
4190 (IS_ICONIFIED(fw) && IS_ICON_STICKY_ACROSS_DESKS(fw)))
4192 return True;
4194 else
4196 return False;
4200 static void move_sticky_window_to_same_page(
4201 int *x11, int *x12, int *y11, int *y12,
4202 int x21, int x22, int y21, int y22)
4204 /* make sure the x coordinate is on the same page as the reference
4205 * window */
4206 if (*x11 >= x22)
4208 while (*x11 >= x22)
4210 *x11 -= Scr.MyDisplayWidth;
4211 *x12 -= Scr.MyDisplayWidth;
4214 else if (*x12 <= x21)
4216 while (*x12 <= x21)
4218 *x11 += Scr.MyDisplayWidth;
4219 *x12 += Scr.MyDisplayWidth;
4222 /* make sure the y coordinate is on the same page as the reference
4223 * window */
4224 if (*y11 >= y22)
4226 while (*y11 >= y22)
4228 *y11 -= Scr.MyDisplayHeight;
4229 *y12 -= Scr.MyDisplayHeight;
4232 else if (*y12 <= y21)
4234 while (*y12 <= y21)
4236 *y11 += Scr.MyDisplayHeight;
4237 *y12 += Scr.MyDisplayHeight;
4241 return;
4244 static void MaximizeHeight(
4245 FvwmWindow *win, int win_width, int win_x, int *win_height,
4246 int *win_y, Bool grow_up, Bool grow_down, int top_border,
4247 int bottom_border, int *layers)
4249 FvwmWindow *cwin;
4250 int x11, x12, x21, x22;
4251 int y11, y12, y21, y22;
4252 int new_y1, new_y2;
4253 rectangle g;
4254 Bool rc;
4256 x11 = win_x; /* Start x */
4257 y11 = *win_y; /* Start y */
4258 x12 = x11 + win_width; /* End x */
4259 y12 = y11 + *win_height; /* End y */
4260 new_y1 = top_border;
4261 new_y2 = bottom_border;
4263 for (cwin = Scr.FvwmRoot.next; cwin; cwin = cwin->next)
4265 if (cwin == win ||
4266 (cwin->Desk != win->Desk &&
4267 !is_window_sticky_across_desks(cwin)))
4269 continue;
4271 if ((layers[0] >= 0 && cwin->layer < layers[0]) ||
4272 (layers[1] >= 0 && cwin->layer > layers[1]))
4274 continue;
4276 rc = get_visible_window_or_icon_geometry(cwin, &g);
4277 if (rc == False)
4279 continue;
4281 x21 = g.x;
4282 y21 = g.y;
4283 x22 = x21 + g.width;
4284 y22 = y21 + g.height;
4285 if (is_window_sticky_across_pages(cwin))
4287 move_sticky_window_to_same_page(
4288 &x21, &x22, &new_y1, &new_y2, x11, x12, y11,
4289 y12);
4292 /* Are they in the same X space? */
4293 if (!((x22 <= x11) || (x21 >= x12)))
4295 if ((y22 <= y11) && (y22 >= new_y1))
4297 new_y1 = y22;
4299 else if ((y12 <= y21) && (new_y2 >= y21))
4301 new_y2 = y21;
4305 if (!grow_up)
4307 new_y1 = y11;
4309 if (!grow_down)
4311 new_y2 = y12;
4313 *win_height = new_y2 - new_y1;
4314 *win_y = new_y1;
4316 return;
4319 static void MaximizeWidth(
4320 FvwmWindow *win, int *win_width, int *win_x, int win_height,
4321 int win_y, Bool grow_left, Bool grow_right, int left_border,
4322 int right_border, int *layers)
4324 FvwmWindow *cwin;
4325 int x11, x12, x21, x22;
4326 int y11, y12, y21, y22;
4327 int new_x1, new_x2;
4328 rectangle g;
4329 Bool rc;
4331 x11 = *win_x; /* Start x */
4332 y11 = win_y; /* Start y */
4333 x12 = x11 + *win_width; /* End x */
4334 y12 = y11 + win_height; /* End y */
4335 new_x1 = left_border;
4336 new_x2 = right_border;
4338 for (cwin = Scr.FvwmRoot.next; cwin; cwin = cwin->next)
4340 if (cwin == win ||
4341 (cwin->Desk != win->Desk &&
4342 !is_window_sticky_across_desks(cwin)))
4344 continue;
4346 if ((layers[0] >= 0 && cwin->layer < layers[0]) ||
4347 (layers[1] >= 0 && cwin->layer > layers[1]))
4349 continue;
4351 rc = get_visible_window_or_icon_geometry(cwin, &g);
4352 if (rc == False)
4354 continue;
4356 x21 = g.x;
4357 y21 = g.y;
4358 x22 = x21 + g.width;
4359 y22 = y21 + g.height;
4360 if (is_window_sticky_across_pages(cwin))
4362 move_sticky_window_to_same_page(
4363 &new_x1, &new_x2, &y21, &y22, x11, x12, y11,
4364 y12);
4367 /* Are they in the same Y space? */
4368 if (!((y22 <= y11) || (y21 >= y12)))
4370 if ((x22 <= x11) && (x22 >= new_x1))
4372 new_x1 = x22;
4374 else if ((x12 <= x21) && (new_x2 >= x21))
4376 new_x2 = x21;
4380 if (!grow_left)
4382 new_x1 = x11;
4384 if (!grow_right)
4386 new_x2 = x12;
4388 *win_width = new_x2 - new_x1;
4389 *win_x = new_x1;
4391 return;
4394 static void unmaximize_fvwm_window(
4395 FvwmWindow *fw)
4397 rectangle new_g;
4399 SET_MAXIMIZED(fw, 0);
4400 get_relative_geometry(&new_g, &fw->g.normal);
4401 if (IS_SHADED(fw))
4403 get_shaded_geometry(fw, &new_g, &new_g);
4405 frame_setup_window(
4406 fw, new_g.x, new_g.y, new_g.width, new_g.height, True);
4407 border_draw_decorations(
4408 fw, PART_ALL, (Scr.Hilite == fw), True, CLEAR_ALL, NULL, NULL);
4409 if (IS_EWMH_FULLSCREEN(fw))
4411 SET_EWMH_FULLSCREEN(fw, False);
4412 if (DO_EWMH_USE_STACKING_HINTS(fw))
4414 new_layer(fw, fw->ewmh_normal_layer);
4416 apply_decor_change(fw);
4418 return;
4421 static void maximize_fvwm_window(
4422 FvwmWindow *fw, rectangle *geometry)
4424 SET_MAXIMIZED(fw, 1);
4425 fw->g.max_defect.width = 0;
4426 fw->g.max_defect.height = 0;
4427 constrain_size(
4428 fw, NULL, &geometry->width, &geometry->height, 0, 0,
4429 CS_UPDATE_MAX_DEFECT);
4430 fw->g.max = *geometry;
4431 if (IS_SHADED(fw))
4433 get_shaded_geometry(fw, geometry, &fw->g.max);
4435 frame_setup_window(
4436 fw, geometry->x, geometry->y, geometry->width,
4437 geometry->height, True);
4438 border_draw_decorations(
4439 fw, PART_ALL, (Scr.Hilite == fw), True, CLEAR_ALL, NULL, NULL);
4440 update_absolute_geometry(fw);
4441 /* remember the offset between old and new position in case the
4442 * maximized window is moved more than the screen width/height. */
4443 fw->g.max_offset.x = fw->g.normal.x - fw->g.max.x;
4444 fw->g.max_offset.y = fw->g.normal.y - fw->g.max.y;
4445 #if 0
4446 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);
4447 #endif
4449 return;
4454 * Procedure:
4455 * (Un)Maximize a window.
4458 void CMD_Maximize(F_CMD_ARGS)
4460 int page_x, page_y;
4461 int val1, val2, val1_unit, val2_unit;
4462 int toggle;
4463 char *token;
4464 char *taction;
4465 Bool grow_up = False;
4466 Bool grow_down = False;
4467 Bool grow_left = False;
4468 Bool grow_right = False;
4469 Bool do_force_maximize = False;
4470 Bool is_screen_given = False;
4471 Bool ignore_working_area = False;
4472 int layers[2] = { -1, -1 };
4473 Bool global_flag_parsed = False;
4474 int scr_x, scr_y;
4475 int scr_w, scr_h;
4476 rectangle new_g;
4477 FvwmWindow *fw = exc->w.fw;
4479 if (
4480 !is_function_allowed(
4481 F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
4483 XBell(dpy, 0);
4484 return;
4486 /* Check for "global" flag ("absolute" is for compatibility with E) */
4487 while (!global_flag_parsed)
4489 token = PeekToken(action, &taction);
4490 if (!token)
4492 global_flag_parsed = True;
4494 else
4496 if (StrEquals(token, "screen"))
4498 int scr;
4500 is_screen_given = True;
4501 token = PeekToken(taction, &action);
4502 scr = FScreenGetScreenArgument(
4503 token, FSCREEN_SPEC_PRIMARY);
4504 FScreenGetScrRect(
4505 NULL, scr, &scr_x, &scr_y, &scr_w,
4506 &scr_h);
4508 else if (StrEquals(token, "ewmhiwa"))
4510 ignore_working_area = True;
4511 action = taction;
4513 else if (StrEquals(token, "growonwindowlayer"))
4515 layers[0] = fw->layer;
4516 layers[1] = fw->layer;
4517 action = taction;
4519 else if (StrEquals(token, "growonlayers"))
4521 int n;
4523 n = GetIntegerArguments(
4524 taction, &action, layers, 2);
4525 if (n != 2)
4527 layers[0] = -1;
4528 layers[1] = -1;
4531 else
4533 global_flag_parsed = True;
4537 toggle = ParseToggleArgument(action, &action, -1, 0);
4538 if (toggle == 0 && !IS_MAXIMIZED(fw))
4540 return;
4543 if (toggle == 1 && IS_MAXIMIZED(fw))
4545 /* Fake that the window is not maximized. */
4546 do_force_maximize = True;
4549 /* find the new page and geometry */
4550 new_g.x = fw->g.frame.x;
4551 new_g.y = fw->g.frame.y;
4552 new_g.width = fw->g.frame.width;
4553 new_g.height = fw->g.frame.height;
4554 get_page_offset_check_visible(&page_x, &page_y, fw);
4556 /* Check if we should constrain rectangle to some Xinerama screen */
4557 if (!is_screen_given)
4559 fscreen_scr_arg fscr;
4561 fscr.xypos.x = fw->g.frame.x + fw->g.frame.width / 2 - page_x;
4562 fscr.xypos.y = fw->g.frame.y + fw->g.frame.height / 2 - page_y;
4563 FScreenGetScrRect(
4564 &fscr, FSCREEN_XYPOS, &scr_x, &scr_y, &scr_w, &scr_h);
4567 if (!ignore_working_area)
4569 EWMH_GetWorkAreaIntersection(
4570 fw, &scr_x, &scr_y, &scr_w, &scr_h,
4571 EWMH_MAXIMIZE_MODE(fw));
4573 #if 0
4574 fprintf(stderr, "%s: page=(%d,%d), scr=(%d,%d, %dx%d)\n", __FUNCTION__,
4575 page_x, page_y, scr_x, scr_y, scr_w, scr_h);
4576 #endif
4578 /* parse first parameter */
4579 val1_unit = scr_w;
4580 token = PeekToken(action, &taction);
4581 if (token && StrEquals(token, "grow"))
4583 grow_left = True;
4584 grow_right = True;
4585 val1 = 100;
4586 val1_unit = scr_w;
4588 else if (token && StrEquals(token, "growleft"))
4590 grow_left = True;
4591 val1 = 100;
4592 val1_unit = scr_w;
4594 else if (token && StrEquals(token, "growright"))
4596 grow_right = True;
4597 val1 = 100;
4598 val1_unit = scr_w;
4600 else
4602 if (GetOnePercentArgument(token, &val1, &val1_unit) == 0)
4604 val1 = 100;
4605 val1_unit = scr_w;
4607 else if (val1 < 0)
4609 /* handle negative offsets */
4610 if (val1_unit == scr_w)
4612 val1 = 100 + val1;
4614 else
4616 val1 = scr_w + val1;
4621 /* parse second parameter */
4622 val2_unit = scr_h;
4623 token = PeekToken(taction, NULL);
4624 if (token && StrEquals(token, "grow"))
4626 grow_up = True;
4627 grow_down = True;
4628 val2 = 100;
4629 val2_unit = scr_h;
4631 else if (token && StrEquals(token, "growup"))
4633 grow_up = True;
4634 val2 = 100;
4635 val2_unit = scr_h;
4637 else if (token && StrEquals(token, "growdown"))
4639 grow_down = True;
4640 val2 = 100;
4641 val2_unit = scr_h;
4643 else
4645 if (GetOnePercentArgument(token, &val2, &val2_unit) == 0)
4647 val2 = 100;
4648 val2_unit = scr_h;
4650 else if (val2 < 0)
4652 /* handle negative offsets */
4653 if (val2_unit == scr_h)
4655 val2 = 100 + val2;
4657 else
4659 val2 = scr_h + val2;
4664 #if 0
4665 fprintf(stderr, "%s: page=(%d,%d), scr=(%d,%d, %dx%d)\n", __FUNCTION__,
4666 page_x, page_y, scr_x, scr_y, scr_w, scr_h);
4667 #endif
4669 if (IS_MAXIMIZED(fw) && !do_force_maximize)
4671 unmaximize_fvwm_window(fw);
4673 else /* maximize */
4675 /* handle command line arguments */
4676 if (grow_up || grow_down)
4678 MaximizeHeight(
4679 fw, new_g.width, new_g.x, &new_g.height,
4680 &new_g.y, grow_up, grow_down, page_y + scr_y,
4681 page_y + scr_y + scr_h, layers);
4683 else if (val2 > 0)
4685 new_g.height = val2 * val2_unit / 100;
4686 new_g.y = page_y + scr_y;
4688 if (grow_left || grow_right)
4690 MaximizeWidth(
4691 fw, &new_g.width, &new_g.x, new_g.height,
4692 new_g.y, grow_left, grow_right,
4693 page_x + scr_x, page_x + scr_x + scr_w,
4694 layers);
4696 else if (val1 >0)
4698 new_g.width = val1 * val1_unit / 100;
4699 new_g.x = page_x + scr_x;
4701 if (val1 == 0 && val2 == 0)
4703 new_g.x = page_x + scr_x;
4704 new_g.y = page_y + scr_y;
4705 new_g.height = scr_h;
4706 new_g.width = scr_w;
4708 /* now maximize it */
4709 maximize_fvwm_window(fw, &new_g);
4711 EWMH_SetWMState(fw, False);
4712 GNOME_SetWinArea(fw);
4714 return;
4719 * Same as CMD_Resize and CMD_ResizeMove, but the window ends up maximized
4720 * without touching the normal geometry.
4723 void CMD_ResizeMaximize(F_CMD_ARGS)
4725 rectangle normal_g;
4726 rectangle max_g;
4727 Bool was_resized;
4728 FvwmWindow *fw = exc->w.fw;
4730 /* keep a copy of the old geometry */
4731 normal_g = fw->g.normal;
4732 /* resize the window normally */
4733 was_resized = __resize_window(F_PASS_ARGS);
4734 if (was_resized == True)
4736 /* set the new geometry as the maximized geometry and restore
4737 * the old normal geometry */
4738 max_g = fw->g.normal;
4739 max_g.x -= Scr.Vx;
4740 max_g.y -= Scr.Vy;
4741 fw->g.normal = normal_g;
4742 /* and mark it as maximized */
4743 maximize_fvwm_window(fw, &max_g);
4745 EWMH_SetWMState(fw, False);
4747 return;
4750 void CMD_ResizeMoveMaximize(F_CMD_ARGS)
4752 rectangle normal_g;
4753 rectangle max_g;
4754 Bool was_resized;
4755 FvwmWindow *fw = exc->w.fw;
4757 /* keep a copy of the old geometry */
4758 normal_g = fw->g.normal;
4759 /* resize the window normally */
4760 was_resized = resize_move_window(F_PASS_ARGS);
4761 if (was_resized == True)
4763 /* set the new geometry as the maximized geometry and restore
4764 * the old normal geometry */
4765 max_g = fw->g.normal;
4766 max_g.x -= Scr.Vx;
4767 max_g.y -= Scr.Vy;
4768 fw->g.normal = normal_g;
4769 /* and mark it as maximized */
4770 maximize_fvwm_window(fw, &max_g);
4772 EWMH_SetWMState(fw, False);
4774 return;
4777 /* ----------------------------- stick code -------------------------------- */
4779 int stick_across_pages(F_CMD_ARGS, int toggle)
4781 FvwmWindow *fw = exc->w.fw;
4783 if ((toggle == 1 && IS_STICKY_ACROSS_PAGES(fw)) ||
4784 (toggle == 0 && !IS_STICKY_ACROSS_PAGES(fw)))
4786 return 0;
4788 if (IS_STICKY_ACROSS_PAGES(fw))
4790 SET_STICKY_ACROSS_PAGES(fw, 0);
4792 else
4794 if (!IsRectangleOnThisPage(&fw->g.frame, Scr.CurrentDesk))
4796 action = "";
4797 __move_window(F_PASS_ARGS, False, MOVE_PAGE);
4799 SET_STICKY_ACROSS_PAGES(fw, 1);
4802 return 1;
4805 int stick_across_desks(F_CMD_ARGS, int toggle)
4807 FvwmWindow *fw = exc->w.fw;
4809 if ((toggle == 1 && IS_STICKY_ACROSS_DESKS(fw)) ||
4810 (toggle == 0 && !IS_STICKY_ACROSS_DESKS(fw)))
4812 return 0;
4815 if (IS_STICKY_ACROSS_DESKS(fw))
4817 SET_STICKY_ACROSS_DESKS(fw, 0);
4818 fw->Desk = Scr.CurrentDesk;
4819 GNOME_SetDeskCount();
4820 GNOME_SetDesk(fw);
4822 else
4824 if (fw->Desk != Scr.CurrentDesk)
4826 do_move_window_to_desk(fw, Scr.CurrentDesk);
4828 SET_STICKY_ACROSS_DESKS(fw, 1);
4831 return 1;
4834 static void __handle_stick_exit(
4835 FvwmWindow *fw, int do_not_draw, int do_silently)
4837 if (do_not_draw == 0)
4839 border_draw_decorations(
4840 fw, PART_TITLE | PART_BUTTONS, (Scr.Hilite==fw), True,
4841 CLEAR_ALL, NULL, NULL);
4843 if (!do_silently)
4845 BroadcastConfig(M_CONFIGURE_WINDOW,fw);
4846 EWMH_SetWMState(fw, False);
4847 EWMH_SetWMDesktop(fw);
4848 GNOME_SetHints(fw);
4851 return;
4854 void handle_stick_across_pages(
4855 F_CMD_ARGS, int toggle, int do_not_draw, int do_silently)
4857 FvwmWindow *fw = exc->w.fw;
4858 int did_change;
4860 did_change = stick_across_pages(F_PASS_ARGS, toggle);
4861 if (did_change)
4863 __handle_stick_exit(fw, do_not_draw, do_silently);
4866 return;
4869 void handle_stick_across_desks(
4870 F_CMD_ARGS, int toggle, int do_not_draw, int do_silently)
4872 FvwmWindow *fw = exc->w.fw;
4873 int did_change;
4875 did_change = stick_across_desks(F_PASS_ARGS, toggle);
4876 if (did_change)
4878 __handle_stick_exit(fw, do_not_draw, do_silently);
4881 return;
4884 void handle_stick(
4885 F_CMD_ARGS, int toggle_page, int toggle_desk, int do_not_draw,
4886 int do_silently)
4888 FvwmWindow *fw = exc->w.fw;
4889 int did_change;
4891 did_change = 0;
4892 did_change |= stick_across_desks(F_PASS_ARGS, toggle_desk);
4893 did_change |= stick_across_pages(F_PASS_ARGS, toggle_page);
4894 if (did_change)
4896 __handle_stick_exit(fw, do_not_draw, do_silently);
4899 return;
4902 void CMD_Stick(F_CMD_ARGS)
4904 int toggle;
4906 toggle = ParseToggleArgument(action, &action, -1, 0);
4907 if (toggle == -1 && IS_STICKY_ACROSS_DESKS(exc->w.fw) !=
4908 IS_STICKY_ACROSS_PAGES(exc->w.fw))
4910 /* don't switch between only stickypage and only stickydesk.
4911 * rather switch it off completely */
4912 toggle = 0;
4914 handle_stick(F_PASS_ARGS, toggle, toggle, 0, 0);
4916 return;
4919 void CMD_StickAcrossPages(F_CMD_ARGS)
4921 int toggle;
4923 toggle = ParseToggleArgument(action, &action, -1, 0);
4924 handle_stick_across_pages(F_PASS_ARGS, toggle, 0, 0);
4926 return;
4929 void CMD_StickAcrossDesks(F_CMD_ARGS)
4931 int toggle;
4933 toggle = ParseToggleArgument(action, &action, -1, 0);
4934 handle_stick_across_desks(F_PASS_ARGS, toggle, 0, 0);
4936 return;