/**
 * Beryl  ADD Helper. Makes it easier to concentrate.
 *
 * Copyright (c) 2007 Kristian Lyngstøl <kristian@beryl-project.org>
 * Ported and highly modified by Patrick Niklaus <marex@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.
 *
 * This plugin provides a toggle-feature that dims all but the active
 * window. This makes it easier for people with lousy concentration
 * to focus. Like me.
 * 
 * Please note any major changes to the code in this header with who you
 * are and what you did. 
 *
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <compiz.h>

#include "addhelper_options.h"

#define GET_ADD_DISPLAY(d)                            \
	((AddHelperDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define ADD_DISPLAY(d)                                \
    AddHelperDisplay *ad = GET_ADD_DISPLAY (d)
#define GET_ADD_SCREEN(s, ad)                         \
	((AddHelperScreen *) (s)->privates[(ad)->screenPrivateIndex].ptr)
#define ADD_SCREEN(s)                                 \
	AddHelperScreen *as = GET_ADD_SCREEN (s, GET_ADD_DISPLAY (s->display))
#define GET_ADD_WINDOW(w, as) \
	((AddHelperWindow *) (w)->privates[ (as)->windowPrivateIndex].ptr)
#define ADD_WINDOW(w) \
	AddHelperWindow *aw = GET_ADD_WINDOW (w, GET_ADD_SCREEN  (w->screen, GET_ADD_DISPLAY (w->screen->display)))

static int displayPrivateIndex = 0;

typedef struct _AddHelperDisplay
{
	int screenPrivateIndex;
	int active_screen;

	GLushort opacity;
	GLushort brightness;
	GLushort saturation;

	CompMatch *match;
	Bool toggle;
	Window lastActive;

	HandleEventProc handleEvent;
} AddHelperDisplay;

typedef struct _AddHelperScreen
{
	int windowPrivateIndex;

	PaintWindowProc paintWindow;
} AddHelperScreen;

typedef struct _AddHelperWindow
{
	Bool dim;
} AddHelperWindow;

/* Walk through all windows of the screen and adjust them if they
 * are not the active window. If reset is true, this will reset
 * the windows, including the active. Otherwise, it will dim 
 * and reset the active. 
 */
static void walk_windows(CompDisplay *d)
{
	ADD_DISPLAY(d);
	
	CompScreen *s;
	CompWindow *w;
	for (s = d->screens; s; s = s->next)
	{
		for (w = s->windows; w; w = w->next)
		{
			ADD_WINDOW(w);

			aw->dim = FALSE;

			if (!ad->toggle)
				continue;

			if (w->id == d->activeWindow)
				continue;

			if(w->invisible || w->destroyed || w->hidden || w->minimized)
				continue;

			if (!matchEval(ad->match, w))
				continue;

			aw->dim = TRUE;
		}

		damageScreen(s);
	}
}

/* Checks if the window is dimmed and, if so, paints it with the modified
 * paint attributes.
 */
static Bool addhelperPaintWindow(CompWindow *w, const WindowPaintAttrib *attrib,
								  const CompTransform *transform,
								  Region region, unsigned int mask)
{
	ADD_SCREEN(w->screen);
	ADD_DISPLAY(w->screen->display);
	ADD_WINDOW(w);
	Bool status;

	if (aw->dim)
	{
		// copy the paint attribute
		WindowPaintAttrib wAttrib = *attrib;

		// applies the lowest value
		wAttrib.opacity = MIN(attrib->opacity, ad->opacity);
		wAttrib.brightness = MIN(attrib->brightness, ad->brightness);
		wAttrib.saturation = MIN(attrib->saturation, ad->saturation);

		// continue painting with the modified attribute
		UNWRAP(as, w->screen, paintWindow);
		status = (*w->screen->paintWindow) (w, &wAttrib, transform, region, mask);
		WRAP(as, w->screen, paintWindow, addhelperPaintWindow);
	}
	else
	{
		// the window is not dimmed, so its painted normal
		UNWRAP(as, w->screen, paintWindow);
		status = (*w->screen->paintWindow) (w, attrib, transform, region, mask);
		WRAP(as, w->screen, paintWindow, addhelperPaintWindow);
	}

	return status;
}

/* Takes the inital event. 
 * This checks for focus change and acts on it.
 */
