/*
 * Copyright (c) 2006 Darryll Truchan <moppsy@comcast.net>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <X11/Xatom.h>

#include <compiz.h>
#include "put_options.h"

#define GET_PUT_DISPLAY(d) \
    ((PutDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define PUT_DISPLAY(d) \
    PutDisplay *pd = GET_PUT_DISPLAY (d)
#define GET_PUT_SCREEN(s, pd) \
    ((PutScreen *) (s)->privates[(pd)->screenPrivateIndex].ptr)
#define PUT_SCREEN(s) \
    PutScreen *ps = GET_PUT_SCREEN (s, GET_PUT_DISPLAY (s->display))
#define GET_PUT_WINDOW(w, ps) \
    ((PutWindow *) (w)->privates[(ps)->windowPrivateIndex].ptr)
#define PUT_WINDOW(w) \
    PutWindow *pw = GET_PUT_WINDOW (w, \
		    GET_PUT_SCREEN (w->screen, \
		    GET_PUT_DISPLAY (w->screen->display)))

static int displayPrivateIndex;

typedef enum
{
    PutUnknown = 0,
    PutBottomLeft = 1,
    PutBottom = 2,
    PutBottomRight = 3,
    PutLeft = 4,
    PutCenter = 5,
    PutRight = 6,
    PutTopLeft = 7,
    PutTop = 8,
    PutTopRight = 9,
    PutRestore = 10,
    PutViewport = 11,
    PutViewportLeft = 12,
    PutViewportRight = 13,
    PutExact = 14,
    PutPointer = 15,
    PutViewportUp = 16,
    PutViewportDown = 17
} PutType;

typedef struct _PutDisplay
{
    int screenPrivateIndex;

    HandleEventProc handleEvent; /* handle event function pointer */

    Atom compizPutWindowAtom;    /* client event atom             */
} PutDisplay;

typedef struct _PutScreen
{
    int windowPrivateIndex;

    PreparePaintScreenProc preparePaintScreen;	/* function pointer         */
    DonePaintScreenProc    donePaintScreen;	/* function pointer         */
    PaintOutputProc        paintOutput;	        /* function pointer         */
    PaintWindowProc        paintWindow;	        /* function pointer         */

    int        moreAdjust;			/* animation flag           */
    int        grabIndex;			/* screen grab index        */
} PutScreen;

typedef struct _PutWindow
{
    GLfloat xVelocity, yVelocity;	/* animation velocity       */
    GLfloat tx, ty;			/* animation translation    */

    int lastX, lastY;			/* starting position        */
    int targetX, targetY;               /* target of the animation  */

    Bool adjust;			/* animation flag           */
} PutWindow;

/*
 * calculate the velocity for the moving window
 */
static int
adjustPutVelocity (CompWindow *w)
{
    float dx, dy, adjust, amount;
    float x1, y1;

    PUT_WINDOW (w);

    x1 = pw->targetX;
    y1 = pw->targetY;

    dx = x1 - (w->attrib.x + pw->tx);
    dy = y1 - (w->attrib.y + pw->ty);

    adjust = dx * 0.15f;
    amount = fabs (dx) * 1.5;
    if (amount < 0.5f)
	amount = 0.5f;
    else if (amount > 5.0f)
	amount = 5.0f;

    pw->xVelocity = (amount * pw->xVelocity + adjust) / (amount + 1.0f);

    adjust = dy * 0.15f;
    amount = fabs (dy) * 1.5f;
    if (amount < 0.5f)
	amount = 0.5f;
    else if (amount > 5.0f)
	amount = 5.0f;

    pw->yVelocity = (amount * pw->yVelocity + adjust) / (amount + 1.0f);

    if (fabs (dx) < 0.1f && fabs (pw->xVelocity) < 0.2f &&
	fabs (dy) < 0.1f && fabs (pw->yVelocity) < 0.2f)
    {
	/* animation done */
	pw->xVelocity = pw->yVelocity = 0.0f;

	pw->tx = x1 - w->attrib.x;
	pw->ty = y1 - w->attrib.y;
	return 0;
    }
    return 1;
}

