/*
 * Copyright © 2005 Novell, Inc.
 *
 * 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.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <unistd.h>

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

#include <beryl.h>

#define SWITCH_NEXT_KEY_DEFAULT       "Tab"
#define SWITCH_NEXT_MODIFIERS_DEFAULT CompAltMask

#define SWITCH_PREV_KEY_DEFAULT       "Tab"
#define SWITCH_PREV_MODIFIERS_DEFAULT (CompAltMask | ShiftMask)

#define SWITCH_NEXT_ALL_KEY_DEFAULT       "Tab"
#define SWITCH_NEXT_ALL_MODIFIERS_DEFAULT (CompAltMask | ControlMask)

#define SWITCH_PREV_ALL_KEY_DEFAULT       "Tab"
#define SWITCH_PREV_ALL_MODIFIERS_DEFAULT \
    (CompAltMask | ControlMask | ShiftMask)

#define SWITCH_RING_NEXT_KEY_DEFAULT       "Tab"
#define SWITCH_RING_NEXT_MODIFIERS_DEFAULT CompSuperMask

#define SWITCH_RING_PREV_KEY_DEFAULT       "Tab"
#define SWITCH_RING_PREV_MODIFIERS_DEFAULT (CompSuperMask | ShiftMask)

#define SWITCH_RING_NEXT_ALL_KEY_DEFAULT       "Tab"
#define SWITCH_RING_NEXT_ALL_MODIFIERS_DEFAULT (CompSuperMask | ControlMask)

#define SWITCH_RING_PREV_ALL_KEY_DEFAULT       "Tab"
#define SWITCH_RING_PREV_ALL_MODIFIERS_DEFAULT \
    (CompSuperMask | ControlMask | ShiftMask)

#define SWITCH_SPEED_DEFAULT   2.5f
#define SWITCH_SPEED_MIN       0.1f
#define SWITCH_SPEED_MAX       50.0f
#define SWITCH_SPEED_PRECISION 0.1f

#define SWITCH_TIMESTEP_DEFAULT   1.1f
#define SWITCH_TIMESTEP_MIN       0.1f
#define SWITCH_TIMESTEP_MAX       50.0f
#define SWITCH_TIMESTEP_PRECISION 0.1f

#define SWITCH_MIPMAP_DEFAULT FALSE

#define SWITCH_BRINGTOFRONT_DEFAULT TRUE
#define SWITCH_SWITCH_FOCUS_INSTANTLY_DEFAULT FALSE

#define SWITCH_SATURATION_DEFAULT 100
#define SWITCH_SATURATION_MIN     0
#define SWITCH_SATURATION_MAX     100

#define SWITCH_BRIGHTNESS_DEFAULT 65
#define SWITCH_BRIGHTNESS_MIN     0
#define SWITCH_BRIGHTNESS_MAX     100

#define SWITCH_OPACITY_DEFAULT    40
#define SWITCH_OPACITY_MIN        0
#define SWITCH_OPACITY_MAX        100

#define SWITCH_ZOOM_DEFAULT   0.0f
#define SWITCH_ZOOM_MIN       0.0f
#define SWITCH_ZOOM_MAX       50.0f
#define SWITCH_ZOOM_PRECISION 0.1f

#define SWITCH_ICON_DEFAULT TRUE

#define SWITCH_MINIMIZED_DEFAULT TRUE

#define SWITCH_SHOW_WINDOW_LIST_DEFAULT TRUE
#define SWITCH_TEMP_UNMINIMIZE_DEFAULT FALSE
#define SWITCH_AUTO_ROTATE_DEFAULT FALSE

#define SWITCH_RING_ALL_TRANSPARENT_DEFAULT TRUE

#define SWITCH_ELLIPSE_WIDTH_MIN	0.01f
#define SWITCH_ELLIPSE_WIDTH_MAX	1.0f
#define SWITCH_ELLIPSE_WIDTH_DEFAULT	0.6f

#define SWITCH_ELLIPSE_HEIGHT_MIN	0.01f
#define SWITCH_ELLIPSE_HEIGHT_MAX	1.0f
#define SWITCH_ELLIPSE_HEIGHT_DEFAULT	0.25f
#define SWITCH_ELLIPSE_PRECISION	0.001f

#define SWITCH_THUMBNAIL_WIDTH_MIN	10
#define SWITCH_THUMBNAIL_WIDTH_MAX	1024
#define SWITCH_THUMBNAIL_WIDTH_DEFAULT	212

#define SWITCH_THUMBNAIL_HEIGHT_MIN	10
#define SWITCH_THUMBNAIL_HEIGHT_MAX	1024
#define SWITCH_THUMBNAIL_HEIGHT_DEFAULT	192

typedef enum _SwitcherMode
{
	ApplicationSwitcher = 0,
	RingSwitcher
} SwitcherMode;

typedef enum _IconCornerMode
{
	BottomRight = 0,
	BottomLeft,
	TopRight,
	TopLeft,
	Center
} IconCornerMode;

static char *iconCornerModes[] = {
	N_("Bottom Right"),
	N_("Bottom Left"),
	N_("Top Right"),
	N_("Top Left"),
	N_("Center")
};

static char *winType[] = {
	N_("Utility"),
	N_("Dialog"),
	N_("ModalDialog"),
	N_("Fullscreen"),
	N_("Normal")
};

#define SWITCH_ICON_CORNER_DEFAULT BottomRight

#define N_ICON_CORNER_MODES (sizeof(iconCornerModes) / sizeof(iconCornerModes[0]))
#define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0]))

static int displayPrivateIndex;

#define SWITCH_DISPLAY_OPTION_NEXT				0
#define SWITCH_DISPLAY_OPTION_PREV				1
#define SWITCH_DISPLAY_OPTION_NEXT_ALL			2
#define SWITCH_DISPLAY_OPTION_PREV_ALL			3
#define SWITCH_DISPLAY_OPTION_RING_NEXT         4
#define SWITCH_DISPLAY_OPTION_RING_PREV         5
#define SWITCH_DISPLAY_OPTION_RING_NEXT_ALL     6
#define SWITCH_DISPLAY_OPTION_RING_PREV_ALL		7
#define SWITCH_DISPLAY_OPTION_NUM				8

typedef struct _SwitchDisplay
{
	int screenPrivateIndex;
	HandleEventProc handleEvent;

	CompOption opt[SWITCH_DISPLAY_OPTION_NUM];

	Atom selectWinAtom;
} SwitchDisplay;

#define SWITCH_SCREEN_OPTION_SHOW_WINDOW_LIST      	0
#define SWITCH_SCREEN_OPTION_SPEED      			1
#define SWITCH_SCREEN_OPTION_TIMESTEP      			2
#define SWITCH_SCREEN_OPTION_BRINGTOFRONT 			3
#define SWITCH_SCREEN_OPTION_AUTO_ROTATE      		4
#define SWITCH_SCREEN_OPTION_SWITCH_FOCUS_INSTANTLY	5
#define SWITCH_SCREEN_OPTION_ZOOM      				6
#define SWITCH_SCREEN_OPTION_WINDOW_TYPE  			7
#define SWITCH_SCREEN_OPTION_MINIMIZED      		8
#define SWITCH_SCREEN_OPTION_TEMP_UNMINIMIZE      	9
#define SWITCH_SCREEN_OPTION_SATURATION      		10
#define SWITCH_SCREEN_OPTION_BRIGHTNESS      		11
#define SWITCH_SCREEN_OPTION_OPACITY      			12
#define SWITCH_SCREEN_OPTION_MIPMAP      			13
#define SWITCH_SCREEN_OPTION_ICON      				14
#define SWITCH_SCREEN_OPTION_ICON_CORNER 			15
#define SWITCH_SCREEN_OPTION_ELLIPSE_WIDTH			16
#define SWITCH_SCREEN_OPTION_ELLIPSE_HEIGHT			17
#define SWITCH_SCREEN_OPTION_THUMBNAIL_WIDTH		18
#define SWITCH_SCREEN_OPTION_THUMBNAIL_HEIGHT		19
#define SWITCH_SCREEN_OPTION_RING_TRANSPARENT_ALL	20
#define SWITCH_SCREEN_OPTION_NUM      				21

typedef struct _SwitchScreen
{
	PreparePaintScreenProc preparePaintScreen;
	DonePaintScreenProc donePaintScreen;
	PaintScreenProc paintScreen;
	PaintWindowProc paintWindow;
	DamageWindowRectProc damageWindowRect;

	CompOption opt[SWITCH_SCREEN_OPTION_NUM];

	Window popupWindow;

	Window selectedWindow;
	Window zoomedWindow;
	unsigned int lastActiveNum;

	float speed;
	float timestep;
	float zoom;

	unsigned int wMask;

	int grabIndex;

	Bool switching;
	Bool zooming;

	int moreAdjust;

	GLfloat mVelocity;
	GLfloat tVelocity;
	GLfloat sVelocity;

	CompWindow **windows;
	int windowsSize;
	int nWindows;

	int pos;
	int move;

	float translate;
	float sTranslate;

	GLushort saturation;
	GLushort brightness;
	GLushort opacity;

	Bool bringToFront;
	Bool allWindows;
	Bool showWindowList;
	Bool autoRotate;
	Bool tempUnMinimize;
	SwitcherMode switchMode;
	IconCornerMode iconCorner;

	int head;

	CompWindow *prevMin;

	int insideAtom;
} SwitchScreen;

#define MwmHintsDecorations (1L << 1)

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

#define SELECT_WIN_PROP "_SWITCH_SELECT_WINDOW"

#define WIDTH  212
#define HEIGHT 192
#define SPACE  10
#define BOX_WIDTH 3

#define ELLIPSE_ROTA(outputRect) int ellipse_a = (outputRect.width*ss->opt[SWITCH_SCREEN_OPTION_ELLIPSE_WIDTH].value.f)/2.0f;
#define ELLIPSE_ROTB(outputRect) int ellipse_b = (outputRect.height*ss->opt[SWITCH_SCREEN_OPTION_ELLIPSE_HEIGHT].value.f)/2.0f;
#define ELLIPSE_ROT(outputRect) ELLIPSE_ROTA(outputRect)\
		    ELLIPSE_ROTB(outputRect)
#define DIST_ROT int dist=(4*ellipse_a)/(ss->nWindows)

static float _boxVertices[] = {
	-(WIDTH >> 1), 0,
	-(WIDTH >> 1), BOX_WIDTH,
	(WIDTH >> 1), BOX_WIDTH,
	(WIDTH >> 1), 0,

	-(WIDTH >> 1), BOX_WIDTH,
	-(WIDTH >> 1), HEIGHT - BOX_WIDTH,
	-(WIDTH >> 1) + BOX_WIDTH, HEIGHT - BOX_WIDTH,
	-(WIDTH >> 1) + BOX_WIDTH, 0,

	(WIDTH >> 1) - BOX_WIDTH, BOX_WIDTH,
	(WIDTH >> 1) - BOX_WIDTH, HEIGHT - BOX_WIDTH,
	(WIDTH >> 1), HEIGHT - BOX_WIDTH,
	(WIDTH >> 1), 0,

	-(WIDTH >> 1), HEIGHT - BOX_WIDTH,
	-(WIDTH >> 1), HEIGHT,
	(WIDTH >> 1), HEIGHT,
	(WIDTH >> 1), HEIGHT - BOX_WIDTH
};

#define WINDOW_WIDTH(count) (WIDTH * (count) + (SPACE << 1))
#define WINDOW_HEIGHT (HEIGHT + (SPACE << 1))

#define GET_SWITCH_DISPLAY(d)                       \
    ((SwitchDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define SWITCH_DISPLAY(d)               \
    SwitchDisplay *sd = GET_SWITCH_DISPLAY (d)

#define GET_SWITCH_SCREEN(s, sd)                   \
    ((SwitchScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr)

#define SWITCH_SCREEN(s)                              \
    SwitchScreen *ss = GET_SWITCH_SCREEN (s, GET_SWITCH_DISPLAY (s->display))

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

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

	SWITCH_SCREEN(screen);

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

	switch (index)
	{
	case SWITCH_SCREEN_OPTION_SPEED:
		if (compSetFloatOption(o, value))
		{
			ss->speed = o->value.f;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_TIMESTEP:
		if (compSetFloatOption(o, value))
		{
			ss->timestep = o->value.f;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_WINDOW_TYPE:
		if (compSetOptionList(o, value))
		{
			ss->wMask = compWindowTypeMaskFromStringList(&o->value);
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_MIPMAP:
	case SWITCH_SCREEN_OPTION_ICON:
	case SWITCH_SCREEN_OPTION_MINIMIZED:
	case SWITCH_SCREEN_OPTION_SWITCH_FOCUS_INSTANTLY:
	case SWITCH_SCREEN_OPTION_RING_TRANSPARENT_ALL :
		if (compSetBoolOption(o, value))
			return TRUE;
		break;
	case SWITCH_SCREEN_OPTION_SHOW_WINDOW_LIST:
		if (compSetBoolOption(o, value))
		{
			ss->showWindowList = o->value.b;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_AUTO_ROTATE:
		if (compSetBoolOption(o, value))
		{
			ss->autoRotate = o->value.b;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_TEMP_UNMINIMIZE:
		if (compSetBoolOption(o, value))
		{
			ss->tempUnMinimize = o->value.b;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_ICON_CORNER:
		if (compSetStringOption(o, value))
		{
			int i;
			ss->iconCorner = SWITCH_ICON_CORNER_DEFAULT;

			for (i = 0; i < o->rest.s.nString; i++)
				if (strcmp(iconCornerModes[i], o->value.s) == 0)
					ss->iconCorner = (IconCornerMode) i;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_SATURATION:
		if (compSetIntOption(o, value))
		{
			ss->saturation = (COLOR * o->value.i) / 100;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_BRIGHTNESS:
		if (compSetIntOption(o, value))
		{
			ss->brightness = (0xffff * o->value.i) / 100;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_OPACITY:
		if (compSetIntOption(o, value))
		{
			ss->opacity = (OPAQUE * o->value.i) / 100;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_BRINGTOFRONT:
		if (compSetBoolOption(o, value))
		{
			ss->bringToFront = o->value.b;
			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_ZOOM:
		if (compSetFloatOption(o, value))
		{
			if (o->value.f < 0.05f)
			{
				ss->zooming = FALSE;
				ss->zoom = 0.0f;
			}
			else
			{
				ss->zooming = TRUE;
				ss->zoom = o->value.f / 30.0f;
			}

			return TRUE;
		}
		break;
	case SWITCH_SCREEN_OPTION_ELLIPSE_WIDTH:
	case SWITCH_SCREEN_OPTION_ELLIPSE_HEIGHT:
		if(compSetFloatOption(o, value))
			return TRUE;
		break;
	case SWITCH_SCREEN_OPTION_THUMBNAIL_WIDTH:
	case SWITCH_SCREEN_OPTION_THUMBNAIL_HEIGHT:
		if(compSetIntOption(o, value))
			return TRUE;
		break;
	default:
		break;
	}

	return FALSE;
}

static void switchScreenInitOptions(SwitchScreen * ss)
{
	CompOption *o;
	int i;

	o = &ss->opt[SWITCH_SCREEN_OPTION_SHOW_WINDOW_LIST];
	o->advanced = False;
	o->name = "show_window_list";
	o->group = N_("Misc. Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Show Window List");
	o->longDesc = N_("Show the Window List box in center of the screen.");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_SHOW_WINDOW_LIST_DEFAULT;

	o = &ss->opt[SWITCH_SCREEN_OPTION_SPEED];
	o->advanced = False;
	o->name = "speed";
	o->group = N_("Misc. Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Speed");
	o->longDesc = N_("How fast the Switcher moves thumbnails.");
	o->type = CompOptionTypeFloat;
	o->value.f = SWITCH_SPEED_DEFAULT;
	o->rest.f.min = SWITCH_SPEED_MIN;
	o->rest.f.max = SWITCH_SPEED_MAX;
	o->rest.f.precision = SWITCH_SPEED_PRECISION;

	o = &ss->opt[SWITCH_SCREEN_OPTION_TIMESTEP];
	o->advanced = False;
	o->name = "timestep";
	o->group = N_("Misc. Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Switcher Timestep");
	o->longDesc = N_("Switcher Timestep.");
	o->type = CompOptionTypeFloat;
	o->value.f = SWITCH_TIMESTEP_DEFAULT;
	o->rest.f.min = SWITCH_TIMESTEP_MIN;
	o->rest.f.max = SWITCH_TIMESTEP_MAX;
	o->rest.f.precision = SWITCH_TIMESTEP_PRECISION;

	o = &ss->opt[SWITCH_SCREEN_OPTION_WINDOW_TYPE];
	o->advanced = False;
	o->name = "window_types";
	o->group = N_("Behaviour");
	o->subGroup = N_("Window handling");
	o->displayHints = "";
	o->shortDesc = N_("Window Types");
	o->longDesc = N_("Window Types that should be shown in Switcher.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = N_WIN_TYPE;
	o->value.list.value = malloc(sizeof(CompOptionValue) * N_WIN_TYPE);
	for (i = 0; i < N_WIN_TYPE; i++)
		o->value.list.value[i].s = strdup(winType[i]);
	o->rest.s.string = (char **)windowTypeString;
	o->rest.s.nString = nWindowTypeString;

	ss->wMask = compWindowTypeMaskFromStringList(&o->value);

	o = &ss->opt[SWITCH_SCREEN_OPTION_MIPMAP];
	o->advanced = False;
	o->name = "mipmap";
	o->group = N_("Appearance");
	o->subGroup = N_("Quality Settings");
	o->displayHints = "";
	o->shortDesc = N_("Mipmap");
	o->longDesc =
			N_("Generate Mipmaps when possible for higher quality "
			   "Scaling.");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_MIPMAP_DEFAULT;

	o = &ss->opt[SWITCH_SCREEN_OPTION_SATURATION];
	o->advanced = False;
	o->name = "saturation";
	o->group = N_("Appearance");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Saturation");
	o->longDesc = N_("Saturation of unfocused windows in percent.");
	o->type = CompOptionTypeInt;
	o->value.i = SWITCH_SATURATION_DEFAULT;
	o->rest.i.min = SWITCH_SATURATION_MIN;
	o->rest.i.max = SWITCH_SATURATION_MAX;

	o = &ss->opt[SWITCH_SCREEN_OPTION_BRIGHTNESS];
	o->advanced = False;
	o->name = "brightness";
	o->group = N_("Appearance");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Brightness");
	o->longDesc = N_("Brightness of the background desktop in percent.");
	o->type = CompOptionTypeInt;
	o->value.i = SWITCH_BRIGHTNESS_DEFAULT;
	o->rest.i.min = SWITCH_BRIGHTNESS_MIN;
	o->rest.i.max = SWITCH_BRIGHTNESS_MAX;

	o = &ss->opt[SWITCH_SCREEN_OPTION_OPACITY];
	o->advanced = False;
	o->name = "opacity";
	o->group = N_("Appearance");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Opacity");
	o->longDesc = N_("Opacity of unfocused windows in percent.");
	o->type = CompOptionTypeInt;
	o->value.i = SWITCH_OPACITY_DEFAULT;
	o->rest.i.min = SWITCH_OPACITY_MIN;
	o->rest.i.max = SWITCH_OPACITY_MAX;

	o = &ss->opt[SWITCH_SCREEN_OPTION_BRINGTOFRONT];
	o->advanced = False;
	o->name = "bring_to_front";
	o->group = N_("Behaviour");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Bring To Front");
	o->longDesc = N_("Bring selected window to Front.");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_BRINGTOFRONT_DEFAULT;

	o = &ss->opt[SWITCH_SCREEN_OPTION_ICON_CORNER];
	o->advanced = False;
	o->name = "icon_corner_mode";
	o->group = N_("Appearance");
	o->subGroup = N_("Icon Options");
	o->displayHints = "";
	o->shortDesc = N_("Icon Corner");
	o->longDesc = N_("Icon Placement.");
	o->type = CompOptionTypeString;
	o->value.s = strdup(iconCornerModes[SWITCH_ICON_CORNER_DEFAULT]);
	o->rest.s.string = iconCornerModes;
	o->rest.s.nString = N_ICON_CORNER_MODES;

	o = &ss->opt[SWITCH_SCREEN_OPTION_ZOOM];
	o->advanced = False;
	o->name = "zoom";
	o->group = N_("Behaviour");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Zoom");
	o->longDesc = N_("Distance desktop should be Zoom out while "
					 "Switching windows.");
	o->type = CompOptionTypeFloat;
	o->value.f = SWITCH_ZOOM_DEFAULT;
	o->rest.f.min = SWITCH_ZOOM_MIN;
	o->rest.f.max = SWITCH_ZOOM_MAX;
	o->rest.f.precision = SWITCH_ZOOM_PRECISION;

	o = &ss->opt[SWITCH_SCREEN_OPTION_ICON];
	o->advanced = False;
	o->name = "icon";
	o->group = N_("Appearance");
	o->subGroup = N_("Icon Options");
	o->displayHints = "";
	o->shortDesc = N_("Icon");
	o->longDesc = N_("Show Icon next to thumbnail.");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_ICON_DEFAULT;

	o = &ss->opt[SWITCH_SCREEN_OPTION_MINIMIZED];
	o->advanced = False;
	o->name = "minimized";
	o->group = N_("Behaviour");
	o->subGroup = N_("Window handling");
	o->displayHints = "";
	o->shortDesc = N_("Show Minimized");
	o->longDesc = N_("Show Minimized windows.");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_MINIMIZED_DEFAULT;


	o = &ss->opt[SWITCH_SCREEN_OPTION_AUTO_ROTATE];
	o->advanced = False;
	o->name = "auto_rotate";
	o->group = N_("Behaviour");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Auto Rotate");
	o->longDesc = N_("Rotate to active window Automatically.");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_AUTO_ROTATE_DEFAULT;

	o = &ss->opt[SWITCH_SCREEN_OPTION_TEMP_UNMINIMIZE];
	o->advanced = False;
	o->name = "temp_unminimize";
	o->group = N_("Behaviour");
	o->subGroup = N_("Window handling");
	o->displayHints = "";
	o->shortDesc = N_("Temp UnMinimize");
	o->longDesc = N_("Temporarily Un-Minimize minimized windows.");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_TEMP_UNMINIMIZE_DEFAULT;

	o = &ss->opt[SWITCH_SCREEN_OPTION_SWITCH_FOCUS_INSTANTLY];
	o->advanced = False;
	o->name = "switch_focus_instantly";
	o->group = N_("Behaviour");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Switch Focus Instantly");
	o->longDesc = N_("If enabled, the Focus is Switched to the window immediately"
					 "on activation; otherwise on Application Switcher termination.");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_SWITCH_FOCUS_INSTANTLY_DEFAULT;


	o = &ss->opt[SWITCH_SCREEN_OPTION_ELLIPSE_WIDTH];
	o->advanced = False;
	o->name = "ellipse_width";
	o->group = N_("Appearance");
	o->subGroup = N_("Rotating List");
	o->displayHints = "";
	o->shortDesc = N_("Ellipse Width");
	o->longDesc = N_("Changes the proportions of the circle (1.0 means "
					 "the width is 100% of the screen width).");
	o->type = CompOptionTypeFloat;
	o->value.f = SWITCH_ELLIPSE_WIDTH_DEFAULT;
	o->rest.f.min = SWITCH_ELLIPSE_WIDTH_MIN;
	o->rest.f.max = SWITCH_ELLIPSE_WIDTH_MAX;
	o->rest.f.precision = SWITCH_ELLIPSE_PRECISION;

	o = &ss->opt[SWITCH_SCREEN_OPTION_ELLIPSE_HEIGHT];
	o->advanced = False;
	o->name = "ellipse_height";
	o->group = N_("Appearance");
	o->subGroup = N_("Rotating List");
	o->displayHints = "";
	o->shortDesc = N_("Ellipse Height");
	o->longDesc = N_("Changes the proportions of the circle (1.0 means "
					 "the height is 100% of the screen height).");
	o->type = CompOptionTypeFloat;
	o->value.f = SWITCH_ELLIPSE_HEIGHT_DEFAULT;
	o->rest.f.min = SWITCH_ELLIPSE_HEIGHT_MIN;
	o->rest.f.max = SWITCH_ELLIPSE_HEIGHT_MAX;
	o->rest.f.precision = SWITCH_ELLIPSE_PRECISION;

	o = &ss->opt[SWITCH_SCREEN_OPTION_THUMBNAIL_WIDTH];
	o->advanced = False;
	o->name = "thumbnail_width";
	o->group = N_("Appearance");
	o->subGroup = N_("Rotating List");
	o->displayHints = "";
	o->shortDesc = N_("Thumbnail Width");
	o->longDesc = N_("Changes the horizontal size of the unscaled Thumbnail.");
	o->type = CompOptionTypeInt;
	o->value.i = SWITCH_THUMBNAIL_WIDTH_DEFAULT;
	o->rest.i.min = SWITCH_THUMBNAIL_WIDTH_MIN;
	o->rest.i.max = SWITCH_THUMBNAIL_WIDTH_MAX;

	o = &ss->opt[SWITCH_SCREEN_OPTION_THUMBNAIL_HEIGHT];
	o->advanced = False;
	o->name = "thumbnail_height";
	o->group = N_("Appearance");
	o->subGroup = N_("Rotating List");
	o->displayHints = "";
	o->shortDesc = N_("Thumbnail Height");
	o->longDesc = N_("Changes the vertical size of the unscaled Thumbnail.");
	o->type = CompOptionTypeInt;
	o->value.i = SWITCH_THUMBNAIL_HEIGHT_DEFAULT;
	o->rest.i.min = SWITCH_THUMBNAIL_HEIGHT_MIN;
	o->rest.i.max = SWITCH_THUMBNAIL_HEIGHT_MAX;
	
	o = &ss->opt[SWITCH_SCREEN_OPTION_RING_TRANSPARENT_ALL];
	o->advanced = False;
	o->name = "ring_transparent_all";
	o->group = N_("Appearance");
	o->subGroup = N_("Rotating List");
	o->displayHints = "";
	o->shortDesc = N_("Fade windows out when using Ring Switcher");
	o->longDesc = N_("When using ring switcher mode fade actual windows out");
	o->type = CompOptionTypeBool;
	o->value.b = SWITCH_RING_ALL_TRANSPARENT_DEFAULT;

}

static CompOption *switchGetScreenOptions(CompScreen * screen, int *count)
{
	if (screen)
	{
		SWITCH_SCREEN(screen);

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

		switchScreenInitOptions(ss);
		*count = NUM_OPTIONS(ss);
		return ss->opt;
	}
}

static void setSelectedWindowHint(CompScreen * s)
{
	SWITCH_DISPLAY(s->display);
	SWITCH_SCREEN(s);

	/* if we don't set this property, the switcher window won't be
       decorated by the DM - exactly what we want for the ring switcher */

	if (ss->switchMode == ApplicationSwitcher)
	{
		XChangeProperty(s->display->display, ss->popupWindow,
						sd->selectWinAtom, XA_WINDOW, 32, PropModeReplace,
						(unsigned char *)&ss->selectedWindow, 1);
	}
	else
	{
		XDeleteProperty(s->display->display, ss->popupWindow,
				sd->selectWinAtom);
	}
}

