All pastes #436252 Raw Edit

Untitled

public text v1 · immutable
·published 2007-04-12 12:13 UTC
rendered paste body
/*
 * Copyright © 2005 Novell, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of
 * Novell, Inc. not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior permission.
 * Novell, Inc. makes no representations about the suitability of this
 * software for any purpose. It is provided "as is" without express or
 * implied warranty.
 *
 * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: David Reveman <davidr@novell.com>
 *         Mirco Müller <macslow@bangang.de> (Skydome support)
 */

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

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

#ifdef USE_LIBRSVG
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>
#include <librsvg/rsvg.h>
#include <librsvg/rsvg-cairo.h>
#endif

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

#include <compiz.h>

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

#define CUBE_IN_DEFAULT FALSE

typedef enum _RotationMode
{
    NoRotation = 0,
    RotationChange,
    RotationManual
} RotationMode;

static char *cubeImages[] = {
    "freedesktop"
};
#define N_CUBE_IMAGES (sizeof (cubeImages) / sizeof (cubeImages[0]))

#define CUBE_SCALE_IMAGE_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       "Down"
#define CUBE_UNFOLD_MODIFIERS_DEFAULT (ControlMask | CompAltMask)

#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_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_OPACITY_MIN 0.0
#define CUBE_OPACITY_MAX 100.0
#define CUBE_OPACITY_PRECISION 1.0

#define CUBE_TRANSPARENT_MANUAL_ONLY_DEFAULT FALSE

#define CUBE_ACTIVE_OPACITY_DEFAULT	30

#define CUBE_INACTIVE_OPACITY_DEFAULT	100

#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 TRUE

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

static int displayPrivateIndex;

typedef struct _CubeDisplay {
    int		    screenPrivateIndex;

    CompOption opt[CUBE_DISPLAY_OPTION_NUM];
    
    HandleCompizEventProc handleCompizEvent;
} CubeDisplay;

#define CUBE_SCREEN_OPTION_COLOR	           0
#define CUBE_SCREEN_OPTION_IN		           1
#define CUBE_SCREEN_OPTION_SCALE_IMAGE	           2
#define CUBE_SCREEN_OPTION_IMAGES	           3
#define CUBE_SCREEN_OPTION_SKYDOME	           4
#define CUBE_SCREEN_OPTION_SKYDOME_IMG	           5
#define CUBE_SCREEN_OPTION_SKYDOME_ANIM	           6
#define CUBE_SCREEN_OPTION_SKYDOME_GRAD_START      7
#define CUBE_SCREEN_OPTION_SKYDOME_GRAD_END        8
#define CUBE_SCREEN_OPTION_ACCELERATION	           9
#define CUBE_SCREEN_OPTION_SPEED	           10
#define CUBE_SCREEN_OPTION_TIMESTEP	           11
#define CUBE_SCREEN_OPTION_MIPMAP	           12
#define CUBE_SCREEN_OPTION_ACTIVE_OPACITY          13
#define CUBE_SCREEN_OPTION_INACTIVE_OPACITY        14
#define CUBE_SCREEN_OPTION_FADE_TIME               15
#define CUBE_SCREEN_OPTION_TRANSPARENT_MANUAL_ONLY 16
#define CUBE_SCREEN_OPTION_NUM                     17

#define MULTM(x,y,z) \
z[0] = x[0] * y[0] + x[4] * y[1] + x[8] * y[2] + x[12] * y[3]; \
z[1] = x[1] * y[0] + x[5] * y[1] + x[9] * y[2] + x[13] * y[3]; \
z[2] = x[2] * y[0] + x[6] * y[1] + x[10] * y[2] + x[14] * y[3]; \
z[3] = x[3] * y[0] + x[7] * y[1] + x[11] * y[2] + x[15] * y[3]; \
z[4] = x[0] * y[4] + x[4] * y[5] + x[8] * y[6] + x[12] * y[7]; \
z[5] = x[1] * y[4] + x[5] * y[5] + x[9] * y[6] + x[13] * y[7]; \
z[6] = x[2] * y[4] + x[6] * y[5] + x[10] * y[6] + x[14] * y[7]; \
z[7] = x[3] * y[4] + x[7] * y[5] + x[11] * y[6] + x[15] * y[7]; \
z[8] = x[0] * y[8] + x[4] * y[9] + x[8] * y[10] + x[12] * y[11]; \
z[9] = x[1] * y[8] + x[5] * y[9] + x[9] * y[10] + x[13] * y[11]; \
z[10] = x[2] * y[8] + x[6] * y[9] + x[10] * y[10] + x[14] * y[11]; \
z[11] = x[3] * y[8] + x[7] * y[9] + x[11] * y[10] + x[15] * y[11]; \
z[12] = x[0] * y[12] + x[4] * y[13] + x[8] * y[14] + x[12] * y[15]; \
z[13] = x[1] * y[12] + x[5] * y[13] + x[9] * y[14] + x[13] * y[15]; \
z[14] = x[2] * y[12] + x[6] * y[13] + x[10] * y[14] + x[14] * y[15]; \
z[15] = x[3] * y[12] + x[7] * y[13] + x[11] * y[14] + x[15] * y[15];

