[beryl-commits] r1488 - in branches: . beryl-group-with-tab-plugin

marex at server.beryl-project.org marex at server.beryl-project.org
Sat Dec 2 00:22:55 CET 2006


Author: marex
Date: 2006-12-02 00:22:54 +0100 (Sat, 02 Dec 2006)
New Revision: 1488

Added:
   branches/beryl-group-with-tab-plugin/
   branches/beryl-group-with-tab-plugin/Makefile
   branches/beryl-group-with-tab-plugin/README
   branches/beryl-group-with-tab-plugin/group.c
Log:
group-tab-branch: init working branch (the animation works already)


Added: branches/beryl-group-with-tab-plugin/Makefile
===================================================================
--- branches/beryl-group-with-tab-plugin/Makefile	                        (rev 0)
+++ branches/beryl-group-with-tab-plugin/Makefile	2006-12-01 23:22:54 UTC (rev 1488)
@@ -0,0 +1,26 @@
+DESTDIR = /usr/lib/beryl
+
+PLUGIN  = group
+
+CC      = gcc
+LIBTOOL = libtool
+INSTALL = install
+
+CFLAGS  = -g -Wall `pkg-config --cflags beryl`
+LDFLAGS = `pkg-config --libs beryl`
+
+%.lo: %.c
+	$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c -o $@ $<
+
+%.la: $(PLUGIN).lo
+	$(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -rpath $(DESTDIR) -o $@ $<
+
+all: lib$(PLUGIN).la
+
+install: lib$(PLUGIN).la
+	$(LIBTOOL) --mode=install $(INSTALL) lib$(PLUGIN).la \
+	$(DESTDIR)/lib$(PLUGIN).la
+
+clean:
+	rm -rf *.lo *.o lib$(PLUGIN).* .libs
+


Property changes on: branches/beryl-group-with-tab-plugin/Makefile
___________________________________________________________________
Name: svn:executable
   + *

Added: branches/beryl-group-with-tab-plugin/README
===================================================================
--- branches/beryl-group-with-tab-plugin/README	                        (rev 0)
+++ branches/beryl-group-with-tab-plugin/README	2006-12-01 23:22:54 UTC (rev 1488)
@@ -0,0 +1,6 @@
+=============== !!! =================
+!!! DO NOT ACTUALLY USE THIS CODE !!!
+IT'S A WORKING BRANCH WITH REALLY
+UNSTABLE AND UNFINISHED CODE.
+IT'S NEEDED FOR CODE SYNC ONLY.
+=============== ... =================

Added: branches/beryl-group-with-tab-plugin/group.c
===================================================================
--- branches/beryl-group-with-tab-plugin/group.c	                        (rev 0)
+++ branches/beryl-group-with-tab-plugin/group.c	2006-12-01 23:22:54 UTC (rev 1488)
@@ -0,0 +1,2316 @@
+/**
+ *
+ * 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>
+
+/*
+ * 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_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_NUM            9
+
+#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 {
+    Window*         windows;
+    int             nWins;
+    int             id;
+
+	Bool			tabbed;
+} 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;
+
+    Bool                    ignoreMode;
+
+} GroupDisplay;
+
+/*
+ * GroupScreen structure
+ */
+typedef struct _GroupScreen {
+    int                         windowPrivateIndex;
+    CompOption                  opt[GROUP_SCREEN_OPTION_NUM];
+
+    WindowMoveNotifyProc        windowMoveNotify;
+    WindowResizeNotifyProc      windowResizeNotify;
+    PaintScreenProc             paintScreen;
+    PaintWindowProc             paintWindow;
+    PaintTransformedScreenProc  paintTransformedScreen; 
+
+    int                         wMask;
+
+    // for selection
+    Bool                        grab;
+    Bool                        wasTransformed;
+    int                         grabIndex;
+    int                         x1;
+    int                         y1;
+    int                         x2;
+    int                         y2;
+
+} 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;
+	Bool	tabWindow;
+	float	stepX;
+	float	stepY;
+	int		steps;
+	int		orgPosX;
+	int		orgPosY;
+	int		destinationX;
+	int		destinationY;
+
+} 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 Window*
+groupFindWindowsInRegion (CompScreen *s, REGION reg, int *c)
+{
+    GROUP_SCREEN(s);
+
+    float tolerance = gs->opt[GROUP_SCREEN_OPTION_TOLERANCE].value.i / 10.0f;
+
+    Window *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 (Window));
+                ret[0] = w->id;
+            } else {
+                ret = realloc (ret, sizeof (Window) * (count+1));
+                ret[count] = w->id;
+            }
+            count++;
+        }
+    }
+    (*c) = count;     
+    return ret;
+}
+
+/*
+ * 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->lastActiveGroup = groupID;
+
+	// main window
+	GROUP_WINDOW (main);
+	gw->tabWindow = TRUE;
+	gw->animate = FALSE;
+	gw->stepX = 0.0f;
+	gw->stepY = 0.0f;
+	gw->orgPosX = 0;
+	gw->orgPosY = 0;
+	gw->destinationX = 0;
+	gw->destinationY = 0;
+
+	int i;
+	int steps = gs->opt[GROUP_SCREEN_OPTION_STEPS].value.i;
+	for (i = 0; i < gd->groups[index].nWins; i++)
+	{
+		CompWindow *cw = findWindowAtDisplay (d, gd->groups[index].windows[i]);
+		if (!cw || cw->id == main->id)
+			continue;
+		GROUP_WINDOW (cw);
+
+		gw->stepX = (float) (WIN_X (main) - WIN_X (cw)) / (float) steps;
+		gw->stepY = (float) (WIN_Y (main) - WIN_Y (cw)) / (float) steps;
+		gw->steps = steps;
+		gw->orgPosX = WIN_X (cw);
+		gw->orgPosY = WIN_Y (cw);
+		gw->destinationX = WIN_X (main);
+		gw->destinationY = WIN_Y (main);
+		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->lastActiveGroup = groupID;
+
+	// main window
+	GROUP_WINDOW (main);
+	gw->tabWindow = TRUE;
+	gw->animate = FALSE;
+	gw->stepX = 0.0f;
+	gw->stepY = 0.0f;
+	gw->orgPosX = 0;
+	gw->orgPosY = 0;
+	gw->destinationX = 0;
+	gw->destinationY = 0;
+
+	int i;
+	int steps = gs->opt[GROUP_SCREEN_OPTION_STEPS].value.i;
+	for (i = 0; i < gd->groups[index].nWins; i++)
+	{
+		CompWindow *cw = findWindowAtDisplay (d, gd->groups[index].windows[i]);
+		if (!cw || cw->id == main->id)
+			continue;
+		GROUP_WINDOW (cw);
+
+		gw->stepX = (float) (gw->orgPosX - WIN_X (main)) / (float) steps;
+		gw->stepY = (float) (gw->orgPosY - WIN_Y (main)) / (float) steps;
+		gw->steps = steps;
+		gw->destinationX = gw->orgPosX;
+		gw->destinationY = gw->orgPosY;
+		gw->orgPosX = WIN_X (cw);
+		gw->orgPosY = WIN_Y (cw);
+		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].nWins != 0 && gd->groups[index].windows != NULL)
+    {
+        Window *buf = gd->groups[index].windows;
+        
+        gd->groups[index].windows = (Window*) calloc (gd->groups[index].nWins-1, sizeof (Window));
+
+        int counter = 0;
+        int i;
+        for (i = 0; i < gd->groups[index].nWins; i++)
+        {
+            if (buf[i] == 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)
+    {
+        int i;
+        for (i = 0; i < gd->groups[index].nWins; i++)
+        {
+            CompWindow *cw = findWindowAtDisplay (d, gd->groups[index].windows[i]);
+            if (!cw)
+                continue;
+
+            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 = (Window*) calloc (1, sizeof (Window));
+        g.windows[0] = w->id;
+        g.nWins = 1;
+		g.tabbed = FALSE;
+        
+        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 = (Window*) realloc (gd->groups[index].windows, sizeof (Window)* (gd->groups[index].nWins+1));
+        gd->groups[index].windows[gd->groups[index].nWins] = w->id;
+        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)
+    {
+        Window *buf = gd->tmpSel.windows;
+        gd->tmpSel.windows = (Window*) calloc (gd->tmpSel.nWins-1, sizeof (Window));
+
+        int counter = 0;
+        int i;
+        for (i = 0; i < gd->tmpSel.nWins; i++)
+        {
+            if (buf[i] == 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 = (Window*) realloc (gd->tmpSel.windows,
+                                                sizeof (Window)* (gd->tmpSel.nWins+1));
+
+    gd->tmpSel.windows[gd->tmpSel.nWins] = w->id;
+    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 = findWindowAtDisplay (d, gd->groups[index].windows[i]);
+        if (!w)
+            continue;
+        syncWindowPosition (w);
+    }
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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 = findWindowAtDisplay (d, gd->groups[index].windows[i]);
+        if (!w || 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 = findWindowAtDisplay (d, gd->groups[index].windows[i]);
+             if (!cw)
+                 continue;
+             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;
+
+         Window *buf = gd->tmpSel.windows;
+         gd->tmpSel.windows = (Window*) calloc (gd->tmpSel.nWins - gd->groups[index].nWins, sizeof (Window));
+
+         int counter = 0;
+         int i;
+         for (i = 0; i < gd->tmpSel.nWins; i++)
+         {
+              CompWindow *cw = findWindowAtDisplay (d, buf[i]);
+              if (!cw)
+                  continue;
+              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);
+    }
+
+}
+
+/*
+ * groupSelect
+ *
+ */
+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;
+            Window *ws = groupFindWindowsInRegion (s, reg, &count);
+
+            if (ws) {
+            // select windows
+            int i;
+            for (i = 0; i < count; i++)
+            {
+                CompWindow *cw = findWindowAtDisplay (d, ws[i]);
+                if (!cw)
+                    continue;
+
+                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 = (CompWindow*) findWindowAtDisplay (d, gd->tmpSel.windows[i]);
+            if (!cw)
+                continue;
+            GROUP_WINDOW (cw);
+
+            groupAddWindowToGroup(d, cw, id);
+
+            gw->inSelection = FALSE;
+            addWindowDamage (cw);
+        }
+
+        // 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;
+}
+
+/*
+ * 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 = findWindowAtDisplay (d, gd->groups[index].windows[i]);
+            if (!cw)
+                continue;
+            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 = findWindowAtDisplay (d, gd->groups[index].windows[i]);
+            if (!cw)
+                continue;
+            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);
+                }
+                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;
+    }
+}
+
+/*
+ * 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 &&
+       !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 = findWindowAtDisplay (w->screen->display, 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));
+}
+
+/*
+ * 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 = findWindowAtDisplay (w->screen->display, 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));
+}
+
+/* 
+ * 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;
+
+    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 ();
+        }
+    }
+	else if (status)
+	{
+		int index = groupFindGroupAtDisplay (gd, gd->lastActiveGroup);
+		if (index == -1)
+			return status;
+	
+		int i;
+		for (i = 0; i < gd->groups[index].nWins; i++)
+		{
+			CompWindow *cw = findWindowAtDisplay (s->display, gd->groups[index].windows[i]);
+			if(!cw)
+				continue;
+			GROUP_WINDOW (cw);
+
+			if (!gw->animate)
+				continue;
+
+			if (!gw->steps) {
+				// move them the remaining distance, it's needed because we are off a few pixels
+				UNWRAP (gs, cw->screen, windowMoveNotify);
+				moveWindow (cw,
+							gw->destinationX - WIN_X (cw), gw->destinationY - WIN_Y (cw),
+							TRUE, FALSE);
+				WRAP (gs, cw->screen, windowMoveNotify, groupWindowMoveNotify);
+				gw->animate = FALSE;				
+				continue;
+			}
+
+			// do a step for every window
+			UNWRAP (gs, cw->screen, windowMoveNotify);
+			moveWindow (cw, gw->stepX, gw->stepY, TRUE, FALSE);
+			WRAP (gs, cw->screen, windowMoveNotify, groupWindowMoveNotify);
+			
+			gw->steps--;
+		}
+	}
+
+    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
+groupPaintWindow (CompWindow * w,
+                  const WindowPaintAttrib * attrib,
+                  Region region, unsigned int mask)
+{
+    Bool status;
+    GROUP_WINDOW  (w);
+    GROUP_SCREEN  (w->screen);
+
+    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;
+    }   
+
+    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);
+    }
+}
+
+/*
+ * 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_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:
+            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->tmpSel.windows = NULL;
+    gd->tmpSel.nWins = 0;
+
+    gd->groups = NULL;
+    gd->nGroups = 0;
+
+    gd->ignoreMode = FALSE;
+
+    groupDisplayInitOptions (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);
+
+    WRAP (gs, s, windowMoveNotify,   groupWindowMoveNotify);
+    WRAP (gs, s, windowResizeNotify, groupWindowResizeNotify);
+    WRAP (gs, s, paintScreen, groupPaintScreen);
+    WRAP (gs, s, paintWindow, groupPaintWindow);
+    WRAP (gs, s, paintTransformedScreen, groupPaintTransformedScreen);
+
+    s->privates[gd->screenPrivateIndex].ptr = gs;
+
+    gs->grab = FALSE;
+    gs->grabIndex = 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);
+
+    UNWRAP (gs, s, windowMoveNotify);
+    UNWRAP (gs, s, windowResizeNotify);
+    UNWRAP (gs, s, paintScreen);
+    UNWRAP (gs, s, paintWindow);
+    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->tabWindow = FALSE;
+	gw->animate = FALSE;
+	gw->stepX = 0.0f;
+	gw->stepY = 0.0f;
+	gw->steps = 0;
+	gw->orgPosX = 0;
+	gw->orgPosY = 0;
+	gw->destinationX = 0;
+	gw->destinationY = 0;
+
+    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, "state" }
+    
+};
+*/
+
+/*
+ * groupVTable
+ *
+ */
+CompPluginVTable groupVTable = {
+    "group",
+    "Window Grouper",
+    "With this plugin you can group windows.",
+    groupInit,
+    groupFini,
+    groupInitDisplay,
+    groupFiniDisplay,
+    groupInitScreen,
+    groupFiniScreen,
+    groupInitWindow,
+    groupFiniWindow,
+    groupGetDisplayOptions,
+    groupSetDisplayOption,
+    groupGetScreenOptions,
+    groupSetScreenOption,
+    0 /* groupDeps */,
+    0 /* sizeof (groupDeps) / sizeof (groupDeps[0]) */,
+    0,
+    0,
+    BERYL_ABI_INFO,
+    "beryl-plugins"
+};
+
+/*
+ * getCompPluginInfo
+ *
+ */
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+    return &groupVTable;
+}
+




More information about the commits mailing list