/*
 * Copyright (c) 2006 Darryll Truchan <moppsy@comcast.net>
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

/*
 * I am sure that this is not the right way to do this.
 * Any advice would be appreciated. I'm still a junior GL'er
 *
 * BUGS
 *  water doesn't draw on things negative
 *  neg also doesn't honor an apps initial, non-paintAttrib, opacity
 *
 * comments still need work.
 *
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>

#include <beryl.h>

/* defaults */

static char *excludeWinType[] = {
	N_("Desktop")
};

#define N_EXCLUDE_WIN_TYPE (sizeof (excludeWinType) / sizeof (excludeWinType[0]))

#define NEG_TOGGLE_KEY_DEFAULT              "n"
#define NEG_TOGGLE_MODIFIERS_DEFAULT        CompSuperMask

#define NEG_TOGGLE_ALL_KEY_DEFAULT          "m"
#define NEG_TOGGLE_ALL_MODIFIERS_DEFAULT    CompSuperMask

#define NEG_DISPLAY_OPTION_WINDOW_TOGGLE     0
#define NEG_DISPLAY_OPTION_SCREEN_TOGGLE     1
#define NEG_DISPLAY_OPTION_NUM               2

#define NEG_SCREEN_OPTION_EXCLUDE_NULL_WM_CLASS 0
#define NEG_SCREEN_OPTION_EXCLUDE_WINDOW_TYPE   1
#define NEG_SCREEN_OPTION_EXCLUDE_LIST          2
#define NEG_SCREEN_OPTION_NUM                   3

#define NEG_EXCLUDE_NULL_WM_CLASS_DEFAULT       FALSE

static int displayPrivateIndex;

typedef struct _NEGDisplay
{
	int screenPrivateIndex;
	CompOption opt[NEG_DISPLAY_OPTION_NUM];	/* array of display options     */
} NEGDisplay;


typedef struct _NEGSCreen
{
	int windowPrivateIndex;
	CompOption opt[NEG_SCREEN_OPTION_NUM];	/* array of screen options */
	DrawWindowTextureProc drawWindowTexture;	/* function pointer     */
	DamageWindowRectProc damageWindowRect;	/* function pointer     */
	Bool isNeg;					/* negative screen flag */
	unsigned int excludeWMask;
} NEGScreen;

typedef struct _NEGWindow
{
	Bool isNeg;					/* negative window flag */
} NEGWindow;

#define GET_NEG_DISPLAY(d) ((NEGDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define NEG_DISPLAY(d) NEGDisplay *nd = GET_NEG_DISPLAY (d)
#define GET_NEG_SCREEN(s, nd) ((NEGScreen *) (s)->privates[(nd)->screenPrivateIndex].ptr)
#define NEG_SCREEN(s) NEGScreen *ns = GET_NEG_SCREEN (s, GET_NEG_DISPLAY (s->display))
#define GET_NEG_WINDOW(w, ns) ((NEGWindow *) (w)->privates[(ns)->windowPrivateIndex].ptr)
#define NEG_WINDOW(w) NEGWindow *nw = GET_NEG_WINDOW  (w, GET_NEG_SCREEN  (w->screen, GET_NEG_DISPLAY (w->screen->display)))

#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))

static void NEGToggle(CompWindow * w)
{
	CompOption *o;
	int i;

	NEG_SCREEN(w->screen);
	NEG_WINDOW(w);

	/* toggle window negative flag */
	nw->isNeg = !nw->isNeg;

	/* check exclude list */
	if (ns->opt[NEG_SCREEN_OPTION_EXCLUDE_NULL_WM_CLASS].value.b &&
		(w->resClass == NULL))
		nw->isNeg = FALSE;

	if (ns->excludeWMask & w->type)
		nw->isNeg = FALSE;

	o = &ns->opt[NEG_SCREEN_OPTION_EXCLUDE_LIST];
	for (i = 0; i < o->value.list.nValue; i++)
	{
		if (w->resClass
			&& (strcmp(o->value.list.value[i].s, w->resClass) == 0))
			nw->isNeg = FALSE;
	}

	/* cause repainting */
	addWindowDamage(w);
}