#define MULTMV(m, v) { \
float v0 = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; \
float v1 = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; \
float v2 = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; \
float v3 = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; \
v[0] = v0; v[1] = v1; v[2] = v2; v[3] = v3; }

#define DIVV(v) \
v[0] /= v[3]; \
v[1] /= v[3]; \
v[2] /= v[3]; \
v[3] /= v[3];

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

    CompOption opt[CUBE_SCREEN_OPTION_NUM];

    int      invert;
    int      xrotations;
    GLfloat  distance;
    Bool     paintTopBottom;
    GLushort color[3];
    GLfloat  tc[12];

    int grabIndex;

    float acceleration;
    float speed;
    float timestep;

    Bool    unfolded;
    GLfloat unfold, unfoldVelocity;

    GLfloat  *vertices;
    int      nvertices;

    GLuint skyListId;
    Bool   animateSkyDome;

    Pixmap	    pixmap;
    int		    pw, ph;
    CompTexture     texture, sky;

    int		    imgNFile;
    int		    imgCurFile;
    CompOptionValue *imgFiles;

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

    Bool cleared[64];

    Bool fullscreenOutput;
    RotationMode rotationMode;

    float outputXScale;
    float outputYScale;
    float outputXOffset;
    float outputYOffset;
    
    Bool disableCaps;
    Bool paintAllViewports;
    
    float desktopOpacity;
    float toOpacity;

#ifdef USE_LIBRSVG
    cairo_t	    *cr;
#endif

} 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
cubeInitSvg (CompScreen *s)

{
    CUBE_SCREEN (s);

    cs->pixmap = None;
    cs->pw = cs->ph = 0;

#ifdef USE_LIBRSVG
    cs->cr = NULL;
#endif

}

static void
cubeFiniSvg (CompScreen *s)

{
    CUBE_SCREEN (s);

#ifdef USE_LIBRSVG
    if (cs->cr)
	cairo_destroy (cs->cr);
#endif

    if (cs->pixmap)
	XFreePixmap (s->display->display, cs->pixmap);
}

