All pastes #278863 Raw Edit

Something

public text v1 · immutable
·published 2006-12-14 13:55 UTC
rendered paste body
/**
 *
 * Beryl group plugin
 *
 * group.c
 *
 * Copyright : (C) 2006 by Patrick Niklaus
 * E-mail    : patrick.niklaus [ a t ] googlemail.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.
 *
 *
 * TODO:
 *      * glow effect
 *      * tab like switching between the windows
 *      * a lot of other tab stuff...
 *      * provide a tree system for groups
 *
 **/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#include <X11/Xlib.h>
#include <beryl.h>
#include <beryl_ipcs.h>
#include <X11/Xatom.h>

#include <math.h>

#include "group_glow.h"

#define PI 3.1415926535897
#define NUM_CORNER_POINTS 25

/*
 * Defaults
 *
 */
#define GROUP_SELECT_MODIFIERS_DEFAULT          (CompSuperMask)
#define GROUP_SELECT_BUTTON_DEFAULT             Button1

#define GROUP_SELECT_SINGLE_MODIFIERS_DEFAULT   (CompSuperMask)
#define GROUP_SELECT_SINGLE_KEY_DEFAULT         "s"

#define GROUP_GROUPING_MODIFIERS_DEFAULT        (CompSuperMask)
#define GROUP_GROUPING_KEY_DEFAULT              "g"

#define GROUP_UNGROUPING_MODIFIERS_DEFAULT      (CompSuperMask)
#define GROUP_UNGROUPING_KEY_DEFAULT            "u"

#define GROUP_TABMODE_MODIFIERS_DEFAULT         (CompSuperMask)
#define GROUP_TABMODE_KEY_DEFAULT               "t"

#define GROUP_CHANGE_TAB_LEFT_MODIFIERS_DEFAULT (CompSuperMask)
#define GROUP_CHANGE_TAB_LEFT_KEY_DEFAULT       "Left"

#define GROUP_CHANGE_TAB_RIGHT_MODIFIERS_DEFAULT (CompSuperMask)
#define GROUP_CHANGE_TAB_RIGHT_KEY_DEFAULT       "Right"

#define GROUP_REMOVEING_MODIFIERS_DEFAULT       (CompSuperMask)
#define GROUP_REMOVEING_KEY_DEFAULT             "r"

#define GROUP_CLOSEING_MODIFIERS_DEFAULT        (CompSuperMask)
#define GROUP_CLOSEING_KEY_DEFAULT              "c"

#define GROUP_MINIMIZEING_MODIFIERS_DEFAULT     (CompSuperMask)
#define GROUP_MINIMIZEING_KEY_DEFAULT           "b"

#define GROUP_IGNORE_MODIFIERS_DEFAULT          (CompAltMask)
#define GROUP_IGNORE_KEY_DEFAULT                "x"

#define GROUP_DISPLAY_OPTION_SELECT           0
#define GROUP_DISPLAY_OPTION_SELECT_SINGLE    1
#define GROUP_DISPLAY_OPTION_GROUPING         2
#define GROUP_DISPLAY_OPTION_UNGROUPING       3
#define GROUP_DISPLAY_OPTION_REMOVEING        4
#define GROUP_DISPLAY_OPTION_CLOSEING         5
#define GROUP_DISPLAY_OPTION_MINIMIZEING      6
#define GROUP_DISPLAY_OPTION_IGNORE           7
#define GROUP_DISPLAY_OPTION_TABMODE          8
#define GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT  9
#define GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT 10
#define GROUP_DISPLAY_OPTION_NUM              11

#define GROUP_OPACITY_DEFAULT            80
#define GROUP_OPACITY_MIN                0
#define GROUP_OPACITY_MAX                100

#define GROUP_SATURATION_DEFAULT         20
#define GROUP_SATURATION_MIN             0
#define GROUP_SATURATION_MAX             100

#define GROUP_BRIGHTNESS_DEFAULT         70
#define GROUP_BRIGHTNESS_MIN             0
#define GROUP_BRIGHTNESS_MAX             100

#define GROUP_TOLERANCE_DEFAULT          0
#define GROUP_TOLERANCE_MIN              0
#define GROUP_TOLERANCE_MAX              10

#define GROUP_STEPS_DEFAULT              10
#define GROUP_STEPS_MIN                  1
#define GROUP_STEPS_MAX                  10

#define GROUP_THUMB_SIZE_DEFAULT		 64
#define GROUP_THUMB_SIZE_MIN			 16
#define GROUP_THUMB_SIZE_MAX			 128

#define GROUP_BORDER_WIDTH_DEFAULT		 10
#define GROUP_BORDER_WIDTH_MIN			 1
#define GROUP_BORDER_WIDTH_MAX			 20

#define GROUP_BORDER_RADIUS_DEFAULT		 10
#define GROUP_BORDER_RADIUS_MIN			 1
#define GROUP_BORDER_RADIUS_MAX			 20

#define GROUP_MOVE_DEFAULT               TRUE
#define GROUP_RESIZE_DEFAULT             TRUE
#define GROUP_RAISE_DEFAULT              TRUE
#define GROUP_AUTO_UNGROUP_DEFAULT       FALSE
#define GROUP_AUTO_GROUP_DEFAULT         FALSE
#define GROUP_RELATIVE_DISTANCE_DEFAULT  FALSE

#define GROUP_COLOR_SELECTION_RED_DEFAULT       0x0000
#define GROUP_COLOR_SELECTION_GREEN_DEFAULT     0x0000
#define GROUP_COLOR_SELECTION_BLUE_DEFAULT      0x0000
#define GROUP_COLOR_SELECTION_ALPHA_DEFAULT     0x9999

#define GROUP_COLOR_LINE_RED_DEFAULT            0x0000
#define GROUP_COLOR_LINE_GREEN_DEFAULT          0x0000
#define GROUP_COLOR_LINE_BLUE_DEFAULT           0x0000
#define GROUP_COLOR_LINE_ALPHA_DEFAULT          0xABAB

#define GROUP_COLOR_TAB_FILL_RED_DEFAULT		0x0000
#define GROUP_COLOR_TAB_FILL_GREEN_DEFAULT		0x0000
#define GROUP_COLOR_TAB_FILL_BLUE_DEFAULT		0x0000
#define GROUP_COLOR_TAB_FILL_ALPHA_DEFAULT		0x9999

#define GROUP_COLOR_TAB_BORDER_RED_DEFAULT		0x0000
#define GROUP_COLOR_TAB_BORDER_GREEN_DEFAULT	0x0000
#define GROUP_COLOR_TAB_BORDER_BLUE_DEFAULT		0x0000
#define GROUP_COLOR_TAB_BORDER_ALPHA_DEFAULT	0xABAB

#define GROUP_TAB_CREATE_MIPMAPS_DEFAULT TRUE
#define GROUP_GLOW_DEFAULT TRUE

#define GROUP_SCREEN_OPTION_TYPES               0
#define GROUP_SCREEN_OPTION_OPACITY             1
#define GROUP_SCREEN_OPTION_SATURATION          2
#define GROUP_SCREEN_OPTION_BRIGHTNESS          3
#define GROUP_SCREEN_OPTION_GLOW		4
#define GROUP_SCREEN_OPTION_MOVE                5
#define GROUP_SCREEN_OPTION_RESIZE              6
#define GROUP_SCREEN_OPTION_RAISE               7
#define GROUP_SCREEN_OPTION_AUTO_UNGROUP        8
#define GROUP_SCREEN_OPTION_AUTO_GROUP          9
#define GROUP_SCREEN_OPTION_RELATIVE_DISTANCE   10
#define GROUP_SCREEN_OPTION_SELECTION_COLOR     11
#define GROUP_SCREEN_OPTION_LINE_COLOR          12
#define GROUP_SCREEN_OPTION_TOLERANCE           13
#define GROUP_SCREEN_OPTION_STEPS               14
#define GROUP_SCREEN_OPTION_THUMB_SIZE          15
#define GROUP_SCREEN_OPTION_BORDER_WIDTH        16
#define GROUP_SCREEN_OPTION_BORDER_RADIUS       17
#define GROUP_SCREEN_OPTION_TAB_BORDER_COLOR	18
#define GROUP_SCREEN_OPTION_TAB_FILL_COLOR	19
#define GROUP_SCREEN_OPTION_TAB_CREATE_MIPMAPS	20
#define GROUP_SCREEN_OPTION_NUM                 21

/*
 * Helpers
 *
 */
#define GET_GROUP_DISPLAY(d) ((GroupDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define GROUP_DISPLAY(d) GroupDisplay *gd = GET_GROUP_DISPLAY (d)
#define GET_GROUP_SCREEN(s, gd) ((GroupScreen *) (s)->privates[ (gd)->screenPrivateIndex].ptr)
#define GROUP_SCREEN(s) GroupScreen *gs = GET_GROUP_SCREEN (s, GET_GROUP_DISPLAY (s->display))
#define GET_GROUP_WINDOW(w, gs) ((GroupWindow *) (w)->privates[ (gs)->windowPrivateIndex].ptr)
#define GROUP_WINDOW(w) GroupWindow *gw = GET_GROUP_WINDOW (w, GET_GROUP_SCREEN  (w->screen, GET_GROUP_DISPLAY (w->screen->display)))

#define WIN_X(w) (w->attrib.x)
#define WIN_Y(w) (w->attrib.y)
#define WIN_WIDTH(w) (w->attrib.width)
#define WIN_HEIGHT(w) (w->attrib.height)
#define WIN_BORDER(w) (w->attrib.border_width)

#define NUM_OPTIONS(s) (sizeof ( (s)->opt) / sizeof (CompOption))
#define N_WIN_TYPE (sizeof (groupDefaultTypes) / sizeof (groupDefaultTypes[0]))
#define N_SEL_MODE (sizeof (groupSelectionModes) / sizeof (groupSelectionModes[0]))

#define REAL_POSITION(x, s) ( (x >= 0)? x: x + (s)->hsize * (s)->width )
#define VIEWPORT(x, s) ( ( REAL_POSITION(x, s) / (s)->width ) % (s)->hsize )

#define TOP_TAB(g) ((g)->windows[(g)->topTab])
#define PREV_TOP_TAB(g) ((g)->windows[(g)->prevTopTab])

/*
 * default windows for selection
 */
static char *groupDefaultTypes[] = {
	N_("Normal"),
	N_("Dialog"),
	N_("ModalDialog"),
	N_("Dnd")
};

/*
 * structs for pending callbacks
 */

typedef struct _GroupPendingMoves GroupPendingMoves;
struct _GroupPendingMoves {
    CompWindow *w;
    int dx;
    int dy;
    GroupPendingMoves *next;
};

typedef struct _GroupPendingResizes GroupPendingResizes;
struct _GroupPendingResizes {
    CompWindow *w;
    int x;
    int y;
    int width;
    int height;
    int borderWidth;
    GroupPendingResizes *next;
};

typedef struct _GroupPendingGrabs GroupPendingGrabs;
struct _GroupPendingGrabs {
    CompWindow *w;
    int x;
    int y;
    unsigned int state;
    unsigned int mask;
    GroupPendingGrabs *next;
};

typedef struct _GroupPendingUngrabs GroupPendingUngrabs;
struct _GroupPendingUngrabs {
    CompWindow *w;
    GroupPendingUngrabs *next;
};

/*
 * pointer to display list
 */
static int displayPrivateIndex;

/*
 * GroupTabBarSlot
 */
typedef struct _GroupTabBarSlot GroupTabBarSlot;
struct _GroupTabBarSlot {
	GroupTabBarSlot		*prev;
	GroupTabBarSlot		*next;

	int			rx;
	int			ry;
	int			height;
	int			width;

	CompWindow	*window;
};

/*
 * GroupTabBar
 */
typedef struct _GroupTabBar {
	GroupTabBarSlot		*slots;
	GroupTabBarSlot		*revSlots;
	int					nSlots;

	Bool				visible;

	int					rx;
	int					ry;
	int					height;
	int					width;
} GroupTabBar;

/*
 * GroupSelection
 */
typedef struct _GroupSelection GroupSelection;
struct _GroupSelection {
	GroupSelection *prev;
	GroupSelection *next;

