/*
 * Beryl blur and reflection effect plugin
 *
 * blurfx.c
 *
 * Copyright : (C) 2006 by Dennis Kasprzyk
 * E-mail    : onestone@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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <sys/stat.h>
#include <unistd.h>
#include <beryl.h>
#include <beryl_helpers.h>

#include "blurfx.h"

// return plugin data
CompPluginVTable *getCompPluginInfo(void)
{
	return &blurfxVTable;
}

static Bool blurfxInit(CompPlugin * p)
{
	blurShader[0][0] = gaussian5x5_V;
	blurShader[0][1] = gaussian5x5_H;
	blurShader[1][0] = linear5x5_V;
	blurShader[1][1] = linear5x5_H;

	blurShader[2][0] = gaussian9x9_V;
	blurShader[2][1] = gaussian9x9_H;
	blurShader[3][0] = linear9x9_V;
	blurShader[3][1] = linear9x9_H;

	blurShader[4][0] = gaussian13x13_V;
	blurShader[4][1] = gaussian13x13_H;
	blurShader[5][0] = linear13x13_V;
	blurShader[5][1] = linear13x13_H;

	blurShader[6][0] = gaussian17x17_V;
	blurShader[6][1] = gaussian17x17_H;
	blurShader[7][0] = linear17x17_V;
	blurShader[7][1] = linear17x17_H;

	blurShader[8][0] = gaussian21x21_V;
	blurShader[8][1] = gaussian21x21_H;
	blurShader[9][0] = linear21x21_V;
	blurShader[9][1] = linear21x21_H;

	displayPrivateIndex = allocateDisplayPrivateIndex();
	if (displayPrivateIndex < 0)
		return FALSE;
	return TRUE;
}

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

static Bool blurfxInitDisplay(CompPlugin * p, CompDisplay * d)
{
	//Generate a blur display
	BlurfxDisplay *bd = (BlurfxDisplay *) calloc(1, sizeof(BlurfxDisplay));

	//Allocate a private index
	bd->screenPrivateIndex = allocateScreenPrivateIndex(d);
	//Check if its valid
	if (bd->screenPrivateIndex < 0)
	{
		//Its invalid so free memory and return
		free(bd);
		return FALSE;
	}

	blurfxDisplayInitOptions(bd);

	//Record the display
	d->privates[displayPrivateIndex].ptr = bd;
	return TRUE;
}

static void blurfxFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	BLURFX_DISPLAY(d);
	//Free the private index
	freeScreenPrivateIndex(d, bd->screenPrivateIndex);
	//Free the pointer
	free(bd);
}

static Bool blurfxInitScreen(CompPlugin * p, CompScreen * s)
{
	BLURFX_DISPLAY(s->display);
	// Create a blur screen
	BlurfxScreen *bs = (BlurfxScreen *) calloc(1, sizeof(BlurfxScreen));

	s->privates[bd->screenPrivateIndex].ptr = bs;

	bs->blur_supported = TRUE;
	bs->fboBlur_supported = TRUE;
	bs->mblur_supported = TRUE;
	bs->reflection_supported = TRUE;

	// Check for extensions and disable unsupported features
	if (!s->fbo)
	{
		fprintf(stderr,
				"No framebuffer_object support! (only simple Blur aviable).\n");
		bs->fboBlur_supported = FALSE;
	}
	if (!s->fragmentProgram)
	{
		fprintf(stderr,
				"No fragment_program support! (only simple Blur aviable).\n");
		bs->fboBlur_supported = FALSE;
	}
	if (!s->textureRectangle)
	{
		fprintf(stderr,
				"No texture_rectangle support! (Blur effects disabled).\n");
		bs->blur_supported = FALSE;
		bs->fboBlur_supported = FALSE;
		bs->mblur_supported = FALSE;
	}
	if (!s->textureEnvCombine)
	{
		fprintf(stderr,
				"No texture_env_combine support! (Blur and Reflection effects disabled).\n");
		bs->blur_supported = FALSE;
		bs->fboBlur_supported = FALSE;
		bs->reflection_supported = FALSE;
	}

	// initialize all variables
	bs->windowPrivateIndex = allocateWindowPrivateIndex(s);

	bs->vertArray.vertices = 0;
	bs->vertArray.vCount = 0;
	bs->vertArray.vertexSize = 0;

	bs->screenDamage = XCreateRegion();
	bs->occlusion = XCreateRegion();
	bs->blurredRegion = XCreateRegion();

	bs->mb_activated = FALSE;
	bs->mb_update = TRUE;

	bs->noBlurWMask = 0;
	bs->noReflectionWMask = 0;

	// setup settings
	blurfxScreenInitOptions(bs);

	int i = 0;

	for (i = 0; i < LIST_SIZE(mBlurModes); i++)
		if (strcmp
			(bs->opt[BLURFX_SCREEN_OPTION_MOTION_BLUR_MODE].value.
			 s, mBlurModes[i]) == 0)
			bs->mb_mode = i;

	for (i = 0; i < LIST_SIZE(blurShaderNames); i++)
		if (strcmp
			(bs->opt[BLURFX_SCREEN_OPTION_BLUR_SHADER].value.s,
			 blurShaderNames[i]) == 0)
			bs->blur_shader = i;

	addScreenAction(s,
					&bd->opt[BLURFX_DISPLAY_OPTION_MOTION_BLUR_TOGGLE].
					value.action);

	//Take over the window draw function
	WRAP(bs, s, drawWindowTexture, blurfxDrawWindowTexture);
	WRAP(bs, s, drawWindow, blurfxDrawWindow);
	WRAP(bs, s, damageWindowRect, blurfxDamageWindowRect);
	WRAP(bs, s, paintScreen, blurfxPaintScreen);
	WRAP(bs, s, addWindowGeometry, blurfxAddWindowGeometry);
	WRAP(bs, s, preparePaintScreen, blurfxPreparePaintScreen);
	WRAP(bs, s, paintTransformedScreen, blurfxPaintTransformedScreen);

	damageScreen(s);

	bs->ipcs_disable_blur =
			IPCS_GetAtom(IPCS_OBJECT(s), IPCS_BOOL, "DISABLE_BLUR", TRUE);
	bs->ipcs_disable_reflection =
			IPCS_GetAtom(IPCS_OBJECT(s), IPCS_BOOL, "DISABLE_REFLECTION",
						 TRUE);
	bs->ipcs_disable_mblur =
			IPCS_GetAtom(IPCS_OBJECT(s), IPCS_BOOL, "DISABLE_MOTION_BLUR",
						 TRUE);

	return TRUE;
}

static void blurfxFiniScreen(CompPlugin * p, CompScreen * s)
{
	BLURFX_SCREEN(s);
	BLURFX_DISPLAY(s->display);

	// delete shaders
	if (bs->blurShaderV)
		(*s->deletePrograms) (1, &bs->blurShaderV);
	if (bs->blurShaderH)
		(*s->deletePrograms) (1, &bs->blurShaderH);

	// delete the textures
	if (bs->backTex.handle)
		glDeleteTextures(1, &bs->backTex.handle);
	if (bs->motionTex.handle)
		glDeleteTextures(1, &bs->motionTex.handle);
	if (bs->blurTempTexV.handle)
		glDeleteTextures(1, &bs->blurTempTexV.handle);
	if (bs->blurTempTexH.handle)
		glDeleteTextures(1, &bs->blurTempTexH.handle);
	if (bs->modTex.handle)
		glDeleteTextures(1, &bs->modTex.handle);

	// delete framebuffer object
	if (bs->fbo)
	{
		s->deleteFramebuffers(1, &bs->fbo);
	}
	// free some pointers
	free(bs->vertArray.vertices);

	freeWindowPrivateIndex(s, bs->windowPrivateIndex);

	XDestroyRegion(bs->screenDamage);
	XDestroyRegion(bs->occlusion);
	XDestroyRegion(bs->blurredRegion);


	// restore the original function
	UNWRAP(bs, s, drawWindowTexture);
	UNWRAP(bs, s, drawWindow);
	UNWRAP(bs, s, damageWindowRect);
	UNWRAP(bs, s, paintScreen);
	UNWRAP(bs, s, addWindowGeometry);
	UNWRAP(bs, s, preparePaintScreen);
	UNWRAP(bs, s, paintTransformedScreen);

	removeScreenAction(s,
					   &bd->
					   opt[BLURFX_DISPLAY_OPTION_MOTION_BLUR_TOGGLE].
					   value.action);

	// free the screen pointer
	free(bs);

}

static Bool blurfxInitWindow(CompPlugin * p, CompWindow * w)
{
	BlurfxWindow *bw;

	BLURFX_SCREEN(w->screen);

	// create window
	bw = calloc(1, sizeof(BlurfxWindow));
	if (!bw)
		return FALSE;

	// initialize variables
	bw->my_next = w->next;
	bw->lastX = w->attrib.x;
	bw->lastY = w->attrib.y;
	bw->lastPAttrib = w->lastPaint;
	bw->decoArray.vertices = NULL;
	bw->decoArray.indices = NULL;
	bw->decoArray.vCount = 0;
	bw->decoArray.vertexSize = 0;
	bw->decoArray.indexSize = 0;
	bw->decoArray.indexCount = 0;
	bw->paintRegion = XCreateRegion();
	bw->damageRegion = XCreateRegion();
	bw->texDamage = XCreateRegion();
	bw->bTexRegion = XCreateRegion();
	bw->clip = XCreateRegion();
	bw->hasTexture = FALSE;
	bw->vertArray.vertices = 0;
	bw->vertArray.vCount = 0;
	bw->vertArray.vertexSize = 0;
	bw->isSwitcher = FALSE;

	Atom type;
	int format;
	unsigned long nitems;
	unsigned long bytes_after;
	Window *xw;

	int result = XGetWindowProperty(w->screen->display->display, w->id,
									XInternAtom(w->screen->display->display,
												"_SWITCH_SELECT_WINDOW",
												0),
									0L, 1L,
									False, XA_WINDOW, &type, &format,
									&nitems,
									&bytes_after, (void *)&xw);

	if (result == Success && type == XA_WINDOW)
		bw->isSwitcher = TRUE;

	w->privates[bs->windowPrivateIndex].ptr = bw;

	damageScreen(w->screen);

	bw->ipcs_disable_blur =
			IPCS_GetAtom(IPCS_OBJECT(w), IPCS_BOOL, "DISABLE_BLUR", TRUE);
	bw->ipcs_disable_reflection =
			IPCS_GetAtom(IPCS_OBJECT(w), IPCS_BOOL, "DISABLE_REFLECTION",
						 TRUE);

	return TRUE;
}

static void blurfxFiniWindow(CompPlugin * p, CompWindow * w)
{
	BLURFX_WINDOW(w);

	// free regions
	XDestroyRegion(bw->paintRegion);
	XDestroyRegion(bw->damageRegion);
	XDestroyRegion(bw->texDamage);
	XDestroyRegion(bw->bTexRegion);
	XDestroyRegion(bw->clip);

	// delete blur cache texture
	if (bw->hasTexture)
		glDeleteTextures(1, &bw->blurTex.handle);

	// free used memory
	if (bw->decoArray.vertices)
		free(bw->decoArray.vertices);
	if (bw->decoArray.indices)
		free(bw->decoArray.indices);
	if (bw->vertArray.vertices)
		free(bw->vertArray.vertices);

	// free window pointer
	free(bw);
}

static CompOption *blurfxGetDisplayOptions(CompDisplay * display, int *count)
{
	if (display)
	{
		BLURFX_DISPLAY(display);

		*count = NUM_OPTIONS(bd);
		return bd->opt;
	}
	else
	{
		BlurfxDisplay *bd =
				(BlurfxDisplay *) calloc(1, sizeof(BlurfxDisplay));
		blurfxDisplayInitOptions(bd);
		*count = NUM_OPTIONS(bd);
		return bd->opt;
	}
}

static Bool
blurfxSetDisplayOption(CompDisplay * display, char *name,
					   CompOptionValue * value)
{
	CompOption *o;
	int index;

	BLURFX_DISPLAY(display);

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

	switch (index)
	{
	case BLURFX_DISPLAY_OPTION_MOTION_BLUR_TOGGLE:
		if (setDisplayAction(display, o, value))
			return TRUE;
		break;
	default:
		break;
	}

	return FALSE;
}

static void blurfxDisplayInitOptions(BlurfxDisplay * bd)
{
	CompOption *o;

	o = &bd->opt[BLURFX_DISPLAY_OPTION_MOTION_BLUR_TOGGLE];
	o->advanced = False;
	o->name = "motion_blur_toggle";
	o->group = N_("Motion Blur");
	o->subGroup = N_("Activate");
	o->displayHints = "";
	o->shortDesc = N_("Toggle Motion Blur");
	o->longDesc = N_("Toggle motion Blur effect.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = blurfxToggleMotionBlur;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.key.modifiers =
			BLURFX_DISPLAY_OPTION_MOTION_BLUR_TOGGLE_MOD_DEFAULT;
	o->value.action.key.keysym =
			XStringToKeysym
			(BLURFX_DISPLAY_OPTION_MOTION_BLUR_TOGGLE_KEY_DEFAULT);
}


static Bool
blurfxSetScreenOption(CompScreen * s, char *name, CompOptionValue * value)
{
	CompOption *o;
	int index;

	BLURFX_SCREEN(s);

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

	switch (index)
	{
	case BLURFX_SCREEN_OPTION_BLUR_WINDOWS:
	case BLURFX_SCREEN_OPTION_BLUR_DECORATION:
	case BLURFX_SCREEN_OPTION_REFLECTION_WINDOWS:
	case BLURFX_SCREEN_OPTION_REFLECTION_DECORATION:
	case BLURFX_SCREEN_OPTION_BLUR_ON_TRANSFORMED_SCREEN:
	case BLURFX_SCREEN_OPTION_BLUR_TRANSFORMED_WINDOW:
	case BLURFX_SCREEN_OPTION_REFLECTION_ON_TRANSFORMED_SCREEN:
	case BLURFX_SCREEN_OPTION_REFLECTION_TRANSFORMED_WINDOW:
	case BLURFX_SCREEN_OPTION_REFLECTION_SCALE:
	case BLURFX_SCREEN_OPTION_REFLECTION_PROPORTIONAL:
	case BLURFX_SCREEN_OPTION_MOTION_BLUR_ON_TRANSFORMED_SCREEN:
	case BLURFX_SCREEN_OPTION_DISABLE_DECORATION_ALPHADEP:
	case BLURFX_SCREEN_OPTION_NO_BLUR_CACHE_ON_TRANSFORMED_SCREEN:
		if (compSetBoolOption(o, value))
		{
			damageScreen(s);
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_BLUR_CACHE_TEXTURES:
		if (compSetBoolOption(o, value))
		{
			if (!value->b)
			{
				CompWindow *w;

				for (w = s->windows; w; w = w->next)
				{
					BLURFX_WINDOW(w);
					if (bw->hasTexture)
					{
						bw->hasTexture = FALSE;
						glDeleteTextures(1, &bw->blurTex.handle);
						bw->blurTex.handle = 0;
					}
				}
			}
			damageScreen(s);
			return TRUE;
		}
		break;

	case BLURFX_SCREEN_OPTION_BLUR_SHADER:
		if (compSetStringOption(o, value))
		{
			int i = 0;

			for (i = 0; i < LIST_SIZE(blurShaderNames); i++)
				if (strcmp(value->s, blurShaderNames[i]) == 0)
					bs->blur_shader = i;

			if (bs->fboBlur_supported && bs->hasInit
				&& !bs->opt[BLURFX_SCREEN_OPTION_FORCE_NON_FBO_BLUR].value.b)
			{
				loadShader(GL_FRAGMENT_PROGRAM_ARB, s,
						   &bs->blurShaderV, blurShader[bs->blur_shader][0]);
				loadShader(GL_FRAGMENT_PROGRAM_ARB, s,
						   &bs->blurShaderH, blurShader[bs->blur_shader][1]);
				damageScreen(s);
				CompWindow *w;

				for (w = s->windows; w; w = w->next)
				{
					BLURFX_WINDOW(w);
					if (bw->hasTexture)
					{
						bw->hasTexture = FALSE;
						glDeleteTextures(1, &bw->blurTex.handle);
						bw->blurTex.handle = 0;
					}
				}
			}
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_DISABLE_BLUR_ON_SCREENGRAB_OF:
	case BLURFX_SCREEN_OPTION_DISABLE_REFLECTION_ON_SCREENGRAB_OF:
	case BLURFX_SCREEN_OPTION_MOTION_BLUR_ON_SCREENGRAB_OF:
		if (compSetOptionList(o, value))
		{
			damageScreen(s);
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_REFLECTION_FILE:
		if (compSetStringOption(o, value))
		{
			if (bs->reflection_supported)
			{
				loadPngToTexture2D(s, &bs->modTex, value->s);
				damageScreen(s);
			}
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_COLOR:
	case BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_ALPHA:
		if (compSetBoolOption(o, value))
		{
			if (bs->reflection_supported)
			{
				loadPngToTexture2D(s, &bs->modTex,
								   bs->
								   opt
								   [BLURFX_SCREEN_OPTION_REFLECTION_FILE].
								   value.s);
				damageScreen(s);
			}
			damageScreen(s);
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_BLUR_SATURATION:
	case BLURFX_SCREEN_OPTION_MOTION_BLUR_STRENGTH:
		if (compSetFloatOption(o, value))
		{
			damageScreen(s);
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_DISABLE_BLUR_WINDOW_TYPE:
		if (compSetOptionList(o, value))
		{
			bs->noBlurWMask = compWindowTypeMaskFromStringList(&o->value);
			damageScreen(s);
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_DISABLE_REFLECTION_WINDOW_TYPE:
		if (compSetOptionList(o, value))
		{
			bs->noReflectionWMask =
					compWindowTypeMaskFromStringList(&o->value);
			damageScreen(s);
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_MOTION_BLUR_MODE:
		if (compSetStringOption(o, value))
		{
			int i;

			for (i = 0; i < LIST_SIZE(mBlurModes); i++)
				if (strcmp(value->s, mBlurModes[i]) == 0)
					bs->mb_mode = i;

			if (bs->hasInit && bs->mblur_supported && bs->motionTex.handle)
			{
				glDeleteTextures(1, &bs->motionTex.handle);
				bs->motionTex.handle = 0;
			}
			if (bs->hasInit && bs->mblur_supported
				&& (bs->mb_mode == 0 || bs->mb_mode == 2))
			{
				genBlurTexture(bs, &bs->motionTex,
							   s->width, s->height, bs->mb_mode);
			}
			bs->mb_update = TRUE;
			damageScreen(s);
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_FORCE_NON_FBO_BLUR:
		if (compSetBoolOption(o, value))
		{
			if (!bs->hasInit)
				return TRUE;

			if (bs->fboBlur_supported && !value->b)
			{
				// load shaders
				loadShader(GL_FRAGMENT_PROGRAM_ARB, s,
						   &bs->blurShaderV, blurShader[bs->blur_shader][0]);
				loadShader(GL_FRAGMENT_PROGRAM_ARB, s,
						   &bs->blurShaderH, blurShader[bs->blur_shader][1]);
				bs->downSample = 1.0;
			}
			CompWindow *w;

			for (w = s->windows; w; w = w->next)
			{
				BLURFX_WINDOW(w);
				if (bw->hasTexture)
				{
					bw->hasTexture = FALSE;
					glDeleteTextures(1, &bw->blurTex.handle);
					bw->blurTex.handle = 0;
				}
			}
			damageScreen(s);
			return TRUE;
		}
		break;
	case BLURFX_SCREEN_OPTION_NON_FBO_BLUR_STRENGTH:
		if (compSetIntOption(o, value))
		{
			if (!bs->hasInit)
				return TRUE;

			if (!bs->fboBlur_supported
				|| bs->opt[BLURFX_SCREEN_OPTION_FORCE_NON_FBO_BLUR].value.b)
			{
				bs->downSample =
						bs->
						opt[BLURFX_SCREEN_OPTION_NON_FBO_BLUR_STRENGTH].value.
						i;
				CompWindow *w;

				for (w = s->windows; w; w = w->next)
				{
					BLURFX_WINDOW(w);
					if (bw->hasTexture)
					{
						bw->hasTexture = FALSE;
						glDeleteTextures(1, &bw->blurTex.handle);
						bw->blurTex.handle = 0;
					}
				}
			}
			damageScreen(s);
			return TRUE;
		}
		break;
	default:
		break;
	}
	return FALSE;
}

static CompOption *blurfxGetScreenOptions(CompScreen * s, int *count)
{
	if (s)
	{
		BLURFX_SCREEN(s);

		*count = NUM_OPTIONS(bs);
		return bs->opt;
	}
	else
	{
		BlurfxScreen *bs = (BlurfxScreen *) calloc(1, sizeof(BlurfxScreen));

		blurfxScreenInitOptions(bs);
		*count = NUM_OPTIONS(bs);
		return bs->opt;
	}
}

static void blurfxScreenInitOptions(BlurfxScreen * bs)
{
	CompOption *o;
	int i;

	o = &bs->opt[BLURFX_SCREEN_OPTION_BLUR_DECORATION];
	o->advanced = False;
	o->name = "blur_decoration";
	o->group = N_("Blur");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Blur Decorations");
	o->longDesc = N_("Blur behind decorations.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_BLUR_DECORATION_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_BLUR_WINDOWS];
	o->advanced = False;
	o->name = "blur_windows";
	o->group = N_("Blur");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Blur Windows");
	o->longDesc = N_("Blur behind windows.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_BLUR_WINDOWS_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_DECORATION];
	o->advanced = False;
	o->name = "reflection_decoration";
	o->group = N_("Reflection");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Reflection for Decorations");
	o->longDesc = N_("Draw Reflection for decorations.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_REFLECTION_DECORATION_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_WINDOWS];
	o->advanced = False;
	o->name = "reflection_windows";
	o->group = N_("Reflection");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Reflection for Windows");
	o->longDesc = N_("Draw Reflection for windows.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_REFLECTION_WINDOWS_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_BLUR_ON_TRANSFORMED_SCREEN];
	o->advanced = False;
	o->name = "blur_transformed_screen";
	o->group = N_("Blur");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Blur on Transformed Screen");
	o->longDesc = N_("Draw Blur if the screen is transformed.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_BLUR_ON_TRANSFORMED_SCREEN_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_BLUR_TRANSFORMED_WINDOW];
	o->advanced = False;
	o->name = "blur_transformed_window";
	o->group = N_("Blur");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Blur Transformed Windows");
	o->longDesc = N_("Draw Blur if window is transformed.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_BLUR_TRANSFORMED_WINDOW_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_BLUR_SHADER];
	o->advanced = False;
	o->name = "blur_shader";
	o->group = N_("Blur");
	o->subGroup = N_("Visibility/Performance");
	o->displayHints = "";
	o->shortDesc = N_("Blur Shader");
	o->longDesc = N_("Blur shader.");
	o->type = CompOptionTypeString;
	o->value.s = strdup(BLURFX_SCREEN_OPTION_BLUR_SHADER_DEFAULT);
	o->rest.s.string = blurShaderNames;
	o->rest.s.nString = LIST_SIZE(blurShaderNames);

	o = &bs->opt[BLURFX_SCREEN_OPTION_FORCE_NON_FBO_BLUR];
	o->advanced = False;
	o->name = "force_non_fbo_blur";
	o->group = N_("Blur");
	o->subGroup = N_("Visibility/Performance");
	o->displayHints = "";
	o->shortDesc = N_("Force non FBO Blur");
	o->longDesc = N_("Force non FBO Blur.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_FORCE_NON_FBO_BLUR_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_NON_FBO_BLUR_STRENGTH];
	o->advanced = False;
	o->name = "non_fbo_blur_strength";
	o->group = N_("Blur");
	o->subGroup = N_("Visibility/Performance");
	o->displayHints = "";
	o->shortDesc = N_("Non FBO Blur Strength");
	o->longDesc = N_("Non FBO mode Blur strength.");
	o->type = CompOptionTypeInt;
	o->value.i = BLURFX_SCREEN_OPTION_NON_FBO_BLUR_STRENGTH_DEFAULT;
	o->rest.i.min = 2;
	o->rest.i.max = 12;

	o = &bs->opt[BLURFX_SCREEN_OPTION_BLUR_CACHE_TEXTURES];
	o->advanced = False;
	o->name = "blur_cache";
	o->group = N_("Blur");
	o->subGroup = N_("Visibility/Performance");
	o->displayHints = "";
	o->shortDesc = N_("Use Blur Cache");
	o->longDesc = N_("Enables Blur cache texture system.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_BLUR_CACHE_TEXTURES_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_NO_BLUR_CACHE_ON_TRANSFORMED_SCREEN];
	o->advanced = False;
	o->name = "no_cache_when_transformed";
	o->group = N_("Blur");
	o->subGroup = N_("Visibility/Performance");
	o->displayHints = "";
	o->shortDesc = N_("Disable Blur Cache on Transformed Screen");
	o->longDesc = N_("Disables Blur cache on transformed screen.");
	o->type = CompOptionTypeBool;
	o->value.b =
			BLURFX_SCREEN_OPTION_NO_BLUR_CACHE_ON_TRANSFORMED_SCREEN_DEFAULT;


	o = &bs->opt[BLURFX_SCREEN_OPTION_DISABLE_DECORATION_ALPHADEP];
	o->advanced = False;
	o->name = "disable_deco_alpha_dep";
	o->group = N_("Blur");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Disable Decoration Alpha Dependency");
	o->longDesc = N_("Disable decoration alpha dependency.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_DISABLE_DECORATION_ALPHADEP_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_DISABLE_BLUR_ON_SCREENGRAB_OF];
	o->advanced = False;
	o->name = "disable_blur_on_screengrab_of";
	o->group = N_("Blur");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Disable Blur on ScreenGrab");
	o->longDesc =
			N_
			("Disable drawing of Blur when an other plugin grabs the input.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = LIST_SIZE(blurDisable);
	o->value.list.value =
			malloc(sizeof(CompOptionValue) * LIST_SIZE(blurDisable));
	for (i = 0; i < LIST_SIZE(blurDisable); i++)
		o->value.list.value[i].s = strdup(blurDisable[i]);

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_ON_TRANSFORMED_SCREEN];
	o->advanced = False;
	o->name = "reflection_transformed_screen";
	o->group = N_("Reflection");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Reflection on Transformed Screen");
	o->longDesc = N_("Draw Reflection if the screen is transformed.");
	o->type = CompOptionTypeBool;
	o->value.b =
			BLURFX_SCREEN_OPTION_REFLECTION_ON_TRANSFORMED_SCREEN_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_TRANSFORMED_WINDOW];
	o->advanced = False;
	o->name = "reflection_transformed_window";
	o->group = N_("Reflection");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Reflection Transformed Windows");
	o->longDesc = N_("Draw Reflection if the window is transformed.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_REFLECTION_TRANSFORMED_WINDOW_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_DISABLE_REFLECTION_ON_SCREENGRAB_OF];
	o->advanced = False;
	o->name = "disable_reflection_on_screengrab_of";
	o->group = N_("Reflection");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Disable Reflection on ScreenGrab");
	o->longDesc =
			N_
			("Disable drawing of Reflection when an other plugin grabs the input.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = NULL;

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_FILE];
	o->advanced = False;
	o->name = "reflection_file";
	o->group = N_("Reflection");
	o->subGroup = N_("Image");
	o->displayHints = "file;image;pngonly;";
	o->shortDesc = N_("Reflection Image File");
	o->longDesc = N_("Reflection image file.");
	o->type = CompOptionTypeString;
	o->value.s = strdup(BLURFX_SCREEN_OPTION_REFLECTION_FILE_DEFAULT);
	o->rest.s.string = 0;
	o->rest.s.nString = 0;

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_SCALE];
	o->advanced = False;
	o->name = "reflection_scale";
	o->group = N_("Reflection");
	o->subGroup = N_("Image");
	o->displayHints = "";
	o->shortDesc = N_("Scale Reflection");
	o->longDesc = N_("Scale Reflection image.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_REFLECTION_SCALE_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_PROPORTIONAL];
	o->advanced = False;
	o->name = "reflection_proportional";
	o->group = N_("Reflection");
	o->subGroup = N_("Image");
	o->displayHints = "";
	o->shortDesc = N_("Scale Proportionally");
	o->longDesc = N_("Scale Reflection proportionally.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_REFLECTION_PROPORTIONAL_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_COLOR];
	o->advanced = False;
	o->name = "reflection_use_image_colors";
	o->group = N_("Reflection");
	o->subGroup = N_("Image");
	o->displayHints = "";
	o->shortDesc = N_("Use Image Colors");
	o->longDesc = N_("Do not generate grayscale Reflection.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_COLOR_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_ALPHA];
	o->advanced = False;
	o->name = "reflection_use_image_alpha";
	o->group = N_("Reflection");
	o->subGroup = N_("Image");
	o->displayHints = "";
	o->shortDesc = N_("Use Image Alpha");
	o->longDesc = N_("Use image alpha channel.");
	o->type = CompOptionTypeBool;
	o->value.b = BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_ALPHA_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_MOTION_BLUR_MODE];
	o->advanced = False;
	o->name = "motion_blur_mode";
	o->group = N_("Motion Blur");
	o->subGroup = N_("Visibility/Performance");
	o->displayHints = "";
	o->shortDesc = N_("Motion Blur Mode");
	o->longDesc =
			N_
			("Motion Blur mode: simple = uses texture copy and blending; accum = "
			 "uses gl accumulation buffer; fbo = uses framebuffer objects and float texture.");
	o->type = CompOptionTypeString;
	o->value.s = strdup(BLURFX_SCREEN_OPTION_MOTION_BLUR_MODE_DEFAULT);
	o->rest.s.string = mBlurModes;
	o->rest.s.nString = LIST_SIZE(mBlurModes);

	o = &bs->opt[BLURFX_SCREEN_OPTION_MOTION_BLUR_ON_SCREENGRAB_OF];
	o->advanced = False;
	o->name = "motion_blur_on_screengrab_of";
	o->group = N_("Motion Blur");
	o->subGroup = N_("Activate");
	o->displayHints = "";
	o->shortDesc = N_("Blur on ScreenGrab");
	o->longDesc = N_("Activate Motion Blur on screengrab.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = NULL;

	o = &bs->opt[BLURFX_SCREEN_OPTION_MOTION_BLUR_ON_TRANSFORMED_SCREEN];
	o->advanced = False;
	o->name = "motion_blur_on_transformed_screen";
	o->group = N_("Motion Blur");
	o->subGroup = N_("Activate");
	o->displayHints = "";
	o->shortDesc = N_("Motion Blur on Transformed Screen");
	o->longDesc = N_("Execute Motion Blur if the screen is transformed.");
	o->type = CompOptionTypeBool;
	o->value.b =
			BLURFX_SCREEN_OPTION_MOTION_BLUR_ON_TRANSFORMED_SCREEN_DEFAULT;

	o = &bs->opt[BLURFX_SCREEN_OPTION_MOTION_BLUR_STRENGTH];
	o->advanced = False;
	o->name = "motion_blur_strength";
	o->group = N_("Motion Blur");
	o->subGroup = N_("Visibility/Performance");
	o->displayHints = "";
	o->shortDesc = N_("Motion Blur Strength");
	o->longDesc = N_("Motion Blur strength.");
	o->type = CompOptionTypeFloat;
	o->value.f = BLURFX_SCREEN_OPTION_MOTION_BLUR_STRENGTH_DEFAULT;
	o->rest.f.min = 0.0;
	o->rest.f.max = 100.0;
	o->rest.f.precision = 0.01;

	o = &bs->opt[BLURFX_SCREEN_OPTION_DISABLE_BLUR_WINDOW_TYPE];
	o->advanced = False;
	o->name = "disable_blur_window_types";
	o->group = N_("Blur");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Disable Blur Window Types");
	o->longDesc = N_("Window types that Blur shouldn't affect.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = NULL;
	o->rest.s.string = (char **)windowTypeString;
	o->rest.s.nString = nWindowTypeString;

	o = &bs->opt[BLURFX_SCREEN_OPTION_DISABLE_REFLECTION_WINDOW_TYPE];
	o->advanced = False;
	o->name = "disable_reflection_window_types";
	o->group = N_("Reflection");
	o->subGroup = N_("Special cases");
	o->displayHints = "";
	o->shortDesc = N_("Disable Reflection Window Types");
	o->longDesc = N_("Window types that Reflection shouldn't affect.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = NULL;
	o->rest.s.string = (char **)windowTypeString;
	o->rest.s.nString = nWindowTypeString;

	o = &bs->opt[BLURFX_SCREEN_OPTION_BLUR_SATURATION];
	o->advanced = False;
	o->name = "blur_saturation";
	o->group = N_("Blur");
	o->subGroup = N_("Visibility/Performance");
	o->displayHints = "";
	o->shortDesc = N_("Blur Saturation");
	o->longDesc = N_("Blur saturation.");
	o->type = CompOptionTypeFloat;
	o->value.f = BLURFX_SCREEN_OPTION_BLUR_SATURATION_DEFAULT;
	o->rest.f.min = 0.0;
	o->rest.f.max = 100.0;
	o->rest.f.precision = 0.1;

	o = &bs->opt[BLURFX_SCREEN_OPTION_WARNING];
	o->advanced = False;
	o->name = "warning";
	o->group = N_("Warning");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Warning: NVidia users experiencing the black window bug should disable Blur");
	o->longDesc = N_("Warning: NVidia users experiencing the black window bug should disable Blur.");
	o->type = CompOptionTypeBool;
	o->value.f = TRUE;


}

static void blurfxPreparePaintScreen(CompScreen * s, int ms)
{
	BLURFX_SCREEN(s);
	Bool mba = FALSE;
	int i;

	// activate motion blur on desired screen grab
	for (i = 0;
		 i <
		 bs->opt[BLURFX_SCREEN_OPTION_MOTION_BLUR_ON_SCREENGRAB_OF].
		 value.list.nValue; i++)
	{
		mba |= screenGrabExist(s,
							   bs->
							   opt
							   [BLURFX_SCREEN_OPTION_MOTION_BLUR_ON_SCREENGRAB_OF].
							   value.list.value[i].s, 0);
	}

	bs->motion_blur_active |= bs->mb_activated;
	bs->motion_blur_active |= mba;

	bs->motion_blur_active &= bs->mblur_supported;

	// fade motion blur out if no longer active
	if (mba || bs->mb_activated)
	{
		bs->mb_timer = 500;
	}
	else
	{
		bs->mb_timer -= ms;
	}

	// calculate motion blur strength dependent on framerate
	float val = 101 - MIN(100, MAX(1, ms));

	float a_val =
			(bs->opt[BLURFX_SCREEN_OPTION_MOTION_BLUR_STRENGTH].value.f /
			 20.0);
	a_val = a_val * a_val;
	a_val /= 100.0;

	bs->mb_alpha = 1.0 - pow(a_val, 1.0 / val);

	if (bs->motion_blur_active && bs->mb_timer <= 0)
		damageScreen(s);

	if (bs->mb_timer <= 0)
	{
		bs->motion_blur_active = FALSE;
	}
	if (bs->mb_update && bs->motion_blur_active)
		damageScreen(s);

	UNWRAP(bs, s, preparePaintScreen);
	(*s->preparePaintScreen) (s, ms);
	WRAP(bs, s, preparePaintScreen, blurfxPreparePaintScreen);
}


static Bool
blurfxPaintScreen(CompScreen * s, const ScreenPaintAttrib * sa,
				  Region region, int output, unsigned int mask)
{

	Bool status, blur_fbo;

	BLURFX_SCREEN(s);

	int i;
	Region bigregion = XCreateRegion();
	XRectangle rect;

	bs->fboActive = FALSE;

	bs->realPaintRegion = region;

	bs->output = output;

	// blur needs bigger repaint regions
	for (i = 0; i < region->numRects; i++)
	{
		rect.x = region->rects[i].x1 - 7;
		rect.y = region->rects[i].y1 - 7;
		rect.width = region->rects[i].x2 - rect.x + 14;
		rect.height = region->rects[i].y2 - rect.y + 14;

		XUnionRectWithRegion(&rect, bigregion, bigregion);
	}

	//Initiliaze the screen if ith hasnt beens
	if (bs->hasInit != 1)
	{
		initBlurfxScreen(bs, s->width, s->height, s);
	}
	// clear screen damage region
	XUnionRegion(getEmptyRegion(), getEmptyRegion(), bs->screenDamage);

	// enable blur if needed
	blur_fbo = bs->blur_supported && bs->fboBlur_supported
			&& !bs->opt[BLURFX_SCREEN_OPTION_FORCE_NON_FBO_BLUR].value.b;
	blur_fbo &= bs->opt[BLURFX_SCREEN_OPTION_BLUR_DECORATION].value.b
			|| bs->opt[BLURFX_SCREEN_OPTION_BLUR_WINDOWS].value.b;

	if (!bs->opt[BLURFX_SCREEN_OPTION_BLUR_ON_TRANSFORMED_SCREEN].value.b)
		blur_fbo &= !(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK);

	for (i = 0;
		 i <
		 bs->opt[BLURFX_SCREEN_OPTION_DISABLE_BLUR_ON_SCREENGRAB_OF].
		 value.list.nValue; i++)
	{
		blur_fbo &=
				!screenGrabExist(s,
								 bs->
								 opt
								 [BLURFX_SCREEN_OPTION_DISABLE_BLUR_ON_SCREENGRAB_OF].
								 value.list.value[i].s, 0);
	}

	bs->od_active = FALSE;

	// calculate occluted regions
	if (bs->opt[BLURFX_SCREEN_OPTION_BLUR_DECORATION].value.b
		|| bs->opt[BLURFX_SCREEN_OPTION_BLUR_WINDOWS].value.b
		|| bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_WINDOWS].value.b)
	{
		bs->mode = MODE_OCCLUSION;
		bs->od_active = TRUE;
		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bs->occlusion);
		CompWindow *w;

		for (w = s->reverseWindows; w; w = w->prev)
		{
			BLURFX_WINDOW(w);
			if (w->destroyed)
				continue;

			if (w->state & CompWindowStateOffscreenMask)
				continue;

			if (!w->shaded)
			{
				if (w->attrib.map_state != IsViewable || !w->damaged)
					continue;
			}
			XUnionRegion(bs->occlusion, getEmptyRegion(), bw->clip);
			(*s->drawWindow) (w, &w->paint, &s->region,
							  PAINT_WINDOW_SOLID_MASK);
		}
	}

	bs->mode = MODE_NORMAL;

	Bool splash = IPCS_GetBoolND(IPCS_OBJECT(s), "SPLASH_IS_ACTIVE", FALSE);

	blur_fbo &= !DISABLE_FEATURE(s, bs->ipcs_disable_blur);
	blur_fbo &= !splash;
	bs->motion_blur_active &= !DISABLE_FEATURE(s, bs->ipcs_disable_mblur);
	bs->motion_blur_active &= !splash;

	if (!bs->motion_blur_active)
		bs->mb_update = TRUE;

	Bool enable_scissor = FALSE;

	// store the projection matrix
	glGetFloatv(GL_PROJECTION_MATRIX, bs->pm);

	if ((blur_fbo || (bs->motion_blur_active && bs->mb_mode == 2)) && s->fbo)
	{

		float ModelView[16];

		glGetFloatv(GL_MODELVIEW_MATRIX, ModelView);


		// redirect all drawing operations into background texture
		Bool fbo_ok = bindFbo(s, bs->backTex);

		glMatrixMode(GL_PROJECTION);
		glLoadMatrixf(bs->pm);
		glMatrixMode(GL_MODELVIEW);
		glLoadMatrixf(ModelView);

		// paint the screen into fbo
		UNWRAP(bs, s, paintScreen);
		status = (*s->paintScreen) (s, sa, bigregion, output, mask);
		WRAP(bs, s, paintScreen, blurfxPaintScreen);

		unbindFbo(s);

		if (fbo_ok)
		{

			if (bs->motion_blur_active && bs->mb_mode == 2
				&& output + 1 == s->nOutputDev)
			{

				// blend actual screen into motion blur texture
				if (glIsEnabled(GL_SCISSOR_TEST))
				{
					glDisable(GL_SCISSOR_TEST);
					enable_scissor = TRUE;
				}
				glPushAttrib(GL_COLOR_BUFFER_BIT | GL_TEXTURE_BIT);
				GLERR;
				bindFbo(s, bs->motionTex);
				glPushMatrix();
				glLoadIdentity();
				glViewport(0, 0, s->width, s->height);
				glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
				glScalef(1.0f / s->width, -1.0f / s->height, 1.0f);
				glTranslatef(0.0f, -s->height, 0.0f);

				if (bs->mb_update)
				{
					glClearColor(1.0, 1.0, 1.0, 1.0);
					glClear(GL_COLOR_BUFFER_BIT);
					glDisable(GL_BLEND);
				}
				else
					glEnable(GL_BLEND);

				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				bs->mb_alpha =
						(bs->mb_timer / 500.0) * bs->mb_alpha + (1.0 -
																 (bs->
																  mb_timer
																  / 500.0)) *
						0.5;
				bs->mb_alpha = MIN(1.0, bs->mb_alpha);
				glColor4f(1, 1, 1, (bs->mb_update) ? 1.0 : bs->mb_alpha);
				enableBlurfxTexture(&bs->backTex, COMP_TEXTURE_FILTER_FAST);

				glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

				glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
				glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
				glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
				glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
				glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
				glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

				glBegin(GL_QUADS);
				glTexCoord2f(0, bs->backTex.height);
				glVertex2f(0, 0);
				glTexCoord2f(0, 0);
				glVertex2f(0, bs->backTex.height);
				glTexCoord2f(bs->backTex.width, 0);
				glVertex2f(bs->backTex.width, bs->backTex.height);
				glTexCoord2f(bs->backTex.width, bs->backTex.height);
				glVertex2f(bs->backTex.width, 0);

				glEnd();

				glDisable(GL_BLEND);

				glPopMatrix();
				unbindFbo(s);

				// paint motion blur texture
				glPushMatrix();
				glLoadIdentity();
				glViewport(0, 0, s->width, s->height);
				glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
				glScalef(1.0f / s->width, -1.0f / s->height, 1.0f);
				glTranslatef(0.0f, -s->height, 0.0f);

				glColor4f(1.0, 1.0, 1.0, 1.0);

				switchBlurfxTexture(&bs->backTex, &bs->motionTex,
									COMP_TEXTURE_FILTER_FAST);

				glBegin(GL_QUADS);
				glTexCoord2f(0, bs->backTex.height);
				glVertex2f(0, 0);
				glTexCoord2f(0, 0);
				glVertex2f(0, bs->backTex.height);
				glTexCoord2f(bs->backTex.width, 0);
				glVertex2f(bs->backTex.width, bs->backTex.height);
				glTexCoord2f(bs->backTex.width, bs->backTex.height);
				glVertex2f(bs->backTex.width, 0);

				glEnd();

				disableBlurfxTexture(&bs->motionTex);

				glPopMatrix();
				glViewport(s->outputDev[bs->output].region.extents.
						   x1,
						   s->height -
						   s->outputDev[bs->output].region.extents.
						   y2, s->outputDev[bs->output].width,
						   s->outputDev[bs->output].height);

				glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
				glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
				glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
				glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
				glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
				glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
				glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);

				glPopAttrib();
				GLERR;

				bs->mb_update = FALSE;
				damageScreen(s);

			}
			else
			{

				glPushMatrix();
				glLoadIdentity();
				prepareXCoords(s, output, -DEFAULT_Z_CAMERA);

				// draw background texture
				int stride;
				CompMatrix mat;

				mat.xx = 1.0;
				mat.yx = 0.0;
				mat.xy = 0.0;
				mat.yy = -1.0;
				mat.x0 = 0;
				mat.y0 = s->height;

				stride = 4 * sizeof(GLfloat);
				genGeometry(&bs->vertArray, mat, 0,
							&s->outputDev[bs->output].region);
				glVertexPointer(2, GL_FLOAT, stride,
								bs->vertArray.vertices + 2);
				glTexCoordPointer(2, GL_FLOAT, stride,
								  bs->vertArray.vertices);
				glColor4f(1.0, 1.0, 1.0, 1.0);

				enableBlurfxTexture(&bs->backTex, COMP_TEXTURE_FILTER_FAST);

				glDrawArrays(GL_QUADS, 0, bs->vertArray.vCount);

				disableBlurfxTexture(&bs->backTex);

				glPopMatrix();
			}
		}
	}
	else
	{
		UNWRAP(bs, s, paintScreen);
		status = (*s->paintScreen) (s, sa, bigregion, output, mask);
		WRAP(bs, s, paintScreen, blurfxPaintScreen);
	}

	if (bs->motion_blur_active && glIsEnabled(GL_SCISSOR_TEST))
	{
		glDisable(GL_SCISSOR_TEST);
		enable_scissor = TRUE;
	}

	if (bs->motion_blur_active && bs->mb_mode == 0
		&& output + 1 == s->nOutputDev)
	{

		// blend motion blur texture to screen
		glPushAttrib(GL_COLOR_BUFFER_BIT | GL_TEXTURE_BIT);
		GLERR;
		glPushMatrix();
		glLoadIdentity();
		glViewport(0, 0, s->width, s->height);
		glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
		glScalef(1.0f / s->width, -1.0f / s->height, 1.0f);
		glTranslatef(0.0f, -s->height, 0.0f);

		enableBlurfxTexture(&bs->motionTex, COMP_TEXTURE_FILTER_FAST);

		if (!bs->mb_update)
		{
			glEnable(GL_BLEND);

			glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
			bs->mb_alpha =
					(bs->mb_timer / 500.0) * bs->mb_alpha + (1.0 -
															 (bs->
															  mb_timer
															  / 500.0)) * 0.5;
			glColor4f(1, 1, 1, bs->mb_alpha);

			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

			glBegin(GL_QUADS);
			glTexCoord2f(0, bs->motionTex.height);
			glVertex2f(0, 0);
			glTexCoord2f(0, 0);
			glVertex2f(0, bs->motionTex.height);
			glTexCoord2f(bs->motionTex.width, 0);
			glVertex2f(bs->motionTex.width, bs->motionTex.height);
			glTexCoord2f(bs->motionTex.width, bs->motionTex.height);
			glVertex2f(bs->motionTex.width, 0);
			glEnd();

			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

			glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
			GLERR;
			glDisable(GL_BLEND);
		}
		// copy new screen to motion blur texture
		glCopyTexSubImage2D(bs->motionTex.target, 0, 0, 0, 0, 0,
							s->width, s->height);
		GLERR;

		disableBlurfxTexture(&bs->motionTex);

		glViewport(s->outputDev[bs->output].region.extents.x1,
				   s->height -
				   s->outputDev[bs->output].region.extents.y2,
				   s->outputDev[bs->output].width,
				   s->outputDev[bs->output].height);

		glPopMatrix();
		glPopAttrib();
		GLERR;

		bs->mb_update = FALSE;
		damageScreen(s);
	}

	if (bs->motion_blur_active && bs->mb_mode == 1
		&& output + 1 == s->nOutputDev)
	{

		// create motion blur effect using accumulation buffer
		bs->mb_alpha =
				(bs->mb_timer / 500.0) * bs->mb_alpha + (1.0 -
														 (bs->
														  mb_timer /
														  500.0)) * 0.5;
		if (bs->mb_update)
		{
			glAccum(GL_LOAD, 1.0);
		}
		else
		{
			glAccum(GL_MULT, 1.0 - bs->mb_alpha);
			glAccum(GL_ACCUM, bs->mb_alpha);
			glAccum(GL_RETURN, 1.0);
		}
		bs->mb_update = FALSE;
		damageScreen(s);
	}

	if (enable_scissor)
		glEnable(GL_SCISSOR_TEST);

	XDestroyRegion(bigregion);
	return status;
}

static void
blurfxPaintTransformedScreen(CompScreen * s, const ScreenPaintAttrib * sa,
							 Region region, int output, unsigned int mask)
{

	BLURFX_SCREEN(s);

	bs->realPaintRegion = &s->region;

	// calculate occluted regions
	if (bs->opt[BLURFX_SCREEN_OPTION_BLUR_DECORATION].value.b
		|| bs->opt[BLURFX_SCREEN_OPTION_BLUR_WINDOWS].value.b
		|| bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_WINDOWS].value.b)
	{
		bs->mode = MODE_OCCLUSION;
		bs->od_active = TRUE;
		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bs->occlusion);
		CompWindow *w;

		for (w = s->reverseWindows; w; w = w->prev)
		{
			BLURFX_WINDOW(w);
			if (w->destroyed)
				continue;

			if (!w->shaded)
			{
				if (w->attrib.map_state != IsViewable || !w->damaged)
					continue;
			}
			XUnionRegion(bs->occlusion, getEmptyRegion(), bw->clip);
			(*s->drawWindow) (w, &w->paint, &s->region,
							  PAINT_WINDOW_SOLID_MASK);
		}
	}
	bs->mode = MODE_NORMAL;
	UNWRAP(bs, s, paintTransformedScreen);
	(*s->paintTransformedScreen) (s, sa, region, output, mask);
	WRAP(bs, s, paintTransformedScreen, blurfxPaintTransformedScreen);
}


static Bool
blurfxDrawWindow(CompWindow * w, const WindowPaintAttrib * attrib,
				 Region region, unsigned int mask)
{
	Bool status;
	int i;

	BLURFX_SCREEN(w->screen);
	BLURFX_WINDOW(w);

	CompScreen *s = w->screen;
	Bool blur_enabled, reflection_enabled;

	if (bs->mode == MODE_OCCLUSION)
	{
		UNWRAP(bs, s, drawWindow);
		status = (*s->drawWindow) (w, attrib, region, mask);
		WRAP(bs, s, drawWindow, blurfxDrawWindow);
		return status;
	}

	// enable blur and/or reflection
	blur_enabled = bs->blur_supported;
	reflection_enabled = bs->reflection_supported;

	blur_enabled &=
			bs->opt[BLURFX_SCREEN_OPTION_BLUR_DECORATION].value.b
			|| bs->opt[BLURFX_SCREEN_OPTION_BLUR_WINDOWS].value.b;
	reflection_enabled &=
			bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_DECORATION].value.b
			|| bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_WINDOWS].value.b;

	blur_enabled &= !(mask & PAINT_WINDOW_SOLID_MASK);

	reflection_enabled &= !(mask & PAINT_WINDOW_SOLID_MASK);
	reflection_enabled &= bs->modTex.handle > 0;

	if (bs->
		opt[BLURFX_SCREEN_OPTION_MOTION_BLUR_ON_TRANSFORMED_SCREEN].
		value.b && (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK))
	{
		bs->motion_blur_active = bs->mblur_supported;
		bs->mb_timer = 500;
	}

	if (!bs->opt[BLURFX_SCREEN_OPTION_BLUR_ON_TRANSFORMED_SCREEN].value.b)
		blur_enabled &= !(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK);
	if (!bs->
		opt[BLURFX_SCREEN_OPTION_REFLECTION_ON_TRANSFORMED_SCREEN].value.b)
		reflection_enabled &=
				!(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK);

	if (!bs->opt[BLURFX_SCREEN_OPTION_BLUR_TRANSFORMED_WINDOW].value.b)
		blur_enabled &= !(mask & PAINT_WINDOW_TRANSFORMED_MASK);
	if (!bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_TRANSFORMED_WINDOW].value.b)
		reflection_enabled &= !(mask & PAINT_WINDOW_TRANSFORMED_MASK);

	for (i = 0;
		 i <
		 bs->opt[BLURFX_SCREEN_OPTION_DISABLE_BLUR_ON_SCREENGRAB_OF].
		 value.list.nValue; i++)
	{
		blur_enabled &=
				!screenGrabExist(s,
								 bs->
								 opt
								 [BLURFX_SCREEN_OPTION_DISABLE_BLUR_ON_SCREENGRAB_OF].
								 value.list.value[i].s, 0);
	}

	for (i = 0;
		 i <
		 bs->
		 opt[BLURFX_SCREEN_OPTION_DISABLE_REFLECTION_ON_SCREENGRAB_OF].
		 value.list.nValue; i++)
	{
		reflection_enabled &=
				!screenGrabExist(s,
								 bs->
								 opt
								 [BLURFX_SCREEN_OPTION_DISABLE_REFLECTION_ON_SCREENGRAB_OF].
								 value.list.value[i].s, 0);
	}

	Bool splash = IPCS_GetBoolND(IPCS_OBJECT(s), "SPLASH_IS_ACTIVE", FALSE);

	blur_enabled &= !DISABLE_FEATURE(s, bs->ipcs_disable_blur);
	blur_enabled &= !DISABLE_FEATURE(w, bw->ipcs_disable_blur);
	blur_enabled &= !splash;

	reflection_enabled &= !DISABLE_FEATURE(s, bs->ipcs_disable_reflection);
	reflection_enabled &= !DISABLE_FEATURE(w, bw->ipcs_disable_reflection);
	reflection_enabled &= !splash;


	// check for restacking and update damage if needed
	if (w->next != bw->my_next)
	{
		XRectangle rect;

		rect.x = WIN_X(w) - 7;
		rect.y = WIN_Y(w) - 7;
		rect.width = WIN_W(w) + 14;
		rect.height = WIN_H(w) + 14;

		XUnionRectWithRegion(&rect, bs->screenDamage, bs->screenDamage);
		bw->my_next = w->next;
	}

	if (bs->opt[BLURFX_SCREEN_OPTION_BLUR_CACHE_TEXTURES].value.b)
		XUnionRegion(bw->texDamage, bs->screenDamage, bw->texDamage);

	bw->mvm_updated = FALSE;

	if (blur_enabled || reflection_enabled)
	{

		// clear our decoration vertex array
		bw->decoArray.vCount = 0;
		bw->decoArray.indexSize = 0;
		bw->decoArray.indexCount = 0;

		// store region we need to paint
		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bw->paintRegion);
		XIntersectRegion(bs->realPaintRegion, region, bw->paintRegion);
		bw->texUpdated = FALSE;

		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bs->blurredRegion);

		// paint blur and reflection

		bs->mode = MODE_BLURREFLECTION;

		bs->paintingSwitcher = bw->isSwitcher;

		UNWRAP(bs, s, drawWindow);
		status = (*s->drawWindow) (w, attrib, region, mask);
		WRAP(bs, s, drawWindow, blurfxDrawWindow);

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

		// paint window
		bs->mode = MODE_NORMAL;
		bs->was_transformed = FALSE;

		UNWRAP(bs, s, drawWindow);
		status = (*s->drawWindow) (w, attrib, region, mask);
		WRAP(bs, s, drawWindow, blurfxDrawWindow);

	}
	else
	{
		// paint window
		bs->mode = MODE_NORMAL;
		bs->was_transformed = FALSE;

		UNWRAP(bs, s, drawWindow);
		status = (*s->drawWindow) (w, attrib, region, mask);
		WRAP(bs, s, drawWindow, blurfxDrawWindow);

	}

	if (bs->was_transformed)
		XUnionRegion(&s->outputDev[bs->output].region,
					 bs->screenDamage, bs->screenDamage);

	if (!(mask & PAINT_WINDOW_SOLID_MASK) && !WINDOW_INPUT_INVISIBLE(w))
	{

		// not all plugins send damage events on move or scale
		XRectangle rect;

		if (bw->lastX != w->attrib.x || bw->lastY != w->attrib.y
			|| w->lastPaint.xScale != attrib->xScale
			|| w->lastPaint.yScale != attrib->yScale)
		{
			rect.x = bw->lastPAttrib.xTranslate +
					(-w->output.left * bw->lastPAttrib.xScale) + bw->lastX;
			rect.y = bw->lastPAttrib.yTranslate +
					(-w->output.top * bw->lastPAttrib.yScale) + bw->lastY;
			rect.width = WIN_W(w) * bw->lastPAttrib.xScale;
			rect.height = WIN_H(w) * bw->lastPAttrib.yScale;
			XUnionRectWithRegion(&rect, bs->screenDamage, bs->screenDamage);
			rect.x = attrib->xTranslate +
					(-w->output.left * attrib->xScale) + w->attrib.x;
			rect.y = attrib->yTranslate +
					(-w->output.top * attrib->yScale) + w->attrib.x;
			rect.width = WIN_W(w) * attrib->xScale;
			rect.height = WIN_H(w) * attrib->yScale;
			XUnionRectWithRegion(&rect, bs->screenDamage, bs->screenDamage);
		}
		bw->lastX = w->attrib.x;
		bw->lastY = w->attrib.y;

		if (bw->lastPAttrib.xScale != 1.0 || bw->lastPAttrib.yScale != 1.0)
		{
			for (i = 0; i < bw->damageRegion->numRects; i++)
			{
				BOX box = bw->damageRegion->rects[i];

				rect.x = bw->lastPAttrib.xTranslate +
						(box.x1 - w->attrib.x) * bw->lastPAttrib.xScale;
				rect.y = bw->lastPAttrib.yTranslate + (box.y1 -
													   w->
													   attrib.
													   y) *
						bw->lastPAttrib.yScale;
				rect.width = (box.x2 - box.x1) * bw->lastPAttrib.xScale;
				rect.height = (box.y2 - box.y1) * bw->lastPAttrib.yScale;
				XUnionRectWithRegion(&rect,
									 bs->screenDamage, bs->screenDamage);
			}
		}
		else
		{
			XUnionRegion(bw->damageRegion, bs->screenDamage,
						 bs->screenDamage);
		}
		bw->lastPAttrib = *attrib;
		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bw->damageRegion);
	}

	return status;
}

static void
blurfxDrawWindowTexture(CompWindow * w, CompTexture * texture,
						const WindowPaintAttrib * attrib, unsigned int mask)
{

	CompScreen *s = w->screen;
	Bool blur_enabled, reflection_enabled;
	int i;


	BLURFX_SCREEN(w->screen);
	BLURFX_WINDOW(w);

	// enable blur and/or reflection
	blur_enabled = bs->blur_supported;
	reflection_enabled = bs->reflection_supported;

	blur_enabled &=
			(mask & PAINT_WINDOW_DECORATION_MASK) ? bs->
			opt[BLURFX_SCREEN_OPTION_BLUR_DECORATION].value.b : bs->
			opt[BLURFX_SCREEN_OPTION_BLUR_WINDOWS].value.b;
	reflection_enabled &=
			(mask & PAINT_WINDOW_DECORATION_MASK) ? bs->
			opt[BLURFX_SCREEN_OPTION_REFLECTION_DECORATION].value.b : bs->
			opt[BLURFX_SCREEN_OPTION_REFLECTION_WINDOWS].value.b;

	if ((mask & PAINT_WINDOW_DECORATION_MASK) && w->input.top == 0
		&& w->input.bottom == 0 && w->input.left == 0 && w->input.right == 0)
	{
		if (!bw->isSwitcher)
		{
			blur_enabled = FALSE;
			reflection_enabled = FALSE;
		}
	}

	if (!(mask & PAINT_WINDOW_DECORATION_MASK))
	{
		blur_enabled &= !(bs->noBlurWMask & w->type);
		reflection_enabled &= !(bs->noReflectionWMask & w->type);
		blur_enabled &= w->alpha || attrib->opacity < OPAQUE;
		reflection_enabled &= w->alpha || attrib->opacity < OPAQUE;
	}

	reflection_enabled &= bs->modTex.handle > 0;

	if (!bs->opt[BLURFX_SCREEN_OPTION_BLUR_ON_TRANSFORMED_SCREEN].value.b)
		blur_enabled &= !(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK);
	if (!bs->
		opt[BLURFX_SCREEN_OPTION_REFLECTION_ON_TRANSFORMED_SCREEN].value.b)
		reflection_enabled &=
				!(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK);

	if (!bs->opt[BLURFX_SCREEN_OPTION_BLUR_TRANSFORMED_WINDOW].value.b)
		blur_enabled &= !(mask & PAINT_WINDOW_TRANSFORMED_MASK);
	if (!bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_TRANSFORMED_WINDOW].value.b)
		reflection_enabled &= !(mask & PAINT_WINDOW_TRANSFORMED_MASK);

	for (i = 0;
		 i <
		 bs->opt[BLURFX_SCREEN_OPTION_DISABLE_BLUR_ON_SCREENGRAB_OF].
		 value.list.nValue; i++)
	{
		blur_enabled &=
				!screenGrabExist(s,
								 bs->
								 opt
								 [BLURFX_SCREEN_OPTION_DISABLE_BLUR_ON_SCREENGRAB_OF].
								 value.list.value[i].s, 0);
	}

	for (i = 0;
		 i <
		 bs->
		 opt[BLURFX_SCREEN_OPTION_DISABLE_REFLECTION_ON_SCREENGRAB_OF].
		 value.list.nValue; i++)
	{
		reflection_enabled &=
				!screenGrabExist(s,
								 bs->
								 opt
								 [BLURFX_SCREEN_OPTION_DISABLE_REFLECTION_ON_SCREENGRAB_OF].
								 value.list.value[i].s, 0);
	}

	Bool splash = IPCS_GetBoolND(IPCS_OBJECT(s), "SPLASH_IS_ACTIVE", FALSE);

	blur_enabled &= !DISABLE_FEATURE(s, bs->ipcs_disable_blur);
	blur_enabled &= !DISABLE_FEATURE(w, bw->ipcs_disable_blur);
	blur_enabled &= !splash;

	reflection_enabled &= !DISABLE_FEATURE(s, bs->ipcs_disable_reflection);
	reflection_enabled &= !DISABLE_FEATURE(w, bw->ipcs_disable_reflection);
	reflection_enabled &= !splash;

	// add window region to occlusion region
	if (bs->mode == MODE_OCCLUSION)
	{
		if (!(mask & PAINT_WINDOW_TRANSFORMED_MASK))
			return;
		if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
			return;
		if (mask & PAINT_WINDOW_DECORATION_MASK)
			return;
		if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
			return;
		if (WINDOW_INPUT_INVISIBLE(w))
			return;
		XUnionRegion(w->region, bs->occlusion, bs->occlusion);
		return;
	}
	// draw normal window
	if (bs->mode == MODE_NORMAL)
	{
		GLERR;
		UNWRAP(bs, w->screen, drawWindowTexture);
		(*w->screen->drawWindowTexture) (w, texture, attrib, mask);
		WRAP(bs, w->screen, drawWindowTexture, blurfxDrawWindowTexture);
#ifdef GL_DEBUG
		glGetError();
#endif
		bs->was_transformed |= (mask & PAINT_WINDOW_TRANSFORMED_MASK);
		return;
	}

	if (!(mask & PAINT_WINDOW_TRANSLUCENT_MASK)
		|| bs->mode != MODE_BLURREFLECTION
		|| (mask & PAINT_WINDOW_SHADOW_MASK))
		return;

	if (WINDOW_INPUT_INVISIBLE(w)
		&& !(w->shaded && (mask & PAINT_WINDOW_DECORATION_MASK)))
		return;

	if (bs->paintingSwitcher && !bw->isSwitcher)
		return;

	int filter;

	Bool useFbo = bs->fboActive
			&& !bs->opt[BLURFX_SCREEN_OPTION_FORCE_NON_FBO_BLUR].value.b;

	// select texture filter
	if (mask &
		(PAINT_WINDOW_TRANSFORMED_MASK |
		 PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK) && useFbo)
		filter = COMP_TEXTURE_FILTER_FAST;
	else
		filter = COMP_TEXTURE_FILTER_GOOD;

	// draw blur
	if (blur_enabled)
	{
		Bool enable_scissor = FALSE;
		Bool enable_clipPlanes = FALSE;

		if (useFbo)
		{
			if (glIsEnabled(GL_CLIP_PLANE0))
			{
				glDisable (GL_CLIP_PLANE0);
				glDisable (GL_CLIP_PLANE1);
				glDisable (GL_CLIP_PLANE2);
				glDisable (GL_CLIP_PLANE3);
				enable_clipPlanes = TRUE;
			}
			if (glIsEnabled(GL_SCISSOR_TEST))
			{
				glDisable(GL_SCISSOR_TEST);
				enable_scissor = TRUE;
			}
			bs->downSample = 1.0;
		}
		else
		{
			bs->downSample =
					bs->opt[BLURFX_SCREEN_OPTION_NON_FBO_BLUR_STRENGTH].value.
					i;
		}
		if (bs->opt[BLURFX_SCREEN_OPTION_BLUR_CACHE_TEXTURES].value.b)
		{
			// update blur cache texture
			bw->texUpdated = TRUE;
			updateBlurTextureSize(w, s, attrib, mask);
			GLERR;
			if (useFbo)
			{
				updateBlurTexture(w, s, attrib, mask, filter);
				GLERR;
			}
			else
			{
				updateBlurTextureNoFBO(w, s, attrib, mask);
				GLERR;
			}
		}
		if (useFbo)
		{
			updateBlur(w, s, attrib, mask, filter);
			GLERR;

			if (enable_clipPlanes)
			{
				glEnable (GL_CLIP_PLANE0);
				glEnable (GL_CLIP_PLANE1);
				glEnable (GL_CLIP_PLANE2);
				glEnable (GL_CLIP_PLANE3);
			}
			if (enable_scissor)
				glEnable(GL_SCISSOR_TEST);
		}
		else
		{
			updateBlurNoFBO(w, s, attrib, mask);
			GLERR;
		}
		drawBlur(w, s, texture, attrib, mask, filter);
	}

	// draw reflection
	if (reflection_enabled)
		drawReflection(w, s, texture, attrib, mask, COMP_TEXTURE_FILTER_GOOD);

}

static void
blurfxAddWindowGeometry(CompWindow * w, CompMatrix * m, int nM,
						Region region, Region clip)
{
	BLURFX_SCREEN(w->screen);
	BLURFX_WINDOW(w);
	REGION box;

	if (bs->mode == MODE_OCCLUSION)
		return;


	// try to detect decorations
	if (bs->mode == MODE_BLURREFLECTION && region->numRects == 1
		&&
		((region->extents.x1 < w->attrib.x
		  && region->extents.x2 <= w->attrib.x)
		 || (region->extents.y1 < w->attrib.y
			 && region->extents.y2 <= w->attrib.y)
		 || (region->extents.x1 >= w->attrib.x + w->width
			 && region->extents.x2 > w->attrib.x + w->width)
		 || (region->extents.y1 >= w->attrib.y + w->height
			 && region->extents.y2 > w->attrib.y + w->height)))
	{
		box.numRects = 1;
		// create vertex array without shadow
		box.rects = &box.extents;
		box.extents.x1 = MAX(region->extents.x1, WIN_XO(w));
		box.extents.y1 = MAX(region->extents.y1, WIN_YO(w));
		box.extents.x2 = MIN(region->extents.x2, WIN_XO(w) + WIN_WO(w));
		box.extents.y2 = MIN(region->extents.y2, WIN_YO(w) + WIN_HO(w));
		if (box.extents.x1 < box.extents.x2
			&& box.extents.y1 < box.extents.y2)
		{
			CompWindow *cw = malloc(sizeof(CompWindow));

			memcpy(cw, w, sizeof(CompWindow));
			cw->vertexSize = bw->decoArray.vertexSize;
			cw->indexSize = bw->decoArray.indexSize;
			cw->vertices = bw->decoArray.vertices;
			cw->indices = bw->decoArray.indices;
			cw->vCount = bw->decoArray.vCount;
			cw->indexCount = bw->decoArray.indexCount;
			UNWRAP(bs, w->screen, addWindowGeometry);
			(*w->screen->addWindowGeometry) (cw, m, nM, &box, clip);
			WRAP(bs, w->screen, addWindowGeometry, blurfxAddWindowGeometry);
			bw->decoArray.vertexSize = cw->vertexSize;
			bw->decoArray.indexSize = cw->indexSize;
			bw->decoArray.vertices = cw->vertices;
			bw->decoArray.indices = cw->indices;
			bw->decoArray.vCount = cw->vCount;
			bw->decoArray.indexCount = cw->indexCount;
			free(cw);
		}
	}

	UNWRAP(bs, w->screen, addWindowGeometry);
	(*w->screen->addWindowGeometry) (w, m, nM, region, clip);
	WRAP(bs, w->screen, addWindowGeometry, blurfxAddWindowGeometry);
}


static Bool blurfxDamageWindowRect(CompWindow * w, Bool initial, BoxPtr box)
{
	Bool status;

	BLURFX_SCREEN(w->screen);
	BLURFX_WINDOW(w);

	XRectangle rect;

	// a bigger damage region is needed to have correct blur
	box->x1 -= 7;
	box->x2 += 7;
	box->y1 -= 7;
	box->y2 += 7;

	rect.x = w->serverX + box->x1;
	rect.y = w->serverY + box->y1;
	rect.width = box->x2 - box->x1;
	rect.height = box->y2 - box->y1;

	// store window damage
	if (bs->opt[BLURFX_SCREEN_OPTION_BLUR_CACHE_TEXTURES].value.b)
		XUnionRectWithRegion(&rect, bw->damageRegion, bw->damageRegion);


	UNWRAP(bs, w->screen, damageWindowRect);
	status = (*w->screen->damageWindowRect) (w, initial, box);
	WRAP(bs, w->screen, damageWindowRect, blurfxDamageWindowRect);

	return status;
}

static void
updateBlurTexture(CompWindow * w, CompScreen * s,
				  const WindowPaintAttrib * attrib, unsigned int mask,
				  CompTextureFilter filter)
{

	Region reblurRegion;
	Region blurPaintRegion;
	int stride, count, stop, vCount, iCount;
	CompMatrix mat;
	GLfloat *vertices, x1, x2, x3, x4, y1, y2, y3, y4, bx1, bx2, by1, by2;
	GLushort *indices;
	XRectangle rect;

	BLURFX_SCREEN(s);
	BLURFX_WINDOW(w);

	if (bw->bc_x != s->x || bw->bc_y != s->y)
	{
		bw->texUpdated = FALSE;
		return;
	}

	if (bs->
		opt[BLURFX_SCREEN_OPTION_NO_BLUR_CACHE_ON_TRANSFORMED_SCREEN].
		value.b && mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)
	{
		bw->texUpdated = FALSE;
		return;
	}
	// Do we have an decoration vertex array thet does not contain shadows?
	if (mask & PAINT_WINDOW_DECORATION_MASK && bw->decoArray.vCount)
	{
		vertices = bw->decoArray.vertices + (w->texUnits * w->texCoordSize);
		indices = bw->decoArray.indices;
		vCount = bw->decoArray.vCount;
		iCount = bw->decoArray.indexCount;
	}
	else
	{
		vertices = w->vertices + (w->texUnits * w->texCoordSize);
		indices = w->indices;
		vCount = w->vCount;
		iCount = w->indexCount;
	}

	if (vCount <= 0)
		return;
	stride = (w->texUnits * w->texCoordSize) + 2;

	// get regions we need not to update
	reblurRegion = XCreateRegion();
	blurPaintRegion = XCreateRegion();

	count = 0;
	stop = (iCount) ? iCount : vCount;

	bx1 = 65535;
	bx2 = 0.0;
	by1 = 65535;
	by2 = 0.0;

	// generate region that needs blur
	while (count < stop)
	{
		if (iCount)
		{
			x1 = vertices[indices[count] * stride];
			y1 = vertices[(indices[count] * stride) + 1];
			count++;
			x2 = vertices[indices[count] * stride];
			y2 = vertices[(indices[count] * stride) + 1];
			count++;
			x3 = vertices[indices[count] * stride];
			y3 = vertices[(indices[count] * stride) + 1];
			count++;
			x4 = vertices[indices[count] * stride];
			y4 = vertices[(indices[count] * stride) + 1];
			count++;

		}
		else
		{
			x1 = vertices[0];
			y1 = vertices[1];
			vertices += stride;
			x2 = vertices[0];
			y2 = vertices[1];
			vertices += stride;
			x3 = vertices[0];
			y3 = vertices[1];
			vertices += stride;
			x4 = vertices[0];
			y4 = vertices[1];
			vertices += stride;
			count += 4;
		}

		if (!(mask & PAINT_WINDOW_TRANSFORMED_MASK))
		{
			bx1 = 65535;
			bx2 = 0.0;
			by1 = 65535;
			by2 = 0.0;
		}

		bx1 = MIN(bx1, x1);
		bx2 = MAX(bx2, x1);
		bx1 = MIN(bx1, x2);
		bx2 = MAX(bx2, x2);
		bx1 = MIN(bx1, x3);
		bx2 = MAX(bx2, x3);
		bx1 = MIN(bx1, x4);
		bx2 = MAX(bx2, x4);

		by1 = MIN(by1, y1);
		by2 = MAX(by2, y1);
		by1 = MIN(by1, y2);
		by2 = MAX(by2, y2);
		by1 = MIN(by1, y3);
		by2 = MAX(by2, y3);
		by1 = MIN(by1, y4);
		by2 = MAX(by2, y4);

		if (bx2 - bx1 > 0 && by2 - by1 > 0
			&& !(mask & PAINT_WINDOW_TRANSFORMED_MASK))
		{
			rect.x = attrib->xTranslate +
					(((bx1 - 7) - w->attrib.x) * attrib->xScale) +
					w->attrib.x;
			rect.y = attrib->yTranslate +
					(((by1 - 1) - w->attrib.y) * attrib->yScale) +
					w->attrib.y;
			rect.width = (bx2 - bx1 + 14) * attrib->xScale;
			rect.height = (by2 - by1 + 2) * attrib->yScale;

			XUnionRectWithRegion(&rect, reblurRegion, reblurRegion);
		}
	}

	if (bx2 - bx1 > 0 && by2 - by1 > 0
		&& (mask & PAINT_WINDOW_TRANSFORMED_MASK))
	{
		rect.x = attrib->xTranslate +
				(((bx1 - 7) - w->attrib.x) * attrib->xScale) + w->attrib.x;
		rect.y = attrib->yTranslate +
				(((by1 - 1) - w->attrib.y) * attrib->yScale) + w->attrib.y;
		rect.width = (bx2 - bx1 + 14) * attrib->xScale;
		rect.height = (by2 - by1 + 2) * attrib->yScale;

		XUnionRectWithRegion(&rect, reblurRegion, reblurRegion);
	}

	XIntersectRegion(reblurRegion, bw->bTexRegion, blurPaintRegion);

	// get regions we need to update
	XIntersectRegion(reblurRegion, bw->paintRegion, reblurRegion);
	XIntersectRegion(reblurRegion, bw->texDamage, reblurRegion);
	XIntersectRegion(reblurRegion, bw->bTexRegion, reblurRegion);

	XIntersectRegion(reblurRegion, &s->outputDev[bs->output].region,
					 reblurRegion);

	// subtract occluded region
	if (bs->od_active)
		XSubtractRegion(reblurRegion, bw->clip, reblurRegion);


	stride = 4 * sizeof(GLfloat);

	if (screenGrabExist(s, "switcher", 0) || bw->isSwitcher)
	{
		bw->texUpdated = FALSE;
		XDestroyRegion(reblurRegion);
		XDestroyRegion(blurPaintRegion);
		return;
	}

	if (reblurRegion->numRects)
	{

		if (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)
		{

			float bm[16] = { s->outputDev[bs->output].width / 2.0, 0, 0,
				0,
				0, s->outputDev[bs->output].height / 2.0,
				0, 0,
				0, 0, 1, 0,
				s->outputDev[bs->output].width / 2.0 +
						s->outputDev[bs->output].region.extents.x1,
				s->outputDev[bs->output].height / 2.0 +
						s->height -
						s->outputDev[bs->output].region.extents.y2, 1, 1
			};
			float bpm[16];
			float tm[16];

			if (!bw->mvm_updated)
			{
				bw->mvm_updated = TRUE;
				glGetFloatv(GL_MODELVIEW_MATRIX, bw->mvm);
			}

			MULTM(bm, bs->pm, bpm);
			MULTM(bpm, bw->mvm, tm);

			XRectangle rect, outputRect;

			XClipBox(reblurRegion, &rect);

			// check region for visibility
			float bbProj[4][2];

			bbProj[0][0] = rect.x * tm[0] + rect.y * tm[4] + tm[12];
			bbProj[0][0] /= rect.x * tm[3] + rect.y * tm[7] + tm[15];
			bbProj[0][1] = rect.x * tm[1] + rect.y * tm[5] + tm[13];
			bbProj[0][1] /= rect.x * tm[3] + rect.y * tm[7] + tm[15];
			bbProj[1][0] =
					(rect.x + rect.width) * tm[0] + rect.y * tm[4] + tm[12];
			bbProj[1][0] /= (rect.x + rect.width) * tm[3] +
					rect.y * tm[7] + tm[15];
			bbProj[1][1] =
					(rect.x + rect.width) * tm[1] + rect.y * tm[5] + tm[13];
			bbProj[1][1] /= (rect.x + rect.width) * tm[3] +
					rect.y * tm[7] + tm[15];
			bbProj[2][0] =
					rect.x * tm[0] + (rect.y + rect.height) * tm[4] + tm[12];
			bbProj[2][0] /= rect.x * tm[3] + (rect.y +
											  rect.height) * tm[7] + tm[15];
			bbProj[2][1] =
					rect.x * tm[1] + (rect.y + rect.height) * tm[5] + tm[13];
			bbProj[2][1] /= rect.x * tm[3] + (rect.y +
											  rect.height) * tm[7] + tm[15];
			bbProj[3][0] =
					(rect.x + rect.width) * tm[0] + (rect.y +
													 rect.height) *
					tm[4] + tm[12];
			bbProj[3][0] /= (rect.x + rect.width) * tm[3] +
					(rect.y + rect.height) * tm[7] + tm[15];
			bbProj[3][1] =
					(rect.x + rect.width) * tm[1] + (rect.y +
													 rect.height) *
					tm[5] + tm[13];
			bbProj[3][1] /= (rect.x + rect.width) * tm[3] +
					(rect.y + rect.height) * tm[7] + tm[15];

			screenGetOutputDevRect(s, bs->output, &outputRect);
			int s_x1 = outputRect.x;
			int s_y1 = outputRect.y;
			int s_x2 = s_x1 + outputRect.width;
			int s_y2 = s_y1 + outputRect.height;

			if (bbProj[0][0] >= bbProj[1][0]
				|| bbProj[2][0] >= bbProj[3][0]
				|| bbProj[2][1] >= bbProj[0][1]
				|| bbProj[3][1] >= bbProj[1][1]
				|| bbProj[1][0] - bbProj[0][0] <
				rect.width * 0.5
				|| bbProj[3][0] - bbProj[2][0] <
				rect.width * 0.5
				|| bbProj[0][1] - bbProj[2][1] <
				rect.height * 0.5
				|| bbProj[1][1] - bbProj[3][1] <
				rect.height * 0.5 || bbProj[0][0] < s_x1
				|| bbProj[1][0] > s_x2 || bbProj[2][0] < s_x1
				|| bbProj[3][0] > s_x2 || bbProj[0][1] < s_y1
				|| bbProj[2][1] > s_y2 || bbProj[1][1] < s_y1
				|| bbProj[3][1] > s_y2)
			{
				XDestroyRegion(reblurRegion);
				XDestroyRegion(blurPaintRegion);

				bw->texUpdated = FALSE;
				return;
			}
			// enable projective texture coordinate generation
			float s_gen[4] = { tm[0], tm[4], tm[8], tm[12] };
			float t_gen[4] = { tm[1], tm[5], tm[9], tm[13] };
			float r_gen[4] = { tm[2], tm[6], tm[10], tm[14] };
			float q_gen[4] = { tm[3], tm[7], tm[11], tm[15] };
			glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
			glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
			glTexGenfv(GL_R, GL_OBJECT_PLANE, r_gen);
			glTexGenfv(GL_Q, GL_OBJECT_PLANE, q_gen);

			glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
			glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
			glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
			glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

			glEnable(GL_TEXTURE_GEN_S);
			glEnable(GL_TEXTURE_GEN_T);
			glEnable(GL_TEXTURE_GEN_R);
			glEnable(GL_TEXTURE_GEN_Q);
		}
		else
		{

			// enable texture coordinate generation
			float s_gen[4] = { 1.0, 0.0, 0.0, 0.0 };
			float t_gen[4] = { 0.0, -1.0, 0.0, s->height };
			glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
			glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
			glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
			glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
			glEnable(GL_TEXTURE_GEN_S);
			glEnable(GL_TEXTURE_GEN_T);
		}

		// draw vertically blured region into temprorary texture
		(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
									GL_COLOR_ATTACHMENT0_EXT,
									GL_TEXTURE_RECTANGLE_ARB,
									bs->blurTempTexV.handle, 0);
		GLERR;

		// bind vertival blur shader
		glEnable(GL_FRAGMENT_PROGRAM_ARB);
		(*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, bs->blurShaderV);

		// bind unblurred background
		enableBlurfxTexture(&bs->backTex, filter);

		mat.xx = 1.0;
		mat.yx = 0.0;
		mat.xy = 0.0;
		mat.yy = -1.0;
		mat.x0 = 0.0;
		mat.y0 = bs->backTex.height;

		genGeometry(&bw->vertArray, mat, 7, reblurRegion);
		glVertexPointer(2, GL_FLOAT, stride, bw->vertArray.vertices + 2);
		glTexCoordPointer(2, GL_FLOAT, stride, bw->vertArray.vertices);

		glDrawArrays(GL_QUADS, 0, bw->vertArray.vCount);

		// draw vertically blured region into blur cache texture
		(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
									GL_COLOR_ATTACHMENT0_EXT,
									GL_TEXTURE_RECTANGLE_ARB,
									bw->blurTex.handle, 0);
		GLERR;

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		glDepthRange(0, 1);
		glViewport(-1, -1, 2, 2);
		glRasterPos2f(0, 0);
		glViewport(0, 0, bw->blurTex.width, bw->blurTex.height);

		glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
		glScalef(1.0f / bw->blurTex.width, -1.0f / bw->blurTex.height, 1.0f);
		glTranslatef(0.0f, -bw->blurTex.height, 0.0f);


		// bind horizontal blur shader
		(*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, bs->blurShaderH);

		// bind temprorary texture
		switchBlurfxTexture(&bs->backTex, &bs->blurTempTexV, filter);

		glTranslated(-bw->blurTex.x, -bw->blurTex.y, 0);

		genGeometry(&bw->vertArray, mat, 0, reblurRegion);
		glVertexPointer(2, GL_FLOAT, stride, bw->vertArray.vertices + 2);
		glTexCoordPointer(2, GL_FLOAT, stride, bw->vertArray.vertices);

		glDrawArrays(GL_QUADS, 0, bw->vertArray.vCount);
		GLERR;

		(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
									GL_COLOR_ATTACHMENT0_EXT,
									GL_TEXTURE_RECTANGLE_ARB,
									bs->backTex.handle, 0);
		GLERR;
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glDepthRange(0, 1);
		glViewport(-1, -1, 2, 2);
		glRasterPos2f(0, 0);
		s->rasterX = s->rasterY = 0;
		glViewport(s->outputDev[bs->output].region.extents.x1,
				   s->height -
				   s->outputDev[bs->output].region.extents.y2,
				   s->outputDev[bs->output].width,
				   s->outputDev[bs->output].height);
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();

		glDisable(GL_FRAGMENT_PROGRAM_ARB);
		disableBlurfxTexture(&bs->blurTempTexV);
		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glDisable(GL_TEXTURE_GEN_R);
		glDisable(GL_TEXTURE_GEN_Q);

		// remove reblurred regions from blur cache texture damage
		XSubtractRegion(bw->texDamage, reblurRegion, bw->texDamage);

	}
	// if the screen is transformed draw blur cache texture into temporary blur texture because there could be some regions we still need a blur for
	if (blurPaintRegion->numRects && (mask & PAINT_WINDOW_TRANSFORMED_MASK))
	{

		(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
									GL_COLOR_ATTACHMENT0_EXT,
									GL_TEXTURE_RECTANGLE_ARB,
									bs->blurTempTexH.handle, 0);
		GLERR;

		mat.xx = 1.0;
		mat.yx = 0.0;
		mat.xy = 0.0;
		mat.yy = -1.0;
		mat.x0 = -bw->blurTex.x;
		mat.y0 = bw->blurTex.height + bw->blurTex.y;

		genGeometry(&bw->vertArray, mat, 0, blurPaintRegion);
		glVertexPointer(2, GL_FLOAT, stride, bw->vertArray.vertices + 2);
		glTexCoordPointer(2, GL_FLOAT, stride, bw->vertArray.vertices);

		enableBlurfxTexture(&bw->blurTex, filter);

		glDrawArrays(GL_QUADS, 0, bw->vertArray.vCount);
		GLERR;

		disableBlurfxTexture(&bw->blurTex);

		(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
									GL_COLOR_ATTACHMENT0_EXT,
									GL_TEXTURE_RECTANGLE_ARB,
									bs->backTex.handle, 0);
		GLERR;
	}
	// cleanup
	XDestroyRegion(reblurRegion);
	XDestroyRegion(blurPaintRegion);
}

static void
updateBlur(CompWindow * w, CompScreen * s,
		   const WindowPaintAttrib * attrib, unsigned int mask,
		   CompTextureFilter filter)
{

	Region reblurRegion;
	int stride, count, stop, vCount, iCount;
	CompMatrix mat;
	GLfloat *vertices, x1, x2, x3, x4, y1, y2, y3, y4, bx1, bx2, by1, by2;
	GLushort *indices;
	XRectangle rect;


	BLURFX_SCREEN(s);
	BLURFX_WINDOW(w);

	// Do we have an decoration vertex array thet does not contain shadows?
	if (mask & PAINT_WINDOW_DECORATION_MASK && bw->decoArray.vCount)
	{
		vertices = bw->decoArray.vertices + (w->texUnits * w->texCoordSize);
		indices = bw->decoArray.indices;
		vCount = bw->decoArray.vCount;
		iCount = bw->decoArray.indexCount;
	}
	else
	{
		vertices = w->vertices + (w->texUnits * w->texCoordSize);
		indices = w->indices;
		vCount = w->vCount;
		iCount = w->indexCount;
	}

	if (vCount <= 0)
		return;

	// get regions we need not to update
	reblurRegion = XCreateRegion();

	stride = (w->texUnits * w->texCoordSize) + 2;

	count = 0;
	stop = (iCount) ? iCount : vCount;

	bx1 = 65535;
	bx2 = 0.0;
	by1 = 65535;
	by2 = 0.0;

	// generate region that needs blur
	while (count < stop)
	{
		if (iCount)
		{
			x1 = vertices[indices[count] * stride];
			y1 = vertices[(indices[count] * stride) + 1];
			count++;
			x2 = vertices[indices[count] * stride];
			y2 = vertices[(indices[count] * stride) + 1];
			count++;
			x3 = vertices[indices[count] * stride];
			y3 = vertices[(indices[count] * stride) + 1];
			count++;
			x4 = vertices[indices[count] * stride];
			y4 = vertices[(indices[count] * stride) + 1];
			count++;

		}
		else
		{
			x1 = vertices[0];
			y1 = vertices[1];
			vertices += stride;
			x2 = vertices[0];
			y2 = vertices[1];
			vertices += stride;
			x3 = vertices[0];
			y3 = vertices[1];
			vertices += stride;
			x4 = vertices[0];
			y4 = vertices[1];
			vertices += stride;
			count += 4;
		}


		if (!(mask & PAINT_WINDOW_TRANSFORMED_MASK))
		{
			bx1 = 65535;
			bx2 = 0.0;
			by1 = 65535;
			by2 = 0.0;
		}

		bx1 = MIN(bx1, x1);
		bx2 = MAX(bx2, x1);
		bx1 = MIN(bx1, x2);
		bx2 = MAX(bx2, x2);
		bx1 = MIN(bx1, x3);
		bx2 = MAX(bx2, x3);
		bx1 = MIN(bx1, x4);
		bx2 = MAX(bx2, x4);

		by1 = MIN(by1, y1);
		by2 = MAX(by2, y1);
		by1 = MIN(by1, y2);
		by2 = MAX(by2, y2);
		by1 = MIN(by1, y3);
		by2 = MAX(by2, y3);
		by1 = MIN(by1, y4);
		by2 = MAX(by2, y4);

		if (bx2 - bx1 > 0 && by2 - by1 > 0
			&& !(mask & PAINT_WINDOW_TRANSFORMED_MASK))
		{
			rect.x = -attrib->xTranslate +
					(((bx1 - 7) - w->attrib.x) * attrib->xScale) +
					w->attrib.x;
			rect.y = -attrib->yTranslate +
					(((by1 - 1) - w->attrib.y) * attrib->yScale) +
					w->attrib.y;
			rect.width = (bx2 - bx1 + 14) * attrib->xScale;
			rect.height = (by2 - by1 + 2) * attrib->yScale;

			XUnionRectWithRegion(&rect, reblurRegion, reblurRegion);
		}
	}

	if (bx2 - bx1 > 0 && by2 - by1 > 0
		&& (mask & PAINT_WINDOW_TRANSFORMED_MASK))
	{
		rect.x = attrib->xTranslate +
				(((bx1 - 7) - w->attrib.x) * attrib->xScale) + w->attrib.x;
		rect.y = attrib->yTranslate +
				(((by1 - 1) - w->attrib.y) * attrib->yScale) + w->attrib.y;
		rect.width = (bx2 - bx1 + 14) * attrib->xScale;
		rect.height = (by2 - by1 + 2) * attrib->yScale;

		XUnionRectWithRegion(&rect, reblurRegion, reblurRegion);
	}

	if (!(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK))
		XIntersectRegion(reblurRegion,
						 &s->outputDev[bs->output].region, reblurRegion);

	XSubtractRegion(reblurRegion, bs->blurredRegion, reblurRegion);

	// subtract occluded region
	if (bs->od_active)
		XSubtractRegion(reblurRegion, bw->clip, reblurRegion);

	// subtract blur cache texture region
	if (bw->texUpdated)
		XSubtractRegion(reblurRegion, bw->bTexRegion, reblurRegion);

	if (!reblurRegion->numRects)
	{
		XDestroyRegion(reblurRegion);
		return;
	}

	XUnionRegion(reblurRegion, bs->blurredRegion, bs->blurredRegion);

	mat.xx = 1.0;
	mat.yx = 0.0;
	mat.xy = 0.0;
	mat.yy = -1.0;
	mat.x0 = 0.0;
	mat.y0 = bs->backTex.height;

	stride = 4 * sizeof(GLfloat);

	// draw vertically blured region into temprorary texture
	(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
								GL_COLOR_ATTACHMENT0_EXT,
								GL_TEXTURE_RECTANGLE_ARB,
								bs->blurTempTexV.handle, 0);
	GLERR;

	glPushMatrix();

	// enable projective texture cooridinate generation
	float bm[16] = { s->outputDev[bs->output].width / 2.0, 0, 0, 0,
		0, s->outputDev[bs->output].height / 2.0, 0, 0,
		0, 0, 1, 0,
		s->outputDev[bs->output].width / 2.0 +
				s->outputDev[bs->output].region.extents.x1,
		s->outputDev[bs->output].height / 2.0 + s->height -
				s->outputDev[bs->output].region.extents.y2, 1, 1
	};
	float bpm[16];
	float tm[16];

	bw->mvm_updated = FALSE;
	glGetFloatv(GL_MODELVIEW_MATRIX, bw->mvm);

	MULTM(bm, bs->pm, bpm);
	MULTM(bpm, bw->mvm, tm);

	float s_gen[4] = { tm[0], tm[4], tm[8], tm[12] };
	float t_gen[4] = { tm[1], tm[5], tm[9], tm[13] };
	float r_gen[4] = { tm[2], tm[6], tm[10], tm[14] };
	float q_gen[4] = { tm[3], tm[7], tm[11], tm[15] };
	glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
	glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
	glTexGenfv(GL_R, GL_OBJECT_PLANE, r_gen);
	glTexGenfv(GL_Q, GL_OBJECT_PLANE, q_gen);

	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	glEnable(GL_TEXTURE_GEN_R);
	glEnable(GL_TEXTURE_GEN_Q);

	// bind vertival blur shader
	glEnable(GL_FRAGMENT_PROGRAM_ARB);
	GLERR;
	(*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, bs->blurShaderV);
	GLERR;


	// bind unblurred background
	enableBlurfxTexture(&bs->backTex, filter);

	genGeometry(&bs->vertArray, mat, 0, reblurRegion);
	glVertexPointer(2, GL_FLOAT, stride, bs->vertArray.vertices + 2);
	GLERR;
	glTexCoordPointer(2, GL_FLOAT, stride, bs->vertArray.vertices);
	GLERR;

	glDrawArrays(GL_QUADS, 0, bs->vertArray.vCount);
	GLERR;

	// draw vertically blured region into temprorary texture
	(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
								GL_COLOR_ATTACHMENT0_EXT,
								GL_TEXTURE_RECTANGLE_ARB,
								bs->blurTempTexH.handle, 0);
	GLERR;

	// bind horizontal blur shader
	(*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, bs->blurShaderH);
	GLERR;

	// bind temprorary texture
	switchBlurfxTexture(&bs->backTex, &bs->blurTempTexV, filter);

	glDrawArrays(GL_QUADS, 0, bs->vertArray.vCount);
	GLERR;

	glPopMatrix();
	GLERR;
	(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
								GL_COLOR_ATTACHMENT0_EXT,
								GL_TEXTURE_RECTANGLE_ARB,
								bs->backTex.handle, 0);
	GLERR;

	(*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, 0);
	GLERR;
	glDisable(GL_FRAGMENT_PROGRAM_ARB);

	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	glDisable(GL_TEXTURE_GEN_R);
	glDisable(GL_TEXTURE_GEN_Q);

	disableBlurfxTexture(&bs->blurTempTexV);

	XDestroyRegion(reblurRegion);
}

static void
updateBlurTextureSize(CompWindow * w, CompScreen * s,
					  const WindowPaintAttrib * attrib, unsigned int mask)
{

	XRectangle box;
	int texCheck = 0;
	float div_w, div_h;

	BLURFX_SCREEN(s);
	BLURFX_WINDOW(w);

	// if the window does not have a blur cache texture create it
	if (!bw->hasTexture && !WINDOW_INPUT_INVISIBLE(w))
	{
		bw->hasTexture = TRUE;

		genBlurTexture(bs, &bw->blurTex,
					   ceil((WIN_W(w) + 80) / bs->downSample),
					   ceil((WIN_H(w) + 80) / bs->downSample), 0);

		bw->blurTex.x =
				floor((WIN_X(w) - 40) / bs->downSample) * bs->downSample;
		bw->blurTex.y =
				floor((WIN_Y(w) - 40) / bs->downSample) * bs->downSample;
		bw->blurTex.downSample = bs->downSample;
		box.x = bw->blurTex.x;
		box.y = bw->blurTex.y;
		box.width = bw->blurTex.width;
		box.height = bw->blurTex.height;

		bw->bc_x = s->x;
		bw->bc_y = s->y;

		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bw->texDamage);
		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bw->bTexRegion);
		XUnionRectWithRegion(&box, bw->texDamage, bw->texDamage);
		XUnionRectWithRegion(&box, bw->bTexRegion, bw->bTexRegion);
	}
	// if the window has moved out of his blur cache texture then update the blur cache texture
	if (bw->blurTex.x > WIN_X(w)
		|| WIN_X(w) + WIN_W(w) > bw->blurTex.x + bw->blurTex.width)
		texCheck = 1;
	if (bw->blurTex.y > WIN_Y(w)
		|| WIN_Y(w) + WIN_H(w) > bw->blurTex.y + bw->blurTex.height)
		texCheck = 1;

	// do not update if the window is still moving
	if ((mask & PAINT_WINDOW_TRANSFORMED_MASK) && texCheck
		&& !(w->attrib.x == bw->lastX && w->attrib.y == bw->lastY))
		texCheck = 0;

	// do we need to resize the blur cache texture
	if (bw->blurTex.width < WIN_W(w) || bw->blurTex.height < WIN_H(w))
		texCheck = 2;
	if (bw->blurTex.width - WIN_W(w) > 160
		|| bw->blurTex.height - WIN_H(w) > 160)
		texCheck = 2;

	if ((bw->bc_x != s->x || bw->bc_y != s->y)
		&& !(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK))
	{
		texCheck = 1;
	}
	// do not update blur cache texture size if window is transformed
	if ((mask & PAINT_WINDOW_TRANSFORMED_MASK) && texCheck == 2)
		texCheck = 0;

	if (bw->blurTex.downSample != bs->downSample)
		texCheck = 2;

	if (texCheck && (!WINDOW_INPUT_INVISIBLE(w) || w->shaded))
	{
		if (texCheck == 2)
		{
			// update blur cache texture size
			enableBlurfxTexture(&bw->blurTex, COMP_TEXTURE_FILTER_FAST);
			glTexImage2D(bw->blurTex.target, 0, 3,
						 ceil((WIN_W(w) +
							   80) / bs->downSample),
						 ceil((WIN_H(w) +
							   80) / bs->downSample), 0,
						 GL_RGB, GL_UNSIGNED_BYTE, NULL);
			disableBlurfxTexture(&bw->blurTex);
			bw->blurTex.width = WIN_W(w) + 80;
			bw->blurTex.height = WIN_H(w) + 80;
		}
		// update blur cache texture position
		div_w = (bw->blurTex.width - WIN_W(w)) / 2;
		div_h = (bw->blurTex.height - WIN_H(w)) / 2;

		bw->blurTex.x =
				floor((WIN_X(w) - div_w) / bs->downSample) * bs->downSample;
		bw->blurTex.y =
				floor((WIN_Y(w) - div_h) / bs->downSample) * bs->downSample;
		bw->blurTex.downSample = bs->downSample;
		box.x = bw->blurTex.x;
		box.y = bw->blurTex.y;
		box.width = bw->blurTex.width;
		box.height = bw->blurTex.height;

		bw->bc_x = s->x;
		bw->bc_y = s->y;

		// update to new regions and add damage
		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bw->texDamage);
		XUnionRegion(getEmptyRegion(), getEmptyRegion(), bw->bTexRegion);
		XUnionRectWithRegion(&box, bw->texDamage, bw->texDamage);
		XUnionRectWithRegion(&box, bw->bTexRegion, bw->bTexRegion);
	}
}

static void
updateBlurTextureNoFBO(CompWindow * w, CompScreen * s,
					   const WindowPaintAttrib * attrib, unsigned int mask)
{

	Region reblurRegion;
	Region blurPaintRegion;
	int stride, count, stop, vCount, iCount;
	GLfloat *vertices, x1, x2, x3, x4, y1, y2, y3, y4, bx1, bx2, by1, by2;
	GLushort *indices;
	XRectangle rect;

	BLURFX_SCREEN(s);
	BLURFX_WINDOW(w);

	if (bw->bc_x != s->x || bw->bc_y != s->y)
	{
		bw->texUpdated = FALSE;
		return;
	}

	if (screenGrabExist(s, "switcher", 0)
		|| mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK
		|| mask & PAINT_WINDOW_TRANSFORMED_MASK || bw->isSwitcher)
	{
		bw->texUpdated = FALSE;
		return;
	}
	// Do we have an decoration vertex array thet does not contain shadows?
	if (mask & PAINT_WINDOW_DECORATION_MASK && bw->decoArray.vCount)
	{
		vertices = bw->decoArray.vertices + (w->texUnits * w->texCoordSize);
		indices = bw->decoArray.indices;
		vCount = bw->decoArray.vCount;
		iCount = bw->decoArray.indexCount;
	}
	else
	{
		vertices = w->vertices + (w->texUnits * w->texCoordSize);
		indices = w->indices;
		vCount = w->vCount;
		iCount = w->indexCount;
	}

	if (vCount <= 0)
		return;
	stride = (w->texUnits * w->texCoordSize) + 2;

	// get regions we need not to update
	reblurRegion = XCreateRegion();
	blurPaintRegion = XCreateRegion();

	count = 0;
	stop = (iCount) ? iCount : vCount;

	bx1 = 65535;
	bx2 = 0.0;
	by1 = 65535;
	by2 = 0.0;

	// generate region that needs blur
	while (count < stop)
	{
		if (iCount)
		{
			x1 = vertices[indices[count] * stride];
			y1 = vertices[(indices[count] * stride) + 1];
			count++;
			x2 = vertices[indices[count] * stride];
			y2 = vertices[(indices[count] * stride) + 1];
			count++;
			x3 = vertices[indices[count] * stride];
			y3 = vertices[(indices[count] * stride) + 1];
			count++;
			x4 = vertices[indices[count] * stride];
			y4 = vertices[(indices[count] * stride) + 1];
			count++;

		}
		else
		{
			x1 = vertices[0];
			y1 = vertices[1];
			vertices += stride;
			x2 = vertices[0];
			y2 = vertices[1];
			vertices += stride;
			x3 = vertices[0];
			y3 = vertices[1];
			vertices += stride;
			x4 = vertices[0];
			y4 = vertices[1];
			vertices += stride;
			count += 4;
		}


		bx1 = 65535;
		bx2 = 0.0;
		by1 = 65535;
		by2 = 0.0;

		bx1 = MIN(bx1, x1);
		bx2 = MAX(bx2, x1);
		bx1 = MIN(bx1, x2);
		bx2 = MAX(bx2, x2);
		bx1 = MIN(bx1, x3);
		bx2 = MAX(bx2, x3);
		bx1 = MIN(bx1, x4);
		bx2 = MAX(bx2, x4);

		by1 = MIN(by1, y1);
		by2 = MAX(by2, y1);
		by1 = MIN(by1, y2);
		by2 = MAX(by2, y2);
		by1 = MIN(by1, y3);
		by2 = MAX(by2, y3);
		by1 = MIN(by1, y4);
		by2 = MAX(by2, y4);

		if (bx2 - bx1 > 0 && by2 - by1 > 0)
		{
			rect.x = attrib->xTranslate +
					(((bx1 - 7) - w->attrib.x) * attrib->xScale) +
					w->attrib.x;
			rect.y = attrib->yTranslate +
					(((by1 - 1) - w->attrib.y) * attrib->yScale) +
					w->attrib.y;
			rect.width = (bx2 - bx1 + 14) * attrib->xScale;
			rect.height = (by2 - by1 + 2) * attrib->yScale;

			XUnionRectWithRegion(&rect, reblurRegion, reblurRegion);
		}
	}

	XIntersectRegion(reblurRegion, bw->bTexRegion, blurPaintRegion);

	// get regions we need to update
	XIntersectRegion(reblurRegion, bw->paintRegion, reblurRegion);
	XIntersectRegion(reblurRegion, bw->texDamage, reblurRegion);
	XIntersectRegion(reblurRegion, bw->bTexRegion, reblurRegion);

	XIntersectRegion(reblurRegion, &s->outputDev[bs->output].region,
					 reblurRegion);

	// subtract occluded region
	if (bs->od_active)
		XSubtractRegion(reblurRegion, bw->clip, reblurRegion);

	if (reblurRegion->numRects)
	{

		// enable projective texture cooridinate generation
		float bm[16] = { s->outputDev[bs->output].width / 2.0, 0, 0, 0,
			0, s->outputDev[bs->output].height / -2.0, 0, 0,
			0, 0, 1, 0,
			s->outputDev[bs->output].width / 2.0 +
					s->outputDev[bs->output].region.extents.x1,
			s->outputDev[bs->output].height / 2.0 +
					s->outputDev[bs->output].region.extents.y1, 1,
			1
		};
		float bpm[16];
		float tm[16];

		if (!bw->mvm_updated)
		{
			bw->mvm_updated = TRUE;
			glGetFloatv(GL_MODELVIEW_MATRIX, bw->mvm);
		}

		MULTM(bm, bs->pm, bpm);
		MULTM(bpm, bw->mvm, tm);

		int i;
		XRectangle sRect, bRect;

		for (i = 0; i < reblurRegion->numRects; i++)
		{

			sRect.x = floor(reblurRegion->rects[i].x1 / bs->downSample);
			sRect.y = floor(reblurRegion->rects[i].y1 / bs->downSample);
			sRect.width =
					MAX(1.0,
						ceil(reblurRegion->rects[i].x2 /
							 bs->downSample) - sRect.x);
			sRect.height =
					MAX(1.0,
						ceil(reblurRegion->rects[i].y2 /
							 bs->downSample) - sRect.y);

			bRect.x = sRect.x * bs->downSample;
			bRect.y = sRect.y * bs->downSample;
			bRect.width = sRect.width * bs->downSample;
			bRect.height = sRect.height * bs->downSample;

			// copy background into temporary texture
			enableBlurfxTexture(&bs->blurTempTexV, COMP_TEXTURE_FILTER_GOOD);
			glCopyTexSubImage2D(bs->blurTempTexV.target, 0, 0,
								0, bRect.x,
								s->height - bRect.y -
								bRect.height, bRect.width, bRect.height);

			// draw downsampled texture

			glBegin(GL_QUADS);
			glTexCoord2f(0, 0);
			glVertex2f(bRect.x, bRect.y);
			glTexCoord2f(0, bRect.height);
			glVertex2f(bRect.x, bRect.y + sRect.height);
			glTexCoord2f(bRect.width, bRect.height);
			glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
			glTexCoord2f(bRect.width, 0);
			glVertex2f(bRect.x + sRect.width, bRect.y);
			glEnd();

			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


			float shift = bs->downSample / 2.0;

			glColor4f(1.0, 1.0, 1.0, 0.5);


			glBegin(GL_QUADS);
			glTexCoord2f(0 + shift, 0);
			glVertex2f(bRect.x, bRect.y);
			glTexCoord2f(0 + shift, bRect.height);
			glVertex2f(bRect.x, bRect.y + sRect.height);
			glTexCoord2f(bRect.width + shift, bRect.height);
			glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
			glTexCoord2f(bRect.width + shift, 0);
			glVertex2f(bRect.x + sRect.width, bRect.y);
			glEnd();

			glColor4f(1.0, 1.0, 1.0, 0.333);


			glBegin(GL_QUADS);
			glTexCoord2f(0 - shift, 0);
			glVertex2f(bRect.x, bRect.y);
			glTexCoord2f(0 - shift, bRect.height);
			glVertex2f(bRect.x, bRect.y + sRect.height);
			glTexCoord2f(bRect.width - shift, bRect.height);
			glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
			glTexCoord2f(bRect.width - shift, 0);
			glVertex2f(bRect.x + sRect.width, bRect.y);
			glEnd();

			glColor4f(1.0, 1.0, 1.0, 0.25);


			glBegin(GL_QUADS);
			glTexCoord2f(0, 0 - shift);
			glVertex2f(bRect.x, bRect.y);
			glTexCoord2f(0, bRect.height - shift);
			glVertex2f(bRect.x, bRect.y + sRect.height);
			glTexCoord2f(bRect.width, bRect.height - shift);
			glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
			glTexCoord2f(bRect.width, 0 - shift);
			glVertex2f(bRect.x + sRect.width, bRect.y);
			glEnd();

			glColor4f(1.0, 1.0, 1.0, 0.2);


			glBegin(GL_QUADS);
			glTexCoord2f(0, 0 + shift);
			glVertex2f(bRect.x, bRect.y);
			glTexCoord2f(0, bRect.height + shift);
			glVertex2f(bRect.x, bRect.y + sRect.height);
			glTexCoord2f(bRect.width, bRect.height + shift);
			glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
			glTexCoord2f(bRect.width, 0 + shift);
			glVertex2f(bRect.x + sRect.width, bRect.y);
			glEnd();


			glDisable(GL_BLEND);
			// copy downsampled background into temporary texture
			switchBlurfxTexture(&bs->blurTempTexV,
								&bw->blurTex, COMP_TEXTURE_FILTER_GOOD);
			glCopyTexSubImage2D(bw->blurTex.target, 0,
								sRect.x -
								(bw->blurTex.x /
								 bs->downSample),
								sRect.y -
								(bw->blurTex.y /
								 bs->downSample), bRect.x,
								s->height - bRect.y -
								sRect.height, sRect.width, sRect.height);

			// copy normal background back to screen
			switchBlurfxTexture(&bw->blurTex,
								&bs->blurTempTexV, COMP_TEXTURE_FILTER_GOOD);

			glBegin(GL_QUADS);
			glTexCoord2f(0, bRect.height);
			glVertex2f(bRect.x, bRect.y);
			glTexCoord2f(0, 0);
			glVertex2f(bRect.x, bRect.y + bRect.height);
			glTexCoord2f(bRect.width, 0);
			glVertex2f(bRect.x + bRect.width, bRect.y + bRect.height);
			glTexCoord2f(bRect.width, bRect.height);
			glVertex2f(bRect.x + bRect.width, bRect.y);
			glEnd();

			disableBlurfxTexture(&bs->blurTempTexV);


		}

		// remove reblurred regions from blur cache texture damage
		XSubtractRegion(bw->texDamage, reblurRegion, bw->texDamage);

	}
	// cleanup
	XDestroyRegion(reblurRegion);
	XDestroyRegion(blurPaintRegion);
}

static void
updateBlurNoFBO(CompWindow * w, CompScreen * s,
				const WindowPaintAttrib * attrib, unsigned int mask)
{

	Region reblurRegion;
	int stride, count, stop, vCount, iCount;
	GLfloat *vertices, x1, x2, x3, x4, y1, y2, y3, y4, bx1, bx2, by1, by2;
	GLushort *indices;
	XRectangle rect;

	BLURFX_SCREEN(s);
	BLURFX_WINDOW(w);

	// Do we have an decoration vertex array thet does not contain shadows?
	if (mask & PAINT_WINDOW_DECORATION_MASK && bw->decoArray.vCount)
	{
		vertices = bw->decoArray.vertices + (w->texUnits * w->texCoordSize);
		indices = bw->decoArray.indices;
		vCount = bw->decoArray.vCount;
		iCount = bw->decoArray.indexCount;
	}
	else
	{
		vertices = w->vertices + (w->texUnits * w->texCoordSize);
		indices = w->indices;
		vCount = w->vCount;
		iCount = w->indexCount;
	}

	if (vCount <= 0)
		return;

	// get regions we need not to update
	reblurRegion = XCreateRegion();

	stride = (w->texUnits * w->texCoordSize) + 2;

	count = 0;
	stop = (iCount) ? iCount : vCount;

	bx1 = 65535;
	bx2 = 0.0;
	by1 = 65535;
	by2 = 0.0;

	// generate region that needs blur
	while (count < stop)
	{
		if (iCount)
		{
			x1 = vertices[indices[count] * stride];
			y1 = vertices[(indices[count] * stride) + 1];
			count++;
			x2 = vertices[indices[count] * stride];
			y2 = vertices[(indices[count] * stride) + 1];
			count++;
			x3 = vertices[indices[count] * stride];
			y3 = vertices[(indices[count] * stride) + 1];
			count++;
			x4 = vertices[indices[count] * stride];
			y4 = vertices[(indices[count] * stride) + 1];
			count++;

		}
		else
		{
			x1 = vertices[0];
			y1 = vertices[1];
			vertices += stride;
			x2 = vertices[0];
			y2 = vertices[1];
			vertices += stride;
			x3 = vertices[0];
			y3 = vertices[1];
			vertices += stride;
			x4 = vertices[0];
			y4 = vertices[1];
			vertices += stride;
			count += 4;
		}


		if (!(mask & PAINT_WINDOW_TRANSFORMED_MASK))
		{
			bx1 = 65535;
			bx2 = 0.0;
			by1 = 65535;
			by2 = 0.0;
		}

		bx1 = MIN(bx1, x1);
		bx2 = MAX(bx2, x1);
		bx1 = MIN(bx1, x2);
		bx2 = MAX(bx2, x2);
		bx1 = MIN(bx1, x3);
		bx2 = MAX(bx2, x3);
		bx1 = MIN(bx1, x4);
		bx2 = MAX(bx2, x4);

		by1 = MIN(by1, y1);
		by2 = MAX(by2, y1);
		by1 = MIN(by1, y2);
		by2 = MAX(by2, y2);
		by1 = MIN(by1, y3);
		by2 = MAX(by2, y3);
		by1 = MIN(by1, y4);
		by2 = MAX(by2, y4);

		if (bx2 - bx1 > 0 && by2 - by1 > 0
			&& !(mask & PAINT_WINDOW_TRANSFORMED_MASK))
		{
			rect.x = attrib->xTranslate +
					(((bx1 - 7) - w->attrib.x) * attrib->xScale) +
					w->attrib.x;
			rect.y = attrib->yTranslate +
					(((by1 - 1) - w->attrib.y) * attrib->yScale) +
					w->attrib.y;
			rect.width = (bx2 - bx1 + 14) * attrib->xScale;
			rect.height = (by2 - by1 + 2) * attrib->yScale;

			XUnionRectWithRegion(&rect, reblurRegion, reblurRegion);
		}
	}

	if (bx2 - bx1 > 0 && by2 - by1 > 0
		&& (mask & PAINT_WINDOW_TRANSFORMED_MASK))
	{
		rect.x = attrib->xTranslate +
				(((bx1 - 7) - w->attrib.x) * attrib->xScale) + w->attrib.x;
		rect.y = attrib->yTranslate +
				(((by1 - 1) - w->attrib.y) * attrib->yScale) + w->attrib.y;
		rect.width = (bx2 - bx1 + 14) * attrib->xScale;
		rect.height = (by2 - by1 + 2) * attrib->yScale;

		XUnionRectWithRegion(&rect, reblurRegion, reblurRegion);
	}

	XIntersectRegion(reblurRegion, &s->region, reblurRegion);

	XSubtractRegion(reblurRegion, bs->blurredRegion, reblurRegion);

	// subtract occluded region
	if (bs->od_active)
		XSubtractRegion(reblurRegion, bw->clip, reblurRegion);

	// subtract blur cache texture region
	if (bw->texUpdated)
		XSubtractRegion(reblurRegion, bw->bTexRegion, reblurRegion);

	if (!reblurRegion->numRects)
	{
		XDestroyRegion(reblurRegion);
		return;
	}

	XUnionRegion(reblurRegion, bs->blurredRegion, bs->blurredRegion);

	glPushMatrix();

	// enable projective texture cooridinate generation
	float bm[16] = { s->outputDev[bs->output].width / 2.0, 0, 0, 0,
		0, s->outputDev[bs->output].height / -2.0, 0, 0,
		0, 0, 1, 0,
		s->outputDev[bs->output].width / 2.0 +
				s->outputDev[bs->output].region.extents.x1,
		s->outputDev[bs->output].height / 2.0 +
				s->outputDev[bs->output].region.extents.y1, 1, 1
	};
	float bpm[16];
	float tm[16];

	bw->mvm_updated = FALSE;
	glGetFloatv(GL_MODELVIEW_MATRIX, bw->mvm);

	MULTM(bm, bs->pm, bpm);
	MULTM(bpm, bw->mvm, tm);

	Bool enable_light = FALSE;

	if (glIsEnabled(GL_LIGHTING))
	{
		glDisable(GL_LIGHTING);
		enable_light = TRUE;
	}

	Bool enable_clipPlanes = FALSE;

	if (glIsEnabled(GL_CLIP_PLANE0))
	{
		glDisable (GL_CLIP_PLANE0);
		glDisable (GL_CLIP_PLANE1);
		glDisable (GL_CLIP_PLANE2);
		glDisable (GL_CLIP_PLANE3);
		enable_clipPlanes = TRUE;
	}

	int i;
	XRectangle sRect, bRect;

	for (i = 0; i < reblurRegion->numRects; i++)
	{

		rect.x = reblurRegion->rects[i].x1;
		rect.y = reblurRegion->rects[i].y1;
		rect.width = reblurRegion->rects[i].x2 - rect.x;
		rect.height = reblurRegion->rects[i].y2 - rect.y;

		// check region for visibility and project it to screen
		float bbProj[4][2];
		float bBox[4];

		bbProj[0][0] = rect.x * tm[0] + rect.y * tm[4] + tm[12];
		bbProj[0][0] /= rect.x * tm[3] + rect.y * tm[7] + tm[15];
		bbProj[0][1] = rect.x * tm[1] + rect.y * tm[5] + tm[13];
		bbProj[0][1] /= rect.x * tm[3] + rect.y * tm[7] + tm[15];
		bbProj[1][0] =
				(rect.x + rect.width) * tm[0] + rect.y * tm[4] + tm[12];
		bbProj[1][0] /= (rect.x + rect.width) * tm[3] +
				rect.y * tm[7] + tm[15];
		bbProj[1][1] =
				(rect.x + rect.width) * tm[1] + rect.y * tm[5] + tm[13];
		bbProj[1][1] /= (rect.x + rect.width) * tm[3] +
				rect.y * tm[7] + tm[15];
		bbProj[2][0] =
				rect.x * tm[0] + (rect.y + rect.height) * tm[4] + tm[12];
		bbProj[2][0] /= rect.x * tm[3] + (rect.y +
										  rect.height) * tm[7] + tm[15];
		bbProj[2][1] =
				rect.x * tm[1] + (rect.y + rect.height) * tm[5] + tm[13];
		bbProj[2][1] /= rect.x * tm[3] + (rect.y +
										  rect.height) * tm[7] + tm[15];
		bbProj[3][0] =
				(rect.x + rect.width) * tm[0] + (rect.y +
												 rect.height) * tm[4] +
				tm[12];
		bbProj[3][0] /= (rect.x + rect.width) * tm[3] + (rect.y +
														 rect.height) *
				tm[7] + tm[15];
		bbProj[3][1] =
				(rect.x + rect.width) * tm[1] + (rect.y +
												 rect.height) * tm[5] +
				tm[13];
		bbProj[3][1] /= (rect.x + rect.width) * tm[3] + (rect.y +
														 rect.height) *
				tm[7] + tm[15];

		// calculate a bounding box of the projected rectangle
		bBox[0] =
				MIN(s->outputDev[bs->output].region.extents.x2, bbProj[0][0]);
		bBox[0] = MIN(bBox[0], bbProj[1][0]);
		bBox[0] = MIN(bBox[0], bbProj[2][0]);
		bBox[0] = MIN(bBox[0], bbProj[3][0]);
		bBox[0] = MAX(bBox[0], s->outputDev[bs->output].region.extents.x1);

		bBox[1] =
				MIN(s->outputDev[bs->output].region.extents.y2, bbProj[0][1]);
		bBox[1] = MIN(bBox[1], bbProj[1][1]);
		bBox[1] = MIN(bBox[1], bbProj[2][1]);
		bBox[1] = MIN(bBox[1], bbProj[3][1]);
		bBox[1] = MAX(bBox[1], s->outputDev[bs->output].region.extents.y1);

		bBox[2] =
				MAX(s->outputDev[bs->output].region.extents.x1, bbProj[0][0]);
		bBox[2] = MAX(bBox[2], bbProj[1][0]);
		bBox[2] = MAX(bBox[2], bbProj[2][0]);
		bBox[2] = MAX(bBox[2], bbProj[3][0]);
		bBox[2] = MIN(bBox[2], s->outputDev[bs->output].region.extents.x2);

		bBox[3] =
				MAX(s->outputDev[bs->output].region.extents.y1, bbProj[0][1]);
		bBox[3] = MAX(bBox[3], bbProj[1][1]);
		bBox[3] = MAX(bBox[3], bbProj[2][1]);
		bBox[3] = MAX(bBox[3], bbProj[3][1]);
		bBox[3] = MIN(bBox[3], s->outputDev[bs->output].region.extents.y2);

		if (bBox[2] - bBox[0] <= 0 || bBox[3] - bBox[1] <= 0)
			continue;

		sRect.x = floor(bBox[0] / bs->downSample);
		sRect.y = floor(bBox[1] / bs->downSample);
		sRect.width = MAX(1.0, ceil(bBox[2] / bs->downSample) - sRect.x);
		sRect.height = MAX(1.0, ceil(bBox[3] / bs->downSample) - sRect.y);


		bRect.x = sRect.x * bs->downSample;
		bRect.y = sRect.y * bs->downSample;
		bRect.width = sRect.width * bs->downSample;
		bRect.height = sRect.height * bs->downSample;

		glLoadIdentity();
		prepareXCoords(s, bs->output, -DEFAULT_Z_CAMERA);

		// copy background into temporary texture
		enableBlurfxTexture(&bs->blurTempTexV, COMP_TEXTURE_FILTER_GOOD);
		glCopyTexSubImage2D(bs->blurTempTexV.target, 0, 0, 0,
							bRect.x,
							s->height - bRect.y - bRect.height,
							bRect.width, bRect.height);

		// draw downsampled texture

		glBegin(GL_QUADS);
		glTexCoord2f(0, 0);
		glVertex2f(bRect.x, bRect.y);
		glTexCoord2f(0, bRect.height);
		glVertex2f(bRect.x, bRect.y + sRect.height);
		glTexCoord2f(bRect.width, bRect.height);
		glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
		glTexCoord2f(bRect.width, 0);
		glVertex2f(bRect.x + sRect.width, bRect.y);
		glEnd();

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


		float shift = bs->downSample / 2.0;


		glColor4f(1.0, 1.0, 1.0, 0.5);


		glBegin(GL_QUADS);
		glTexCoord2f(0 + shift, 0);
		glVertex2f(bRect.x, bRect.y);
		glTexCoord2f(0 + shift, bRect.height);
		glVertex2f(bRect.x, bRect.y + sRect.height);
		glTexCoord2f(bRect.width + shift, bRect.height);
		glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
		glTexCoord2f(bRect.width + shift, 0);
		glVertex2f(bRect.x + sRect.width, bRect.y);
		glEnd();

		glColor4f(1.0, 1.0, 1.0, 0.333);


		glBegin(GL_QUADS);
		glTexCoord2f(0 - shift, 0);
		glVertex2f(bRect.x, bRect.y);
		glTexCoord2f(0 - shift, bRect.height);
		glVertex2f(bRect.x, bRect.y + sRect.height);
		glTexCoord2f(bRect.width - shift, bRect.height);
		glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
		glTexCoord2f(bRect.width - shift, 0);
		glVertex2f(bRect.x + sRect.width, bRect.y);
		glEnd();

		glColor4f(1.0, 1.0, 1.0, 0.25);


		glBegin(GL_QUADS);
		glTexCoord2f(0, 0 - shift);
		glVertex2f(bRect.x, bRect.y);
		glTexCoord2f(0, bRect.height - shift);
		glVertex2f(bRect.x, bRect.y + sRect.height);
		glTexCoord2f(bRect.width, bRect.height - shift);
		glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
		glTexCoord2f(bRect.width, 0 - shift);
		glVertex2f(bRect.x + sRect.width, bRect.y);
		glEnd();

		glColor4f(1.0, 1.0, 1.0, 0.2);


		glBegin(GL_QUADS);
		glTexCoord2f(0, 0 + shift);
		glVertex2f(bRect.x, bRect.y);
		glTexCoord2f(0, bRect.height + shift);
		glVertex2f(bRect.x, bRect.y + sRect.height);
		glTexCoord2f(bRect.width, bRect.height + shift);
		glVertex2f(bRect.x + sRect.width, bRect.y + sRect.height);
		glTexCoord2f(bRect.width, 0 + shift);
		glVertex2f(bRect.x + sRect.width, bRect.y);
		glEnd();


		glDisable(GL_BLEND);
		// copy downsampled background into temporary texture
		switchBlurfxTexture(&bs->blurTempTexV, &bs->blurTempTexH,
							COMP_TEXTURE_FILTER_GOOD);
		glCopyTexSubImage2D(bs->blurTempTexV.target, 0, sRect.x,
							sRect.y +
							((s->height -
							  s->outputDev[bs->output].region.
							  extents.y2) / bs->downSample),
							bRect.x,
							s->height - bRect.y - sRect.height,
							sRect.width, sRect.height);

		// copy normal background back to screen
		switchBlurfxTexture(&bs->blurTempTexH, &bs->blurTempTexV,
							COMP_TEXTURE_FILTER_GOOD);

		glBegin(GL_QUADS);
		glTexCoord2f(0, bRect.height);
		glVertex2f(bRect.x, bRect.y);
		glTexCoord2f(0, 0);
		glVertex2f(bRect.x, bRect.y + bRect.height);
		glTexCoord2f(bRect.width, 0);
		glVertex2f(bRect.x + bRect.width, bRect.y + bRect.height);
		glTexCoord2f(bRect.width, bRect.height);
		glVertex2f(bRect.x + bRect.width, bRect.y);
		glEnd();

		disableBlurfxTexture(&bs->blurTempTexV);

	}

	glPopMatrix();

	if (enable_light)
		glEnable(GL_LIGHTING);

	if (enable_clipPlanes)
	{
		glEnable (GL_CLIP_PLANE0);
		glEnable (GL_CLIP_PLANE1);
		glEnable (GL_CLIP_PLANE2);
		glEnable (GL_CLIP_PLANE3);
	}

	XDestroyRegion(reblurRegion);
}

static void
drawBlur(CompWindow * w, CompScreen * s, CompTexture * texture,
		 const WindowPaintAttrib * attrib, int mask, CompTextureFilter filter)
{

	int i, count, vCount, iCount, stride;
	GLfloat *vertices;
	GLushort *indices;
	GLfloat constant[4];

	unsigned int actTex = 0;

	BLURFX_SCREEN(s);
	BLURFX_WINDOW(w);

	glPushMatrix();

	if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
	{
		glTranslatef(w->attrib.x, w->attrib.y, 0.0f);
		glScalef(attrib->xScale, attrib->yScale, 0.0f);
		glTranslatef(attrib->xTranslate / attrib->xScale -
					 w->attrib.x,
					 attrib->yTranslate / attrib->yScale - w->attrib.y, 0.0f);
	}
	// Do we have an decoration vertex array thet does not contain shadows?
	if (mask & PAINT_WINDOW_DECORATION_MASK && bw->decoArray.vCount)
	{
		vertices = bw->decoArray.vertices + (w->texUnits * w->texCoordSize);
		indices = bw->decoArray.indices;
		vCount = bw->decoArray.vCount;
		iCount = bw->decoArray.indexCount;
	}
	else
	{
		vertices = w->vertices + (w->texUnits * w->texCoordSize);
		indices = w->indices;
		vCount = w->vCount;
		iCount = w->indexCount;
	}

	if (vCount <= 0)
		return;

	i = 0;
	count = 0;
	stride = (w->texUnits * w->texCoordSize + 2) * sizeof(GLfloat);

	// draw blur only for visible regions
	if (!(mask & PAINT_WINDOW_DECORATION_MASK) && !w->alpha
		&& !(mask & PAINT_WINDOW_TRANSFORMED_MASK) && !w->shaded
		&& bs->od_active)
	{
		Region drawBlurRegion = XCreateRegion();

		XIntersectRegion(w->region, bw->paintRegion, drawBlurRegion);
		if (bw->texUpdated)
			XIntersectRegion(drawBlurRegion, bw->bTexRegion, drawBlurRegion);
		XSubtractRegion(drawBlurRegion, bw->clip, drawBlurRegion);

		CompMatrix mat;

		mat.xx = 0.0;
		mat.yx = 0.0;
		mat.xy = 0.0;
		mat.yy = 0.0;
		mat.x0 = 0.0;
		mat.y0 = 0.0;

		genGeometry(&bs->vertArray, mat, 0, drawBlurRegion);
		stride = 4 * sizeof(GLfloat);
		vCount = bs->vertArray.vCount;
		iCount = 0;
		vertices = bs->vertArray.vertices + 2;
		XDestroyRegion(drawBlurRegion);
	}

	glVertexPointer(2, GL_FLOAT, stride, vertices);
	GLERR;


	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	(*s->activeTexture) (GL_TEXTURE0_ARB);

	float tm[16];

	if ((mask & PAINT_WINDOW_TRANSFORMED_MASK) || !bw->texUpdated)
	{
		// enable projective texture cooridinate generation
		float bm[16] =
				{ s->outputDev[bs->output].width / (2.0 * bs->downSample), 0,
			0, 0, 0,
			s->outputDev[bs->output].height / (2.0 * bs->downSample),
			0, 0, 0, 0, 1, 0,
			s->outputDev[bs->output].width / (2.0 *
											  bs->downSample) +
					(s->outputDev[bs->output].region.extents.x1 /
					 bs->downSample),
			s->outputDev[bs->output].height / (2.0 * bs->downSample) +
					((s->height -
					  s->outputDev[bs->output].region.extents.y2) /
					 bs->downSample), 1,
			1
		};
		if (bs->downSample != 1.0)
			bm[5] *= -1;
		float bpm[16];

		//if (!bw->mvm_updated)
		{
			bw->mvm_updated = TRUE;
			glGetFloatv(GL_MODELVIEW_MATRIX, bw->mvm);
		}

		MULTM(bm, bs->pm, bpm);
		MULTM(bpm, bw->mvm, tm);

		float s_gen[4] = { tm[0], tm[4], tm[8], tm[12] };
		float t_gen[4] = { tm[1], tm[5], tm[9], tm[13] };
		float r_gen[4] = { tm[2], tm[6], tm[10], tm[14] };
		float q_gen[4] = { tm[3], tm[7], tm[11], tm[15] };
		glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
		glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
		glTexGenfv(GL_R, GL_OBJECT_PLANE, r_gen);
		glTexGenfv(GL_Q, GL_OBJECT_PLANE, q_gen);

		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		glEnable(GL_TEXTURE_GEN_R);
		glEnable(GL_TEXTURE_GEN_Q);
		enableBlurfxTexture(&bs->blurTempTexH, filter);

	}
	else
	{
		// enable texture cooridinate generation
		float s_gen[4] = { 1.0 / bs->downSample, 0.0, 0.0,
			-bw->blurTex.x / bs->downSample
		};

		if (bs->downSample != 1.0)
		{
			float t_gen[4] = { 0.0, 1.0 / bs->downSample, 0.0,
				-bw->blurTex.y / bs->downSample
			};
			glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
		}
		else
		{
			float t_gen[4] = { 0.0, -1.0, 0.0,
				bw->blurTex.height + bw->blurTex.y
			};
			glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
		}
		glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		enableBlurfxTexture(&bw->blurTex, filter);
	}

	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	// simple switcher detection hack
	Bool switcher = FALSE;

	if (w->resName == 0 && w->resClass == 0
		&& (mask & PAINT_WINDOW_DECORATION_MASK)
		&& screenGrabExist(s, "switcher", 0))
		switcher = TRUE;

	// we need alpha dependend blur if window has alpha channel
	if (((mask & PAINT_WINDOW_DECORATION_MASK)
		 && !bs->opt[BLURFX_SCREEN_OPTION_DISABLE_DECORATION_ALPHADEP].
		 value.b) || switcher
		|| (!(mask & PAINT_WINDOW_DECORATION_MASK) && w->alpha))
	{
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

		actTex++;
		(*s->activeTexture) (GL_TEXTURE0_ARB + actTex);
		glDisable(GL_TEXTURE_GEN_T);
		glDisable(GL_TEXTURE_GEN_S);
		enableTexture(s, texture, filter);
		GLERR;

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);


		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
		glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 4.0);

		constant[3] = attrib->opacity / 65535.0f;

		glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);

		(*s->clientActiveTexture) (GL_TEXTURE1_ARB);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2, GL_FLOAT, stride,
						  vertices - (w->texUnits * w->texCoordSize));
		GLERR;
	}
	else
	{
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		constant[3] = MIN(1.0, (attrib->opacity / 65535.0f) * 4.0);

		glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);

	}

	if (s->canDoSlightlySaturated
		&& bs->opt[BLURFX_SCREEN_OPTION_BLUR_SATURATION].value.f < 100.0)
	{

		actTex++;
		(*s->activeTexture) (GL_TEXTURE0_ARB + actTex);
		enableBlurfxTexture(&bs->blurTempTexH, filter);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PRIMARY_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

		glColor4f(1.0f, 1.0f, 1.0f, 0.5f);

		actTex++;
		(*s->activeTexture) (GL_TEXTURE0_ARB + actTex);
		enableBlurfxTexture(&bs->blurTempTexH, filter);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

		constant[0] = 0.5f + 0.5f * RED_SATURATION_WEIGHT;
		constant[1] = 0.5f + 0.5f * GREEN_SATURATION_WEIGHT;
		constant[2] = 0.5f + 0.5f * BLUE_SATURATION_WEIGHT;
		constant[3] = 1.0;

		glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);

		actTex++;
		(*s->activeTexture) (GL_TEXTURE0_ARB + actTex);
		enableBlurfxTexture(&bs->blurTempTexH, filter);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

		constant[3] =
				bs->opt[BLURFX_SCREEN_OPTION_BLUR_SATURATION].value.f / 100.0;

		glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);
	}

	// draw blur
	if (iCount)
	{
		glDrawElements(GL_QUADS, iCount, GL_UNSIGNED_SHORT, indices);
		GLERR;
	}
	else
	{
		glDrawArrays(GL_QUADS, 0, vCount);
		GLERR;
	}

	// set all back to normal

	if (s->canDoSlightlySaturated
		&& bs->opt[BLURFX_SCREEN_OPTION_BLUR_SATURATION].value.f < 100.0)
	{

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

		disableBlurfxTexture(&bs->blurTempTexH);
		actTex--;
		(*s->activeTexture) (GL_TEXTURE0_ARB + actTex);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glDisable(GL_TEXTURE_GEN_R);
		glDisable(GL_TEXTURE_GEN_Q);

		disableBlurfxTexture(&bs->blurTempTexH);
		actTex--;
		(*s->activeTexture) (GL_TEXTURE0_ARB + actTex);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

		disableBlurfxTexture(&bs->blurTempTexH);
		actTex--;
		(*s->activeTexture) (GL_TEXTURE0_ARB + actTex);

	}

	if (((mask & PAINT_WINDOW_DECORATION_MASK)
		 && !bs->opt[BLURFX_SCREEN_OPTION_DISABLE_DECORATION_ALPHADEP].
		 value.b) || switcher
		|| (!(mask & PAINT_WINDOW_DECORATION_MASK) && w->alpha))
	{
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		disableTexture(s, texture);
		GLERR;

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);


		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);

		glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1.0);

		(*s->clientActiveTexture) (GL_TEXTURE0_ARB);
		actTex--;
		(*s->activeTexture) (GL_TEXTURE0_ARB + actTex);

	}

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
	glDisable(GL_TEXTURE_RECTANGLE_ARB);
	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	glDisable(GL_TEXTURE_GEN_R);
	glDisable(GL_TEXTURE_GEN_Q);
	glDisable(GL_BLEND);

	glPopMatrix();
	GLERR;

}

static void
drawReflection(CompWindow * w, CompScreen * s, CompTexture * texture,
			   const WindowPaintAttrib * attrib, int mask,
			   CompTextureFilter filter)
{

	int vCount, iCount;
	GLfloat *vertices;
	GLushort *indices;

	float dx, dy;

	BLURFX_SCREEN(w->screen);
	BLURFX_WINDOW(w);

	glPushMatrix();

	if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
	{
		glTranslatef(w->attrib.x, w->attrib.y, 0.0f);
		glScalef(attrib->xScale, attrib->yScale, 0.0f);
		glTranslatef(attrib->xTranslate / attrib->xScale -
					 w->attrib.x,
					 attrib->yTranslate / attrib->yScale - w->attrib.y, 0.0f);

	}
	// setup reflection texture scaling
	if (bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_SCALE].value.b)
	{
		if (bs->opt[BLURFX_SCREEN_OPTION_REFLECTION_PROPORTIONAL].value.b)
		{

			dx = 1.0 /
					(((s->width / bs->modTex.width) *
					  bs->modTex.height >
					  s->height) ? (s->height /
									bs->modTex.height) *
					 bs->modTex.width : s->width);
			dy = 1.0 /
					(((s->width / bs->modTex.width) *
					  bs->modTex.height >
					  s->height) ? s->height : (s->width /
												bs->modTex.width) *
					 bs->modTex.height);
		}
		else
		{
			dx = 1.0 / s->width;
			dy = 1.0 / s->height;
		}
	}
	else
	{
		dx = 1.0 / bs->modTex.width;
		dy = 1.0 / bs->modTex.height;
	}


	// Do we have an decoration vertex array thet does not contain shadows?
	if (mask & PAINT_WINDOW_DECORATION_MASK && bw->decoArray.vCount)
	{
		vertices = bw->decoArray.vertices + (w->texUnits * w->texCoordSize);
		indices = bw->decoArray.indices;
		vCount = bw->decoArray.vCount;
		iCount = bw->decoArray.indexCount;
	}
	else
	{
		vertices = w->vertices + (w->texUnits * w->texCoordSize);
		indices = w->indices;
		vCount = w->vCount;
		iCount = w->indexCount;
	}

	if (vCount <= 0)
		return;

	int stride = (w->texUnits * w->texCoordSize + 2) * sizeof(GLfloat);

	// draw reflection only for visible regions
	if (!(mask & PAINT_WINDOW_DECORATION_MASK) && !w->alpha
		&& !(mask & PAINT_WINDOW_TRANSFORMED_MASK) && !w->shaded)
	{
		Region drawBlurRegion = XCreateRegion();

		XIntersectRegion(w->region, bw->paintRegion, drawBlurRegion);
		XSubtractRegion(drawBlurRegion, bw->clip, drawBlurRegion);

		CompMatrix mat;

		mat.xx = 0.0;
		mat.yx = 0.0;
		mat.xy = 0.0;
		mat.yy = 0.0;
		mat.x0 = 0.0;
		mat.y0 = 0.0;

		genGeometry(&bs->vertArray, mat, 0, drawBlurRegion);
		stride = 4 * sizeof(GLfloat);
		vCount = bs->vertArray.vCount;
		iCount = 0;
		vertices = bs->vertArray.vertices + 2;
		XDestroyRegion(drawBlurRegion);
	}

	glVertexPointer(2, GL_FLOAT, stride, vertices);


	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


	(*s->activeTexture) (GL_TEXTURE0_ARB);
	enableBlurfxTexture(&bs->modTex, filter);

	if (mask &
		(PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK |
		 PAINT_WINDOW_TRANSFORMED_MASK))
	{
		// enable projective texture cooridinate generation
		float bm[16] = { (s->width * dx) / 2.0, 0, 0, 0, 0,
			(s->height * dy) / -2.0, 0,
			0, 0, 0, 1, 0, (s->width * dx) / 2.0,
			(s->height * dy) / 2.0, 1, 1
		};
		float bpm[16];
		float tm[16];
		float mvm[16];

		glPushMatrix();
		glLoadIdentity();
		glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
		glScalef(1.0f / s->width, -1.0f / s->height, 1.0f);
		glTranslatef(0.0f, -s->height, 0.0f);

		if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
		{
			glTranslatef(w->attrib.x, w->attrib.y, 0.0f);
			glScalef(attrib->xScale, attrib->yScale, 0.0f);
			glTranslatef(attrib->xTranslate / attrib->xScale -
						 w->attrib.x,
						 attrib->yTranslate / attrib->yScale -
						 w->attrib.y, 0.0f);
		}
		glGetFloatv(GL_MODELVIEW_MATRIX, mvm);
		glPopMatrix();

		MULTM(bm, bs->pm, bpm);
		MULTM(bpm, mvm, tm);

		float s_gen[4] = { tm[0], tm[4], tm[8], tm[12] };
		float t_gen[4] = { tm[1], tm[5], tm[9], tm[13] };
		float r_gen[4] = { tm[2], tm[6], tm[10], tm[14] };
		float q_gen[4] = { tm[3], tm[7], tm[11], tm[15] };
		glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
		glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
		glTexGenfv(GL_R, GL_OBJECT_PLANE, r_gen);
		glTexGenfv(GL_Q, GL_OBJECT_PLANE, q_gen);

		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		glEnable(GL_TEXTURE_GEN_R);
		glEnable(GL_TEXTURE_GEN_Q);
	}
	else
	{
		// enable texture cooridinate generation
		float s_gen[4] = { dx, 0.0, 0.0, 0.0 };
		float t_gen[4] = { 0.0, dy, 0.0, 0.0 };
		glTexGenfv(GL_T, GL_OBJECT_PLANE, t_gen);
		glTexGenfv(GL_S, GL_OBJECT_PLANE, s_gen);
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
	}

	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	glColor4f(1.0, 1.0, 1.0, attrib->opacity / 65535.0f);

	// simple switcher detection hack
	Bool switcher = FALSE;

	if (w->resName == 0 && w->resClass == 0
		&& (mask & PAINT_WINDOW_DECORATION_MASK)
		&& screenGrabExist(s, "switcher", 0))
		switcher = TRUE;

	// we need alpha dependend reflection if window has alpha channel
	if (((mask & PAINT_WINDOW_DECORATION_MASK)
		 && !bs->opt[BLURFX_SCREEN_OPTION_DISABLE_DECORATION_ALPHADEP].
		 value.b) || switcher
		|| (!(mask & PAINT_WINDOW_DECORATION_MASK) && w->alpha))
	{
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);

		(*s->activeTexture) (GL_TEXTURE1_ARB);
		enableTexture(s, texture, filter);
		GLERR;

		glDisable(GL_TEXTURE_GEN_T);
		glDisable(GL_TEXTURE_GEN_S);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);

		(*s->clientActiveTexture) (GL_TEXTURE1_ARB);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2, GL_FLOAT, stride,
						  vertices - (w->texUnits * w->texCoordSize));
	}
	else
	{
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
	}

	// draw reflection
	if (iCount)
	{
		glDrawElements(GL_QUADS, iCount, GL_UNSIGNED_SHORT, indices);
		GLERR;
	}
	else
	{
		glDrawArrays(GL_QUADS, 0, vCount);
		GLERR;
	}

	// set all back to normal
	if (((mask & PAINT_WINDOW_DECORATION_MASK)
		 && !bs->opt[BLURFX_SCREEN_OPTION_DISABLE_DECORATION_ALPHADEP].
		 value.b) || switcher
		|| (!(mask & PAINT_WINDOW_DECORATION_MASK) && w->alpha))
	{
		disableTexture(s, texture);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);

		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		(*s->clientActiveTexture) (GL_TEXTURE0_ARB);
		(*s->activeTexture) (GL_TEXTURE0_ARB);
	}

	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
	glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
	glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

	glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
	glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
	glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);

	disableBlurfxTexture(&bs->modTex);

	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	glDisable(GL_TEXTURE_GEN_R);
	glDisable(GL_TEXTURE_GEN_Q);

	glPopMatrix();

	glDisable(GL_BLEND);

}

// activate/deactivate motion blur
static Bool
blurfxToggleMotionBlur(CompDisplay * d, CompAction * ac,
					   CompActionState state, CompOption * option,
					   int nOption)
{
	CompScreen *s;

	s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
	if (s)
	{
		BLURFX_SCREEN(s);
		bs->mb_activated = !bs->mb_activated;
		bs->mb_activated &= bs->mblur_supported;
	}
	return FALSE;
}


// bind our fbo and bind the texture to it
static Bool bindFbo(CompScreen * s, BlurTexture tex)
{

	BLURFX_SCREEN(s);

	GLERR;

	(*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, bs->fbo);
	(*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
								GL_COLOR_ATTACHMENT0_EXT,
								GL_TEXTURE_RECTANGLE_ARB, tex.handle, 0);

	if (!bs->fboStatus)
	{
		bs->fboStatus = (*s->checkFramebufferStatus) (GL_FRAMEBUFFER_EXT);
		if (bs->fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT)
		{
			(*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0);
			s->bindRenderbuffer(GL_RENDERBUFFER_EXT, 0);
			printf("Framebuffer attach failed\n");
			bs->fboStatus = 0;
			bs->fboActive = FALSE;

			glMatrixMode(GL_PROJECTION);
			glPushMatrix();
			glLoadIdentity();
			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			glLoadIdentity();
			glDepthRange(0, 1);
			glViewport(-1, -1, 2, 2);
			glRasterPos2f(0, 0);
			s->rasterX = s->rasterY = 0;
			glViewport(s->outputDev[bs->output].region.extents.x1,
					   s->height - s->outputDev[bs->output].region.extents.y2,
					   s->outputDev[bs->output].width,
					   s->outputDev[bs->output].height);

			glMatrixMode(GL_PROJECTION);
			glPopMatrix();
			glMatrixMode(GL_MODELVIEW);
			glPopMatrix();

			glDrawBuffer(GL_BACK);
			glReadBuffer(GL_BACK);

			return FALSE;
		}
	}
	//Set the draw  buffer
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glDepthRange(0, 1);
	glViewport(-1, -1, 2, 2);
	glRasterPos2f(0, 0);
	glViewport(s->outputDev[bs->output].region.extents.x1,
			   s->height - s->outputDev[bs->output].region.extents.y2,
			   s->outputDev[bs->output].width,
			   s->outputDev[bs->output].height);

	prepareXCoords(s, bs->output, -DEFAULT_Z_CAMERA);

	bs->fboActive = TRUE;

	GLERR;

	return TRUE;
}

// unbind the fbo and set everything back to normal
static void unbindFbo(CompScreen * s)
{

	BLURFX_SCREEN(s);

	if (!bs->fboActive)
		return;

	GLERR;
	// unbind fbo
	(*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0);

	// set alles back to normal
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glDepthRange(0, 1);
	glViewport(-1, -1, 2, 2);
	glRasterPos2f(0, 0);
	s->rasterX = s->rasterY = 0;
	glViewport(s->outputDev[bs->output].region.extents.x1,
			   s->height - s->outputDev[bs->output].region.extents.y2,
			   s->outputDev[bs->output].width,
			   s->outputDev[bs->output].height);

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	glDrawBuffer(GL_BACK);
	glReadBuffer(GL_BACK);

	bs->fboActive = FALSE;

	GLERR;
}

// generate a vertex and coordinate aray for the given region
static void
genGeometry(BlurfxVertArray * vA, CompMatrix matrix, int xextend,
			Region region)
{

	BoxPtr pBox;
	int nBox;
	int vSize;
	int n, x1, y1, x2, y2;
	GLfloat *d;

	pBox = region->rects;
	nBox = region->numRects;

	vSize = 4;

	n = 0;

	if (nBox * vSize * 4 > vA->vertexSize)
	{
		if (!moreVertices(vA, nBox * vSize * 4))
			return;
	}

	d = vA->vertices;

	while (nBox--)
	{
		x1 = pBox->x1 - xextend;
		y1 = pBox->y1;
		x2 = pBox->x2 + xextend;
		y2 = pBox->y2;

		pBox++;

		if (x1 < x2 && y1 < y2)
		{
			BFX_ADD_RECT(d, matrix, x1, y1, x2, y2);
			n++;
		}
	}
	vA->vCount = n * 4;

}

// helper function for genGeometry
static Bool moreVertices(BlurfxVertArray * vA, int newSize)
{
	if (newSize > vA->vertexSize)
	{
		GLfloat *vertices;

		vertices = realloc(vA->vertices, sizeof(GLfloat) * newSize);
		if (!vertices)
			return FALSE;

		vA->vertices = vertices;
		vA->vertexSize = newSize;
	}
	return TRUE;
}

// does opengl level iniliazation of the screen
static void
initBlurfxScreen(BlurfxScreen * bs, int Screenwidth, int Screenheight,
				 CompScreen * s)
{
	bs->blurShaderV = 0;
	bs->blurShaderH = 0;

	bs->backTex.handle = 0;
	bs->blurTempTexV.handle = 0;
	bs->blurTempTexH.handle = 0;
	bs->motionTex.handle = 0;

	bs->fbo = 0;

	if (bs->fboBlur_supported
		&& !bs->opt[BLURFX_SCREEN_OPTION_FORCE_NON_FBO_BLUR].value.b)
	{
		// load shaders
		loadShader(GL_FRAGMENT_PROGRAM_ARB, s, &bs->blurShaderV,
				   blurShader[bs->blur_shader][0]);
		loadShader(GL_FRAGMENT_PROGRAM_ARB, s, &bs->blurShaderH,
				   blurShader[bs->blur_shader][1]);

		bs->downSample = 1.0;
	}

	if (bs->blur_supported)
	{
		genBlurTexture(bs, &bs->blurTempTexV, Screenwidth, Screenheight, 0);
		genBlurTexture(bs, &bs->blurTempTexH, Screenwidth, Screenheight, 0);
	}
	//Generate a fbo
	if (s->fbo)
	{
		(*s->genFramebuffers) (1, &bs->fbo);
		GLERR;
		bs->fboStatus = 0;

		// generate textures
		genBlurTexture(bs, &bs->backTex, Screenwidth, Screenheight, 1);
	}

	if (bs->mblur_supported && bs->mb_mode != 1)
	{
		// generate morion blur texture
		genBlurTexture(bs, &bs->motionTex, Screenwidth,
					   Screenheight, bs->mb_mode);
	}

	bs->modTex.handle = 0;

	if (bs->reflection_supported)
	{
		// load reflection map
		loadPngToTexture2D(s, &bs->modTex,
						   bs->
						   opt[BLURFX_SCREEN_OPTION_REFLECTION_FILE].value.s);
	}

	bs->hasInit = 1;
}

// generate texture
static void
genBlurTexture(BlurfxScreen * bs, BlurTexture * tex, int width, int height,
			   int type)
{
	GLERR;

	//Enable texturing
	glEnable(GL_TEXTURE_RECTANGLE_ARB);
	//Generate the OpenGL textures
	if (!tex->handle)
		glGenTextures(1, &tex->handle);
	//Write important info into the structure
	tex->width = width;
	tex->height = height;
	//Bind the texture
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex->handle);
	//Load the parameters
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
					GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
					GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);

	tex->target = GL_TEXTURE_RECTANGLE_ARB;
	tex->active_filter = GL_LINEAR;

	//Generate a large image
	switch (type)
	{
	case 1:
		glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, width,
					 height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
		break;
	case 2:
		glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB16F_ARB,
					 width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
		break;
	case 0:
	default:
		glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, width,
					 height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
		break;
	}
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
	glDisable(GL_TEXTURE_RECTANGLE_ARB);

	GLERR;
}

// load shader
static void
loadShader(GLenum type, CompScreen * s, GLuint * shader, const char *prog)
{

	//Switch a bunch of strings
	char buffer[4096];

	sprintf(buffer, prog);
	int errorPos, errorNum;

	glGetError();

	//Load the fragment program
	if (*shader == 0)
		(*s->genPrograms) (1, shader);
	(*s->bindProgram) (type, *shader);

	(*s->programString) (type, GL_PROGRAM_FORMAT_ASCII_ARB,
						 strlen(buffer), buffer);


	glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
	errorNum = glGetError();
	if (errorNum != GL_NO_ERROR || errorPos != -1)
	{
		fprintf(stderr,
				"%s: (0x%X) error loading fragment program at line: %d\n",
				getProgramName(), errorNum, errorPos);
		fprintf(stderr, "%s\n", glGetString(GL_PROGRAM_ERROR_STRING_ARB));
		glGetIntegerv(GL_MAX_PROGRAM_INSTRUCTIONS_ARB, &errorPos);
		if (*shader)
			(*s->deletePrograms) (1, shader);
		*shader = 0;
	}
	(*s->bindProgram) (type, 0);
}

// load png
static void
loadPngToTexture2D(CompScreen * s, BlurTexture * tex, char *filename)
{

	int img_w = 0, img_h = 0, stride = 0;
	char *img_data;
	char *img_mod;
	struct stat fInfo;

	int i = 0;
	float Y, R, G, B;
	int pix;

	BLURFX_SCREEN(s);

	if (strlen(filename) == 0)
		return;

	GLERR;

	if (stat(filename, &fInfo) ||
		!S_ISREG(fInfo.st_mode) ||
		S_ISDIR(fInfo.st_mode) || access(filename, F_OK) != 0)
	{
		fprintf(stderr, "Unable to read image \"%s\" \n", filename);
		return;
	}
	fprintf(stderr, "Loading image \"%s\" \n", filename);

	// Was: readPng(filename, &img_data, &img_w, &img_h);
	if (!(*s->display->fileToImage)
		(s->display, NULL, filename, &img_w, &img_h, &stride,
		 (void *)&img_data))
	{
		printf("Unable to load image \"%s\" \n", filename);
		return;
	}

	printf("%s loaded. w: %i; h: %i\n", filename, img_w, img_h);

	//Generate the OpenGL textures
	if (tex->handle == 0)
		glGenTextures(1, &tex->handle);

	glEnable(GL_TEXTURE_2D);
	//Bind the texture
	glBindTexture(GL_TEXTURE_2D, tex->handle);

	//Load the parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

	tex->target = GL_TEXTURE_2D;
	tex->active_filter = GL_LINEAR;
	tex->width = img_w;
	tex->height = img_h;

	// merge all colors to an alpha texture if not disabled
	img_mod = malloc(img_w * img_h * 4);
	for (i = 0; i < img_w * img_h; i++)
	{
		R = img_data[(i * 4) + 2];
		G = img_data[(i * 4) + 1];
		B = img_data[i * 4];
		Y = (R * 0.299) + (G * 0.587) + (B * 0.114);
		pix = Y;
		img_mod[i * 4] =
				(bs->
				 opt[BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_COLOR].
				 value.b) ? img_data[(i * 4) + 2] : (char)pix;
		img_mod[(i * 4) + 1] =
				(bs->
				 opt[BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_COLOR].
				 value.b) ? img_data[(i * 4) + 1] : (char)pix;
		img_mod[(i * 4) + 2] =
				(bs->
				 opt[BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_COLOR].
				 value.b) ? img_data[i * 4] : (char)pix;
		img_mod[(i * 4) + 3] =
				(bs->
				 opt[BLURFX_SCREEN_OPTION_REFLECTION_USE_IMAGE_ALPHA].
				 value.b) ? img_data[(i * 4) + 3] : (char)pix;
	}

	glTexImage2D(GL_TEXTURE_2D, 0, 4, img_w, img_h, 0, GL_RGBA,
				 GL_UNSIGNED_BYTE, img_mod);
	GLERR;

	glBindTexture(GL_TEXTURE_2D, 0);
	glDisable(GL_TEXTURE_2D);
	free(img_mod);
	free(img_data);

}

// enable texture and set filter
static void enableBlurfxTexture(BlurTexture * tex, CompTextureFilter filter)
{
	glEnable(tex->target);
	glBindTexture(tex->target, tex->handle);
	GLERR;
	switch (filter)
	{
	case COMP_TEXTURE_FILTER_FAST:
		if (tex->active_filter != GL_NEAREST)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			tex->active_filter = GL_NEAREST;
		}
		break;
	case COMP_TEXTURE_FILTER_GOOD:
	default:
		if (tex->active_filter != GL_LINEAR)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			tex->active_filter = GL_LINEAR;
		}
		break;
	}
}

// disable texture
static void disableBlurfxTexture(BlurTexture * tex)
{
	glBindTexture(tex->target, 0);
	GLERR;
	glDisable(tex->target);
}

// switch to another texture and set filter
static void
switchBlurfxTexture(BlurTexture * from, BlurTexture * to,
					CompTextureFilter filter)
{
	if (from->target != to->target)
	{
		disableBlurfxTexture(from);
		enableBlurfxTexture(to, filter);
	}
	glBindTexture(to->target, to->handle);
	GLERR;
	switch (filter)
	{
	case COMP_TEXTURE_FILTER_FAST:
		if (to->active_filter != GL_NEAREST)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			to->active_filter = GL_NEAREST;
		}
		break;
	case COMP_TEXTURE_FILTER_GOOD:
	default:
		if (to->active_filter != GL_LINEAR)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			to->active_filter = GL_LINEAR;
		}
		break;
	}
}


syntax highlighted by Code2HTML, v. 0.9.1