All pastes #461829 Raw Edit

Untitled

public text v1 · immutable
#461829 ·published 2007-04-27 21:55 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 <math.h>
#include <sys/time.h>

#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/X.h>
#include <X11/extensions/Xevie.h>
#include <X11/Xutil.h>

#include <X11/Xatom.h>
#include <X11/cursorfont.h>

#include <compiz.h>

#include <X11/extensions/Xevie.h>

#define WIN_X(w) ((w)->attrib.x - (w)->input.left)
#define WIN_Y(w) ((w)->attrib.y - (w)->input.top)
#define WIN_W(w) ((w)->width + (w)->input.left + (w)->input.right)
#define WIN_H(w) ((w)->height + (w)->input.top + (w)->input.bottom)

#define SCALE_SPACING_DEFAULT 10
#define SCALE_SPACING_MIN     0
#define SCALE_SPACING_MAX     250

#define SCALE_INITIATE_KEY_DEFAULT       "Up"
#define SCALE_INITIATE_MODIFIERS_DEFAULT (ControlMask | CompAltMask)

#define SCALE_SPEED_DEFAULT   1.5f
#define SCALE_SPEED_MIN       0.1f
#define SCALE_SPEED_MAX       50.0f
#define SCALE_SPEED_PRECISION 0.1f

#define SCALE_TIMESTEP_DEFAULT   1.2f
#define SCALE_TIMESTEP_MIN       0.1f
#define SCALE_TIMESTEP_MAX       50.0f
#define SCALE_TIMESTEP_PRECISION 0.1f

#define SCALE_STATE_NONE 0
#define SCALE_STATE_OUT  1
#define SCALE_STATE_WAIT 2
#define SCALE_STATE_IN   3

#define SCALE_DARKEN_BACK_DEFAULT TRUE

#define SCALE_OPACITY_DEFAULT 75
#define SCALE_OPACITY_MIN     0
#define SCALE_OPACITY_MAX     100

#define SCALE_WINDOW_MATCH_DEFAULT \
    "Toolbar | Utility | Dialog | Normal | Unknown"

typedef enum {
    ScaleIconNone = 0,
    ScaleIconEmblem,
    ScaleIconBig
} IconOverlay;

static char *iconOverlayString[] = {
    N_("None"),
    N_("Emblem"),
    N_("Big")
};

static IconOverlay iconOverlay[] = {
    ScaleIconNone,
    ScaleIconEmblem,
    ScaleIconBig
};
#define N_ICON_TYPE (sizeof (iconOverlayString) / sizeof (iconOverlayString[0]))
#define SCALE_ICON_DEFAULT (iconOverlayString[1])

#define SCALE_HOVER_TIME_DEFAULT 750
#define SCALE_HOVER_TIME_MIN     50
#define SCALE_HOVER_TIME_MAX     10000

static int displayPrivateIndex;

typedef struct _ScaleSlot {
    int   x1, y1, x2, y2;
    int   filled;
    float scale;
} ScaleSlot;

#define SCALE_DISPLAY_OPTION_INITIATE        0
#define SCALE_DISPLAY_OPTION_INITIATE_ALL    1
#define SCALE_DISPLAY_OPTION_INITIATE_GROUP  2
#define SCALE_DISPLAY_OPTION_INITIATE_OUTPUT 3
#define SCALE_DISPLAY_OPTION_NUM             4

typedef struct _ScaleDisplay {
    int		    screenPrivateIndex;
    HandleEventProc handleEvent;

    CompOption opt[SCALE_DISPLAY_OPTION_NUM];
    
    Bool xevie;

    unsigned int lastActiveNum;
    Window       lastActiveWindow;
    Window       selectedWindow;
    KeyCode	 leftKeyCode, rightKeyCode, upKeyCode, downKeyCode;
} ScaleDisplay;

#define SCALE_SCREEN_OPTION_SPACING      0
#define SCALE_SCREEN_OPTION_SPEED	 1
#define SCALE_SCREEN_OPTION_TIMESTEP	 2
#define SCALE_SCREEN_OPTION_WINDOW_MATCH 3
#define SCALE_SCREEN_OPTION_DARKEN_BACK  4
#define SCALE_SCREEN_OPTION_OPACITY      5
#define SCALE_SCREEN_OPTION_ICON         6
#define SCALE_SCREEN_OPTION_HOVER_TIME   7
#define SCALE_SCREEN_OPTION_NUM          8

typedef enum {
    ScaleTypeNormal = 0,
    ScaleTypeOutput,
    ScaleTypeGroup,
    ScaleTypeAll
} ScaleType;

typedef struct _ScaleScreen {
    int windowPrivateIndex;

    PreparePaintScreenProc preparePaintScreen;
    DonePaintScreenProc    donePaintScreen;
    PaintScreenProc        paintScreen;
    PaintWindowProc        paintWindow;
    DamageWindowRectProc   damageWindowRect;

    CompOption opt[SCALE_SCREEN_OPTION_NUM];

    int spacing;

    float speed;
    float timestep;

    Bool grab;
//    int  grabIndex;

    Window dndTarget;

    CompTimeoutHandle hoverHandle;

    int state;
    int moreAdjust;

    Cursor cursor;

    ScaleSlot *slots;
    int        slotsSize;
    int        nSlots;

    /* only used for sorting */
    CompWindow **windows;
    int        windowsSize;
    int        nWindows;

    Bool     darkenBack;
    GLushort opacity;

    IconOverlay iconOverlay;

    ScaleType type;

    Window clientLeader;

    CompMatch match;
    CompMatch *currentMatch;
} ScaleScreen;

typedef struct _ScaleWindow {
    ScaleSlot *slot;

    int sid;
    int distance;

    GLfloat xVelocity, yVelocity, scaleVelocity;
    GLfloat scale;
    GLfloat tx, ty;
    float   delta;
    Bool    adjust;

    float lastThumbOpacity;
} ScaleWindow;