static void NEGToggleScreen(CompScreen * s)
{
	CompWindow *w;

	NEG_SCREEN(s);

	/* toggle screen negative flag */
	ns->isNeg = !ns->isNeg;

	/* toggle every window */
	for (w = s->windows; w; w = w->next)
		if (w)
			NEGToggle(w);
}

static Bool
negToggle(CompDisplay * d, CompAction * action, CompActionState state,
		  CompOption * option, int nOption)
{
	CompWindow *w;
	Window xid;

	xid = getIntOptionNamed(option, nOption, "window", 0);

	w = findWindowAtDisplay(d, xid);

	if (w)
		NEGToggle(w);

	return TRUE;
}

static Bool
negToggleAll(CompDisplay * d, CompAction * action, CompActionState state,
			 CompOption * option, int nOption)
{
	CompScreen *s;
	Window xid;

	xid = getIntOptionNamed(option, nOption, "root", 0);

	s = findScreenAtDisplay(d, xid);

	if (s)
		NEGToggleScreen(s);

	return TRUE;
}

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

	NEG_SCREEN(w->screen);
	NEG_WINDOW(w);

	/* PAINT_WINDOW_DECORATION_MASK is set when the decorations are being painted
	 * I didn't want to paint them negative too
	 */
	if (nw->isNeg && !(mask & PAINT_WINDOW_DECORATION_MASK))
	{

		/* this is for the most part taken from paint.c */

		/* push the current matrix */
		glPushMatrix();

		/* we need this to handle windows that are not regular, like during scale */
		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);

			filter = w->screen->filter[WINDOW_TRANS_FILTER];
		}
		else if (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)
		{
			filter = w->screen->filter[SCREEN_TRANS_FILTER];
		}
		else
		{
			filter = w->screen->filter[NOTHING_TRANS_FILTER];
		}

		/* if we can addjust saturation, even if it's just on and off */
		if (w->screen->canDoSaturated && attrib->saturation != COLOR)
		{
			GLfloat constant[4];

			/* if the paint mask has this set we want to blend */
			if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
				glEnable(GL_BLEND);

			/* enable the texture */
			enableTexture(w->screen, texture, filter);

			/* texture combiner */
			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_TEXTURE);
			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_ONE_MINUS_SRC_COLOR);	/* negate */
			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_TEXTURE);
			glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

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

			/* make another texture active */
			w->screen->activeTexture(GL_TEXTURE1_ARB);

			/* enable that texture */
			enableTexture(w->screen, texture, 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);

			/* if we can do saturation that is in between min and max */
			if (w->screen->canDoSlightlySaturated && attrib->saturation > 0)
			{
				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);

				/* mack another texture active */
				w->screen->activeTexture(GL_TEXTURE2_ARB);

				/* enable that texture */
				enableTexture(w->screen, texture, 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_ONE_MINUS_SRC_COLOR);	/* negate */
				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);

				/* color constant */
				constant[3] = attrib->saturation / 65535.0f;

				glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);

				/* if we are not opaque or not fully bright */
				if (attrib->opacity < OPAQUE || attrib->brightness != BRIGHT)
				{
					/* activate a new texture */
					w->screen->activeTexture(GL_TEXTURE3_ARB);

					/* enable that texture */
					enableTexture(w->screen, texture, filter);

					/* color constant */
					constant[3] = attrib->opacity / 65535.0f;
					constant[0] = constant[1] =
							constant[2] =
							constant[3] * attrib->brightness / 65535.0f;

					glTexEnvfv(GL_TEXTURE_ENV,
							   GL_TEXTURE_ENV_COLOR, constant);

					glTexEnvf(GL_TEXTURE_ENV,
							  GL_TEXTURE_ENV_MODE, GL_COMBINE);

					glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
					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_MODULATE);
					glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
					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);

					/* draw the window geometry */
					(*w->screen->drawWindowGeometry) (w);

					/* disable the current texture */
					disableTexture(w->screen, texture);

					/* set texture mode back to replace */
					glTexEnvi(GL_TEXTURE_ENV,
							  GL_TEXTURE_ENV_MODE, GL_REPLACE);

					/* re-activate last texture */
					w->screen->activeTexture(GL_TEXTURE2_ARB);
				}
				else
				{
					/* fully opaque and bright */

					/* draw the window geometry */
					(*w->screen->drawWindowGeometry) (w);
				}

				/* disable the current texture */
				disableTexture(w->screen, texture);

				/* set the texture mode back to replace */
				glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

				/* re-activate last texture */
				w->screen->activeTexture(GL_TEXTURE1_ARB);
			}
			else
			{
				/* fully saturated or fully unsaturated */

				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_CONSTANT);
				glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
				glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);

				/* color constant */
				constant[3] = attrib->opacity / 65535.0f;
				constant[0] = constant[1] = constant[2] =
						constant[3] * attrib->brightness / 65535.0f;

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

				glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);

				/* draw the window geometry */
				(*w->screen->drawWindowGeometry) (w);
			}

			/* disable the current texture */
			disableTexture(w->screen, texture);

			/* set the texture mode back to replace */
			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

			/* re-activate last texture */
			w->screen->activeTexture(GL_TEXTURE0_ARB);

			/* disable that texture */
			disableTexture(w->screen, texture);

			/* set the default color */
			glColor4usv(defaultColor);

			/* set screens texture mode back to replace */
			screenTexEnvMode(w->screen, GL_REPLACE);

			/* if it's a translucent window, disable blending */
			if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
				glDisable(GL_BLEND);
		}
		else
		{
			/* no saturation adjustments */

			/* enable the current texture */
			enableTexture(w->screen, texture, filter);

			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_ONE_MINUS_SRC_COLOR);	/* negate */


			/* we are not opaque or fully bright */
			if ((mask & PAINT_WINDOW_TRANSLUCENT_MASK)
				|| attrib->brightness != BRIGHT)
			{
				GLfloat constant[4];

				/* enable blending */
				glEnable(GL_BLEND);

				/* color constant */
				constant[3] = attrib->opacity / 65535.0f;
				constant[0] = constant[3] * attrib->brightness / 65535.0f;
				constant[1] = constant[3] * attrib->brightness / 65535.0f;
				constant[2] = constant[3] * attrib->brightness / 65535.0f;
				glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);

				glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
				glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
				glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
				glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
				glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);	/* negate */
				glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

				glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
				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);

				/* draw the window geometry */
				(*w->screen->drawWindowGeometry) (w);

				/* disable blending */
				glDisable(GL_BLEND);

			}
			else
			{
				/* no adjustments to saturation, brightness or opacity */

				/* draw the window geometry */
				(*w->screen->drawWindowGeometry) (w);
			}

			/* disable the current texture */
			disableTexture(w->screen, texture);

			/* set the screens texture mode back to replace */
			screenTexEnvMode(w->screen, GL_REPLACE);
		}
		/* pop previous matrix */
		glPopMatrix();
	}
	else
	{
		/* not negative */
		UNWRAP(ns, w->screen, drawWindowTexture);
		(*w->screen->drawWindowTexture) (w, texture, attrib, mask);
		WRAP(ns, w->screen, drawWindowTexture, NEGDrawWindowTexture);
	}
}
static Bool NEGDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect)
{
	int status;

	NEG_SCREEN(w->screen);
	NEG_WINDOW(w);

	/* the window is initial when it is being mapped */
	if (initial)
	{
		/* if the screen is negative, negate the new window */
		if (ns->isNeg && !nw->isNeg)
			NEGToggle(w);
	}

	UNWRAP(ns, w->screen, damageWindowRect);
	status = (*w->screen->damageWindowRect) (w, initial, rect);
	WRAP(ns, w->screen, damageWindowRect, NEGDamageWindowRect);

	return status;
}