static Bool
readSvgToTexture (CompScreen   *s,
		  CompTexture  *texture,
		  const char   *svgFileName,
		  unsigned int *returnWidth,
		  unsigned int *returnHeight)
{

#ifdef USE_LIBRSVG
    unsigned int      width, height, pw, ph;
    char	      *name;
    GError	      *error = NULL;
    RsvgHandle	      *svgHandle;
    RsvgDimensionData svgDimension;

    CUBE_SCREEN (s);

    name = strdup (svgFileName);

    svgHandle = rsvg_handle_new_from_file (name, &error);

    free (name);

    if (!svgHandle)
	return FALSE;

    rsvg_handle_get_dimensions (svgHandle, &svgDimension);

    width  = svgDimension.width;
    height = svgDimension.height;

    if (cs->opt[CUBE_SCREEN_OPTION_SCALE_IMAGE].value.b)
    {
	pw = (cs->nOutput > 1) ? s->outputDev[0].width  : s->width;
	ph = (cs->nOutput > 1) ? s->outputDev[0].height : s->height;
    }
    else
    {
	pw = width;
	ph = height;
    }

    if (!cs->pixmap || cs->pw != pw || cs->ph != ph)
    {
	cairo_surface_t *surface;
	Visual		*visual;
	int		depth;

	if (cs->cr)
	{
	    cairo_destroy (cs->cr);
	    cs->cr = NULL;
	}

	if (cs->pixmap)
	    XFreePixmap (s->display->display, cs->pixmap);

	cs->pw = pw;
	cs->ph = ph;

	depth = DefaultDepth (s->display->display, s->screenNum);
	cs->pixmap = XCreatePixmap (s->display->display, s->root,
				    cs->pw, cs->ph,
				    depth);

	if (!bindPixmapToTexture (s, texture, cs->pixmap,
				  cs->pw, cs->ph, depth))
	{
	    fprintf (stderr, "%s: Couldn't bind slide pixmap 0x%x to "
		     "texture\n", programName, (int) cs->pixmap);

	    return FALSE;
	}

	visual = DefaultVisual (s->display->display, s->screenNum);
	surface = cairo_xlib_surface_create (s->display->display,
					     cs->pixmap, visual,
					     cs->pw, cs->ph);
	cs->cr = cairo_create (surface);
	cairo_surface_destroy (surface);
    }

    cairo_save (cs->cr);
    cairo_set_source_rgb (cs->cr,
			  (double) cs->color[0] / 0xffff,
			  (double) cs->color[1] / 0xffff,
			  (double) cs->color[2] / 0xffff);
    cairo_rectangle (cs->cr, 0, 0, cs->pw, cs->ph);
    cairo_fill (cs->cr);

    cairo_scale (cs->cr, (double) cs->pw / width, (double) cs->ph / height);

    rsvg_handle_render_cairo (svgHandle, cs->cr);

    rsvg_handle_free (svgHandle);

    cairo_restore (cs->cr);

    *returnWidth  = cs->pw;
    *returnHeight = cs->ph;

    return TRUE;
#else
    return FALSE;
#endif

}

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

    CUBE_SCREEN (s);

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

    if (!cs->imgNFile || cs->pw != pw || cs->ph != ph)
    {
	finiTexture (s, &cs->texture);
	initTexture (s, &cs->texture);
	cubeFiniSvg (s);
	cubeInitSvg (s);

	if (!cs->imgNFile)
	    return;
    }

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

    if (readImageToTexture (s, &cs->texture,
			    cs->imgFiles[cs->imgCurFile].s,
			    &width, &height))
    {
	cubeFiniSvg (s);
	cubeInitSvg (s);
    }
    else if (!readSvgToTexture (s, &cs->texture,
				cs->imgFiles[cs->imgCurFile].s,
				&width, &height))
    {
	fprintf (stderr, "%s: Failed to load slide: %s\n",
		 programName, cs->imgFiles[cs->imgCurFile].s);

	finiTexture (s, &cs->texture);
	initTexture (s, &cs->texture);
	cubeFiniSvg (s);
	cubeInitSvg (s);

	return;
    }

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

    if (cs->opt[CUBE_SCREEN_OPTION_SCALE_IMAGE].value.b)
    {
	cs->tc[2] = COMP_TEX_COORD_X (&cs->texture.matrix, width);
	cs->tc[3] = COMP_TEX_COORD_Y (&cs->texture.matrix, 0.0f);

	cs->tc[4] = COMP_TEX_COORD_X (&cs->texture.matrix, 0.0f);
	cs->tc[5] = COMP_TEX_COORD_Y (&cs->texture.matrix, 0.0f);

	cs->tc[6] = COMP_TEX_COORD_X (&cs->texture.matrix, 0.0f);
	cs->tc[7] = COMP_TEX_COORD_Y (&cs->texture.matrix, height);

	cs->tc[8] = COMP_TEX_COORD_X (&cs->texture.matrix, width);
	cs->tc[9] = COMP_TEX_COORD_Y (&cs->texture.matrix, height);

	cs->tc[10] = COMP_TEX_COORD_X (&cs->texture.matrix, width);
	cs->tc[11] = COMP_TEX_COORD_Y (&cs->texture.matrix, 0.0f);
    }
    else
    {
	float x1 = width  / 2.0f - pw / 2.0f;
	float y1 = height / 2.0f - ph / 2.0f;
	float x2 = width  / 2.0f + pw / 2.0f;
	float y2 = height / 2.0f + ph / 2.0f;

	cs->tc[2] = COMP_TEX_COORD_X (&cs->texture.matrix, x2);
	cs->tc[3] = COMP_TEX_COORD_Y (&cs->texture.matrix, y1);

	cs->tc[4] = COMP_TEX_COORD_X (&cs->texture.matrix, x1);
	cs->tc[5] = COMP_TEX_COORD_Y (&cs->texture.matrix, y1);

	cs->tc[6] = COMP_TEX_COORD_X (&cs->texture.matrix, x1);
	cs->tc[7] = COMP_TEX_COORD_Y (&cs->texture.matrix, y2);

	cs->tc[8] = COMP_TEX_COORD_X (&cs->texture.matrix, x2);
	cs->tc[9] = COMP_TEX_COORD_Y (&cs->texture.matrix, y2);

	cs->tc[10] = COMP_TEX_COORD_X (&cs->texture.matrix, x2);
	cs->tc[11] = COMP_TEX_COORD_Y (&cs->texture.matrix, y1);
    }
}

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

    CUBE_SCREEN (s);

    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 (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 CompOption *
cubeGetScreenOptions (CompPlugin *plugin,
		      CompScreen *screen,
		      int	 *count)
{
    CUBE_SCREEN (screen);

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

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 (CompPlugin      *plugin,
		     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))
		return TRUE;
	}
	break;
    case CUBE_SCREEN_OPTION_SCALE_IMAGE:
	if (compSetBoolOption (o, value))
	{
	    cubeLoadImg (screen, cs->imgCurFile);
	    damageScreen (screen);

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

	    cubeLoadImg (screen, cs->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))
	{
	    cubeUpdateSkyDomeTexture (screen);
	    cubeUpdateSkyDomeList (screen, 1.0f);
	    damageScreen (screen);
	    return TRUE;
	}
	break;
    case CUBE_SCREEN_OPTION_SKYDOME_GRAD_END:
	if (compSetColorOption (o, value))
	{
	    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_FADE_TIME:
    case CUBE_SCREEN_OPTION_ACTIVE_OPACITY:
    case CUBE_SCREEN_OPTION_INACTIVE_OPACITY:
	if (compSetFloatOption (o,value))
	    return TRUE;
    default:
	break;
    }

    return FALSE;
}

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

    o = &cs->opt[CUBE_SCREEN_OPTION_COLOR];
    o->name	  = "color";
    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->name	  = "in";
    o->shortDesc  = N_("Inside Cube");
    o->longDesc	  = N_("Inside cube");
    o->type	  = CompOptionTypeBool;
    o->value.b    = CUBE_IN_DEFAULT;

    o = &cs->opt[CUBE_SCREEN_OPTION_SCALE_IMAGE];
    o->name	  = "scale_image";
    o->shortDesc  = N_("Scale image");
    o->longDesc	  = N_("Scale images to cover top face of cube");
    o->type	  = CompOptionTypeBool;
    o->value.b    = CUBE_SCALE_IMAGE_DEFAULT;

    o = &cs->opt[CUBE_SCREEN_OPTION_IMAGES];
    o->name	         = "images";
    o->shortDesc         = N_("Image files");
    o->longDesc	         = N_("List of PNG and SVG files that should be rendered "
	"on 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_SKYDOME];
    o->name	  = "skydome";
    o->shortDesc  = N_("Skydome");
    o->longDesc	  = N_("Render skydome");
    o->type	  = CompOptionTypeBool;
    o->value.b    = CUBE_SKYDOME_DEFAULT;

    o = &cs->opt[CUBE_SCREEN_OPTION_SKYDOME_IMG];
    o->name	      = "skydome_image";
    o->shortDesc      = N_("Skydome Image");
    o->longDesc	      = N_("Image to use as texture for the skydome");
    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->name	  = "skydome_animated";
    o->shortDesc  = N_("Animate Skydome");
    o->longDesc	  = N_("Animate skydome when rotating cube");
    o->type	  = CompOptionTypeBool;
    o->value.b    = CUBE_SKYDOME_ANIMATE_DEFAULT;

    o = &cs->opt[CUBE_SCREEN_OPTION_SKYDOME_GRAD_START];
    o->name	  = "skydome_gradient_start_color";
    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->name	  = "skydome_gradient_end_color";
    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->name		= "acceleration";
    o->shortDesc	= N_("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->name		= "speed";
    o->shortDesc	= N_("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->name		= "timestep";
    o->shortDesc	= N_("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->name	  = "mipmap";
    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_FADE_TIME];
    o->name = "fade_time";
    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->name = "active_opacity";
    o->shortDesc = N_("Opacity During Rotation");
    o->longDesc = N_("Opacity of desktop window during rotation.");
    o->type = CompOptionTypeFloat;
    o->value.f = CUBE_ACTIVE_OPACITY_DEFAULT;
    o->rest.f.min = CUBE_OPACITY_MIN;
    o->rest.f.max = CUBE_OPACITY_MAX;
    o->rest.f.precision = CUBE_OPACITY_PRECISION;


    o = &cs->opt[CUBE_SCREEN_OPTION_INACTIVE_OPACITY];
    o->name = "inactive_opacity";
    o->shortDesc = N_("Opacity When Not Rotating");
    o->longDesc = N_("Opacity of desktop window when not rotating.");
    o->type = CompOptionTypeFloat;
    o->value.f = CUBE_INACTIVE_OPACITY_DEFAULT;
    o->rest.f.min = CUBE_OPACITY_MIN;
    o->rest.f.max = CUBE_OPACITY_MAX;
    o->rest.f.precision = CUBE_OPACITY_PRECISION;

    o = &cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT_MANUAL_ONLY];
    o->name = "transparent_manual_only";
    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 = CUBE_TRANSPARENT_MANUAL_ONLY_DEFAULT;
}

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;
	    }
	}
    }

    memset (cs->cleared, 0, sizeof (Bool) * s->nOutputDev);
   
    //transparency, mostly copied/ported from beryl's cube.c, orig. by onestone
    if (cs->rotationMode != NoRotation && ((cs->rotationMode == RotationManual) || 
		!(cs->opt[CUBE_SCREEN_OPTION_TRANSPARENT_MANUAL_ONLY].value.b)))
    {
	cs->toOpacity = 
	      (cs->opt[CUBE_SCREEN_OPTION_ACTIVE_OPACITY].value.f / 100.0f) * OPAQUE;
    }
    else
	cs->toOpacity =
	    (cs->opt[CUBE_SCREEN_OPTION_INACTIVE_OPACITY].value.f / 100.0f) * OPAQUE;

    if (cs->opt[CUBE_SCREEN_OPTION_FADE_TIME].value.f == 0.0f)
	cs->desktopOpacity = cs->toOpacity;

    else if (cs->desktopOpacity != cs->toOpacity)
    {
	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,
		 const CompTransform	*transform,
		 Region			 region,
		 int			 output,
		 unsigned int		 mask)
{
    Bool status;

    CUBE_SCREEN (s);

    if (cs->grabIndex || cs->desktopOpacity != OPAQUE)
    {
	mask &= ~PAINT_SCREEN_REGION_MASK;
	mask |= PAINT_SCREEN_TRANSFORMED_MASK;
    }

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

    return status;
}

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

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

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

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

    if (cs->desktopOpacity != OPAQUE)
    {
	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)
    {
	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,
		 const CompTransform	  *transform,
		 Region			  region,
		 unsigned int		  mask)
{
    Bool status;
    CompScreen* s = w->screen;
    CUBE_SCREEN(s);
    
    WindowPaintAttrib wa = *attrib;

    if (w->type & CompWindowTypeDesktopMask)
	wa.opacity = cs->desktopOpacity;

    UNWRAP (cs, s, paintWindow);
    status = (*s->paintWindow) (w, &wa, transform, region, mask);
    WRAP (cs, s, paintWindow, cubePaintWindow);
    
    return status;
}

