All pastes #269805 Raw Edit

Stuff

public text v1 · immutable
·published 2006-12-06 20:50 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 <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_MODIFIERS_DEFAULT      (CompSuperMask)

#define GROUP_CHANGE_TAB_KEY_DEFAULT            "Left"



#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     9

#define GROUP_DISPLAY_OPTION_NUM            10



#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_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_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_MOVE                4

#define GROUP_SCREEN_OPTION_RESIZE              5

#define GROUP_SCREEN_OPTION_RAISE               6

#define GROUP_SCREEN_OPTION_AUTO_UNGROUP        7

#define GROUP_SCREEN_OPTION_AUTO_GROUP          8

#define GROUP_SCREEN_OPTION_RELATIVE_DISTANCE   9

#define GROUP_SCREEN_OPTION_SELECTION_COLOR     10

#define GROUP_SCREEN_OPTION_LINE_COLOR          11

#define GROUP_SCREEN_OPTION_TOLERANCE           12

#define GROUP_SCREEN_OPTION_STEPS               13

#define GROUP_SCREEN_OPTION_NUM                 14



/*

 * 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]))



/*

 * default windows for selection

 */

static char *groupDefaultTypes[] = {

	N_("Normal"),

	N_("Dialog"),

	N_("ModalDialog"),

	N_("Dnd")

};



/*

 * pointer to display list

 */

static int displayPrivateIndex;



/*

 * Call-Back for state

 */

typedef void (*AddWindowToGroupProc) (CompDisplay * d, CompWindow * w,

				      int groupID);



/*

 * GroupSelection

 */

typedef struct _GroupSelection {

	CompWindow **windows;

	int nWins;

	int id;



	CompWindow *topTab;

	Bool tabbed;



	GLushort color[4];

} GroupSelection;



/*

 * GroupDisplay structure

 */

typedef struct _GroupDisplay {

	int screenPrivateIndex;

	CompOption opt[GROUP_DISPLAY_OPTION_NUM];

	HandleEventProc handleEvent;



	GroupSelection tmpSel;



	GroupSelection *groups;

	int nGroups;



	Bool inAction;

	int 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;

	PaintTransformedScreenProc paintTransformedScreen;



	int wMask;



	// for selection

	Bool grab;

	Bool wasTransformed;

	int grabIndex;

	int x1;

	int y1;

	int x2;

	int y2;



	GLuint glowTexture;

} GroupScreen;



/*

 * GroupWindow structure

 */

typedef struct _GroupWindow {

	int groupID;

	Bool inGroup;

	Bool inSelection;



	// for scale notify...

	int oldWidth;

	int oldHeight;

	int oldX;

	int oldY;



	// for tab animation

	Bool animate;

	int steps;

	int orgPosX;

	int orgPosY;

	int destinationX;

	int destinationY;



	Bool offscreen;



} GroupWindow;



/*

 * groupFindGroupAtDisplay

 * Note: Returns the index of the group in gd->groups!

 *

 */

static int groupFindGroupAtDisplay(GroupDisplay * gd, int id)

{

	int i;

	for (i = 0; i < gd->nGroups; i++) {

		if (gd->groups[i].id == id)

			return i;

	}



	return -1;

}



#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;

}



/*

 * 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);



	// 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 &&

	    !gw->animate &&

	    !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) {

		int index = groupFindGroupAtDisplay(gd, gw->groupID);

		if (index != -1) {

			int i;

			for (i = 0; i < gd->groups[index].nWins; i++) {

				CompWindow *cw = gd->groups[index].windows[i];

				if (!cw)

					continue;



				if (cw->id != w->id) {

					UNWRAP(gs, cw->screen, windowMoveNotify);

					moveWindow(cw, dx, dy, TRUE, FALSE);

					WRAP(gs, cw->screen, windowMoveNotify, groupWindowMoveNotify);



					groupUpdatePosBuffer(cw, WIN_X(cw) + dx, WIN_Y(cw) + dy, WIN_WIDTH(cw), WIN_HEIGHT(cw));

				}

			}

		}



		gd->lastActiveGroup = gw->groupID;

		gd->inAction = TRUE;

	}



	groupUpdatePosBuffer(w, WIN_X(w) + dx, WIN_Y(w) + dy, WIN_WIDTH(w), WIN_HEIGHT(w));

}



/*

 * groupTabGroup

 *

 */