#define GET_SCALE_DISPLAY(d)				      \
    ((ScaleDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define SCALE_DISPLAY(d)		     \
    ScaleDisplay *sd = GET_SCALE_DISPLAY (d)

#define GET_SCALE_SCREEN(s, sd)					  \
    ((ScaleScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr)

#define SCALE_SCREEN(s)							   \
    ScaleScreen *ss = GET_SCALE_SCREEN (s, GET_SCALE_DISPLAY (s->display))

#define GET_SCALE_WINDOW(w, ss)					  \
    ((ScaleWindow *) (w)->privates[(ss)->windowPrivateIndex].ptr)

#define SCALE_WINDOW(w)					       \
    ScaleWindow *sw = GET_SCALE_WINDOW  (w,		       \
		      GET_SCALE_SCREEN  (w->screen,	       \
		      GET_SCALE_DISPLAY (w->screen->display)))

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

static CompOption *
scaleGetScreenOptions (CompPlugin  *plugin,
		       CompScreen *screen,
		       int	  *count)
{
    SCALE_SCREEN (screen);

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

static Bool
scaleSetScreenOption (CompPlugin  *plugin,
		      CompScreen      *screen,
		      char	      *name,
		      CompOptionValue *value)
{
    CompOption *o;
    int	       index;

    SCALE_SCREEN (screen);

    o = compFindOption (ss->opt, NUM_OPTIONS (ss), name, &index);

    if (!o)
	return FALSE;

    switch (index) {
    case SCALE_SCREEN_OPTION_SPACING:
	if (compSetIntOption (o, value))
	{
	    ss->spacing = o->value.i;
	    return TRUE;
	}
	break;
    case SCALE_SCREEN_OPTION_SPEED:
	if (compSetFloatOption (o, value))
	{
	    ss->speed = o->value.f;
	    return TRUE;
	}
	break;
    case SCALE_SCREEN_OPTION_TIMESTEP:
	if (compSetFloatOption (o, value))
	{
	    ss->timestep = o->value.f;
	    return TRUE;
	}
	break;
    case SCALE_SCREEN_OPTION_DARKEN_BACK:
	if (compSetBoolOption (o, value))
	{
	    ss->darkenBack = o->value.b;
	    return TRUE;
	}
	break;
    case SCALE_SCREEN_OPTION_OPACITY:
	if (compSetIntOption (o, value))
	{
	    ss->opacity = (OPAQUE * o->value.i) / 100;
	    return TRUE;
	}
	break;
    case SCALE_SCREEN_OPTION_ICON:
	if (compSetStringOption (o, value))
	{
	    int i;

	    for (i = 0; i < N_ICON_TYPE; i++)
	    {
		if (strcmp (o->value.s, iconOverlayString[i]) == 0)
		{
		    ss->iconOverlay = iconOverlay[i];
		    return TRUE;
		}
	    }
	}
	break;
    default:
	if (compSetOption (o, value))
	    return TRUE;
	break;
    }

    return FALSE;
}

static void
scaleScreenInitOptions (ScaleScreen *ss)
{
    CompOption *o;

    o = &ss->opt[SCALE_SCREEN_OPTION_SPACING];
    o->name	  = "spacing";
    o->shortDesc  = N_("Spacing");
    o->longDesc   = N_("Space between windows");
    o->type	  = CompOptionTypeInt;
    o->value.i	  = SCALE_SPACING_DEFAULT;
    o->rest.i.min = SCALE_SPACING_MIN;
    o->rest.i.max = SCALE_SPACING_MAX;

    o = &ss->opt[SCALE_SCREEN_OPTION_SPEED];
    o->name		= "speed";
    o->shortDesc	= N_("Speed");
    o->longDesc		= N_("Scale speed");
    o->type		= CompOptionTypeFloat;
    o->value.f		= SCALE_SPEED_DEFAULT;
    o->rest.f.min	= SCALE_SPEED_MIN;
    o->rest.f.max	= SCALE_SPEED_MAX;
    o->rest.f.precision = SCALE_SPEED_PRECISION;

    o = &ss->opt[SCALE_SCREEN_OPTION_TIMESTEP];
    o->name		= "timestep";
    o->shortDesc	= N_("Timestep");
    o->longDesc		= N_("Scale timestep");
    o->type		= CompOptionTypeFloat;
    o->value.f		= SCALE_TIMESTEP_DEFAULT;
    o->rest.f.min	= SCALE_TIMESTEP_MIN;
    o->rest.f.max	= SCALE_TIMESTEP_MAX;
    o->rest.f.precision = SCALE_TIMESTEP_PRECISION;

    o = &ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH];
    o->name	 = "window_match";
    o->shortDesc = N_("Scale Windows");
    o->longDesc	 = N_("Windows that should be scaled in scale mode");
    o->type	 = CompOptionTypeMatch;

    matchInit (&o->value.match);
    matchAddFromString (&o->value.match, SCALE_WINDOW_MATCH_DEFAULT);

    o = &ss->opt[SCALE_SCREEN_OPTION_DARKEN_BACK];
    o->name      = "darken_back";
    o->shortDesc = N_("Darken Background");
    o->longDesc  = N_("Darken background when scaling windows");
    o->type      = CompOptionTypeBool;
    o->value.b   = SCALE_DARKEN_BACK_DEFAULT;

    o = &ss->opt[SCALE_SCREEN_OPTION_OPACITY];
    o->name	  = "opacity";
    o->shortDesc  = N_("Opacity");
    o->longDesc	  = N_("Amount of opacity in percent");
    o->type	  = CompOptionTypeInt;
    o->value.i    = SCALE_OPACITY_DEFAULT;
    o->rest.i.min = SCALE_OPACITY_MIN;
    o->rest.i.max = SCALE_OPACITY_MAX;

    o = &ss->opt[SCALE_SCREEN_OPTION_ICON];
    o->name	      = "overlay_icon";
    o->shortDesc      = N_("Overlay Icon");
    o->longDesc	      = N_("Overlay an icon on windows once they are scaled");
    o->type	      = CompOptionTypeString;
    o->value.s	      = strdup (SCALE_ICON_DEFAULT);
    o->rest.s.string  = iconOverlayString;
    o->rest.s.nString = N_ICON_TYPE;

    o = &ss->opt[SCALE_SCREEN_OPTION_HOVER_TIME];
    o->name	  = "hover_time";
    o->shortDesc  = N_("Hover Time");
    o->longDesc	  = N_("Time (in ms) before scale mode is terminated when "
		       "hovering over a window");
    o->type	  = CompOptionTypeInt;
    o->value.i    = SCALE_HOVER_TIME_DEFAULT;
    o->rest.i.min = SCALE_HOVER_TIME_MIN;
    o->rest.i.max = SCALE_HOVER_TIME_MAX;
}

static Bool
isNeverScaleWin (CompWindow *w)
{
    if (w->attrib.override_redirect)
	return TRUE;

    if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
	return TRUE;

    return FALSE;
}

static Bool
isScaleWin (CompWindow *w)
{
    SCALE_SCREEN (w->screen);

    if (isNeverScaleWin (w))
	return FALSE;

    if (!ss->type || ss->type == ScaleTypeOutput)
    {
	if (!(*w->screen->focusWindow) (w))
	    return FALSE;
    }

    if (w->state & CompWindowStateSkipPagerMask)
	return FALSE;

    if (w->state & CompWindowStateShadedMask)
	return FALSE;

    if (!w->mapNum || w->attrib.map_state != IsViewable)
	return FALSE;

    switch (ss->type) {
    case ScaleTypeGroup:
	if (ss->clientLeader != w->clientLeader &&
	    ss->clientLeader != w->id)
	    return FALSE;
	break;
    case ScaleTypeOutput:
	if (outputDeviceForWindow(w) != w->screen->currentOutputDev)
	    return FALSE;
    default:
	break;
    }

    if (!matchEval (ss->currentMatch, w))
	return FALSE;

    return TRUE;
}

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

    SCALE_SCREEN (s);

    if (ss->state != SCALE_STATE_NONE)
    {
	WindowPaintAttrib sAttrib = *attrib;
	Bool		  scaled = FALSE;

	SCALE_WINDOW (w);

	if (sw->adjust || sw->slot)
	{
	    SCALE_DISPLAY (s->display);

	    if (w->id	    != sd->selectedWindow &&
		ss->opacity != OPAQUE		  &&
		ss->state   != SCALE_STATE_IN)
	    {
		/* modify opacity of windows that are not active */
		sAttrib.opacity = (sAttrib.opacity * ss->opacity) >> 16;
	    }

	    scaled = TRUE;

	    mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
	}
	else if (ss->state != SCALE_STATE_IN)
	{
	    if (ss->darkenBack)
	    {
		/* modify brightness of the other windows */
		sAttrib.brightness = sAttrib.brightness / 2;
	    }

	    /* hide windows on this output that are not in scale mode */
	    if (!isNeverScaleWin (w) &&
		outputDeviceForWindow (w) == s->currentOutputDev)
	    {
		sAttrib.opacity = 0;
	    }
	}

	UNWRAP (ss, s, paintWindow);
	status = (*s->paintWindow) (w, &sAttrib, transform, region, mask);
	WRAP (ss, s, paintWindow, scalePaintWindow);

	if (scaled)
	{
	    FragmentAttrib fragment;
	    CompTransform  wTransform = *transform;

	    if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
		return FALSE;

	    initFragmentAttrib (&fragment, &w->lastPaint);

	    if (w->alpha || fragment.opacity != OPAQUE)
		mask |= PAINT_WINDOW_TRANSLUCENT_MASK;

	    matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
	    matrixScale (&wTransform, sw->scale, sw->scale, 0.0f);
	    matrixTranslate (&wTransform,
			     sw->tx / sw->scale - w->attrib.x,
			     sw->ty / sw->scale - w->attrib.y,
			     0.0f);

	    glPushMatrix ();
	    glLoadMatrixf (wTransform.m);

	    (*s->drawWindow) (w, &wTransform, &fragment, region,
			      mask | PAINT_WINDOW_TRANSFORMED_MASK);

	    glPopMatrix ();
	}

	if ((ss->iconOverlay != ScaleIconNone) && scaled)
	{
	    CompIcon *icon;

	    icon = getWindowIcon (w, 96, 96);
	    if (!icon)
		icon = w->screen->defaultIcon;

	    if (icon && (icon->texture.name || iconToTexture (w->screen, icon)))
	    {
		REGION iconReg;
		float  scale;
		float  x, y;
		int    width, height;
		int    scaledWinWidth, scaledWinHeight;
		float  ds;

		scaledWinWidth  = w->width  * sw->scale;
		scaledWinHeight = w->height * sw->scale;

		switch (ss->iconOverlay) {
		case ScaleIconNone:
		case ScaleIconEmblem:
		    scale = 1.0f;
		    break;
		case ScaleIconBig:
		default:
		    sAttrib.opacity /= 3;
		    scale = MIN (((float) scaledWinWidth / icon->width),
				 ((float) scaledWinHeight / icon->height));
		    break;
		}

		width  = icon->width  * scale;
		height = icon->height * scale;

		switch (ss->iconOverlay) {
		case ScaleIconNone:
		case ScaleIconEmblem:
		    x = w->attrib.x + scaledWinWidth - icon->width;
		    y = w->attrib.y + scaledWinHeight - icon->height;
		    break;
		case ScaleIconBig:
		default:
		    x = w->attrib.x + scaledWinWidth / 2 - width / 2;
		    y = w->attrib.y + scaledWinHeight / 2 - height / 2;
		    break;
		}

		x += sw->tx;
		y += sw->ty;

		if (sw->slot)
		{
		    sw->delta =
			fabs (sw->slot->x1 - w->attrib.x) +
			fabs (sw->slot->y1 - w->attrib.y) +
			fabs (1.0f - sw->slot->scale) * 500.0f;
		}

		if (sw->delta)
		{
		    float o;

		    ds =
			fabs (sw->tx) +
			fabs (sw->ty) +
			fabs (1.0f - sw->scale) * 500.0f;

		    if (ds > sw->delta)
			ds = sw->delta;

		    o = ds / sw->delta;

		    if (sw->slot)
		    {
			if (o < sw->lastThumbOpacity)
			    o = sw->lastThumbOpacity;
		    }
		    else
		    {
			if (o > sw->lastThumbOpacity)
			    o = 0.0f;
		    }

		    sw->lastThumbOpacity = o;

		    sAttrib.opacity = sAttrib.opacity * o;
		}

		mask |= PAINT_WINDOW_BLEND_MASK;

		iconReg.rects    = &iconReg.extents;
		iconReg.numRects = 1;

		iconReg.extents.x1 = 0;
		iconReg.extents.y1 = 0;
		iconReg.extents.x2 = iconReg.extents.x1 + width;
		iconReg.extents.y2 = iconReg.extents.y1 + height;

		w->vCount = w->indexCount = 0;
		if (iconReg.extents.x1 < iconReg.extents.x2 &&
		    iconReg.extents.y1 < iconReg.extents.y2)
		    (*w->screen->addWindowGeometry) (w,
						     &icon->texture.matrix, 1,
						     &iconReg, &iconReg);

		if (w->vCount)
		{
		    FragmentAttrib fragment;
		    CompTransform  wTransform = *transform;

		    initFragmentAttrib (&fragment, &sAttrib);

		    matrixScale (&wTransform, scale, scale, 1.0f);
		    matrixTranslate (&wTransform, x / scale, y / scale, 0.0f);

		    glPushMatrix ();
		    glLoadMatrixf (wTransform.m);

		    (*w->screen->drawWindowTexture) (w,
						     &icon->texture, &fragment,
						     mask);

		    glPopMatrix ();
		}
	    }
	}
    }
    else
    {
	UNWRAP (ss, s, paintWindow);
	status = (*s->paintWindow) (w, attrib, transform, region, mask);
	WRAP (ss, s, paintWindow, scalePaintWindow);
    }

    return status;
}

static int
compareWindowsDistance (const void *elem1,
			const void *elem2)
{
    CompWindow *w1 = *((CompWindow **) elem1);
    CompWindow *w2 = *((CompWindow **) elem2);

    SCALE_SCREEN (w1->screen);

    return
	GET_SCALE_WINDOW (w1, ss)->distance -
	GET_SCALE_WINDOW (w2, ss)->distance;
}

static void
layoutSlots (CompScreen *s)
{
    int i, j, x, y, width, height, lines, n;
    int x1, y1, x2, y2;

    SCALE_SCREEN (s);

    ss->nSlots = 0;

    lines = sqrt (ss->nWindows + 1);

    getCurrentOutputExtents (s, &x1, &y1, &x2, &y2);

    if (s->workArea.x > x1)
	x1 = s->workArea.x;
    if (s->workArea.x + s->workArea.width < x2)
	x2 = s->workArea.x + s->workArea.width;
    if (s->workArea.y > y1)
	y1 = s->workArea.y;
    if (s->workArea.y + s->workArea.height < y2)
	y2 = s->workArea.y + s->workArea.height;

    y      = y1 + ss->spacing;
    height = ((y2 - y1) - (lines + 1) * ss->spacing) / lines;

    for (i = 0; i < lines; i++)
    {
	n = MIN (ss->nWindows - ss->nSlots,
		 ceilf ((float) ss->nWindows / lines));

	x     = x1 + ss->spacing;
	width = ((x2 - x1) - (n + 1) * ss->spacing) / n;

	for (j = 0; j < n; j++)
	{
	    ss->slots[ss->nSlots].x1 = x;
	    ss->slots[ss->nSlots].y1 = y;
	    ss->slots[ss->nSlots].x2 = x + width;
	    ss->slots[ss->nSlots].y2 = y + height;

	    ss->slots[ss->nSlots].filled = FALSE;

	    x += width + ss->spacing;

	    ss->nSlots++;
	}

	y += height + ss->spacing;
    }
}

static void
findBestSlots (CompScreen *s)
{
    CompWindow *w;
    int        i, j, d, d0 = 0;
    float      sx, sy, cx, cy;

    SCALE_SCREEN (s);

    for (i = 0; i < ss->nWindows; i++)
    {
	w = ss->windows[i];

	SCALE_WINDOW (w);

	if (sw->slot)
	    continue;

	sw->sid      = 0;
	sw->distance = MAXSHORT;

	for (j = 0; j < ss->nSlots; j++)
	{
	    if (!ss->slots[j].filled)
	    {
		sx = (ss->slots[j].x2 + ss->slots[j].x1) / 2;
		sy = (ss->slots[j].y2 + ss->slots[j].y1) / 2;

		cx = w->serverX + w->width  / 2;
		cy = w->serverY + w->height / 2;

		cx -= sx;
		cy -= sy;

		d = sqrt (cx * cx + cy * cy);
		if (d0 + d < sw->distance)
		{
		    sw->sid      = j;
		    sw->distance = d0 + d;
		}
	    }
	}

	d0 += sw->distance;
    }
}

static Bool
fillInWindows (CompScreen *s)
{
    CompWindow *w;
    int        i, width, height;
    float      sx, sy, cx, cy;

    SCALE_SCREEN (s);

    for (i = 0; i < ss->nWindows; i++)
    {
	w = ss->windows[i];

	SCALE_WINDOW (w);

	if (!sw->slot)
	{
	    if (ss->slots[sw->sid].filled)
		return TRUE;

	    sw->slot = &ss->slots[sw->sid];

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

	    sx = (float) (sw->slot->x2 - sw->slot->x1) / width;
	    sy = (float) (sw->slot->y2 - sw->slot->y1) / height;

	    sw->slot->scale = MIN (MIN (sx, sy), 1.0f);

	    sx = width  * sw->slot->scale;
	    sy = height * sw->slot->scale;
	    cx = (sw->slot->x1 + sw->slot->x2) / 2;
	    cy = (sw->slot->y1 + sw->slot->y2) / 2;

	    cx += w->input.left * sw->slot->scale;
	    cy += w->input.top  * sw->slot->scale;

	    sw->slot->x1 = cx - sx / 2;
	    sw->slot->y1 = cy - sy / 2;
	    sw->slot->x2 = cx + sx / 2;
	    sw->slot->y2 = cy + sy / 2;

	    sw->slot->filled = TRUE;

	    sw->lastThumbOpacity = 0.0f;

	    sw->adjust = TRUE;
	}
    }

    return FALSE;
}

static Bool
layoutThumbs (CompScreen *s)
{
    CompWindow *w;

    SCALE_SCREEN (s);

    ss->nWindows = 0;

    /* add windows scale list, top most window first */
    for (w = s->reverseWindows; w; w = w->prev)
    {
	SCALE_WINDOW (w);

	if (sw->slot)
	    sw->adjust = TRUE;

	sw->slot = 0;

	if (!isScaleWin (w))
	    continue;

	if (ss->windowsSize <= ss->nWindows)
	{
	    ss->windows = realloc (ss->windows,
				   sizeof (CompWindow *) * (ss->nWindows + 32));
	    if (!ss->windows)
		return FALSE;

	    ss->windowsSize = ss->nWindows + 32;
	}

	ss->windows[ss->nWindows++] = w;
    }

    if (ss->nWindows == 0)
	return FALSE;

    if (ss->slotsSize < ss->nWindows)
    {
	ss->slots = realloc (ss->slots, sizeof (ScaleSlot) * ss->nWindows);
	if (!ss->slots)
	    return FALSE;

	ss->slotsSize = ss->nWindows;
    }

    /* create a grid of slots */
    layoutSlots (s);

    do
    {
	/* find most appropriate slots for windows */
	findBestSlots (s);

	/* sort windows, window with closest distance to a slot first */
	qsort (ss->windows, ss->nWindows, sizeof (CompWindow *),
	       compareWindowsDistance);

    } while (fillInWindows (s));

    return TRUE;
}

static int
adjustScaleVelocity (CompWindow *w)
{
    float dx, dy, ds, adjust, amount;
    float x1, y1, scale;

    SCALE_WINDOW (w);

    if (sw->slot)
    {
	x1 = sw->slot->x1;
	y1 = sw->slot->y1;
	scale = sw->slot->scale;
    }
    else
    {
	x1 = w->attrib.x;
	y1 = w->attrib.y;
	scale = 1.0f;
    }

    dx = x1 - (w->attrib.x + sw->tx);

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

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

    dy = y1 - (w->attrib.y + sw->ty);

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

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

    ds = scale - sw->scale;

    adjust = ds * 0.1f;
    amount = fabs (ds) * 7.0f;
    if (amount < 0.01f)
	amount = 0.01f;
    else if (amount > 0.15f)
	amount = 0.15f;

    sw->scaleVelocity = (amount * sw->scaleVelocity + adjust) /
	(amount + 1.0f);

    if (fabs (dx) < 0.1f && fabs (sw->xVelocity) < 0.2f &&
	fabs (dy) < 0.1f && fabs (sw->yVelocity) < 0.2f &&
	fabs (ds) < 0.001f && fabs (sw->scaleVelocity) < 0.002f)
    {
	sw->xVelocity = sw->yVelocity = sw->scaleVelocity = 0.0f;
	sw->tx = x1 - w->attrib.x;
	sw->ty = y1 - w->attrib.y;
	sw->scale = scale;

	return 0;
    }

    return 1;
}

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

    SCALE_SCREEN (s);

    if (ss->state != SCALE_STATE_NONE)
	mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;

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

    return status;
}

static void
scalePreparePaintScreen (CompScreen *s,
			 int	     msSinceLastPaint)
{
    SCALE_SCREEN (s);

    if (ss->state != SCALE_STATE_NONE && ss->state != SCALE_STATE_WAIT)
    {
	CompWindow *w;
	int        steps;
	float      amount, chunk;

	amount = msSinceLastPaint * 0.05f * ss->speed;
	steps  = amount / (0.5f * ss->timestep);
	if (!steps) steps = 1;
	chunk  = amount / (float) steps;

	while (steps--)
	{
	    ss->moreAdjust = 0;

	    for (w = s->windows; w; w = w->next)
	    {
		SCALE_WINDOW (w);

		if (sw->adjust)
		{
		    sw->adjust = adjustScaleVelocity (w);

		    ss->moreAdjust |= sw->adjust;

		    sw->tx += sw->xVelocity * chunk;
		    sw->ty += sw->yVelocity * chunk;
		    sw->scale += sw->scaleVelocity * chunk;
		}
	    }

	    if (!ss->moreAdjust)
		break;
	}
    }

    UNWRAP (ss, s, preparePaintScreen);
    (*s->preparePaintScreen) (s, msSinceLastPaint);
    WRAP (ss, s, preparePaintScreen, scalePreparePaintScreen);
}

static void
scaleDonePaintScreen (CompScreen *s)
{
    SCALE_SCREEN (s);

    if (ss->state != SCALE_STATE_NONE)
    {
	if (ss->moreAdjust)
	{
	    damageScreen (s);
	}
	else
	{
	    if (ss->state == SCALE_STATE_IN)
		ss->state = SCALE_STATE_NONE;
	    else if (ss->state == SCALE_STATE_OUT)
		ss->state = SCALE_STATE_WAIT;
	}
    }

    UNWRAP (ss, s, donePaintScreen);
    (*s->donePaintScreen) (s);
    WRAP (ss, s, donePaintScreen, scaleDonePaintScreen);
}

static CompWindow *
scaleCheckForWindowAt (CompScreen *s,
		       int        x,
		       int        y)
{
    int        x1, y1, x2, y2;
    CompWindow *w;

    for (w = s->reverseWindows; w; w = w->prev)
    {
	SCALE_WINDOW (w);

	if (sw->slot)
	{
	    x1 = w->attrib.x - w->input.left * sw->scale;
	    y1 = w->attrib.y - w->input.top  * sw->scale;
	    x2 = w->attrib.x + (w->width  + w->input.right)  * sw->scale;
	    y2 = w->attrib.y + (w->height + w->input.bottom) * sw->scale;

	    x1 += sw->tx;
	    y1 += sw->ty;
	    x2 += sw->tx;
	    y2 += sw->ty;

	    if (x1 <= x && y1 <= y && x2 > x && y2 > y)
		return w;
	}
    }

    return 0;
}

static void
scaleTransformXY(CompWindow* w, int x, int y, int *newX, int *newY)
{
    int x1, y1;

    SCALE_WINDOW(w);

    x1 = w->attrib.x + sw->tx;
    y1 = w->attrib.y + sw->ty;
    
    *newX = (x - x1) / sw->scale;
    *newY = (y - y1) / sw->scale;
}

static void
sendViewportMoveRequest (CompScreen *s,
			 int	    x,
			 int	    y)
{
    XEvent xev;

    xev.xclient.type    = ClientMessage;
    xev.xclient.display = s->display->display;
    xev.xclient.format  = 32;

    xev.xclient.message_type = s->display->desktopViewportAtom;
    xev.xclient.window	     = s->root;

    xev.xclient.data.l[0] = x;
    xev.xclient.data.l[1] = y;
    xev.xclient.data.l[2] = 0;
    xev.xclient.data.l[3] = 0;
    xev.xclient.data.l[4] = 0;

    XSendEvent (s->display->display,
		s->root,
		FALSE,
		SubstructureRedirectMask | SubstructureNotifyMask,
		&xev);
}

static void
sendDndStatusMessage (CompScreen *s,
		      Window	 source)
{
    XEvent xev;

    SCALE_SCREEN (s);

    xev.xclient.type    = ClientMessage;
    xev.xclient.display = s->display->display;
    xev.xclient.format  = 32;

    xev.xclient.message_type = s->display->xdndStatusAtom;
    xev.xclient.window	     = source;

    xev.xclient.data.l[0] = ss->dndTarget;
    xev.xclient.data.l[1] = 2;
    xev.xclient.data.l[2] = 0;
    xev.xclient.data.l[3] = 0;
    xev.xclient.data.l[4] = None;

    XSendEvent (s->display->display, source, FALSE, 0, &xev);
}

static Bool
scaleTerminate (CompDisplay     *d,
		CompAction      *action,
		CompActionState state,
		CompOption      *option,
		int	        nOption)
{
    CompScreen *s;
    Window     xid;

    SCALE_DISPLAY (d);

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

    for (s = d->screens; s; s = s->next)
    {
	SCALE_SCREEN (s);

	if (xid && s->root != xid)
	    continue;

	if (ss->grab)
	{
	    /*if (ss->grabIndex)
	    {
		removeScreenGrab (s, ss->grabIndex, 0);
		ss->grabIndex = 0;
	    }*/
	    
	    if (sd->xevie)
	    {
		sd->xevie = FALSE;
		XevieEnd(d->display);
	    }

	    if (ss->dndTarget)
		XUnmapWindow (d->display, ss->dndTarget);

	    ss->grab = FALSE;

	    if (ss->state != SCALE_STATE_NONE)
	    {
		CompWindow *w;

		for (w = s->windows; w; w = w->next)
		{
		    SCALE_WINDOW (w);

		    if (sw->slot)
		    {
			sw->slot = 0;
			sw->adjust = TRUE;
		    }
		}

		if (ss->state != SCALE_STATE_IN)
		{
		    w = findWindowAtScreen (s, sd->lastActiveWindow);
		    if (w)
		    {
			int x, y;

			activateWindow (w);

			defaultViewportForWindow (w, &x, &y);

			if (x != s->x || y != s->y)
			    sendViewportMoveRequest (s,
						     x * s->width,
						     y * s->height);
		    }
		}

		ss->state = SCALE_STATE_IN;

		damageScreen (s);
	    }

	    sd->lastActiveNum = 0;
	}
    }

    return FALSE;
}

static Bool
scaleEnsureDndRedirectWindow (CompScreen *s)
{
    SCALE_SCREEN (s);

    if (!ss->dndTarget)
    {
	XSetWindowAttributes attr;
	long		     xdndVersion = 3;

	attr.override_redirect = TRUE;

	ss->dndTarget = XCreateWindow (s->display->display,
				       s->root,
				       0, 0, 1, 1, 0,
				       CopyFromParent,
				       InputOnly,
				       CopyFromParent,
				       CWOverrideRedirect, &attr);

	XChangeProperty (s->display->display, ss->dndTarget,
			 s->display->xdndAwareAtom,
			 XA_ATOM, 32, PropModeReplace,
			 (unsigned char *) &xdndVersion, 1);
    }

    XMoveResizeWindow (s->display->display, ss->dndTarget,
		       0, 0, s->width, s->height);
    XMapRaised (s->display->display, ss->dndTarget);

    return TRUE;
}

static Bool
scaleInitiateCommon (CompScreen      *s,
		     CompAction      *action,
		     CompActionState state,
		     CompOption      *option,
		     int	     nOption)
{
    CompMatch *match;

    SCALE_DISPLAY (s->display);
    SCALE_SCREEN (s);

    if (otherScreenGrabExist (s, "scale", 0))
	return FALSE;

    ss->currentMatch = &ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH].value.match;

    match = getMatchOptionNamed (option, nOption, "match", NULL);
    if (match)
    {
	matchFini (&ss->match);
	matchInit (&ss->match);
	if (matchCopy (&ss->match, match))
	{
	    matchUpdate (s->display, &ss->match);
	    ss->currentMatch = &ss->match;
	}
    }

    if (!layoutThumbs (s))
	return FALSE;

    if (state & CompActionStateInitEdgeDnd)
    {
	if (scaleEnsureDndRedirectWindow (s))
	    ss->grab = TRUE;
    }
    else
    {
	/*ss->grabIndex = pushScreenGrab (s, ss->cursor, "scale");
	if (ss->grabIndex)
	    ss->grab = TRUE;*/
	if(XevieStart(s->display->display))
	{
	    ss->grab = TRUE;
	    sd->xevie = TRUE;
	}
    }

    if (ss->grab)
    {
	if (!sd->lastActiveNum)
	    sd->lastActiveNum = s->activeNum - 1;

	sd->lastActiveWindow = s->display->activeWindow;
	sd->selectedWindow   = s->display->activeWindow;

	ss->state = SCALE_STATE_OUT;

	damageScreen (s);
    }

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

    if (state & CompActionStateInitKey)
	action->state |= CompActionStateTermKey;

    return FALSE;
}