static Bool
cubeCheckFTB (CompScreen *s, 
              const ScreenPaintAttrib *sAttrib, 
              const CompTransform  *transform,
              int output)
{
    float mvp[16];
    CompTransform sTransform = *transform;
    
    (*s->applyScreenTransform) (s, sAttrib, output, &sTransform);
    transformToScreenSpace (s, output, -sAttrib->zTranslate, &sTransform);

    MULTM(s->projection, sTransform.m, mvp);

    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 ortho[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]};

    if (ortho[2] > 0.0f)	//The viewport is reversed, should be painted front to back.
	return TRUE;
    
    return FALSE;
}

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

    Bool ftb = cubeCheckFTB(s, sAttrib, transform, output);
    
    if (ftb && !(mask & PAINT_SCREEN_ORDER_FRONT_TO_BACK_MASK))
	return;
    if (!ftb && (mask & PAINT_SCREEN_ORDER_FRONT_TO_BACK_MASK))
	return;

    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;

	dView      = cubeOutput / cs->nOutput;
	cubeOutput = cubeOutput % cs->nOutput;

	if (cubeOutput < 0)
	{
	    cubeOutput += cs->nOutput;
	    dView--;
	}

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

	moveScreenViewport (s, -dView, 0, FALSE);
	(*s->paintTransformedScreen) (s, sAttrib, transform,
				      &s->outputDev[output].region,
				      output, mask);
	moveScreenViewport (s, dView, 0, FALSE);
    }
    else
    {
	moveScreenViewport (s, dx, 0, FALSE);
	(*s->paintTransformedScreen) (s, sAttrib, transform, &s->region,
				      output, mask);
	moveScreenViewport (s, -dx, 0, FALSE);
    }
}

