/*
 *
 * Beryl thumbnail plugin
 *
 * thumbnail.c
 *
 * Copyright : (C) 2007 by Dennis Kasprzyk
 * E-mail    : onestone@beryl-project.org
 *
 * Based on thumbnail.c:
 * Copyright : (C) 2007 Stjepan Glavina
 * E-mail    : stjepang@gmail.com
 *
 * 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.
 *
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <unistd.h>

#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>

#include <beryl.h>
#include <beryl-text.h>
#include "thumbnail_tex.h"

#define GET_THUMB_DISPLAY(d)				       \
    ((ThumbDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define THUMB_DISPLAY(d)		       \
    ThumbDisplay *td = GET_THUMB_DISPLAY (d)

#define GET_THUMB_SCREEN(s, td)				   \
    ((ThumbScreen *) (s)->privates[(td)->screenPrivateIndex].ptr)

#define THUMB_SCREEN(s)						      \
    ThumbScreen *ts = GET_THUMB_SCREEN (s, GET_THUMB_DISPLAY (s->display))

#define GET_THUMB_WINDOW(w, ts)                               \
    ((ThumbWindow *) (w)->privates[(ts)->windowPrivateIndex].ptr)

#define THUMB_WINDOW_PTR(w)                                   \
    GET_THUMB_WINDOW  (w,                                    \
            GET_THUMB_SCREEN  (w->screen,                            \
                GET_THUMB_DISPLAY (w->screen->display)))

#define THUMB_WINDOW(w)                                       \
    ThumbWindow *tw = THUMB_WINDOW_PTR(w)

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

#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 THUMB_SIZE_DEFAULT   200
#define THUMB_SIZE_MIN       50
#define THUMB_SIZE_MAX       1500

#define THUMB_DELAY_DEFAULT   100
#define THUMB_DELAY_MIN       100
#define THUMB_DELAY_MAX       10000

#define THUMB_FADE_DEFAULT      0.5
#define THUMB_FADE_MIN          0.0
#define THUMB_FADE_MAX          5.0
#define THUMB_FADE_PRECISION    0.1

#define THUMB_COLOR_RED_DEFAULT     0x0000
#define THUMB_COLOR_GREEN_DEFAULT   0x0000
#define THUMB_COLOR_BLUE_DEFAULT    0x0000
#define THUMB_COLOR_ALPHA_DEFAULT   0x7fff

#define THUMB_BORDER_DEFAULT   16
#define THUMB_BORDER_MIN       1
#define THUMB_BORDER_MAX       32


#define THUMB_CURRENT_VIEWPORT_DEFAULT	 TRUE
#define THUMB_ALWAYS_TOP_DEFAULT         FALSE
#define THUMB_WINDOW_LIKE_DEFAULT        TRUE
#define THUMB_MIPMAP_DEFAULT             FALSE

#define THUMB_TITLE_DEFAULT        TRUE
#define THUMB_FONT_BOLD_DEFAULT    TRUE
#define THUMB_FONT_SIZE_DEFAULT    12
#define THUMB_FONT_SIZE_MIN        6
#define THUMB_FONT_SIZE_MAX        36
#define THUMB_FONT_COLOR_RED       0x0
#define THUMB_FONT_COLOR_GREEN     0x0
#define THUMB_FONT_COLOR_BLUE      0x0
#define THUMB_FONT_COLOR_ALPHA     0xffff

#define THUMB_SCREEN_OPTION_SIZE	  0
#define THUMB_SCREEN_OPTION_DELAY	  1
#define THUMB_SCREEN_OPTION_COLOR	  2
#define THUMB_SCREEN_OPTION_BORDER	  3
#define THUMB_SCREEN_OPTION_FADE      4
#define THUMB_SCREEN_OPTION_CURRENT_VIEWPORT	  5
#define THUMB_SCREEN_OPTION_ALWAYS_TOP           6
#define THUMB_SCREEN_OPTION_WINDOW_LIKE           7
#define THUMB_SCREEN_OPTION_MIPMAP    8
#define THUMB_SCREEN_OPTION_TITLE     9
#define THUMB_SCREEN_OPTION_FONT_BOLD     10
#define THUMB_SCREEN_OPTION_FONT_SIZE     11
#define THUMB_SCREEN_OPTION_FONT_COLOR    12
#define THUMB_SCREEN_OPTION_NUM	      13

#define THUMB_MOUSE_UPDATE_SPEED 100

#define TEXT_DISTANCE 10

static int displayPrivateIndex;

typedef struct _ThumbDisplay {
	int	screenPrivateIndex;

	HandleEventProc handleEvent;
	Atom winIconGeometryAtom;

} ThumbDisplay;

typedef struct _Thumbnail {
	int x;
	int y;
	int width;
	int height;
	float scale;
	float opacity;
	int offset;
	CompWindow *win;
	CompWindow *dock;
	CompTexture textTexture;
	Pixmap textPixmap;
	int tWidth;
	int tHeight;
} Thumbnail;

typedef struct _ThumbScreen {
	int	windowPrivateIndex;

	CompTimeoutHandle mouseTimeout;
	CompTimeoutHandle displayTimeout;

	PreparePaintScreenProc preparePaintScreen;
    PaintScreenProc	paintScreen;
	PaintWindowProc	paintWindow;
	DonePaintScreenProc donePaintScreen;
    DamageWindowRectProc   damageWindowRect;
	WindowResizeNotifyProc	windowResizeNotify;
	PaintTransformedScreenProc paintTransformedScreen;

	CompWindow *dock;
	CompWindow *pointedWin;
	Bool showingThumb;
	Thumbnail thumb;
	Thumbnail oldThumb;
	Bool painted;

	CompTexture glowTexture;
	CompTexture windowTexture;

	int x;
	int y;

    CompOption opt[THUMB_SCREEN_OPTION_NUM];
} ThumbScreen;

typedef struct _IconGeometry {
	int x;
	int y;
	int width;
	int height;
	Bool isSet;
} IconGeometry;

typedef struct _ThumbWindow {
	IconGeometry ig;
} ThumbWindow;

char * getUtf8Property(CompDisplay *d,Window id,Atom atom)
{
	Atom type;
	int format;
	unsigned long nitems;
	unsigned long bytes_after;
	char *val;
	int result;
	char *retval;

	type = None;
	val = NULL;
	result = XGetWindowProperty (d->display, id, atom, 0L, 65536, False,
								 d->utf8StringAtom,	&type, &format, &nitems,
								 &bytes_after, (unsigned char **)&val);

	if (result != Success)
		return NULL;

	if (type != d->utf8StringAtom || format != 8 || nitems == 0)
	{
		if (val)
			XFree (val);
		return NULL;
	}

	if ((retval = (char *) malloc(nitems + 1)) != NULL) {
		bzero (retval, nitems + 1);
		strncpy (retval, val, nitems);
	}

	XFree (val);

	return retval;
}

char * getTextProperty(CompDisplay *d,Window id,Atom atom)
{
	XTextProperty text;
	char *retval;

	text.nitems = 0;
	if (XGetTextProperty (d->display, id, &text, atom))
	{
		if ((retval = (char *) malloc(text.nitems + 1)) != NULL) {
			bzero (retval, text.nitems + 1);
			strncpy (retval, (char *)text.value, text.nitems);
		}
		if (text.value)
			XFree (text.value);
	}
	else
	{
		retval = NULL;
	}
	return retval;
}


char * getWindowName(CompWindow *w)
{
	char *name = NULL;

	CompDisplay *d = w->screen->display;

	Atom visName = XInternAtom(d->display, "_NET_WM_VISIBLE_NAME", FALSE);

	name = getUtf8Property (d, w->id, visName);

	if (name == NULL)
		name = getUtf8Property(d, w->id, d->wmNameAtom);

	if (name == NULL)
		name = getTextProperty (d, w->id, XA_WM_NAME);

	return name;
}

static void freeThumbText(CompScreen *s,Thumbnail *t)
{
	if (!t->textPixmap)
		return;
	releasePixmapFromTexture(s,&t->textTexture);
	initTexture (s, &t->textTexture);
	XFreePixmap(s->display->display,t->textPixmap);
	t->textPixmap = None;
}

static void renderThumbText(CompScreen *s,Thumbnail *t, Bool freeThumb)
{
	THUMB_SCREEN(s);

	if (freeThumb)
		freeThumbText(s,t);

	CompTextAttrib tA;

	tA.maxwidth = t->width;
	tA.maxheight = 100;
	tA.screen = s;
	tA.size = ts->opt[THUMB_SCREEN_OPTION_FONT_SIZE].value.i;
	tA.color[0] = ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR].value.c[0];
	tA.color[1] = ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR].value.c[1];
	tA.color[2] = ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR].value.c[2];
	tA.color[3] = ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR].value.c[3];
	tA.style = (ts->opt[THUMB_SCREEN_OPTION_FONT_BOLD].value.b)?
				TEXT_STYLE_BOLD : TEXT_STYLE_NORMAL;
	tA.family = "Sans";
	tA.ellipsize = TRUE;

	tA.text = getWindowName(t->win);

	if (!tA.text)
		return;

	int stride;
	void *data;

	initTexture (s, &t->textTexture);

	if ((*s->display->fileToImage)(s->display,TEXT_ID,(char *)&tA,
		  						   &t->tWidth,&t->tHeight,&stride,&data))
	{
		t->textPixmap = (Pixmap)data;
		bindPixmapToTexture(s,&t->textTexture,t->textPixmap,
							t->tWidth,t->tHeight,32);
	}
	else
	{
		t->textPixmap = None;
		t->tWidth = 0;
		t->tHeight = 0;
	}

	free (tA.text);
}


static void
updateWindowIconGeometry (CompWindow *w)
{
	THUMB_DISPLAY(w->screen->display);
	THUMB_WINDOW(w);

	Atom actual;
    int result, format;
    unsigned long n, left;
    unsigned char *data;
    unsigned long *mydata;

    result = XGetWindowProperty (w->screen->display->display,
    							 w->id, td->winIconGeometryAtom, 0L, 4L,
    							 FALSE, XA_CARDINAL, &actual, &format, &n,
    							 &left, &data);

	mydata = (unsigned long *) data;

	tw->ig.isSet = FALSE;

	if (result == Success && actual == XA_CARDINAL && n == 4)
	{
		tw->ig.x = mydata[0];
		tw->ig.y = mydata[1];
		tw->ig.width = mydata[2];
		tw->ig.height = mydata[3];
		tw->ig.isSet = TRUE;
		XFree (data);
	}
}

static void
damageThumbRegion (CompScreen *s, Thumbnail *t)
{
	REGION region;
	region.extents.x1 = t->x - t->offset;
	region.extents.y1 = t->y - t->offset;
	region.extents.x2 = region.extents.x1 + t->width + (t->offset * 2);
	region.extents.y2 = region.extents.y1 + t->height + (t->offset * 2);
	if (t->textPixmap)
		region.extents.y2 += t->tHeight + TEXT_DISTANCE;
	region.rects = &region.extents;
	region.numRects = region.size = 1;

	damageScreenRegion(s, &region);
}

#define GET_DISTANCE(a,b) \
	(sqrt((((a)[0] - (b)[0]) * ((a)[0] - (b)[0])) + \
		  (((a)[1] - (b)[1]) * ((a)[1] - (b)[1]))))

static void
thumbUpdateThumbnail( CompScreen *s)
{
	THUMB_SCREEN(s);

	if (ts->thumb.win == ts->pointedWin)
		return;

	if (ts->thumb.opacity > 0.0 && ts->oldThumb.opacity > 0.0)
		return;

	if (ts->thumb.win)
		damageThumbRegion(s,&ts->thumb);

	freeThumbText(s,&ts->oldThumb);
	ts->oldThumb = ts->thumb;

	ts->thumb.win = ts->pointedWin;
	ts->thumb.dock = ts->dock;

	if (!ts->thumb.win)
		return;

	float maxSize = ts->opt[THUMB_SCREEN_OPTION_SIZE].value.i;
	double scale = 1.0;
	// do we nee to scale the window down?
	if (WIN_W(ts->thumb.win) > maxSize || WIN_H(ts->thumb.win) > maxSize)
	{
		if (WIN_W(ts->thumb.win) >= WIN_H(ts->thumb.win))
			scale = maxSize / WIN_W(ts->thumb.win);
		else
			scale = maxSize / WIN_H(ts->thumb.win);
	}
	ts->thumb.width = WIN_W(ts->thumb.win) * scale;
	ts->thumb.height = WIN_H(ts->thumb.win) * scale;

	ts->thumb.scale = scale;

	THUMB_WINDOW(ts->thumb.win);

	if (ts->opt[THUMB_SCREEN_OPTION_TITLE].value.b)
		renderThumbText(s,&ts->thumb,FALSE);
	else
		freeThumbText(s,&ts->thumb);

	int igMidPoint[2] = { tw->ig.x + (tw->ig.width / 2),
						  tw->ig.y + (tw->ig.height / 2)};

	int tMidPoint[2];
	int tPos[2];
	int tmpPos[2];
	float distance = 1000000;
	int off = ts->opt[THUMB_SCREEN_OPTION_BORDER].value.i;
	int oDev = screenGetOutputDev(s,tw->ig.x,tw->ig.y,tw->ig.width,tw->ig.height);
	int ox1,oy1,ox2,oy2,ow,oh;
	if (s->nOutputDev == 1 || oDev > s->nOutputDev)
	{
		ox1 = 0;
		oy1 = 0;
		ox2 = s->width;
		oy2 = s->height;
		ow = s->width;
		oh = s->height;
	}
	else
	{
		ox1 = s->outputDev[oDev].region.extents.x1;
		ox2 = s->outputDev[oDev].region.extents.x2;
		oy1 = s->outputDev[oDev].region.extents.y1;
		oy2 = s->outputDev[oDev].region.extents.y2;
		ow = ox2 - ox1;
		oh = oy2 - oy1;
	}

	int tHeight = ts->thumb.height;
	if (ts->thumb.textPixmap)
		tHeight += ts->thumb.tHeight + TEXT_DISTANCE;

	// failsave position
	tPos[0] = igMidPoint[0] - (ts->thumb.width / 2.0);
	if (tw->ig.y - tHeight >= 0)
	{
		tPos[1] = tw->ig.y - tHeight;
	}
	else
	{
		tPos[1] = tw->ig.y + tw->ig.height;
	}

	// above
	tmpPos[0] = igMidPoint[0] - (ts->thumb.width / 2.0);
	if (tmpPos[0] - off < ox1)
		tmpPos[0] = ox1 + off;
	if (tmpPos[0] + off + ts->thumb.width > ox2)
	{
		if (ts->thumb.width + (2*off) <= ow)
			tmpPos[0] = ox2 - ts->thumb.width - off;
		else
			tmpPos[0] = ox1 + off;
	}
	tMidPoint[0] = tmpPos[0] + (ts->thumb.width / 2.0);

	tmpPos[1] = WIN_Y(ts->dock) - tHeight - off;
	tMidPoint[1] = tmpPos[1] + (tHeight / 2.0);
	if (tmpPos[1] > oy1)
	{
		tPos[0] = tmpPos[0];
		tPos[1] = tmpPos[1];
		distance = GET_DISTANCE(igMidPoint,tMidPoint);
	}

	// below
	tmpPos[1] = WIN_Y(ts->dock) + WIN_H(ts->dock) + off;
	tMidPoint[1] = tmpPos[1] + (tHeight / 2.0);
	if (tmpPos[1] + tHeight + off < oy2 &&
	    GET_DISTANCE(igMidPoint,tMidPoint) < distance)
	{
		tPos[0] = tmpPos[0];
		tPos[1] = tmpPos[1];
		distance = GET_DISTANCE(igMidPoint,tMidPoint);
	}

	// left
	tmpPos[1] = igMidPoint[1] - (tHeight / 2.0);
	if (tmpPos[1] - off < oy1)
		tmpPos[1] = oy1 + off;
	if (tmpPos[1] + off + tHeight > oy2)
	{
		if (tHeight + (2*off) <= oh)
			tmpPos[1] = oy2 - ts->thumb.height - off;
		else
			tmpPos[1] = oy1 + off;
	}
	tMidPoint[1] = tmpPos[1] + (tHeight / 2.0);

	tmpPos[0] = WIN_X(ts->dock) - ts->thumb.width - off;
	tMidPoint[0] = tmpPos[0] + (ts->thumb.width / 2.0);
	if (tmpPos[0] > ox1 &&
	    GET_DISTANCE(igMidPoint,tMidPoint) < distance)
	{
		tPos[0] = tmpPos[0];
		tPos[1] = tmpPos[1];
		distance = GET_DISTANCE(igMidPoint,tMidPoint);
	}

	// right
	tmpPos[0] = WIN_X(ts->dock) + WIN_W(ts->dock) + off;
	tMidPoint[0] = tmpPos[0] + (ts->thumb.width / 2.0);
	if (tmpPos[0] + ts->thumb.width + off < ox2 &&
	    GET_DISTANCE(igMidPoint,tMidPoint) < distance)
	{
		tPos[0] = tmpPos[0];
		tPos[1] = tmpPos[1];
		distance = GET_DISTANCE(igMidPoint,tMidPoint);
	}

	ts->thumb.x = tPos[0];
	ts->thumb.y = tPos[1];

	ts->thumb.offset = off;

	ts->thumb.opacity = 0.0;

	damageThumbRegion(s,&ts->thumb);
}

static Bool
thumbShowThumbnail (void *vs)
{
	CompScreen *s = (CompScreen *) vs;
	THUMB_SCREEN(s);
	ts->showingThumb = TRUE;
	thumbUpdateThumbnail(s);
	damageThumbRegion(s,&ts->thumb);
	return TRUE;
}

static Bool
checkPosition (CompWindow *w)
{
	THUMB_SCREEN(w->screen);

	if (ts->opt[THUMB_SCREEN_OPTION_CURRENT_VIEWPORT].value.b)
	{
		// TODO: We need a faster calculation here
		Bool onViewport = FALSE;
		Region reg = XCreateRegion();
		XIntersectRegion(w->region,&w->screen->region,reg);
		if (reg->numRects)
			onViewport = TRUE;
		XDestroyRegion(reg);
		if (!onViewport)
			return FALSE;
	}

	return TRUE;
}

static Bool
thumbUpdateMouse (void *vs)
{
	CompScreen *s = (CompScreen *) vs;

	THUMB_SCREEN(s);

	int winX, winY;
	int rootX, rootY;
	unsigned int mask_return;
	Window root_return;
	Window child_return;

	XQueryPointer(s->display->display, s->root,
					&root_return, &child_return,
					&rootX, &rootY, &winX,
					&winY, &mask_return);

	CompWindow *w = findWindowAtDisplay(s->display, child_return);

	if (w && w->type & CompWindowTypeDockMask)
	{
		if (ts->dock != w)
		{
			ts->dock = w;
			if (ts->displayTimeout)
			{
				compRemoveTimeout(ts->displayTimeout);
				ts->displayTimeout = 0;
			}
			ts->pointedWin = NULL;
			ts->showingThumb = FALSE;
		}

		CompWindow *cw = s->windows;
		CompWindow *found = NULL;
		for (;cw && !found;cw = cw->next)
		{
			THUMB_WINDOW(cw);
			if (!tw->ig.isSet)
				continue;
			if (cw->attrib.map_state != IsViewable && !cw->thumbPixmap)
				continue;
			if (cw->state & CompWindowStateSkipTaskbarMask)
                continue;
			if (cw->state & CompWindowStateSkipPagerMask)
                continue;
			if (cw->state & CompWindowStateOffscreenMask)
				continue;
			if (!cw->managed)
				continue;

			if (rootX >= tw->ig.x && rootX < tw->ig.x + tw->ig.width &&
				rootY >= tw->ig.y && rootY < tw->ig.y + tw->ig.height &&
			    checkPosition(cw))
			{
				found = cw;
			}
		}

		if (found)
		{
			if (!ts->showingThumb &&
				!(ts->thumb.opacity != 0.0 && ts->thumb.win == found))
			{
				if (ts->displayTimeout)
				{
					if (ts->pointedWin != found)
					{
						compRemoveTimeout(ts->displayTimeout);
						ts->displayTimeout = compAddTimeout(
								ts->opt[THUMB_SCREEN_OPTION_DELAY].value.i,
								thumbShowThumbnail, s);
					}
				}
				else
				{
					ts->displayTimeout =
						compAddTimeout(ts->opt[THUMB_SCREEN_OPTION_DELAY].value.i,
									   thumbShowThumbnail, s);
				}
			}
			ts->pointedWin = found;
			thumbUpdateThumbnail(s);
		}
		else
		{
			ts->dock = NULL;
			if (ts->displayTimeout)
			{
				compRemoveTimeout(ts->displayTimeout);
				ts->displayTimeout = 0;
			}
			ts->pointedWin = 0;
			ts->showingThumb = FALSE;
		}

	} else
	{
		ts->dock = NULL;
		if (ts->displayTimeout)
		{
			compRemoveTimeout(ts->displayTimeout);
			ts->displayTimeout = 0;
		}
		ts->pointedWin = NULL;
		ts->showingThumb = FALSE;
	}

	ts->mouseTimeout = compAddTimeout(THUMB_MOUSE_UPDATE_SPEED, thumbUpdateMouse, s);
	return FALSE;
}

static void
thumbScreenInitOptions (ThumbScreen *ts)
{
    CompOption *o;

    o = &ts->opt[THUMB_SCREEN_OPTION_SIZE];
    o->name			= "thumb_size";
    o->shortDesc	= N_("Thumbnail Size");
    o->longDesc		= N_("Thumbnail window Size.");
    o->type			= CompOptionTypeInt;
    o->value.i		= THUMB_SIZE_DEFAULT;
    o->rest.i.min	= THUMB_SIZE_MIN;
    o->rest.i.max	= THUMB_SIZE_MAX;
	o->group		= N_("Settings");
	o->subGroup		= "";
	o->advanced		= False;
	o->displayHints	= "";


    o = &ts->opt[THUMB_SCREEN_OPTION_DELAY];
    o->name			= "show_delay";
    o->shortDesc	= N_("Show Delay");
    o->longDesc		= N_("Time (in ms) before Thumbnail is shown "
						 "when hovering over a taskbar button (10-10000).");
    o->type			= CompOptionTypeInt;
    o->value.i		= THUMB_DELAY_DEFAULT;
    o->rest.i.min	= THUMB_DELAY_MIN;
    o->rest.i.max	= THUMB_DELAY_MAX;
	o->group		= N_("Settings");
	o->subGroup		= "";
	o->advanced		= False;
	o->displayHints	= "";

	o = &ts->opt[THUMB_SCREEN_OPTION_BORDER];
    o->name			= "border";
    o->shortDesc	= N_("Thumbnail Border Size");
    o->longDesc		= N_("Size of Thumbnail Border.");
    o->type			= CompOptionTypeInt;
    o->value.i		= THUMB_BORDER_DEFAULT;
    o->rest.i.min	= THUMB_BORDER_MIN;
    o->rest.i.max	= THUMB_BORDER_MAX;
	o->group		= N_("Settings");
	o->subGroup		= "";
	o->advanced		= False;
	o->displayHints	= "";

  	o = &ts->opt[THUMB_SCREEN_OPTION_COLOR];
	o->name = "thumb_color";
	o->group = N_("Settings");
	o->subGroup = N_("");
	o->advanced = False;
	o->shortDesc = N_("Thumbnail Border Glow Color");
	o->longDesc = N_("Thumbnail Background and Border Glow Color.");
	o->displayHints = "";
	o->type = CompOptionTypeColor;
	o->value.c[0] = THUMB_COLOR_RED_DEFAULT;
	o->value.c[1] = THUMB_COLOR_GREEN_DEFAULT;
	o->value.c[2] = THUMB_COLOR_BLUE_DEFAULT;
	o->value.c[3] = THUMB_COLOR_ALPHA_DEFAULT;

	o = &ts->opt[THUMB_SCREEN_OPTION_FADE];
	o->name = "fade_speed";
	o->group = N_("Settings");
	o->subGroup = N_("");
	o->advanced = False;
	o->shortDesc = N_("Fade In/Out Duration");
	o->longDesc = N_("Fade In/Out Duration in seconds.");
	o->displayHints = "";
	o->type = CompOptionTypeFloat;
	o->value.f = THUMB_FADE_DEFAULT;
	o->rest.f.min = THUMB_FADE_MIN;
	o->rest.f.max = THUMB_FADE_MAX;
	o->rest.f.precision = THUMB_FADE_PRECISION;

	o = &ts->opt[THUMB_SCREEN_OPTION_CURRENT_VIEWPORT];
    o->name			= "current_viewport";
    o->shortDesc	= N_("Taskbar Shows Only Windows of Current Viewport");
    o->longDesc		= N_("Set it if the Taskbar shows only Windows of Current Viewport.");
    o->type			= CompOptionTypeBool;
    o->value.b		= THUMB_CURRENT_VIEWPORT_DEFAULT;
	o->group		= N_("Settings");
	o->subGroup		= N_("Taskbar");
	o->advanced		= False;
	o->displayHints	= "";

	o = &ts->opt[THUMB_SCREEN_OPTION_ALWAYS_TOP];
    o->name			= "always_on_top";
    o->shortDesc	= N_("Thumbnails Always on Top");
    o->longDesc		= N_("Paint Thumbnails Always on Top.");
    o->type			= CompOptionTypeBool;
    o->value.b		= THUMB_ALWAYS_TOP_DEFAULT;
	o->group		= N_("Settings");
	o->subGroup		= N_("Taskbar");
	o->advanced		= False;
	o->displayHints	= "";

	o = &ts->opt[THUMB_SCREEN_OPTION_WINDOW_LIKE];
    o->name			= "window_like";
    o->shortDesc	= N_("Paint Window Like Background");
    o->longDesc		= N_("Paint Window Like Background instead of Glow.");
    o->type			= CompOptionTypeBool;
    o->value.b		= THUMB_WINDOW_LIKE_DEFAULT;
	o->group		= N_("Settings");
	o->subGroup		= "";
	o->advanced		= False;
	o->displayHints	= "";

	o = &ts->opt[THUMB_SCREEN_OPTION_MIPMAP];
    o->name			= "mipmap";
    o->shortDesc	= N_("Generate Mipmaps");
    o->longDesc		= N_("Generate mipmaps when possible for higher quality "
						 "scaling.");
    o->type			= CompOptionTypeBool;
    o->value.b		= THUMB_MIPMAP_DEFAULT;
	o->group		= N_("Settings");
	o->subGroup		= "";
	o->advanced		= TRUE;
	o->displayHints	= "";

	o = &ts->opt[THUMB_SCREEN_OPTION_TITLE];
    o->name			= "title_enabled";
    o->shortDesc	= N_("Enable Titles");
    o->longDesc		= N_("Show Window Title in Thumbnail.");
    o->type			= CompOptionTypeBool;
    o->value.b		= THUMB_TITLE_DEFAULT;
	o->group		= N_("Settings");
	o->subGroup		= N_("Window title");
	o->advanced		= FALSE;
	o->displayHints	= "";

	o = &ts->opt[THUMB_SCREEN_OPTION_FONT_BOLD];
    o->name			= "font_bold";
    o->shortDesc	= N_("Bold Font");
    o->longDesc		= N_("Should be the window title Bold.");
    o->type			= CompOptionTypeBool;
    o->value.b		= THUMB_FONT_BOLD_DEFAULT;
	o->group		= N_("Settings");
	o->subGroup		= N_("Window title");
	o->advanced		= FALSE;
	o->displayHints	= "";

	o = &ts->opt[THUMB_SCREEN_OPTION_FONT_SIZE];
    o->name			= "font_size";
    o->shortDesc	= N_("Font Size");
    o->longDesc		= N_("Window title Font Size.");
    o->type			= CompOptionTypeInt;
	o->group		= N_("Settings");
	o->subGroup		= N_("Window title");
	o->advanced		= FALSE;
	o->displayHints	= "";
	o->value.i		= THUMB_FONT_SIZE_DEFAULT;
	o->rest.i.min = THUMB_FONT_SIZE_MIN;
	o->rest.i.max = THUMB_FONT_SIZE_MAX;

	o = &ts->opt[THUMB_SCREEN_OPTION_FONT_COLOR];
	o->name 		= "font_color";
	o->group 		= N_("Settings");
	o->subGroup 	= N_("Window title");
	o->advanced 	= False;
	o->shortDesc 	= N_("Font Color");
	o->longDesc 	= N_("Window title Font Color.");
	o->advanced		= FALSE;
	o->displayHints = "";
	o->type = CompOptionTypeColor;
	o->value.c[0] = THUMB_FONT_COLOR_RED;
	o->value.c[1] = THUMB_FONT_COLOR_GREEN;
	o->value.c[2] = THUMB_FONT_COLOR_BLUE;
	o->value.c[3] = THUMB_FONT_COLOR_ALPHA;

}


static CompOption *
thumbGetScreenOptions (CompScreen *screen,
					   int		  *count)
{
	if (screen)
	{
	    THUMB_SCREEN (screen);

	    *count = NUM_OPTIONS (ts);
		return ts->opt;
	}
	else
	{
		ThumbScreen * ts = malloc(sizeof(ThumbScreen));
		thumbScreenInitOptions(ts);

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


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

    THUMB_SCREEN (screen);

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

    switch (index)
    {
	    case THUMB_SCREEN_OPTION_SIZE:
		case THUMB_SCREEN_OPTION_DELAY:
		case THUMB_SCREEN_OPTION_BORDER:
		case THUMB_SCREEN_OPTION_FONT_SIZE:
			if (compSetIntOption (o, value))
			{
			    return TRUE;
			}
			break;
		case THUMB_SCREEN_OPTION_COLOR:
		case THUMB_SCREEN_OPTION_FONT_COLOR:
			if (compSetColorOption(o, value))
			{
				return TRUE;
			}
			break;
		case THUMB_SCREEN_OPTION_FADE:
			if (compSetFloatOption(o, value))
			{
				return TRUE;
			}
			break;
		case THUMB_SCREEN_OPTION_ALWAYS_TOP:
		case THUMB_SCREEN_OPTION_CURRENT_VIEWPORT:
		case THUMB_SCREEN_OPTION_WINDOW_LIKE:
		case THUMB_SCREEN_OPTION_MIPMAP:
		case THUMB_SCREEN_OPTION_TITLE:
		case THUMB_SCREEN_OPTION_FONT_BOLD:
			if (compSetBoolOption(o, value))
			{
				return TRUE;
			}
	    default:
			break;
    }

    return FALSE;
}

static void
thumbWindowResizeNotify(CompWindow *w, int dx, int dy, int dwidth, int dheight,
						Bool preview)
{
	THUMB_SCREEN ( w->screen );

	if (!preview)
		thumbUpdateThumbnail(w->screen);

	UNWRAP(ts,w->screen,windowResizeNotify);
	(*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight, preview);
	WRAP(ts,w->screen,windowResizeNotify,thumbWindowResizeNotify);
}

static void
thumbHandleEvent (CompDisplay *d,
		   		  XEvent      *event)
{
    THUMB_DISPLAY (d);

    UNWRAP (td, d, handleEvent);
    (*d->handleEvent) (d, event);
    WRAP (td, d, handleEvent, thumbHandleEvent);

	CompWindow *w;

    switch (event->type)
	{
		case PropertyNotify:
			if (event->xproperty.atom == td->winIconGeometryAtom)
			{
				w = findWindowAtDisplay(d, event->xproperty.window);
				if (w)
				{
					updateWindowIconGeometry(w);
				}
			}
			break;
		case ButtonPress:
			{
				CompScreen *s = findScreenAtDisplay (d, event->xbutton.root);
				if (!s)
					break;

				THUMB_SCREEN(s);
				ts->dock = NULL;
				if (ts->displayTimeout)
				{
					compRemoveTimeout(ts->displayTimeout);
					ts->displayTimeout = 0;
				}
				ts->pointedWin = 0;
				ts->showingThumb = FALSE;
			}
			break;
		default:
			break;
	}
}


static void
thumbPaintThumb(CompScreen *s,Thumbnail *t)
{
	THUMB_SCREEN(s);
	DrawWindowGeometryProc oldDrawWindowGeometry;
	AddWindowGeometryProc oldAddWindowGeometry;
	CompWindow *w = t->win;

	if (!w)
		return;

	int wx = t->x;
	int wy = t->y;
	float width = t->width;
	float height = t->height;
	WindowPaintAttrib sAttrib = w->paint;

	if (t->textPixmap)
		height += t->tHeight + TEXT_DISTANCE;

	/* Wrap drawWindowGeometry to make sure the general
	   drawWindowGeometry function is used */
	oldDrawWindowGeometry = w->screen->drawWindowGeometry;
	w->screen->drawWindowGeometry = getBaseDrawWindowGeometry();
	oldAddWindowGeometry = w->screen->addWindowGeometry;
	w->screen->addWindowGeometry = getBaseAddWindowGeometry();

	unsigned int mask = PAINT_WINDOW_TRANSFORMED_MASK;

	if (w->attrib.map_state == IsViewable || w->thumbPixmap)
	{

		int off = t->offset;

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		if (ts->opt[THUMB_SCREEN_OPTION_WINDOW_LIKE].value.b)
		{
			glColor4f(1.0,1.0,1.0,t->opacity);
			enableTexture(s, &ts->windowTexture, COMP_TEXTURE_FILTER_GOOD);
		}
		else
		{
			glColor4us(ts->opt[THUMB_SCREEN_OPTION_COLOR].value.c[0],
					ts->opt[THUMB_SCREEN_OPTION_COLOR].value.c[1],
					ts->opt[THUMB_SCREEN_OPTION_COLOR].value.c[2],
					ts->opt[THUMB_SCREEN_OPTION_COLOR].value.c[3] * t->opacity);

			enableTexture(s, &ts->glowTexture, COMP_TEXTURE_FILTER_GOOD);
		}

		glBegin(GL_QUADS);

		glTexCoord2f(1,0);
		glVertex2f(wx,wy);
		glVertex2f(wx,wy+height);
		glVertex2f(wx+width,wy+height);
		glVertex2f(wx+width,wy);

		glTexCoord2f(0,1);
		glVertex2f(wx-off,wy-off);
		glTexCoord2f(0,0);
		glVertex2f(wx-off,wy);
		glTexCoord2f(1,0);
		glVertex2f(wx,wy);
		glTexCoord2f(1,1);
		glVertex2f(wx,wy-off);

		glTexCoord2f(1,1);
		glVertex2f(wx+width,wy-off);
		glTexCoord2f(1,0);
		glVertex2f(wx+width,wy);
		glTexCoord2f(0,0);
		glVertex2f(wx+width+off,wy);
		glTexCoord2f(0,1);
		glVertex2f(wx+width+off,wy-off);

		glTexCoord2f(0,0);
		glVertex2f(wx-off,wy+height);
		glTexCoord2f(0,1);
		glVertex2f(wx-off,wy+height+off);
		glTexCoord2f(1,1);
		glVertex2f(wx,wy+height+off);
		glTexCoord2f(1,0);
		glVertex2f(wx,wy+height);

		glTexCoord2f(1,0);
		glVertex2f(wx+width,wy+height);
		glTexCoord2f(1,1);
		glVertex2f(wx+width,wy+height+off);
		glTexCoord2f(0,1);
		glVertex2f(wx+width+off,wy+height+off);
		glTexCoord2f(0,0);
		glVertex2f(wx+width+off,wy+height);

		glTexCoord2f(1,1);
		glVertex2f(wx,wy-off);
		glTexCoord2f(1,0);
		glVertex2f(wx,wy);
		glTexCoord2f(1,0);
		glVertex2f(wx+width,wy);
		glTexCoord2f(1,1);
		glVertex2f(wx+width,wy-off);

		glTexCoord2f(1,0);
		glVertex2f(wx,wy+height);
		glTexCoord2f(1,1);
		glVertex2f(wx,wy+height+off);
		glTexCoord2f(1,1);
		glVertex2f(wx+width,wy+height+off);
		glTexCoord2f(1,0);
		glVertex2f(wx+width,wy+height);

		glTexCoord2f(0,0);
		glVertex2f(wx-off,wy);
		glTexCoord2f(0,0);
		glVertex2f(wx-off,wy+height);
		glTexCoord2f(1,0);
		glVertex2f(wx,wy+height);
		glTexCoord2f(1,0);
		glVertex2f(wx,wy);

		glTexCoord2f(1,0);
		glVertex2f(wx+width,wy);
		glTexCoord2f(1,0);
		glVertex2f(wx+width,wy+height);
		glTexCoord2f(0,0);
		glVertex2f(wx+width+off,wy+height);
		glTexCoord2f(0,0);
		glVertex2f(wx+width+off,wy);

		glEnd();

		if (ts->opt[THUMB_SCREEN_OPTION_WINDOW_LIKE].value.b)
		{
			disableTexture(s, &ts->windowTexture);
		}
		else
		{
			disableTexture(s, &ts->glowTexture);
		}

		glColor4usv(defaultColor);
		glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);

		if (t->textPixmap)
		{
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
			glColor4f(1.0,1.0,1.0,t->opacity);

			enableTexture(s, &t->textTexture,COMP_TEXTURE_FILTER_GOOD);

			CompMatrix *m = &t->textTexture.matrix;

			float ox = 0.0;
			if (t->tWidth < width)
				ox = (width - t->tWidth) / 2.0;

			float w = MIN(width,t->tWidth);
			float h = t->tHeight;

			glBegin(GL_QUADS);

			glTexCoord2f(COMP_TEX_COORD_X(m,0),COMP_TEX_COORD_Y(m,0));
			glVertex2f(wx+ox,wy+height-h);
			glTexCoord2f(COMP_TEX_COORD_X(m,0),COMP_TEX_COORD_Y(m,h));
			glVertex2f(wx+ox,wy+height);
			glTexCoord2f(COMP_TEX_COORD_X(m,w),COMP_TEX_COORD_Y(m,h));
			glVertex2f(wx+ox+w,wy+height);
			glTexCoord2f(COMP_TEX_COORD_X(m,w),COMP_TEX_COORD_Y(m,0));
			glVertex2f(wx+ox+w,wy+height-h);

			glEnd();

			disableTexture(s, &t->textTexture);
			glColor4usv(defaultColor);
		}

		glDisable(GL_BLEND);
		screenTexEnvMode (s,GL_REPLACE);

		glColor4usv(defaultColor);

		sAttrib.opacity *= t->opacity;
		sAttrib.yScale = t->scale;
		sAttrib.xScale = t->scale;

		sAttrib.xTranslate =
				wx - w->attrib.x + w->input.left * sAttrib.xScale;
		sAttrib.yTranslate = wy - w->attrib.y + w->input.top * sAttrib.yScale;

		GLenum filter = s->display->textureFilter;

		if (ts->opt[THUMB_SCREEN_OPTION_MIPMAP].value.b)
			s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;

		(w->screen->drawWindow) (w, &sAttrib, getInfiniteRegion(), mask);

		s->display->textureFilter = filter;
	}

	w->screen->drawWindowGeometry = oldDrawWindowGeometry;
	w->screen->addWindowGeometry = oldAddWindowGeometry;
}