static Bool
scaleInitiate (CompDisplay     *d,
	       CompAction      *action,
	       CompActionState state,
	       CompOption      *option,
	       int	       nOption)
{
    CompScreen *s;
    Window     xid;

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

    s = findScreenAtDisplay (d, xid);
    if (s)
    {
	SCALE_SCREEN (s);

	if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
	{
	    ss->type = ScaleTypeNormal;
	    return scaleInitiateCommon (s, action, state, option, nOption);
	}
    }

    return FALSE;
}

static Bool
scaleInitiateAll (CompDisplay     *d,
		  CompAction      *action,
		  CompActionState state,
		  CompOption      *option,
		  int		  nOption)
{
    CompScreen *s;
    Window     xid;

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

    s = findScreenAtDisplay (d, xid);
    if (s)
    {
	SCALE_SCREEN (s);

	if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
	{
	    ss->type = ScaleTypeAll;
	    return scaleInitiateCommon (s, action, state, option, nOption);
	}
    }

    return FALSE;
}

static Bool
scaleInitiateGroup (CompDisplay     *d,
		    CompAction      *action,
		    CompActionState state,
		    CompOption      *option,
		    int		    nOption)
{
    CompScreen *s;
    Window     xid;

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

    s = findScreenAtDisplay (d, xid);
    if (s)
    {
	SCALE_SCREEN (s);

	if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
	{
	    CompWindow *w;

	    w = findWindowAtDisplay (d, getIntOptionNamed (option, nOption,
							   "window", 0));
	    if (w)
	    {
		ss->type	 = ScaleTypeGroup;
		ss->clientLeader = (w->clientLeader) ? w->clientLeader : w->id;

		return scaleInitiateCommon (s, action, state, option, nOption);
	    }
	}
    }

    return FALSE;
}