void
cubePaintAllViewports (CompScreen              *s,
                       ScreenPaintAttrib       *sAttrib,
	               const CompTransform     *transform,
                       Region		       region,
                       int                     output,
                       unsigned int            mask,
                       int                     xMove,
                       int                     size, 
                       int                     hsize)
{
    CUBE_SCREEN(s);

    ScreenPaintAttrib sa = *sAttrib;

    int i;
    int iFirstSign;	// 1 if we do xMove += i first and -1 if we do xMove -= i first.
    
    if (cs->invert == 1)
    {
	// xMove ==> dx for the viewport which is the nearest to the viewer in z axis.
	// xMove +/- hsize / 2 ==> dx for the viewport which is the farest to the viewer in z axis.
    
	if ((sa.xRotate < 0.0f && hsize % 2 == 1) ||
	    (sa.xRotate > 0.0f && hsize % 2 == 0))
	{
	    xMove += hsize / 2;
	    sa.yRotate -= (hsize / 2) * 360.0f / size;
	    iFirstSign = 1;
	}
        
	else
	{
	    xMove -= hsize / 2;
	    sa.yRotate += (hsize / 2) * 360.0f / size;
	    iFirstSign = -1;
	}
    }
    
    else
    {
    	// xMove is already the dx for farest viewport.
    
	if (sa.xRotate > 0.0f)
	    iFirstSign = -1;
	else
	    iFirstSign = 1;
    }
    
    for (i = 0; i <= hsize / 2; i++)
    {
	xMove += iFirstSign * i;
	sa.yRotate -= cs->invert * iFirstSign * i * 360.0f / size;
	cubeMoveViewportAndPaint (s, &sa, transform, output, mask,
	                          xMove);
	sa.yRotate += cs->invert * iFirstSign * i * 360.0f / size;
	xMove -= iFirstSign * i;

	if (i == 0 || i * 2 == hsize)	// same viewport.
	    continue;

	xMove -= iFirstSign * i;
	sa.yRotate += cs->invert * iFirstSign * i * 360.0f / size;
	cubeMoveViewportAndPaint (s, &sa, transform, output, mask,
	                          xMove);
	sa.yRotate -= cs->invert * iFirstSign * i * 360.0f / size;
	xMove += iFirstSign * i;
    }
}