static Bool isSwitchWin(CompWindow * w)
{
	SWITCH_SCREEN(w->screen);

	if (w->state & CompWindowStateOffscreenMask)
		return FALSE;

	if (!w->mapNum || w->attrib.map_state != IsViewable)
	{
		if (ss->opt[SWITCH_SCREEN_OPTION_MINIMIZED].value.b)
		{
			if (!((w->minimized || w->inShowDesktopMode)
				  && (w->state & CompWindowStateHiddenMask)) && !w->shaded)
				return FALSE;
		}
		else
		{
			return FALSE;
		}
	}

	if (w->attrib.override_redirect)
		return FALSE;

	if (!(ss->wMask & w->type))
		return FALSE;

	if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
		return FALSE;

	if (w->state & CompWindowStateSkipTaskbarMask)
		return FALSE;

	if (!ss->allWindows)
	{
		if (!w->mapNum || w->attrib.map_state != IsViewable)
		{
			if (w->serverX + w->width <= 0 ||
				w->serverY + w->height <= 0 ||
				w->serverX >= w->screen->width ||
				w->serverY >= w->screen->height)
				return FALSE;
		}
		else
		{
			if (!(*w->screen->focusWindow) (w))
				return FALSE;
		}
	}

	return TRUE;
}

static int compareWindows(const void *elem1, const void *elem2)
{
	CompWindow *w1 = *((CompWindow **) elem1);
	CompWindow *w2 = *((CompWindow **) elem2);

	if (w1->mapNum && !w2->mapNum)
		return -1;

	if (w2->mapNum && !w1->mapNum)
		return 1;

	return w2->activeNum - w1->activeNum;
}