static Bool
scaleInitiateOutput (CompDisplay     *d,
		     CompAction      *action,
		     CompActionState state,
		     CompOption      *option,
		     int	     nOption)
{
    CompScreen *s;
    Window     xid;

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

    s = findScreenAtDisplay (d, xid);
    if (s)
    {
	SCALE_SCREEN (s);

	if (ss->state != SCALE_STATE_WAIT && ss->state != SCALE_STATE_OUT)
	{
	    ss->type = ScaleTypeOutput;
	    return scaleInitiateCommon (s, action, state, option, nOption);
	}
    }

    return FALSE;
}

static void
scaleSelectWindow (CompWindow *w)

{
    SCALE_DISPLAY (w->screen->display);

    if (sd->selectedWindow != w->id)
    {
	CompWindow *old, *new;

	old = findWindowAtScreen (w->screen, sd->selectedWindow);
	new = findWindowAtScreen (w->screen, w->id);

	sd->selectedWindow = w->id;

	if (old)
	    addWindowDamage (old);

	addWindowDamage (new);
    }
}

static Bool
scaleSelectWindowAt (CompScreen *s,
		     int	 x,
		     int	 y,
		     Bool	 moveInputFocus)

{
    CompWindow *w;

    w = scaleCheckForWindowAt (s, x, y);
    if (w && isScaleWin (w))
    {
	SCALE_DISPLAY (s->display);

	scaleSelectWindow (w);

	if (moveInputFocus)
	{
	    sd->lastActiveNum    = w->activeNum;
	    sd->lastActiveWindow = w->id;

	    moveInputFocusToWindow (w);
	}

	return TRUE;
    }

    return FALSE;
}