static void thumbPreparePaintScreen(CompScreen * s, int ms)
{
	THUMB_SCREEN(s);

	float val = ms;
	val /= 1000;
	val /= ts->opt[THUMB_SCREEN_OPTION_FADE].value.f;

	if (otherScreenGrabExist(s, 0))
	{
		ts->dock = NULL;
		if (ts->displayTimeout)
		{
			compRemoveTimeout(ts->displayTimeout);
			ts->displayTimeout = 0;
		}
		ts->pointedWin = 0;
		ts->showingThumb = FALSE;
	}

	if (ts->showingThumb && ts->thumb.win == ts->pointedWin)
	{
		ts->thumb.opacity = MIN(1.0,ts->thumb.opacity + val);
	}
	if (!ts->showingThumb || ts->thumb.win != ts->pointedWin)
	{
		ts->thumb.opacity = MAX(0.0,ts->thumb.opacity - val);
	}
	ts->oldThumb.opacity = MAX(0.0,ts->oldThumb.opacity - val);

	UNWRAP(ts, s, preparePaintScreen);
	(*s->preparePaintScreen) (s,ms);
	WRAP(ts, s, preparePaintScreen, thumbPreparePaintScreen);
}

static void thumbDonePaintScreen(CompScreen * s)
{
	THUMB_SCREEN(s);

	if (ts->thumb.opacity > 0.0 && ts->thumb.opacity < 1.0)
		damageThumbRegion(s,&ts->thumb);
	if (ts->oldThumb.opacity > 0.0 && ts->oldThumb.opacity < 1.0)
		damageThumbRegion(s,&ts->oldThumb);

	UNWRAP(ts, s, donePaintScreen);
	(*s->donePaintScreen) (s);
	WRAP(ts, s, donePaintScreen, thumbDonePaintScreen);
}

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

    THUMB_SCREEN (s);

	ts->painted = FALSE;

	ts->x = s->x;
	ts->y = s->y;

	unsigned int newMask = mask;

	if ((ts->oldThumb.opacity > 0.0 && ts->oldThumb.win) ||
		(ts->thumb.opacity > 0.0 && ts->thumb.win))
		newMask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;

	UNWRAP (ts, s, paintScreen);
	status = (*s->paintScreen) (s, sAttrib, region, output, newMask);
	WRAP (ts, s, paintScreen, thumbPaintScreen);

	if (ts->opt[THUMB_SCREEN_OPTION_ALWAYS_TOP].value.b && !ts->painted)
	{
		if (ts->oldThumb.opacity > 0.0 && ts->oldThumb.win)
		{
			glPushMatrix();
			prepareXCoords(s, output,-DEFAULT_Z_CAMERA);
			thumbPaintThumb(s,&ts->oldThumb);
			glPopMatrix();
		}
		if (ts->thumb.opacity > 0.0 && ts->thumb.win)
		{
			glPushMatrix();
			prepareXCoords(s, output,-DEFAULT_Z_CAMERA);
			thumbPaintThumb(s,&ts->thumb);
			glPopMatrix();
		}
	}

    return status;
}