static void
cubePaintTransformedScreen (CompScreen		    *s,
			    const ScreenPaintAttrib *sAttrib,
			    const CompTransform	    *transform,
			    Region		    region,
			    int			    output,
			    unsigned int	    mask)
{
    ScreenPaintAttrib sa = *sAttrib;
    int		      hsize, xMove = 0;
    float	      size;
    Bool	      clear;
    Bool wasCulled = FALSE;
    GLenum filter = s->display->textureFilter;

    CUBE_SCREEN (s);

    hsize = s->hsize * cs->nOutput;
    size  = hsize;
    
    if (cs->desktopOpacity != OPAQUE)
    {
	wasCulled = glIsEnabled(GL_CULL_FACE);
	if (wasCulled)
	    glDisable(GL_CULL_FACE);
    }

    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;
    }

    clear = cs->cleared[output];
    if (!clear)
    {
	if (cs->sky.name)
	{
	    screenLighting (s, FALSE);

	    glPushMatrix ();

	    if (cs->animateSkyDome && cs->grabIndex == 0)
	    {
		glRotatef (sAttrib->xRotate, 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
	{
	    clearTargetOutput (s->display, GL_COLOR_BUFFER_BIT);
	}

	cs->cleared[output] = TRUE;
    }

    mask &= ~PAINT_SCREEN_CLEAR_MASK;

    UNWRAP (cs, s, paintTransformedScreen);

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

    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));

	/* distance we move the camera back when unfolding the cube.
	   currently hardcoded to 1.5 but it should probably be optional. */
	sa.zCamera -= cs->unfold * 1.5f;
    }
    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;
	
    if (sa.xRotate > 0.0f)
	cs->xrotations = (int) (hsize * sa.xRotate + 180) / 360;
    else
	cs->xrotations = (int) (hsize * sa.xRotate - 180) / 360;
    
    sa.xRotate -= (360.0f * cs->xrotations) / size;
    sa.xRotate *= cs->invert;
    
    xMove = cs->xrotations;
    
    if (cs->grabIndex && cs->opt[CUBE_SCREEN_OPTION_MIPMAP].value.b)
	s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
    
    if (cs->invert == 1)
	mask |= PAINT_SCREEN_ORDER_FRONT_TO_BACK_MASK;	// Outside cube - start with FTB faces.
	                                              	// Inside cube - start with BTF faces.
    
    if (cs->paintAllViewports || cs->desktopOpacity != OPAQUE || cs->invert == -1)
	cubePaintAllViewports(s, &sa, transform, region, output, mask, xMove, size, hsize);
    
    if (cs->invert == 1)
	mask &= ~PAINT_SCREEN_ORDER_FRONT_TO_BACK_MASK;	// Outside cube - continue with caps and then BTF faces.
    else
	mask |= PAINT_SCREEN_ORDER_FRONT_TO_BACK_MASK;	// Inside cube - continue with caps and then FTB faces.

    if (!clear && cs->grabIndex == 0 && hsize > 2 &&
	(cs->invert != 1 || sa.vRotate != 0.0f || sa.yTranslate != 0.0f))
    {
	CompTransform sTransform = *transform;

	screenLighting (s, TRUE);

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

	glPushMatrix ();

	sa.yRotate += 360.0f / size;
	(*s->applyScreenTransform) (s, &sa, output, &sTransform);
	glLoadMatrixf (sTransform.m);
	glTranslatef (cs->outputXOffset, -cs->outputYOffset, 0.0f);
	glScalef (cs->outputXScale, cs->outputYScale, 1.0f);
	sa.yRotate -= 360.0f / size;

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

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

	int i;
	for (i = 0; i < 2; i++)
	{
	    if ((i == 0 && sAttrib->vRotate < 0.0f) ||
	        (i == 1 && sAttrib->vRotate > 0.0f))
	    {
		glNormal3f (0.0f, -1.0f, 0.0f);
		if (cs->invert == 1 && hsize == 4 && cs->texture.name)
		{
		    enableTexture (s, &cs->texture, COMP_TEXTURE_FILTER_GOOD);
		    glTexCoordPointer (2, GL_FLOAT, 0, cs->tc);
		    glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nvertices >> 1);
		    disableTexture (s, &cs->texture);
		    glDisableClientState (GL_TEXTURE_COORD_ARRAY);
		}
		else
		{
		    glDisableClientState (GL_TEXTURE_COORD_ARRAY);
		    glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nvertices >> 1);
		}
	    }
	    else
	    {
	        glNormal3f (0.0f, 1.0f, 0.0f);
	        glDrawArrays (GL_TRIANGLE_FAN, cs->nvertices >> 1,
		              cs->nvertices >> 1);
	    }
	}

	glNormal3f (0.0f, 1.0f, 0.0f);

	glDrawArrays (GL_TRIANGLE_FAN, cs->nvertices >> 1,
		      cs->nvertices >> 1);

	glNormal3f (0.0f, 0.0f, -1.0f);

	glPopMatrix ();

	glColor4usv (defaultColor);
	glEnableClientState (GL_TEXTURE_COORD_ARRAY);
	
	glDisable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    }

    if (cs->paintAllViewports || cs->desktopOpacity != OPAQUE || cs->invert == 1)
	cubePaintAllViewports(s, &sa, transform, region, output, mask, xMove, size, hsize);

    WRAP (cs, s, paintTransformedScreen, cubePaintTransformedScreen);
    
    if (cs->desktopOpacity != OPAQUE && wasCulled)
	glEnable(GL_CULL_FACE);
    
    s->display->textureFilter = filter;
}

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

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

    UNWRAP(cs, s, setClipPlanes);
    (*s->setClipPlanes) (s, region, output);
    WRAP(cs, s, setClipPlanes, cubeSetClipPlanes);

    if (cs->invert == 1)
    {
	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);
    }
    else
    {
	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);
    }
}

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

    matrixTranslate (transform, cs->outputXOffset, -cs->outputYOffset, 0.0f);
    matrixScale (transform, cs->outputXScale, cs->outputYScale, 1.0f);

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

    matrixScale (transform, 1.0f / cs->outputXScale,
		 1.0f / cs->outputYScale, 1.0f);
    matrixTranslate (transform, -cs->outputXOffset, cs->outputYOffset, 0.0f);
}