static void addhelperHandleEvent(CompDisplay * d, XEvent * event)
{
	ADD_DISPLAY(d);

	UNWRAP(ad, d, handleEvent);
	(*d->handleEvent) (d, event);
	WRAP(ad, d, handleEvent, addhelperHandleEvent);
	
	if (!ad->toggle)
		return;

	if (event->type == PropertyNotify && ad->lastActive != d->activeWindow)
	{
		walk_windows(d);
		ad->lastActive = d->activeWindow;
	}
}

/* Configuration, initialization, boring stuff. ----------------------- */

/* Takes the action and toggles us.
 */
static Bool addhelperToggle(CompDisplay * d, CompAction * ac,
						   CompActionState state, CompOption * option,
						   int nOption)
{
	ADD_DISPLAY(d);

	ad->toggle = !ad->toggle;
	walk_windows(d);

	return TRUE;
}

/* Change notify for bcop */
static void addhelperDisplayOptionChanged(CompDisplay *d, CompOption *opt, AddhelperDisplayOptions num)
{
	ADD_DISPLAY(d);

	switch (num)
	{
	case AddhelperDisplayOptionBrightness:
		ad->brightness = (addhelperGetBrightness(d) * 0xffff) / 100;
		break;
	case AddhelperDisplayOptionSaturation:
		ad->saturation = (addhelperGetSaturation(d) * 0xffff) / 100;
		break;
	case AddhelperDisplayOptionOpacity:
		ad->opacity = (addhelperGetOpacity(d) * 0xffff) / 100;
		break;
	case AddhelperDisplayOptionWindowTypes:
		ad->match = addhelperGetWindowTypes(d);
		break;
	default:
		break;
	}

}

static Bool addhelperInitWindow(CompPlugin * p, CompWindow * w)
{
	ADD_SCREEN(w->screen);

	AddHelperWindow *aw = (AddHelperWindow *) malloc(sizeof(AddHelperWindow));

	w->privates[as->windowPrivateIndex].ptr = aw;

	aw->dim = FALSE;

	return TRUE;
}

static void addhelperFiniWindow(CompPlugin * p, CompWindow * w)
{
	ADD_WINDOW(w);

	free(aw);
}

static Bool addhelperInitScreen(CompPlugin * p, CompScreen * s)
{
	ADD_DISPLAY(s->display);

	AddHelperScreen *as = (AddHelperScreen*) malloc(sizeof(AddHelperScreen));

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

	WRAP(as, s, paintWindow, addhelperPaintWindow);

	s->privates[ad->screenPrivateIndex].ptr = as;

	return TRUE;
}

static void addhelperFiniScreen(CompPlugin * p, CompScreen * s)
{
	ADD_SCREEN(s);

	UNWRAP(as, s, paintWindow);
	
	free(as);
}

static Bool addhelperInitDisplay(CompPlugin * p, CompDisplay * d)
{
	AddHelperDisplay *ad = (AddHelperDisplay *) malloc(sizeof(AddHelperDisplay));

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

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

	addhelperSetToggleInitiate(d, addhelperToggle);
	addhelperSetBrightnessNotify(d, addhelperDisplayOptionChanged);
	addhelperSetOpacityNotify(d, addhelperDisplayOptionChanged);
	addhelperSetSaturationNotify(d, addhelperDisplayOptionChanged);

	ad->active_screen = d->screens->screenNum;
	ad->toggle = FALSE;
	ad->match = addhelperGetWindowTypes(d);
	ad->brightness = (addhelperGetBrightness(d) * 0xffff) / 100;
	ad->opacity = (addhelperGetOpacity(d) * 0xffff) / 100;
	ad->saturation = (addhelperGetSaturation(d) * 0xffff) / 100;
	ad->lastActive = None;

	WRAP(ad, d, handleEvent, addhelperHandleEvent);

	return TRUE;
}

static void addhelperFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	ADD_DISPLAY(d);

	UNWRAP(ad, d, handleEvent);

	freeScreenPrivateIndex(d, ad->screenPrivateIndex);
	free(ad);
}

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

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

static int addhelperGetVersion(CompPlugin *p, int version)
{
	return ABIVERSION;
}

CompPluginVTable addhelperVTable = {
	"addhelper",
	addhelperGetVersion,
	0,
	addhelperInit,
	addhelperFini,
	addhelperInitDisplay,
	addhelperFiniDisplay,
	addhelperInitScreen,
	addhelperFiniScreen,
	addhelperInitWindow,
	addhelperFiniWindow,
	0, // addhelperGetDisplayOptions
	0, // addhelperSetDisplayOptions
	0, // addhelperGetScreenOptions,
	0, // addhelperSetScreenOptions,
};

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


syntax highlighted by Code2HTML, v. 0.9.1