	CompWindow **windows;
	int nWins;

	int topTab;
	int prevTopTab;
	
	Bool tabbed;
	GroupTabBar *tabBar;
	int animationSteps;

	int dx, dy;	//For tabbed windows.

	Bool	ungroup;

	GLushort color[4];
};

/*
 * GroupDisplay structure
 */
typedef struct _GroupDisplay {
	int screenPrivateIndex;
	CompOption opt[GROUP_DISPLAY_OPTION_NUM];
	HandleEventProc handleEvent;

	GroupSelection tmpSel;

	GroupSelection *groups;
	//GroupSelection *revGroups;

	Bool inAction;
	GroupSelection *lastActiveGroup;

	//int animatedGroup;

	Bool ignoreMode;

	float cornerPointsX[NUM_CORNER_POINTS];
	float cornerPointsY[NUM_CORNER_POINTS];
} GroupDisplay;

/*
 * GroupScreen structure
 */
typedef struct _GroupScreen {
	int windowPrivateIndex;
	CompOption opt[GROUP_SCREEN_OPTION_NUM];

	WindowMoveNotifyProc windowMoveNotify;
	WindowResizeNotifyProc windowResizeNotify;
	PreparePaintScreenProc preparePaintScreen;
	PaintScreenProc paintScreen;
	DrawWindowProc drawWindow;
	PaintWindowProc paintWindow;
	PaintTransformedScreenProc paintTransformedScreen;
	WindowGrabNotifyProc windowGrabNotify;
	WindowUngrabNotifyProc windowUngrabNotify;

	GroupPendingMoves *pendingMoves;
	GroupPendingResizes *pendingResizes;
	GroupPendingGrabs *pendingGrabs;
	GroupPendingUngrabs *pendingUngrabs;

	int wMask;
	Bool ignoreCallback;

	// for selection
	Bool grab;
	Bool wasTransformed;
	int grabIndex;
	int x1;
	int y1;
	int x2;
	int y2;

	CompTexture glowTexture;
} GroupScreen;

/*
 * GroupWindow structure
 */
typedef struct _GroupWindow {
	GroupSelection *group;
	Bool inGroup;
	Bool inSelection;

	// for the tab bar
	GroupTabBarSlot *slot;

	// for scale notify...
	int oldWidth;
	int oldHeight;
	int oldX;
	int oldY;

	// for tab animation
	int orgPosX; //Relative to the tabbed window.
	int orgPosY; //Relative to the tabbed window.
	
	int destinationX;
	int destinationY;
} GroupWindow;

#define TOLRNC_MIN(n1, n2, t) (n1 - t < n2)
#define TOLRNC_MAX(n1, n2, t) (n1 + t > n2)

/*
 * groupFindWindowsInRegion
 *
 */
static CompWindow **groupFindWindowsInRegion(CompScreen * s, REGION reg, int *c)
{
	GROUP_SCREEN(s);

	float tolerance = gs->opt[GROUP_SCREEN_OPTION_TOLERANCE].value.i / 10.0f;

	CompWindow **ret = NULL;
	int count = 0;
	CompWindow *w;
	for (w = s->windows; w; w = w->next) {
		if ((gs->wMask & w->type) &&
		    !w->invisible &&
		    TOLRNC_MIN(reg.extents.x1, WIN_X(w), tolerance * WIN_WIDTH(w)) &&
		    TOLRNC_MIN(reg.extents.y1, WIN_Y(w), tolerance * WIN_HEIGHT(w)) &&
		    TOLRNC_MAX(reg.extents.x2, (WIN_X(w) + WIN_WIDTH(w)), tolerance * WIN_WIDTH(w)) &&
		    TOLRNC_MAX(reg.extents.y2, (WIN_Y(w) + WIN_HEIGHT(w)), tolerance * WIN_HEIGHT(w))) {
			if (count == 0) {
				ret = calloc(1, sizeof(CompWindow));
				ret[0] = w;
			} else {
				ret = realloc(ret, sizeof(CompWindow) * (count + 1));
				ret[count] = w;
			}
			count++;
		}
	}
	(*c) = count;
	return ret;
}

/*
 * groupUpdatePosBuffer
 *
 */
static void
groupUpdatePosBuffer(CompWindow * w, int x, int y, int width, int height)
{
	GROUP_WINDOW(w);
	gw->oldX = x;
	gw->oldY = y;
	gw->oldWidth = width;
	gw->oldHeight = height;
}

/*
 * functions enqueuing pending notifies
 *
 */

static void groupEnqueueMoveNotify (CompScreen *s, CompWindow *w, int dx, int dy)
{
    GroupPendingMoves *move;

    GROUP_SCREEN (s);

    move = malloc (sizeof(GroupPendingMoves));

    if (!move)
	return;

    move->w = w;
    move->dx = dx;
    move->dy = dy;
    move->next = NULL;

    if (gs->pendingMoves) {
	GroupPendingMoves *temp;
	for (temp = gs->pendingMoves; temp->next; temp = temp->next);

	temp->next = move;
    } else {
	gs->pendingMoves = move;
    }
}

static void groupDequeueMoveNotifies (CompScreen *s)
{
    GroupPendingMoves *move, *next;

    GROUP_SCREEN (s);

    gs->ignoreCallback = TRUE;

    move = gs->pendingMoves;
    while (move) {
	moveWindow (move->w, move->dx, move->dy, TRUE, FALSE);
	groupUpdatePosBuffer (move->w, WIN_X(move->w) + move->dx, 
		WIN_Y(move->w) + move->dy, WIN_WIDTH(move->w), WIN_HEIGHT(move->w));

	next = move->next;
	free (move);
	move = next;
    }
    gs->pendingMoves = NULL;

    gs->ignoreCallback = FALSE;
}

static void groupEnqueueResizeNotify (CompScreen *s, CompWindow *w, int x, int y, 
	int width, int height, int border_width)
{
    GroupPendingResizes *resize;

    GROUP_SCREEN (s);

    resize = malloc (sizeof(GroupPendingResizes));

    if (!resize)
	return;

    resize->w = w;
    resize->x = x;
    resize->y = y;
    resize->width = width;
    resize->height = height;
    resize->borderWidth = border_width;
    resize->next = NULL;

    if (gs->pendingResizes) {
	GroupPendingResizes *temp;
	for (temp = gs->pendingResizes; temp->next; temp = temp->next);

	temp->next = resize;
    } else {
	gs->pendingResizes = resize;
    }
}

static void groupDequeueResizeNotifies (CompScreen *s)
{
    GroupPendingResizes *resize, *next;

    GROUP_SCREEN (s);

    gs->ignoreCallback = TRUE;

    resize = gs->pendingResizes;
    while (resize) {
	resizeWindow (resize->w, resize->x, resize->y, resize->width,
		resize->height, resize->borderWidth);
	groupUpdatePosBuffer (resize->w, resize->x, resize->y, resize->width, resize->height);

	next = resize->next;
	free (resize);
	resize = next;
    }
    gs->pendingResizes = NULL;

    gs->ignoreCallback = FALSE;
}

static void groupEnqueueGrabNotify (CompScreen *s, CompWindow *w, int x, int y, 
	unsigned int state, unsigned int mask)
{
    GroupPendingGrabs *grab;

    GROUP_SCREEN (s);

    grab = malloc (sizeof(GroupPendingGrabs));

    if (!grab)
	return;

    grab->w = w;
    grab->x = x;
    grab->y = y;
    grab->state = state;
    grab->mask = mask;
    grab->next = NULL;

    if (gs->pendingGrabs) {
	GroupPendingGrabs *temp;
	for (temp = gs->pendingGrabs; temp->next; temp = temp->next);

	temp->next = grab;
    } else {
	gs->pendingGrabs = grab;
    }
}

static void groupDequeueGrabNotifies (CompScreen *s)
{
    GroupPendingGrabs *grab, *next;

    GROUP_SCREEN (s);

    gs->ignoreCallback = TRUE;

    grab = gs->pendingGrabs;
    while (grab) {
	(*(grab->w)->screen->windowGrabNotify) (grab->w, grab->x, grab->y, grab->state, grab->mask);

	next = grab->next;
	free (grab);
	grab = next;
    }
    gs->pendingGrabs = NULL;

    gs->ignoreCallback = FALSE;
}

static void groupEnqueueUngrabNotify (CompScreen *s, CompWindow *w)
{
    GroupPendingUngrabs *ungrab;

    GROUP_SCREEN (s);

    ungrab = malloc (sizeof(GroupPendingUngrabs));

    if (!ungrab)
	return;

    ungrab->w = w;
    ungrab->next = NULL;

    if (gs->pendingUngrabs) {
	GroupPendingUngrabs *temp;
	for (temp = gs->pendingUngrabs; temp->next; temp = temp->next);

	temp->next = ungrab;
    } else {
	gs->pendingUngrabs = ungrab;
    }
}

static void groupDequeueUngrabNotifies (CompScreen *s)
{
    GroupPendingUngrabs *ungrab, *next;

    GROUP_SCREEN (s);

    gs->ignoreCallback = TRUE;

    ungrab = gs->pendingUngrabs;
    while (ungrab) {
	(*(ungrab->w)->screen->windowUngrabNotify) (ungrab->w);

	next = ungrab->next;
	free (ungrab);
	ungrab = next;
    }
    gs->pendingUngrabs = NULL;

    gs->ignoreCallback = FALSE;
}


/*
 * groupAddSkipMasks
 *
 */
static void
groupAddSkipMasks(CompWindow *w)
{
	unsigned int state = getWindowState (w->screen->display, w->id);
	state |= CompWindowStateSkipTaskbarMask;
	state |= CompWindowStateSkipPagerMask;
	setWindowState (w->screen->display, state, w->id);
}

/*
 * groupRemoveSkipMasks
 *
 */
static void
groupRemoveSkipMasks(CompWindow *w)
{
	unsigned int state = getWindowState (w->screen->display, w->id);
	state &= ~CompWindowStateSkipTaskbarMask;
	state &= ~CompWindowStateSkipPagerMask;
	setWindowState (w->screen->display, state, w->id);
}

/*
 * groupFindWindowIndex
 *
 */
static int
groupFindWindowIndex(CompWindow *w, GroupSelection *g)
{
	int i;
	
	for(i = 0; i < g->nWins; i++)
	{
		if(g->windows[i]->id == w->id)
			return i;
	}
	
	return -1;
}

/*
 * groupWindowMoveNotify
 *
 */
static void
groupWindowMoveNotify(CompWindow * w, int dx, int dy, Bool immediate)
{
	GROUP_SCREEN(w->screen);
	GROUP_DISPLAY(w->screen->display);
	GROUP_WINDOW(w);

	UNWRAP(gs, w->screen, windowMoveNotify);
	(*w->screen->windowMoveNotify) (w, dx, dy, immediate);
	WRAP(gs, w->screen, windowMoveNotify, groupWindowMoveNotify);
	
	groupUpdatePosBuffer(w, WIN_X(w) + dx, WIN_Y(w) + dy, WIN_WIDTH(w), WIN_HEIGHT(w));

	if (gw->inGroup && gw->group->tabbed &&
	    gw->group->topTab != -1)
	{
		if (TOP_TAB(gw->group)->id != w->id)
		{
			groupEnqueueMoveNotify (w->screen, w, -dx, -dy);
		}
		else
		{
			gw->group->dx += dx;
			gw->group->dy += dy;
		}

		return;
	}

       // workaround for viewports
       Bool vpMove = FALSE;
       if ((dx % w->screen->workArea.width == 0 && dx != 0) ||
           (dy % w->screen->workArea.height == 0 && dy != 0))
               vpMove = TRUE;

	if (!gw->inGroup || gs->ignoreCallback ||
	    gw->group->animationSteps ||
	    vpMove ||
	    IPCS_GetBoolND(IPCS_OBJECT(w), "SHOWDESKTOP_MOVE", FALSE) ||
	    IPCS_GetBoolND(IPCS_OBJECT(w->screen->display), "SCALE_INIT", FALSE) ||
	    !gs->opt[GROUP_SCREEN_OPTION_MOVE].value.b ||
	    gd->ignoreMode)
		return;
	
	int i;
		
	for (i = 0; i < gw->group->nWins; i++) {
		CompWindow *cw = gw->group->windows[i];
		if (!cw)
			continue;

		if (cw->id != w->id) {
			groupEnqueueMoveNotify (cw->screen, cw, dx, dy);
		}
	}

	gd->lastActiveGroup = gw->group;
	gd->inAction = TRUE;
}