/*
 * setup for paint screen
 */
static void
putPreparePaintScreen (CompScreen *s,
		       int        msSinceLastPaint)
{
    PUT_SCREEN (s);

    if (ps->moreAdjust && ps->grabIndex)
    {
	CompWindow *w;
	int        steps;
	float      amount, chunk;

	amount = msSinceLastPaint * 0.025f * putGetSpeed (s);
	steps = amount / (0.5f * putGetTimestep (s));
	if (!steps)
	    steps = 1;
	chunk = amount / (float)steps;

	while (steps--)
	{
	    ps->moreAdjust = 0;
	    for (w = s->windows; w; w = w->next)
	    {
		PUT_WINDOW (w);

		if (pw->adjust)
		{
		    pw->adjust = adjustPutVelocity (w);
		    ps->moreAdjust |= pw->adjust;

		    pw->tx += pw->xVelocity * chunk;
		    pw->ty += pw->yVelocity * chunk;

		    if (!pw->adjust)
		    {
			/* animation done */
			moveWindow (w, pw->targetX - w->attrib.x,
				    pw->targetY - w->attrib.y, TRUE, TRUE);
			syncWindowPosition (w);
			pw->tx = pw->ty = 0;
		    }
		}
    	    }
	    if (!ps->moreAdjust)
	    {
		/* unfocus moved window if enabled */
		if (putGetUnfocusWindow (s))
		    focusDefaultWindow (s->display);
		break;
    	    }
	}
    }

    UNWRAP (ps, s, preparePaintScreen);
    (*s->preparePaintScreen) (s, msSinceLastPaint);
    WRAP (ps, s, preparePaintScreen, putPreparePaintScreen);
}

/*
 * after painting clean up
 */