static void
scaleMoveFocusWindow (CompScreen *s,
		      int	 dx,
		      int	 dy)

{
    CompWindow *active;
    CompWindow *focus = NULL;

    active = findWindowAtScreen (s, s->display->activeWindow);
    if (active)
    {
	SCALE_WINDOW (active);

	if (sw->slot)
	{
	    SCALE_SCREEN (s);

	    CompWindow *w;
	    ScaleSlot  *slot;
	    int	       x, y, cx, cy, d, min = MAXSHORT;

	    cx = (sw->slot->x1 + sw->slot->x2) / 2;
	    cy = (sw->slot->y1 + sw->slot->y2) / 2;

	    for (w = s->windows; w; w = w->next)
	    {
		slot = GET_SCALE_WINDOW (w, ss)->slot;
		if (!slot)
		    continue;

		x = (slot->x1 + slot->x2) / 2;
		y = (slot->y1 + slot->y2) / 2;

		d = abs (x - cx) + abs (y - cy);
		if (d < min)
		{
		    if ((dx > 0 && slot->x1 < sw->slot->x2) ||
			(dx < 0 && slot->x2 > sw->slot->x1) ||
			(dy > 0 && slot->y1 < sw->slot->y2) ||
			(dy < 0 && slot->y2 > sw->slot->y1))
			continue;

		    min   = d;
		    focus = w;
		}
	    }
	}
    }

    /* move focus to the last focused window if no slot window is currently
       focused */
    if (!focus)
    {
	CompWindow *w;

	SCALE_SCREEN (s);

	for (w = s->windows; w; w = w->next)
	{
	    if (!GET_SCALE_WINDOW (w, ss)->slot)
		continue;

	    if (!focus || focus->activeNum < w->activeNum)
		focus = w;
	}
    }

    if (focus)
    {
	SCALE_DISPLAY (s->display);

	scaleSelectWindow (focus);

	sd->lastActiveNum    = focus->activeNum;
	sd->lastActiveWindow = focus->id;

	moveInputFocusToWindow (focus);
    }
}