static void switchAddWindowToList(CompScreen * s, CompWindow * w)
{
	SWITCH_SCREEN(s);

	if (ss->windowsSize <= ss->nWindows)
	{
		ss->windows = realloc(ss->windows,
							  sizeof(CompWindow *) * (ss->nWindows + 32));
		if (!ss->windows)
			return;

		ss->windowsSize = ss->nWindows + 32;
	}

	ss->windows[ss->nWindows++] = w;
}

static void switchUpdateWindowList(CompScreen * s, int count)
{
	SWITCH_SCREEN(s);

	if (count > 1)
	{
		count -= (count + 1) & 1;
		if (count < 3)
			count = 3;
	}

	if (ss->switchMode == RingSwitcher)
	{
		XRectangle outputRect;
		screenGetOutputDevRect(s, ss->head, &outputRect);
		ELLIPSE_ROTA(outputRect);
		ss->pos = ellipse_a;
	}
	else
	{
		ss->pos = ((count >> 1) - ss->nWindows) * WIDTH;
	}

	ss->move = 0;

	ss->selectedWindow = ss->windows[0]->id;

	if (ss->popupWindow)
	{
		XRectangle outputRect;

		screenGetOutputDevRect(s, ss->head, &outputRect);

		XMoveResizeWindow(s->display->display, ss->popupWindow,
				outputRect.x + outputRect.width / 2 - WINDOW_WIDTH(count) / 2,
				outputRect.y + outputRect.height / 2 - WINDOW_HEIGHT / 2,
				WINDOW_WIDTH(count), WINDOW_HEIGHT);
	}

}

