Fix segfault when creating a tearoff menu using a Pixmap background.
[fvwm.git] / fvwm / move_resize.c
blobf1644c47cb5fa54002a0a04723c867a1662bccdd
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 /* syntax error, ignore rest of string */
407 break;
409 s1 += n;
410 /* parse suffix */
411 n = ParsePositionArgumentSuffix(&f, s1, wfactor, sfactor);
412 s1 += n;
413 final_pos += __get_shift(val, f);
415 *pFinalPos = final_pos;
417 return 1;
420 /* GetMoveArguments is used for Move & AnimatedMove
421 * It lets you specify in all the following ways
422 * 20 30 Absolute percent position, from left edge and top
423 * -50 50 Absolute percent position, from right edge and top
424 * 10p 5p Absolute pixel position
425 * 10p -0p Absolute pixel position, from bottom
426 * w+5 w-10p Relative position, right 5%, up ten pixels
427 * m+5 m-10p Pointer relative position, right 5%, up ten pixels
428 * Returns 2 when x & y have parsed without error, 0 otherwise
430 int GetMoveArguments(
431 char **paction, int w, int h, int *pFinalX, int *pFinalY,
432 Bool *fWarp, Bool *fPointer, Bool fKeep)
434 char *s1 = NULL;
435 char *s2 = NULL;
436 char *warp = NULL;
437 char *action;
438 char *naction;
439 int scr_x = 0;
440 int scr_y = 0;
441 int scr_w = Scr.MyDisplayWidth;
442 int scr_h = Scr.MyDisplayHeight;
443 int retval = 0;
445 if (!paction)
447 return 0;
449 action = *paction;
450 action = GetNextToken(action, &s1);
451 if (s1 && fPointer && StrEquals(s1, "pointer"))
453 *fPointer = True;
454 *paction = action;
455 free(s1);
456 return 0;
458 if (s1 && StrEquals(s1, "screen"))
460 char *token;
461 int scr;
462 fscreen_scr_arg arg;
463 fscreen_scr_arg* parg;
465 free(s1);
466 token = PeekToken(action, &action);
467 scr = FScreenGetScreenArgument(token, FSCREEN_SPEC_PRIMARY);
468 if (scr == FSCREEN_XYPOS)
470 arg.xypos.x = *pFinalX;
471 arg.xypos.y = *pFinalY;
472 parg = &arg;
474 else
476 parg = NULL;
478 FScreenGetScrRect(parg, scr, &scr_x, &scr_y, &scr_w, &scr_h);
479 action = GetNextToken(action, &s1);
481 action = GetNextToken(action, &s2);
482 if (fWarp)
484 warp = PeekToken(action, &naction);
485 if (StrEquals(warp, "Warp"))
487 *fWarp = True;
488 action = naction;
492 if (s1 != NULL && s2 != NULL)
494 retval = 0;
495 if (fKeep == True && StrEquals(s1, "keep"))
497 retval++;
499 else if (
500 GetOnePositionArgument(
501 s1, *pFinalX, w, pFinalX, (float)scr_w / 100,
502 scr_w, scr_x, True))
504 retval++;
506 if (fKeep == True && StrEquals(s2, "keep"))
508 retval++;
510 else if (
511 GetOnePositionArgument(
512 s2, *pFinalY, h, pFinalY, (float)scr_h / 100,
513 scr_h, scr_y, False))
515 retval++;
517 if (retval == 0)
519 /* make sure warping is off for interactive moves */
520 *fWarp = False;
523 else
525 /* not enough arguments, switch to current page. */
526 while (*pFinalX < 0)
528 *pFinalX = Scr.MyDisplayWidth + *pFinalX;
530 while (*pFinalY < 0)
532 *pFinalY = Scr.MyDisplayHeight + *pFinalY;
536 if (s1)
538 free(s1);
540 if (s2)
542 free(s2);
544 *paction = action;
546 return retval;
549 static int ParseOneResizeArgument(
550 char *arg, int scr_size, int base_size, int size_inc, int add_size,
551 int *ret_size)
553 float factor;
554 int val;
555 int add_base_size = 0;
556 int cch = strlen(arg);
557 int tmp_size;
559 if (cch == 0)
561 return 0;
563 if (StrEquals(arg, "keep"))
565 /* do not change size */
566 return 1;
568 if (arg[cch-1] == 'p')
570 factor = 1;
571 arg[cch-1] = '\0';
573 else if (arg[cch-1] == 'c')
575 factor = size_inc;
576 add_base_size = base_size;
577 arg[cch-1] = '\0';
579 else
581 factor = (float)scr_size / 100.0;
583 if (strcmp(arg,"w") == 0)
585 /* do not change size */
587 else if (sscanf(arg,"w-%d",&val) == 1)
589 tmp_size = (int)(val * factor + 0.5);
590 if (tmp_size < *ret_size)
592 *ret_size -= tmp_size;
594 else
596 *ret_size = 0;
599 else if (sscanf(arg,"w+%d",&val) == 1 || sscanf(arg,"w%d",&val) == 1)
601 tmp_size = (int)(val * factor + 0.5);
602 if (-tmp_size < *ret_size)
604 *ret_size += tmp_size;
606 else
608 *ret_size = 0;
611 else if (sscanf(arg,"-%d",&val) == 1)
613 tmp_size = (int)(val * factor + 0.5);
614 if (tmp_size < scr_size + add_size)
616 *ret_size = scr_size - tmp_size + add_size;
618 else
620 *ret_size = 0;
623 else if (sscanf(arg,"+%d",&val) == 1 || sscanf(arg,"%d",&val) == 1)
625 tmp_size = (int)(val * factor + 0.5);
626 if (-tmp_size < add_size + add_base_size)
628 *ret_size = tmp_size + add_size + add_base_size;
630 else
632 *ret_size = 0;
635 else
637 return 0;
640 return 1;
643 static int GetResizeArguments(
644 char **paction, int x, int y, int w_base, int h_base, int w_inc,
645 int h_inc, size_borders *sb, int *pFinalW, int *pFinalH,
646 direction_t *ret_dir, Bool *is_direction_fixed,
647 Bool *do_warp_to_border)
649 int n;
650 char *naction;
651 char *token;
652 char *s1;
653 char *s2;
654 int w_add;
655 int h_add;
656 int has_frame_option;
658 *ret_dir = DIR_NONE;
659 *is_direction_fixed = False;
660 *do_warp_to_border = False;
661 if (!paction)
663 return 0;
665 token = PeekToken(*paction, &naction);
666 if (!token)
668 return 0;
670 if (StrEquals(token, "bottomright") || StrEquals(token, "br"))
672 int nx = x + *pFinalW - 1;
673 int ny = y + *pFinalH - 1;
675 n = GetMoveArguments(
676 &naction, 0, 0, &nx, &ny, NULL, NULL, True);
677 if (n < 2)
679 return 0;
681 *pFinalW = nx - x + 1;
682 *pFinalH = ny - y + 1;
683 *paction = naction;
685 return n;
687 has_frame_option = 0;
688 for ( ; ; token = PeekToken(naction, &naction))
690 if (StrEquals(token, "frame"))
692 has_frame_option = 1;
694 else if (StrEquals(token, "direction"))
696 if (token == NULL)
698 return 0;
700 *ret_dir = gravity_parse_dir_argument(
701 naction, &naction, DIR_NONE);
702 if (*ret_dir != DIR_NONE)
704 *is_direction_fixed = True;
707 else if (StrEquals(token, "fixeddirection"))
709 *is_direction_fixed = True;
711 else if (StrEquals(token, "warptoborder"))
713 *do_warp_to_border = True;
715 else
717 break;
720 if (has_frame_option)
722 w_add = 0;
723 h_add = 0;
725 else
727 w_add = sb->total_size.width;
728 h_add = sb->total_size.height;
730 s1 = NULL;
731 if (token != NULL)
733 s1 = safestrdup(token);
735 naction = GetNextToken(naction, &s2);
736 if (!s2)
738 if (s1 != NULL)
740 free(s1);
742 return 0;
744 *paction = naction;
746 n = 0;
747 n += ParseOneResizeArgument(
748 s1, Scr.MyDisplayWidth, w_base, w_inc, w_add, pFinalW);
749 n += ParseOneResizeArgument(
750 s2, Scr.MyDisplayHeight, h_base, h_inc, h_add, pFinalH);
751 if (s1 != NULL)
753 free(s1);
755 if (s2 != NULL)
757 free(s2);
759 if (n < 2)
761 n = 0;
764 return n;
767 static int GetResizeMoveArguments(
768 char **paction, int w_base, int h_base, int w_inc, int h_inc,
769 size_borders *sb, int *pFinalX, int *pFinalY,
770 int *pFinalW, int *pFinalH, Bool *fWarp, Bool *fPointer)
772 char *action = *paction;
773 direction_t dir;
774 Bool dummy;
776 if (!paction)
778 return 0;
780 if (GetResizeArguments(
781 &action, *pFinalX, *pFinalY, w_base, h_base, w_inc, h_inc,
782 sb, pFinalW, pFinalH, &dir, &dummy, &dummy) < 2)
784 return 0;
786 if (GetMoveArguments(
787 &action, *pFinalW, *pFinalH, pFinalX, pFinalY, fWarp,
788 NULL, True) < 2)
790 return 0;
792 *paction = action;
794 return 4;
797 /* Positions the SizeWindow on the current ("moused") xinerama-screen */
798 static void position_geometry_window(const XEvent *eventp)
800 int x;
801 int y;
802 fscreen_scr_arg fscr;
804 fscr.mouse_ev = (XEvent *)eventp;
805 /* Probably should remove this positioning code from {builtins,fvwm}.c?
807 if (Scr.gs.do_emulate_mwm)
809 FScreenCenterOnScreen(
810 &fscr, FSCREEN_CURRENT, &x, &y, sizew_g.width,
811 sizew_g.height);
813 else
815 FScreenGetScrRect(&fscr, FSCREEN_CURRENT, &x, &y, NULL, NULL);
817 if (x != sizew_g.x || y != sizew_g.y)
819 switch_move_resize_grid(False);
820 XMoveWindow(dpy, Scr.SizeWindow, x, y);
821 switch_move_resize_grid(True);
822 sizew_g.x = x;
823 sizew_g.y = y;
826 return;
829 void resize_geometry_window(void)
831 int w;
832 int h;
833 int cset = Scr.DefaultColorset;
835 Scr.SizeStringWidth =
836 FlocaleTextWidth(Scr.DefaultFont, GEOMETRY_WINDOW_STRING,
837 sizeof(GEOMETRY_WINDOW_STRING) - 1);
838 w = Scr.SizeStringWidth + 2 * GEOMETRY_WINDOW_BW;
839 h = Scr.DefaultFont->height + 2 * GEOMETRY_WINDOW_BW;
840 if (w != sizew_g.width || h != sizew_g.height)
842 XResizeWindow(dpy, Scr.SizeWindow, w, h);
843 sizew_g.width = w;
844 sizew_g.height = h;
846 if (cset >= 0)
848 SetWindowBackground(
849 dpy, Scr.SizeWindow, w, h, &Colorset[cset], Pdepth,
850 Scr.StdGC, False);
852 else
854 XSetWindowBackground(dpy, Scr.SizeWindow, Scr.StdBack);
857 return;
862 * Procedure:
863 * DisplayPosition - display the position in the dimensions window
865 * Inputs:
866 * tmp_win - the current fvwm window
867 * x, y - position of the window
871 static void DisplayPosition(
872 const FvwmWindow *tmp_win, const XEvent *eventp, int x, int y,int Init)
874 char str[100];
875 int offset;
876 fscreen_scr_arg fscr;
877 FlocaleWinString fstr;
879 if (Scr.gs.do_hide_position_window)
881 return;
883 position_geometry_window(eventp);
884 /* Translate x,y into local screen coordinates,
885 * in case Xinerama is used. */
886 fscr.xypos.x = x;
887 fscr.xypos.y = y;
888 FScreenTranslateCoordinates(
889 NULL, FSCREEN_GLOBAL, &fscr, FSCREEN_XYPOS, &x, &y);
890 (void)sprintf(str, GEOMETRY_WINDOW_POS_STRING, x, y);
891 if (Init)
893 XClearWindow(dpy, Scr.SizeWindow);
895 else
897 /* just clear indside the relief lines to reduce flicker */
898 XClearArea(dpy, Scr.SizeWindow,
899 GEOMETRY_WINDOW_BW, GEOMETRY_WINDOW_BW,
900 Scr.SizeStringWidth, Scr.DefaultFont->height, False);
903 if (Pdepth >= 2)
905 RelieveRectangle(
906 dpy, Scr.SizeWindow, 0, 0,
907 Scr.SizeStringWidth + GEOMETRY_WINDOW_BW * 2 - 1,
908 Scr.DefaultFont->height + GEOMETRY_WINDOW_BW * 2 - 1,
909 Scr.StdReliefGC, Scr.StdShadowGC, GEOMETRY_WINDOW_BW);
911 offset = (Scr.SizeStringWidth -
912 FlocaleTextWidth(Scr.DefaultFont, str, strlen(str))) / 2;
913 offset += GEOMETRY_WINDOW_BW;
915 memset(&fstr, 0, sizeof(fstr));
916 if (Scr.DefaultColorset >= 0)
918 fstr.colorset = &Colorset[Scr.DefaultColorset];
919 fstr.flags.has_colorset = True;
921 fstr.str = str;
922 fstr.win = Scr.SizeWindow;
923 fstr.gc = Scr.StdGC;
924 fstr.x = offset;
925 fstr.y = Scr.DefaultFont->ascent + GEOMETRY_WINDOW_BW;
926 FlocaleDrawString(dpy, Scr.DefaultFont, &fstr, 0);
928 return;
934 * Procedure:
935 * DisplaySize - display the size in the dimensions window
937 * Inputs:
938 * tmp_win - the current fvwm window
939 * width - the width of the rubber band
940 * height - the height of the rubber band
943 static void DisplaySize(
944 const FvwmWindow *tmp_win, const XEvent *eventp, int width,
945 int height, Bool Init, Bool resetLast)
947 char str[100];
948 int dwidth,dheight,offset;
949 size_borders b;
950 static int last_width = 0;
951 static int last_height = 0;
952 FlocaleWinString fstr;
954 if (Scr.gs.do_hide_resize_window)
956 return;
958 position_geometry_window(eventp);
959 if (resetLast)
961 last_width = 0;
962 last_height = 0;
964 if (last_width == width && last_height == height)
966 return;
968 last_width = width;
969 last_height = height;
971 get_window_borders(tmp_win, &b);
972 dheight = height - b.total_size.height;
973 dwidth = width - b.total_size.width;
974 dwidth -= tmp_win->hints.base_width;
975 dheight -= tmp_win->hints.base_height;
976 dwidth /= tmp_win->hints.width_inc;
977 dheight /= tmp_win->hints.height_inc;
979 (void)sprintf(str, GEOMETRY_WINDOW_SIZE_STRING, dwidth, dheight);
980 if (Init)
982 XClearWindow(dpy,Scr.SizeWindow);
984 else
986 /* just clear indside the relief lines to reduce flicker */
987 XClearArea(
988 dpy, Scr.SizeWindow, GEOMETRY_WINDOW_BW,
989 GEOMETRY_WINDOW_BW, Scr.SizeStringWidth,
990 Scr.DefaultFont->height, False);
993 if (Pdepth >= 2)
995 RelieveRectangle(
996 dpy, Scr.SizeWindow, 0, 0,
997 Scr.SizeStringWidth + GEOMETRY_WINDOW_BW * 2 - 1,
998 Scr.DefaultFont->height + GEOMETRY_WINDOW_BW*2 - 1,
999 Scr.StdReliefGC, Scr.StdShadowGC, GEOMETRY_WINDOW_BW);
1001 offset = (Scr.SizeStringWidth -
1002 FlocaleTextWidth(Scr.DefaultFont, str, strlen(str))) / 2;
1003 offset += GEOMETRY_WINDOW_BW;
1004 memset(&fstr, 0, sizeof(fstr));
1005 if (Scr.DefaultColorset >= 0)
1007 fstr.colorset = &Colorset[Scr.DefaultColorset];
1008 fstr.flags.has_colorset = True;
1010 fstr.str = str;
1011 fstr.win = Scr.SizeWindow;
1012 fstr.gc = Scr.StdGC;
1013 fstr.x = offset;
1014 fstr.y = Scr.DefaultFont->ascent + GEOMETRY_WINDOW_BW;
1015 FlocaleDrawString(dpy, Scr.DefaultFont, &fstr, 0);
1017 return;
1020 static Bool resize_move_window(F_CMD_ARGS)
1022 int FinalX = 0;
1023 int FinalY = 0;
1024 int FinalW = 0;
1025 int FinalH = 0;
1026 int n;
1027 int x,y;
1028 Bool fWarp = False;
1029 Bool fPointer = False;
1030 Bool has_focus;
1031 int dx;
1032 int dy;
1033 size_borders b;
1034 FvwmWindow *fw = exc->w.fw;
1035 Window w = exc->w.w;
1037 if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
1039 return False;
1041 if (!is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True))
1043 return False;
1046 /* gotta have a window */
1047 w = FW_W_FRAME(fw);
1048 if (!XGetGeometry(
1049 dpy, w, &JunkRoot, &x, &y, (unsigned int*)&FinalW,
1050 (unsigned int*)&FinalH, (unsigned int*)&JunkBW,
1051 (unsigned int*)&JunkDepth))
1053 XBell(dpy, 0);
1054 return False;
1057 FinalX = x;
1058 FinalY = y;
1060 get_window_borders(fw, &b);
1061 n = GetResizeMoveArguments(
1062 &action,
1063 fw->hints.base_width, fw->hints.base_height,
1064 fw->hints.width_inc, fw->hints.height_inc,
1065 &b, &FinalX, &FinalY, &FinalW, &FinalH, &fWarp, &fPointer);
1066 if (n < 4)
1068 return False;
1071 if (IS_MAXIMIZED(fw))
1073 /* must redraw the buttons now so that the 'maximize' button
1074 * does not stay depressed. */
1075 SET_MAXIMIZED(fw, 0);
1076 border_draw_decorations(
1077 fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
1078 NULL, NULL);
1080 dx = FinalX - fw->g.frame.x;
1081 dy = FinalY - fw->g.frame.y;
1082 /* size will be less or equal to requested */
1083 constrain_size(fw, NULL, &FinalW, &FinalH, 0, 0, 0);
1084 if (IS_SHADED(fw))
1086 frame_setup_window(
1087 fw, FinalX, FinalY, FinalW, fw->g.frame.height, False);
1089 else
1091 frame_setup_window(fw, FinalX, FinalY, FinalW, FinalH, True);
1093 if (fWarp)
1095 FWarpPointer(
1096 dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y);
1098 if (IS_MAXIMIZED(fw))
1100 fw->g.max.x += dx;
1101 fw->g.max.y += dy;
1103 else
1105 fw->g.normal.x += dx;
1106 fw->g.normal.y += dy;
1108 has_focus = (fw == get_focus_window())? True : False;
1109 update_absolute_geometry(fw);
1110 maximize_adjust_offset(fw);
1111 XFlush(dpy);
1112 GNOME_SetWinArea(fw);
1114 return True;
1117 void CMD_ResizeMove(F_CMD_ARGS)
1119 FvwmWindow *fw = exc->w.fw;
1121 if (IS_EWMH_FULLSCREEN(fw))
1123 /* do not unmaximize ! */
1124 CMD_ResizeMoveMaximize(F_PASS_ARGS);
1125 return;
1127 resize_move_window(F_PASS_ARGS);
1129 return;
1132 static void InteractiveMove(
1133 Window *win, const exec_context_t *exc, int *FinalX, int *FinalY,
1134 Bool do_start_at_pointer)
1136 int origDragX,origDragY,DragX, DragY, DragWidth, DragHeight;
1137 int XOffset, YOffset;
1138 Window w;
1139 Bool do_move_opaque = False;
1141 w = *win;
1143 if (Scr.bo.do_install_root_cmap)
1145 InstallRootColormap();
1147 else
1149 InstallFvwmColormap();
1151 /* warp the pointer to the cursor position from before menu appeared */
1152 /* domivogt (17-May-1999): an XFlush should not hurt anyway, so do it
1153 * unconditionally to remove the external */
1154 XFlush(dpy);
1156 if (do_start_at_pointer)
1158 if (FQueryPointer(
1159 dpy, Scr.Root, &JunkRoot, &JunkChild, &DragX,
1160 &DragY, &JunkX, &JunkY, &JunkMask) == False)
1162 /* pointer is on a different screen */
1163 DragX = 0;
1164 DragY = 0;
1167 else
1169 /* Although a move is usually done with a button depressed we
1170 * have to check for ButtonRelease too since the event may be
1171 * faked. */
1172 fev_get_evpos_or_query(
1173 dpy, Scr.Root, exc->x.elast, &DragX, &DragY);
1176 MyXGrabServer(dpy);
1177 if (!XGetGeometry(
1178 dpy, w, &JunkRoot, &origDragX, &origDragY,
1179 (unsigned int*)&DragWidth, (unsigned int*)&DragHeight,
1180 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1182 MyXUngrabServer(dpy);
1183 return;
1185 MyXGrabKeyboard(dpy);
1186 if (do_start_at_pointer)
1188 origDragX = DragX;
1189 origDragY = DragY;
1192 if (IS_ICONIFIED(exc->w.fw))
1194 do_move_opaque = True;
1196 else if (IS_MAPPED(exc->w.fw))
1198 float areapct;
1200 areapct = 100.0;
1201 areapct *= ((float)DragWidth / (float)Scr.MyDisplayWidth);
1202 areapct *= ((float)DragHeight / (float)Scr.MyDisplayHeight);
1203 /* round up */
1204 areapct += 0.1;
1205 if (Scr.OpaqueSize < 0 ||
1206 (float)areapct <= (float)Scr.OpaqueSize)
1208 do_move_opaque = True;
1211 if (do_move_opaque)
1213 MyXUngrabServer(dpy);
1215 else
1217 Scr.flags.is_wire_frame_displayed = True;
1220 if (!do_move_opaque && IS_ICONIFIED(exc->w.fw))
1222 XUnmapWindow(dpy,w);
1225 XOffset = origDragX - DragX;
1226 YOffset = origDragY - DragY;
1227 if (!Scr.gs.do_hide_position_window)
1229 position_geometry_window(NULL);
1230 XMapRaised(dpy,Scr.SizeWindow);
1232 __move_loop(
1233 exc, XOffset, YOffset, DragWidth, DragHeight, FinalX, FinalY,
1234 do_move_opaque, CRS_MOVE);
1235 if (!Scr.gs.do_hide_position_window)
1237 XUnmapWindow(dpy,Scr.SizeWindow);
1239 if (Scr.bo.do_install_root_cmap)
1241 UninstallRootColormap();
1243 else
1245 UninstallFvwmColormap();
1248 if (!do_move_opaque)
1250 /* Throw away some events that dont interest us right now. */
1251 discard_events(EnterWindowMask|LeaveWindowMask);
1252 Scr.flags.is_wire_frame_displayed = False;
1253 MyXUngrabServer(dpy);
1255 MyXUngrabKeyboard(dpy);
1257 return;
1260 /* Perform the movement of the window. ppctMovement *must* have a 1.0 entry
1261 * somewhere in ins list of floats, and movement will stop when it hits a 1.0
1262 * entry */
1263 static void AnimatedMoveAnyWindow(
1264 FvwmWindow *fw, Window w, int startX, int startY, int endX,
1265 int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement,
1266 MenuRepaintTransparentParameters *pmrtp)
1268 int pointerX, pointerY;
1269 int currentX, currentY;
1270 int lastX, lastY;
1271 int deltaX, deltaY;
1272 Bool first = True;
1273 XEvent evdummy;
1274 unsigned int draw_parts = PART_NONE;
1276 if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
1278 return;
1281 /* set our defaults */
1282 if (ppctMovement == NULL)
1284 ppctMovement = rgpctMovementDefault;
1286 if (cmsDelay < 0)
1288 cmsDelay = cmsDelayDefault;
1291 if (startX < 0 || startY < 0)
1293 if (
1294 !XGetGeometry(
1295 dpy, w, &JunkRoot, &currentX, &currentY,
1296 (unsigned int*)&JunkWidth,
1297 (unsigned int*)&JunkHeight,
1298 (unsigned int*)&JunkBW,
1299 (unsigned int*)&JunkDepth))
1301 XBell(dpy, 0);
1302 return;
1304 if (startX < 0)
1306 startX = currentX;
1308 if (startY < 0)
1310 startY = currentY;
1314 deltaX = endX - startX;
1315 deltaY = endY - startY;
1316 lastX = startX;
1317 lastY = startY;
1319 if (deltaX == 0 && deltaY == 0)
1321 /* go nowhere fast */
1322 return;
1325 if (fw && w == FW_W_FRAME(fw))
1327 draw_parts = border_get_transparent_decorations_part(fw);
1330 /* Needed for aborting */
1331 MyXGrabKeyboard(dpy);
1334 currentX = startX + deltaX * (*ppctMovement);
1335 currentY = startY + deltaY * (*ppctMovement);
1336 if (lastX == currentX && lastY == currentY)
1338 /* don't waste time in the same spot */
1339 continue;
1341 if (pmrtp != NULL)
1343 update_transparent_menu_bg(
1344 pmrtp, lastX, lastY, currentX, currentY,
1345 endX, endY);
1347 XMoveWindow(dpy,w,currentX,currentY);
1348 if (pmrtp != NULL)
1350 repaint_transparent_menu(
1351 pmrtp, first,
1352 currentX, currentY, endX, endY, True);
1354 else if (draw_parts != PART_NONE)
1356 border_draw_decorations(
1357 fw, draw_parts,
1358 ((fw == get_focus_window())) ?
1359 True : False,
1360 True, CLEAR_ALL, NULL, NULL);
1362 if (fw && pmrtp == NULL && IS_TEAR_OFF_MENU(fw))
1364 menu_redraw_transparent_tear_off_menu(fw, False);
1366 if (fWarpPointerToo == True)
1368 if (FQueryPointer(
1369 dpy, Scr.Root, &JunkRoot, &JunkChild,
1370 &JunkX, &JunkY, &pointerX, &pointerY,
1371 &JunkMask) == False)
1373 /* pointer is on a different screen */
1374 pointerX = currentX;
1375 pointerY = currentY;
1377 else
1379 pointerX += currentX - lastX;
1380 pointerY += currentY - lastY;
1382 FWarpPointer(
1383 dpy, None, Scr.Root, 0, 0, 0, 0, pointerX,
1384 pointerY);
1386 if (fw && !IS_SHADED(fw) && !Scr.bo.do_disable_configure_notify)
1388 /* send configure notify event for windows that care
1389 * about their location */
1390 SendConfigureNotify(
1391 fw, currentX, currentY,
1392 fw->g.frame.width,
1393 fw->g.frame.height, 0, False);
1394 #ifdef FVWM_DEBUG_MSGS
1395 fvwm_msg(DBG,"AnimatedMoveAnyWindow",
1396 "Sent ConfigureNotify (w == %d, h == %d)",
1397 fw->g.frame.width,
1398 fw->g.frame.height);
1399 #endif
1401 XFlush(dpy);
1402 if (fw)
1404 fw->g.frame.x = currentX;
1405 fw->g.frame.y = currentY;
1406 update_absolute_geometry(fw);
1407 maximize_adjust_offset(fw);
1408 BroadcastConfig(M_CONFIGURE_WINDOW, fw);
1409 FlushAllMessageQueues();
1412 usleep(cmsDelay * 1000); /* usleep takes microseconds */
1413 /* this didn't work for me -- maybe no longer necessary since
1414 * we warn the user when they use > .5 seconds as a
1415 * between-frame delay time.
1417 * domivogt (28-apr-1999): That is because the keyboard was not
1418 * grabbed. works nicely now.
1420 if (FCheckMaskEvent(
1421 dpy, ButtonPressMask|ButtonReleaseMask|KeyPressMask,
1422 &evdummy))
1424 /* finish the move immediately */
1425 if (pmrtp != NULL)
1427 update_transparent_menu_bg(
1428 pmrtp, lastX, lastY,
1429 currentX, currentY, endX, endY);
1431 XMoveWindow(dpy,w,endX,endY);
1432 if (pmrtp != NULL)
1434 repaint_transparent_menu(
1435 pmrtp, first,
1436 endX, endY, endX, endY, True);
1438 break;
1440 lastX = currentX;
1441 lastY = currentY;
1442 first = False;
1443 } while (*ppctMovement != 1.0 && ppctMovement++);
1444 MyXUngrabKeyboard(dpy);
1445 XFlush(dpy);
1446 if (fw)
1448 GNOME_SetWinArea(fw);
1451 return;
1454 /* used for moving menus, not a client window */
1455 void AnimatedMoveOfWindow(
1456 Window w, int startX, int startY, int endX, int endY,
1457 Bool fWarpPointerToo, int cmsDelay, float *ppctMovement,
1458 MenuRepaintTransparentParameters *pmrtp)
1460 AnimatedMoveAnyWindow(
1461 NULL, w, startX, startY, endX, endY, fWarpPointerToo,
1462 cmsDelay, ppctMovement, pmrtp);
1464 return;
1467 /* used for moving client windows */
1468 void AnimatedMoveFvwmWindow(
1469 FvwmWindow *fw, Window w, int startX, int startY, int endX,
1470 int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement)
1472 AnimatedMoveAnyWindow(
1473 fw, w, startX, startY, endX, endY, fWarpPointerToo,
1474 cmsDelay, ppctMovement, NULL);
1476 return;
1479 int placement_binding(int button, KeySym keysym, int modifier, char *action)
1481 if (keysym != 0)
1483 /* fixme */
1484 fvwm_msg(
1485 ERR, "placement_binding",
1486 "sorry, placement keybindings not allowed. yet.");
1487 return 1;
1489 if (modifier != 0)
1491 /* fixme */
1492 fvwm_msg(
1493 ERR, "placement_binding",
1494 "sorry, placement binding modifiers not allowed. yet.");
1495 return 1;
1497 if (strcmp(action,"-") == 0 ||
1498 strcasecmp(action,"CancelPlacement") == 0)
1500 if (keysym == 0) /* must be button binding */
1502 if (button == 0)
1504 move_drag_finish_button_mask = 0;
1505 move_interactive_finish_button_mask = 0;
1507 else if (button > 0 && button <=
1508 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1510 move_drag_finish_button_mask &=
1511 ~(1<<(button-1));
1512 move_interactive_finish_button_mask &=
1513 ~(1<<(button-1));
1517 else if (strcasecmp(action,"CancelPlacementDrag") == 0)
1519 if (keysym == 0) /* must be button binding */
1521 if (button == 0)
1523 move_drag_finish_button_mask = 0;
1525 else if (button > 0 && button <=
1526 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1528 move_drag_finish_button_mask &=
1529 ~(1<<(button-1));
1533 else if (strcasecmp(action,"CancelPlacementInteractive") == 0)
1535 if (keysym == 0) /* must be button binding */
1537 if (button == 0)
1539 move_interactive_finish_button_mask = 0;
1541 else if (button > 0 && button <=
1542 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1544 move_interactive_finish_button_mask &=
1545 ~(1<<(button-1));
1549 else if (strcasecmp(action,"PlaceWindow") == 0)
1551 if (keysym == 0) /* must be button binding */
1553 if (button == 0)
1555 move_interactive_finish_button_mask =
1556 move_drag_finish_button_mask =
1557 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1559 else if (button > 0 && button <=
1560 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1562 move_drag_finish_button_mask |= (1<<(button-1));
1563 move_interactive_finish_button_mask |=
1564 (1<<(button-1));
1568 else if (strcasecmp(action,"PlaceWindowDrag") == 0)
1570 if (keysym == 0) /* must be button binding */
1572 if (button == 0)
1574 move_drag_finish_button_mask =
1575 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1577 else if (button > 0 && button <=
1578 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1580 move_drag_finish_button_mask |= (1<<(button-1));
1584 else if (strcasecmp(action,"PlaceWindowInteractive") == 0)
1586 if (keysym == 0) /* must be button binding */
1588 if (button == 0)
1590 move_interactive_finish_button_mask =
1591 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
1593 else if (button > 0 && button <=
1594 NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
1596 move_interactive_finish_button_mask |=
1597 (1<<(button-1));
1601 else
1603 fvwm_msg(
1604 ERR, "placement_binding",
1605 "invalid action %s", action);
1608 return 0;
1614 * Start a window move operation
1617 void __move_icon(
1618 FvwmWindow *fw, int x, int y, int old_x, int old_y,
1619 Bool do_move_animated, Bool do_warp_pointer)
1621 rectangle gt;
1622 rectangle gp;
1623 Bool has_icon_title;
1624 Bool has_icon_picture;
1625 Window tw;
1626 int tx;
1627 int ty;
1629 set_icon_position(fw, x, y);
1630 broadcast_icon_geometry(fw, False);
1631 has_icon_title = get_visible_icon_title_geometry(fw, &gt);
1632 has_icon_picture = get_visible_icon_picture_geometry(fw, &gp);
1633 if (has_icon_picture)
1635 tw = FW_W_ICON_PIXMAP(fw);
1636 tx = gp.x;
1637 ty = gp.y;
1639 else if (has_icon_title)
1641 tw = FW_W_ICON_TITLE(fw);
1642 tx = gt.x;
1643 ty = gt.y;
1645 else
1647 return;
1649 if (do_move_animated)
1651 AnimatedMoveOfWindow(
1652 tw, -1, -1, tx, ty, do_warp_pointer, -1, NULL, NULL);
1653 do_warp_pointer = 0;
1655 if (has_icon_title)
1657 XMoveWindow(dpy, FW_W_ICON_TITLE(fw), gt.x, gt.y);
1659 if (has_icon_picture)
1661 XMoveWindow(dpy, FW_W_ICON_PIXMAP(fw), gp.x, gp.y);
1662 if (fw->Desk == Scr.CurrentDesk)
1664 XMapWindow(dpy, FW_W_ICON_PIXMAP(fw));
1665 if (has_icon_title)
1667 XMapWindow(dpy, FW_W_ICON_TITLE(fw));
1671 if (do_warp_pointer)
1673 FWarpPointer(dpy, None, None, 0, 0, 0, 0, x - old_x, y - old_y);
1676 return;
1679 static void __move_window(F_CMD_ARGS, Bool do_animate, int mode)
1681 int FinalX = 0;
1682 int FinalY = 0;
1683 int n;
1684 int x;
1685 int y;
1686 int width, height;
1687 int page_x, page_y;
1688 Bool fWarp = False;
1689 Bool fPointer = False;
1690 int dx;
1691 int dy;
1692 FvwmWindow *fw = exc->w.fw;
1693 Window w;
1695 if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
1697 return;
1699 /* gotta have a window */
1700 w = FW_W_FRAME(fw);
1701 if (IS_ICONIFIED(fw))
1703 if (FW_W_ICON_PIXMAP(fw) != None)
1705 w = FW_W_ICON_PIXMAP(fw);
1706 XUnmapWindow(dpy,FW_W_ICON_TITLE(fw));
1708 else
1710 w = FW_W_ICON_TITLE(fw);
1712 if (w == None && (mode == MOVE_PAGE || mode == MOVE_SCREEN))
1714 w = FW_W_FRAME(fw);
1717 if (
1718 !XGetGeometry(
1719 dpy, w, &JunkRoot, &x, &y, (unsigned int*)&width,
1720 (unsigned int*)&height, (unsigned int*)&JunkBW,
1721 (unsigned int*)&JunkDepth))
1723 return;
1725 if (mode == MOVE_PAGE && IS_STICKY_ACROSS_PAGES(fw))
1727 return;
1729 if (mode == MOVE_PAGE)
1731 rectangle r;
1732 rectangle s;
1733 rectangle t;
1735 do_animate = False;
1736 r.x = x;
1737 r.y = y;
1738 r.width = width;
1739 r.height = height;
1740 get_absolute_geometry(&t, &r);
1741 get_page_offset_rectangle(&page_x, &page_y, &t);
1742 if (!get_page_arguments(action, &page_x, &page_y))
1744 page_x = Scr.Vx;
1745 page_y = Scr.Vy;
1747 s.x = page_x - Scr.Vx;
1748 s.y = page_y - Scr.Vy;
1749 s.width = Scr.MyDisplayWidth;
1750 s.height = Scr.MyDisplayHeight;
1751 fvwmrect_move_into_rectangle(&r, &s);
1752 FinalX = r.x;
1753 FinalY = r.y;
1755 else if (mode == MOVE_SCREEN)
1757 rectangle r;
1758 rectangle s;
1759 rectangle p;
1760 int fscreen;
1762 do_animate = False;
1763 fscreen = FScreenGetScreenArgument(
1764 action, FSCREEN_SPEC_CURRENT);
1765 FScreenGetScrRect(
1766 NULL, fscreen, &s.x, &s.y, &s.width, &s.height);
1767 page_x = Scr.Vx;
1768 page_y = Scr.Vy;
1769 r.x = x;
1770 r.y = y;
1771 r.width = width;
1772 r.height = height;
1773 p.x = page_x - Scr.Vx;
1774 p.y = page_y - Scr.Vy;
1775 p.width = Scr.MyDisplayWidth;
1776 p.height = Scr.MyDisplayHeight;
1777 /* move to page first */
1778 fvwmrect_move_into_rectangle(&r, &p);
1779 /* then move to screen */
1780 fvwmrect_move_into_rectangle(&r, &s);
1781 FinalX = r.x;
1782 FinalY = r.y;
1784 else
1786 FinalX = x;
1787 FinalY = y;
1788 n = GetMoveArguments(
1789 &action, width, height, &FinalX, &FinalY, &fWarp,
1790 &fPointer, True);
1792 if (n != 2 || fPointer)
1794 InteractiveMove(&w, exc, &FinalX, &FinalY, fPointer);
1796 else if (IS_ICONIFIED(fw))
1798 SET_ICON_MOVED(fw, 1);
1802 if (w == FW_W_FRAME(fw))
1804 dx = FinalX - fw->g.frame.x;
1805 dy = FinalY - fw->g.frame.y;
1806 if (do_animate)
1808 AnimatedMoveFvwmWindow(
1809 fw, w, -1, -1, FinalX, FinalY, fWarp, -1,
1810 NULL);
1812 frame_setup_window(
1813 fw, FinalX, FinalY, fw->g.frame.width,
1814 fw->g.frame.height, True);
1815 if (fWarp & !do_animate)
1817 FWarpPointer(
1818 dpy, None, None, 0, 0, 0, 0, FinalX - x,
1819 FinalY - y);
1821 if (IS_MAXIMIZED(fw))
1823 fw->g.max.x += dx;
1824 fw->g.max.y += dy;
1826 else
1828 fw->g.normal.x += dx;
1829 fw->g.normal.y += dy;
1831 update_absolute_geometry(fw);
1832 maximize_adjust_offset(fw);
1833 XFlush(dpy);
1834 GNOME_SetWinArea(fw);
1836 else /* icon window */
1838 __move_icon(fw, FinalX, FinalY, x, y, do_animate, fWarp);
1839 XFlush(dpy);
1841 focus_grab_buttons_on_layer(fw->layer);
1843 return;
1846 void CMD_Move(F_CMD_ARGS)
1848 __move_window(F_PASS_ARGS, False, MOVE_NORMAL);
1850 return;
1853 void CMD_AnimatedMove(F_CMD_ARGS)
1855 __move_window(F_PASS_ARGS, True, MOVE_NORMAL);
1857 return;
1860 void CMD_MoveToPage(F_CMD_ARGS)
1862 __move_window(F_PASS_ARGS, False, MOVE_PAGE);
1864 return;
1867 void CMD_MoveToScreen(F_CMD_ARGS)
1869 __move_window(F_PASS_ARGS, False, MOVE_SCREEN);
1871 return;
1874 /* This function does the SnapAttraction stuff. It takes x and y coordinates
1875 * (*px and *py) and returns the snapped values. */
1876 static void DoSnapAttract(
1877 FvwmWindow *fw, int Width, int Height, int *px, int *py)
1879 int nyt,nxl,dist,closestLeft,closestRight,closestBottom,closestTop;
1880 rectangle self;
1882 /* resist based on window edges */
1883 closestTop = fw->snap_attraction.proximity;
1884 closestBottom = fw->snap_attraction.proximity;
1885 closestRight = fw->snap_attraction.proximity;
1886 closestLeft = fw->snap_attraction.proximity;
1887 nxl = -99999;
1888 nyt = -99999;
1889 self.x = *px;
1890 self.y = *py;
1891 self.width = Width;
1892 self.height = Height;
1894 rectangle g;
1895 Bool rc;
1897 rc = get_visible_icon_title_geometry(fw, &g);
1898 if (rc == True)
1900 self.height += g.height;
1905 * Snap grid handling
1907 if (fw->snap_grid_x > 1 && nxl == -99999)
1909 if (*px != *px / fw->snap_grid_x * fw->snap_grid_x)
1911 *px = (*px + ((*px >= 0) ?
1912 fw->snap_grid_x : -fw->snap_grid_x) /
1913 2) / fw->snap_grid_x * fw->snap_grid_x;
1916 if (fw->snap_grid_y > 1 && nyt == -99999)
1918 if (*py != *py / fw->snap_grid_y * fw->snap_grid_y)
1920 *py = (*py + ((*py >= 0) ?
1921 fw->snap_grid_y : -fw->snap_grid_y) /
1922 2) / fw->snap_grid_y * fw->snap_grid_y;
1927 * snap attraction
1929 /* snap to other windows or icons*/
1930 if (fw->snap_attraction.proximity > 0 &&
1931 (fw->snap_attraction.mode & (SNAP_ICONS | SNAP_WINDOWS | SNAP_SAME)))
1933 FvwmWindow *tmp;
1934 int maskout = (SNAP_SCREEN | SNAP_SCREEN_WINDOWS |
1935 SNAP_SCREEN_ICONS | SNAP_SCREEN_ALL);
1937 for (tmp = Scr.FvwmRoot.next; tmp; tmp = tmp->next)
1939 rectangle other;
1941 if (fw->Desk != tmp->Desk || fw == tmp)
1943 continue;
1945 /* check snapping type */
1946 switch (fw->snap_attraction.mode & ~(maskout))
1948 case SNAP_WINDOWS: /* we only snap windows */
1949 if (IS_ICONIFIED(tmp) || IS_ICONIFIED(fw))
1951 continue;
1953 break;
1954 case SNAP_ICONS: /* we only snap icons */
1955 if (!IS_ICONIFIED(tmp) || !IS_ICONIFIED(fw))
1957 continue;
1959 break;
1960 case SNAP_SAME: /* we don't snap unequal */
1961 if (IS_ICONIFIED(tmp) != IS_ICONIFIED(fw))
1963 continue;
1965 break;
1966 default: /* All */
1967 /* NOOP */
1968 break;
1970 /* get other window dimensions */
1971 get_visible_window_or_icon_geometry(tmp, &other);
1972 /* prevent that window snaps off screen */
1973 if (other.x <= 0)
1975 other.x -= fw->snap_attraction.proximity + 10000;
1976 other.width += fw->snap_attraction.proximity + 10000;
1978 if (other.y <= 0)
1980 other.y -= fw->snap_attraction.proximity + 10000;
1981 other.height += fw->snap_attraction.proximity + 10000;
1983 if (other.x + other.width >= Scr.MyDisplayWidth)
1985 other.width += fw->snap_attraction.proximity + 10000;
1987 if (other.y + other.height >= Scr.MyDisplayHeight)
1989 other.height += fw->snap_attraction.proximity + 10000;
1992 /* snap horizontally */
1993 if (
1994 other.y + other.height > *py &&
1995 other.y < *py + self.height)
1997 dist = abs(other.x - (*px + self.width));
1998 if (dist < closestRight)
2000 closestRight = dist;
2001 if (*px + self.width >= other.x &&
2002 *px + self.width <
2003 other.x + fw->snap_attraction.proximity)
2005 nxl = other.x - self.width;
2007 if (*px + self.width >=
2008 other.x - fw->snap_attraction.proximity &&
2009 *px + self.width < other.x)
2011 nxl = other.x - self.width;
2014 dist = abs(other.x + other.width - *px);
2015 if (dist < closestLeft)
2017 closestLeft = dist;
2018 if (*px <= other.x + other.width &&
2019 *px > other.x + other.width -
2020 fw->snap_attraction.proximity)
2022 nxl = other.x + other.width;
2024 if (*px <= other.x + other.width +
2025 fw->snap_attraction.proximity &&
2026 *px > other.x + other.width)
2028 nxl = other.x + other.width;
2032 /* snap vertically */
2033 if (
2034 other.x + other.width > *px &&
2035 other.x < *px + self.width)
2037 dist = abs(other.y - (*py + self.height));
2038 if (dist < closestBottom)
2040 closestBottom = dist;
2041 if (*py + self.height >= other.y &&
2042 *py + self.height < other.y +
2043 fw->snap_attraction.proximity)
2045 nyt = other.y - self.height;
2047 if (*py + self.height >=
2048 other.y - fw->snap_attraction.proximity &&
2049 *py + self.height < other.y)
2051 nyt = other.y - self.height;
2054 dist = abs(other.y + other.height - *py);
2055 if (dist < closestTop)
2057 closestTop = dist;
2058 if (*py <=
2059 other.y + other.height &&
2060 *py > other.y + other.height -
2061 fw->snap_attraction.proximity)
2063 nyt = other.y + other.height;
2065 if (*py <= other.y + other.height +
2066 fw->snap_attraction.proximity &&
2067 *py > other.y + other.height)
2069 nyt = other.y + other.height;
2073 } /* for */
2074 } /* snap to other windows */
2076 /* snap to screen egdes */
2077 if (fw->snap_attraction.proximity > 0 && (
2078 ( fw->snap_attraction.mode & SNAP_SCREEN && (
2079 fw->snap_attraction.mode & SNAP_SAME ||
2080 ( IS_ICONIFIED(fw) &&
2081 fw->snap_attraction.mode & SNAP_ICONS ) ||
2082 ( !IS_ICONIFIED(fw) &&
2083 fw->snap_attraction.mode & SNAP_WINDOWS ))) ||
2084 ( !IS_ICONIFIED(fw) &&
2085 fw->snap_attraction.mode & SNAP_SCREEN_WINDOWS ) ||
2086 ( IS_ICONIFIED(fw) &&
2087 fw->snap_attraction.mode & SNAP_SCREEN_ICONS ) ||
2088 fw->snap_attraction.mode & SNAP_SCREEN_ALL ))
2090 /* horizontally */
2091 if (!(Scr.MyDisplayWidth < (*px) ||
2092 (*px + self.width) < 0))
2094 dist = abs(Scr.MyDisplayHeight - (*py + self.height));
2095 if (dist < closestBottom)
2097 closestBottom = dist;
2098 if (*py + self.height >=
2099 Scr.MyDisplayHeight &&
2100 *py + self.height <
2101 Scr.MyDisplayHeight + fw->snap_attraction.proximity)
2103 nyt = Scr.MyDisplayHeight -
2104 self.height;
2106 if (*py + self.height >=
2107 Scr.MyDisplayHeight - fw->snap_attraction.proximity &&
2108 *py + self.height < Scr.MyDisplayHeight)
2110 nyt = Scr.MyDisplayHeight -
2111 self.height;
2114 dist = abs(*py);
2115 if (dist < closestTop)
2117 closestTop = dist;
2118 if ((*py <= 0)&&(*py > - fw->snap_attraction.proximity))
2120 nyt = 0;
2122 if ((*py <= fw->snap_attraction.proximity)&&(*py > 0))
2124 nyt = 0;
2127 } /* horizontally */
2128 /* vertically */
2129 if (!(Scr.MyDisplayHeight < (*py) ||
2130 (*py + self.height) < 0))
2132 dist = abs(
2133 Scr.MyDisplayWidth - (*px + self.width));
2134 if (dist < closestRight)
2136 closestRight = dist;
2138 if (*px + self.width >= Scr.MyDisplayWidth &&
2139 *px + self.width <
2140 Scr.MyDisplayWidth + fw->snap_attraction.proximity)
2142 nxl = Scr.MyDisplayWidth - self.width;
2145 if (*px + self.width >=
2146 Scr.MyDisplayWidth - fw->snap_attraction.proximity &&
2147 *px + self.width < Scr.MyDisplayWidth)
2149 nxl = Scr.MyDisplayWidth - self.width;
2152 dist = abs(*px);
2153 if (dist < closestLeft)
2155 closestLeft = dist;
2157 if ((*px <= 0) &&
2158 (*px > - fw->snap_attraction.proximity))
2160 nxl = 0;
2162 if ((*px <= fw->snap_attraction.proximity) &&
2163 (*px > 0))
2165 nxl = 0;
2168 } /* vertically */
2169 } /* snap to screen edges */
2171 if (nxl != -99999)
2173 *px = nxl;
2175 if (nyt != -99999)
2177 *py = nyt;
2181 * Resist moving windows beyond the edge of the screen
2183 if (fw->edge_resistance_move > 0)
2185 /* snap to right edge */
2186 if (
2187 *px + Width >= Scr.MyDisplayWidth &&
2188 *px + Width < Scr.MyDisplayWidth +
2189 fw->edge_resistance_move)
2191 *px = Scr.MyDisplayWidth - Width;
2193 /* snap to left edge */
2194 else if ((*px <= 0) && (*px > -fw->edge_resistance_move))
2196 *px = 0;
2198 /* snap to bottom edge */
2199 if (
2200 *py + Height >= Scr.MyDisplayHeight &&
2201 *py + Height < Scr.MyDisplayHeight +
2202 fw->edge_resistance_move)
2204 *py = Scr.MyDisplayHeight - Height;
2206 /* snap to top edge */
2207 else if (*py <= 0 && *py > -fw->edge_resistance_move)
2209 *py = 0;
2212 /* Resist moving windows between xineramascreens */
2213 if (fw->edge_resistance_xinerama_move > 0 && FScreenIsEnabled())
2215 int scr_x0, scr_y0;
2216 int scr_x1, scr_y1;
2217 Bool do_recalc_rectangle = False;
2219 FScreenGetResistanceRect(
2220 *px, *py, Width, Height, &scr_x0, &scr_y0, &scr_x1,
2221 &scr_y1);
2223 /* snap to right edge */
2224 if (scr_x1 < Scr.MyDisplayWidth &&
2225 *px + Width >= scr_x1 && *px + Width <
2226 scr_x1 + fw->edge_resistance_xinerama_move)
2228 *px = scr_x1 - Width;
2229 do_recalc_rectangle = True;
2231 /* snap to left edge */
2232 else if (
2233 scr_x0 > 0 &&
2234 *px <= scr_x0 && scr_x0 - *px <
2235 fw->edge_resistance_xinerama_move)
2237 *px = scr_x0;
2238 do_recalc_rectangle = True;
2240 if (do_recalc_rectangle)
2242 /* Snapping in X direction can move the window off a
2243 * screen. Thus, it may no longer be necessary to snap
2244 * in Y direction. */
2245 FScreenGetResistanceRect(
2246 *px, *py, Width, Height, &scr_x0, &scr_y0,
2247 &scr_x1, &scr_y1);
2249 /* snap to bottom edge */
2250 if (scr_y1 < Scr.MyDisplayHeight &&
2251 *py + Height >= scr_y1 && *py + Height <
2252 scr_y1 + fw->edge_resistance_xinerama_move)
2254 *py = scr_y1 - Height;
2256 /* snap to top edge */
2257 else if (
2258 scr_y0 > 0 &&
2259 *py <= scr_y0 && scr_y0 - *py <
2260 fw->edge_resistance_xinerama_move)
2262 *py = scr_y0;
2266 return;
2271 * Move the rubberband around, return with the new window location
2273 * Returns True if the window has to be resized after the move.
2276 Bool __move_loop(
2277 const exec_context_t *exc, int XOffset, int YOffset, int Width,
2278 int Height, int *FinalX, int *FinalY, Bool do_move_opaque, int cursor)
2280 extern Window bad_window;
2281 Bool is_finished = False;
2282 Bool is_aborted = False;
2283 int xl,xl2,yt,yt2,delta_x,delta_y,paged;
2284 unsigned int button_mask = 0;
2285 FvwmWindow fw_copy;
2286 int dx = Scr.EdgeScrollX ? Scr.EdgeScrollX : Scr.MyDisplayWidth;
2287 int dy = Scr.EdgeScrollY ? Scr.EdgeScrollY : Scr.MyDisplayHeight;
2288 const int vx = Scr.Vx;
2289 const int vy = Scr.Vy;
2290 int xl_orig = 0;
2291 int yt_orig = 0;
2292 int cnx = 0;
2293 int cny = 0;
2294 int x_virtual_offset = 0;
2295 int y_virtual_offset = 0;
2296 Bool sent_cn = False;
2297 Bool do_resize_too = False;
2298 int x_bak;
2299 int y_bak;
2300 Window move_w = None;
2301 int orig_icon_x = 0;
2302 int orig_icon_y = 0;
2303 Bool do_snap = True;
2304 Bool was_snapped = False;
2305 /* if Alt is initially pressed don't enable no-snap until Alt is
2306 * released */
2307 Bool nosnap_enabled = False;
2308 /* Must not set placed by button if the event is a modified KeyEvent */
2309 Bool is_fake_event;
2310 FvwmWindow *fw = exc->w.fw;
2311 unsigned int draw_parts = PART_NONE;
2312 XEvent e;
2314 if (!GrabEm(cursor, GRAB_NORMAL))
2316 XBell(dpy, 0);
2317 return False;
2319 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
2321 do_move_opaque = False;
2323 bad_window = None;
2324 if (IS_ICONIFIED(fw))
2326 if (FW_W_ICON_PIXMAP(fw) != None)
2328 move_w = FW_W_ICON_PIXMAP(fw);
2330 else if (FW_W_ICON_TITLE(fw) != None)
2332 move_w = FW_W_ICON_TITLE(fw);
2335 else
2337 move_w = FW_W_FRAME(fw);
2339 if (
2340 !XGetGeometry(
2341 dpy, move_w, &JunkRoot, &x_bak, &y_bak,
2342 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
2343 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
2345 /* This is allright here since the window may not be mapped
2346 * yet. */
2349 if (IS_ICONIFIED(fw))
2351 rectangle g;
2353 get_visible_icon_geometry(fw, &g);
2354 orig_icon_x = g.x;
2355 orig_icon_y = g.y;
2358 /* make a copy of the fw structure for sending to the pager */
2359 memcpy(&fw_copy, fw, sizeof(FvwmWindow));
2360 /* prevent flicker when paging */
2361 SET_WINDOW_BEING_MOVED_OPAQUE(fw, do_move_opaque);
2363 if (FQueryPointer(
2364 dpy, Scr.Root, &JunkRoot, &JunkChild, &xl, &yt,
2365 &JunkX, &JunkY, &button_mask) == False)
2367 /* pointer is on a different screen */
2368 xl = 0;
2369 yt = 0;
2371 else
2373 xl += XOffset;
2374 yt += YOffset;
2376 button_mask &= DEFAULT_ALL_BUTTONS_MASK;
2377 xl_orig = xl;
2378 yt_orig = yt;
2380 /* draw initial outline */
2381 if (!IS_ICONIFIED(fw) &&
2382 ((!do_move_opaque && !Scr.gs.do_emulate_mwm) || !IS_MAPPED(fw)))
2384 draw_move_resize_grid(xl, yt, Width - 1, Height - 1);
2387 if (move_w == FW_W_FRAME(fw) && do_move_opaque)
2389 draw_parts = border_get_transparent_decorations_part(fw);
2391 DisplayPosition(fw, exc->x.elast, xl, yt, True);
2393 memset(&e, 0, sizeof(e));
2395 /* Unset the placed by button mask.
2396 * If the move is canceled this will remain as zero.
2398 fw->placed_by_button = 0;
2399 while (!is_finished && bad_window != FW_W(fw))
2401 int rc = 0;
2402 int old_xl;
2403 int old_yt;
2405 old_xl = xl;
2406 old_yt = yt;
2407 /* wait until there is an interesting event */
2408 while (rc != -1 &&
2409 (!FPending(dpy) ||
2410 !FCheckMaskEvent(
2411 dpy, ButtonPressMask | ButtonReleaseMask |
2412 KeyPressMask | PointerMotionMask |
2413 ButtonMotionMask | ExposureMask, &e)))
2415 XEvent le;
2417 fev_get_last_event(&le);
2418 rc = HandlePaging(
2419 &le, dx, dy, &xl, &yt, &delta_x, &delta_y,
2420 False, False, True, fw->edge_delay_ms_move);
2421 if (rc == 1)
2423 /* Fake an event to force window reposition */
2424 if (delta_x)
2426 x_virtual_offset = 0;
2428 xl += XOffset;
2429 if (delta_y)
2431 y_virtual_offset = 0;
2433 yt += YOffset;
2434 if (do_snap)
2436 DoSnapAttract(
2437 fw, Width, Height, &xl, &yt);
2438 was_snapped = True;
2440 fev_make_null_event(&e, dpy);
2441 e.type = MotionNotify;
2442 e.xmotion.time = fev_get_evtime();
2443 e.xmotion.x_root = xl - XOffset;
2444 e.xmotion.y_root = yt - YOffset;
2445 e.xmotion.same_screen = True;
2446 break;
2449 if (rc == -1)
2451 /* block until an event arrives */
2452 /* dv (2004-07-01): With XFree 4.1.0.1, some Mouse
2453 * events are not reported to fvwm when the pointer
2454 * moves very fast and suddenly stops in the corner of
2455 * the screen. Handle EnterNotify/LeaveNotify events
2456 * too to get an idea where the pointer might be. */
2457 FMaskEvent(
2458 dpy, ButtonPressMask | ButtonReleaseMask |
2459 KeyPressMask | PointerMotionMask |
2460 ButtonMotionMask | ExposureMask |
2461 EnterWindowMask | LeaveWindowMask, &e);
2464 /* discard extra events before a logical release */
2465 if (e.type == MotionNotify ||
2466 e.type == EnterNotify || e.type == LeaveNotify)
2468 while (FPending(dpy) > 0 &&
2469 FCheckMaskEvent(
2470 dpy, ButtonMotionMask |
2471 PointerMotionMask | ButtonPressMask |
2472 ButtonRelease | KeyPressMask |
2473 EnterWindowMask | LeaveWindowMask, &e))
2475 if (e.type == ButtonPress ||
2476 e.type == ButtonRelease ||
2477 e.type == KeyPress)
2479 break;
2483 if (e.type == EnterNotify || e.type == LeaveNotify)
2485 XEvent e2;
2486 int x;
2487 int y;
2489 /* Query the pointer to catch the latest information.
2490 * This *is* necessary. */
2491 if (FQueryPointer(
2492 dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
2493 &y, &JunkX, &JunkY, &JunkMask) == True)
2495 fev_make_null_event(&e2, dpy);
2496 e2.type = MotionNotify;
2497 e2.xmotion.time = fev_get_evtime();
2498 e2.xmotion.x_root = x;
2499 e2.xmotion.y_root = y;
2500 e2.xmotion.state = JunkMask;
2501 e2.xmotion.same_screen = True;
2502 e = e2;
2503 fev_fake_event(&e);
2505 else
2507 /* pointer is on a different screen,
2508 * ignore event */
2511 is_fake_event = False;
2512 /* Handle a limited number of key press events to allow
2513 * mouseless operation */
2514 if (e.type == KeyPress)
2516 Keyboard_shortcuts(
2517 &e, fw, &x_virtual_offset,
2518 &y_virtual_offset, ButtonRelease);
2520 is_fake_event = (e.type != KeyPress);
2522 switch (e.type)
2524 case KeyPress:
2525 if (!(e.xkey.state & Mod1Mask))
2527 nosnap_enabled = True;
2529 do_snap = nosnap_enabled &&
2530 (e.xkey.state & Mod1Mask) ? False : True;
2532 /* simple code to bag out of move - CKH */
2533 if (XLookupKeysym(&(e.xkey), 0) == XK_Escape)
2535 if (!do_move_opaque)
2537 switch_move_resize_grid(False);
2539 if (!IS_ICONIFIED(fw))
2541 if (do_move_opaque)
2543 *FinalX = fw->g.frame.x;
2544 *FinalY = fw->g.frame.y;
2547 else
2549 *FinalX = orig_icon_x;
2550 *FinalY = orig_icon_y;
2552 is_aborted = True;
2553 is_finished = True;
2555 break;
2556 case ButtonPress:
2557 if (e.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
2558 ((Button1Mask << (e.xbutton.button - 1)) &
2559 button_mask))
2561 /* No new button was pressed, just a delayed
2562 * event */
2563 break;
2565 if (!IS_MAPPED(fw) &&
2566 ((e.xbutton.button == 2 && !Scr.gs.do_emulate_mwm)
2568 (e.xbutton.button == 1 && Scr.gs.do_emulate_mwm &&
2569 (e.xbutton.state & ShiftMask))))
2571 do_resize_too = True;
2572 /* Fallthrough to button-release */
2574 else if (!button_mask && e.xbutton.button <=
2575 NUMBER_OF_EXTENDED_MOUSE_BUTTONS &&
2576 e.xbutton.button > 0 &&
2577 (move_interactive_finish_button_mask &
2578 (1<<(e.xbutton.button-1))))
2580 do_resize_too = False;
2581 break;
2583 else if (button_mask && e.xbutton.button <=
2584 NUMBER_OF_EXTENDED_MOUSE_BUTTONS &&
2585 e.xbutton.button > 0 &&
2586 (move_drag_finish_button_mask &
2587 (1<<(e.xbutton.button-1))))
2589 do_resize_too = False;
2590 /* Fallthrough to button-release */
2592 else
2594 /* Abort the move if
2595 * - the move started with a pressed button
2596 * and another button was pressed during the
2597 * operation
2598 * - Any button not in the
2599 * move_finish_button_mask is pressed
2601 /* if (button_mask) */
2602 /* - button_mask will always be set here.
2603 * only add an if if we want to be able to
2604 * place windows dragged by other means
2605 * than releasing the initial button.
2608 if (!do_move_opaque)
2610 switch_move_resize_grid(False);
2612 if (!IS_ICONIFIED(fw))
2614 *FinalX = fw->g.frame.x;
2615 *FinalY = fw->g.frame.y;
2617 else
2619 *FinalX = orig_icon_x;
2620 *FinalY = orig_icon_y;
2622 is_aborted = True;
2623 is_finished = True;
2625 break;
2627 case ButtonRelease:
2628 if (!is_fake_event)
2630 fw->placed_by_button = e.xbutton.button;
2632 if (!do_move_opaque)
2634 switch_move_resize_grid(False);
2636 xl2 = e.xbutton.x_root + XOffset + x_virtual_offset;
2637 yt2 = e.xbutton.y_root + YOffset + y_virtual_offset;
2638 /* ignore the position of the button release if it was
2639 * on a different page. */
2640 if (!(((xl < 0 && xl2 >= 0) ||
2641 (xl >= 0 && xl2 < 0) ||
2642 (yt < 0 && yt2 >= 0) ||
2643 (yt >= 0 && yt2 < 0)) &&
2644 (abs(xl - xl2) > Scr.MyDisplayWidth / 2 ||
2645 abs(yt - yt2) > Scr.MyDisplayHeight / 2)))
2647 xl = xl2;
2648 yt = yt2;
2650 if (xl != xl_orig || yt != yt_orig || vx != Scr.Vx ||
2651 vy != Scr.Vy || was_snapped)
2653 /* only snap if the window actually moved! */
2654 if (do_snap)
2656 DoSnapAttract(
2657 fw, Width, Height, &xl, &yt);
2658 was_snapped = True;
2662 *FinalX = xl;
2663 *FinalY = yt;
2665 is_finished = True;
2666 break;
2668 case MotionNotify:
2669 if (e.xmotion.same_screen == False)
2671 continue;
2673 if (!(e.xmotion.state & Mod1Mask))
2675 nosnap_enabled = True;
2677 do_snap = nosnap_enabled &&
2678 (e.xmotion.state & Mod1Mask) ? False : True;
2679 xl = e.xmotion.x_root;
2680 yt = e.xmotion.y_root;
2681 if (xl > 0 && xl < Scr.MyDisplayWidth - 1)
2683 /* pointer was moved away from the left/right
2684 * border with the mouse, reset the virtual x
2685 * offset */
2686 x_virtual_offset = 0;
2688 if (yt > 0 && yt < Scr.MyDisplayHeight - 1)
2690 /* pointer was moved away from the top/bottom
2691 * border with the mouse, reset the virtual y
2692 * offset */
2693 y_virtual_offset = 0;
2695 xl += XOffset + x_virtual_offset;
2696 yt += YOffset + y_virtual_offset;
2698 if (do_snap)
2700 DoSnapAttract(fw, Width, Height, &xl, &yt);
2701 was_snapped = True;
2704 /* check Paging request once and only once after
2705 * outline redrawn redraw after paging if needed
2706 * - mab */
2707 for (paged = 0; paged <= 1; paged++)
2709 if (!do_move_opaque)
2711 draw_move_resize_grid(
2712 xl, yt, Width - 1, Height - 1);
2714 else
2716 if (IS_ICONIFIED(fw))
2718 set_icon_position(fw, xl, yt);
2719 move_icon_to_position(fw);
2720 broadcast_icon_geometry(
2721 fw, False);
2723 else
2725 XMoveWindow(
2726 dpy, FW_W_FRAME(fw),
2727 xl, yt);
2730 DisplayPosition(fw, &e, xl, yt, False);
2732 /* prevent window from lagging behind mouse
2733 * when paging - mab */
2734 if (paged == 0)
2736 XEvent le;
2738 xl = e.xmotion.x_root;
2739 yt = e.xmotion.y_root;
2740 fev_get_last_event(&le);
2741 HandlePaging(
2742 &le, dx, dy, &xl, &yt,
2743 &delta_x, &delta_y, False,
2744 False, False,
2745 fw->edge_delay_ms_move);
2746 if (delta_x)
2748 x_virtual_offset = 0;
2750 xl += XOffset;
2751 if (delta_y)
2753 y_virtual_offset = 0;
2755 yt += YOffset;
2756 if (do_snap)
2758 DoSnapAttract(
2759 fw, Width, Height,
2760 &xl, &yt);
2761 was_snapped = True;
2763 if (!delta_x && !delta_y)
2765 /* break from while
2766 * (paged <= 1) */
2767 break;
2771 break;
2773 case Expose:
2774 if (!do_move_opaque)
2776 /* must undraw the rubber band in case the
2777 * event causes some drawing */
2778 switch_move_resize_grid(False);
2780 dispatch_event(&e);
2781 if (!do_move_opaque)
2783 draw_move_resize_grid(
2784 xl, yt, Width - 1, Height - 1);
2786 break;
2788 default:
2789 /* cannot happen */
2790 break;
2791 } /* switch */
2792 xl += x_virtual_offset;
2793 yt += y_virtual_offset;
2794 if (do_move_opaque && !IS_ICONIFIED(fw) &&
2795 !IS_SHADED(fw) && !Scr.bo.do_disable_configure_notify)
2797 /* send configure notify event for windows that care
2798 * about their location; don't send anything if
2799 * position didn't change */
2800 if (!sent_cn || cnx != xl || cny != yt)
2802 cnx = xl;
2803 cny = yt;
2804 sent_cn = True;
2805 SendConfigureNotify(
2806 fw, xl, yt, Width, Height, 0,
2807 False);
2808 #ifdef FVWM_DEBUG_MSGS
2809 fvwm_msg(
2810 DBG, "frame_setup_window",
2811 "Sent ConfigureNotify (w %d, h %d)",
2812 Width, Height);
2813 #endif
2816 if (do_move_opaque)
2818 if (!IS_ICONIFIED(fw))
2820 fw_copy.g.frame.x = xl;
2821 fw_copy.g.frame.y = yt;
2823 if (xl != old_xl || yt != old_yt)
2825 /* only do this with opaque moves, (i.e. the
2826 * server is not grabbed) */
2827 if (draw_parts != PART_NONE)
2829 border_draw_decorations(
2830 fw, draw_parts,
2831 ((fw == get_focus_window())) ?
2832 True : False,
2833 True, CLEAR_ALL, NULL, NULL);
2835 if (IS_TEAR_OFF_MENU(fw))
2837 menu_redraw_transparent_tear_off_menu(
2838 fw, False);
2840 BroadcastConfig(M_CONFIGURE_WINDOW, &fw_copy);
2841 FlushAllMessageQueues();
2844 } /* while (!is_finished) */
2846 if (!Scr.gs.do_hide_position_window)
2848 XUnmapWindow(dpy,Scr.SizeWindow);
2850 if (is_aborted || bad_window == FW_W(fw))
2852 if (vx != Scr.Vx || vy != Scr.Vy)
2854 MoveViewport(vx, vy, False);
2856 if (is_aborted && do_move_opaque)
2858 XMoveWindow(dpy, move_w, x_bak, y_bak);
2859 if (draw_parts != PART_NONE)
2861 border_draw_decorations(
2862 fw, draw_parts,
2863 ((fw == get_focus_window())) ?
2864 True : False,
2865 True, CLEAR_ALL, NULL, NULL);
2867 menu_redraw_transparent_tear_off_menu(fw, False);
2869 if (bad_window == FW_W(fw))
2871 XUnmapWindow(dpy, move_w);
2872 border_undraw_decorations(fw);
2873 XBell(dpy, 0);
2876 if (!is_aborted && bad_window != FW_W(fw) && IS_ICONIFIED(fw))
2878 SET_ICON_MOVED(fw, 1);
2880 UngrabEm(GRAB_NORMAL);
2881 if (!do_resize_too)
2883 /* Don't wait for buttons to come up when user is placing a new
2884 * window and wants to resize it. */
2885 WaitForButtonsUp(True);
2887 SET_WINDOW_BEING_MOVED_OPAQUE(fw, 0);
2888 bad_window = None;
2890 return do_resize_too;
2893 void CMD_MoveThreshold(F_CMD_ARGS)
2895 int val = 0;
2897 if (GetIntegerArguments(action, NULL, &val, 1) < 1 || val < 0)
2899 Scr.MoveThreshold = DEFAULT_MOVE_THRESHOLD;
2901 else
2903 Scr.MoveThreshold = val;
2906 return;
2909 void CMD_OpaqueMoveSize(F_CMD_ARGS)
2911 int val;
2913 if (GetIntegerArguments(action, NULL, &val, 1) < 1)
2915 if (strncasecmp(action, "unlimited", 9) == 0)
2917 Scr.OpaqueSize = -1;
2919 else
2921 Scr.OpaqueSize = DEFAULT_OPAQUE_MOVE_SIZE;
2924 else
2926 Scr.OpaqueSize = val;
2929 return;
2933 static char *hide_options[] =
2935 "never",
2936 "move",
2937 "resize",
2938 NULL
2941 void CMD_HideGeometryWindow(F_CMD_ARGS)
2943 char *token = PeekToken(action, NULL);
2945 Scr.gs.do_hide_position_window = 0;
2946 Scr.gs.do_hide_resize_window = 0;
2947 switch(GetTokenIndex(token, hide_options, 0, NULL))
2949 case 0:
2950 break;
2951 case 1:
2952 Scr.gs.do_hide_position_window = 1;
2953 break;
2954 case 2:
2955 Scr.gs.do_hide_resize_window = 1;
2956 break;
2957 default:
2958 Scr.gs.do_hide_position_window = 1;
2959 Scr.gs.do_hide_resize_window = 1;
2960 break;
2962 return;
2965 void CMD_SnapAttraction(F_CMD_ARGS)
2967 char *cmd;
2968 size_t len;
2970 len = strlen(action);
2971 len += 99;
2972 cmd = safemalloc(len);
2973 sprintf(cmd, "Style * SnapAttraction %s", action);
2974 fvwm_msg(
2975 OLD, "CMD_SnapAttraction",
2976 "The command SnapAttraction is obsolete. Please use the"
2977 " following command instead:");
2978 fvwm_msg(OLD, "", cmd);
2979 execute_function(
2980 cond_rc, exc, cmd,
2981 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
2982 free(cmd);
2984 return;
2987 void CMD_SnapGrid(F_CMD_ARGS)
2989 char *cmd;
2990 size_t len;
2992 len = strlen(action);
2993 len += 99;
2994 cmd = safemalloc(len);
2995 sprintf(cmd, "Style * SnapGrid %s", action);
2996 fvwm_msg(
2997 OLD, "CMD_SnapGrid",
2998 "The command SnapGrid is obsolete. Please use the following"
2999 " command instead:");
3000 fvwm_msg(OLD, "", cmd);
3001 execute_function(
3002 cond_rc, exc, cmd,
3003 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
3004 free(cmd);
3006 return;
3009 static Pixmap XorPixmap = None;
3011 void CMD_XorValue(F_CMD_ARGS)
3013 int val;
3014 XGCValues gcv;
3015 unsigned long gcm;
3017 if (GetIntegerArguments(action, NULL, &val, 1) != 1)
3019 val = 0;
3022 PictureUseDefaultVisual();
3023 gcm = GCFunction|GCLineWidth|GCForeground|GCFillStyle|GCSubwindowMode;
3024 gcv.subwindow_mode = IncludeInferiors;
3025 gcv.function = GXxor;
3026 gcv.line_width = 1;
3027 /* use passed in value, or try to calculate appropriate value if 0 */
3028 /* ctwm method: */
3030 gcv.foreground = (val1)?(val1):((((unsigned long) 1) <<
3031 Scr.d_depth) - 1);
3033 /* Xlib programming manual suggestion: */
3034 gcv.foreground = (val)?
3035 (val):(PictureBlackPixel() ^ PictureWhitePixel());
3036 gcv.fill_style = FillSolid;
3037 gcv.subwindow_mode = IncludeInferiors;
3039 /* modify XorGC, only create once */
3040 if (Scr.XorGC)
3042 XChangeGC(dpy, Scr.XorGC, gcm, &gcv);
3044 else
3046 Scr.XorGC = fvwmlib_XCreateGC(dpy, Scr.Root, gcm, &gcv);
3049 /* free up XorPixmap if neccesary */
3050 if (XorPixmap != None) {
3051 XFreePixmap(dpy, XorPixmap);
3052 XorPixmap = None;
3054 PictureUseFvwmVisual();
3056 return;
3060 void CMD_XorPixmap(F_CMD_ARGS)
3062 char *PixmapName;
3063 FvwmPicture *xp;
3064 XGCValues gcv;
3065 unsigned long gcm;
3066 FvwmPictureAttributes fpa;
3068 action = GetNextToken(action, &PixmapName);
3069 if (PixmapName == NULL)
3071 /* return to default value. */
3072 action = "0";
3073 CMD_XorValue(F_PASS_ARGS);
3074 return;
3076 /* get the picture in the root visual, colorlimit is ignored because the
3077 * pixels will be freed */
3078 fpa.mask = FPAM_NO_COLOR_LIMIT | FPAM_NO_ALPHA;
3079 PictureUseDefaultVisual();
3080 xp = PGetFvwmPicture(dpy, Scr.Root, NULL, PixmapName, fpa);
3081 if (xp == NULL)
3083 fvwm_msg(ERR,"SetXORPixmap","Can't find pixmap %s", PixmapName);
3084 free(PixmapName);
3085 PictureUseFvwmVisual();
3086 return;
3088 free(PixmapName);
3089 /* free up old pixmap */
3090 if (XorPixmap != None)
3092 XFreePixmap(dpy, XorPixmap);
3095 /* make a copy of the picture pixmap */
3096 XorPixmap = XCreatePixmap(dpy, Scr.Root, xp->width, xp->height, Pdepth);
3097 XCopyArea(dpy, xp->picture, XorPixmap, DefaultGC(dpy, Scr.screen), 0, 0,
3098 xp->width, xp->height, 0, 0);
3099 /* destroy picture and free colors */
3100 PDestroyFvwmPicture(dpy, xp);
3101 PictureUseFvwmVisual();
3103 /* create Graphics context */
3104 gcm = GCFunction|GCLineWidth|GCTile|GCFillStyle|GCSubwindowMode;
3105 gcv.subwindow_mode = IncludeInferiors;
3106 gcv.function = GXxor;
3107 /* line width of 1 is necessary for Exceed servers */
3108 gcv.line_width = 1;
3109 gcv.tile = XorPixmap;
3110 gcv.fill_style = FillTiled;
3111 gcv.subwindow_mode = IncludeInferiors;
3112 /* modify XorGC, only create once */
3113 if (Scr.XorGC)
3115 XChangeGC(dpy, Scr.XorGC, gcm, &gcv);
3117 else
3119 Scr.XorGC = fvwmlib_XCreateGC(dpy, Scr.Root, gcm, &gcv);
3122 return;
3126 /* ----------------------------- resizing code ----------------------------- */
3128 static void __resize_get_dir_from_window(
3129 int *ret_xmotion, int *ret_ymotion, FvwmWindow *fw, Window context_w)
3131 if (context_w != Scr.Root && context_w != None)
3133 if (context_w == FW_W_SIDE(fw, 0)) /* top */
3135 *ret_ymotion = 1;
3137 else if (context_w == FW_W_SIDE(fw, 1)) /* right */
3139 *ret_xmotion = -1;
3141 else if (context_w == FW_W_SIDE(fw, 2)) /* bottom */
3143 *ret_ymotion = -1;
3145 else if (context_w == FW_W_SIDE(fw, 3)) /* left */
3147 *ret_xmotion = 1;
3149 else if (context_w == FW_W_CORNER(fw, 0)) /* upper-left */
3151 *ret_xmotion = 1;
3152 *ret_ymotion = 1;
3154 else if (context_w == FW_W_CORNER(fw, 1)) /* upper-right */
3156 *ret_xmotion = -1;
3157 *ret_ymotion = 1;
3159 else if (context_w == FW_W_CORNER(fw, 2)) /* lower left */
3161 *ret_xmotion = 1;
3162 *ret_ymotion = -1;
3164 else if (context_w == FW_W_CORNER(fw, 3)) /* lower right */
3166 *ret_xmotion = -1;
3167 *ret_ymotion = -1;
3171 return;
3174 static void __resize_get_dir_proximity(
3175 int *ret_xmotion, int *ret_ymotion, FvwmWindow *fw, int x_off,
3176 int y_off, int px, int py)
3178 int tx;
3179 int ty;
3181 if (px < 0 || x_off < 0 || py < 0 || y_off < 0)
3183 return;
3185 /* Now find the place to warp to. We simply use the sectors
3186 * drawn when we start resizing the window. */
3187 #if 0
3188 tx = orig->width / 10 - 1;
3189 ty = orig->height / 10 - 1;
3190 #else
3191 tx = 0;
3192 ty = 0;
3193 #endif
3194 tx = max(fw->boundary_width, tx);
3195 ty = max(fw->boundary_width, ty);
3196 if (px < tx)
3198 *ret_xmotion = 1;
3200 else if (x_off < tx)
3202 *ret_xmotion = -1;
3204 if (py < ty)
3206 *ret_ymotion = 1;
3208 else if (y_off < ty)
3210 *ret_ymotion = -1;
3213 return;
3216 static void __resize_get_refpos(
3217 int *ret_x, int *ret_y, int xmotion, int ymotion, int w, int h,
3218 FvwmWindow *fw)
3220 if (xmotion > 0)
3222 *ret_x = 0;
3224 else if (xmotion < 0)
3226 *ret_x = w - 1;
3228 else
3230 *ret_x = w / 2;
3232 if (ymotion > 0)
3234 *ret_y = 0;
3236 else if (ymotion < 0)
3238 *ret_y = h - 1;
3240 else
3242 *ret_y = h / 2;
3245 return;
3248 /* Procedure:
3249 * __resize_step - move the rubberband around. This is called for
3250 * each motion event when we are resizing
3252 * Inputs:
3253 * x_root - the X corrdinate in the root window
3254 * y_root - the Y corrdinate in the root window
3255 * x_off - x offset of pointer from border (input/output)
3256 * y_off - y offset of pointer from border (input/output)
3257 * drag - resize internal structure
3258 * orig - resize internal structure
3259 * xmotionp - pointer to xmotion in resize_window
3260 * ymotionp - pointer to ymotion in resize_window
3263 static void __resize_step(
3264 const exec_context_t *exc, int x_root, int y_root, int *x_off,
3265 int *y_off, rectangle *drag, const rectangle *orig, int *xmotionp,
3266 int *ymotionp, Bool do_resize_opaque, Bool is_direction_fixed)
3268 int action = 0;
3269 XEvent e;
3270 int x2;
3271 int y2;
3272 int xdir;
3273 int ydir;
3275 x2 = x_root - *x_off;
3276 x_root += *x_off;
3277 if (is_direction_fixed == True && (*xmotionp != 0 || *ymotionp != 0))
3279 xdir = *xmotionp;
3281 else if (x2 <= orig->x ||
3282 (*xmotionp == 1 && x2 < orig->x + orig->width - 1))
3284 xdir = 1;
3286 else if (x2 >= orig->x + orig->width - 1 ||
3287 (*xmotionp == -1 && x2 > orig->x))
3289 xdir = -1;
3291 else
3293 xdir = 0;
3295 switch (xdir)
3297 case 1:
3298 if (*xmotionp != 1)
3300 *x_off = -*x_off;
3301 x_root = x2;
3302 *xmotionp = 1;
3304 drag->x = x_root;
3305 drag->width = orig->x + orig->width - x_root;
3306 action = 1;
3307 break;
3308 case -1:
3309 if (*xmotionp != -1)
3311 *x_off = -*x_off;
3312 x_root = x2;
3313 *xmotionp = -1;
3315 drag->x = orig->x;
3316 drag->width = 1 + x_root - drag->x;
3317 action = 1;
3318 break;
3319 default:
3320 break;
3322 y2 = y_root - *y_off;
3323 y_root += *y_off;
3324 if (is_direction_fixed == True && (*xmotionp != 0 || *ymotionp != 0))
3326 ydir = *ymotionp;
3328 else if (y2 <= orig->y ||
3329 (*ymotionp == 1 && y2 < orig->y + orig->height - 1))
3331 ydir = 1;
3333 else if (y2 >= orig->y + orig->height - 1 ||
3334 (*ymotionp == -1 && y2 > orig->y))
3336 ydir = -1;
3338 else
3340 ydir = 0;
3342 switch (ydir)
3344 case 1:
3345 if (*ymotionp != 1)
3347 *y_off = -*y_off;
3348 y_root = y2;
3349 *ymotionp = 1;
3351 drag->y = y_root;
3352 drag->height = orig->y + orig->height - y_root;
3353 action = 1;
3354 break;
3355 case -1:
3356 if (*ymotionp != -1)
3358 *y_off = -*y_off;
3359 y_root = y2;
3360 *ymotionp = -1;
3362 drag->y = orig->y;
3363 drag->height = 1 + y_root - drag->y;
3364 action = 1;
3365 break;
3366 default:
3367 break;
3370 if (action)
3372 /* round up to nearest OK size to keep pointer inside
3373 * rubberband */
3374 constrain_size(
3375 exc->w.fw, exc->x.elast, &drag->width, &drag->height,
3376 *xmotionp, *ymotionp, CS_ROUND_UP);
3377 if (*xmotionp == 1)
3379 drag->x = orig->x + orig->width - drag->width;
3381 if (*ymotionp == 1)
3383 drag->y = orig->y + orig->height - drag->height;
3385 if (!do_resize_opaque)
3387 draw_move_resize_grid(
3388 drag->x, drag->y, drag->width - 1,
3389 drag->height - 1);
3391 else
3393 frame_setup_window(
3394 exc->w.fw, drag->x, drag->y, drag->width,
3395 drag->height, False);
3398 e.type = MotionNotify;
3399 e.xbutton.x_root = x_root;
3400 e.xbutton.y_root = y_root;
3401 DisplaySize(exc->w.fw, &e, drag->width, drag->height, False, False);
3403 return;
3406 /* Starts a window resize operation */
3407 static Bool __resize_window(F_CMD_ARGS)
3409 extern Window bad_window;
3410 FvwmWindow *fw = exc->w.fw;
3411 Bool is_finished = False, is_done = False, is_aborted = False;
3412 Bool do_send_cn = False;
3413 Bool do_resize_opaque;
3414 Bool do_warp_to_border;
3415 Bool is_direction_fixed;
3416 Bool fButtonAbort = False;
3417 Bool fForceRedraw = False;
3418 Bool called_from_title = False;
3419 int x,y,delta_x,delta_y,stashed_x,stashed_y;
3420 Window ResizeWindow;
3421 int dx = Scr.EdgeScrollX ? Scr.EdgeScrollX : Scr.MyDisplayWidth;
3422 int dy = Scr.EdgeScrollY ? Scr.EdgeScrollY : Scr.MyDisplayHeight;
3423 const int vx = Scr.Vx;
3424 const int vy = Scr.Vy;
3425 int n;
3426 unsigned int button_mask = 0;
3427 rectangle sdrag;
3428 rectangle sorig;
3429 rectangle *drag = &sdrag;
3430 const rectangle *orig = &sorig;
3431 const window_g g_backup = fw->g;
3432 int ymotion = 0;
3433 int xmotion = 0;
3434 int was_maximized;
3435 unsigned edge_wrap_x;
3436 unsigned edge_wrap_y;
3437 int px;
3438 int py;
3439 int i;
3440 size_borders b;
3441 frame_move_resize_args mr_args = NULL;
3442 long evmask;
3443 XEvent ev;
3444 int ref_x;
3445 int ref_y;
3446 int x_off;
3447 int y_off;
3448 direction_t dir;
3450 bad_window = False;
3451 ResizeWindow = FW_W_FRAME(fw);
3452 if (fev_get_evpos_or_query(dpy, Scr.Root, exc->x.etrigger, &px, &py) ==
3453 False ||
3454 XTranslateCoordinates(
3455 dpy, Scr.Root, ResizeWindow, px, py, &px, &py,
3456 &JunkChild) == False)
3458 /* pointer is on a different screen - that's okay here */
3459 px = 0;
3460 py = 0;
3462 button_mask &= DEFAULT_ALL_BUTTONS_MASK;
3464 if (!is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True))
3466 XBell(dpy, 0);
3467 return False;
3470 if (IS_SHADED(fw) || !IS_MAPPED(fw))
3472 do_resize_opaque = False;
3473 evmask = XEVMASK_RESIZE;
3475 else
3477 do_resize_opaque = DO_RESIZE_OPAQUE(fw);
3478 evmask = XEVMASK_RESIZE_OPAQUE;
3481 /* no suffix = % of screen, 'p' = pixels, 'c' = increment units */
3482 if (IS_SHADED(fw))
3484 get_unshaded_geometry(fw, drag);
3486 else
3488 drag->width = fw->g.frame.width;
3489 drag->height = fw->g.frame.height;
3492 get_window_borders(fw, &b);
3493 n = GetResizeArguments(
3494 &action, fw->g.frame.x, fw->g.frame.y,
3495 fw->hints.base_width, fw->hints.base_height,
3496 fw->hints.width_inc, fw->hints.height_inc,
3497 &b, &(drag->width), &(drag->height),
3498 &dir, &is_direction_fixed, &do_warp_to_border);
3500 if (n == 2)
3502 rectangle new_g;
3504 /* size will be less or equal to requested */
3505 if (IS_SHADED(fw))
3507 rectangle shaded_g;
3509 get_unshaded_geometry(fw, &new_g);
3510 SET_MAXIMIZED(fw, 0);
3511 constrain_size(
3512 fw, NULL, &drag->width, &drag->height, xmotion,
3513 ymotion, 0);
3514 gravity_resize(
3515 fw->hints.win_gravity, &new_g,
3516 drag->width - new_g.width,
3517 drag->height - new_g.height);
3518 fw->g.normal = new_g;
3519 get_shaded_geometry(fw, &shaded_g, &new_g);
3520 frame_setup_window(
3521 fw, shaded_g.x, shaded_g.y, shaded_g.width,
3522 shaded_g.height, False);
3524 else
3526 new_g = fw->g.frame;
3527 SET_MAXIMIZED(fw, 0);
3528 constrain_size(
3529 fw, NULL, &drag->width, &drag->height, xmotion,
3530 ymotion, 0);
3531 gravity_resize(
3532 fw->hints.win_gravity, &new_g,
3533 drag->width - new_g.width,
3534 drag->height - new_g.height);
3535 frame_setup_window(
3536 fw, new_g.x, new_g.y, drag->width,
3537 drag->height, False);
3539 update_absolute_geometry(fw);
3540 maximize_adjust_offset(fw);
3541 GNOME_SetWinArea(fw);
3542 ResizeWindow = None;
3543 return True;
3546 was_maximized = IS_MAXIMIZED(fw);
3547 SET_MAXIMIZED(fw, 0);
3548 if (was_maximized)
3550 /* must redraw the buttons now so that the 'maximize' button
3551 * does not stay depressed. */
3552 border_draw_decorations(
3553 fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
3554 NULL, NULL);
3557 if (Scr.bo.do_install_root_cmap)
3559 InstallRootColormap();
3561 else
3563 InstallFvwmColormap();
3565 if (!GrabEm(CRS_RESIZE, GRAB_NORMAL))
3567 XBell(dpy, 0);
3568 return False;
3571 /* handle problems with edge-wrapping while resizing */
3572 edge_wrap_x = Scr.flags.do_edge_wrap_x;
3573 edge_wrap_y = Scr.flags.do_edge_wrap_y;
3574 Scr.flags.do_edge_wrap_x = 0;
3575 Scr.flags.do_edge_wrap_y = 0;
3577 if (!do_resize_opaque)
3579 MyXGrabServer(dpy);
3581 if (!XGetGeometry(
3582 dpy, (Drawable)ResizeWindow, &JunkRoot, &drag->x, &drag->y,
3583 (unsigned int*)&drag->width, (unsigned int*)&drag->height,
3584 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
3586 UngrabEm(GRAB_NORMAL);
3587 if (!do_resize_opaque)
3589 MyXUngrabServer(dpy);
3591 return False;
3593 if (IS_SHADED(fw))
3595 SET_MAXIMIZED(fw, was_maximized);
3596 get_unshaded_geometry(fw, drag);
3597 SET_MAXIMIZED(fw, 0);
3599 if (do_resize_opaque)
3601 mr_args = frame_create_move_resize_args(
3602 fw, FRAME_MR_OPAQUE, &fw->g.frame, &fw->g.frame, 0,
3603 DIR_NONE);
3605 else
3607 Scr.flags.is_wire_frame_displayed = True;
3609 MyXGrabKeyboard(dpy);
3611 sorig = *drag;
3612 ymotion = 0;
3613 xmotion = 0;
3615 /* pop up a resize dimensions window */
3616 if (!Scr.gs.do_hide_resize_window)
3618 position_geometry_window(NULL);
3619 XMapRaised(dpy, Scr.SizeWindow);
3621 DisplaySize(fw, exc->x.elast, orig->width, orig->height, True, True);
3623 if (dir != DIR_NONE)
3625 int grav;
3627 grav = gravity_dir_to_grav(dir);
3628 gravity_get_offsets(grav, &xmotion, &ymotion);
3629 xmotion = -xmotion;
3630 ymotion = -ymotion;
3632 if (xmotion == 0 && ymotion == 0)
3634 __resize_get_dir_from_window(&xmotion, &ymotion, fw, PressedW);
3636 if (FW_W_TITLE(fw) != None && PressedW == FW_W_TITLE(fw))
3638 /* title was pressed to start the resize */
3639 called_from_title = True;
3641 else
3643 for (i = NUMBER_OF_TITLE_BUTTONS; i--; )
3645 /* see if the title button was pressed to that the
3646 * resize */
3647 if (FW_W_BUTTON(fw, i) != None &&
3648 FW_W_BUTTON(fw, i) == PressedW)
3650 /* yes */
3651 called_from_title = True;
3655 /* don't warp if the resize was triggered by a press somwhere on the
3656 * title bar */
3657 if (PressedW != Scr.Root && xmotion == 0 && ymotion == 0 &&
3658 !called_from_title)
3660 __resize_get_dir_proximity(
3661 &xmotion, &ymotion, fw, orig->width - px,
3662 orig->height - py, px, py);
3663 if (xmotion != 0 || ymotion != 0)
3665 do_warp_to_border = True;
3668 if (!IS_SHADED(fw))
3670 __resize_get_refpos(
3671 &ref_x, &ref_y, xmotion, ymotion, orig->width,
3672 orig->height, fw);
3674 else
3676 switch (SHADED_DIR(fw))
3678 case DIR_N:
3679 case DIR_NW:
3680 case DIR_NE:
3681 if (ymotion == -1)
3683 ymotion = 0;
3685 break;
3686 case DIR_S:
3687 case DIR_SW:
3688 case DIR_SE:
3689 if (ymotion == 1)
3691 ymotion = 0;
3693 break;
3694 default:
3695 break;
3697 switch (SHADED_DIR(fw))
3699 case DIR_E:
3700 case DIR_NE:
3701 case DIR_SE:
3702 if (xmotion == 1)
3704 xmotion = 0;
3706 break;
3707 case DIR_W:
3708 case DIR_NW:
3709 case DIR_SW:
3710 if (xmotion == -1)
3712 xmotion = 0;
3714 break;
3715 default:
3716 break;
3718 __resize_get_refpos(
3719 &ref_x, &ref_y, xmotion, ymotion, fw->g.frame.width,
3720 fw->g.frame.height, fw);
3722 x_off = 0;
3723 y_off = 0;
3724 if (do_warp_to_border == True)
3726 int dx;
3727 int dy;
3729 dx = (xmotion == 0) ? px : ref_x;
3730 dy = (ymotion == 0) ? py : ref_y;
3731 /* warp the pointer to the border */
3732 FWarpPointer(
3733 dpy, None, ResizeWindow, 0, 0, 1, 1, dx, dy);
3734 XFlush(dpy);
3736 else if (xmotion != 0 || ymotion != 0)
3738 /* keep the distance between pointer and border */
3739 x_off = (xmotion == 0) ? 0 : ref_x - px;
3740 y_off = (ymotion == 0) ? 0 : ref_y - py;
3742 else
3744 /* wait until the pointer hits a border before making a
3745 * decision about the resize direction */
3748 /* draw the rubber-band window */
3749 if (!do_resize_opaque)
3751 draw_move_resize_grid(
3752 drag->x, drag->y, drag->width - 1, drag->height - 1);
3754 /* kick off resizing without requiring any motion if invoked with a key
3755 * press */
3756 if (exc->x.elast->type == KeyPress)
3758 int xo;
3759 int yo;
3761 if (FQueryPointer(
3762 dpy, Scr.Root, &JunkRoot, &JunkChild, &stashed_x,
3763 &stashed_y, &JunkX, &JunkY, &JunkMask) == False)
3765 /* pointer is on a different screen */
3766 stashed_x = 0;
3767 stashed_y = 0;
3769 xo = 0;
3770 yo = 0;
3771 __resize_step(
3772 exc, stashed_x, stashed_y, &xo, &yo, drag, orig,
3773 &xmotion, &ymotion, do_resize_opaque, True);
3775 else
3777 stashed_x = stashed_y = -1;
3780 /* loop to resize */
3781 memset(&ev, 0, sizeof(ev));
3782 while (!is_finished && bad_window != FW_W(fw))
3784 int rc = 0;
3786 /* block until there is an interesting event */
3787 while (rc != -1 &&
3788 (!FPending(dpy) || !FCheckMaskEvent(dpy, evmask, &ev)))
3790 rc = HandlePaging(
3791 &ev, dx, dy, &x, &y, &delta_x, &delta_y, False,
3792 False, True, fw->edge_delay_ms_resize);
3793 if (rc == 1)
3795 /* Fake an event to force window reposition */
3796 ev.type = MotionNotify;
3797 ev.xmotion.time = fev_get_evtime();
3798 fForceRedraw = True;
3799 break;
3802 if (rc == -1)
3804 FMaskEvent(
3805 dpy,
3806 evmask | EnterWindowMask | LeaveWindowMask,
3807 &ev);
3809 if (ev.type == MotionNotify ||
3810 ev.type == EnterNotify || ev.type == LeaveNotify)
3812 Bool is_motion;
3814 is_motion = (ev.type == MotionNotify) ? True : False;
3815 /* discard any extra motion events before a release */
3816 /* dv (2004-07-01): With XFree 4.1.0.1, some Mouse
3817 * events are not reported to fvwm when the pointer
3818 * moves very fast and suddenly stops in the corner of
3819 * the screen. Handle EnterNotify/LeaveNotify events
3820 * too to get an idea where the pointer might be. */
3821 while (
3822 FCheckMaskEvent(
3823 dpy, ButtonMotionMask |
3824 PointerMotionMask | ButtonReleaseMask |
3825 ButtonPressMask | EnterWindowMask |
3826 LeaveWindowMask, &ev) == True)
3828 if (ev.type == ButtonRelease ||
3829 ev.type == ButtonPress ||
3830 ev.type == KeyPress)
3832 break;
3836 if (ev.type == EnterNotify || ev.type == LeaveNotify)
3838 XEvent e2;
3839 int x;
3840 int y;
3842 /* Query the pointer to catch the latest information.
3843 * This *is* necessary. */
3844 if (FQueryPointer(
3845 dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
3846 &y, &JunkX, &JunkY, &JunkMask) == True)
3848 /* Must NOT use button_mask here, or resize
3849 * will not work with num lock */
3850 fev_make_null_event(&e2, dpy);
3851 e2.type = MotionNotify;
3852 e2.xmotion.time = fev_get_evtime();
3853 e2.xmotion.x_root = x;
3854 e2.xmotion.y_root = y;
3855 e2.xmotion.state = JunkMask;
3856 e2.xmotion.same_screen = True;
3857 ev = e2;
3858 fev_fake_event(&ev);
3860 else
3862 /* pointer is on a different screen,
3863 * ignore event */
3867 is_done = False;
3868 /* Handle a limited number of key press events to allow
3869 * mouseless operation */
3870 if (ev.type == KeyPress)
3872 Keyboard_shortcuts(&ev, fw, NULL, NULL, ButtonRelease);
3873 if (ev.type == ButtonRelease)
3875 do_send_cn = True;
3878 switch (ev.type)
3880 case ButtonPress:
3881 is_done = True;
3882 if (ev.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
3883 ((Button1Mask << (ev.xbutton.button - 1)) &
3884 button_mask))
3886 /* No new button was pressed, just a delayed
3887 * event */
3888 break;
3890 /* Abort the resize if
3891 * - the move started with a pressed button and
3892 * another button was pressed during the operation
3893 * - no button was started at the beginning and any
3894 * button except button 1 was pressed. */
3895 if (button_mask || (ev.xbutton.button != 1))
3897 fButtonAbort = True;
3898 /* fall through */
3900 else
3902 is_finished = True;
3903 do_send_cn = True;
3904 break;
3906 case KeyPress:
3907 /* simple code to bag out of move - CKH */
3908 if (fButtonAbort ||
3909 XLookupKeysym(&ev.xkey, 0) == XK_Escape)
3911 is_aborted = True;
3912 do_send_cn = True;
3913 is_finished = True;
3915 is_done = True;
3916 break;
3918 case ButtonRelease:
3919 is_finished = True;
3920 is_done = True;
3921 break;
3923 case MotionNotify:
3924 if (ev.xmotion.same_screen == False)
3926 continue;
3928 if (!fForceRedraw)
3930 x = ev.xmotion.x_root;
3931 y = ev.xmotion.y_root;
3932 /* resize before paging request to prevent
3933 * resize from lagging * mouse - mab */
3934 __resize_step(
3935 exc, x, y, &x_off, &y_off, drag, orig,
3936 &xmotion, &ymotion, do_resize_opaque,
3937 is_direction_fixed);
3938 /* need to move the viewport */
3939 HandlePaging(
3940 &ev, dx, dy, &x, &y, &delta_x,
3941 &delta_y, False, False, False,
3942 fw->edge_delay_ms_resize);
3944 /* redraw outline if we paged - mab */
3945 if (delta_x != 0 || delta_y != 0)
3947 sorig.x -= delta_x;
3948 sorig.y -= delta_y;
3949 drag->x -= delta_x;
3950 drag->y -= delta_y;
3952 __resize_step(
3953 exc, x, y, &x_off, &y_off, drag, orig,
3954 &xmotion, &ymotion, do_resize_opaque,
3955 is_direction_fixed);
3957 fForceRedraw = False;
3958 is_done = True;
3959 break;
3961 case PropertyNotify:
3963 evh_args_t ea;
3964 exec_context_changes_t ecc;
3966 ecc.x.etrigger = &ev;
3967 ea.exc = exc_clone_context(exc, &ecc, ECC_ETRIGGER);
3968 HandlePropertyNotify(&ea);
3969 exc_destroy_context(ea.exc);
3970 is_done = True;
3971 break;
3974 default:
3975 break;
3977 if (!is_done)
3979 if (!do_resize_opaque)
3981 /* must undraw the rubber band in case the
3982 * event causes some drawing */
3983 switch_move_resize_grid(False);
3985 dispatch_event(&ev);
3986 if (!do_resize_opaque)
3988 draw_move_resize_grid(
3989 drag->x, drag->y, drag->width - 1,
3990 drag->height - 1);
3993 else
3995 if (do_resize_opaque)
3997 /* only do this with opaque resizes, (i.e. the
3998 * server is not grabbed) */
3999 BroadcastConfig(M_CONFIGURE_WINDOW, fw);
4000 FlushAllMessageQueues();
4005 /* erase the rubber-band */
4006 if (!do_resize_opaque)
4008 switch_move_resize_grid(False);
4010 /* pop down the size window */
4011 if (!Scr.gs.do_hide_resize_window)
4013 XUnmapWindow(dpy, Scr.SizeWindow);
4015 if (is_aborted || bad_window == FW_W(fw))
4017 /* return pointer if aborted resize was invoked with key */
4018 if (stashed_x >= 0)
4020 FWarpPointer(
4021 dpy, None, Scr.Root, 0, 0, 0, 0, stashed_x,
4022 stashed_y);
4024 if (was_maximized)
4026 /* since we aborted the resize, the window is still
4027 * maximized */
4028 SET_MAXIMIZED(fw, 1);
4030 if (do_resize_opaque)
4032 int xo;
4033 int yo;
4034 rectangle g;
4036 xo = 0;
4037 yo = 0;
4038 xmotion = 1;
4039 ymotion = 1;
4040 g = sorig;
4041 __resize_step(
4042 exc, sorig.x, sorig.y, &xo, &yo, &g, orig,
4043 &xmotion, &ymotion, do_resize_opaque, True);
4045 if (vx != Scr.Vx || vy != Scr.Vy)
4047 MoveViewport(vx, vy, False);
4049 /* restore all geometry-related info */
4050 fw->g = g_backup;
4051 if (bad_window == FW_W(fw))
4053 XUnmapWindow(dpy, FW_W_FRAME(fw));
4054 border_undraw_decorations(fw);
4055 XBell(dpy, 0);
4058 else if (!is_aborted && bad_window != FW_W(fw))
4060 rectangle new_g;
4062 /* size will be >= to requested */
4063 constrain_size(
4064 fw, exc->x.elast, &drag->width, &drag->height,
4065 xmotion, ymotion, CS_ROUND_UP);
4066 if (IS_SHADED(fw))
4068 get_shaded_geometry(fw, &new_g, drag);
4070 else
4072 new_g = *drag;
4074 if (do_resize_opaque)
4076 frame_update_move_resize_args(mr_args, &new_g);
4078 else
4080 frame_setup_window(
4081 fw, new_g.x, new_g.y, new_g.width,
4082 new_g.height, False);
4084 if (IS_SHADED(fw))
4086 fw->g.normal.width = drag->width;
4087 fw->g.normal.height = drag->height;
4090 if (is_aborted && was_maximized)
4092 /* force redraw */
4093 border_draw_decorations(
4094 fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
4095 NULL, NULL);
4097 if (Scr.bo.do_install_root_cmap)
4099 UninstallRootColormap();
4101 else
4103 UninstallFvwmColormap();
4105 ResizeWindow = None;
4106 if (!do_resize_opaque)
4108 /* Throw away some events that dont interest us right now. */
4109 discard_events(EnterWindowMask|LeaveWindowMask);
4110 Scr.flags.is_wire_frame_displayed = False;
4111 MyXUngrabServer(dpy);
4113 if (mr_args != NULL)
4115 frame_free_move_resize_args(fw, mr_args);
4117 if (do_send_cn == True)
4119 rectangle g;
4121 if (is_aborted)
4123 g = sorig;
4125 else
4127 g = *drag;
4129 SendConfigureNotify(fw, g.x, g.y, g.width, g.height, 0, True);
4131 MyXUngrabKeyboard(dpy);
4132 WaitForButtonsUp(True);
4133 UngrabEm(GRAB_NORMAL);
4134 Scr.flags.do_edge_wrap_x = edge_wrap_x;
4135 Scr.flags.do_edge_wrap_y = edge_wrap_y;
4136 update_absolute_geometry(fw);
4137 maximize_adjust_offset(fw);
4138 GNOME_SetWinArea(fw);
4139 if (is_aborted)
4141 return False;
4144 return True;
4147 void CMD_Resize(F_CMD_ARGS)
4149 FvwmWindow *fw = exc->w.fw;
4151 if (IS_EWMH_FULLSCREEN(fw))
4153 /* do not unmaximize ! */
4154 CMD_ResizeMaximize(F_PASS_ARGS);
4155 return;
4158 __resize_window(F_PASS_ARGS);
4160 return;
4163 /* ----------------------------- maximizing code --------------------------- */
4165 Bool is_window_sticky_across_pages(FvwmWindow *fw)
4167 if (IS_STICKY_ACROSS_PAGES(fw) ||
4168 (IS_ICONIFIED(fw) && IS_ICON_STICKY_ACROSS_PAGES(fw)))
4170 return True;
4172 else
4174 return False;
4178 Bool is_window_sticky_across_desks(FvwmWindow *fw)
4180 if (IS_STICKY_ACROSS_DESKS(fw) ||
4181 (IS_ICONIFIED(fw) && IS_ICON_STICKY_ACROSS_DESKS(fw)))
4183 return True;
4185 else
4187 return False;
4191 static void move_sticky_window_to_same_page(
4192 int *x11, int *x12, int *y11, int *y12,
4193 int x21, int x22, int y21, int y22)
4195 /* make sure the x coordinate is on the same page as the reference
4196 * window */
4197 if (*x11 >= x22)
4199 while (*x11 >= x22)
4201 *x11 -= Scr.MyDisplayWidth;
4202 *x12 -= Scr.MyDisplayWidth;
4205 else if (*x12 <= x21)
4207 while (*x12 <= x21)
4209 *x11 += Scr.MyDisplayWidth;
4210 *x12 += Scr.MyDisplayWidth;
4213 /* make sure the y coordinate is on the same page as the reference
4214 * window */
4215 if (*y11 >= y22)
4217 while (*y11 >= y22)
4219 *y11 -= Scr.MyDisplayHeight;
4220 *y12 -= Scr.MyDisplayHeight;
4223 else if (*y12 <= y21)
4225 while (*y12 <= y21)
4227 *y11 += Scr.MyDisplayHeight;
4228 *y12 += Scr.MyDisplayHeight;
4232 return;
4235 static void MaximizeHeight(
4236 FvwmWindow *win, int win_width, int win_x, int *win_height,
4237 int *win_y, Bool grow_up, Bool grow_down, int top_border,
4238 int bottom_border, int *layers)
4240 FvwmWindow *cwin;
4241 int x11, x12, x21, x22;
4242 int y11, y12, y21, y22;
4243 int new_y1, new_y2;
4244 rectangle g;
4245 Bool rc;
4247 x11 = win_x; /* Start x */
4248 y11 = *win_y; /* Start y */
4249 x12 = x11 + win_width; /* End x */
4250 y12 = y11 + *win_height; /* End y */
4251 new_y1 = top_border;
4252 new_y2 = bottom_border;
4254 for (cwin = Scr.FvwmRoot.next; cwin; cwin = cwin->next)
4256 if (cwin == win ||
4257 (cwin->Desk != win->Desk &&
4258 !is_window_sticky_across_desks(cwin)))
4260 continue;
4262 if ((layers[0] >= 0 && cwin->layer < layers[0]) ||
4263 (layers[1] >= 0 && cwin->layer > layers[1]))
4265 continue;
4267 rc = get_visible_window_or_icon_geometry(cwin, &g);
4268 if (rc == False)
4270 continue;
4272 x21 = g.x;
4273 y21 = g.y;
4274 x22 = x21 + g.width;
4275 y22 = y21 + g.height;
4276 if (is_window_sticky_across_pages(cwin))
4278 move_sticky_window_to_same_page(
4279 &x21, &x22, &new_y1, &new_y2, x11, x12, y11,
4280 y12);
4283 /* Are they in the same X space? */
4284 if (!((x22 <= x11) || (x21 >= x12)))
4286 if ((y22 <= y11) && (y22 >= new_y1))
4288 new_y1 = y22;
4290 else if ((y12 <= y21) && (new_y2 >= y21))
4292 new_y2 = y21;
4296 if (!grow_up)
4298 new_y1 = y11;
4300 if (!grow_down)
4302 new_y2 = y12;
4304 *win_height = new_y2 - new_y1;
4305 *win_y = new_y1;
4307 return;
4310 static void MaximizeWidth(
4311 FvwmWindow *win, int *win_width, int *win_x, int win_height,
4312 int win_y, Bool grow_left, Bool grow_right, int left_border,
4313 int right_border, int *layers)
4315 FvwmWindow *cwin;
4316 int x11, x12, x21, x22;
4317 int y11, y12, y21, y22;
4318 int new_x1, new_x2;
4319 rectangle g;
4320 Bool rc;
4322 x11 = *win_x; /* Start x */
4323 y11 = win_y; /* Start y */
4324 x12 = x11 + *win_width; /* End x */
4325 y12 = y11 + win_height; /* End y */
4326 new_x1 = left_border;
4327 new_x2 = right_border;
4329 for (cwin = Scr.FvwmRoot.next; cwin; cwin = cwin->next)
4331 if (cwin == win ||
4332 (cwin->Desk != win->Desk &&
4333 !is_window_sticky_across_desks(cwin)))
4335 continue;
4337 if ((layers[0] >= 0 && cwin->layer < layers[0]) ||
4338 (layers[1] >= 0 && cwin->layer > layers[1]))
4340 continue;
4342 rc = get_visible_window_or_icon_geometry(cwin, &g);
4343 if (rc == False)
4345 continue;
4347 x21 = g.x;
4348 y21 = g.y;
4349 x22 = x21 + g.width;
4350 y22 = y21 + g.height;
4351 if (is_window_sticky_across_pages(cwin))
4353 move_sticky_window_to_same_page(
4354 &new_x1, &new_x2, &y21, &y22, x11, x12, y11,
4355 y12);
4358 /* Are they in the same Y space? */
4359 if (!((y22 <= y11) || (y21 >= y12)))
4361 if ((x22 <= x11) && (x22 >= new_x1))
4363 new_x1 = x22;
4365 else if ((x12 <= x21) && (new_x2 >= x21))
4367 new_x2 = x21;
4371 if (!grow_left)
4373 new_x1 = x11;
4375 if (!grow_right)
4377 new_x2 = x12;
4379 *win_width = new_x2 - new_x1;
4380 *win_x = new_x1;
4382 return;
4385 static void unmaximize_fvwm_window(
4386 FvwmWindow *fw)
4388 rectangle new_g;
4390 SET_MAXIMIZED(fw, 0);
4391 get_relative_geometry(&new_g, &fw->g.normal);
4392 if (IS_SHADED(fw))
4394 get_shaded_geometry(fw, &new_g, &new_g);
4396 frame_setup_window(
4397 fw, new_g.x, new_g.y, new_g.width, new_g.height, True);
4398 border_draw_decorations(
4399 fw, PART_ALL, (Scr.Hilite == fw), True, CLEAR_ALL, NULL, NULL);
4400 if (IS_EWMH_FULLSCREEN(fw))
4402 SET_EWMH_FULLSCREEN(fw, False);
4403 if (DO_EWMH_USE_STACKING_HINTS(fw))
4405 new_layer(fw, fw->ewmh_normal_layer);
4407 apply_decor_change(fw);
4409 return;
4412 static void maximize_fvwm_window(
4413 FvwmWindow *fw, rectangle *geometry)
4415 SET_MAXIMIZED(fw, 1);
4416 fw->g.max_defect.width = 0;
4417 fw->g.max_defect.height = 0;
4418 constrain_size(
4419 fw, NULL, &geometry->width, &geometry->height, 0, 0,
4420 CS_UPDATE_MAX_DEFECT);
4421 fw->g.max = *geometry;
4422 if (IS_SHADED(fw))
4424 get_shaded_geometry(fw, geometry, &fw->g.max);
4426 frame_setup_window(
4427 fw, geometry->x, geometry->y, geometry->width,
4428 geometry->height, True);
4429 border_draw_decorations(
4430 fw, PART_ALL, (Scr.Hilite == fw), True, CLEAR_ALL, NULL, NULL);
4431 update_absolute_geometry(fw);
4432 /* remember the offset between old and new position in case the
4433 * maximized window is moved more than the screen width/height. */
4434 fw->g.max_offset.x = fw->g.normal.x - fw->g.max.x;
4435 fw->g.max_offset.y = fw->g.normal.y - fw->g.max.y;
4436 #if 0
4437 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);
4438 #endif
4440 return;
4445 * Procedure:
4446 * (Un)Maximize a window.
4449 void CMD_Maximize(F_CMD_ARGS)
4451 int page_x, page_y;
4452 int val1, val2, val1_unit, val2_unit;
4453 int toggle;
4454 char *token;
4455 char *taction;
4456 Bool grow_up = False;
4457 Bool grow_down = False;
4458 Bool grow_left = False;
4459 Bool grow_right = False;
4460 Bool do_force_maximize = False;
4461 Bool is_screen_given = False;
4462 Bool ignore_working_area = False;
4463 int layers[2] = { -1, -1 };
4464 Bool global_flag_parsed = False;
4465 int scr_x, scr_y;
4466 int scr_w, scr_h;
4467 rectangle new_g;
4468 FvwmWindow *fw = exc->w.fw;
4470 if (
4471 !is_function_allowed(
4472 F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
4474 XBell(dpy, 0);
4475 return;
4477 /* Check for "global" flag ("absolute" is for compatibility with E) */
4478 while (!global_flag_parsed)
4480 token = PeekToken(action, &taction);
4481 if (!token)
4483 global_flag_parsed = True;
4485 else
4487 if (StrEquals(token, "screen"))
4489 int scr;
4491 is_screen_given = True;
4492 token = PeekToken(taction, &action);
4493 scr = FScreenGetScreenArgument(
4494 token, FSCREEN_SPEC_PRIMARY);
4495 FScreenGetScrRect(
4496 NULL, scr, &scr_x, &scr_y, &scr_w,
4497 &scr_h);
4499 else if (StrEquals(token, "ewmhiwa"))
4501 ignore_working_area = True;
4502 action = taction;
4504 else if (StrEquals(token, "growonwindowlayer"))
4506 layers[0] = fw->layer;
4507 layers[1] = fw->layer;
4508 action = taction;
4510 else if (StrEquals(token, "growonlayers"))
4512 int n;
4514 n = GetIntegerArguments(
4515 taction, &action, layers, 2);
4516 if (n != 2)
4518 layers[0] = -1;
4519 layers[1] = -1;
4522 else
4524 global_flag_parsed = True;
4528 toggle = ParseToggleArgument(action, &action, -1, 0);
4529 if (toggle == 0 && !IS_MAXIMIZED(fw))
4531 return;
4534 if (toggle == 1 && IS_MAXIMIZED(fw))
4536 /* Fake that the window is not maximized. */
4537 do_force_maximize = True;
4540 /* find the new page and geometry */
4541 new_g.x = fw->g.frame.x;
4542 new_g.y = fw->g.frame.y;
4543 new_g.width = fw->g.frame.width;
4544 new_g.height = fw->g.frame.height;
4545 get_page_offset_check_visible(&page_x, &page_y, fw);
4547 /* Check if we should constrain rectangle to some Xinerama screen */
4548 if (!is_screen_given)
4550 fscreen_scr_arg fscr;
4552 fscr.xypos.x = fw->g.frame.x + fw->g.frame.width / 2 - page_x;
4553 fscr.xypos.y = fw->g.frame.y + fw->g.frame.height / 2 - page_y;
4554 FScreenGetScrRect(
4555 &fscr, FSCREEN_XYPOS, &scr_x, &scr_y, &scr_w, &scr_h);
4558 if (!ignore_working_area)
4560 EWMH_GetWorkAreaIntersection(
4561 fw, &scr_x, &scr_y, &scr_w, &scr_h,
4562 EWMH_MAXIMIZE_MODE(fw));
4564 #if 0
4565 fprintf(stderr, "%s: page=(%d,%d), scr=(%d,%d, %dx%d)\n", __FUNCTION__,
4566 page_x, page_y, scr_x, scr_y, scr_w, scr_h);
4567 #endif
4569 /* parse first parameter */
4570 val1_unit = scr_w;
4571 token = PeekToken(action, &taction);
4572 if (token && StrEquals(token, "grow"))
4574 grow_left = True;
4575 grow_right = True;
4576 val1 = 100;
4577 val1_unit = scr_w;
4579 else if (token && StrEquals(token, "growleft"))
4581 grow_left = True;
4582 val1 = 100;
4583 val1_unit = scr_w;
4585 else if (token && StrEquals(token, "growright"))
4587 grow_right = True;
4588 val1 = 100;
4589 val1_unit = scr_w;
4591 else
4593 if (GetOnePercentArgument(token, &val1, &val1_unit) == 0)
4595 val1 = 100;
4596 val1_unit = scr_w;
4598 else if (val1 < 0)
4600 /* handle negative offsets */
4601 if (val1_unit == scr_w)
4603 val1 = 100 + val1;
4605 else
4607 val1 = scr_w + val1;
4612 /* parse second parameter */
4613 val2_unit = scr_h;
4614 token = PeekToken(taction, NULL);
4615 if (token && StrEquals(token, "grow"))
4617 grow_up = True;
4618 grow_down = True;
4619 val2 = 100;
4620 val2_unit = scr_h;
4622 else if (token && StrEquals(token, "growup"))
4624 grow_up = True;
4625 val2 = 100;
4626 val2_unit = scr_h;
4628 else if (token && StrEquals(token, "growdown"))
4630 grow_down = True;
4631 val2 = 100;
4632 val2_unit = scr_h;
4634 else
4636 if (GetOnePercentArgument(token, &val2, &val2_unit) == 0)
4638 val2 = 100;
4639 val2_unit = scr_h;
4641 else if (val2 < 0)
4643 /* handle negative offsets */
4644 if (val2_unit == scr_h)
4646 val2 = 100 + val2;
4648 else
4650 val2 = scr_h + val2;
4655 #if 0
4656 fprintf(stderr, "%s: page=(%d,%d), scr=(%d,%d, %dx%d)\n", __FUNCTION__,
4657 page_x, page_y, scr_x, scr_y, scr_w, scr_h);
4658 #endif
4660 if (IS_MAXIMIZED(fw) && !do_force_maximize)
4662 unmaximize_fvwm_window(fw);
4664 else /* maximize */
4666 /* handle command line arguments */
4667 if (grow_up || grow_down)
4669 MaximizeHeight(
4670 fw, new_g.width, new_g.x, &new_g.height,
4671 &new_g.y, grow_up, grow_down, page_y + scr_y,
4672 page_y + scr_y + scr_h, layers);
4674 else if (val2 > 0)
4676 new_g.height = val2 * val2_unit / 100;
4677 new_g.y = page_y + scr_y;
4679 if (grow_left || grow_right)
4681 MaximizeWidth(
4682 fw, &new_g.width, &new_g.x, new_g.height,
4683 new_g.y, grow_left, grow_right,
4684 page_x + scr_x, page_x + scr_x + scr_w,
4685 layers);
4687 else if (val1 >0)
4689 new_g.width = val1 * val1_unit / 100;
4690 new_g.x = page_x + scr_x;
4692 if (val1 == 0 && val2 == 0)
4694 new_g.x = page_x + scr_x;
4695 new_g.y = page_y + scr_y;
4696 new_g.height = scr_h;
4697 new_g.width = scr_w;
4699 /* now maximize it */
4700 maximize_fvwm_window(fw, &new_g);
4702 EWMH_SetWMState(fw, False);
4703 GNOME_SetWinArea(fw);
4705 return;
4710 * Same as CMD_Resize and CMD_ResizeMove, but the window ends up maximized
4711 * without touching the normal geometry.
4714 void CMD_ResizeMaximize(F_CMD_ARGS)
4716 rectangle normal_g;
4717 rectangle max_g;
4718 Bool was_resized;
4719 FvwmWindow *fw = exc->w.fw;
4721 /* keep a copy of the old geometry */
4722 normal_g = fw->g.normal;
4723 /* resize the window normally */
4724 was_resized = __resize_window(F_PASS_ARGS);
4725 if (was_resized == True)
4727 /* set the new geometry as the maximized geometry and restore
4728 * the old normal geometry */
4729 max_g = fw->g.normal;
4730 max_g.x -= Scr.Vx;
4731 max_g.y -= Scr.Vy;
4732 fw->g.normal = normal_g;
4733 /* and mark it as maximized */
4734 maximize_fvwm_window(fw, &max_g);
4736 EWMH_SetWMState(fw, False);
4738 return;
4741 void CMD_ResizeMoveMaximize(F_CMD_ARGS)
4743 rectangle normal_g;
4744 rectangle max_g;
4745 Bool was_resized;
4746 FvwmWindow *fw = exc->w.fw;
4748 /* keep a copy of the old geometry */
4749 normal_g = fw->g.normal;
4750 /* resize the window normally */
4751 was_resized = resize_move_window(F_PASS_ARGS);
4752 if (was_resized == True)
4754 /* set the new geometry as the maximized geometry and restore
4755 * the old normal geometry */
4756 max_g = fw->g.normal;
4757 max_g.x -= Scr.Vx;
4758 max_g.y -= Scr.Vy;
4759 fw->g.normal = normal_g;
4760 /* and mark it as maximized */
4761 maximize_fvwm_window(fw, &max_g);
4763 EWMH_SetWMState(fw, False);
4765 return;
4768 /* ----------------------------- stick code -------------------------------- */
4770 int stick_across_pages(F_CMD_ARGS, int toggle)
4772 FvwmWindow *fw = exc->w.fw;
4774 if ((toggle == 1 && IS_STICKY_ACROSS_PAGES(fw)) ||
4775 (toggle == 0 && !IS_STICKY_ACROSS_PAGES(fw)))
4777 return 0;
4779 if (IS_STICKY_ACROSS_PAGES(fw))
4781 SET_STICKY_ACROSS_PAGES(fw, 0);
4783 else
4785 if (!IsRectangleOnThisPage(&fw->g.frame, Scr.CurrentDesk))
4787 action = "";
4788 __move_window(F_PASS_ARGS, False, MOVE_PAGE);
4790 SET_STICKY_ACROSS_PAGES(fw, 1);
4793 return 1;
4796 int stick_across_desks(F_CMD_ARGS, int toggle)
4798 FvwmWindow *fw = exc->w.fw;
4800 if ((toggle == 1 && IS_STICKY_ACROSS_DESKS(fw)) ||
4801 (toggle == 0 && !IS_STICKY_ACROSS_DESKS(fw)))
4803 return 0;
4806 if (IS_STICKY_ACROSS_DESKS(fw))
4808 SET_STICKY_ACROSS_DESKS(fw, 0);
4809 fw->Desk = Scr.CurrentDesk;
4810 GNOME_SetDeskCount();
4811 GNOME_SetDesk(fw);
4813 else
4815 if (fw->Desk != Scr.CurrentDesk)
4817 do_move_window_to_desk(fw, Scr.CurrentDesk);
4819 SET_STICKY_ACROSS_DESKS(fw, 1);
4822 return 1;
4825 static void __handle_stick_exit(
4826 FvwmWindow *fw, int do_not_draw, int do_silently)
4828 if (do_not_draw == 0)
4830 border_draw_decorations(
4831 fw, PART_TITLE | PART_BUTTONS, (Scr.Hilite==fw), True,
4832 CLEAR_ALL, NULL, NULL);
4834 if (!do_silently)
4836 BroadcastConfig(M_CONFIGURE_WINDOW,fw);
4837 EWMH_SetWMState(fw, False);
4838 EWMH_SetWMDesktop(fw);
4839 GNOME_SetHints(fw);
4842 return;
4845 void handle_stick_across_pages(
4846 F_CMD_ARGS, int toggle, int do_not_draw, int do_silently)
4848 FvwmWindow *fw = exc->w.fw;
4849 int did_change;
4851 did_change = stick_across_pages(F_PASS_ARGS, toggle);
4852 if (did_change)
4854 __handle_stick_exit(fw, do_not_draw, do_silently);
4857 return;
4860 void handle_stick_across_desks(
4861 F_CMD_ARGS, int toggle, int do_not_draw, int do_silently)
4863 FvwmWindow *fw = exc->w.fw;
4864 int did_change;
4866 did_change = stick_across_desks(F_PASS_ARGS, toggle);
4867 if (did_change)
4869 __handle_stick_exit(fw, do_not_draw, do_silently);
4872 return;
4875 void handle_stick(
4876 F_CMD_ARGS, int toggle_page, int toggle_desk, int do_not_draw,
4877 int do_silently)
4879 FvwmWindow *fw = exc->w.fw;
4880 int did_change;
4882 did_change = 0;
4883 did_change |= stick_across_desks(F_PASS_ARGS, toggle_desk);
4884 did_change |= stick_across_pages(F_PASS_ARGS, toggle_page);
4885 if (did_change)
4887 __handle_stick_exit(fw, do_not_draw, do_silently);
4890 return;
4893 void CMD_Stick(F_CMD_ARGS)
4895 int toggle;
4897 toggle = ParseToggleArgument(action, &action, -1, 0);
4898 if (toggle == -1 && IS_STICKY_ACROSS_DESKS(exc->w.fw) !=
4899 IS_STICKY_ACROSS_PAGES(exc->w.fw))
4901 /* don't switch between only stickypage and only stickydesk.
4902 * rather switch it off completely */
4903 toggle = 0;
4905 handle_stick(F_PASS_ARGS, toggle, toggle, 0, 0);
4907 return;
4910 void CMD_StickAcrossPages(F_CMD_ARGS)
4912 int toggle;
4914 toggle = ParseToggleArgument(action, &action, -1, 0);
4915 handle_stick_across_pages(F_PASS_ARGS, toggle, 0, 0);
4917 return;
4920 void CMD_StickAcrossDesks(F_CMD_ARGS)
4922 int toggle;
4924 toggle = ParseToggleArgument(action, &action, -1, 0);
4925 handle_stick_across_desks(F_PASS_ARGS, toggle, 0, 0);
4927 return;