/*
 * groupRecalcTabBarPos
 *
 */
static void groupRecalcTabBarPos(GroupSelection *group)
{
	if (group->topTab < 0 || !group->tabBar)
		return;

	GroupTabBar *bar = group->tabBar;
	CompWindow *topTab = TOP_TAB(group);
	GROUP_SCREEN(topTab->screen);

	int border_width = gs->opt[GROUP_SCREEN_OPTION_BORDER_WIDTH].value.i;
	int thumb_size = gs->opt[GROUP_SCREEN_OPTION_THUMB_SIZE].value.i;
	
	bar->width = border_width * (bar->nSlots + 1) + thumb_size * bar->nSlots;
	bar->height = border_width * 2 + thumb_size;
	bar->rx = WIN_WIDTH(topTab) / 2 - bar->width / 2;
	bar->ry = 0;
}

/*
 * groupInsertTabBarSlot
 *
 */
static void groupInsertTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot)
{

	if (bar->slots != NULL) {
			bar->revSlots->next = slot;
			slot->prev = bar->revSlots;
			slot->next = NULL;
	} else {
			slot->prev = NULL;
			slot->next = NULL;
			bar->slots = slot;
	}

	bar->revSlots = slot;

	CompWindow *w = slot->window;
	GROUP_WINDOW(w);
	groupRecalcTabBarPos(gw->group);
}

/*
 * groupUnhookTabBarSlot
 *
 */
static void groupUnhookTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot)
{
	GroupTabBarSlot *prev = slot->prev;
	GroupTabBarSlot *next = slot->next;
	if (prev || next) {
		if (prev)
		{
			if (next)
				prev->next = next;
			else
				prev->next = NULL;
		}
		if (next)
		{
			if (prev)
				next->prev = prev;
			else
				next->prev = NULL;
		}
	} else {
		bar->slots = NULL;
		bar->revSlots = NULL;
	}

	bar->nSlots--;

	CompWindow *w = slot->window;
	GROUP_WINDOW(w);
	groupRecalcTabBarPos(gw->group);

	free(slot);
}

/*
 * groupInitTabBar
 *
 */
static void	groupInitTabBar(GroupSelection *group)
{
	// error
	if (group->topTab < 0)
		return;

	CompWindow *topTab = group->windows[group->topTab];
	GROUP_SCREEN(topTab->screen);

	int border_width = gs->opt[GROUP_SCREEN_OPTION_BORDER_WIDTH].value.i;
	int thumb_size = gs->opt[GROUP_SCREEN_OPTION_THUMB_SIZE].value.i;

	GroupTabBar *bar = (GroupTabBar*) malloc(sizeof(GroupTabBar));
	bar->slots = NULL;
	bar->visible = FALSE;

	int i;
	for(i = 0; i < group->nWins; i++)
	{
		GroupTabBarSlot *slot = (GroupTabBarSlot*) malloc(sizeof(GroupTabBarSlot));
		slot->window = group->windows[i];
		slot->width = thumb_size;
		slot->height = thumb_size;
		slot->rx = border_width + ((slot->width + border_width) * i);
		slot->ry = border_width;

		groupInsertTabBarSlot(bar, slot);

		GROUP_WINDOW(group->windows[i]);
		gw->slot = slot;
	}
	bar->nSlots = group->nWins;
	group->tabBar = bar;

	groupRecalcTabBarPos(group);
}

/*
 * groupDeleteTabBar
 *
 */
static void groupDeleteTabBar(GroupSelection *group)
{
	GroupTabBar *bar = group->tabBar;

	GroupTabBarSlot *slot;
	for(slot = bar->slots; slot; slot = slot->next)
	{
		if (slot->prev) {
			free(slot->prev);
		}
		if (!slot->next) {
			free(slot);
			break;
		}
	}

	free(bar);
	group->tabBar = NULL;
}

/*
 * groupTabGroup
 *
 */
static void groupTabGroup(CompWindow * main)
{
	GROUP_WINDOW(main);
	GROUP_SCREEN(main->screen);
	GroupSelection *group = gw->group;
	
	if(!group || !gw->inGroup)
		return;
	
	int steps = gs->opt[GROUP_SCREEN_OPTION_STEPS].value.i;
	
	group->tabbed = TRUE;
	group->animationSteps = steps;
	
	// main window
	group->topTab = groupFindWindowIndex(main, group);
	group->prevTopTab = -1;
	
	if(group->topTab == -1)
		return;

	groupInitTabBar(group);
	group->tabBar->visible = TRUE;

	int i;
	for (i = 0; i < group->nWins; i++) {
		
		CompWindow *cw = group->windows[i];
		
		GROUP_WINDOW(cw);

		// center the window to the main window
		gw->destinationX = WIN_X(main) + (WIN_WIDTH(main) / 2) - (WIN_WIDTH(cw) / 2);
		gw->destinationY = WIN_Y(main) + (WIN_HEIGHT(main) / 2) - (WIN_HEIGHT(cw) / 2);

		gw->orgPosX = WIN_X(cw) - gw->destinationX;
		gw->orgPosY = WIN_Y(cw) - gw->destinationY;
	}
	
	damageScreen(main->screen);
}

/*
 * groupUntabGroup
 *
 */
static void
groupUntabGroup(CompDisplay *d, GroupSelection *group)
{
	//TODO: Respect window borders, struts and constrain y options...

	if(group->topTab == -1)
		return;

	GROUP_SCREEN(d->screens);
	GROUP_WINDOW(TOP_TAB(group));
	
	int maxY = 0, minY = TOP_TAB(group)->screen->height;
	int decreaseY = 0;
		
	int steps = gs->opt[GROUP_SCREEN_OPTION_STEPS].value.i;
	int mainOrgPosX = gw->orgPosX;
	int mainOrgPosY = gw->orgPosY;

	group->tabbed = FALSE;
	group->animationSteps = steps;

	// main window
	group->prevTopTab = group->topTab;
	group->topTab = -1;

	group->tabBar->visible = FALSE;
	groupDeleteTabBar(group);

	int i;
	
	for (i = 0; i < group->nWins; i++) {
		
		CompWindow *cw = group->windows[i];
		
		GROUP_WINDOW(cw);
		
		gw->destinationX = WIN_X(cw) + gw->orgPosX - mainOrgPosX;
		gw->destinationY = WIN_Y(cw) + gw->orgPosY - mainOrgPosY;
		
		if(i != group->prevTopTab)	//Window is offscreen.
		{
			gw->destinationX -= cw->screen->width;

			gw->destinationX += group->dx;
			gw->destinationY += group->dy;
		}
				
		if(gw->destinationY > maxY)
			maxY = gw->destinationY;
			
		if(gw->destinationY < minY)
			minY = gw->destinationY;
		
		gw->orgPosX = 0;
		gw->orgPosY = 0;
	}
	
	if(maxY > PREV_TOP_TAB(group)->screen->height)
		decreaseY = maxY - PREV_TOP_TAB(group)->screen->height;
	else if(minY < 0)
		decreaseY = minY;
	
	for (i = 0; i < group->nWins; i++) {
		
		CompWindow *cw = group->windows[i];
		
		GROUP_WINDOW(cw);
		
		gw->destinationY -= decreaseY;
	}
	
	
	damageScreen(PREV_TOP_TAB(group)->screen);
}

/*
 * groupDeleteGroup - pre-definition for groupDeleteGroupWindow
 *
 */
static void groupDeleteGroup(CompDisplay * d, GroupSelection *group);

/*
 * groupDeleteGroupWindow
 *
 */
static void groupDeleteGroupWindow(CompDisplay * d, CompWindow * w)
{
	GROUP_WINDOW(w);
	GROUP_SCREEN(w->screen);
	GroupSelection *group = gw->group;

	if (group->tabbed) {
		if (group->topTab >= 0 && 
			TOP_TAB(group)->id == w->id)
			groupUntabGroup(d, group);
		else
			groupUnhookTabBarSlot(group->tabBar, gw->slot);
	}

	if (group->nWins != 0
	    && group->windows != NULL) {
		CompWindow **buf = group->windows;

		group->windows = (CompWindow **) calloc(group->nWins - 1, sizeof(CompWindow *));

		int counter = 0;
		int i;
		for (i = 0; i < group->nWins; i++) {
			if (buf[i]->id == w->id)
				continue;
			group->windows[counter++] = buf[i];
		}
		group->nWins = counter;

		if (group->nWins == 1 &&
		    gs->opt[GROUP_SCREEN_OPTION_AUTO_UNGROUP].value.b) {
			groupDeleteGroup(d, gw->group);
		} else if (group->nWins <= 0) {
			free(group->windows);
			group->windows = NULL;
			groupDeleteGroup(d, group);
		}

		free(buf);

		gw->inGroup = FALSE;
	}
}

/*
 * groupDeleteGroup
 *
 */
static void groupDeleteGroup(CompDisplay * d, GroupSelection *group)
{
	GROUP_DISPLAY(d);
	gd->inAction = TRUE;
	gd->lastActiveGroup = NULL;

	if (group->windows != NULL) {
		if (group->tabbed) {
			groupUntabGroup(d, group);
			group->ungroup = TRUE;
			return;
		}

		int i;
		for (i = 0; i < group->nWins; i++) {
			CompWindow *cw = group->windows[i];
			GROUP_WINDOW(cw);

			gw->inGroup = FALSE;
		}
		free(group->windows);
	}

	GroupSelection *prev = group->prev;
	GroupSelection *next = group->next;

	// relink stack
	if (prev || next) {
		if (prev) {
			if (next)
				prev->next = next;
			else {
				prev->next = NULL;
				//gd->revGroups = prev;
			}
		}
		if (next) {
			if (prev)
				next->prev = prev;
			else {
				next->prev = NULL;
				gd->groups = next;
			}
		}
	} else {
		gd->groups = NULL;
	}
	free(group);

	gd->inAction = FALSE;
}

/*
 * groupAddWindowToGroup
 *
 */
static void
groupAddWindowToGroup(CompDisplay * d, CompWindow * w, GroupSelection *group)
{
	GROUP_DISPLAY(d);
	GROUP_WINDOW(w);

	if (gw->inGroup) 
		groupDeleteGroupWindow(d, w);

	if (group) {
		group->windows = (CompWindow **) realloc(group->windows, sizeof(CompWindow *) * (group->nWins + 1));
		group->windows[group->nWins] = w;
		group->nWins++;

		gw->group = group;
		gw->inGroup = TRUE;
	} else {
		GroupSelection *g = malloc(sizeof(GroupSelection));

		g->windows = (CompWindow **) calloc(1, sizeof(CompWindow *));
		g->windows[0] = w;
		g->nWins = 1;
		g->topTab = -1;
		g->prevTopTab = -1;
		g->animationSteps = 0;
		g->tabbed = FALSE;
		g->ungroup = FALSE;
		g->tabBar = NULL;

		g->dx = 0;
		g->dy = 0;

		// glow color
		srand(time(NULL));
		g->color[0] = rand() % 0xFFFF;
		g->color[1] = rand() % 0xFFFF;
		g->color[2] = rand() % 0xFFFF;
		g->color[3] = 0xFFFF;

		// relink stack
		if (gd->groups) {
			gd->groups->prev = g;
			g->next = gd->groups;
			g->prev = NULL;
			gd->groups = g;
		} else {
			g->prev = NULL;
			g->next = NULL;
			gd->groups = g;
		}

		gw->group = g;
		gw->inGroup = TRUE;
	}
}

/*
 * groupDeleteSelectionWindow
 *
 */