static void switchCreateWindowList(CompScreen * s, int count)
{
	CompWindow *w;

	SWITCH_SCREEN(s);

	ss->nWindows = 0;

	for (w = s->windows; w; w = w->next)
	{
		if (isSwitchWin(w))
			switchAddWindowToList(s, w);
	}

	qsort(ss->windows, ss->nWindows, sizeof(CompWindow *), compareWindows);

	if ((ss->switchMode == ApplicationSwitcher) && (ss->nWindows == 2))
	{
		switchAddWindowToList(s, ss->windows[0]);
		switchAddWindowToList(s, ss->windows[1]);
	}

	switchUpdateWindowList(s, count);
}

static void switchToWindow(CompScreen * s, Bool toNext)
{
	CompWindow *w;
	int cur;

	SWITCH_SCREEN(s);

	if (!ss->grabIndex)
		return;

	for (cur = 0; cur < ss->nWindows; cur++)
	{
		if (ss->windows[cur]->id == ss->selectedWindow)
			break;
	}

	if (cur == ss->nWindows)
		return;

	if (toNext)
		w = ss->windows[(cur + 1) % ss->nWindows];
	else
		w = ss->windows[(cur + ss->nWindows - 1) % ss->nWindows];

	if (w)
	{
		Window old = ss->selectedWindow;

		ss->lastActiveNum = w->activeNum;
		ss->selectedWindow = w->id;

		if (!ss->zoomedWindow)
			ss->zoomedWindow = ss->selectedWindow;

		if (old != w->id)
		{
			if (toNext)
			{
				if (ss->switchMode == RingSwitcher)
				{
					XRectangle outputRect;
					screenGetOutputDevRect(s, ss->head, &outputRect);
					ELLIPSE_ROTA(outputRect);
					DIST_ROT;
					ss->move -= dist;
				}
				else
				{
					ss->move -= WIDTH;
				}
			}
			else
			{
				if (ss->switchMode == RingSwitcher)
				{
					XRectangle outputRect;
					screenGetOutputDevRect(s, ss->head, &outputRect);
					ELLIPSE_ROTA(outputRect);
					DIST_ROT;
					ss->move += dist;
				}
				else
				{
					ss->move += WIDTH;
				}
			}
			ss->moreAdjust = 1;
		}

		if (ss->popupWindow)
		{
			CompWindow *popup;

			popup = findWindowAtScreen(s, ss->popupWindow);
			if (popup)
				addWindowDamage(popup);

			setSelectedWindowHint(s);
		}


		if (ss->tempUnMinimize)
		{
			if (ss->prevMin != NULL)
			{
				minimizeWindow(ss->prevMin);
				ss->prevMin = NULL;
			}

			if (w->minimized)
				ss->prevMin = w;
		}

		if (ss->autoRotate)
			changeToWindowViewport(s, ss->selectedWindow, w);
		if (ss->tempUnMinimize && w->minimized)
			unminimizeWindow(w);

		if (ss->opt[SWITCH_SCREEN_OPTION_SWITCH_FOCUS_INSTANTLY].value.b)
			sendWindowActivationRequest(w->screen, w->id);

		addWindowDamage(w);

		if ((w = findWindowAtScreen(s, old)))
		{
			addWindowDamage(w);
		}
	}
}

static int switchCountWindows(CompScreen * s)
{
	CompWindow *w;
	int count = 0;
	XRectangle outputRect;

	//    SWITCH_SCREEN(s);

	for (w = s->windows; w && count < 5; w = w->next)
		if (isSwitchWin(w))
			count++;

	int head = screenGetCurrentOutputDev(s);

	screenGetOutputDevRect(s, head, &outputRect);

	if (count == 5 && outputRect.width <= WINDOW_WIDTH(5))
		count = 3;

	return count;
}

static Visual *findArgbVisual(Display * dpy, int scr)
{
	XVisualInfo *xvi;
	XVisualInfo template;
	int nvi;
	int i;
	XRenderPictFormat *format;
	Visual *visual;

	template.screen = scr;
	template.depth = 32;
	template.class = TrueColor;

	xvi = XGetVisualInfo(dpy,
						 VisualScreenMask |
						 VisualDepthMask | VisualClassMask, &template, &nvi);
	if (!xvi)
		return 0;

	visual = 0;
	for (i = 0; i < nvi; i++)
	{
		format = XRenderFindVisualFormat(dpy, xvi[i].visual);
		if (format->type == PictTypeDirect && format->direct.alphaMask)
		{
			visual = xvi[i].visual;
			break;
		}
	}

	XFree(xvi);

	return visual;
}

static void switchUpdatePopupWindow(CompScreen *s, int head,
		int count, SwitcherMode switchMode)
{
	SWITCH_SCREEN(s);
	Display *dpy = s->display->display;

	if ((switchMode != ss->switchMode) && ss->popupWindow)
	{
		XDestroyWindow(s->display->display, ss->popupWindow);
		ss->popupWindow = None;
	}

	if ((!ss->popupWindow && ss->showWindowList) || head != ss->head)
	{
		XRectangle outputRect;
		XSizeHints xsh;
		XWMHints xwmh;
		XSetWindowAttributes attr;
		Visual *visual;
		Atom mwmHintsAtom;
		MwmHints mwmHints;
		Atom type;
		Atom state[5];
		int nState = 0;

		visual = findArgbVisual(dpy, s->screenNum);
		if (!visual)
			return;

		if (count > 1)
		{
			count -= (count + 1) & 1;
			if (count < 3)
				count = 3;
		}

		xsh.flags = PSize | PPosition;
		xsh.width = WINDOW_WIDTH(count);
		xsh.height = WINDOW_HEIGHT;

		xwmh.flags = InputHint;
		xwmh.input = 0;

		attr.background_pixel = 0;
		attr.border_pixel = 0;
		attr.colormap = XCreateColormap(dpy, s->root, visual, AllocNone);

		if (ss->showWindowList)
		{
			if (ss->popupWindow)
			{
				XDestroyWindow(s->display->display, ss->popupWindow);
				ss->popupWindow = None;
			}

			screenGetOutputDevRect(s, head, &outputRect);

			ss->popupWindow =
					XCreateWindow(dpy, s->root,
								  outputRect.x + outputRect.width / 2 - xsh.width / 2,
								  outputRect.y + outputRect.height / 2 - xsh.height / 2,
								  xsh.width, xsh.height, 0, 32, InputOutput, visual,
								  CWBackPixel | CWBorderPixel | CWColormap, &attr);

			XSetWMProperties(dpy, ss->popupWindow, NULL, NULL,
							 getProgramArgv(),
							 getProgramArgc(), &xsh, &xwmh, NULL);

			mwmHintsAtom = XInternAtom(dpy, "_MOTIF_WM_HINTS", 0);

			memset(&mwmHints, 0, sizeof(mwmHints));

			mwmHints.flags = MwmHintsDecorations;
			mwmHints.decorations = 0;

			XChangeProperty(dpy, ss->popupWindow, mwmHintsAtom,
							mwmHintsAtom, 8, PropModeReplace,
							(unsigned char *)&mwmHints, sizeof(mwmHints));

			type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH", 0);
			XChangeProperty(dpy, ss->popupWindow,
							XInternAtom(dpy,
										"_NET_WM_WINDOW_TYPE",
										0), XA_ATOM, 32,
							PropModeReplace, (unsigned char *)&type, 1);

			setWindowProp(s->display, ss->popupWindow,
						  s->display->winDesktopAtom, 0xffffffff);

			state[nState++] = s->display->winStateAboveAtom;
			state[nState++] = s->display->winStateStickyAtom;
			state[nState++] = s->display->winStateSkipTaskbarAtom;
			state[nState++] = s->display->winStateSkipPagerAtom;

			if (switchMode == RingSwitcher)
				state[nState++] = s->display->winStateFullscreenAtom;

			XChangeProperty(dpy, ss->popupWindow,
					XInternAtom(dpy, "_NET_WM_STATE", 0), XA_ATOM, 32,
					PropModeReplace, (unsigned char *)state, nState);
		}
	}
}

static void switchInitiate(CompScreen * s, Bool allWindows, SwitcherMode switchMode)
{
	int count;

	SWITCH_SCREEN(s);

	if (otherScreenGrabExist(s, "scale", "cube", 0))
		return;

	ss->allWindows = allWindows;

	count = switchCountWindows(s);
	if (count < 1)
		return;

	int head = screenGetCurrentOutputDev(s);

	switchUpdatePopupWindow(s, head, count, switchMode);

	ss->switchMode = switchMode;
	ss->head = head;

	if (!ss->showWindowList && ss->popupWindow)
	{
		XUnmapWindow(s->display->display, ss->popupWindow);
	}

	if (!ss->grabIndex)
		ss->grabIndex = pushScreenGrab(s, s->invisibleCursor, "switcher");

	if (ss->grabIndex)
	{
		if (!ss->switching)
		{
			ss->lastActiveNum = s->activeNum;

			switchCreateWindowList(s, count);

			ss->sTranslate = ss->zoom;

			if (ss->showWindowList && ss->popupWindow)
			{
				CompWindow *w;

				w = findWindowAtScreen(s, ss->popupWindow);
				if (w && (w->state & CompWindowStateHiddenMask))
				{
					w->hidden = FALSE;
					showWindow(w);
				}
				else
				{
					XMapWindow(s->display->display, ss->popupWindow);
				}

				setSelectedWindowHint(s);
			}
		}

		damageScreen(s);

		ss->switching = TRUE;
		ss->moreAdjust = 1;
	}
}

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

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


	for (s = d->screens; s; s = s->next)
	{
		SWITCH_SCREEN(s);

		if (xid && s->root != xid)
			continue;

		if (ss->tempUnMinimize)
			ss->prevMin = NULL;

		if (ss->grabIndex)
		{
			CompWindow *w;

			if (ss->popupWindow)
			{
				w = findWindowAtScreen(s, ss->popupWindow);
				if (w && w->managed && w->mapNum)
				{
					w->hidden = TRUE;
					hideWindow(w);
				}
				else
				{
					XUnmapWindow(s->display->display, ss->popupWindow);
				}
			}

			ss->switching = FALSE;

			if (!ss->opt[SWITCH_SCREEN_OPTION_SWITCH_FOCUS_INSTANTLY].value.b)
			{
				if (state && ss->selectedWindow)
				{
					w = findWindowAtScreen(s, ss->selectedWindow);
					if (w)
						sendWindowActivationRequest(w->screen, w->id);
				}
			}

			removeScreenGrab(s, ss->grabIndex, 0);
			ss->grabIndex = 0;

			if (!ss->zooming)
			{
				ss->selectedWindow = None;
				ss->zoomedWindow = None;
			}
			else
			{
				ss->moreAdjust = 1;
			}

			ss->lastActiveNum = 0;

			damageScreen(s);
		}
	}

	if (action)
		action->state &=
				~(CompActionStateTermKey | CompActionStateTermButton);


	return FALSE;
}

