/*
 * 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.
 *
 * Authors: David Reveman <davidr@novell.com>
 *          Mirco Müller <macslow@bangang.de> (Skydome support)
 *          Guillaume Seguin <ixce@beryl-project.org> (Bottom caps, rotation stuff)
 *          Dennis Kasprzyk <onestone@beryl-project.org> (Transparent cube)
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>

#include <X11/Xatom.h>
#include <X11/Xproto.h>

#include <beryl.h>
#include <beryl_helpers.h>

#define CUBE_COLOR_RED_DEFAULT   0xefef
#define CUBE_COLOR_GREEN_DEFAULT 0xebeb
#define CUBE_COLOR_BLUE_DEFAULT  0xe7e7

typedef enum _ImagesRotateMode
{
	ImagesRotateModeNever,
	ImagesRotateModeAfterMoving,
	ImagesRotateModeWhileMoving
} ImagesRotateMode;

char *imagesRotateModes[] = {
	N_("Never rotate caps"),
	N_("Rotate after moving"),
	N_("Rotate while moving")
};

#define IMAGES_ROTATE_MODE_DEFAULT ImagesRotateModeNever
#define NUM_IMAGES_ROTATE_MODES 3

typedef enum _MultiMonitorMode
{
	Multiple,
	OneBig,
} MultiMonitorMode;

char *multiMonitorModes[] = {
	N_("Multiple cubes"),
	N_("One big cube")
};

#define MULTIMONITOR_MODE_DEFAULT OneBig
#define NUM_MULTIMONITOR_MODES 2

#define CUBE_IN_DEFAULT                     FALSE

static char *cubeImages[] = {
	IMAGEDIR "/cubecaps.png"
};

#define N_CUBE_IMAGES (sizeof (cubeImages) / sizeof (cubeImages[0]))

#define CUBE_SCALE_IMAGE_TOP_DEFAULT FALSE
#define CUBE_SCALE_IMAGE_BOTTOM_DEFAULT FALSE

#define CUBE_NEXT_KEY_DEFAULT       "Space"
#define CUBE_NEXT_MODIFIERS_DEFAULT 0

#define CUBE_PREV_KEY_DEFAULT       "BackSpace"
#define CUBE_PREV_MODIFIERS_DEFAULT 0

#define CUBE_SKYDOME_DEFAULT FALSE

#define CUBE_SKYDOME_ANIMATE_DEFAULT FALSE

#define CUBE_SKYDOME_GRAD_START_RED_DEFAULT   0x0d0d
#define CUBE_SKYDOME_GRAD_START_GREEN_DEFAULT 0xb1b1
#define CUBE_SKYDOME_GRAD_START_BLUE_DEFAULT  0xfdfd

#define CUBE_SKYDOME_GRAD_END_RED_DEFAULT   0xfefe
#define CUBE_SKYDOME_GRAD_END_GREEN_DEFAULT 0xffff
#define CUBE_SKYDOME_GRAD_END_BLUE_DEFAULT  0xc7c7

#define CUBE_UNFOLD_KEY_DEFAULT       "Next"
#define CUBE_UNFOLD_MODIFIERS_DEFAULT (ControlMask | CompAltMask)

#define CUBE_UNFOLD_ZOOM_DEFAULT 1.0f
#define CUBE_UNFOLD_ZOOM_PRECISION 0.1f
#define CUBE_UNFOLD_ZOOM_MIN 0.0f
#define CUBE_UNFOLD_ZOOM_MAX 10.0f

#define CUBE_ACCELERATION_DEFAULT   4.0f
#define CUBE_ACCELERATION_MIN       1.0f
#define CUBE_ACCELERATION_MAX       20.0f
#define CUBE_ACCELERATION_PRECISION 0.1f

#define CUBE_SPEED_DEFAULT   1.5f
#define CUBE_SPEED_MIN       0.1f
#define CUBE_SPEED_MAX       50.0f
#define CUBE_SPEED_PRECISION 0.1f

#define CUBE_TIMESTEP_DEFAULT   1.2f
#define CUBE_TIMESTEP_MIN       0.1f
#define CUBE_TIMESTEP_MAX       50.0f
#define CUBE_TIMESTEP_PRECISION 0.1f

#define CUBE_MIPMAP_DEFAULT FALSE

#define CUBE_VIEWPORT_SLIDE_DEFAULT      FALSE
#define CUBE_VIEWPORT_SLIDE_NO3D_DEFAULT FALSE
#define CUBE_DRAW_CAPS_DEFAULT  TRUE
#define CUBE_TRANSPARENT_DEFAULT FALSE

#define CUBE_DISPLAY_OPTION_UNFOLD 0
#define CUBE_DISPLAY_OPTION_NEXT   1
#define CUBE_DISPLAY_OPTION_PREV   2

#define CUBE_DISPLAY_OPTION_NUM    3



#define CUBE_FADE_TIME_DEFAULT			1.0
#define CUBE_FADE_TIME_MAX				10.0
#define CUBE_FADE_TIME_MIN				0.0
#define CUBE_FADE_TIME_PRECISION		0.1
#define CUBE_ACTIVE_OPACITY_DEFAULT		30
#define CUBE_INACTIVE_OPACITY_DEFAULT	100
#define CAP_TRANSPARENT_DEFAULT FALSE

static int displayPrivateIndex;

typedef struct _CubeDisplay
{
	int screenPrivateIndex;

	CompOption opt[CUBE_DISPLAY_OPTION_NUM];
} CubeDisplay;

#define CUBE_SCREEN_OPTION_COLOR                0
#define CUBE_SCREEN_OPTION_IN                   1
#define CUBE_SCREEN_OPTION_DRAW_CAPS            2
#define CUBE_SCREEN_OPTION_IMAGES_ROTATE_MODE   3
#define CUBE_SCREEN_OPTION_SCALE_IMAGE_TOP      4
#define CUBE_SCREEN_OPTION_IMAGES_TOP           5
#define CUBE_SCREEN_OPTION_SCALE_IMAGE_BOTTOM   6
#define CUBE_SCREEN_OPTION_IMAGES_BOTTOM        7
#define CUBE_SCREEN_OPTION_SKYDOME              8
#define CUBE_SCREEN_OPTION_SKYDOME_IMG          9
#define CUBE_SCREEN_OPTION_SKYDOME_ANIM        10
#define CUBE_SCREEN_OPTION_SKYDOME_GRAD_START  11
#define CUBE_SCREEN_OPTION_SKYDOME_GRAD_END    12
#define CUBE_SCREEN_OPTION_ACCELERATION        13
#define CUBE_SCREEN_OPTION_SPEED	           14
#define CUBE_SCREEN_OPTION_TIMESTEP	           15
#define CUBE_SCREEN_OPTION_MIPMAP	           16
#define CUBE_SCREEN_OPTION_VIEWPORT_SLIDE      17
#define CUBE_SCREEN_OPTION_VIEWPORT_SLIDE_NO3D 18
#define CUBE_SCREEN_OPTION_MULTIMONITOR	       19
#define CUBE_SCREEN_OPTION_TRANSPARENT         20
#define CUBE_SCREEN_OPTION_FADE_TIME           21
#define CUBE_SCREEN_OPTION_ACTIVE_OPACITY      22
#define CUBE_SCREEN_OPTION_INACTIVE_OPACITY    23
#define CUBE_SCREEN_OPTION_CAP_TRANSPARENT       24
#define CUBE_TRANSPARENT_MANUALONLY		25
#define CUBE_SCREEN_OPTION_UNFOLD_ZOOM_DISTANCE 	26
#define CUBE_SCREEN_OPTION_STUCK_TO_SCREEN 27
#define CUBE_SCREEN_OPTION_NUM                 28

typedef struct _CubeCapInfo
{
	CompTexture texture;
	GLfloat tc[12];

	int imgNFile;
	int imgCurFile;
	CompOptionValue *imgFiles;

	Bool scale;
} CubeCapInfo;

typedef struct _CubeScreen
{
	PreparePaintScreenProc preparePaintScreen;
	DonePaintScreenProc donePaintScreen;
	PaintScreenProc paintScreen;
	PaintTransformedScreenProc paintTransformedScreen;
	ApplyScreenTransformProc applyScreenTransform;
	PaintBackgroundProc paintBackground;
	PaintWindowProc paintWindow;
	SetScreenOptionProc setScreenOption;
	OutputChangeNotifyProc outputChangeNotify;
	SetClipPlanesProc setClipPlanes;

	CompOption opt[CUBE_SCREEN_OPTION_NUM];

	int invert;
	int xrotations;
	GLfloat distance;
	Bool rotateImages;
	Bool neverRotateImages;
	int previousRotationAtom;

	Bool paintingCaps;
	int paintingCapsAtom;

	GLushort color[3];

	int grabIndex;

	float acceleration;
	float speed;
	float timestep;

	Bool unfolded;
	GLfloat unfold, unfoldVelocity, unfoldDistance;

	GLfloat *vertices;
	int nvertices;

	GLuint skyListId;
	Bool animateSkyDome;
	GLushort skyGradStartColor[3];
	GLushort skyGradEndColor[3];

	CubeCapInfo cubeCapTop;
	CubeCapInfo cubeCapBottom;

	CompTexture sky;

	int nOutput;
	int output[64];
	int outputMask[64];

	Bool fullscreenOutput;
	int manualAtom;
	int unfoldedAtom;
	float outputXScale;
	float outputYScale;
	float outputXOffset;
	float outputYOffset;

	MultiMonitorMode mmMode;
	int mmModeAtom;

	int insideAtom;
	int snapTopBottomAtom;

	float desktopOpacity;
	float toOpacity;
	Bool noManaged;

	int zoomLevelAtom;

	Bool finalPaint;
} CubeScreen;

#define GET_CUBE_DISPLAY(d)				     \
    ((CubeDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define CUBE_DISPLAY(d)			   \
    CubeDisplay *cd = GET_CUBE_DISPLAY (d)

#define GET_CUBE_SCREEN(s, cd)					 \
    ((CubeScreen *) (s)->privates[(cd)->screenPrivateIndex].ptr)

#define CUBE_SCREEN(s)							\
    CubeScreen *cs = GET_CUBE_SCREEN (s, GET_CUBE_DISPLAY (s->display))

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

static void
cubeInitTextureCoords(CompScreen * s, CubeCapInfo * cubeCap,
					  unsigned int width, unsigned int height)
{
	float x1, x2, y1, y2;
	CompMatrix *matrix;

	if (!cubeCap)
		return;

	matrix = &cubeCap->texture.matrix;

	if (cubeCap->scale)
	{
		x1 = 0.0f;
		y1 = 0.0f;
		x2 = width;
		y2 = height;
	}
	else
	{
		int bigscr, i;
		int bigWidth, bigHeight;

		CUBE_SCREEN(s);
		bigWidth = s->width;
		bigHeight = s->height;
		/* Scale the texture in a sane way for multi head too */
		if (s->nOutputDev > 1 && cs->mmMode != OneBig)
		{
			for (i = bigscr = 0; i < s->nOutputDev; i++)
				if (s->outputDev[i].width > s->outputDev[bigscr].width)
					bigscr = i;
			bigWidth = s->outputDev[bigscr].width;
			bigHeight = s->outputDev[bigscr].height;
		}

		x1 = width / 2.0f - bigWidth / 2.0f;
		y1 = height / 2.0f - bigHeight / 2.0f;
		x2 = width / 2.0f + bigWidth / 2.0f;
		y2 = height / 2.0f + bigHeight / 2.0f;
	}

	cubeCap->tc[0] = COMP_TEX_COORD_X(matrix, width / 2.0f);
	cubeCap->tc[1] = COMP_TEX_COORD_Y(matrix, height / 2.0f);

	cubeCap->tc[2] = COMP_TEX_COORD_X(matrix, x2);
	cubeCap->tc[3] = COMP_TEX_COORD_Y(matrix, y1);

	cubeCap->tc[4] = COMP_TEX_COORD_X(matrix, x1);
	cubeCap->tc[5] = COMP_TEX_COORD_Y(matrix, y1);

	cubeCap->tc[6] = COMP_TEX_COORD_X(matrix, x1);
	cubeCap->tc[7] = COMP_TEX_COORD_Y(matrix, y2);

	cubeCap->tc[8] = COMP_TEX_COORD_X(matrix, x2);
	cubeCap->tc[9] = COMP_TEX_COORD_Y(matrix, y2);

	cubeCap->tc[10] = COMP_TEX_COORD_X(matrix, x2);
	cubeCap->tc[11] = COMP_TEX_COORD_Y(matrix, y1);
}

