Logo Search packages:      
Sourcecode: beryl-plugins version File versions

cube.c

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

Generated by  Doxygen 1.6.0   Back to index