static void cubeHandleCompizEvent(CompDisplay * d, char *pluginName,
				 char *eventName, CompOption * option, int nOption)
{
    CUBE_DISPLAY(d);

    UNWRAP (cd, d, handleCompizEvent);
    (*d->handleCompizEvent) (d, pluginName, eventName, option, nOption);
    WRAP (cd, d, handleCompizEvent, cubeHandleCompizEvent);
	
    if (strcmp(pluginName, "3d") == 0)
    {
	if (strcmp(eventName, "disableCapsEvent") == 0)
	{
	    Window xid = getIntOptionNamed(option, nOption, "root", 0);
	    CompScreen *s = findScreenAtDisplay(d, xid);

	    if (s)
	    {
		CUBE_SCREEN(s);
		cs->disableCaps = getBoolOptionNamed(option, nOption, "disableCaps", FALSE);
	    }
	}
	
	else if (strcmp(eventName, "paintAllViewportsEvent") == 0)
	{
	    Window xid = getIntOptionNamed(option, nOption, "root", 0);
	    CompScreen *s = findScreenAtDisplay(d, xid);

	    if (s)
	    {
		CUBE_SCREEN(s);
		cs->paintAllViewports = getBoolOptionNamed(option, nOption, "paintAllViewports", FALSE);
	    }
	}
    }
    if (strcmp(pluginName, "rotate") == 0)
    {
	if (strcmp(eventName, "rotateEvent") == 0)
	{
	    Window xid = getIntOptionNamed(option, nOption, "root", 0);
	    CompScreen *s = findScreenAtDisplay(d, xid);

	    if (s)
	    {
		CUBE_SCREEN(s);
		Bool rotate = getBoolOptionNamed(option, nOption, "rotating", FALSE);
		Bool manual = getBoolOptionNamed(option, nOption, "manual", FALSE);

		if (rotate && manual)
		    cs->rotationMode = RotationManual;
		else if (rotate)
		    cs->rotationMode = RotationChange;
		else
		    cs->rotationMode = NoRotation;
		damageScreen(s);
	    }
	}
    }
}

