--- src/grid/grid.c	2016-04-10 12:23:24.000000000 -0700
+++ src/grid/grid.c	2017-03-01 23:23:09.809718104 -0800
@@ -1,7 +1,8 @@
 /*
- * Compiz Fusion Grid plugin
+ * Compiz Grid plugin
  *
  * Copyright (c) 2008 Stephen Kennedy <suasol@gmail.com>
+ * Copyright (c) 2010 Scott Moreau <oreaus@gmail.com>
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -37,6 +38,8 @@
 #   define DEBUG_PRINT(ARGS)
 #endif
 
+static int displayPrivateIndex;
+
 typedef enum
 {
     GridUnknown = 0,
@@ -49,8 +52,23 @@
     GridTopLeft = 7,
     GridTop = 8,
     GridTopRight = 9,
+    GridMaximize = 10,
 } GridType;
 
+
+typedef enum
+{
+    NoEdge = 0,
+    BottomLeft,
+    Bottom,
+    BottomRight,
+    Left,
+    Right,
+    TopLeft,
+    Top,
+    TopRight,
+} EdgeType;
+
 typedef struct _GridProps
 {
     int gravityRight;
@@ -76,6 +94,56 @@
     {1,0, 2,2},
 };
 
+typedef struct _Animation
+{
+    GLfloat progress;
+    XRectangle fromRect;
+    XRectangle targetRect;
+    XRectangle currentRect;
+    GLfloat opacity;
+    GLfloat timer;
+    int duration;
+    Bool complete;
+    Bool fadingOut;
+} Animation;
+
+typedef struct _GridDisplay {
+    int		    screenPrivateIndex;
+    HandleEventProc handleEvent;
+} GridDisplay;
+
+typedef struct _GridScreen
+{
+    WindowGrabNotifyProc   windowGrabNotify;
+    WindowUngrabNotifyProc windowUngrabNotify;
+    PaintOutputProc	   paintOutput;
+    PreparePaintScreenProc preparePaintScreen;
+
+    Bool grabIsMove;
+    EdgeType edge, lastEdge;
+    XRectangle     workarea;
+    XRectangle     desiredSlot;
+    XRectangle     desiredRect;
+    XRectangle     currentRect;
+    GridProps      props;
+    CompWindow *w;
+    Bool drawing;
+    Animation anim;
+    Bool animating;
+} GridScreen;
+
+#define GET_GRID_DISPLAY(d)					       \
+    ((GridDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
+
+#define GRID_DISPLAY(d)		           \
+    GridDisplay *gd = GET_GRID_DISPLAY (d)
+
+#define GET_GRID_SCREEN(s, gd)					       \
+    ((GridScreen *) (s)->base.privates[(gd)->screenPrivateIndex].ptr)
+
+#define GRID_SCREEN(s)						       \
+    GridScreen *gs = GET_GRID_SCREEN (s, GET_GRID_DISPLAY (s->display))
+
 static void
 slotToRect (CompWindow *w,
 	    XRectangle *slot,
@@ -117,144 +185,599 @@
     *rect = r;
 }
 
+
+static void
+getTargetRect (CompWindow *cw,
+	       GridType	  where)
+{
+    GRID_SCREEN (cw->screen);
+
+    gs->props = gridProps[where];
+
+    DEBUG_PRINT ((gridOut, "\nPressed KP_%i\n", where));
+
+    /* get current available area */
+    getWorkareaForOutput (cw->screen, outputDeviceForWindow(cw), &gs->workarea);
+    DEBUG_RECT (workarea);
+
+    /* Convention:
+     * xxxSlot include decorations (it's the screen area occupied)
+     * xxxRect are undecorated (it's the constrained position
+				of the contents)
+     */
+
+    /* slice and dice to get desired slot - including decorations */
+    gs->desiredSlot.y = gs->workarea.y + gs->props.gravityDown *
+	                (gs->workarea.height / gs->props.numCellsY);
+    gs->desiredSlot.height = gs->workarea.height / gs->props.numCellsY;
+    gs->desiredSlot.x = gs->workarea.x + gs->props.gravityRight *
+	                (gs->workarea.width / gs->props.numCellsX);
+    gs->desiredSlot.width = gs->workarea.width / gs->props.numCellsX;
+    DEBUG_RECT (desiredSlot);
+
+    /* Adjust for constraints and decorations */
+    constrainSize (cw, &gs->desiredSlot, &gs->desiredRect);
+    DEBUG_RECT (gs->desiredRect);
+}
+
+/* just keeping this for reference, but can use maximizeWindow instead */
+static void sendMaximizationRequest (CompWindow *w)
+{
+    XEvent xev;
+    CompScreen *s = w->screen;
+    CompDisplay *d = s->display;
+
+    xev.xclient.type = ClientMessage;
+    xev.xclient.display = d->display;
+    xev.xclient.format = 32;
+
+    xev.xclient.message_type = d->winStateAtom;
+    xev.xclient.window = w->id;
+
+    xev.xclient.data.l[0] = 1;
+    xev.xclient.data.l[1] = d->winStateMaximizedHorzAtom;
+    xev.xclient.data.l[2] = d->winStateMaximizedVertAtom;
+    xev.xclient.data.l[3] = 0;
+    xev.xclient.data.l[4] = 0;
+
+    XSendEvent (d->display, s->root, FALSE,
+    SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+}
+
+
+static void
+gridCommonWindow (CompWindow *cw,
+                  GridType   where)
+{
+    GRID_SCREEN (cw->screen);
+
+    if ((cw) && (where != GridUnknown))
+    {
+	/* add maximize option */
+	if (where == GridMaximize)
+	{
+	    sendMaximizationRequest (cw);
+	    /* maximizeWindow (cw, MAXIMIZE_STATE); */
+	}
+	else
+	{
+	    unsigned int valueMask = 0;
+	    int desiredState = 0;
+
+	    getTargetRect (cw, where);
+
+	    XWindowChanges xwc;
+
+	    /* if keys are pressed again then cycle through 1/3 or 2/3 widths... */
+
+	    /* Get current rect not including decorations */
+	    gs->currentRect.x = cw->serverX;
+	    gs->currentRect.y = cw->serverY;
+	    gs->currentRect.width  = cw->serverWidth;
+	    gs->currentRect.height = cw->serverHeight;
+	    DEBUG_RECT (currentRect);
+
+	    if ((gs->desiredRect.y == gs->currentRect.y &&
+		gs->desiredRect.height == gs->currentRect.height) &&
+		gridGetCycleSizes(cw->screen->display))
+	    {
+		int slotWidth33 = gs->workarea.width / 3;
+		int slotWidth66 = gs->workarea.width - slotWidth33;
+
+		DEBUG_PRINT ((gridOut, "Multi!\n"));
+
+		if (gs->props.numCellsX == 2) /* keys (1, 4, 7, 3, 6, 9) */
+		{
+		    if (gs->currentRect.width == gs->desiredRect.width &&
+			gs->currentRect.x == gs->desiredRect.x)
+		    {
+			gs->desiredSlot.width = slotWidth66;
+			gs->desiredSlot.x = gs->workarea.x +
+					    gs->props.gravityRight * slotWidth33;
+		    }
+		    else
+		    {
+			/* tricky, have to allow for window constraints when
+			 * computing what the 33% and 66% offsets would be
+			 */
+			XRectangle rect33, rect66, slot33, slot66;
+
+			slot33 = gs->desiredSlot;
+			slot33.x = gs->workarea.x +
+				   gs->props.gravityRight * slotWidth66;
+			slot33.width = slotWidth33;
+			constrainSize (cw, &slot33, &rect33);
+			DEBUG_RECT (slot33);
+			DEBUG_RECT (rect33);
+
+			slot66 = gs->desiredSlot;
+			slot66.x = gs->workarea.x +
+				   gs->props.gravityRight * slotWidth33;
+			slot66.width = slotWidth66;
+			constrainSize (cw, &slot66, &rect66);
+			DEBUG_RECT (slot66);
+			DEBUG_RECT (rect66);
+
+			if (gs->currentRect.width == rect66.width &&
+			    gs->currentRect.x == rect66.x)
+			{
+			    gs->desiredSlot.width = slotWidth33;
+			    gs->desiredSlot.x = gs->workarea.x +
+						gs->props.gravityRight * slotWidth66;
+			}
+		    }
+		}
+		else /* keys (2, 5, 8) */
+		{
+		    if (gs->currentRect.width == gs->desiredRect.width &&
+			gs->currentRect.x == gs->desiredRect.x)
+		    {
+			gs->desiredSlot.width = slotWidth33;
+			gs->desiredSlot.x = gs->workarea.x + slotWidth33;
+		    }
+		}
+		constrainSize (cw, &gs->desiredSlot, &gs->desiredRect);
+		DEBUG_RECT (gs->desiredRect);
+	    }
+
+	    xwc.x = gs->desiredRect.x;
+	    xwc.y = gs->desiredRect.y;
+	    xwc.width  = gs->desiredRect.width;
+	    xwc.height = gs->desiredRect.height;
+
+	    if (cw->mapNum)
+		sendSyncRequest (cw);
+
+	    if (where == GridRight || where == GridLeft)
+	    {
+		desiredState = CompWindowStateMaximizedVertMask;
+		valueMask = CWX | CWWidth;
+	    }
+	    else if (where == GridTop || where == GridBottom)
+	    {
+		desiredState = CompWindowStateMaximizedHorzMask;
+		valueMask = CWY | CWHeight;
+	    }
+	    else
+	    {
+		desiredState = 0;
+		valueMask = CWX | CWY | CWWidth | CWHeight;
+	    }
+
+	   if (cw->state != desiredState)
+		maximizeWindow (cw, desiredState);
+
+	    /* TODO: animate move+resize */
+	    configureXWindow (cw, valueMask, &xwc);
+
+	}
+    }
+}
+
 static Bool
-gridCommon (CompDisplay     *d,
-	    CompAction      *action,
+gridCommon (CompDisplay	    *d,
+	    CompAction	    *action,
 	    CompActionState state,
-	    CompOption      *option,
-	    int             nOption,
-	    GridType        where)
+	    CompOption	    *option,
+	    int		    nOption,
+	    GridType	    where)
 {
     Window     xid;
     CompWindow *cw;
 
     xid = getIntOptionNamed (option, nOption, "window", 0);
     cw  = findWindowAtDisplay (d, xid);
-    if (cw)
+
+    gridCommonWindow(cw, where);
+
+    return TRUE;
+}
+
+static GridType
+edgeToGridType (CompDisplay *d,
+		EdgeType edge)
+{
+    GridType ret = GridUnknown;
+
+    switch (edge)
     {
-	XRectangle     workarea;
-	XRectangle     desiredSlot;
-	XRectangle     desiredRect;
-	XRectangle     currentRect;
-	GridProps      props = gridProps[where];
-	XWindowChanges xwc;
-
-	DEBUG_PRINT ((gridOut, "\nPressed KP_%i\n", where));
-
-	/* get current available area */
-	getWorkareaForOutput (cw->screen, outputDeviceForWindow(cw), &workarea);
-	DEBUG_RECT (workarea);
-
-	/* Convention:
-	 * xxxSlot include decorations (it's the screen area occupied)
-	 * xxxRect are undecorated (it's the constrained position
-	                            of the contents)
-	 */
-
-	/* slice and dice to get desired slot - including decorations */
-	desiredSlot.y =  workarea.y + props.gravityDown *
-	                 (workarea.height / props.numCellsY);
-	desiredSlot.height = workarea.height / props.numCellsY;
-	desiredSlot.x =  workarea.x + props.gravityRight *
-	                 (workarea.width / props.numCellsX);
-	desiredSlot.width = workarea.width / props.numCellsX;
-	DEBUG_RECT (desiredSlot);
-
-	/* Adjust for constraints and decorations */
-	constrainSize (cw, &desiredSlot, &desiredRect);
-	DEBUG_RECT (desiredRect);
-
-	/* Get current rect not including decorations */
-	currentRect.x = cw->serverX;
-	currentRect.y = cw->serverY;
-	currentRect.width  = cw->serverWidth;
-	currentRect.height = cw->serverHeight;
-	DEBUG_RECT (currentRect);
+	case Left:
+	    ret = (GridType) gridGetLeftEdgeAction (d);
+	    break;
+	case Right:
+	    ret = (GridType) gridGetRightEdgeAction (d);
+	    break;
+	case Top:
+	     ret = (GridType) gridGetTopEdgeAction (d);
+	     break;
+	case Bottom:
+	    ret = (GridType) gridGetBottomEdgeAction (d);
+	    break;
+	case TopLeft:
+	    ret = (GridType) gridGetTopLeftCornerAction (d);
+	    break;
+	case TopRight:
+	    ret = (GridType) gridGetTopRightCornerAction (d);
+	    break;
+	case BottomLeft:
+	    ret = (GridType) gridGetBottomLeftCornerAction (d);
+	    break;
+	case BottomRight:
+	    ret = (GridType) gridGetBottomRightCornerAction (d);
+	    break;
+	case NoEdge:
+	default:
+	    ret = -1;
+	    break;
+    }
 
-	if (desiredRect.y == currentRect.y &&
-	    desiredRect.height == currentRect.height)
-	{
-	    int slotWidth33  = workarea.width / 3;
-	    int slotWidth66  = workarea.width - slotWidth33;
+    return ret;
+}
 
-	    DEBUG_PRINT ((gridOut, "Multi!\n"));
+static void
+gridHandleEvent (CompDisplay *d,
+		 XEvent      *event)
+{
+    GridType     where;
+    GRID_DISPLAY (d);
 
-	    if (props.numCellsX == 2) /* keys (1, 4, 7, 3, 6, 9) */
+    if (event->type == MotionNotify)
+    {
+	CompScreen *s;
+	s = findScreenAtDisplay (d, event->xmotion.root);
+	if (s)
+	{
+	    GRID_SCREEN (s);
+	    if (gs->grabIsMove)
 	    {
-		if (currentRect.width == desiredRect.width &&
-		    currentRect.x == desiredRect.x)
-		{
-		    desiredSlot.width = slotWidth66;
-		    desiredSlot.x = workarea.x +
-			            props.gravityRight * slotWidth33;
-		}
+		/* detect corners first */
+		/* Bottom Left */
+		if (pointerY > (s->height - gridGetBottomEdgeThreshold(d)) &&
+		    pointerX < gridGetLeftEdgeThreshold(d))
+		    gs->edge = BottomLeft;
+		/* Bottom Right */
+		else if (pointerY > (s->height - gridGetBottomEdgeThreshold(d)) &&
+			 pointerX > (s->width - gridGetRightEdgeThreshold(d)))
+		    gs->edge = BottomRight;
+		/* Top Left */
+		else if (pointerY < gridGetTopEdgeThreshold(d) && pointerX < gridGetLeftEdgeThreshold(d))
+		    gs->edge = TopLeft;
+		/* Top Right */
+		else if (pointerY < gridGetTopEdgeThreshold(d) &&
+			 pointerX > (s->width - gridGetRightEdgeThreshold(d)))
+		    gs->edge = TopRight;
+		/* Left */
+		else if (pointerX < gridGetLeftEdgeThreshold(d))
+		    gs->edge = Left;
+		/* Right */
+		else if (pointerX > (s->width - gridGetRightEdgeThreshold(d)))
+		    gs->edge = Right;
+		/* Top */
+		else if (pointerY < gridGetTopEdgeThreshold(d))
+		    gs->edge = Top;
+		/* Bottom */
+		else if (pointerY > (s->height - gridGetBottomEdgeThreshold(d)))
+		    gs->edge = Bottom;
+		/* No Edge */
 		else
-		{
-		    /* tricky, have to allow for window constraints when
-		     * computing what the 33% and 66% offsets would be
-		     */
-		    XRectangle rect33, rect66, slot33, slot66;
-
-		    slot33 = desiredSlot;
-		    slot33.x = workarea.x + props.gravityRight * slotWidth66;
-		    slot33.width = slotWidth33;
-		    constrainSize (cw, &slot33, &rect33);
-		    DEBUG_RECT (slot33);
-		    DEBUG_RECT (rect33);
-
-		    slot66 = desiredSlot;
-		    slot66.x = workarea.x + props.gravityRight * slotWidth33;
-		    slot66.width = slotWidth66;
-		    constrainSize (cw, &slot66, &rect66);
-		    DEBUG_RECT (slot66);
-		    DEBUG_RECT (rect66);
+		    gs->edge = NoEdge;
 
-		    if (currentRect.width == rect66.width &&
-			currentRect.x == rect66.x)
+		/* detect edge region change */
+		if (gs->lastEdge != gs->edge)
+		{
+		    if (gs->edge != NoEdge)
 		    {
-			desiredSlot.width = slotWidth33;
-			desiredSlot.x = workarea.x +
-			                props.gravityRight * slotWidth66;
+			where = edgeToGridType(d, gs->edge);
+
+			/* treat Maximize visual indicator same as GridCenter */
+			if (where == GridMaximize)
+			    where=GridCenter;
+
+			getTargetRect (gs->w, where);
+
+			gs->anim.duration = gridGetAnimationDuration (d);
+			gs->anim.timer = gs->anim.duration;
+			gs->anim.opacity = 0.0f;
+			gs->anim.progress = 0.0f;
+			gs->anim.currentRect.x = gs->w->serverX;
+			gs->anim.currentRect.y = gs->w->serverY;
+			gs->anim.currentRect.width = gs->w->serverWidth;
+			gs->anim.currentRect.height = gs->w->serverHeight;
+			gs->anim.targetRect = gs->desiredSlot;
+			gs->anim.fromRect.x = gs->w->serverX - gs->w->input.left;
+			gs->anim.fromRect.y = gs->w->serverY - gs->w->input.top;
+			gs->anim.fromRect.width = gs->w->serverWidth +
+						  gs->w->input.left +
+						  gs->w->input.right +
+						  gs->w->serverBorderWidth * 2;
+			gs->anim.fromRect.height = gs->w->serverHeight +
+						   gs->w->input.top +
+						   gs->w->input.bottom +
+						   gs->w->serverBorderWidth * 2;
+			gs->animating = TRUE;
+			gs->anim.fadingOut = FALSE;
 		    }
+		    else
+			gs->anim.fadingOut = TRUE;
+
+		    gs->lastEdge = gs->edge;
 		}
+
 	    }
-	    else /* keys (2, 5, 8) */
-	    {
-		if (currentRect.width == desiredRect.width &&
-		    currentRect.x == desiredRect.x)
-		{
-		    desiredSlot.width = slotWidth33;
-		    desiredSlot.x = workarea.x + slotWidth33;
-		}
-	    }
-	    constrainSize (cw, &desiredSlot, &desiredRect);
-	    DEBUG_RECT (desiredRect);
 	}
+    }
+
+    UNWRAP (gd, d, handleEvent);
+    (*d->handleEvent) (d, event);
+    WRAP (gd, d, handleEvent, gridHandleEvent);
+}
+
+static void
+gridWindowGrabNotify (CompWindow   *w,
+		      int          x,
+		      int          y,
+		      unsigned int state,
+		      unsigned int mask)
+{
+    CompScreen *s = w->screen;
+
+    GRID_SCREEN (s);
+
+    if (mask & CompWindowGrabMoveMask)
+    {
+	gs->grabIsMove = TRUE;
+	gs->w = w;
+    }
+
+    UNWRAP (gs, s, windowGrabNotify);
+    (*s->windowGrabNotify) (w, x, y, state, mask);
+    WRAP (gs, s, windowGrabNotify, gridWindowGrabNotify);
+}
+
+static void
+gridWindowUngrabNotify (CompWindow *w)
+{
+    CompScreen *s = w->screen;
+    CompDisplay *d = s->display;
+
+    GRID_SCREEN (s);
+
+    if (gs->grabIsMove)
+    {
+	gs->grabIsMove = FALSE;
+
+	if (gs->edge != NoEdge)
+	{
+	    gridCommonWindow (w, edgeToGridType(d, gs->edge));
+	    gs->anim.fadingOut = TRUE;
+	}
+    }
+
+    gs->edge = NoEdge;
+    gs->lastEdge = NoEdge;
+
+    UNWRAP (gs, s, windowUngrabNotify);
+    (*s->windowUngrabNotify) (w);
+    WRAP (gs, s, windowUngrabNotify, gridWindowUngrabNotify);
+}
+
+static int
+applyProgress (int a, int b, float progress)
+{
+    return a < b ?
+	   b - (abs (a - b) * progress) :
+	   b + (abs (a - b) * progress);
+}
+
+static void
+setCurrentRect (CompScreen *s)
+{
+    GRID_SCREEN (s);
+
+    gs->anim.currentRect.x = applyProgress (gs->anim.targetRect.x,
+					    gs->anim.fromRect.x,
+					    gs->anim.progress);
+    gs->anim.currentRect.width = applyProgress (gs->anim.targetRect.width,
+						gs->anim.fromRect.width,
+						gs->anim.progress);
+    gs->anim.currentRect.y = applyProgress (gs->anim.targetRect.y,
+					    gs->anim.fromRect.y,
+					    gs->anim.progress);
+    gs->anim.currentRect.height = applyProgress (gs->anim.targetRect.height,
+						 gs->anim.fromRect.height,
+						 gs->anim.progress);
+}
+
+static void
+glPaintRectangle (CompScreen		  *s,
+		  const ScreenPaintAttrib *sAttrib,
+		  const CompTransform	  *transform,
+		  CompOutput		  *output)
+{
+    float alpha = 0;
+
+    GRID_SCREEN (s);
+
+    BoxRec      rect;
+
+    setCurrentRect (s);
+
+    rect.x1=gs->anim.currentRect.x;
+    rect.y1=gs->anim.currentRect.y;
+    rect.x2=gs->anim.currentRect.x + gs->anim.currentRect.width;
+    rect.y2=gs->anim.currentRect.y + gs->anim.currentRect.height;
+    CompTransform sTransform = *transform;
+
+    /* rect = desiredSlot;*/
+
+    glPushMatrix ();
+
+    transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
+
+    glLoadMatrixf (sTransform.m);
+
+    glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+    glEnable (GL_BLEND);
+
+    /* fill rectangle */
+    /* TODO: have multiple animations
+       for (iter = animations.begin (); iter != animations.end () && animating; iter++)
+       { */
+
+    alpha = ((float) gridGetFillColorAlpha (s->display) / 65535.0f) *
+	     gs->anim.opacity;
 
-	xwc.x = desiredRect.x;
-	xwc.y = desiredRect.y;
-	xwc.width  = desiredRect.width;
-	xwc.height = desiredRect.height;
+    glColor4f (((float) gridGetFillColorRed (s->display) / 65535.0f) * alpha,
+	       ((float) gridGetFillColorGreen (s->display) / 65535.0f) * alpha,
+	       ((float) gridGetFillColorBlue (s->display) / 65535.0f) * alpha,
+	       alpha);
 
-	if (cw->mapNum)
-	    sendSyncRequest (cw);
+    glRecti (rect.x1, rect.y2, rect.x2, rect.y1);
 
-	if (cw->state & MAXIMIZE_STATE)
+    /* draw outline */
+
+    alpha = ((float) gridGetOutlineColorAlpha (s->display) / 65535.0f) *
+	     gs->anim.opacity;
+
+    glColor4f (((float) gridGetOutlineColorRed (s->display) / 65535.0f) * alpha,
+	       ((float) gridGetOutlineColorGreen (s->display) / 65535.0f) * alpha,
+	       ((float) gridGetOutlineColorBlue (s->display) / 65535.0f) * alpha,
+	       alpha);
+
+    int thickness = gridGetOutlineThickness (s->display);
+    glLineWidth (thickness);
+    glBegin (GL_LINE_LOOP);
+
+    /* set outline rect smaller to avoid damage issues */
+    /* TODO: maybe get a better way of doing this */
+    float half_thickness = thickness * 0.5;
+    glVertex2f (rect.x1 + half_thickness, rect.y1 + half_thickness);
+    glVertex2f (rect.x2 - half_thickness, rect.y1 + half_thickness);
+    glVertex2f (rect.x2 - half_thickness, rect.y2 - half_thickness);
+    glVertex2f (rect.x1 + half_thickness, rect.y2 - half_thickness);
+    glEnd ();
+
+    /* clean up */
+    glColor4usv (defaultColor);
+    glDisable (GL_BLEND);
+    glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+    glPopMatrix ();
+}
+
+static void
+damagePaintRegion (CompScreen *s)
+{
+    REGION reg;
+    int    x, y;
+
+    GRID_SCREEN (s);
+
+    /* if (!is->fadeTime && !is->drawing)
+       return; */
+
+    x = gs->anim.currentRect.x;
+    y = gs->anim.currentRect.y;
+
+    reg.rects    = &reg.extents;
+    reg.numRects = 1;
+
+    reg.extents.x1 = x - 5;
+    reg.extents.y1 = y - 5;
+    reg.extents.x2 = x + gs->anim.currentRect.width + 5;
+    reg.extents.y2 = y + gs->anim.currentRect.height + 5;
+
+    damageScreenRegion (s, &reg);
+}
+
+static Bool
+gridPaintOutput (CompScreen		 *s,
+		 const ScreenPaintAttrib *sAttrib,
+		 const CompTransform	 *transform,
+		 Region			 region,
+		 CompOutput		 *output,
+		 unsigned int		 mask)
+{
+    Bool status;
+
+    GRID_SCREEN (s);
+
+    UNWRAP (gs, s, paintOutput);
+    status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
+    WRAP (gs, s, paintOutput, gridPaintOutput);
+
+    if (gs->animating && gridGetDrawIndicator (s->display))
+    {
+	glPaintRectangle (s, sAttrib, transform, output);
+	damagePaintRegion (s);
+    }
+
+    return status;
+}
+
+
+/* handle the fade in /fade out */
+static void
+gridPreparePaintScreen (CompScreen *s,
+			int        ms)
+{
+    GRID_SCREEN (s);
+
+    if (gs->animating)
+    {
+	gs->anim.timer -= ms;
+
+	if (gs->anim.timer < 0)
+	    gs->anim.timer = 0;
+
+	if (gs->anim.fadingOut)
+	    gs->anim.opacity -= ms * 0.002;
+	else
+	{
+	    if (gs->anim.opacity < 1.0f)
+		gs->anim.opacity = gs->anim.progress * gs->anim.progress;
+	    else
+		gs->anim.opacity = 1.0f;
+	}
+
+	if (gs->anim.opacity < 0)
 	{
-	    /* maximized state interferes with us, clear it */
-	    maximizeWindow (cw, 0);
+	    gs->anim.opacity = 0.0f;
+	    gs->anim.fadingOut = FALSE;
+	    gs->anim.complete = TRUE;
+	    gs->animating = FALSE;
 	}
 
-	/* TODO: animate move+resize */
-	configureXWindow (cw, CWX | CWY | CWWidth | CWHeight, &xwc);
+	gs->anim.progress = (gs->anim.duration - gs->anim.timer) / gs->anim.duration;
     }
 
-    return TRUE;
+    UNWRAP (gs, s, preparePaintScreen);
+    (*s->preparePaintScreen) (s, ms);
+    WRAP (gs, s, preparePaintScreen, gridPreparePaintScreen);
 }
 
 #define HANDLER(WHERE)                                        \
     static Bool                                               \
-	grid##WHERE(CompDisplay     *d,                       \
+	grid##WHERE(CompDisplay	    *d,                       \
 		    CompAction      *action,                  \
 		    CompActionState state,                    \
 		    CompOption      *option,                  \
@@ -273,6 +796,7 @@
 HANDLER (TopLeft)
 HANDLER (Top)
 HANDLER (TopRight)
+HANDLER (Maximize)
 
 #undef HANDLER
 
@@ -282,6 +806,8 @@
 gridInitDisplay (CompPlugin  *p,
 		 CompDisplay *d)
 {
+    GridDisplay *gd;
+
     if (!checkPluginABI ("core", CORE_ABIVERSION))
 	return FALSE;
 
@@ -294,22 +820,129 @@
     gridSetPutToprightKeyInitiate (d, gridTopRight);
     gridSetPutBottomleftKeyInitiate (d, gridBottomLeft);
     gridSetPutBottomrightKeyInitiate (d, gridBottomRight);
+    gridSetPutMaximizeKeyInitiate (d, gridMaximize);
+
+    gd = malloc (sizeof (GridDisplay));
+    if (!gd)
+	return FALSE;
+
+    gd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+    if (gd->screenPrivateIndex < 0)
+    {
+	free (gd);
+	return FALSE;
+    }
+
+    WRAP (gd, d, handleEvent, gridHandleEvent);
+
+    d->base.privates[displayPrivateIndex].ptr = gd;
+
+    return TRUE;
+}
+
+static void
+gridFiniDisplay (CompPlugin  *p,
+		 CompDisplay *d)
+{
+    GRID_DISPLAY (d);
+
+    freeScreenPrivateIndex (d, gd->screenPrivateIndex);
+
+    UNWRAP (gd, d, handleEvent);
+
+    free (gd);
+}
+
+static Bool
+gridInitScreen (CompPlugin *p,
+		CompScreen *s)
+{
+    GridScreen * gs;
+
+    GRID_DISPLAY (s->display);
+
+    gs = malloc (sizeof (GridScreen));
+    if (!gs)
+	return FALSE;
+
+    gs->grabIsMove = FALSE;
+    gs->edge = NoEdge;
+    gs->lastEdge = NoEdge;
+    gs->drawing  = FALSE;
+
+    gs->w = 0;
+
+    gs->anim.progress = 0.0f;
+    gs->anim.fromRect.x = 0;
+    gs->anim.fromRect.y = 0;
+    gs->anim.fromRect.width = 0;
+    gs->anim.fromRect.height =0;
+    gs->anim.targetRect.x = 0;
+    gs->anim.targetRect.y = 0;
+    gs->anim.targetRect.width = 0;
+    gs->anim.targetRect.height = 0;
+    gs->anim.currentRect.x = 0;
+    gs->anim.currentRect.y = 0;
+    gs->anim.currentRect.width = 0;
+    gs->anim.currentRect.height = 0;
+    gs->anim.opacity = 0.5f;
+    gs->anim.timer = 0.0f;
+    gs->anim.duration = 0;
+    gs->anim.complete = FALSE;
+    gs->anim.fadingOut = FALSE;
+
+    gs->animating=FALSE;
+
+    WRAP (gs, s, windowGrabNotify, gridWindowGrabNotify);
+    WRAP (gs, s, windowUngrabNotify, gridWindowUngrabNotify);
+    WRAP (gs, s, paintOutput, gridPaintOutput);
+    WRAP (gs, s, preparePaintScreen, gridPreparePaintScreen);
+
+    s->base.privates[gd->screenPrivateIndex].ptr = gs;
 
     return TRUE;
 }
 
+static void
+gridFiniScreen (CompPlugin *p,
+		CompScreen *s)
+{
+    GRID_SCREEN (s);
+
+    UNWRAP (gs, s, windowGrabNotify);
+    UNWRAP (gs, s, windowUngrabNotify);
+    UNWRAP (gs, s, paintOutput);
+    UNWRAP (gs, s, preparePaintScreen);
+
+    free (gs);
+}
+
 static CompBool
 gridInitObject (CompPlugin *p,
 		CompObject *o)
 {
     static InitPluginObjectProc dispTab[] = {
 	(InitPluginObjectProc) 0, /* InitCore */
-	(InitPluginObjectProc) gridInitDisplay
+	(InitPluginObjectProc) gridInitDisplay,
+	(InitPluginObjectProc) gridInitScreen
     };
 
     RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
 }
 
+static void
+gridFiniObject (CompPlugin *p,
+		CompObject *o)
+{
+    static FiniPluginObjectProc dispTab[] = {
+	   (FiniPluginObjectProc) 0, /* FiniCore */
+	   (FiniPluginObjectProc) gridFiniDisplay,
+	   (FiniPluginObjectProc) gridFiniScreen
+    };
+
+    DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
+}
+
 static Bool
 gridInitPlugin (CompPlugin *p)
 {
@@ -318,6 +951,10 @@
     setlinebuf(gridOut);
 #endif
 
+    displayPrivateIndex = allocateDisplayPrivateIndex ();
+    if (displayPrivateIndex < 0)
+	return FALSE;
+
     return TRUE;
 }
 
@@ -328,6 +965,8 @@
     fclose(gridOut);
     gridOut = NULL;
 #endif
+
+    freeDisplayPrivateIndex(displayPrivateIndex);
 }
 
 CompPluginVTable gridVTable =
@@ -337,7 +976,7 @@
     gridInitPlugin,
     gridFiniPlugin,
     gridInitObject,
-    0,
+    gridFiniObject,
     0,
     0
 };
@@ -346,4 +985,4 @@
 getCompPluginInfo ()
 {
     return &gridVTable;
-}
+}
\ No newline at end of file