static void
thumbPaintTransformedScreen (CompScreen		      *s,
				  const ScreenPaintAttrib *sAttrib,
				  Region		   		  region,
				  int			   		  output,
				  unsigned int			  mask)
{

    THUMB_SCREEN (s);

	UNWRAP (ts, s, paintTransformedScreen);
	(*s->paintTransformedScreen) (s, sAttrib, region, output, mask);
	WRAP (ts, s, paintTransformedScreen, thumbPaintTransformedScreen);

	if (ts->opt[THUMB_SCREEN_OPTION_ALWAYS_TOP].value.b &&
	    ts->x == s->x && ts->y == s->y)
	{
		ts->painted = TRUE;
		if (ts->oldThumb.opacity > 0.0 && ts->oldThumb.win)
		{
			glPushMatrix();
			(s->applyScreenTransform) (s, sAttrib, output);
			prepareXCoords(s, output,-sAttrib->zTranslate);
			thumbPaintThumb(s,&ts->oldThumb);
			glPopMatrix();
		}
		if (ts->thumb.opacity > 0.0 && ts->thumb.win)
		{
			glPushMatrix();
			(s->applyScreenTransform) (s, sAttrib, output);
			prepareXCoords(s, output,-sAttrib->zTranslate);
			thumbPaintThumb(s,&ts->thumb);
			glPopMatrix();
		}
	}
}

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

    THUMB_SCREEN (s);

	UNWRAP (ts, s, paintWindow);
	status = (*s->paintWindow) (w, attrib, region, mask);
	WRAP (ts, s, paintWindow, thumbPaintWindow);

	if (!ts->opt[THUMB_SCREEN_OPTION_ALWAYS_TOP].value.b &&
	    ts->x == s->x && ts->y == s->y)
	{
		if (ts->oldThumb.opacity > 0.0 && ts->oldThumb.win &&
			ts->oldThumb.dock == w)
		{
			thumbPaintThumb(s,&ts->oldThumb);
		}
		if (ts->thumb.opacity > 0.0 && ts->thumb.win && ts->thumb.dock == w)
		{
			thumbPaintThumb(s,&ts->thumb);
		}
	}

    return status;
}