static Bool
switchDoSwitch(CompDisplay * d, CompAction * action, CompActionState state,
		CompOption * option, int nOption, Bool next, Bool allWindows, SwitcherMode switchMode)
{
	CompScreen *s;
	Window xid;

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

	s = findScreenAtDisplay(d, xid);
	if (s)
	{
		SWITCH_SCREEN(s);

		if (!ss->switching)
		{
			switchInitiate(s, allWindows, switchMode);

			if (state & CompActionStateInitKey)
				action->state |= CompActionStateTermKey;

			if (state & CompActionStateInitButton)
				action->state |= CompActionStateTermButton;
		}

		switchToWindow(s, next);
	}

	return FALSE;
}

static Bool
switchNext(CompDisplay * d,
		   CompAction * action,
		   CompActionState state, CompOption * option, int nOption)
{
	return switchDoSwitch(d, action, state, option, nOption, TRUE, FALSE, ApplicationSwitcher);
}

static Bool
switchPrev(CompDisplay * d,
		   CompAction * action,
		   CompActionState state, CompOption * option, int nOption)
{
	return switchDoSwitch(d, action, state, option, nOption, FALSE, FALSE, ApplicationSwitcher);
}
static Bool
switchNextAll(CompDisplay * d,
		      CompAction * action,
		      CompActionState state, CompOption * option, int nOption)
{
	return switchDoSwitch(d, action, state, option, nOption, TRUE, TRUE, ApplicationSwitcher);
}
static Bool
switchPrevAll(CompDisplay * d,
		      CompAction * action,
		      CompActionState state, CompOption * option, int nOption)
{
	return switchDoSwitch(d, action, state, option, nOption, FALSE, TRUE, ApplicationSwitcher);
}
static Bool
switchNextRing(CompDisplay * d,
		       CompAction * action,
		       CompActionState state, CompOption * option, int nOption)
{
	return switchDoSwitch(d, action, state, option, nOption, TRUE, FALSE, RingSwitcher);
}
static Bool
switchPrevRing(CompDisplay * d,
			   CompAction * action,
		       CompActionState state, CompOption * option, int nOption)
{
	return switchDoSwitch(d, action, state, option, nOption, FALSE, FALSE, RingSwitcher);
}
static Bool
switchNextAllRing(CompDisplay * d,
				  CompAction * action,
				  CompActionState state, CompOption * option, int nOption)
{
	return switchDoSwitch(d, action, state, option, nOption, TRUE, TRUE, RingSwitcher);
}
static Bool
switchPrevAllRing(CompDisplay * d,
				  CompAction * action,
				  CompActionState state, CompOption * option, int nOption)
{
	return switchDoSwitch(d, action, state, option, nOption, FALSE, TRUE, RingSwitcher);
}

static void switchWindowRemove(CompDisplay * d, Window id)
{
	CompWindow *w;

	w = findWindowAtDisplay(d, id);
	if (w)
	{
		Bool inList = FALSE;
		int count, j, i = 0;
		Window selected, old;

		SWITCH_SCREEN(w->screen);

		if (isSwitchWin(w))
			return;

		old = selected = ss->selectedWindow;

		while (i < ss->nWindows)
		{
			if (ss->windows[i] == w)
			{
				inList = TRUE;

				if (w->id == selected)
				{
					if (i < ss->nWindows)
						selected = ss->windows[i + 1]->id;
					else
						selected = ss->windows[0]->id;
				}

				ss->nWindows--;
				for (j = i; j < ss->nWindows; j++)
					ss->windows[j] = ss->windows[j + 1];
			}
			else
			{
				i++;
			}
		}

		if (!inList)
			return;

		count = ss->nWindows;

		if (ss->nWindows == 2)
		{
			if (ss->windows[0] == ss->windows[1])
			{
				ss->nWindows--;
				count = 1;
			}
			else
			{
				switchAddWindowToList(w->screen, ss->windows[0]);
				switchAddWindowToList(w->screen, ss->windows[1]);
			}
		}

		if (ss->nWindows == 0)
		{
			CompOption o;

			o.type = CompOptionTypeInt;
			o.name = "root";
			o.value.i = w->screen->root;

			switchTerminate(d, NULL, 0, &o, 1);
			return;
		}

		if (!ss->grabIndex)
			return;

		switchUpdateWindowList(w->screen, count);

		for (i = 0; i < ss->nWindows; i++)
		{
			ss->selectedWindow = ss->windows[i]->id;

			if (ss->selectedWindow == selected)
				break;

			ss->pos -= WIDTH;
			if (ss->pos < -ss->nWindows * WIDTH)
				ss->pos += ss->nWindows * WIDTH;
		}

		if (ss->popupWindow)
		{
			CompWindow *popup;

			popup = findWindowAtScreen(w->screen, ss->popupWindow);
			if (popup)
				addWindowDamage(popup);

			setSelectedWindowHint(w->screen);
		}

		if (old != ss->selectedWindow)
		{
			addWindowDamage(w);

			w = findWindowAtScreen(w->screen, old);
			if (w)
				addWindowDamage(w);

			ss->moreAdjust = 1;
		}
	}
}

static void switchHandleEvent(CompDisplay * d, XEvent * event)
{
	SWITCH_DISPLAY(d);

	UNWRAP(sd, d, handleEvent);
	(*d->handleEvent) (d, event);
	WRAP(sd, d, handleEvent, switchHandleEvent);

	switch (event->type)
	{
	case UnmapNotify:
		switchWindowRemove(d, event->xunmap.window);
		break;
	case DestroyNotify:
		switchWindowRemove(d, event->xdestroywindow.window);
	default:
		break;
	}
}

static int adjustSwitchVelocity(CompScreen * s)
{
	float dx, adjust, amount;

	SWITCH_SCREEN(s);

	dx = ss->move;

	adjust = dx * 0.15f;
	amount = fabs(dx) * 1.5f;
	if (amount < 0.2f)
		amount = 0.2f;
	else if (amount > 2.0f)
		amount = 2.0f;

	ss->mVelocity = (amount * ss->mVelocity + adjust) / (amount + 1.0f);

	if (ss->zooming)
	{
		float dt, ds;

		if (ss->switching)
			dt = ss->zoom - ss->translate;
		else
			dt = 0.0f - ss->translate;

		adjust = dt * 0.15f;
		amount = fabs(dt) * 1.5f;
		if (amount < 0.2f)
			amount = 0.2f;
		else if (amount > 2.0f)
			amount = 2.0f;

		ss->tVelocity = (amount * ss->tVelocity + adjust) / (amount + 1.0f);

		if (ss->selectedWindow == ss->zoomedWindow)
			ds = ss->zoom - ss->sTranslate;
		else
			ds = 0.0f - ss->sTranslate;

		adjust = ds * 0.5f;
		amount = fabs(ds) * 5.0f;
		if (amount < 1.0f)
			amount = 1.0f;
		else if (amount > 6.0f)
			amount = 6.0f;

		ss->sVelocity = (amount * ss->sVelocity + adjust) / (amount + 1.0f);

		if (ss->selectedWindow == ss->zoomedWindow)
		{
			if (fabs(dx) < 0.1f && fabs(ss->mVelocity) < 0.2f
				&& fabs(dt) < 0.002f
				&& fabs(ss->tVelocity) < 0.002f
				&& fabs(ds) < 0.002f && fabs(ss->sVelocity) < 0.002f)
			{
				ss->mVelocity = ss->tVelocity = ss->sVelocity = 0.0f;
				return 0;
			}
		}
	}
	else
	{
		if (fabs(dx) < 0.1f && fabs(ss->mVelocity) < 0.2f)
		{
			ss->mVelocity = 0.0f;
			return 0;
		}
	}

	return 1;
}

static void switchPreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
	SWITCH_SCREEN(s);

	if (ss->moreAdjust)
	{
		int steps, m;
		float amount, chunk;

		amount = msSinceLastPaint * 0.05f * ss->speed;
		steps = amount / (0.5f * ss->timestep);
		if (!steps)
			steps = 1;
		chunk = amount / (float)steps;

		while (steps--)
		{
			ss->moreAdjust = adjustSwitchVelocity(s);
			if (!ss->moreAdjust)
			{
				ss->pos += ss->move;
				ss->move = 0;

				if (ss->zooming)
				{
					if (ss->switching)
					{
						ss->translate = ss->zoom;
						ss->sTranslate = ss->zoom;
					}
					else
					{
						ss->translate = 0.0f;
						ss->sTranslate = ss->zoom;

						ss->selectedWindow = None;
						ss->zoomedWindow = None;

						if (ss->grabIndex)
						{
							removeScreenGrab(s, ss->grabIndex, 0);
							ss->grabIndex = 0;
						}
					}
				}
				break;
			}

			m = ss->mVelocity * chunk;
			if (!m)
			{
				if (ss->mVelocity)
					m = (ss->move > 0) ? 1 : -1;
			}

			ss->move -= m;
			ss->pos += m;

			if (ss->switchMode == ApplicationSwitcher)
			{
				if (ss->pos < -ss->nWindows * WIDTH)
					ss->pos += ss->nWindows * WIDTH;
				else if (ss->pos > 0)
					ss->pos -= ss->nWindows * WIDTH;
			}

			ss->translate += ss->tVelocity * chunk;
			ss->sTranslate += ss->sVelocity * chunk;

			if (ss->selectedWindow != ss->zoomedWindow)
			{
				if (ss->sTranslate < 0.01f)
					ss->zoomedWindow = ss->selectedWindow;
			}
		}
	}

	UNWRAP(ss, s, preparePaintScreen);
	(*s->preparePaintScreen) (s, msSinceLastPaint);
	WRAP(ss, s, preparePaintScreen, switchPreparePaintScreen);
}