static void
scaleWindowRemove (CompDisplay *d,
		   Window      id)
{
    CompWindow *w;

    w = findWindowAtDisplay (d, id);
    if (w)
    {
	SCALE_SCREEN (w->screen);

	if (ss->state != SCALE_STATE_NONE && ss->state != SCALE_STATE_IN)
	{
	    int i;

	    for (i = 0; i < ss->nWindows; i++)
	    {
		if (ss->windows[i] == w)
		{
		    if (layoutThumbs (w->screen))
		    {
			ss->state = SCALE_STATE_OUT;
			damageScreen (w->screen);
			break;
		    }
		}
	    }
	}
    }
}

static Bool
scaleHoverTimeout (void *closure)
{
    CompScreen *s = closure;

    SCALE_SCREEN (s);
    SCALE_DISPLAY (s->display);

    if (ss->grab && ss->state != SCALE_STATE_IN)
    {
	CompWindow *w;
	CompOption o;
	CompAction *action =
	    &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action;

	w = findWindowAtDisplay (s->display, sd->selectedWindow);
	if (w)
	{
	    sd->lastActiveNum    = w->activeNum;
	    sd->lastActiveWindow = w->id;

	    moveInputFocusToWindow (w);
	}

	o.type    = CompOptionTypeInt;
	o.name    = "root";
	o.value.i = s->root;

	scaleTerminate (s->display, action, 0, &o, 1);
    }

    ss->hoverHandle = 0;

    return FALSE;
}

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

    SCALE_DISPLAY (d);

    switch (event->type) {
    /*case KeyPress:
	s = findScreenAtDisplay (d, event->xkey.root);
	if (s)
	{
	    SCALE_SCREEN (s);

	    if (ss->grabIndex)
	    {
		if (event->xkey.keycode == sd->leftKeyCode)
		    scaleMoveFocusWindow (s, -1, 0);
		else if (event->xkey.keycode == sd->rightKeyCode)
		    scaleMoveFocusWindow (s, 1, 0);
		else if (event->xkey.keycode == sd->upKeyCode)
		    scaleMoveFocusWindow (s, 0, -1);
		else if (event->xkey.keycode == sd->downKeyCode)
		    scaleMoveFocusWindow (s, 0, 1);
	    }
	}
	break;
    case ButtonPress:
	if (event->xbutton.button == Button1)
	{
	    s = findScreenAtDisplay (d, event->xbutton.root);
	    if (s)
	    {
		CompAction *action =
		    &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action;

		SCALE_SCREEN (s);

		if (ss->grabIndex && ss->state != SCALE_STATE_IN)
		{
		    CompOption o;

		    o.type    = CompOptionTypeInt;
		    o.name    = "root";
		    o.value.i = s->root;

		    if (scaleSelectWindowAt (s,
					     event->xbutton.x_root,
					     event->xbutton.y_root,
					     TRUE))
		    {
			scaleTerminate (d, action, 0, &o, 1);
		    }
		    else if (event->xbutton.x_root > s->workArea.x &&
			     event->xbutton.x_root < (s->workArea.x +
						      s->workArea.width) &&
			     event->xbutton.y_root > s->workArea.y &&
			     event->xbutton.y_root < (s->workArea.y +
						      s->workArea.height))
		    {
			scaleTerminate (d, action, 0, &o, 1);
			(*s->enterShowDesktopMode) (s);
		    }
		}
	    }
	}
	break;*/
    case MotionNotify:
	s = findScreenAtDisplay (d, event->xmotion.root);
	if (s)
	{
	    SCALE_SCREEN (s);

	    if (ss->grab && ss->state != SCALE_STATE_IN)
	    {
		Bool focus;

		focus = !d->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b;

		scaleSelectWindowAt (s,
				     event->xmotion.x_root,
				     event->xmotion.y_root,
				     focus);
	    }
	}
	break;
    case ClientMessage:
	if (event->xclient.message_type == d->xdndPositionAtom)
	{
	    CompWindow *w;

	    w = findWindowAtDisplay (d, event->xclient.window);
	    if (w)
	    {
		Bool focus;

		SCALE_SCREEN (w->screen);

		s = w->screen;

		focus = !d->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b;

		if (w->id == ss->dndTarget)
		    sendDndStatusMessage (w->screen, event->xclient.data.l[0]);

		if (ss->grab			&&
		    ss->state != SCALE_STATE_IN &&
		    w->id == ss->dndTarget)
		{
		    int x = event->xclient.data.l[2] >> 16;
		    int y = event->xclient.data.l[2] & 0xffff;

		    w = scaleCheckForWindowAt (s, x, y);
		    if (w && isScaleWin (w))
		    {
			int time;

			time = ss->opt[SCALE_SCREEN_OPTION_HOVER_TIME].value.i;

			if (ss->hoverHandle)
			{
			    if (w->id != sd->selectedWindow)
			    {
				compRemoveTimeout (ss->hoverHandle);
				ss->hoverHandle = 0;
			    }
			}

			if (!ss->hoverHandle)
			    ss->hoverHandle =
				compAddTimeout (time,
						scaleHoverTimeout,
						s);

			scaleSelectWindowAt (s, x, y, focus);
		    }
		    else
		    {
			if (ss->hoverHandle)
			    compRemoveTimeout (ss->hoverHandle);

			ss->hoverHandle = 0;
		    }
		}
	    }
	}
	else if (event->xclient.message_type == d->xdndDropAtom ||
		 event->xclient.message_type == d->xdndLeaveAtom)
	{
	    CompWindow *w;

	    w = findWindowAtDisplay (d, event->xclient.window);
	    if (w)
	    {
		CompAction *action =
		    &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action;

		SCALE_SCREEN (w->screen);

		if (ss->grab			&&
		    ss->state != SCALE_STATE_IN &&
		    w->id == ss->dndTarget)
		{
		    CompOption o;

		    o.type    = CompOptionTypeInt;
		    o.name    = "root";
		    o.value.i = w->screen->root;

		    scaleTerminate (d, action, 0, &o, 1);
		}
	    }
	}
    default:
	break;
    }

    UNWRAP (sd, d, handleEvent);
    (*d->handleEvent) (d, event);
    WRAP (sd, d, handleEvent, scaleHandleEvent);

    switch (event->type) {
    case UnmapNotify:
	scaleWindowRemove (d, event->xunmap.window);
	break;
    case DestroyNotify:
	scaleWindowRemove (d, event->xdestroywindow.window);
	break;
    }
    
    if (sd->xevie)
    {
	XEvent newEvent = *event;
	Bool eventModified = FALSE;
	Bool xevieEvent = FALSE;
	
	switch (newEvent.type)
	{
	case KeyPress:
	case KeyRelease:
	    xevieEvent = TRUE;
	    break;
	
	case ButtonPress:
	case ButtonRelease:
	    xevieEvent = TRUE;
	    s = findScreenAtDisplay (d, newEvent.xbutton.root);
	    if (s)
	    {
		CompWindow* w = scaleCheckForWindowAt(s, newEvent.xbutton.x_root, newEvent.xbutton.y_root);
		
		if (w)
		{
		    int newX, newY;
		    scaleTransformXY(w, newEvent.xbutton.x_root, newEvent.xbutton.y_root, &newX, &newY);
		
		    newEvent.xbutton.window = w->id;
		    newEvent.xbutton.x = newX;
		    newEvent.xbutton.y = newY;
		    printf("%d %d\n", newX, newY);
		    eventModified = TRUE;
		    
		    newEvent.xbutton.x_root = newX + WIN_X(w);
		    newEvent.xbutton.y_root = newY + WIN_Y(w);
	        }
	    }
	    break;
	
	case MotionNotify:
	    xevieEvent = TRUE;
	    
	    s = findScreenAtDisplay (d, newEvent.xmotion.root);
	    if (s)
	    {
		CompWindow* w = scaleCheckForWindowAt(s, newEvent.xmotion.x_root, newEvent.xmotion.y_root);
		
		if (w)
		{
		    int newX, newY;
		    scaleTransformXY(w, newEvent.xmotion.x_root, newEvent.xmotion.y_root, &newX, &newY);
		
		    newEvent.xmotion.window = w->id;
		    newEvent.xmotion.x = newX;
		    newEvent.xmotion.y = newY;
		    printf("%d %d\n", newX, newY);
		    eventModified = TRUE;
		    
		    newEvent.xmotion.x_root = newX + WIN_X(w);
		    newEvent.xmotion.y_root = newY + WIN_Y(w);
	        }
	    }
	    
	    break;
	
	default:
	    break;
	}
	
	if (xevieEvent)
	    XevieSendEvent(d->display, &newEvent, (eventModified)? XEVIE_MODIFIED: XEVIE_UNMODIFIED);
    }
}