static void
cubeFoldEvent(CompScreen *s, Bool unfolded)
{
    CompOption o[2];
    
    o[0].type = CompOptionTypeInt;
    o[0].name = "root";
    o[0].value.i = s->root;

    o[1].type = CompOptionTypeBool;
    o[1].name = "unfolded";
    o[1].value.b = unfolded;

    (*s->display->handleCompizEvent) (s->display, 
                                      "cube", 
                                      "unfoldEvent", 
                                      o, 2);
}

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);

	if (s->hsize * cs->nOutput < 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)
	{
	    cubeFoldEvent (s, TRUE);
	    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);

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

	if (cs->grabIndex)
	{
	    cubeFoldEvent (s, FALSE);
	    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->imgNFile)
	{
	    cubeLoadImg (s, (cs->imgCurFile + 1) % cs->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->imgNFile)
	{
	    cubeLoadImg (s, (cs->imgCurFile - 1 + cs->imgNFile) % cs->imgNFile);
	    damageScreen (s);
	}
    }

    return FALSE;
}

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

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

    if (cs->imgNFile)
	cubeLoadImg (s, cs->imgCurFile);

    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 CompOption *
cubeGetDisplayOptions (CompPlugin  *plugin,
		       CompDisplay *display,
		       int	   *count)
{
    CUBE_DISPLAY (display);

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

static Bool
cubeSetDisplayOption (CompPlugin  *plugin,
		      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,
			Display     *display)
{
    CompOption *o;

    o = &cd->opt[CUBE_DISPLAY_OPTION_UNFOLD];
    o->name			  = "unfold";
    o->shortDesc		  = N_("Unfold");
    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.keycode   =
	XKeysymToKeycode (display,
			  XStringToKeysym (CUBE_UNFOLD_KEY_DEFAULT));

    o = &cd->opt[CUBE_DISPLAY_OPTION_NEXT];
    o->name			  = "next_slide";
    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	  = CompBindingTypeKey;
    o->value.action.key.modifiers = CUBE_NEXT_MODIFIERS_DEFAULT;
    o->value.action.key.keycode   =
	XKeysymToKeycode (display,
			  XStringToKeysym (CUBE_NEXT_KEY_DEFAULT));

    o = &cd->opt[CUBE_DISPLAY_OPTION_PREV];
    o->name			  = "prev_slide";
    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	  = CompBindingTypeKey;
    o->value.action.key.modifiers = CUBE_PREV_MODIFIERS_DEFAULT;
    o->value.action.key.keycode   =
	XKeysymToKeycode (display,
			  XStringToKeysym (CUBE_PREV_KEY_DEFAULT));
}

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->display);

    d->privates[displayPrivateIndex].ptr = cd;
    
    WRAP (cd, d, handleCompizEvent, cubeHandleCompizEvent);

    return TRUE;
}

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

#ifdef USE_LIBRSVG
    rsvg_term ();
#endif

    CUBE_DISPLAY (d);
    
    UNWRAP (cd, d, handleCompizEvent);

    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->tc[0] = cs->tc[1] = cs->tc[2] = cs->tc[3] = 0.0f;
    cs->tc[4] = cs->tc[5] = cs->tc[6] = cs->tc[7] = 0.0f;

    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;

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

    cs->paintTopBottom = FALSE;

    initTexture (s, &cs->texture);
    initTexture (s, &cs->sky);

    cubeInitSvg (s);

    cs->imgFiles   = 0;
    cs->imgNFile   = 0;
    cs->imgCurFile = 0;

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

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

    cs->unfoldVelocity = 0.0f;

    cs->fullscreenOutput = TRUE;
    
    cs->disableCaps = FALSE;
    cs->paintAllViewports = FALSE;
    cs->rotationMode = NoRotation;
    
    cs->desktopOpacity = (CUBE_INACTIVE_OPACITY_DEFAULT / 100.0f) * OPAQUE;

    memset (cs->cleared, 0, sizeof (cs->cleared));

    cubeScreenInitOptions (cs, s->display->display);

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

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

    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, setScreenOption, cubeSetGlobalScreenOption);
    WRAP (cs, s, outputChangeNotify, cubeOutputChangeNotify);
    WRAP (cs, s, setClipPlanes, cubeSetClipPlanes);
    WRAP (cs, s, paintBackground, cubePaintBackground);
    WRAP (cs, s, paintWindow, cubePaintWindow);

    cubeUpdateOutputs (s);

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

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

    return TRUE;
}

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

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

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

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

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

    cubeFiniSvg (s);

    free (cs);
}

static Bool
cubeInit (CompPlugin *p)
{

#ifdef USE_LIBRSVG
    rsvg_init ();
#endif

    displayPrivateIndex = allocateDisplayPrivateIndex ();
    if (displayPrivateIndex < 0)
	return FALSE;

    return TRUE;
}

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

static int
cubeGetVersion (CompPlugin *plugin,
		int	   version)
{
    return ABIVERSION;
}

CompPluginDep cubeDeps[] = {
    { CompPluginRuleBefore, "scale" },
    { CompPluginRuleBefore, "switcher" }
};

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

CompPluginVTable cubeVTable = {
    "cube",
    N_("Desktop Cube"),
    N_("Place windows on cube"),
    cubeGetVersion,
    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])
};

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