All pastes #444811 Raw Edit

Unnamed

public text v1 · immutable
·published 2007-04-17 15:06 UTC
rendered paste body
/*
 * Copyright  2005 Novell, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of
 * Novell, Inc. not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior permission.
 * Novell, Inc. makes no representations about the suitability of this
 * software for any purpose. It is provided "as is" without express or
 * implied warranty.
 *
 * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <X11/cursorfont.h>

#include <compiz.h>

#define ResizeUpMask    (1L << 0)
#define ResizeDownMask  (1L << 1)
#define ResizeLeftMask  (1L << 2)
#define ResizeRightMask (1L << 3)

#define RESIZE_INITIATE_BUTTON_DEFAULT    Button2
#define RESIZE_INITIATE_BUTTON_MODIFIERS_DEFAULT CompAltMask

#define RESIZE_INITIATE_KEY_DEFAULT           "F8"
#define RESIZE_INITIATE_KEY_MODIFIERS_DEFAULT CompAltMask

#define RESIZE_BORDER_COLOR_RED   0x2fff
#define RESIZE_BORDER_COLOR_GREEN 0x2fff
#define RESIZE_BORDER_COLOR_BLUE  0x4fff
#define RESIZE_BORDER_COLOR_ALPHA 0x9fff

#define RESIZE_FILL_COLOR_RED   0x2fff
#define RESIZE_FILL_COLOR_GREEN 0x2fff
#define RESIZE_FILL_COLOR_BLUE  0x4fff
#define RESIZE_FILL_COLOR_ALPHA 0x4fff

struct _ResizeKeys {
    char	 *name;
    int		 dx;
    int		 dy;
    unsigned int warpMask;
    unsigned int resizeMask;
} rKeys[] = {
    { "Left",  -1,  0, ResizeLeftMask | ResizeRightMask, ResizeLeftMask },
    { "Right",  1,  0, ResizeLeftMask | ResizeRightMask, ResizeRightMask },
    { "Up",     0, -1, ResizeUpMask | ResizeDownMask,    ResizeUpMask },
    { "Down",   0,  1, ResizeUpMask | ResizeDownMask,    ResizeDownMask }
};

#define NUM_KEYS (sizeof (rKeys) / sizeof (rKeys[0]))

#define MIN_KEY_WIDTH_INC  24
#define MIN_KEY_HEIGHT_INC 24

#define RESIZE_DISPLAY_OPTION_INITIATE     0
#define RESIZE_DISPLAY_OPTION_MODE         1
#define RESIZE_DISPLAY_OPTION_BORDER_COLOR 2
#define RESIZE_DISPLAY_OPTION_FILL_COLOR   3
#define RESIZE_DISPLAY_OPTION_NUM          4

typedef enum _ResizeMode {
    ResizeModeNormal = 0,
    ResizeModeStretch,
    ResizeModeOutline,
    ResizeModeFilledOutline
} ResizeMode;

char * resizeModes[] = {
    N_("Normal"),
    N_("Stretch"),
    N_("Outline"),
    N_("Filled Outline")
};

#define RESIZE_MODE_DEFAULT ResizeModeNormal
#define NUM_RESIZE_MODES    (sizeof (resizeModes) / sizeof (resizeModes[0]))

static int displayPrivateIndex;

typedef struct _ResizeDisplay {
    CompOption opt[RESIZE_DISPLAY_OPTION_NUM];

    int		    screenPrivateIndex;
    HandleEventProc handleEvent;

    CompWindow	      *w;
    XWindowAttributes savedAttrib;
    int		      releaseButton;
    unsigned int      mask;

    int               currentX;
    int               currentY;
    int               currentWidth;
    int               currentHeight;

    /* offsets between pointer and upper left 
       window edge on resize start */
    int               xDelta;
    int               yDelta;

    int               rightEdge;
    int               bottomEdge;

    ResizeMode        resizeMode;
    
    KeyCode	      key[NUM_KEYS];
} ResizeDisplay;

typedef struct _ResizeScreen {
    int grabIndex;

    PaintWindowProc paintWindow;
    PaintScreenProc paintScreen;

    Cursor leftCursor;
    Cursor rightCursor;
    Cursor upCursor;
    Cursor upLeftCursor;
    Cursor upRightCursor;
    Cursor downCursor;
    Cursor downLeftCursor;
    Cursor downRightCursor;
    Cursor middleCursor;
    Cursor cursor[NUM_KEYS];
} ResizeScreen;