static void groupDeleteSelectionWindow(CompDisplay * d, CompWindow * w)
{
	GROUP_DISPLAY(d);

	if (gd->tmpSel.nWins > 0 && gd->tmpSel.windows) {
		CompWindow **buf = gd->tmpSel.windows;
		gd->tmpSel.windows = (CompWindow **) calloc(gd->tmpSel.nWins - 1, sizeof(CompWindow *));

		int counter = 0;
		int i;
		for (i = 0; i < gd->tmpSel.nWins; i++) {
			if (buf[i]->id == w->id)
				continue;
			gd->tmpSel.windows[counter++] = buf[i];
		}

		gd->tmpSel.nWins = counter;
		free(buf);
	}
}

/*
 * groupAddWindowToSelection
 *
 */
static void groupAddWindowToSelection(CompDisplay * d, CompWindow * w)
{
	GROUP_DISPLAY(d);

	gd->tmpSel.windows = (CompWindow **) realloc(gd->tmpSel.windows, sizeof(CompWindow *) * (gd->tmpSel.nWins + 1));

	gd->tmpSel.windows[gd->tmpSel.nWins] = w;
	gd->tmpSel.nWins++;
}

/*
 * groupSyncWindows
 *
 */
static void groupSyncWindows(GroupSelection *group)
{
	int i;
	for (i = 0; i < group->nWins; i++) {
		CompWindow *w = group->windows[i];
		syncWindowPosition(w);
	}
}

/*
 * groupRaiseWindows
 *
 */
static void
groupRaiseWindows(CompWindow * top, GroupSelection *group)
{
	int i;
	for (i = 0; i < group->nWins; i++) {
		CompWindow *w = group->windows[i];
		if (w->id == top->id)
			continue;
		restackWindowBelow(w, top);
	}
}

/*
 * groupScreenInitOptions
 *
 */
static void groupScreenInitOptions(GroupScreen * gs)
{

	CompOption *o;
	int i;

	o = &gs->opt[GROUP_SCREEN_OPTION_TYPES];
	o->name = "mask";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Window Types");
	o->longDesc = N_("The types of windows which will be grouped");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = N_WIN_TYPE;
	o->value.list.value = malloc(sizeof(CompOptionValue) * N_WIN_TYPE);
	for (i = 0; i < N_WIN_TYPE; i++) {
		o->value.list.value[i].s = strdup(groupDefaultTypes[i]);
		o->rest.s.string = (char **) windowTypeString;
		o->rest.s.nString = nWindowTypeString;
	}
	gs->wMask = compWindowTypeMaskFromStringList(&o->value);

	o = &gs->opt[GROUP_SCREEN_OPTION_OPACITY];
	o->name = "opacity";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Opacity");
	o->longDesc = N_("Opacity of selected windows");
	o->type = CompOptionTypeInt;
	o->value.i = GROUP_OPACITY_DEFAULT;
	o->rest.i.min = GROUP_OPACITY_MIN;
	o->rest.i.max = GROUP_OPACITY_MAX;

	o = &gs->opt[GROUP_SCREEN_OPTION_SATURATION];
	o->name = "saturation";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Saturation");
	o->longDesc = N_("Saturation of selected windows");
	o->type = CompOptionTypeInt;
	o->value.i = GROUP_SATURATION_DEFAULT;
	o->rest.i.min = GROUP_SATURATION_MIN;
	o->rest.i.max = GROUP_SATURATION_MAX;

	o = &gs->opt[GROUP_SCREEN_OPTION_BRIGHTNESS];
	o->name = "brightness";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Brightness");
	o->longDesc = N_("Brightness of selected windows");
	o->type = CompOptionTypeInt;
	o->value.i = GROUP_BRIGHTNESS_DEFAULT;
	o->rest.i.min = GROUP_BRIGHTNESS_MIN;
	o->rest.i.max = GROUP_BRIGHTNESS_MAX;

	o = &gs->opt[GROUP_SCREEN_OPTION_TOLERANCE];
	o->name = "tolerance";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Tolerance");
	o->longDesc = N_("Tolerance of the seletion.");
	o->type = CompOptionTypeInt;
	o->value.i = GROUP_TOLERANCE_DEFAULT;
	o->rest.i.min = GROUP_TOLERANCE_MIN;
	o->rest.i.max = GROUP_TOLERANCE_MAX;

	o = &gs->opt[GROUP_SCREEN_OPTION_STEPS];
	o->name = "steps";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Steps");
	o->longDesc =
	    N_("The animation steps that are needed for animation.");
	o->type = CompOptionTypeInt;
	o->value.i = GROUP_STEPS_DEFAULT;
	o->rest.i.min = GROUP_STEPS_MIN;
	o->rest.i.max = GROUP_STEPS_MAX;

	o = &gs->opt[GROUP_SCREEN_OPTION_THUMB_SIZE];
	o->name = "thumb_size";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Thumb Size");
	o->longDesc =
	    N_("The size of the window thumbs in the tab bar.");
	o->type = CompOptionTypeInt;
	o->value.i = GROUP_THUMB_SIZE_DEFAULT;
	o->rest.i.min = GROUP_THUMB_SIZE_MIN;
	o->rest.i.max = GROUP_THUMB_SIZE_MAX;

	o = &gs->opt[GROUP_SCREEN_OPTION_BORDER_WIDTH];
	o->name = "broder_width";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Border Width");
	o->longDesc =
	    N_("The width of the border around the tab bar.");
	o->type = CompOptionTypeInt;
	o->value.i = GROUP_BORDER_WIDTH_DEFAULT;
	o->rest.i.min = GROUP_BORDER_WIDTH_MIN;
	o->rest.i.max = GROUP_BORDER_WIDTH_MAX;

	o = &gs->opt[GROUP_SCREEN_OPTION_BORDER_RADIUS];
	o->name = "broder_radius";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Border Radius");
	o->longDesc =
	    N_("The radius for the edges of the tab bar.");
	o->type = CompOptionTypeInt;
	o->value.i = GROUP_BORDER_RADIUS_DEFAULT;
	o->rest.i.min = GROUP_BORDER_RADIUS_MIN;
	o->rest.i.max = GROUP_BORDER_RADIUS_MAX;

	o = &gs->opt[GROUP_SCREEN_OPTION_MOVE];
	o->name = "move";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Move every window in the group");
	o->longDesc = N_("If one window in the group gets moved, "
			 "every other window in the group gets moved as well.");
	o->type = CompOptionTypeBool;
	o->value.b = GROUP_MOVE_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_RESIZE];
	o->name = "resize";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Resize every window in the group");
	o->longDesc = N_("If one window in the group gets resized, "
			 "every other window in the group gets resized as well.");
	o->type = CompOptionTypeBool;
	o->value.b = GROUP_RESIZE_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_RAISE];
	o->name = "raise";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Raise every window in the group");
	o->longDesc = N_("If one window in the group gets selected, "
			 "every window in the group gets raised.");
	o->type = CompOptionTypeBool;
	o->value.b = GROUP_RAISE_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_AUTO_GROUP];
	o->name = "auto_group";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Group the windows after selection");
	o->longDesc = N_("If you have selected your windows,"
			 "this automatically groups them. "
			 "(Doesn't work with selection mode 'normal')");
	o->type = CompOptionTypeBool;
	o->value.b = GROUP_AUTO_GROUP_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_AUTO_UNGROUP];
	o->name = "auto_ungroup";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc =
	    N_("Ungroup the windows if only one window is left");
	o->longDesc =
	    N_
	    ("If there is only 1 window in the group left, it will be ungrouped.");
	o->type = CompOptionTypeBool;
	o->value.b = GROUP_AUTO_UNGROUP_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_RELATIVE_DISTANCE];
	o->name = "relative_distance";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Compute distance relative");
	o->longDesc =
	    N_
	    ("The distance between the windows is computed relative to the window size. "
	     "This allows you to have windows staying next to eachother.");
	o->type = CompOptionTypeBool;
	o->value.b = GROUP_RELATIVE_DISTANCE_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_SELECTION_COLOR];
	o->name = "fill_color";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Selection Color");
	o->longDesc = N_("Fill color of the selection.");
	o->type = CompOptionTypeColor;
	o->value.c[0] = GROUP_COLOR_SELECTION_RED_DEFAULT;
	o->value.c[1] = GROUP_COLOR_SELECTION_GREEN_DEFAULT;
	o->value.c[2] = GROUP_COLOR_SELECTION_BLUE_DEFAULT;
	o->value.c[3] = GROUP_COLOR_SELECTION_ALPHA_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_LINE_COLOR];
	o->name = "line_color";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Line Color");
	o->longDesc = N_("Line color of the selection.");
	o->type = CompOptionTypeColor;
	o->value.c[0] = GROUP_COLOR_LINE_RED_DEFAULT;
	o->value.c[1] = GROUP_COLOR_LINE_GREEN_DEFAULT;
	o->value.c[2] = GROUP_COLOR_LINE_BLUE_DEFAULT;
	o->value.c[3] = GROUP_COLOR_LINE_ALPHA_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_TAB_FILL_COLOR];
	o->name = "tab_color";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Tab Color");
	o->longDesc = N_("Fill color of the tab bar.");
	o->type = CompOptionTypeColor;
	o->value.c[0] = GROUP_COLOR_TAB_FILL_RED_DEFAULT;
	o->value.c[1] = GROUP_COLOR_TAB_FILL_GREEN_DEFAULT;
	o->value.c[2] = GROUP_COLOR_TAB_FILL_BLUE_DEFAULT;
	o->value.c[3] = GROUP_COLOR_TAB_FILL_ALPHA_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_TAB_BORDER_COLOR];
	o->name = "border_color";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Tab Border Color");
	o->longDesc = N_("Border color of the tab bar.");
	o->type = CompOptionTypeColor;
	o->value.c[0] = GROUP_COLOR_TAB_BORDER_RED_DEFAULT;
	o->value.c[1] = GROUP_COLOR_TAB_BORDER_GREEN_DEFAULT;
	o->value.c[2] = GROUP_COLOR_TAB_BORDER_BLUE_DEFAULT;
	o->value.c[3] = GROUP_COLOR_TAB_BORDER_ALPHA_DEFAULT;
	
	o = &gs->opt[GROUP_SCREEN_OPTION_TAB_CREATE_MIPMAPS];
        o->name                 = "mipmaps";
        o->shortDesc        	= N_("create mipmaps for thumbnails.");
        o->group                = N_("");
        o->subGroup             = N_("");
        o->displayHints         = "";
        o->longDesc             = N_("Create mipmaps for thumbnails in the tab-bar.");
        o->type                 = CompOptionTypeBool;
        o->value.b              = GROUP_TAB_CREATE_MIPMAPS_DEFAULT;

	o = &gs->opt[GROUP_SCREEN_OPTION_GLOW];
        o->name                 = "glow";
        o->shortDesc        	= N_("Enable Glow");
        o->group                = N_("");
        o->subGroup             = N_("");
        o->displayHints         = "";
        o->longDesc             = N_("Enable grouped window glowing.");
        o->type                 = CompOptionTypeBool;
        o->value.b              = GROUP_GLOW_DEFAULT;
}

/*
 * groupGetScreenOptions
 *
 */
static CompOption *groupGetScreenOptions(CompScreen * s, int *count)
{
	if (s) {
		GROUP_SCREEN(s);

		*count = NUM_OPTIONS(gs);
		return gs->opt;
	} else {
		GroupScreen *gs = malloc(sizeof(GroupScreen));
		groupScreenInitOptions(gs);
		*count = NUM_OPTIONS(gs);
		return gs->opt;
	}
}


/*
 * groupSetScreenOption
 *
 */