static Bool
scaleDamageWindowRect (CompWindow *w,
		       Bool	  initial,
		       BoxPtr     rect)
{
    Bool status = FALSE;

    SCALE_SCREEN (w->screen);

    if (initial)
    {
	if (ss->grab && isScaleWin (w))
	{
	    if (layoutThumbs (w->screen))
	    {
		ss->state = SCALE_STATE_OUT;
		damageScreen (w->screen);
	    }
	}
    }
    else if (ss->state == SCALE_STATE_WAIT)
    {
	SCALE_WINDOW (w);

	if (sw->slot)
	{
	    damageTransformedWindowRect (w,
					 sw->scale,
					 sw->scale,
					 sw->tx,
					 sw->ty,
					 rect);

	    status = TRUE;
	}
    }

    UNWRAP (ss, w->screen, damageWindowRect);
    status |= (*w->screen->damageWindowRect) (w, initial, rect);
    WRAP (ss, w->screen, damageWindowRect, scaleDamageWindowRect);

    return status;
}

static CompOption *
scaleGetDisplayOptions (CompPlugin  *plugin,
			CompDisplay *display,
			int	    *count)
{
    SCALE_DISPLAY (display);

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

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

    SCALE_DISPLAY (display);

    o = compFindOption (sd->opt, NUM_OPTIONS (sd), name, &index);

    if (!o)
	return FALSE;

    switch (index) {
    case SCALE_DISPLAY_OPTION_INITIATE:
    case SCALE_DISPLAY_OPTION_INITIATE_ALL:
    case SCALE_DISPLAY_OPTION_INITIATE_GROUP:
    case SCALE_DISPLAY_OPTION_INITIATE_OUTPUT:
	if (setDisplayAction (display, o, value))
	    return TRUE;
    default:
	break;
    }

    return FALSE;
}