static void
putDonePaintScreen (CompScreen *s)
{
    PUT_SCREEN (s);

    if (ps->moreAdjust && ps->grabIndex)
	damageScreen (s);
    else
    {
	if (ps->grabIndex)
	{
	    /* release the screen grab */
	    removeScreenGrab (s, ps->grabIndex, NULL);
	    ps->grabIndex = 0;
	}
    }

    UNWRAP (ps, s, donePaintScreen);
    (*s->donePaintScreen) (s);
    WRAP (ps, s, donePaintScreen, putDonePaintScreen);
}

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

    PUT_SCREEN (s);

    if (ps->moreAdjust)
	mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;

    UNWRAP (ps, s, paintOutput);
    status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
    WRAP (ps, s, paintOutput, putPaintOutput);

    return status;
}

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

    PUT_SCREEN (s);
    PUT_WINDOW (w);

    if (pw->adjust)
    {
	CompTransform wTransform = *transform;

	matrixTranslate (&wTransform, pw->tx, pw->ty, 0.0f);

	mask |= PAINT_WINDOW_TRANSFORMED_MASK;

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

    return status;
}

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

    xid = getIntOptionNamed (option, nOption, "window", 0);
    if (!xid)
	xid = d->activeWindow;

    w = findWindowAtDisplay (d, xid);
    if (w)
    {
	CompScreen *s = w->screen;

	PUT_SCREEN (s);

	if (!ps->grabIndex)
	{
	    /* this will keep put from working while something
	       else has a screen grab */
	    if (otherScreenGrabExist (s, "put", 0))
		return FALSE;

	    /* we are ok, so grab the screen */
	    ps->grabIndex = pushScreenGrab (s, s->invisibleCursor, "put");
	}

	if (ps->grabIndex)
	{
	    int        px, py, x, y, dx, dy;
	    int        head, width, height, hx, hy;
	    PutType    type;
	    XRectangle workArea;

	    PUT_WINDOW (w);

	    px = getIntOptionNamed (option, nOption, "x", 0);
	    py = getIntOptionNamed (option, nOption, "y", 0);
	    type = getIntOptionNamed (option, nOption, "type", PutCenter);

	    /* we don't want to do anything with override redirect windows */
	    if (w->attrib.override_redirect)
		return FALSE;

	    /* we don't want to be moving the desktop, docks,
	       or fullscreen windows */
	    if (w->type & CompWindowTypeDesktopMask ||
		w->type & CompWindowTypeDockMask ||
		w->type & CompWindowTypeFullscreenMask)
	    {
		return FALSE;
	    }

	    /* get the Xinerama head from the options list */
	    head = getIntOptionNamed(option, nOption, "head", -1);

	    /* no head in options list so we use the current head */
	    if (head == -1)
		head = s->currentOutputDev;

	    /* make sure the head number is not out of bounds */
	    head = MIN (head, s->nOutputDev - 1);

    	    /* some error has occured so we bail out */
	    if (head < 0)
		return FALSE;

	    /* working area of the screen */
	    getWorkareaForOutput (s, head, &workArea);
	    width  = workArea.width;
    	    height = workArea.height;
	    hx     = workArea.x;
	    hy     = workArea.y;

    	    /* the windows location */
	    x = w->attrib.x;
	    y = w->attrib.y;

	    /* handle the put types
	     *
	     */
	    switch (type)
	    {
	    case PutCenter:
		/* center of the screen */
		dx = (width / 2) - (w->width / 2) - (x - hx);
		dy = (height / 2) - (w->height / 2) - (y - hy);
		break;
	    case PutLeft:
		/* center of the left edge */
		dx = -(x - hx) + w->input.left + putGetPadLeft (s);
		dy = (height / 2) - (w->height / 2) - (y - hy);
		break;
	    case PutTopLeft:
		/* top left corner */
		dx = -(x - hx) + w->input.left + putGetPadLeft (s);
		dy = -(y - hy) + w->input.top + putGetPadTop (s);
		break;
	    case PutTop:
		/* center of top edge */
		dx = (width / 2) - (w->width / 2) - (x - hx);
		dy = -(y - hy) + w->input.top + putGetPadTop (s);
		break;
	    case PutTopRight:
		/* top right corner */
		dx = width - w->width - (x - hx) -
		     w->input.right - putGetPadRight (s);
		dy = -(y - hy) + w->input.top + putGetPadTop (s);
		break;
	    case PutRight:
		/* center of right edge */
		dx = width - w->width - (x - hx) -
		     w->input.right - putGetPadRight (s);
		dy = (height / 2) - (w->height / 2) - (y - hy);
		break;
	    case PutBottomRight:
		/* bottom right corner */
		dx = width - w->width - (x - hx) -
		     w->input.right - putGetPadRight (s);
		dy = height - w->height - (y - hy) -
		     w->input.bottom - putGetPadBottom (s);
		break;
	    case PutBottom:
		/* center of bottom edge */
		dx = (width / 2) - (w->width / 2) - (x - hx);
		dy = height - w->height - (y - hy) -
		     w->input.bottom - putGetPadBottom (s);
		break;
	    case PutBottomLeft:
		/* bottom left corner */
		dx = -(x - hx) + w->input.left + putGetPadLeft (s);
		dy = height - w->height - (y - hy) -
		     w->input.bottom - putGetPadBottom (s);
		break;
	    case PutRestore:
		/* back to last position */
		dx = pw->lastX - x;
		dy = pw->lastY - y;
		break;
	    case PutViewport:
		{
		    int face, faceX, faceY, hDirection, vDirection;

		    /* get the face to move to from the options list */
		    face = getIntOptionNamed(option, nOption, "face", -1);

		    /* if it wasn't supplied, bail out */
		    if (face < 0)
			return FALSE;

		    /* split 1D face value into 2D x and y face */
		    faceX = face % s->hsize;
		    faceY = face / s->hsize;
		    if (faceY > s->vsize)
			faceY = s->vsize - 1;

	    	    /* take the shortest horizontal path to the
		       destination viewport */
    		    hDirection = (faceX - s->x);
		    if (hDirection > s->hsize / 2)
			hDirection = (hDirection - s->hsize);
		    else if (hDirection < -s->hsize / 2)
			hDirection = (hDirection + s->hsize);

		    /* we need to do this for the vertical
		       destination viewport too */
    		    vDirection = (faceY - s->y);
		    if (vDirection > s->vsize / 2)
			vDirection = (vDirection - s->vsize);
		    else if (vDirection < -s->hsize / 2)
			vDirection = (vDirection + s->vsize);

		    dx = s->width * hDirection;
	    	    dy = s->height * vDirection;
    		    break;
		}
	    case PutViewportLeft:
		/* move to the viewport on the left */
		dx = -s->width;
		dy = 0;
		break;
	    case PutViewportRight:
		/* move to the viewport on the right */
		dx = s->width;
		dy = 0;
		break;
	    case PutViewportUp:
		/* move to the viewport above */
		dx = 0;
		dy = -s->height;
		break;
	    case PutViewportDown:
		/* move to the viewport below */
		dx = 0;
		dy = s->height;
		break;
	    case PutExact:
		/* move the window to an exact position */
		if (px < 0)
		    /* account for a specified negative position,
		       like geometry without (-0) */
		    dx = px + s->width - w->width - x - w->input.right;
		else
		    dx = px - x + w->input.left;

		if (py < 0)
		    /* account for a specified negative position,
		       like geometry without (-0) */
		    dy = py + s->height - w->height - y - w->input.bottom;
		else
		    dy = py - y + w->input.top;
		break;
	    case PutPointer:
		{
		    /* move the window to the pointers position
		     * using the current quad of the screen to determine
		     * which corner to move to the pointer
		     */
		    int          rx, ry;
		    Window       root, child;
		    int          winX, winY;
		    unsigned int pMask;

		    /* get the pointers position from the X server */
    		    if (XQueryPointer (d->display, w->id, &root, &child,
				       &rx, &ry, &winX, &winY, &pMask)) 
		    {

		        if (putGetWindowCenter (s))
			    {
			        /* window center */
			        dx = rx - (w->width / 2) - x;
				dy = ry - (w->height / 2) - y;
			    }
			else if (rx < s->workArea.width / 2 &&
				ry < s->workArea.height / 2)
			    {
			        /* top left quad */
			        dx = rx - x + w->input.left;
				dy = ry - y + w->input.top;
			    }
			else if (rx < s->workArea.width / 2 &&
				ry >= s->workArea.height / 2)
			    {
			        /* bottom left quad */
			        dx = rx - x + w->input.left;
				dy = ry - w->height - y - w->input.bottom;
			    }
			else if (rx >= s->workArea.width / 2 &&
				ry < s->workArea.height / 2)
			    {
			        /* top right quad */
			        dx = rx - w->width - x - w->input.right;
				dy = ry - y + w->input.top;
			    }
			else
			    {
			        /* bottom right quad */
			        dx = rx - w->width - x - w->input.right;
				dy = ry - w->height - y - w->input.bottom;
			    }
		    } else {
		        dx = dy = 0;
		    }
		}
		break;
	    default:
		/* if an unknown type is specified, do nothing */
		dx = dy = 0;
		break;
	    }

	    /* don't do anything if there is nothing to do */
	    if (dx != 0 || dy != 0)
	    {
		if (putGetAvoidOffscreen (s))
		{
		    /* avoids window borders offscreen */
		    if ((-(x - hx) + w->input.left + putGetPadLeft (s)) > dx)
		    {
			dx = -(x - hx) + w->input.left + putGetPadLeft (s);
		    }
	    	    else if ((width - w->width - (x - hx) -
			      w->input.right - putGetPadRight (s)) < dx)
		    {
			dx = width - w->width - (x - hx) -
			     w->input.right - putGetPadRight (s);
		    }

	    	    if ((-(y - hy) + w->input.top + putGetPadTop (s)) > dy)
		    {
			dy = -(y - hy) + w->input.top + putGetPadTop (s);
		    }
	    	    else if ((height - w->height - (y - hy) - w->input.bottom -
			      putGetPadBottom (s)) < dy)
		    {
			dy = height - w->height - (y - hy) -
			     w->input.bottom - putGetPadBottom (s);
		    }
		}
		/* save the windows position in the saveMask
		 * this is used when unmaximizing the window
		 */
		if (w->saveMask & CWX)
		    w->saveWc.x += dx;

		if (w->saveMask & CWY)
		    w->saveWc.y += dy;

		/* Make sure everyting starts out at the windows
		   current position */
		pw->lastX = x;
		pw->lastY = y;

		pw->targetX = x + dx;
		pw->targetY = y + dy;

		/* mark for animation */
		pw->adjust = TRUE;
		ps->moreAdjust = TRUE;

		/* cause repainting */
		addWindowDamage (w);
	    }
	}
    }

    /* tell event.c handleEvent to not call XAllowEvents */
    return FALSE;
}