static Bool
groupSetScreenOption(CompScreen * s, char *name, CompOptionValue * value)
{

	CompOption *o;
	int index;

	GROUP_SCREEN(s);

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

	switch (index) {
	case GROUP_SCREEN_OPTION_TYPES:
		if (compSetOptionList(o, value)) {
			gs->wMask =
			    compWindowTypeMaskFromStringList(&o->value);
			return TRUE;
		}
		break;

	case GROUP_SCREEN_OPTION_SELECTION_COLOR:
	case GROUP_SCREEN_OPTION_LINE_COLOR:
	case GROUP_SCREEN_OPTION_TAB_FILL_COLOR:
	case GROUP_SCREEN_OPTION_TAB_BORDER_COLOR:
		if (compSetColorOption(o, value)) {
			return TRUE;
		}
		break;

	case GROUP_SCREEN_OPTION_OPACITY:
	case GROUP_SCREEN_OPTION_BRIGHTNESS:
	case GROUP_SCREEN_OPTION_SATURATION:
	case GROUP_SCREEN_OPTION_TOLERANCE:
	case GROUP_SCREEN_OPTION_STEPS:
	case GROUP_SCREEN_OPTION_THUMB_SIZE:
	case GROUP_SCREEN_OPTION_BORDER_WIDTH:
	case GROUP_SCREEN_OPTION_BORDER_RADIUS:
		if (compSetIntOption(o, value)) {
			return TRUE;
		}
		break;

	case GROUP_SCREEN_OPTION_GLOW:
	case GROUP_SCREEN_OPTION_MOVE:
	case GROUP_SCREEN_OPTION_RESIZE:
	case GROUP_SCREEN_OPTION_RAISE:
	case GROUP_SCREEN_OPTION_AUTO_UNGROUP:
	case GROUP_SCREEN_OPTION_AUTO_GROUP:
	case GROUP_SCREEN_OPTION_RELATIVE_DISTANCE:
	case GROUP_SCREEN_OPTION_TAB_CREATE_MIPMAPS:
		if (compSetBoolOption(o, value)) {
			return TRUE;
		}
		break;

	default:
		break;
	}

	return FALSE;
}

/*
 * groupSelectWindow
 *
 */
static void groupSelectWindow(CompDisplay * d, CompWindow * w)
{
	GROUP_DISPLAY(d);
	GROUP_SCREEN(w->screen);
	GROUP_WINDOW(w);

	// select singe window
	if ((gs->wMask & w->type) &&
	    !w->invisible && !gw->inSelection && !gw->inGroup) {

		groupAddWindowToSelection(d, w);

		gw->inSelection = TRUE;
		addWindowDamage(w);
	}
	// unselect single window
	else if ((gs->wMask & w->type) &&
		 !w->invisible && gw->inSelection && !gw->inGroup) {

		groupDeleteSelectionWindow(d, w);
		gw->inSelection = FALSE;
		addWindowDamage(w);
	}
	// select group
	else if ((gs->wMask & w->type) &&
		 !w->invisible && !gw->inSelection && gw->inGroup) {

		int i;
		for (i = 0; i < gw->group->nWins; i++) {
			CompWindow *cw = gw->group->windows[i];
			GROUP_WINDOW(cw);

			groupAddWindowToSelection(d, cw);

			gw->inSelection = TRUE;
			addWindowDamage(cw);
		}
	}
	// Unselect group
	else if ((gs->wMask & w->type) &&
		 !w->invisible && gw->inSelection && gw->inGroup) {

		GroupSelection *group = gw->group;
		CompWindow **buf = gd->tmpSel.windows;
		gd->tmpSel.windows = (CompWindow **) calloc(gd->tmpSel.nWins - gw->group->nWins, sizeof(CompWindow *));

		int counter = 0;
		int i;
		for (i = 0; i < gd->tmpSel.nWins; i++) {
			CompWindow *cw = buf[i];
			GROUP_WINDOW(cw);

			if (gw->inGroup
			    && gw->group == group) {
				gw->inSelection = FALSE;
				addWindowDamage(cw);
				continue;
			}

			gd->tmpSel.windows[counter++] = buf[i];
		}
		gd->tmpSel.nWins = counter;
		free(buf);
	}

}

/*
 * groupSelectSingle
 *
 */
static Bool
groupSelectSingle(CompDisplay * d, CompAction * action,
		  CompActionState state, CompOption * option, int nOption)
{
	CompWindow *w = (CompWindow *) findWindowAtDisplay(d, d->activeWindow);

	if (w)
		groupSelectWindow(d, w);

	return TRUE;
}

/*
 * groupSelect
 *
 */
static Bool
groupSelect(CompDisplay * d, CompAction * action, CompActionState state,
	    CompOption * option, int nOption)
{
	CompWindow *w = (CompWindow *) findWindowAtDisplay(d, d->activeWindow);
	if (!w)
		return FALSE;

	GROUP_SCREEN(w->screen);

	if (!gs->grab) {

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

		if (!gs->grabIndex)
			gs->grabIndex =
			    pushScreenGrab(w->screen, None, "group");

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

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

		gs->x1 = gs->x2 = d->pointerX;
		gs->y1 = gs->y2 = d->pointerY;
		gs->grab = TRUE;
	}

	return TRUE;
}

/*
 * groupGroupWindows - pre-definition for groupSelectTerminate
 *
 */
static Bool
groupGroupWindows(CompDisplay * d, CompAction * action,
		  CompActionState state, CompOption * option, int nOption);

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

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

	for (s = d->screens; s; s = s->next) {
		if (xid && s->root != xid)
			continue;

		break;
	}

	if (s) {

		GROUP_SCREEN(s);

		if (gs->grab) {
			if (gs->grabIndex) {
				removeScreenGrab(s, gs->grabIndex, NULL);
				gs->grabIndex = 0;
			}

			if (gs->x1 != gs->x2 && gs->y1 != gs->y2) {
				REGION reg;
				reg.rects = &reg.extents;
				reg.numRects = 1;

				reg.extents.x1 = MIN(gs->x1, gs->x2) - 1;
				reg.extents.y1 = MIN(gs->y1, gs->y2) - 1;
				reg.extents.x2 = MAX(gs->x1, gs->x2) + 1;
				reg.extents.y2 = MAX(gs->y1, gs->y2) + 1;

				damageScreenRegion(s, &reg);

				int count;
				CompWindow **ws = groupFindWindowsInRegion(s, reg, &count);

				if (ws) {
					// select windows
					int i;
					for (i = 0; i < count; i++) {
						CompWindow *cw = ws[i];

						groupSelectWindow(d, cw);
					}
					if (gs->opt[GROUP_SCREEN_OPTION_AUTO_GROUP].value.b) {
						groupGroupWindows(d, NULL, 0, NULL, 0);
					}
					free(ws);
				}
			}

			gs->grab = FALSE;
		}

	}

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

	return FALSE;
}

/*
 * groupGroupWindows
 *
 */
static Bool
groupGroupWindows(CompDisplay * d, CompAction * action,
		  CompActionState state, CompOption * option, int nOption)
{
	GROUP_DISPLAY(d);

	if (gd->tmpSel.nWins > 0) {
		int i;
		CompWindow *cw;

		// we need to do one first to get the pointer of a new group
		cw = gd->tmpSel.windows[0];
		groupAddWindowToGroup(d, cw, NULL);

		GROUP_WINDOW (cw);
		gw->inSelection = FALSE;
		damageScreen(cw->screen);

		GroupSelection *group = gw->group;
		for (i = 1; i < gd->tmpSel.nWins; i++) {
			cw = gd->tmpSel.windows[i];
			GROUP_WINDOW(cw);

			groupAddWindowToGroup(d, cw, group);

			gw->inSelection = FALSE;
			damageScreen(cw->screen);
		}

		// exit selection
		free(gd->tmpSel.windows);
		gd->tmpSel.windows = NULL;
		gd->tmpSel.nWins = 0;
	}

	return FALSE;
}

/*
 * groupUnGroupWindows
 *
 */
static Bool
groupUnGroupWindows(CompDisplay * d, CompAction * action,
		    CompActionState state, CompOption * option,
		    int nOption)
{
	CompWindow *cw = findWindowAtDisplay(d, d->activeWindow);

	if (!cw)
		return FALSE;

	GROUP_WINDOW(cw);
	GROUP_SCREEN(cw->screen);

	if (gw->inGroup) {
		groupDeleteGroup(d, gw->group);
		if (gs->opt[GROUP_SCREEN_OPTION_GLOW].value.b)
		    damageScreen(cw->screen);
	}

	return FALSE;
}

/*
 * groupInitTab
 *
 */
static Bool
groupInitTab(CompDisplay * d, CompAction * action, CompActionState state,
	     CompOption * option, int nOption)
{
	CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
	if (!w)
		return FALSE;

	GROUP_WINDOW(w);
	if (!gw->inGroup)
		return FALSE;

	if (gw->group->tabbed) {
		groupUntabGroup(d, gw->group);
	} else {
		groupTabGroup(w);
	}

	return TRUE;
}

#define LEFT TRUE
#define RIGHT FALSE

/*
 * groupChangeTab
 *
 */
static Bool
groupChangeTab(CompDisplay * d, Bool direction)
{
	//TODO: return better values... :P
	//
	CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
	
	if (!w)
		return TRUE;
	
	GROUP_WINDOW(w);
	
	if (!gw->inGroup || !gw->group->tabbed || gw->group->topTab == -1)
		return TRUE;
	
	gw->group->prevTopTab = gw->group->topTab;
	
	if(direction == RIGHT)
		gw->group->topTab++;
	else
		gw->group->topTab--;
	
	if(gw->group->topTab == gw->group->nWins)
		gw->group->topTab = 0;
	else if(gw->group->topTab == -1)
		gw->group->topTab = gw->group->nWins - 1;
		

	return TRUE;
}

/*
 * groupChangeTabLeft
 *
 */
static Bool
groupChangeTabLeft(CompDisplay * d, CompAction * action, CompActionState state,
	       CompOption * option, int nOption)
{
	return groupChangeTab(d, LEFT);
}
	       
/*
 * groupChangeTabRight
 *
 */
static Bool
groupChangeTabRight(CompDisplay * d, CompAction * action, CompActionState state,
	       CompOption * option, int nOption)
{
	return groupChangeTab(d, RIGHT);
}

/*
 * groupRemoveWindow
 *
 */
static Bool
groupRemoveWindow(CompDisplay * d, CompAction * action,
		  CompActionState state, CompOption * option, int nOption)
{
	CompWindow *cw = findWindowAtDisplay(d, d->activeWindow);
	if (!cw)
		return FALSE;
	GROUP_WINDOW(cw);

	if (gw->inGroup)
		groupDeleteGroupWindow(d, cw);

	return FALSE;
}

/*
 * groupCloseWindows
 *
 */
static Bool
groupCloseWindows(CompDisplay * d, CompAction * action,
		  CompActionState state, CompOption * option, int nOption)
{
	CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
	if (!w)
		return FALSE;
	GROUP_WINDOW(w);

	if (gw->inGroup) {

		int nWins = gw->group->nWins;
		int i;
		for (i = 0; i < nWins; i++) {
			CompWindow *cw = gw->group->windows[i];
			closeWindow(cw, getCurrentTimeFromDisplay(d));
		}
	}

	return FALSE;
}

/*
 * groupMinimizeWindows
 *
 */
static Bool
groupMinimizeWindows(CompDisplay * d, CompAction * action,
		     CompActionState state, CompOption * option,
		     int nOption)
{
	CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
	if (!w)
		return FALSE;
	GROUP_WINDOW(w);

	if (gw->inGroup) {

		int nWins = gw->group->nWins;
		int i;
		for (i = 0; i < nWins; i++) {
			CompWindow *cw = gw->group->windows[i];
			minimizeWindow(cw);
		}
	}

	return FALSE;
}

/*
 * groupSetIgnore
 *
 */
static Bool
groupSetIgnore(CompDisplay * d, CompAction * action, CompActionState state,
	       CompOption * option, int nOption)
{
	GROUP_DISPLAY(d);

	gd->ignoreMode = TRUE;

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

	return FALSE;
}

/*
 * groupUnsetIgnore
 *
 */
static Bool
groupUnsetIgnore(CompDisplay * d, CompAction * action,
		 CompActionState state, CompOption * option, int nOption)
{
	GROUP_DISPLAY(d);

	gd->ignoreMode = FALSE;

	action->state &= ~CompActionStateTermKey;

	return FALSE;
}

/*
 * groupHandleMotionEvent - pre-definition for groupHandleEvent
 *
 */
static void groupHandleMotionEvent(CompScreen * s, int xRoot, int yRoot);

/*
 * groupHandleEvent
 *
 */
