/*
 * state plugin for beryl
 *
 * Copyright (C) 2006 John Wall <wall_john@sohu.com> (http://wall_john.blogeden.cn)
 *
 * 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.
 *
 * Authors:
 *  John Wall <wall_john@sohu.com> (http://wall_john.blogeden.cn)
 *  Kristian Lyngstøl <kristian@beryl-project.org> (Locking).
 */

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

#include <beryl.h>

#include <glib.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#define _GNU_SOURCE
#include <regex.h>
#include <fnmatch.h>

typedef struct _MwmHints
{
	unsigned long flags;
	unsigned long functions;
	unsigned long decorations;
} MwmHints;


static int displayPrivateIndex;

typedef struct _StateDisplay
{
	int screenPrivateIndex;
	HandleEventProc handleEvent;
	Atom wmPidAtom;
	Atom wmVisibleNameAtom;
	Atom wmNameAtom;
	Atom utf8String;
	Atom nameAtom;
} StateDisplay;

typedef enum _StateOption
{
	STATE_SCREEN_OPTION_OPACITY,
	STATE_SCREEN_OPTION_SATURATION,
	STATE_SCREEN_OPTION_BRIGHTNESS,
	STATE_SCREEN_OPTION_OPACITY_ABS,
	STATE_SCREEN_OPTION_SATURATION_ABS,
	STATE_SCREEN_OPTION_BRIGHTNESS_ABS,
	STATE_SCREEN_OPTION_VIEWPORT,
//    STATE_SCREEN_OPTION_WIDGET,
	STATE_SCREEN_OPTION_POSITION,
	STATE_SCREEN_OPTION_BORDER,
	STATE_SCREEN_OPTION_NOARGB,
	STATE_SCREEN_OPTION_NUM
} StateOption;

#define STATE_SCREEN_OPTION_OPACITY_MAX 100
#define STATE_SCREEN_OPTION_OPACITY_MIN 0

#define STATE_SCREEN_OPTION_BRIGHTNESS_MAX 100
#define STATE_SCREEN_OPTION_BRIGHTNESS_MIN 0

#define STATE_SCREEN_OPTION_SATURATION_MAX 100
#define STATE_SCREEN_OPTION_SATURATION_MIN 0

typedef enum _StateType
{
	NIL, WINDOW, TITLE, PROGRAM, CLASS, NAME, ROLE
} StateType;

typedef struct _StateIntValue
{
	StateType type;
	char *name;
	int value;
	Bool is_regex;
	regex_t match;
} StateIntValue;

typedef struct _StateIntValues
{
	StateIntValue **values;
	int count;
} StateIntValues;

typedef struct _StateScreen
{
	CompOption opt[STATE_SCREEN_OPTION_NUM];
	DamageWindowRectProc damageWindowRect;

	StateIntValues *opacities;
	StateIntValues *brightnesses;
	StateIntValues *saturations;
	StateIntValues *opacitiesabs;
	StateIntValues *brightnessesabs;
	StateIntValues *saturationsabs;
	StateIntValues *viewports;
	StateIntValues *widgets;
	StateIntValues *positions;
	StateIntValues *borders;
	StateIntValues *noargb;

	MwmHints mwmHints;
} StateScreen;