static Bool
switchPaintScreen(CompScreen * s,
				  const ScreenPaintAttrib * sAttrib,
				  Region region, int output, unsigned int mask)
{
	Bool status;

	SWITCH_SCREEN(s);

	if (ss->grabIndex || (ss->zooming && ss->translate > 0.001f))
	{
		ScreenPaintAttrib sa = *sAttrib;
		CompWindow *zoomed;
		CompWindow *switcher;
		Window zoomedAbove = None;
		Bool saveDestroyed = FALSE;

		if (ss->zooming || (ss->switchMode == RingSwitcher))
		{
			mask &= ~PAINT_SCREEN_REGION_MASK;
			mask |= PAINT_SCREEN_TRANSFORMED_MASK | PAINT_SCREEN_CLEAR_MASK;

			sa.zCamera -= ss->translate;
		}

		switcher = findWindowAtScreen(s, ss->popupWindow);
		if (switcher)
		{
			saveDestroyed = switcher->destroyed;
			switcher->destroyed = TRUE;
		}

		if (ss->bringToFront)
		{
			zoomed = findWindowAtScreen(s, ss->zoomedWindow);
			if (zoomed)
			{
				CompWindow *w;

				for (w = zoomed->prev; w && w->id <= 1; w = w->prev);
				zoomedAbove = (w) ? w->id : None;

				unhookWindowFromScreen(s, zoomed);
				insertWindowIntoScreen(s, zoomed, s->reverseWindows->id);
			}
		}
		else
		{
			zoomed = NULL;
		}

		UNWRAP(ss, s, paintScreen);
		status = (*s->paintScreen) (s, &sa, region, output, mask);
		WRAP(ss, s, paintScreen, switchPaintScreen);

		if (zoomed)
		{
			unhookWindowFromScreen(s, zoomed);
			insertWindowIntoScreen(s, zoomed, zoomedAbove);
		}

		if (switcher)
		{
			switcher->destroyed = saveDestroyed;

			glPushMatrix();

			prepareXCoords(s, output, -DEFAULT_Z_CAMERA);

			if (!switcher->destroyed &&
				switcher->attrib.map_state == IsViewable && switcher->damaged)
			{
				(*s->paintWindow) (switcher,
								   &switcher->paint, getInfiniteRegion(), 0);
			}

			glPopMatrix();
		}
	}
	else
	{
		UNWRAP(ss, s, paintScreen);
		status = (*s->paintScreen) (s, sAttrib, region, output, mask);
		WRAP(ss, s, paintScreen, switchPaintScreen);
	}

	return status;
}

static void switchDonePaintScreen(CompScreen * s)
{
	SWITCH_SCREEN(s);

	if ((ss->grabIndex || ss->zooming) && ss->moreAdjust)
	{
		if (ss->zooming || (ss->switchMode == RingSwitcher))
		{
			damageScreen(s);
		}
		else
		{
			CompWindow *w;

			w = findWindowAtScreen(s, ss->popupWindow);
			if (w)
				addWindowDamage(w);
		}
	}

	UNWRAP(ss, s, donePaintScreen);
	(*s->donePaintScreen) (s);
	WRAP(ss, s, donePaintScreen, switchDonePaintScreen);
}

static void
switchPaintThumb(CompWindow * w,
				 const WindowPaintAttrib * attrib,
				 unsigned int mask, int x, int y, int x1, int x2, float scale)
{
	DrawWindowGeometryProc oldDrawWindowGeometry;
	AddWindowGeometryProc oldAddWindowGeometry;
	WindowPaintAttrib sAttrib = *attrib;
	int wx, wy;
	float width, height;
	CompIcon *icon = NULL;

	SWITCH_SCREEN(w->screen);

	/* Wrap drawWindowGeometry to make sure the general
	   drawWindowGeometry function is used */
	oldDrawWindowGeometry = w->screen->drawWindowGeometry;
	w->screen->drawWindowGeometry = getBaseDrawWindowGeometry();
	oldAddWindowGeometry = w->screen->addWindowGeometry;
	w->screen->addWindowGeometry = getBaseAddWindowGeometry();

	mask |= PAINT_WINDOW_TRANSFORMED_MASK;

	if (w->mapNum || w->thumbPixmap)
	{
		int ww, wh;

		if (ss->switchMode == RingSwitcher)
		{
			width = ss->opt[SWITCH_SCREEN_OPTION_THUMBNAIL_WIDTH].value.i;
			height = ss->opt[SWITCH_SCREEN_OPTION_THUMBNAIL_HEIGHT].value.i;
		}
		else
		{
			width = WIDTH - (SPACE << 1);
			height = HEIGHT - (SPACE << 1);
		}

		ww = w->width + w->input.left + w->input.right;
		wh = w->height + w->input.top + w->input.bottom;

		if (ww > width)
			sAttrib.xScale = width / ww;
		else
			sAttrib.xScale = 1.0f;

		if (wh > height)
			sAttrib.yScale = height / wh;
		else
			sAttrib.yScale = 1.0f;

		if (sAttrib.xScale < sAttrib.yScale)
			sAttrib.yScale = sAttrib.xScale;
		else
			sAttrib.xScale = sAttrib.yScale;

		width = ww * sAttrib.xScale;
		height = wh * sAttrib.yScale;

		if (ss->switchMode == RingSwitcher)
		{
			sAttrib.xScale *= scale;
			sAttrib.yScale *= scale;

			x -= ((w->width * sAttrib.xScale) / 2.0f);

			wx = x;
			wy = y;
		}
		else
		{
			wx = x + SPACE + ((WIDTH - (SPACE << 1)) - width) / 2;
			wy = y + SPACE + ((HEIGHT - (SPACE << 1)) - height) / 2;
		}

		sAttrib.xTranslate =
				wx - w->attrib.x + w->input.left * sAttrib.xScale;
		sAttrib.yTranslate = wy - w->attrib.y + w->input.top * sAttrib.yScale;

		(w->screen->drawWindow) (w, &sAttrib, getInfiniteRegion(), mask);

		if (ss->opt[SWITCH_SCREEN_OPTION_ICON].value.b)
		{
			icon = getWindowIcon(w, 96, 96);
			if (!icon)
				icon = getWindowIcon(w, 128, 128);
			if (!icon)
				icon = getWindowIcon(w, 256, 256);
			if (!icon)
				icon = getWindowIcon(w, 512, 512);

			if (icon)
			{
				if (icon->width > 96)
				{
					sAttrib.xScale = sAttrib.yScale =
							MIN((48.0f /
								 (float)icon->width),
								(48.0f / (float)icon->height));
				}
				else
					sAttrib.xScale = sAttrib.yScale = 1.0f * scale;

				float wwidth, wspace, iwidth;
				float wheight, iheight;
				if (ss->switchMode == RingSwitcher)
				{
					wwidth = width * scale;
					wheight = height * scale;
					wspace = 0;
					iwidth = icon->width * scale;
					iheight = icon->height * scale;
				}
				else
				{
					wx = x + WIDTH - icon->width * sAttrib.xScale - SPACE;

					wwidth = WIDTH;
					wheight = HEIGHT;
					wspace = SPACE;
					iwidth = icon->width;
					iheight = icon->height;
				}

				switch(ss->iconCorner)
				{
				case BottomRight:
					wx = x + (wwidth - wspace) - iwidth;
					wy = y + (wheight - wspace) - iheight;
					break;
				case BottomLeft:
					wx = x + wspace;
					wy = y + (wheight - wspace) - iheight;
					break;
				case TopRight:
					wx = x + (wwidth - wspace) - iwidth;
					wy = y + wspace;
					break;
				case TopLeft:
					wx = x + wspace;
					wy = y + wspace;
					break;
				case Center:
					wx = x + (wwidth / 2) - (iwidth / 2);
					wy = y + (wheight / 2) - (iheight / 2);
					break;
				}
			}
		}
	}
	else
	{
		if (ss->switchMode == RingSwitcher)
		{
			width = ss->opt[SWITCH_SCREEN_OPTION_THUMBNAIL_WIDTH].value.i;
			height = ss->opt[SWITCH_SCREEN_OPTION_THUMBNAIL_HEIGHT].value.i;
		}
		else
		{
			width = WIDTH;
			height = HEIGHT;
		}
		width *= 0.75f;
		height *= 0.75f;

		icon = getWindowIcon(w, width, height);
		if (!icon)
			icon = w->screen->defaultIcon;

		if (icon)
		{
			int iw, ih;

			iw = width - SPACE;
			ih = height - SPACE;

			if (icon->width < (iw >> 1))
				sAttrib.xScale = (iw / icon->width);
			else
				sAttrib.xScale = 1.0f;

			if (icon->height < (ih >> 1))
				sAttrib.yScale = (ih / icon->height);
			else
				sAttrib.yScale = 1.0f;

			if (sAttrib.xScale < sAttrib.yScale)
				sAttrib.yScale = sAttrib.xScale;
			else
				sAttrib.xScale = sAttrib.yScale;

			width = icon->width * sAttrib.xScale;
			height = icon->height * sAttrib.yScale;

			if (ss->switchMode == RingSwitcher)
			{
				sAttrib.xScale *= scale;
				sAttrib.yScale *= scale;

				wx = x + SPACE + ((ss->opt[SWITCH_SCREEN_OPTION_THUMBNAIL_WIDTH].value.i - (SPACE << 1)) - width) / 2;
				wy = y + SPACE + ((ss->opt[SWITCH_SCREEN_OPTION_THUMBNAIL_HEIGHT].value.i - (SPACE << 1)) - height) / 2;
			}
			else
			{
				wx = x + SPACE + ((WIDTH - (SPACE << 1)) - width) / 2;
				wy = y + SPACE + ((HEIGHT - (SPACE << 1)) - height) / 2;
			}
		}
	}

	if (icon && (icon->texture.name || iconToTexture(w->screen, icon)))
	{
		REGION iconReg;
		CompMatrix matrix;

		mask |= PAINT_WINDOW_TRANSLUCENT_MASK;

		iconReg.rects = &iconReg.extents;
		iconReg.numRects = 1;

		iconReg.extents.x1 = w->attrib.x;
		iconReg.extents.y1 = w->attrib.y;
		iconReg.extents.x2 = w->attrib.x + icon->width;
		iconReg.extents.y2 = w->attrib.y + icon->height;

		matrix = icon->texture.matrix;
		matrix.x0 -= (w->attrib.x * icon->texture.matrix.xx);
		matrix.y0 -= (w->attrib.y * icon->texture.matrix.yy);

		sAttrib.xTranslate = wx - w->attrib.x;
		sAttrib.yTranslate = wy - w->attrib.y;

		w->vCount = 0;
		addWindowGeometry(w, &matrix, 1, &iconReg, getInfiniteRegion());

		if (w->vCount)
			(*w->screen->drawWindowTexture) (w,
											 &icon->texture, &sAttrib, mask);
	}

	w->screen->drawWindowGeometry = oldDrawWindowGeometry;
	w->screen->addWindowGeometry = oldAddWindowGeometry;
}