static void
scaleDisplayInitOptions (ScaleDisplay *sd,
			 Display      *display)
{
    CompOption *o;

    o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE];
    o->name			  = "initiate";
    o->shortDesc		  = N_("Initiate Window Picker");
    o->longDesc			  = N_("Layout and start transforming windows");
    o->type			  = CompOptionTypeAction;
    o->value.action.initiate	  = scaleInitiate;
    o->value.action.terminate	  = scaleTerminate;
    o->value.action.bell	  = FALSE;
    o->value.action.edgeMask	  = (1 << SCREEN_EDGE_TOPRIGHT);
    o->value.action.state	  = CompActionStateInitEdge;
    o->value.action.state	 |= CompActionStateInitEdgeDnd;
    o->value.action.type	  = CompBindingTypeKey;
    o->value.action.state	 |= CompActionStateInitKey;
    o->value.action.state	 |= CompActionStateInitButton;
    o->value.action.key.modifiers = SCALE_INITIATE_MODIFIERS_DEFAULT;
    o->value.action.key.keycode   =
	XKeysymToKeycode (display,
			  XStringToKeysym (SCALE_INITIATE_KEY_DEFAULT));

    o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL];
    o->name		      = "initiate_all";
    o->shortDesc	      = N_("Initiate Window Picker For All Windows");
    o->longDesc		      = N_("Layout and start transforming all windows");
    o->type		      = CompOptionTypeAction;
    o->value.action.initiate  = scaleInitiateAll;
    o->value.action.terminate = scaleTerminate;
    o->value.action.bell      = FALSE;
    o->value.action.edgeMask  = 0;
    o->value.action.state     = CompActionStateInitEdge;
    o->value.action.state    |= CompActionStateInitEdgeDnd;
    o->value.action.type      = 0;
    o->value.action.state    |= CompActionStateInitKey;
    o->value.action.state    |= CompActionStateInitButton;

    o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_GROUP];
    o->name		      = "initiate_group";
    o->shortDesc	      = N_("Initiate Window Picker For Window Group");
    o->longDesc		      = N_("Layout and start transforming "
				   "window group");
    o->type		      = CompOptionTypeAction;
    o->value.action.initiate  = scaleInitiateGroup;
    o->value.action.terminate = scaleTerminate;
    o->value.action.bell      = FALSE;
    o->value.action.edgeMask  = 0;
    o->value.action.state     = CompActionStateInitEdge;
    o->value.action.state    |= CompActionStateInitEdgeDnd;
    o->value.action.type      = 0;
    o->value.action.state    |= CompActionStateInitKey;
    o->value.action.state    |= CompActionStateInitButton;

    o = &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_OUTPUT];
    o->name		      = "initiate_output";
    o->shortDesc	      = N_("Initiate Window Picker For Windows on "
				   "Current Output");
    o->longDesc		      = N_("Layout and start transforming "
				   "windows on current output");
    o->type		      = CompOptionTypeAction;
    o->value.action.initiate  = scaleInitiateOutput;
    o->value.action.terminate = scaleTerminate;
    o->value.action.bell      = FALSE;
    o->value.action.edgeMask  = 0;
    o->value.action.state     = CompActionStateInitEdge;
    o->value.action.state    |= CompActionStateInitEdgeDnd;
    o->value.action.type      = 0;
    o->value.action.state    |= CompActionStateInitKey;
    o->value.action.state    |= CompActionStateInitButton;
}

static Bool
scaleInitDisplay (CompPlugin  *p,
		  CompDisplay *d)
{
    ScaleDisplay *sd;

    sd = malloc (sizeof (ScaleDisplay));
    if (!sd)
	return FALSE;

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

    sd->lastActiveNum = None;
    sd->selectedWindow = None;

    scaleDisplayInitOptions (sd, d->display);

    sd->leftKeyCode  = XKeysymToKeycode (d->display, XStringToKeysym ("Left"));
    sd->rightKeyCode = XKeysymToKeycode (d->display, XStringToKeysym ("Right"));
    sd->upKeyCode    = XKeysymToKeycode (d->display, XStringToKeysym ("Up"));
    sd->downKeyCode  = XKeysymToKeycode (d->display, XStringToKeysym ("Down"));

    WRAP (sd, d, handleEvent, scaleHandleEvent);

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

    return TRUE;
}

static void
scaleFiniDisplay (CompPlugin  *p,
		  CompDisplay *d)
{
    SCALE_DISPLAY (d);

    freeScreenPrivateIndex (d, sd->screenPrivateIndex);

    UNWRAP (sd, d, handleEvent);

    free (sd);
}

static Bool
scaleInitScreen (CompPlugin *p,
		 CompScreen *s)
{
    ScaleScreen *ss;

    SCALE_DISPLAY (s->display);

    ss = malloc (sizeof (ScaleScreen));
    if (!ss)
	return FALSE;

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

    ss->grab      = FALSE;
   // ss->grabIndex = 0;

    ss->hoverHandle = 0;

    ss->dndTarget = None;

    ss->state = SCALE_STATE_NONE;

    ss->slots = 0;
    ss->slotsSize = 0;

    ss->windows = 0;
    ss->windowsSize = 0;

    ss->spacing = SCALE_SPACING_DEFAULT;

    ss->speed    = SCALE_SPEED_DEFAULT;
    ss->timestep = SCALE_TIMESTEP_DEFAULT;
    ss->opacity  = (OPAQUE * SCALE_OPACITY_DEFAULT) / 100;

    ss->darkenBack = SCALE_DARKEN_BACK_DEFAULT;

    ss->iconOverlay = ScaleIconEmblem;

    scaleScreenInitOptions (ss);

    matchInit (&ss->match);
    matchUpdate (s->display,
		 &ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH].value.match);

    addScreenAction (s, &sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action);
    addScreenAction (s,
		     &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL].value.action);
    addScreenAction (s,
		     &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_GROUP].value.action);
    addScreenAction (s,
		     &sd->opt[SCALE_DISPLAY_OPTION_INITIATE_OUTPUT].value.action);

    WRAP (ss, s, preparePaintScreen, scalePreparePaintScreen);
    WRAP (ss, s, donePaintScreen, scaleDonePaintScreen);
    WRAP (ss, s, paintScreen, scalePaintScreen);
    WRAP (ss, s, paintWindow, scalePaintWindow);
    WRAP (ss, s, damageWindowRect, scaleDamageWindowRect);

    ss->cursor = XCreateFontCursor (s->display->display, XC_left_ptr);

    s->privates[sd->screenPrivateIndex].ptr = ss;

    return TRUE;
}

static void
scaleFiniScreen (CompPlugin *p,
		 CompScreen *s)
{
    SCALE_SCREEN (s);
    SCALE_DISPLAY (s->display);

    matchFini (&ss->match);
    matchFini (&ss->opt[SCALE_SCREEN_OPTION_WINDOW_MATCH].value.match);

    removeScreenAction (s, 
			&sd->opt[SCALE_DISPLAY_OPTION_INITIATE].value.action);
    removeScreenAction (s,
	   		&sd->opt[SCALE_DISPLAY_OPTION_INITIATE_ALL].value.action);
    removeScreenAction (s,
	   		&sd->opt[SCALE_DISPLAY_OPTION_INITIATE_GROUP].value.action);
    removeScreenAction (s,
	   		&sd->opt[SCALE_DISPLAY_OPTION_INITIATE_OUTPUT].value.action);

    UNWRAP (ss, s, preparePaintScreen);
    UNWRAP (ss, s, donePaintScreen);
    UNWRAP (ss, s, paintScreen);
    UNWRAP (ss, s, paintWindow);
    UNWRAP (ss, s, damageWindowRect);

    if (ss->cursor)
	XFreeCursor (s->display->display, ss->cursor);

    if (ss->slotsSize)
	free (ss->slots);

    if (ss->windowsSize)
	free (ss->windows);

    free (ss);
}

static Bool
scaleInitWindow (CompPlugin *p,
		 CompWindow *w)
{
    ScaleWindow *sw;

    SCALE_SCREEN (w->screen);

    sw = malloc (sizeof (ScaleWindow));
    if (!sw)
	return FALSE;

    sw->slot = 0;
    sw->scale = 1.0f;
    sw->tx = sw->ty = 0.0f;
    sw->adjust = FALSE;
    sw->xVelocity = sw->yVelocity = 0.0f;
    sw->scaleVelocity = 1.0f;
    sw->delta = 1.0f;
    sw->lastThumbOpacity = 0.0f;

    w->privates[ss->windowPrivateIndex].ptr = sw;

    return TRUE;
}

static void
scaleFiniWindow (CompPlugin *p,
		 CompWindow *w)
{
    SCALE_WINDOW (w);

    free (sw);
}

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

    return TRUE;
}

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

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

CompPluginVTable scaleVTable = {
    "scale",
    N_("Scale"),
    N_("Scale windows"),
    scaleGetVersion,
    0, /* GetMetadata */
    scaleInit,
    scaleFini,
    scaleInitDisplay,
    scaleFiniDisplay,
    scaleInitScreen,
    scaleFiniScreen,
    scaleInitWindow,
    scaleFiniWindow,
    scaleGetDisplayOptions,
    scaleSetDisplayOption,
    scaleGetScreenOptions,
    scaleSetScreenOption,
    0, /* Deps */
    0, /* nDeps */
    0, /* Features */
    0  /* nFeatures */
};

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