static void groupHandleEvent(CompDisplay * d, XEvent * event)
{
	GROUP_DISPLAY(d);

	switch (event->type) {
	case MotionNotify:
		{
			CompScreen *s = findScreenAtDisplay(d, event->xmotion.root);
			if (s)
				groupHandleMotionEvent(s, d->pointerX, d->pointerY);
			break;
		}

	case ButtonRelease:
		// update windows
		if (event->xbutton.button == 1) {
			if (gd->inAction && gd->lastActiveGroup) {
				groupSyncWindows(gd->lastActiveGroup);
				gd->lastActiveGroup = NULL;
				gd->inAction = FALSE;
			}
		}
		break;

	case UnmapNotify:
		{
			CompWindow *w = findWindowAtDisplay(d, event->xunmap.window);
			if (!w)
				break;
			GROUP_WINDOW(w);

			// close event
			if (gw->inGroup && !w->pendingUnmaps) {
				groupDeleteGroupWindow(d, w);
				damageScreen(w->screen);
			}
			break;
		}

	default:
		break;
	}

	UNWRAP(gd, d, handleEvent);
	(*d->handleEvent) (d, event);
	WRAP(gd, d, handleEvent, groupHandleEvent);

	switch (event->type) {
	case PropertyNotify:
		// focus event
		if (event->xproperty.atom == d->winActiveAtom) {
			if (!gd->inAction) {
				CompWindow *w = (CompWindow *) findWindowAtDisplay(d, d->activeWindow);
				if (!w)
					break;

				GROUP_WINDOW(w);
				GROUP_SCREEN(w->screen);

				if (gw->inGroup &&
					!gw->group->tabbed &&
				    !IPCS_GetBoolND(IPCS_OBJECT(w->screen->display), "SCALE_INIT", FALSE) &&
				    gs->opt[GROUP_SCREEN_OPTION_RAISE].value.b) {
					groupRaiseWindows(w, gw->group);
				} else if(gw->inGroup && gw->group->tabbed) {
					focusWindow(TOP_TAB(gw->group));
					moveInputFocusToWindow(TOP_TAB(gw->group));
				}
			}
			
		}
		break;

	default:
		break;
	}
}

/*
 * groupWindowResizeNotify
 *
 */
static void groupWindowResizeNotify(CompWindow * w)
{
	GROUP_SCREEN(w->screen);
	GROUP_DISPLAY(w->screen->display);
	GROUP_WINDOW(w);

	UNWRAP(gs, w->screen, windowResizeNotify);
	(*w->screen->windowResizeNotify) (w);
	WRAP(gs, w->screen, windowResizeNotify, groupWindowResizeNotify);

	if (gw->inGroup && !gd->ignoreMode && !gs->ignoreCallback &&
	    gs->opt[GROUP_SCREEN_OPTION_RESIZE].value.b) {
		int rw = WIN_WIDTH(w) - gw->oldWidth;
		int rh = WIN_HEIGHT(w) - gw->oldHeight;
		int rx = WIN_X(w) - gw->oldX;
		int ry = WIN_Y(w) - gw->oldY;

		int i;
		for (i = 0; i < gw->group->nWins; i++) {
			CompWindow *cw =  gw->group->windows[i];
			if (!cw)
				continue;

			if (cw->id != w->id) {
				int nx = 0;
				int ny = 0;

				if (gs->opt[GROUP_SCREEN_OPTION_RELATIVE_DISTANCE].value.b) {
					int distX = WIN_X(cw) - gw->oldX;
					int distY = WIN_Y(cw) - gw->oldY;
					int ndistX = distX * ((float) WIN_WIDTH (w) / (float) gw->oldWidth);
					int ndistY = distY * ((float) WIN_HEIGHT(w) / (float) gw->oldHeight);
					nx = WIN_X(w) + ndistX;
					ny = WIN_Y(w) + ndistY;
				} else {
					nx = WIN_X(cw) + rx;
					ny = WIN_Y(cw) + ry;
				}

				int nwidth = WIN_WIDTH(cw) + rw;
				int nheight = WIN_HEIGHT(cw) + rh;

				XWindowChanges xwc;
				xwc.x = nx;
				xwc.y = ny;
				xwc.width = nwidth;
				xwc.height = nheight;
				configureXWindow(cw, CWX | CWY | CWWidth |CWHeight, &xwc);
				groupEnqueueResizeNotify (cw->screen, cw, nx, ny, nwidth, nheight, WIN_BORDER(cw));
			}

		}
		gd->lastActiveGroup = gw->group;
		gd->inAction = TRUE;
	}

	groupUpdatePosBuffer(w, WIN_X(w), WIN_Y(w), WIN_WIDTH(w), WIN_HEIGHT(w));
}

static void
groupWindowGrabNotify(CompWindow * w,
                       int x, int y, unsigned int state, unsigned int mask)
{
        GROUP_SCREEN(w->screen);
        GROUP_DISPLAY(w->screen->display);
        GROUP_WINDOW(w);

        if (gw->inGroup && !gd->ignoreMode && !gs->ignoreCallback) {
        	int i;
                for (i = 0; i < gw->group->nWins; i++) {
                	CompWindow *cw = gw->group->windows[i];
                        if (!cw)
                        	continue;

                        if (cw->id != w->id) {
				groupEnqueueGrabNotify (cw->screen, cw, x, y, state, mask);
			}
		}
	}

        UNWRAP(gs, w->screen, windowGrabNotify);
        (*w->screen->windowGrabNotify) (w, x, y, state, mask);
        WRAP(gs, w->screen, windowGrabNotify, groupWindowGrabNotify);
}

static void groupWindowUngrabNotify(CompWindow * w)
{
        GROUP_SCREEN(w->screen);
        GROUP_DISPLAY(w->screen->display);
        GROUP_WINDOW(w);

        if (gw->inGroup && !gd->ignoreMode && !gs->ignoreCallback) {
        	int i;
                for (i = 0; i < gw->group->nWins; i++) {
                	CompWindow *cw = gw->group->windows[i];
                        if (!cw)
                                continue;

                        if (cw->id != w->id) {
				groupEnqueueUngrabNotify (cw->screen, cw);
			}
		}
	}

        UNWRAP(gs, w->screen, windowUngrabNotify);
        (*w->screen->windowUngrabNotify) (w);
        WRAP(gs, w->screen, windowUngrabNotify, groupWindowUngrabNotify);
}


/*
 * groupHandleChanges
 *
 */
static void
groupHandleChanges(CompScreen* s)
{
	int i;
	GROUP_SCREEN(s);
	GROUP_DISPLAY(s->display);
	
	GroupSelection *group;
	CompWindow* w;

	gs->ignoreCallback = TRUE;

	for(group = gd->groups; group; group = group->next)
	{
		// Ungroup
		if (group->prev)
		{
			if (group->prev->ungroup && !group->prev->animationSteps)
				groupDeleteGroup(s->display, group->prev);
		}
		if (!group->next) {
			if (group->ungroup && !group->animationSteps) {
				groupDeleteGroup(s->display, group);
				break;
			}
		}

		if (group->topTab == group->prevTopTab)
			continue;
		
		if (group->animationSteps == 0)
		{
			if (group->prevTopTab == -1) // Tab
			{
				for(i = 0; i < group->nWins; i++)
				{					
					if (i == group->topTab)
						continue;
					w = group->windows[i];
				
					moveWindow(w, w->screen->width, 0, FALSE, FALSE);
					syncWindowPosition(w);
					groupUpdatePosBuffer(w, WIN_X(w), WIN_Y(w), WIN_WIDTH(w), WIN_HEIGHT(w));
					groupAddSkipMasks(w);
					
				}
				group->prevTopTab = group->topTab;

				group->dx = 0;
				group->dy = 0;
			}
			
			else if(group->topTab != -1) // Change Tab
			{
				CompWindow* topTab = TOP_TAB(group);
				CompWindow* prevTopTab = PREV_TOP_TAB(group);
				
				moveWindow(topTab, group->dx - topTab->screen->width, group->dy, FALSE, FALSE);
				syncWindowPosition(topTab);
				groupUpdatePosBuffer(topTab, WIN_X(topTab), WIN_Y(topTab),
											 WIN_WIDTH(topTab), WIN_HEIGHT(topTab));
				groupRemoveSkipMasks(topTab);

				activateWindow (topTab);			
	
				moveWindow(prevTopTab, prevTopTab->screen->width, 0, FALSE, FALSE);
				syncWindowPosition(prevTopTab);
				groupUpdatePosBuffer(prevTopTab, WIN_X(prevTopTab), WIN_Y(prevTopTab),
												 WIN_WIDTH(prevTopTab), WIN_HEIGHT(prevTopTab));
				groupAddSkipMasks(prevTopTab);
				
				group->prevTopTab = group->topTab;
	
				group->dx = 0;
				group->dy = 0;
			}
		}
		else
		{
			// Untab
			if (group->topTab == -1)
			{
				for(i = 0; i < group->nWins; i++) {
					w = group->windows[i];
			
					if(i == group->prevTopTab)
						continue;
				
					moveWindow(w, group->dx - w->screen->width, group->dy, FALSE, FALSE);
					syncWindowPosition(w);
					groupUpdatePosBuffer(w, WIN_X(w), WIN_Y(w), WIN_WIDTH(w), WIN_HEIGHT(w));
					groupRemoveSkipMasks(w);
				}
		
				group->prevTopTab = group->topTab;

				group->dx = 0;
				group->dy = 0;
			}
		}
	}

	gs->ignoreCallback = FALSE;
}

/*
 * groupDrawTabAnimation
 *
 */
static void groupDrawTabAnimation(CompScreen * s)
{
	GROUP_DISPLAY(s->display);

	int stepX, stepY;
	int i;
	
	GroupSelection  *group;
	for(group = gd->groups; group; group = group->next)
	{
		if(!group->animationSteps)
			continue;
		
		for(i = 0; i < group->nWins; i++)
		{
			CompWindow *cw = group->windows[i];
			
			if( i == group->topTab || i == group->prevTopTab)
				continue;
			
			if (!cw)
				continue;
			
			GROUP_WINDOW(cw);

			stepX = (gw->destinationX - WIN_X(cw)) / group->animationSteps;
			stepY = (gw->destinationY - WIN_Y(cw)) / group->animationSteps;

			moveWindow(cw, stepX, stepY, TRUE, FALSE);
		}
		
		group->animationSteps--;
	
		groupSyncWindows(group);
	}

}

/*
 * groupTabBarVertices
 *
 */
static void
groupTabBarVertices(CompScreen *s, int x, int y, int width, int height)
{
	GROUP_SCREEN(s);
	GROUP_DISPLAY(s->display);
	int i;
	int border_radius = gs->opt[GROUP_SCREEN_OPTION_BORDER_RADIUS].value.i;

	for (i = 0; i < NUM_CORNER_POINTS; i++)
		glVertex2f(border_radius * gd->cornerPointsX[i] + x +
			   width - border_radius,
			   -border_radius * gd->cornerPointsY[i] +
			   border_radius + y);

	for (i = NUM_CORNER_POINTS - 1; i >= 0; i--)
		glVertex2f(-border_radius * gd->cornerPointsX[i] + x +
			   border_radius,
			   -border_radius * gd->cornerPointsY[i] +
			   border_radius + y);

	for (i = 0; i < NUM_CORNER_POINTS; i++)
		glVertex2f(-border_radius * gd->cornerPointsX[i] + x +
			   border_radius,
			   border_radius * gd->cornerPointsY[i] + y +
			   height - border_radius);

	for (i = NUM_CORNER_POINTS - 1; i >= 0; i--)
		glVertex2f(border_radius * gd->cornerPointsX[i] + x +
			   width - border_radius,
			   border_radius * gd->cornerPointsY[i] + y +
			   height - border_radius);
}

/*
 * groupPaintThumb - taken from switcher and modified for tab bar
 *
 */