static void NEGDisplayInitOptions(NEGDisplay * nd)
{
	CompOption *o;

	o = &nd->opt[NEG_DISPLAY_OPTION_WINDOW_TOGGLE];
	o->advanced = False;
	o->name = "window_toggle";
	o->group = N_("Bindings");
	o->subGroup = N_("Window bindings");
	o->displayHints = "";
	o->shortDesc = N_("Toggle Window Negative");
	o->longDesc = N_("Toggle Window Negative.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = negToggle;
	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.state |= CompActionStateInitButton;
	o->value.action.key.modifiers = NEG_TOGGLE_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(NEG_TOGGLE_KEY_DEFAULT);

	o = &nd->opt[NEG_DISPLAY_OPTION_SCREEN_TOGGLE];
	o->advanced = False;
	o->name = "screen_toggle";
	o->group = N_("Bindings");
	o->subGroup = N_("Screen bindings");
	o->displayHints = "";
	o->shortDesc = N_("Toggle Screen Negative");
	o->longDesc = N_("Toggle Screen Negative.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = negToggleAll;
	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.state |= CompActionStateInitButton;
	o->value.action.key.modifiers = NEG_TOGGLE_ALL_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(NEG_TOGGLE_ALL_KEY_DEFAULT);
}

static CompOption *NEGGetDisplayOptions(CompDisplay * display, int *count)
{
	if (display)
	{
		NEG_DISPLAY(display);

		*count = NUM_OPTIONS(nd);
		return nd->opt;
	}
	else
	{
		NEGDisplay *nd = malloc(sizeof(NEGDisplay));

		NEGDisplayInitOptions(nd);
		*count = NUM_OPTIONS(nd);
		return nd->opt;
	}
}

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

	NEG_DISPLAY(display);

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

	switch (index)
	{
	case NEG_DISPLAY_OPTION_WINDOW_TOGGLE:
	case NEG_DISPLAY_OPTION_SCREEN_TOGGLE:
		if (setDisplayAction(display, o, value))
			return TRUE;
		break;

	default:
		break;
	}
	return FALSE;
}


static Bool NEGInitDisplay(CompPlugin * p, CompDisplay * d)
{
	NEGDisplay *nd;

	nd = malloc(sizeof(NEGDisplay));
	if (!nd)
		return FALSE;

	nd->screenPrivateIndex = allocateScreenPrivateIndex(d);
	if (nd->screenPrivateIndex < 0)
	{
		free(nd);
		return FALSE;
	}

	/* initialize the display options */
	NEGDisplayInitOptions(nd);

	d->privates[displayPrivateIndex].ptr = nd;

	return TRUE;
}

static void NEGFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	NEG_DISPLAY(d);
	freeScreenPrivateIndex(d, nd->screenPrivateIndex);
	free(nd);
}

