summaryrefslogtreecommitdiff
path: root/beryl-plugins/src/group
diff options
context:
space:
mode:
authormarex <marex@d7aaf104-2d23-0410-ae22-9d23157bf5a3>2007-02-11 15:51:05 +0000
committermarex <marex@d7aaf104-2d23-0410-ae22-9d23157bf5a3>2007-02-11 15:51:05 +0000
commitd286c698b37c4c356d0914681122bbe845cdd578 (patch)
tree4fe5da7414b48d9fe46fca8a317d374515d5d53f /beryl-plugins/src/group
parentf2a4453ed909a0d34e288e5874a69b9c6e2290d1 (diff)
downloadmarex-dev-d286c698b37c4c356d0914681122bbe845cdd578.tar.gz
marex-dev-d286c698b37c4c356d0914681122bbe845cdd578.tar.bz2
beryl-plugins: Move group to beryl-plugins...
git-svn-id: file:///beryl/trunk@3959 d7aaf104-2d23-0410-ae22-9d23157bf5a3
Diffstat (limited to 'beryl-plugins/src/group')
-rw-r--r--beryl-plugins/src/group/Makefile.am20
-rw-r--r--beryl-plugins/src/group/debug.c205
-rw-r--r--beryl-plugins/src/group/group.c1456
-rw-r--r--beryl-plugins/src/group/group.h650
-rw-r--r--beryl-plugins/src/group/group_glow.h400
-rw-r--r--beryl-plugins/src/group/init.c390
-rw-r--r--beryl-plugins/src/group/option.c1280
-rw-r--r--beryl-plugins/src/group/paint.c1020
-rw-r--r--beryl-plugins/src/group/queues.c189
-rw-r--r--beryl-plugins/src/group/selection.c361
-rw-r--r--beryl-plugins/src/group/tab.c2367
11 files changed, 8338 insertions, 0 deletions
diff --git a/beryl-plugins/src/group/Makefile.am b/beryl-plugins/src/group/Makefile.am
new file mode 100644
index 0000000..4bb97d9
--- /dev/null
+++ b/beryl-plugins/src/group/Makefile.am
@@ -0,0 +1,20 @@
+PFLAGS=-module -avoid-version -no-undefined
+libgroup_la_LDFLAGS = $(PFLAGS)
+libgroup_la_LIBADD = @BERYL_LIBS@ @BERYLSETTINGS_LIBS@ -lcairo
+libgroup_la_SOURCES = group.c init.c option.c paint.c queues.c selection.c tab.c group.h group_glow.h
+
+INCLUDES = \
+ @BERYL_CFLAGS@ \
+ @LIBRSVG_CFLAGS@ \
+ `pkg-config --cflags cairo` \
+ @GLIB_CFLAGS@ \
+ -DDATADIR='"$(datadir)"' \
+ -DLIBDIR='"$(libdir)"' \
+ -DALL_LINGUAS="\"@ALL_LINGUAS@\"" \
+ -DLOCALEDIR="\"@datadir@/locale\"" \
+ -DIMAGEDIR='"$(imagedir)"' \
+ -I$(top_srcdir)/include
+
+moduledir = $(plugindir)
+
+module_LTLIBRARIES=libgroup.la
diff --git a/beryl-plugins/src/group/debug.c b/beryl-plugins/src/group/debug.c
new file mode 100644
index 0000000..c068aa0
--- /dev/null
+++ b/beryl-plugins/src/group/debug.c
@@ -0,0 +1,205 @@
+#include "group.h"
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * debug.c
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ **/
+
+#define DEBUG_LEVEL DEBUG_ALL
+
+/*
+ * groupDebugPrintMsg
+ *
+ */
+void groupDebugPrintMsg(char* msg, DebugLevel level)
+{
+ if (level >= DEBUG_LEVEL) {
+ printf("DEBUG: %s\n", msg);
+ }
+}
+
+/*
+ * groupDebugPrintGroup
+ *
+ */
+void groupDebugPrintGroup(GroupSelection *group)
+{
+ if (DEBUG_LEVEL == DEBUG_OFF)
+ return;
+
+ printf("DEBUG: Debugging group (%p)...\n", group);
+
+ if (DEBUG_LEVEL >= DEBUG_IMPORTANT ) {
+ printf("topTab => %p\n", group->topTab);
+ printf("prevTopTab => %p\n", group->prevTopTab);
+ printf("nextTopTab => %p\n", group->nextTopTab);
+ printf("tabBar => %p\n", group->tabBar);
+ printf("changeAnimationTime => %i\n", group->changeAnimationTime);
+ printf("changeAnimationDirection => %i\n", group->changeAnimationDirection);
+ printf("changeState => %i\n", group->changeState);
+ printf("tabbingState => %i\n", group->tabbingState);
+ printf("changeTab => %s\n", (group->changeTab ? "TRUE" : "FALSE"));
+ printf("doTabbing => %s\n", (group->doTabbing ? "TRUE" : "FALSE"));
+ printf("ungroupState => %d\n", group->ungroupState);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_INFO) {
+ printf("oldTopTabCenterX => %i\n", group->oldTopTabCenterX);
+ printf("oldTopTabCenterY => %i\n", group->oldTopTabCenterY);
+ printf("inputPrevention.id => %i\n", (int)group->inputPrevention);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_ALL) {
+ printf("prev => %p\n", group->prev);
+ printf("next => %p\n", group->next);
+ printf("nWins => %i\n", group->nWins);
+ printf("windows => %p\n", group->windows);
+ printf("Color: Red => %i Green => %i Blue => %i Alpha => %i\n",
+ group->color[0], group->color[1], group->color[2], group->color[3]);
+ }
+}
+
+/*
+ * groupDebugPrintScreen
+ *
+ */
+void groupDebugPrintScreen(GroupScreen *gs)
+{
+ if (DEBUG_LEVEL == DEBUG_OFF)
+ return;
+
+ printf("DEBUG: Debugging screen (%p)...\n", gs);
+
+ if (DEBUG_LEVEL >= DEBUG_IMPORTANT ) {
+ printf("queued => %s\n", (gs->queued ? "TRUE" : "FALSE"));
+ printf("draggedSlot => %p\n", gs->draggedSlot);
+ //printf("draggedSlotTabBar => %p\n", gs->draggedSlotTabBar);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_INFO) {
+ printf("prevX => %i\n", gs->prevX);
+ printf("prevY => %i\n", gs->prevY);
+ printf("grabIndex => %i\n", gs->grabIndex);
+ printf("grabState => %i\n", gs->grabState);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_ALL) {
+ printf("glowType => %i\n", gs->glowType);
+ printf("glowTexture => 0x%i\n", gs->glowTexture.name);
+ printf("x1 => %i y1 => %i\n", gs->x1, gs->y1);
+ printf("x2 => %i y2 => %i\n", gs->x2, gs->y2);
+ }
+}
+
+/*
+ * groupDebugPrintWindow
+ *
+ */
+void groupDebugPrintWindow(GroupWindow *gw)
+{
+ if (DEBUG_LEVEL == DEBUG_OFF)
+ return;
+
+ printf("DEBUG: Debugging window (%p)...\n", gw);
+
+ if (DEBUG_LEVEL >= DEBUG_IMPORTANT ) {
+ printf("group => %p\n", gw->group);
+ printf("slot => %p\n", gw->slot);
+ printf("animateState => %i\n", gw->animateState);
+ printf("mainTabOffset => (%i, %i)\n", gw->mainTabOffset.x, gw->mainTabOffset.y);
+ printf("destination => (%i, %i)\n", gw->destination.x, gw->destination.y);
+ printf("orgPos => (%i, %i)\n", gw->orgPos.x, gw->orgPos.y);
+ printf("tx => %f ty => %f\n", gw->ty, gw->ty);
+ printf("xVelocity => %f yVelocity => %f\n", gw->xVelocity, gw->yVelocity);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_INFO) {
+ printf("needsPosSync => %s\n", (gw->needsPosSync ? "TRUE" : "FALSE"));
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_ALL) {
+ printf("inSelection => %s\n", (gw->inSelection ? "TRUE" : "FALSE"));
+ }
+}
+
+/*
+ * groupDebugPrintTabBar
+ *
+ */
+void groupDebugPrintTabBar(GroupTabBar *bar)
+{
+ if (DEBUG_LEVEL == DEBUG_OFF)
+ return;
+
+ printf("DEBUG: Debugging tab bar (%p)...\n", bar);
+
+ if (DEBUG_LEVEL >= DEBUG_IMPORTANT ) {
+ printf("hoveredSlot => %p\n", bar->hoveredSlot);
+ printf("textSlot => %p\n", bar->textSlot);
+ //printf("mouseOver => %s\n", (bar->mouseOver ? "TRUE" : "FALSE"));
+ printf("state => %i\n", bar->state);
+ printf("timeoutHandle => %i\n", bar->timeoutHandle);
+ printf("animationTime => %i\n", bar->animationTime);
+ printf("region => (%i, %i) - (%i, %i)\n",
+ bar->region->extents.x1, bar->region->extents.y1,
+ bar->region->extents.x2, bar->region->extents.y2);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_INFO) {
+ printf("slots => %p\n", bar->slots);
+ printf("revSlots => %p\n", bar->revSlots);
+ printf("nSlots => %i\n", bar->nSlots);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_ALL) {
+ printf("textLayer => %p\n", bar->textLayer);
+ }
+}
+
+/*
+ * groupDebugPrintTabBarSlot
+ *
+ */
+void groupDebugPrintTabBarSlot(GroupTabBarSlot *slot)
+{
+ if (DEBUG_LEVEL == DEBUG_OFF)
+ return;
+
+ printf("DEBUG: Debugging slot (%p)...\n", slot);
+
+ if (DEBUG_LEVEL >= DEBUG_IMPORTANT ) {
+ printf("prev => %p\n", slot->prev);
+ printf("next => %p\n", slot->next);
+ printf("region => (%i, %i) - (%i, %i)\n",
+ slot->region->extents.x1, slot->region->extents.y1,
+ slot->region->extents.x2, slot->region->extents.y2);
+ printf("window => %p\n", slot->window);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_INFO) {
+ printf("name => %s\n", slot->name);
+ }
+
+ if (DEBUG_LEVEL >= DEBUG_ALL) {
+ // empty
+ }
+}
diff --git a/beryl-plugins/src/group/group.c b/beryl-plugins/src/group/group.c
new file mode 100644
index 0000000..4242c07
--- /dev/null
+++ b/beryl-plugins/src/group/group.c
@@ -0,0 +1,1456 @@
+#include "group.h"
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * group.c
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * 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.
+ *
+ **/
+
+/* helper for activating a window after a certain time a slot has dragged over it */
+
+static Bool
+groupDragHoverTimeout(void* closure)
+{
+ CompWindow *w = (CompWindow *) closure;
+
+ if (!w)
+ return FALSE;
+
+ GROUP_SCREEN(w->screen);
+
+ activateWindow(w);
+ gs->dragHoverTimeoutHandle = 0;
+
+ return FALSE;
+}
+
+/*
+ * groupFindWindowIndex
+ *
+ */
+int
+groupFindWindowIndex(CompWindow *w, GroupSelection *g)
+{
+ int i;
+
+ for(i = 0; i < g->nWins; i++)
+ {
+ if(g->windows[i]->id == w->id)
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * groupGrabScreen
+ *
+ */
+void groupGrabScreen(CompScreen * s, GroupScreenGrabState newState)
+{
+ GROUP_SCREEN(s);
+
+ if ((gs->grabState != newState) && gs->grabIndex) {
+ removeScreenGrab(s, gs->grabIndex, NULL);
+ gs->grabIndex = 0;
+ }
+
+ if (newState == ScreenGrabSelect) {
+ gs->grabIndex = pushScreenGrab(s, None, "group");
+ } else if (newState == ScreenGrabTabDrag) {
+ gs->grabIndex = pushScreenGrab(s, None, "group-drag");
+ }
+
+ gs->grabState = newState;
+}
+
+/*
+ * groupSyncWindows
+ *
+ */
+void groupSyncWindows(GroupSelection *group)
+{
+ int i;
+ for (i = 0; i < group->nWins; i++) {
+ CompWindow *w = group->windows[i];
+ syncWindowPosition(w);
+ }
+}
+
+/*
+ * groupRaiseWindows
+ *
+ */
+void
+groupRaiseWindows(CompWindow * top, GroupSelection *group)
+{
+ int i;
+ for (i = 0; i < group->nWins; i++) {
+ CompWindow *w = group->windows[i];
+ if (w->id == top->id)
+ continue;
+ restackWindowBelow(w, top);
+ }
+}
+
+/*
+ * groupMinimizeWindows
+ *
+ */
+static void
+groupMinimizeWindows(CompWindow *top, GroupSelection *group, Bool minimize)
+{
+ int i;
+ for (i = 0; i < group->nWins; i++) {
+ CompWindow *w = group->windows[i];
+ if (w->id == top->id)
+ continue;
+
+ if (minimize)
+ minimizeWindow(w);
+ else
+ unminimizeWindow(w);
+ }
+}
+
+/*
+ * groupShadeWindows
+ *
+ */
+static void
+groupShadeWindows(CompWindow *top, GroupSelection *group, Bool shade)
+{
+ int i;
+ for (i = 0; i < group->nWins; i++) {
+ CompWindow *w = group->windows[i];
+ if (w->id == top->id)
+ continue;
+
+ if (shade)
+ w->state |= CompWindowStateShadedMask;
+ else
+ w->state &= ~CompWindowStateShadedMask;
+
+ updateWindowAttributes(w, FALSE);
+ }
+}
+
+/*
+ * groupDeleteGroupWindow
+ *
+ * Note: allowRegroup is need for a special case when groupAddWindowToGroup
+ * calls this function.
+ *
+ */
+void groupDeleteGroupWindow(CompWindow * w, Bool allowRegroup)
+{
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+ GroupSelection *group = gw->group;
+
+ if (!group)
+ return;
+
+ if (group->tabBar) {
+ if (gw->slot)
+ {
+ if (gs->draggedSlot && gs->dragged && gs->draggedSlot->window->id == w->id)
+ groupUnhookTabBarSlot(group->tabBar, gw->slot, FALSE);
+ else
+ groupDeleteTabBarSlot(group->tabBar, gw->slot);
+ }
+
+ if(!gw->ungroup && group->nWins > 1)
+ {
+ if(HAS_TOP_WIN(group))
+ {
+ // TODO: maybe add the IS_ANIMATED to the topTab for better constraining...
+
+ CompWindow *topTab = TOP_TAB(group);
+ GroupWindow *gtw = GET_GROUP_WINDOW(topTab, gs);
+ int oldX = gw->orgPos.x;
+ int oldY = gw->orgPos.y;
+
+ gw->orgPos.x = group->oldTopTabCenterX - WIN_WIDTH(w) / 2;
+ gw->orgPos.y = group->oldTopTabCenterY - WIN_HEIGHT(w) / 2;
+
+ gw->destination.x = group->oldTopTabCenterX - WIN_WIDTH(w)/2 + gw->mainTabOffset.x -
+ gtw->mainTabOffset.x;
+ gw->destination.y = group->oldTopTabCenterY - WIN_HEIGHT(w)/2 + gw->mainTabOffset.y -
+ gtw->mainTabOffset.y;
+
+ gw->mainTabOffset.x = oldX;
+ gw->mainTabOffset.y = oldY;
+
+ gw->animateState |= IS_ANIMATED;
+
+ gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f;
+ }
+
+ // Although when there is no top-tab, it will never really animate anything,
+ // if we don't start the animation, the window will never get remvoed.
+ groupStartTabbingAnimation(group, FALSE);
+
+ group->ungroupState = UngroupSingle;
+ gw->ungroup = TRUE;
+
+ return;
+ }
+ }
+
+ if (group->nWins && group->windows) {
+ CompWindow **buf = group->windows;
+
+ group->windows = (CompWindow **) calloc(group->nWins - 1, sizeof(CompWindow *));
+
+ int counter = 0;
+ int i;
+ for (i = 0; i < group->nWins; i++) {
+ if (buf[i]->id == w->id)
+ continue;
+ group->windows[counter++] = buf[i];
+ }
+ group->nWins = counter;
+
+ if (group->nWins == 1)
+ {
+ damageWindowOutputExtents(group->windows[0]); // Glow was removed from this window too.
+ updateWindowOutputExtents(group->windows[0]);
+ }
+
+ if (group->nWins == 1 &&
+ gs->opt[GROUP_SCREEN_OPTION_AUTO_UNGROUP].value.b) {
+ if (group->changeTab) {
+ /* a change animation is pending: this most
+ likely means that a window must be moved
+ back onscreen, so we do that here */
+ CompWindow *lw = group->windows[0];
+
+ gs->queued = TRUE;
+ moveWindowOnscreen(lw);
+ moveWindow(lw, group->oldTopTabCenterX - WIN_X(lw) - WIN_WIDTH(lw) / 2,
+ group->oldTopTabCenterY - WIN_Y(lw) - WIN_HEIGHT(lw) / 2, TRUE, TRUE);
+ syncWindowPosition(lw);
+ gs->queued = FALSE;
+ }
+ groupDeleteGroup(group);
+ } else if (group->nWins <= 0) {
+ free(group->windows);
+ group->windows = NULL;
+ groupDeleteGroup(group);
+ }
+
+ free(buf);
+
+ damageWindowOutputExtents(w);
+ gw->group = NULL;
+ updateWindowOutputExtents(w);
+
+ if (allowRegroup && gs->opt[GROUP_SCREEN_OPTION_AUTOTAB].value.b && (w->type & gs->wMask)) {
+ groupAddWindowToGroup(w, NULL);
+ groupTabGroup(w);
+ }
+ }
+}
+
+/*
+ * groupDeleteGroup
+ *
+ */
+void groupDeleteGroup(GroupSelection *group)
+{
+ GROUP_SCREEN(group->screen);
+
+ if (group->windows != NULL) {
+ if (group->tabBar) {
+ groupUntabGroup(group);
+ group->ungroupState = UngroupAll;
+ return;
+ }
+
+ int i;
+ for (i = 0; i < group->nWins; i++) {
+ CompWindow *cw = group->windows[i];
+ GROUP_WINDOW(cw);
+ GROUP_SCREEN(cw->screen);
+
+ damageWindowOutputExtents(cw);
+ gw->group = NULL;
+ updateWindowOutputExtents(cw);
+
+ if (gs->opt[GROUP_SCREEN_OPTION_AUTOTAB].value.b && (cw->type & gs->wMask)) {
+ groupAddWindowToGroup(cw, NULL);
+ groupTabGroup(cw);
+ }
+ }
+ free(group->windows);
+ group->windows = NULL;
+ } else if (group->tabBar)
+ groupDeleteTabBar(group);
+
+ GroupSelection *prev = group->prev;
+ GroupSelection *next = group->next;
+
+ // relink stack
+ if (prev || next) {
+ if (prev) {
+ if (next)
+ prev->next = next;
+ else {
+ prev->next = NULL;
+ }
+ }
+ if (next) {
+ if (prev)
+ next->prev = prev;
+ else {
+ next->prev = NULL;
+ gs->groups = next;
+ }
+ }
+ } else {
+ gs->groups = NULL;
+ }
+ free(group);
+}
+
+/*
+ * groupAddWindowToGroup
+ *
+ */
+void
+groupAddWindowToGroup(CompWindow * w, GroupSelection *group)
+{
+ GROUP_SCREEN(w->screen);
+ GROUP_WINDOW(w);
+
+ if(group && (gw->group == group))
+ return;
+
+ if (gw->group)
+ {
+ gw->ungroup = TRUE; //This will prevent setting up animations on the previous group.
+ groupDeleteGroupWindow(w, FALSE);
+ gw->ungroup = FALSE;
+ }
+
+ if (group) {
+ group->windows = (CompWindow **) realloc(group->windows, sizeof(CompWindow *) * (group->nWins + 1));
+ group->windows[group->nWins] = w;
+ group->nWins++;
+
+ gw->group = group;
+ updateWindowOutputExtents(w);
+
+ if(group->nWins == 2)
+ updateWindowOutputExtents(group->windows[0]); // First window in the group got its glow too...
+
+ if (group->tabBar && group->topTab)
+ {
+ CompWindow *topTab = TOP_TAB(group);
+
+ if(!gw->slot)
+ groupCreateSlot(group, w);
+
+ gw->destination.x = WIN_X(topTab) + (WIN_WIDTH(topTab) / 2) - (WIN_WIDTH(w) / 2);
+ gw->destination.y = WIN_Y(topTab) + (WIN_HEIGHT(topTab) / 2) - (WIN_HEIGHT(w) / 2);
+ gw->mainTabOffset.x = WIN_X(w) - gw->destination.x;
+ gw->mainTabOffset.y = WIN_Y(w) - gw->destination.y;
+ gw->orgPos.x = WIN_X(w);
+ gw->orgPos.y = WIN_Y(w);
+
+ gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f;
+
+ gw->animateState = IS_ANIMATED;
+
+ groupStartTabbingAnimation(group, TRUE);
+
+ damageScreen(w->screen);
+ }
+ } else {
+ GroupSelection *g = malloc(sizeof(GroupSelection));
+
+ g->windows = (CompWindow **) calloc(1, sizeof(CompWindow *));
+ g->windows[0] = w;
+ g->screen = w->screen;
+ g->nWins = 1;
+ g->topTab = NULL;
+ g->prevTopTab = NULL;
+ g->nextTopTab = NULL;
+ g->activateTab = NULL;
+ g->doTabbing = FALSE;
+ g->changeAnimationTime = 0;
+ g->changeAnimationDirection = 0;
+ g->changeState = PaintOff;
+ g->tabbingState = PaintOff;
+ g->changeTab = FALSE;
+ g->ungroupState = UngroupNone;
+ g->tabBar = NULL;
+
+ g->grabWindow = None;
+ g->grabMask = 0;
+
+ g->inputPrevention = None;
+
+ g->oldTopTabCenterX = 0;
+ g->oldTopTabCenterY = 0;
+
+ // glow color
+ srand(time(NULL));
+ g->color[0] = rand() % 0xFFFF;
+ g->color[1] = rand() % 0xFFFF;
+ g->color[2] = rand() % 0xFFFF;
+ g->color[3] = 0xFFFF;
+
+ // relink stack
+ if (gs->groups) {
+ gs->groups->prev = g;
+ g->next = gs->groups;
+ g->prev = NULL;
+ gs->groups = g;
+ } else {
+ g->prev = NULL;
+ g->next = NULL;
+ gs->groups = g;
+ }
+
+ gw->group = g;
+ }
+}
+
+/*
+ * groupGroupWindows
+ *
+ */
+Bool
+groupGroupWindows(CompDisplay * d, CompAction * action,
+ CompActionState state, CompOption * option, int nOption)
+{
+ GROUP_DISPLAY(d);
+
+ if (gd->tmpSel.nWins > 0) {
+ int i;
+ CompWindow *cw;
+ GroupSelection *group = NULL;
+ Bool tabbed = FALSE;
+
+ for(i = 0; i < gd->tmpSel.nWins; i++)
+ {
+ cw = gd->tmpSel.windows[i];
+ GROUP_WINDOW(cw);
+
+ if (gw->group)
+ {
+ if(!tabbed || group->tabBar)
+ group = gw->group;
+
+ if(group->tabBar)
+ tabbed = TRUE;
+ }
+ }
+
+ // we need to do one first to get the pointer of a new group
+ cw = gd->tmpSel.windows[0];
+ groupAddWindowToGroup(cw, group);
+
+ GROUP_WINDOW (cw);
+ gw->inSelection = FALSE;
+ damageScreen(cw->screen);
+ group = gw->group;
+
+ for (i = 1; i < gd->tmpSel.nWins; i++) {
+ cw = gd->tmpSel.windows[i];
+ GROUP_WINDOW(cw);
+
+ groupAddWindowToGroup(cw, group);
+
+ gw->inSelection = FALSE;
+ damageScreen(cw->screen);
+ }
+
+ // exit selection
+ free(gd->tmpSel.windows);
+ gd->tmpSel.windows = NULL;
+ gd->tmpSel.nWins = 0;
+ }
+
+ return FALSE;
+}
+
+/*
+ * groupUnGroupWindows
+ *
+ */
+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->group) {
+ groupDeleteGroup(gw->group);
+ }
+
+ return FALSE;
+}
+
+/*
+ * groupRemoveWindow
+ *
+ */
+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->group)
+ groupDeleteGroupWindow(cw, TRUE);
+
+ return FALSE;
+}
+
+/*
+ * groupCloseWindows
+ *
+ */
+Bool
+groupCloseWindows(CompDisplay * d, CompAction * action,
+ CompActionState state, CompOption * option, int nOption)
+{
+ CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
+ if (!w)
+ return FALSE;
+ GROUP_WINDOW(w);
+
+ if (gw->group) {
+
+ int nWins = gw->group->nWins;
+ int i;
+ for (i = 0; i < nWins; i++) {
+ CompWindow *cw = gw->group->windows[i];
+ closeWindow(cw, getCurrentTimeFromDisplay(d));
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * groupChangeColor
+ *
+ */
+Bool
+groupChangeColor(CompDisplay * d, CompAction * action,
+ CompActionState state, CompOption * option,
+ int nOption)
+{
+ CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
+ if (!w)
+ return FALSE;
+ GROUP_WINDOW(w);
+
+ if (gw->group) {
+ srand(time(NULL));
+ gw->group->color[0] = rand() % 0xFFFF;
+ gw->group->color[1] = rand() % 0xFFFF;
+ gw->group->color[2] = rand() % 0xFFFF;
+
+ groupRenderTopTabHighlight(gw->group);
+
+ damageScreen(w->screen);
+ }
+
+ return FALSE;
+}
+
+/*
+ * groupSetIgnore
+ *
+ */
+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
+ *
+ */
+Bool
+groupUnsetIgnore(CompDisplay * d, CompAction * action,
+ CompActionState state, CompOption * option, int nOption)
+{
+ GROUP_DISPLAY(d);
+
+ gd->ignoreMode = FALSE;
+
+ action->state &= ~CompActionStateTermKey;
+
+ return FALSE;
+}
+
+/*
+ * groupHandleButtonPressEvent
+ *
+ */
+static void
+groupHandleButtonPressEvent(CompDisplay *d, XEvent *event)
+{
+ int xRoot = event->xbutton.x_root;
+ int yRoot = event->xbutton.y_root;
+ int button = event->xbutton.button;
+
+ CompScreen *s = findScreenAtDisplay(d, event->xbutton.root);
+
+ if (!s)
+ return;
+
+ GROUP_SCREEN(s);
+
+ if (button == 1) {
+ GroupSelection *group;
+
+ for (group = gs->groups; group; group = group->next) {
+ if ((group->inputPrevention == event->xbutton.window) && group->tabBar) {
+ GroupTabBarSlot *slot;
+
+ for (slot = group->tabBar->slots; slot; slot = slot->next) {
+ if (XPointInRegion (slot->region, xRoot, yRoot)) {
+ CompScreen *s = group->screen;
+ GROUP_SCREEN(s);
+
+ gs->draggedSlot = slot;
+ gs->dragged = FALSE; // The slot isn't dragged yet...
+
+ gs->prevX = xRoot;
+ gs->prevY = yRoot;
+
+ if (gs->grabState == ScreenGrabNone) {
+ if (!otherScreenGrabExist(s, "group", "group-drag"))
+ groupGrabScreen(s, ScreenGrabTabDrag);
+ }
+ }
+ }
+ }
+ }
+ } else if ((button == 4) || (button == 5)) {
+ CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
+ CompWindow *topTab = w;
+
+ if(!w)
+ return;
+
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ if(!gw->slot || !gw->group || !gw->group->tabBar)
+ return;
+
+ if(gw->group->tabBar->state != PaintOn && gw->group->tabBar->state != PaintPermanentOn)
+ return;
+
+ if(!XPointInRegion(gw->group->tabBar->region, xRoot, yRoot))
+ return;
+
+ if(gw->group->nextTopTab)
+ topTab = NEXT_TOP_TAB(gw->group);
+ else if(gw->group->topTab)
+ topTab = TOP_TAB(gw->group); // If there are no tabbing animations, topTab is never NULL.
+
+ gw = GET_GROUP_WINDOW(topTab, gs);
+
+ if (button == 4) {
+ if(gw->slot->prev)
+ groupChangeTab(gw->slot->prev, RotateLeft);
+ else
+ groupChangeTab(gw->group->tabBar->revSlots, RotateLeft);
+ } else {
+ if(gw->slot->next)
+ groupChangeTab(gw->slot->next, RotateRight);
+ else
+ groupChangeTab(gw->group->tabBar->slots, RotateRight);
+ }
+ }
+}
+
+/*
+ * groupHandleButtonReleaseEvent
+ *
+ */
+static void
+groupHandleButtonReleaseEvent(CompDisplay *d, XEvent *event)
+{
+ GroupSelection *group;
+
+ if (event->xbutton.button != 1)
+ return;
+
+ CompScreen *s = findScreenAtDisplay(d, event->xbutton.root);
+ if (!s)
+ return;
+
+ GROUP_SCREEN(s);
+
+ if(!gs->draggedSlot)
+ return;
+
+ if(!gs->dragged) {
+ groupChangeTab(gs->draggedSlot, RotateUncertain);
+ gs->draggedSlot = NULL;
+
+ if (gs->grabState == ScreenGrabTabDrag)
+ groupGrabScreen(s, ScreenGrabNone);
+
+ return;
+ }
+
+ GROUP_WINDOW(gs->draggedSlot->window);
+
+ int vx, vy;
+ Region newRegion = XCreateRegion();
+ XUnionRegion(newRegion, gs->draggedSlot->region, newRegion);
+
+ groupGetDrawOffsetForSlot(gs->draggedSlot, &vx, &vy);
+ XOffsetRegion(newRegion, vx, vy);
+
+ Bool inserted = FALSE;
+ for(group = gs->groups; group; group = group->next) {
+ Bool inTabBar;
+
+ if (!group->tabBar)
+ continue;
+
+ // create clipping region
+ Region clip = groupGetClippingRegion(TOP_TAB(group));
+
+ Region buf = XCreateRegion();
+ XIntersectRegion(newRegion, group->tabBar->region, buf);
+ XSubtractRegion(buf, clip, buf);
+ XDestroyRegion(clip);
+
+ inTabBar = !XEmptyRegion(buf);
+
+ XDestroyRegion(buf);
+
+ if(!inTabBar)
+ continue;
+
+ GroupTabBarSlot *slot;
+
+ for(slot = group->tabBar->slots; slot; slot = slot->next) {
+ if(slot == gs->draggedSlot)
+ continue;
+
+ Region slotRegion = XCreateRegion();
+ XRectangle rect;
+ Bool inSlot;
+
+ if(slot->prev && slot->prev != gs->draggedSlot)
+ rect.x = slot->prev->region->extents.x2;
+ else if(slot->prev && slot->prev == gs->draggedSlot && gs->draggedSlot->prev)
+ rect.x = gs->draggedSlot->prev->region->extents.x2;
+ else
+ rect.x = group->tabBar->region->extents.x1;
+
+ rect.y = slot->region->extents.y1;
+
+ if(slot->next && slot->next != gs->draggedSlot)
+ rect.width = slot->next->region->extents.x1 - rect.x;
+ else if(slot->next && slot->next == gs->draggedSlot && gs->draggedSlot->next)
+ rect.width = gs->draggedSlot->next->region->extents.x1 - rect.x;
+ else
+ rect.width = group->tabBar->region->extents.x2;
+
+ rect.height = slot->region->extents.y2 - slot->region->extents.y1;
+
+ XUnionRectWithRegion(&rect, slotRegion, slotRegion);
+
+ Region buf = XCreateRegion();
+ XIntersectRegion(newRegion, slotRegion, buf);
+
+ inSlot = !XEmptyRegion(buf);
+
+ XDestroyRegion(buf);
+ XDestroyRegion(slotRegion);
+
+ if (!inSlot)
+ continue;
+
+ GroupTabBarSlot *tmpDraggedSlot = gs->draggedSlot;
+
+ if(group != gw->group) {
+ GroupSelection *tmpGroup = gw->group;
+
+ // if the dragged window is not the top tab, move it onscreen
+ if (!IS_TOP_TAB(gs->draggedSlot->window, tmpGroup) && tmpGroup->topTab) {
+ CompWindow *tw = tmpGroup->topTab->window;
+
+ tmpGroup->oldTopTabCenterX = WIN_X(tw) + WIN_WIDTH(tw) / 2;
+ tmpGroup->oldTopTabCenterY = WIN_Y(tw) + WIN_HEIGHT(tw) / 2;
+
+ gs->queued = TRUE;
+ moveWindowOnscreen(gs->draggedSlot->window);
+ moveWindow(gs->draggedSlot->window,
+ gw->group->oldTopTabCenterX - WIN_X(gs->draggedSlot->window) - WIN_WIDTH(gs->draggedSlot->window) / 2,
+ gw->group->oldTopTabCenterY - WIN_Y(gs->draggedSlot->window) - WIN_HEIGHT(gs->draggedSlot->window) / 2,
+ TRUE, TRUE);
+ syncWindowPosition(gs->draggedSlot->window);
+ gs->queued = FALSE;
+ }
+
+ // Change the group.
+ groupAddWindowToGroup(gs->draggedSlot->window, group);
+ } else
+ groupUnhookTabBarSlot(group->tabBar, gs->draggedSlot, TRUE);
+
+ // for re-calculating the tab-bar including the dragged window
+ gs->draggedSlot = NULL;
+ gs->dragged = FALSE;
+ inserted = TRUE;
+
+ if((tmpDraggedSlot->region->extents.x1 + tmpDraggedSlot->region->extents.x2 + (2 * vx)) / 2 >
+ (slot->region->extents.x1 + slot->region->extents.x2) / 2)
+ groupInsertTabBarSlotAfter(group->tabBar, tmpDraggedSlot, slot);
+ else
+ groupInsertTabBarSlotBefore(group->tabBar, tmpDraggedSlot, slot);
+
+ // Hide tab-bars.
+ GroupSelection *tmpGroup;
+ for(tmpGroup = gs->groups; tmpGroup; tmpGroup = tmpGroup->next)
+ groupTabSetVisibility(tmpGroup, FALSE, PERMANENT);
+
+ break;
+ }
+
+ if (inserted)
+ break;
+ }
+
+ if (newRegion)
+ XDestroyRegion(newRegion);
+
+ if (!inserted) {
+ CompWindow *draggedSlotWindow = gs->draggedSlot->window;
+ GroupSelection *tmpGroup;
+
+ for(tmpGroup = gs->groups; tmpGroup; tmpGroup = tmpGroup->next)
+ groupTabSetVisibility(tmpGroup, FALSE, PERMANENT);
+
+ gs->draggedSlot = NULL;
+ gs->dragged = FALSE;
+
+ if(gs->opt[GROUP_SCREEN_OPTION_DND_UNGROUP_WINDOW].value.b)
+ groupDeleteGroupWindow(draggedSlotWindow, TRUE);
+
+ else if (gw->group && gw->group->topTab) {
+ groupRecalcTabBarPos(gw->group,
+ (gw->group->tabBar->region->extents.x1 + gw->group->tabBar->region->extents.x2) / 2,
+ gw->group->tabBar->region->extents.x1, gw->group->tabBar->region->extents.x2);
+ }
+
+ // to remove the painted slot
+ damageScreen(s);
+ }
+
+ if (gs->grabState == ScreenGrabTabDrag)
+ groupGrabScreen(s, ScreenGrabNone);
+
+ if (gs->dragHoverTimeoutHandle) {
+ compRemoveTimeout(gs->dragHoverTimeoutHandle);
+ gs->dragHoverTimeoutHandle = 0;
+ }
+}
+
+/*
+ * groupHandleMotionEvent
+ *
+ */
+
+/* the radius to determine if it was a click or a drag */
+#define RADIUS 5
+
+static void
+groupHandleMotionEvent(CompScreen * s, int xRoot, int yRoot)
+{
+ GROUP_SCREEN(s);
+
+ if (gs->grabState == ScreenGrabTabDrag) {
+ int dx, dy;
+ REGION reg;
+ Region draggedRegion = gs->draggedSlot->region;
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ dx = xRoot - gs->prevX;
+ dy = yRoot - gs->prevY;
+
+ if(gs->dragged || abs(dx) > RADIUS || abs(dy) > RADIUS) {
+ gs->prevX = xRoot;
+ gs->prevY = yRoot;
+
+ if(!gs->dragged) {
+ GroupSelection *group;
+ GROUP_WINDOW(gs->draggedSlot->window);
+
+ gs->dragged = TRUE;
+
+ for(group = gs->groups; group; group = group->next)
+ groupTabSetVisibility(group, TRUE, PERMANENT);
+
+ groupRecalcTabBarPos(gw->group, (gw->group->tabBar->region->extents.x1 + gw->group->tabBar->region->extents.x2) / 2,
+ gw->group->tabBar->region->extents.x1, gw->group->tabBar->region->extents.x2);
+ }
+
+
+ int vx,vy;
+ groupGetDrawOffsetForSlot(gs->draggedSlot, &vx, &vy);
+
+ // (border = 5) + (buffer = 2) == (damage buffer = 7)
+ //TODO: respect border option.
+ reg.extents.x1 = draggedRegion->extents.x1 - 7 + vx;
+ reg.extents.y1 = draggedRegion->extents.y1 - 7 + vy;
+ reg.extents.x2 = draggedRegion->extents.x2 + 7 + vx;
+ reg.extents.y2 = draggedRegion->extents.y2 + 7 + vy;
+ damageScreenRegion(s, &reg);
+
+ XOffsetRegion (gs->draggedSlot->region, dx, dy);
+ gs->draggedSlot->springX = (gs->draggedSlot->region->extents.x1 + gs->draggedSlot->region->extents.x2) / 2;
+
+ reg.extents.x1 = draggedRegion->extents.x1 - 7 + vx;
+ reg.extents.y1 = draggedRegion->extents.y1 - 7 + vy;
+ reg.extents.x2 = draggedRegion->extents.x2 + 7 + vx;
+ reg.extents.y2 = draggedRegion->extents.y2 + 7 + vy;
+ damageScreenRegion(s, &reg);
+ }
+ } else
+ groupDamageSelectionRect(s, xRoot, yRoot);
+}
+
+/*
+ * groupHandleEvent
+ *
+ */
+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 ButtonPress:
+ groupHandleButtonPressEvent(d, event);
+ break;
+
+ case ButtonRelease:
+ groupHandleButtonReleaseEvent(d, event);
+ break;
+
+ case UnmapNotify:
+ {
+ CompWindow *w = findWindowAtDisplay(d, event->xunmap.window);
+ if (!w)
+ break;
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ if (w->pendingUnmaps) {
+ if (w->shaded) {
+ gw->windowState = WindowShaded;
+
+ if (gw->group && gs->opt[GROUP_SCREEN_OPTION_SHADE_ALL].value.b)
+ groupShadeWindows(w, gw->group, TRUE);
+ } else if (!w->invisible) {
+ gw->windowState = WindowMinimized;
+
+ if (gw->group && gs->opt[GROUP_SCREEN_OPTION_MINIMIZE_ALL].value.b)
+ groupMinimizeWindows(w, gw->group, TRUE);
+ }
+ }
+
+ if (gw->group) {
+ if (gw->group->tabBar && IS_TOP_TAB(w, gw->group)) {
+ /* on unmap of the top tab, hide the tab bar and the
+ input prevention window */
+ groupTabSetVisibility(gw->group, FALSE, PERMANENT);
+ }
+ if (!w->pendingUnmaps) {
+ //close event
+ gw->ungroup = TRUE; // This will prevent setting up animations on group.
+ groupDeleteGroupWindow(w, FALSE);
+ gw->ungroup = FALSE;
+ damageScreen(w->screen);
+ }
+ }
+ break;
+ }
+
+ case ClientMessage:
+ if (event->xclient.message_type == d->winActiveAtom) {
+ CompWindow *w = findWindowAtDisplay(d, event->xclient.window);
+ if (!w)
+ return;
+
+ GROUP_WINDOW(w);
+
+ if (gw->group && gw->group->tabBar && HAS_TOP_WIN(gw->group)) {
+ CompWindow *tw = TOP_TAB(gw->group);
+
+ if (w->id != tw->id) {
+ /* if a non top-tab has been activated, switch to the
+ top-tab instead - but only if is visible */
+ if (tw->shaded) {
+ tw->state &= ~CompWindowStateShadedMask;
+ updateWindowAttributes(tw, FALSE);
+ } else if (tw->minimized)
+ unminimizeWindow(tw);
+
+ if (!(tw->state & CompWindowStateHiddenMask)) {
+ if (!gw->group->changeTab)
+ gw->group->activateTab = gw->slot;
+ sendWindowActivationRequest(tw->screen, tw->id);
+ }
+ }
+ }
+ }
+ 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) {
+ CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
+ if (!w)
+ break;
+
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ if (gw->group) {
+ if (!gw->group->tabBar && !screenGrabExist(w->screen, "scale", 0) &&
+ gs->opt[GROUP_SCREEN_OPTION_RAISE].value.b) {
+ groupRaiseWindows(w, gw->group);
+ }
+
+ if (gw->group->activateTab) {
+ groupChangeTab(gw->group->activateTab, RotateUncertain);
+ gw->group->activateTab = NULL;
+ }
+ }
+ }
+ break;
+
+ case EnterNotify:
+ {
+ CompWindow *w = findWindowAtDisplay(d, event->xcrossing.window);
+ if (!w)
+ break;
+
+ GROUP_WINDOW(w);
+ if (gw->group) {
+ GROUP_SCREEN(w->screen);
+
+ if (gs->draggedSlot && gs->dragged && IS_TOP_TAB(w, gw->group)) {
+ int hoverTime =
+ gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_HOVER_TIME].value.f * 1000;
+ if (gs->dragHoverTimeoutHandle)
+ compRemoveTimeout(gs->dragHoverTimeoutHandle);
+
+ if (hoverTime > 0)
+ gs->dragHoverTimeoutHandle =
+ compAddTimeout(hoverTime, groupDragHoverTimeout, w);
+ }
+ }
+ }
+
+ case ConfigureNotify:
+ {
+ CompWindow *w = findWindowAtDisplay(d, event->xconfigure.window);
+
+ if (!w)
+ break;
+
+ GROUP_WINDOW(w);
+
+ if (gw->group && gw->group->tabBar && IS_TOP_TAB(w, gw->group) && gw->group->inputPrevention)
+ groupUpdateInputPreventionWindow(gw->group);
+ }
+ break;
+
+
+ default:
+ break;
+ }
+}
+
+/*
+ * groupGetOutputExtentsForWindow
+ *
+ */
+void
+groupGetOutputExtentsForWindow (CompWindow * w, CompWindowExtents * output)
+{
+ GROUP_SCREEN (w->screen);
+ GROUP_WINDOW (w);
+
+ UNWRAP (gs, w->screen, getOutputExtentsForWindow);
+ (*w->screen->getOutputExtentsForWindow) (w, output);
+ WRAP (gs, w->screen, getOutputExtentsForWindow,
+ groupGetOutputExtentsForWindow);
+
+ if (gw->group && gw->group->nWins > 1)
+ {
+ int glowSize = gs->opt[GROUP_SCREEN_OPTION_GLOW_SIZE].value.i;
+
+ if (glowSize > output->left)
+ output->left = glowSize;
+ if (glowSize > output->right)
+ output->right = glowSize;
+ if (glowSize > output->top)
+ output->top = glowSize;
+ if (glowSize > output->bottom)
+ output->bottom = glowSize;
+ }
+}
+
+/*
+ * groupWindowResizeNotify
+ *
+ */
+void groupWindowResizeNotify(CompWindow * w, int dx, int dy, int dwidth,
+ int dheight, Bool preview)
+{
+ GROUP_SCREEN(w->screen);
+ GROUP_DISPLAY(w->screen->display);
+ GROUP_WINDOW(w);
+
+ UNWRAP(gs, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight, preview);
+ WRAP(gs, w->screen, windowResizeNotify, groupWindowResizeNotify);
+
+ if (gw->glowQuads)
+ groupComputeGlowQuads (w, &gs->glowTexture.matrix);
+
+ if (!dx && !dy && !dwidth && !dheight)
+ return;
+
+ if (preview)
+ return;
+
+ if (gw->group && gw->group->tabBar &&
+ (gw->group->tabBar->state != PaintOff) && IS_TOP_TAB(w, gw->group)) {
+ groupRecalcTabBarPos(gw->group, w->screen->display->pointerX,
+ WIN_X(w), WIN_X(w) + WIN_WIDTH(w));
+ }
+
+ if (gw->group && !gd->ignoreMode) {
+ if (((gw->lastState & MAXIMIZE_STATE) != (w->state & MAXIMIZE_STATE))
+ && gs->opt[GROUP_SCREEN_OPTION_RESIZE_UNMAXIMIZE].value.b)
+ {
+ int i;
+ for (i = 0; i < gw->group->nWins; i++) {
+ CompWindow *cw = gw->group->windows[i];
+ if (!cw)
+ continue;
+
+ if (cw->id == w->id)
+ continue;
+
+ maximizeWindow(cw, w->state & MAXIMIZE_STATE);
+ }
+ } else if ((gw->group->grabWindow == w->id) && gs->opt[GROUP_SCREEN_OPTION_RESIZE].value.b) {
+ int i;
+ for (i = 0; i < gw->group->nWins; i++) {
+ CompWindow *cw = gw->group->windows[i];
+ if (!cw)
+ continue;
+
+ if (cw->state & MAXIMIZE_STATE)
+ continue;
+
+ if (cw->id == w->id)
+ continue;
+
+ int nx = 0;
+ int ny = 0;
+
+ if (gs->opt[GROUP_SCREEN_OPTION_RELATIVE_DISTANCE].value.b) {
+ int distX = cw->serverX - (w->serverX - dx);
+ int distY = cw->serverY - (w->serverY - dy);
+ int ndistX = distX * ((float) w->serverWidth / (float) (w->serverWidth - dwidth));
+ int ndistY = distY * ((float) w->serverHeight / (float) (w->serverHeight - dheight));
+ nx = w->serverX + ndistX;
+ ny = w->serverY + ndistY;
+ } else {
+ nx = cw->serverX + dx;
+ ny = cw->serverY + dy;
+ }
+
+ int nwidth = cw->serverWidth + dwidth;
+ int nheight = cw->serverHeight + dheight;
+
+ constrainNewWindowSize(cw, nwidth, nheight, &nwidth, &nheight);
+
+ XWindowChanges xwc;
+
+ xwc.x = nx;
+ xwc.y = ny;
+ xwc.width = nwidth;
+ xwc.height = nheight;
+
+ configureXWindow(cw, CWX | CWY | CWWidth | CWHeight, &xwc);
+ }
+ }
+ }
+}
+
+/*
+ * groupWindowMoveNotify
+ *
+ */
+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);
+
+ if (gw->glowQuads)
+ groupComputeGlowQuads (w, &gs->glowTexture.matrix);
+
+ if(!gw->group || gs->queued)
+ return;
+
+ if (w->state & CompWindowStateOffscreenMask)
+ return;
+
+ Bool viewportChange = screenGrabExist (w->screen, "rotate", 0) &&
+ ((dx && !(dx % w->screen->width)) || (dy && !(dy % w->screen->height)));
+
+ if (viewportChange && (gw->animateState & IS_ANIMATED)) {
+ gw->destination.x += dx;
+ gw->destination.y += dy;
+ }
+
+ if (gw->group->tabBar && IS_TOP_TAB(w, gw->group)) {
+ GroupTabBarSlot *slot;
+ GroupTabBar *bar = gw->group->tabBar;
+
+ if(gs->opt[GROUP_SCREEN_OPTION_SPRING_MODEL_ON_MOVE].value.b)
+ XOffsetRegion (bar->region, 0, dy);
+ else
+ XOffsetRegion (bar->region, dx, dy);
+
+ bar->rightSpringX += dx;
+ bar->leftSpringX += dx;
+
+ for(slot = bar->slots; slot; slot = slot->next) {
+ if(gs->opt[GROUP_SCREEN_OPTION_SPRING_MODEL_ON_MOVE].value.b)
+ XOffsetRegion (slot->region, 0, dy);
+ else
+ XOffsetRegion (slot->region, dx, dy);
+ slot->springX += dx;
+ }
+
+ groupUpdateInputPreventionWindow(gw->group);
+
+ return;
+ }
+
+ if (gw->group->doTabbing || gd->ignoreMode ||
+ (gw->group->grabWindow != w->id) ||
+ !screenGrabExist (w->screen, "move", 0) ||
+ !gs->opt[GROUP_SCREEN_OPTION_MOVE].value.b)
+ return;
+
+ int i;
+
+ for (i = 0; i < gw->group->nWins; i++) {
+ CompWindow *cw = gw->group->windows[i];
+ if (!cw)
+ continue;
+
+ if (cw->id == w->id)
+ continue;
+
+ GROUP_WINDOW(cw);
+
+ if (cw->state & MAXIMIZE_STATE) {
+ if (viewportChange) {
+ gw->needsPosSync = TRUE;
+ groupEnqueueMoveNotify (cw, -dx, -dy, TRUE);
+ }
+ } else if (!viewportChange) {
+ gw->needsPosSync = TRUE;
+ groupEnqueueMoveNotify (cw, dx, dy, FALSE);
+ }
+ }
+}
+
+void
+groupWindowGrabNotify(CompWindow * w,
+ int x, int y, unsigned int state, unsigned int mask)
+{
+ GROUP_SCREEN(w->screen);
+ GROUP_DISPLAY(w->screen->display);
+ GROUP_WINDOW(w);
+
+ if (gw->group && !gd->ignoreMode && !gs->queued) {
+ int i;
+ for (i = 0; i < gw->group->nWins; i++) {
+ CompWindow *cw = gw->group->windows[i];
+ if (!cw)
+ continue;
+
+ if (cw->id != w->id)
+ groupEnqueueGrabNotify (cw, x, y, state, mask);
+ }
+
+ gw->group->grabWindow = w->id;
+ gw->group->grabMask = mask;
+ }
+
+ UNWRAP(gs, w->screen, windowGrabNotify);
+ (*w->screen->windowGrabNotify) (w, x, y, state, mask);
+ WRAP(gs, w->screen, windowGrabNotify, groupWindowGrabNotify);
+}
+
+void groupWindowUngrabNotify(CompWindow * w)
+{
+ GROUP_SCREEN(w->screen);
+ GROUP_DISPLAY(w->screen->display);
+ GROUP_WINDOW(w);
+
+ if (gw->group && !gd->ignoreMode && !gs->queued) {
+ int i;
+
+ groupDequeueMoveNotifies(w->screen);
+
+ for (i = 0; i < gw->group->nWins; i++) {
+ CompWindow *cw = gw->group->windows[i];
+ if (!cw)
+ continue;
+
+ if (cw->id != w->id) {
+ GROUP_WINDOW(cw);
+
+ if (gw->needsPosSync) {
+ syncWindowPosition(cw);
+ gw->needsPosSync = FALSE;
+ }
+ groupEnqueueUngrabNotify (cw);
+ }
+ }
+
+ gw->group->grabWindow = None;
+ gw->group->grabMask = 0;
+ }
+
+ UNWRAP(gs, w->screen, windowUngrabNotify);
+ (*w->screen->windowUngrabNotify) (w);
+ WRAP(gs, w->screen, windowUngrabNotify, groupWindowUngrabNotify);
+}
+
+
+Bool groupDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect)
+{
+
+ GROUP_SCREEN(w->screen);
+ Bool status;
+
+ UNWRAP(gs,w->screen,damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w,initial,rect);
+ WRAP(gs,w->screen,damageWindowRect,groupDamageWindowRect);
+
+ if (initial) {
+ GROUP_WINDOW(w);
+
+ if (gs->opt[GROUP_SCREEN_OPTION_AUTOTAB].value.b && (w->type & gs->wMask)) {
+ if (!gw->group && (gw->windowState == WindowNormal)) {
+ groupAddWindowToGroup(w, NULL);
+ groupTabGroup(w);
+ }
+ }
+
+ if ((gw->windowState == WindowMinimized) && gw->group) {
+ if (gs->opt[GROUP_SCREEN_OPTION_MINIMIZE_ALL].value.b)
+ groupMinimizeWindows(w, gw->group, FALSE);
+ } else if ((gw->windowState == WindowShaded) && gw->group) {
+ if (gs->opt[GROUP_SCREEN_OPTION_SHADE_ALL].value.b)
+ groupShadeWindows(w, gw->group, FALSE);
+ }
+
+ gw->windowState = WindowNormal;
+ }
+
+ return status;
+
+}
+
+void groupWindowStateChangeNotify(CompWindow *w)
+{
+ GROUP_SCREEN(w->screen);
+ GROUP_WINDOW(w);
+
+ gw->lastState = w->lastState;
+
+ UNWRAP(gs, w->screen, windowStateChangeNotify);
+ (*w->screen->windowStateChangeNotify) (w);
+ WRAP(gs, w->screen, windowStateChangeNotify, groupWindowStateChangeNotify);
+}
diff --git a/beryl-plugins/src/group/group.h b/beryl-plugins/src/group/group.h
new file mode 100644
index 0000000..185b388
--- /dev/null
+++ b/beryl-plugins/src/group/group.h
@@ -0,0 +1,650 @@
+#ifndef _GROUP_H
+#define _GROUP_H
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * group.h
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * 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.
+ *
+ **/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include <time.h>
+#include <X11/Xlib.h>
+#include <cairo-xlib-xrender.h>
+#include <beryl.h>
+#include <beryl_ipcs.h>
+#include <beryl-text.h>
+#include <X11/Xatom.h>
+
+#include <math.h>
+#include <limits.h>
+
+#include "group_glow.h"
+
+/*
+ * Constants
+ *
+ */
+#define PI 3.1415926535897
+
+#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_REMOVING 4
+#define GROUP_DISPLAY_OPTION_CLOSING 5
+#define GROUP_DISPLAY_OPTION_IGNORE 6
+#define GROUP_DISPLAY_OPTION_TABMODE 7
+#define GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT 8
+#define GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT 9
+#define GROUP_DISPLAY_OPTION_CHANGE_COLOR 10
+#define GROUP_DISPLAY_OPTION_NUM 11
+
+#define GROUP_SCREEN_OPTION_TYPES 0
+#define GROUP_SCREEN_OPTION_OPACITY 1
+#define GROUP_SCREEN_OPTION_SATURATION 2
+#define GROUP_SCREEN_OPTION_BRIGHTNESS 3
+#define GROUP_SCREEN_OPTION_GLOW 4
+#define GROUP_SCREEN_OPTION_MOVE 5
+#define GROUP_SCREEN_OPTION_RESIZE 6
+#define GROUP_SCREEN_OPTION_RAISE 7
+#define GROUP_SCREEN_OPTION_AUTO_UNGROUP 8
+#define GROUP_SCREEN_OPTION_AUTO_GROUP 9
+#define GROUP_SCREEN_OPTION_RELATIVE_DISTANCE 10
+#define GROUP_SCREEN_OPTION_SELECTION_COLOR 11
+#define GROUP_SCREEN_OPTION_LINE_COLOR 12
+#define GROUP_SCREEN_OPTION_PRECISION 13
+#define GROUP_SCREEN_OPTION_TABBING_SPEED 14
+#define GROUP_SCREEN_OPTION_TABBING_TIMESTEP 15
+#define GROUP_SCREEN_OPTION_THUMB_SIZE 16
+#define GROUP_SCREEN_OPTION_BORDER_WIDTH 17
+#define GROUP_SCREEN_OPTION_BORDER_RADIUS 18
+#define GROUP_SCREEN_OPTION_TAB_BORDER_COLOR 19
+#define GROUP_SCREEN_OPTION_TAB_FILL_COLOR 20
+#define GROUP_SCREEN_OPTION_GLOW_SIZE 21
+#define GROUP_SCREEN_OPTION_GLOW_TYPE 22
+#define GROUP_SCREEN_OPTION_FADE_TIME 23
+#define GROUP_SCREEN_OPTION_VISIBILITY_TIME 24
+#define GROUP_SCREEN_OPTION_UNTAB_ON_CLOSE 25
+#define GROUP_SCREEN_OPTION_TABBAR_FONTSIZE 26
+#define GROUP_SCREEN_OPTION_FADE_TEXT_TIME 27
+#define GROUP_SCREEN_OPTION_TABBAR_FONTCOLOR 28
+#define GROUP_SCREEN_OPTION_TAB_CREATE_MIPMAPS 29
+#define GROUP_SCREEN_OPTION_CHANGE_ANIMATION_TIME 30
+#define GROUP_SCREEN_OPTION_TAB_DRAG_HOVER_TIME 31
+#define GROUP_SCREEN_OPTION_TAB_DRAG_SPRING_K 32
+#define GROUP_SCREEN_OPTION_TAB_DRAG_FRICTION 33
+#define GROUP_SCREEN_OPTION_TAB_DRAG_Y_DISTANCE 34
+#define GROUP_SCREEN_OPTION_TAB_DRAG_SPEED_LIMIT 35
+#define GROUP_SCREEN_OPTION_SPRING_MODEL_ON_MOVE 36
+#define GROUP_SCREEN_OPTION_AUTOTAB 37
+#define GROUP_SCREEN_OPTION_DND_UNGROUP_WINDOW 38
+#define GROUP_SCREEN_OPTION_RESIZE_UNMAXIMIZE 39
+#define GROUP_SCREEN_OPTION_MINIMIZE_ALL 40
+#define GROUP_SCREEN_OPTION_SHADE_ALL 41
+#define GROUP_SCREEN_OPTION_NUM 42
+
+/*
+ * 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)
+
+/* definitions used for glow painting */
+#define WIN_REAL_X(w) (w->attrib.x - w->input.left)
+#define WIN_REAL_Y(w) (w->attrib.y - w->input.top)
+#define WIN_REAL_WIDTH(w) (w->width + 2 * w->attrib.border_width + w->input.left + w->input.right)
+#define WIN_REAL_HEIGHT(w) (w->height + 2 * w->attrib.border_width + w->input.top + w->input.bottom)
+
+#define NUM_OPTIONS(s) (sizeof ( (s)->opt) / sizeof (CompOption))
+#define N_WIN_TYPE (sizeof (groupDefaultTypes) / sizeof (groupDefaultTypes[0]))
+#define N_SEL_MODE (sizeof (groupSelectionModes) / sizeof (groupSelectionModes[0]))
+
+#define REAL_POSITION(x, s) ( (x >= 0)? x: x + (s)->hsize * (s)->width )
+#define VIEWPORT(x, s) ( ( REAL_POSITION(x, s) / (s)->width ) % (s)->hsize )
+
+#define TOP_TAB(g) ((g)->topTab->window)
+#define PREV_TOP_TAB(g) ((g)->prevTopTab->window)
+#define NEXT_TOP_TAB(g) ((g)->nextTopTab->window)
+
+#define HAS_TOP_WIN(group) (((group)->topTab) && ((group)->topTab->window))
+#define HAS_PREV_TOP_WIN(group) (((group)->prevTopTab) && ((group)->prevTopTab->window))
+
+#define IS_TOP_TAB(w, group) (HAS_TOP_WIN(group) && ((TOP_TAB(group)->id) == (w)->id))
+#define IS_PREV_TOP_TAB(w, group) (HAS_PREV_TOP_WIN(group) && ((PREV_TOP_TAB(group)->id) == (w)->id))
+
+/*
+ * Debug
+ *
+ */
+#define GROUP_DEBUG(msg, level) (groupDebugPrint(msg, level))
+
+typedef enum {
+ DEBUG_OFF = 0,
+ DEBUG_IMPORTANT,
+ DEBUG_INFO,
+ DEBUG_ALL
+} DebugLevel;
+
+/*
+ * Structs
+ *
+ */
+
+/*
+ * window states
+*/
+typedef enum {
+ WindowNormal = 0,
+ WindowMinimized,
+ WindowShaded
+} GroupWindowState;
+
+/*
+ * screengrab states
+ */
+typedef enum {
+ ScreenGrabNone = 0,
+ ScreenGrabSelect,
+ ScreenGrabTabDrag
+} GroupScreenGrabState;
+
+/*
+ * ungrouping states
+ */
+typedef enum {
+ UngroupNone = 0,
+ UngroupAll,
+ UngroupSingle
+} GroupUngroupState;
+
+/*
+ * rotation direction for change tab animation
+ */
+typedef enum {
+ RotateUncertain = 0,
+ RotateLeft,
+ RotateRight
+} ChangeTabAnimationDirection;
+
+/*
+ * structs for glow type
+ */
+typedef enum {
+ GlowTextureRectangular = 0,
+ GlowTextureRing,
+ GlowTextureNum
+} GlowTextureType;
+
+static const char *glowTextureName[] = {
+ N_("Rectangular glow"),
+ N_("Glow ring")
+};
+static const int nGlowTextureNames =
+ sizeof (glowTextureName) / sizeof (glowTextureName[0]);
+
+typedef struct _GlowTextureProperties {
+ char * textureData;
+ int textureSize;
+ int glowOffset;
+} GlowTextureProperties;
+
+static const GlowTextureProperties glowTextureProperties[GlowTextureNum] = {
+ // GlowTextureRectangular
+ {glowTexRect, 32, 21},
+ // GlowTextureRing
+ {glowTexRing, 32, 16}
+};
+
+/*
+ * structs for pending callbacks
+ */
+typedef struct _GroupPendingMoves GroupPendingMoves;
+struct _GroupPendingMoves {
+ CompWindow *w;
+ int dx;
+ int dy;
+ Bool sync;
+ GroupPendingMoves *next;
+};
+
+typedef struct _GroupPendingGrabs GroupPendingGrabs;
+struct _GroupPendingGrabs {
+ CompWindow *w;
+ int x;
+ int y;
+ unsigned int state;
+ unsigned int mask;
+ GroupPendingGrabs *next;
+};
+
+typedef struct _GroupPendingUngrabs GroupPendingUngrabs;
+struct _GroupPendingUngrabs {
+ CompWindow *w;
+ GroupPendingUngrabs *next;
+};
+
+/*
+ * pointer to display list
+ */
+int displayPrivateIndex;
+
+/*
+ * PaintState
+ */
+
+/* mask values for groupTabSetVisibility */
+#define SHOW_BAR_INSTANTLY_MASK (1 << 0)
+#define PERMANENT (1 << 1)
+
+/* mask values for tabbing animation */
+#define IS_ANIMATED (1 << 0)
+#define FINISHED_ANIMATION (1 << 1)
+#define CONSTRAINED_X (1 << 2)
+#define CONSTRAINED_Y (1 << 3)
+#define DONT_CONSTRAIN (1 << 4)
+
+typedef enum {
+ PaintOff = 0,
+ PaintFadeIn,
+ PaintFadeOut,
+ PaintOn,
+ PaintPermanentOn
+} PaintState;
+
+
+typedef struct _GroupCairoLayer {
+ unsigned char *texBuf;
+ CompTexture texture;
+ cairo_surface_t *surface;
+ cairo_t *cairo;
+
+ int texWidth, texHeight;
+
+ PaintState state;
+ int animationTime;
+} GroupCairoLayer;
+
+/*
+ * GroupTabBarSlot
+ */
+typedef struct _GroupTabBarSlot GroupTabBarSlot;
+struct _GroupTabBarSlot {
+ GroupTabBarSlot *prev;
+ GroupTabBarSlot *next;
+
+ Region region;
+
+ char *name;
+ CompWindow *window;
+
+ //for DnD animations.
+ int springX;
+ int speed;
+ float msSinceLastMove;
+};
+
+/*
+ * GroupTabBar
+ */
+typedef struct _GroupTabBar {
+ GroupTabBarSlot *slots;
+ GroupTabBarSlot *revSlots;
+ int nSlots;
+
+ GroupTabBarSlot *hoveredSlot;
+ GroupTabBarSlot *textSlot;
+
+ GroupCairoLayer *textLayer;
+ GroupCairoLayer *bgLayer;
+ GroupCairoLayer *selectionLayer;
+
+ PaintState state;
+ int timeoutHandle;
+ int animationTime;
+ Region region;
+
+ //for DnD animations.
+ int leftSpringX, rightSpringX;
+ int leftSpeed, rightSpeed;
+ float leftMsSinceLastMove, rightMsSinceLastMove;
+} GroupTabBar;
+
+/*
+ * GroupGlow
+ */
+
+typedef struct _GlowQuad {
+ BoxRec box;
+ CompMatrix matrix;
+} GlowQuad;
+
+#define GLOWQUAD_TOPLEFT 0
+#define GLOWQUAD_TOPRIGHT 1
+#define GLOWQUAD_BOTTOMLEFT 2
+#define GLOWQUAD_BOTTOMRIGHT 3
+#define GLOWQUAD_TOP 4
+#define GLOWQUAD_BOTTOM 5
+#define GLOWQUAD_LEFT 6
+#define GLOWQUAD_RIGHT 7
+#define NUM_GLOWQUADS 8
+
+/*
+ * GroupSelection
+ */
+typedef struct _GroupSelection GroupSelection;
+struct _GroupSelection {
+ GroupSelection *prev;
+ GroupSelection *next;
+
+ CompScreen *screen;
+ CompWindow **windows;
+ int nWins;
+
+ GroupTabBarSlot* topTab;
+ GroupTabBarSlot* prevTopTab;
+
+ //Those two are only for the change-tab animation, when the tab was changed again during animation.
+ //Another animation should be started again, switching for this window.
+ ChangeTabAnimationDirection nextDirection;
+ GroupTabBarSlot* nextTopTab;
+
+ GroupTabBarSlot* activateTab;
+
+ GroupTabBar *tabBar;
+
+ int changeAnimationTime;
+ int changeAnimationDirection;
+ PaintState changeState;
+ Bool changeTab;
+
+ Bool doTabbing;
+ PaintState tabbingState;
+
+ GroupUngroupState ungroupState;
+
+ Window grabWindow;
+ unsigned int grabMask;
+
+ int oldTopTabCenterX;
+ int oldTopTabCenterY;
+
+ Window inputPrevention;
+
+ GLushort color[4];
+};
+
+/*
+ * GroupDisplay structure
+ */
+typedef struct _GroupDisplay {
+ int screenPrivateIndex;
+ CompOption opt[GROUP_DISPLAY_OPTION_NUM];
+ HandleEventProc handleEvent;
+
+ GroupSelection tmpSel;
+ Bool ignoreMode;
+
+ CompTimeoutHandle timeoutHandle;
+} GroupDisplay;
+
+/*
+ * GroupScreen structure
+ */
+typedef struct _GroupScreen {
+ int windowPrivateIndex;
+ CompOption opt[GROUP_SCREEN_OPTION_NUM];
+
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowResizeNotifyProc windowResizeNotify;
+ GetOutputExtentsForWindowProc getOutputExtentsForWindow;
+ PreparePaintScreenProc preparePaintScreen;
+ PaintScreenProc paintScreen;
+ DrawWindowProc drawWindow;
+ PaintWindowProc paintWindow;
+ PaintTransformedScreenProc paintTransformedScreen;
+ DonePaintScreenProc donePaintScreen;
+ WindowGrabNotifyProc windowGrabNotify;
+ WindowUngrabNotifyProc windowUngrabNotify;
+ DamageWindowRectProc damageWindowRect;
+ WindowStateChangeNotifyProc windowStateChangeNotify;
+ GroupPendingMoves *pendingMoves;
+ GroupPendingGrabs *pendingGrabs;
+ GroupPendingUngrabs *pendingUngrabs;
+
+ GroupSelection *groups;
+
+ int wMask;
+ Bool queued;
+ Bool tabBarVisible;
+
+ GlowTextureType glowType;
+
+ GroupScreenGrabState grabState;
+ int grabIndex;
+
+ // for selection
+ Bool painted;
+ int vpX, vpY;
+ Bool isRotating;
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+
+ //For d&d
+ GroupTabBarSlot *draggedSlot;
+ int dragHoverTimeoutHandle;
+ Bool dragged;
+ int prevX, prevY; //buffer for mouse coordinates
+
+ CompTexture glowTexture;
+} GroupScreen;
+
+/*
+ * GroupWindow structure
+ */
+typedef struct _GroupWindow {
+ GroupSelection *group;
+ Bool inSelection;
+
+ // for the tab bar
+ GroupTabBarSlot *slot;
+ int oldWindowState;
+
+ // for resize notify...
+ Bool needsPosSync;
+
+ GlowQuad *glowQuads;
+
+ GroupWindowState windowState;
+
+ // for tab animation
+ Bool ungroup;
+ int animateState;
+ XPoint mainTabOffset;
+ XPoint destination;
+ XPoint orgPos;
+ float tx,ty;
+ float xVelocity, yVelocity;
+
+ int lastState;
+} GroupWindow;
+
+/*
+ * Pre-Definitions
+ *
+ */
+
+/*
+ * options.c
+ */
+void groupDisplayInitOptions(GroupDisplay * gd);
+CompOption *groupGetDisplayOptions(CompDisplay * d, int *count);
+Bool groupSetDisplayOption(CompDisplay * d, char *name, CompOptionValue * value);
+void groupScreenInitOptions(GroupScreen * gs);
+CompOption *groupGetScreenOptions(CompScreen * s, int *count);
+Bool groupSetScreenOption(CompScreen * s, char *name, CompOptionValue * value);
+
+/*
+ * group.c
+ */
+void groupGrabScreen(CompScreen * s, GroupScreenGrabState newState);
+void groupHandleEvent(CompDisplay * d, XEvent * event);
+Bool groupGroupWindows(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupUnGroupWindows(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+int groupFindWindowIndex(CompWindow *w, GroupSelection *g);
+void groupDeleteGroupWindow(CompWindow * w, Bool allowRegroup);
+void groupDeleteGroup(GroupSelection *group);
+void groupAddWindowToGroup(CompWindow * w, GroupSelection *group);
+void groupSyncWindows(GroupSelection *group);
+void groupRaiseWindows(CompWindow * top, GroupSelection *group);
+Bool groupRemoveWindow(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupCloseWindows(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupChangeColor(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupSetIgnore(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupUnsetIgnore(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+void groupWindowResizeNotify(CompWindow * w, int dx, int dy, int dwidth, int dheight, Bool preview);
+void groupWindowGrabNotify(CompWindow * w, int x, int y, unsigned int state, unsigned int mask);
+void groupWindowUngrabNotify(CompWindow * w);
+void groupWindowMoveNotify(CompWindow * w, int dx, int dy, Bool immediate);
+void groupWindowStateChangeNotify(CompWindow *w);
+void groupGetOutputExtentsForWindow(CompWindow * w, CompWindowExtents * output);
+
+Bool groupDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect);
+
+/*
+ * tab.c
+ */
+void groupHandleChanges(CompScreen* s);
+void groupHandleHoverDetection(GroupSelection *group);
+void groupHandleTabBarFade(GroupSelection *group, int msSinceLastPaint);
+void groupHandleTextFade(GroupSelection *group, int msSinceLastPaint);
+void groupDrawTabAnimation(CompScreen * s, int msSinceLastPaint);
+Bool groupUpdateTabBars(void* display);
+void groupCheckForVisibleTabBars(CompScreen *s);
+void groupGetDrawOffsetForSlot(GroupTabBarSlot *slot, int *hoffset, int *voffset);
+void groupTabSetVisibility(GroupSelection *group, Bool visible, unsigned int mask);
+Bool groupGetCurrentMousePosition(CompScreen *s, int *x, int *y);
+void groupClearCairoLayer(GroupCairoLayer *layer);
+void groupDestroyCairoLayer(CompScreen *s, GroupCairoLayer *layer);
+GroupCairoLayer *groupRebuildCairoLayer(CompScreen *s, GroupCairoLayer *layer, int width, int height);
+GroupCairoLayer *groupCreateCairoLayer(CompScreen *s, int width, int height);
+void groupRecalcTabBarPos(GroupSelection *group, int middleX, int minX1, int maxX2);
+void groupInsertTabBarSlotAfter(GroupTabBar *bar, GroupTabBarSlot *slot, GroupTabBarSlot *prevSlot);
+void groupInsertTabBarSlotBefore(GroupTabBar *bar, GroupTabBarSlot *slot, GroupTabBarSlot *nextSlot);
+void groupInsertTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot);
+void groupUnhookTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot, Bool temporary);
+void groupDeleteTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot);
+void groupCreateSlot(GroupSelection *group, CompWindow *w);
+void groupApplyForces(CompScreen *s, GroupTabBar *bar, GroupTabBarSlot* draggedSlot);
+void groupApplySpeeds(CompScreen* s, GroupTabBar* bar, int msSinceLastRepaint);
+void groupInitTabBar(GroupSelection *group, CompWindow* topTab);
+void groupDeleteTabBar(GroupSelection *group);
+void groupStartTabbingAnimation(GroupSelection *group, Bool tab);
+void groupTabGroup(CompWindow * main);
+void groupUntabGroup(GroupSelection *group);
+Bool groupInitTab(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupChangeTab(GroupTabBarSlot* topTab, ChangeTabAnimationDirection direction);
+Bool groupChangeTabLeft(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupChangeTabRight(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+void groupUpdateInputPreventionWindow(GroupSelection* group);
+void groupSwitchTopTabInput(GroupSelection *group, Bool enable);
+void groupCreateInputPreventionWindow(GroupSelection* group);
+void groupDestroyInputPreventionWindow(GroupSelection* group);
+Region groupGetClippingRegion(CompWindow *w);
+
+/*
+ * paint.c
+ */
+void groupRecomputeGlow(CompScreen *s);
+void groupComputeGlowQuads(CompWindow *w, CompMatrix *matrix);
+void groupRenderTopTabHighlight(GroupSelection *group);
+void groupRenderTabBarBackground(GroupSelection *group);
+void groupRenderWindowTitle(GroupSelection *group);
+void groupPaintThumb(GroupSelection *group, GroupTabBarSlot *slot, int targetOpacity);
+void groupPaintTabBar(GroupSelection * group, const WindowPaintAttrib *wAttrib, Region clipRegion);
+void groupPreparePaintScreen(CompScreen * s, int msSinceLastPaint);
+Bool groupPaintScreen(CompScreen * s, const ScreenPaintAttrib * sAttrib, Region region, int output, unsigned int mask);
+void groupPaintTransformedScreen(CompScreen * s, const ScreenPaintAttrib * sa, Region region, int output, unsigned int mask);
+void groupDonePaintScreen(CompScreen * s);
+Bool groupDrawWindow(CompWindow * w, const WindowPaintAttrib * attrib, Region region, unsigned int mask);
+Bool groupPaintWindow(CompWindow * w, const WindowPaintAttrib * attrib, Region region, unsigned int mask);
+
+/*
+ * init.c
+ */
+Bool groupInitDisplay(CompPlugin * p, CompDisplay * d);
+void groupFiniDisplay(CompPlugin * p, CompDisplay * d);
+Bool groupInitScreen(CompPlugin * p, CompScreen * s);
+void groupFiniScreen(CompPlugin * p, CompScreen * s);
+Bool groupInitWindow(CompPlugin * p, CompWindow * w);
+void groupFiniWindow(CompPlugin * p, CompWindow * w);
+Bool groupInit(CompPlugin * p);
+void groupFini(CompPlugin * p);
+
+/*
+ * queues.c
+ */
+void groupEnqueueMoveNotify (CompWindow *w, int dx, int dy, Bool sync);
+void groupDequeueMoveNotifies (CompScreen *s);
+void groupEnqueueGrabNotify (CompWindow *w, int x, int y, unsigned int state, unsigned int mask);
+void groupDequeueGrabNotifies (CompScreen *s);
+void groupEnqueueUngrabNotify (CompWindow *w);
+void groupDequeueUngrabNotifies (CompScreen *s);
+
+/*
+ * selection.c
+ */
+CompWindow **groupFindWindowsInRegion(CompScreen * s, Region reg, int *c);
+void groupDeleteSelectionWindow(CompDisplay * d, CompWindow * w);
+void groupAddWindowToSelection(CompDisplay * d, CompWindow * w);
+void groupSelectWindow(CompDisplay * d, CompWindow * w);
+Bool groupSelectSingle(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupSelect(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+Bool groupSelectTerminate(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption);
+void groupDamageSelectionRect(CompScreen* s, int xRoot, int yRoot);
+
+/*
+ * debug.c
+ */
+void groupDebugPrintMsg(char* msg, DebugLevel level);
+void groupDebugPrintGroup(GroupSelection *group);
+void groupDebugPrintScreen(GroupScreen *gs);
+void groupDebugPrintWindow(GroupWindow *gw);
+void groupDebugPrintTabBar(GroupTabBar *bar);
+void groupDebugPrintTabBarSlot(GroupTabBarSlot *slot);
+
+#endif
diff --git a/beryl-plugins/src/group/group_glow.h b/beryl-plugins/src/group/group_glow.h
new file mode 100644
index 0000000..2a9571b
--- /dev/null
+++ b/beryl-plugins/src/group/group_glow.h
@@ -0,0 +1,400 @@
+#ifndef _GROUP_GLOW_H
+#define _GROUP_GLOW_H
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * group_glow.h
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * 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.
+ *
+ **/
+
+/*
+ * glowTex
+ */
+
+static char glowTexRect[4096] = {
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377"
+ "\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377"
+ "\377\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\6\377\377\377\6\377\377"
+ "\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377"
+ "\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\6"
+ "\377\377\377\14\377\377\377\14\377\377\377\14\377\377\377\14\377\377\377"
+ "\14\377\377\377\14\377\377\377\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\6\377\377\377\6\377\377\377\14"
+ "\377\377\377\14\377\377\377\22\377\377\377\22\377\377\377\22\377\377\377"
+ "\27\377\377\377\27\377\377\377\27\377\377\377\27\377\377\377\27\377\377\377"
+ "\27\377\377\377\27\377\377\377\27\377\377\377\27\377\377\377\27\377\377\377"
+ "\35\377\377\377\35\377\377\377\35\377\377\377\35\377\377\377\35\377\377\377"
+ "\35\377\377\377\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377"
+ "\377\6\377\377\377\6\377\377\377\14\377\377\377\22\377\377\377\27\377\377"
+ "\377\27\377\377\377\35\377\377\377#\377\377\377'\377\377\377'\377\377\377"
+ "+\377\377\377+\377\377\377+\377\377\377+\377\377\377+\377\377\377+\377\377"
+ "\377+\377\377\377+\377\377\377+\377\377\3771\377\377\3771\377\377\3771\377"
+ "\377\3771\377\377\3771\377\377\3771\377\377\3771\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\22\377\377\377"
+ "\27\377\377\377\35\377\377\377#\377\377\377+\377\377\3771\377\377\3776\377"
+ "\377\377<\377\377\377>\377\377\377C\377\377\377I\377\377\377I\377\377\377"
+ "I\377\377\377I\377\377\377I\377\377\377I\377\377\377I\377\377\377I\377\377"
+ "\377L\377\377\377L\377\377\377L\377\377\377L\377\377\377L\377\377\377L\377"
+ "\377\377L\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377"
+ "\377\377\22\377\377\377\27\377\377\377#\377\377\377+\377\377\3776\377\377"
+ "\377C\377\377\377L\377\377\377U\377\377\377]\377\377\377`\377\377\377d\377"
+ "\377\377h\377\377\377k\377\377\377k\377\377\377k\377\377\377k\377\377\377"
+ "k\377\377\377k\377\377\377k\377\377\377p\377\377\377p\377\377\377p\377\377"
+ "\377p\377\377\377p\377\377\377p\377\377\377p\0\0\0\0\0\0\0\0\0\0\0\0\377"
+ "\377\377\6\377\377\377\14\377\377\377\22\314\314\314\35\377\377\377'\377"
+ "\377\3771\377\377\377>\357\357\357P\377\377\377]\363\363\363k\365\365\365"
+ "v\365\365\365|\377\377\377\202\367\367\367\210\367\367\367\214\367\367\367"
+ "\216\367\367\367\221\367\367\367\221\367\367\367\221\367\367\367\221\367"
+ "\367\367\221\367\367\367\221\367\367\367\224\367\367\367\224\367\367\367"
+ "\224\367\367\367\224\367\367\367\224\367\367\367\224\367\367\367\224\0\0"
+ "\0\0\0\0\0\0\377\377\377\6\377\377\377\6\377\377\377\22\377\377\377\27\377"
+ "\377\377'\377\377\3776\377\377\377I\377\377\377Y\377\377\377k\376\376\376"
+ "y\377\377\377\210\377\377\377\224\377\377\377\235\377\377\377\245\377\377"
+ "\377\253\377\377\377\255\377\377\377\262\377\377\377\262\377\377\377\263"
+ "\377\377\377\263\377\377\377\263\377\377\377\263\377\377\377\263\377\377"
+ "\377\266\377\377\377\266\377\377\377\266\377\377\377\266\377\377\377\266"
+ "\377\377\377\266\377\377\377\266\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377"
+ "\14\377\377\377\27\377\377\377#\377\377\3771\377\377\377I\377\377\377]\377"
+ "\377\377r\377\377\377\205\377\377\377\231\377\377\377\247\377\377\377\263"
+ "\377\377\377\275\377\377\377\304\377\377\377\310\377\377\377\313\377\377"
+ "\377\316\377\377\377\320\377\377\377\320\377\377\377\320\377\377\377\320"
+ "\377\377\377\320\377\377\377\320\377\377\377\322\377\377\377\322\377\377"
+ "\377\322\377\377\377\322\377\377\377\322\377\377\377\322\377\377\377\322"
+ "\0\0\0\0\377\377\377\6\377\377\377\6\377\377\377\22\377\377\377\35\377\377"
+ "\377+\377\377\377>\377\377\377Y\377\377\377r\377\377\377\210\376\376\376"
+ "\237\377\377\377\262\377\377\377\302\377\377\377\313\377\377\377\324\377"
+ "\377\377\332\376\376\376\336\377\377\377\341\377\377\377\342\377\377\377"
+ "\344\377\377\377\344\377\377\377\344\377\377\377\344\377\377\377\344\377"
+ "\377\377\344\377\377\377\345\377\377\377\345\377\377\377\345\377\377\377"
+ "\345\377\377\377\345\377\377\377\345\377\377\377\345\0\0\0\0\377\377\377"
+ "\6\377\377\377\14\377\377\377\27\377\377\377#\377\377\3776\377\377\377P\377"
+ "\377\377k\377\377\377\205\376\376\376\237\372\372\372\266\377\377\377\307"
+ "\373\373\373\325\373\373\373\337\374\374\374\345\374\374\374\352\374\374"
+ "\374\355\374\374\374\357\374\374\374\360\374\374\374\361\374\374\374\361"
+ "\374\374\374\362\374\374\374\362\374\374\374\362\374\374\374\362\374\374"
+ "\374\362\374\374\374\362\374\374\374\362\374\374\374\362\374\374\374\362"
+ "\374\374\374\362\374\374\374\362\0\0\0\0\377\377\377\6\377\377\377\14\377"
+ "\377\377\35\377\377\377+\377\377\377C\377\377\377]\377\377\377|\377\377\377"
+ "\231\377\377\377\263\377\377\377\307\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377"
+ "\377\377\6\377\377\377\22\324\324\324#\377\377\3771\377\377\377L\363\363"
+ "\363k\377\377\377\210\377\377\377\247\377\377\377\302\377\377\377\325\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\22\377\377"
+ "\377#\377\377\377<\377\377\377U\377\377\377v\377\377\377\226\377\377\377"
+ "\263\377\377\377\315\377\377\377\337\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377"
+ "\6\377\377\377\14\377\377\377\27\377\377\377'\377\377\377>\377\377\377]\377"
+ "\377\377|\370\370\370\237\377\377\377\275\373\373\373\325\377\377\377\345"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\27\377"
+ "\377\377+\377\377\377C\377\377\377`\377\377\377\202\377\377\377\247\377\377"
+ "\377\304\377\377\377\332\377\377\377\352\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377"
+ "\377\6\377\377\377\14\377\377\377\27\377\377\377+\377\377\377C\377\377\377"
+ "d\377\377\377\210\377\377\377\253\377\377\377\310\376\376\376\336\374\374"
+ "\374\355\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377"
+ "\35\377\377\377+\377\377\377I\377\377\377h\377\377\377\214\377\377\377\260"
+ "\377\377\377\313\374\374\374\342\374\374\374\357\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\377\377\377\6\377\377\377\14\377\377\377\35\342\342\3421\377\377\377I\377"
+ "\377\377k\377\377\377\216\377\377\377\262\377\377\377\316\374\374\374\344"
+ "\377\377\377\360\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377"
+ "\377\377\35\377\377\3771\377\377\377L\377\377\377k\377\377\377\221\377\377"
+ "\377\263\377\377\377\320\377\377\377\344\377\377\377\361\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377\377\3771\377\377"
+ "\377L\377\377\377k\377\377\377\221\377\377\377\263\377\377\377\320\377\377"
+ "\377\344\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377"
+ "\14\377\377\377\35\377\377\3771\377\377\377L\364\364\364p\377\377\377\221"
+ "\372\372\372\266\377\377\377\320\374\374\374\345\377\377\377\362\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377\377\377"
+ "1\377\377\377L\377\377\377p\377\377\377\221\377\377\377\266\373\373\373\322"
+ "\377\377\377\345\377\377\377\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377"
+ "\377\377\14\377\377\377\35\377\377\3771\377\377\377L\377\377\377p\377\377"
+ "\377\221\377\377\377\266\373\373\373\322\377\377\377\345\377\377\377\362"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377"
+ "\377\3771\377\377\377L\377\377\377p\377\377\377\221\377\377\377\266\373\373"
+ "\373\322\377\377\377\345\377\377\377\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377"
+ "\377\6\377\377\377\14\377\377\377\35\377\377\3771\377\377\377L\377\377\377"
+ "p\367\367\367\224\377\377\377\266\377\377\377\322\377\377\377\345\374\374"
+ "\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377"
+ "\35\377\377\3771\377\377\377L\377\377\377p\367\367\367\224\377\377\377\266"
+ "\377\377\377\322\377\377\377\345\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\377\377\377\6\377\377\377\14\377\377\377\35\377\377\3771\377\377\377L\377"
+ "\377\377p\367\367\367\224\377\377\377\266\377\377\377\322\377\377\377\345"
+ "\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377"
+ "\377\377\35\377\377\3771\377\377\377L\377\377\377p\367\367\367\224\377\377"
+ "\377\266\377\377\377\322\377\377\377\345\374\374\374\362\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377\377\3771\377\377"
+ "\377L\377\377\377p\367\367\367\224\377\377\377\266\377\377\377\322\377\377"
+ "\377\345\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377"
+ "\14\377\377\377\35\377\377\3771\377\377\377L\377\377\377p\367\367\367\224"
+ "\377\377\377\266\377\377\377\322\377\377\377\345\374\374\374\362\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\377\377\377\6\377\377\377\14\377\377\377\35\377\377\377"
+ "1\377\377\377L\377\377\377p\367\367\367\224\377\377\377\266\377\377\377\322"
+ "\377\377\377\345\374\374\374\362\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
+};
+
+static char glowTexRing [4096]= {
+"\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\201\202\202\1\205\205\205\2\206"
+ "\206\206\4\210\210\210\6\211\211\211\10\212\212\212\13\213\213\213\14\213"
+ "\213\213\15\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\204"
+ "\203\203\0\206\206\206\1\207\207\207\3\210\211\211\10\213\213\213\16\216"
+ "\216\216\25\220\220\220\33\222\223\222\40\224\224\224%\225\226\225(\227\227"
+ "\227*\227\227\227+\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\202\203\203\0\206\206"
+ "\205\3\211\211\211\10\214\214\214\22\220\220\220\34\225\225\225&\230\231"
+ "\230.\234\234\2347\237\237\237=\242\242\242C\244\244\244G\245\245\246J\246"
+ "\246\246L\247\247\247M\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\205\205\204\0\207\207\207\3\213\213\213\14\217"
+ "\217\217\27\224\224\224$\232\232\2310\236\236\236:\244\244\244E\247\250\250"
+ "M\254\253\254V\257\256\257\\\261\261\261b\264\264\264g\265\265\265j\266\266"
+ "\266l\267\267\267m\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\205\205\205\1\212\212\212\12\217\217\217\30\225\225\225'\233\234"
+ "\2335\242\242\242B\250\250\250O\255\255\255Z\263\263\263d\267\267\267m\273"
+ "\273\273u\276\276\276|\301\301\301\203\304\304\304\207\305\305\305\213\307"
+ "\307\307\215\307\307\307\217\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\205\205\205"
+ "\1\211\211\211\7\215\215\216\24\225\225\225&\234\234\2345\243\243\243F\253"
+ "\252\252S\261\261\261a\267\270\267n\275\275\275y\303\303\303\204\307\307"
+ "\307\216\314\313\313\226\317\317\317\235\322\322\322\244\324\324\324\251"
+ "\326\326\326\254\330\330\330\257\330\330\330\260\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\205"
+ "\205\205\1\211\211\211\13\221\221\221\35\230\230\230.\241\241\241A\251\251"
+ "\251P\261\260\261a\270\270\270p\277\277\277~\306\306\305\213\313\313\313"
+ "\226\321\321\321\242\325\325\325\253\332\332\332\264\336\336\336\273\341"
+ "\341\341\302\344\344\344\310\346\345\345\313\347\347\347\316\347\347\350"
+ "\317\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\205\204\204\0\207\207\207\4\215\215\215\22\224\224\224%\236\236\236:\246"
+ "\246\246K\260\260\260_\270\267\270o\300\300\300\200\310\310\310\217\317\317"
+ "\317\236\326\326\326\254\334\334\334\267\342\342\342\304\346\346\346\315"
+ "\353\353\353\327\357\357\357\336\362\362\362\345\365\365\365\352\367\367"
+ "\366\355\370\367\367\357\367\370\367\360\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\206\205\205\0\210\207\210\4\215\215\215\24\227\226\226"
+ ")\237\237\237=\252\251\252R\262\262\262d\274\274\274x\305\305\305\211\315"
+ "\315\315\232\325\325\325\252\334\334\334\271\344\343\343\310\351\352\351"
+ "\323\360\360\360\340\364\364\364\351\371\371\371\361\372\372\372\364\372"
+ "\372\373\364\372\371\371\362\371\371\371\357\371\370\370\355\370\370\370"
+ "\354\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\205\205\205\0\210\210\210\5"
+ "\217\217\217\27\230\230\230.\243\243\243D\255\255\255X\267\267\267n\300\300"
+ "\300\200\313\313\313\226\323\323\323\247\334\334\334\271\344\345\344\311"
+ "\354\354\354\331\363\363\363\347\370\370\370\361\372\373\373\364\372\372"
+ "\372\361\366\367\367\350\364\364\364\340\361\361\361\331\357\357\357\323"
+ "\355\355\355\316\354\354\354\313\353\353\354\312\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\204\204\204\0\210\210\207\4\217\217\217\27\230\230\230-\243\243\243D\256"
+ "\256\256[\271\271\271p\303\303\304\206\315\315\315\231\330\330\327\257\340"
+ "\340\340\301\352\352\352\324\362\362\362\343\370\370\370\361\372\372\372"
+ "\363\370\370\370\354\364\364\363\340\360\360\360\325\353\354\354\312\351"
+ "\351\351\302\346\346\345\272\343\343\343\264\342\342\342\260\341\340\341"
+ "\255\340\340\340\254\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\207\207\207\3\215\215\215\24"
+ "\230\230\230-\243\243\243D\257\257\257\\\272\272\272t\305\305\305\212\320"
+ "\320\320\240\332\332\332\263\345\345\345\312\356\356\356\334\367\367\367"
+ "\356\372\372\372\364\370\370\370\354\362\362\362\334\355\355\355\317\350"
+ "\350\350\301\344\344\344\266\337\340\337\252\335\334\335\242\331\331\331"
+ "\231\327\327\327\223\325\325\325\217\324\324\324\213\323\323\323\212\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\205\205\205\1\214\214\214\21\226\226\226(\242\242\242C\256\256\256"
+ "[\272\272\272t\306\306\306\213\321\321\321\241\334\334\334\271\346\346\346"
+ "\315\362\362\362\343\371\371\371\361\371\371\371\360\364\364\364\341\356"
+ "\356\356\320\350\350\350\300\343\342\342\262\335\335\335\243\331\331\331"
+ "\227\324\324\324\213\321\321\321\202\316\315\316z\313\313\313s\312\312\312"
+ "n\310\310\310k\310\310\310i\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\205\205\205\1\211\211\211\12\224\224\224$\237\237"
+ "\237<\254\254\254X\270\270\270o\304\305\304\211\321\321\321\241\334\334\334"
+ "\270\350\350\350\320\362\362\362\345\372\372\373\364\370\370\370\355\361"
+ "\361\361\332\352\352\353\307\344\344\343\265\335\335\335\244\330\330\330"
+ "\225\322\322\322\206\315\316\315z\311\311\311m\305\305\305d\302\302\302Z"
+ "\300\300\277T\276\275\276O\274\274\274K\274\274\274I\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\211\211\211\6\220\220\220"
+ "\34\235\235\2358\251\251\251Q\267\267\266m\303\303\303\206\320\320\320\240"
+ "\334\334\334\270\350\350\350\320\364\364\364\350\372\373\372\365\366\366"
+ "\366\347\357\357\357\323\347\347\347\276\340\340\340\253\331\331\331\230"
+ "\322\322\322\206\315\315\314w\306\307\307g\302\302\302Z\275\275\275L\271"
+ "\272\271C\266\266\266:\264\264\2632\262\262\262,\260\260\260)\260\260\260"
+ "'\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\204\204\204\1\215"
+ "\215\215\22\227\230\230,\245\245\245J\262\262\262c\300\300\300\177\314\314"
+ "\314\230\332\332\331\263\346\346\346\314\362\362\362\344\372\372\372\364"
+ "\367\367\367\352\356\356\356\321\347\346\346\275\336\336\336\247\327\327"
+ "\327\223\320\320\320\177\311\311\311m\303\303\303]\275\274\275L\270\270\270"
+ ">\263\263\2630\257\257\257&\254\254\254\34\252\252\252\25\250\250\250\20"
+ "\250\247\250\14\250\247\250\12\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\204\204\204\0\211\211\211\11\224\224\224$\240\240\240?\257\257\257]\274"
+ "\274\274w\312\312\312\224\327\327\327\256\344\344\344\311\362\361\362\343"
+ "\372\373\372\364\366\366\367\347\356\357\356\321\344\344\344\267\335\335"
+ "\335\242\324\324\324\213\314\314\314v\305\305\305b\276\276\276O\270\270\270"
+ ">\261\261\261,\255\254\255\36\251\251\251\20\246\246\246\7\243\243\243\2"
+ "\241\241\241\1\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\207\206\207\3\216\215\216\25\233\233\2333\247\247"
+ "\250N\267\267\267m\303\304\304\207\323\323\323\245\337\340\337\277\355\355"
+ "\355\333\371\371\371\361\371\370\371\355\357\357\357\324\347\347\346\275"
+ "\335\335\335\242\325\325\325\215\313\314\313u\304\304\304_\274\274\274K\265"
+ "\265\2656\257\257\257$\251\252\251\22\246\246\246\7\245\244\244\1\243\243"
+ "\243\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\202\203\202\0\212\212\212\12\224\224\224%"
+ "\242\242\242C\260\260\260_\277\277\277~\314\314\314\230\334\334\333\267\351"
+ "\351\351\322\366\366\366\355\372\372\372\361\362\362\362\332\350\347\347"
+ "\277\336\336\336\247\324\324\324\214\314\314\314u\303\303\303\\\272\272\273"
+ "F\263\263\2630\254\254\254\33\247\247\247\12\243\243\243\1\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\206\205\205\2\216\216"
+ "\216\25\232\232\2322\252\251\251Q\267\267\267m\307\307\307\215\324\324\324"
+ "\250\344\344\343\307\361\361\361\342\372\372\372\364\365\365\365\343\353"
+ "\353\353\310\340\340\340\254\327\327\327\224\314\314\315w\304\304\304`\273"
+ "\272\273F\263\263\263/\253\253\253\31\247\247\247\7\244\244\244\1\242\242"
+ "\242\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\203\202\202\0"
+ "\210\210\210\7\223\223\223!\240\240\240?\260\260\260^\276\275\276{\316\315"
+ "\316\233\333\333\333\267\353\353\353\326\367\367\370\357\370\370\370\356"
+ "\357\357\357\322\344\344\344\267\332\331\331\232\320\320\320\201\305\305"
+ "\305c\274\274\274L\263\263\2631\254\253\254\31\246\246\246\6\243\243\243"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\206\206\206\1\214\214\214\17\230\230\230-\247\246\246K\266\266"
+ "\266k\304\304\304\210\325\324\324\251\342\342\342\305\362\362\362\345\372"
+ "\372\372\364\363\363\363\337\351\351\351\302\336\336\336\246\323\323\323"
+ "\210\311\311\311n\276\276\276P\266\266\2658\254\254\254\34\247\247\247\7"
+ "\243\243\243\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\206\207\207\2\217\217\217\30\235\235\235"
+ "7\254\254\254V\273\273\273v\312\312\312\223\332\332\332\264\350\350\350\321"
+ "\367\370\370\360\371\371\370\356\356\356\356\321\343\343\343\264\331\331"
+ "\331\227\315\315\315y\304\304\304^\270\270\270@\257\257\257&\247\247\247"
+ "\13\244\244\244\1\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\210\210\210\6\223\223"
+ "\223#\242\242\242B\261\261\261a\301\301\301\201\317\317\317\237\340\340\340"
+ "\300\356\356\356\335\372\372\372\365\365\365\365\343\351\351\351\304\336"
+ "\336\336\245\323\323\323\210\307\307\307i\275\276\276N\262\262\262.\252\252"
+ "\252\24\244\243\244\1\242\242\242\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\202\202\202\0\212"
+ "\212\212\13\226\226\227*\246\246\246J\265\265\265i\305\305\305\212\324\324"
+ "\324\247\345\345\345\312\363\363\363\346\372\372\372\363\361\361\361\331"
+ "\345\345\345\271\332\332\332\232\316\316\316}\303\303\303]\271\271\271A\255"
+ "\255\255\40\247\247\247\10\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\203\203\203\1\214\214\214\21\232\232\2322\252\251\252R\271\271\271r\311"
+ "\311\312\222\330\330\331\261\351\351\351\323\367\367\367\357\370\370\370"
+ "\354\355\355\355\316\341\341\341\256\325\325\325\217\312\312\312p\276\276"
+ "\276P\264\264\2643\251\251\251\22\245\245\245\2\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\205\205\205\2\217\217\217\26\235\235\2359\255\255"
+ "\255X\274\274\274x\315\315\315\231\334\334\334\270\355\355\355\332\372\371"
+ "\372\363\365\365\365\344\352\352\352\306\336\336\336\245\322\322\322\206"
+ "\306\307\307g\273\273\273F\260\260\260)\246\246\246\12\242\242\243\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\207\207\207\4\221\221\220\33"
+ "\240\240\237>\257\257\257^\277\277\277~\320\320\320\240\337\337\337\276\360"
+ "\360\360\341\372\372\372\364\363\363\363\335\347\347\347\276\333\333\333"
+ "\236\317\317\317}\303\304\304^\267\267\267=\255\255\255\40\244\244\244\4"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\211\210\210"
+ "\5\222\222\222\40\242\242\242B\262\262\262b\302\301\302\203\322\322\322\244"
+ "\341\341\341\303\363\363\363\346\372\372\372\363\360\360\360\327\345\345"
+ "\345\271\330\330\330\227\314\315\315w\301\301\301X\265\265\2657\253\253\253"
+ "\31\242\242\242\1\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\210\211\210\10\224\223\223#\243\243\243E\263\263\263e\303\303\303"
+ "\206\324\323\324\250\343\343\343\307\365\364\364\351\372\372\372\362\357"
+ "\357\357\323\344\344\343\264\327\327\327\223\313\313\313r\277\277\277S\263"
+ "\263\2631\252\252\252\23\241\241\241\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\211\211\211\11\225\224\224%\244\244\244G\264"
+ "\264\264g\304\304\304\210\325\325\325\252\345\344\344\311\366\366\366\353"
+ "\372\372\372\361\356\356\356\320\342\342\343\261\326\326\326\220\312\312"
+ "\312p\276\276\276P\262\262\262-\251\251\251\17\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\211\211\211\11\225\225"
+ "\225&\244\245\244H\265\265\265i\305\305\305\211\325\326\325\253\345\345\345"
+ "\312\366\366\366\354\371\371\371\360\355\355\355\317\342\342\342\260\325"
+ "\325\325\217\312\311\312n\276\276\276N\262\262\262,\250\250\250\16\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+};
+
+#endif
diff --git a/beryl-plugins/src/group/init.c b/beryl-plugins/src/group/init.c
new file mode 100644
index 0000000..e7eb08c
--- /dev/null
+++ b/beryl-plugins/src/group/init.c
@@ -0,0 +1,390 @@
+#include "group.h"
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * init.c
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ **/
+
+#define UPDATE_TAB_BAR_INTERVAL 200
+
+/*
+ * groupInitDisplay
+ *
+ */
+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->tmpSel.windows = NULL;
+ gd->tmpSel.nWins = 0;
+
+ //gd->revGroups = NULL;
+
+ gd->ignoreMode = FALSE;
+
+ groupDisplayInitOptions(gd);
+
+ WRAP(gd, d, handleEvent, groupHandleEvent);
+
+ gd->timeoutHandle = compAddTimeout (UPDATE_TAB_BAR_INTERVAL, groupUpdateTabBars, d);
+
+ d->privates[displayPrivateIndex].ptr = gd;
+
+ return TRUE;
+}
+
+/*
+ * groupFiniDisplay
+ *
+ */
+void groupFiniDisplay(CompPlugin * p, CompDisplay * d)
+{
+ GROUP_DISPLAY(d);
+
+ freeScreenPrivateIndex(d, gd->screenPrivateIndex);
+
+ UNWRAP(gd, d, handleEvent);
+
+ compRemoveTimeout(gd->timeoutHandle);
+
+ free(gd);
+}
+
+/*
+ * groupInitScreen
+ *
+ */
+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);
+
+ gs->wMask=compWindowTypeMaskFromStringList(&gs->opt[GROUP_SCREEN_OPTION_TYPES].value);
+
+ 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_REMOVING].value.action);
+ addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CLOSING].value.action);
+ addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_COLOR].value.action);
+ addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_TABMODE].value.action);
+ addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT].value.action);
+ addScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT].value.action);
+
+ WRAP(gs, s, windowMoveNotify, groupWindowMoveNotify);
+ WRAP(gs, s, windowResizeNotify, groupWindowResizeNotify);
+ WRAP(gs, s, getOutputExtentsForWindow, groupGetOutputExtentsForWindow);
+ WRAP(gs, s, preparePaintScreen, groupPreparePaintScreen);
+ WRAP(gs, s, paintScreen, groupPaintScreen);
+ WRAP(gs, s, drawWindow, groupDrawWindow);
+ WRAP(gs, s, paintWindow, groupPaintWindow);
+ WRAP(gs, s, paintTransformedScreen, groupPaintTransformedScreen);
+ WRAP(gs, s, donePaintScreen, groupDonePaintScreen);
+ WRAP(gs, s, windowGrabNotify, groupWindowGrabNotify);
+ WRAP(gs, s, windowUngrabNotify, groupWindowUngrabNotify);
+ WRAP(gs,s,damageWindowRect,groupDamageWindowRect);
+ WRAP(gs,s,windowStateChangeNotify,groupWindowStateChangeNotify);
+
+ s->privates[gd->screenPrivateIndex].ptr = gs;
+
+ gs->groups = NULL;
+
+ gs->grabIndex = 0;
+ gs->grabState = ScreenGrabNone;
+ gs->queued = FALSE;
+ gs->tabBarVisible = FALSE;
+
+ gs->pendingMoves = NULL;
+ gs->pendingGrabs = NULL;
+ gs->pendingUngrabs = NULL;
+
+ gs->draggedSlot = NULL;
+ gs->dragged = FALSE;
+ gs->dragHoverTimeoutHandle = 0;
+ gs->prevX = 0;
+ gs->prevY = 0;
+
+ gs->isRotating = FALSE;
+
+ /* gs->glowType is already initialized in groupScreenInitOptions */
+ initTexture (s, &gs->glowTexture);
+
+ RGBAimageToTexture (s, &gs->glowTexture,
+ glowTextureProperties[gs->glowType].textureData,
+ glowTextureProperties[gs->glowType].textureSize,
+ glowTextureProperties[gs->glowType].textureSize);
+
+ return TRUE;
+}
+
+/*
+ * groupFiniScreen
+ *
+ */
+void groupFiniScreen(CompPlugin * p, CompScreen * s)
+{
+ GROUP_SCREEN(s);
+ GROUP_DISPLAY(s->display);
+
+ if (gs->groups) {
+ GroupSelection *group, *next_group;
+ for(group = gs->groups; group;)
+ {
+ if (group->tabBar) {
+ GroupTabBarSlot *slot, *next_slot;
+ for (slot = group->tabBar->slots; slot;) {
+
+ if (slot->region)
+ XDestroyRegion(slot->region);
+ if (slot->name)
+ free(slot->name);
+
+ next_slot = slot->next;
+ free(slot);
+ slot = next_slot;
+ }
+
+ groupDestroyCairoLayer(group->screen, group->tabBar->textLayer);
+ groupDestroyCairoLayer(group->screen, group->tabBar->bgLayer);
+ groupDestroyCairoLayer(group->screen, group->tabBar->selectionLayer);
+
+ if (group->inputPrevention)
+ XDestroyWindow(s->display->display, group->inputPrevention);
+
+ if (group->tabBar->region)
+ XDestroyRegion(group->tabBar->region);
+
+ free(group->tabBar);
+ }
+
+ next_group = group->next;
+ free(group);
+ group = next_group;
+ }
+ }
+
+ 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_REMOVING].value.action);
+ removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CLOSING].value.action);
+ removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_COLOR].value.action);
+ removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_TABMODE].value.action);
+ removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT].value.action);
+ removeScreenAction(s, &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT].value.action);
+
+ UNWRAP(gs, s, windowMoveNotify);
+ UNWRAP(gs, s, windowResizeNotify);
+ UNWRAP(gs, s, getOutputExtentsForWindow);
+ UNWRAP(gs, s, preparePaintScreen);
+ UNWRAP(gs, s, paintScreen);
+ UNWRAP(gs, s, drawWindow);
+ UNWRAP(gs, s, paintWindow);
+ UNWRAP(gs, s, paintTransformedScreen);
+ UNWRAP(gs, s, donePaintScreen);
+ UNWRAP(gs, s, windowGrabNotify);
+ UNWRAP(gs, s, windowUngrabNotify);
+ UNWRAP(gs,s,damageWindowRect);
+ UNWRAP(gs,s,windowStateChangeNotify);
+
+ finiTexture (s, &gs->glowTexture);
+ free(gs);
+}
+
+/*
+ * groupInitWindow
+ *
+ */
+Bool groupInitWindow(CompPlugin * p, CompWindow * w)
+{
+ GroupWindow *gw;
+ GROUP_SCREEN(w->screen);
+
+ gw = malloc(sizeof(GroupWindow));
+ if (!gw)
+ return FALSE;
+
+ gw->group = NULL;
+ gw->inSelection = FALSE;
+
+ gw->needsPosSync = FALSE;
+
+ // for tab
+ gw->oldWindowState = getWindowState(w->screen->display, w->id);
+ gw->animateState = 0;
+ gw->ungroup = FALSE;
+ gw->slot = NULL;
+ gw->tx = gw->ty = 0;
+ gw->xVelocity = gw->yVelocity = 0;
+ gw->orgPos.x = 0;
+ gw->orgPos.y = 0;
+ gw->mainTabOffset.x = 0;
+ gw->mainTabOffset.y = 0;
+ gw->destination.x = 0;
+ gw->destination.y = 0;
+
+ if (w->minimized)
+ gw->windowState = WindowMinimized;
+ else if (w->shaded)
+ gw->windowState = WindowShaded;
+ else
+ gw->windowState = WindowNormal;
+
+ gw->lastState = w->state;
+
+ //gw->offscreen = FALSE;
+ w->privates[gs->windowPrivateIndex].ptr = gw;
+
+ gw->glowQuads = NULL;
+ groupComputeGlowQuads (w, &gs->glowTexture.matrix);
+
+ return TRUE;
+}
+
+/*
+ * groupFiniWindow
+ *
+ */
+void groupFiniWindow(CompPlugin * p, CompWindow * w)
+{
+ GROUP_WINDOW(w);
+
+ /* TODO: this needs more work; positioning is not correct
+ at the moment - but it's way better than leaving the
+ window offscreen */
+ if (gw->group && gw->group->tabBar && !IS_TOP_TAB(w, gw->group))
+ moveWindowOnscreen(w);
+
+ if (gw->glowQuads)
+ free (gw->glowQuads);
+
+ free(gw);
+}
+
+/*
+ * groupInit
+ *
+ */
+Bool groupInit(CompPlugin * p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * groupFini
+ *
+ */
+void groupFini(CompPlugin * p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex(displayPrivateIndex);
+}
+
+/*
+ * groupDeps array
+ *
+ */
+
+static CompPluginDep groupDeps[] = {
+ {CompPluginRuleAfter, "place"},
+};
+
+static GroupDesc groupGroupDescs[] = {
+ {"General", N_("General options"), NULL, 0},
+ {"Selection", N_("Options for the window selection"), NULL, 0},
+ {"Tabbing", N_("Options for window tabbing"), NULL, 0},
+ {"Glow", N_("Configuration options for the window glow"), NULL, 0}
+};
+
+/*
+ * groupVTable
+ *
+ */
+CompPluginVTable groupVTable = {
+ "group",
+ N_("Group and Tab Windows"),
+ N_("With this plugin you can group and tab 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-extra",
+ "wm",
+ groupGroupDescs,
+ sizeof(groupGroupDescs) / sizeof(groupGroupDescs[0]),
+ True, /* enabled by default */
+};
+
+/*
+ * getCompPluginInfo
+ *
+ */
+CompPluginVTable *getCompPluginInfo(void)
+{
+ return &groupVTable;
+}
diff --git a/beryl-plugins/src/group/option.c b/beryl-plugins/src/group/option.c
new file mode 100644
index 0000000..b84a85c
--- /dev/null
+++ b/beryl-plugins/src/group/option.c
@@ -0,0 +1,1280 @@
+#include "group.h"
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * option.c
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * 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.
+ *
+ **/
+
+/*
+ * Defaults
+ *
+ */
+#define GROUP_SELECT_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_SELECT_BUTTON_DEFAULT Button1
+
+#define GROUP_SELECT_SINGLE_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_SELECT_SINGLE_KEY_DEFAULT "s"
+
+#define GROUP_GROUPING_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_GROUPING_KEY_DEFAULT "g"
+
+#define GROUP_UNGROUPING_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_UNGROUPING_KEY_DEFAULT "u"
+
+#define GROUP_TABMODE_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_TABMODE_KEY_DEFAULT "t"
+
+#define GROUP_CHANGE_TAB_LEFT_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_CHANGE_TAB_LEFT_KEY_DEFAULT "Left"
+
+#define GROUP_CHANGE_TAB_RIGHT_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_CHANGE_TAB_RIGHT_KEY_DEFAULT "Right"
+
+#define GROUP_REMOVING_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_REMOVING_KEY_DEFAULT "r"
+
+#define GROUP_CLOSING_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_CLOSING_KEY_DEFAULT "c"
+
+#define GROUP_IGNORE_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_IGNORE_KEY_DEFAULT "x"
+
+#define GROUP_CHANGE_COLOR_MODIFIERS_DEFAULT (CompSuperMask)
+#define GROUP_CHANGE_COLOR_KEY_DEFAULT "y"
+
+#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_PRECISION_DEFAULT 25
+#define GROUP_PRECISION_MIN 1
+#define GROUP_PRECISION_MAX 100
+
+#define GROUP_THUMB_SIZE_DEFAULT 128
+#define GROUP_THUMB_SIZE_MIN 16
+#define GROUP_THUMB_SIZE_MAX 256
+
+#define GROUP_BORDER_WIDTH_DEFAULT 10
+#define GROUP_BORDER_WIDTH_MIN 1
+#define GROUP_BORDER_WIDTH_MAX 20
+
+#define GROUP_BORDER_RADIUS_DEFAULT 10
+#define GROUP_BORDER_RADIUS_MIN 1
+#define GROUP_BORDER_RADIUS_MAX 20
+
+#define GROUP_GLOW_SIZE_DEFAULT 64
+#define GROUP_GLOW_SIZE_MIN 1
+#define GROUP_GLOW_SIZE_MAX 300
+
+#define GROUP_TABBAR_FONTSIZE_DEFAULT 12
+#define GROUP_TABBAR_FONTSIZE_MIN 6
+#define GROUP_TABBAR_FONTSIZE_MAX 24
+
+#define GROUP_TAB_DRAG_Y_DISTANCE_DEFAULT 400
+#define GROUP_TAB_DRAG_Y_DISTANCE_MIN 10
+#define GROUP_TAB_DRAG_Y_DISTANCE_MAX 2500
+
+#define GROUP_TAB_DRAG_SPEED_LIMIT_DEFAULT 800
+#define GROUP_TAB_DRAG_SPEED_LIMIT_MIN 0
+#define GROUP_TAB_DRAG_SPEED_LIMIT_MAX 2000
+
+#define GROUP_MOVE_DEFAULT TRUE
+#define GROUP_RESIZE_DEFAULT FALSE
+#define GROUP_RAISE_DEFAULT TRUE
+#define GROUP_AUTO_UNGROUP_DEFAULT FALSE
+#define GROUP_AUTO_GROUP_DEFAULT TRUE
+#define GROUP_RELATIVE_DISTANCE_DEFAULT FALSE
+#define GROUP_TAB_CREATE_MIPMAPS_DEFAULT FALSE
+#define GROUP_GLOW_DEFAULT TRUE
+#define GROUP_UNTAB_ON_CLOSE_DEFAULT FALSE
+#define GROUP_SPRING_MODEL_ON_MOVE_DEFAULT FALSE
+#define GROUP_AUTOTAB_DEFAULT FALSE
+#define GROUP_DND_UNGROUP_WINDOW_DEFAULT TRUE
+#define GROUP_RESIZE_UNMAXIMIZE_DEFAULT FALSE
+#define GROUP_MINIMIZE_ALL_DEFAULT TRUE
+#define GROUP_SHADE_ALL_DEFAULT FALSE
+
+#define GROUP_COLOR_SELECTION_RED_DEFAULT 0x0000
+#define GROUP_COLOR_SELECTION_GREEN_DEFAULT 0x0000
+#define GROUP_COLOR_SELECTION_BLUE_DEFAULT 0x0000
+#define GROUP_COLOR_SELECTION_ALPHA_DEFAULT 0x9999
+
+#define GROUP_COLOR_LINE_RED_DEFAULT 0x0000
+#define GROUP_COLOR_LINE_GREEN_DEFAULT 0x0000
+#define GROUP_COLOR_LINE_BLUE_DEFAULT 0x0000
+#define GROUP_COLOR_LINE_ALPHA_DEFAULT 0xABAB
+
+#define GROUP_COLOR_TAB_FILL_RED_DEFAULT 0x0000
+#define GROUP_COLOR_TAB_FILL_GREEN_DEFAULT 0x0000
+#define GROUP_COLOR_TAB_FILL_BLUE_DEFAULT 0x0000
+#define GROUP_COLOR_TAB_FILL_ALPHA_DEFAULT 0x9999
+
+#define GROUP_COLOR_TAB_BORDER_RED_DEFAULT 0x0000
+#define GROUP_COLOR_TAB_BORDER_GREEN_DEFAULT 0x0000
+#define GROUP_COLOR_TAB_BORDER_BLUE_DEFAULT 0x0000
+#define GROUP_COLOR_TAB_BORDER_ALPHA_DEFAULT 0xABAB
+
+#define GROUP_COLOR_TABBAR_FONT_RED_DEFAULT 0xFFFF
+#define GROUP_COLOR_TABBAR_FONT_BLUE_DEFAULT 0xFFFF
+#define GROUP_COLOR_TABBAR_FONT_GREEN_DEFAULT 0xFFFF
+#define GROUP_COLOR_TABBAR_FONT_ALPHA_DEFAULT 0xFFFF
+
+#define GROUP_GLOW_TYPE_DEFAULT GlowTextureRectangular
+
+#define GROUP_FADE_TIME_MIN 0.0f
+#define GROUP_FADE_TIME_MAX 5.0f
+#define GROUP_FADE_TIME_PRECISION 0.1f
+#define GROUP_FADE_TIME_DEFAULT 0.2f
+
+#define GROUP_FADE_TEXT_TIME_MIN 0.00f
+#define GROUP_FADE_TEXT_TIME_MAX 5.00f
+#define GROUP_FADE_TEXT_TIME_PRECISION 0.05f
+#define GROUP_FADE_TEXT_TIME_DEFAULT 0.25f
+
+#define GROUP_CHANGE_ANIMATION_TIME_DEFAULT 0.5f
+#define GROUP_CHANGE_ANIMATION_TIME_MIN 0.0f
+#define GROUP_CHANGE_ANIMATION_TIME_MAX 5.0f
+#define GROUP_CHANGE_ANIMATION_TIME_PRECISION 0.05f
+
+#define GROUP_TABBING_SPEED_DEFAULT 1.2f
+#define GROUP_TABBING_SPEED_MIN 0.1f
+#define GROUP_TABBING_SPEED_MAX 50.0f
+#define GROUP_TABBING_SPEED_PRECISION 0.1f
+
+#define GROUP_TABBING_TIMESTEP_DEFAULT 0.1f
+#define GROUP_TABBING_TIMESTEP_MIN 0.1f
+#define GROUP_TABBING_TIMESTEP_MAX 50.0f
+#define GROUP_TABBING_TIMESTEP_PRECISION 0.1f
+
+#define GROUP_VISIBILITY_TIME_MIN 0.0f
+#define GROUP_VISIBILITY_TIME_MAX 10.0f
+#define GROUP_VISIBILITY_TIME_PRECISION 0.05f
+#define GROUP_VISIBILITY_TIME_DEFAULT 0.0f
+
+#define GROUP_TAB_DRAG_HOVER_TIME_DEFAULT 0.5f
+#define GROUP_TAB_DRAG_HOVER_TIME_MIN 0.0f
+#define GROUP_TAB_DRAG_HOVER_TIME_MAX 5.0f
+#define GROUP_TAB_DRAG_HOVER_TIME_PRECISION 0.1f
+
+#define GROUP_TAB_DRAG_SPRING_K_DEFAULT 8.0f
+#define GROUP_TAB_DRAG_SPRING_K_MIN 0.1f
+#define GROUP_TAB_DRAG_SPRING_K_MAX 30.0f
+#define GROUP_TAB_DRAG_SPRING_K_PRECISION 0.1f
+
+#define GROUP_TAB_DRAG_FRICTION_DEFAULT 35.0f
+#define GROUP_TAB_DRAG_FRICTION_MIN 0.1f
+#define GROUP_TAB_DRAG_FRICTION_MAX 100.0f
+#define GROUP_TAB_DRAG_FRICTION_PRECISION 0.1f
+
+char *groupDefaultTypes[] = {
+ N_("Normal"),
+ N_("Dialog"),
+ N_("ModalDialog")
+};
+
+/*
+ * groupScreenInitOptions
+ *
+ */
+void groupScreenInitOptions(GroupScreen * gs)
+{
+
+ CompOption *o;
+ int i;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TYPES];
+ o->name = "mask";
+ o->group = N_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = True;
+ 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;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_OPACITY];
+ o->name = "opacity";
+ o->group = N_("Selection");
+ o->subGroup = N_("Options");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("Selection");
+ o->subGroup = N_("Options");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("Selection");
+ o->subGroup = N_("Options");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_PRECISION];
+ o->name = "precision";
+ o->group = N_("Selection");
+ o->subGroup = N_("Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Precision");
+ o->longDesc = N_("Precision of the seletion, which means its the percent "
+ "of the window area that has to be visible and in the selection.");
+ o->type = CompOptionTypeInt;
+ o->value.i = GROUP_PRECISION_DEFAULT;
+ o->rest.i.min = GROUP_PRECISION_MIN;
+ o->rest.i.max = GROUP_PRECISION_MAX;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TABBING_SPEED];
+ o->name = "tabbing_speed";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Animation Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Tabbing Speed");
+ o->longDesc =
+ N_("Tabbing Speed");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_TABBING_SPEED_DEFAULT;
+ o->rest.f.min = GROUP_TABBING_SPEED_MIN;
+ o->rest.f.max = GROUP_TABBING_SPEED_MAX;
+ o->rest.f.precision = GROUP_TABBING_SPEED_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TABBING_TIMESTEP];
+ o->name = "tabbing_timestep";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Animation Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Tabbing Timestep");
+ o->longDesc =
+ N_("The duration (in s) of the tabbing animation.");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_TABBING_TIMESTEP_DEFAULT;
+ o->rest.f.min = GROUP_TABBING_TIMESTEP_MIN;
+ o->rest.f.max = GROUP_TABBING_TIMESTEP_MAX;
+ o->rest.f.precision = GROUP_TABBING_TIMESTEP_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_THUMB_SIZE];
+ o->name = "thumb_size";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Misc. Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Thumb Size");
+ o->longDesc =
+ N_("The size of the window thumbs in the tab bar.");
+ o->type = CompOptionTypeInt;
+ o->value.i = GROUP_THUMB_SIZE_DEFAULT;
+ o->rest.i.min = GROUP_THUMB_SIZE_MIN;
+ o->rest.i.max = GROUP_THUMB_SIZE_MAX;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_BORDER_WIDTH];
+ o->name = "border_width";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Misc. Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Border Width");
+ o->longDesc =
+ N_("The width of the border around the tab bar.");
+ o->type = CompOptionTypeInt;
+ o->value.i = GROUP_BORDER_WIDTH_DEFAULT;
+ o->rest.i.min = GROUP_BORDER_WIDTH_MIN;
+ o->rest.i.max = GROUP_BORDER_WIDTH_MAX;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_BORDER_RADIUS];
+ o->name = "border_radius";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Misc. Options");
+ o->displayHints = "";
+ o->advanced = True;
+ o->shortDesc = N_("Border Radius");
+ o->longDesc =
+ N_("The radius for the edges of the tab bar.");
+ o->type = CompOptionTypeInt;
+ o->value.i = GROUP_BORDER_RADIUS_DEFAULT;
+ o->rest.i.min = GROUP_BORDER_RADIUS_MIN;
+ o->rest.i.max = GROUP_BORDER_RADIUS_MAX;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TABBAR_FONTSIZE];
+ o->name = "tabbar_font_size";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Window title font");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Font Size");
+ o->longDesc =
+ N_("The size of window title font in the tab bar.");
+ o->type = CompOptionTypeInt;
+ o->value.i = GROUP_TABBAR_FONTSIZE_DEFAULT;
+ o->rest.i.min = GROUP_TABBAR_FONTSIZE_MIN;
+ o->rest.i.max = GROUP_TABBAR_FONTSIZE_MAX;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TABBAR_FONTCOLOR];
+ o->name = "tabbar_font_color";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Window title font");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Font Color");
+ o->longDesc = N_("The color of the window title in the tab bar.");
+ o->type = CompOptionTypeColor;
+ o->value.c[0] = GROUP_COLOR_TABBAR_FONT_RED_DEFAULT;
+ o->value.c[1] = GROUP_COLOR_TABBAR_FONT_GREEN_DEFAULT;
+ o->value.c[2] = GROUP_COLOR_TABBAR_FONT_BLUE_DEFAULT;
+ o->value.c[3] = GROUP_COLOR_TABBAR_FONT_ALPHA_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_MOVE];
+ o->name = "move";
+ o->group = N_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = True;
+ 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_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = True;
+ 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_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("Selection");
+ o->subGroup = N_("Colors");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("Selection");
+ o->subGroup = N_("Colors");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Line Color");
+ o->longDesc = N_("Line color of the selection.");
+ o->type = CompOptionTypeColor;
+ o->value.c[0] = GROUP_COLOR_LINE_RED_DEFAULT;
+ o->value.c[1] = GROUP_COLOR_LINE_GREEN_DEFAULT;
+ o->value.c[2] = GROUP_COLOR_LINE_BLUE_DEFAULT;
+ o->value.c[3] = GROUP_COLOR_LINE_ALPHA_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TAB_FILL_COLOR];
+ o->name = "tab_color";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Colors");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Tab Color");
+ o->longDesc = N_("Fill color of the tab bar.");
+ o->type = CompOptionTypeColor;
+ o->value.c[0] = GROUP_COLOR_TAB_FILL_RED_DEFAULT;
+ o->value.c[1] = GROUP_COLOR_TAB_FILL_GREEN_DEFAULT;
+ o->value.c[2] = GROUP_COLOR_TAB_FILL_BLUE_DEFAULT;
+ o->value.c[3] = GROUP_COLOR_TAB_FILL_ALPHA_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TAB_BORDER_COLOR];
+ o->name = "border_color";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Colors");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Tab Border Color");
+ o->longDesc = N_("Border color of the tab bar.");
+ o->type = CompOptionTypeColor;
+ o->value.c[0] = GROUP_COLOR_TAB_BORDER_RED_DEFAULT;
+ o->value.c[1] = GROUP_COLOR_TAB_BORDER_GREEN_DEFAULT;
+ o->value.c[2] = GROUP_COLOR_TAB_BORDER_BLUE_DEFAULT;
+ o->value.c[3] = GROUP_COLOR_TAB_BORDER_ALPHA_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TAB_CREATE_MIPMAPS];
+ o->name = "mipmaps";
+ o->shortDesc = N_("Create mipmaps for thumbnails.");
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Misc. Options");
+ o->displayHints = "";
+ o->advanced = True;
+ o->longDesc = N_("Create mipmaps for thumbnails in the tab-bar.");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_TAB_CREATE_MIPMAPS_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_GLOW];
+ o->name = "glow";
+ o->shortDesc = N_("Enable Glow");
+ o->group = N_("Glow");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ o->longDesc = N_("Enable grouped window glowing.");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_GLOW_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_GLOW_SIZE];
+ o->name = "glow_size";
+ o->group = N_("Glow");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Glow size");
+ o->longDesc =
+ N_("The size of the grouped window glow.");
+ o->type = CompOptionTypeInt;
+ o->value.i = GROUP_GLOW_SIZE_DEFAULT;
+ o->rest.i.min = GROUP_GLOW_SIZE_MIN;
+ o->rest.i.max = GROUP_GLOW_SIZE_MAX;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_GLOW_TYPE];
+ o->name = "glow_type";
+ o->group = N_("Glow");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Glow type");
+ o->longDesc =
+ N_("The type of the glow.");
+ o->type = CompOptionTypeString;
+ o->value.s = strdup(glowTextureName[GROUP_GLOW_TYPE_DEFAULT]);
+ o->rest.s.string = (char **) glowTextureName;
+ o->rest.s.nString = nGlowTextureNames;
+
+ /* initialize glowType here to prevent code duplication in init.c */
+ gs->glowType = GROUP_GLOW_TYPE_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_FADE_TIME];
+ o->name = "fade_time";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Animation Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Fade time for tab-bar animations.");
+ o->longDesc = N_("Duration (in s) of the fading animation of the tab-bar when unmapping / mapping it.");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_FADE_TIME_DEFAULT;
+ o->rest.f.min = GROUP_FADE_TIME_MIN;
+ o->rest.f.max = GROUP_FADE_TIME_MAX;
+ o->rest.f.precision = GROUP_FADE_TIME_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_FADE_TEXT_TIME];
+ o->name = "fade_text_time";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Animation Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Fade time for text animations.");
+ o->longDesc = N_("Duration (in s) of the fading animation of the text when showing / hiding it.");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_FADE_TEXT_TIME_DEFAULT;
+ o->rest.f.min = GROUP_FADE_TEXT_TIME_MIN;
+ o->rest.f.max = GROUP_FADE_TEXT_TIME_MAX;
+ o->rest.f.precision = GROUP_FADE_TEXT_TIME_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_VISIBILITY_TIME];
+ o->name = "visibility_time";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Misc. Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Tab bar visibility time after tab change");
+ o->longDesc = N_("Time (in s) the tab bar is visible after a tab change.");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_VISIBILITY_TIME_DEFAULT;
+ o->rest.f.min = GROUP_VISIBILITY_TIME_MIN;
+ o->rest.f.max = GROUP_VISIBILITY_TIME_MAX;
+ o->rest.f.precision = GROUP_VISIBILITY_TIME_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_UNTAB_ON_CLOSE];
+ o->name = "untab_on_close";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Misc. Options");
+ o->displayHints = "";
+ o->advanced = True;
+ o->shortDesc = N_("Untab when closing the window.");
+ o->longDesc = N_("Untab the group when closing the tabbed window, "
+ "instead of changing tab.");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_UNTAB_ON_CLOSE_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_CHANGE_ANIMATION_TIME];
+ o->name = "change_animation_time";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Animation Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Change Tab Animation Duration");
+ o->longDesc =
+ N_("The duration (in s) of the animation that happens when changing tabs.");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_CHANGE_ANIMATION_TIME_DEFAULT;
+ o->rest.f.min = GROUP_CHANGE_ANIMATION_TIME_MIN;
+ o->rest.f.max = GROUP_CHANGE_ANIMATION_TIME_MAX;
+ o->rest.f.precision = GROUP_CHANGE_ANIMATION_TIME_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_HOVER_TIME];
+ o->name = "drag_hover_time";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Slot dragging");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Hover time for slot dragging");
+ o->longDesc = N_("Timespan (in s) after which a grouped window is activated if "
+ "a window of another group is dragged over it.");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_TAB_DRAG_HOVER_TIME_DEFAULT;
+ o->rest.f.min = GROUP_TAB_DRAG_HOVER_TIME_MIN;
+ o->rest.f.max = GROUP_TAB_DRAG_HOVER_TIME_MAX;
+ o->rest.f.precision = GROUP_TAB_DRAG_HOVER_TIME_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_SPRING_K];
+ o->name = "drag_spring_k";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Slot dragging");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Slot drag Spring K");
+ o->longDesc = N_("Spring Konstant used for slot dragging.");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_TAB_DRAG_SPRING_K_DEFAULT;
+ o->rest.f.min = GROUP_TAB_DRAG_SPRING_K_MIN;
+ o->rest.f.max = GROUP_TAB_DRAG_SPRING_K_MAX;
+ o->rest.f.precision = GROUP_TAB_DRAG_SPRING_K_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_FRICTION];
+ o->name = "drag_friction";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Slot dragging");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Slot drag friction");
+ o->longDesc = N_("Spring friction for slot dragging.");
+ o->type = CompOptionTypeFloat;
+ o->value.f = GROUP_TAB_DRAG_FRICTION_DEFAULT;
+ o->rest.f.min = GROUP_TAB_DRAG_FRICTION_MIN;
+ o->rest.f.max = GROUP_TAB_DRAG_FRICTION_MAX;
+ o->rest.f.precision = GROUP_TAB_DRAG_FRICTION_PRECISION;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_Y_DISTANCE];
+ o->name = "drag_y_distance";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Slot dragging");
+ o->displayHints = "";
+ o->advanced = True;
+ o->shortDesc = N_("Y distance for spring model");
+ o->longDesc = N_("Distance (in pixels) between the tab bar and the"
+ "dragged slot for applying the spring model. If the distance"
+ "is larger than that value, the model isn't applied.");
+ o->type = CompOptionTypeInt;
+ o->value.i = GROUP_TAB_DRAG_Y_DISTANCE_DEFAULT;
+ o->rest.i.min = GROUP_TAB_DRAG_Y_DISTANCE_MIN;
+ o->rest.i.max = GROUP_TAB_DRAG_Y_DISTANCE_MAX;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_SPEED_LIMIT];
+ o->name = "drag_speed_limit";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Slot dragging");
+ o->displayHints = "";
+ o->advanced = True;
+ o->shortDesc = N_("Speed limit");
+ o->longDesc = N_("Mouse pointer movement speed limit (in pixels/secons)"
+ "for the spring model.");
+ o->type = CompOptionTypeInt;
+ o->value.i = GROUP_TAB_DRAG_SPEED_LIMIT_DEFAULT;
+ o->rest.i.min = GROUP_TAB_DRAG_SPEED_LIMIT_MIN;
+ o->rest.i.max = GROUP_TAB_DRAG_SPEED_LIMIT_MAX;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_SPRING_MODEL_ON_MOVE];
+ o->name = "spring_model_on_move";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Slot dragging");
+ o->displayHints = "";
+ o->advanced = True;
+ o->shortDesc = N_("Spring model on move");
+ o->longDesc = N_("Use spring model for the tab-bar when moving the window.");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_SPRING_MODEL_ON_MOVE_DEFAULT;
+
+ o=&gs->opt[GROUP_SCREEN_OPTION_AUTOTAB];
+ o->name="autotab_create";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Misc. Options");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Autotab windows on creation");
+ o->longDesc = N_("Autotab windows with themselves on window creation");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_AUTOTAB_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_DND_UNGROUP_WINDOW];
+ o->name = "dnd_ungroup_window";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Slot dragging");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Remove window from group after dropping it outside a tab-bar.");
+ o->longDesc = N_("Remove window from group after droping it outside a tab-bar.");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_DND_UNGROUP_WINDOW_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_RESIZE_UNMAXIMIZE];
+ o->name = "resize_unmaximize";
+ o->group = N_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Maximize/unmaximize with group.");
+ o->longDesc = N_("Maximize/unmaximize with group.");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_RESIZE_UNMAXIMIZE_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_MINIMIZE_ALL];
+ o->name = "minimize_all";
+ o->group = N_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Minimize with group.");
+ o->longDesc = N_("Minimize with group.");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_MINIMIZE_ALL_DEFAULT;
+
+ o = &gs->opt[GROUP_SCREEN_OPTION_SHADE_ALL];
+ o->name = "shade_all";
+ o->group = N_("General");
+ o->subGroup = N_("");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Shade with group.");
+ o->longDesc = N_("Shade with group.");
+ o->type = CompOptionTypeBool;
+ o->value.b = GROUP_SHADE_ALL_DEFAULT;
+}
+
+/*
+ * groupGetScreenOptions
+ *
+ */
+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
+ *
+ */
+Bool
+groupSetScreenOption(CompScreen * s, char *name, CompOptionValue * value)
+{
+
+ CompOption *o;
+ int index;
+
+ GROUP_SCREEN(s);
+
+ o = compFindOption(gs->opt, NUM_OPTIONS(gs), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case GROUP_SCREEN_OPTION_TYPES:
+ if (compSetOptionList(o, value)) {
+ gs->wMask =
+ compWindowTypeMaskFromStringList(&o->value);
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_SELECTION_COLOR:
+ case GROUP_SCREEN_OPTION_LINE_COLOR:
+ case GROUP_SCREEN_OPTION_TAB_FILL_COLOR:
+ case GROUP_SCREEN_OPTION_TAB_BORDER_COLOR:
+ if (compSetColorOption(o, value)) {
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_TABBAR_FONTCOLOR:
+ if (compSetColorOption(o, value)) {
+ GroupSelection *group;
+
+ for (group = gs->groups; group; group = group->next)
+ groupRenderWindowTitle(group);
+
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_GLOW_TYPE:
+ if (compSetStringOption(o, value)) {
+ int i;
+ for (i = 0; i < GlowTextureNum; i++) {
+ if (strcmp (o->value.s, glowTextureName[i]) == 0) {
+ if (i != gs->glowType) {
+ gs->glowType = i;
+
+ finiTexture (s, &gs->glowTexture);
+ initTexture (s, &gs->glowTexture);
+
+ RGBAimageToTexture (s, &gs->glowTexture,
+ glowTextureProperties[gs->glowType].textureData,
+ glowTextureProperties[gs->glowType].textureSize,
+ glowTextureProperties[gs->glowType].textureSize);
+
+ if (gs->opt[GROUP_SCREEN_OPTION_GLOW].value.b && gs->groups) {
+ groupRecomputeGlow (s);
+ damageScreen (s);
+ }
+ }
+ return TRUE;
+ }
+ }
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_OPACITY:
+ case GROUP_SCREEN_OPTION_BRIGHTNESS:
+ case GROUP_SCREEN_OPTION_SATURATION:
+ case GROUP_SCREEN_OPTION_PRECISION:
+ case GROUP_SCREEN_OPTION_TAB_DRAG_Y_DISTANCE:
+ case GROUP_SCREEN_OPTION_TAB_DRAG_SPEED_LIMIT:
+ if (compSetIntOption(o, value)) {
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_THUMB_SIZE:
+ case GROUP_SCREEN_OPTION_BORDER_WIDTH:
+ if (compSetIntOption(o, value)) {
+ GroupSelection *group;
+
+ for (group = gs->groups; group; group = group->next)
+ {
+ if(group->tabBar)
+ groupRecalcTabBarPos(group, (group->tabBar->region->extents.x1 + group->tabBar->region->extents.x2) / 2,
+ group->tabBar->region->extents.x1, group->tabBar->region->extents.x2);
+ }
+
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_BORDER_RADIUS:
+ if (compSetIntOption(o, value)) {
+ GroupSelection *group;
+
+ for (group = gs->groups; group; group = group->next)
+ {
+ if(group->tabBar)
+ groupRenderTabBarBackground(group);
+ }
+
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_TABBAR_FONTSIZE:
+ if (compSetIntOption(o, value)) {
+ GroupSelection *group;
+ for (group = gs->groups; group; group = group->next) {
+ groupRenderWindowTitle(group);
+ }
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_TABBING_SPEED:
+ case GROUP_SCREEN_OPTION_TABBING_TIMESTEP:
+ case GROUP_SCREEN_OPTION_FADE_TIME:
+ case GROUP_SCREEN_OPTION_FADE_TEXT_TIME:
+ case GROUP_SCREEN_OPTION_VISIBILITY_TIME:
+ case GROUP_SCREEN_OPTION_CHANGE_ANIMATION_TIME:
+ case GROUP_SCREEN_OPTION_TAB_DRAG_HOVER_TIME:
+ case GROUP_SCREEN_OPTION_TAB_DRAG_SPRING_K:
+ case GROUP_SCREEN_OPTION_TAB_DRAG_FRICTION:
+ if (compSetFloatOption(o, value)) {
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_GLOW_SIZE:
+ if (compSetIntOption(o, value)) {
+ if (gs->opt[GROUP_SCREEN_OPTION_GLOW].value.b) {
+ CompWindow *w;
+
+ groupRecomputeGlow (s);
+
+ for (w = s->windows; w; w = w->next) {
+ GROUP_WINDOW (w);
+
+ if (gw->glowQuads) {
+ damageWindowOutputExtents (w);
+ updateWindowOutputExtents (w);
+ damageWindowOutputExtents (w);
+ }
+ }
+ }
+ return TRUE;
+ }
+ break;
+
+ case GROUP_SCREEN_OPTION_GLOW:
+ if (compSetBoolOption(o, value)) {
+ CompWindow *w;
+
+ groupRecomputeGlow (s);
+
+ for (w = s->windows; w; w = w->next) {
+ GROUP_WINDOW (w);
+
+ if (gw->glowQuads) {
+ damageWindowOutputExtents (w);
+ updateWindowOutputExtents (w);
+ damageWindowOutputExtents (w);
+ }
+ }
+ 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:
+ case GROUP_SCREEN_OPTION_TAB_CREATE_MIPMAPS:
+ case GROUP_SCREEN_OPTION_UNTAB_ON_CLOSE:
+ case GROUP_SCREEN_OPTION_SPRING_MODEL_ON_MOVE:
+ case GROUP_SCREEN_OPTION_AUTOTAB:
+ case GROUP_SCREEN_OPTION_DND_UNGROUP_WINDOW:
+ case GROUP_SCREEN_OPTION_RESIZE_UNMAXIMIZE:
+ case GROUP_SCREEN_OPTION_MINIMIZE_ALL:
+ case GROUP_SCREEN_OPTION_SHADE_ALL:
+ if (compSetBoolOption(o, value)) {
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/*
+ * groupDisplayInitOptions
+ *
+ */
+void groupDisplayInitOptions(GroupDisplay * gd)
+{
+ CompOption *o;
+
+ o = &gd->opt[GROUP_DISPLAY_OPTION_SELECT];
+ o->name = "select";
+ o->group = N_("Selection");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("Selection");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("Grouping");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("Grouping");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_("Tabbing");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Tab");
+ o->longDesc = N_("The key for entering the tab mode.");
+ o->type = CompOptionTypeAction;
+ o->value.action.initiate = groupInitTab;
+ o->value.action.terminate = 0;
+ o->value.action.bell = FALSE;
+ o->value.action.edgeMask = 0;
+ o->value.action.state = CompActionStateInitKey;
+ o->value.action.type = CompBindingTypeKey;
+ o->value.action.key.modifiers = GROUP_TABMODE_MODIFIERS_DEFAULT;
+ o->value.action.key.keysym =
+ XStringToKeysym(GROUP_TABMODE_KEY_DEFAULT);
+
+ o = &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT];
+ o->name = "change_tab_left";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Change Tab Left");
+ o->longDesc = N_("The key for changing the tab to the left.");
+ o->type = CompOptionTypeAction;
+ o->value.action.initiate = groupChangeTabLeft;
+ o->value.action.terminate = 0;
+ o->value.action.bell = FALSE;
+ o->value.action.edgeMask = 0;
+ o->value.action.state = CompActionStateInitKey;
+ o->value.action.type = CompBindingTypeKey;
+ o->value.action.key.modifiers = GROUP_CHANGE_TAB_LEFT_MODIFIERS_DEFAULT;
+ o->value.action.key.keysym =
+ XStringToKeysym(GROUP_CHANGE_TAB_LEFT_KEY_DEFAULT);
+
+ o = &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT];
+ o->name = "change_tab_right";
+ o->group = N_("Tabbing");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Change Tab Right");
+ o->longDesc = N_("The key for changing the tab to the right.");
+ o->type = CompOptionTypeAction;
+ o->value.action.initiate = groupChangeTabRight;
+ o->value.action.terminate = 0;
+ o->value.action.bell = FALSE;
+ o->value.action.edgeMask = 0;
+ o->value.action.state = CompActionStateInitKey;
+ o->value.action.type = CompBindingTypeKey;
+ o->value.action.key.modifiers = GROUP_CHANGE_TAB_RIGHT_MODIFIERS_DEFAULT;
+ o->value.action.key.keysym =
+ XStringToKeysym(GROUP_CHANGE_TAB_RIGHT_KEY_DEFAULT);
+
+ o = &gd->opt[GROUP_DISPLAY_OPTION_REMOVING];
+ o->name = "remove";
+ o->group = N_("Grouping");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_REMOVING_MODIFIERS_DEFAULT;
+ o->value.action.key.keysym =
+ XStringToKeysym(GROUP_REMOVING_KEY_DEFAULT);
+
+ o = &gd->opt[GROUP_DISPLAY_OPTION_CLOSING];
+ o->name = "close";
+ o->group = N_("Grouping");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ 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_CLOSING_MODIFIERS_DEFAULT;
+ o->value.action.key.keysym =
+ XStringToKeysym(GROUP_CLOSING_KEY_DEFAULT);
+
+ o = &gd->opt[GROUP_DISPLAY_OPTION_IGNORE];
+ o->name = "ignore";
+ o->group = N_("Grouping");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ 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);
+
+ o = &gd->opt[GROUP_DISPLAY_OPTION_CHANGE_COLOR];
+ o->name = "change_color";
+ o->group = N_("Glow");
+ o->subGroup = N_("Key bindings");
+ o->displayHints = "";
+ o->advanced = False;
+ o->shortDesc = N_("Change the glow color");
+ o->longDesc =
+ N_("If you don't like the current color of the glow, with this key you can change it.");
+ o->type = CompOptionTypeAction;
+ o->value.action.initiate = groupChangeColor;
+ 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.type = 0;
+ o->value.action.key.modifiers =
+ GROUP_CHANGE_COLOR_MODIFIERS_DEFAULT;
+ o->value.action.key.keysym =
+ XStringToKeysym(GROUP_CHANGE_COLOR_KEY_DEFAULT);
+}
+
+/*
+ * groupGetDisplayOptions
+ *
+ */
+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
+ *
+ */
+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_REMOVING:
+ case GROUP_DISPLAY_OPTION_CLOSING:
+ case GROUP_DISPLAY_OPTION_CHANGE_COLOR:
+ case GROUP_DISPLAY_OPTION_IGNORE:
+ case GROUP_DISPLAY_OPTION_TABMODE:
+ case GROUP_DISPLAY_OPTION_CHANGE_TAB_LEFT:
+ case GROUP_DISPLAY_OPTION_CHANGE_TAB_RIGHT:
+ if (setDisplayAction(d, o, value))
+ return TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
diff --git a/beryl-plugins/src/group/paint.c b/beryl-plugins/src/group/paint.c
new file mode 100644
index 0000000..dc5e706
--- /dev/null
+++ b/beryl-plugins/src/group/paint.c
@@ -0,0 +1,1020 @@
+#include "group.h"
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * paint.c
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * 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.
+ *
+ **/
+
+/*
+ * groupPaintThumb - taken from switcher and modified for tab bar
+ *
+ */
+void groupPaintThumb(GroupSelection *group, GroupTabBarSlot *slot, int targetOpacity)
+{
+ DrawWindowGeometryProc oldDrawWindowGeometry;
+ AddWindowGeometryProc oldAddWindowGeometry;
+
+ CompWindow *w = slot->window;
+
+ GROUP_SCREEN(w->screen);
+
+ int tw, th;
+ tw = slot->region->extents.x2 - slot->region->extents.x1;
+ th = slot->region->extents.y2 - slot->region->extents.y1;
+
+ /* Wrap drawWindowGeometry to make sure the general
+ drawWindowGeometry function is used */
+ oldDrawWindowGeometry = w->screen->drawWindowGeometry;
+ w->screen->drawWindowGeometry = getBaseDrawWindowGeometry();
+ oldAddWindowGeometry = w->screen->addWindowGeometry;
+ w->screen->addWindowGeometry = getBaseAddWindowGeometry();
+
+ WindowPaintAttrib sAttrib = w->paint;
+
+ // animate fade
+ if (group && group->tabBar->state == PaintFadeIn)
+ sAttrib.opacity -= sAttrib.opacity * group->tabBar->animationTime /
+ (gs->opt[GROUP_SCREEN_OPTION_FADE_TIME].value.f * 1000);
+ else if (group && group->tabBar->state == PaintFadeOut)
+ sAttrib.opacity = sAttrib.opacity * group->tabBar->animationTime /
+ (gs->opt[GROUP_SCREEN_OPTION_FADE_TIME].value.f * 1000);
+
+ sAttrib.opacity = sAttrib.opacity * targetOpacity / 0xffff;
+
+ 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;
+
+ int vx, vy;
+ groupGetDrawOffsetForSlot(slot, &vx, &vy);
+
+ sAttrib.xTranslate = slot->region->extents.x1 - w->attrib.x + vx;
+ sAttrib.yTranslate = slot->region->extents.y1 - w->attrib.y + vy;
+
+ (w->screen->drawWindow) (w, &sAttrib, getInfiniteRegion(),
+ PAINT_WINDOW_TRANSFORMED_MASK);
+
+ addWindowDamage(w);
+ }
+
+ w->screen->drawWindowGeometry = oldDrawWindowGeometry;
+ w->screen->addWindowGeometry = oldAddWindowGeometry;
+}
+
+/*
+ * groupRenderTopTabHighlight
+ *
+ */
+void groupRenderTopTabHighlight(GroupSelection *group)
+{
+ GroupTabBar *bar;
+ GroupCairoLayer *layer;
+ cairo_t *cr;
+
+ if (!group->tabBar || !HAS_TOP_WIN(group) || !group->tabBar->selectionLayer || !group->tabBar->selectionLayer->cairo)
+ return;
+
+ bar = group->tabBar;
+
+ int width = group->topTab->region->extents.x2 - group->topTab->region->extents.x1 + 10;
+ int height = group->topTab->region->extents.y2 - group->topTab->region->extents.y1 + 10;
+
+ bar->selectionLayer = groupRebuildCairoLayer(group->screen, bar->selectionLayer, width, height);
+ layer = bar->selectionLayer;
+ cr = layer->cairo;
+
+ layer->texWidth = width;
+ layer->texHeight = height;
+
+ // fill
+ cairo_set_line_width(cr, 2);
+ cairo_set_source_rgba(cr,
+ (group->color[0] / 65535.0f),
+ (group->color[1] / 65535.0f),
+ (group->color[2] / 65535.0f),
+ (group->color[3] / (65535.0f*2)));
+
+ cairo_move_to(cr, 0, 0);
+ cairo_rectangle(cr, 0, 0, width, height);
+
+ // fill
+ cairo_fill_preserve(cr);
+
+ // outline
+ cairo_set_source_rgba(cr,
+ (group->color[0] / 65535.0f),
+ (group->color[1] / 65535.0f),
+ (group->color[2] / 65535.0f),
+ (group->color[3] / 65535.0f));
+ cairo_stroke(cr);
+
+ imageToTexture(group->screen, &layer->texture, (char*) layer->texBuf, width, height);
+}
+
+/*
+ * groupRenderTabBarBackground
+ *
+ */
+void groupRenderTabBarBackground(GroupSelection *group)
+{
+ GROUP_SCREEN(group->screen);
+ GroupTabBar *bar;
+ GroupCairoLayer *layer;
+ cairo_t *cr;
+
+ if (!group->tabBar || !HAS_TOP_WIN(group) || !group->tabBar->bgLayer || !group->tabBar->bgLayer->cairo)
+ return;
+
+ bar = group->tabBar;
+
+ int x, y, width, height, radius;
+ width = bar->region->extents.x2 - bar->region->extents.x1;
+ height = bar->region->extents.y2 - bar->region->extents.y1;
+ radius = gs->opt[GROUP_SCREEN_OPTION_BORDER_RADIUS].value.i;
+ x = 0;
+ y = 0;
+
+ bar->bgLayer = groupRebuildCairoLayer(group->screen, bar->bgLayer, width, height);
+ layer = bar->bgLayer;
+ cr = layer->cairo;
+
+ layer->texWidth = width;
+ layer->texHeight = height;
+
+ float r, g, b, a;
+
+ cairo_set_line_width(cr, 2);
+
+ cairo_move_to(cr, 0, 0);
+ cairo_move_to (cr, x + radius, y);
+ cairo_arc (cr, x + width - radius, y + radius, radius, M_PI * 1.5, M_PI * 2.0);
+ cairo_arc (cr, x + width - radius, y + height - radius, radius, 0.0, M_PI * 0.5);
+ cairo_arc (cr, x + radius, y + height - radius, radius, M_PI * 0.5, M_PI);
+ cairo_arc (cr, x + radius, y + radius, radius, M_PI, M_PI * 1.5);
+
+ // fill
+ r = gs->opt[GROUP_SCREEN_OPTION_TAB_FILL_COLOR].value.c[0] / 65535.0f;
+ g = gs->opt[GROUP_SCREEN_OPTION_TAB_FILL_COLOR].value.c[1] / 65535.0f;
+ b = gs->opt[GROUP_SCREEN_OPTION_TAB_FILL_COLOR].value.c[2] / 65535.0f;
+ a = gs->opt[GROUP_SCREEN_OPTION_TAB_FILL_COLOR].value.c[3] / 65535.0f;
+ cairo_set_source_rgba(cr, r, g, b, a);
+ cairo_fill_preserve(cr);
+
+ // outline
+ r = gs->opt[GROUP_SCREEN_OPTION_TAB_BORDER_COLOR].value.c[0] / 65535.0f;
+ g = gs->opt[GROUP_SCREEN_OPTION_TAB_BORDER_COLOR].value.c[1] / 65535.0f;
+ b = gs->opt[GROUP_SCREEN_OPTION_TAB_BORDER_COLOR].value.c[2] / 65535.0f;
+ a = gs->opt[GROUP_SCREEN_OPTION_TAB_BORDER_COLOR].value.c[3] / 65535.0f;
+ cairo_set_source_rgba(cr, r, g, b, a);
+ cairo_stroke(cr);
+
+ imageToTexture(group->screen, &layer->texture, (char*) layer->texBuf, width, height);
+}
+
+/*
+ * groupRenderWindowTitle
+ *
+ */
+void groupRenderWindowTitle(GroupSelection *group)
+{
+ GROUP_SCREEN(group->screen);
+ GroupTabBar *bar;
+ GroupCairoLayer *layer;
+ char *title;
+
+ if (!group->tabBar || !HAS_TOP_WIN(group) || !group->tabBar->textLayer)
+ return;
+
+ bar = group->tabBar;
+
+ int width = bar->region->extents.x2 - bar->region->extents.x1;
+ int height = bar->region->extents.y2 - bar->region->extents.y1;
+
+ bar->textLayer = groupRebuildCairoLayer(group->screen, bar->textLayer, width, height);
+ layer = bar->textLayer;
+
+ int font_size = gs->opt[GROUP_SCREEN_OPTION_TABBAR_FONTSIZE].value.i;
+
+ if (bar->textSlot && bar->textSlot->name)
+ title = bar->textSlot->name;
+ else
+ title = " ";
+
+ CompTextAttrib text_attrib;
+ text_attrib.family = "Sans";
+ text_attrib.size = font_size;
+ text_attrib.style = TEXT_STYLE_BOLD;
+ text_attrib.color[0] = gs->opt[GROUP_SCREEN_OPTION_TABBAR_FONTCOLOR].value.c[0];
+ text_attrib.color[1] = gs->opt[GROUP_SCREEN_OPTION_TABBAR_FONTCOLOR].value.c[1];
+ text_attrib.color[2] = gs->opt[GROUP_SCREEN_OPTION_TABBAR_FONTCOLOR].value.c[2];
+ text_attrib.color[3] = gs->opt[GROUP_SCREEN_OPTION_TABBAR_FONTCOLOR].value.c[3];
+ text_attrib.ellipsize = TRUE;
+
+ text_attrib.maxwidth = width;
+ text_attrib.maxheight = height;
+ text_attrib.screen = group->screen;
+ text_attrib.text = title;
+
+ void *data = NULL;
+ int stride;
+
+ if (!((*group->screen->display->fileToImage)(group->screen->display, "TextToPixmap",
+ (const char*) &text_attrib, &width,
+ &height, &stride, &data))) {
+ /* getting the pixmap failed, so create an empty one */
+ Pixmap emptyPixmap = XCreatePixmap(group->screen->display->display,
+ group->screen->root, width, height, 32);
+
+ if (emptyPixmap) {
+ XGCValues gcv;
+ gcv.foreground = 0x00000000;
+ gcv.plane_mask = 0xffffffff;
+
+ GC gc = XCreateGC(group->screen->display->display, emptyPixmap,
+ GCForeground, &gcv);
+
+ XFillRectangle(group->screen->display->display, emptyPixmap, gc,
+ 0, 0, width, height);
+
+ XFreeGC(group->screen->display->display, gc);
+
+ data = (void*) emptyPixmap;
+ }
+ }
+
+ if(data)
+ bindPixmapToTexture(group->screen, &layer->texture, (Pixmap) data, width, height, 32);
+
+ layer->texWidth = width;
+ layer->texHeight = height;
+}
+
+/*
+ * groupPaintTabBar
+ *
+ */
+void groupPaintTabBar(GroupSelection * group, const WindowPaintAttrib *wAttrib, Region clipRegion)
+{
+ if (!group || !HAS_TOP_WIN(group) || !group->tabBar || (group->tabBar->state == PaintOff))
+ return;
+
+ CompWindow *topTab = TOP_TAB(group);
+ CompScreen *s = group->screen;
+ GroupTabBarSlot *slot;
+ GroupTabBar *bar = group->tabBar;
+
+ GROUP_SCREEN(s);
+
+ /* we do not want to paint the tab bar if we currently rotate the screen */
+ if (gs->isRotating)
+ return;
+
+ int i;
+ int alpha;
+ float w_scale;
+ float h_scale;
+ GroupCairoLayer *layer;
+
+ REGION box;
+
+ /* make sure we get the core drawWindowGeometry function */
+ DrawWindowGeometryProc oldDrawWindowGeometry = s->drawWindowGeometry;
+ s->drawWindowGeometry = getBaseDrawWindowGeometry();
+
+ WindowPaintAttrib attrib = *wAttrib;
+ attrib.opacity = OPAQUE;
+ attrib.saturation = COLOR;
+ attrib.brightness = BRIGHT;
+
+#define PAINT_BG 0
+#define PAINT_SEL 1
+#define PAINT_TEXT 2
+#define PAINT_MAX 3
+
+ box.rects = &box.extents;
+ box.numRects = 1;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ for (i = 0; i < PAINT_MAX; i++) {
+ alpha = 0xffff;
+
+ if (bar->state == PaintFadeIn)
+ alpha -= alpha * bar->animationTime /
+ (gs->opt[GROUP_SCREEN_OPTION_FADE_TIME].value.f * 1000);
+ else if (bar->state == PaintFadeOut)
+ alpha = alpha * bar->animationTime /
+ (gs->opt[GROUP_SCREEN_OPTION_FADE_TIME].value.f * 1000);
+
+ switch (i) {
+ case PAINT_BG:
+ layer = bar->bgLayer;
+
+ h_scale = 1.0f;
+ w_scale = (double)(bar->region->extents.x2 - bar->region->extents.x1) /
+ (double)(layer->texWidth);
+
+ box.extents = bar->region->extents;
+ break;
+ case PAINT_SEL:
+ if (group->topTab != gs->draggedSlot) {
+ layer = bar->selectionLayer;
+
+ h_scale = 1.0f;
+ w_scale = 1.0f;
+
+ box.extents.x1 = group->topTab->region->extents.x1 - 5;
+ box.extents.x2 = group->topTab->region->extents.x2 + 5;
+ box.extents.y1 = group->topTab->region->extents.y1 - 5;
+ box.extents.y2 = group->topTab->region->extents.y2 + 5;
+ } else
+ layer = NULL;
+ break;
+ case PAINT_TEXT:
+ if (bar->textLayer->state != PaintOff) {
+ layer = bar->textLayer;
+
+ h_scale = 1.0f;
+ w_scale = 1.0f;
+
+ box.extents.x1 = bar->region->extents.x1 + 5;
+ box.extents.x2 = bar->region->extents.x1 + bar->textLayer->texWidth + 5;
+ box.extents.y1 = bar->region->extents.y2 - bar->textLayer->texHeight - 5;
+ box.extents.y2 = bar->region->extents.y2 - 5;
+
+ if (box.extents.x2 > bar->region->extents.x2)
+ box.extents.x2 = bar->region->extents.x2;
+
+ // recalculate the alpha again...
+ if (bar->textLayer->state == PaintFadeIn)
+ alpha -= alpha * bar->textLayer->animationTime /
+ (gs->opt[GROUP_SCREEN_OPTION_FADE_TEXT_TIME].value.f * 1000);
+ else if (group->tabBar->textLayer->state == PaintFadeOut)
+ alpha = alpha * bar->textLayer->animationTime /
+ (gs->opt[GROUP_SCREEN_OPTION_FADE_TEXT_TIME].value.f * 1000);
+ } else
+ layer = NULL;
+ break;
+ default:
+ layer = NULL;
+ w_scale = 1.0f;
+ h_scale = 1.0f;
+ break;
+ }
+
+ if (layer) {
+ CompMatrix matrix = layer->texture.matrix;
+
+ // remove the old x1 and y1 so we have a relative value
+ box.extents.x2 -= box.extents.x1;
+ box.extents.y2 -= box.extents.y1;
+ box.extents.x1 = (box.extents.x1 - topTab->attrib.x) / w_scale + topTab->attrib.x;
+ box.extents.y1 = (box.extents.y1 - topTab->attrib.y) / h_scale + topTab->attrib.y;
+ // now add the new x1 and y1 so we have a absolute value again,
+ // also we don't want to stretch the texture...
+ if (box.extents.x2*w_scale < layer->texWidth)
+ box.extents.x2 += box.extents.x1;
+ else
+ box.extents.x2 = box.extents.x1 + layer->texWidth;
+ if (box.extents.y2*h_scale < layer->texHeight)
+ box.extents.y2 += box.extents.y1;
+ else
+ box.extents.y2 = box.extents.y1 + layer->texHeight;
+
+
+ matrix.x0 -= box.extents.x1 * matrix.xx;
+ matrix.y0 -= box.extents.y1 * matrix.yy;
+
+ attrib.xScale = w_scale;
+ attrib.yScale = h_scale;
+
+ topTab->vCount = 0;
+
+ addWindowGeometry(topTab, &matrix, 1, &box, clipRegion);
+
+ alpha = alpha * wAttrib->opacity / 0xffff;
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glColor4us(alpha, alpha, alpha, alpha);
+
+ (*group->screen->drawWindowTexture) (topTab,
+ &layer->texture, &attrib,
+ PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK);
+
+ screenTexEnvMode (s, GL_REPLACE);
+ glColor4usv(defaultColor);
+ }
+ }
+
+ s->drawWindowGeometry = oldDrawWindowGeometry;
+
+ glColor4usv(defaultColor);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_BLEND);
+
+ // draw thumbs
+ GLenum oldTextureFilter = s->display->textureFilter;
+
+ if (gs->opt[GROUP_SCREEN_OPTION_TAB_CREATE_MIPMAPS].value.b)
+ s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
+
+ for(slot = bar->slots; slot; slot = slot->next)
+ {
+ if(slot != gs->draggedSlot || !gs->dragged)
+ groupPaintThumb(group, slot, wAttrib->opacity);
+ }
+
+ s->display->textureFilter = oldTextureFilter;
+}
+
+/*
+ * groupPaintSelectionOutline
+ *
+ */
+static void
+groupPaintSelectionOutline (CompScreen *s, const ScreenPaintAttrib *sa,
+ int output, Bool transformed)
+{
+ GROUP_SCREEN(s);
+
+ 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->grabState == ScreenGrabSelect) {
+ glPushMatrix();
+
+ if (transformed) {
+ glLoadIdentity();
+ (s->applyScreenTransform) (s, sa, output);
+ prepareXCoords(s, output, -sa->zTranslate);
+ } else
+ 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);
+
+ glLineWidth(3);
+ glEnable(GL_LINE_SMOOTH);
+ 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();
+ glDisable(GL_LINE_SMOOTH);
+ glLineWidth(1); // back to default
+
+ glColor4usv(defaultColor);
+ glDisable(GL_BLEND);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glPopMatrix();
+ }
+}
+
+/*
+ * groupPreparePaintScreen
+ *
+ */
+void groupPreparePaintScreen(CompScreen * s, int msSinceLastPaint)
+{
+ GROUP_SCREEN(s);
+ GroupSelection *group;
+
+ UNWRAP(gs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP(gs, s, preparePaintScreen, groupPreparePaintScreen);
+
+ for (group = gs->groups; group; group = group->next)
+ {
+ GroupTabBar *bar = group->tabBar;
+
+ if (group->changeState != PaintOff)
+ group->changeAnimationTime -= msSinceLastPaint;
+
+ if (!bar)
+ continue;
+
+ groupApplyForces(s, bar, (gs->dragged)? gs->draggedSlot: NULL);
+ groupApplySpeeds(s, bar, msSinceLastPaint);
+
+ groupHandleHoverDetection(group);
+ groupHandleTabBarFade(group, msSinceLastPaint);
+ groupHandleTextFade(group, msSinceLastPaint);
+ }
+
+ groupHandleChanges(s);
+ groupDrawTabAnimation(s, msSinceLastPaint);
+
+ groupDequeueMoveNotifies (s);
+ groupDequeueGrabNotifies (s);
+ groupDequeueUngrabNotifies (s);
+}
+
+/*
+ * groupPaintScreen
+ *
+ */
+Bool
+groupPaintScreen(CompScreen * s,
+ const ScreenPaintAttrib * sAttrib,
+ Region region, int output, unsigned int mask)
+{
+ GROUP_SCREEN(s);
+ GroupSelection *group;
+ Bool status;
+
+ gs->painted = FALSE;
+ gs->vpX = s->x;
+ gs->vpY = s->y;
+
+ for (group = gs->groups; group; group = group->next)
+ {
+ if (group->changeState != PaintOff)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+ }
+
+ if (gs->tabBarVisible)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ UNWRAP(gs, s, paintScreen);
+ status = (*s->paintScreen) (s, sAttrib, region, output, mask);
+ WRAP(gs, s, paintScreen, groupPaintScreen);
+
+ gs->isRotating = FALSE;
+
+ if (status && !gs->painted) {
+ if ((gs->grabState == ScreenGrabTabDrag) && gs->draggedSlot) {
+ GROUP_WINDOW(gs->draggedSlot->window);
+
+ glPushMatrix();
+ prepareXCoords(s, output, -DEFAULT_Z_CAMERA);
+
+ // prevent tab bar drawing..
+ PaintState state = gw->group->tabBar->state;
+ gw->group->tabBar->state = PaintOff;
+ groupPaintThumb(NULL, gs->draggedSlot, 0xffff);
+ gw->group->tabBar->state = state;
+
+ glPopMatrix();
+ } else if (gs->grabState == ScreenGrabSelect) {
+ groupPaintSelectionOutline (s, sAttrib, output, FALSE);
+ }
+ }
+
+ return status;
+}
+
+/*
+ * groupaintTransformedScreen
+ *
+ */
+void
+groupPaintTransformedScreen(CompScreen * s, const ScreenPaintAttrib * sa,
+ Region region, int output, unsigned int mask)
+{
+ GROUP_SCREEN(s);
+
+ gs->isRotating = ((fmod(sa->xRotate, 90.0) != 0.0) || (fmod(sa->yRotate, 90.0) != 0.0) ||
+ (fmod(sa->vRotate, 90.0) != 0.0));
+
+ UNWRAP(gs, s, paintTransformedScreen);
+ (*s->paintTransformedScreen) (s, sa, region, output, mask);
+ WRAP(gs, s, paintTransformedScreen, groupPaintTransformedScreen);
+
+ if ((gs->vpX == s->x) && (gs->vpY == s->y)) {
+ gs->painted = TRUE;
+
+ if ((gs->grabState == ScreenGrabTabDrag) && gs->draggedSlot && gs->dragged) {
+ glPushMatrix();
+
+ glLoadIdentity();
+ (s->applyScreenTransform) (s, sa, output);
+ prepareXCoords(s, output, -sa->zTranslate);
+
+ groupPaintThumb(NULL, gs->draggedSlot, 0xffff);
+
+ glPopMatrix();
+ } else if (gs->grabState == ScreenGrabSelect) {
+ groupPaintSelectionOutline (s, sa, output, TRUE);
+ }
+ }
+}
+
+void groupRecomputeGlow (CompScreen *s)
+{
+ GROUP_SCREEN(s);
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ groupComputeGlowQuads (w, &gs->glowTexture.matrix);
+}
+
+/*
+ * groupDonePaintScreen
+ *
+ */
+void groupDonePaintScreen(CompScreen * s)
+{
+ GROUP_SCREEN(s);
+ GroupSelection *group;
+
+ UNWRAP(gs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP(gs, s, donePaintScreen, groupDonePaintScreen);
+
+ for(group = gs->groups; group; group = group->next)
+ {
+ if (group->doTabbing)
+ damageScreen(s);
+
+ if (group->changeState != PaintOff)
+ damageScreen(s);
+
+ if (group->tabBar && group->tabBar->state != PaintOff)
+ damageScreenRegion(s, group->tabBar->region);
+ }
+}
+
+void
+groupComputeGlowQuads (CompWindow *w, CompMatrix *matrix)
+{
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ BoxRec *box;
+ CompMatrix *quadMatrix;
+
+ if (gs->opt[GROUP_SCREEN_OPTION_GLOW].value.b && matrix) {
+ if (!gw->glowQuads)
+ gw->glowQuads = malloc (NUM_GLOWQUADS * sizeof(GlowQuad));
+ if (!gw->glowQuads)
+ return;
+ } else {
+ if (gw->glowQuads) {
+ free (gw->glowQuads);
+ gw->glowQuads = NULL;
+ }
+ return;
+ }
+
+ int glowSize = gs->opt[GROUP_SCREEN_OPTION_GLOW_SIZE].value.i;
+ int glowOffset = (glowSize * glowTextureProperties[gs->glowType].glowOffset /
+ glowTextureProperties[gs->glowType].textureSize) + 1;
+
+ /* Top left corner */
+ box = &gw->glowQuads[GLOWQUAD_TOPLEFT].box;
+ gw->glowQuads[GLOWQUAD_TOPLEFT].matrix = *matrix;
+ quadMatrix = &gw->glowQuads[GLOWQUAD_TOPLEFT].matrix;
+
+ box->x1 = WIN_REAL_X(w) - glowSize + glowOffset;
+ box->y1 = WIN_REAL_Y(w) - glowSize + glowOffset;
+ box->x2 = WIN_REAL_X(w) + glowOffset;
+ box->y2 = WIN_REAL_Y(w) + glowOffset;
+
+ quadMatrix->xx = 1.0f / glowSize;
+ quadMatrix->yy = -1.0f / glowSize;
+ quadMatrix->x0 = -(box->x1 * quadMatrix->xx);
+ quadMatrix->y0 = 1.0 -(box->y1 * quadMatrix->yy);
+
+ box->x2 = MIN(WIN_REAL_X(w) + glowOffset, WIN_REAL_X(w) + (WIN_REAL_WIDTH(w) / 2));
+ box->y2 = MIN(WIN_REAL_Y(w) + glowOffset, WIN_REAL_Y(w) + (WIN_REAL_HEIGHT(w) / 2));
+
+ /* Top right corner */
+ box = &gw->glowQuads[GLOWQUAD_TOPRIGHT].box;
+ gw->glowQuads[GLOWQUAD_TOPRIGHT].matrix = *matrix;
+ quadMatrix = &gw->glowQuads[GLOWQUAD_TOPRIGHT].matrix;
+
+ box->x1 = WIN_REAL_X(w) + WIN_REAL_WIDTH(w) - glowOffset;
+ box->y1 = WIN_REAL_Y(w) - glowSize + glowOffset;
+ box->x2 = WIN_REAL_X(w) + WIN_REAL_WIDTH(w) + glowSize - glowOffset;
+ box->y2 = WIN_REAL_Y(w) + glowOffset;
+
+ quadMatrix->xx = -1.0f / glowSize;
+ quadMatrix->yy = -1.0f / glowSize;
+ quadMatrix->x0 = 1.0 - (box->x1 * quadMatrix->xx);
+ quadMatrix->y0 = 1.0 - (box->y1 * quadMatrix->yy);
+
+ box->x1 = MAX(WIN_REAL_X(w) + WIN_REAL_WIDTH(w) - glowOffset,
+ WIN_REAL_X(w) + (WIN_REAL_WIDTH(w) / 2));
+ box->y2 = MIN(WIN_REAL_Y(w) + glowOffset,
+ WIN_REAL_Y(w) + (WIN_REAL_HEIGHT(w) / 2));
+
+ /* Bottom left corner */
+ box = &gw->glowQuads[GLOWQUAD_BOTTOMLEFT].box;
+ gw->glowQuads[GLOWQUAD_BOTTOMLEFT].matrix = *matrix;
+ quadMatrix = &gw->glowQuads[GLOWQUAD_BOTTOMLEFT].matrix;
+
+ box->x1 = WIN_REAL_X(w) - glowSize + glowOffset;
+ box->y1 = WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) - glowOffset;
+ box->x2 = WIN_REAL_X(w) + glowOffset;
+ box->y2 = WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) + glowSize - glowOffset;
+
+ quadMatrix->xx = 1.0f / glowSize;
+ quadMatrix->yy = 1.0f / glowSize;
+ quadMatrix->x0 = -(box->x1 * quadMatrix->xx);
+ quadMatrix->y0 = -(box->y1 * quadMatrix->yy);
+
+ box->y1 = MAX(WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) - glowOffset,
+ WIN_REAL_Y(w) + (WIN_REAL_HEIGHT(w) / 2));
+ box->x2 = MIN(WIN_REAL_X(w) + glowOffset,
+ WIN_REAL_X(w) + (WIN_REAL_WIDTH(w) / 2));
+
+ /* Bottom right corner */
+ box = &gw->glowQuads[GLOWQUAD_BOTTOMRIGHT].box;
+ gw->glowQuads[GLOWQUAD_BOTTOMRIGHT].matrix = *matrix;
+ quadMatrix = &gw->glowQuads[GLOWQUAD_BOTTOMRIGHT].matrix;
+
+ box->x1 = WIN_REAL_X(w) + WIN_REAL_WIDTH(w) - glowOffset;
+ box->y1 = WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) - glowOffset;
+ box->x2 = WIN_REAL_X(w) + WIN_REAL_WIDTH(w) + glowSize - glowOffset;
+ box->y2 = WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) + glowSize - glowOffset;
+
+ quadMatrix->xx = -1.0f / glowSize;
+ quadMatrix->yy = 1.0f / glowSize;
+ quadMatrix->x0 = 1.0 - (box->x1 * quadMatrix->xx);
+ quadMatrix->y0 = -(box->y1 * quadMatrix->yy);
+
+ box->x1 = MAX(WIN_REAL_X(w) + WIN_REAL_WIDTH(w) - glowOffset,
+ WIN_REAL_X(w) + (WIN_REAL_WIDTH(w) / 2));
+ box->y1 = MAX(WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) - glowOffset,
+ WIN_REAL_Y(w) + (WIN_REAL_HEIGHT(w) / 2));
+
+ /* Top edge */
+ box = &gw->glowQuads[GLOWQUAD_TOP].box;
+ gw->glowQuads[GLOWQUAD_TOP].matrix = *matrix;
+ quadMatrix = &gw->glowQuads[GLOWQUAD_TOP].matrix;
+
+ box->x1 = WIN_REAL_X(w) + glowOffset;
+ box->y1 = WIN_REAL_Y(w) - glowSize + glowOffset;
+ box->x2 = WIN_REAL_X(w) + WIN_REAL_WIDTH(w) - glowOffset;
+ box->y2 = WIN_REAL_Y(w) + glowOffset;
+
+ quadMatrix->xx = 0.0f;
+ quadMatrix->yy = -1.0f / glowSize;
+ quadMatrix->x0 = 1.0;
+ quadMatrix->y0 = 1.0 - (box->y1 * quadMatrix->yy);
+
+ /* Bottom edge */
+ box = &gw->glowQuads[GLOWQUAD_BOTTOM].box;
+ gw->glowQuads[GLOWQUAD_BOTTOM].matrix = *matrix;
+ quadMatrix = &gw->glowQuads[GLOWQUAD_BOTTOM].matrix;
+
+ box->x1 = WIN_REAL_X(w) + glowOffset;
+ box->y1 = WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) - glowOffset;
+ box->x2 = WIN_REAL_X(w) + WIN_REAL_WIDTH(w) - glowOffset;
+ box->y2 = WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) + glowSize - glowOffset;
+
+ quadMatrix->xx = 0.0f;
+ quadMatrix->yy = 1.0f / glowSize;
+ quadMatrix->x0 = 1.0;
+ quadMatrix->y0 = -(box->y1 * quadMatrix->yy);
+
+ /* Left edge */
+ box = &gw->glowQuads[GLOWQUAD_LEFT].box;
+ gw->glowQuads[GLOWQUAD_LEFT].matrix = *matrix;
+ quadMatrix = &gw->glowQuads[GLOWQUAD_LEFT].matrix;
+
+ box->x1 = WIN_REAL_X(w) - glowSize + glowOffset;
+ box->y1 = WIN_REAL_Y(w) + glowOffset;
+ box->x2 = WIN_REAL_X(w) + glowOffset;
+ box->y2 = WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) - glowOffset;
+
+ quadMatrix->xx = 1.0f / glowSize;
+ quadMatrix->yy = 0.0f;
+ quadMatrix->x0 = -(box->x1 * quadMatrix->xx);
+ quadMatrix->y0 = 0.0;
+
+ /* Right edge */
+ box = &gw->glowQuads[GLOWQUAD_RIGHT].box;
+ gw->glowQuads[GLOWQUAD_RIGHT].matrix = *matrix;
+ quadMatrix = &gw->glowQuads[GLOWQUAD_RIGHT].matrix;
+
+ box->x1 = WIN_REAL_X(w) + WIN_REAL_WIDTH(w) - glowOffset;
+ box->y1 = WIN_REAL_Y(w) + glowOffset;
+ box->x2 = WIN_REAL_X(w) + WIN_REAL_WIDTH(w) + glowSize - glowOffset;
+ box->y2 = WIN_REAL_Y(w) + WIN_REAL_HEIGHT(w) - glowOffset;
+
+ quadMatrix->xx = -1.0f / glowSize;
+ quadMatrix->yy = 0.0f;
+ quadMatrix->x0 = 1.0 - (box->x1 * quadMatrix->xx);
+ quadMatrix->y0 = 0.0;
+}
+
+/*
+ * groupDrawWindow
+ *
+ */
+Bool
+groupDrawWindow(CompWindow * w,
+ const WindowPaintAttrib * attrib,
+ Region region, unsigned int mask)
+{
+ Bool status;
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ if (!(mask & PAINT_WINDOW_SOLID_MASK) && gw->group && gw->group->nWins > 1 && gw->glowQuads) {
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ region = getInfiniteRegion();
+
+ if (region->numRects) {
+ REGION box;
+ int i;
+
+ box.rects = &box.extents;
+ box.numRects = 1;
+
+ w->vCount = 0;
+
+ for (i = 0; i < NUM_GLOWQUADS; i++) {
+ box.extents = gw->glowQuads[i].box;
+
+ if (box.extents.x1 < box.extents.x2 &&
+ box.extents.y1 < box.extents.y2) {
+ (*w->screen->addWindowGeometry) (w,
+ &gw->glowQuads[i].matrix, 1, &box, region);
+ }
+ }
+
+ if (w->vCount) {
+ WindowPaintAttrib wAttrib = *attrib;
+
+ wAttrib.opacity = OPAQUE;
+ wAttrib.saturation = COLOR;
+ wAttrib.brightness = BRIGHT;
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ //glBlendFunc(GL_SRC_ALPHA, GL_ONE); - maybe add an option for that...
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4us(gw->group->color[0], gw->group->color[1],
+ gw->group->color[2], attrib->opacity);
+
+ /* we use PAINT_WINDOW_TRANSFORMED_MASK here to force
+ the usage of a good texture filter */
+ (*w->screen->drawWindowTexture) (w, &gs->glowTexture, &wAttrib,
+ mask | PAINT_WINDOW_TRANSLUCENT_MASK |
+ PAINT_WINDOW_TRANSFORMED_MASK | PAINT_WINDOW_DECORATION_MASK);
+
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ screenTexEnvMode (w->screen, GL_REPLACE);
+ glColor4usv(defaultColor);
+ }
+ }
+ }
+
+ UNWRAP(gs, w->screen, drawWindow);
+ status = (*w->screen->drawWindow) (w, attrib, region, mask);
+ WRAP(gs, w->screen, drawWindow, groupDrawWindow);
+
+ return status;
+}
+
+/*
+ * groupPaintWindow
+ *
+ */
+Bool
+groupPaintWindow(CompWindow * w,
+ const WindowPaintAttrib * attrib,
+ Region region, unsigned int mask)
+{
+ Bool status;
+ Bool doRotate;
+ GROUP_SCREEN(w->screen);
+ GROUP_WINDOW(w);
+
+ WindowPaintAttrib gAttrib = *attrib;
+
+ if (gw->inSelection) {
+ int opacity = gs->opt[GROUP_SCREEN_OPTION_OPACITY].value.i;
+ int saturation = gs->opt[GROUP_SCREEN_OPTION_SATURATION].value.i;
+ int brightness = gs->opt[GROUP_SCREEN_OPTION_BRIGHTNESS].value.i;
+
+ opacity = OPAQUE * opacity / 100;
+ saturation = COLOR * saturation / 100;
+ brightness = BRIGHT * brightness / 100;
+
+ gAttrib.opacity = opacity;
+ gAttrib.saturation = saturation;
+ gAttrib.brightness = brightness;
+ } else if (gw->group && gw->group->tabbingState != PaintOff &&
+ (gw->animateState & (IS_ANIMATED | FINISHED_ANIMATION))) {
+ //fade the window out
+ float opacity;
+
+ int origDistanceX = (gw->orgPos.x - gw->destination.x);
+ int origDistanceY = (gw->orgPos.y - gw->destination.y);
+ float origDistance = sqrt(pow(origDistanceX, 2) + pow(origDistanceY,2));
+
+ float distanceX = (WIN_X(w) - gw->destination.x);
+ float distanceY = (WIN_Y(w) - gw->destination.y);
+ float distance = sqrt(pow(distanceX, 2) + pow(distanceY, 2));
+
+ if(distance > origDistance)
+ opacity = 100.0f;
+ else {
+ if(!origDistanceX && !origDistanceY) {
+ if (IS_TOP_TAB(w, gw->group) && (gw->group->tabbingState == PaintFadeIn))
+ opacity = 100.0f;
+ else
+ opacity = 0.0f;
+ } else
+ opacity = 100.0f * distance / origDistance;
+
+ if (gw->group->tabbingState == PaintFadeOut)
+ opacity = 100.0f - opacity;
+ }
+
+ gAttrib.opacity = gAttrib.opacity * opacity / 100;
+ }
+
+ doRotate = gw->group && (gw->group->changeState != PaintOff) &&
+ (IS_TOP_TAB(w, gw->group) || IS_PREV_TOP_TAB(w, gw->group));
+
+ if (doRotate)
+ {
+ float rotateAngle;
+ float timeLeft = gw->group->changeAnimationTime;
+
+ if(gw->group->changeState == PaintFadeIn)
+ timeLeft += gs->opt[GROUP_SCREEN_OPTION_CHANGE_ANIMATION_TIME].value.f * 500.0f;
+
+ rotateAngle = timeLeft * 180.0f / (gs->opt[GROUP_SCREEN_OPTION_CHANGE_ANIMATION_TIME].value.f * 1000.0f);
+ if (IS_PREV_TOP_TAB(w, gw->group))
+ rotateAngle += 180.0f;
+
+ if (gw->group->changeAnimationDirection < 0)
+ rotateAngle *= -1.0f;
+
+ glPushMatrix();
+
+ glScalef(1.0f, 1.0f, 1.0f / w->screen->width);
+
+ glTranslatef(WIN_X(w) + WIN_WIDTH(w)/2.0f, 0.0f, 0.0f);
+ glRotatef(rotateAngle, 0.0f, 1.0f, 0.0f);
+ glTranslatef(-WIN_X(w) - WIN_WIDTH(w)/2.0f, 0.0f, 0.0f);
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ }
+
+ UNWRAP(gs, w->screen, paintWindow);
+
+ status = (*w->screen->paintWindow) (w, &gAttrib, region, mask);
+
+ if (gw->group && gw->group->tabBar) {
+ if (HAS_TOP_WIN(gw->group) && IS_TOP_TAB(w, gw->group)) {
+ if ((gw->group->changeState == PaintOff) || (gw->group->changeState == PaintFadeOut))
+ groupPaintTabBar(gw->group, attrib, region);
+ } else if (IS_PREV_TOP_TAB(w, gw->group)) {
+ if (gw->group->changeState == PaintFadeIn)
+ groupPaintTabBar(gw->group, attrib, region);
+ }
+ }
+
+ WRAP(gs, w->screen, paintWindow, groupPaintWindow);
+
+ if(doRotate)
+ glPopMatrix();
+
+ return status;
+}
diff --git a/beryl-plugins/src/group/queues.c b/beryl-plugins/src/group/queues.c
new file mode 100644
index 0000000..70c8af8
--- /dev/null
+++ b/beryl-plugins/src/group/queues.c
@@ -0,0 +1,189 @@
+#include "group.h"
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * queues.c
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * 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.
+ *
+ **/
+
+/*
+ * functions enqueuing pending notifies
+ *
+ */
+
+void groupEnqueueMoveNotify (CompWindow *w, int dx, int dy, Bool sync)
+{
+ GroupPendingMoves *move;
+
+ GROUP_SCREEN (w->screen);
+
+ move = malloc (sizeof(GroupPendingMoves));
+
+ if (!move)
+ return;
+
+ move->w = w;
+ move->dx = dx;
+ move->dy = dy;
+ move->sync = sync;
+ move->next = NULL;
+
+ if (gs->pendingMoves)
+ {
+ GroupPendingMoves *temp;
+ for (temp = gs->pendingMoves; temp->next; temp = temp->next);
+
+ temp->next = move;
+ }
+
+ else
+ gs->pendingMoves = move;
+
+ /* damageScreen to make sure the queue is emptied */
+ addWindowDamage (w);
+}
+
+void groupDequeueMoveNotifies (CompScreen *s)
+{
+ GroupPendingMoves *move;
+
+ GROUP_SCREEN (s);
+
+ gs->queued = TRUE;
+
+ while (gs->pendingMoves)
+ {
+ move = gs->pendingMoves;
+ gs->pendingMoves = move->next;
+
+ moveWindow (move->w, move->dx, move->dy, TRUE, FALSE);
+ if (move->sync)
+ syncWindowPosition(move->w);
+
+ free (move);
+ }
+
+ gs->queued = FALSE;
+}
+
+void groupEnqueueGrabNotify (CompWindow *w, int x, int y,
+ unsigned int state, unsigned int mask)
+{
+ GroupPendingGrabs *grab;
+
+ GROUP_SCREEN (w->screen);
+
+ grab = malloc (sizeof(GroupPendingGrabs));
+
+ if (!grab)
+ return;
+
+ grab->w = w;
+ grab->x = x;
+ grab->y = y;
+ grab->state = state;
+ grab->mask = mask;
+ grab->next = NULL;
+
+ if (gs->pendingGrabs)
+ {
+ GroupPendingGrabs *temp;
+ for (temp = gs->pendingGrabs; temp->next; temp = temp->next);
+
+ temp->next = grab;
+ }
+ else
+ gs->pendingGrabs = grab;
+
+ /* damageScreen to make sure the queue is emptied */
+ addWindowDamage (w);
+}
+
+void groupDequeueGrabNotifies (CompScreen *s)
+{
+ GroupPendingGrabs *grab;
+
+ GROUP_SCREEN (s);
+
+ gs->queued = TRUE;
+
+ while (gs->pendingGrabs)
+ {
+ grab = gs->pendingGrabs;
+ gs->pendingGrabs = gs->pendingGrabs->next;
+
+ (*(grab->w)->screen->windowGrabNotify) (grab->w, grab->x, grab->y, grab->state, grab->mask);
+
+ free (grab);
+ }
+
+ gs->queued = FALSE;
+}
+
+void groupEnqueueUngrabNotify (CompWindow *w)
+{
+ GroupPendingUngrabs *ungrab;
+
+ GROUP_SCREEN (w->screen);
+
+ ungrab = malloc (sizeof(GroupPendingUngrabs));
+
+ if (!ungrab)
+ return;
+
+ ungrab->w = w;
+ ungrab->next = NULL;
+
+ if (gs->pendingUngrabs)
+ {
+ GroupPendingUngrabs *temp;
+ for (temp = gs->pendingUngrabs; temp->next; temp = temp->next);
+
+ temp->next = ungrab;
+ }
+
+ else
+ gs->pendingUngrabs = ungrab;
+
+ /* damageScreen to make sure the queue is emptied */
+ addWindowDamage (w);
+}
+
+void groupDequeueUngrabNotifies (CompScreen *s)
+{
+ GroupPendingUngrabs *ungrab;
+
+ GROUP_SCREEN (s);
+
+ gs->queued = TRUE;
+
+ while (gs->pendingUngrabs)
+ {
+ ungrab = gs->pendingUngrabs;
+ gs->pendingUngrabs = gs->pendingUngrabs->next;
+
+ (*(ungrab->w)->screen->windowUngrabNotify) (ungrab->w);
+
+ free (ungrab);
+ }
+
+ gs->queued = FALSE;
+}
diff --git a/beryl-plugins/src/group/selection.c b/beryl-plugins/src/group/selection.c
new file mode 100644
index 0000000..72394e5
--- /dev/null
+++ b/beryl-plugins/src/group/selection.c
@@ -0,0 +1,361 @@
+#include "group.h"
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * selection.c
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * 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.
+ *
+ **/
+
+/*
+ * groupWindowInRegion
+ *
+ */
+static Bool
+groupWindowInRegion(CompWindow *w, Region src, float precision)
+{
+ Region buf = XCreateRegion();
+ XIntersectRegion(w->region, src, buf);
+
+ // buf area
+ int i;
+ int area = 0;
+ BOX *box;
+ for(i = 0; i < buf->numRects; i++) {
+ box = &buf->rects[i];
+ area += (box->x2 - box->x1) * (box->y2 - box->y1); // width * height
+ }
+
+ XDestroyRegion(buf);
+
+ if (area >= WIN_WIDTH(w) * WIN_HEIGHT(w) * precision) {
+ XSubtractRegion(src, w->region, src);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * groupFindGroupInWindows
+ *
+ */
+static Bool groupFindGroupInWindows(GroupSelection *group, CompWindow **windows, int nWins)
+{
+ int i;
+ for (i = 0; i < nWins; i++) {
+ CompWindow *cw = windows[i];
+ GROUP_WINDOW(cw);
+ if (gw->group == group)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * groupFindWindowsInRegion
+ *
+ */
+CompWindow **groupFindWindowsInRegion(CompScreen * s, Region reg, int *c)
+{
+ GROUP_SCREEN(s);
+
+ float precision = gs->opt[GROUP_SCREEN_OPTION_PRECISION].value.i / 100.0f;
+
+ CompWindow **ret = NULL;
+ int count = 0;
+ CompWindow *w;
+ for (w = s->reverseWindows; w; w = w->prev) {
+ if ((gs->wMask & w->type) && !w->invisible &&
+ groupWindowInRegion(w, reg, precision))
+ {
+ GROUP_WINDOW(w);
+ if (gw->group && groupFindGroupInWindows(gw->group, ret, count))
+ continue;
+
+ 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;
+}
+
+/*
+ * groupDeleteSelectionWindow
+ *
+ */
+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
+ *
+ */
+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++;
+}
+
+/*
+ * groupSelectWindow
+ *
+ */
+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->group) {
+
+ groupAddWindowToSelection(d, w);
+
+ gw->inSelection = TRUE;
+ addWindowDamage(w);
+ }
+ // unselect single window
+ else if ((gs->wMask & w->type) &&
+ !w->invisible && gw->inSelection && !gw->group) {
+
+ groupDeleteSelectionWindow(d, w);
+ gw->inSelection = FALSE;
+ addWindowDamage(w);
+ }
+ // select group
+ else if ((gs->wMask & w->type) &&
+ !w->invisible && !gw->inSelection && gw->group) {
+
+ int i;
+ for (i = 0; i < gw->group->nWins; i++) {
+ CompWindow *cw = gw->group->windows[i];
+ GROUP_WINDOW(cw);
+
+ groupAddWindowToSelection(d, cw);
+
+ gw->inSelection = TRUE;
+ addWindowDamage(cw);
+ }
+ }
+ // Unselect group
+ else if ((gs->wMask & w->type) &&
+ !w->invisible && gw->inSelection && gw->group) {
+
+ GroupSelection *group = gw->group;
+ CompWindow **buf = gd->tmpSel.windows;
+ gd->tmpSel.windows = (CompWindow **) calloc(gd->tmpSel.nWins - gw->group->nWins, sizeof(CompWindow *));
+
+ int counter = 0;
+ int i;
+ for (i = 0; i < gd->tmpSel.nWins; i++) {
+ CompWindow *cw = buf[i];
+ GROUP_WINDOW(cw);
+
+ if (gw->group == group) {
+ gw->inSelection = FALSE;
+ addWindowDamage(cw);
+ continue;
+ }
+
+ gd->tmpSel.windows[counter++] = buf[i];
+ }
+ gd->tmpSel.nWins = counter;
+ free(buf);
+ }
+
+}
+
+/*
+ * groupSelectSingle
+ *
+ */
+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
+ *
+ */
+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->grabState == ScreenGrabNone) {
+ groupGrabScreen(w->screen, ScreenGrabSelect);
+
+ if (state & CompActionStateInitKey)
+ action->state |= CompActionStateTermKey;
+
+ if (state & CompActionStateInitButton)
+ action->state |= CompActionStateTermButton;
+
+ gs->x1 = gs->x2 = d->pointerX;
+ gs->y1 = gs->y2 = d->pointerY;
+ }
+
+ return TRUE;
+}
+
+/*
+ * groupSelectTerminate
+ *
+ */
+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->grabState == ScreenGrabSelect) {
+ groupGrabScreen(s, ScreenGrabNone);
+
+ if (gs->x1 != gs->x2 && gs->y1 != gs->y2) {
+ Region reg = XCreateRegion();
+ XRectangle rect;
+
+ rect.x = MIN(gs->x1, gs->x2) - 2;
+ rect.y = MIN(gs->y1, gs->y2) - 2;
+ rect.width = MAX(gs->x1, gs->x2) - MIN(gs->x1, gs->x2) + 4;
+ rect.height = MAX(gs->y1, gs->y2) - MIN(gs->y1, gs->y2) + 4;
+ XUnionRectWithRegion(&rect, reg, reg);
+
+ 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);
+ }
+
+ XDestroyRegion(reg);
+ }
+ }
+
+ }
+
+ action->state &=
+ ~(CompActionStateTermKey | CompActionStateTermButton);
+
+ return FALSE;
+}
+
+/*
+ * groupDamageSelectionRect
+ *
+ */
+void
+groupDamageSelectionRect(CompScreen* s, int xRoot, int yRoot)
+{
+ GROUP_SCREEN(s);
+ REGION reg;
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents.x1 = MIN(gs->x1, gs->x2) - 5;
+ reg.extents.y1 = MIN(gs->y1, gs->y2) - 5;
+ reg.extents.x2 = MAX(gs->x1, gs->x2) + 5;
+ reg.extents.y2 = MAX(gs->y1, gs->y2) + 5;
+ damageScreenRegion(s, &reg);
+
+ gs->x2 = xRoot;
+ gs->y2 = yRoot;
+
+ reg.extents.x1 = MIN(gs->x1, gs->x2) - 5;
+ reg.extents.y1 = MIN(gs->y1, gs->y2) - 5;
+ reg.extents.x2 = MAX(gs->x1, gs->x2) + 5;
+ reg.extents.y2 = MAX(gs->y1, gs->y2) + 5;
+ damageScreenRegion(s, &reg);
+}
diff --git a/beryl-plugins/src/group/tab.c b/beryl-plugins/src/group/tab.c
new file mode 100644
index 0000000..0fdbfaf
--- /dev/null
+++ b/beryl-plugins/src/group/tab.c
@@ -0,0 +1,2367 @@
+#include "group.h"
+
+/**
+ *
+ * Beryl group plugin
+ *
+ * tab.c
+ *
+ * Copyright : (C) 2006 by Patrick Niklaus, Roi Cohen, Danny Baumann
+ * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
+ * Roi Cohen <roico@beryl-project.org>
+ * Danny Baumann <maniac@beryl-project.org>
+ *
+ *
+ * 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.
+ *
+ **/
+
+/*
+ * groupGetCurrentMousePosition
+ *
+ */
+Bool groupGetCurrentMousePosition(CompScreen *s, int *x, int *y)
+{
+ unsigned int rmask;
+ int mouseX, mouseY, winX, winY;
+ Window root;
+ Window child;
+ Bool result;
+
+ result = XQueryPointer (s->display->display, s->root, &root,
+ &child, &mouseX, &mouseY, &winX, &winY, &rmask);
+
+ if (result) {
+ (*x) = mouseX;
+ (*y) = mouseY;
+ }
+
+ return result;
+}
+
+/*
+ * groupGetWindowTitle - mostely copied from state.c
+ *
+ */
+static char *groupGetWindowTitle(CompWindow *w)
+{
+ Atom type;
+ int format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ unsigned long *val;
+ Display *d = w->screen->display->display;
+ char *retval;
+ int result;
+ Atom utf8_string;
+
+ utf8_string = XInternAtom(d, "UTF8_STRING", 0);
+
+ type = None;
+ val = NULL;
+ result = XGetWindowProperty(d, w->id,
+ XInternAtom(d, "_NET_WM_NAME", 0), 0, LONG_MAX, False,
+ utf8_string, &type, &format, &nitems, &bytes_after,
+ (unsigned char **)&val);
+
+ if (result != Success)
+ return NULL;
+
+ if (type != utf8_string || format != 8 || nitems == 0)
+ {
+ if (val)
+ XFree(val);
+ return NULL;
+ }
+
+
+ retval = (char *)malloc(sizeof(char) * (nitems + 1));
+ strncpy(retval, (char *)val, nitems);
+ retval[nitems] = '\0';
+
+ XFree(val);
+
+ return retval;
+}
+
+/*
+ * groupGetClippingRegion
+ *
+ */
+Region groupGetClippingRegion(CompWindow *w)
+{
+ Region clip = XCreateRegion();
+ CompWindow *cw;
+ for(cw = w->next; cw; cw = cw->next)
+ {
+ if (!cw->invisible && !(cw->state & (CompWindowStateHiddenMask | CompWindowStateOffscreenMask))) {
+ Region buf = XCreateRegion();
+
+ XRectangle rect;
+ rect.x = WIN_REAL_X(cw);
+ rect.y = WIN_REAL_Y(cw);
+ rect.width = WIN_REAL_WIDTH(cw);
+ rect.height = WIN_REAL_HEIGHT(cw);
+ XUnionRectWithRegion(&rect, buf, buf);
+
+ XUnionRegion(clip, buf, clip);
+ XDestroyRegion(buf);
+ }
+ }
+
+ return clip;
+}
+
+/*
+ * groupTabBarTimeout
+ *
+ */
+static Bool
+groupTabBarTimeout(void *data)
+{
+ GroupSelection *group = (GroupSelection *) data;
+
+ groupTabSetVisibility(group, FALSE, PERMANENT);
+
+ group->tabBar->timeoutHandle = 0;
+
+ return FALSE; //This will free the timer.
+}
+
+/*
+ * groupCheckForVisibleTabBars
+ *
+ */
+void
+groupCheckForVisibleTabBars(CompScreen *s)
+{
+ GroupSelection *group;
+ GROUP_SCREEN(s);
+
+ gs->tabBarVisible = FALSE;
+
+ for (group = gs->groups; group; group = group->next) {
+ if (group->tabBar && (group->tabBar->state != PaintOff)) {
+ gs->tabBarVisible = TRUE;
+ break;
+ }
+ }
+}
+
+/*
+ * groupTabSetVisibility
+ *
+ */
+void groupTabSetVisibility(GroupSelection *group, Bool visible, unsigned int mask)
+{
+ // fixes bad crash...
+ if (!group || !group->windows || !group->tabBar || !HAS_TOP_WIN(group))
+ return;
+
+ GroupTabBar *bar = group->tabBar;
+ CompWindow *topTab = TOP_TAB(group);
+ GROUP_SCREEN(group->screen);
+ PaintState oldState;
+
+ oldState = bar->state;
+
+ /* hide tab bars for invisible top windows */
+ if ((topTab->state & (CompWindowStateHiddenMask |
+ CompWindowStateOffscreenMask)) || topTab->invisible)
+ {
+ bar->state = PaintOff;
+ groupSwitchTopTabInput(group, TRUE);
+ }
+
+ else if (visible && bar->state != PaintPermanentOn &&
+ (mask & PERMANENT))
+ {
+ bar->state = PaintPermanentOn;
+ groupSwitchTopTabInput(group, FALSE);
+
+ }
+
+ else if (visible &&
+ (bar->state == PaintOff || bar->state == PaintFadeOut))
+ {
+ bar->state = PaintFadeIn;
+ groupSwitchTopTabInput(group, FALSE);
+ }
+
+ else if (!visible && (bar->state != PaintPermanentOn || (mask & PERMANENT)) &&
+ (bar->state == PaintOn || bar->state == PaintPermanentOn || bar->state == PaintFadeIn))
+ {
+ bar->state = PaintFadeOut;
+ groupSwitchTopTabInput(group, TRUE);
+ }
+
+ if (bar->state != oldState && bar->state != PaintPermanentOn) // FIXME remove that when we have a new state for PaintPermanentFadeIn
+ bar->animationTime = (gs->opt[GROUP_SCREEN_OPTION_FADE_TIME].value.f * 1000) -
+ bar->animationTime;
+
+ groupCheckForVisibleTabBars(group->screen);
+}
+
+/*
+ * groupGetDrawOffsetForSlot
+ *
+ */
+void
+groupGetDrawOffsetForSlot(GroupTabBarSlot *slot, int *hoffset, int *voffset)
+{
+ if (!slot || !slot->window)
+ return;
+
+ CompWindow *w = slot->window;
+
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ if (slot != gs->draggedSlot) {
+ if (hoffset)
+ *hoffset = 0;
+ if (voffset)
+ *voffset = 0;
+
+ return;
+ }
+
+ int vx, vy;
+ int oldX = w->serverX;
+ int oldY = w->serverY;
+
+ if (gw->group) {
+ w->serverX = WIN_X(TOP_TAB(gw->group)) + WIN_WIDTH(TOP_TAB(gw->group)) / 2 - WIN_WIDTH(w) / 2;
+ w->serverY = WIN_Y(TOP_TAB(gw->group)) + WIN_HEIGHT(TOP_TAB(gw->group)) / 2 - WIN_HEIGHT(w) / 2;
+ }
+
+ defaultViewportForWindow(w, &vx, &vy);
+
+ if (hoffset)
+ *hoffset = ((w->screen->x - vx) % w->screen->hsize) * w->screen->width;
+
+ if (voffset)
+ *voffset = ((w->screen->y - vy) % w->screen->vsize) * w->screen->height;
+
+ w->serverX = oldX;
+ w->serverY = oldY;
+}
+
+/*
+ * groupHandleHoverDetection
+ *
+ */
+void groupHandleHoverDetection(GroupSelection *group)
+{
+ GROUP_SCREEN(group->screen);
+ GroupTabBar *bar = group->tabBar;
+
+ if (!HAS_TOP_WIN(group))
+ return;
+
+ CompWindow *topTab = TOP_TAB(group);
+
+ if (bar->state != PaintOff) { // Tab-bar is visible.
+ int mouseX, mouseY;
+ Bool mouseOnScreen;
+
+ mouseOnScreen = groupGetCurrentMousePosition(group->screen, &mouseX, &mouseY);
+
+ if (mouseOnScreen && !(bar->hoveredSlot && XPointInRegion(bar->hoveredSlot->region, mouseX, mouseY)))
+ {
+ bar->hoveredSlot = NULL;
+
+ Region clip;
+ clip = groupGetClippingRegion(topTab);
+
+ GroupTabBarSlot *slot;
+ for (slot = bar->slots; slot; slot = slot->next)
+ {
+ Region reg = XCreateRegion();
+ XSubtractRegion(slot->region, clip, reg);
+
+ if (XPointInRegion(reg, mouseX, mouseY))
+ {
+ bar->hoveredSlot = slot;
+ break;
+ }
+
+ XDestroyRegion(reg);
+ }
+
+ XDestroyRegion(clip);
+
+ if ((bar->textLayer->state == PaintFadeIn || bar->textLayer->state == PaintOn) &&
+ bar->hoveredSlot != bar->textSlot)
+ {
+ bar->textLayer->animationTime = (gs->opt[GROUP_SCREEN_OPTION_FADE_TEXT_TIME].value.f * 1000)
+ - bar->textLayer->animationTime;
+ bar->textLayer->state = PaintFadeOut;
+ }
+
+ else if (bar->textLayer->state == PaintFadeOut && bar->hoveredSlot == bar->textSlot && bar->hoveredSlot)
+ {
+ bar->textLayer->animationTime = (gs->opt[GROUP_SCREEN_OPTION_FADE_TEXT_TIME].value.f * 1000)
+ - bar->textLayer->animationTime;
+ bar->textLayer->state = PaintFadeIn;
+ }
+ }
+ }
+}
+
+/*
+ * groupHandleTabBarFade
+ *
+ */
+void groupHandleTabBarFade(GroupSelection *group, int msSinceLastPaint)
+{
+ GroupTabBar *bar = group->tabBar;
+
+ if ((bar->state == PaintFadeIn || bar->state == PaintFadeOut) &&
+ bar->animationTime > 0)
+ {
+ bar->animationTime -= msSinceLastPaint;
+
+ if (bar->animationTime < 0)
+ bar->animationTime = 0;
+
+ if (bar->animationTime == 0)
+ {
+ if (bar->state == PaintFadeIn) {
+ bar->state = PaintOn;
+ groupCheckForVisibleTabBars(group->screen);
+ }
+
+ else if (bar->state == PaintFadeOut) {
+ bar->state = PaintOff;
+ groupCheckForVisibleTabBars(group->screen);
+
+ if (bar->textLayer) {
+ // Tab-bar is no longer painted, clean up text animation variables.
+ bar->textLayer->animationTime = 0;
+ bar->textLayer->state = PaintOff;
+ bar->textSlot = bar->hoveredSlot = NULL;
+
+ groupRenderWindowTitle(group);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * groupHanldeTextFade
+ *
+ */
+void groupHandleTextFade(GroupSelection *group, int msSinceLastPaint)
+{
+ GROUP_SCREEN(group->screen);
+ GroupTabBar *bar = group->tabBar;
+ GroupCairoLayer *textLayer = bar->textLayer;
+
+ if (!textLayer)
+ return;
+
+ if ((textLayer->state == PaintFadeIn || textLayer->state == PaintFadeOut) &&
+ textLayer->animationTime > 0)
+ {
+ textLayer->animationTime -= msSinceLastPaint;
+
+ if (textLayer->animationTime < 0)
+ textLayer->animationTime = 0;
+
+ if (textLayer->animationTime == 0) {
+ if (textLayer->state == PaintFadeIn)
+ textLayer->state = PaintOn;
+
+ else if (textLayer->state == PaintFadeOut)
+ textLayer->state = PaintOff;
+ }
+ }
+
+ if (textLayer->state == PaintOff && bar->hoveredSlot)
+ {
+ // Start text animation for the new hovered slot.
+ bar->textSlot = bar->hoveredSlot;
+ textLayer->state = PaintFadeIn;
+ textLayer->animationTime = (gs->opt[GROUP_SCREEN_OPTION_FADE_TEXT_TIME].value.f * 1000);
+
+ groupRenderWindowTitle(group);
+ }
+
+ else if (textLayer->state == PaintOff && bar->textSlot)
+ {
+ // Clean Up.
+ bar->textSlot = NULL;
+ groupRenderWindowTitle(group);
+ }
+}
+
+/*
+ * groupHandleTabChange
+ *
+ */
+static void
+groupHandleTabChange(CompScreen *s, GroupSelection *group)
+{
+ GROUP_SCREEN(s);
+
+ if (!group || !HAS_TOP_WIN(group) || !group->changeTab)
+ return;
+
+ if (screenGrabExist(s, "rotate", "plane", 0))
+ return;
+
+ CompWindow* topTab = TOP_TAB(group);
+
+ if(group->tabbingState != PaintOff)
+ {
+ // if the previous top-tab window is being removed from the group, move the new top-tab window onscreen.
+ if(group->ungroupState == UngroupSingle && group->prevTopTab == NULL)
+ {
+ gs->queued = TRUE;
+ moveWindowOnscreen(topTab);
+ moveWindow(topTab, group->oldTopTabCenterX - WIN_X(topTab) - WIN_WIDTH(topTab) / 2,
+ group->oldTopTabCenterY - WIN_Y(topTab) - WIN_HEIGHT(topTab) / 2,
+ TRUE, TRUE);
+ syncWindowPosition(topTab);
+ gs->queued = FALSE;
+
+ // recalc here is needed (for y value)!
+ groupRecalcTabBarPos(group, (group->tabBar->region->extents.x1 + group->tabBar->region->extents.x2) / 2,
+ WIN_REAL_X(topTab),
+ WIN_REAL_X(topTab) + WIN_REAL_WIDTH(topTab));
+
+ group->prevTopTab = group->topTab;
+ }
+
+ return;
+ }
+
+ gs->queued = TRUE;
+ moveWindowOnscreen(topTab);
+ moveWindow(topTab, group->oldTopTabCenterX - WIN_X(topTab) - WIN_WIDTH(topTab) / 2,
+ group->oldTopTabCenterY - WIN_Y(topTab) - WIN_HEIGHT(topTab) / 2,
+ TRUE, TRUE);
+ syncWindowPosition(topTab);
+ gs->queued = FALSE;
+
+ activateWindow (topTab);
+
+ if(group->prevTopTab) {
+ //we use only the half time here - the second half will be PaintFadeOut
+ group->changeAnimationTime = gs->opt[GROUP_SCREEN_OPTION_CHANGE_ANIMATION_TIME].value.f * 500;
+
+ group->changeState = PaintFadeIn;
+
+ group->changeTab = FALSE;
+ }
+
+ else //No window to do animation with.
+ {
+ group->prevTopTab = group->topTab;
+ group->changeTab = FALSE;
+ }
+
+ return;
+}
+
+/*
+ * groupHandleAnimation
+ *
+ */
+static void
+groupHandleAnimation(CompScreen *s, GroupSelection *group)
+{
+ GROUP_SCREEN(s);
+
+ if(group->tabbingState != PaintOff || !HAS_TOP_WIN(group))
+ return;
+
+ if (screenGrabExist(s, "rotate", "plane", 0))
+ return;
+
+ if(group->changeState == PaintFadeIn && group->changeAnimationTime <= 0) {
+
+ // recalc here is needed (for y value)!
+ groupRecalcTabBarPos(group, (group->tabBar->region->extents.x1 + group->tabBar->region->extents.x2) / 2,
+ WIN_REAL_X(TOP_TAB(group)),
+ WIN_REAL_X(TOP_TAB(group)) + WIN_REAL_WIDTH(TOP_TAB(group)));
+
+ group->changeAnimationTime = gs->opt[GROUP_SCREEN_OPTION_CHANGE_ANIMATION_TIME].value.f * 500 + group->changeAnimationTime;
+
+ group->changeState = PaintFadeOut;
+ }
+
+ if (group->changeState == PaintFadeOut && group->changeAnimationTime <= 0)
+ {
+ if (group->prevTopTab)
+ moveWindowOffscreen(PREV_TOP_TAB(group));
+
+ group->prevTopTab = group->topTab;
+
+ group->changeState = PaintOff;
+ group->changeAnimationTime = 0;
+
+ if (group->nextTopTab) {
+ groupChangeTab(group->nextTopTab, group->nextDirection);
+ group->nextTopTab = NULL;
+
+ groupHandleTabChange(s, group);
+ }
+
+ else if (gs->opt[GROUP_SCREEN_OPTION_VISIBILITY_TIME].value.f != 0.0f) {
+ groupTabSetVisibility (group, TRUE, PERMANENT | SHOW_BAR_INSTANTLY_MASK);
+
+ if (group->tabBar->timeoutHandle)
+ compRemoveTimeout(group->tabBar->timeoutHandle);
+
+ group->tabBar->timeoutHandle = compAddTimeout (
+ gs->opt[GROUP_SCREEN_OPTION_VISIBILITY_TIME].value.f * 1000,
+ groupTabBarTimeout, group);
+ }
+ }
+
+ return;
+}
+
+/*
+ * groupHandleTab
+ *
+ */
+static void
+groupHandleTab(CompScreen *s, GroupSelection *group)
+{
+ if (group->tabbingState == PaintOff || group->doTabbing ||
+ !HAS_TOP_WIN(group) || !group->changeTab)
+ return;
+
+ GroupTabBarSlot *slot;
+
+ for(slot = group->tabBar->slots; slot; slot = slot->next) {
+ if (!slot->window)
+ continue;
+
+ CompWindow *w = slot->window;
+ GROUP_WINDOW(w);
+
+ if (slot == group->topTab || !(gw->animateState & FINISHED_ANIMATION) || gw->ungroup)
+ continue;
+
+ moveWindowOffscreen(w);
+ }
+
+ group->changeTab = FALSE;
+ group->prevTopTab = group->topTab;
+
+ return;
+}
+
+/*
+ * groupHandleTabbingAnimation
+ *
+ */
+static void
+groupHandleTabbingAnimation(CompScreen *s, GroupSelection *group)
+{
+ int i;
+
+ if (group->tabbingState == PaintOff || group->doTabbing)
+ return;
+
+ // Not animated any more.
+ group->tabbingState = PaintOff;
+ groupSyncWindows(group);
+
+ for(i = 0; i < group->nWins; i++) {
+ CompWindow *w = group->windows[i];
+ GROUP_WINDOW(w);
+
+ gw->animateState = 0;
+ }
+
+ return;
+}
+
+/*
+ * groupHandleUntab
+ *
+ */
+static void
+groupHandleUntab(CompScreen *s, GroupSelection *group)
+{
+ if (group->tabbingState == PaintOff || !group->doTabbing)
+ return;
+
+ if (group->topTab || !group->changeTab)
+ return;
+
+ groupDeleteTabBar(group);
+
+ group->changeAnimationTime = 0;
+ group->changeState = PaintOff;
+ group->nextTopTab = NULL;
+
+ group->changeTab = FALSE;
+ group->prevTopTab = group->topTab;
+
+ return;
+}
+
+/*
+ * groupHandleUngroup
+ *
+ */
+static Bool
+groupHandleUngroup(CompScreen *s, GroupSelection *group)
+{
+ int i;
+ GROUP_SCREEN(s);
+
+ if((group->ungroupState == UngroupSingle) && group->doTabbing && group->changeTab)
+ {
+ for(i = 0; i < group->nWins; i++) {
+ CompWindow *w = group->windows[i];
+ GROUP_WINDOW(w);
+
+ if (gw->ungroup)
+ {
+ gs->queued = TRUE;
+ moveWindowOnscreen(w);
+ moveWindow(w, group->oldTopTabCenterX - WIN_X(w) - WIN_WIDTH(w) / 2,
+ group->oldTopTabCenterY - WIN_Y(w) - WIN_HEIGHT(w) / 2,
+ TRUE, TRUE);
+ syncWindowPosition(w);
+ gs->queued = FALSE;
+ }
+ }
+
+ group->changeTab = FALSE;
+
+ }
+
+ if ((group->ungroupState == UngroupSingle) && !group->doTabbing) {
+ Bool morePending;
+
+ do {
+ morePending = FALSE;
+
+ for(i = 0;i < group->nWins; i++) {
+ CompWindow *w = group->windows[i];
+ GROUP_WINDOW(w);
+
+ if (gw->ungroup) {
+ groupDeleteGroupWindow(w, TRUE);
+ gw->ungroup = FALSE;
+ morePending = TRUE;
+ }
+ }
+ } while (morePending);
+
+ group->ungroupState = UngroupNone;
+ }
+
+ if (group->prev)
+ {
+ if ((group->prev->ungroupState == UngroupAll) && !group->prev->doTabbing)
+ groupDeleteGroup(group->prev);
+ }
+ if (!group->next) {
+ if ((group->ungroupState == UngroupAll) && !group->doTabbing) {
+ groupDeleteGroup(group);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * groupHandleChanges
+ *
+ */
+void
+groupHandleChanges(CompScreen* s)
+{
+ GROUP_SCREEN(s);
+
+ GroupSelection *group;
+
+ for(group = gs->groups; group; group = group ? group->next : NULL)
+ {
+ groupHandleUntab(s, group);
+ groupHandleTab(s, group);
+ groupHandleTabbingAnimation(s, group);
+ groupHandleTabChange(s, group);
+ groupHandleAnimation(s, group);
+
+ if (!groupHandleUngroup(s, group))
+ group = NULL;
+ }
+}
+
+/* adjust velocity for each animation step (adapted from the scale plugin) */
+static int
+adjustTabVelocity(CompWindow * w)
+{
+ float dx, dy, adjust, amount;
+ float x1, y1;
+
+ GROUP_WINDOW(w);
+
+ x1 = y1 = 0.0;
+
+ if (!(gw->animateState & IS_ANIMATED))
+ return 0;
+
+ x1 = gw->destination.x;
+ y1 = gw->destination.y;
+
+ dx = x1 - (w->serverX + gw->tx);
+
+ adjust = dx * 0.15f;
+ amount = fabs(dx) * 1.5f;
+ if (amount < 0.5f)
+ amount = 0.5f;
+ else if (amount > 5.0f)
+ amount = 5.0f;
+
+ gw->xVelocity = (amount * gw->xVelocity + adjust) / (amount + 1.0f);
+
+ dy = y1 - (w->serverY + gw->ty);
+
+ adjust = dy * 0.15f;
+ amount = fabs(dy) * 1.5f;
+ if (amount < 0.5f)
+ amount = 0.5f;
+ else if (amount > 5.0f)
+ amount = 5.0f;
+
+ gw->yVelocity = (amount * gw->yVelocity + adjust) / (amount + 1.0f);
+
+ if (fabs(dx) < 0.1f && fabs(gw->xVelocity) < 0.2f &&
+ fabs(dy) < 0.1f && fabs(gw->yVelocity) < 0.2f)
+ {
+ gw->xVelocity = gw->yVelocity = 0.0f;
+ gw->tx = x1 - w->serverX;
+ gw->ty = y1 - w->serverY;
+
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * groupDrawTabAnimation
+ *
+ */
+void groupDrawTabAnimation(CompScreen * s, int msSinceLastPaint)
+{
+ GROUP_SCREEN(s);
+
+ int i;
+
+ GroupSelection *group;
+ for(group = gs->groups; group; group = group->next)
+ {
+ if(group->tabbingState == PaintOff)
+ continue;
+
+ int steps, dx, dy;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f *
+ gs->opt[GROUP_SCREEN_OPTION_TABBING_SPEED].value.f;
+ steps = amount / (0.5f *
+ gs->opt[GROUP_SCREEN_OPTION_TABBING_TIMESTEP].value.f);
+ if (!steps)
+ steps = 1;
+ chunk = amount / (float)steps;
+
+ while (steps--) {
+ group->doTabbing = FALSE;
+
+ for(i = 0; i < group->nWins; i++) {
+ CompWindow *cw = group->windows[i];
+
+ if(!cw)
+ continue;
+
+ GROUP_WINDOW(cw);
+
+ if (!(gw->animateState & IS_ANIMATED))
+ continue;
+
+ if (!adjustTabVelocity(cw))
+ {
+ gw->animateState |= FINISHED_ANIMATION;
+ gw->animateState &= ~(IS_ANIMATED);
+ }
+
+ gw->tx += gw->xVelocity * chunk;
+ gw->ty += gw->yVelocity * chunk;
+
+ dx = (cw->serverX + gw->tx) - cw->attrib.x;
+ dy = (cw->serverY + gw->ty) - cw->attrib.y;
+
+ group->doTabbing |= (gw->animateState & IS_ANIMATED);
+
+ gs->queued = TRUE;
+ moveWindow(cw, dx, dy, FALSE, FALSE);
+ gs->queued = FALSE;
+ }
+ if (!group->doTabbing)
+ break;
+ }
+ }
+}
+
+/*
+ * groupUpdateTabBars
+ *
+ */
+Bool groupUpdateTabBars(void *display)
+{
+ CompDisplay *d = (CompDisplay*) display;
+
+ GroupTabBar *bar;
+ CompWindow *topTab;
+ GroupSelection *group;
+
+ Bool titleBarMouseOver;
+ Bool tabBarMouseOver;
+
+ int mouseX, mouseY;
+
+ CompScreen *s;
+
+ for (s = d->screens; s; s = s->next) {
+ if (groupGetCurrentMousePosition(s, &mouseX, &mouseY))
+ break;
+ }
+
+ if (!s)
+ return TRUE;
+
+ GROUP_SCREEN(s);
+
+ for(group = gs->groups; group; group = group->next)
+ {
+ if(!group->tabBar || !HAS_TOP_WIN(group))
+ continue;
+
+ topTab = TOP_TAB(group);
+
+ if (topTab->state & (CompWindowStateHiddenMask | CompWindowStateOffscreenMask))
+ continue;
+
+ if (topTab->invisible)
+ continue;
+
+ bar = group->tabBar;
+
+ // create clipping region
+ Region clip = groupGetClippingRegion(topTab);
+
+ // title bar
+ Region reg = XCreateRegion();
+ XRectangle rect;
+ rect.x = WIN_X(topTab) - topTab->input.left;
+ rect.y = WIN_Y(topTab) - topTab->input.top;
+ rect.width = WIN_WIDTH(topTab) + topTab->input.right;
+ rect.height = WIN_Y(topTab) - rect.y;
+ XUnionRectWithRegion(&rect, reg, reg);
+
+ // clip title bar with stack
+ XSubtractRegion(reg, clip, reg);
+
+ titleBarMouseOver = XPointInRegion(reg, mouseX, mouseY);
+ XDestroyRegion(reg);
+
+ // clip tab bar with stack
+ reg = XCreateRegion();
+ XSubtractRegion(bar->region, clip, reg);
+
+ if (bar->state != PaintOff && bar->state != PaintFadeOut)
+ tabBarMouseOver = XPointInRegion(reg, mouseX, mouseY);
+ else
+ tabBarMouseOver = FALSE;
+
+ if ((bar->state == PaintOff || bar->state == PaintFadeOut) && titleBarMouseOver) {
+ groupRecalcTabBarPos(group, mouseX, WIN_REAL_X(topTab),
+ WIN_REAL_X(topTab) + WIN_REAL_WIDTH(topTab));
+ addWindowDamage(topTab);
+ }
+
+ XDestroyRegion(reg);
+ XDestroyRegion(clip);
+
+ groupTabSetVisibility (group, titleBarMouseOver | tabBarMouseOver, 0);
+ }
+
+ return TRUE;
+}
+
+/*
+ * groupGetConstrainRegion
+ *
+ */
+static Region groupGetConstrainRegion(CompScreen *s)
+{
+ CompWindow *w;
+ Region region;
+ REGION r;
+ int i;
+
+ region = XCreateRegion ();
+ if (!region)
+ return NULL;
+
+ for (i = 0;i < s->nOutputDev; i++)
+ XUnionRegion (&s->outputDev[i].region, region, region);
+
+ r.rects = &r.extents;
+ r.numRects = r.size = 1;
+
+ for (w = s->windows; w; w = w->next) {
+ if (!w->mapNum)
+ continue;
+
+ if (w->struts) {
+ r.extents.x1 = w->struts->top.x;
+ r.extents.y1 = w->struts->top.y;
+ r.extents.x2 = r.extents.x1 + w->struts->top.width;
+ r.extents.y2 = r.extents.y1 + w->struts->top.height;
+
+ XSubtractRegion (region, &r, region);
+
+ r.extents.x1 = w->struts->bottom.x;
+ r.extents.y1 = w->struts->bottom.y;
+ r.extents.x2 = r.extents.x1 + w->struts->bottom.width;
+ r.extents.y2 = r.extents.y1 + w->struts->bottom.height;
+
+ XSubtractRegion (region, &r, region);
+
+ r.extents.x1 = w->struts->left.x;
+ r.extents.y1 = w->struts->left.y;
+ r.extents.x2 = r.extents.x1 + w->struts->left.width;
+ r.extents.y2 = r.extents.y1 + w->struts->left.height;
+
+ XSubtractRegion (region, &r, region);
+
+ r.extents.x1 = w->struts->right.x;
+ r.extents.y1 = w->struts->right.y;
+ r.extents.x2 = r.extents.x1 + w->struts->right.width;
+ r.extents.y2 = r.extents.y1 + w->struts->right.height;
+
+ XSubtractRegion (region, &r, region);
+ }
+ }
+
+ return region;
+}
+
+/*
+ * groupConstrainMovement
+ *
+ */
+static Bool groupConstrainMovement(CompWindow *w, Region constrainRegion, int dx, int dy,
+ int *new_dx, int *new_dy)
+{
+ GROUP_WINDOW(w);
+ int status;
+ int origDx = dx, origDy = dy;
+ int x, y, width, height;
+
+ if (!gw->group)
+ return FALSE;
+
+ if (!dx && !dy)
+ return FALSE;
+
+ x = gw->orgPos.x - w->input.left + dx;
+ y = gw->orgPos.y - w->input.top + dy;
+ width = WIN_REAL_WIDTH(w);
+ height = WIN_REAL_HEIGHT(w);
+
+ status = XRectInRegion(constrainRegion, x, y, width, height);
+
+ int xStatus = status;
+ while (dx && (xStatus != RectangleIn)) {
+ xStatus = XRectInRegion(constrainRegion, x, y - dy,
+ width, height);
+
+ if (xStatus != RectangleIn)
+ dx += (dx < 0) ? 1 : -1;
+
+ x = gw->orgPos.x - w->input.left + dx;
+ }
+
+ while (dy && (status != RectangleIn)) {
+ status = XRectInRegion(constrainRegion, x, y,
+ width, height);
+
+ if (status != RectangleIn)
+ dy += (dy < 0) ? 1 : -1;
+
+ y = gw->orgPos.y - w->input.top + dy;
+ }
+
+ if (new_dx)
+ *new_dx = dx;
+
+ if (new_dy)
+ *new_dy = dy;
+
+ if ((dx != origDx) || (dy != origDy))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ * groupApplyConstrainingToWindows
+ *
+ */
+static void groupApplyConstrainingToWindows(GroupSelection *group,
+ Region constrainRegion, Window constrainedWindow, int dx, int dy)
+{
+ int i;
+ CompWindow *w;
+
+ if (!dx && !dy)
+ return;
+
+ for (i = 0; i < group->nWins; i++) {
+ w = group->windows[i];
+ GROUP_WINDOW(w);
+
+ /* ignore certain windows: we don't want to apply
+ the constraining results on the constrained window
+ itself, not do we want to change the target position
+ of unamimated windows and of windows which
+ already are constrained */
+ if (w->id == constrainedWindow)
+ continue;
+
+ if(!(gw->animateState & IS_ANIMATED))
+ continue;
+
+ if (gw->animateState & DONT_CONSTRAIN)
+ continue;
+
+ if (!(gw->animateState & CONSTRAINED_X)) {
+ gw->animateState |= IS_ANIMATED;
+
+ /* applying the constraining result of another window
+ might move the window offscreen, too, so check
+ if this is not the case */
+ if (groupConstrainMovement(w, constrainRegion, dx, 0, &dx, NULL))
+ gw->animateState |= CONSTRAINED_X;
+
+ gw->destination.x += dx;
+ gw->orgPos.x += dx;
+ }
+
+ if (!(gw->animateState & CONSTRAINED_Y)) {
+ gw->animateState |= IS_ANIMATED;
+
+ /* analog to X case */
+ if (groupConstrainMovement(w, constrainRegion, 0, dy, NULL, &dy))
+ gw->animateState |= CONSTRAINED_Y;
+
+ gw->destination.y += dy;
+ gw->orgPos.y += dy;
+ }
+ }
+}
+
+/*
+ * groupStartTabbingAnimation
+ *
+ */
+void groupStartTabbingAnimation(GroupSelection *group, Bool tab)
+{
+ if (!group || (group->tabbingState != PaintOff))
+ return;
+
+ CompScreen *s = group->windows[0]->screen;
+ int i;
+ int dx, dy;
+ int constrainStatus;
+
+ group->doTabbing = TRUE;
+ group->changeTab = TRUE;
+
+ group->tabbingState = (tab) ? PaintFadeIn : PaintFadeOut;
+
+ if (!tab) {
+ /* we need to set up the X/Y constraining on untabbing */
+ Region constrainRegion = groupGetConstrainRegion(s);
+ Bool constrainedWindows = TRUE;
+
+ if (!constrainRegion)
+ return;
+
+ /* reset all flags */
+ for (i = 0; i < group->nWins; i++) {
+ GROUP_WINDOW(group->windows[i]);
+ gw->animateState &= ~(CONSTRAINED_X | CONSTRAINED_Y | DONT_CONSTRAIN);
+ }
+
+ /* as we apply the constraining in a flat loop,
+ we may need to run multiple times through this
+ loop until all constraining dependencies are met */
+ while (constrainedWindows) {
+ constrainedWindows = FALSE;
+ /* loop through all windows and try to constrain their
+ animation path (going from gw->orgPos to
+ gw->destination) to the active screen area */
+ for (i = 0; i < group->nWins; i++) {
+ CompWindow *w = group->windows[i];
+ GROUP_WINDOW(w);
+
+ /* ignore windows which aren't animated and/or
+ already are at the edge of the screen area */
+ if (!(gw->animateState & IS_ANIMATED))
+ continue;
+
+ if (gw->animateState & DONT_CONSTRAIN)
+ continue;
+
+ /* is the original position inside the screen area? */
+ constrainStatus = XRectInRegion(constrainRegion,
+ gw->orgPos.x - w->input.left,
+ gw->orgPos.y - w->input.top,
+ WIN_REAL_WIDTH(w), WIN_REAL_HEIGHT(w));
+
+ /* constrain the movement */
+ if (groupConstrainMovement(w, constrainRegion,
+ gw->destination.x - gw->orgPos.x,
+ gw->destination.y - gw->orgPos.y, &dx, &dy)) {
+ /* handle the case where the window is outside the screen
+ area on its whole animation path */
+ if (constrainStatus != RectangleIn && !dx && !dy) {
+ gw->animateState |= DONT_CONSTRAIN;
+ gw->animateState |= CONSTRAINED_X | CONSTRAINED_Y;
+
+ /* use the original position as last resort */
+ gw->destination.x = gw->mainTabOffset.x;
+ gw->destination.y = gw->mainTabOffset.y;
+ } else {
+ /* if we found a valid target position, apply
+ the change also to other windows to retain
+ the distance between the windows */
+ groupApplyConstrainingToWindows(group,
+ constrainRegion, w->id,
+ dx - gw->destination.x + gw->orgPos.x,
+ dy - gw->destination.y + gw->orgPos.y);
+
+ /* if we hit constraints, adjust the mask and the
+ target position accordingly */
+ if (dx != (gw->destination.x - gw->orgPos.x)) {
+ gw->animateState |= CONSTRAINED_X;
+ gw->destination.x = gw->orgPos.x + dx;
+ }
+
+ if (dy != (gw->destination.y - gw->orgPos.y)) {
+ gw->animateState |= CONSTRAINED_Y;
+ gw->destination.y = gw->orgPos.y + dy;
+ }
+
+ constrainedWindows = TRUE;
+ }
+ }
+ }
+ }
+ XDestroyRegion(constrainRegion);
+ }
+}
+
+/*
+ * groupTabGroup
+ *
+ */
+void groupTabGroup(CompWindow *main)
+{
+ GROUP_WINDOW(main);
+ GROUP_SCREEN(main->screen);
+ GroupSelection *group = gw->group;
+
+ if(!group || group->tabBar)
+ return;
+
+ groupInitTabBar(group, main);
+ groupCreateInputPreventionWindow(group);
+
+ group->tabbingState = PaintOff;
+ groupChangeTab(gw->slot, RotateUncertain); //Slot is initialized after groupInitTabBar(group);
+ groupRecalcTabBarPos(gw->group, WIN_X(main) + WIN_WIDTH(main)/2,
+ WIN_X(main), WIN_X(main) + WIN_WIDTH(main));
+
+ int width, height;
+ width = group->tabBar->region->extents.x2 - group->tabBar->region->extents.x1;
+ height = group->tabBar->region->extents.y2 - group->tabBar->region->extents.y1;
+
+ group->tabBar->textLayer = groupCreateCairoLayer(main->screen, width, height);
+ group->tabBar->textLayer->state = PaintOff;
+ group->tabBar->textLayer->animationTime = 0;
+ groupRenderWindowTitle(group);
+ group->tabBar->textLayer->animationTime = gs->opt[GROUP_SCREEN_OPTION_FADE_TEXT_TIME].value.f * 1000;
+ group->tabBar->textLayer->state = PaintFadeIn;
+
+ group->tabBar->bgLayer = groupCreateCairoLayer(main->screen, width, height);
+ group->tabBar->bgLayer->state = PaintOn;
+ group->tabBar->bgLayer->animationTime = 0;
+ groupRenderTabBarBackground(group);
+
+ width = group->topTab->region->extents.x2 - group->topTab->region->extents.x1;
+ height = group->topTab->region->extents.y2 - group->topTab->region->extents.y1;
+
+ group->tabBar->selectionLayer = groupCreateCairoLayer(main->screen, width, height);
+ group->tabBar->selectionLayer->state = PaintOn;
+ group->tabBar->selectionLayer->animationTime = 0;
+ groupRenderTopTabHighlight(group);
+
+ if(!HAS_TOP_WIN(group))
+ return;
+
+ GroupTabBarSlot *slot;
+
+ for(slot = group->tabBar->slots; slot; slot = slot->next) {
+ CompWindow *cw = slot->window;
+
+ GROUP_WINDOW(cw);
+
+ int x = WIN_X(cw), y = WIN_Y(cw);
+
+ if(gw->animateState & IS_ANIMATED)
+ {
+ x = gw->destination.x;
+ y = gw->destination.y;
+ }
+
+ // center the window to the main window
+ gw->destination.x = WIN_X(main) + (WIN_WIDTH(main) / 2) - (WIN_WIDTH(cw) / 2);
+ gw->destination.y = WIN_Y(main) + (WIN_HEIGHT(main) / 2) - (WIN_HEIGHT(cw) / 2);
+
+ gw->mainTabOffset.x = x - gw->destination.x; //Distance from destination.
+ gw->mainTabOffset.y = y - gw->destination.y;
+
+ gw->orgPos.x = WIN_X(cw);
+ gw->orgPos.y = WIN_Y(cw);
+
+ gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f;
+
+ gw->animateState |= IS_ANIMATED;
+ }
+
+ groupStartTabbingAnimation(group, TRUE);
+}
+
+/*
+ * groupUntabGroup
+ *
+ */
+void
+groupUntabGroup(GroupSelection *group)
+{
+ if(!HAS_TOP_WIN(group))
+ return;
+
+ GROUP_WINDOW(TOP_TAB(group));
+ GROUP_SCREEN(TOP_TAB(group)->screen);
+
+ int mainOrgPosX = gw->mainTabOffset.x;
+ int mainOrgPosY = gw->mainTabOffset.y;
+ int oldX, oldY;
+
+ CompWindow* prevTopTab;
+
+ if(group->prevTopTab)
+ prevTopTab = PREV_TOP_TAB(group);
+ else
+ prevTopTab = TOP_TAB(group); //If prevTopTab isn't set, we have no choice but using topTab.
+ //It happens when there is still animation, which means the tab wasn't changed anyway.
+
+ group->oldTopTabCenterX = WIN_X(prevTopTab) + WIN_WIDTH(prevTopTab)/2;
+ group->oldTopTabCenterY = WIN_Y(prevTopTab) + WIN_HEIGHT(prevTopTab)/2;
+
+ group->topTab = NULL;
+
+ GroupTabBarSlot *slot;
+
+ for(slot = group->tabBar->slots; slot; slot = slot->next) {
+
+ CompWindow *cw = slot->window;
+
+ GROUP_WINDOW(cw);
+
+ gs->queued = TRUE;
+ moveWindowOnscreen(cw);
+ moveWindow(cw, group->oldTopTabCenterX - WIN_X(cw) - WIN_WIDTH(cw) / 2,
+ group->oldTopTabCenterY - WIN_Y(cw) - WIN_HEIGHT(cw) / 2,
+ TRUE, TRUE);
+ syncWindowPosition(cw);
+ gs->queued = FALSE;
+
+ /* save the old original position - we might need it if constraining fails */
+ oldX = gw->orgPos.x;
+ oldY = gw->orgPos.y;
+
+ gw->orgPos.x = group->oldTopTabCenterX - WIN_WIDTH(cw) / 2;
+ gw->orgPos.y = group->oldTopTabCenterY - WIN_HEIGHT(cw) / 2;
+
+ gw->destination.x = WIN_X(prevTopTab) + WIN_WIDTH(prevTopTab)/2 - WIN_WIDTH(cw)/2 + gw->mainTabOffset.x - mainOrgPosX;
+ gw->destination.y = WIN_Y(prevTopTab) + WIN_HEIGHT(prevTopTab)/2 - WIN_HEIGHT(cw)/2 + gw->mainTabOffset.y - mainOrgPosY;
+
+ gw->mainTabOffset.x = oldX;
+ gw->mainTabOffset.y = oldY;
+
+ gw->animateState |= IS_ANIMATED;
+ gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f;
+ }
+
+ group->tabbingState = PaintOff;
+ groupStartTabbingAnimation(group, FALSE);
+
+ damageScreen(group->screen);
+}
+
+/*
+ * groupChangeTab
+ *
+ */
+Bool
+groupChangeTab(GroupTabBarSlot* topTab, ChangeTabAnimationDirection direction)
+{
+ if(!topTab)
+ return TRUE;
+
+ CompWindow* w = topTab->window;
+ GROUP_WINDOW(w);
+
+ if(!gw->group || gw->group->topTab == topTab || gw->group->tabbingState != PaintOff)
+ return TRUE;
+
+ if (gw->group->prevTopTab && gw->group->changeState == PaintOff) {
+ gw->group->oldTopTabCenterX = WIN_X(PREV_TOP_TAB(gw->group)) + WIN_WIDTH(PREV_TOP_TAB(gw->group))/2;
+ gw->group->oldTopTabCenterY = WIN_Y(PREV_TOP_TAB(gw->group)) + WIN_HEIGHT(PREV_TOP_TAB(gw->group))/2;
+ }
+
+ if(gw->group->changeState != PaintOff)
+ gw->group->nextDirection = direction;
+ else if (direction == RotateLeft)
+ gw->group->changeAnimationDirection = 1;
+ else if (direction == RotateRight)
+ gw->group->changeAnimationDirection = -1;
+ else {
+ int distanceOld = 0, distanceNew = 0;
+ GroupTabBarSlot *slot;
+
+ if (gw->group->topTab)
+ for (slot = gw->group->tabBar->slots; slot && (slot != gw->group->topTab);
+ slot = slot->next, distanceOld++);
+
+ for (slot = gw->group->tabBar->slots; slot && (slot != topTab);
+ slot = slot->next, distanceNew++);
+
+ if (distanceNew < distanceOld)
+ gw->group->changeAnimationDirection = 1; //left
+ else
+ gw->group->changeAnimationDirection = -1; //right
+
+ //check if the opposite direction is shorter
+ if (abs(distanceNew - distanceOld) > (gw->group->tabBar->nSlots / 2))
+ gw->group->changeAnimationDirection *= -1;
+ }
+
+ if(gw->group->changeState != PaintOff)
+ gw->group->nextTopTab = topTab;
+
+ else
+ {
+ gw->group->topTab = topTab;
+ gw->group->changeTab = TRUE;
+
+ groupRenderWindowTitle(gw->group);
+ groupRenderTopTabHighlight(gw->group);
+ addWindowDamage(w);
+ }
+
+ return TRUE;
+}
+
+/*
+ * groupRebuildCairoLayer
+ *
+ */
+GroupCairoLayer* groupRebuildCairoLayer(CompScreen *s, GroupCairoLayer *layer, int width, int height)
+{
+ int timeBuf = layer->animationTime;
+ PaintState stateBuf = layer->state;
+
+ groupDestroyCairoLayer(s, layer);
+ layer = groupCreateCairoLayer(s, width, height);
+
+ layer->animationTime = timeBuf;
+ layer->state = stateBuf;
+
+ return layer;
+}
+
+/*
+ * groupClearCairoLayer
+ *
+ */
+void groupClearCairoLayer(GroupCairoLayer *layer)
+{
+ cairo_t *cr = layer->cairo;
+ cairo_save(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(cr);
+ cairo_restore(cr);
+}
+
+/*
+ * groupDestroyCairoLayer
+ *
+ */
+void groupDestroyCairoLayer(CompScreen *s, GroupCairoLayer *layer)
+{
+ if (layer->cairo)
+ cairo_destroy(layer->cairo);
+
+ if (layer->surface)
+ cairo_surface_destroy(layer->surface);
+
+ if (layer->texBuf)
+ free(layer->texBuf);
+
+ if (&layer->texture)
+ finiTexture(s, &layer->texture);
+
+ free(layer);
+}
+
+/*
+ * groupCreateCairoLayer
+ *
+ */
+GroupCairoLayer* groupCreateCairoLayer(CompScreen *s, int width, int height)
+{
+ GroupCairoLayer* layer = (GroupCairoLayer*) malloc(sizeof(GroupCairoLayer));
+
+ layer->surface = NULL;
+ layer->cairo = NULL;
+ layer->texBuf = NULL;
+ layer->animationTime = 0;
+ layer->state = PaintOff;
+
+ initTexture(s, &layer->texture);
+ layer->texBuf = calloc(4 * width * height, sizeof (unsigned char));
+ layer->surface = cairo_image_surface_create_for_data(layer->texBuf, CAIRO_FORMAT_ARGB32,
+ width, height, 4 * width);
+
+ if (cairo_surface_status(layer->surface) != CAIRO_STATUS_SUCCESS) {
+ free(layer->texBuf);
+ free(layer);
+ return NULL;
+ }
+
+ layer->cairo = cairo_create(layer->surface);
+ if (cairo_status(layer->cairo) != CAIRO_STATUS_SUCCESS) {
+ free(layer->texBuf);
+ free(layer);
+ return NULL;
+ }
+
+ groupClearCairoLayer(layer);
+
+ return layer;
+}
+
+/*
+ * groupRecalcSlotPos
+ *
+ */
+static void
+groupRecalcSlotPos(GroupTabBarSlot *slot, int slotPos)
+{
+ GROUP_WINDOW (slot->window);
+ GROUP_SCREEN (slot->window->screen);
+ GroupSelection *group = gw->group;
+ XRectangle box;
+
+ if (!HAS_TOP_WIN(group) || !group->tabBar)
+ return;
+
+ int border_width = gs->opt[GROUP_SCREEN_OPTION_BORDER_WIDTH].value.i;
+ int thumb_size = gs->opt[GROUP_SCREEN_OPTION_THUMB_SIZE].value.i;
+
+ EMPTY_REGION(slot->region);
+
+ box.x = border_width + ((thumb_size + border_width) * slotPos);
+ box.y = border_width;
+
+ box.width = thumb_size;
+ box.height = thumb_size;
+
+ XUnionRectWithRegion(&box, slot->region, slot->region);
+
+ // in case the name changed we need a new one here
+ if(slot->name)
+ free(slot->name);
+ slot->name = groupGetWindowTitle(slot->window);
+}
+
+/*
+ * groupRecalcTabBarPos
+ *
+ */
+void groupRecalcTabBarPos(GroupSelection *group, int middleX, int minX1, int maxX2)
+{
+ if (!HAS_TOP_WIN(group) || !group->tabBar)
+ return;
+
+ GroupTabBarSlot *slot;
+ GroupTabBar *bar = group->tabBar;
+ CompWindow *topTab = TOP_TAB(group);
+ Bool isDraggedSlotGroup = FALSE;
+ GROUP_SCREEN(group->screen);
+
+ /* first damage the old region to make sure it is updated */
+ damageScreenRegion (group->screen, bar->region);
+
+ int border_width = gs->opt[GROUP_SCREEN_OPTION_BORDER_WIDTH].value.i;
+ int bar_width;
+ int currentSlot;
+ XRectangle box;
+
+ // calculate the space which the tabs need
+ int tabs_width = 0;
+ int tabs_height = 0;
+ for(slot = bar->slots; slot; slot = slot->next)
+ {
+ if (slot == gs->draggedSlot && gs->dragged) {
+ isDraggedSlotGroup = TRUE;
+ continue;
+ }
+
+ tabs_width += (slot->region->extents.x2 - slot->region->extents.x1);
+ if ((slot->region->extents.y2 - slot->region->extents.y1) > tabs_height)
+ tabs_height = slot->region->extents.y2 - slot->region->extents.y1;
+ }
+
+ // just a little work-a-round for first call
+ int thumb_size = gs->opt[GROUP_SCREEN_OPTION_THUMB_SIZE].value.i;
+ if (bar->nSlots && tabs_width <= 0) { // first call
+ tabs_width = thumb_size * bar->nSlots;
+ if (bar->nSlots && tabs_height < thumb_size) // we need to do the standard height too
+ tabs_height = thumb_size;
+ }
+
+ bar_width = border_width * (bar->nSlots + 1) + tabs_width;
+
+ if (isDraggedSlotGroup)
+ bar_width -= border_width; //1 tab is missing, so we have 1 less border
+
+ if (maxX2 - minX1 < bar_width)
+ box.x = (maxX2 + minX1)/2 - bar_width / 2;
+ else if (middleX - bar_width/2 < minX1)
+ box.x = minX1;
+ else if (middleX + bar_width/2 > maxX2)
+ box.x = maxX2 - bar_width;
+ else
+ box.x = middleX - bar_width / 2;
+
+ box.y = WIN_Y(topTab);
+ box.width = bar_width;
+ box.height = border_width * 2 + tabs_height;
+
+ EMPTY_REGION(bar->region);
+ XUnionRectWithRegion(&box, bar->region, bar->region);
+
+ // recalc every slot region
+ currentSlot = 0;
+ for(slot = bar->slots; slot; slot = slot->next)
+ {
+ if(slot == gs->draggedSlot && gs->dragged)
+ continue;
+
+ groupRecalcSlotPos (slot, currentSlot);
+ XOffsetRegion (slot->region,
+ bar->region->extents.x1,
+ bar->region->extents.y1);
+ slot->springX = (slot->region->extents.x1 + slot->region->extents.x2) / 2;
+ slot->speed = 0;
+ slot->msSinceLastMove = 0;
+
+ currentSlot++;
+ }
+
+ bar->leftSpringX = box.x;
+ bar->rightSpringX = box.x + box.width;
+
+ bar->rightSpeed = 0;
+ bar->leftSpeed = 0;
+
+ bar->rightMsSinceLastMove = 0;
+ bar->leftMsSinceLastMove = 0;
+
+ groupUpdateInputPreventionWindow(group);
+ groupRenderTabBarBackground(group);
+}
+
+/*
+ * groupInsertTabBarSlotBefore
+ *
+ */
+void groupInsertTabBarSlotBefore(GroupTabBar *bar, GroupTabBarSlot *slot, GroupTabBarSlot *nextSlot)
+{
+ GroupTabBarSlot *prev = nextSlot->prev;
+
+ if(prev) {
+ slot->prev = prev;
+ prev->next = slot;
+ } else {
+ bar->slots = slot;
+ slot->prev = NULL;
+ }
+
+ slot->next = nextSlot;
+ nextSlot->prev = slot;
+ bar->nSlots++;
+
+ CompWindow *w = slot->window;
+ GROUP_WINDOW(w);
+ //Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work,
+ //because the tab-bar got wider now, so it will put it in the average between them,
+ //which is (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway.
+ groupRecalcTabBarPos(gw->group, (bar->region->extents.x1 + bar->region->extents.x2) / 2,
+ bar->region->extents.x1, bar->region->extents.x2);
+}
+
+/*
+ * groupInsertTabBarSlotAfter
+ *
+ */
+void groupInsertTabBarSlotAfter(GroupTabBar *bar, GroupTabBarSlot *slot, GroupTabBarSlot *prevSlot)
+{
+ GroupTabBarSlot *next = prevSlot->next;
+
+ if (next) {
+ slot->next = next;
+ next->prev = slot;
+ } else {
+ bar->revSlots = slot;
+ slot->next = NULL;
+ }
+
+ slot->prev = prevSlot;
+ prevSlot->next = slot;
+ bar->nSlots++;
+
+ CompWindow *w = slot->window;
+ GROUP_WINDOW(w);
+ //Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work,
+ //because the tab-bar got wider now, so it will put it in the average between them,
+ //which is (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway.
+ groupRecalcTabBarPos(gw->group, (bar->region->extents.x1 + bar->region->extents.x2) / 2,
+ bar->region->extents.x1, bar->region->extents.x2);
+}
+
+/*
+ * groupInsertTabBarSlot
+ *
+ */
+void groupInsertTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot)
+{
+
+ if (bar->slots != NULL) {
+ bar->revSlots->next = slot;
+ slot->prev = bar->revSlots;
+ slot->next = NULL;
+ } else {
+ slot->prev = NULL;
+ slot->next = NULL;
+ bar->slots = slot;
+ }
+
+ bar->revSlots = slot;
+ bar->nSlots++;
+
+ CompWindow *w = slot->window;
+ GROUP_WINDOW(w);
+ //Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work,
+ //because the tab-bar got wider now, so it will put it in the average between them,
+ //which is (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway.
+ groupRecalcTabBarPos(gw->group, (bar->region->extents.x1 + bar->region->extents.x2) / 2,
+ bar->region->extents.x1, bar->region->extents.x2);
+}
+
+/*
+ * groupUnhookTabBarSlot
+ *
+ */
+void groupUnhookTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot, Bool temporary)
+{
+ GroupTabBarSlot *prev = slot->prev;
+ GroupTabBarSlot *next = slot->next;
+
+ if(prev)
+ prev->next = next;
+ else
+ bar->slots = next;
+
+ if(next)
+ next->prev = prev;
+ else
+ bar->revSlots = prev;
+
+ slot->prev = NULL;
+ slot->next = NULL;
+
+ bar->nSlots--;
+
+ CompWindow *w = slot->window;
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ if (IS_TOP_TAB(w, gw->group) && !temporary)
+ {
+ if(next)
+ groupChangeTab(next, RotateRight);
+ else if(prev)
+ groupChangeTab(prev, RotateLeft);
+ else if(gw->group->nWins == 1)
+ gw->group->topTab = NULL;
+
+ if(gs->opt[GROUP_SCREEN_OPTION_UNTAB_ON_CLOSE].value.b)
+ groupUntabGroup(gw->group);
+ }
+
+ if(IS_PREV_TOP_TAB(w, gw->group) && !temporary)
+ gw->group->prevTopTab = NULL;
+
+ if (slot == bar->hoveredSlot)
+ bar->hoveredSlot = NULL;
+
+ if (slot == bar->textSlot)
+ {
+ bar->textSlot = NULL;
+
+ if (bar->textLayer->state == PaintFadeIn || bar->textLayer->state == PaintOn)
+ {
+ bar->textLayer->animationTime = (gs->opt[GROUP_SCREEN_OPTION_FADE_TEXT_TIME].value.f * 1000)
+ - bar->textLayer->animationTime;
+ bar->textLayer->state = PaintFadeOut;
+ }
+ }
+
+ //Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work,
+ //because the tab-bar got thiner now, so (bar->region->extents.x1 + bar->region->extents.x2) / 2
+ //Won't cause the new x1 / x2 to be outside the original region.
+ groupRecalcTabBarPos(gw->group, (bar->region->extents.x1 + bar->region->extents.x2) / 2,
+ bar->region->extents.x1, bar->region->extents.x2);
+}
+
+/*
+ * groupDeleteTabBarSlot
+ *
+ */
+void groupDeleteTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot)
+{
+ groupUnhookTabBarSlot(bar, slot, FALSE);
+
+ if (slot->region)
+ XDestroyRegion(slot->region);
+ if (slot->name)
+ free(slot->name);
+
+ CompWindow *w = slot->window;
+ CompScreen *s = w->screen;
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(s);
+
+ if(slot == gs->draggedSlot)
+ {
+ gs->draggedSlot = NULL;
+ gs->dragged = FALSE;
+
+ if (gs->grabState == ScreenGrabTabDrag)
+ groupGrabScreen(s, ScreenGrabNone);
+ }
+
+ gw->slot = NULL;
+ free(slot);
+}
+
+/*
+ * groupCreateSlot
+ *
+ */
+void groupCreateSlot(GroupSelection *group, CompWindow *w)
+{
+ if(!group->tabBar)
+ return;
+
+ GROUP_WINDOW(w);
+
+ GroupTabBarSlot *slot = (GroupTabBarSlot*) malloc(sizeof(GroupTabBarSlot));
+ slot->window = w;
+ slot->name = groupGetWindowTitle(slot->window);
+
+ slot->region = XCreateRegion();
+
+ groupInsertTabBarSlot(group->tabBar, slot);
+ gw->slot = slot;
+}
+
+#define SPRING_K gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_SPRING_K].value.f
+#define FRICTION gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_FRICTION].value.f
+#define SIZE gs->opt[GROUP_SCREEN_OPTION_THUMB_SIZE].value.i
+#define BORDER gs->opt[GROUP_SCREEN_OPTION_BORDER_RADIUS].value.i
+#define Y_START_MOVE gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_Y_DISTANCE].value.i
+#define SPEED_LIMIT gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_SPEED_LIMIT].value.i
+
+/*
+ * groupSpringForce
+ *
+ */
+static int groupSpringForce(CompScreen *s, int centerX, int springX)
+{
+ GROUP_SCREEN(s);
+
+ // Each slot has a spring attached to it, starting at springX, and ending at the center of the slot (centerX).
+ // The spring will cause the slot to move, using the well-known physical formula F = k * dl...
+
+ return -SPRING_K * (centerX - springX);
+}
+
+/*
+ * groupDraggedSlotForce
+ *
+ */
+static int groupDraggedSlotForce(CompScreen *s, int distanceX, int distanceY)
+{
+ // The dragged slot will make the slot move, to get DnD animations (slots will make room for the newly inserted slot).
+ // As the dragged slot is closer to the slot, it will put more force on the slot, causing it to make room for the dragged slot...
+ // But if the dragged slot gets too close to the slot, they are going to be reordered soon, so the force will get lower.
+
+ // If the dragged slot is in the other side of the slot, it will have to make force in the opposite direction.
+
+ // So we the needed funtion is an odd function that goes up at first, and down after that.
+ // Sinus is a function like that... :)
+
+ // The maximum is got when x = (x1 + x2) / 2, in this case: x = SIZE + BORDER.
+ // Because of that, for x = SIZE + BORDER, we get a force of SPRING_K * (SIZE + BORDER) / 2.
+ // That equals to the force we get from the the spring.
+ // This way, the slot won't move when its distance from the dragged slot is SIZE + BORDER (which is the default distance between slots).
+
+ GROUP_SCREEN(s);
+
+ float a = SPRING_K * (SIZE + BORDER) / 2; // The maximum value.
+ float b = PI / (2 * SIZE + 2 * BORDER); // This will make distanceX == 2 * (SIZE + BORDER) to get 0,
+ // and distanceX == (SIZE + BORDER) to get the maximum.
+
+ // If there is some distance between the slots in the y axis, the slot should get less force...
+ // For this, we change max to a lower value, using a simple linear function.
+
+ if(distanceY < Y_START_MOVE)
+ a *= 1.0f - (float)distanceY / Y_START_MOVE;
+ else
+ a = 0;
+
+ if(abs(distanceX) < 2 * (SIZE + BORDER))
+ return a * sin(b * distanceX);
+ else
+ return 0;
+}
+
+/*
+ * groupApplyFriction
+ *
+ */
+static void groupApplyFriction(CompScreen *s, int *speed)
+{
+ GROUP_SCREEN(s);
+
+ if(abs(*speed) < FRICTION)
+ *speed = 0;
+
+ else if(*speed > 0)
+ *speed -= FRICTION;
+
+ else if(*speed < 0)
+ *speed += FRICTION;
+}
+
+/*
+ * groupApplySpeedLimit
+ *
+ */
+static void groupApplySpeedLimit(CompScreen *s, int *speed)
+{
+ GROUP_SCREEN(s);
+
+ if(*speed > SPEED_LIMIT)
+ *speed = SPEED_LIMIT;
+
+ else if(*speed < -SPEED_LIMIT)
+ *speed = - SPEED_LIMIT;
+}
+
+/*
+ * groupApplyForces
+ *
+ */
+void groupApplyForces(CompScreen *s, GroupTabBar *bar, GroupTabBarSlot* draggedSlot)
+{
+ GROUP_SCREEN(s);
+
+ GroupTabBarSlot* slot, *slot2;
+ int centerX, centerY;
+ int draggedCenterX, draggedCenterY;
+
+ if (draggedSlot) {
+ int vx, vy;
+ groupGetDrawOffsetForSlot(draggedSlot, &vx, &vy);
+
+ draggedCenterX = ((draggedSlot->region->extents.x1 + draggedSlot->region->extents.x2) / 2) + vx;
+ draggedCenterY = ((draggedSlot->region->extents.y1 + draggedSlot->region->extents.y2) / 2) + vy;
+ } else {
+ draggedCenterX = 0;
+ draggedCenterY = 0;
+ }
+
+ bar->leftSpeed += groupSpringForce(s, bar->region->extents.x1, bar->leftSpringX);
+ bar->rightSpeed += groupSpringForce(s, bar->region->extents.x2, bar->rightSpringX);
+
+ if (draggedSlot)
+ {
+ int leftForce = groupDraggedSlotForce(s, bar->region->extents.x1 - SIZE / 2 - draggedCenterX,
+ abs((bar->region->extents.y1 + bar->region->extents.y2) / 2 - draggedCenterY));
+
+ int rightForce = groupDraggedSlotForce(s, bar->region->extents.x2 + SIZE / 2 - draggedCenterX,
+ abs((bar->region->extents.y1 + bar->region->extents.y2) / 2 - draggedCenterY));
+
+ if(leftForce < 0)
+ bar->leftSpeed += leftForce;
+ if(rightForce > 0)
+ bar->rightSpeed += rightForce;
+ }
+
+ for(slot = bar->slots; slot; slot = slot->next)
+ {
+ centerX = (slot->region->extents.x1 + slot->region->extents.x2) / 2;
+ centerY = (slot->region->extents.y1 + slot->region->extents.y2) / 2;
+
+ slot->speed += groupSpringForce(s, centerX, slot->springX);
+
+ if(draggedSlot && draggedSlot != slot)
+ {
+ int draggedSlotForce = groupDraggedSlotForce(s, centerX - draggedCenterX,
+ abs(centerY - draggedCenterY));
+
+ slot->speed += draggedSlotForce ;
+
+ slot2 = NULL;
+
+ if(draggedSlotForce < 0)
+ {
+ slot2 = slot->prev;
+ bar->leftSpeed += draggedSlotForce;
+ }
+
+ else if(draggedSlotForce > 0)
+ {
+ slot2 = slot->next;
+ bar->rightSpeed += draggedSlotForce;
+ }
+
+ for( ; slot2; slot2 = (draggedSlotForce < 0)? slot2->prev: slot2->next)
+ {
+ if(slot2 != draggedSlot)
+ slot2->speed += draggedSlotForce;
+ }
+ }
+ }
+
+ for(slot = bar->slots; slot; slot = slot->next)
+ {
+ groupApplyFriction(s, &slot->speed);
+ groupApplySpeedLimit(s, &slot->speed);
+ }
+
+ groupApplyFriction(s, &bar->leftSpeed);
+ groupApplySpeedLimit(s, &bar->leftSpeed);
+
+ groupApplyFriction(s, &bar->rightSpeed);
+ groupApplySpeedLimit(s, &bar->rightSpeed);
+}
+
+/*
+ * groupApplySpeeds
+ *
+ */
+void groupApplySpeeds(CompScreen* s, GroupTabBar* bar, int msSinceLastRepaint)
+{
+ GROUP_SCREEN(s);
+
+ GroupTabBarSlot* slot;
+ int move;
+ XRectangle box;
+ Bool updateTabBar = FALSE;
+
+ box.x = bar->region->extents.x1;
+ box.y = bar->region->extents.y1;
+ box.width = bar->region->extents.x2 - bar->region->extents.x1;
+ box.height = bar->region->extents.y2 - bar->region->extents.y1;
+
+ bar->leftMsSinceLastMove += msSinceLastRepaint;
+ bar->rightMsSinceLastMove += msSinceLastRepaint;
+
+ // Left
+ move = bar->leftSpeed * bar->leftMsSinceLastMove / 1000;
+
+ if(move)
+ {
+ box.x += move;
+ box.width -= move;
+
+ bar->leftMsSinceLastMove = 0;
+ updateTabBar = TRUE;
+ }
+
+ else if(bar->leftSpeed == 0 && bar->region->extents.x1 != bar->leftSpringX &&
+ SPRING_K * abs(bar->region->extents.x1 - bar->leftSpringX) < FRICTION)
+ {
+ // Friction is preventing from the left border to get to its original position.
+ box.x += bar->leftSpringX - bar->region->extents.x1;
+ box.width -= bar->leftSpringX - bar->region->extents.x1;
+
+ bar->leftMsSinceLastMove = 0;
+ updateTabBar = TRUE;
+ }
+
+ else if(bar->leftSpeed == 0)
+ bar->leftMsSinceLastMove = 0;
+
+ //Right
+ move = bar->rightSpeed * bar->rightMsSinceLastMove / 1000;
+
+ if(move)
+ {
+ box.width += move;
+
+ bar->rightMsSinceLastMove = 0;
+ updateTabBar = TRUE;
+ }
+
+ else if(bar->rightSpeed == 0 && bar->region->extents.x2 != bar->rightSpringX &&
+ SPRING_K * abs(bar->region->extents.x2 - bar->rightSpringX) < FRICTION)
+ {
+ // Friction is preventing from the right border to get to its original position.
+ box.width += bar->leftSpringX - bar->region->extents.x1;
+
+ bar->leftMsSinceLastMove = 0;
+ updateTabBar = TRUE;
+ }
+
+ else if(bar->rightSpeed == 0)
+ bar->rightMsSinceLastMove = 0;
+
+ if(updateTabBar)
+ {
+ EMPTY_REGION(bar->region);
+ XUnionRectWithRegion(&box, bar->region, bar->region);
+ }
+
+ for(slot = bar->slots; slot; slot = slot->next)
+ {
+ slot->msSinceLastMove += msSinceLastRepaint;
+ move = slot->speed * slot->msSinceLastMove / 1000;
+
+ if(move)
+ {
+ XOffsetRegion (slot->region,
+ move, 0);
+ slot->msSinceLastMove = 0;
+ }
+
+ else if(slot->speed == 0 && (slot->region->extents.x1 + slot->region->extents.x2) / 2 != slot->springX &&
+ SPRING_K * abs((slot->region->extents.x1 + slot->region->extents.x2) / 2 - slot->springX) < FRICTION)
+ {
+ // Friction is preventing from the slot to get to its original position.
+
+ XOffsetRegion (slot->region,
+ slot->springX - (slot->region->extents.x1 + slot->region->extents.x2) / 2, 0);
+ slot->msSinceLastMove = 0;
+ }
+
+ else if(slot->speed == 0)
+ slot->msSinceLastMove = 0;
+ }
+}
+
+/*
+ * groupInitTabBar
+ *
+ */
+void groupInitTabBar(GroupSelection *group, CompWindow* topTab)
+{
+ // error
+ if (group->tabBar)
+ return;
+
+ GroupTabBar *bar = (GroupTabBar*) malloc(sizeof(GroupTabBar));
+ bar->slots = NULL;
+ bar->nSlots = 0;
+ bar->state = PaintOff;
+ bar->animationTime = 0;
+ bar->timeoutHandle = 0;
+ bar->textLayer = NULL;
+ bar->bgLayer = NULL;
+ bar->selectionLayer = NULL;
+ bar->hoveredSlot = NULL;
+ bar->textSlot = NULL;
+ group->tabBar = bar;
+
+ bar->region = XCreateRegion();
+
+ int i;
+ for(i = 0; i < group->nWins; i++)
+ groupCreateSlot(group, group->windows[i]);
+
+ groupRecalcTabBarPos(group, WIN_X(topTab) + WIN_WIDTH(topTab) / 2,
+ WIN_X(topTab), WIN_X(topTab) + WIN_WIDTH(topTab));
+}
+
+/*
+ * groupDeleteTabBar
+ *
+ */
+void groupDeleteTabBar(GroupSelection *group)
+{
+ GroupTabBar *bar = group->tabBar;
+
+ groupDestroyCairoLayer(group->screen, bar->textLayer);
+ groupDestroyCairoLayer(group->screen, bar->bgLayer);
+ groupDestroyCairoLayer(group->screen, bar->selectionLayer);
+
+ groupDestroyInputPreventionWindow(group);
+
+ if (bar->timeoutHandle)
+ compRemoveTimeout(bar->timeoutHandle);
+
+ while (bar->slots)
+ groupDeleteTabBarSlot(bar, bar->slots);
+
+ if (bar->region)
+ XDestroyRegion(bar->region);
+
+ free(bar);
+ group->tabBar = NULL;
+}
+
+/*
+ * groupInitTab
+ *
+ */
+Bool
+groupInitTab(CompDisplay * d, CompAction * action, CompActionState state,
+ CompOption * option, int nOption)
+{
+ CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
+ Bool allowUntab = TRUE;
+
+ if (!w)
+ return TRUE;
+
+ GROUP_WINDOW(w);
+
+ if (gw->inSelection)
+ {
+ groupGroupWindows(d, action, state, option, nOption);
+ allowUntab = FALSE; // If the window was selected, we don't want to untab the group.
+ // because the user probably wanted to tab the selected windows.
+ }
+
+ if (!gw->group)
+ return TRUE;
+
+ if (gw->group->tabbingState != PaintOff)
+ groupSyncWindows(gw->group);
+
+ if (!gw->group->tabBar) {
+ groupTabGroup(w);
+ } else if (allowUntab) {
+ groupUntabGroup(gw->group);
+ }
+
+
+ damageScreen(w->screen);
+
+ return TRUE;
+}
+
+/*
+ * groupChangeTabLeft
+ *
+ */
+Bool
+groupChangeTabLeft(CompDisplay * d, CompAction * action, CompActionState state,
+ CompOption * option, int nOption)
+{
+ CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
+ CompWindow *topTab = w;
+
+ if(!w)
+ return TRUE;
+
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ if(!gw->slot || !gw->group)
+ return TRUE;
+
+ if(gw->group->nextTopTab)
+ topTab = NEXT_TOP_TAB(gw->group);
+ else if(gw->group->topTab)
+ topTab = TOP_TAB(gw->group); //If there are no tabbing animations, topTab is never NULL.
+
+ gw = GET_GROUP_WINDOW(topTab, gs);
+
+ if(gw->slot->prev)
+ return groupChangeTab(gw->slot->prev, RotateLeft);
+ else
+ return groupChangeTab(gw->group->tabBar->revSlots, RotateLeft);
+}
+
+/*
+ * groupChangeTabRight
+ *
+ */
+Bool
+groupChangeTabRight(CompDisplay * d, CompAction * action, CompActionState state,
+ CompOption * option, int nOption)
+{
+ CompWindow *w = findWindowAtDisplay(d, d->activeWindow);
+ CompWindow *topTab = w;
+
+ if(!w)
+ return TRUE;
+
+ GROUP_WINDOW(w);
+ GROUP_SCREEN(w->screen);
+
+ if(!gw->slot || !gw->group)
+ return TRUE;
+
+ if(gw->group->nextTopTab)
+ topTab = NEXT_TOP_TAB(gw->group);
+ else if(gw->group->topTab)
+ topTab = TOP_TAB(gw->group); //If there are no tabbing animations, topTab is never NULL.
+
+ gw = GET_GROUP_WINDOW(topTab, gs);
+
+ if(gw->slot->next)
+ return groupChangeTab(gw->slot->next, RotateRight);
+ else
+ return groupChangeTab(gw->group->tabBar->slots, RotateRight);
+}
+
+/*
+ * groupSwitchTopTabInput
+ *
+ */
+void
+groupSwitchTopTabInput(GroupSelection *group, Bool enable)
+{
+ if(!group->tabBar || !HAS_TOP_WIN(group))
+ return;
+
+ if (!group->inputPrevention)
+ groupCreateInputPreventionWindow(group);
+
+ if (!enable)
+ XMapWindow(group->screen->display->display, group->inputPrevention);
+ else
+ XUnmapWindow(group->screen->display->display, group->inputPrevention);
+}
+
+/*
+ * groupUpdateInputPreventionWindow
+ *
+ */
+void
+groupUpdateInputPreventionWindow(GroupSelection *group)
+{
+ XWindowChanges xwc;
+
+ if(!group->tabBar || !HAS_TOP_WIN(group))
+ return;
+
+ if (!group->inputPrevention)
+ groupCreateInputPreventionWindow(group);
+
+ CompWindow *w = TOP_TAB(group);
+
+ xwc.x = group->tabBar->leftSpringX;
+ xwc.y = group->tabBar->region->extents.y1;
+ xwc.width = group->tabBar->rightSpringX - group->tabBar->leftSpringX;
+ xwc.height = group->tabBar->region->extents.y2 - group->tabBar->region->extents.y1;
+
+ xwc.stack_mode = Above;
+
+ xwc.sibling = w->id;
+
+ XConfigureWindow(group->screen->display->display, group->inputPrevention,
+ CWSibling | CWStackMode | CWX | CWY | CWWidth | CWHeight, &xwc);
+}
+
+/*
+ * groupCreateInputPreventionWindow
+ *
+ */
+void groupCreateInputPreventionWindow(GroupSelection* group)
+{
+ if (!group->inputPrevention) //For preventing a memory leak.
+ {
+ XSetWindowAttributes attrib;
+ attrib.override_redirect = TRUE;
+ attrib.event_mask = ButtonPressMask;
+
+ group->inputPrevention = XCreateWindow(group->screen->display->display,
+ group->screen->root, -100, -100, 1, 1, 0, CopyFromParent,
+ InputOnly, CopyFromParent, CWOverrideRedirect | CWEventMask, &attrib);
+ }
+}
+
+/*
+ * groupDestroyInputPreventionWindow
+ *
+ */
+void groupDestroyInputPreventionWindow(GroupSelection* group)
+{
+ if(group->inputPrevention)
+ {
+ XDestroyWindow(group->screen->display->display, group->inputPrevention);
+
+ group->inputPrevention = None;
+ }
+//
+// if (group->tabBar)
+// group->tabBar->lastHoveredSlot = NULL;
+}
+
+