static void groupPaintThumb(GroupSelection *group, GroupTabBarSlot *slot)
{
	CompWindow *w = slot->window;
	CompWindow *topTab = TOP_TAB(group);
	int tw, th, tx, ty;
	tw = slot->width;
	th = slot->height;
	tx = slot->rx + group->tabBar->rx + WIN_X(topTab);
	ty = slot->ry + group->tabBar->ry + WIN_Y(topTab);

	WindowPaintAttrib sAttrib = w->lastPaint;

	if (w->mapNum) {

		if (WIN_WIDTH(w) > tw)
			sAttrib.xScale = (float) tw / WIN_WIDTH(w);
		else
			sAttrib.xScale = 1.0f;

		if (WIN_HEIGHT(w) > th)
			sAttrib.yScale = (float) tw / WIN_HEIGHT(w);
		else
			sAttrib.yScale = 1.0f;

		if (sAttrib.xScale < sAttrib.yScale)
			sAttrib.yScale = sAttrib.xScale;
		else
			sAttrib.xScale = sAttrib.yScale;

		sAttrib.xTranslate = tx - w->attrib.x;
		sAttrib.yTranslate = ty - w->attrib.y;

		(w->screen->drawWindow) (w, &sAttrib, getInfiniteRegion(),
					 PAINT_WINDOW_TRANSFORMED_MASK);
		addWindowDamage(w);
	}
}

/*
 * groupPaintTabBar
 *
 */
static void groupPaintTabBar(GroupSelection * group)
{
	if(group->topTab < 0 ||
	   !group->tabBar->visible)
		return;
	
	CompWindow *topTab = group->windows[group->topTab];
	CompScreen *s = topTab->screen;

	GROUP_SCREEN(s);

	if (VIEWPORT(topTab->attrib.x, s))
		return;

	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnable(GL_BLEND);

	int x1, x2, y1, y2, width, height;
	width = group->tabBar->width;
	height = group->tabBar->height;
	x1 = group->tabBar->rx + WIN_X(topTab);
	y1 = group->tabBar->ry + WIN_Y(topTab);
	x2 = x1 + width;
	y2 = y1 + height;

	glColor4usv(gs->opt[GROUP_SCREEN_OPTION_TAB_FILL_COLOR].value.c);
	glBegin(GL_POLYGON);
	groupTabBarVertices(s, x1, y1, width, height);
	glEnd();

	glColor4usv(gs->opt[GROUP_SCREEN_OPTION_TAB_BORDER_COLOR].value.c);
	glBegin(GL_LINE_LOOP);
	groupTabBarVertices(s, x1, y1, width, height);
	glEnd();

	glColor4usv(defaultColor);
	glDisable(GL_BLEND);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	// draw thumbs
	if(gs->opt[GROUP_SCREEN_OPTION_TAB_CREATE_MIPMAPS].value.b)
		s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
	
	GroupTabBarSlot *slot;
	for(slot = group->tabBar->slots; slot; slot = slot->next)
	{
		groupPaintThumb(group, slot);
	}


	addWindowDamage(topTab);
}

/*
 * groupPreparePaintScreen
 *
 */
static void groupPreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
	GROUP_SCREEN(s);

	UNWRAP(gs, s, preparePaintScreen);
	(*s->preparePaintScreen) (s, msSinceLastPaint);
	WRAP(gs, s, preparePaintScreen, groupPreparePaintScreen);

	groupDequeueMoveNotifies (s);
	groupDequeueResizeNotifies (s);
	groupDequeueGrabNotifies (s);
	groupDequeueUngrabNotifies (s);

	groupHandleChanges(s);	
	groupDrawTabAnimation(s);
}

/* 
 * groupPaintScreen
 *
 */
static Bool
groupPaintScreen(CompScreen * s,
		 const ScreenPaintAttrib * sAttrib,
		 Region region, int output, unsigned int mask)
{
	GROUP_SCREEN(s);
	GROUP_DISPLAY(s->display);

	Bool status;
	gs->wasTransformed = FALSE;

	// if we draw groups use BTF
	if (gd->groups) {
		mask &= ~PAINT_SCREEN_ORDER_FRONT_TO_BACK_MASK;
		mask |= PAINT_SCREEN_ORDER_BACK_TO_FRONT_MASK;
	}

	UNWRAP(gs, s, paintScreen);
	status = (*s->paintScreen) (s, sAttrib, region, output, mask);
	WRAP(gs, s, paintScreen, groupPaintScreen);

	if (status && gs->grab && !gs->wasTransformed) {
		int x1, x2, y1, y2;

		x1 = MIN(gs->x1, gs->x2);
		y1 = MIN(gs->y1, gs->y2);
		x2 = MAX(gs->x1, gs->x2);
		y2 = MAX(gs->y1, gs->y2);

		if (gs->grabIndex) {
			glPushMatrix();
			prepareXCoords(s, output, -DEFAULT_Z_CAMERA);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glEnable(GL_BLEND);

			glColor4usv(gs->opt[GROUP_SCREEN_OPTION_SELECTION_COLOR].value.c);
			glRecti(x1, y2, x2, y1);

			glColor4usv(gs->opt[GROUP_SCREEN_OPTION_LINE_COLOR].value.c);
			glBegin(GL_LINE_LOOP);
			glVertex2i(x1, y1);
			glVertex2i(x2, y1);
			glVertex2i(x2, y2);
			glVertex2i(x1, y2);
			glEnd();

			glColor4usv(defaultColor);
			glDisable(GL_BLEND);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glPopMatrix();
		}
	}
	
	return status;
}

/* 
 * groupPaintTransformedScreen
 *
 */
static void
groupPaintTransformedScreen(CompScreen * s, const ScreenPaintAttrib * sa,
			    Region region, int output, unsigned int mask)
{
	GROUP_SCREEN(s);

	UNWRAP(gs, s, paintTransformedScreen);
	(*s->paintTransformedScreen) (s, sa, region, output, mask);
	WRAP(gs, s, paintTransformedScreen, groupPaintTransformedScreen);

	if (gs->grab) {

		gs->wasTransformed = TRUE;

		int x1, x2, y1, y2;

		x1 = MIN(gs->x1, gs->x2);
		y1 = MIN(gs->y1, gs->y2);
		x2 = MAX(gs->x1, gs->x2);
		y2 = MAX(gs->y1, gs->y2);

		if (gs->grabIndex) {
			glPushMatrix();
			glLoadIdentity();
			(s->applyScreenTransform) (s, sa, output);
			prepareXCoords(s, output, -sa->zTranslate);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glEnable(GL_BLEND);

			glColor4usv(gs->opt[GROUP_SCREEN_OPTION_SELECTION_COLOR].value.c);
			glRecti(x1, y2, x2, y1);

			glColor4usv(gs->opt[GROUP_SCREEN_OPTION_LINE_COLOR].value.c);
			glBegin(GL_LINE_LOOP);
			glVertex2i(x1, y1);
			glVertex2i(x2, y1);
			glVertex2i(x2, y2);
			glVertex2i(x1, y2);
			glEnd();

			glColor4usv(defaultColor);
			glDisable(GL_BLEND);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glPopMatrix();
		}
	}
}

/*
 * groupDrawWindow
 *
 */
static Bool
groupDrawWindow(CompWindow * w,
		const WindowPaintAttrib * attrib,
		Region region, unsigned int mask)
{
    Bool status;
    GROUP_WINDOW(w);
    GROUP_SCREEN(w->screen);

    if (!(mask & PAINT_WINDOW_SOLID_MASK)) {
        if (gw->inGroup && gs->opt[GROUP_SCREEN_OPTION_GLOW].value.b) {
	    if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
		region = getInfiniteRegion();

	    if (region->numRects) {
		REGION box;
		CompMatrix matrix;

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

		w->vCount = 0;

		int glowBorderX = 12;
		int glowBorderY = 12;
		int glowWidth	= 128;
		int glowHeight	= 128;

		int overlapX, overlapY;
		overlapX = (WIN_WIDTH(w)  * glowBorderX) / (glowWidth  - glowBorderX * 2);
		overlapY = (WIN_HEIGHT(w) * glowBorderY) / (glowHeight - glowBorderY * 2);

		box.extents.x1 = WIN_X(w) - w->input.left - overlapX;
		box.extents.y1 = WIN_Y(w) - w->input.top - overlapY;
		box.extents.x2 = WIN_X(w) + w->input.right + WIN_WIDTH(w) + overlapX;
		box.extents.y2 = WIN_Y(w) + w->input.right + WIN_HEIGHT(w) + overlapY;

		matrix = gs->glowTexture.matrix;
		matrix.xx = 1.0f / (box.extents.x2 - box.extents.x1);
		matrix.yy = -1.0f / (box.extents.y2 - box.extents.y1);
		matrix.x0 -= (box.extents.x1 * matrix.xx);
		matrix.y0 -= (box.extents.y1 * matrix.yy);
		
		if (box.extents.x1 < box.extents.x2 &&
		    box.extents.y1 < box.extents.y2) {
		    (*w->screen->addWindowGeometry) (w, &matrix, 1, &box, region);
		}
	    }

	    if (w->vCount) {
		WindowPaintAttrib wAttrib;
		
		wAttrib.opacity = attrib->opacity;
		wAttrib.saturation = COLOR;
		wAttrib.brightness = BRIGHT;
		
		wAttrib.opacity = gw->group->color[3];

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
		glColor4us(gw->group->color[0], gw->group->color[1],
			gw->group->color[2],
			((gw->group->color[3] * wAttrib.opacity) / 0xFFFF));

		(*w->screen->drawWindowTexture) (w, &gs->glowTexture, &wAttrib,
		    mask | PAINT_WINDOW_TRANSLUCENT_MASK);

		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
		screenTexEnvMode (w->screen, GL_REPLACE);
		glColor4usv(defaultColor);
	    }
	}
    }

    UNWRAP(gs, w->screen, drawWindow);

    status = (*w->screen->drawWindow) (w, attrib, region, mask);

    if (gw->inGroup &&
        gw->group->tabbed && gw->group->topTab != -1 &&
        TOP_TAB(gw->group)->id == w->id) {
    	groupPaintTabBar(gw->group);
    }

    WRAP(gs, w->screen, drawWindow, groupDrawWindow);

    return status;
}

/*
 * groupPaintWindow
 *
 */
static Bool
groupPaintWindow(CompWindow * w,
		const WindowPaintAttrib * attrib,
		Region region, unsigned int mask)
{
	Bool status;
	GROUP_SCREEN(w->screen);
	GROUP_WINDOW(w);

	WindowPaintAttrib gAttrib = *attrib;

	if (gw->inSelection) {
		int opacity = gs->opt[GROUP_SCREEN_OPTION_OPACITY].value.i;
		int saturation = gs->opt[GROUP_SCREEN_OPTION_SATURATION].value.i;
		int brightness = gs->opt[GROUP_SCREEN_OPTION_BRIGHTNESS].value.i;

		opacity = OPAQUE * opacity / 100;
		saturation = COLOR * saturation / 100;
		brightness = BRIGHT * brightness / 100;

		gAttrib.opacity = opacity;
		gAttrib.saturation = saturation;
		gAttrib.brightness = brightness;
	}

	if (gw->inGroup &&
		gw->group->tabbed && gw->group->topTab != -1 &&
		!gw->group->animationSteps &&
		TOP_TAB(gw->group)->id != w->id) {

		return TRUE;
	}

	UNWRAP(gs, w->screen, paintWindow);
	status = (*w->screen->paintWindow) (w, &gAttrib, region, mask);
	WRAP(gs, w->screen, paintWindow, groupPaintWindow);

	return status;
}

/*
 * groupHandleMotionEvent
 *
 */
static void groupHandleMotionEvent(CompScreen * s, int xRoot, int yRoot)
{
	GROUP_SCREEN(s);

	if (gs->grab && gs->grabIndex) {
		REGION reg;

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

		reg.extents.x1 = MIN(gs->x1, gs->x2) - 1;
		reg.extents.y1 = MIN(gs->y1, gs->y2) - 1;
		reg.extents.x2 = MAX(gs->x1, gs->x2) + 1;
		reg.extents.y2 = MAX(gs->y1, gs->y2) + 1;

		damageScreenRegion(s, &reg);

		gs->x2 = xRoot;
		gs->y2 = yRoot;

		reg.extents.x1 = MIN(gs->x1, gs->x2) - 1;
		reg.extents.y1 = MIN(gs->y1, gs->y2) - 1;
		reg.extents.x2 = MAX(gs->x1, gs->x2) + 1;
		reg.extents.y2 = MAX(gs->y1, gs->y2) + 1;

		damageScreenRegion(s, &reg);
	}
}