static Bool
thumbDamageWindowRect (CompWindow *w,
					   Bool		  initial,
					   BoxPtr	  rect)
{
    Bool status;

    THUMB_SCREEN (w->screen);

	if (ts->thumb.win == w && ts->thumb.opacity > 0.0)
		damageThumbRegion(w->screen,&ts->thumb);
	if (ts->oldThumb.win == w && ts->oldThumb.opacity > 0.0)
		damageThumbRegion(w->screen,&ts->oldThumb);

    UNWRAP (ts, w->screen, damageWindowRect);
    status = (*w->screen->damageWindowRect) (w, initial, rect);
    WRAP (ts, w->screen, damageWindowRect, thumbDamageWindowRect);

    return status;
}


static Bool
thumbInitDisplay (CompPlugin  *p,
		   CompDisplay *d)
{
    ThumbDisplay *td;

    td = malloc (sizeof (ThumbDisplay));
    if (!td)
		return FALSE;

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

	td->winIconGeometryAtom = XInternAtom (d->display,
											"_NET_WM_ICON_GEOMETRY", FALSE);

    WRAP (td, d, handleEvent, thumbHandleEvent);

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

    return TRUE;
}


static void
thumbFiniDisplay (CompPlugin  *p,
				  CompDisplay *d)
{
    THUMB_DISPLAY (d);

    freeScreenPrivateIndex (d, td->screenPrivateIndex);

    UNWRAP (td, d, handleEvent);

    free (td);
}