static void NEGScreenInitOptions(NEGScreen * ns)
{
	CompOption *o;
	unsigned int i;

	o = &ns->opt[NEG_SCREEN_OPTION_EXCLUDE_NULL_WM_CLASS];
	o->advanced = True;
	o->name = "exclude_null_wm_class";
	o->group = N_("Exclusions");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Exclude Null WM_CLASS");
	o->longDesc = N_("Exclude to Negate if WM_CLASS is Null.");
	o->type = CompOptionTypeBool;
	o->value.b = NEG_EXCLUDE_NULL_WM_CLASS_DEFAULT;

	o = &ns->opt[NEG_SCREEN_OPTION_EXCLUDE_WINDOW_TYPE];
	o->advanced = False;
	o->name = "exclude_window_types";
	o->group = N_("Exclusions");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Exclude Window Types");
	o->longDesc = N_("Window Types that should not be Negated.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = N_EXCLUDE_WIN_TYPE;
	o->value.list.value =
			malloc(sizeof(CompOptionValue) * N_EXCLUDE_WIN_TYPE);
	for (i = 0; i < N_EXCLUDE_WIN_TYPE; i++)
		o->value.list.value[i].s = strdup(excludeWinType[i]);
	o->rest.s.string = (char **)windowTypeString;
	o->rest.s.nString = nWindowTypeString;

	ns->excludeWMask = compWindowTypeMaskFromStringList(&o->value);

	o = &ns->opt[NEG_SCREEN_OPTION_EXCLUDE_LIST];
	o->advanced = False;
	o->name = "exclude_list";
	o->group = N_("Exclusions");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("WM_CLASS to Exclude");
	o->longDesc = N_("Window Classes which should not be Negated.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;
	o->rest.s.string = 0;
	o->rest.s.nString = 0;
}

static CompOption *NEGGetScreenOptions(CompScreen * screen, int *count)
{
	if (screen)
	{
		NEG_SCREEN(screen);
		*count = NUM_OPTIONS(ns);
		return ns->opt;
	}
	else
	{
		NEGScreen *ns = malloc(sizeof(NEGScreen));

		NEGScreenInitOptions(ns);
		*count = NUM_OPTIONS(ns);
		return ns->opt;
	}
}

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

	NEG_SCREEN(screen);

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

	switch (index)
	{
	case NEG_SCREEN_OPTION_EXCLUDE_NULL_WM_CLASS:
		if (compSetBoolOption(o, value))
			return TRUE;
		break;

	case NEG_SCREEN_OPTION_EXCLUDE_WINDOW_TYPE:
		if (compSetOptionList(o, value))
		{
			ns->excludeWMask = compWindowTypeMaskFromStringList(&o->value);
			return TRUE;
		}
		break;

	case NEG_SCREEN_OPTION_EXCLUDE_LIST:
		if (compSetOptionList(o, value))
			return TRUE;
		break;

	default:
		break;
	}

	return FALSE;
}

static Bool NEGInitScreen(CompPlugin * p, CompScreen * s)
{
	NEGScreen *ns;

	NEG_DISPLAY(s->display);

	ns = malloc(sizeof(NEGScreen));
	if (!ns)
		return FALSE;

	ns->windowPrivateIndex = allocateWindowPrivateIndex(s);
	if (ns->windowPrivateIndex < 0)
	{
		free(ns);
		return FALSE;
	}

	/* initialize the screen variables
	 * you know what happens if you don't
	 */
	ns->isNeg = FALSE;

	/* Initialize the screen options */
	NEGScreenInitOptions(ns);

	/* add screen actions */
	addScreenAction(s,
					&nd->opt[NEG_DISPLAY_OPTION_WINDOW_TOGGLE].value.action);
	addScreenAction(s,
					&nd->opt[NEG_DISPLAY_OPTION_SCREEN_TOGGLE].value.action);

	/* wrap overloaded functions */
	WRAP(ns, s, drawWindowTexture, NEGDrawWindowTexture);
	WRAP(ns, s, damageWindowRect, NEGDamageWindowRect);

	s->privates[nd->screenPrivateIndex].ptr = ns;

	return TRUE;
}


static void NEGFiniScreen(CompPlugin * p, CompScreen * s)
{
	NEG_SCREEN(s);
	NEG_DISPLAY(s->display);
	freeWindowPrivateIndex(s, ns->windowPrivateIndex);
	UNWRAP(ns, s, drawWindowTexture);
	UNWRAP(ns, s, damageWindowRect);
	removeScreenAction(s,
					   &nd->opt[NEG_DISPLAY_OPTION_WINDOW_TOGGLE].
					   value.action);
	removeScreenAction(s,
					   &nd->opt[NEG_DISPLAY_OPTION_SCREEN_TOGGLE].
					   value.action);
	free(ns);
}

static Bool NEGInitWindow(CompPlugin * p, CompWindow * w)
{
	NEGWindow *nw;

	NEG_SCREEN(w->screen);

	nw = malloc(sizeof(NEGWindow));
	if (!nw)
		return FALSE;

	nw->isNeg = FALSE;

	w->privates[ns->windowPrivateIndex].ptr = nw;

	return TRUE;
}

static void NEGFiniWindow(CompPlugin * p, CompWindow * w)
{
	NEG_WINDOW(w);
	free(nw);
}

static Bool NEGInit(CompPlugin * p)
{
	displayPrivateIndex = allocateDisplayPrivateIndex();
	if (displayPrivateIndex < 0)
		return FALSE;

	return TRUE;
}

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

CompPluginVTable NEGVTable = {
	"neg",
	N_("Negative"),
	N_("Used to set a window or the screen negative"),
	NEGInit,
	NEGFini,
	NEGInitDisplay,
	NEGFiniDisplay,
	NEGInitScreen,
	NEGFiniScreen,
	NEGInitWindow,
	NEGFiniWindow,
	NEGGetDisplayOptions,
	NEGSetDisplayOption,
	NEGGetScreenOptions,
	NEGSetScreenOption,
	NULL,
	0,
	0,
	0,
	BERYL_ABI_INFO,
	"beryl-plugins",
	"accessibility",
	0,
	0,
	True,
};

CompPluginVTable *getCompPluginInfo(void)
{
	return &NEGVTable;
}


syntax highlighted by Code2HTML, v. 0.9.1