static Bool
putToViewport (CompDisplay     *d,
	       CompAction      *action,
	       CompActionState state,
	       CompOption      *option,
	       int             nOption)
{
    int        face;
    CompOption o[5];

    /* get the face option */
    face = getIntOptionNamed(option, nOption, "face", -1);

    /* if it's not supplied, lets figure it out */
    if (face < 0)
    {
	PutDisplayOptions i;
	CompOption        *opt;

	i = PutDisplayOptionPutViewport1;

	while (i <= PutDisplayOptionPutViewport12)
	{
	    opt = putGetDisplayOption (d, i);
	    if (&opt->value.action == action)
	    {
		face = i - PutDisplayOptionPutViewport1;
		break;
	    }
	    i++;
	}
    }

    /* setup the options for putInitiate */
    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "face";
    o[2].value.i = face;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "type";
    o[3].value.i = PutViewport;

    o[4].type    = CompOptionTypeInt;
    o[4].name    = "window";
    o[4].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 5);

    return FALSE;
}

static Bool
putViewportLeft (CompDisplay     *d,
		 CompAction      *action,
 		 CompActionState state,
		 CompOption      *option,
		 int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutViewportLeft;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putViewportRight (CompDisplay     *d,
		  CompAction      *action,
 		  CompActionState state,
		  CompOption      *option,
		  int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutViewportRight;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putViewportUp (CompDisplay     *d,
	       CompAction      *action,
	       CompActionState state,
	       CompOption      *option,
	       int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutViewportUp;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putViewportDown (CompDisplay     *d,
		 CompAction      *action,
 		 CompActionState state,
		 CompOption      *option,
		 int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutViewportDown;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
restore (CompDisplay     *d,
	 CompAction      *action,
	 CompActionState state,
	 CompOption      *option,
	 int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutRestore;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putPointer (CompDisplay     *d,
   	    CompAction      *action,
	    CompActionState state,
	    CompOption      *option,
	    int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutPointer;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putExact (CompDisplay     *d,
 	  CompAction      *action,
	  CompActionState state,
	  CompOption      *option,
	  int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutExact;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putCenter (CompDisplay     *d,
	   CompAction      *action,
	   CompActionState state,
	   CompOption      *option,
	   int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutCenter;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putLeft (CompDisplay     *d,
	 CompAction      *action,
	 CompActionState state,
	 CompOption      *option,
	 int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutLeft;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putTopLeft (CompDisplay     *d,
   	    CompAction      *action,
	    CompActionState state,
	    CompOption      *option,
	    int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutTopLeft;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putTop (CompDisplay     *d,
	CompAction      *action,
	CompActionState state,
	CompOption      *option,
	int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutTop;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putTopRight (CompDisplay     *d,
	     CompAction      *action,
	     CompActionState state,
	     CompOption      *option,
	     int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutTopRight;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putRight (CompDisplay     *d,
 	  CompAction      *action,
	  CompActionState state,
	  CompOption      *option,
	  int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutRight;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putBottomRight (CompDisplay     *d,
		CompAction      *action,
		CompActionState state,
		CompOption      *option,
		int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutBottomRight;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putBottom (CompDisplay     *d,
	   CompAction      *action,
	   CompActionState state,
	   CompOption      *option,
	   int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutBottom;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate(d, NULL, 0, o, 4);

    return FALSE;
}

static Bool
putBottomLeft (CompDisplay     *d,
	       CompAction      *action,
	       CompActionState state,
	       CompOption      *option,
	       int             nOption)
{
    /* setup the options for putInitiate */
    CompOption o[4];

    o[0].type    = CompOptionTypeInt;
    o[0].name    = "x";
    o[0].value.i = getIntOptionNamed (option, nOption, "x", 0);

    o[1].type    = CompOptionTypeInt;
    o[1].name    = "y";
    o[1].value.i = getIntOptionNamed (option, nOption, "y", 0);

    o[2].type    = CompOptionTypeInt;
    o[2].name    = "type";
    o[2].value.i = PutBottomLeft;

    o[3].type    = CompOptionTypeInt;
    o[3].name    = "window";
    o[3].value.i = getIntOptionNamed (option, nOption, "window", 0);

    putInitiate (d, NULL, 0, o, 4);

    return FALSE;
}

static void
putHandleEvent (CompDisplay *d,
		XEvent      *event)
{
    PUT_DISPLAY (d);

    switch (event->type)
    {
	/* handle client events */
    case ClientMessage:
	/* accept the custom atom for putting windows */
	if (event->xclient.message_type == pd->compizPutWindowAtom)
	{
	    CompWindow *w;

	    w = findWindowAtDisplay(d, event->xclient.window);
	    if (w)
	    {
		/*
		 * get the values from the xclientmessage event and populate
		 * the options for put initiate
		 *
		 * the format is 32
		 * and the data is
		 * l[0] = x position - unused (for future PutExact)
		 * l[1] = y position - unused (for future PutExact)
		 * l[2] = face number
		 * l[3] = put type, int value from enum
		 * l[4] = Xinerama head number
		 */
		CompOption opt[6];

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

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

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

		opt[3].type    = CompOptionTypeInt;
		opt[3].name    = "face";
		opt[3].value.i = event->xclient.data.l[2];

		opt[4].type    = CompOptionTypeInt;
		opt[4].name    = "type";
		opt[4].value.i = event->xclient.data.l[3];

		opt[5].type    = CompOptionTypeInt;
		opt[5].name    = "head";
		opt[5].value.i = event->xclient.data.l[4];

		putInitiate (w->screen->display, NULL, 0, opt, 6);
	    }
	}
	break;
    default:
	break;
    }

    UNWRAP (pd, d, handleEvent);
    (*d->handleEvent) (d, event);
    WRAP (pd, d, handleEvent, putHandleEvent);
}

static Bool
putInitDisplay (CompPlugin  *p,
		CompDisplay *d)
{
    PutDisplay *pd;

    pd = malloc (sizeof (PutDisplay));
    if (!pd)
	return FALSE;

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

    /* custom atom for client events */
    pd->compizPutWindowAtom = XInternAtom(d->display,
					  "_COMPIZ_PUT_WINDOW", 0);

    putSetPutViewportInitiate (d, putToViewport);
    putSetPutViewport1Initiate (d, putToViewport);
    putSetPutViewport2Initiate (d, putToViewport);
    putSetPutViewport3Initiate (d, putToViewport);
    putSetPutViewport4Initiate (d, putToViewport);
    putSetPutViewport5Initiate (d, putToViewport);
    putSetPutViewport6Initiate (d, putToViewport);
    putSetPutViewport7Initiate (d, putToViewport);
    putSetPutViewport8Initiate (d, putToViewport);
    putSetPutViewport9Initiate (d, putToViewport);
    putSetPutViewport10Initiate (d, putToViewport);
    putSetPutViewport11Initiate (d, putToViewport);
    putSetPutViewport12Initiate (d, putToViewport);
    putSetPutViewportLeftInitiate (d, putViewportLeft);
    putSetPutViewportRightInitiate (d, putViewportRight);
    putSetPutViewportUpInitiate (d, putViewportUp);
    putSetPutViewportDownInitiate (d, putViewportDown);
    putSetPutRestoreInitiate (d, restore);
    putSetPutPointerInitiate (d, putPointer);
    putSetPutExactInitiate (d, putExact);
    putSetPutCenterInitiate (d, putCenter);
    putSetPutLeftInitiate (d, putLeft);
    putSetPutRightInitiate (d, putRight);
    putSetPutTopInitiate (d, putTop);
    putSetPutBottomInitiate (d, putBottom);
    putSetPutTopleftInitiate (d, putTopLeft);
    putSetPutToprightInitiate (d, putTopRight);
    putSetPutBottomleftInitiate (d, putBottomLeft);
    putSetPutBottomrightInitiate (d, putBottomRight);

    WRAP (pd, d, handleEvent, putHandleEvent);
    d->privates[displayPrivateIndex].ptr = pd;

    return TRUE;
}

static void
putFiniDisplay (CompPlugin  *p,
		CompDisplay *d)
{
    PUT_DISPLAY (d);

    freeScreenPrivateIndex (d, pd->screenPrivateIndex);
    UNWRAP (pd, d, handleEvent);

    free (pd);
}

static Bool
putInitScreen (CompPlugin *p,
	       CompScreen *s)
{
    PutScreen *ps;

    PUT_DISPLAY (s->display);

    ps = malloc (sizeof (PutScreen));
    if (!ps)
	return FALSE;

    ps->windowPrivateIndex = allocateWindowPrivateIndex (s);
    if (ps->windowPrivateIndex < 0)
    {
	free (ps);
	return FALSE;
    }

    /* initialize variables
     * bad stuff happens if we don't do this
     */
    ps->moreAdjust = FALSE;
    ps->grabIndex = 0;

    /* wrap the overloaded functions */
    WRAP (ps, s, preparePaintScreen, putPreparePaintScreen);
    WRAP (ps, s, donePaintScreen, putDonePaintScreen);
    WRAP (ps, s, paintOutput, putPaintOutput);
    WRAP (ps, s, paintWindow, putPaintWindow);

    s->privates[pd->screenPrivateIndex].ptr = ps;
    return TRUE;
}

static void
putFiniScreen (CompPlugin *p,
	       CompScreen *s)
{
    PUT_SCREEN (s);

    freeWindowPrivateIndex (s, ps->windowPrivateIndex);

    UNWRAP (ps, s, preparePaintScreen);
    UNWRAP (ps, s, donePaintScreen);
    UNWRAP (ps, s, paintOutput);
    UNWRAP (ps, s, paintWindow);

    free (ps);
}

static Bool
putInitWindow(CompPlugin *p,
	      CompWindow *w)
{
    PutWindow *pw;

    PUT_SCREEN (w->screen);

    pw = malloc (sizeof (PutWindow));
    if (!pw)
	return FALSE;

    /* initialize variables
     * I don't need to repeat it
     */
    pw->tx = pw->ty = pw->xVelocity = pw->yVelocity = 0.0f;
    pw->lastX = w->serverX;
    pw->lastY = w->serverY;
    pw->adjust = FALSE;

    w->privates[ps->windowPrivateIndex].ptr = pw;

    return TRUE;
}

static void
putFiniWindow (CompPlugin *p,
	       CompWindow *w)
{
    PUT_WINDOW (w);

    free (pw);
}

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

    return TRUE;
}

static void
putFini (CompPlugin * p)
{
    freeDisplayPrivateIndex (displayPrivateIndex);
}

static int
putGetVersion (CompPlugin *p,
	       int        version)
{
    return ABIVERSION;
}

/*
 * vTable tells the dl
 * what we offer
 */
CompPluginVTable putVTable = {
    "put",
    putGetVersion,
    0,
    putInit,
    putFini,
    putInitDisplay,
    putFiniDisplay,
    putInitScreen,
    putFiniScreen,
    putInitWindow,
    putFiniWindow,
    NULL,
    NULL,
    NULL,
    NULL
};

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


syntax highlighted by Code2HTML, v. 0.9.1