static Bool thumbInitWindow(CompPlugin * p, CompWindow * w)
{
	ThumbWindow *tw;

	THUMB_SCREEN(w->screen);

	// create window
	tw = calloc(1, sizeof(ThumbWindow));
	if (!tw)
		return FALSE;

	w->privates[ts->windowPrivateIndex].ptr = tw;

	updateWindowIconGeometry(w);

	return TRUE;
}

static void thumbFiniWindow(CompPlugin * p, CompWindow * w)
{
	THUMB_WINDOW(w);
	THUMB_SCREEN(w->screen);

	if (ts->thumb.win == w)
	{
		damageThumbRegion(w->screen,&ts->thumb);
		ts->thumb.win = NULL;
		ts->thumb.opacity = 0;
	}

	if (ts->oldThumb.win == w)
	{
		damageThumbRegion(w->screen,&ts->oldThumb);
		ts->oldThumb.win = NULL;
		ts->oldThumb.opacity = 0;
	}


	// free window pointer
	free(tw);
}


static Bool
thumbInitScreen (CompPlugin *p,
				  CompScreen *s)
{
    ThumbScreen *ts;

    THUMB_DISPLAY (s->display);

    ts = calloc (1,sizeof (ThumbScreen));
    if (!ts)
		return FALSE;

	ts->windowPrivateIndex = allocateWindowPrivateIndex(s);

    thumbScreenInitOptions (ts);

    WRAP (ts, s, paintScreen, thumbPaintScreen);
    WRAP (ts, s, damageWindowRect, thumbDamageWindowRect);
	WRAP (ts, s, preparePaintScreen, thumbPreparePaintScreen);
	WRAP (ts, s, donePaintScreen, thumbDonePaintScreen);
	WRAP (ts, s, paintWindow, thumbPaintWindow);
	WRAP (ts,s,windowResizeNotify, thumbWindowResizeNotify);
	WRAP (ts, s, paintTransformedScreen, thumbPaintTransformedScreen);

	ts->dock = NULL;
	ts->pointedWin = NULL;
	ts->displayTimeout = 0;
	ts->thumb.win = NULL;
	ts->oldThumb.win = NULL;
	ts->showingThumb = FALSE;

    s->privates[td->screenPrivateIndex].ptr = ts;

	ts->mouseTimeout =
		compAddTimeout(THUMB_MOUSE_UPDATE_SPEED, thumbUpdateMouse, s);

	initTexture (s, &ts->glowTexture);
	initTexture (s, &ts->windowTexture);

	imageToTexture (s, &ts->glowTexture,glowTex,32,32);
	imageToTexture (s, &ts->windowTexture,windowTex,32,32);

	ts->thumb.textPixmap = None;
	ts->oldThumb.textPixmap = None;

    return TRUE;
}