/*
 * reloadCornerPoints
 *
 */
static void initCornerPoints(GroupDisplay * gd)
{
	int i;
	float angle;

	for (i = 0; i < NUM_CORNER_POINTS; i++) {
		angle = (PI / 2) * ((float) i / (NUM_CORNER_POINTS - 1));

		gd->cornerPointsY[i] = sin(angle);
		gd->cornerPointsX[i] = cos(angle);
	}
}

/*
 * groupDisplayInitOptions 
 *
 */
static void groupDisplayInitOptions(GroupDisplay * gd)
{
	CompOption *o;

	o = &gd->opt[GROUP_DISPLAY_OPTION_SELECT];
	o->name = "select";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Select");
	o->longDesc = N_("The key for starting selecting windows.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupSelect;
	o->value.action.terminate = groupSelectTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitButton;
	o->value.action.type = CompBindingTypeButton;
	o->value.action.button.modifiers = GROUP_SELECT_MODIFIERS_DEFAULT;
	o->value.action.button.button = GROUP_SELECT_BUTTON_DEFAULT;

	o = &gd->opt[GROUP_DISPLAY_OPTION_SELECT_SINGLE];
	o->name = "select_single";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Select single window");
	o->longDesc = N_("The key for selecting the current window.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupSelectSingle;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers =
	    GROUP_SELECT_SINGLE_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_SELECT_SINGLE_KEY_DEFAULT);

	o = &gd->opt[GROUP_DISPLAY_OPTION_GROUPING];
	o->name = "group";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Group");
	o->longDesc = N_("The key for grouing windows.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupGroupWindows;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = GROUP_GROUPING_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_GROUPING_KEY_DEFAULT);

	o = &gd->opt[GROUP_DISPLAY_OPTION_UNGROUPING];
	o->name = "ungroup";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Ungroup");
	o->longDesc = N_("The key for ungrouing windows.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupUnGroupWindows;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = GROUP_UNGROUPING_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_UNGROUPING_KEY_DEFAULT);

	o = &gd->opt[GROUP_DISPLAY_OPTION_TABMODE];
	o->name = "tabmode";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Tab");
	o->longDesc = N_("The key for entering the tab mode.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupInitTab;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = GROUP_TABMODE_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_TABMODE_KEY_DEFAULT);

	o = &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT];
	o->name = "change_tab_left";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Change Tab");
	o->longDesc = N_("The key for changing the tab to the left.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupChangeTabLeft;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = GROUP_CHANGE_TAB_LEFT_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_CHANGE_TAB_LEFT_KEY_DEFAULT);
	
	o = &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT];
	o->name = "change_tab_right";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Change Tab");
	o->longDesc = N_("The key for changing the tab to the right.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupChangeTabRight;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = GROUP_CHANGE_TAB_RIGHT_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_CHANGE_TAB_RIGHT_KEY_DEFAULT);

	o = &gd->opt[GROUP_DISPLAY_OPTION_REMOVEING];
	o->name = "remove";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Remove Window");
	o->longDesc = N_("The key for removing the selected window.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupRemoveWindow;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = GROUP_REMOVEING_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_REMOVEING_KEY_DEFAULT);

	o = &gd->opt[GROUP_DISPLAY_OPTION_CLOSEING];
	o->name = "close";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Close Windows");
	o->longDesc = N_("The key for closing all windows in the group.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupCloseWindows;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = GROUP_CLOSEING_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_CLOSEING_KEY_DEFAULT);

	o = &gd->opt[GROUP_DISPLAY_OPTION_MINIMIZEING];
	o->name = "minimize";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Minimize Windows");
	o->longDesc =
	    N_("The key for minimizeing all windows in the group.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupMinimizeWindows;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers =
	    GROUP_MINIMIZEING_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_MINIMIZEING_KEY_DEFAULT);

	o = &gd->opt[GROUP_DISPLAY_OPTION_IGNORE];
	o->name = "ignore";
	o->group = N_("");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Ignore Group");
	o->longDesc = N_("The key for ignoring the group."
			 "If this key is pressed you can resize/move a single"
			 "window in the group.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = groupSetIgnore;
	o->value.action.terminate = groupUnsetIgnore;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = GROUP_IGNORE_MODIFIERS_DEFAULT;
	o->value.action.key.keysym =
	    XStringToKeysym(GROUP_IGNORE_KEY_DEFAULT);
}

/*
 * groupGetDisplayOptions
 *
 */
static CompOption *groupGetDisplayOptions(CompDisplay * d, int *count)
{
	if (d) {
		GROUP_DISPLAY(d);

		*count = NUM_OPTIONS(gd);
		return gd->opt;
	} else {
		GroupDisplay *gd = malloc(sizeof(GroupDisplay));
		groupDisplayInitOptions(gd);
		*count = NUM_OPTIONS(gd);
		return gd->opt;
	}
}

/*
 * groupSetDisplayOption
 *
 */
static Bool
groupSetDisplayOption(CompDisplay * d, char *name, CompOptionValue * value)
{
	CompOption *o;
	int index;

	GROUP_DISPLAY(d);

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

	switch (index) {

	case GROUP_DISPLAY_OPTION_SELECT:
	case GROUP_DISPLAY_OPTION_SELECT_SINGLE:
	case GROUP_DISPLAY_OPTION_GROUPING:
	case GROUP_DISPLAY_OPTION_UNGROUPING:
	case GROUP_DISPLAY_OPTION_REMOVEING:
	case GROUP_DISPLAY_OPTION_CLOSEING:
	case GROUP_DISPLAY_OPTION_MINIMIZEING:
	case GROUP_DISPLAY_OPTION_IGNORE:
	case GROUP_DISPLAY_OPTION_TABMODE:
	case GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT:
	case GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT:
		if (setDisplayAction(d, o, value))
			return TRUE;
		break;

	default:
		break;
	}

	return FALSE;
}

/*
 * groupInitDisplay
 *
 */
static Bool groupInitDisplay(CompPlugin * p, CompDisplay * d)
{

	GroupDisplay *gd;

	gd = malloc(sizeof(GroupDisplay));
	if (!gd)
		return FALSE;

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

	gd->inAction = FALSE;
	gd->lastActiveGroup = NULL;
	//gd->animatedGroup = -1;

	gd->tmpSel.windows = NULL;
	gd->tmpSel.nWins = 0;

	gd->groups = NULL;
	//gd->revGroups = NULL;

	gd->ignoreMode = FALSE;

	groupDisplayInitOptions(gd);

	initCornerPoints(gd);

	WRAP(gd, d, handleEvent, groupHandleEvent);

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

	return TRUE;
}

/*
 * groupFiniDisplay
 *
 */
static void groupFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	GROUP_DISPLAY(d);

	if (gd->groups) {
		GroupSelection *group;
		GroupSelection *following;
		for(group = gd->groups; group; group = following)
		{
		    if (group) {
		        following = group->next;
		        free(group);
		    } else {
		        following = 0;
		    }
		}
	}
	freeScreenPrivateIndex(d, gd->screenPrivateIndex);

	UNWRAP(gd, d, handleEvent);

	free(gd);
}

/*
 * groupInitScreen
 *
 */
static Bool groupInitScreen(CompPlugin * p, CompScreen * s)
{

	GroupScreen *gs;

	GROUP_DISPLAY(s->display);

	gs = malloc(sizeof(GroupScreen));
	if (!gs)
		return FALSE;

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

	groupScreenInitOptions(gs);

	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_SELECT].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_SELECT_SINGLE].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_GROUPING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_UNGROUPING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_REMOVEING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CLOSEING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_MINIMIZEING].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_TABMODE].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT].value.action);
	addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT].value.action);

	WRAP(gs, s, windowMoveNotify, groupWindowMoveNotify);
	WRAP(gs, s, windowResizeNotify, groupWindowResizeNotify);
	WRAP(gs, s, preparePaintScreen, groupPreparePaintScreen);
	WRAP(gs, s, paintScreen, groupPaintScreen);
	WRAP(gs, s, drawWindow, groupDrawWindow);
	WRAP(gs, s, paintWindow, groupPaintWindow);
	WRAP(gs, s, paintTransformedScreen, groupPaintTransformedScreen);
	WRAP(gs, s, windowGrabNotify, groupWindowGrabNotify);
	WRAP(gs, s, windowUngrabNotify, groupWindowUngrabNotify);

	s->privates[gd->screenPrivateIndex].ptr = gs;

	gs->grab = FALSE;
	gs->grabIndex = 0;
	gs->ignoreCallback = FALSE;

	gs->pendingMoves = NULL;
	gs->pendingResizes = NULL;
	gs->pendingGrabs = NULL;
	gs->pendingUngrabs = NULL;

	// bind texture for glow
 	initTexture (s, &gs->glowTexture);
 	imageToTexture (s, &gs->glowTexture, (char*) glowTex, 128, 128);

	return TRUE;
}

/*
 * groupFiniScreen
 *
 */
static void groupFiniScreen(CompPlugin * p, CompScreen * s)
{
	GROUP_SCREEN(s);
	GROUP_DISPLAY(s->display);
	freeWindowPrivateIndex(s, gs->windowPrivateIndex);

	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_SELECT].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_SELECT_SINGLE].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_GROUPING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_UNGROUPING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_REMOVEING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CLOSEING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_MINIMIZEING].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_TABMODE].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT].value.action);
	removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT].value.action);

	UNWRAP(gs, s, windowMoveNotify);
	UNWRAP(gs, s, windowResizeNotify);
	UNWRAP(gs, s, preparePaintScreen);
	UNWRAP(gs, s, paintScreen);
	UNWRAP(gs, s, drawWindow);
	UNWRAP(gs, s, paintWindow);
	UNWRAP(gs, s, paintTransformedScreen);
	UNWRAP(gs, s, windowGrabNotify);
	UNWRAP(gs, s, windowUngrabNotify);

	free(gs);
}

/*
 * groupInitWindow
 *
 */
static Bool groupInitWindow(CompPlugin * p, CompWindow * w)
{
	GroupWindow *gw;

	GROUP_SCREEN(w->screen);

	gw = malloc(sizeof(GroupWindow));
	if (!gw)
		return FALSE;
	gw->inGroup = FALSE;
	gw->inSelection = FALSE;

	// for move/resize
	gw->oldWidth = WIN_WIDTH(w);
	gw->oldHeight = WIN_HEIGHT(w);
	gw->oldX = WIN_X(w);
	gw->oldY = WIN_Y(w);

	// for tab
	gw->slot = NULL;
	gw->orgPosX = 0;
	gw->orgPosY = 0;
	gw->destinationX = 0;
	gw->destinationY = 0;

	//gw->offscreen = FALSE;

	w->privates[gs->windowPrivateIndex].ptr = gw;

	return TRUE;
}

/*
 * groupFiniWindow
 *
 */
static void groupFiniWindow(CompPlugin * p, CompWindow * w)
{
	GROUP_WINDOW(w);

	free(gw);
}

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

	return TRUE;
}

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

/*
 * groupDeps array
 *
 */

CompPluginDep groupDeps[] = {

	{CompPluginRuleBefore, "cube"}

};


/*
 * groupVTable
 *
 */
CompPluginVTable groupVTable = {
	"group",
	N_("Window Grouper"),
	N_("With this plugin you can group windows."),
	groupInit,
	groupFini,
	groupInitDisplay,
	groupFiniDisplay,
	groupInitScreen,
	groupFiniScreen,
	groupInitWindow,
	groupFiniWindow,
	groupGetDisplayOptions,
	groupSetDisplayOption,
	groupGetScreenOptions,
	groupSetScreenOption,
	groupDeps,
	sizeof(groupDeps) / sizeof(groupDeps[0]),
	0,
	0,
	BERYL_ABI_INFO,
	"beryl-plugins"
};

/*
 * getCompPluginInfo
 *
 */
CompPluginVTable *getCompPluginInfo(void)
{
	return &groupVTable;
}