#define GET_RESIZE_DISPLAY(d)				       \
    ((ResizeDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define RESIZE_DISPLAY(d)		       \
    ResizeDisplay *rd = GET_RESIZE_DISPLAY (d)

#define GET_RESIZE_SCREEN(s, rd)				   \
    ((ResizeScreen *) (s)->privates[(rd)->screenPrivateIndex].ptr)

#define RESIZE_SCREEN(s)						      \
    ResizeScreen *rs = GET_RESIZE_SCREEN (s, GET_RESIZE_DISPLAY (s->display))

#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))

static void
resizeUpdateRealWindowSize (CompDisplay *d)
{
    RESIZE_DISPLAY (d);
    XWindowChanges xwc;

    if (!rd->w)
	return;

    if (rd->w->state & CompWindowStateMaximizedVertMask)
	rd->currentHeight = rd->w->serverHeight;

    if (rd->w->state & CompWindowStateMaximizedHorzMask)
	rd->currentWidth = rd->w->serverWidth;

    if (rd->currentWidth  == rd->w->serverWidth &&
	rd->currentHeight == rd->w->serverHeight)
	return;

    if (rd->w->syncWait)
	return;

    xwc.x = rd->currentX;
    xwc.y = rd->currentY;
    xwc.width = rd->currentWidth;
    xwc.height = rd->currentHeight;

    sendSyncRequest (rd->w);

    configureXWindow (rd->w, CWX | CWY | CWWidth | CWHeight, &xwc);
}

static Bool
resizeInitiate (CompDisplay     *d,
		CompAction      *action,
		CompActionState state,
		CompOption      *option,
		int	        nOption)
{
    CompWindow *w;
    Window     xid;

    RESIZE_DISPLAY (d);

    xid = getIntOptionNamed (option, nOption, "window", 0);

    w = findWindowAtDisplay (d, xid);
    if (w && (w->actions & CompWindowActionResizeMask))
    {
	unsigned int mods;
	unsigned int mask;
	int          x, y;
	int	     button;

	RESIZE_SCREEN (w->screen);

	mods = getIntOptionNamed (option, nOption, "modifiers", 0);

	x = getIntOptionNamed (option, nOption, "x",
			       w->attrib.x + (w->width / 2));
	y = getIntOptionNamed (option, nOption, "y",
			       w->attrib.y + (w->height / 2));

	button = getIntOptionNamed (option, nOption, "button", -1);

	mask = getIntOptionNamed (option, nOption, "direction", 0);

	/* Initiate the resize in the direction suggested by the
	 * quarter of the window the mouse is in, eg drag in top left
	 * will resize up and to the left.  Keyboard resize starts out
	 * with the cursor in the middle of the window and then starts
	 * resizing the edge corresponding to the next key press. */
	if (state & CompActionStateInitKey)
	{
	    mask = 0;
	}
	else if (!mask)
	{
	    mask |= ((x - w->attrib.x) < (w->width / 2)) ?
		ResizeLeftMask : ResizeRightMask;

	    mask |= ((y - w->attrib.y) < (w->height / 2)) ?
		ResizeUpMask : ResizeDownMask;
	}

	if (otherScreenGrabExist (w->screen, "resize", 0))
	    return FALSE;

	if (rd->w)
	    return FALSE;

	if (w->type & (CompWindowTypeDesktopMask |
		       CompWindowTypeDockMask	 |
		       CompWindowTypeFullscreenMask))
	    return FALSE;

	if (w->attrib.override_redirect)
	    return FALSE;

	if (state & CompActionStateInitButton)
	    action->state |= CompActionStateTermButton;

	if (w->shaded)
	    mask &= ~(ResizeUpMask | ResizeDownMask);

	rd->w	        = w;
	rd->mask        = mask;
	rd->savedAttrib = w->attrib;

 	rd->currentX      = w->attrib.x;
 	rd->currentY      = w->attrib.y;
  	rd->currentWidth  = w->attrib.width;
 	rd->currentHeight = w->attrib.height;

 	rd->rightEdge  = rd->currentX + rd->currentWidth;
 	rd->bottomEdge = rd->currentY + rd->currentHeight;

 	rd->xDelta = 0;
	rd->yDelta = 0;

	lastPointerX = x;
	lastPointerY = y;

	if (!rs->grabIndex)
	{
	    Cursor cursor;

	    if (state & CompActionStateInitKey)
	    {
		cursor = rs->middleCursor;
	    }
	    else if (mask & ResizeLeftMask)
	    {
		if (mask & ResizeDownMask)
		    cursor = rs->downLeftCursor;
		else if (mask & ResizeUpMask)
		    cursor = rs->upLeftCursor;
		else
		    cursor = rs->leftCursor;
	    }
	    else if (mask & ResizeRightMask)
	    {
		if (mask & ResizeDownMask)
		    cursor = rs->downRightCursor;
		else if (mask & ResizeUpMask)
		    cursor = rs->upRightCursor;
		else
		    cursor = rs->rightCursor;
	    }
	    else if (mask & ResizeUpMask)
	    {
		cursor = rs->upCursor;
	    }
	    else
	    {
		cursor = rs->downCursor;
	    }

	    rs->grabIndex = pushScreenGrab (w->screen, cursor, "resize");
	}

	if (rs->grabIndex)
	{
	    rd->releaseButton = button;

	    (w->screen->windowGrabNotify) (w, x, y, state,
					   CompWindowGrabResizeMask |
					   CompWindowGrabButtonMask);

	    if (state & CompActionStateInitKey)
	    {
		int xRoot, yRoot;

		xRoot = w->attrib.x + (w->width  / 2);
		yRoot = w->attrib.y + (w->height / 2);

		warpPointer (d, xRoot - pointerX, yRoot - pointerY);
	    }

 	    rd->xDelta = (x - rd->currentX);
 	    if (rd->mask & ResizeRightMask)
 		rd->xDelta = rd->w->serverWidth - rd->xDelta;

 	    rd->yDelta = (y - rd->currentY);
 	    if (rd->mask & ResizeDownMask)
 		rd->yDelta = rd->w->serverHeight - rd->yDelta;

	    if (rd->resizeMode != ResizeModeNormal)
		damageScreen (w->screen);
	}
    }

    return FALSE;
}

static Bool
resizeTerminate (CompDisplay	 *d,
		 CompAction	 *action,
		 CompActionState state,
		 CompOption	 *option,
		 int		 nOption)
{
    RESIZE_DISPLAY (d);

    if (rd->w)
    {
	RESIZE_SCREEN (rd->w->screen);

	(rd->w->screen->windowUngrabNotify) (rd->w);

	if (state & CompActionStateCancel)
	{
	    XWindowChanges xwc;

	    sendSyncRequest (rd->w);

	    xwc.x      = rd->savedAttrib.x;
	    xwc.y      = rd->savedAttrib.y;
	    xwc.width  = rd->savedAttrib.width;
	    xwc.height = rd->savedAttrib.height;

	    configureXWindow (rd->w,
			      CWX | CWY | CWWidth | CWHeight,
			      &xwc);
	}
	else
	{
 	    if (rd->resizeMode != ResizeModeNormal)
 		resizeUpdateRealWindowSize (d);
	    else
		syncWindowPosition (rd->w);
	}

	if (rd->resizeMode != ResizeModeNormal)
	    damageScreen (rd->w->screen);

	if (rs->grabIndex)
	{
	    removeScreenGrab (rd->w->screen, rs->grabIndex, NULL);
	    rs->grabIndex = 0;
	}

	rd->w		  = 0;
	rd->releaseButton = 0;
    }

    action->state &= ~(CompActionStateTermKey | CompActionStateTermButton);

    return FALSE;
}

#define CLAMP(v, min, max) ((v) <= (min) ? (min) : (v) >= (max) ? (max) : (v))

static void
resizeConstrainMinMax (CompWindow *w,
		       int        width,
		       int        height,
		       int        *newWidth,
		       int        *newHeight)
{
    const XSizeHints *hints = &w->sizeHints;
    int		     min_width = 0;
    int		     min_height = 0;
    int		     max_width = MAXSHORT;
    int		     max_height = MAXSHORT;

    if ((hints->flags & PBaseSize) && (hints->flags & PMinSize))
    {
	min_width = hints->min_width;
	min_height = hints->min_height;
    }
    else if (hints->flags & PBaseSize)
    {
	min_width = hints->base_width;
	min_height = hints->base_height;
    }
    else if (hints->flags & PMinSize)
    {
	min_width = hints->min_width;
	min_height = hints->min_height;
    }

    if (hints->flags & PMaxSize)
    {
	max_width = hints->max_width;
	max_height = hints->max_height;
    }

    /* clamp width and height to min and max values */
    width  = CLAMP (width, min_width, max_width);
    height = CLAMP (height, min_height, max_height);

    *newWidth  = width;
    *newHeight = height;
}

static void
resizeConstrainResizeIncrement (CompWindow * w, 
				int        width, 
				int        height, 
				int        *newWidth, 
				int        *newHeight)
{
    int baseWidth, baseHeight;
    const XSizeHints *hints = &w->sizeHints;

    if (hints->flags & PResizeInc) 
    {
	if (hints->flags & PBaseSize) 
	{
	    baseWidth = hints->base_width;
	    baseHeight = hints->base_height;
	} 
	else 
	{
	    baseWidth = 0;
	    baseHeight = 0;
	}

	if ((width - baseWidth) % hints->width_inc) 
	    width = baseWidth + (((width - baseWidth) / 
				  hints->width_inc) * hints->width_inc);

	if ((height - baseHeight) % hints->height_inc) 
	    height = baseHeight + (((height - baseHeight) / 
				    hints->height_inc) * hints->height_inc);
    }

    *newWidth = width;
    *newHeight = height;
}

/* this code has its origin in metacity's boxes.c */
static void
resizeFindLinePointClosestToPoint (double x1, double y1, 
		   		   double x2, double y2, 
				   double px, double py, 
				   double *valx, double *valy)
{
    /* I'll use the shorthand rx, ry for the return values, valx & valy.
     * Now, we need (rx,ry) to be on the line between (x1,y1) and (x2,y2).
     * For that to happen, we first need the slope of the line from (x1,y1)
     * to (rx,ry) must match the slope of (x1,y1) to (x2,y2), i.e.:
     *   (ry-y1)   (y2-y1)
     *   ------- = -------
     *   (rx-x1)   (x2-x1)
     * If x1==x2, though, this gives divide by zero errors, so we want to
     * rewrite the equation by multiplying both sides by (rx-x1)*(x2-x1):
     *   (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
     * This is a valid requirement even when x1==x2 (when x1==x2, this latter
     * equation will basically just mean that rx must be equal to both x1 and
     * x2)
     *
     * The other requirement that we have is that the line from (rx,ry) to
     * (px,py) must be perpendicular to the line from (x1,y1) to (x2,y2).  So
     * we just need to get a vector in the direction of each line, take the
     * dot product of the two, and ensure that the result is 0:
     *   (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
     *
     * This gives us two equations and two unknowns:
     *
     *   (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
     *   (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
     *
     * This particular pair of equations is always solvable so long as
     * (x1,y1) and (x2,y2) are not the same point (and note that anyone who
     * calls this function that way is braindead because it means that they
     * really didn't specify a line after all).  However, the caller should
     * be careful to avoid making (x1,y1) and (x2,y2) too close (e.g. like
     * 10^{-8} apart in each coordinate), otherwise roundoff error could
     * cause issues.  Solving these equations by hand (or using Maple(TM) or
     * Mathematica(TM) or whatever) results in slightly messy expressions,
     * but that's all the below few lines do.
     */

    double diffx, diffy, den;
    diffx = x2 - x1;
    diffy = y2 - y1;
    den = diffx * diffx + diffy * diffy;

    *valx = (py * diffx * diffy + px * diffx * diffx +
	     y2 * x1 * diffy - y1 * x2 * diffy) / den;
    *valy = (px * diffx * diffy + py * diffy * diffy +
	     x2 * y1 * diffx - x1 * y2 * diffx) / den;
}

/* this function is heavily inspired by metacity's constrain_aspect_ratio */
static Bool
resizeConstrainAspectRatio(CompWindow * w, 
			   int        width, 
			   int        height, 
			   int        *retWidth, 
			   int        *retHeight)
{
    const XSizeHints *hints = &w->sizeHints;
    double minRatio, maxRatio;
    int newWidth, newHeight;
    double altWidth, altHeight;
    double bestWidth, bestHeight;
    int tolerance = 1;
    Bool resizeLeftRight = FALSE;
    Bool resizeUpDown = FALSE;

    RESIZE_DISPLAY(w->screen->display);

    newWidth = width;
    newHeight = height;

    if (hints->flags & PAspect) 
    {
	minRatio = hints->min_aspect.x / (double) hints->min_aspect.y;
	maxRatio = hints->max_aspect.x / (double) hints->max_aspect.y;

	if (minRatio > maxRatio)
	    return FALSE;

	resizeLeftRight = (rd->mask == (rd->mask & ResizeLeftMask)) ||
	                  (rd->mask == (rd->mask & ResizeRightMask));
	resizeUpDown = (rd->mask == (rd->mask & ResizeUpMask)) ||
	               (rd->mask == (rd->mask & ResizeDownMask));

	if (resizeLeftRight || resizeUpDown)
	    tolerance = 2;

	/* return if constraint already is satisfied */
	if (((width - (height * minRatio)) > (-minRatio * tolerance)) &&
	    ((width - (height * maxRatio)) < (maxRatio * tolerance)))
	{
	    return FALSE;
	}

	if (resizeLeftRight)
	    newHeight = CLAMP (newHeight, newWidth / maxRatio, newWidth / minRatio);
	else if (resizeUpDown)
	    newWidth = CLAMP (newWidth, newHeight * minRatio, newHeight * maxRatio);
	else 
	{
	    altWidth = CLAMP (newWidth, newHeight * minRatio, newHeight * maxRatio);
	    altHeight = CLAMP (newHeight, newWidth / maxRatio, newWidth / minRatio);

	    resizeFindLinePointClosestToPoint(altWidth, newHeight, 
					      newWidth, altHeight,
      					      newWidth, newHeight, 
					      &bestWidth, &bestHeight);

	    newWidth = bestWidth;
	    newHeight = bestHeight;
	}
    }

    *retWidth = newWidth;
    *retHeight = newHeight;

    return TRUE;
}

#undef CLAMP

static void
resizeUpdateWindowSize (CompDisplay *d, int dx, int dy)
{
    int width, height;
    int newHeight, newWidth;

    RESIZE_DISPLAY (d);

    if (!rd->w)
       return;

    /* don't apply resizing in the direction 
       windows are maximized to */
    if (rd->w->state & CompWindowStateMaximizedVertMask)
  	dy = 0;

    if (rd->w->state & CompWindowStateMaximizedHorzMask)
 	dx = 0;

    width = rd->currentWidth;
    height = rd->currentHeight;

    /* Ensure we don't push the window if constraints stop 
       it from being resized. */
    if (!(rd->mask & (ResizeLeftMask | ResizeRightMask)))
 	dx = 0;

    if (!(rd->mask & (ResizeUpMask | ResizeDownMask)))
 	dy = 0;

    /* Apply the width/height modifications */
    if (rd->mask & ResizeLeftMask)
 	width -= dx;
    else if (rd->mask & ResizeRightMask)
 	width += dx;

    if (rd->mask & ResizeUpMask)
 	height -= dy;
    else if (rd->mask & ResizeDownMask)
 	height += dy;

    /* Apply constraints */
    resizeConstrainResizeIncrement (rd->w, width, height, &width, &height);

    newWidth = width;
    newHeight = height;
    resizeConstrainAspectRatio (rd->w, width, height, &newWidth, &newHeight);
    resizeConstrainMinMax (rd->w, newWidth, newHeight, &newWidth, &newHeight);

    if ((width != newWidth) || (height != newHeight))
    {
	/* if the resizing hit constraints, move the mouse
	   pointer to the new border to avoid desynchronization */
	int pointerAdjustX = 0, pointerAdjustY = 0;

	if (rd->mask & ResizeRightMask)
	    pointerAdjustX = newWidth - width;
	else if (rd->mask & ResizeLeftMask)
	    pointerAdjustX = width - newWidth;

	if (rd->mask & ResizeDownMask)
	    pointerAdjustY = newHeight - height;
	else if (rd->mask & ResizeUpMask)
	    pointerAdjustY = height - newHeight;

	warpPointer (d, pointerAdjustX, pointerAdjustY);
    }

    dx -= newWidth - width;
    dy -= newHeight - height;

    /* If no resizing, drop out now. */
    if (!dx && !dy)
       return;

    if (rd->resizeMode != ResizeModeNormal)
 	damageScreen(rd->w->screen);

    /* Move window by delta if resizing from left/top */
    if (rd->mask & ResizeLeftMask && newWidth != rd->currentWidth)
 	rd->currentX += dx;

    if (rd->mask & ResizeUpMask && newHeight != rd->currentHeight)
 	rd->currentY += dy;

    rd->currentWidth = newWidth;
    rd->currentHeight = newHeight;

    if (rd->mask & ResizeLeftMask)
 	rd->currentX = rd->rightEdge - rd->currentWidth;

    if (rd->mask & ResizeUpMask)
 	rd->currentY = rd->bottomEdge - rd->currentHeight;

    if (rd->resizeMode == ResizeModeNormal)
    {
 	resizeUpdateRealWindowSize(d);
    }
    else if (rd->resizeMode == ResizeModeStretch)
    {
 	moveWindow(rd->w, 
  		   rd->currentX - rd->w->attrib.x,
  		   rd->currentY - rd->w->attrib.y, 
  		   TRUE, TRUE);
  	syncWindowPosition(rd->w);
    }
}

static void
resizeHandleKeyEvent (CompScreen *s,
		      KeyCode	 keycode)
{
    RESIZE_SCREEN (s);
    RESIZE_DISPLAY (s->display);

    if (rs->grabIndex && rd->w)
    {
	CompWindow *w;
	int i, widthInc, heightInc;

	w = rd->w;

	widthInc  = w->sizeHints.width_inc;
	heightInc = w->sizeHints.height_inc;

	if (widthInc < MIN_KEY_WIDTH_INC)
	    widthInc = MIN_KEY_WIDTH_INC;

	if (heightInc < MIN_KEY_HEIGHT_INC)
	    heightInc = MIN_KEY_HEIGHT_INC;

	for (i = 0; i < NUM_KEYS; i++)
	{
	    if (keycode != rd->key[i])
	      continue;

	    if (rd->mask & rKeys[i].warpMask)
	    {
		XWarpPointer (s->display->display, None, None, 0, 0, 0, 0,
			      rKeys[i].dx * widthInc,
			      rKeys[i].dy * heightInc);
	    }
	    else
	    {
		int x, y, left, top, width, height;

		left   = w->attrib.x - w->input.left;
		top    = w->attrib.y - w->input.top;
		width  = w->input.left + w->attrib.width  + w->input.right;
		height = w->input.top  + w->attrib.height + w->input.bottom;

		x = left + width  * (rKeys[i].dx + 1) / 2;
		y = top  + height * (rKeys[i].dy + 1) / 2;

 		rd->mask = rKeys[i].resizeMask;

 		/* recalculate delta */
 		rd->xDelta = (x - rd->currentX);
 		if (rd->mask & ResizeRightMask)
 		    rd->xDelta = rd->w->attrib.width - rd->xDelta;

 		rd->yDelta = (y - rd->currentY);
 		if (rd->mask & ResizeDownMask)
 		    rd->yDelta = rd->w->attrib.height - rd->yDelta;

		warpPointer (s->display, x - pointerX, y - pointerY);
		updateScreenGrab (s, rs->grabIndex, rs->cursor[i]);
	    }
	    break;
	}
    }
}

static void
resizeHandleMotionEvent (CompScreen *s,
			 int	    xRoot,
			 int	    yRoot)
{
    RESIZE_SCREEN (s);

    if (!rs->grabIndex)
	return;

    RESIZE_DISPLAY (s->display);

    int pointerDx, pointerDy;

    pointerDx = xRoot - rd->currentX;
    pointerDy = yRoot - rd->currentY;

    if (rd->mask & ResizeRightMask)
 	pointerDx -= (rd->currentWidth - rd->xDelta);
 
    if (rd->mask & ResizeLeftMask)
 	pointerDx -= rd->xDelta;
 
    if (rd->mask & ResizeDownMask)
 	pointerDy -= (rd->currentHeight - rd->yDelta);
 
    if (rd->mask & ResizeUpMask)
 	pointerDy -= rd->yDelta;
 
    if (!pointerDx && !pointerDy)
 	return;
 
    resizeUpdateWindowSize (s->display, 
 			    pointerDx,
 			    pointerDy);
}

static void
resizeHandleEvent (CompDisplay *d,
		   XEvent      *event)
{
    CompScreen *s;

    RESIZE_DISPLAY (d);

    switch (event->type) {
    case KeyPress:
	s = findScreenAtDisplay (d, event->xkey.root);
	if (s)
	    resizeHandleKeyEvent (s, event->xkey.keycode);
	break;
    case KeyRelease:
	break;
    case ButtonRelease: {
	CompAction *action =
	    &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;

	if (action->state & CompActionStateTermButton)
	{
	    if (event->type == ButtonRelease &&
		(rd->releaseButton     == -1 ||
		 event->xbutton.button == rd->releaseButton))
	    {
		resizeTerminate (d,
				 action,
				 CompActionStateTermButton,
				 NULL,
				 0);
	    }
	}
    } break;
    case MotionNotify:
	s = findScreenAtDisplay (d, event->xmotion.root);
	if (s)
	    resizeHandleMotionEvent (s, pointerX, pointerY);
	break;
    case EnterNotify:
    case LeaveNotify:
	s = findScreenAtDisplay (d, event->xcrossing.root);
	if (s)
	    resizeHandleMotionEvent (s, pointerX, pointerY);
	break;
    case ClientMessage:
	if (event->xclient.message_type == d->wmMoveResizeAtom)
	{
	    CompWindow *w;

	    if (event->xclient.data.l[2] <= WmMoveResizeSizeLeft ||
		event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
	    {
		w = findWindowAtDisplay (d, event->xclient.window);
		if (w)
		{
		    CompOption o[6];
		    CompAction *action =
			&rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;

		    o[0].type    = CompOptionTypeInt;
		    o[0].name    = "window";
		    o[0].value.i = event->xclient.window;

		    if (event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
		    {
			o[1].type    = CompOptionTypeInt;
			o[1].name    = "button";
			o[1].value.i = 0;

			resizeInitiate (d, action,
					CompActionStateInitKey,
					o, 2);
		    }
		    else
		    {
			static unsigned int mask[] = {
			    ResizeUpMask | ResizeLeftMask,
			    ResizeUpMask,
			    ResizeUpMask | ResizeRightMask,
			    ResizeRightMask,
			    ResizeDownMask | ResizeRightMask,
			    ResizeDownMask,
			    ResizeDownMask | ResizeLeftMask,
			    ResizeLeftMask,
			};
			unsigned int mods;
			Window	     root, child;
			int	     xRoot, yRoot, i;

			XQueryPointer (d->display, w->screen->root,
				       &root, &child, &xRoot, &yRoot,
				       &i, &i, &mods);

			/* TODO: not only button 1 */
			if (mods & Button1Mask)
			{
			    o[1].type	 = CompOptionTypeInt;
			    o[1].name	 = "modifiers";
			    o[1].value.i = mods;

			    o[2].type	 = CompOptionTypeInt;
			    o[2].name	 = "x";
			    o[2].value.i = event->xclient.data.l[0];

			    o[3].type	 = CompOptionTypeInt;
			    o[3].name	 = "y";
			    o[3].value.i = event->xclient.data.l[1];

			    o[4].type	 = CompOptionTypeInt;
			    o[4].name	 = "direction";
			    o[4].value.i = mask[event->xclient.data.l[2]];

			    o[5].type	 = CompOptionTypeInt;
			    o[5].name	 = "button";
			    o[5].value.i = event->xclient.data.l[3] ?
				event->xclient.data.l[3] : -1;

			    resizeInitiate (d,
					    action,
					    CompActionStateInitButton,
					    o, 6);

			    resizeHandleMotionEvent (w->screen, xRoot, yRoot);
			}
		    }
		}
	    }
	}
	break;
    case DestroyNotify:
	if (rd->w && rd->w->id == event->xdestroywindow.window)
	{
	    CompAction *action =
		&rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;

	    resizeTerminate (d, action, 0, NULL, 0);
	}
	break;
    case UnmapNotify:
	if (rd->w && rd->w->id == event->xunmap.window)
	{
	    CompAction *action =
		&rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action;

	    resizeTerminate (d, action, 0, NULL, 0);
	}
    default:
	break;
    }

    UNWRAP (rd, d, handleEvent);
    (*d->handleEvent) (d, event);
    WRAP (rd, d, handleEvent, resizeHandleEvent);

    if (event->type == d->syncEvent + XSyncAlarmNotify)
    {
	if (rd->w)
	{
	    XSyncAlarmNotifyEvent *sa;

	    sa = (XSyncAlarmNotifyEvent *) event;

	    if (rd->w->syncAlarm == sa->alarm)
		resizeUpdateRealWindowSize (d);
	}
    }
}

static Bool
resizePaintWindow(CompWindow              *w,
		  const WindowPaintAttrib *attrib,
		  const CompTransform     *transform,
		  Region                  region, 
		  unsigned int            mask)
{
    CompScreen *s = w->screen;
    Bool status;

    RESIZE_SCREEN (s);
    RESIZE_DISPLAY (s->display);

    if (rs->grabIndex && rd->w && (rd->w->id == w->id))
    {
	CompTransform wTransform = *transform;

	if (rd->resizeMode == ResizeModeStretch)
	{
	    float xScale = (float)(rd->currentWidth) /
		           (float)(rd->w->attrib.width);
	    float yScale = (float)(rd->currentHeight) /
	                   (float)(rd->w->attrib.height);

	    matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
	    matrixScale (&wTransform, xScale, yScale, 0.0f);
	    matrixTranslate (&wTransform, -w->attrib.x, -w->attrib.y, 0.0f);

	    mask |= PAINT_WINDOW_TRANSFORMED_MASK;
	}

	UNWRAP(rs, s, paintWindow);
	status = (*s->paintWindow) (w, attrib, &wTransform, region, mask);
	WRAP(rs, s, paintWindow, resizePaintWindow);
    }
    else
    {
	UNWRAP(rs, s, paintWindow);
	status = (*s->paintWindow) (w, attrib, transform, region, mask);
	WRAP(rs, s, paintWindow, resizePaintWindow);
    }

    return status;
}

static void
resizePaintOutline(CompScreen              *s, 
		   const ScreenPaintAttrib *sa,
		   const CompTransform     *transform, 
		   int                     output,
		   Bool                    transformed)
{
    int           x1, x2, y1, y2;
    CompTransform sTransform;
 
    RESIZE_SCREEN(s);

    if (!rs->grabIndex)
	return;
    
    RESIZE_DISPLAY(s->display);

    if ((rd->resizeMode != ResizeModeOutline) &&
	(rd->resizeMode != ResizeModeFilledOutline))
	return;

    if (!rd->w || (rd->w->state & MAXIMIZE_STATE))
	return;

    x1 = rd->currentX - rd->w->input.left;
    x2 = rd->currentX + rd->currentWidth + rd->w->input.right;
    y1 = rd->currentY - rd->w->input.top;
    y2 = rd->currentY + rd->w->input.bottom +
	 (rd->w->shaded ? rd->w->height : rd->currentHeight);

    sTransform = *transform;

    if (transformed)
    {
	(s->applyScreenTransform) (s, sa, output, &sTransform);
	transformToScreenSpace (s, output, -sa->zTranslate, &sTransform);
    }
    else
	transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);

    glPushMatrix ();
    glLoadMatrixf (sTransform.m);
 
    glDisableClientState (GL_TEXTURE_COORD_ARRAY);
    glEnable (GL_BLEND);
 
    if (rd->resizeMode == ResizeModeFilledOutline)
    {
	glColor4usv (rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR].value.c);
	glRecti (x1, y2, x2, y1);
    }

    /* This section draws the outline */
    glColor4usv (rd->opt[RESIZE_DISPLAY_OPTION_BORDER_COLOR].value.c);
    glLineWidth (2.0);
    glBegin (GL_LINE_LOOP);
    glVertex2i (x1, y1);
    glVertex2i (x2, y1);
    glVertex2i (x2, y2);
    glVertex2i (x1, y2);
    glEnd ();

    /* Clean up */
    glColor4usv (defaultColor);
    glDisable (GL_BLEND);
    glEnableClientState (GL_TEXTURE_COORD_ARRAY);
    glPopMatrix ();
}

static Bool
resizePaintScreen(CompScreen              *s,
		  const ScreenPaintAttrib *sAttrib,
		  const CompTransform     *transform,
		  Region                  region, 
		  int                     output, 
		  unsigned int            mask)
{
    Bool status = FALSE;

    RESIZE_SCREEN(s);
    RESIZE_DISPLAY(s->display);

    if (rs->grabIndex && rd->w && rd->resizeMode == ResizeModeStretch)
	mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;

    UNWRAP(rs, s, paintScreen);
    status = (*s->paintScreen) (s, sAttrib, transform, region, output, mask);
    WRAP(rs, s, paintScreen, resizePaintScreen);

    if (status)
	resizePaintOutline(s, sAttrib, transform, output, FALSE);

    return status;
}

static CompOption *
resizeGetDisplayOptions (CompPlugin  *plugin,
			 CompDisplay *display,
			 int	     *count)
{
    RESIZE_DISPLAY (display);

    *count = NUM_OPTIONS (rd);
    return rd->opt;
}

static Bool
resizeSetDisplayOption (CompPlugin      *plugin,
			CompDisplay     *display,
			char	        *name,
			CompOptionValue *value)
{
    CompOption *o;
    int	       index;

    RESIZE_DISPLAY (display);

    o = compFindOption (rd->opt, NUM_OPTIONS (rd), name, &index);
    if (!o)
	return FALSE;

    switch (index) {
    case RESIZE_DISPLAY_OPTION_INITIATE:
	if (setDisplayAction (display, o, value))
	    return TRUE;
	break;
    case RESIZE_DISPLAY_OPTION_MODE:
	if (compSetStringOption (o, value))
	{
	    int i;

	    rd->resizeMode = RESIZE_MODE_DEFAULT;
	    for (i = 0; i < o->rest.s.nString; i++)
		if (strcmp (resizeModes[i], o->value.s) == 0)
		    rd->resizeMode = (ResizeMode) i;

	    return TRUE;
	}
	break;
    case RESIZE_DISPLAY_OPTION_BORDER_COLOR:
    case RESIZE_DISPLAY_OPTION_FILL_COLOR:
	if (compSetColorOption (o, value))
	    return TRUE;
	break;
    default:
	break;
    }

    return FALSE;
}

static void
resizeDisplayInitOptions (ResizeDisplay *rd,
			  Display       *display)
{
    CompOption *o;

    o = &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE];
    o->name			     = "initiate";
    o->shortDesc		     = N_("Initiate Window Resize");
    o->longDesc			     = N_("Start resizing window");
    o->type			     = CompOptionTypeAction;
    o->value.action.initiate	     = resizeInitiate;
    o->value.action.terminate	     = resizeTerminate;
    o->value.action.bell	     = FALSE;
    o->value.action.edgeMask	     = 0;
    o->value.action.type	     = CompBindingTypeButton;
    o->value.action.state	     = CompActionStateInitButton;
    o->value.action.button.modifiers = RESIZE_INITIATE_BUTTON_MODIFIERS_DEFAULT;
    o->value.action.button.button    = RESIZE_INITIATE_BUTTON_DEFAULT;
    o->value.action.type	    |= CompBindingTypeKey;
    o->value.action.state	    |= CompActionStateInitKey;
    o->value.action.key.modifiers    = RESIZE_INITIATE_KEY_MODIFIERS_DEFAULT;
    o->value.action.key.keycode      =
	XKeysymToKeycode (display,
			  XStringToKeysym (RESIZE_INITIATE_KEY_DEFAULT));
    
    o = &rd->opt[RESIZE_DISPLAY_OPTION_MODE];
    o->name           = "resize_mode";
    o->shortDesc      = N_("Resize Display Mode");
    o->longDesc       = N_("Select between 'Normal', 'Stretched Texture', "
			   "'Outline and Filled' outline modes.");
    o->type           = CompOptionTypeString;
    o->value.s        = strdup(resizeModes[RESIZE_MODE_DEFAULT]);
    o->rest.s.string  = resizeModes;
    o->rest.s.nString = NUM_RESIZE_MODES;

    o = &rd->opt[RESIZE_DISPLAY_OPTION_BORDER_COLOR];
    o->name       = "border_color";
    o->shortDesc  = N_("Outline Color");
    o->longDesc   = N_("Outline Color for Outline and Filled Outline modes.");
    o->type       = CompOptionTypeColor;
    o->value.c[0] = RESIZE_BORDER_COLOR_RED;
    o->value.c[1] = RESIZE_BORDER_COLOR_GREEN;
    o->value.c[2] = RESIZE_BORDER_COLOR_BLUE;
    o->value.c[3] = RESIZE_BORDER_COLOR_ALPHA;

    o = &rd->opt[RESIZE_DISPLAY_OPTION_FILL_COLOR];
    o->name       = "fill_color";
    o->shortDesc  = N_("Fill Color");
    o->longDesc   = N_("Fill Color for Filled Outline mode.");
    o->type       = CompOptionTypeColor;
    o->value.c[0] = RESIZE_FILL_COLOR_RED;
    o->value.c[1] = RESIZE_FILL_COLOR_GREEN;
    o->value.c[2] = RESIZE_FILL_COLOR_BLUE;
    o->value.c[3] = RESIZE_FILL_COLOR_ALPHA;
}

static Bool
resizeInitDisplay (CompPlugin  *p,
		   CompDisplay *d)
{
    ResizeDisplay *rd;
    int	          i;

    rd = malloc (sizeof (ResizeDisplay));
    if (!rd)
	return FALSE;

    rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
    if (rd->screenPrivateIndex < 0)
    {
	free (rd);
	return FALSE;
    }

    resizeDisplayInitOptions (rd, d->display);

    rd->w = 0;

    rd->releaseButton = 0;

    rd->resizeMode = RESIZE_MODE_DEFAULT;

    for (i = 0; i < NUM_KEYS; i++)
	rd->key[i] = XKeysymToKeycode (d->display,
				       XStringToKeysym (rKeys[i].name));

    WRAP (rd, d, handleEvent, resizeHandleEvent);

    d->privates[displayPrivateIndex].ptr = rd;

    return TRUE;
}

static void
resizeFiniDisplay (CompPlugin  *p,
		   CompDisplay *d)
{
    RESIZE_DISPLAY (d);

    freeScreenPrivateIndex (d, rd->screenPrivateIndex);

    UNWRAP (rd, d, handleEvent);

    free (rd);
}

static Bool
resizeInitScreen (CompPlugin *p,
		  CompScreen *s)
{
    ResizeScreen *rs;

    RESIZE_DISPLAY (s->display);

    rs = malloc (sizeof (ResizeScreen));
    if (!rs)
	return FALSE;

    rs->grabIndex = 0;

    rs->leftCursor	= XCreateFontCursor (s->display->display, XC_left_side);
    rs->rightCursor	= XCreateFontCursor (s->display->display, XC_right_side);
    rs->upCursor	= XCreateFontCursor (s->display->display,
					     XC_top_side);
    rs->upLeftCursor	= XCreateFontCursor (s->display->display,
					     XC_top_left_corner);
    rs->upRightCursor	= XCreateFontCursor (s->display->display,
					     XC_top_right_corner);
    rs->downCursor	= XCreateFontCursor (s->display->display,
					     XC_bottom_side);
    rs->downLeftCursor	= XCreateFontCursor (s->display->display,
					     XC_bottom_left_corner);
    rs->downRightCursor = XCreateFontCursor (s->display->display,
					     XC_bottom_right_corner);
    rs->middleCursor    = XCreateFontCursor (s->display->display, XC_fleur);

    rs->cursor[0] = rs->leftCursor;
    rs->cursor[1] = rs->rightCursor;
    rs->cursor[2] = rs->upCursor;
    rs->cursor[3] = rs->downCursor;

    addScreenAction (s, &rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action);
 
    WRAP (rs, s, paintWindow, resizePaintWindow);
    WRAP (rs, s, paintScreen, resizePaintScreen);

    s->privates[rd->screenPrivateIndex].ptr = rs;

    return TRUE;
}

static void
resizeFiniScreen (CompPlugin *p,
		  CompScreen *s)
{
    RESIZE_SCREEN (s);
    RESIZE_DISPLAY (s->display);

    if (rs->leftCursor)
	XFreeCursor (s->display->display, rs->leftCursor);
    if (rs->rightCursor)
	XFreeCursor (s->display->display, rs->rightCursor);
    if (rs->upCursor)
	XFreeCursor (s->display->display, rs->upCursor);
    if (rs->downCursor)
	XFreeCursor (s->display->display, rs->downCursor);
    if (rs->middleCursor)
	XFreeCursor (s->display->display, rs->middleCursor);
    if (rs->upLeftCursor)
	XFreeCursor (s->display->display, rs->upLeftCursor);
    if (rs->upRightCursor)
	XFreeCursor (s->display->display, rs->upRightCursor);
    if (rs->downLeftCursor)
	XFreeCursor (s->display->display, rs->downLeftCursor);
    if (rs->downRightCursor)
	XFreeCursor (s->display->display, rs->downRightCursor);
    
    removeScreenAction (s, 
			&rd->opt[RESIZE_DISPLAY_OPTION_INITIATE].value.action);

    UNWRAP (rs, s, paintWindow);
    UNWRAP (rs, s, paintScreen);

    free (rs);
}

static Bool
resizeInit (CompPlugin *p)
{
    displayPrivateIndex = allocateDisplayPrivateIndex ();
    if (displayPrivateIndex < 0)
	return FALSE;

    return TRUE;
}

static void
resizeFini (CompPlugin *p)
{
    if (displayPrivateIndex >= 0)
	freeDisplayPrivateIndex (displayPrivateIndex);
}

static int
resizeGetVersion (CompPlugin *plugin,
		  int	     version)
{
    return ABIVERSION;
}

CompPluginVTable resizeVTable = {
    "resize",
    N_("Resize Window"),
    N_("Resize window"),
    resizeGetVersion,
    resizeInit,
    resizeFini,
    resizeInitDisplay,
    resizeFiniDisplay,
    resizeInitScreen,
    resizeFiniScreen,
    0, /* InitWindow */
    0, /* FiniWindow */
    resizeGetDisplayOptions,
    resizeSetDisplayOption,
    0, /* GetScreenOptions */
    0, /* SetScreenOption */
    0, /* Deps */
    0, /* nDeps */
    0, /* Features */
    0  /* nFeatures */
};

CompPluginVTable *
getCompPluginInfo (void)
{
    return &resizeVTable;
}