static float calc_ellipse(float x,float a, float b)
{
	return sqrt((b*b)*(1-((x*x)/(a*a))));
}

typedef struct
{
	float y;
	float x;
	float scale;
	int index;
} ArrayElement;

static int compareElements(const void *elem1, const void *elem2)
{
	ArrayElement *a1 = (ArrayElement *) elem1;
	ArrayElement *a2 = (ArrayElement *) elem2;

	if(a1->y < a2->y)
		return -1;
	else if(a1->y > a2->y)
		return 1;
	else
		return 0;
}

static Bool
switchPaintWindow(CompWindow * w,
				  const WindowPaintAttrib * attrib,
				  Region region, unsigned int mask)
{
	CompScreen *s = w->screen;
	Bool status;
	ArrayElement *draw_array = NULL;
	XRectangle outputRect;
	int xo, yo;

	SWITCH_SCREEN(s);

	if (ss->switchMode == RingSwitcher)
	{
		screenGetOutputDevRect(s, ss->head, &outputRect);
		xo = outputRect.x + outputRect.width/2.0f;
		yo = outputRect.y + (outputRect.height/2.0f) - (outputRect.height/5.0f);

		draw_array = malloc(ss->nWindows*sizeof(ArrayElement));
	}

	if (w->id == ss->popupWindow)
	{
		GLenum filter;
		int x, y, x1, x2, cx, i;

		if (mask & PAINT_WINDOW_SOLID_MASK)
			return FALSE;

		UNWRAP(ss, s, paintWindow);
		status = (*s->paintWindow) (w, attrib, region, mask);
		WRAP(ss, s, paintWindow, switchPaintWindow);

		if (!(mask & PAINT_WINDOW_TRANSFORMED_MASK) && region->numRects == 0)
			return TRUE;

		x1 = w->attrib.x + SPACE;
		x2 = w->attrib.x + w->width - SPACE;

		x = x1 + ss->pos;
		y = w->attrib.y + SPACE;

		filter = s->display->textureFilter;

		if (ss->opt[SWITCH_SCREEN_OPTION_MIPMAP].value.b)
			s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;

		glPushAttrib(GL_SCISSOR_BIT);
		glEnable(GL_SCISSOR_TEST);
		glScissor(x1, 0, x2 - x1, w->screen->height);

		if (ss->switchMode == RingSwitcher)
		{
			int x_pos = x;
			Bool front = FALSE;
			int tmp = 0;
			ELLIPSE_ROTA(outputRect);
			ELLIPSE_ROTB(outputRect);
			DIST_ROT;
			int sw = 0;
			float y_max = calc_ellipse(0,ellipse_a,ellipse_b);

			for (i = 0; i < ss->nWindows; i++)
			{
				tmp = x + (i * dist);
				sw = (abs(tmp) / (2 * ellipse_a)) % 2;

				switch(sw)
				{
				case 0:
					x_pos = ellipse_a - (abs(tmp) % (2 * ellipse_a));
					front = (tmp >= 0);
					break;
				case 1:
					if(tmp < 0)
					{
						x_pos = ((abs(tmp) % (2*ellipse_a))-ellipse_a);
						front = TRUE;
					}
					else
					{
						x_pos = -1 * (ellipse_a - (abs(tmp) % (2 * ellipse_a)));
						front = FALSE;
					}
					break;
				}

				if (front)
				{
					draw_array[i].x = x_pos + xo;
					draw_array[i].y = calc_ellipse(x_pos, ellipse_a, ellipse_b) + yo;

					draw_array[i].scale = 1.0f + ((draw_array[i].y - yo) / y_max) / 0.9f;
					draw_array[i].index = i;
				}
				else
				{
					draw_array[i].x = x_pos + xo;
					draw_array[i].y = -1 * calc_ellipse(x_pos, ellipse_a, ellipse_b) + yo;

					draw_array[i].scale = 1.0f - ((-1 * (draw_array[i].y - yo)) / y_max) + 0.4f;
					draw_array[i].index = i;
				}
			}

			qsort(draw_array, ss->nWindows, sizeof(ArrayElement), compareElements);

			for (i = 0; i < ss->nWindows; i++)
			{
				switchPaintThumb(ss->windows[draw_array[i].index],
						&w->lastPaint, mask, draw_array[i].x,
						draw_array[i].y , x1, x2,draw_array[i].scale);
			}
		}
		else
		{
			for (i = 0; i < ss->nWindows; i++)
			{
				if (x + WIDTH > x1)
					switchPaintThumb(ss->windows[i], &w->lastPaint,
									mask, x, y, x1, x2, 1.0f);

				x += WIDTH;
			}

			for (i = 0; i < ss->nWindows; i++)
			{
				if (x > x2)
					break;

				switchPaintThumb(ss->windows[i], &w->lastPaint,
								 mask, x, y, x1, x2, 1.0f);

				x += WIDTH;
			}
		}

		glPopAttrib();

		s->display->textureFilter = filter;

		cx = w->attrib.x + (w->width >> 1);

		if (ss->switchMode == ApplicationSwitcher)
		{
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glEnable(GL_BLEND);
			glColor4us(0, 0, 0, w->lastPaint.opacity);
			glPushMatrix();
			glTranslatef(cx, y, 0.0f);
			glVertexPointer(2, GL_FLOAT, 0, _boxVertices);
			glDrawArrays(GL_QUADS, 0, 16);
			glPopMatrix();
			glColor4usv(defaultColor);
			glDisable(GL_BLEND);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		}
	}
	else if (w->id == ss->selectedWindow)
	{
		glPushMatrix();

		if (ss->bringToFront)
		{
			if ((ss->selectedWindow == ss->zoomedWindow) &&
					!IPCS_GetBool(IPCS_OBJECT(s), ss->insideAtom))
				glTranslatef(0.0f, 0.0f, MIN(ss->translate, ss->sTranslate));
		}

		if (((ss->wMask & w->type) && ss->opacity != OPAQUE) && 
			(ss->opt[SWITCH_SCREEN_OPTION_RING_TRANSPARENT_ALL].value.b && 
			(ss->switchMode == RingSwitcher)))
		{
			status = TRUE;
		}
		else
		{
			UNWRAP(ss, s, paintWindow);
			status = (*s->paintWindow) (w, attrib, region, mask);
			WRAP(ss, s, paintWindow, switchPaintWindow);
		}

		glPopMatrix();
	}
	else if (ss->switching)
	{
		WindowPaintAttrib sAttrib = *attrib;

		if (ss->saturation != COLOR)
			sAttrib.saturation = (sAttrib.saturation * ss->saturation) >> 16;

		if (ss->brightness != 0xffff)
			sAttrib.brightness = (sAttrib.brightness * ss->brightness) >> 16;

		if ((ss->wMask & w->type) && ss->opacity != OPAQUE)
			sAttrib.opacity = (sAttrib.opacity * ss->opacity) >> 16;

		if (((ss->wMask & w->type) && ss->opacity != OPAQUE) && 
			(ss->opt[SWITCH_SCREEN_OPTION_RING_TRANSPARENT_ALL].value.b && 
			(ss->switchMode == RingSwitcher)))
		{
			status = TRUE;
		}
		else
		{
			if (ss->bringToFront && w->id == ss->zoomedWindow)
			{
				glPushMatrix();
				glTranslatef(0.0f, 0.0f, MIN(ss->translate, ss->sTranslate));

				UNWRAP(ss, s, paintWindow);
				status = (*s->paintWindow) (w, &sAttrib, region, mask);
				WRAP(ss, s, paintWindow, switchPaintWindow);
	
				glPopMatrix();
			}
			else
			{
				UNWRAP(ss, s, paintWindow);
				status = (*s->paintWindow) (w, &sAttrib, region, mask);
				WRAP(ss, s, paintWindow, switchPaintWindow);
			}
		}
	}
	else
	{
		UNWRAP(ss, s, paintWindow);
		status = (*s->paintWindow) (w, attrib, region, mask);
		WRAP(ss, s, paintWindow, switchPaintWindow);
	}

	if (draw_array)
		free(draw_array);

	return status;
}

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

	SWITCH_SCREEN(w->screen);

	if (ss->grabIndex)
	{
		CompWindow *popup;
		int i;

		for (i = 0; i < ss->nWindows; i++)
		{
			if (ss->windows[i] == w)
			{
				popup = findWindowAtScreen(w->screen, ss->popupWindow);
				if (popup)
					addWindowDamage(popup);

				break;
			}
		}
	}

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

	return status;
}

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

	SWITCH_DISPLAY(display);

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

	switch (index)
	{
	case SWITCH_DISPLAY_OPTION_NEXT:
	case SWITCH_DISPLAY_OPTION_NEXT_ALL:
	case SWITCH_DISPLAY_OPTION_PREV:
	case SWITCH_DISPLAY_OPTION_PREV_ALL:
	case SWITCH_DISPLAY_OPTION_RING_NEXT:
	case SWITCH_DISPLAY_OPTION_RING_NEXT_ALL:
	case SWITCH_DISPLAY_OPTION_RING_PREV:
	case SWITCH_DISPLAY_OPTION_RING_PREV_ALL:
		if (setDisplayAction(display, o, value))
			return TRUE;
	default:
		break;
	}

	return FALSE;
}

