/* -*-c-*- */
/* 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 <stdio.h>
#include <math.h>
#include <X11/keysym.h>
#include "libs/fvwmlib.h"
#include "libs/FScreen.h"
#include "libs/Picture.h"
#include "libs/Grab.h"
#include "libs/Parse.h"
#include "libs/Graphics.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "execcontext.h"
#include "commands.h"
#include "misc.h"
#include "screen.h"
#include "menus.h"
#include "menuparameters.h"
#include "module_list.h"
#include "module_interface.h"
#include "focus.h"
#include "borders.h"
#include "frame.h"
#include "geometry.h"
#include "gnome.h"
#include "ewmh.h"
#include "virtual.h"
#include "decorations.h"
#include "events.h"
#include "eventhandler.h"
#include "eventmask.h"
#include "colormaps.h"
#include "update.h"
#include "stack.h"
#include "move_resize.h"
/* ----- move globals ----- */
#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
};
static int move_interactive_finish_button_mask =
((1<<(NUMBER_OF_EXTENDED_MOUSE_BUTTONS))-1) & ~0x2;
static int move_drag_finish_button_mask =
((1<<(NUMBER_OF_EXTENDED_MOUSE_BUTTONS))-1) & ~0x3;
/* ----- 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 draw_move_resize_grid(int x, int y, int width, int height);
/* ----- end of resize globals ----- */
/*
*
* 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);
XFlush(dpy);
}
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;
}
static int ParsePositionArgumentSuffix(
float *ret_factor, char *suffix, float wfactor, float sfactor)
{
int n;
switch (*suffix)
{
case 'p':
case 'P':
*ret_factor = 1.0;
n = 1;
break;
case 'w':
case 'W':
*ret_factor = wfactor;
n = 1;
break;
default:
*ret_factor = sfactor;
n = 0;
break;
}
return n;
}
static int __get_shift(int val, float factor)
{
int shift;
if (val >= 0)
{
shift = (int)(val * factor + 0.5);
}
else
{
shift = (int)(val * factor - 0.5);
}
return shift;
}
/* The vars are named for the x-direction, but this is used for both x and y */
static int GetOnePositionArgument(
char *s1, int window_pos, int window_size, int *pFinalPos,
float sfactor, int screen_size, int screen_pos, Bool is_x)
{
int final_pos;
float wfactor;
if (s1 == 0 || *s1 == 0)
{
return 0;
}
wfactor = (float)window_size / 100;
/* get start position */
switch (*s1)
{
case 'w':
case 'W':
final_pos = window_pos;
s1++;
break;
case 'm':
case 'M':
{
int x;
int y;
if (
FQueryPointer(
dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX,
&JunkY, &x, &y, &JunkMask) == False)
{
/* pointer is on a different screen - that's okay here
*/
final_pos = 0;
}
else
{
final_pos = (is_x) ? x : y;
}
s1++;
break;
}
default:
final_pos = screen_pos;
if (*s1 != 0)
{
int val;
int n;
float f;
/* parse value */
if (sscanf(s1, "-%d%n", &val, &n) >= 1)
{
/* i.e. -1, -+1 or --1 */
final_pos += (screen_size - window_size);
val = -val;
}
else if (
sscanf(s1, "+%d%n", &val, &n) >= 1 ||
sscanf(s1, "%d%n", &val, &n) >= 1)
{
/* i.e. 1, +1, ++1 or +-1 */
}
else
{
/* syntax error, ignore rest of string */
break;
}
s1 += n;
/* parse suffix */
n = ParsePositionArgumentSuffix(
&f, s1, wfactor, sfactor);
s1 += n;
final_pos += __get_shift(val, f);
}
break;
}
/* loop over shift arguments */
while (*s1 != 0)
{
int val;
int n;
float f;
/* parse value */
if (sscanf(s1, "%d%n", &val, &n) < 1)
{
/* syntax error, ignore rest of string */
break;
}
s1 += n;
/* parse suffix */
n = ParsePositionArgumentSuffix(&f, s1, wfactor, sfactor);
s1 += n;
final_pos += __get_shift(val, f);
}
*pFinalPos = final_pos;
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
* m+5 m-10p Pointer relative position, right 5%, up ten pixels
* Returns 2 when x & y have parsed without error, 0 otherwise
*/
int GetMoveArguments(
char **paction, int w, int h, int *pFinalX, int *pFinalY,
Bool *fWarp, Bool *fPointer, Bool fKeep)
{
char *s1 = NULL;
char *s2 = NULL;
char *warp = NULL;
char *action;
char *naction;
int scr_x = 0;
int scr_y = 0;
int scr_w = Scr.MyDisplayWidth;
int scr_h = 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;
}
if (s1 && StrEquals(s1, "screen"))
{
char *token;
int scr;
free(s1);
token = PeekToken(action, &action);
scr = FScreenGetScreenArgument(token, FSCREEN_SPEC_PRIMARY);
FScreenGetScrRect(NULL, scr, &scr_x, &scr_y, &scr_w, &scr_h);
action = GetNextToken(action, &s1);
}
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 (fKeep == True && StrEquals(s1, "keep"))
{
retval++;
}
else if (
GetOnePositionArgument(
s1, *pFinalX, w, pFinalX, (float)scr_w / 100,
scr_w, scr_x, True))
{
retval++;
}
if (fKeep == True && StrEquals(s2, "keep"))
{
retval++;
}
else if (
GetOnePositionArgument(
s2, *pFinalY, h, pFinalY, (float)scr_h / 100,
scr_h, scr_y, False))
{
retval++;
}
if (retval == 0)
{
/* make sure warping is off for interactive moves */
*fWarp = False;
}
}
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 add_size,
int *ret_size)
{
float factor;
int val;
int add_base_size = 0;
int cch = strlen(arg);
int tmp_size;
if (cch == 0)
{
return 0;
}
if (StrEquals(arg, "keep"))
{
/* do not change size */
return 1;
}
if (arg[cch-1] == 'p')
{
factor = 1;
arg[cch-1] = '\0';
}
else if (arg[cch-1] == 'c')
{
factor = size_inc;
add_base_size = base_size;
arg[cch-1] = '\0';
}
else
{
factor = (float)scr_size / 100.0;
}
if (strcmp(arg,"w") == 0)
{
/* do not change size */
}
else if (sscanf(arg,"w-%d",&val) == 1)
{
tmp_size = (int)(val * factor + 0.5);
if (tmp_size < *ret_size)
{
*ret_size -= tmp_size;
}
else
{
*ret_size = 0;
}
}
else if (sscanf(arg,"w+%d",&val) == 1 || sscanf(arg,"w%d",&val) == 1)
{
tmp_size = (int)(val * factor + 0.5);
if (-tmp_size < *ret_size)
{
*ret_size += tmp_size;
}
else
{
*ret_size = 0;
}
}
else if (sscanf(arg,"-%d",&val) == 1)
{
tmp_size = (int)(val * factor + 0.5);
if (tmp_size < scr_size + add_size)
{
*ret_size = scr_size - tmp_size + add_size;
}
else
{
*ret_size = 0;
}
}
else if (sscanf(arg,"+%d",&val) == 1 || sscanf(arg,"%d",&val) == 1)
{
tmp_size = (int)(val * factor + 0.5);
if (-tmp_size < add_size + add_base_size)
{
*ret_size = tmp_size + add_size + add_base_size;
}
else
{
*ret_size = 0;
}
}
else
{
return 0;
}
return 1;
}
static int GetResizeArguments(
char **paction, int x, int y, int w_base, int h_base, int w_inc,
int h_inc, size_borders *sb, int *pFinalW, int *pFinalH,
direction_t *ret_dir, Bool *is_direction_fixed,
Bool *do_warp_to_border)
{
int n;
char *naction;
char *token;
char *s1;
char *s2;
int w_add;
int h_add;
int has_frame_option;
*ret_dir = DIR_NONE;
*is_direction_fixed = False;
*do_warp_to_border = False;
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, True);
if (n < 2)
{
return 0;
}
*pFinalW = nx - x + 1;
*pFinalH = ny - y + 1;
*paction = naction;
return n;
}
has_frame_option = 0;
for ( ; ; token = PeekToken(naction, &naction))
{
if (StrEquals(token, "frame"))
{
has_frame_option = 1;
}
else if (StrEquals(token, "direction"))
{
if (token == NULL)
{
return 0;
}
*ret_dir = gravity_parse_dir_argument(
naction, &naction, DIR_NONE);
if (*ret_dir != DIR_NONE)
{
*is_direction_fixed = True;
}
}
else if (StrEquals(token, "fixeddirection"))
{
*is_direction_fixed = True;
}
else if (StrEquals(token, "warptoborder"))
{
*do_warp_to_border = True;
}
else
{
break;
}
}
if (has_frame_option)
{
w_add = 0;
h_add = 0;
}
else
{
w_add = sb->total_size.width;
h_add = sb->total_size.height;
}
s1 = NULL;
if (token != NULL)
{
s1 = safestrdup(token);
}
naction = GetNextToken(naction, &s2);
if (!s2)
{
if (s1 != NULL)
{
free(s1);
}
return 0;
}
*paction = naction;
n = 0;
n += ParseOneResizeArgument(
s1, Scr.MyDisplayWidth, w_base, w_inc, w_add, pFinalW);
n += ParseOneResizeArgument(
s2, Scr.MyDisplayHeight, h_base, h_inc, h_add, pFinalH);
if (s1 != NULL)
{
free(s1);
}
if (s2 != NULL)
{
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,
size_borders *sb, int *pFinalX, int *pFinalY,
int *pFinalW, int *pFinalH, Bool *fWarp, Bool *fPointer)
{
char *action = *paction;
direction_t dir;
Bool dummy;
if (!paction)
{
return 0;
}
if (GetResizeArguments(
&action, *pFinalX, *pFinalY, w_base, h_base, w_inc, h_inc,
sb, pFinalW, pFinalH, &dir, &dummy, &dummy) < 2)
{
return 0;
}
if (GetMoveArguments(
&action, *pFinalW, *pFinalH, pFinalX, pFinalY, fWarp,
NULL, True) < 2)
{
return 0;
}
*paction = action;
return 4;
}
/* Positions the SizeWindow on the current ("moused") xinerama-screen */
static void position_geometry_window(const XEvent *eventp)
{
int x;
int y;
fscreen_scr_arg fscr;
fscr.mouse_ev = (XEvent *)eventp;
/* Probably should remove this positioning code from {builtins,fvwm}.c?
*/
if (Scr.gs.do_emulate_mwm)
{
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 =
FlocaleTextWidth(Scr.DefaultFont, 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(
const FvwmWindow *tmp_win, const XEvent *eventp, int x, int y,int Init)
{
char str[100];
int offset;
fscreen_scr_arg fscr;
FlocaleWinString fstr;
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 -
FlocaleTextWidth(Scr.DefaultFont, str, strlen(str))) / 2;
offset += GEOMETRY_WINDOW_BW;
memset(&fstr, 0, sizeof(fstr));
if (Scr.DefaultColorset >= 0)
{
fstr.colorset = &Colorset[Scr.DefaultColorset];
fstr.flags.has_colorset = True;
}
fstr.str = str;
fstr.win = Scr.SizeWindow;
fstr.gc = Scr.StdGC;
fstr.x = offset;
fstr.y = Scr.DefaultFont->ascent + GEOMETRY_WINDOW_BW;
FlocaleDrawString(dpy, Scr.DefaultFont, &fstr, 0);
return;
}
/*
*
* 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(
const FvwmWindow *tmp_win, const XEvent *eventp, int width,
int height, Bool Init, Bool resetLast)
{
char str[100];
int dwidth,dheight,offset;
size_borders b;
static int last_width = 0;
static int last_height = 0;
FlocaleWinString fstr;
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;
get_window_borders(tmp_win, &b);
dheight = height - b.total_size.height;
dwidth = width - b.total_size.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 -
FlocaleTextWidth(Scr.DefaultFont, str, strlen(str))) / 2;
offset += GEOMETRY_WINDOW_BW;
memset(&fstr, 0, sizeof(fstr));
if (Scr.DefaultColorset >= 0)
{
fstr.colorset = &Colorset[Scr.DefaultColorset];
fstr.flags.has_colorset = True;
}
fstr.str = str;
fstr.win = Scr.SizeWindow;
fstr.gc = Scr.StdGC;
fstr.x = offset;
fstr.y = Scr.DefaultFont->ascent + GEOMETRY_WINDOW_BW;
FlocaleDrawString(dpy, Scr.DefaultFont, &fstr, 0);
return;
}
static Bool resize_move_window(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;
size_borders b;
FvwmWindow *fw = exc->w.fw;
Window w = exc->w.w;
if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
{
return False;
}
if (!is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True))
{
return False;
}
/* gotta have a window */
w = FW_W_FRAME(fw);
if (!XGetGeometry(
dpy, w, &JunkRoot, &x, &y, (unsigned int*)&FinalW,
(unsigned int*)&FinalH, (unsigned int*)&JunkBW,
(unsigned int*)&JunkDepth))
{
XBell(dpy, 0);
return False;
}
FinalX = x;
FinalY = y;
get_window_borders(fw, &b);
n = GetResizeMoveArguments(
&action,
fw->hints.base_width, fw->hints.base_height,
fw->hints.width_inc, fw->hints.height_inc,
&b, &FinalX, &FinalY, &FinalW, &FinalH, &fWarp, &fPointer);
if (n < 4)
{
return False;
}
if (IS_MAXIMIZED(fw))
{
/* must redraw the buttons now so that the 'maximize' button
* does not stay depressed. */
SET_MAXIMIZED(fw, 0);
border_draw_decorations(
fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
NULL, NULL);
}
dx = FinalX - fw->g.frame.x;
dy = FinalY - fw->g.frame.y;
/* size will be less or equal to requested */
constrain_size(fw, NULL, &FinalW, &FinalH, 0, 0, 0);
if (IS_SHADED(fw))
{
frame_setup_window(
fw, FinalX, FinalY, FinalW, fw->g.frame.height, False);
}
else
{
frame_setup_window(fw, FinalX, FinalY, FinalW, FinalH, True);
}
if (fWarp)
{
FWarpPointer(
dpy, None, None, 0, 0, 0, 0, FinalX - x, FinalY - y);
}
if (IS_MAXIMIZED(fw))
{
fw->g.max.x += dx;
fw->g.max.y += dy;
}
else
{
fw->g.normal.x += dx;
fw->g.normal.y += dy;
}
has_focus = (fw == get_focus_window())? True : False;
update_absolute_geometry(fw);
maximize_adjust_offset(fw);
XFlush(dpy);
GNOME_SetWinArea(fw);
return True;
}
void CMD_ResizeMove(F_CMD_ARGS)
{
FvwmWindow *fw = exc->w.fw;
if (IS_EWMH_FULLSCREEN(fw))
{
/* do not unmaximize ! */
CMD_ResizeMoveMaximize(F_PASS_ARGS);
return;
}
resize_move_window(F_PASS_ARGS);
return;
}
static void InteractiveMove(
Window *win, const exec_context_t *exc, int *FinalX, int *FinalY,
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.do_install_root_cmap)
{
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 (FQueryPointer(
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. */
fev_get_evpos_or_query(
dpy, Scr.Root, exc->x.elast, &DragX, &DragY);
}
MyXGrabServer(dpy);
if (!XGetGeometry(
dpy, w, &JunkRoot, &origDragX, &origDragY,
(unsigned int*)&DragWidth, (unsigned int*)&DragHeight,
(unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
{
MyXUngrabServer(dpy);
return;
}
MyXGrabKeyboard(dpy);
if (do_start_at_pointer)
{
origDragX = DragX;
origDragY = DragY;
}
if (IS_ICONIFIED(exc->w.fw))
{
do_move_opaque = True;
}
else if (IS_MAPPED(exc->w.fw))
{
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(exc->w.fw))
{
XUnmapWindow(dpy,w);
}
XOffset = origDragX - DragX;
YOffset = origDragY - DragY;
if (!Scr.gs.do_hide_position_window)
{
position_geometry_window(NULL);
XMapRaised(dpy,Scr.SizeWindow);
}
__move_loop(
exc, XOffset, YOffset, DragWidth, DragHeight, FinalX, FinalY,
do_move_opaque, CRS_MOVE);
if (!Scr.gs.do_hide_position_window)
{
XUnmapWindow(dpy,Scr.SizeWindow);
}
if (Scr.bo.do_install_root_cmap)
{
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 *fw, Window w, int startX, int startY, int endX,
int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement,
MenuRepaintTransparentParameters *pmrtp)
{
int pointerX, pointerY;
int currentX, currentY;
int lastX, lastY;
int deltaX, deltaY;
Bool first = True;
XEvent evdummy;
unsigned int draw_parts = PART_NONE;
if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
{
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,
(unsigned int*)&JunkWidth,
(unsigned int*)&JunkHeight,
(unsigned int*)&JunkBW,
(unsigned int*)&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;
}
if (fw && w == FW_W_FRAME(fw))
{
draw_parts = border_get_transparent_decorations_part(fw);
}
/* 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;
}
if (pmrtp != NULL)
{
update_transparent_menu_bg(
pmrtp, lastX, lastY, currentX, currentY,
endX, endY);
}
XMoveWindow(dpy,w,currentX,currentY);
if (pmrtp != NULL)
{
repaint_transparent_menu(
pmrtp, first,
currentX, currentY, endX, endY, True);
}
else if (draw_parts != PART_NONE)
{
border_draw_decorations(
fw, draw_parts,
((fw == get_focus_window())) ?
True : False,
True, CLEAR_ALL, NULL, NULL);
}
if (fw && pmrtp == NULL && IS_TEAR_OFF_MENU(fw))
{
menu_redraw_transparent_tear_off_menu(fw, False);
}
if (fWarpPointerToo == True)
{
if (FQueryPointer(
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;
}
FWarpPointer(
dpy, None, Scr.Root, 0, 0, 0, 0, pointerX,
pointerY);
}
if (fw && !IS_SHADED(fw) && !Scr.bo.do_disable_configure_notify)
{
/* send configure notify event for windows that care
* about their location */
SendConfigureNotify(
fw, currentX, currentY,
fw->g.frame.width,
fw->g.frame.height, 0, False);
#ifdef FVWM_DEBUG_MSGS
fvwm_msg(DBG,"AnimatedMoveAnyWindow",
"Sent ConfigureNotify (w == %d, h == %d)",
fw->g.frame.width,
fw->g.frame.height);
#endif
}
XFlush(dpy);
if (fw)
{
fw->g.frame.x = currentX;
fw->g.frame.y = currentY;
update_absolute_geometry(fw);
maximize_adjust_offset(fw);
BroadcastConfig(M_CONFIGURE_WINDOW, fw);
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 (FCheckMaskEvent(
dpy, ButtonPressMask|ButtonReleaseMask|KeyPressMask,
&evdummy))
{
/* finish the move immediately */
if (pmrtp != NULL)
{
update_transparent_menu_bg(
pmrtp, lastX, lastY,
currentX, currentY, endX, endY);
}
XMoveWindow(dpy,w,endX,endY);
if (pmrtp != NULL)
{
repaint_transparent_menu(
pmrtp, first,
endX, endY, endX, endY, True);
}
break;
}
lastX = currentX;
lastY = currentY;
first = False;
} while (*ppctMovement != 1.0 && ppctMovement++);
MyXUngrabKeyboard(dpy);
XFlush(dpy);
if (fw)
{
GNOME_SetWinArea(fw);
}
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,
MenuRepaintTransparentParameters *pmrtp)
{
AnimatedMoveAnyWindow(
NULL, w, startX, startY, endX, endY, fWarpPointerToo,
cmsDelay, ppctMovement, pmrtp);
return;
}
/* used for moving client windows */
void AnimatedMoveFvwmWindow(
FvwmWindow *fw, Window w, int startX, int startY, int endX,
int endY, Bool fWarpPointerToo, int cmsDelay, float *ppctMovement)
{
AnimatedMoveAnyWindow(
fw, w, startX, startY, endX, endY, fWarpPointerToo,
cmsDelay, ppctMovement, NULL);
return;
}
int placement_binding(int button, KeySym keysym, int modifier, char *action)
{
if (keysym != 0)
{
/* fixme */
fvwm_msg(
ERR, "placement_binding",
"sorry, placement keybindings not allowed. yet.");
return 1;
}
if (modifier != 0)
{
/* fixme */
fvwm_msg(
ERR, "placement_binding",
"sorry, placement binding modifiers not allowed. yet.");
return 1;
}
if (strcmp(action,"-") == 0 ||
strcasecmp(action,"CancelPlacement") == 0)
{
if (keysym == 0) /* must be button binding */
{
if (button == 0)
{
move_drag_finish_button_mask = 0;
move_interactive_finish_button_mask = 0;
}
else if (button > 0 && button <=
NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
{
move_drag_finish_button_mask &=
~(1<<(button-1));
move_interactive_finish_button_mask &=
~(1<<(button-1));
}
}
}
else if (strcasecmp(action,"CancelPlacementDrag") == 0)
{
if (keysym == 0) /* must be button binding */
{
if (button == 0)
{
move_drag_finish_button_mask = 0;
}
else if (button > 0 && button <=
NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
{
move_drag_finish_button_mask &=
~(1<<(button-1));
}
}
}
else if (strcasecmp(action,"CancelPlacementInteractive") == 0)
{
if (keysym == 0) /* must be button binding */
{
if (button == 0)
{
move_interactive_finish_button_mask = 0;
}
else if (button > 0 && button <=
NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
{
move_interactive_finish_button_mask &=
~(1<<(button-1));
}
}
}
else if (strcasecmp(action,"PlaceWindow") == 0)
{
if (keysym == 0) /* must be button binding */
{
if (button == 0)
{
move_interactive_finish_button_mask =
move_drag_finish_button_mask =
(1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
}
else if (button > 0 && button <=
NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
{
move_drag_finish_button_mask |= (1<<(button-1));
move_interactive_finish_button_mask |=
(1<<(button-1));
}
}
}
else if (strcasecmp(action,"PlaceWindowDrag") == 0)
{
if (keysym == 0) /* must be button binding */
{
if (button == 0)
{
move_drag_finish_button_mask =
(1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
}
else if (button > 0 && button <=
NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
{
move_drag_finish_button_mask |= (1<<(button-1));
}
}
}
else if (strcasecmp(action,"PlaceWindowInteractive") == 0)
{
if (keysym == 0) /* must be button binding */
{
if (button == 0)
{
move_interactive_finish_button_mask =
(1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS)-1;
}
else if (button > 0 && button <=
NUMBER_OF_EXTENDED_MOUSE_BUTTONS)
{
move_interactive_finish_button_mask |=
(1<<(button-1));
}
}
}
else
{
fvwm_msg(
ERR, "placement_binding",
"invalid action %s", action);
}
return 0;
}
/*
*
* Start a window move operation
*
*/
void __move_icon(
FvwmWindow *fw, int x, int y, int old_x, int old_y,
Bool do_move_animated, Bool do_warp_pointer)
{
rectangle gt;
rectangle gp;
Bool has_icon_title;
Bool has_icon_picture;
Window tw;
int tx;
int ty;
set_icon_position(fw, x, y);
broadcast_icon_geometry(fw, False);
has_icon_title = get_visible_icon_title_geometry(fw, >);
has_icon_picture = get_visible_icon_picture_geometry(fw, &gp);
if (has_icon_picture)
{
tw = FW_W_ICON_PIXMAP(fw);
tx = gp.x;
ty = gp.y;
}
else if (has_icon_title)
{
tw = FW_W_ICON_TITLE(fw);
tx = gt.x;
ty = gt.y;
}
else
{
return;
}
if (do_move_animated)
{
AnimatedMoveOfWindow(
tw, -1, -1, tx, ty, do_warp_pointer, -1, NULL, NULL);
do_warp_pointer = 0;
}
if (has_icon_title)
{
XMoveWindow(dpy, FW_W_ICON_TITLE(fw), gt.x, gt.y);
}
if (has_icon_picture)
{
XMoveWindow(dpy, FW_W_ICON_PIXMAP(fw), gp.x, gp.y);
if (fw->Desk == Scr.CurrentDesk)
{
XMapWindow(dpy, FW_W_ICON_PIXMAP(fw));
if (has_icon_title)
{
XMapWindow(dpy, FW_W_ICON_TITLE(fw));
}
}
}
if (do_warp_pointer)
{
FWarpPointer(dpy, None, None, 0, 0, 0, 0, x - old_x, y - old_y);
}
return;
}
static void __move_window(F_CMD_ARGS, Bool do_animate, int mode)
{
int FinalX = 0;
int FinalY = 0;
int n;
int x;
int y;
int width, height;
int page_x, page_y;
Bool fWarp = False;
Bool fPointer = False;
int dx;
int dy;
FvwmWindow *fw = exc->w.fw;
Window w;
if (!is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
{
return;
}
/* gotta have a window */
w = FW_W_FRAME(fw);
if (IS_ICONIFIED(fw))
{
if (FW_W_ICON_PIXMAP(fw) != None)
{
w = FW_W_ICON_PIXMAP(fw);
XUnmapWindow(dpy,FW_W_ICON_TITLE(fw));
}
else
{
w = FW_W_ICON_TITLE(fw);
}
if (w == None && (mode == MOVE_PAGE || mode == MOVE_SCREEN))
{
w = FW_W_FRAME(fw);
}
}
if (
!XGetGeometry(
dpy, w, &JunkRoot, &x, &y, (unsigned int*)&width,
(unsigned int*)&height, (unsigned int*)&JunkBW,
(unsigned int*)&JunkDepth))
{
return;
}
if (mode == MOVE_PAGE && IS_STICKY_ACROSS_PAGES(fw))
{
return;
}
if (mode == MOVE_PAGE)
{
rectangle r;
rectangle s;
rectangle t;
do_animate = False;
r.x = x;
r.y = y;
r.width = width;
r.height = height;
get_absolute_geometry(&t, &r);
get_page_offset_rectangle(&page_x, &page_y, &t);
if (!get_page_arguments(action, &page_x, &page_y))
{
page_x = Scr.Vx;
page_y = Scr.Vy;
}
s.x = page_x - Scr.Vx;
s.y = page_y - Scr.Vy;
s.width = Scr.MyDisplayWidth;
s.height = Scr.MyDisplayHeight;
fvwmrect_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;
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 */
fvwmrect_move_into_rectangle(&r, &p);
/* then move to screen */
fvwmrect_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, True);
if (n != 2 || fPointer)
{
InteractiveMove(&w, exc, &FinalX, &FinalY, fPointer);
}
else if (IS_ICONIFIED(fw))
{
SET_ICON_MOVED(fw, 1);
}
}
if (w == FW_W_FRAME(fw))
{
dx = FinalX - fw->g.frame.x;
dy = FinalY - fw->g.frame.y;
if (do_animate)
{
AnimatedMoveFvwmWindow(
fw, w, -1, -1, FinalX, FinalY, fWarp, -1,
NULL);
}
frame_setup_window(
fw, FinalX, FinalY, fw->g.frame.width,
fw->g.frame.height, True);
if (fWarp & !do_animate)
{
FWarpPointer(
dpy, None, None, 0, 0, 0, 0, FinalX - x,
FinalY - y);
}
if (IS_MAXIMIZED(fw))
{
fw->g.max.x += dx;
fw->g.max.y += dy;
}
else
{
fw->g.normal.x += dx;
fw->g.normal.y += dy;
}
update_absolute_geometry(fw);
maximize_adjust_offset(fw);
XFlush(dpy);
GNOME_SetWinArea(fw);
}
else /* icon window */
{
__move_icon(fw, FinalX, FinalY, x, y, do_animate, fWarp);
XFlush(dpy);
}
focus_grab_buttons_on_layer(fw->layer);
return;
}
void CMD_Move(F_CMD_ARGS)
{
__move_window(F_PASS_ARGS, False, MOVE_NORMAL);
return;
}
void CMD_AnimatedMove(F_CMD_ARGS)
{
__move_window(F_PASS_ARGS, True, MOVE_NORMAL);
return;
}
void CMD_MoveToPage(F_CMD_ARGS)
{
__move_window(F_PASS_ARGS, False, MOVE_PAGE);
return;
}
void CMD_MoveToScreen(F_CMD_ARGS)
{
__move_window(F_PASS_ARGS, False, MOVE_SCREEN);
return;
}
/* This function does the SnapAttraction stuff. If takes x and y coordinates
* (*px and *py) and returns the snapped values. */
static void DoSnapAttract(
FvwmWindow *fw, int Width, int Height, int *px, int *py)
{
int nyt,nxl,dist,closestLeft,closestRight,closestBottom,closestTop;
rectangle self;
/* 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;
{
rectangle g;
Bool rc;
rc = get_visible_icon_title_geometry(fw, &g);
if (rc == True)
{
self.height += g.height;
}
}
/*
* 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;
}
}
/*
* snap attraction
*/
/* snap to other windows */
if ((Scr.SnapMode & (SNAP_ICONS | SNAP_WINDOWS | SNAP_SAME)) &&
Scr.SnapAttraction > 0)
{
FvwmWindow *tmp;
for (tmp = Scr.FvwmRoot.next; tmp; tmp = tmp->next)
{
rectangle other;
if (fw->Desk != tmp->Desk || fw == tmp)
{
continue;
}
/* check snapping type */
switch (Scr.SnapMode)
{
case 1: /* SameType */
if (IS_ICONIFIED(tmp) != IS_ICONIFIED(fw))
{
continue;
}
break;
case 2: /* Icons */
if (!IS_ICONIFIED(tmp) ||
!IS_ICONIFIED(fw))
{
continue;
}
break;
case 3: /* Windows */
if (IS_ICONIFIED(tmp) || IS_ICONIFIED(fw))
{
continue;
}
break;
case 0: /* All */
default:
/* NOOP */
break;
}
/* get other window dimensions */
get_visible_window_or_icon_geometry(tmp, &other);
/* 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 + other.height > *py &&
other.y < *py + self.height)
{
dist = abs(other.x - (*px + self.width));
if (dist < closestRight)
{
closestRight = dist;
if (*px + self.width >= other.x &&
*px + self.width <
other.x + Scr.SnapAttraction)
{
nxl = other.x - self.width;
}
if (*px + self.width >=
other.x - Scr.SnapAttraction &&
*px + self.width < other.x)
{
nxl = other.x - self.width;
}
}
dist = abs(other.x + other.width - *px);
if (dist < closestLeft)
{
closestLeft = dist;
if (*px <= other.x + other.width &&
*px > other.x + other.width -
Scr.SnapAttraction)
{
nxl = other.x + other.width;
}
if (*px <= other.x + other.width +
Scr.SnapAttraction &&
*px > other.x + other.width)
{
nxl = other.x + other.width;
}
}
}
/* snap vertically */
if (
other.x + other.width > *px &&
other.x < *px + self.width)
{
dist = abs(other.y - (*py + self.height));
if (dist < closestBottom)
{
closestBottom = dist;
if (*py + self.height >= other.y &&
*py + self.height < other.y +
Scr.SnapAttraction)
{
nyt = other.y - self.height;
}
if (*py + self.height >=
other.y - Scr.SnapAttraction &&
*py + self.height < other.y)
{
nyt = other.y - self.height;
}
}
dist = abs(other.y + other.height - *py);
if (dist < closestTop)
{
closestTop = dist;
if (*py <=
other.y + other.height &&
*py > other.y + other.height -
Scr.SnapAttraction)
{
nyt = other.y + other.height;
}
if (*py <= other.y + other.height +
Scr.SnapAttraction &&
*py > other.y + other.height)
{
nyt = other.y + other.height;
}
}
}
} /* for */
} /* snap to other windows */
/* snap to screen egdes */
if ((Scr.SnapMode & SNAP_SCREEN) && Scr.SnapAttraction > 0)
{
/* horizontally */
if (!(Scr.MyDisplayWidth < (*px) ||
(*px + self.width) < 0))
{
dist = abs(Scr.MyDisplayHeight - (*py + self.height));
if (dist < closestBottom)
{
closestBottom = dist;
if (*py + self.height >=
Scr.MyDisplayHeight &&
*py + self.height <
Scr.MyDisplayHeight + Scr.SnapAttraction)
{
nyt = Scr.MyDisplayHeight -
self.height;
}
if (*py + self.height >=
Scr.MyDisplayHeight - Scr.SnapAttraction &&
*py + self.height < Scr.MyDisplayHeight)
{
nyt = Scr.MyDisplayHeight -
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 + self.height) < 0))
{
dist = abs(
Scr.MyDisplayWidth - (*px + self.width));
if (dist < closestRight)
{
closestRight = dist;
if (*px + self.width >= Scr.MyDisplayWidth &&
*px + self.width <
Scr.MyDisplayWidth + Scr.SnapAttraction)
{
nxl = Scr.MyDisplayWidth - self.width;
}
if (*px + self.width >=
Scr.MyDisplayWidth - Scr.SnapAttraction &&
*px + self.width < Scr.MyDisplayWidth)
{
nxl = Scr.MyDisplayWidth - 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;
}
/*
* 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;
int 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 __move_loop(
const exec_context_t *exc, int XOffset, int YOffset, int Width,
int Height, int *FinalX, int *FinalY, Bool do_move_opaque, int cursor)
{
extern Window bad_window;
Bool is_finished = False;
Bool is_aborted = False;
int xl,xl2,yt,yt2,delta_x,delta_y,paged;
unsigned int button_mask = 0;
FvwmWindow fw_copy;
int dx = Scr.EdgeScrollX ? Scr.EdgeScrollX : Scr.MyDisplayWidth;
int dy = Scr.EdgeScrollY ? Scr.EdgeScrollY : Scr.MyDisplayHeight;
const int vx = Scr.Vx;
const 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;
int x_bak;
int y_bak;
Window move_w = None;
int orig_icon_x = 0;
int orig_icon_y = 0;
Bool do_snap = True;
Bool was_snapped = False;
/* if Alt is initially pressed don't enable no-snap until Alt is
* released */
Bool nosnap_enabled = False;
/* Must not set placed by button if the event is a modified KeyEvent */
Bool is_fake_event;
FvwmWindow *fw = exc->w.fw;
unsigned int draw_parts = PART_NONE;
XEvent e;
if (!GrabEm(cursor, GRAB_NORMAL))
{
XBell(dpy, 0);
return False;
}
if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
{
do_move_opaque = False;
}
bad_window = None;
if (IS_ICONIFIED(fw))
{
if (FW_W_ICON_PIXMAP(fw) != None)
{
move_w = FW_W_ICON_PIXMAP(fw);
}
else if (FW_W_ICON_TITLE(fw) != None)
{
move_w = FW_W_ICON_TITLE(fw);
}
}
else
{
move_w = FW_W_FRAME(fw);
}
if (
!XGetGeometry(
dpy, move_w, &JunkRoot, &x_bak, &y_bak,
(unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
(unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
{
/* This is allright here since the window may not be mapped
* yet. */
}
if (IS_ICONIFIED(fw))
{
rectangle g;
get_visible_icon_geometry(fw, &g);
orig_icon_x = g.x;
orig_icon_y = g.y;
}
/* make a copy of the fw structure for sending to the pager */
memcpy(&fw_copy, fw, sizeof(FvwmWindow));
/* prevent flicker when paging */
SET_WINDOW_BEING_MOVED_OPAQUE(fw, do_move_opaque);
if (FQueryPointer(
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(fw) &&
((!do_move_opaque && !Scr.gs.do_emulate_mwm) || !IS_MAPPED(fw)))
{
draw_move_resize_grid(xl, yt, Width - 1, Height - 1);
}
if (move_w == FW_W_FRAME(fw) && do_move_opaque)
{
draw_parts = border_get_transparent_decorations_part(fw);
}
DisplayPosition(fw, exc->x.elast, xl, yt, True);
memset(&e, 0, sizeof(e));
/* Unset the placed by button mask.
* If the move is canceled this will remain as zero.
*/
fw->placed_by_button = 0;
while (!is_finished && bad_window != FW_W(fw))
{
int rc = 0;
int old_xl;
int old_yt;
old_xl = xl;
old_yt = yt;
/* wait until there is an interesting event */
while (rc != -1 &&
(!FPending(dpy) ||
!FCheckMaskEvent(
dpy, ButtonPressMask | ButtonReleaseMask |
KeyPressMask | PointerMotionMask |
ButtonMotionMask | ExposureMask, &e)))
{
XEvent le;
fev_get_last_event(&le);
rc = HandlePaging(
&le, 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;
}
xl += XOffset;
if (delta_y)
{
y_virtual_offset = 0;
}
yt += YOffset;
if (do_snap)
{
DoSnapAttract(
fw, Width, Height, &xl, &yt);
was_snapped = True;
}
fev_make_null_event(&e, dpy);
e.type = MotionNotify;
e.xmotion.time = fev_get_evtime();
e.xmotion.x_root = xl - XOffset;
e.xmotion.y_root = yt - YOffset;
e.xmotion.same_screen = True;
break;
}
}
if (rc == -1)
{
/* block until an event arrives */
/* dv (2004-07-01): With XFree 4.1.0.1, some Mouse
* events are not reported to fvwm when the pointer
* moves very fast and suddenly stops in the corner of
* the screen. Handle EnterNotify/LeaveNotify events
* too to get an idea where the pointer might be. */
FMaskEvent(
dpy, ButtonPressMask | ButtonReleaseMask |
KeyPressMask | PointerMotionMask |
ButtonMotionMask | ExposureMask |
EnterWindowMask | LeaveWindowMask, &e);
}
/* discard extra events before a logical release */
if (e.type == MotionNotify ||
e.type == EnterNotify || e.type == LeaveNotify)
{
while (FPending(dpy) > 0 &&
FCheckMaskEvent(
dpy, ButtonMotionMask |
PointerMotionMask | ButtonPressMask |
ButtonRelease | KeyPressMask |
EnterWindowMask | LeaveWindowMask, &e))
{
if (e.type == ButtonPress ||
e.type == ButtonRelease ||
e.type == KeyPress)
{
break;
}
}
}
if (e.type == EnterNotify || e.type == LeaveNotify)
{
XEvent e2;
int x;
int y;
/* Query the pointer to catch the latest information.
* This *is* necessary. */
if (FQueryPointer(
dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
&y, &JunkX, &JunkY, &JunkMask) == True)
{
fev_make_null_event(&e2, dpy);
e2.type = MotionNotify;
e2.xmotion.time = fev_get_evtime();
e2.xmotion.x_root = x;
e2.xmotion.y_root = y;
e2.xmotion.state = JunkMask;
e2.xmotion.same_screen = True;
e = e2;
fev_fake_event(&e);
}
else
{
/* pointer is on a different screen,
* ignore event */
}
}
is_fake_event = False;
/* Handle a limited number of key press events to allow
* mouseless operation */
if (e.type == KeyPress)
{
Keyboard_shortcuts(
&e, fw, &x_virtual_offset,
&y_virtual_offset, ButtonRelease);
is_fake_event = (e.type != KeyPress);
}
switch (e.type)
{
case KeyPress:
if (!(e.xkey.state & Mod1Mask))
{
nosnap_enabled = True;
}
do_snap = nosnap_enabled &&
(e.xkey.state & Mod1Mask) ? False : True;
/* simple code to bag out of move - CKH */
if (XLookupKeysym(&(e.xkey), 0) == XK_Escape)
{
if (!do_move_opaque)
{
switch_move_resize_grid(False);
}
if (!IS_ICONIFIED(fw))
{
if (do_move_opaque)
{
*FinalX = fw->g.frame.x;
*FinalY = fw->g.frame.y;
}
}
else
{
*FinalX = orig_icon_x;
*FinalY = orig_icon_y;
}
is_aborted = True;
is_finished = True;
}
break;
case ButtonPress:
if (e.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
((Button1Mask << (e.xbutton.button - 1)) &
button_mask))
{
/* No new button was pressed, just a delayed
* event */
break;
}
if (!IS_MAPPED(fw) &&
((e.xbutton.button == 2 && !Scr.gs.do_emulate_mwm)
||
(e.xbutton.button == 1 && Scr.gs.do_emulate_mwm &&
(e.xbutton.state & ShiftMask))))
{
do_resize_too = True;
/* Fallthrough to button-release */
}
else if (!button_mask && e.xbutton.button <=
NUMBER_OF_EXTENDED_MOUSE_BUTTONS &&
e.xbutton.button > 0 &&
(move_interactive_finish_button_mask &
(1<<(e.xbutton.button-1))))
{
do_resize_too = False;
break;
}
else if (button_mask && e.xbutton.button <=
NUMBER_OF_EXTENDED_MOUSE_BUTTONS &&
e.xbutton.button > 0 &&
(move_drag_finish_button_mask &
(1<<(e.xbutton.button-1))))
{
do_resize_too = False;
/* Fallthrough to button-release */
}
else
{
/* Abort the move if
* - the move started with a pressed button
* and another button was pressed during the
* operation
* - Any button not in the
* move_finish_button_mask is pressed
*/
/* if (button_mask) */
/* - button_mask will always be set here.
* only add an if if we want to be able to
* place windows dragged by other means
* than releasing the initial button.
*/
{
if (!do_move_opaque)
{
switch_move_resize_grid(False);
}
if (!IS_ICONIFIED(fw))
{
*FinalX = fw->g.frame.x;
*FinalY = fw->g.frame.y;
}
else
{
*FinalX = orig_icon_x;
*FinalY = orig_icon_y;
}
is_aborted = True;
is_finished = True;
}
break;
}
case ButtonRelease:
if (!is_fake_event)
{
fw->placed_by_button = e.xbutton.button;
}
if (!do_move_opaque)
{
switch_move_resize_grid(False);
}
xl2 = e.xbutton.x_root + XOffset + x_virtual_offset;
yt2 = e.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 || was_snapped)
{
/* only snap if the window actually moved! */
if (do_snap)
{
DoSnapAttract(
fw, Width, Height, &xl, &yt);
was_snapped = True;
}
}
*FinalX = xl;
*FinalY = yt;
is_finished = True;
break;
case MotionNotify:
if (e.xmotion.same_screen == False)
{
continue;
}
if (!(e.xmotion.state & Mod1Mask))
{
nosnap_enabled = True;
}
do_snap = nosnap_enabled &&
(e.xmotion.state & Mod1Mask) ? False : True;
xl = e.xmotion.x_root;
yt = e.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;
if (do_snap)
{
DoSnapAttract(fw, Width, Height, &xl, &yt);
was_snapped = True;
}
/* check Paging request once and only once after
* outline redrawn redraw after paging if needed
* - mab */
for (paged = 0; paged <= 1; paged++)
{
if (!do_move_opaque)
{
draw_move_resize_grid(
xl, yt, Width - 1, Height - 1);
}
else
{
if (IS_ICONIFIED(fw))
{
set_icon_position(fw, xl, yt);
move_icon_to_position(fw);
broadcast_icon_geometry(
fw, False);
}
else
{
XMoveWindow(
dpy, FW_W_FRAME(fw),
xl, yt);
}
}
DisplayPosition(fw, &e, xl, yt, False);
/* prevent window from lagging behind mouse
* when paging - mab */
if (paged == 0)
{
XEvent le;
xl = e.xmotion.x_root;
yt = e.xmotion.y_root;
fev_get_last_event(&le);
HandlePaging(
&le, dx, dy, &xl, &yt,
&delta_x, &delta_y, False,
False, False);
if (delta_x)
{
x_virtual_offset = 0;
}
xl += XOffset;
if (delta_y)
{
y_virtual_offset = 0;
}
yt += YOffset;
if (do_snap)
{
DoSnapAttract(
fw, Width, Height,
&xl, &yt);
was_snapped = True;
}
if (!delta_x && !delta_y)
{
/* break from while
* (paged <= 1) */
break;
}
}
}
break;
case Expose:
if (!do_move_opaque)
{
/* must undraw the rubber band in case the
* event causes some drawing */
switch_move_resize_grid(False);
}
dispatch_event(&e);
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(fw) &&
!IS_SHADED(fw) && !Scr.bo.do_disable_configure_notify)
{
/* 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(
fw, xl, yt, Width, Height, 0,
False);
#ifdef FVWM_DEBUG_MSGS
fvwm_msg(
DBG, "frame_setup_window",
"Sent ConfigureNotify (w %d, h %d)",
Width, Height);
#endif
}
}
if (do_move_opaque)
{
if (!IS_ICONIFIED(fw))
{
fw_copy.g.frame.x = xl;
fw_copy.g.frame.y = yt;
}
if (xl != old_xl || yt != old_yt)
{
/* only do this with opaque moves, (i.e. the
* server is not grabbed) */
if (draw_parts != PART_NONE)
{
border_draw_decorations(
fw, draw_parts,
((fw == get_focus_window())) ?
True : False,
True, CLEAR_ALL, NULL, NULL);
}
if (IS_TEAR_OFF_MENU(fw))
{
menu_redraw_transparent_tear_off_menu(
fw, False);
}
BroadcastConfig(M_CONFIGURE_WINDOW, &fw_copy);
FlushAllMessageQueues();
}
}
} /* while (!is_finished) */
if (!Scr.gs.do_hide_position_window)
{
XUnmapWindow(dpy,Scr.SizeWindow);
}
if (is_aborted || bad_window == FW_W(fw))
{
if (vx != Scr.Vx || vy != Scr.Vy)
{
MoveViewport(vx, vy, False);
}
if (is_aborted && do_move_opaque)
{
XMoveWindow(dpy, move_w, x_bak, y_bak);
if (draw_parts != PART_NONE)
{
border_draw_decorations(
fw, draw_parts,
((fw == get_focus_window())) ?
True : False,
True, CLEAR_ALL, NULL, NULL);
}
menu_redraw_transparent_tear_off_menu(fw, False);
}
if (bad_window == FW_W(fw))
{
XUnmapWindow(dpy, move_w);
border_undraw_decorations(fw);
XBell(dpy, 0);
}
}
if (!is_aborted && bad_window != FW_W(fw) && IS_ICONIFIED(fw))
{
SET_ICON_MOVED(fw, 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(fw, 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;
}
return;
}
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;
}
return;
}
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;
}
return;
}
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;
}
PictureUseDefaultVisual();
gcm = GCFunction|GCLineWidth|GCForeground|GCFillStyle|GCSubwindowMode;
gcv.subwindow_mode = IncludeInferiors;
gcv.function = GXxor;
gcv.line_width = 1;
/* 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):(PictureBlackPixel() ^ PictureWhitePixel());
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;
}
PictureUseFvwmVisual();
return;
}
void CMD_XorPixmap(F_CMD_ARGS)
{
char *PixmapName;
FvwmPicture *xp;
XGCValues gcv;
unsigned long gcm;
FvwmPictureAttributes fpa;
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 */
fpa.mask = FPAM_NO_COLOR_LIMIT | FPAM_NO_ALPHA;
PictureUseDefaultVisual();
xp = PGetFvwmPicture(dpy, Scr.Root, NULL, PixmapName, fpa);
if (xp == NULL)
{
fvwm_msg(ERR,"SetXORPixmap","Can't find pixmap %s", PixmapName);
free(PixmapName);
PictureUseFvwmVisual();
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 */
PDestroyFvwmPicture(dpy, xp);
PictureUseFvwmVisual();
/* 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);
}
return;
}
/* ----------------------------- resizing code ----------------------------- */
static void __resize_get_dir_from_window(
int *ret_xmotion, int *ret_ymotion, FvwmWindow *fw, Window context_w)
{
if (context_w != Scr.Root && context_w != None)
{
if (context_w == FW_W_SIDE(fw, 0)) /* top */
{
*ret_ymotion = 1;
}
else if (context_w == FW_W_SIDE(fw, 1)) /* right */
{
*ret_xmotion = -1;
}
else if (context_w == FW_W_SIDE(fw, 2)) /* bottom */
{
*ret_ymotion = -1;
}
else if (context_w == FW_W_SIDE(fw, 3)) /* left */
{
*ret_xmotion = 1;
}
else if (context_w == FW_W_CORNER(fw, 0)) /* upper-left */
{
*ret_xmotion = 1;
*ret_ymotion = 1;
}
else if (context_w == FW_W_CORNER(fw, 1)) /* upper-right */
{
*ret_xmotion = -1;
*ret_ymotion = 1;
}
else if (context_w == FW_W_CORNER(fw, 2)) /* lower left */
{
*ret_xmotion = 1;
*ret_ymotion = -1;
}
else if (context_w == FW_W_CORNER(fw, 3)) /* lower right */
{
*ret_xmotion = -1;
*ret_ymotion = -1;
}
}
return;
}
static void __resize_get_dir_proximity(
int *ret_xmotion, int *ret_ymotion, FvwmWindow *fw, int x_off,
int y_off, int px, int py)
{
int tx;
int ty;
if (px < 0 || x_off < 0 || py < 0 || y_off < 0)
{
return;
}
/* 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(fw->boundary_width, tx);
ty = max(fw->boundary_width, ty);
if (px < tx)
{
*ret_xmotion = 1;
}
else if (x_off < tx)
{
*ret_xmotion = -1;
}
if (py < ty)
{
*ret_ymotion = 1;
}
else if (y_off < ty)
{
*ret_ymotion = -1;
}
return;
}
static void __resize_get_refpos(
int *ret_x, int *ret_y, int xmotion, int ymotion, int w, int h,
FvwmWindow *fw)
{
if (xmotion > 0)
{
*ret_x = 0;
}
else if (xmotion < 0)
{
*ret_x = w - 1;
}
else
{
*ret_x = w / 2;
}
if (ymotion > 0)
{
*ret_y = 0;
}
else if (ymotion < 0)
{
*ret_y = h - 1;
}
else
{
*ret_y = h / 2;
}
return;
}
/* Procedure:
* __resize_step - 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
* x_off - x offset of pointer from border (input/output)
* y_off - y offset of pointer from border (input/output)
* drag - resize internal structure
* orig - resize internal structure
* xmotionp - pointer to xmotion in resize_window
* ymotionp - pointer to ymotion in resize_window
*
*/
static void __resize_step(
const exec_context_t *exc, int x_root, int y_root, int *x_off,
int *y_off, rectangle *drag, const rectangle *orig, int *xmotionp,
int *ymotionp, Bool do_resize_opaque, Bool is_direction_fixed)
{
int action = 0;
XEvent e;
int x2;
int y2;
int xdir;
int ydir;
x2 = x_root - *x_off;
x_root += *x_off;
if (is_direction_fixed == True && (*xmotionp != 0 || *ymotionp != 0))
{
xdir = *xmotionp;
}
else if (x2 <= orig->x ||
(*xmotionp == 1 && x2 < orig->x + orig->width - 1))
{
xdir = 1;
}
else if (x2 >= orig->x + orig->width - 1 ||
(*xmotionp == -1 && x2 > orig->x))
{
xdir = -1;
}
else
{
xdir = 0;
}
switch (xdir)
{
case 1:
if (*xmotionp != 1)
{
*x_off = -*x_off;
x_root = x2;
*xmotionp = 1;
}
drag->x = x_root;
drag->width = orig->x + orig->width - x_root;
action = 1;
break;
case -1:
if (*xmotionp != -1)
{
*x_off = -*x_off;
x_root = x2;
*xmotionp = -1;
}
drag->x = orig->x;
drag->width = 1 + x_root - drag->x;
action = 1;
break;
default:
break;
}
y2 = y_root - *y_off;
y_root += *y_off;
if (is_direction_fixed == True && (*xmotionp != 0 || *ymotionp != 0))
{
ydir = *ymotionp;
}
else if (y2 <= orig->y ||
(*ymotionp == 1 && y2 < orig->y + orig->height - 1))
{
ydir = 1;
}
else if (y2 >= orig->y + orig->height - 1 ||
(*ymotionp == -1 && y2 > orig->y))
{
ydir = -1;
}
else
{
ydir = 0;
}
switch (ydir)
{
case 1:
if (*ymotionp != 1)
{
*y_off = -*y_off;
y_root = y2;
*ymotionp = 1;
}
drag->y = y_root;
drag->height = orig->y + orig->height - y_root;
action = 1;
break;
case -1:
if (*ymotionp != -1)
{
*y_off = -*y_off;
y_root = y2;
*ymotionp = -1;
}
drag->y = orig->y;
drag->height = 1 + y_root - drag->y;
action = 1;
break;
default:
break;
}
if (action)
{
/* round up to nearest OK size to keep pointer inside
* rubberband */
constrain_size(
exc->w.fw, exc->x.elast, &drag->width, &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
{
frame_setup_window(
exc->w.fw, 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(exc->w.fw, &e, drag->width, drag->height, False, False);
return;
}
/* Starts a window resize operation */
static Bool __resize_window(F_CMD_ARGS)
{
extern Window bad_window;
FvwmWindow *fw = exc->w.fw;
Bool is_finished = False, is_done = False, is_aborted = False;
Bool do_send_cn = False;
Bool do_resize_opaque;
Bool do_warp_to_border;
Bool is_direction_fixed;
Bool fButtonAbort = False;
Bool fForceRedraw = False;
Bool called_from_title = False;
int x,y,delta_x,delta_y,stashed_x,stashed_y;
Window ResizeWindow;
int dx = Scr.EdgeScrollX ? Scr.EdgeScrollX : Scr.MyDisplayWidth;
int dy = Scr.EdgeScrollY ? Scr.EdgeScrollY : Scr.MyDisplayHeight;
const int vx = Scr.Vx;
const int vy = Scr.Vy;
int n;
unsigned int button_mask = 0;
rectangle sdrag;
rectangle sorig;
rectangle *drag = &sdrag;
const rectangle *orig = &sorig;
const window_g g_backup = fw->g;
int ymotion = 0;
int xmotion = 0;
int was_maximized;
unsigned edge_wrap_x;
unsigned edge_wrap_y;
int px;
int py;
int i;
size_borders b;
frame_move_resize_args mr_args = NULL;
long evmask;
XEvent ev;
int ref_x;
int ref_y;
int x_off;
int y_off;
direction_t dir;
bad_window = False;
ResizeWindow = FW_W_FRAME(fw);
if (fev_get_evpos_or_query(dpy, Scr.Root, exc->x.etrigger, &px, &py) ==
False ||
XTranslateCoordinates(
dpy, Scr.Root, ResizeWindow, px, py, &px, &py,
&JunkChild) == False)
{
/* pointer is on a different screen - that's okay here */
px = 0;
py = 0;
}
button_mask &= DEFAULT_ALL_BUTTONS_MASK;
if (!is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, True))
{
XBell(dpy, 0);
return False;
}
if (IS_SHADED(fw) || !IS_MAPPED(fw))
{
do_resize_opaque = False;
evmask = XEVMASK_RESIZE;
}
else
{
do_resize_opaque = DO_RESIZE_OPAQUE(fw);
evmask = XEVMASK_RESIZE_OPAQUE;
}
/* no suffix = % of screen, 'p' = pixels, 'c' = increment units */
if (IS_SHADED(fw))
{
get_unshaded_geometry(fw, drag);
}
else
{
drag->width = fw->g.frame.width;
drag->height = fw->g.frame.height;
}
get_window_borders(fw, &b);
n = GetResizeArguments(
&action, fw->g.frame.x, fw->g.frame.y,
fw->hints.base_width, fw->hints.base_height,
fw->hints.width_inc, fw->hints.height_inc,
&b, &(drag->width), &(drag->height),
&dir, &is_direction_fixed, &do_warp_to_border);
if (n == 2)
{
rectangle new_g;
/* size will be less or equal to requested */
if (IS_SHADED(fw))
{
rectangle shaded_g;
get_unshaded_geometry(fw, &new_g);
SET_MAXIMIZED(fw, 0);
constrain_size(
fw, NULL, &drag->width, &drag->height, xmotion,
ymotion, 0);
gravity_resize(
fw->hints.win_gravity, &new_g,
drag->width - new_g.width,
drag->height - new_g.height);
fw->g.normal = new_g;
get_shaded_geometry(fw, &shaded_g, &new_g);
frame_setup_window(
fw, shaded_g.x, shaded_g.y, shaded_g.width,
shaded_g.height, False);
}
else
{
new_g = fw->g.frame;
SET_MAXIMIZED(fw, 0);
constrain_size(
fw, NULL, &drag->width, &drag->height, xmotion,
ymotion, 0);
gravity_resize(
fw->hints.win_gravity, &new_g,
drag->width - new_g.width,
drag->height - new_g.height);
frame_setup_window(
fw, new_g.x, new_g.y, drag->width,
drag->height, False);
}
update_absolute_geometry(fw);
maximize_adjust_offset(fw);
GNOME_SetWinArea(fw);
ResizeWindow = None;
return True;
}
was_maximized = IS_MAXIMIZED(fw);
SET_MAXIMIZED(fw, 0);
if (was_maximized)
{
/* must redraw the buttons now so that the 'maximize' button
* does not stay depressed. */
border_draw_decorations(
fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
NULL, NULL);
}
if (Scr.bo.do_install_root_cmap)
{
InstallRootColormap();
}
else
{
InstallFvwmColormap();
}
if (!GrabEm(CRS_RESIZE, GRAB_NORMAL))
{
XBell(dpy, 0);
return False;
}
/* handle problems with edge-wrapping while resizing */
edge_wrap_x = Scr.flags.do_edge_wrap_x;
edge_wrap_y = Scr.flags.do_edge_wrap_y;
Scr.flags.do_edge_wrap_x = 0;
Scr.flags.do_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,
(unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
{
UngrabEm(GRAB_NORMAL);
if (!do_resize_opaque)
{
MyXUngrabServer(dpy);
}
return False;
}
if (IS_SHADED(fw))
{
SET_MAXIMIZED(fw, was_maximized);
get_unshaded_geometry(fw, drag);
SET_MAXIMIZED(fw, 0);
}
if (do_resize_opaque)
{
mr_args = frame_create_move_resize_args(
fw, FRAME_MR_OPAQUE, &fw->g.frame, &fw->g.frame, 0,
DIR_NONE);
}
else
{
Scr.flags.is_wire_frame_displayed = True;
}
MyXGrabKeyboard(dpy);
sorig = *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(fw, exc->x.elast, orig->width, orig->height, True, True);
if (dir != DIR_NONE)
{
int grav;
grav = gravity_dir_to_grav(dir);
gravity_get_offsets(grav, &xmotion, &ymotion);
xmotion = -xmotion;
ymotion = -ymotion;
}
if (xmotion == 0 && ymotion == 0)
{
__resize_get_dir_from_window(&xmotion, &ymotion, fw, PressedW);
}
if (FW_W_TITLE(fw) != None && PressedW == FW_W_TITLE(fw))
{
/* title was pressed to start the resize */
called_from_title = True;
}
else
{
for (i = NUMBER_OF_TITLE_BUTTONS; i--; )
{
/* see if the title button was pressed to that the
* resize */
if (FW_W_BUTTON(fw, i) != None &&
FW_W_BUTTON(fw, i) == PressedW)
{
/* 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)
{
__resize_get_dir_proximity(
&xmotion, &ymotion, fw, orig->width - px,
orig->height - py, px, py);
if (xmotion != 0 || ymotion != 0)
{
do_warp_to_border = True;
}
}
if (!IS_SHADED(fw))
{
__resize_get_refpos(
&ref_x, &ref_y, xmotion, ymotion, orig->width,
orig->height, fw);
}
else
{
switch (SHADED_DIR(fw))
{
case DIR_N:
case DIR_NW:
case DIR_NE:
if (ymotion == -1)
{
ymotion = 0;
}
break;
case DIR_S:
case DIR_SW:
case DIR_SE:
if (ymotion == 1)
{
ymotion = 0;
}
break;
default:
break;
}
switch (SHADED_DIR(fw))
{
case DIR_E:
case DIR_NE:
case DIR_SE:
if (xmotion == 1)
{
xmotion = 0;
}
break;
case DIR_W:
case DIR_NW:
case DIR_SW:
if (xmotion == -1)
{
xmotion = 0;
}
break;
default:
break;
}
__resize_get_refpos(
&ref_x, &ref_y, xmotion, ymotion, fw->g.frame.width,
fw->g.frame.height, fw);
}
x_off = 0;
y_off = 0;
if (do_warp_to_border == True)
{
int dx;
int dy;
dx = (xmotion == 0) ? px : ref_x;
dy = (ymotion == 0) ? py : ref_y;
/* warp the pointer to the border */
FWarpPointer(
dpy, None, ResizeWindow, 0, 0, 1, 1, dx, dy);
XFlush(dpy);
}
else if (xmotion != 0 || ymotion != 0)
{
/* keep the distance between pointer and border */
x_off = (xmotion == 0) ? 0 : ref_x - px;
y_off = (ymotion == 0) ? 0 : ref_y - py;
}
else
{
/* wait until the pointer hits a border before making a
* decision about the resize direction */
}
/* 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 (exc->x.elast->type == KeyPress)
{
int xo;
int yo;
if (FQueryPointer(
dpy, Scr.Root, &JunkRoot, &JunkChild, &stashed_x,
&stashed_y, &JunkX, &JunkY, &JunkMask) == False)
{
/* pointer is on a different screen */
stashed_x = 0;
stashed_y = 0;
}
xo = 0;
yo = 0;
__resize_step(
exc, stashed_x, stashed_y, &xo, &yo, drag, orig,
&xmotion, &ymotion, do_resize_opaque, True);
}
else
{
stashed_x = stashed_y = -1;
}
/* loop to resize */
memset(&ev, 0, sizeof(ev));
while (!is_finished && bad_window != FW_W(fw))
{
int rc = 0;
/* block until there is an interesting event */
while (rc != -1 &&
(!FPending(dpy) || !FCheckMaskEvent(dpy, evmask, &ev)))
{
rc = HandlePaging(
&ev, dx, dy, &x, &y, &delta_x, &delta_y, False,
False, True);
if (rc == 1)
{
/* Fake an event to force window reposition */
ev.type = MotionNotify;
ev.xmotion.time = fev_get_evtime();
fForceRedraw = True;
break;
}
}
if (rc == -1)
{
FMaskEvent(
dpy,
evmask | EnterWindowMask | LeaveWindowMask,
&ev);
}
if (ev.type == MotionNotify ||
ev.type == EnterNotify || ev.type == LeaveNotify)
{
Bool is_motion;
is_motion = (ev.type == MotionNotify) ? True : False;
/* discard any extra motion events before a release */
/* dv (2004-07-01): With XFree 4.1.0.1, some Mouse
* events are not reported to fvwm when the pointer
* moves very fast and suddenly stops in the corner of
* the screen. Handle EnterNotify/LeaveNotify events
* too to get an idea where the pointer might be. */
while (
FCheckMaskEvent(
dpy, ButtonMotionMask |
PointerMotionMask | ButtonReleaseMask |
ButtonPressMask | EnterWindowMask |
LeaveWindowMask, &ev) == True)
{
if (ev.type == ButtonRelease ||
ev.type == ButtonPress ||
ev.type == KeyPress)
{
break;
}
}
}
if (ev.type == EnterNotify || ev.type == LeaveNotify)
{
XEvent e2;
int x;
int y;
/* Query the pointer to catch the latest information.
* This *is* necessary. */
if (FQueryPointer(
dpy, Scr.Root, &JunkRoot, &JunkChild, &x,
&y, &JunkX, &JunkY, &JunkMask) == True)
{
/* Must NOT use button_mask here, or resize
* will not work with num lock */
fev_make_null_event(&e2, dpy);
e2.type = MotionNotify;
e2.xmotion.time = fev_get_evtime();
e2.xmotion.x_root = x;
e2.xmotion.y_root = y;
e2.xmotion.state = JunkMask;
e2.xmotion.same_screen = True;
ev = e2;
fev_fake_event(&ev);
}
else
{
/* pointer is on a different screen,
* ignore event */
}
}
is_done = False;
/* Handle a limited number of key press events to allow
* mouseless operation */
if (ev.type == KeyPress)
{
Keyboard_shortcuts(&ev, fw, NULL, NULL, ButtonRelease);
if (ev.type == ButtonRelease)
{
do_send_cn = True;
}
}
switch (ev.type)
{
case ButtonPress:
is_done = True;
if (ev.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
((Button1Mask << (ev.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 || (ev.xbutton.button != 1))
{
fButtonAbort = True;
/* fall through */
}
else
{
is_finished = True;
do_send_cn = True;
break;
}
case KeyPress:
/* simple code to bag out of move - CKH */
if (fButtonAbort ||
XLookupKeysym(&ev.xkey, 0) == XK_Escape)
{
is_aborted = True;
do_send_cn = True;
is_finished = True;
}
is_done = True;
break;
case ButtonRelease:
is_finished = True;
is_done = True;
break;
case MotionNotify:
if (ev.xmotion.same_screen == False)
{
continue;
}
if (!fForceRedraw)
{
x = ev.xmotion.x_root;
y = ev.xmotion.y_root;
/* resize before paging request to prevent
* resize from lagging * mouse - mab */
__resize_step(
exc, x, y, &x_off, &y_off, drag, orig,
&xmotion, &ymotion, do_resize_opaque,
is_direction_fixed);
/* need to move the viewport */
HandlePaging(
&ev, dx, dy, &x, &y, &delta_x,
&delta_y, False, False, False);
}
/* redraw outline if we paged - mab */
if (delta_x != 0 || delta_y != 0)
{
sorig.x -= delta_x;
sorig.y -= delta_y;
drag->x -= delta_x;
drag->y -= delta_y;
__resize_step(
exc, x, y, &x_off, &y_off, drag, orig,
&xmotion, &ymotion, do_resize_opaque,
is_direction_fixed);
}
fForceRedraw = False;
is_done = True;
break;
case PropertyNotify:
{
evh_args_t ea;
exec_context_changes_t ecc;
ecc.x.etrigger = &ev;
ea.exc = exc_clone_context(exc, &ecc, ECC_ETRIGGER);
HandlePropertyNotify(&ea);
exc_destroy_context(ea.exc);
is_done = True;
break;
}
default:
break;
}
if (!is_done)
{
if (!do_resize_opaque)
{
/* must undraw the rubber band in case the
* event causes some drawing */
switch_move_resize_grid(False);
}
dispatch_event(&ev);
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, fw);
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 (is_aborted || bad_window == FW_W(fw))
{
/* return pointer if aborted resize was invoked with key */
if (stashed_x >= 0)
{
FWarpPointer(
dpy, None, Scr.Root, 0, 0, 0, 0, stashed_x,
stashed_y);
}
if (was_maximized)
{
/* since we aborted the resize, the window is still
* maximized */
SET_MAXIMIZED(fw, 1);
}
if (do_resize_opaque)
{
int xo;
int yo;
rectangle g;
xo = 0;
yo = 0;
xmotion = 1;
ymotion = 1;
g = sorig;
__resize_step(
exc, sorig.x, sorig.y, &xo, &yo, &g, orig,
&xmotion, &ymotion, do_resize_opaque, True);
}
if (vx != Scr.Vx || vy != Scr.Vy)
{
MoveViewport(vx, vy, False);
}
/* restore all geometry-related info */
fw->g = g_backup;
if (bad_window == FW_W(fw))
{
XUnmapWindow(dpy, FW_W_FRAME(fw));
border_undraw_decorations(fw);
XBell(dpy, 0);
}
}
else if (!is_aborted && bad_window != FW_W(fw))
{
rectangle new_g;
/* size will be >= to requested */
constrain_size(
fw, exc->x.elast, &drag->width, &drag->height,
xmotion, ymotion, CS_ROUND_UP);
if (IS_SHADED(fw))
{
get_shaded_geometry(fw, &new_g, drag);
}
else
{
new_g = *drag;
}
if (do_resize_opaque)
{
frame_update_move_resize_args(mr_args, &new_g);
}
else
{
frame_setup_window(
fw, new_g.x, new_g.y, new_g.width,
new_g.height, False);
}
if (IS_SHADED(fw))
{
fw->g.normal.width = drag->width;
fw->g.normal.height = drag->height;
}
}
if (is_aborted && was_maximized)
{
/* force redraw */
border_draw_decorations(
fw, PART_BUTTONS, (fw == Scr.Hilite), True, CLEAR_ALL,
NULL, NULL);
}
if (Scr.bo.do_install_root_cmap)
{
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);
}
if (mr_args != NULL)
{
frame_free_move_resize_args(fw, mr_args);
}
if (do_send_cn == True)
{
rectangle g;
if (is_aborted)
{
g = sorig;
}
else
{
g = *drag;
}
SendConfigureNotify(fw, g.x, g.y, g.width, g.height, 0, True);
}
MyXUngrabKeyboard(dpy);
WaitForButtonsUp(True);
UngrabEm(GRAB_NORMAL);
Scr.flags.do_edge_wrap_x = edge_wrap_x;
Scr.flags.do_edge_wrap_y = edge_wrap_y;
update_absolute_geometry(fw);
maximize_adjust_offset(fw);
GNOME_SetWinArea(fw);
if (is_aborted)
{
return False;
}
return True;
}
void CMD_Resize(F_CMD_ARGS)
{
FvwmWindow *fw = exc->w.fw;
if (IS_EWMH_FULLSCREEN(fw))
{
/* do not unmaximize ! */
CMD_ResizeMaximize(F_PASS_ARGS);
return;
}
__resize_window(F_PASS_ARGS);
return;
}
/* ----------------------------- maximizing code --------------------------- */
Bool is_window_sticky_across_pages(FvwmWindow *fw)
{
if (IS_STICKY_ACROSS_PAGES(fw) ||
(IS_ICONIFIED(fw) && IS_ICON_STICKY_ACROSS_PAGES(fw)))
{
return True;
}
else
{
return False;
}
}
Bool is_window_sticky_across_desks(FvwmWindow *fw)
{
if (IS_STICKY_ACROSS_DESKS(fw) ||
(IS_ICONIFIED(fw) && IS_ICON_STICKY_ACROSS_DESKS(fw)))
{
return True;
}
else
{
return False;
}
}
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;
}
}
return;
}
static void MaximizeHeight(
FvwmWindow *win, int win_width, int win_x, int *win_height,
int *win_y, Bool grow_up, Bool grow_down, int top_border,
int bottom_border, int *layers)
{
FvwmWindow *cwin;
int x11, x12, x21, x22;
int y11, y12, y21, y22;
int new_y1, new_y2;
rectangle g;
Bool rc;
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 (cwin == win ||
(cwin->Desk != win->Desk &&
!is_window_sticky_across_desks(cwin)))
{
continue;
}
if ((layers[0] >= 0 && cwin->layer < layers[0]) ||
(layers[1] >= 0 && cwin->layer > layers[1]))
{
continue;
}
rc = get_visible_window_or_icon_geometry(cwin, &g);
if (rc == False)
{
continue;
}
x21 = g.x;
y21 = g.y;
x22 = x21 + g.width;
y22 = y21 + g.height;
if (is_window_sticky_across_pages(cwin))
{
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;
return;
}
static void MaximizeWidth(
FvwmWindow *win, int *win_width, int *win_x, int win_height,
int win_y, Bool grow_left, Bool grow_right, int left_border,
int right_border, int *layers)
{
FvwmWindow *cwin;
int x11, x12, x21, x22;
int y11, y12, y21, y22;
int new_x1, new_x2;
rectangle g;
Bool rc;
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 (cwin == win ||
(cwin->Desk != win->Desk &&
!is_window_sticky_across_desks(cwin)))
{
continue;
}
if ((layers[0] >= 0 && cwin->layer < layers[0]) ||
(layers[1] >= 0 && cwin->layer > layers[1]))
{
continue;
}
rc = get_visible_window_or_icon_geometry(cwin, &g);
if (rc == False)
{
continue;
}
x21 = g.x;
y21 = g.y;
x22 = x21 + g.width;
y22 = y21 + g.height;
if (is_window_sticky_across_pages(cwin))
{
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;
return;
}
static void unmaximize_fvwm_window(
FvwmWindow *fw)
{
rectangle new_g;
SET_MAXIMIZED(fw, 0);
get_relative_geometry(&new_g, &fw->g.normal);
if (IS_SHADED(fw))
{
get_shaded_geometry(fw, &new_g, &new_g);
}
frame_setup_window(
fw, new_g.x, new_g.y, new_g.width, new_g.height, True);
border_draw_decorations(
fw, PART_ALL, (Scr.Hilite == fw), True, CLEAR_ALL, NULL, NULL);
if (IS_EWMH_FULLSCREEN(fw))
{
SET_EWMH_FULLSCREEN(fw, False);
if (DO_EWMH_USE_STACKING_HINTS(fw))
{
new_layer(fw, fw->ewmh_normal_layer);
}
apply_decor_change(fw);
}
return;
}
static void maximize_fvwm_window(
FvwmWindow *fw, rectangle *geometry)
{
SET_MAXIMIZED(fw, 1);
fw->g.max_defect.width = 0;
fw->g.max_defect.height = 0;
constrain_size(
fw, NULL, &geometry->width, &geometry->height, 0, 0,
CS_UPDATE_MAX_DEFECT);
fw->g.max = *geometry;
if (IS_SHADED(fw))
{
get_shaded_geometry(fw, geometry, &fw->g.max);
}
frame_setup_window(
fw, geometry->x, geometry->y, geometry->width,
geometry->height, True);
border_draw_decorations(
fw, PART_ALL, (Scr.Hilite == fw), True, CLEAR_ALL, NULL, NULL);
update_absolute_geometry(fw);
/* remember the offset between old and new position in case the
* maximized window is moved more than the screen width/height. */
fw->g.max_offset.x = fw->g.normal.x - fw->g.max.x;
fw->g.max_offset.y = fw->g.normal.y - fw->g.max.y;
#if 0
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);
#endif
return;
}
/*
*
* 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;
Bool ignore_working_area = False;
int layers[2] = { -1, -1 };
Bool global_flag_parsed = False;
int scr_x, scr_y;
int scr_w, scr_h;
rectangle new_g;
FvwmWindow *fw = exc->w.fw;
if (
!is_function_allowed(
F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
{
XBell(dpy, 0);
return;
}
/* Check for "global" flag ("absolute" is for compatibility with E) */
while (!global_flag_parsed)
{
token = PeekToken(action, &taction);
if (!token)
{
global_flag_parsed = True;
}
else
{
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);
}
else if (StrEquals(token, "ewmhiwa"))
{
ignore_working_area = True;
action = taction;
}
else if (StrEquals(token, "growonwindowlayer"))
{
layers[0] = fw->layer;
layers[1] = fw->layer;
action = taction;
}
else if (StrEquals(token, "growonlayers"))
{
int n;
n = GetIntegerArguments(
taction, &action, layers, 2);
if (n != 2)
{
layers[0] = -1;
layers[1] = -1;
}
}
else
{
global_flag_parsed = True;
}
}
}
toggle = ParseToggleArgument(action, &action, -1, 0);
if (toggle == 0 && !IS_MAXIMIZED(fw))
{
return;
}
if (toggle == 1 && IS_MAXIMIZED(fw))
{
/* Fake that the window is not maximized. */
do_force_maximize = True;
}
/* find the new page and geometry */
new_g.x = fw->g.frame.x;
new_g.y = fw->g.frame.y;
new_g.width = fw->g.frame.width;
new_g.height = fw->g.frame.height;
get_page_offset_check_visible(&page_x, &page_y, fw);
/* Check if we should constrain rectangle to some Xinerama screen */
if (!is_screen_given)
{
fscreen_scr_arg fscr;
fscr.xypos.x = fw->g.frame.x + fw->g.frame.width / 2 - page_x;
fscr.xypos.y = fw->g.frame.y + fw->g.frame.height / 2 - page_y;
FScreenGetScrRect(
&fscr, FSCREEN_XYPOS, &scr_x, &scr_y, &scr_w, &scr_h);
}
if (!ignore_working_area)
{
EWMH_GetWorkAreaIntersection(
fw, &scr_x, &scr_y, &scr_w, &scr_h,
EWMH_MAXIMIZE_MODE(fw));
}
#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 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
if (IS_MAXIMIZED(fw) && !do_force_maximize)
{
unmaximize_fvwm_window(fw);
}
else /* maximize */
{
/* handle command line arguments */
if (grow_up || grow_down)
{
MaximizeHeight(
fw, new_g.width, new_g.x, &new_g.height,
&new_g.y, grow_up, grow_down, page_y + scr_y,
page_y + scr_y + scr_h, layers);
}
else if (val2 > 0)
{
new_g.height = val2 * val2_unit / 100;
new_g.y = page_y + scr_y;
}
if (grow_left || grow_right)
{
MaximizeWidth(
fw, &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,
layers);
}
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 */
maximize_fvwm_window(fw, &new_g);
}
EWMH_SetWMState(fw, False);
GNOME_SetWinArea(fw);
return;
}
/*
*
* Same as CMD_Resize and CMD_ResizeMove, but the window ends up maximized
* without touching the normal geometry.
*
*/
void CMD_ResizeMaximize(F_CMD_ARGS)
{
rectangle normal_g;
rectangle max_g;
Bool was_resized;
FvwmWindow *fw = exc->w.fw;
/* keep a copy of the old geometry */
normal_g = fw->g.normal;
/* resize the window normally */
was_resized = __resize_window(F_PASS_ARGS);
if (was_resized == True)
{
/* set the new geometry as the maximized geometry and restore
* the old normal geometry */
max_g = fw->g.normal;
max_g.x -= Scr.Vx;
max_g.y -= Scr.Vy;
fw->g.normal = normal_g;
/* and mark it as maximized */
maximize_fvwm_window(fw, &max_g);
}
EWMH_SetWMState(fw, False);
return;
}
void CMD_ResizeMoveMaximize(F_CMD_ARGS)
{
rectangle normal_g;
rectangle max_g;
Bool was_resized;
FvwmWindow *fw = exc->w.fw;
/* keep a copy of the old geometry */
normal_g = fw->g.normal;
/* resize the window normally */
was_resized = resize_move_window(F_PASS_ARGS);
if (was_resized == True)
{
/* set the new geometry as the maximized geometry and restore
* the old normal geometry */
max_g = fw->g.normal;
max_g.x -= Scr.Vx;
max_g.y -= Scr.Vy;
fw->g.normal = normal_g;
/* and mark it as maximized */
maximize_fvwm_window(fw, &max_g);
}
EWMH_SetWMState(fw, False);
return;
}
/* ----------------------------- stick code -------------------------------- */
int stick_across_pages(F_CMD_ARGS, int toggle)
{
FvwmWindow *fw = exc->w.fw;
if ((toggle == 1 && IS_STICKY_ACROSS_PAGES(fw)) ||
(toggle == 0 && !IS_STICKY_ACROSS_PAGES(fw)))
{
return 0;
}
if (IS_STICKY_ACROSS_PAGES(fw))
{
SET_STICKY_ACROSS_PAGES(fw, 0);
}
else
{
if (!IsRectangleOnThisPage(&fw->g.frame, Scr.CurrentDesk))
{
action = "";
__move_window(F_PASS_ARGS, False, MOVE_PAGE);
}
SET_STICKY_ACROSS_PAGES(fw, 1);
}
return 1;
}
int stick_across_desks(F_CMD_ARGS, int toggle)
{
FvwmWindow *fw = exc->w.fw;
if ((toggle == 1 && IS_STICKY_ACROSS_DESKS(fw)) ||
(toggle == 0 && !IS_STICKY_ACROSS_DESKS(fw)))
{
return 0;
}
if (IS_STICKY_ACROSS_DESKS(fw))
{
SET_STICKY_ACROSS_DESKS(fw, 0);
fw->Desk = Scr.CurrentDesk;
GNOME_SetDeskCount();
GNOME_SetDesk(fw);
}
else
{
if (fw->Desk != Scr.CurrentDesk)
{
do_move_window_to_desk(fw, Scr.CurrentDesk);
}
SET_STICKY_ACROSS_DESKS(fw, 1);
}
return 1;
}
static void __handle_stick_exit(
FvwmWindow *fw, int do_not_draw, int do_silently)
{
if (do_not_draw == 0)
{
border_draw_decorations(
fw, PART_TITLE | PART_BUTTONS, (Scr.Hilite==fw), True,
CLEAR_ALL, NULL, NULL);
}
if (!do_silently)
{
BroadcastConfig(M_CONFIGURE_WINDOW,fw);
EWMH_SetWMState(fw, False);
EWMH_SetWMDesktop(fw);
GNOME_SetHints(fw);
}
return;
}
void handle_stick_across_pages(
F_CMD_ARGS, int toggle, int do_not_draw, int do_silently)
{
FvwmWindow *fw = exc->w.fw;
int did_change;
did_change = stick_across_pages(F_PASS_ARGS, toggle);
if (did_change)
{
__handle_stick_exit(fw, do_not_draw, do_silently);
}
return;
}
void handle_stick_across_desks(
F_CMD_ARGS, int toggle, int do_not_draw, int do_silently)
{
FvwmWindow *fw = exc->w.fw;
int did_change;
did_change = stick_across_desks(F_PASS_ARGS, toggle);
if (did_change)
{
__handle_stick_exit(fw, do_not_draw, do_silently);
}
return;
}
void handle_stick(
F_CMD_ARGS, int toggle_page, int toggle_desk, int do_not_draw,
int do_silently)
{
FvwmWindow *fw = exc->w.fw;
int did_change;
did_change = 0;
did_change |= stick_across_desks(F_PASS_ARGS, toggle_desk);
did_change |= stick_across_pages(F_PASS_ARGS, toggle_page);
if (did_change)
{
__handle_stick_exit(fw, do_not_draw, do_silently);
}
return;
}
void CMD_Stick(F_CMD_ARGS)
{
int toggle;
toggle = ParseToggleArgument(action, &action, -1, 0);
if (toggle == -1 && IS_STICKY_ACROSS_DESKS(exc->w.fw) !=
IS_STICKY_ACROSS_PAGES(exc->w.fw))
{
/* don't switch between only stickypage and only stickydesk.
* rather switch it off completely */
toggle = 0;
}
handle_stick(F_PASS_ARGS, toggle, toggle, 0, 0);
return;
}
void CMD_StickAcrossPages(F_CMD_ARGS)
{
int toggle;
toggle = ParseToggleArgument(action, &action, -1, 0);
handle_stick_across_pages(F_PASS_ARGS, toggle, 0, 0);
return;
}
void CMD_StickAcrossDesks(F_CMD_ARGS)
{
int toggle;
toggle = ParseToggleArgument(action, &action, -1, 0);
handle_stick_across_desks(F_PASS_ARGS, toggle, 0, 0);
return;
}
syntax highlighted by Code2HTML, v. 0.9.1