static void cubeSetClipPlanes (CompScreen *s, int output)
{
	CUBE_SCREEN(s);

	if (cs->mmMode == Multiple || s->nOutputDev == 1)
	{
		if (cs->invert != 1)
		{
			UNWRAP(cs, s, setClipPlanes);
			(*s->setClipPlanes) (s, output);
			WRAP(cs, s, setClipPlanes, cubeSetClipPlanes);
			return;
		}
		GLdouble clipPlane0[] = {  1.0, 0.0, 0.5 / cs->distance, 0.0 };
		GLdouble clipPlane1[] = {  -1.0,  0.0, 0.5 / cs->distance, 0.0 };
		GLdouble clipPlane2[] = {  0.0,  -1.0, 0.5 / cs->distance, 0.0 };
		GLdouble clipPlane3[] = { 0.0,  1.0, 0.5 / cs->distance, 0.0 };

		glClipPlane (GL_CLIP_PLANE0, clipPlane0);
		glClipPlane (GL_CLIP_PLANE1, clipPlane1);
		glClipPlane (GL_CLIP_PLANE2, clipPlane2);
		glClipPlane (GL_CLIP_PLANE3, clipPlane3);
		return;
	}

	float left = s->outputDev[output].region.extents.x1 /
				 s->outputDev[output].width;
	float top = s->outputDev[output].region.extents.y1 /
				s->outputDev[output].height;

	float right = (s->width - s->outputDev[output].region.extents.x2) /
					s->outputDev[output].width;
	float bottom = (s->height - s->outputDev[output].region.extents.y2) /
					s->outputDev[output].height;


	if (cs->invert != 1)
	{
		GLdouble clipPlane0[] = {  0.0, -1.0, 0.0, 0.5 + top};
		GLdouble clipPlane1[] = {  0.0,  1.0, 0.0, 0.5 + bottom };
		GLdouble clipPlane2[] = {  1.0,  0.0, 0.0, 0.5 + left};
		GLdouble clipPlane3[] = { -1.0,  0.0, 0.0, 0.5 + right};

		glClipPlane (GL_CLIP_PLANE0, clipPlane0);
		glClipPlane (GL_CLIP_PLANE1, clipPlane1);
		glClipPlane (GL_CLIP_PLANE2, clipPlane2);
		glClipPlane (GL_CLIP_PLANE3, clipPlane3);
		return;
	}


	float w = (float)(s->width - s->outputDev[output].width) /
				 s->outputDev[output].width;

	float h = (float)(s->height - s->outputDev[output].height) /
				s->outputDev[output].height;

	GLdouble clipPlane0[] = {  1.0, 0.0, (0.5 + w) / cs->distance, 0.0 };
	GLdouble clipPlane1[] = {  -1.0,  0.0, (0.5 + w) / cs->distance, 0.0 };
	GLdouble clipPlane2[] = {  0.0,  -1.0, (0.5 + h) / cs->distance, 0.0 };
	GLdouble clipPlane3[] = { 0.0,  1.0, (0.5 + h) / cs->distance, 0.0 };

	glClipPlane (GL_CLIP_PLANE0, clipPlane0);
	glClipPlane (GL_CLIP_PLANE1, clipPlane1);
	glClipPlane (GL_CLIP_PLANE2, clipPlane2);
	glClipPlane (GL_CLIP_PLANE3, clipPlane3);
}

static void cubeLoadImg(CompScreen * s, CubeCapInfo * cubeCap, int n)
{
	unsigned int width, height;
	int pw, ph;

	CUBE_SCREEN(s);

	if (!cubeCap)
		return;

	if (!cs->fullscreenOutput)
	{
		pw = s->width;
		ph = s->height;
	}
	else
	{
		pw = s->outputDev[0].width;
		ph = s->outputDev[0].height;
	}

	finiTexture(s, &cubeCap->texture);
	initTexture(s, &cubeCap->texture);

	if (!cubeCap->imgNFile)
		return;

	cubeCap->imgCurFile = n % cubeCap->imgNFile;

	if (!readImageToTexture
		(s, &cubeCap->texture,
		 cubeCap->imgFiles[cubeCap->imgCurFile].s, &width, &height))
	{
		fprintf(stderr, "%s: Failed to load slide: %s\n",
				getProgramName(), cubeCap->imgFiles[cubeCap->imgCurFile].s);

		finiTexture(s, &cubeCap->texture);
		initTexture(s, &cubeCap->texture);

		return;
	}
	cubeInitTextureCoords(s, cubeCap, width, height);
}

static void cubeInitCubeCap(CompScreen * s, CubeCapInfo * cubeCap)
{
	memset(cubeCap->tc, 0, sizeof(cubeCap->tc));

	initTexture(s, &cubeCap->texture);

	cubeCap->scale = FALSE;
	cubeCap->imgCurFile = 0;

	if (cubeCap->imgNFile)
	{
		cubeLoadImg(s, cubeCap, cubeCap->imgCurFile);
		damageScreen(s);
	}
}

static Bool cubeUpdateGeometry(CompScreen * s, int sides, Bool invert)
{
	GLfloat radius, distance;
	GLfloat *v;
	int i, n;

	CUBE_SCREEN(s);

	if (cs->mmMode != Multiple)
		sides *= cs->nOutput;

	distance = 0.5f / tanf(M_PI / sides);
	radius = 0.5f / sinf(M_PI / sides);

	n = (sides + 2) * 2;

	if (cs->nvertices != n)
	{
		v = realloc(cs->vertices, sizeof(GLfloat) * n * 3);
		if (!v)
			return FALSE;

		cs->nvertices = n;
		cs->vertices = v;
	}
	else
		v = cs->vertices;

	*v++ = 0.0f;
	*v++ = 0.5 * invert;
	*v++ = 0.0f;

	for (i = 0; i <= sides; i++)
	{
		*v++ = radius * sinf(i * 2 * M_PI / sides + M_PI / sides);
		*v++ = 0.5 * invert;
		*v++ = radius * cosf(i * 2 * M_PI / sides + M_PI / sides);
	}

	*v++ = 0.0f;
	*v++ = -0.5 * invert;
	*v++ = 0.0f;

	for (i = sides; i >= 0; i--)
	{
		*v++ = radius * sinf(i * 2 * M_PI / sides + M_PI / sides);
		*v++ = -0.5 * invert;
		*v++ = radius * cosf(i * 2 * M_PI / sides + M_PI / sides);
	}

	cs->invert = invert;
	cs->distance = distance;



	return TRUE;
}


static void cubeUpdateOutputs(CompScreen * s)
{
	BoxPtr pBox0, pBox1;
	int i, j, k, x;

	CUBE_SCREEN(s);

	k = 0;

	cs->fullscreenOutput = TRUE;

	for (i = 0; i < s->nOutputDev; i++)
	{
		cs->outputMask[i] = -1;

		/* dimensions must match first output */
		if (s->outputDev[i].width != s->outputDev[0].width ||
			s->outputDev[i].height != s->outputDev[0].height)
			continue;

		pBox0 = &s->outputDev[0].region.extents;
		pBox1 = &s->outputDev[i].region.extents;

		/* top and bottom line must match first output */
		if (pBox0->y1 != pBox1->y1 || pBox0->y2 != pBox1->y2)
			continue;

		k++;

		for (j = 0; j < s->nOutputDev; j++)
		{
			pBox0 = &s->outputDev[j].region.extents;

			/* must not intersect other output region */
			if (i != j && pBox0->x2 > pBox1->x1 && pBox0->x1 < pBox1->x2)
			{
				k--;
				break;
			}
		}
	}

	if (cs->mmMode == OneBig)
		k = 1;
	if (cs->mmMode == Multiple)
		k = s->nOutputDev;


	if (k != s->nOutputDev)
	{
		cs->fullscreenOutput = FALSE;
		cs->nOutput = 1;
		return;
	}

	/* add output indices from left to right */
	j = 0;
	for (;;)
	{
		x = MAXSHORT;
		k = -1;

		for (i = 0; i < s->nOutputDev; i++)
		{
			if (cs->outputMask[i] != -1)
				continue;

			if (s->outputDev[i].region.extents.x1 < x)
			{
				x = s->outputDev[i].region.extents.x1;
				k = i;
			}
		}

		if (k < 0)
			break;

		cs->outputMask[k] = j;
		cs->output[j] = k;

		j++;
	}
	cs->nOutput = j;


	if (cs->nOutput == 1)
	{
		if (s->outputDev[0].width != s->width ||
			s->outputDev[0].height != s->height)
			cs->fullscreenOutput = FALSE;
	}
}