static void
thumbFiniScreen (CompPlugin *p,
				 CompScreen *s)
{
    THUMB_SCREEN (s);

    UNWRAP (ts, s, paintScreen);
    UNWRAP (ts, s, damageWindowRect);
	UNWRAP (ts, s, preparePaintScreen);
	UNWRAP (ts, s, donePaintScreen);
	UNWRAP (ts, s, paintWindow);
	UNWRAP (ts,s,windowResizeNotify);
	UNWRAP (ts, s, paintTransformedScreen);

	if (ts->mouseTimeout)
		compRemoveTimeout (ts->mouseTimeout);

	freeThumbText (s,&ts->thumb);
	freeThumbText (s,&ts->oldThumb);

	finiTexture (s, &ts->glowTexture);
	finiTexture (s, &ts->windowTexture);

    free (ts);
}


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

    return TRUE;
}


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


CompPluginVTable thumbVTable = {
    "thumbnail",
    N_("Window Previews"),
    N_("Window thumbnails at the taskbar"),
    thumbInit,
    thumbFini,
    thumbInitDisplay,
    thumbFiniDisplay,
    thumbInitScreen,
    thumbFiniScreen,
    thumbInitWindow,
    thumbFiniWindow,
    0, /* thumbGetDisplayOptions */
    0, /* thumbSetDisplayOption */
    thumbGetScreenOptions,
    thumbSetScreenOption,
    0, /* Deps */
    0, /* nDeps */
    0, /* Features */
    0, /* nFeatures */
	BERYL_ABI_INFO,
	"thumbnail",
	"misc",
	0,
	0,
	True,
};


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



syntax highlighted by Code2HTML, v. 0.9.1