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

	layer->texWidth = width;
	layer->texHeight = height;
	layer->pixmap = (Pixmap) data;

	if(data)
		bindPixmapToTexture(group->screen, &layer->texture, (Pixmap) data, width, height, 32);
}

/*
 * 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;
}


syntax highlighted by Code2HTML, v. 0.9.1