#define GET_STATE_DISPLAY(d)				     \
    ((StateDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define STATE_DISPLAY(d)			   \
    StateDisplay *fd = GET_STATE_DISPLAY (d)

#define GET_STATE_SCREEN(s, fd)					 \
    ((StateScreen *) (s)->privates[(fd)->screenPrivateIndex].ptr)

#define STATE_SCREEN(s)							\
    StateScreen *fs = GET_STATE_SCREEN (s, GET_STATE_DISPLAY (s->display))

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

static int stateClipValue(int value, int min, int max)
{
	if (value < min)
		value = min;
	if (value > max)
		value = max;
	return value;
}

static StateIntValue *stateParseIntValue(char *s)
{
	StateIntValue *value = NULL;
	char *sep1, *sep2;

	if (s && *s)
	{
		value = (StateIntValue *) malloc(sizeof(StateIntValue));
		value->name = NULL;
		value->is_regex = False;
		if (s[0] == 'w')
			value->type = WINDOW;
		else if (s[0] == 't')
			value->type = TITLE;
		else if (s[0] == 'p')
			value->type = PROGRAM;
		else if (s[0] == 'c')
			value->type = CLASS;
		else if (s[0] == 'r')
			value->type = ROLE;
		else if (s[0] == 'n')
			value->type = NAME;
		else
			goto err;

		if (strlen(s) < 3)
			goto err;
		if (s[1] == ':')
		{
			sep1 = s + 2;
			sep2 = strchr(sep1, ':');
			if (!sep2)
				goto err;

			value->name = (char *)malloc(sizeof(char) * (sep2 - sep1 + 1));
			strncpy(value->name, sep1, sep2 - sep1);
			value->name[sep2 - sep1] = 0;

			value->value = atoi(sep2 + 1);
		}
		else if (s[1] == '/')
		{
			//now we have to search the rest of the string until we find a
			//'/' that's not preceeded by a '\'
			sep1 = s + 2;
			sep2 = sep1;
			while ((sep2 = strchr(sep2, '/')) && sep2[-1] == '\\');
			if (!sep2)
				goto err;
			value->name = (char *)malloc(sizeof(char) * (sep2 - sep1 + 1));
			strncpy(value->name, sep1, sep2 - sep1);
			value->name[sep2 - sep1] = 0;
			value->value = atoi(sep2 + 1);
			if (regcomp(&value->match, value->name, REG_EXTENDED | REG_NOSUB))
				goto err;
			value->is_regex = True;
		}
		return value;
	}
  err:
	if (value)
	{
		if (value->name)
			free(value->name);
		if (value->is_regex)
			regfree(&value->match);
		free(value);
	}
	return NULL;
}

static StateIntValues *stateLoadIntValuesFromStringList(CompOptionValue *
														value, int min,
														int max)
{
	StateIntValue *ivalue;
	StateIntValues *values;
	int i, count = 0;

	if (value->list.nValue > 0)
	{
		values = (StateIntValues *) malloc(sizeof(StateIntValues));
		values->values =
				(StateIntValue **) malloc(sizeof(StateIntValue *) *
										  value->list.nValue);
		for (i = 0; i < value->list.nValue; i++)
		{
			ivalue = stateParseIntValue(value->list.value[i].s);
			if (ivalue)
			{
				ivalue->value = stateClipValue(ivalue->value, min, max);
				values->values[count++] = ivalue;
			}
		}
		if (count == 0)
		{
			free(values->values);
			free(values);
			return NULL;
		}
		else
		{
			values->count = count;
			return values;
		}
	}
	return NULL;
}

static void stateFreeIntValues(StateIntValues * values)
{
	int i;

	if (values != NULL)
	{
		for (i = 0; i < values->count; ++i)
		{
			if (values->values[i]->is_regex)
				regfree(&values->values[i]->match);
			free(values->values[i]->name);
			free(values->values[i]);
		}
		free(values);
	}
}

static void stateScreenInitOptions(StateScreen * fs)
{
	CompOption *o;

	o = &fs->opt[STATE_SCREEN_OPTION_OPACITY];
	o->advanced = False;
	o->name = "opacity";
	o->group = N_("Rules");
	o->subGroup = N_("Window Opacity");
	o->displayHints = "";
	o->shortDesc = N_("Window Opacity");
	o->longDesc = N_("Set Window Opacity by conditions.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_BRIGHTNESS];
	o->advanced = False;
	o->name = "brightness";
	o->group = N_("Rules");
	o->subGroup = N_("Window Brightness");
	o->displayHints = "";
	o->shortDesc = N_("Window Brightness");
	o->longDesc = N_("Set Window Brightness by conditions.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_SATURATION];
	o->advanced = False;
	o->name = "saturation";
	o->group = N_("Rules");
	o->subGroup = N_("Window Saturation");
	o->displayHints = "";
	o->shortDesc = N_("Window Saturation");
	o->longDesc = N_("Set Window Saturation by conditions.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_OPACITY_ABS];
	o->advanced = False;
	o->name = "opacityabs";
	o->group = N_("Rules");
	o->subGroup = N_("Window Opacity");
	o->displayHints = "";
	o->shortDesc = N_("Absoloute Window Opacity");
	o->longDesc = N_("Set Window Opacity by conditions, this "
					 "overrides plugins like Opacify.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_BRIGHTNESS_ABS];
	o->advanced = False;
	o->name = "brightnessabs";
	o->group = N_("Rules");
	o->subGroup = N_("Window Brightness");
	o->displayHints = "";
	o->shortDesc = N_("Absoloute Window Brightness");
	o->longDesc = N_("Set Window Brightness by conditions, this "
					 "overrides plugins like Opacify.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_SATURATION_ABS];
	o->advanced = False;
	o->name = "saturationabs";
	o->group = N_("Rules");
	o->subGroup = N_("Window Saturation");
	o->displayHints = "";
	o->shortDesc = N_("Absoloute Window Saturation");
	o->longDesc = N_("Set Window Saturation by conditions, this "
					 "overrides plugins like Opacify.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_VIEWPORT];
	o->advanced = False;
	o->name = "viewport";
	o->group = N_("Rules");
	o->subGroup = N_("Initial Viewport");
	o->displayHints = "";
	o->shortDesc = N_("Initial Viewport");
	o->longDesc = N_("Set Initial Viewport for windows by conditions.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_POSITION];
	o->advanced = False;
	o->name = "position";
	o->group = N_("Rules");
	o->subGroup = N_("Position");
	o->displayHints = "";
	o->shortDesc = N_("Position");
	o->longDesc =
			N_("Set the initial window position.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_BORDER];
	o->advanced = False;
	o->name = "border";
	o->group = N_("Rules");
	o->subGroup = N_("Border");
	o->displayHints = "";
	o->shortDesc = N_("Disable window border");
	o->longDesc = N_("Enable or disable window border");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */

	o = &fs->opt[STATE_SCREEN_OPTION_NOARGB];
	o->advanced = False;
	o->name = "noargb";
	o->group = N_("Rules");
	o->subGroup = N_("Disable ARGB visual");
	o->displayHints = "";
	o->shortDesc = N_("Disable ARGB visual");
	o->longDesc = N_("Enable or disable ARGB visual");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = 0;
	o->value.list.value = 0;	/* NULL; */
	o->rest.s.string = 0;		/* windowTypeString; */
	o->rest.s.nString = 0;		/* nWindowTypeString; */
}

static CompOption *stateGetScreenOptions(CompScreen * screen, int *count)
{
	if (screen)
	{
		STATE_SCREEN(screen);

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

		stateScreenInitOptions(fs);
		*count = NUM_OPTIONS(fs);
		return fs->opt;
	}
}


static void stateChangeWindowOpacity(CompWindow * w, int opacity)
{
	opacity = OPAQUE * opacity / 100;
	setDefaultWindowOpacity(w, opacity, PL_PLUGIN_ONE);
}

static void stateChangeWindowBrightness(CompWindow * w, int brightness)
{
	brightness = BRIGHT * brightness / 100;
	setDefaultWindowBrightness(w, brightness, PL_PLUGIN_ONE);
}

static void stateChangeWindowSaturation(CompWindow * w, int saturation)
{
	saturation = COLOR * saturation / 100;
	setDefaultWindowSaturation(w, saturation, PL_PLUGIN_ONE);
}

static unsigned int stateWindowTypeMaskFromString(char *value)
{
	unsigned int mask = 0;

	if (strcasecmp(value, "desktop") == 0)
		mask |= CompWindowTypeDesktopMask;
	else if (strcasecmp(value, "dock") == 0)
		mask |= CompWindowTypeDockMask;
	else if (strcasecmp(value, "toolbar") == 0)
		mask |= CompWindowTypeToolbarMask;
	else if (strcasecmp(value, "menu") == 0)
		mask |= CompWindowTypeMenuMask;
	else if (strcasecmp(value, "utility") == 0)
		mask |= CompWindowTypeUtilMask;
	else if (strcasecmp(value, "splash") == 0)
		mask |= CompWindowTypeSplashMask;
	else if (strcasecmp(value, "dialog") == 0)
		mask |= CompWindowTypeDialogMask;
	else if (strcasecmp(value, "modaldialog") == 0)
		mask |= CompWindowTypeModalDialogMask;
	else if (strcasecmp(value, "normal") == 0)
		mask |= CompWindowTypeNormalMask;
	else if (strcasecmp(value, "fullscreen") == 0)
		mask |= CompWindowTypeFullscreenMask;
	else if (strcasecmp(value, "unknown") == 0)
		mask |= CompWindowTypeUnknownMask;
	else if (strcasecmp(value, "dropdownmenu") == 0)
		mask |= CompWindowTypeDropdownMenuMask;
	else if (strcasecmp(value, "popupmenu") == 0)
		mask |= CompWindowTypePopupMenuMask;
	else if (strcasecmp(value, "tooltip") == 0)
		mask |= CompWindowTypeTooltipMask;
	else if (strcasecmp(value, "notification") == 0)
		mask |= CompWindowTypeNotificationMask;
	else if (strcasecmp(value, "combo") == 0)
		mask |= CompWindowTypeComboMask;
	else if (strcasecmp(value, "dnd") == 0)
		mask |= CompWindowTypeDndMask;

	return mask;
}

static char *stateGetProgramPathFromPid(int pid)
{
	int len;
	char *path;

	if (pid >= 0)
	{
		path = (char *)malloc(sizeof(char) * 256);
		sprintf(path, "/proc/%d/exe", pid);
		if (access(path, F_OK) == 0 && (len = readlink(path, path, 256)) > 0)
		{
			path[len] = 0;
			return path;
		}
		free(path);
	}
	return NULL;
}

static Bool stateGetXPropertyCardinal(CompWindow * win, Atom atom, int *val)
{
	Atom type;
	int format;
	unsigned long nitems;
	unsigned long bytes_after;
	unsigned long *num;
	int result;

	*val = 0;

	type = None;
	result = XGetWindowProperty(win->screen->display->display,
								win->id,
								atom,
								0, LONG_MAX,
								False, XA_CARDINAL, &type, &format,
								&nitems, &bytes_after, (void *)&num);
	if (result != Success)
		return FALSE;

	if (type != XA_CARDINAL)
	{
		XFree(num);
		return FALSE;
	}

	*val = *num;
	XFree(num);
	return TRUE;
}

static char *stateGetXPropertyUtf8(CompWindow * win, Atom atom)
{
	Atom type;
	int format;
	unsigned long nitems;
	unsigned long bytes_after;
	unsigned long *val;
	int result;
	char *retval;
	Atom utf8_string;

	STATE_DISPLAY(win->screen->display);

	utf8_string = fd->utf8String;

	type = None;
	val = NULL;
	result = XGetWindowProperty(win->screen->display->display,
								win->id,
								atom,
								0, LONG_MAX,
								False, utf8_string,
								&type, &format, &nitems,
								&bytes_after, (unsigned char **)&val);

	if (result != Success)
		return NULL;

	if (type != utf8_string || format != 8 || nitems == 0)
	{
		if (val)
			XFree(val);
		return NULL;
	}


	retval = (char *)malloc(sizeof(char) * (nitems + 1));
	strncpy(retval, (char *)val, nitems);
	retval[nitems] = '\0';

	XFree(val);

	return retval;
}

static char *stateGetXPropertyString(CompWindow * win, Atom atom)
{
	Atom type;
	int format;
	unsigned long nitems;
	unsigned long bytes_after;
	unsigned long *val;
	int result;
	char *retval;
	Atom string;

	string = XInternAtom(win->screen->display->display, "STRING", 0);

	type = None;
	val = NULL;
	result = XGetWindowProperty(win->screen->display->display,
								win->id,
								atom,
								0, LONG_MAX,
								False, string,
								&type, &format, &nitems,
								&bytes_after, (unsigned char **)&val);

	if (result != Success)
		return NULL;

	if (type != string || nitems == 0)
	{
		if (val)
			XFree(val);
		return NULL;
	}


	retval = (char *)malloc(sizeof(char) * (nitems + 1));
	strncpy(retval, (char *)val, nitems);
	retval[nitems] = '\0';

	XFree(val);

	return retval;
}

static char *stateGetWindowRole(CompWindow * w)
{
	return stateGetXPropertyString(w,
								   XInternAtom(w->screen->display->
											   display, "WM_WINDOW_ROLE", 0));
}

static char *stateGetWindowTitle(CompWindow * w)
{
	char *title;

	STATE_DISPLAY(w->screen->display);

	title = stateGetXPropertyUtf8(w, fd->wmVisibleNameAtom);

	if (title == NULL)
		title = stateGetXPropertyUtf8(w, fd->wmNameAtom);

	if (title == NULL)
		title = stateGetXPropertyString(w, fd->nameAtom);

	return title;
}

static int stateGetWindowPid(CompWindow * w)
{
	int val;

	STATE_DISPLAY(w->screen->display);

	if (!stateGetXPropertyCardinal(w, fd->wmPidAtom, &val))
		return -1;
	else
		return val;
}

static char *strchrr(char *chars, char ch)
{
	int pos = -1;
	char *tmp = chars;

	if (chars == NULL)
		return NULL;
	while (*tmp)
	{
		if (*tmp == ch)
			pos = tmp - chars;
		tmp++;
	}
	if (pos != -1)
		return chars + pos + 1;
	return NULL;
}

static Bool stateMatch(char *match, StateIntValue * value)
{
	if (value->is_regex)
		return regexec(&value->match, match, 0, 0, 0) != REG_NOMATCH;
	else
		return fnmatch(value->name, match, FNM_CASEFOLD) == 0;
}

static signed int
stateGetParamForWindow(CompWindow * w, StateIntValues * params)
{
	signed int ret = -1;
	StateType changed;
	StateType type;
	char *name;
	char *title;
	char *progr;
	char *sname;
	char *role;
	int dret;
	int i;
	unsigned int mask;


	if (!params)
		return -1;
	changed = NIL;
	title = stateGetWindowTitle(w);
	progr = stateGetProgramPathFromPid(stateGetWindowPid(w));

	role = stateGetWindowRole(w);

	sname = strchrr(progr, '/');
	for (i = 0; i < params->count; ++i)
	{
		type = params->values[i]->type;
		name = params->values[i]->name;
		dret = params->values[i]->value;
		if (type == WINDOW)
		{
			mask = stateWindowTypeMaskFromString(name);
			if ((w->type & mask) && changed <= WINDOW)
			{
				ret = dret;
				changed = WINDOW;
			}
		}
		else if (type == TITLE)
		{
			if (title
				&& stateMatch(title, params->values[i]) && changed <= TITLE)
			{
				ret = dret;
				changed = TITLE;
			}

		}
		else if (type == PROGRAM)
		{
			if (progr
				&&
				(stateMatch(progr, params->values[i])
				 || stateMatch(sname, params->values[i])))
			{
				ret = dret;
				changed = PROGRAM;
			}
		}
		else if (type == CLASS)
		{
			if (w->resClass
				&&
				(stateMatch
				 (w->resClass, params->values[i]) && changed <= CLASS))
			{
				ret = dret;
				changed = CLASS;
			}
		}
		else if (type == ROLE)
		{
			if (role
				&& stateMatch(role, params->values[i]) && changed <= ROLE)
			{
				ret = dret;
				changed = ROLE;
			}
		}
		else if (type == NAME)
		{
			if (w->resName
				&&
				(stateMatch
				 (w->resName, params->values[i]) && changed <= NAME))
			{
				ret = dret;
				changed = NAME;
			}
		}
	}
	if (title)
		free(title);
	if (progr)
		free(progr);
	if (role)
		free(role);

	return ret;
}

static void stateAdjustWindowPaintParams(CompWindow * w)
{
	STATE_SCREEN(w->screen);
	signed int val;

	val = stateGetParamForWindow(w, fs->opacities);
	if (val != -1)
		stateChangeWindowOpacity(w, val);
	val = stateGetParamForWindow(w, fs->saturations);
	if (val != -1)
		stateChangeWindowSaturation(w, val);
	val = stateGetParamForWindow(w, fs->brightnesses);
	if (val != -1)
		stateChangeWindowBrightness(w, val);
	val = stateGetParamForWindow(w, fs->opacitiesabs);
	if (val != -1)
		setWindowOpacity(w, val * OPAQUE / 100, PL_PLUGIN_ONE);
	val = stateGetParamForWindow(w, fs->saturationsabs);
	if (val != -1)
		setWindowSaturation(w, val * COLOR / 100, PL_PLUGIN_ONE);
	val = stateGetParamForWindow(w, fs->brightnessesabs);
	if (val != -1)
		setWindowBrightness(w, val * BRIGHT / 100, PL_PLUGIN_ONE);
}

static void stateResetWindowPaintParams(CompWindow * w)
{
	STATE_SCREEN(w->screen);
	signed int val;

	val = stateGetParamForWindow(w, fs->opacities);
	if (val != -1)
		setWindowBailoutOpacity(w, PL_PLUGIN_ONE);
	val = stateGetParamForWindow(w, fs->saturations);
	if (val != -1)
		setWindowBailoutSaturation(w, PL_PLUGIN_ONE);
	val = stateGetParamForWindow(w, fs->brightnesses);
	if (val != -1)
		setWindowBailoutBrightness(w, PL_PLUGIN_ONE);
	val = stateGetParamForWindow(w, fs->opacitiesabs);
	if (val != -1)
		resetWindowOpacity(w, PL_PLUGIN_ONE);
	val = stateGetParamForWindow(w, fs->saturationsabs);
	if (val != -1)
		resetWindowSaturation(w, PL_PLUGIN_ONE);
	val = stateGetParamForWindow(w, fs->brightnessesabs);
	if (val != -1)
		resetWindowBrightness(w, PL_PLUGIN_ONE);
}

static void stateAdjustAllWindowsPaintParams(CompScreen * screen)
{
	CompWindow *w = screen->windows;

	while (w)
	{
		stateAdjustWindowPaintParams(w);
		w = w->next;
	}
}

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

	for (w = s->windows; w; w = w->next)
		stateResetWindowPaintParams(w);
}

/*
static void
updateWidgetStatusForWindow (CompWindow * w)
{
    Atom a = XInternAtom (w->screen->display->display, "_BERYL_WIDGET",
                          FALSE);
    STATE_SCREEN (w->screen);
    signed int val = stateGetParamForWindow (w, fs->widgets);
    if (val != -1)
    {
        if (val == 0)
        {
            XDeleteProperty (w->screen->display->display, w->id, a);
        }
        else
        {
            if (w->inShowDesktopMode || w->mapNum ||
                w->attrib.map_state == IsViewable || w->minimized)
            {
                if (w->minimized || w->inShowDesktopMode)
                    unminimizeWindow (w);
                XChangeProperty (w->screen->display->display, w->id, a,
                                 XA_STRING, 8, PropModeReplace,
                                 (unsigned char *)&val, 1);
            }
        }
    }
}

static void
updateWidgetStatus (CompScreen * screen)
{
    CompWindow *w = screen->windows;
    while (w)
    {
        updateWidgetStatusForWindow (w);
        w = w->next;
    }
}
*/

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

	STATE_SCREEN(screen);

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

	switch (index)
	{
	case STATE_SCREEN_OPTION_OPACITY:
		if (compSetOptionList(o, value))
		{
			stateResetAllWindowsPaintParams(screen);
			stateFreeIntValues(fs->opacities);
			fs->opacities =
					stateLoadIntValuesFromStringList
					(&o->value, STATE_SCREEN_OPTION_OPACITY_MIN,
					 STATE_SCREEN_OPTION_OPACITY_MAX);
			stateAdjustAllWindowsPaintParams(screen);

			return TRUE;
		}
		break;
	case STATE_SCREEN_OPTION_BRIGHTNESS:
		if (compSetOptionList(o, value))
		{
			stateResetAllWindowsPaintParams(screen);
			stateFreeIntValues(fs->brightnesses);
			fs->brightnesses =
					stateLoadIntValuesFromStringList
					(&o->value, STATE_SCREEN_OPTION_BRIGHTNESS_MIN,
					 STATE_SCREEN_OPTION_BRIGHTNESS_MAX);
			stateAdjustAllWindowsPaintParams(screen);

			return TRUE;
		}
		break;
	case STATE_SCREEN_OPTION_SATURATION:
		if (compSetOptionList(o, value))
		{
			stateResetAllWindowsPaintParams(screen);
			stateFreeIntValues(fs->saturations);
			fs->saturations =
					stateLoadIntValuesFromStringList
					(&o->value, STATE_SCREEN_OPTION_SATURATION_MIN,
					 STATE_SCREEN_OPTION_SATURATION_MAX);
			stateAdjustAllWindowsPaintParams(screen);

			return TRUE;
		}
		break;
	case STATE_SCREEN_OPTION_OPACITY_ABS:
		if (compSetOptionList(o, value))
		{
			stateResetAllWindowsPaintParams(screen);
			stateFreeIntValues(fs->opacitiesabs);
			fs->opacitiesabs =
					stateLoadIntValuesFromStringList
					(&o->value, STATE_SCREEN_OPTION_OPACITY_MIN,
					 STATE_SCREEN_OPTION_OPACITY_MAX);
			stateAdjustAllWindowsPaintParams(screen);

			return TRUE;
		}
		break;
	case STATE_SCREEN_OPTION_BRIGHTNESS_ABS:
		if (compSetOptionList(o, value))
		{
			stateResetAllWindowsPaintParams(screen);
			stateFreeIntValues(fs->brightnessesabs);
			fs->brightnessesabs =
					stateLoadIntValuesFromStringList
					(&o->value, STATE_SCREEN_OPTION_BRIGHTNESS_MIN,
					 STATE_SCREEN_OPTION_BRIGHTNESS_MAX);
			stateAdjustAllWindowsPaintParams(screen);

			return TRUE;
		}
		break;
	case STATE_SCREEN_OPTION_SATURATION_ABS:
		if (compSetOptionList(o, value))
		{
			stateResetAllWindowsPaintParams(screen);
			stateFreeIntValues(fs->saturationsabs);
			fs->saturationsabs =
					stateLoadIntValuesFromStringList
					(&o->value, STATE_SCREEN_OPTION_SATURATION_MIN,
					 STATE_SCREEN_OPTION_SATURATION_MAX);
			stateAdjustAllWindowsPaintParams(screen);

			return TRUE;
		}
		break;
	case STATE_SCREEN_OPTION_VIEWPORT:
		if (compSetOptionList(o, value))
		{
			stateFreeIntValues(fs->viewports);
			fs->viewports =
					stateLoadIntValuesFromStringList
					(&o->value, 1, (screen->hsize * screen->vsize));
			return TRUE;
		}
		break;
//    case STATE_SCREEN_OPTION_WIDGET:
//        if (compSetOptionList (o, value))
//        {
//            stateFreeIntValues (fs->widgets);
//            fs->widgets = stateLoadIntValuesFromStringList (&o->value, 1, 2);
//            updateWidgetStatus (screen);
//            return TRUE;
//        }
//        break;
	case STATE_SCREEN_OPTION_POSITION:
		if (compSetOptionList(o, value))
		{
			stateFreeIntValues(fs->positions);
			fs->positions =
					stateLoadIntValuesFromStringList(&o->value, 1, 99);

			return TRUE;
		}
		break;
	case STATE_SCREEN_OPTION_BORDER:
		if (compSetOptionList(o, value))
		{
			stateFreeIntValues(fs->borders);
			fs->borders = stateLoadIntValuesFromStringList(&o->value, 0, 1);

			return TRUE;
		}
		break;
	case STATE_SCREEN_OPTION_NOARGB:
		if (compSetOptionList(o, value))
		{
			stateFreeIntValues(fs->noargb);
			fs->noargb = stateLoadIntValuesFromStringList(&o->value, 0, 1);

			return TRUE;
		}
		break;
	default:
		break;
	}

	return FALSE;
}

static Bool stateDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect)
{
	Bool status;
	Bool wasPlaced;

	STATE_SCREEN(w->screen);

	wasPlaced = w->placed;

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

	if (initial && !w->attrib.override_redirect && w->placed && !wasPlaced)
	{

		int p = stateGetParamForWindow(w, fs->positions);

		if (p != -1)
		{
			int h = 0;

			h = p / 10;
			p = p % 10;

			// send window to numbered position
			XClientMessageEvent xev;

			xev.type = ClientMessage;
			xev.window = w->id;
			xev.message_type =
					XInternAtom(w->screen->display->display,
								"_BERYL_PUT_WINDOW", 0);
			xev.format = 32;
			xev.data.l[0] = 0;
			xev.data.l[1] = 0;
			xev.data.l[2] = 0;
			xev.data.l[3] = p;
			xev.data.l[4] = h;

			XSendEvent(w->screen->display->display,
					   w->screen->root, 0, StructureNotifyMask,
					   (XEvent *) & xev);
			XSync(w->screen->display->display, FALSE);
		}


		signed int v = stateGetParamForWindow(w, fs->viewports);

		if (v != -1)
		{
			int vx = v;
			int vy = 0;

			/*
			 * Check to see if we are using the plane plugin
			 */
			CompPlugin *plug;

			for (plug = getPlugins(); plug; plug = plug->next)
			{
				if (strcmp(plug->vTable->name, "plane") == 0)
				{
					/* split 1D face value into 2D x and y face */
					vx = v % w->screen->hsize;
					vy = v / w->screen->hsize;
					break;
				}
			}

			//check to see first if the window is already
			//on the right viewport.
			int x, y;

			defaultViewportForWindow(w, &x, &y);
			if (x != vx || y != vy)
			{
				/* TODO
				 * take the shortest path
				 */
				moveWindow(w,
						   (vx -
							w->screen->x) *
						   w->screen->width,
						   (vy -
							w->screen->y) * w->screen->height, TRUE, TRUE);
				syncWindowPosition(w);
			}
		}
	}
	return status;
}

static void stateHandleEvent(CompDisplay * d, XEvent * event)
{
	CompWindow *w;

	STATE_DISPLAY(d);
	w = NULL;
	switch (event->type)
	{
	case MapNotify:
		w = findWindowAtDisplay(d, event->xmap.window);
		if (w && (!w->placed))
		{
			stateAdjustWindowPaintParams(w);
			//updateWidgetStatusForWindow (w);
		}
		break;
	default:
		break;
	}

	UNWRAP(fd, d, handleEvent);
	(*d->handleEvent) (d, event);
	WRAP(fd, d, handleEvent, stateHandleEvent);

	switch (event->type)
	{
	case CreateNotify:
	{
		CompWindow *w = findWindowAtDisplay(d,
											event->xcreatewindow.window);

		if (w)
		{
			STATE_SCREEN(w->screen);
			int b = stateGetParamForWindow(w, fs->borders);

			if (b != -1)
			{
				XChangeProperty(w->screen->
								display->display,
								w->id,
								w->screen->
								display->
								mwmHintsAtom,
								w->screen->
								display->
								mwmHintsAtom, 8,
								PropModeReplace,
								(unsigned char *)
								&fs->mwmHints, sizeof(fs->mwmHints));
			}
			b = stateGetParamForWindow(w, fs->noargb);
			if (b != -1)
			{
				w->alpha = FALSE;
			}
		}
	}
		break;

	default:
		break;
	}
}

static Bool stateInitDisplay(CompPlugin * p, CompDisplay * d)
{
	StateDisplay *fd;

	fd = malloc(sizeof(StateDisplay));
	if (!fd)
		return FALSE;

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

	fd->utf8String = XInternAtom(d->display, "UTF8_STRING", 0);
	fd->wmPidAtom = XInternAtom(d->display, "_NET_WM_PID", 0);
	fd->wmNameAtom = XInternAtom(d->display, "_NET_WM_NAME", 0);
	fd->nameAtom = XInternAtom(d->display, "WM_NAME", 0);
	fd->wmVisibleNameAtom =
			XInternAtom(d->display, "_NET_WM_VISIBLE_NAME", 0);

	WRAP(fd, d, handleEvent, stateHandleEvent);

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

	return TRUE;
}

static void stateFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	STATE_DISPLAY(d);

	freeScreenPrivateIndex(d, fd->screenPrivateIndex);

	UNWRAP(fd, d, handleEvent);

	free(fd);
}

static Bool stateInitScreen(CompPlugin * p, CompScreen * s)
{
	StateScreen *fs;

	STATE_DISPLAY(s->display);

	fs = malloc(sizeof(StateScreen));
	if (!fs)
		return FALSE;

	stateScreenInitOptions(fs);

	fs->opacities = NULL;
	fs->brightnesses = NULL;
	fs->saturations = NULL;
	fs->opacitiesabs = NULL;
	fs->brightnessesabs = NULL;
	fs->saturationsabs = NULL;
	fs->viewports = NULL;
	fs->widgets = NULL;
	fs->positions = NULL;
	fs->borders = NULL;
	fs->noargb = NULL;

	memset(&fs->mwmHints, 0, sizeof(fs->mwmHints));
	fs->mwmHints.flags = (1 << 1);	// MwmHintsDecorations
	fs->mwmHints.decorations = 0;	// MwmDecorNone

	s->privates[fd->screenPrivateIndex].ptr = fs;

	WRAP(fs, s, damageWindowRect, stateDamageWindowRect);

	return TRUE;
}

static void stateFiniScreen(CompPlugin * p, CompScreen * s)
{
	STATE_SCREEN(s);
	stateResetAllWindowsPaintParams(s);

	stateFreeIntValues(fs->opacities);
	fs->opacities = NULL;
	stateFreeIntValues(fs->brightnesses);
	fs->brightnesses = NULL;
	stateFreeIntValues(fs->saturations);
	fs->saturations = NULL;
	stateFreeIntValues(fs->opacitiesabs);
	fs->opacitiesabs = NULL;
	stateFreeIntValues(fs->brightnessesabs);
	fs->brightnessesabs = NULL;
	stateFreeIntValues(fs->saturationsabs);
	fs->saturationsabs = NULL;
	stateFreeIntValues(fs->viewports);
	fs->viewports = NULL;
	stateFreeIntValues(fs->widgets);
	fs->widgets = NULL;
	stateFreeIntValues(fs->positions);
	fs->positions = NULL;
	stateFreeIntValues(fs->borders);
	fs->borders = NULL;
	stateFreeIntValues(fs->noargb);
	fs->noargb = NULL;

	UNWRAP(fs, s, damageWindowRect);

	free(fs);
}

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

	return TRUE;
}

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

CompPluginDep stateDeps[] = {
	{CompPluginRuleAfter, "place"}
};

static CompPluginVTable stateVTable = {
	"state",
	N_("Set Window Attribs by various criteria"),
	N_("w = window type, c = window class, t = window title, p = owning program, n = class name, r = class role"),
	stateInit,
	stateFini,
	stateInitDisplay,
	stateFiniDisplay,
	stateInitScreen,
	stateFiniScreen,
	0,							/* stateInitWindow,        */
	0,							/* stateFiniWindow,        */
	0,							/* stateGetDisplayOptions, */
	0,							/* stateSetDisplayOption,  */
	stateGetScreenOptions,
	stateSetScreenOption,
	stateDeps,
	sizeof(stateDeps) / sizeof(stateDeps[0]),
	0,
	0,
	BERYL_ABI_INFO,
	"beryl-plugins",
	"wm",
	0,
	0,
	False,
};

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


syntax highlighted by Code2HTML, v. 0.9.1