/* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /***************************************************************************** * This module is all original code * by Rob Nation * Copyright 1993, Robert Nation * You may use this code for any purpose, as long as the original * copyright remains in the source code and all documentation ****************************************************************************/ /************************************************************************ * * code for moving and resizing windows * ***********************************************************************/ #include "config.h" #include #include "libs/fvwmlib.h" #include "libs/Colorset.h" #include "libs/FScreen.h" #include "fvwm.h" #include "externs.h" #include "cursor.h" #include "functions.h" #include "bindings.h" #include "misc.h" #include "screen.h" #include "defaults.h" #include "move_resize.h" #include "module_interface.h" #include "focus.h" #include "borders.h" #include "geometry.h" #include "gnome.h" #include "colormaps.h" #include "virtual.h" #include "decorations.h" #include "events.h" #include /* ----- move globals ----- */ extern XEvent Event; #define SNAP_NONE 0x00 #define SNAP_WINDOWS 0x01 #define SNAP_ICONS 0x02 #define SNAP_SAME 0x04 #define SNAP_SCREEN 0x08 #define MOVE_NORMAL 0x00 #define MOVE_PAGE 0x01 #define MOVE_SCREEN 0x02 /* Animated move stuff added by Greg J. Badros, gjb@cs.washington.edu */ float rgpctMovementDefault[32] = { -.01, 0, .01, .03,.08,.18,.3,.45,.60,.75,.85,.90,.94,.97,.99,1.0 /* must end in 1.0 */ }; int cmsDelayDefault = 10; /* milliseconds */ /* current geometry of size window */ static rectangle sizew_g = { -1, -1, -1, -1 }; /* ----- end of move globals ----- */ /* ----- resize globals ----- */ /* DO NOT USE (STATIC) GLOBALS IN THIS MODULE! * Since some functions are called from other modules unwanted side effects * (i.e. bugs.) would be created */ extern Window PressedW; static void DoResize( int x_root, int y_root, FvwmWindow *tmp_win, rectangle *drag, rectangle *orig, int *xmotionp, int *ymotionp, Bool do_resize_opaque); static void DisplaySize( FvwmWindow *tmp_win, XEvent *eventp, int width, int height, Bool Init, Bool resetLast); static void draw_move_resize_grid(int x, int y, int width, int height); /* ----- end of resize globals ----- */ /* The vars are named for the x-direction, but this is used for both x and y */ static int GetOnePositionArgument( char *s1,int x,int w,int *pFinalX,float factor, int max, Bool is_x) { int val; int cch = strlen(s1); Bool add_pointer_position = False; if (cch == 0) return 0; if (s1[cch-1] == 'p') { factor = 1; /* Use pixels, so don't multiply by factor */ s1[cch-1] = '\0'; } if (strcmp(s1,"w") == 0) { *pFinalX = x; } else if (sscanf(s1,"w-%d",&val) == 1) { *pFinalX = x - (int)(val*factor); } else if (sscanf(s1,"w+%d",&val) == 1 || sscanf(s1,"w%d",&val) == 1 ) { *pFinalX = x + (int)(val*factor); } else if (sscanf(s1,"m-%d",&val) == 1) { add_pointer_position = True; *pFinalX = -(int)(val*factor); } else if (sscanf(s1,"m+%d",&val) == 1 || sscanf(s1,"m%d",&val) == 1 ) { add_pointer_position = True; *pFinalX = (int)(val*factor); } else if (sscanf(s1,"-%d",&val) == 1) { *pFinalX = max-w - (int)(val*factor); } else if (sscanf(s1,"%d",&val) == 1) { *pFinalX = (int)(val*factor); } else { return 0; } if (add_pointer_position) { int x = 0; int y = 0; XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY, &x, &y, &JunkMask); *pFinalX += (is_x) ? x : y; } return 1; } /* GetMoveArguments is used for Move & AnimatedMove * It lets you specify in all the following ways * 20 30 Absolute percent position, from left edge and top * -50 50 Absolute percent position, from right edge and top * 10p 5p Absolute pixel position * 10p -0p Absolute pixel position, from bottom * w+5 w-10p Relative position, right 5%, up ten pixels * w+5 w-10p Pointer relative position, right 5%, up ten pixels * Returns 2 when x & y have parsed without error, 0 otherwise */ static int GetMoveArguments( char **paction, int w, int h, int *pFinalX, int *pFinalY, Bool *fWarp, Bool *fPointer) { char *s1 = NULL; char *s2 = NULL; char *warp = NULL; char *action; char *naction; int scrWidth = Scr.MyDisplayWidth; int scrHeight = Scr.MyDisplayHeight; int retval = 0; if (!paction) return 0; action = *paction; action = GetNextToken(action, &s1); if (s1 && fPointer && StrEquals(s1, "pointer")) { *fPointer = True; *paction = action; free(s1); return 0; } else { action = GetNextToken(action, &s2); if (fWarp) { warp = PeekToken(action, &naction); if (StrEquals(warp, "Warp")) { *fWarp = True; action = naction; } } } if (s1 != NULL && s2 != NULL) { retval = 0; if (StrEquals(s1, "keep")) { retval++; } else if (GetOnePositionArgument( s1, *pFinalX, w, pFinalX, (float)scrWidth/100, scrWidth, True)) { retval++; } if (StrEquals(s2, "keep")) { retval++; } else if (GetOnePositionArgument( s2, *pFinalY, h, pFinalY, (float)scrHeight/100, scrHeight, False)) { retval++; } if (retval == 0) *fWarp = False; /* make sure warping is off for interactive moves */ } else { /* not enough arguments, switch to current page. */ while (*pFinalX < 0) { *pFinalX = Scr.MyDisplayWidth + *pFinalX; } while (*pFinalY < 0) { *pFinalY = Scr.MyDisplayHeight + *pFinalY; } } if (s1) free(s1); if (s2) free(s2); *paction = action; return retval; } static int ParseOneResizeArgument( char *arg, int scr_size, int base_size, int size_inc, int bw, int title_size, int *ret_size) { int unit_table[3]; int value; int suffix; if (StrEquals(arg, "keep")) { /* do not change width */ } else { if (GetSuffixedIntegerArguments(arg, NULL, &value, 1, "pc", &suffix) < 1) { return 0; } else { /* convert the value/suffix pairs to pixels */ unit_table[0] = scr_size; unit_table[1] = 100; unit_table[2] = 100 * size_inc; *ret_size = SuffixToPercentValue(value, suffix, unit_table); if (*ret_size < 0) *ret_size += scr_size; else { if (suffix == 2) { /* account for base width */ *ret_size += base_size; } *ret_size += title_size + 2 * bw; } } } return 1; } static int GetResizeArguments( char **paction, int x, int y, int w_base, int h_base, int w_inc, int h_inc, int bw, int h_title, int *pFinalW, int *pFinalH) { int n; char *naction; char *token; char *s1; char *s2; if (!paction) return 0; token = PeekToken(*paction, &naction); if (!token) return 0; if (StrEquals(token, "bottomright") || StrEquals(token, "br")) { int nx = x + *pFinalW - 1; int ny = y + *pFinalH - 1; n = GetMoveArguments(&naction, 0, 0, &nx, &ny, NULL, NULL); if (n < 2) return 0; *pFinalW = nx - x + 1; *pFinalH = ny - y + 1; *paction = naction; } else { s1 = safestrdup(token); naction = GetNextToken(naction, &s2); if (!s2) { free(s1); return 0; } *paction = naction; n = 0; n += ParseOneResizeArgument( s1, Scr.MyDisplayWidth, w_base, w_inc, bw, 0, pFinalW); n += ParseOneResizeArgument( s2, Scr.MyDisplayHeight, h_base, h_inc, bw, h_title, pFinalH); free(s1); free(s2); if (n < 2) n = 0; } return n; } static int GetResizeMoveArguments( char **paction, int w_base, int h_base, int w_inc, int h_inc, int bw, int h_title, int *pFinalX, int *pFinalY, int *pFinalW, int *pFinalH, Bool *fWarp, Bool *fPointer) { char *action = *paction; if (!paction) return 0; if (GetResizeArguments( &action, *pFinalX, *pFinalY, w_base, h_base, w_inc, h_inc, bw, h_title, pFinalW, pFinalH) < 2) { return 0; } if (GetMoveArguments( &action, *pFinalW, *pFinalH, pFinalX, pFinalY, fWarp, NULL) < 2) { return 0; } *paction = action; return 4; } /* Positions the SizeWindow on the current ("moused") xinerama-screen */ static void position_geometry_window(XEvent *eventp) { int x; int y; fscreen_scr_arg fscr; fscr.mouse_ev = eventp; /* Probably should remove this positioning code from {builtins,fvwm}.c? */ if (Scr.gs.EmulateMWM) { FScreenCenterOnScreen( &fscr, FSCREEN_CURRENT, &x, &y, sizew_g.width, sizew_g.height); } else { FScreenGetScrRect(&fscr, FSCREEN_CURRENT, &x, &y, NULL, NULL); } if (x != sizew_g.x || y != sizew_g.y) { switch_move_resize_grid(False); XMoveWindow(dpy, Scr.SizeWindow, x, y); switch_move_resize_grid(True); sizew_g.x = x; sizew_g.y = y; } return; } void resize_geometry_window(void) { int w; int h; int cset = Scr.DefaultColorset; Scr.SizeStringWidth = XTextWidth(Scr.DefaultFont.font, GEOMETRY_WINDOW_STRING, sizeof(GEOMETRY_WINDOW_STRING) - 1); w = Scr.SizeStringWidth + 2 * GEOMETRY_WINDOW_BW; h = Scr.DefaultFont.height + 2 * GEOMETRY_WINDOW_BW; if (w != sizew_g.width || h != sizew_g.height) { XResizeWindow(dpy, Scr.SizeWindow, w, h); sizew_g.width = w; sizew_g.height = h; } if (cset >= 0) { SetWindowBackground( dpy, Scr.SizeWindow, w, h, &Colorset[cset], Pdepth, Scr.StdGC, False); } else { XSetWindowBackground(dpy, Scr.SizeWindow, Scr.StdBack); } return; } /*********************************************************************** * * Procedure: * DisplayPosition - display the position in the dimensions window * * Inputs: * tmp_win - the current fvwm window * x, y - position of the window * ************************************************************************/ static void DisplayPosition( FvwmWindow *tmp_win, XEvent *eventp, int x, int y,int Init) { char str[100]; int offset; fscreen_scr_arg fscr; if (Scr.gs.do_hide_position_window) return; position_geometry_window(eventp); /* Translate x,y into local screen coordinates, in case Xinerama is used. */ fscr.xypos.x = x; fscr.xypos.y = y; FScreenTranslateCoordinates( NULL, FSCREEN_GLOBAL, &fscr, FSCREEN_XYPOS, &x, &y); (void)sprintf(str, GEOMETRY_WINDOW_POS_STRING, x, y); if (Init) { XClearWindow(dpy, Scr.SizeWindow); } else { /* just clear indside the relief lines to reduce flicker */ XClearArea(dpy,Scr.SizeWindow, GEOMETRY_WINDOW_BW, GEOMETRY_WINDOW_BW, Scr.SizeStringWidth, Scr.DefaultFont.height, False); } if (Pdepth >= 2) { RelieveRectangle( dpy, Scr.SizeWindow, 0, 0, Scr.SizeStringWidth + GEOMETRY_WINDOW_BW * 2 - 1, Scr.DefaultFont.height + GEOMETRY_WINDOW_BW * 2 - 1, Scr.StdReliefGC, Scr.StdShadowGC, GEOMETRY_WINDOW_BW); } offset = (Scr.SizeStringWidth - XTextWidth(Scr.DefaultFont.font, str, strlen(str))) / 2; offset += GEOMETRY_WINDOW_BW; #ifdef I18N_MB XmbDrawString(dpy, Scr.SizeWindow, Scr.DefaultFont.fontset, Scr.StdGC, offset, Scr.DefaultFont.font->ascent + GEOMETRY_WINDOW_BW, str, strlen(str)); #else XDrawString(dpy, Scr.SizeWindow, Scr.StdGC, offset, Scr.DefaultFont.font->ascent + GEOMETRY_WINDOW_BW, str, strlen(str)); #endif } /*********************************************************************** * * Procedure: * DisplaySize - display the size in the dimensions window * * Inputs: * tmp_win - the current fvwm window * width - the width of the rubber band * height - the height of the rubber band * ***********************************************************************/ static void DisplaySize( FvwmWindow *tmp_win, XEvent *eventp, int width, int height, Bool Init, Bool resetLast) { char str[100]; int dwidth,dheight,offset; static int last_width = 0; static int last_height = 0; if (Scr.gs.do_hide_resize_window) return; position_geometry_window(eventp); if (resetLast) { last_width = 0; last_height = 0; } if (last_width == width && last_height == height) return; last_width = width; last_height = height; dheight = height - tmp_win->title_g.height - 2*tmp_win->boundary_width; dwidth = width - 2*tmp_win->boundary_width; dwidth -= tmp_win->hints.base_width; dheight -= tmp_win->hints.base_height; dwidth /= tmp_win->hints.width_inc; dheight /= tmp_win->hints.height_inc; (void) sprintf (str, GEOMETRY_WINDOW_SIZE_STRING, dwidth, dheight); if (Init) { XClearWindow(dpy,Scr.SizeWindow); } else { /* just clear indside the relief lines to reduce flicker */ XClearArea(dpy, Scr.SizeWindow, GEOMETRY_WINDOW_BW, GEOMETRY_WINDOW_BW, Scr.SizeStringWidth, Scr.DefaultFont.height, False); } if (Pdepth >= 2) { RelieveRectangle( dpy, Scr.SizeWindow, 0, 0, Scr.SizeStringWidth + GEOMETRY_WINDOW_BW * 2 - 1, Scr.DefaultFont.height + GEOMETRY_WINDOW_BW*2 - 1, Scr.StdReliefGC, Scr.StdShadowGC, GEOMETRY_WINDOW_BW); } offset = (Scr.SizeStringWidth - XTextWidth(Scr.DefaultFont.font, str, strlen(str))) / 2; offset += GEOMETRY_WINDOW_BW; #ifdef I18N_MB XmbDrawString(dpy, Scr.SizeWindow, Scr.DefaultFont.fontset, Scr.StdGC, offset, Scr.DefaultFont.font->ascent + GEOMETRY_WINDOW_BW, str, strlen(str)); #else XDrawString(dpy, Scr.SizeWindow, Scr.StdGC, offset, Scr.DefaultFont.font->ascent + GEOMETRY_WINDOW_BW, str, strlen(str)); #endif } void CMD_ResizeMove(F_CMD_ARGS) { int FinalX = 0; int FinalY = 0; int FinalW = 0; int FinalH = 0; int n; int x,y; Bool fWarp = False; Bool fPointer = False; Bool has_focus; int dx; int dy; if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_RESIZE, ButtonPress)) return; if (tmp_win == NULL) return; if (IS_FIXED(tmp_win)) return; if (check_if_function_allowed(F_RESIZE,tmp_win,True,NULL) == 0) { XBell(dpy, 0); return; } /* gotta have a window */ w = tmp_win->frame; if (!XGetGeometry(dpy, w, &JunkRoot, &x, &y, (unsigned int *)&FinalW, (unsigned int *)&FinalH, &JunkBW, &JunkDepth)) { XBell(dpy, 0); return; } FinalX = x; FinalY = y; n = GetResizeMoveArguments( &action, tmp_win->hints.base_width, tmp_win->hints.base_height, tmp_win->hints.width_inc, tmp_win->hints.height_inc, tmp_win->boundary_width, tmp_win->title_g.height, &FinalX, &FinalY, &FinalW, &FinalH, &fWarp, &fPointer); if (n < 4) return; if (IS_MAXIMIZED(tmp_win)) { /* must redraw the buttons now so that the 'maximize' button does not stay * depressed. */ SET_MAXIMIZED(tmp_win, 0); DrawDecorations( tmp_win, DRAW_BUTTONS, (tmp_win == Scr.Hilite), True, None); } dx = FinalX - tmp_win->frame_g.x; dy = FinalY - tmp_win->frame_g.y; /* size will be less or equal to requested */ constrain_size(tmp_win, (unsigned int *)&FinalW, (unsigned int *)&FinalH, 0, 0, 0); if (IS_SHADED(tmp_win)) { SetupFrame(tmp_win, FinalX, FinalY, FinalW, tmp_win->frame_g.height, False); } else { SetupFrame(tmp_win, FinalX, FinalY, FinalW, FinalH, True); } if (fWarp) XWarpPointer(dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y); if (IS_MAXIMIZED(tmp_win)) { tmp_win->max_g.x += dx; tmp_win->max_g.y += dy; } else { tmp_win->normal_g.x += dx; tmp_win->normal_g.y += dy; } has_focus = (tmp_win == get_focus_window())? True : False; DrawDecorations(tmp_win, DRAW_ALL, has_focus, True, None); update_absolute_geometry(tmp_win); maximize_adjust_offset(tmp_win); XSync(dpy, 0); GNOME_SetWinArea(tmp_win); return; } static void InteractiveMove( Window *win, FvwmWindow *tmp_win, int *FinalX, int *FinalY, XEvent *eventp, Bool do_start_at_pointer) { int origDragX,origDragY,DragX, DragY, DragWidth, DragHeight; int XOffset, YOffset; Window w; Bool do_move_opaque = False; w = *win; if (Scr.bo.InstallRootCmap) InstallRootColormap(); else InstallFvwmColormap(); /* warp the pointer to the cursor position from before menu appeared */ /* domivogt (17-May-1999): an XFlush should not hurt anyway, so do it * unconditionally to remove the external */ XFlush(dpy); if (do_start_at_pointer) { if (XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild, &DragX, &DragY, &JunkX, &JunkY, &JunkMask) == False) { /* pointer is on a different screen */ DragX = 0; DragY = 0; } } else { /* Although a move is usually done with a button depressed we have to check * for ButtonRelease too since the event may be faked. */ GetLocationFromEventOrQuery(dpy, Scr.Root, &Event, &DragX, &DragY); } MyXGrabServer(dpy); if (!XGetGeometry(dpy, w, &JunkRoot, &origDragX, &origDragY, (unsigned int *)&DragWidth, (unsigned int *)&DragHeight, &JunkBW, &JunkDepth)) { MyXUngrabServer(dpy); return; } MyXGrabKeyboard(dpy); if (do_start_at_pointer) { origDragX = DragX; origDragY = DragY; } if (IS_ICONIFIED(tmp_win)) do_move_opaque = True; else if (IS_MAPPED(tmp_win)) { float areapct; areapct = 100.0; areapct *= ((float)DragWidth / (float)Scr.MyDisplayWidth); areapct *= ((float)DragHeight / (float)Scr.MyDisplayHeight); /* round up */ areapct += 0.1; if (Scr.OpaqueSize < 0 || (float)areapct <= (float)Scr.OpaqueSize) { do_move_opaque = True; } } if (do_move_opaque) { MyXUngrabServer(dpy); } else { Scr.flags.is_wire_frame_displayed = True; } if (!do_move_opaque && IS_ICONIFIED(tmp_win)) XUnmapWindow(dpy,w); XOffset = origDragX - DragX; YOffset = origDragY - DragY; if (!Scr.gs.do_hide_position_window) { position_geometry_window(NULL); XMapRaised(dpy,Scr.SizeWindow); } moveLoop(tmp_win, XOffset,YOffset,DragWidth,DragHeight, FinalX,FinalY, do_move_opaque); if (!Scr.gs.do_hide_position_window) { XUnmapWindow(dpy,Scr.SizeWindow); } if (Scr.bo.InstallRootCmap) UninstallRootColormap(); else UninstallFvwmColormap(); if (!do_move_opaque) { /* Throw away some events that dont interest us right now. */ discard_events(EnterWindowMask|LeaveWindowMask); Scr.flags.is_wire_frame_displayed = False; MyXUngrabServer(dpy); } MyXUngrabKeyboard(dpy); return; } /* Perform the movement of the window. ppctMovement *must* have a 1.0 entry * somewhere in ins list of floats, and movement will stop when it hits a 1.0 * entry */ static void AnimatedMoveAnyWindow( FvwmWindow *tmp_win, Window w, int startX, int startY, int endX, int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement) { int pointerX, pointerY; int currentX, currentY; int lastX, lastY; int deltaX, deltaY; if (tmp_win && IS_FIXED(tmp_win)) return; /* set our defaults */ if (ppctMovement == NULL) ppctMovement = rgpctMovementDefault; if (cmsDelay < 0) cmsDelay = cmsDelayDefault; if (startX < 0 || startY < 0) { if (!XGetGeometry(dpy, w, &JunkRoot, ¤tX, ¤tY, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth)) { XBell(dpy, 0); return; } if (startX < 0) startX = currentX; if (startY < 0) startY = currentY; } deltaX = endX - startX; deltaY = endY - startY; lastX = startX; lastY = startY; if (deltaX == 0 && deltaY == 0) /* go nowhere fast */ return; /* Needed for aborting */ MyXGrabKeyboard(dpy); do { currentX = startX + deltaX * (*ppctMovement); currentY = startY + deltaY * (*ppctMovement); if (lastX == currentX && lastY == currentY) /* don't waste time in the same spot */ continue; XMoveWindow(dpy,w,currentX,currentY); if (fWarpPointerToo == True) { if (XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX,&JunkY,&pointerX,&pointerY,&JunkMask) == False) { /* pointer is on a different screen */ pointerX = currentX; pointerY = currentY; } else { pointerX += currentX - lastX; pointerY += currentY - lastY; } XWarpPointer(dpy,None,Scr.Root,0,0,0,0, pointerX,pointerY); } if (tmp_win && !IS_SHADED(tmp_win) && !Scr.bo.DisableConfigureNotify) { /* send configure notify event for windows that care about their * location */ SendConfigureNotify( tmp_win, currentX, currentY, tmp_win->frame_g.width, tmp_win->frame_g.height, 0, False); #ifdef FVWM_DEBUG_MSGS fvwm_msg(DBG,"AnimatedMoveAnyWindow", "Sent ConfigureNotify (w == %d, h == %d)", tmp_win->frame_g.width, tmp_win->frame_g.height); #endif } XFlush(dpy); if (tmp_win) { tmp_win->frame_g.x = currentX; tmp_win->frame_g.y = currentY; update_absolute_geometry(tmp_win); maximize_adjust_offset(tmp_win); BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win); FlushAllMessageQueues(); } usleep(cmsDelay * 1000); /* usleep takes microseconds */ /* this didn't work for me -- maybe no longer necessary since * we warn the user when they use > .5 seconds as a between-frame delay * time. * * domivogt (28-apr-1999): That is because the keyboard was not grabbed. * works nicely now. */ if (XCheckMaskEvent(dpy, ButtonPressMask|ButtonReleaseMask|KeyPressMask, &Event)) { StashEventTime(&Event); /* finish the move immediately */ XMoveWindow(dpy,w,endX,endY); break; } lastX = currentX; lastY = currentY; } while (*ppctMovement != 1.0 && ppctMovement++); MyXUngrabKeyboard(dpy); XFlush(dpy); if (tmp_win) GNOME_SetWinArea(tmp_win); return; } /* used for moving menus, not a client window */ void AnimatedMoveOfWindow(Window w, int startX, int startY, int endX, int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement) { AnimatedMoveAnyWindow(NULL, w, startX, startY, endX, endY, fWarpPointerToo, cmsDelay, ppctMovement); } /* used for moving client windows */ void AnimatedMoveFvwmWindow(FvwmWindow *tmp_win, Window w, int startX, int startY, int endX, int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement) { AnimatedMoveAnyWindow(tmp_win, w, startX, startY, endX, endY, fWarpPointerToo, cmsDelay, ppctMovement); } /**************************************************************************** * * Start a window move operation * ****************************************************************************/ static void move_window_doit(F_CMD_ARGS, Bool do_animate, int mode) { int FinalX = 0; int FinalY = 0; int n; int x,y; unsigned int width, height; int page_x, page_y; Bool fWarp = False; Bool fPointer = False; int dx; int dy; if (DeferExecution( eventp,&w,&tmp_win,&context, (mode == MOVE_NORMAL) ? CRS_MOVE : CRS_SELECT, ButtonPress)) { return; } if (tmp_win == NULL) return; if (IS_FIXED(tmp_win)) return; if (check_if_function_allowed(F_MOVE, tmp_win, True, NULL) == 0) { return; } /* gotta have a window */ w = tmp_win->frame; if(IS_ICONIFIED(tmp_win)) { if(tmp_win->icon_pixmap_w != None) { w = tmp_win->icon_pixmap_w; XUnmapWindow(dpy,tmp_win->icon_w); } else { w = tmp_win->icon_w; } } if (!XGetGeometry(dpy, w, &JunkRoot, &x, &y, &width, &height, &JunkBW, &JunkDepth)) { return; } if (mode == MOVE_PAGE) { rectangle r; rectangle s; do_animate = False; SET_STICKY(tmp_win, 0); if (!get_page_arguments(action, &page_x, &page_y)) { page_x = Scr.Vx; page_y = Scr.Vy; } r.x = x; r.y = y; r.width = width; r.height = height; s.x = page_x - Scr.Vx; s.y = page_y - Scr.Vy; s.width = Scr.MyDisplayWidth; s.height = Scr.MyDisplayHeight; move_into_rectangle(&r, &s); FinalX = r.x; FinalY = r.y; } else if (mode == MOVE_SCREEN) { rectangle r; rectangle s; rectangle p; int fscreen; do_animate = False; SET_STICKY(tmp_win, 0); fscreen = FScreenGetScreenArgument(action, FSCREEN_SPEC_CURRENT); FScreenGetScrRect(NULL, fscreen, &s.x, &s.y, &s.width, &s.height); page_x = Scr.Vx; page_y = Scr.Vy; r.x = x; r.y = y; r.width = width; r.height = height; p.x = page_x - Scr.Vx; p.y = page_y - Scr.Vy; p.width = Scr.MyDisplayWidth; p.height = Scr.MyDisplayHeight; /* move to page first */ move_into_rectangle(&r, &p); /* then move to screen */ move_into_rectangle(&r, &s); FinalX = r.x; FinalY = r.y; } else { FinalX = x; FinalY = y; n = GetMoveArguments( &action, width, height, &FinalX, &FinalY, &fWarp, &fPointer); if (n != 2 || fPointer) InteractiveMove(&w, tmp_win, &FinalX, &FinalY, eventp, fPointer); else if (IS_ICONIFIED(tmp_win)) { SET_ICON_MOVED(tmp_win, 1); } } if (w == tmp_win->frame) { dx = FinalX - tmp_win->frame_g.x; dy = FinalY - tmp_win->frame_g.y; if (do_animate) { AnimatedMoveFvwmWindow(tmp_win,w,-1,-1,FinalX,FinalY,fWarp,-1,NULL); } SetupFrame(tmp_win, FinalX, FinalY, tmp_win->frame_g.width, tmp_win->frame_g.height, True); if (fWarp & !do_animate) XWarpPointer(dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y); if (IS_MAXIMIZED(tmp_win)) { tmp_win->max_g.x += dx; tmp_win->max_g.y += dy; } else { tmp_win->normal_g.x += dx; tmp_win->normal_g.y += dy; } update_absolute_geometry(tmp_win); maximize_adjust_offset(tmp_win); XSync(dpy, 0); GNOME_SetWinArea(tmp_win); } else /* icon window */ { tmp_win->icon_g.x = FinalX; tmp_win->icon_xl_loc = FinalX - (tmp_win->icon_g.width - tmp_win->icon_p_width)/2; tmp_win->icon_g.y = FinalY; BroadcastPacket(M_ICON_LOCATION, 7, tmp_win->w, tmp_win->frame, (unsigned long)tmp_win, tmp_win->icon_g.x, tmp_win->icon_g.y, tmp_win->icon_p_width, tmp_win->icon_g.height + tmp_win->icon_p_height); if (do_animate) { AnimatedMoveOfWindow(tmp_win->icon_w,-1,-1,tmp_win->icon_xl_loc, FinalY+tmp_win->icon_p_height, fWarp,-1,NULL); } else { XMoveWindow(dpy,tmp_win->icon_w, tmp_win->icon_xl_loc, FinalY+tmp_win->icon_p_height); if (fWarp) XWarpPointer(dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y); } if(tmp_win->icon_pixmap_w != None) { XMapWindow(dpy,tmp_win->icon_w); if (do_animate) { AnimatedMoveOfWindow(tmp_win->icon_pixmap_w, -1,-1, tmp_win->icon_g.x,FinalY,fWarp,-1,NULL); } else { XMoveWindow(dpy, tmp_win->icon_pixmap_w, tmp_win->icon_g.x, FinalY); if (fWarp) XWarpPointer(dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y); } XMapWindow(dpy,w); } XSync(dpy, 0); } return; } void CMD_Move(F_CMD_ARGS) { move_window_doit(F_PASS_ARGS, False, MOVE_NORMAL); } void CMD_AnimatedMove(F_CMD_ARGS) { move_window_doit(F_PASS_ARGS, True, MOVE_NORMAL); } void CMD_MoveToPage(F_CMD_ARGS) { move_window_doit(F_PASS_ARGS, False, MOVE_PAGE); } void CMD_MoveToScreen(F_CMD_ARGS) { move_window_doit(F_PASS_ARGS, False, MOVE_SCREEN); } /* This function does the SnapAttraction stuff. If takes x and y coordinates * (*px and *py) and returns the snapped values. */ static void DoSnapAttract( FvwmWindow *tmp_win, unsigned int Width, unsigned int Height, int *px, int *py) { int nyt,nxl,dist,closestLeft,closestRight,closestBottom,closestTop; rectangle self, other; FvwmWindow *tmp; /* resist based on window edges */ closestTop = Scr.SnapAttraction; closestBottom = Scr.SnapAttraction; closestRight = Scr.SnapAttraction; closestLeft = Scr.SnapAttraction; nxl = -99999; nyt = -99999; self.x = *px; self.y = *py; self.width = Width; self.height = Height; if (IS_ICONIFIED(tmp_win) && !HAS_NO_ICON_TITLE(tmp_win)) { self.height += tmp_win->icon_g.height; } /* * snap attraction */ /* snap to other windows */ if ((Scr.SnapMode & (SNAP_ICONS | SNAP_WINDOWS | SNAP_SAME)) && Scr.SnapAttraction > 0) { for (tmp = Scr.FvwmRoot.next; tmp; tmp = tmp->next) { if (tmp_win->Desk != tmp->Desk || tmp_win == tmp) { continue; } /* check snapping type */ switch (Scr.SnapMode) { case 1: /* SameType */ if (IS_ICONIFIED(tmp) != IS_ICONIFIED(tmp_win)) { continue; } break; case 2: /* Icons */ if (!IS_ICONIFIED(tmp) || !IS_ICONIFIED(tmp_win)) { continue; } break; case 3: /* Windows */ if (IS_ICONIFIED(tmp) || IS_ICONIFIED(tmp_win)) { continue; } break; case 0: /* All */ default: /* NOOP */ break; } /* get other window dimensions */ if (IS_ICONIFIED(tmp)) { if (tmp->icon_p_height > 0) { other.width = tmp->icon_p_width; other.height = tmp->icon_p_height; } else { other.width = tmp->icon_g.width; other.height = tmp->icon_g.height; } other.x = tmp->icon_g.x; other.y = tmp->icon_g.y; if (!HAS_NO_ICON_TITLE(tmp)) { other.height += tmp->icon_g.height; } } else { other.width = tmp->frame_g.width; other.height = tmp->frame_g.height; other.x = tmp->frame_g.x; other.y = tmp->frame_g.y; } /* prevent that window snaps off screen */ if (other.x <= 0) { other.x -= Scr.SnapAttraction + 10000; other.width += Scr.SnapAttraction + 10000; } if (other.y <= 0) { other.y -= Scr.SnapAttraction + 10000; other.height += Scr.SnapAttraction + 10000; } if (other .x + other.width >= Scr.MyDisplayWidth) { other.width += Scr.SnapAttraction + 10000; } if (other .y + other.height >= Scr.MyDisplayHeight) { other.height += Scr.SnapAttraction + 10000; } /* snap horizontally */ if (!((other.y + (int)other.height) < (*py) || (other.y) > (*py + (int)self.height) )) { dist = abs(other.x - (*px + (int)self.width)); if (dist < closestRight) { closestRight = dist; if (((*px + (int)self.width) >= other.x) && ((*px + (int)self.width) < other.x + Scr.SnapAttraction)) { nxl = other.x - (int)self.width; } if (((*px + (int)self.width) >= other.x - Scr.SnapAttraction) && ((*px + (int)self.width) < other.x)) { nxl = other.x - (int)self.width; } } dist = abs(other.x + (int)other.width - *px); if (dist < closestLeft) { closestLeft = dist; if ((*px <= other.x + (int)other.width) && (*px > other.x + (int)other.width - Scr.SnapAttraction)) { nxl = other.x + (int)other.width; } if ((*px <= other.x + (int)other.width + Scr.SnapAttraction) && (*px > other.x + (int)other.width)) { nxl = other.x + (int)other.width; } } } /* horizontally */ /* snap vertically */ if (!((other.x + (int)other.width) < (*px) || (other.x) > (*px + (int)self.width))) { dist = abs(other.y - (*py + (int)self.height)); if (dist < closestBottom) { closestBottom = dist; if (((*py + (int)self.height) >= other.y) && ((*py + (int)self.height) < other.y + Scr.SnapAttraction)) { nyt = other.y - (int)self.height; } if (((*py + (int)self.height) >= other.y - Scr.SnapAttraction) && ((*py + (int)self.height) < other.y)) { nyt = other.y - (int)self.height; } } dist = abs(other.y + (int)other.height - *py); if (dist < closestTop) { closestTop = dist; if ((*py <= other.y + (int)other.height) && (*py > other.y + (int)other.height - Scr.SnapAttraction)) { nyt = other.y + (int)other.height; } if ((*py <= other.y + (int)other.height + Scr.SnapAttraction) && (*py > other.y + (int)other.height)) { nyt = other.y + (int)other.height; } } } /* vertically */ } /* for */ } /* snap to other windows */ /* snap to screen egdes */ if ((Scr.SnapMode & SNAP_SCREEN) && Scr.SnapAttraction > 0) { /* horizontally */ if (!(Scr.MyDisplayWidth < (*px) || (*px + (int)self.width) < 0)) { dist = abs(Scr.MyDisplayHeight - (*py + (int)self.height)); if (dist < closestBottom) { closestBottom = dist; if (((*py + (int)self.height) >= Scr.MyDisplayHeight) && ((*py + (int)self.height) < Scr.MyDisplayHeight + Scr.SnapAttraction)) { nyt = Scr.MyDisplayHeight - (int)self.height; } if (((*py + (int)self.height) >= Scr.MyDisplayHeight - Scr.SnapAttraction) && ((*py + (int)self.height) < Scr.MyDisplayHeight)) { nyt = Scr.MyDisplayHeight - (int)self.height; } } dist = abs(*py); if (dist < closestTop) { closestTop = dist; if ((*py <= 0)&&(*py > - Scr.SnapAttraction)) { nyt = 0; } if ((*py <= Scr.SnapAttraction)&&(*py > 0)) { nyt = 0; } } } /* horizontally */ /* vertically */ if (!(Scr.MyDisplayHeight < (*py) || (*py + (int)self.height) < 0)) { dist = abs(Scr.MyDisplayWidth - (*px + (int)self.width)); if (dist < closestRight) { closestRight = dist; if (((*px + (int)self.width) >= Scr.MyDisplayWidth) && ((*px + (int)self.width) < Scr.MyDisplayWidth + Scr.SnapAttraction)) { nxl = Scr.MyDisplayWidth - (int)self.width; } if (((*px + (int)self.width) >= Scr.MyDisplayWidth - Scr.SnapAttraction) && ((*px + (int)self.width) < Scr.MyDisplayWidth)) { nxl = Scr.MyDisplayWidth - (int)self.width; } } dist = abs(*px); if (dist < closestLeft) { closestLeft = dist; if ((*px <= 0) && (*px > - Scr.SnapAttraction)) { nxl = 0; } if ((*px <= Scr.SnapAttraction) && (*px > 0)) { nxl = 0; } } } /* vertically */ } /* snap to screen edges */ if (nxl != -99999) { *px = nxl; } if (nyt != -99999) { *py = nyt; } /* * Snap grid handling */ if (Scr.SnapGridX > 1 && nxl == -99999) { if (*px != *px / Scr.SnapGridX * Scr.SnapGridX) { *px = (*px + ((*px >= 0) ? Scr.SnapGridX : -Scr.SnapGridX) / 2) / Scr.SnapGridX * Scr.SnapGridX; } } if (Scr.SnapGridY > 1 && nyt == -99999) { if (*py != *py / Scr.SnapGridY * Scr.SnapGridY) { *py = (*py + ((*py >= 0) ? Scr.SnapGridY : -Scr.SnapGridY) / 2) / Scr.SnapGridY * Scr.SnapGridY; } } /* * Resist moving windows beyond the edge of the screen */ if (Scr.MoveResistance > 0) { /* snap to right edge */ if (((*px + Width) >= Scr.MyDisplayWidth) && ((*px + Width) < Scr.MyDisplayWidth + Scr.MoveResistance)) { *px = Scr.MyDisplayWidth - Width; } /* snap to left edge */ else if ((*px <= 0) && (*px > -Scr.MoveResistance)) { *px = 0; } /* snap to bottom edge */ if (((*py + Height) >= Scr.MyDisplayHeight) && ((*py + Height) < Scr.MyDisplayHeight + Scr.MoveResistance)) { *py = Scr.MyDisplayHeight - Height; } /* snap to top edge */ else if ((*py <= 0) && (*py > -Scr.MoveResistance)) { *py = 0; } } /* Resist moving windows between xineramascreens */ if (Scr.XiMoveResistance > 0 && FScreenIsEnabled()) { int scr_x0, scr_y0, scr_x1, scr_y1; Bool do_recalc_rectangle = False; FScreenGetResistanceRect( *px, *py, Width, Height, &scr_x0, &scr_y0, &scr_x1, &scr_y1); /* snap to right edge */ if (scr_x1 != Scr.MyDisplayWidth && *px + Width >= scr_x1 && *px + Width < scr_x1 + Scr.XiMoveResistance) { *px = scr_x1 - Width; do_recalc_rectangle = True; } /* snap to left edge */ else if (scr_x0 != 0 && *px <= scr_x0 && scr_x0 - *px < Scr.XiMoveResistance) { *px = scr_x0; do_recalc_rectangle = True; } if (do_recalc_rectangle) { /* Snapping in X direction can move the window off a screen. Thus, it * may no longer be necessary to snap in Y direction. */ FScreenGetResistanceRect( *px, *py, Width, Height, &scr_x0, &scr_y0, &scr_x1, &scr_y1); } /* snap to bottom edge */ if (scr_y1 != Scr.MyDisplayHeight && *py + Height >= scr_y1 && *py + Height < scr_y1 + Scr.XiMoveResistance) { *py = scr_y1 - Height; } /* snap to top edge */ else if (scr_y0 != 0 && *py <= scr_y0 && scr_y0 - *py < Scr.XiMoveResistance) { *py = scr_y0; } } return; } /**************************************************************************** * * Move the rubberband around, return with the new window location * * Returns True if the window has to be resized after the move. * ****************************************************************************/ Bool moveLoop(FvwmWindow *tmp_win, int XOffset, int YOffset, int Width, int Height, int *FinalX, int *FinalY,Bool do_move_opaque) { extern Window bad_window; Bool finished = False; Bool aborted = False; int xl,xl2,yt,yt2,delta_x,delta_y,paged; unsigned int button_mask = 0; FvwmWindow tmp_win_copy; int dx = Scr.EdgeScrollX ? Scr.EdgeScrollX : Scr.MyDisplayWidth; int dy = Scr.EdgeScrollY ? Scr.EdgeScrollY : Scr.MyDisplayHeight; int vx = Scr.Vx; int vy = Scr.Vy; int xl_orig = 0; int yt_orig = 0; int cnx = 0; int cny = 0; int x_virtual_offset = 0; int y_virtual_offset = 0; Bool sent_cn = False; Bool do_resize_too = False; Bool do_exec_placement_func = False; int x_bak; int y_bak; Window move_w = None; int orig_icon_x = 0; int orig_icon_y = 0; if (!GrabEm(CRS_MOVE, GRAB_NORMAL)) { XBell(dpy, 0); return False; } if (!IS_MAPPED(tmp_win) && !IS_ICONIFIED(tmp_win)) do_move_opaque = False; bad_window = None; if (IS_ICONIFIED(tmp_win)) { if (tmp_win->icon_pixmap_w != None) move_w = tmp_win->icon_pixmap_w; else if (tmp_win->icon_w != None) move_w = tmp_win->icon_w; } else { move_w = tmp_win->frame; } if (!XGetGeometry(dpy, move_w, &JunkRoot, &x_bak, &y_bak, &JunkWidth, &JunkHeight, &JunkBW,&JunkDepth)) { /* This is allright here since the window may not be mapped yet. */ } if (IS_ICONIFIED(tmp_win)) { orig_icon_x = tmp_win->icon_g.x; orig_icon_y = tmp_win->icon_g.y; } /* make a copy of the tmp_win structure for sending to the pager */ memcpy(&tmp_win_copy, tmp_win, sizeof(FvwmWindow)); /* prevent flicker when paging */ SET_WINDOW_BEING_MOVED_OPAQUE(tmp_win, do_move_opaque); if (XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild, &xl, &yt, &JunkX, &JunkY, &button_mask) == False) { /* pointer is on a different screen */ xl = 0; yt = 0; } else { xl += XOffset; yt += YOffset; } button_mask &= DEFAULT_ALL_BUTTONS_MASK; xl_orig = xl; yt_orig = yt; /* draw initial outline */ if (!IS_ICONIFIED(tmp_win) && ((!do_move_opaque && !Scr.gs.EmulateMWM) || !IS_MAPPED(tmp_win))) draw_move_resize_grid(xl, yt, Width - 1, Height - 1); DisplayPosition(tmp_win, &Event, xl, yt, True); while (!finished && bad_window != tmp_win->w) { int rc = 0; /* wait until there is an interesting event */ while (rc != -1 && (!XPending(dpy) || !XCheckMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask | ButtonMotionMask | ExposureMask, &Event))) { rc = HandlePaging( dx, dy, &xl, &yt, &delta_x, &delta_y, False, False, True); if (rc == 1) { /* Fake an event to force window reposition */ if (delta_x) { x_virtual_offset = 0; } if (XOffset) { xl += XOffset; } if (delta_y) { y_virtual_offset = 0; } if (YOffset) { yt += YOffset; } DoSnapAttract(tmp_win, Width, Height, &xl, &yt); Event.type = MotionNotify; Event.xmotion.time = lastTimestamp; Event.xmotion.x_root = xl - XOffset; Event.xmotion.y_root = yt - YOffset; break; } } if (rc == -1) { /* block until an event arrives */ XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask | ButtonMotionMask | ExposureMask, &Event); } StashEventTime(&Event); /* discard any extra motion events before a logical release */ if (Event.type == MotionNotify) { XEvent new_event; /*** logic borrowed from icewm ***/ while (XPending(dpy) > 0 && XCheckMaskEvent(dpy, ButtonMotionMask | PointerMotionMask | ButtonPressMask | ButtonRelease | KeyPressMask, &new_event)) { if (Event.type != new_event.type) { XPutBackEvent(dpy, &new_event); break; } else { Event = new_event; } } /*** end of code borrowed from icewm ***/ StashEventTime(&Event); } /* if (Event.type == MotionNotify) */ /* Handle a limited number of key press events to allow mouseless * operation */ if (Event.type == KeyPress) { Keyboard_shortcuts( &Event, tmp_win, &x_virtual_offset, &y_virtual_offset, ButtonRelease); } switch(Event.type) { case KeyPress: /* simple code to bag out of move - CKH */ if (XLookupKeysym(&(Event.xkey),0) == XK_Escape) { if (!do_move_opaque) switch_move_resize_grid(False); if (!IS_ICONIFIED(tmp_win)) { if (do_move_opaque) { *FinalX = tmp_win->frame_g.x; *FinalY = tmp_win->frame_g.y; } } else { *FinalX = orig_icon_x; *FinalY = orig_icon_y; } aborted = True; finished = True; } break; case ButtonPress: if (Event.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS && ((Button1Mask << (Event.xbutton.button - 1)) & button_mask)) { /* No new button was pressed, just a delayed event */ break; } if(((Event.xbutton.button == 2)&&(!Scr.gs.EmulateMWM))|| ((Event.xbutton.button == 1)&&(Scr.gs.EmulateMWM)&& (Event.xbutton.state & ShiftMask))) { do_resize_too = True; do_exec_placement_func = False; SET_PLACED_WB3(tmp_win,False); /* Fallthrough to button-release */ } else if(Event.xbutton.button == 3) { do_exec_placement_func = True; do_resize_too = False; SET_PLACED_WB3(tmp_win,True); /* Fallthrough to button-release */ } else { /* Abort the move if * - the move started with a pressed button and another button * was pressed during the operation * - no button was pressed at the beginning and any button * except button 1 was pressed. */ SET_PLACED_WB3(tmp_win,False); if (button_mask || (Event.xbutton.button != 1)) { if(!do_move_opaque) switch_move_resize_grid(False); if (!IS_ICONIFIED(tmp_win)) { *FinalX = tmp_win->frame_g.x; *FinalY = tmp_win->frame_g.y; } else { *FinalX = orig_icon_x; *FinalY = orig_icon_y; } aborted = True; finished = True; } break; } case ButtonRelease: if(!do_move_opaque) switch_move_resize_grid(False); xl2 = Event.xbutton.x_root + XOffset + x_virtual_offset; yt2 = Event.xbutton.y_root + YOffset + y_virtual_offset; /* ignore the position of the button release if it was on a * different page. */ if (!(((xl < 0 && xl2 >= 0) || (xl >= 0 && xl2 < 0) || (yt < 0 && yt2 >= 0) || (yt >= 0 && yt2 < 0)) && (abs(xl - xl2) > Scr.MyDisplayWidth / 2 || abs(yt - yt2) > Scr.MyDisplayHeight / 2))) { xl = xl2; yt = yt2; } if (xl != xl_orig || yt != yt_orig || vx != Scr.Vx || vy != Scr.Vy) { /* only snap if the window actually moved! */ DoSnapAttract(tmp_win, Width, Height, &xl, &yt); } *FinalX = xl; *FinalY = yt; finished = True; break; case MotionNotify: if (Event.xmotion.same_screen == False) { continue; } xl = Event.xmotion.x_root; yt = Event.xmotion.y_root; if (xl > 0 && xl < Scr.MyDisplayWidth - 1) { /* pointer was moved away from the left/right border with the mouse, * reset the virtual x offset */ x_virtual_offset = 0; } if (yt > 0 && yt < Scr.MyDisplayHeight - 1) { /* pointer was moved away from the top/bottom border with the mouse, * reset the virtual y offset */ y_virtual_offset = 0; } xl += XOffset + x_virtual_offset; yt += YOffset + y_virtual_offset; DoSnapAttract(tmp_win, Width, Height, &xl, &yt); /* check Paging request once and only once after outline redrawn */ /* redraw after paging if needed - mab */ paged = 0; while (paged <= 1) { if(!do_move_opaque) draw_move_resize_grid(xl, yt, Width - 1, Height - 1); else { if (IS_ICONIFIED(tmp_win)) { tmp_win->icon_g.x = xl ; tmp_win->icon_xl_loc = xl - (tmp_win->icon_g.width - tmp_win->icon_p_width)/2; tmp_win->icon_g.y = yt; if(tmp_win->icon_pixmap_w != None) XMoveWindow(dpy, tmp_win->icon_pixmap_w, tmp_win->icon_g.x,yt); else if (tmp_win->icon_w != None) XMoveWindow(dpy, tmp_win->icon_w,tmp_win->icon_xl_loc, yt+tmp_win->icon_p_height); } else { XMoveWindow(dpy,tmp_win->frame,xl,yt); } } DisplayPosition(tmp_win, &Event, xl, yt, False); /* prevent window from lagging behind mouse when paging - mab */ if (paged == 0) { xl = Event.xmotion.x_root; yt = Event.xmotion.y_root; HandlePaging( dx, dy, &xl, &yt, &delta_x, &delta_y, False, False, False); if (delta_x) { x_virtual_offset = 0; } if (XOffset) { xl += XOffset; } if (delta_y) { y_virtual_offset = 0; } if (YOffset) { yt += YOffset; } DoSnapAttract(tmp_win, Width, Height, &xl, &yt); if (!delta_x && !delta_y) /* break from while (paged <= 1) */ break; } paged++; } /* end while (paged) */ break; case Expose: if (!do_move_opaque) { /* must undraw the rubber band in case the event causes some drawing */ switch_move_resize_grid(False); } DispatchEvent(False); if (!do_move_opaque) draw_move_resize_grid(xl, yt, Width - 1, Height - 1); break; default: /* cannot happen */ break; } /* switch */ xl += x_virtual_offset; yt += y_virtual_offset; if (do_move_opaque && !IS_ICONIFIED(tmp_win) && !IS_SHADED(tmp_win) && !Scr.bo.DisableConfigureNotify) { /* send configure notify event for windows that care about their * location; don't send anything if position didn't change */ if (!sent_cn || cnx != xl || cny != yt) { cnx = xl; cny = yt; sent_cn = True; SendConfigureNotify(tmp_win, xl, yt, Width, Height, 0, False); #ifdef FVWM_DEBUG_MSGS fvwm_msg( DBG, "SetupFrame","Sent ConfigureNotify (w %d, h %d)", Width, Height); #endif } } if (do_move_opaque) { if (!IS_ICONIFIED(tmp_win)) { tmp_win_copy.frame_g.x = xl; tmp_win_copy.frame_g.y = yt; } /* only do this with opaque moves, (i.e. the server is not grabbed) */ BroadcastConfig(M_CONFIGURE_WINDOW, &tmp_win_copy); FlushAllMessageQueues(); } } /* while (!finished) */ if (!Scr.gs.do_hide_position_window) XUnmapWindow(dpy,Scr.SizeWindow); if (aborted || bad_window == tmp_win->w) { if (vx != Scr.Vx || vy != Scr.Vy) { MoveViewport(vx, vy, False); } if (aborted && do_move_opaque) { XMoveWindow(dpy, move_w, x_bak, y_bak); } if (bad_window == tmp_win->w) { XUnmapWindow(dpy, move_w); XBell(dpy, 0); } } if (!aborted && bad_window != tmp_win->w && IS_ICONIFIED(tmp_win)) { SET_ICON_MOVED(tmp_win, 1); } UngrabEm(GRAB_NORMAL); if (!do_resize_too) { /* Don't wait for buttons to come up when user is placing a new window * and wants to resize it. */ WaitForButtonsUp(True); } SET_WINDOW_BEING_MOVED_OPAQUE(tmp_win, 0); bad_window = None; return do_resize_too; } void CMD_MoveThreshold(F_CMD_ARGS) { int val = 0; if (GetIntegerArguments(action, NULL, &val, 1) < 1 || val < 0) Scr.MoveThreshold = DEFAULT_MOVE_THRESHOLD; else Scr.MoveThreshold = val; } void CMD_OpaqueMoveSize(F_CMD_ARGS) { int val; if (GetIntegerArguments(action, NULL, &val, 1) < 1) { if (strncasecmp(action, "unlimited", 9) == 0) Scr.OpaqueSize = -1; else Scr.OpaqueSize = DEFAULT_OPAQUE_MOVE_SIZE; } else Scr.OpaqueSize = val; } static char *hide_options[] = { "never", "move", "resize", NULL }; void CMD_HideGeometryWindow(F_CMD_ARGS) { char *token = PeekToken(action, NULL); Scr.gs.do_hide_position_window = 0; Scr.gs.do_hide_resize_window = 0; switch(GetTokenIndex(token, hide_options, 0, NULL)) { case 0: break; case 1: Scr.gs.do_hide_position_window = 1; break; case 2: Scr.gs.do_hide_resize_window = 1; break; default: Scr.gs.do_hide_position_window = 1; Scr.gs.do_hide_resize_window = 1; break; } return; } void CMD_SnapAttraction(F_CMD_ARGS) { int val; char *token; if (GetIntegerArguments(action, &action, &val, 1) != 1) { Scr.SnapAttraction = DEFAULT_SNAP_ATTRACTION; Scr.SnapMode = DEFAULT_SNAP_ATTRACTION_MODE; return; } Scr.SnapAttraction = val; if (val < 0) { Scr.SnapAttraction = DEFAULT_SNAP_ATTRACTION; } if (val == 0) { return; } token = PeekToken(action, &action); if (token == NULL) { return; } if (StrEquals(token,"All")) { Scr.SnapMode = SNAP_ICONS | SNAP_WINDOWS; } else if (StrEquals(token,"SameType")) { Scr.SnapMode = SNAP_SAME; } else if (StrEquals(token,"Icons")) { Scr.SnapMode = SNAP_ICONS; } else if (StrEquals(token,"Windows")) { Scr.SnapMode = SNAP_WINDOWS; } if (Scr.SnapMode == 0) { Scr.SnapMode = DEFAULT_SNAP_ATTRACTION_MODE; } else { token = PeekToken(action, &action); if (token == NULL) { return; } } if (StrEquals(token, "Screen")) { Scr.SnapMode |= SNAP_SCREEN; } else { fvwm_msg(ERR,"SetSnapAttraction", "Invalid argument: %s", token); } return; } void CMD_SnapGrid(F_CMD_ARGS) { int val[2]; if(GetIntegerArguments(action, NULL, &val[0], 2) != 2) { Scr.SnapGridX = DEFAULT_SNAP_GRID_X; Scr.SnapGridY = DEFAULT_SNAP_GRID_Y; return; } Scr.SnapGridX = val[0]; if(Scr.SnapGridX < 1) { Scr.SnapGridX = DEFAULT_SNAP_GRID_X; } Scr.SnapGridY = val[1]; if(Scr.SnapGridY < 1) { Scr.SnapGridY = DEFAULT_SNAP_GRID_Y; } } static Pixmap XorPixmap = None; void CMD_XorValue(F_CMD_ARGS) { int val; XGCValues gcv; unsigned long gcm; if(GetIntegerArguments(action, NULL, &val, 1) != 1) { val = 0; } gcm = GCFunction|GCLineWidth|GCForeground|GCFillStyle|GCSubwindowMode; gcv.subwindow_mode = IncludeInferiors; gcv.function = GXxor; gcv.line_width = 0; /* use passed in value, or try to calculate appropriate value if 0 */ /* ctwm method: */ /* gcv.foreground = (val1)?(val1):((((unsigned long) 1) << Scr.d_depth) - 1); */ /* Xlib programming manual suggestion: */ gcv.foreground = (val)? (val):(BlackPixel(dpy,Scr.screen) ^ WhitePixel(dpy,Scr.screen)); gcv.fill_style = FillSolid; gcv.subwindow_mode = IncludeInferiors; /* modify XorGC, only create once */ if (Scr.XorGC) XChangeGC(dpy, Scr.XorGC, gcm, &gcv); else Scr.XorGC = fvwmlib_XCreateGC(dpy, Scr.Root, gcm, &gcv); /* free up XorPixmap if neccesary */ if (XorPixmap != None) { XFreePixmap(dpy, XorPixmap); XorPixmap = None; } } void CMD_XorPixmap(F_CMD_ARGS) { char *PixmapName; Picture *xp; XGCValues gcv; unsigned long gcm; action = GetNextToken(action, &PixmapName); if(PixmapName == NULL) { /* return to default value. */ action = "0"; CMD_XorValue(F_PASS_ARGS); return; } /* get the picture in the root visual, colorlimit is ignored because the * pixels will be freed */ UseDefaultVisual(); xp = GetPicture(dpy, Scr.Root, NULL, PixmapName, 0); if (xp == NULL) { fvwm_msg(ERR,"SetXORPixmap","Can't find pixmap %s", PixmapName); free(PixmapName); UseFvwmVisual(); return; } free(PixmapName); /* free up old pixmap */ if (XorPixmap != None) XFreePixmap(dpy, XorPixmap); /* make a copy of the picture pixmap */ XorPixmap = XCreatePixmap(dpy, Scr.Root, xp->width, xp->height, Pdepth); XCopyArea(dpy, xp->picture, XorPixmap, DefaultGC(dpy, Scr.screen), 0, 0, xp->width, xp->height, 0, 0); /* destroy picture and free colors */ DestroyPicture(dpy, xp); UseFvwmVisual(); /* create Graphics context */ gcm = GCFunction|GCLineWidth|GCTile|GCFillStyle|GCSubwindowMode; gcv.subwindow_mode = IncludeInferiors; gcv.function = GXxor; /* line width of 1 is necessary for Exceed servers */ gcv.line_width = 1; gcv.tile = XorPixmap; gcv.fill_style = FillTiled; gcv.subwindow_mode = IncludeInferiors; /* modify XorGC, only create once */ if (Scr.XorGC) XChangeGC(dpy, Scr.XorGC, gcm, &gcv); else Scr.XorGC = fvwmlib_XCreateGC(dpy, Scr.Root, gcm, &gcv); } /* ----------------------------- resizing code ----------------------------- */ /*********************************************************************** * * window resizing borrowed from the "wm" window manager * ***********************************************************************/ /**************************************************************************** * * Starts a window resize operation * ****************************************************************************/ void CMD_Resize(F_CMD_ARGS) { extern Window bad_window; Bool finished = False, done = False, abort = False; Bool do_resize_opaque; int x,y,delta_x,delta_y,stashed_x,stashed_y; Window ResizeWindow; Bool fButtonAbort = False; Bool fForceRedraw = False; int n; unsigned int button_mask = 0; rectangle sdrag; rectangle sorig; rectangle *drag = &sdrag; rectangle *orig = &sorig; rectangle start_g; int ymotion = 0; int xmotion = 0; int was_maximized; unsigned edge_wrap_x; unsigned edge_wrap_y; int px; int py; int i; Bool called_from_title = False; bad_window = False; if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_RESIZE, ButtonPress)) return; if (tmp_win == NULL) return; ResizeWindow = tmp_win->frame; XQueryPointer( dpy, ResizeWindow, &JunkRoot, &JunkChild, &JunkX, &JunkY, &px, &py, &button_mask); button_mask &= DEFAULT_ALL_BUTTONS_MASK; if (!check_if_function_allowed(F_RESIZE, tmp_win, True, NULL)) { XBell(dpy, 0); return; } was_maximized = IS_MAXIMIZED(tmp_win); SET_MAXIMIZED(tmp_win, 0); if (was_maximized) { /* must redraw the buttons now so that the 'maximize' button does not stay * depressed. */ DrawDecorations( tmp_win, DRAW_BUTTONS, (tmp_win == Scr.Hilite), True, None); } if (IS_SHADED(tmp_win) || !IS_MAPPED(tmp_win)) do_resize_opaque = False; else do_resize_opaque = DO_RESIZE_OPAQUE(tmp_win); /* no suffix = % of screen, 'p' = pixels, 'c' = increment units */ drag->width = tmp_win->frame_g.width; drag->height = tmp_win->frame_g.height; n = GetResizeArguments( &action, tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->hints.base_width, tmp_win->hints.base_height, tmp_win->hints.width_inc, tmp_win->hints.height_inc, tmp_win->boundary_width, tmp_win->title_g.height, &(drag->width), &(drag->height)); if (n == 2) { rectangle new_g; /* size will be less or equal to requested */ new_g = tmp_win->frame_g; constrain_size( tmp_win, (unsigned int *)&drag->width, (unsigned int *)&drag->height, xmotion, ymotion, 0); gravity_resize( tmp_win->hints.win_gravity, &new_g, drag->width - new_g.width, drag->height - new_g.height); tmp_win->frame_g = new_g; if (IS_SHADED(tmp_win)) { ForceSetupFrame(tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y, drag->width, tmp_win->frame_g.height, False); } else { ForceSetupFrame(tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y, drag->width, drag->height, False); } DrawDecorations(tmp_win, DRAW_ALL, True, True, None); update_absolute_geometry(tmp_win); maximize_adjust_offset(tmp_win); GNOME_SetWinArea(tmp_win); ResizeWindow = None; return; } if (Scr.bo.InstallRootCmap) InstallRootColormap(); else InstallFvwmColormap(); if(!GrabEm(CRS_RESIZE, GRAB_NORMAL)) { XBell(dpy, 0); return; } /* handle problems with edge-wrapping while resizing */ edge_wrap_x = Scr.flags.edge_wrap_x; edge_wrap_y = Scr.flags.edge_wrap_y; Scr.flags.edge_wrap_x = 0; Scr.flags.edge_wrap_y = 0; if (!do_resize_opaque) { MyXGrabServer(dpy); } if (!XGetGeometry( dpy, (Drawable) ResizeWindow, &JunkRoot, &drag->x, &drag->y, (unsigned int *)&drag->width, (unsigned int *)&drag->height, &JunkBW, &JunkDepth)) { UngrabEm(GRAB_NORMAL); if (!do_resize_opaque) { MyXUngrabServer(dpy); } return; } if (IS_SHADED(tmp_win)) { SET_MAXIMIZED(tmp_win, was_maximized); get_unshaded_geometry(tmp_win, drag); SET_MAXIMIZED(tmp_win, 0); } if (!do_resize_opaque) { Scr.flags.is_wire_frame_displayed = True; } MyXGrabKeyboard(dpy); *orig = *drag; start_g = *drag; ymotion = 0; xmotion = 0; /* pop up a resize dimensions window */ if (!Scr.gs.do_hide_resize_window) { position_geometry_window(NULL); XMapRaised(dpy, Scr.SizeWindow); } DisplaySize(tmp_win, &Event, orig->width, orig->height,True,True); if((PressedW != Scr.Root)&&(PressedW != None)) { /* Get the current position to determine which border to resize */ if(PressedW == tmp_win->sides[0]) /* top */ ymotion = 1; else if(PressedW == tmp_win->sides[1]) /* right */ xmotion = -1; else if(PressedW == tmp_win->sides[2]) /* bottom */ ymotion = -1; else if(PressedW == tmp_win->sides[3]) /* left */ xmotion = 1; else if(PressedW == tmp_win->corners[0]) /* upper-left */ { ymotion = 1; xmotion = 1; } else if(PressedW == tmp_win->corners[1]) /* upper-right */ { xmotion = -1; ymotion = 1; } else if(PressedW == tmp_win->corners[2]) /* lower left */ { ymotion = -1; xmotion = 1; } else if(PressedW == tmp_win->corners[3]) /* lower right */ { ymotion = -1; xmotion = -1; } } /* begin of code responsible for warping the pointer to the border when * starting a resize. */ if (tmp_win->title_w != None && PressedW == tmp_win->title_w) { /* title was pressed to thart the resize */ called_from_title = True; } else { for (i = NUMBER_OF_BUTTONS; i--; ) { /* see if the title button was pressed to that the resize */ if (tmp_win->button_w[i] != None && PressedW == tmp_win->button_w[i]) { /* yes */ called_from_title = True; } } } /* don't warp if the resize was triggered by a press somwhere on the title * bar */ if(PressedW != Scr.Root && xmotion == 0 && ymotion == 0 && !called_from_title) { int dx = orig->width - px; int dy = orig->height - py; int wx = -1; int wy = -1; int tx; int ty; /* Now find the place to warp to. We simply use the sectors drawn when we * start resizing the window. */ #if 0 tx = orig->width / 10 - 1; ty = orig->height / 10 - 1; #else tx = 0; ty = 0; #endif tx = max(tmp_win->boundary_width, tx); ty = max(tmp_win->boundary_width, ty); if (px >= 0 && dx >= 0 && py >= 0 && dy >= 0) { if (px < tx) { if (py < ty) { xmotion = 1; ymotion = 1; wx = 0; wy = 0; } else if (dy < ty) { xmotion = 1; ymotion = -1; wx = 0; wy = orig->height -1; } else { xmotion = 1; wx = 0; wy = orig->height/2; wy = py; } } else if (dx < tx) { if (py < ty) { xmotion = -1; ymotion = 1; wx = orig->width - 1; wy = 0; } else if (dy < ty) { xmotion = -1; ymotion = -1; wx = orig->width - 1; wy = orig->height -1; } else { xmotion = -1; wx = orig->width - 1; wy = orig->height/2; wy = py; } } else { if (py < ty) { ymotion = 1; wx = orig->width/2; wy = 0; wx = px; } else if (dy < ty) { ymotion = -1; wx = orig->width/2; wy = orig->height -1; wx = px; } } } if (wx != -1) { /* now warp the pointer to the border */ XWarpPointer(dpy, None, ResizeWindow, 0, 0, 1, 1, wx, wy); XFlush(dpy); } } /* end of code responsible for warping the pointer to the border when * starting a resize. */ /* draw the rubber-band window */ if (!do_resize_opaque) draw_move_resize_grid(drag->x, drag->y, drag->width - 1, drag->height - 1); /* kick off resizing without requiring any motion if invoked with a key * press */ if (eventp->type == KeyPress) { XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &stashed_x,&stashed_y,&JunkX, &JunkY, &JunkMask); DoResize(stashed_x, stashed_y, tmp_win, drag, orig, &xmotion, &ymotion, do_resize_opaque); } else stashed_x = stashed_y = -1; /* loop to resize */ while(!finished && bad_window != tmp_win->w) { int rc = 0; /* block until there is an interesting event */ while (rc != -1 && (!XCheckMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask | ButtonMotionMask | ExposureMask, &Event))) { rc = HandlePaging( Scr.EdgeScrollX, Scr.EdgeScrollY, &x, &y, &delta_x, &delta_y, False, False, True); if (rc == 1) { /* Fake an event to force window reposition */ Event.type = MotionNotify; Event.xmotion.time = lastTimestamp; fForceRedraw = True; break; } } if (rc == -1) { XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask | ButtonMotionMask | ExposureMask, &Event); } StashEventTime(&Event); if (Event.type == MotionNotify) { /* discard any extra motion events before a release */ while(XCheckMaskEvent(dpy, ButtonMotionMask | PointerMotionMask | ButtonReleaseMask | ButtonPressMask, &Event)) { StashEventTime(&Event); if (Event.type == ButtonRelease || Event.type == ButtonPress) break; } } done = False; /* Handle a limited number of key press events to allow mouseless * operation */ if(Event.type == KeyPress) Keyboard_shortcuts(&Event, tmp_win, NULL, NULL, ButtonRelease); switch(Event.type) { case ButtonPress: done = True; if (Event.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS && ((Button1Mask << (Event.xbutton.button - 1)) & button_mask)) { /* No new button was pressed, just a delayed event */ break; } /* Abort the resize if * - the move started with a pressed button and another button * was pressed during the operation * - no button was started at the beginning and any button * except button 1 was pressed. */ if (button_mask || (Event.xbutton.button != 1)) fButtonAbort = True; case KeyPress: /* simple code to bag out of move - CKH */ if (XLookupKeysym(&(Event.xkey),0) == XK_Escape || fButtonAbort) { abort = True; finished = True; /* return pointer if aborted resize was invoked with key */ if (stashed_x >= 0) { XWarpPointer(dpy, None, Scr.Root, 0, 0, 0, 0, stashed_x, stashed_y); } if (do_resize_opaque) { DoResize( start_g.x, start_g.y, tmp_win, &start_g, orig, &xmotion, &ymotion, do_resize_opaque); DrawDecorations(tmp_win, DRAW_ALL, True, True, None); } } done = True; break; case ButtonRelease: finished = True; done = True; break; case MotionNotify: if (Event.xmotion.same_screen == False) { continue; } if (!fForceRedraw) { x = Event.xmotion.x_root; y = Event.xmotion.y_root; /* resize before paging request to prevent resize from lagging * mouse - mab */ DoResize( x, y, tmp_win, drag, orig, &xmotion, &ymotion, do_resize_opaque); /* need to move the viewport */ HandlePaging(Scr.EdgeScrollX, Scr.EdgeScrollY, &x, &y, &delta_x, &delta_y, False, False, False); } /* redraw outline if we paged - mab */ if (delta_x != 0 || delta_y != 0) { orig->x -= delta_x; orig->y -= delta_y; drag->x -= delta_x; drag->y -= delta_y; DoResize( x, y, tmp_win, drag, orig, &xmotion, &ymotion, do_resize_opaque); } fForceRedraw = False; done = True; break; default: break; } if(!done) { if (!do_resize_opaque) /* must undraw the rubber band in case the event causes some drawing */ switch_move_resize_grid(False); DispatchEvent(False); if (!do_resize_opaque) draw_move_resize_grid( drag->x, drag->y, drag->width - 1, drag->height - 1); } else { if (do_resize_opaque) { /* only do this with opaque resizes, (i.e. the server is not grabbed) */ BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win); FlushAllMessageQueues(); } } } /* erase the rubber-band */ if (!do_resize_opaque) switch_move_resize_grid(False); /* pop down the size window */ if (!Scr.gs.do_hide_resize_window) XUnmapWindow(dpy, Scr.SizeWindow); if(!abort && bad_window != tmp_win->w) { /* size will be >= to requested */ constrain_size( tmp_win, (unsigned int *)&drag->width, (unsigned int *)&drag->height, xmotion, ymotion, CS_ROUND_UP); if (IS_SHADED(tmp_win)) { if (HAS_BOTTOM_TITLE(tmp_win)) { SetupFrame(tmp_win, drag->x, tmp_win->frame_g.y, drag->width, tmp_win->frame_g.height, False); } else { SetupFrame(tmp_win, drag->x, drag->y, drag->width, tmp_win->frame_g.height, False); } tmp_win->normal_g.height = drag->height; } else { SetupFrame( tmp_win, drag->x, drag->y, drag->width, drag->height, False); } } if (abort && was_maximized) { /* since we aborted the resize, the window is still maximized */ SET_MAXIMIZED(tmp_win, 1); /* force redraw */ DrawDecorations( tmp_win, DRAW_BUTTONS, (tmp_win == Scr.Hilite), True, None); } if (bad_window == tmp_win->w) { XUnmapWindow(dpy, tmp_win->frame); XBell(dpy, 0); } if (Scr.bo.InstallRootCmap) UninstallRootColormap(); else UninstallFvwmColormap(); ResizeWindow = None; if (!do_resize_opaque) { /* Throw away some events that dont interest us right now. */ discard_events(EnterWindowMask|LeaveWindowMask); Scr.flags.is_wire_frame_displayed = False; MyXUngrabServer(dpy); } MyXUngrabKeyboard(dpy); xmotion = 0; ymotion = 0; WaitForButtonsUp(True); UngrabEm(GRAB_NORMAL); Scr.flags.edge_wrap_x = edge_wrap_x; Scr.flags.edge_wrap_y = edge_wrap_y; update_absolute_geometry(tmp_win); maximize_adjust_offset(tmp_win); GNOME_SetWinArea(tmp_win); return; } /*********************************************************************** * * Procedure: * DoResize - move the rubberband around. This is called for * each motion event when we are resizing * * Inputs: * x_root - the X corrdinate in the root window * y_root - the Y corrdinate in the root window * tmp_win - the current fvwm window * drag - resize internal structure * orig - resize internal structure * xmotionp - pointer to xmotion in resize_window * ymotionp - pointer to ymotion in resize_window * ************************************************************************/ static void DoResize( int x_root, int y_root, FvwmWindow *tmp_win, rectangle *drag, rectangle *orig, int *xmotionp, int *ymotionp, Bool do_resize_opaque) { int action = 0; XEvent e; if ((y_root <= orig->y) || ((*ymotionp == 1)&&(y_root < orig->y+orig->height-1))) { drag->y = y_root; drag->height = orig->y + orig->height - y_root; action = 1; *ymotionp = 1; } else if ((y_root >= orig->y + orig->height - 1)|| ((*ymotionp == -1)&&(y_root > orig->y))) { drag->y = orig->y; drag->height = 1 + y_root - drag->y; action = 1; *ymotionp = -1; } if ((x_root <= orig->x)|| ((*xmotionp == 1)&&(x_root < orig->x + orig->width - 1))) { drag->x = x_root; drag->width = orig->x + orig->width - x_root; action = 1; *xmotionp = 1; } if ((x_root >= orig->x + orig->width - 1)|| ((*xmotionp == -1)&&(x_root > orig->x))) { drag->x = orig->x; drag->width = 1 + x_root - orig->x; action = 1; *xmotionp = -1; } if (action) { /* round up to nearest OK size to keep pointer inside rubberband */ constrain_size( tmp_win, (unsigned int *)&drag->width, (unsigned int *)&drag->height, *xmotionp, *ymotionp, CS_ROUND_UP); if (*xmotionp == 1) drag->x = orig->x + orig->width - drag->width; if (*ymotionp == 1) drag->y = orig->y + orig->height - drag->height; if(!do_resize_opaque) { draw_move_resize_grid( drag->x, drag->y, drag->width - 1, drag->height - 1); } else { SetupFrame( tmp_win, drag->x, drag->y, drag->width, drag->height, False); } } e.type = MotionNotify; e.xbutton.x_root = x_root; e.xbutton.y_root = y_root; DisplaySize(tmp_win, &e, drag->width, drag->height,False,False); } /*********************************************************************** * * Procedure: * draw_move_resize_grid - move a window outline * * Inputs: * root - the window we are outlining * x - upper left x coordinate * y - upper left y coordinate * width - the width of the rectangle * height - the height of the rectangle * ***********************************************************************/ static int get_outline_rects( XRectangle *rects, int x, int y, int width, int height) { int i; int n; int m; n = 3; m = (width - 5) / 2; if (m < n) { n = m; } m = (height - 5) / 2; if (m < n) { n = m; } if (n < 1) { n = 1; } for (i = 0; i < n; i++) { rects[i].x = x + i; rects[i].y = y + i; rects[i].width = width - (i << 1); rects[i].height = height - (i << 1); } if (width - (n << 1) >= 5 && height - (n << 1) >= 5) { if (width - (n << 1) >= 10) { int off = (width - (n << 1)) / 3 + n; rects[i].x = x + off; rects[i].y = y + n; rects[i].width = width - (off << 1); rects[i].height = height - (n << 1); i++; } if (height - (n << 1) >= 10) { int off = (height - (n << 1)) / 3 + n; rects[i].x = x + n; rects[i].y = y + off; rects[i].width = width - (n << 1); rects[i].height = height - (off << 1); i++; } } return i; } struct { rectangle geom; struct { unsigned is_enabled : 1; } flags; } move_resize_grid = { { 0, 0, 0, 0 }, { 0 } }; static void draw_move_resize_grid(int x, int y, int width, int height) { int nrects = 0; XRectangle rects[10]; if (move_resize_grid.flags.is_enabled && x == move_resize_grid.geom.x && y == move_resize_grid.geom.y && width == move_resize_grid.geom.width && height == move_resize_grid.geom.height) { return; } memset(rects, 0, 10 * sizeof(XRectangle)); /* place the resize rectangle into the array of rectangles */ /* interleave them for best visual look */ /* draw the new one, if any */ if (move_resize_grid.flags.is_enabled /*move_resize_grid.geom.width && move_resize_grid.geom.height*/) { move_resize_grid.flags.is_enabled = 0; nrects += get_outline_rects( &(rects[0]), move_resize_grid.geom.x, move_resize_grid.geom.y, move_resize_grid.geom.width, move_resize_grid.geom.height); } if (width && height) { move_resize_grid.flags.is_enabled = 1; move_resize_grid.geom.x = x; move_resize_grid.geom.y = y; move_resize_grid.geom.width = width; move_resize_grid.geom.height = height; nrects += get_outline_rects(&(rects[nrects]), x, y, width, height); } if (nrects > 0) { XDrawRectangles(dpy, Scr.Root, Scr.XorGC, rects, nrects); XSync(dpy, 0); } return; } void switch_move_resize_grid(Bool state) { if (state == False) { if (move_resize_grid.flags.is_enabled) { draw_move_resize_grid(0, 0, 0, 0); } else { move_resize_grid.geom.x = 0; move_resize_grid.geom.y = 0; move_resize_grid.geom.width = 0; move_resize_grid.geom.height = 0; } } else if (!move_resize_grid.flags.is_enabled) { if (move_resize_grid.geom.width && move_resize_grid.geom.height) { draw_move_resize_grid( move_resize_grid.geom.x, move_resize_grid.geom.y, move_resize_grid.geom.width, move_resize_grid.geom.height); } } return; } /* ----------------------------- maximizing code --------------------------- */ static void move_sticky_window_to_same_page( int *x11, int *x12, int *y11, int *y12, int x21, int x22, int y21, int y22) { /* make sure the x coordinate is on the same page as the reference window */ if (*x11 >= x22) { while (*x11 >= x22) { *x11 -= Scr.MyDisplayWidth; *x12 -= Scr.MyDisplayWidth; } } else if (*x12 <= x21) { while (*x12 <= x21) { *x11 += Scr.MyDisplayWidth; *x12 += Scr.MyDisplayWidth; } } /* make sure the y coordinate is on the same page as the reference window */ if (*y11 >= y22) { while (*y11 >= y22) { *y11 -= Scr.MyDisplayHeight; *y12 -= Scr.MyDisplayHeight; } } else if (*y12 <= y21) { while (*y12 <= y21) { *y11 += Scr.MyDisplayHeight; *y12 += Scr.MyDisplayHeight; } } } static void MaximizeHeight( FvwmWindow *win, unsigned int win_width, int win_x, unsigned int *win_height, int *win_y, Bool grow_up, Bool grow_down, int top_border, int bottom_border) { FvwmWindow *cwin; int x11, x12, x21, x22; int y11, y12, y21, y22; int new_y1, new_y2; Bool is_sticky; x11 = win_x; /* Start x */ y11 = *win_y; /* Start y */ x12 = x11 + win_width; /* End x */ y12 = y11 + *win_height; /* End y */ new_y1 = top_border; new_y2 = bottom_border; for (cwin = Scr.FvwmRoot.next; cwin; cwin = cwin->next) { if (IS_STICKY(cwin) || (IS_ICONIFIED(cwin) && IS_ICON_STICKY(cwin))) is_sticky = True; else is_sticky = False; if (cwin == win || (cwin->Desk != win->Desk && !is_sticky)) continue; if (IS_ICONIFIED(cwin)) { if(cwin->icon_w == None || IS_ICON_UNMAPPED(cwin)) continue; x21 = cwin->icon_g.x; y21 = cwin->icon_g.y; x22 = x21 + cwin->icon_p_width; y22 = y21 + cwin->icon_p_height + cwin->icon_g.height; } else { x21 = cwin->frame_g.x; y21 = cwin->frame_g.y; x22 = x21 + cwin->frame_g.width; y22 = y21 + cwin->frame_g.height; } if (is_sticky) { move_sticky_window_to_same_page( &x21, &x22, &new_y1, &new_y2, x11, x12, y11, y12); } /* Are they in the same X space? */ if (!((x22 <= x11) || (x21 >= x12))) { if ((y22 <= y11) && (y22 >= new_y1)) { new_y1 = y22; } else if ((y12 <= y21) && (new_y2 >= y21)) { new_y2 = y21; } } } if (!grow_up) new_y1 = y11; if (!grow_down) new_y2 = y12; *win_height = new_y2 - new_y1; *win_y = new_y1; } static void MaximizeWidth( FvwmWindow *win, unsigned int *win_width, int *win_x, unsigned int win_height, int win_y, Bool grow_left, Bool grow_right, int left_border, int right_border) { FvwmWindow *cwin; int x11, x12, x21, x22; int y11, y12, y21, y22; int new_x1, new_x2; Bool is_sticky; x11 = *win_x; /* Start x */ y11 = win_y; /* Start y */ x12 = x11 + *win_width; /* End x */ y12 = y11 + win_height; /* End y */ new_x1 = left_border; new_x2 = right_border; for (cwin = Scr.FvwmRoot.next; cwin; cwin = cwin->next) { if (IS_STICKY(cwin) || (IS_ICONIFIED(cwin) && IS_ICON_STICKY(cwin))) is_sticky = True; else is_sticky = False; if (cwin == win || (cwin->Desk != win->Desk && !is_sticky)) continue; if (IS_ICONIFIED(cwin)) { if(cwin->icon_w == None || IS_ICON_UNMAPPED(cwin)) continue; x21 = cwin->icon_g.x; y21 = cwin->icon_g.y; x22 = x21 + cwin->icon_p_width; y22 = y21 + cwin->icon_p_height + cwin->icon_g.height; } else { x21 = cwin->frame_g.x; y21 = cwin->frame_g.y; x22 = x21 + cwin->frame_g.width; y22 = y21 + cwin->frame_g.height; } if (is_sticky) { move_sticky_window_to_same_page( &new_x1, &new_x2, &y21, &y22, x11, x12, y11, y12); } /* Are they in the same Y space? */ if (!((y22 <= y11) || (y21 >= y12))) { if ((x22 <= x11) && (x22 >= new_x1)) { new_x1 = x22; } else if ((x12 <= x21) && (new_x2 >= x21)) { new_x2 = x21; } } } if (!grow_left) new_x1 = x11; if (!grow_right) new_x2 = x12; *win_width = new_x2 - new_x1; *win_x = new_x1; } /*********************************************************************** * * Procedure: * (Un)Maximize a window. * ***********************************************************************/ void CMD_Maximize(F_CMD_ARGS) { int page_x, page_y; int val1, val2, val1_unit, val2_unit; int toggle; char *token; char *taction; Bool grow_up = False; Bool grow_down = False; Bool grow_left = False; Bool grow_right = False; Bool do_force_maximize = False; Bool is_screen_given = False; int scr_x, scr_y, scr_w, scr_h; rectangle new_g; FvwmWindow *sf; if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease)) return; if (tmp_win == NULL) return; if (check_if_function_allowed(F_MAXIMIZE,tmp_win,True,NULL) == 0) { XBell(dpy, 0); return; } /* Check for "global" flag ("absolute" is for compatibility with E) */ token = PeekToken(action, &taction); if (token) { if (StrEquals(token, "screen")) { int scr; is_screen_given = True; token = PeekToken(taction, &action); scr = FScreenGetScreenArgument(token, FSCREEN_SPEC_PRIMARY); FScreenGetScrRect(NULL, scr, &scr_x, &scr_y, &scr_w, &scr_h); } } toggle = ParseToggleArgument(action, &action, -1, 0); if (toggle == 0 && !IS_MAXIMIZED(tmp_win)) return; if (toggle == 1 && IS_MAXIMIZED(tmp_win)) { /* Fake that the window is not maximized. */ do_force_maximize = True; } /* find the new page and geometry */ new_g.x = tmp_win->frame_g.x; new_g.y = tmp_win->frame_g.y; new_g.width = tmp_win->frame_g.width; new_g.height = tmp_win->frame_g.height; if (IsRectangleOnThisPage(&tmp_win->frame_g, tmp_win->Desk)) { /* maximize on visible page if any part of the window is visible */ page_x = 0; page_y = 0; } else { int xoff = Scr.Vx % Scr.MyDisplayWidth; int yoff = Scr.Vy % Scr.MyDisplayHeight; /* maximize on the page where the center of the window is */ page_x = truncate_to_multiple( tmp_win->frame_g.x + tmp_win->frame_g.width / 2 + xoff, Scr.MyDisplayWidth) - xoff; page_y = truncate_to_multiple( tmp_win->frame_g.y + tmp_win->frame_g.height / 2 + yoff, Scr.MyDisplayHeight) - yoff; } /* Check if we should constrain rectangle to some Xinerama screen */ if (!is_screen_given) { fscreen_scr_arg fscr; fscr.xypos.x = tmp_win->frame_g.x + tmp_win->frame_g.width / 2 - page_x; fscr.xypos.y = tmp_win->frame_g.y + tmp_win->frame_g.height / 2 - page_y; FScreenGetScrRect(&fscr, FSCREEN_XYPOS, &scr_x, &scr_y, &scr_w, &scr_h); } #if 0 fprintf(stderr, "%s: page=(%d,%d), scr=(%d,%d, %dx%d)\n", __FUNCTION__, page_x, page_y, scr_x, scr_y, scr_w, scr_h); #endif /* parse first parameter */ val1_unit = scr_w; token = PeekToken(action, &taction); if (token && StrEquals(token, "grow")) { grow_left = True; grow_right = True; val1 = 100; val1_unit = scr_w; } else if (token && StrEquals(token, "growleft")) { grow_left = True; val1 = 100; val1_unit = scr_w; } else if (token && StrEquals(token, "growright")) { grow_right = True; val1 = 100; val1_unit = scr_w; } else { if (GetOnePercentArgument(token, &val1, &val1_unit) == 0) { val1 = 100; val1_unit = scr_w; } else if (val1 < 0) { /* handle negative offsets */ if (val1_unit == scr_w) { val1 = 100 + val1; } else { val1 = scr_w + val1; } } } /* parse second parameter */ val2_unit = scr_h; token = PeekToken(taction, NULL); if (token && StrEquals(token, "grow")) { grow_up = True; grow_down = True; val2 = 100; val2_unit = scr_h; } else if (token && StrEquals(token, "growup")) { grow_up = True; val2 = 100; val2_unit = scr_h; } else if (token && StrEquals(token, "growdown")) { grow_down = True; val2 = 100; val2_unit = scr_h; } else { if (GetOnePercentArgument(token, &val2, &val2_unit) == 0) { val2 = 100; val2_unit = scr_h; } else if (val2 < 0) { /* handle negative offsets */ if (val2_unit == scr_h) { val2 = 100 + val2; } else { val2 = scr_h + val2; } } } if (IS_MAXIMIZED(tmp_win) && !do_force_maximize) { SET_MAXIMIZED(tmp_win, 0); get_relative_geometry(&tmp_win->frame_g, &tmp_win->normal_g); if (IS_SHADED(tmp_win)) get_shaded_geometry(tmp_win, &tmp_win->frame_g, &tmp_win->frame_g); ForceSetupFrame( tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width, tmp_win->frame_g.height, True); DrawDecorations(tmp_win, DRAW_ALL, True, True, None); } else /* maximize */ { /* handle command line arguments */ if (grow_up || grow_down) { MaximizeHeight( tmp_win, new_g.width, new_g.x, (unsigned int *)&new_g.height, &new_g.y, grow_up, grow_down, page_y + scr_y, page_y + scr_y + scr_h); } else if(val2 > 0) { new_g.height = val2 * val2_unit / 100; new_g.y = page_y + scr_y; } if (grow_left || grow_right) { MaximizeWidth( tmp_win, (unsigned int *)&new_g.width, &new_g.x, new_g.height, new_g.y, grow_left, grow_right, page_x + scr_x, page_x + scr_x + scr_w); } else if(val1 >0) { new_g.width = val1 * val1_unit / 100; new_g.x = page_x + scr_x; } if(val1 == 0 && val2 == 0) { new_g.x = page_x + scr_x; new_g.y = page_y + scr_y; new_g.height = scr_h; new_g.width = scr_w; } /* now maximize it */ SET_MAXIMIZED(tmp_win, 1); tmp_win->max_g_defect.width = 0; tmp_win->max_g_defect.height = 0; constrain_size(tmp_win, (unsigned int *)&new_g.width, (unsigned int *)&new_g.height, 0, 0, CS_UPDATE_MAX_DEFECT); tmp_win->max_g = new_g; if (IS_SHADED(tmp_win)) get_shaded_geometry(tmp_win, &new_g, &tmp_win->max_g); SetupFrame( tmp_win, new_g.x, new_g.y, new_g.width, new_g.height, True); DrawDecorations(tmp_win, DRAW_ALL, (Scr.Hilite == tmp_win), True, None); update_absolute_geometry(tmp_win); /* remember the offset between old and new position in case the maximized * window is moved more than the screen width/height. */ tmp_win->max_offset.x = tmp_win->normal_g.x - tmp_win->max_g.x; tmp_win->max_offset.y = tmp_win->normal_g.y - tmp_win->max_g.y; #if 0 fprintf(stderr,"%d %d %d %d, max_offset.x = %d, max_offset.y = %d\n", tmp_win->max_g.x, tmp_win->max_g.y, tmp_win->max_g.width, tmp_win->max_g.height, tmp_win->max_offset.x, tmp_win->max_offset.y); #endif } if ((sf = get_focus_window())) { focus_grab_buttons(sf, True); } GNOME_SetWinArea(tmp_win); } /* ----------------------------- stick code -------------------------------- */ void handle_stick(F_CMD_ARGS, int toggle) { FvwmWindow *sf; if ((toggle == 1 && IS_STICKY(tmp_win)) || (toggle == 0 && !IS_STICKY(tmp_win))) return; if(IS_STICKY(tmp_win)) { SET_STICKY(tmp_win, 0); tmp_win->Desk = Scr.CurrentDesk; GNOME_SetDeskCount(); GNOME_SetDesk(tmp_win); } else { if (tmp_win->Desk != Scr.CurrentDesk) do_move_window_to_desk(tmp_win, Scr.CurrentDesk); SET_STICKY(tmp_win, 1); if (!IsRectangleOnThisPage(&tmp_win->frame_g, Scr.CurrentDesk)) { action = ""; move_window_doit(F_PASS_ARGS, FALSE, MOVE_PAGE); /* move_window_doit resets the STICKY flag, so we must set it after the * call! */ SET_STICKY(tmp_win, 1); } } BroadcastConfig(M_CONFIGURE_WINDOW,tmp_win); DrawDecorations( tmp_win, DRAW_TITLE | DRAW_BUTTONS, (Scr.Hilite==tmp_win), True, None); if ((sf = get_focus_window())) { focus_grab_buttons(sf, True); } GNOME_SetHints(tmp_win); } void CMD_Stick(F_CMD_ARGS) { int toggle; if (DeferExecution(eventp,&w,&tmp_win,&context,CRS_SELECT,ButtonRelease)) return; toggle = ParseToggleArgument(action, &action, -1, 0); handle_stick(F_PASS_ARGS, toggle); }