static void cubeUpdateSkyDomeTexture(CompScreen * screen)
{
	CUBE_SCREEN(screen);

	finiTexture(screen, &cs->sky);
	initTexture(screen, &cs->sky);

	if (!cs->opt[CUBE_SCREEN_OPTION_SKYDOME].value.b)
		return;

	if (strlen(cs->opt[CUBE_SCREEN_OPTION_SKYDOME_IMG].value.s) == 0 ||
		!readImageToTexture(screen,
							&cs->sky,
							cs->opt[CUBE_SCREEN_OPTION_SKYDOME_IMG].
							value.s, NULL, NULL))
	{
		GLfloat aaafTextureData[128][128][3];

		GLfloat fRStart =
				(GLfloat) cs->
				opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_START].value.c[0] /
				0xffff;
		GLfloat fGStart =
				(GLfloat) cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_START].
				value.c[1] / 0xffff;
		GLfloat fBStart =
				(GLfloat) cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_START].
				value.c[2] / 0xffff;
		GLfloat fREnd =
				(GLfloat) cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_END].value.
				c[0] / 0xffff;
		GLfloat fGEnd =
				(GLfloat) cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_END].value.
				c[1] / 0xffff;
		GLfloat fBEnd =
				(GLfloat) cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_END].value.
				c[2] / 0xffff;

		GLfloat fRStep = (fREnd - fRStart) / 128.0f;
		GLfloat fGStep = (fGEnd - fGStart) / 128.0f;
		GLfloat fBStep = (fBStart - fBEnd) / 128.0f;
		GLfloat fR = fRStart;
		GLfloat fG = fGStart;
		GLfloat fB = fBStart;

		int iX, iY;

		for (iX = 127; iX >= 0; iX--)
		{
			fR += fRStep;
			fG += fGStep;
			fB -= fBStep;

			for (iY = 0; iY < 128; iY++)
			{
				aaafTextureData[iX][iY][0] = fR;
				aaafTextureData[iX][iY][1] = fG;
				aaafTextureData[iX][iY][2] = fB;
			}
		}

		cs->sky.target = GL_TEXTURE_2D;
		cs->sky.filter = GL_LINEAR;
		cs->sky.wrap = GL_CLAMP_TO_EDGE;

		glGenTextures(1, &cs->sky.name);
		glBindTexture(cs->sky.target, cs->sky.name);

		glTexParameteri(cs->sky.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(cs->sky.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		glTexParameteri(cs->sky.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(cs->sky.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		glTexImage2D(cs->sky.target,
					 0,
					 GL_RGB, 128, 128, 0, GL_RGB, GL_FLOAT, aaafTextureData);

		glBindTexture(cs->sky.target, 0);
	}
}

static Bool fillCircleTable(GLfloat ** ppSint, GLfloat ** ppCost, const int n)
{
	const GLfloat angle = 2 * M_PI / (GLfloat) ((n == 0) ? 1 : n);
	const int size = abs(n);
	int i;

	*ppSint = (GLfloat *) calloc(sizeof(GLfloat), size + 1);
	*ppCost = (GLfloat *) calloc(sizeof(GLfloat), size + 1);

	if (!(*ppSint) || !(*ppCost))
	{
		free(*ppSint);
		free(*ppCost);

		return FALSE;
	}

	(*ppSint)[0] = 0.0;
	(*ppCost)[0] = 1.0;

	for (i = 1; i < size; i++)
	{
		(*ppSint)[i] = sin(angle * i);
		(*ppCost)[i] = cos(angle * i);
	}

	(*ppSint)[size] = (*ppSint)[0];
	(*ppCost)[size] = (*ppCost)[0];

	return TRUE;
}

static void cubeUpdateSkyDomeList(CompScreen * s, GLfloat fRadius)
{
	GLint iSlices = 128;
	GLint iStacks = 64;
	GLfloat afTexCoordX[4];
	GLfloat afTexCoordY[4];
	GLfloat *sint1;
	GLfloat *cost1;
	GLfloat *sint2;
	GLfloat *cost2;
	GLfloat r;
	GLfloat x;
	GLfloat y;
	GLfloat z;
	int i;
	int j;
	int iStacksStart;
	int iStacksEnd;
	int iSlicesStart;
	int iSlicesEnd;
	GLfloat fStepX;
	GLfloat fStepY;

	CUBE_SCREEN(s);

	if (cs->animateSkyDome)
	{
		iStacksStart = 11;		/* min.   0 */
		iStacksEnd = 53;		/* max.  64 */
		iSlicesStart = 0;		/* min.   0 */
		iSlicesEnd = 128;		/* max. 128 */
	}
	else
	{
		iStacksStart = 21;		/* min.   0 */
		iStacksEnd = 43;		/* max.  64 */
		iSlicesStart = 21;		/* min.   0 */
		iSlicesEnd = 44;		/* max. 128 */
	}

	fStepX = 1.0 / (GLfloat) (iSlicesEnd - iSlicesStart);
	fStepY = 1.0 / (GLfloat) (iStacksEnd - iStacksStart);

	if (!fillCircleTable(&sint1, &cost1, -iSlices))
		return;

	if (!fillCircleTable(&sint2, &cost2, iStacks * 2))
	{
		free(sint1);
		free(cost1);
		return;
	}

	afTexCoordX[0] = 1.0f;
	afTexCoordY[0] = fStepY;
	afTexCoordX[1] = 1.0f - fStepX;
	afTexCoordY[1] = fStepY;
	afTexCoordX[2] = 1.0f - fStepX;
	afTexCoordY[2] = 0.0f;
	afTexCoordX[3] = 1.0f;
	afTexCoordY[3] = 0.0f;

	if (!cs->skyListId)
		cs->skyListId = glGenLists(1);

	glNewList(cs->skyListId, GL_COMPILE);

	enableTexture(s, &cs->sky, COMP_TEXTURE_FILTER_GOOD);

	glBegin(GL_QUADS);

	for (i = iStacksStart; i < iStacksEnd; i++)
	{
		afTexCoordX[0] = 1.0f;
		afTexCoordX[1] = 1.0f - fStepX;
		afTexCoordX[2] = 1.0f - fStepX;
		afTexCoordX[3] = 1.0f;

		for (j = iSlicesStart; j < iSlicesEnd; j++)
		{
			/* bottom-right */
			z = cost2[i];
			r = sint2[i];
			x = cost1[j];
			y = sint1[j];

			glTexCoord2f(afTexCoordX[3], afTexCoordY[3]);
			glVertex3f(x * r * fRadius, y * r * fRadius, z * fRadius);

			/* top-right */
			z = cost2[i + 1];
			r = sint2[i + 1];
			x = cost1[j];
			y = sint1[j];

			glTexCoord2f(afTexCoordX[0], afTexCoordY[0]);
			glVertex3f(x * r * fRadius, y * r * fRadius, z * fRadius);

			/* top-left */
			z = cost2[i + 1];
			r = sint2[i + 1];
			x = cost1[j + 1];
			y = sint1[j + 1];

			glTexCoord2f(afTexCoordX[1], afTexCoordY[1]);
			glVertex3f(x * r * fRadius, y * r * fRadius, z * fRadius);

			/* bottom-left */
			z = cost2[i];
			r = sint2[i];
			x = cost1[j + 1];
			y = sint1[j + 1];

			glTexCoord2f(afTexCoordX[2], afTexCoordY[2]);
			glVertex3f(x * r * fRadius, y * r * fRadius, z * fRadius);

			afTexCoordX[0] -= fStepX;
			afTexCoordX[1] -= fStepX;
			afTexCoordX[2] -= fStepX;
			afTexCoordX[3] -= fStepX;
		}

		afTexCoordY[0] += fStepY;
		afTexCoordY[1] += fStepY;
		afTexCoordY[2] += fStepY;
		afTexCoordY[3] += fStepY;
	}

	glEnd();

	disableTexture(s, &cs->sky);

	glEndList();

	free(sint1);
	free(cost1);
	free(sint2);
	free(cost2);
}

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

	CUBE_SCREEN(screen);

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

	switch (index)
	{
	case CUBE_SCREEN_OPTION_COLOR:
		if (compSetColorOption(o, value))
		{
			memcpy(cs->color, o->value.c, sizeof(cs->color));
			damageScreen(screen);
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_IN:
		if (compSetBoolOption(o, value))
		{
			if (cubeUpdateGeometry
				(screen, screen->hsize, o->value.b ? -1 : 1))
			{
				IPCS_SetBool(IPCS_OBJECT(screen), cs->insideAtom, o->value.b);

				return TRUE;
			}
		}
		break;
	case CUBE_SCREEN_OPTION_SCALE_IMAGE_TOP:
		if (compSetBoolOption(o, value))
		{
			cs->cubeCapTop.scale = o->value.b;
			cubeLoadImg(screen, &cs->cubeCapTop, cs->cubeCapTop.imgCurFile);
			damageScreen(screen);

			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_SCALE_IMAGE_BOTTOM:
		if (compSetBoolOption(o, value))
		{
			cs->cubeCapBottom.scale = o->value.b;
			cubeLoadImg(screen, &cs->cubeCapBottom,
						cs->cubeCapBottom.imgCurFile);
			damageScreen(screen);

			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_IMAGES_ROTATE_MODE:
		if (compSetStringOption(o, value))
		{
			int i;
			ImagesRotateMode mode = IMAGES_ROTATE_MODE_DEFAULT;

			for (i = 0; i < o->rest.s.nString; i++)
				if (strcmp(imagesRotateModes[i], o->value.s) == 0)
					mode = (ImagesRotateMode) i;
			switch (mode)
			{
			case ImagesRotateModeAfterMoving:
				cs->neverRotateImages = FALSE;
				cs->rotateImages = FALSE;
				break;
			case ImagesRotateModeWhileMoving:
				cs->neverRotateImages = FALSE;
				cs->rotateImages = TRUE;
				break;
			case ImagesRotateModeNever:
			default:
				cs->neverRotateImages = TRUE;
				cs->rotateImages = FALSE;
				break;
			}
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_MULTIMONITOR:
		if (compSetStringOption(o, value))
		{
			int i;
			MultiMonitorMode mode = MULTIMONITOR_MODE_DEFAULT;

			for (i = 0; i < o->rest.s.nString; i++)
				if (strcmp(multiMonitorModes[i], o->value.s) == 0)
					mode = (MultiMonitorMode) i;
			cs->mmMode = mode;
			if (cs->mmMode == OneBig)
				screen->projectionStyle = COMP_PERSPECTIVE_GLOBAL;
			else
				screen->projectionStyle = COMP_PERSPECTIVE_LOCAL;
			cubeUpdateOutputs(screen);
			cubeUpdateGeometry(screen, screen->hsize, cs->invert);
			IPCS_SetInt(IPCS_OBJECT(screen), cs->mmModeAtom, cs->mmMode);
			if (cs->opt[CUBE_SCREEN_OPTION_DRAW_CAPS].value.b)
			{
				cubeLoadImg(screen, &cs->cubeCapBottom,
							cs->cubeCapBottom.imgCurFile);
				cubeLoadImg(screen, &cs->cubeCapTop,
							cs->cubeCapTop.imgCurFile);
			}
			damageScreen(screen);
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_IMAGES_TOP:
		if (compSetOptionList(o, value))
		{
			cs->cubeCapTop.imgFiles =
					cs->opt[CUBE_SCREEN_OPTION_IMAGES_TOP].value.list.value;
			cs->cubeCapTop.imgNFile =
					cs->opt[CUBE_SCREEN_OPTION_IMAGES_TOP].value.list.nValue;

			cubeLoadImg(screen, &cs->cubeCapTop, cs->cubeCapTop.imgCurFile);

			damageScreen(screen);

			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_IMAGES_BOTTOM:
		if (compSetOptionList(o, value))
		{
			cs->cubeCapBottom.imgFiles =
					cs->opt[CUBE_SCREEN_OPTION_IMAGES_BOTTOM].value.list.
					value;
			cs->cubeCapBottom.imgNFile =
					cs->opt[CUBE_SCREEN_OPTION_IMAGES_BOTTOM].value.list.
					nValue;

			cubeLoadImg(screen, &cs->cubeCapBottom,
						cs->cubeCapBottom.imgCurFile);
			damageScreen(screen);

			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_SKYDOME:
		if (compSetBoolOption(o, value))
		{
			cubeUpdateSkyDomeTexture(screen);
			cubeUpdateSkyDomeList(screen, 1.0f);
			damageScreen(screen);
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_SKYDOME_IMG:
		if (compSetStringOption(o, value))
		{
			cubeUpdateSkyDomeTexture(screen);
			cubeUpdateSkyDomeList(screen, 1.0f);
			damageScreen(screen);
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_SKYDOME_ANIM:
		if (compSetBoolOption(o, value))
		{
			cs->animateSkyDome = o->value.b;
			cubeUpdateSkyDomeTexture(screen);
			cubeUpdateSkyDomeList(screen, 1.0f);
			damageScreen(screen);
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_SKYDOME_GRAD_START:
		if (compSetColorOption(o, value))
		{
			memcpy(cs->skyGradStartColor, o->value.c,
				   sizeof(cs->skyGradStartColor));
			cubeUpdateSkyDomeTexture(screen);
			cubeUpdateSkyDomeList(screen, 1.0f);
			damageScreen(screen);
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_SKYDOME_GRAD_END:
		if (compSetColorOption(o, value))
		{
			memcpy(cs->skyGradEndColor, o->value.c,
				   sizeof(cs->skyGradEndColor));
			cubeUpdateSkyDomeTexture(screen);
			cubeUpdateSkyDomeList(screen, 1.0f);
			damageScreen(screen);
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_ACCELERATION:
		if (compSetFloatOption(o, value))
		{
			cs->acceleration = o->value.f;
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_SPEED:
		if (compSetFloatOption(o, value))
		{
			cs->speed = o->value.f;
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_TIMESTEP:
		if (compSetFloatOption(o, value))
		{
			cs->timestep = o->value.f;
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_MIPMAP:
		if (compSetBoolOption(o, value))
			return TRUE;
		break;
	case CUBE_SCREEN_OPTION_UNFOLD_ZOOM_DISTANCE:
		if (compSetFloatOption(o, value))
		{
			cs->unfoldDistance = o->value.f;
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_FADE_TIME:
		if (compSetFloatOption(o, value))
		{
			return TRUE;
		}
		break;

	case CUBE_SCREEN_OPTION_ACTIVE_OPACITY:
	case CUBE_SCREEN_OPTION_INACTIVE_OPACITY:
		if (compSetIntOption(o, value))
		{
			return TRUE;
		}
		break;
	case CUBE_SCREEN_OPTION_TRANSPARENT:
	case CUBE_SCREEN_OPTION_STUCK_TO_SCREEN:
	case CUBE_SCREEN_OPTION_CAP_TRANSPARENT:
	case CUBE_SCREEN_OPTION_VIEWPORT_SLIDE:
	case CUBE_TRANSPARENT_MANUALONLY:
	case CUBE_SCREEN_OPTION_VIEWPORT_SLIDE_NO3D:
	case CUBE_SCREEN_OPTION_DRAW_CAPS:
		if (compSetBoolOption(o, value))
			return TRUE;
		break;
	default:
		break;
	}

	return FALSE;
}

static void cubeScreenInitOptions(CubeScreen * cs)
{
	CompOption *o;
	int i;

	o = &cs->opt[CUBE_SCREEN_OPTION_COLOR];
	o->advanced = False;
	o->name = "color";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Cube Color");
	o->longDesc = N_("Color of top and bottom sides of the Cube.");
	o->type = CompOptionTypeColor;
	o->value.c[0] = CUBE_COLOR_RED_DEFAULT;
	o->value.c[1] = CUBE_COLOR_GREEN_DEFAULT;
	o->value.c[2] = CUBE_COLOR_BLUE_DEFAULT;
	o->value.c[3] = 0xffff;

	o = &cs->opt[CUBE_SCREEN_OPTION_IN];
	o->advanced = False;
	o->name = "in";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Inside Cube");
	o->longDesc = N_("Change perspective to inside the Cube, looking out.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_IN_DEFAULT;

	o = &cs->opt[CUBE_TRANSPARENT_MANUALONLY];
	o->advanced = False;
	o->name = "manualonly";
	o->group = N_("Transparency");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Transparency Only\non Mouse Rotate");
	o->longDesc =
			N_
			("Initiates Cube transparency only if rotation is mouse driven.");
	o->type = CompOptionTypeBool;
	o->value.b = FALSE;

	o = &cs->opt[CUBE_SCREEN_OPTION_SCALE_IMAGE_TOP];
	o->advanced = False;
	o->name = "scale_image_top";
	o->group = N_("Caps");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Scale Image on Top");
	o->longDesc = N_("Scale images to cover top face of Cube.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_SCALE_IMAGE_TOP_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_IMAGES_TOP];
	o->advanced = False;
	o->name = "images_top";
	o->group = N_("Caps");
	o->subGroup = N_("");
	o->displayHints = "file;image;";
	o->shortDesc = N_("Image Files on Top");
	o->longDesc =
			N_("List of JPEG, PNG and SVG files that should be "
				"rendered on the top face of Cube.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = N_CUBE_IMAGES;
	o->value.list.value = malloc(sizeof(CompOptionValue) * N_CUBE_IMAGES);
	for (i = 0; i < N_CUBE_IMAGES; i++)
		o->value.list.value[i].s = strdup(cubeImages[i]);
	o->rest.s.string = 0;
	o->rest.s.nString = 0;

	o = &cs->opt[CUBE_SCREEN_OPTION_SCALE_IMAGE_BOTTOM];
	o->advanced = False;
	o->name = "scale_image_bottom";
	o->group = N_("Caps");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Scale Image on Bottom");
	o->longDesc = N_("Scale images to cover bottom face of Cube.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_SCALE_IMAGE_BOTTOM_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_IMAGES_ROTATE_MODE];
	o->advanced = False;
	o->name = "images_rotate_mode";
	o->group = N_("Caps");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Images Rotate Mode");
	o->longDesc =
			N_("Select between never, after moving and while moving mode.");
	o->type = CompOptionTypeString;
	o->value.s = strdup(imagesRotateModes[IMAGES_ROTATE_MODE_DEFAULT]);
	o->rest.s.string = imagesRotateModes;
	o->rest.s.nString = NUM_IMAGES_ROTATE_MODES;

	o = &cs->opt[CUBE_SCREEN_OPTION_IMAGES_BOTTOM];
	o->advanced = False;
	o->name = "images_bottom";
	o->group = N_("Caps");
	o->subGroup = N_("");
	o->displayHints = "file;image;";
	o->shortDesc = N_("Image Files on Bottom");
	o->longDesc =
			N_("List of JPEG, PNG and SVG files that should be "
				"rendered on the bottom face of Cube.");
	o->type = CompOptionTypeList;
	o->value.list.type = CompOptionTypeString;
	o->value.list.nValue = N_CUBE_IMAGES;
	o->value.list.value = malloc(sizeof(CompOptionValue) * N_CUBE_IMAGES);
	for (i = 0; i < N_CUBE_IMAGES; i++)
		o->value.list.value[i].s = strdup(cubeImages[i]);
	o->rest.s.string = 0;
	o->rest.s.nString = 0;

	o = &cs->opt[CUBE_SCREEN_OPTION_SKYDOME];
	o->advanced = False;
	o->name = "skydome";
	o->group = N_("Skydome");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Skydome");
	o->longDesc = N_("Background image, shown behind the Cube.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_SKYDOME_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_SKYDOME_IMG];
	o->advanced = False;
	o->name = "skydome_image";
	o->group = N_("Skydome");
	o->subGroup = N_("");
	o->displayHints = "file;image";
	o->shortDesc = N_("Skydome Image");
	o->longDesc =
			N_
			("Image to use for the Skydome. Dimensions must be a "
			 "power of two (i.e. 1024, 2048, 4096....");
	o->type = CompOptionTypeString;
	o->value.s = strdup("");
	o->rest.s.string = 0;
	o->rest.s.nString = 0;

	o = &cs->opt[CUBE_SCREEN_OPTION_SKYDOME_ANIM];
	o->advanced = False;
	o->name = "skydome_animated";
	o->group = N_("Skydome");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Animate Skydome");
	o->longDesc =
			N_
			("Animate Skydome when rotating Cube giving the appearance that you (not the cube) are moving.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_SKYDOME_ANIMATE_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_START];
	o->advanced = False;
	o->name = "skydome_gradient_start_color";
	o->group = N_("Skydome");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Skydome Gradient Start Color");
	o->longDesc =
			N_
			("Color to use for the top color-stop of the Skydome-fallback gradient.");
	o->type = CompOptionTypeColor;
	o->value.c[0] = CUBE_SKYDOME_GRAD_START_RED_DEFAULT;
	o->value.c[1] = CUBE_SKYDOME_GRAD_START_GREEN_DEFAULT;
	o->value.c[2] = CUBE_SKYDOME_GRAD_START_BLUE_DEFAULT;
	o->value.c[3] = 0xffff;

	o = &cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_END];
	o->advanced = False;
	o->name = "skydome_gradient_end_color";
	o->group = N_("Skydome");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Skydome Gradient End Color");
	o->longDesc =
			N_
			("Color to use for the bottom color-stop of the Skydome-fallback gradient.");
	o->type = CompOptionTypeColor;
	o->value.c[0] = CUBE_SKYDOME_GRAD_END_RED_DEFAULT;
	o->value.c[1] = CUBE_SKYDOME_GRAD_END_GREEN_DEFAULT;
	o->value.c[2] = CUBE_SKYDOME_GRAD_END_BLUE_DEFAULT;
	o->value.c[3] = 0xffff;

	o = &cs->opt[CUBE_SCREEN_OPTION_ACCELERATION];
	o->advanced = False;
	o->name = "acceleration";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Fold Acceleration");
	o->longDesc = N_("Fold acceleration.");
	o->type = CompOptionTypeFloat;
	o->value.f = CUBE_ACCELERATION_DEFAULT;
	o->rest.f.min = CUBE_ACCELERATION_MIN;
	o->rest.f.max = CUBE_ACCELERATION_MAX;
	o->rest.f.precision = CUBE_ACCELERATION_PRECISION;

	o = &cs->opt[CUBE_SCREEN_OPTION_SPEED];
	o->advanced = False;
	o->name = "speed";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Fold Speed");
	o->longDesc = N_("Fold speed.");
	o->type = CompOptionTypeFloat;
	o->value.f = CUBE_SPEED_DEFAULT;
	o->rest.f.min = CUBE_SPEED_MIN;
	o->rest.f.max = CUBE_SPEED_MAX;
	o->rest.f.precision = CUBE_SPEED_PRECISION;

	o = &cs->opt[CUBE_SCREEN_OPTION_TIMESTEP];
	o->advanced = False;
	o->name = "timestep";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Fold Timestep");
	o->longDesc = N_("Fold timestep.");
	o->type = CompOptionTypeFloat;
	o->value.f = CUBE_TIMESTEP_DEFAULT;
	o->rest.f.min = CUBE_TIMESTEP_MIN;
	o->rest.f.max = CUBE_TIMESTEP_MAX;
	o->rest.f.precision = CUBE_TIMESTEP_PRECISION;

	o = &cs->opt[CUBE_SCREEN_OPTION_MIPMAP];
	o->advanced = False;
	o->name = "mipmap";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Mipmap");
	o->longDesc =
			N_("Generate Mipmaps when possible for higher quality scaling.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_MIPMAP_DEFAULT;


	o = &cs->opt[CUBE_SCREEN_OPTION_VIEWPORT_SLIDE];
	o->advanced = False;
	o->name = "change_viewport_slide";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Slide When Changing Viewports\nInstead of Rotating.");
	o->longDesc = N_("Slide when changing viewports instead of rotating.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_VIEWPORT_SLIDE_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_VIEWPORT_SLIDE_NO3D];
	o->advanced = False;
	o->name = "change_viewport_slide_no3d";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Keep the Cube Planar Even\nWhen Mouse Grabbing.");
	o->longDesc = N_("Keep the Cube planar even when mouse grabbing.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_VIEWPORT_SLIDE_NO3D_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_DRAW_CAPS];
	o->advanced = False;
	o->name = "draw_caps";
	o->group = N_("Caps");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Draw Caps");
	o->longDesc =
			N_("Draw the Cube 'caps'; uncheck to keep them transparent.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_DRAW_CAPS_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_MULTIMONITOR];
	o->advanced = False;
	o->name = "multimonitor_mode";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("MultiMonitor Mode");
	o->longDesc = N_("Multi monitor mode behavior.");
	o->type = CompOptionTypeString;
	o->value.s = strdup(multiMonitorModes[MULTIMONITOR_MODE_DEFAULT]);
	o->rest.s.string = multiMonitorModes;
	o->rest.s.nString = NUM_MULTIMONITOR_MODES;


	o = &cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT];
	o->advanced = False;
	o->name = "transparent";
	o->group = N_("Transparency");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Transparent Cube");
	o->longDesc = N_("Change desktop window opacity and draw complete Cube.");
	o->type = CompOptionTypeBool;
	o->value.b = CUBE_TRANSPARENT_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_STUCK_TO_SCREEN];
	o->advanced = False;
	o->name = "stuck_to_screen";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Stick Sticky Windows to Screen");
	o->longDesc = N_("Cause dock and sticky windows to be drawn stuck to the screen instead of the Cube.");
	o->type = CompOptionTypeBool;
	o->value.b = FALSE;

	o = &cs->opt[CUBE_SCREEN_OPTION_CAP_TRANSPARENT];
	o->advanced = False;
	o->name = "cap_transparent_snap";
	o->group = N_("Transparency");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Activate Transparancy on\nTop/Bottom Snap");
	o->longDesc =
			N_("Treat the Cube opacity as if the Cube was active when the "
			   "Cube is snapped to the top/bottom caps. ");
	o->type = CompOptionTypeBool;
	o->value.b = CAP_TRANSPARENT_DEFAULT;

	o = &cs->opt[CUBE_SCREEN_OPTION_FADE_TIME];
	o->advanced = False;
	o->name = "fade_time";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Fade Time");
	o->longDesc = N_("Desktop Window Opacity Fade Time.");
	o->type = CompOptionTypeFloat;
	o->value.f = CUBE_FADE_TIME_DEFAULT;
	o->rest.f.min = CUBE_FADE_TIME_MIN;
	o->rest.f.max = CUBE_FADE_TIME_MAX;
	o->rest.f.precision = CUBE_FADE_TIME_PRECISION;

	o = &cs->opt[CUBE_SCREEN_OPTION_ACTIVE_OPACITY];
	o->advanced = False;
	o->name = "active_opacity";
	o->group = N_("Transparency");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Opacity During Move");
	o->longDesc = N_("Opacity of desktop window during move.");
	o->type = CompOptionTypeInt;
	o->value.i = CUBE_ACTIVE_OPACITY_DEFAULT;
	o->rest.i.min = 0;
	o->rest.i.max = 100;

	o = &cs->opt[CUBE_SCREEN_OPTION_INACTIVE_OPACITY];
	o->advanced = False;
	o->name = "inactive_opacity";
	o->group = N_("Transparency");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Opacity When Not Moving");
	o->longDesc = N_("Opacity of desktop window when not moving.");
	o->type = CompOptionTypeInt;
	o->value.i = CUBE_INACTIVE_OPACITY_DEFAULT;
	o->rest.i.min = 0;
	o->rest.i.max = 100;
	o = &cs->opt[CUBE_SCREEN_OPTION_UNFOLD_ZOOM_DISTANCE];
	o->advanced = False;
	o->name = "unfold_distancee";
	o->group = N_("Options");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Unfold Zoomback Distance");
	o->longDesc = N_("Distance to zoom back when Unfolded.");
	o->type = CompOptionTypeFloat;
	o->value.f = CUBE_UNFOLD_ZOOM_DEFAULT;
	o->rest.f.min = CUBE_UNFOLD_ZOOM_MIN;
	o->rest.f.max = CUBE_UNFOLD_ZOOM_MAX;
	o->rest.f.precision = CUBE_UNFOLD_ZOOM_PRECISION;

}

static CompOption *cubeGetScreenOptions(CompScreen * screen, int *count)
{
	if (screen)
	{
		CUBE_SCREEN(screen);

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

		cubeScreenInitOptions(cs);
		*count = NUM_OPTIONS(cs);
		return cs->opt;
	}
}

static int adjustVelocity(CubeScreen * cs)
{
	float unfold, adjust, amount;

	if (cs->unfolded)
		unfold = 1.0f - cs->unfold;
	else
		unfold = 0.0f - cs->unfold;

	adjust = unfold * 0.02f * cs->acceleration;
	amount = fabs(unfold);
	if (amount < 1.0f)
		amount = 1.0f;
	else if (amount > 3.0f)
		amount = 3.0f;

	cs->unfoldVelocity = (amount * cs->unfoldVelocity + adjust) /
			(amount + 2.0f);

	return (fabs(unfold) < 0.002f && fabs(cs->unfoldVelocity) < 0.01f);
}

static void cubePreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
	CUBE_SCREEN(s);

	if (cs->grabIndex)
	{
		int steps;
		float amount, chunk;

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

		while (steps--)
		{
			cs->unfold += cs->unfoldVelocity * chunk;
			if (cs->unfold > 1.0f)
				cs->unfold = 1.0f;

			if (adjustVelocity(cs))
			{
				if (cs->unfold < 0.5f)
				{
					if (cs->grabIndex)
					{
						removeScreenGrab(s, cs->grabIndex, NULL);
						cs->grabIndex = 0;
					}

					cs->unfold = 0.0f;
				}
				break;
			}
		}
	}
	/* Transparency */
	if (cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT].value.b)
	{


		if (screenGrabExist(s, "rotate", 0) && (IPCS_GetBool(IPCS_OBJECT(s), cs->manualAtom) || !(cs->opt[CUBE_TRANSPARENT_MANUALONLY].value.b)))	/* TODO: Add key to set boolean to toggle drawing at active opacity */
		{

			if (!cs->opt[CUBE_SCREEN_OPTION_CAP_TRANSPARENT].value.b
				&& IPCS_GetBool(IPCS_OBJECT(s), cs->snapTopBottomAtom))
				cs->toOpacity = cs->
						opt[CUBE_SCREEN_OPTION_INACTIVE_OPACITY].value.i;
			else
				cs->toOpacity = cs->
						opt[CUBE_SCREEN_OPTION_ACTIVE_OPACITY].value.i;


		}
		else
			cs->toOpacity =
					cs->opt[CUBE_SCREEN_OPTION_INACTIVE_OPACITY].value.i;

		cs->toOpacity = cs->toOpacity / 100.0 * 0xffff;
	}
	else
	{
		cs->toOpacity = OPAQUE;
	}

	if (cs->opt[CUBE_SCREEN_OPTION_FADE_TIME].value.f == 0.0f)
	{
		cs->desktopOpacity = cs->toOpacity;
	}
	else
	{
		float steps = (msSinceLastPaint * OPAQUE / 1000.0) /
				cs->opt[CUBE_SCREEN_OPTION_FADE_TIME].value.f;
		if (steps < 12)
			steps = 12;

		if (cs->toOpacity > cs->desktopOpacity)
		{
			cs->desktopOpacity += steps;
			cs->desktopOpacity = MIN(cs->toOpacity, cs->desktopOpacity);
		}
		if (cs->toOpacity < cs->desktopOpacity)
		{
			cs->desktopOpacity -= steps;
			cs->desktopOpacity = MAX(cs->toOpacity, cs->desktopOpacity);
		}
	}

	UNWRAP(cs, s, preparePaintScreen);
	(*s->preparePaintScreen) (s, msSinceLastPaint);
	WRAP(cs, s, preparePaintScreen, cubePreparePaintScreen);

}

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

	CUBE_SCREEN(s);

	if (cs->grabIndex
		|| (cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT].value.b
			&& ((cs->desktopOpacity != OPAQUE || s->berylDesktopManaged) || s->berylDesktopManaged)))
		mask &= ~PAINT_SCREEN_REGION_MASK;
	cs->finalPaint=TRUE;

	if (cs->grabIndex)
	{
		cs->finalPaint=!(cs->opt[CUBE_SCREEN_OPTION_STUCK_TO_SCREEN].value.b);
		mask |= PAINT_SCREEN_TRANSFORMED_MASK;
	}

	if ((!mask & PAINT_SCREEN_TRANSFORMED_MASK) &&
		(cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT].value.b &&
		 ((cs->desktopOpacity != OPAQUE || s->berylDesktopManaged) || s->berylDesktopManaged)))
	{
		cs->finalPaint=!(cs->opt[CUBE_SCREEN_OPTION_STUCK_TO_SCREEN].value.b);
		if (cs->sky.name)
		{
			screenLighting(s, FALSE);

			glPushMatrix();

			if (cs->mmMode == OneBig)
			{
				glScalef(cs->outputXScale, cs->outputYScale, 1);
				glTranslatef(cs->outputXOffset / cs->outputXScale,
							 -cs->outputYOffset / cs->outputYScale, 0);
			}
			if (cs->animateSkyDome)	//  && cs->grabIndex == 0)
			{
				glRotatef(sAttrib->xRotate -
						  ((s->x * 360.0f) / s->hsize), 0.0f, 1.0f, 0.0f);
				glRotatef(sAttrib->vRotate / 5.0f + 90.0f, 1.0f, 0.0f, 0.0f);
			}
			else
			{
				glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
			}

			glCallList(cs->skyListId);
			glPopMatrix();
		}
		else
		{
				glClear(GL_COLOR_BUFFER_BIT);
		}
		mask &= ~PAINT_SCREEN_CLEAR_MASK;
	}

	UNWRAP(cs, s, paintScreen);
	status = (*s->paintScreen) (s, sAttrib, region, output, mask);
	WRAP(cs, s, paintScreen, cubePaintScreen);

	if (!cs->finalPaint)
	{
		glPushMatrix();

		prepareXCoords(s, output, -DEFAULT_Z_CAMERA);
		cs->finalPaint=TRUE;
		CompWindow * w;
		for (w=s->reverseWindows;w;w=w->prev)
		{
			if (((w->type & CompWindowTypeDockMask) || (w->state & CompWindowStateStickyMask && !(w->state & CompWindowStateBelowMask)) ||
						w->attrib.override_redirect) &&
				(w->mapNum && w->attrib.map_state == IsViewable && !(w->minimized) && !(w->state & CompWindowStateHiddenMask)))
			{
				s->paintWindow(w,&w->paint,getInfiniteRegion(),0);
			}
		}
		glPopMatrix();
	}

	return status;
}

static void cubeDonePaintScreen(CompScreen * s)
{
	CUBE_SCREEN(s);

	if (cs->grabIndex || cs->toOpacity != cs->desktopOpacity)
		damageScreen(s);

	UNWRAP(cs, s, donePaintScreen);
	(*s->donePaintScreen) (s);
	WRAP(cs, s, donePaintScreen, cubeDonePaintScreen);
}

typedef struct _CubeFacePaint
{
	int xMove;
	ScreenPaintAttrib sa;
	float z;
	Bool rev;
	Bool painted;
} CubeFacePaint;

static void
paintReversed(CompScreen * s,
			  const ScreenPaintAttrib * sA,
			  int output, int xMove, CubeFacePaint * face)
{
	CUBE_SCREEN(s);

	float pm[16];
	float mvm[16];
	float mvp[16];

	glPushMatrix();				//Get the matrices.
	// inputzoom should not scale here
	cs->paintingCaps = TRUE;
	(s->applyScreenTransform) (s, sA, output);
	prepareXCoords(s, output, -sA->zTranslate);
	cs->paintingCaps = FALSE;
	glGetFloatv(GL_MODELVIEW_MATRIX, mvm);
	glGetFloatv(GL_PROJECTION_MATRIX, pm);

	MULTM(pm, mvm, mvp);

	glPopMatrix();

	float pntA[4] = { s->outputDev[output].region.extents.x1,
		s->outputDev[output].region.extents.y1, 0, 1
	};
	float pntB[4] = { s->outputDev[output].region.extents.x2,
		s->outputDev[output].region.extents.y1, 0, 1
	};
	float pntC[4] = { s->outputDev[output].region.extents.x1 +
				s->outputDev[output].width / 2.0f,
		s->outputDev[output].region.extents.y1 +
				s->outputDev[output].height / 2.0f, 0, 1
	};

	MULTMV(mvp, pntA);
	DIVV(pntA);
	MULTMV(mvp, pntB);
	DIVV(pntB);
	MULTMV(mvp, pntC);
	DIVV(pntC);

	float vecA[3] =
			{ pntC[0] - pntA[0], pntC[1] - pntA[1], pntC[2] - pntA[2] };
	float vecB[3] =
			{ pntC[0] - pntB[0], pntC[1] - pntB[1], pntC[2] - pntB[2] };

	float normal[3] = { vecA[1] * vecB[2] - vecA[2] * vecB[1],
		vecA[2] * vecB[0] - vecA[0] * vecB[2],
		vecA[0] * vecB[1] - vecA[1] * vecB[0]
	};

	float length = sqrt(normal[0] * normal[0] +
						normal[1] * normal[1] + normal[2] * normal[2]);

	normal[0] /= length;
	normal[1] /= length;
	normal[2] /= length;

	face->sa = *sA;
	face->xMove = xMove;
	face->z = pntC[2];
	face->rev = FALSE;

	if (normal[2] > 0.0)
	{
		face->rev = TRUE;
	}
}

static Bool capsReversed(void)
{
	float pm[16];
	float mvm[16];
	float mvp[16];

	glGetFloatv(GL_MODELVIEW_MATRIX, mvm);
	glGetFloatv(GL_PROJECTION_MATRIX, pm);

	MULTM(pm, mvm, mvp);


	float pntA[4] = { 0.0, 0.5, 0.0, 1 };
	float pntB[4] = { 0.0, -0.5, 0.0, 1 };

	MULTMV(mvp, pntA);
	DIVV(pntA);
	MULTMV(mvp, pntB);
	DIVV(pntB);

	float vec[3] =
			{ pntB[0] - pntA[0], pntB[1] - pntA[1], pntB[2] - pntA[2] };

	if (vec[2] > 0.0)
	{
		return TRUE;
	}

	return FALSE;
}

static void
cubeMoveViewportAndPaint(CompScreen * s,
						 const ScreenPaintAttrib * sAttrib,
						 int output, unsigned int mask, int dx)
{
	CUBE_SCREEN(s);

	Bool save;
	save=cs->finalPaint;

	if (cs->nOutput > 1)
	{
		int cubeOutput, dView;

		/* translate to cube output */
		cubeOutput = cs->outputMask[output];

		/* convert from window movement to viewport movement */
		dView = -dx;

		cubeOutput += dView;

		if (cs->mmMode != Multiple)
		{
			dView = cubeOutput / cs->nOutput;
			cubeOutput = cubeOutput % cs->nOutput;
			if (cubeOutput < 0)
			{
				cubeOutput += cs->nOutput;
				dView--;
			}

			/* translate back to beryl output */
			output = cs->output[cubeOutput];
		}

		if (dView != 0)
			cs->finalPaint=!(cs->opt[CUBE_SCREEN_OPTION_STUCK_TO_SCREEN].value.b);
		if (dView != 0 && !screenGrabExist(s, "rotate", 0) &&

			(((cs->desktopOpacity != OPAQUE || s->berylDesktopManaged))))


			cs->noManaged = TRUE;
		moveScreenViewport(s, -dView, 0, FALSE);
		(*s->paintTransformedScreen) (s, sAttrib,
									  &s->outputDev[output].region,
									  output, mask);
		moveScreenViewport(s, dView, 0, FALSE);
	}
	else
	{
		if (dx != 0)
			cs->finalPaint=!(cs->opt[CUBE_SCREEN_OPTION_STUCK_TO_SCREEN].value.b);
		if (dx != 0 && !screenGrabExist(s, "rotate", 0) &&
			(cs->desktopOpacity != OPAQUE || s->berylDesktopManaged))
			cs->noManaged = TRUE;
		moveScreenViewport(s, dx, 0, FALSE);
		(*s->paintTransformedScreen) (s, sAttrib, &s->region, output, mask);
		moveScreenViewport(s, -dx, 0, FALSE);
	}
	cs->finalPaint=save;
	cs->noManaged = FALSE;
}

static void
cubeDrawCubeCap(CompScreen * s, int hsize, int arrayOffset,
				CubeCapInfo * capOutside, CubeCapInfo * capInside,
				unsigned short opacity)
{
	
	CubeCapInfo *cubeCap;

	CUBE_SCREEN(s);

	if (cs->invert == 1)
		cubeCap = capOutside;
	else if (cs->invert != 1)
		cubeCap = capInside;
	else
		cubeCap = NULL;

	glColor4us(cs->color[0], cs->color[1], cs->color[2],
			opacity);
	glDrawArrays(GL_TRIANGLE_FAN, arrayOffset, cs->nvertices >> 1);
	/*if (cubeCap && cubeCap->texture.name && s->hsize == 4)
	{
		if (opacity != OPAQUE)
		{
			glColor4us(0xffff, 0xffff, 0xffff, opacity);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		}
		enableTexture(s, &cubeCap->texture, COMP_TEXTURE_FILTER_GOOD);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		// as GL expects the texture coords with offset, but our
		//   texture coord array is 0-based, we have to subtract the offset here
		glTexCoordPointer(2, GL_FLOAT, 0, cubeCap->tc - (arrayOffset << 1));
		glDrawArrays(GL_TRIANGLE_FAN, arrayOffset, cs->nvertices >> 1);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		disableTexture(s, &cubeCap->texture);
		screenTexEnvMode(s, GL_REPLACE);
		
	}*/
	if (cubeCap && cubeCap->texture.name)
	{
		//screenTexEnvMode(s, GL_REPLACE);
		enableTexture(s, &cubeCap->texture, COMP_TEXTURE_FILTER_GOOD);

		int centerx = *cs->vertices;
		int centery = *(cs->vertices+1);
		int centerz = *(cs->vertices+2);
		
		glColor4f(1.0,1.0,1.0,1.0);
		if (opacity != OPAQUE)
		{
			glColor4us(0xffff, 0xffff, 0xffff,  opacity);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        	}
		GLfloat x1,y1,x2,y2;
		x1 = cubeCap->tc[4];
		x2 = cubeCap->tc[2];
		y1 = cubeCap->tc[3];
		y2 = cubeCap->tc[9];

		glBegin(GL_QUADS);
		if (arrayOffset)
			centery-=1;
		if (arrayOffset)
		{
			glTexCoord2f(x1,y1);
			glVertex3f(centerx-0.5,centery+0.5,centerz+0.5);
			glTexCoord2f(x1,y2);
			glVertex3f(centerx-0.5,centery+0.5,centerz-0.5);
			glTexCoord2f(x2,y2);
			glVertex3f(centerx+0.5, centery+0.5, centerz-0.5);
			glTexCoord2f(x2,y1);
			glVertex3f(centerx+0.5,centery+0.5,centerz+0.5);
		}
		else
		{
			glTexCoord2f(x2,y2);
			glVertex3f(centerx+0.5,centery+0.5,centerz+0.5);
			glTexCoord2f(x2,y1);
			glVertex3f(centerx+0.5, centery+0.5, centerz-0.5);
			glTexCoord2f(x1,y1);
			glVertex3f(centerx-0.5,centery+0.5,centerz-0.5);
			glTexCoord2f(x1,y2);
			glVertex3f(centerx-0.5,centery+0.5,centerz+0.5);
		}
		  
		glEnd();
		disableTexture(s, &cubeCap->texture);
	}
}

static void
cubePaintTransformedScreen(CompScreen * s,
						   const ScreenPaintAttrib * sAttrib,
						   Region region, int output, unsigned int mask)
{
	ScreenPaintAttrib sa = *sAttrib;
	int hsize, xMove = 0;
	float size;

	CUBE_SCREEN(s);

	hsize = s->hsize;
	if (cs->mmMode != Multiple)
		hsize *= cs->nOutput;
	size = hsize;

	if (!cs->fullscreenOutput)
	{
		cs->outputXScale = (float)s->width / s->outputDev[output].width;
		cs->outputYScale = (float)s->height / s->outputDev[output].height;

		cs->outputXOffset =
				(s->width / 2.0f -
				 (s->outputDev[output].region.extents.x1 +
				  s->outputDev[output].region.extents.x2) / 2.0f) /
				(float)s->outputDev[output].width;

		cs->outputYOffset =
				(s->height / 2.0f -
				 (s->outputDev[output].region.extents.y1 +
				  s->outputDev[output].region.extents.y2) / 2.0f) /
				(float)s->outputDev[output].height;
	}
	else
	{
		cs->outputXScale = 1.0f;
		cs->outputYScale = 1.0f;
		cs->outputXOffset = 0.0f;
		cs->outputYOffset = 0.0f;
	}


	if (cs->sky.name)
	{
		screenLighting(s, FALSE);

		glPushMatrix();

		if (cs->mmMode == OneBig)
		{
			glScalef(cs->outputXScale, cs->outputYScale, 1);
			glTranslatef(cs->outputXOffset / cs->outputXScale,
						 -cs->outputYOffset / cs->outputYScale, 0);
		}
		if (cs->animateSkyDome)	//  && cs->grabIndex == 0)
		{

			glRotatef(sAttrib->vRotate / 5.0f + 90.0f, 1.0f, 0.0f, 0.0f);

			if (cs->invert != 1)
				glRotatef(-
						  (sAttrib->xRotate -
						   ((s->x * 360.0f) / s->hsize)), 0.0f, 0.0f, -1.0f);
			else
				glRotatef(sAttrib->xRotate -
						  ((s->x * 360.0f) / s->hsize), 0.0f, 0.0f, -1.0f);
		}
		else
		{
			glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
		}

		glCallList(cs->skyListId);
		glPopMatrix();
	}
	else
	{
		clearTargetOutput(s->display, GL_COLOR_BUFFER_BIT);
	}

	mask &= ~PAINT_SCREEN_CLEAR_MASK;

	UNWRAP(cs, s, paintTransformedScreen);

	sa.xTranslate = sAttrib->xTranslate;
	sa.yTranslate = sAttrib->yTranslate;


	if (screenGrabExist(s, "rotate", "cube", 0) || sAttrib->zCamera!=-DEFAULT_Z_CAMERA)
		cs->finalPaint=!(cs->opt[CUBE_SCREEN_OPTION_STUCK_TO_SCREEN].value.b);
	else
		cs->finalPaint=TRUE;

	if (cs->grabIndex)
	{
		sa.vRotate = 0.0f;

		size += cs->unfold * 8.0f;
		size += powf(cs->unfold, 6) * 64.0;
		size += powf(cs->unfold, 16) * 8192.0;

		sa.zTranslate = -cs->invert * (0.5f / tanf(M_PI / size));

		sa.zCamera -= cs->unfold * cs->unfoldDistance;
		sa.zCamera /= IPCS_IsSet(IPCS_OBJECT(s),
								 cs->
								 zoomLevelAtom) ?
				IPCS_GetFloat(IPCS_OBJECT(s), cs->zoomLevelAtom) : 1;

		sa.xRotate = sAttrib->xRotate * cs->invert;
		if (sa.xRotate > 0.0f)
		{
			cs->xrotations = (int)(hsize * sa.xRotate) / 360;
			sa.xRotate = sa.xRotate - (360.0f * cs->xrotations) / hsize;
		}
		else
		{
			cs->xrotations = (int)(hsize * sa.xRotate) / 360;
			sa.xRotate = sa.xRotate -
					(360.0f * cs->xrotations) / s->hsize + 360.0f / hsize;
			cs->xrotations--;
		}

		sa.xRotate = sa.xRotate / size * hsize;
	}
	else if (cs->opt[CUBE_SCREEN_OPTION_VIEWPORT_SLIDE].value.b
			 && (sa.vRotate == 0.0f
				 || cs->opt[CUBE_SCREEN_OPTION_VIEWPORT_SLIDE_NO3D].value.b))
	{
		if (cs->opt[CUBE_SCREEN_OPTION_VIEWPORT_SLIDE_NO3D].value.b)
			sa.vRotate = 0.0f;

		size += 8.0f + 64.0f + 8192.0f;
		sa.zTranslate = -cs->invert * (0.5f / tanf(M_PI / (size)));
		sa.xRotate = sAttrib->xRotate * cs->invert;
		if (sa.xRotate > 0.0f)
		{
			cs->xrotations = (int)(hsize * sa.xRotate) / 360;
			sa.xRotate = sa.xRotate - (360.0f * cs->xrotations) / hsize;
		}
		else
		{
			cs->xrotations = (int)(hsize * sa.xRotate) / 360;
			sa.xRotate = sa.xRotate -
					(360.0f * cs->xrotations) / hsize + 360.0f / hsize;
			cs->xrotations--;
		}
		sa.xRotate = sa.xRotate / size * hsize;
	}
	else
	{
		if (sAttrib->vRotate > 100.0f)
			sa.vRotate = 100.0f;
		else if (sAttrib->vRotate < -100.0f)
			sa.vRotate = -100.0f;
		else
			sa.vRotate = sAttrib->vRotate;

		sa.zTranslate = -cs->invert * cs->distance;
		sa.xRotate = sAttrib->xRotate * cs->invert;
		if (sa.xRotate > 0.0f)
		{
			cs->xrotations = (int)(size * sa.xRotate) / 360;
			sa.xRotate = sa.xRotate - (360.0f * cs->xrotations) / size;
		}
		else
		{
			cs->xrotations = (int)(size * sa.xRotate) / 360;
			sa.xRotate = sa.xRotate -
					(360.0f * cs->xrotations) / size + 360.0f / size;
			cs->xrotations--;
		}
	}

	ScreenPaintAttrib soa = sa;
	int rotations = cs->xrotations;

	CubeFacePaint *faces = malloc(sizeof(CubeFacePaint) * MAX(hsize, 3));
	int fc = 0;

	int i;

	for (i = 0; i < MAX(hsize, 3); i++)
		faces[i].painted = FALSE;

	/* outside cube */
	if (cs->invert == 1)
	{
		float yRotSav = sa.yRotate;

		if (cs->grabIndex || hsize > 4 ||
			(cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT].value.b &&
			 (cs->desktopOpacity != OPAQUE || s->berylDesktopManaged)) ||
			compDisplayGetRequestFlagForAny(s->display, "DRAW_ALL_FACES"))
		{
			int i;

			xMove = cs->xrotations - ((hsize >> 1) - 1);
			sa.yRotate += (360.0f / size) * ((hsize >> 1) - 1);

			for (i = 0; i < hsize; i++)
			{
				paintReversed(s, &sa, output, xMove, &faces[fc]);
				fc++;
				sa.yRotate -= 360.0f / size;

				xMove++;
			}

		}
		else
		{
			if (sAttrib->xRotate != 0.0f)
			{
				xMove = cs->xrotations;

				paintReversed(s, &sa, output, xMove, &faces[fc]);
				fc++;

				xMove++;
			}
			sa.yRotate -= 360.0f / size;

			paintReversed(s, &sa, output, xMove, &faces[fc]);
			fc++;
		}
		sa.yRotate = yRotSav;
	}
	else
	{
		if (sa.xRotate > 180.0f / size)
		{
			sa.yRotate -= 360.0f / size;
			cs->xrotations++;
		}

		sa.yRotate -= 360.0f / size;
		xMove = -1 - cs->xrotations;

		if (cs->grabIndex || sAttrib->vRotate > 60.0)
		{
			int i;

			if (sa.xRotate > 180.0f / size)
			{
				xMove -= ((hsize >> 1) - 2);
				sa.yRotate -= (360.0f / size) * ((hsize >> 1) - 2);
			}
			else
			{
				xMove -= ((hsize >> 1) - 1);
				sa.yRotate -= (360.0f / size) * ((hsize >> 1) - 1);
			}

			for (i = 0; i < hsize; i++)
			{
				paintReversed(s, &sa, output, xMove, &faces[fc]);
				fc++;

				sa.yRotate += 360.0f / size;
				xMove++;
			}
		}
		else
		{
			paintReversed(s, &sa, output, xMove, &faces[fc]);
			fc++;

			sa.yRotate += 360.0f / size;
			xMove = -cs->xrotations;

			paintReversed(s, &sa, output, xMove, &faces[fc]);
			fc++;


			sa.yRotate += 360.0f / size;
			xMove = 1 - cs->xrotations;

			paintReversed(s, &sa, output, xMove, &faces[fc]);
			fc++;
		}
	}

	Bool disabledCull = FALSE;
	GLenum filter;
	unsigned int newMask;
	int num = 0, j;

	if (cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT].value.b &&
		(cs->desktopOpacity != OPAQUE || s->berylDesktopManaged))
		mask |= PAINT_SCREEN_TRANSFORMED_MASK;

	if ((cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT].value.b ||
		 compDisplayGetRequestFlagForAny(s->display, "DRAW_ALL_FACES"))
		&& cs->invert == 1)
	{
		if (glIsEnabled(GL_CULL_FACE) && (cs->desktopOpacity != OPAQUE || s->berylDesktopManaged))
		{
			disabledCull = TRUE;
			glDisable(GL_CULL_FACE);
		}

		glNormal3f(0.0, 0.0, 1.0);

		newMask = mask | PAINT_SCREEN_ORDER_FRONT_TO_BACK_MASK;

		filter = s->display->textureFilter;
		if (cs->opt[CUBE_SCREEN_OPTION_MIPMAP].value.b)
			s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;


		for (i = 0; i < fc; i++)
			if (faces[i].rev)
				num++;

		for (i = 0; i < num; i++)
		{
			int found = 0;
			float minZ = -2.0;

			for (j = 0; j < fc; j++)
			{
				if (faces[j].z > minZ && !faces[j].painted && faces[j].rev)
				{
					found = j;
					minZ = faces[j].z;
				}
			}
			faces[found].painted = TRUE;
			cubeMoveViewportAndPaint(s, &faces[found].sa,
									 output, newMask, faces[found].xMove);
		}

		s->display->textureFilter = filter;

		if (disabledCull)
		{
			glEnable(GL_CULL_FACE);
			disabledCull = FALSE;
		}
	}

	cs->paintingCaps = TRUE;

	if (cs->grabIndex == 0 && (hsize > 2) &&
		(cs->invert != 1 || sa.vRotate != 0.0f || sa.yTranslate != 0.0f
		 || (cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT].value.b
			 && (cs->desktopOpacity != OPAQUE || s->berylDesktopManaged)))
		&& cs->opt[CUBE_SCREEN_OPTION_DRAW_CAPS].value.b
		&& !compDisplayGetRequestFlagForAny(s->display, "NO_CUBE_CAPS"))
	{
		screenLighting(s, TRUE);

		glColor4us(cs->color[0], cs->color[1], cs->color[2],
				   cs->desktopOpacity);

		glPushMatrix();

		if (sAttrib->xRotate > 0.0f)
		{
			sa.yRotate += 360.0f / size;
			(s->applyScreenTransform) (s, &soa, output);
			glTranslatef(cs->outputXOffset, -cs->outputYOffset, 0.0f);
			glScalef(cs->outputXScale, cs->outputYScale, 1.0f);
			sa.yRotate -= 360.0f / size;
		}
		else
		{
			(s->applyScreenTransform) (s, &soa, output);
			glTranslatef(cs->outputXOffset, -cs->outputYOffset, 0.0f);
			glScalef(cs->outputXScale, cs->outputYScale, 1.0f);
		}

		glVertexPointer(3, GL_FLOAT, 0, cs->vertices);

		//if (hsize != 4 && cs->invert != 1)
		//	glRotatef(180.0f, 0.0f, 1.0f, 0.0f);

			glRotatef(360.0f/(float)hsize, 0.0f, 1.0f, 0.0f);

			if (cs->rotateImages)
			{
				if (soa.xRotate > (360.0f/(float)hsize)/2.0f)
					glRotatef(-360.0f/(float)hsize, 0.0f, 1.0f, 0.0f);
			}
			else if (cs->neverRotateImages)
			{
				glRotatef(rotations * (360.0f/(float)hsize), 0.0f, 1.0f, 0.0f);
				float previousRotation = IPCS_GetFloat(IPCS_OBJECT(s),
													   cs->
													   previousRotationAtom);
				if (cs->invert != 1)
					previousRotation = -previousRotation;
				glRotatef(previousRotation * (360.0f/(float)hsize), 0.0f, 1.0f, 0.0f);
			}
			else
			{
				glRotatef(rotations *(360.0f/(float)hsize), 0.0f, 1.0f, 0.0f);
			}

		if (glIsEnabled(GL_CULL_FACE)
			&& (cs->desktopOpacity != OPAQUE || s->berylDesktopManaged) && cs->invert == 1)
		{
			disabledCull = TRUE;
			glDisable(GL_CULL_FACE);
		}

		if ((cs->desktopOpacity != OPAQUE || s->berylDesktopManaged))
		{
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		}

		if (cs->invert == 1)
		{
			if (capsReversed())
			{
				glNormal3f(0.0, -1.0, 0.0);
					glRotatef(360.0f-(360.0f/(float)hsize), 0.0f, 1.0f, 0.0f);
				cubeDrawCubeCap(s, hsize,
								cs->nvertices >> 1,
								&cs->cubeCapBottom,
								&cs->cubeCapTop, cs->desktopOpacity);

				glNormal3f(0.0, -1.0, 0.0);
					//glRotatef(-360.0f+(360.0f/(float)hsize), 0.0f, 1.0f, 0.0f);
				cubeDrawCubeCap(s, hsize, 0,
								&cs->cubeCapTop,
								&cs->cubeCapBottom, cs->desktopOpacity);
			}
			else
			{
				glNormal3f(0.0, 1.0, 0.0);
					glRotatef(360.0f-(360.0f/(float)hsize), 0.0f, 1.0f, 0.0f);
				cubeDrawCubeCap(s, hsize, 0,
								&cs->cubeCapTop,
								&cs->cubeCapBottom, cs->desktopOpacity);

				glNormal3f(0.0, 1.0, 0.0);
				cubeDrawCubeCap(s, hsize,
								cs->nvertices >> 1,
								&cs->cubeCapBottom,
								&cs->cubeCapTop, cs->desktopOpacity);
			}
		}
		else
		{
			glNormal3f(0.0, -1.0, 0.0);
			cubeDrawCubeCap(s, hsize, 0, &cs->cubeCapTop,
							&cs->cubeCapBottom, cs->desktopOpacity);

					glRotatef(360.0f-(360.0f/(float)hsize), 0.0f, 1.0f, 0.0f);
			glNormal3f(0.0, 1.0, 0.0);
			cubeDrawCubeCap(s, hsize, cs->nvertices >> 1,
							&cs->cubeCapBottom,
							&cs->cubeCapTop, cs->desktopOpacity);
		}

		glDisable(GL_BLEND);
		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

		if (disabledCull)
			glEnable(GL_CULL_FACE);

		glPopMatrix();

		glColor4usv(defaultColor);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	glNormal3f(0.0, 0.0, -1.0);

	cs->paintingCaps = FALSE;

	newMask = mask | PAINT_SCREEN_ORDER_BACK_TO_FRONT_MASK;

	filter = s->display->textureFilter;
	if (cs->opt[CUBE_SCREEN_OPTION_MIPMAP].value.b)
		s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;

	if (cs->invert == 1)
	{
		num = 0;
		for (i = 0; i < fc; i++)
			if (!faces[i].rev)
				num++;
	}
	else
		num = fc;

	for (i = 0; i < num; i++)
	{
		int found = 0;
		float minZ = -2.0;

		for (j = 0; j < fc; j++)
		{
			if (faces[j].z > minZ && !faces[j].painted &&
				(!faces[j].rev || cs->invert != 1))
			{
				found = j;
				minZ = faces[j].z;
			}
		}
		faces[found].painted = TRUE;
		cubeMoveViewportAndPaint(s, &faces[found].sa, output,
								 newMask, faces[found].xMove);
	}

	s->display->textureFilter = filter;

	WRAP(cs, s, paintTransformedScreen, cubePaintTransformedScreen);

	free(faces);
}


static void
cubeApplyScreenTransform(CompScreen * s,
						 const ScreenPaintAttrib * sAttrib, int output)
{
	CUBE_SCREEN(s);


	glTranslatef(cs->outputXOffset, -cs->outputYOffset, 0.0f);
	glScalef(cs->outputXScale, cs->outputYScale, 1.0f);

	UNWRAP(cs, s, applyScreenTransform);
	(*s->applyScreenTransform) (s, sAttrib, output);
	WRAP(cs, s, applyScreenTransform, cubeApplyScreenTransform);

	glScalef(1.0f / cs->outputXScale, 1.0f / cs->outputYScale, 1.0f);
	glTranslatef(-cs->outputXOffset, cs->outputYOffset, 0.0f);
}

static void
cubePaintBackground(CompScreen * s, Region region, unsigned int mask)
{
	CUBE_SCREEN(s);

	if ((cs->desktopOpacity != OPAQUE || s->berylDesktopManaged))
	{
		if (s->desktopWindowCount)
		{
			glColor4us(0, 0, 0, 0);
			glEnable(GL_BLEND);
		}
		else
		{
			glColor4us(0xffff, 0xffff, 0xffff, cs->desktopOpacity);
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		}
	}

	UNWRAP(cs, s, paintBackground);
	(*s->paintBackground) (s, region, mask);
	WRAP(cs, s, paintBackground, cubePaintBackground);

	if ((cs->desktopOpacity != OPAQUE || s->berylDesktopManaged))
	{
		if (s->desktopWindowCount)
		{
			glColor3usv(defaultColor);
			glDisable(GL_BLEND);
		}
		else
		{
			glColor3usv(defaultColor);
			glDisable(GL_BLEND);
			glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
			screenTexEnvMode(s, GL_REPLACE);
		}
	}
}

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

	CUBE_SCREEN(w->screen);

	WindowPaintAttrib wa = *attrib;

	if (w->type & CompWindowTypeDesktopMask)
	{
		if (cs->desktopOpacity == 0)
			return TRUE;
		wa.opacity = cs->desktopOpacity;
	}

	if (!w->managed && cs->noManaged)
		return TRUE;

	if ((w->type & CompWindowTypeDockMask) || (w->state & CompWindowStateStickyMask && !(w->state & CompWindowStateBelowMask)))
	{
		if (!cs->finalPaint)
		{
			return TRUE;
		}
	}

	UNWRAP(cs, w->screen, paintWindow);
	status = (*w->screen->paintWindow) (w, &wa, region, mask);
	WRAP(cs, w->screen, paintWindow, cubePaintWindow);

	return status;
}


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

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


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

		IPCS_SetBool(IPCS_OBJECT(s),cs->unfoldedAtom, TRUE);

		int hsize = s->hsize;

		if (cs->mmMode != Multiple)
			hsize *= cs->nOutput;

		if (hsize < 4)
			return FALSE;

		if (otherScreenGrabExist(s, "rotate", "switcher", "cube", 0))
			return FALSE;

		if (!cs->grabIndex)
			cs->grabIndex = pushScreenGrab(s, s->invisibleCursor, "cube");

		if (cs->grabIndex)
		{
			cs->unfolded = TRUE;
			damageScreen(s);
		}

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

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

	return FALSE;
}

static Bool
cubeFold(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)
	{
		CUBE_SCREEN(s);
		
		IPCS_SetBool(IPCS_OBJECT(s),cs->unfoldedAtom, FALSE);

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

		if (cs->grabIndex)
		{
			cs->unfolded = FALSE;
			damageScreen(s);
		}
	}

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

	return FALSE;
}

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

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

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

		if (cs->cubeCapTop.imgNFile)
		{
			cubeLoadImg(s, &cs->cubeCapTop,
						(cs->cubeCapTop.imgCurFile +
						 1) % cs->cubeCapTop.imgNFile);
			damageScreen(s);
		}
		if (cs->cubeCapBottom.imgNFile)
		{
			cubeLoadImg(s, &cs->cubeCapBottom,
						(cs->cubeCapBottom.imgCurFile +
						 1) % cs->cubeCapBottom.imgNFile);
			damageScreen(s);
		}
	}

	return FALSE;
}

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

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

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

		if (cs->cubeCapTop.imgNFile)
		{
			cubeLoadImg(s, &cs->cubeCapBottom,
						(cs->cubeCapTop.imgCurFile - 1 +
						 cs->cubeCapTop.imgNFile) % cs->cubeCapTop.imgNFile);
			damageScreen(s);
		}
		if (cs->cubeCapBottom.imgNFile)
		{
			cubeLoadImg(s, &cs->cubeCapBottom,
						(cs->cubeCapBottom.imgCurFile - 1 +
						 cs->cubeCapBottom.imgNFile) %
						cs->cubeCapBottom.imgNFile);
			damageScreen(s);
		}

	}

	return FALSE;
}

static void cubeOutputChangeNotify(CompScreen * s)
{
	CUBE_SCREEN(s);

	cubeUpdateOutputs(s);
	cubeUpdateGeometry(s, s->hsize, cs->invert);

	UNWRAP(cs, s, outputChangeNotify);
	(*s->outputChangeNotify) (s);
	WRAP(cs, s, outputChangeNotify, cubeOutputChangeNotify);
}

static Bool
cubeSetGlobalScreenOption(CompScreen * s, char *name, CompOptionValue * value)
{
	Bool status;

	CUBE_SCREEN(s);

	UNWRAP(cs, s, setScreenOption);
	status = (*s->setScreenOption) (s, name, value);
	WRAP(cs, s, setScreenOption, cubeSetGlobalScreenOption);

	if (status && strcmp(name, "hsize") == 0)
		cubeUpdateGeometry(s, s->hsize, cs->invert);

	return status;
}


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

	CUBE_DISPLAY(display);

	o = compFindOption(cd->opt, NUM_OPTIONS(cd), name, &index);

	if (!o)
		return FALSE;

	switch (index)
	{
	case CUBE_DISPLAY_OPTION_UNFOLD:
		if (setDisplayAction(display, o, value))
			return TRUE;
		break;
	case CUBE_DISPLAY_OPTION_NEXT:
	case CUBE_DISPLAY_OPTION_PREV:
		if (compSetActionOption(o, value))
			return TRUE;

	default:
		break;
	}

	return FALSE;
}

static void cubeDisplayInitOptions(CubeDisplay * cd)
{
	CompOption *o;

	o = &cd->opt[CUBE_DISPLAY_OPTION_UNFOLD];
	o->advanced = False;
	o->name = "unfold";
	o->group = N_("Bindings");
	o->subGroup = N_("Unfold");
	o->displayHints = "";
	o->shortDesc = N_("Unfold Cube");
	o->longDesc = N_("Unfold Cube.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = cubeUnfold;
	o->value.action.terminate = cubeFold;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitEdge;
	o->value.action.state |= CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = CompBindingTypeKey;
	o->value.action.key.modifiers = CUBE_UNFOLD_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(CUBE_UNFOLD_KEY_DEFAULT);

	o = &cd->opt[CUBE_DISPLAY_OPTION_NEXT];
	o->advanced = False;
	o->name = "next_slide";
	o->group = N_("Bindings");
	o->subGroup = N_("Next Slide");
	o->displayHints = "";
	o->shortDesc = N_("Next Slide");
	o->longDesc = N_("Advance to Next Slide.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = cubeNextImage;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitEdge;
	o->value.action.state |= CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = 0;
	//o->value.action.type |= CompBindingTypeKey;
	o->value.action.key.modifiers = CUBE_NEXT_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(CUBE_NEXT_KEY_DEFAULT);

	o = &cd->opt[CUBE_DISPLAY_OPTION_PREV];
	o->advanced = False;
	o->name = "prev_slide";
	o->group = N_("Bindings");
	o->subGroup = N_("Previous Slide");
	o->displayHints = "";
	o->shortDesc = N_("Previous Slide");
	o->longDesc = N_("Go back to Previous Slide.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = cubePrevImage;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.state = CompActionStateInitEdge;
	o->value.action.state |= CompActionStateInitKey;
	o->value.action.state |= CompActionStateInitButton;
	o->value.action.type = 0;
	//o->value.action.type |= CompBindingTypeKey;
	o->value.action.key.modifiers = CUBE_PREV_MODIFIERS_DEFAULT;
	o->value.action.key.keysym = XStringToKeysym(CUBE_PREV_KEY_DEFAULT);
}

static CompOption *cubeGetDisplayOptions(CompDisplay * display, int *count)
{
	if (display)
	{
		CUBE_DISPLAY(display);

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

		cubeDisplayInitOptions(cd);
		*count = NUM_OPTIONS(cd);
		return cd->opt;
	}
}

static Bool cubeInitDisplay(CompPlugin * p, CompDisplay * d)
{
	CubeDisplay *cd;

	cd = malloc(sizeof(CubeDisplay));
	if (!cd)
		return FALSE;

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

	cubeDisplayInitOptions(cd);

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

	return TRUE;
}

static void cubeFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	CUBE_DISPLAY(d);

	freeScreenPrivateIndex(d, cd->screenPrivateIndex);

	free(cd);
}

static Bool cubeInitScreen(CompPlugin * p, CompScreen * s)
{
	CubeScreen *cs;

	CUBE_DISPLAY(s->display);

	cs = malloc(sizeof(CubeScreen));
	if (!cs)
		return FALSE;

	cs->invert = 1;

	cs->color[0] = CUBE_COLOR_RED_DEFAULT;
	cs->color[1] = CUBE_COLOR_GREEN_DEFAULT;
	cs->color[2] = CUBE_COLOR_BLUE_DEFAULT;

	cs->nvertices = 0;
	cs->vertices = NULL;

	cs->grabIndex = 0;

	cs->skyListId = 0;
	cs->animateSkyDome = CUBE_SKYDOME_ANIMATE_DEFAULT;

	cs->skyGradStartColor[0] = CUBE_SKYDOME_GRAD_START_RED_DEFAULT;
	cs->skyGradStartColor[1] = CUBE_SKYDOME_GRAD_START_GREEN_DEFAULT;
	cs->skyGradStartColor[2] = CUBE_SKYDOME_GRAD_START_BLUE_DEFAULT;
	cs->skyGradEndColor[0] = CUBE_SKYDOME_GRAD_END_RED_DEFAULT;
	cs->skyGradEndColor[1] = CUBE_SKYDOME_GRAD_END_GREEN_DEFAULT;
	cs->skyGradEndColor[2] = CUBE_SKYDOME_GRAD_END_BLUE_DEFAULT;

	cs->rotateImages = FALSE;
	cs->neverRotateImages = TRUE;

	cs->snapTopBottomAtom = IPCS_GetAtom(IPCS_OBJECT(s), IPCS_BOOL,
										 "CUBE_SNAP_TOP_BOTTOM", TRUE);
	cs->previousRotationAtom = IPCS_GetAtom(IPCS_OBJECT(s), IPCS_FLOAT,
											"PREVIOUS_ROTATION", TRUE);
	cs->paintingCapsAtom = IPCS_GetAtom(IPCS_OBJECT(s), IPCS_VPTR,
										"CUBE_PAINTING_CAPS_BOOL_PTR", TRUE);
	cs->manualAtom =
			IPCS_GetAtom(IPCS_OBJECT(s), IPCS_BOOL, "MOUSE_INITIATED_ROTATE",
						 TRUE);
	cs->zoomLevelAtom =
			IPCS_GetAtom(IPCS_OBJECT(s), IPCS_FLOAT, "ZOOM_LEVEL", TRUE);
	cs->unfoldedAtom = IPCS_GetAtom(IPCS_OBJECT(s),IPCS_BOOL,"CUBE_UNFOLDED", TRUE);
	cs->paintingCaps = FALSE;
	IPCS_SetVPtr(IPCS_OBJECT(s), cs->paintingCapsAtom,
				 (void *)&cs->paintingCaps);

	s->privates[cd->screenPrivateIndex].ptr = cs;

	initTexture(s, &cs->sky);

	cs->acceleration = CUBE_ACCELERATION_DEFAULT;
	cs->speed = CUBE_SPEED_DEFAULT;
	cs->timestep = CUBE_TIMESTEP_DEFAULT;

	cs->unfolded = FALSE;
	cs->unfold = 0.0f;
	cs->unfoldDistance = CUBE_UNFOLD_ZOOM_DEFAULT;

	cs->unfoldVelocity = 0.0f;

	cubeScreenInitOptions(cs);

	cs->cubeCapTop.imgFiles =
			cs->opt[CUBE_SCREEN_OPTION_IMAGES_TOP].value.list.value;
	cs->cubeCapTop.imgNFile =
			cs->opt[CUBE_SCREEN_OPTION_IMAGES_TOP].value.list.nValue;

	cs->cubeCapBottom.imgFiles =
			cs->opt[CUBE_SCREEN_OPTION_IMAGES_BOTTOM].value.list.value;
	cs->cubeCapBottom.imgNFile =
			cs->opt[CUBE_SCREEN_OPTION_IMAGES_BOTTOM].value.list.nValue;

	cs->mmMode = MULTIMONITOR_MODE_DEFAULT;

	cs->mmModeAtom = IPCS_GetAtom(IPCS_OBJECT(s), IPCS_INT, "MM_MODE", TRUE);
	IPCS_SetInt(IPCS_OBJECT(s), cs->mmModeAtom, cs->mmMode);

	cs->insideAtom = IPCS_GetAtom(IPCS_OBJECT(s), IPCS_BOOL, "INSIDE", TRUE);
	IPCS_SetBool(IPCS_OBJECT(s), cs->insideAtom, CUBE_IN_DEFAULT);

	addScreenAction(s, &cd->opt[CUBE_DISPLAY_OPTION_UNFOLD].value.action);

	cs->desktopOpacity = OPAQUE;
	cs->noManaged = FALSE;
	if (cs->mmMode == OneBig)
		s->projectionStyle = COMP_PERSPECTIVE_GLOBAL;
	else
		s->projectionStyle = COMP_PERSPECTIVE_LOCAL;

	WRAP(cs, s, preparePaintScreen, cubePreparePaintScreen);
	WRAP(cs, s, donePaintScreen, cubeDonePaintScreen);
	WRAP(cs, s, paintScreen, cubePaintScreen);
	WRAP(cs, s, paintTransformedScreen, cubePaintTransformedScreen);
	WRAP(cs, s, applyScreenTransform, cubeApplyScreenTransform);
	WRAP(cs, s, paintBackground, cubePaintBackground);
	WRAP(cs, s, paintWindow, cubePaintWindow);
	WRAP(cs, s, setScreenOption, cubeSetGlobalScreenOption);
	WRAP(cs, s, outputChangeNotify, cubeOutputChangeNotify);
	WRAP(cs, s, setClipPlanes, cubeSetClipPlanes);

	cubeUpdateOutputs(s);

	cubeInitCubeCap(s, &cs->cubeCapTop);
	cubeInitCubeCap(s, &cs->cubeCapBottom);

	if (!cubeUpdateGeometry(s, s->hsize, cs->invert))
		return FALSE;

	return TRUE;
}

static void cubeFiniScreen(CompPlugin * p, CompScreen * s)
{
	CUBE_SCREEN(s);
	CUBE_DISPLAY(s->display);

	if (cs->skyListId)
		glDeleteLists(cs->skyListId, 1);

	IPCS_Unset(IPCS_OBJECT(s), cs->paintingCapsAtom);

	UNWRAP(cs, s, preparePaintScreen);
	UNWRAP(cs, s, donePaintScreen);
	UNWRAP(cs, s, paintScreen);
	UNWRAP(cs, s, paintTransformedScreen);
	UNWRAP(cs, s, applyScreenTransform);
	UNWRAP(cs, s, paintBackground);
	UNWRAP(cs, s, paintWindow);
	UNWRAP(cs, s, setScreenOption);
	UNWRAP(cs, s, outputChangeNotify);
	UNWRAP(cs, s, setClipPlanes);

	removeScreenAction(s, &cd->opt[CUBE_DISPLAY_OPTION_UNFOLD].value.action);

	finiTexture(s, &cs->cubeCapTop.texture);
	finiTexture(s, &cs->cubeCapBottom.texture);
	finiTexture(s, &cs->sky);

	if (cs->vertices)
		free(cs->vertices);

	free(cs);
}

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

	return TRUE;
}

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

CompPluginDep cubeDeps[] = {
	{CompPluginRuleAfter, "animation"}
	,
	{CompPluginRuleAfter, "decoration"}
	,
	{CompPluginRuleAfter, "fade"}
	,
	{CompPluginRuleAfter, "wobbly"}
	,
	{CompPluginRuleAfter, "inputzoom"}
	,
	{CompPluginRuleAfter, "3d"}
	,
	{CompPluginRuleAfterCategory, "imageformat"}
	,
};

CompPluginFeature cubeFeatures[] = {
	{"largedesktop"}
	,
	{"cube"}
};

CompPluginVTable cubeVTable = {
	"cube",
	N_("Desktop Cube"),
	N_("Place windows on cube"),
	cubeInit,
	cubeFini,
	cubeInitDisplay,
	cubeFiniDisplay,
	cubeInitScreen,
	cubeFiniScreen,
	0,							/* InitWindow */
	0,							/* FiniWindow */
	cubeGetDisplayOptions,
	cubeSetDisplayOption,
	cubeGetScreenOptions,
	cubeSetScreenOption,
	cubeDeps,
	sizeof(cubeDeps) / sizeof(cubeDeps[0]),
	cubeFeatures,
	sizeof(cubeFeatures) / sizeof(cubeFeatures[0]),
	BERYL_ABI_INFO,
	"beryl-plugins",
	"desktop",
	0,
	0,
	True,
};

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


syntax highlighted by Code2HTML, v. 0.9.1