static void groupTabGroup(CompDisplay * d, CompWindow * main, int groupID)

{

	GROUP_DISPLAY(d);

	GROUP_SCREEN(main->screen);



	int index = groupFindGroupAtDisplay(gd, groupID);

	if (index == -1)

		return;



	gd->groups[index].tabbed = TRUE;

	gd->animatedGroup = groupID;



	// main window

	GROUP_WINDOW(main);

	gd->groups[index].topTab = main;

	gw->animate = FALSE;



	int i;

	int steps = gs->opt[GROUP_SCREEN_OPTION_STEPS].value.i;

	for (i = 0; i < gd->groups[index].nWins; i++) {

		CompWindow *cw = gd->groups[index].windows[i];

		if (cw->id == main->id)

			continue;

		GROUP_WINDOW(cw);



		gw->steps = steps;

		gw->orgPosX = WIN_X(cw);

		gw->orgPosY = WIN_Y(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->animate = TRUE;

	}



	damageScreen(main->screen);

}



/*

 * groupUntabGroup

 *

 */

static void

groupUntabGroup(CompDisplay * d, CompWindow * main, int groupID)

{

	GROUP_DISPLAY(d);

	GROUP_SCREEN(main->screen);



	int index = groupFindGroupAtDisplay(gd, groupID);

	if (index == -1)

		return;



	gd->groups[index].tabbed = FALSE;

	gd->animatedGroup = groupID;



	// main window

	GROUP_WINDOW(main);

	gd->groups[index].topTab = main;

	gw->animate = FALSE;



	int i;

	int steps = gs->opt[GROUP_SCREEN_OPTION_STEPS].value.i;

	for (i = 0; i < gd->groups[index].nWins; i++) {

		CompWindow *cw = gd->groups[index].windows[i];

		if (cw->id == main->id)

			continue;

		GROUP_WINDOW(cw);



		gw->steps = steps;

		gw->destinationX = gw->orgPosX;

		gw->destinationY = gw->orgPosY;

		gw->orgPosX = WIN_X(cw);

		gw->orgPosY = WIN_Y(cw);

		/*cw->hidden = FALSE;

		   showWindow (cw);



		   unsigned int state = getWindowState (d, cw->id);

		   state &= ~CompWindowStateSkipTaskbarMask;

		   state &= ~CompWindowStateSkipPagerMask;

		   setWindowState (d, state, cw->id); */

		
		UNWRAP(gs, cw->screen, windowMoveNotify);



		insertWindowIntoScreen(cw->screen, cw, main->id);
		
		if (gw->offscreen) {

			moveWindow(cw, -1 * cw->screen->width, 0, FALSE, FALSE);
			syncWindowPosition(cw);



			gw->offscreen = FALSE;

		}
		
		WRAP(gs, cw->screen, windowMoveNotify, groupWindowMoveNotify);



		gw->animate = TRUE;

	}



	damageScreen(main->screen);

}



/*

 * groupDeleteGroup - pre-definition for groupDeleteGroupWindow

 *

 */

static void groupDeleteGroup(CompDisplay * d, int id);



/*

 * groupDeleteGroupWindow

 *

 */

static void groupDeleteGroupWindow(CompDisplay * d, CompWindow * w)

{

	GROUP_DISPLAY(d);

	GROUP_WINDOW(w);

	GROUP_SCREEN(w->screen);



	int index = groupFindGroupAtDisplay(gd, gw->groupID);

	if (index == -1)

		return;



	if (gd->groups[index].tabbed &&

	    gd->groups[index].topTab->id == w->id) {

		groupUntabGroup(d, w, gw->groupID);

		gd->groups[index].topTab = NULL;

	}



	if (gd->groups[index].nWins != 0

	    && gd->groups[index].windows != NULL) {

		CompWindow **buf = gd->groups[index].windows;



		gd->groups[index].windows = (CompWindow **) calloc(gd->groups[index].nWins - 1, sizeof(CompWindow *));



		int counter = 0;

		int i;

		for (i = 0; i < gd->groups[index].nWins; i++) {

			if (buf[i]->id == w->id)

				continue;

			gd->groups[index].windows[counter++] = buf[i];

		}

		gd->groups[index].nWins = counter;



		if (gd->groups[index].nWins == 1 &&

		    gs->opt[GROUP_SCREEN_OPTION_AUTO_UNGROUP].value.b) {

			groupDeleteGroup(d, gw->groupID);

		} else if (gd->groups[index].nWins <= 0) {

			free(gd->groups[index].windows);

			gd->groups[index].windows = NULL;

			groupDeleteGroup(d, gw->groupID);

		}



		free(buf);



		gw->inGroup = FALSE;

	}

}



/*

 * groupDeleteGroup

 *

 */

static void groupDeleteGroup(CompDisplay * d, int id)

{

	GROUP_DISPLAY(d);

	gd->inAction = TRUE;

	gd->lastActiveGroup = -1;



	int index = groupFindGroupAtDisplay(gd, id);

	if (index == -1) {

		gd->inAction = FALSE;

		return;

	}



	if (gd->groups[index].windows != NULL) {

		if (gd->groups[index].tabbed) {

			groupUntabGroup(d, gd->groups[index].topTab, id);

			// we don't want an animation here because it won't work...

			gd->groups[index].topTab = NULL;

			gd->animatedGroup = -1;

		}



		int i;

		for (i = 0; i < gd->groups[index].nWins; i++) {

			CompWindow *cw = gd->groups[index].windows[i];

			GROUP_WINDOW(cw);



			gw->inGroup = FALSE;

		}

		free(gd->groups[index].windows);

	}



	GroupSelection *groupsBuf = gd->groups;

	gd->groups = calloc(gd->nGroups - 1, sizeof(GroupSelection));

	int counter = 0;

	int i;

	for (i = 0; i < gd->nGroups; i++) {

		if (groupsBuf[i].id == id) {

			continue;

		}



		gd->groups[counter++] = groupsBuf[i];

	}

	gd->nGroups--;

	free(groupsBuf);



	gd->inAction = FALSE;

}



/*

 * groupAddWindowToGroup

 *

 */

static void

groupAddWindowToGroup(CompDisplay * d, CompWindow * w, int groupID)

{

	GROUP_DISPLAY(d);

	GROUP_WINDOW(w);



	if (gw->inGroup) {

		groupDeleteGroupWindow(d, w);

	}



	int index = groupFindGroupAtDisplay(gd, groupID);

	if (index == -1) {

		GroupSelection g;



		g.id = groupID;

		g.windows = (CompWindow **) calloc(1, sizeof(CompWindow *));

		g.windows[0] = w;

		g.nWins = 1;

		g.topTab = NULL;

		g.tabbed = FALSE;



		// glow color

		srand(time(NULL));

		g.color[0] = rand() % 0xFFFF;

		g.color[1] = rand() % 0xFFFF;

		g.color[2] = rand() % 0xFFFF;

		g.color[3] = 0xFFFF;



		gd->groups = (GroupSelection *) realloc(gd->groups, sizeof(GroupSelection) * (gd->nGroups + 1));

		gd->groups[gd->nGroups] = g;

		gd->nGroups++;



		gw->groupID = groupID;

		gw->inGroup = TRUE;

	} else {

		gd->groups[index].windows = (CompWindow **) realloc(gd->groups[index].windows,

															sizeof(CompWindow *) * (gd->groups[index].nWins + 1));

		gd->groups[index].windows[gd->groups[index].nWins] = w;

		gd->groups[index].nWins++;



		gw->groupID = groupID;

		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(CompDisplay * d, int groupID)

{

	GROUP_DISPLAY(d);



	int index = groupFindGroupAtDisplay(gd, groupID);

	if (index == -1)

		return;



	int i;

	for (i = 0; i < gd->groups[index].nWins; i++) {

		CompWindow *w = gd->groups[index].windows[i];

		syncWindowPosition(w);

	}

}



/*

 * groupRaiseWindows

 *

 */

static void

groupRaiseWindows(CompDisplay * d, CompWindow * top, int groupID)

{

	GROUP_DISPLAY(d);



	int index = groupFindGroupAtDisplay(gd, groupID);

	if (index == -1)

		return;



	int i;

	for (i = 0; i < gd->groups[index].nWins; i++) {

		CompWindow *w = gd->groups[index].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_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;

}



/*

 * 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:

		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:

		if (compSetIntOption(o, value)) {

			return TRUE;

		}

		break;



	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:

		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 index = groupFindGroupAtDisplay(gd, gw->groupID);

		if (index == -1)

			return;



		int i;

		for (i = 0; i < gd->groups[index].nWins; i++) {

			CompWindow *cw = gd->groups[index].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) {

		int index = groupFindGroupAtDisplay(gd, gw->groupID);

		if (index == -1)

			return;



		CompWindow **buf = gd->tmpSel.windows;

		gd->tmpSel.windows = (CompWindow **) calloc(gd->tmpSel.nWins - gd->groups[index].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->groupID == gd->groups[index].id) {

				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 id = gd->nGroups;

		int index = groupFindGroupAtDisplay(gd, id);

		while (index != -1) {

			id++;

			index = groupFindGroupAtDisplay(gd, id);

		}



		int i;

		for (i = 0; i < gd->tmpSel.nWins; i++) {

			CompWindow *cw = gd->tmpSel.windows[i];

			GROUP_WINDOW(cw);



			groupAddWindowToGroup(d, cw, id);



			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);

	if (gw->inGroup)

		groupDeleteGroup(d, gw->groupID);



	return FALSE;

}



/*

 * groupInitTab

 *

 */

static Bool

groupInitTab(CompDisplay * d, CompAction * action, CompActionState state,

	     CompOption * option, int nOption)

{

	GROUP_DISPLAY(d);

	CompWindow *w = findWindowAtDisplay(d, d->activeWindow);

	if (!w)

		return FALSE;



	GROUP_WINDOW(w);

	if (!gw->inGroup)

		return FALSE;



	int index = groupFindGroupAtDisplay(gd, gw->groupID);

	if (index == -1)

		return FALSE;



	if (gd->groups[index].tabbed) {

		groupUntabGroup(d, w, gw->groupID);

	} else {

		groupTabGroup(d, w, gw->groupID);

	}



	return TRUE;

}



/*

 * groupChangeTab

 *

 */

static Bool

groupChangeTab(CompDisplay * d, CompAction * action, CompActionState state,

	       CompOption * option, int nOption)

{

	GROUP_DISPLAY(d);

	CompWindow *w = findWindowAtDisplay(d, d->activeWindow);

	if (!w)

		return FALSE;

	GROUP_SCREEN(w->screen);



	GroupWindow *mgw = GET_GROUP_WINDOW(w, gs);

	if (!mgw->inGroup)

		return FALSE;



	int index = groupFindGroupAtDisplay(gd, mgw->groupID);

	if (index == -1)

		return FALSE;



	if (gd->groups[index].tabbed) {

		int i;

		for (i = 0; i < gd->groups[index].nWins; i++) {

			CompWindow *cw = gd->groups[index].windows[i];

			if (cw->id == w->id)

				continue;

			GROUP_WINDOW(cw);



			if (gw->inGroup && gw->groupID == mgw->groupID) {

				/*cw->hidden = FALSE;

				   showWindow (cw);

				   raiseWindow (cw);

				   lowerWindow (w);

				   w->hidden = TRUE;

				   hideWindow (w);

				   moveInputFocusToWindow (cw); */



				//lowerWindow(w);



				UNWRAP(gs, cw->screen, windowMoveNotify);

				
				insertWindowIntoScreen(cw->screen, cw,

						       w->id);
				
				if (gw->offscreen) {

					moveWindow(cw, -1 * cw->screen->width, 0, FALSE, FALSE);

					syncWindowPosition(cw);
					

					mgw->offscreen = FALSE;

				}
				
				if (!mgw->offscreen) {

					moveWindow(w, w->screen->width, 0, FALSE, FALSE);
					syncWindowPosition(w);
					

					mgw->offscreen = TRUE;

				}

				
				unhookWindowFromScreen(w->screen, w);



				//raiseWindow (cw);

				moveInputFocusToWindow (cw);



				//groupSyncWindows (d, gw->groupID);

				gd->groups[index].topTab = cw;
				
				WRAP(gs, cw->screen, windowMoveNotify, groupWindowMoveNotify);

				
				break;

			}

		}

	}



	return TRUE;

}



/*

 * 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);

	GROUP_DISPLAY(w->screen->display);



	if (gw->inGroup) {

		int index = groupFindGroupAtDisplay(gd, gw->groupID);

		if (index == -1)

			return FALSE;



		int nWins = gd->groups[index].nWins;

		int i;

		for (i = 0; i < nWins; i++) {

			CompWindow *cw = gd->groups[index].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);

	GROUP_DISPLAY(w->screen->display);



	if (gw->inGroup) {

		int index = groupFindGroupAtDisplay(gd, gw->groupID);

		if (index == -1)

			return FALSE;



		int nWins = gd->groups[index].nWins;

		int i;

		for (i = 0; i < nWins; i++) {

			CompWindow *cw = gd->groups[index].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 != -1) {

				groupSyncWindows(d, gd->lastActiveGroup);

				gd->lastActiveGroup = -1;

				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 &&

				    !IPCS_GetBoolND(IPCS_OBJECT(w->screen->display), "SCALE_INIT", FALSE) &&

				    gs->opt[GROUP_SCREEN_OPTION_RAISE].value.b) {

					groupRaiseWindows(d, w,

							  gw->groupID);

				}

			}

		}

		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->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 index = groupFindGroupAtDisplay(gd, gw->groupID);

		if (index != -1) {

			int i;

			for (i = 0; i < gd->groups[index].nWins; i++) {

				CompWindow *cw =

				    gd->groups[index].windows[i];

				if (!cw)

					continue;



				if (cw->id != w->id) {



					UNWRAP(gs, cw->screen,

					       windowResizeNotify);

					UNWRAP(gs, cw->screen,

					       windowMoveNotify);



					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);



					resizeWindow(cw, nx, ny, nwidth, nheight, WIN_BORDER(cw));

					groupUpdatePosBuffer(cw, nx, ny, nwidth, nheight);



					WRAP(gs, cw->screen, windowMoveNotify, groupWindowMoveNotify);

					WRAP(gs, cw->screen, windowResizeNotify, groupWindowResizeNotify);

				}



			}

			gd->lastActiveGroup = gw->groupID;

			gd->inAction = TRUE;

		}

	}



	groupUpdatePosBuffer(w, WIN_X(w), WIN_Y(w), WIN_WIDTH(w),

			     WIN_HEIGHT(w));

}



/*

 * groupDrawTabAnimation

 *

 */

static void groupDrawTabAnimation(CompScreen * s)

{

	GROUP_SCREEN(s);

	GROUP_DISPLAY(s->display);



	int index = groupFindGroupAtDisplay(gd, gd->animatedGroup);

	if (index == -1)

		return;



	int stepX, stepY;

	int i;

	for (i = 0; i < gd->groups[index].nWins; i++) {

		CompWindow *cw = gd->groups[index].windows[i];

		if (!cw)

			continue;

		GROUP_WINDOW(cw);



		if (!gw->steps && gw->animate) {

			gw->animate = FALSE;

			gd->animatedGroup = -1;



			if (gd->groups[index].tabbed &&

			    gd->groups[index].topTab != NULL &&

			    gd->groups[index].topTab->id != cw->id) {

				/*cw->hidden = TRUE;

				   hideWindow (cw);



				   unsigned int state = getWindowState (s->display, cw->id);

				   state |= CompWindowStateSkipTaskbarMask;

				   state |= CompWindowStateSkipPagerMask;

				   setWindowState (s->display, state, cw->id); */



				//lowerWindow(cw);
				UNWRAP(gs, cw->screen, windowMoveNotify);
				

				if (!gw->offscreen) {

					moveWindow(cw, cw->screen->width, 0, FALSE, FALSE);



					gw->offscreen = TRUE;

				}
				

				syncWindowPosition (cw);

				unhookWindowFromScreen(s, cw);
				
				WRAP(gs, cw->screen, windowMoveNotify, groupWindowMoveNotify);

			}

		}



		if (gw->animate) {

			stepX = (gw->destinationX - WIN_X(cw)) / gw->steps;

			stepY = (gw->destinationY - WIN_Y(cw)) / gw->steps;



			UNWRAP(gs, cw->screen, windowMoveNotify);

			moveWindow(cw, stepX, stepY, TRUE, FALSE);

			WRAP(gs, cw->screen, windowMoveNotify, groupWindowMoveNotify);



			gw->steps--;

		}

		// last window and animation has finished

		if (i == gd->groups[index].nWins - 1 &&

		    gd->animatedGroup == -1) {

			groupSyncWindows(s->display, gw->groupID);

			if (gd->groups[index].topTab)

				raiseWindow(gd->groups[index].topTab);

		}

	}

}



#define THUMB_SIZE 128

#define SPACING 20

#define BORDER_COLOR 0.0f, 0.0f, 0.0f, 1.0f

#define BAR_COLOR 0.8f, 0.8f, 0.8f, 0.3f

#define BORDER_WIDTH 5

#define BORDER_RADIUS 10



#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 )



/*

 * groupTabBarVertices

 *

 */

static void

groupTabBarVertices(GroupDisplay * gd, int x, int y, int width, int height)

{

	int 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(CompWindow * w, int tx, int ty, int tw, int th)

{

	DrawWindowGeometryProc oldDrawWindowGeometry;

	WindowPaintAttrib sAttrib = w->lastPaint;



	/* Wrap drawWindowGeometry to make sure the general

	   drawWindowGeometry function is used */

	oldDrawWindowGeometry = w->screen->drawWindowGeometry;

	w->screen->drawWindowGeometry = getBaseDrawWindowGeometry();



	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);

	}



	w->screen->drawWindowGeometry = oldDrawWindowGeometry;

}



/*

 * groupPaintTabBar

 *

 */

static void groupPaintTabBar(GroupSelection * group)

{

	CompScreen *s = group->topTab->screen;

	CompDisplay *d = s->display;



	GROUP_SCREEN(s);

	GROUP_DISPLAY(d);



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

		return;



	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glEnable(GL_BLEND);



	int x1, x2, y1, y2, width, height;

	width  = BORDER_WIDTH * (group->nWins + 1) + THUMB_SIZE * group->nWins;

	height = BORDER_WIDTH * 2 + THUMB_SIZE;

	x1 = WIN_X(group->topTab) + WIN_WIDTH(group->topTab) / 2 - width / 2;

	y1 = WIN_Y(group->topTab) + 10;

	x2 = x1 + width;

	y2 = y1 + height;



	//glColor4f(BAR_COLOR);

	glColor4usv(gs->opt[GROUP_SCREEN_OPTION_SELECTION_COLOR].value.c);

	glBegin(GL_POLYGON);

	groupTabBarVertices(gd, x1, y1, width, height);

	glEnd();



	//glColor4f(BORDER_COLOR);

	glColor4usv(gs->opt[GROUP_SCREEN_OPTION_LINE_COLOR].value.c);

	glBegin(GL_LINE_LOOP);

	groupTabBarVertices(gd, x1, y1, width, height);

	glEnd();



	glColor4usv(defaultColor);

	glDisable(GL_BLEND);

	glEnableClientState(GL_TEXTURE_COORD_ARRAY);



	// draw thumbs

	int i;

	for (i = 0; i < group->nWins; i++) {

		CompWindow *cw = group->windows[i];

		if (!cw)

			continue;



		// calculate coords

		int tx1, ty1;

		tx1 = x1 + BORDER_WIDTH;

		ty1 = y1;

		tx1 += (THUMB_SIZE + BORDER_WIDTH) * i;

		ty1 += BORDER_WIDTH;



		groupPaintThumb(cw, tx1, ty1, THUMB_SIZE, THUMB_SIZE);

	}



	addWindowDamage(group->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);



	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;



	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();

		}

	}

	// for the animation

	if (gd->animatedGroup != -1)

		damageScreen(s);



	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);

	GROUP_DISPLAY(w->screen->display);



	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;

	}



	int index = -1;

	if (gw->inGroup)

		index = groupFindGroupAtDisplay(gd, gw->groupID);

	

	// paint glow

	if (gw->inGroup && index != -1) {

		glPushMatrix();

		glDisableClientState(GL_TEXTURE_COORD_ARRAY);



		glEnable(GL_BLEND);

		if (gs->glowTexture) {

			glBindTexture(GL_TEXTURE_2D, gs->glowTexture);

			glEnable(GL_TEXTURE_2D);

		}

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);



		glBlendFunc(GL_SRC_ALPHA, GL_ONE);

		glColor4usv(gd->groups[index].color);



		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);



		int x1, x2, y1, y2;

		x1 = WIN_X(w) - overlapX;

		y1 = WIN_Y(w) - overlapY;

		x2 = WIN_X(w) + WIN_WIDTH(w) + overlapX;

		y2 = WIN_Y(w) + WIN_HEIGHT(w) + overlapY;



		glBegin(GL_QUADS);

		glTexCoord2d(0.0, 1.0);

		glVertex2i(x1, y1);

		glTexCoord2d(0.0, 0.0);

		glVertex2i(x1, y2);

		glTexCoord2d(1.0, 0.0);

		glVertex2i(x2, y2);

		glTexCoord2d(1.0, 1.0);

		glVertex2i(x2, y1);

		glEnd();



		glDisable(GL_STENCIL_TEST);

		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

		glColor4usv(defaultColor);

		glDisable(GL_TEXTURE_2D);

		glDisable(GL_BLEND);

		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		glPopMatrix();



		Region reg = XCreateRegion();

		XRectangle rect;

		rect.x = x1;

		rect.y = y1;

		rect.width = x2 - x1;

		rect.height = y2 - y1;

		XUnionRectWithRegion(&rect, reg, reg);

		XSubtractRegion(reg, w->region, reg);



		damageScreenRegion(w->screen, reg);

	}



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



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



	if (index != -1 &&

	    gd->groups[index].tabbed &&

	    gd->groups[index].topTab->id == w->id)

		groupPaintTabBar(&gd->groups[index]);



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



	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];

	o->name = "change_tab";

	o->group = N_("");

	o->subGroup = N_("");

	o->displayHints = "";

	o->shortDesc = N_("Change Tab");

	o->longDesc = N_("The key for changing the tab.");

	o->type = CompOptionTypeAction;

	o->value.action.initiate = groupChangeTab;

	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_MODIFIERS_DEFAULT;

	o->value.action.key.keysym =

	    XStringToKeysym(GROUP_CHANGE_TAB_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:

		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 = -1;

	gd->animatedGroup = -1;



	gd->tmpSel.windows = NULL;

	gd->tmpSel.nWins = 0;



	gd->groups = NULL;

	gd->nGroups = 0;



	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 && gd->groups != NULL && gd->nGroups != 0) {

		free(gd->groups);

	}

	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].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, paintTransformedScreen, groupPaintTransformedScreen);



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



	gs->grab = FALSE;

	gs->grabIndex = 0;



	// bind texture for glow

	glGenTextures(1, &gs->glowTexture);

	glBindTexture(GL_TEXTURE_2D, gs->glowTexture);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0,

		     GL_RGBA, GL_UNSIGNED_BYTE, glowTex);

	glBindTexture(GL_TEXTURE_2D, 0);



	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].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, paintTransformedScreen);



	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->animate = FALSE;

	gw->steps = 0;

	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;

}