static void switchDisplayInitOptions(SwitchDisplay * sd)
{
	CompOption *o;

	o = &sd->opt[SWITCH_DISPLAY_OPTION_NEXT];
	o->advanced = False;
	o->name = "next";
	o->group = N_("Bindings");
	o->subGroup = N_("Current workspace");
	o->displayHints = "";
	o->shortDesc = N_("Next Window");
	o->longDesc = N_("Popup Switcher if not visible and "
					 "select Next Window.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = switchNext;
	o->value.action.terminate = switchTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = SWITCH_NEXT_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(SWITCH_NEXT_KEY_DEFAULT);

	o = &sd->opt[SWITCH_DISPLAY_OPTION_PREV];
	o->advanced = False;
	o->name = "prev";
	o->group = N_("Bindings");
	o->subGroup = N_("Current workspace");
	o->displayHints = "";
	o->shortDesc = N_("Prev Window");
	o->longDesc = N_("Popup Switcher if not visible and "
					 "select Previous Window.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = switchPrev;
	o->value.action.terminate = switchTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = SWITCH_PREV_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(SWITCH_PREV_KEY_DEFAULT);

	o = &sd->opt[SWITCH_DISPLAY_OPTION_NEXT_ALL];
	o->advanced = False;
	o->name = "next_all";
	o->group = N_("Bindings");
	o->subGroup = N_("All workspaces");
	o->displayHints = "";
	o->shortDesc = N_("Next Window (All Workspaces)");
	o->longDesc = N_("Popup Switcher if not visible and "
					 "select Next Window out of All Windows.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = switchNextAll;
	o->value.action.terminate = switchTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = SWITCH_NEXT_ALL_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(SWITCH_NEXT_ALL_KEY_DEFAULT);

	o = &sd->opt[SWITCH_DISPLAY_OPTION_PREV_ALL];
	o->advanced = False;
	o->name = "prev_all";
	o->group = N_("Bindings");
	o->subGroup = N_("All workspaces");
	o->displayHints = "";
	o->shortDesc = N_("Prev Window (All Workspaces)");
	o->longDesc = N_("Popup Switcher if not visible and "
					 "select Previous Window out of All Windows.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = switchPrevAll;
	o->value.action.terminate = switchTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = SWITCH_PREV_ALL_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(SWITCH_PREV_ALL_KEY_DEFAULT);

	o = &sd->opt[SWITCH_DISPLAY_OPTION_RING_NEXT];
	o->advanced = False;
	o->name = "next_ring";
	o->group = N_("Bindings (Ring)");
	o->subGroup = N_("Current workspace");
	o->displayHints = "";
	o->shortDesc = N_("Next Window");
	o->longDesc = N_("Popup Switcher if not visible and "
					 "select Next Window.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = switchNextRing;
	o->value.action.terminate = switchTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = SWITCH_RING_NEXT_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(SWITCH_RING_NEXT_KEY_DEFAULT);

	o = &sd->opt[SWITCH_DISPLAY_OPTION_RING_PREV];
	o->advanced = False;
	o->name = "prev_ring";
	o->group = N_("Bindings (Ring)");
	o->subGroup = N_("Current workspace");
	o->displayHints = "";
	o->shortDesc = N_("Prev Window");
	o->longDesc = N_("Popup Switcher if not visible and "
					 "select Previous Window.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = switchPrevRing;
	o->value.action.terminate = switchTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = SWITCH_RING_PREV_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(SWITCH_RING_PREV_KEY_DEFAULT);

	o = &sd->opt[SWITCH_DISPLAY_OPTION_RING_NEXT_ALL];
	o->advanced = False;
	o->name = "next_all_ring";
	o->group = N_("Bindings (Ring)");
	o->subGroup = N_("All workspaces");
	o->displayHints = "";
	o->shortDesc = N_("Next Window (All Workspaces)");
	o->longDesc = N_("Popup Switcher if not visible and "
					 "select Next Window out of All Windows.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = switchNextAllRing;
	o->value.action.terminate = switchTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = SWITCH_RING_NEXT_ALL_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(SWITCH_RING_NEXT_ALL_KEY_DEFAULT);

	o = &sd->opt[SWITCH_DISPLAY_OPTION_RING_PREV_ALL];
	o->advanced = False;
	o->name = "prev_all_ring";
	o->group = N_("Bindings (Ring)");
	o->subGroup = N_("All workspaces");
	o->displayHints = "";
	o->shortDesc = N_("Prev Window (All Workspaces)");
	o->longDesc = N_("Popup Switcher if not visible and "
					 "select Previous Window out of All Windows.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = switchPrevAllRing;
	o->value.action.terminate = switchTerminate;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = SWITCH_RING_PREV_ALL_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(SWITCH_RING_PREV_ALL_KEY_DEFAULT);
}

static CompOption *switchGetDisplayOptions(CompDisplay * display, int *count)
{
	if (display)
	{
		SWITCH_DISPLAY(display);

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

		switchDisplayInitOptions(sd);
		*count = NUM_OPTIONS(sd);
		return sd->opt;
	}
}


static Bool switchInitDisplay(CompPlugin * p, CompDisplay * d)
{
	SwitchDisplay *sd;

	sd = malloc(sizeof(SwitchDisplay));
	if (!sd)
		return FALSE;

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

	sd->selectWinAtom = XInternAtom(d->display, SELECT_WIN_PROP, 0);

	switchDisplayInitOptions(sd);

	WRAP(sd, d, handleEvent, switchHandleEvent);

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

	return TRUE;
}

static void switchFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	SWITCH_DISPLAY(d);

	freeScreenPrivateIndex(d, sd->screenPrivateIndex);

	UNWRAP(sd, d, handleEvent);

	free(sd);
}

static Bool switchInitScreen(CompPlugin * p, CompScreen * s)
{
	SwitchScreen *ss;

	SWITCH_DISPLAY(s->display);

	ss = malloc(sizeof(SwitchScreen));
	if (!ss)
		return FALSE;

	ss->popupWindow = None;

	ss->selectedWindow = None;
	ss->zoomedWindow = None;

	ss->lastActiveNum = 0;

	ss->windows = 0;
	ss->nWindows = 0;
	ss->windowsSize = 0;

	ss->pos = ss->move = 0;

	ss->switching = FALSE;
	ss->prevMin = NULL;

	ss->grabIndex = 0;

	ss->speed = SWITCH_SPEED_DEFAULT;
	ss->timestep = SWITCH_TIMESTEP_DEFAULT;
	ss->zoom = SWITCH_ZOOM_DEFAULT / 30.0f;

	ss->zooming = (SWITCH_ZOOM_DEFAULT > 0.05f) ? TRUE : FALSE;

	ss->moreAdjust = 0;

	ss->mVelocity = 0.0f;
	ss->tVelocity = 0.0f;
	ss->sVelocity = 0.0f;

	ss->translate = 0.0f;
	ss->sTranslate = 0.0f;

	ss->saturation = (COLOR * SWITCH_SATURATION_DEFAULT) / 100;
	ss->brightness = (0xffff * SWITCH_BRIGHTNESS_DEFAULT) / 100;
	ss->opacity = (OPAQUE * SWITCH_OPACITY_DEFAULT) / 100;

	ss->bringToFront = SWITCH_BRINGTOFRONT_DEFAULT;
	ss->showWindowList = SWITCH_SHOW_WINDOW_LIST_DEFAULT;
	ss->autoRotate = SWITCH_AUTO_ROTATE_DEFAULT;
	ss->autoRotate = SWITCH_TEMP_UNMINIMIZE_DEFAULT;
	ss->allWindows = FALSE;
	ss->head = 0;
	ss->tempUnMinimize = FALSE;
	ss->switchMode = ApplicationSwitcher;
	ss->iconCorner = SWITCH_ICON_CORNER_DEFAULT;

	ss->insideAtom = IPCS_GetAtom(IPCS_OBJECT(s), IPCS_BOOL, "INSIDE", TRUE);

	switchScreenInitOptions(ss);

	addScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_NEXT].value.action);
	addScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_PREV].value.action);
	addScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_NEXT_ALL].value.action);
	addScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_PREV_ALL].value.action);
	addScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_RING_NEXT].value.action);
	addScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_RING_PREV].value.action);
	addScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_RING_NEXT_ALL].value.action);
	addScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_RING_PREV_ALL].value.action);

	WRAP(ss, s, preparePaintScreen, switchPreparePaintScreen);
	WRAP(ss, s, donePaintScreen, switchDonePaintScreen);
	WRAP(ss, s, paintScreen, switchPaintScreen);
	WRAP(ss, s, paintWindow, switchPaintWindow);
	WRAP(ss, s, damageWindowRect, switchDamageWindowRect);

	s->privates[sd->screenPrivateIndex].ptr = ss;

	return TRUE;
}

static void switchFiniScreen(CompPlugin * p, CompScreen * s)
{
	SWITCH_SCREEN(s);
	SWITCH_DISPLAY(s->display);

	UNWRAP(ss, s, preparePaintScreen);
	UNWRAP(ss, s, donePaintScreen);
	UNWRAP(ss, s, paintScreen);
	UNWRAP(ss, s, paintWindow);
	UNWRAP(ss, s, damageWindowRect);

	removeScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_NEXT].value.action);
	removeScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_PREV].value.action);
	removeScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_NEXT_ALL].value.action);
	removeScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_PREV_ALL].value.action);
	removeScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_RING_NEXT].value.action);
	removeScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_RING_PREV].value.action);
	removeScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_RING_NEXT_ALL].value.action);
	removeScreenAction(s, &sd->opt[SWITCH_DISPLAY_OPTION_RING_PREV_ALL].value.action);

	if (ss->popupWindow)
		XDestroyWindow(s->display->display, ss->popupWindow);

	if (ss->windowsSize)
		free(ss->windows);

	free(ss);
}

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

	return TRUE;
}

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

CompPluginDep setDeps[] = {
	{CompPluginRuleAfter, "cube"},
	{CompPluginRuleAfter, "decoration"},
};

CompPluginVTable switchVTable = {
	"switcher",
	N_("Application Window Switcher"),
	N_("Application Window Switcher"),
	switchInit,
	switchFini,
	switchInitDisplay,
	switchFiniDisplay,
	switchInitScreen,
	switchFiniScreen,
	0,
	0,
	switchGetDisplayOptions,
	switchSetDisplayOption,
	switchGetScreenOptions,
	switchSetScreenOption,
	setDeps,
	sizeof(setDeps) / sizeof(setDeps[0]),
	0,
	0,
	BERYL_ABI_INFO,
	"beryl-plugins",
	"wm",
	0,
	0,
	True,
};

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


syntax highlighted by Code2HTML, v. 0.9.1