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

animation.c

/*
 * Animation plugin for beryl
 *
 * animation.c
 *
 * Copyright : (C) 2006 Erkin Bahceci
 * E-mail    : cornelius@beryl-project.org
 *
 * Based on Wobbly and Minimize plugins by
 *           : David Reveman
 * E-mail    : davidr@novell.com>
 *
 * Particle system added by : (C) 2006 Dennis Kasprzyk
 * E-mail                   : onestone@beryl-project.org
 *
 * Added Beam-Up : Florencio Guimaraes
 * E-mail        : florencio@nexcorp.com.br
 *
 *
 * 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.
 */

/*
 * TODO:
 *
 * - Auto direction option: Close in opposite direction of opening
 * - Proper side surface normals for lighting
 * - decoration shadows
 *   - shadow quad generation
 *   - shadow texture coords (from clip tex. matrices)
 *   - draw shadows
 *   - fade in shadows
 *
 * - Voronoi tessellation
 * - Brick tessellation
 * - Triangle tessellation
 * - Hexagonal tessellation
 *
 * Effects:
 * - Circular action for tornado type fx
 * - Tornado 3D (especially for minimize)
 * - Helix 3D (hor. strips descend while they rotate and fade in)
 * - Glass breaking 3D
 *   - Gaussian distr. points (for gradually increasing polygon size
 *                           starting from center or near mouse pointer)
 *   - Drawing cracks
 *   - Gradual cracking
 *
 * - fix slowness during transparent cube with <100 opacity
 * - fix occasional wrong side color in some windows
 * - fix on top windows and panels
 *   (These two only matter for viewing during Rotate Cube.
 *    All windows should be painted with depth test on
 *    like 3d-plugin does)
 * - play better with rotate (fix cube face drawn on top of polygons
 *   after 45 deg. rotation)
 *
 */

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

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

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

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

#include <beryl.h>

#include "animation_tex.h"

#define FAKE_ICON_SIZE 4

#define ANIM_TEXTURE_LIST_INCREMENT 5
#define ANIM_CLIP_LIST_INCREMENT 20
#define NOT_INITIALIZED -10000

#define WIN_X(w) ((w)->attrib.x - (w)->output.left)
#define WIN_Y(w) ((w)->attrib.y - (w)->output.top)
#define WIN_W(w) ((w)->width + (w)->output.left + (w)->output.right)
#define WIN_H(w) ((w)->height + (w)->output.top + (w)->output.bottom)

#define BORDER_X(w) ((w)->attrib.x - (w)->input.left)
#define BORDER_Y(w) ((w)->attrib.y - (w)->input.top)
#define BORDER_W(w) ((w)->width + (w)->input.left + (w)->input.right)
#define BORDER_H(w) ((w)->height + (w)->input.top + (w)->input.bottom)

#define RAND_FLOAT() ((float)rand() / RAND_MAX)


typedef struct _xy_pair
{
      float x, y;
} Point, Vector;

typedef struct
{
      float x1, x2, y1, y2;
} Boxf;

typedef struct _xyz_tuple
{
      float x, y, z;
} Point3d, Vector3d;

// This is intended to be a closed 3D piece of a window with convex polygon
// faces and quad-strip sides. Since decoration texture is separate from
// the window texture, it is more complicated than it would be with a single
// texture: we use glClipPlane with the rectangles (clips) to clip 3D space
// to the region falling within that clip.
// If the polygon is on an edge/corner, it also has 2D shadow quad(s)
// (to be faded out at the beginning of 3D animation if necessary).
typedef struct _PolygonObject
{
      int nVertices;                      // number of total vertices (front + back)
      int nSides;                         // number of sides
      GLfloat *vertices;                  // Positions of vertices relative to center
      GLushort *sideIndices;        // Indices of quad strip for "sides"
      GLfloat *normals;             // Surface normals for 2+nSides faces

      Box boundingBox;              // Bound. box to test intersection with clips

      GLfloat *vertexTexCoords4Clips;
      // Tex coords for each intersecting clip and for each vertex
      // ordered as c1.v1.x, c1.v1.y, c1.v2.x, c1.v2.y, c2.v1.x, c2.v1.y, ...

      /*
         int *vertexOnEdge;           // 1,2,3,4: W,E,N,S edge  0: not edge
         // 5,6,7,8: NW,NE,SW,SE corner
         // (used for shadow quad generation)
         int nShadowQuads;
         GLfloat *shadowVertices; // Shadow vertices positions relative to center
         GLfloat *shadowTexCoords;    // Texture coords of shadow vertices
       */
      // Animation effect parameters

      Point3d centerPosStart;       // Starting position of center
      float rotAngleStart;          // Starting rotation angle

      Point3d centerPos;                  // Position of center
      Vector3d rotAxis;             // Rotation axis vector
      float rotAngle;                     // Rotation angle
      Point3d rotAxisOffset;        // Rotation axis translate amount

      Point centerRelPos;                 // Relative pos of center within the window

      Vector3d finalRelPos;         // Velocity factor for scripted movement
      float finalRotAng;                  // Final rotation angle around rotAxis

      float moveStartTime;          // Movement starts at this time ([0-1] range)
      float moveDuration;                 // Movement lasts this long     ([0-1] range)

      float fadeStartTime;          // Fade out starts at this time ([0,1] range)
      float fadeDuration;                 // Duration of fade out         ([0,1] range)
} PolygonObject;

typedef struct _Clip4Polygons // Rectangular clips
{                                               // (to hold clips passed to AddWindowGeometry)
      int id;                                   // clip id (what number this clip is among
      // passed clips)
      Box box;                            // Coords
      Boxf boxf;                          // Float coords (for small clipping adjustment)
      CompMatrix texMatrix;         // Corresponding texture coord. matrix
      int *intersectingPolygons;
      int nIntersectingPolygons;    // Clips (in PolygonSet) that intersect
      GLfloat *polygonVertexTexCoords;
      // Tex coords for each intersecting polygon and for each vertex
      // ordered as p1.v1.x, p1.v1.y, p1.v2.x, p1.v2.y, p2.v1.x, p2.v1.y, ...
} Clip4Polygons;

typedef struct _PolygonSet          // Polygon objects with same thickness
{
      int nClips;                         // Rect. clips collected in AddWindowGeometries
      Clip4Polygons *clips;         // List of clips
      int clipCapacity;             // # of clips this list can hold
      int firstNondrawnClip;
      int *lastClipInGroup;         // index of the last clip in each group of clips
      // drawn in drawGeometry func.

      Bool doDepthTest;           // whether depth testing should be used in the effect
      Bool doLighting;            // whether lighting should be used in the effect
      Bool correctPerspective;    // Whether perspective look should be neutralized
      PolygonObject *polygons;      // The polygons in this set
      int nPolygons;
      float thickness;              // Window thickness (depth along z axis)
      int nTotalFrontVertices;      // Total # of polygon vertices on front faces
      float backAndSidesFadeDur;    // How long side and back faces should fade in/out
      float allFadeDuration;        // Duration of fade out at the end in [0,1] range
      // when all polygons fade out at the same time.
      // If >-1, this overrides fadeDuration in PolygonObject

      Bool includeShadows;        // include shadows in polygon
} PolygonSet;

typedef struct _WaveParam
{
      float halfWidth;
      float amp;
      float pos;
} WaveParam;

typedef enum
{
      WindowEventNone = 0,
      WindowEventMinimize,
      WindowEventUnminimize,
      WindowEventClose,
      WindowEventCreate,
      WindowEventFocus,
      WindowEventShade,
      WindowEventUnshade
} WindowEvent;

typedef struct _Object
{
      Point gridPosition;           // position on window in [0,1] range
      Point position;               // position on screen
      Point3d posRel3d;             // position relative to model center
      //                                     (for 3d looking effects)

      // Texture x, y coordinates will be offset by given amounts
      // for quads that fall after and before this object in x and y directions.
      // Currently only y offset can be used.
      Point offsetTexCoordForQuadBefore;
      Point offsetTexCoordForQuadAfter;
} Object;

typedef struct _Model
{
      Object *objects;
      int numObjects;
      int gridWidth;
      int gridHeight;

      int winWidth;                       // keeps win. size when model was created
      int winHeight;                      //

      float remainderSteps;
      Vector scale;
      Point scaleOrigin;
      Point topLeft;
      Point bottomRight;

      int magicLampWaveCount;
      WaveParam *magicLampWaves;
      WindowEvent forWindowEvent;
      float topHeight;
      float bottomHeight;
} Model;


static void
modelInitObjects(Model * model, int x, int y, int width, int height);

static void
postAnimationCleanup(CompWindow * w, Bool resetAnimation);


#define ANIM_MAGIC_LAMP1_GRID_RES_DEFAULT  100
#define ANIM_MAGIC_LAMP1_GRID_RES_MIN      4
#define ANIM_MAGIC_LAMP1_GRID_RES_MAX      200

#define ANIM_MAGIC_LAMP1_MAX_WAVES_DEFAULT  5
#define ANIM_MAGIC_LAMP1_MAX_WAVES_MIN      3
#define ANIM_MAGIC_LAMP1_MAX_WAVES_MAX      20

#define ANIM_MAGIC_LAMP1_WAVE_AMP_MIN_DEFAULT  200
#define ANIM_MAGIC_LAMP1_WAVE_AMP_MIN_MIN      200
#define ANIM_MAGIC_LAMP1_WAVE_AMP_MIN_MAX      2000
#define ANIM_MAGIC_LAMP1_WAVE_AMP_MIN_PRECISION 5

#define ANIM_MAGIC_LAMP1_WAVE_AMP_MAX_DEFAULT  300
#define ANIM_MAGIC_LAMP1_WAVE_AMP_MAX_MIN      200
#define ANIM_MAGIC_LAMP1_WAVE_AMP_MAX_MAX      2000
#define ANIM_MAGIC_LAMP1_WAVE_AMP_MAX_PRECISION 5

#define ANIM_MAGIC_LAMP1_CREATE_START_WIDTH_DEFAULT  30
#define ANIM_MAGIC_LAMP1_CREATE_START_WIDTH_MIN      0
#define ANIM_MAGIC_LAMP1_CREATE_START_WIDTH_MAX      500

#define ANIM_MAGIC_LAMP2_GRID_RES_DEFAULT  100
#define ANIM_MAGIC_LAMP2_GRID_RES_MIN      2
#define ANIM_MAGIC_LAMP2_GRID_RES_MAX      200

#define ANIM_MAGIC_LAMP2_MAX_WAVES_DEFAULT  3
#define ANIM_MAGIC_LAMP2_MAX_WAVES_MIN      3
#define ANIM_MAGIC_LAMP2_MAX_WAVES_MAX      20

#define ANIM_MAGIC_LAMP2_WAVE_AMP_MIN_DEFAULT  200
#define ANIM_MAGIC_LAMP2_WAVE_AMP_MIN_MIN      200
#define ANIM_MAGIC_LAMP2_WAVE_AMP_MIN_MAX      2000
#define ANIM_MAGIC_LAMP2_WAVE_AMP_MIN_PRECISION 5

#define ANIM_MAGIC_LAMP2_WAVE_AMP_MAX_DEFAULT  300
#define ANIM_MAGIC_LAMP2_WAVE_AMP_MAX_MIN      200
#define ANIM_MAGIC_LAMP2_WAVE_AMP_MAX_MAX      2000
#define ANIM_MAGIC_LAMP2_WAVE_AMP_MAX_PRECISION 5

#define ANIM_MAGIC_LAMP2_CREATE_START_WIDTH_DEFAULT  30
#define ANIM_MAGIC_LAMP2_CREATE_START_WIDTH_MIN      0
#define ANIM_MAGIC_LAMP2_CREATE_START_WIDTH_MAX      500

#define ANIM_MAGIC_LAMP3_GRID_RES_DEFAULT  100
#define ANIM_MAGIC_LAMP3_GRID_RES_MIN      2
#define ANIM_MAGIC_LAMP3_GRID_RES_MAX      200

#define ANIM_MAGIC_LAMP3_MAX_WAVES_DEFAULT  0
#define ANIM_MAGIC_LAMP3_MAX_WAVES_MIN      0
#define ANIM_MAGIC_LAMP3_MAX_WAVES_MAX      20

#define ANIM_MAGIC_LAMP3_WAVE_AMP_MIN_DEFAULT  200
#define ANIM_MAGIC_LAMP3_WAVE_AMP_MIN_MIN      200
#define ANIM_MAGIC_LAMP3_WAVE_AMP_MIN_MAX      2000
#define ANIM_MAGIC_LAMP3_WAVE_AMP_MIN_PRECISION 5

#define ANIM_MAGIC_LAMP3_WAVE_AMP_MAX_DEFAULT  300
#define ANIM_MAGIC_LAMP3_WAVE_AMP_MAX_MIN      200
#define ANIM_MAGIC_LAMP3_WAVE_AMP_MAX_MAX      2000
#define ANIM_MAGIC_LAMP3_WAVE_AMP_MAX_PRECISION 5

#define ANIM_MAGIC_LAMP3_CREATE_START_WIDTH_DEFAULT  30
#define ANIM_MAGIC_LAMP3_CREATE_START_WIDTH_MIN      0
#define ANIM_MAGIC_LAMP3_CREATE_START_WIDTH_MAX      500

#define ANIM_SIDEKICK_NUM_ROTATIONS_DEFAULT      0.5
#define ANIM_SIDEKICK_NUM_ROTATIONS_MIN          0
#define ANIM_SIDEKICK_NUM_ROTATIONS_MAX          5.0
#define ANIM_SIDEKICK_NUM_ROTATIONS_PRECISION    0.01

#define ANIM_MINIMIZE_DURATION_DEFAULT   0.2
#define ANIM_MINIMIZE_DURATION_MIN       0.1
#define ANIM_MINIMIZE_DURATION_MAX       10
#define ANIM_MINIMIZE_DURATION_PRECISION 0.05

#define ANIM_UNMINIMIZE_DURATION_DEFAULT   0.2
#define ANIM_UNMINIMIZE_DURATION_MIN       0.1
#define ANIM_UNMINIMIZE_DURATION_MAX       10
#define ANIM_UNMINIMIZE_DURATION_PRECISION 0.05

#define ANIM_CREATE1_DURATION_DEFAULT   0.2
#define ANIM_CREATE1_DURATION_MIN       0.1
#define ANIM_CREATE1_DURATION_MAX       10
#define ANIM_CREATE1_DURATION_PRECISION 0.05

#define ANIM_CLOSE1_DURATION_DEFAULT   0.2
#define ANIM_CLOSE1_DURATION_MIN       0.1
#define ANIM_CLOSE1_DURATION_MAX       10
#define ANIM_CLOSE1_DURATION_PRECISION 0.05

#define ANIM_CREATE2_DURATION_DEFAULT   0.15
#define ANIM_CREATE2_DURATION_MIN       0.1
#define ANIM_CREATE2_DURATION_MAX       10
#define ANIM_CREATE2_DURATION_PRECISION 0.05

#define ANIM_CLOSE2_DURATION_DEFAULT   0.15
#define ANIM_CLOSE2_DURATION_MIN       0.1
#define ANIM_CLOSE2_DURATION_MAX       10
#define ANIM_CLOSE2_DURATION_PRECISION 0.05

#define ANIM_FOCUS_DURATION_DEFAULT   0.5
#define ANIM_FOCUS_DURATION_MIN       0.1
#define ANIM_FOCUS_DURATION_MAX       10
#define ANIM_FOCUS_DURATION_PRECISION 0.05

#define ANIM_SHADE_DURATION_DEFAULT   0.5
#define ANIM_SHADE_DURATION_MIN       0.1
#define ANIM_SHADE_DURATION_MAX       10
#define ANIM_SHADE_DURATION_PRECISION 0.05

#define ANIM_UNSHADE_DURATION_DEFAULT   0.5
#define ANIM_UNSHADE_DURATION_MIN       0.1
#define ANIM_UNSHADE_DURATION_MAX       10
#define ANIM_UNSHADE_DURATION_PRECISION 0.05

#define ANIM_FOCUS_WAVE_WIDTH_DEFAULT   0.7
#define ANIM_FOCUS_WAVE_WIDTH_MIN       0.02
#define ANIM_FOCUS_WAVE_WIDTH_MAX       3
#define ANIM_FOCUS_WAVE_WIDTH_PRECISION 0.02

#define ANIM_FOCUS_WAVE_AMP_DEFAULT   0.03
#define ANIM_FOCUS_WAVE_AMP_MIN       0
#define ANIM_FOCUS_WAVE_AMP_MAX       1
#define ANIM_FOCUS_WAVE_AMP_PRECISION 0.01

#define ANIM_CURVED_FOLD_AMP_DEFAULT   0.15
#define ANIM_CURVED_FOLD_AMP_MIN      -0.5
#define ANIM_CURVED_FOLD_AMP_MAX       0.5
#define ANIM_CURVED_FOLD_AMP_PRECISION 0.01

#define ANIM_HORIZONTAL_FOLDS_NUM_FOLDS_DEFAULT  5
#define ANIM_HORIZONTAL_FOLDS_NUM_FOLDS_MIN      1
#define ANIM_HORIZONTAL_FOLDS_NUM_FOLDS_MAX      50

#define ANIM_HORIZONTAL_FOLDS_AMP_DEFAULT   0.07
#define ANIM_HORIZONTAL_FOLDS_AMP_MIN      -0.5
#define ANIM_HORIZONTAL_FOLDS_AMP_MAX       0.5
#define ANIM_HORIZONTAL_FOLDS_AMP_PRECISION 0.01

#define ANIM_TIME_STEP_DEFAULT   10
#define ANIM_TIME_STEP_MIN       1
#define ANIM_TIME_STEP_MAX       400

#define ANIM_TIME_STEP_INTENSE_DEFAULT   30
#define ANIM_TIME_STEP_INTENSE_MIN       1
#define ANIM_TIME_STEP_INTENSE_MAX       400

#define ANIM_ROLLUP_FIXED_INTERIOR_DEFAULT  FALSE

#define ANIM_ALL_RANDOM_DEFAULT  FALSE

#define ANIM_FIRE_SIZE_DEFAULT          5.0
#define ANIM_FIRE_SIZE_MIN              0.1
#define ANIM_FIRE_SIZE_MAX              20.0
#define ANIM_FIRE_SIZE_PRECISION        0.1

#define ANIM_FIRE_SLOWDOWN_DEFAULT      0.5
#define ANIM_FIRE_SLOWDOWN_MIN          0.1
#define ANIM_FIRE_SLOWDOWN_MAX          10.0
#define ANIM_FIRE_SLOWDOWN_PRECISION    0.1

#define ANIM_FIRE_LIFE_DEFAULT          0.7
#define ANIM_FIRE_LIFE_MIN              0.1
#define ANIM_FIRE_LIFE_MAX              1.0
#define ANIM_FIRE_LIFE_PRECISION        0.1

#define ANIM_FIRE_PARTICLES_DEFAULT     1000
#define ANIM_FIRE_PARTICLES_MIN         100
#define ANIM_FIRE_PARTICLES_MAX         10000

#define ANIM_FIRE_COLOR_RED_DEFAULT     0xffff
#define ANIM_FIRE_COLOR_GREEN_DEFAULT   0x3333
#define ANIM_FIRE_COLOR_BLUE_DEFAULT    0x0555
#define ANIM_FIRE_COLOR_ALPHA_DEFAULT   0xffff

#define ANIM_FIRE_CONSTANT_SPEED_DEFAULT FALSE

#define ANIM_FIRE_SMOKE_DEFAULT         FALSE
#define ANIM_FIRE_MYSTICAL_DEFAULT         FALSE

#define ANIM_BEAMUP_SIZE_DEFAULT          8.0
#define ANIM_BEAMUP_SIZE_MIN              0.1
#define ANIM_BEAMUP_SIZE_MAX              20.0
#define ANIM_BEAMUP_SIZE_PRECISION        0.1

#define ANIM_BEAMUP_LIFE_DEFAULT          0.7
#define ANIM_BEAMUP_LIFE_MIN              0.1
#define ANIM_BEAMUP_LIFE_MAX              1.0
#define ANIM_BEAMUP_LIFE_PRECISION        0.1


#define ANIM_BEAMUP_SLOWDOWN_DEFAULT      1.0
#define ANIM_BEAMUP_SLOWDOWN_MIN          0.1
#define ANIM_BEAMUP_SLOWDOWN_MAX          10.0
#define ANIM_BEAMUP_SLOWDOWN_PRECISION    0.1

#define ANIM_BEAMUP_SPACING_DEFAULT     5
#define ANIM_BEAMUP_SPACING_MIN         1
#define ANIM_BEAMUP_SPACING_MAX         20

#define ANIM_BEAMUP_COLOR_RED_DEFAULT   0x7fff
#define ANIM_BEAMUP_COLOR_GREEN_DEFAULT 0x7fff
#define ANIM_BEAMUP_COLOR_BLUE_DEFAULT  0x7fff
#define ANIM_BEAMUP_COLOR_ALPHA_DEFAULT 0xffff


#define ANIM_GLIDE1_AWAY_POS_DEFAULT      1.0
#define ANIM_GLIDE1_AWAY_POS_MIN          -2.0
#define ANIM_GLIDE1_AWAY_POS_MAX          1.0
#define ANIM_GLIDE1_AWAY_POS_PRECISION    0.05

#define ANIM_GLIDE1_AWAY_ANGLE_DEFAULT      90.0
#define ANIM_GLIDE1_AWAY_ANGLE_MIN          -540.0
#define ANIM_GLIDE1_AWAY_ANGLE_MAX          540.0
#define ANIM_GLIDE1_AWAY_ANGLE_PRECISION    5.0

#define ANIM_GLIDE1_THICKNESS_DEFAULT      0.0
#define ANIM_GLIDE1_THICKNESS_MIN          0.0
#define ANIM_GLIDE1_THICKNESS_MAX          500.0
#define ANIM_GLIDE1_THICKNESS_PRECISION    5.0

#define ANIM_GLIDE2_AWAY_POS_DEFAULT      -0.4
#define ANIM_GLIDE2_AWAY_POS_MIN          -2.0
#define ANIM_GLIDE2_AWAY_POS_MAX          1.0
#define ANIM_GLIDE2_AWAY_POS_PRECISION    0.05

#define ANIM_GLIDE2_AWAY_ANGLE_DEFAULT      -45.0
#define ANIM_GLIDE2_AWAY_ANGLE_MIN          -540.0
#define ANIM_GLIDE2_AWAY_ANGLE_MAX          540.0
#define ANIM_GLIDE2_AWAY_ANGLE_PRECISION    5.0

#define ANIM_GLIDE2_THICKNESS_DEFAULT      0.0
#define ANIM_GLIDE2_THICKNESS_MIN          0.0
#define ANIM_GLIDE2_THICKNESS_MAX          500.0
#define ANIM_GLIDE2_THICKNESS_PRECISION    5.0


typedef enum
{
      AnimDirectionDown = 0,
      AnimDirectionUp,
      AnimDirectionLeft,
      AnimDirectionRight,
      AnimDirectionRandom,
      AnimDirectionAuto
} AnimDirection;

static char *animDirectionName[] = {
      N_("Down"),
      N_("Up"),
      N_("Left"),
      N_("Right"),
      N_("Random"),
      N_("Automatic")
};

#define ANIM_FIRE_DIRECTION_DEFAULT AnimDirectionDown
#define ANIM_DOMINO_DIRECTION_DEFAULT AnimDirectionAuto
#define ANIM_RAZR_DIRECTION_DEFAULT AnimDirectionAuto

typedef enum
{
      PostprocessDisablingNone = 0,
      PostprocessDisablingWindow,
      PostprocessDisablingScreen
} PostprocessDisabling;

static char *ppDisablingName[] = {
      N_("No disabling"),
      N_("Animated window"),
      N_("All windows")
};

typedef enum
{
      ZoomFromCenterOff = 0,
      ZoomFromCenterMin,
      ZoomFromCenterCreate,
      ZoomFromCenterOn
} ZoomFromCenter;

static char *zoomFromCenterOption[] = {
      N_("Off"),
      N_("Minimize/Unminimize Only"),
      N_("Create/Close Only"),
      N_("On")
};

#define ANIM_ZOOM_FROM_CENTER_DEFAULT 0

#define ANIM_DISABLE_PP_FX_DEFAULT PostprocessDisablingWindow

// =====================  Particle engine  =========================

typedef struct _Particle
{
      float life;                         // particle life
      float fade;                         // fade speed
      float width;                        // particle width
      float height;                       // particle height
      float w_mod;                        // particle size modification during life
      float h_mod;                        // particle size modification during life
      float r;                            // red value
      float g;                            // green value
      float b;                            // blue value
      float a;                            // alpha value
      float x;                            // X position
      float y;                            // Y position
      float z;                            // Z position
      float xi;                           // X direction
      float yi;                           // Y direction
      float zi;                           // Z direction
      float xg;                           // X gravity
      float yg;                           // Y gravity
      float zg;                           // Z gravity
      float xo;                           // orginal X position
      float yo;                           // orginal Y position
      float zo;                           // orginal Z position
} Particle;

typedef struct _ParticleSystem
{
      int numParticles;
      Particle *particles;
      float slowdown;
      GLuint tex;
      Bool active;
      int x, y;
      float darken;
      GLuint blendMode;

      // Moved from drawParticles to get rid of spurious malloc's
      GLfloat *vertices_cache;
      int vertex_cache_count;
      GLfloat *coords_cache;
      int coords_cache_count;
      GLfloat *colors_cache;
      int color_cache_count;
      GLfloat *dcolors_cache;
      int dcolors_cache_count;
} ParticleSystem;


static void initParticles(int numParticles, ParticleSystem * ps)
{
      if (ps->particles)
            free(ps->particles);
      ps->particles = calloc(1, sizeof(Particle) * numParticles);
      ps->tex = 0;
      ps->numParticles = numParticles;
      ps->slowdown = 1;
      ps->active = FALSE;

      // Initialize cache
      ps->vertices_cache = NULL;
      ps->colors_cache = NULL;
      ps->coords_cache = NULL;
      ps->dcolors_cache = NULL;
      ps->vertex_cache_count = 0;
      ps->color_cache_count = 0;
      ps->coords_cache_count = 0;
      ps->dcolors_cache_count = 0;

      int i;

      for (i = 0; i < numParticles; i++)
      {
            ps->particles[i].life = 0.0f;
      }
}

static void drawParticles(CompScreen * s, CompWindow * w, ParticleSystem * ps)
{
      glPushMatrix();
      if (w)
            glTranslated(WIN_X(w) - ps->x, WIN_Y(w) - ps->y, 0);

      glEnable(GL_BLEND);
      if (ps->tex)
      {
            glBindTexture(GL_TEXTURE_2D, ps->tex);
            glEnable(GL_TEXTURE_2D);
      }
      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

      int i;
      Particle *part;

      /* Check that the cache is big enough */
      if (ps->numParticles > ps->vertex_cache_count)
      {
            ps->vertices_cache =
                        realloc(ps->vertices_cache,
                                    ps->numParticles * 4 * 3 * sizeof(GLfloat));
            ps->vertex_cache_count = ps->numParticles;
      }

      if (ps->numParticles > ps->coords_cache_count)
      {
            ps->coords_cache =
                        realloc(ps->coords_cache,
                                    ps->numParticles * 4 * 2 * sizeof(GLfloat));
            ps->coords_cache_count = ps->numParticles;
      }

      if (ps->numParticles > ps->color_cache_count)
      {
            ps->colors_cache =
                        realloc(ps->colors_cache,
                                    ps->numParticles * 4 * 4 * sizeof(GLfloat));
            ps->color_cache_count = ps->numParticles;
      }

      if (ps->darken > 0)
      {
            if (ps->dcolors_cache_count < ps->numParticles)
            {
                  ps->dcolors_cache =
                              realloc(ps->dcolors_cache,
                                          ps->numParticles * 4 * 4 * sizeof(GLfloat));
                  ps->dcolors_cache_count = ps->numParticles;
            }
      }

      GLfloat *dcolors = ps->dcolors_cache;
      GLfloat *vertices = ps->vertices_cache;
      GLfloat *coords = ps->coords_cache;
      GLfloat *colors = ps->colors_cache;

      int numActive = 0;

      for (i = 0; i < ps->numParticles; i++)
      {
            part = &ps->particles[i];
            if (part->life > 0.0f)
            {
                  numActive += 4;

                  float w = part->width / 2;
                  float h = part->height / 2;

                  w += (w * part->w_mod) * part->life;
                  h += (h * part->h_mod) * part->life;

                  vertices[0] = part->x - w;
                  vertices[1] = part->y - h;
                  vertices[2] = part->z;

                  vertices[3] = part->x - w;
                  vertices[4] = part->y + h;
                  vertices[5] = part->z;

                  vertices[6] = part->x + w;
                  vertices[7] = part->y + h;
                  vertices[8] = part->z;

                  vertices[9] = part->x + w;
                  vertices[10] = part->y - h;
                  vertices[11] = part->z;

                  vertices += 12;

                  coords[0] = 0.0;
                  coords[1] = 0.0;

                  coords[2] = 0.0;
                  coords[3] = 1.0;

                  coords[4] = 1.0;
                  coords[5] = 1.0;

                  coords[6] = 1.0;
                  coords[7] = 0.0;

                  coords += 8;

                  colors[0] = part->r;
                  colors[1] = part->g;
                  colors[2] = part->b;
                  colors[3] = part->life * part->a;
                  colors[4] = part->r;
                  colors[5] = part->g;
                  colors[6] = part->b;
                  colors[7] = part->life * part->a;
                  colors[8] = part->r;
                  colors[9] = part->g;
                  colors[10] = part->b;
                  colors[11] = part->life * part->a;
                  colors[12] = part->r;
                  colors[13] = part->g;
                  colors[14] = part->b;
                  colors[15] = part->life * part->a;

                  colors += 16;

                  if (ps->darken > 0)
                  {

                        dcolors[0] = part->r;
                        dcolors[1] = part->g;
                        dcolors[2] = part->b;
                        dcolors[3] = part->life * part->a * ps->darken;
                        dcolors[4] = part->r;
                        dcolors[5] = part->g;
                        dcolors[6] = part->b;
                        dcolors[7] = part->life * part->a * ps->darken;
                        dcolors[8] = part->r;
                        dcolors[9] = part->g;
                        dcolors[10] = part->b;
                        dcolors[11] = part->life * part->a * ps->darken;
                        dcolors[12] = part->r;
                        dcolors[13] = part->g;
                        dcolors[14] = part->b;
                        dcolors[15] = part->life * part->a * ps->darken;

                        dcolors += 16;
                  }
            }
      }

      glEnableClientState(GL_COLOR_ARRAY);

      glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), ps->coords_cache);
      glVertexPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), ps->vertices_cache);

      // darken the background
      if (ps->darken > 0)
      {
            glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
            glColorPointer(4, GL_FLOAT, 4 * sizeof(GLfloat), ps->dcolors_cache);
            glDrawArrays(GL_QUADS, 0, numActive);
      }
      // draw particles
      glBlendFunc(GL_SRC_ALPHA, ps->blendMode);

      glColorPointer(4, GL_FLOAT, 4 * sizeof(GLfloat), ps->colors_cache);

      glDrawArrays(GL_QUADS, 0, numActive);

      glDisableClientState(GL_COLOR_ARRAY);

      glPopMatrix();
      glColor4usv(defaultColor);
      screenTexEnvMode(s, GL_REPLACE);
      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
      glDisable(GL_TEXTURE_2D);
      glDisable(GL_BLEND);
}

static void updateParticles(ParticleSystem * ps, float time)
{
      int i;
      Particle *part;
      float speed = (time / 50.0);
      float slowdown = ps->slowdown * (1 - MAX(0.99, time / 1000.0)) * 1000;

      ps->active = FALSE;

      for (i = 0; i < ps->numParticles; i++)
      {
            part = &ps->particles[i];
            if (part->life > 0.0f)
            {
                  // move particle
                  part->x += part->xi / slowdown;
                  part->y += part->yi / slowdown;
                  part->z += part->zi / slowdown;

                  // modify speed
                  part->xi += part->xg * speed;
                  part->yi += part->yg * speed;
                  part->zi += part->zg * speed;

                  // modify life
                  part->life -= part->fade * speed;
                  ps->active = TRUE;
            }
      }
}

static void finiParticles(ParticleSystem * ps)
{
      free(ps->particles);
      if (ps->tex)
            glDeleteTextures(1, &ps->tex);

      if (ps->vertices_cache)
            free(ps->vertices_cache);
      if (ps->colors_cache)
            free(ps->colors_cache);
      if (ps->coords_cache)
            free(ps->coords_cache);
      if (ps->dcolors_cache)
            free(ps->dcolors_cache);
}

// =====================  END: Particle engine  =========================

static char *allEffectName[] = {
      N_("None"),
      N_("Random"),
      N_("Beam Up"),
      N_("Burn"),
      N_("Curved Fold"),
      N_("Domino (3D)"),
      N_("Dream"),
      N_("Explode (3D)"),
      N_("Fade"),
      N_("Glide 1 (3D)"),
      N_("Glide 2 (3D)"),
      N_("Horizontal Folds"),
      N_("Leaf Spread (3D)"),
      N_("Magic Lamp 1"),
      N_("Magic Lamp 2"),
      N_("Magic Lamp 3 (Vacuum)"),
      N_("Razr (3D)"),
      N_("Roll Up"),
      N_("Sidekick"),
      N_("Wave"),
      N_("Zoom")
};

typedef enum
{
      AnimEffectNone = 0,
      AnimEffectRandom,
      AnimEffectBeamUp,
      AnimEffectBurn,
      AnimEffectCurvedFold,
      AnimEffectDomino3D,
      AnimEffectDream,
      AnimEffectExplode3D,
      AnimEffectFade,
      AnimEffectGlide3D1,
      AnimEffectGlide3D2,
      AnimEffectHorizontalFolds,
      AnimEffectLeafSpread3D,
      AnimEffectMagicLamp1,
      AnimEffectMagicLamp2,
      AnimEffectMagicLamp3,
      AnimEffectRazr3D,
      AnimEffectRollUp,
      AnimEffectSidekick,
      AnimEffectWave,
      AnimEffectZoom,
      AnimEffectNum
} AnimEffect;

#define ANIM_MINIMIZE_DEFAULT   AnimEffectMagicLamp2
#define ANIM_UNMINIMIZE_DEFAULT AnimEffectMagicLamp2
#define ANIM_CLOSE1_DEFAULT     AnimEffectZoom
#define ANIM_CREATE1_DEFAULT    AnimEffectZoom
#define ANIM_CLOSE2_DEFAULT     AnimEffectFade
#define ANIM_CREATE2_DEFAULT    AnimEffectFade
#define ANIM_FOCUS_DEFAULT      AnimEffectNone
#define ANIM_SHADE_DEFAULT      AnimEffectRollUp
#define ANIM_UNSHADE_DEFAULT    AnimEffectRollUp

static char *minimizeEffectName[] = {
      N_("None"),
      N_("Random"),
      N_("Beam Up"),
      N_("Burn"),
      N_("Curved Fold"),
      N_("Domino (3D)"),
      N_("Dream"),
      N_("Explode (3D)"),
      N_("Fade"),
      N_("Glide 1 (3D)"),
      N_("Glide 2 (3D)"),
      N_("Horizontal Folds"),
      N_("Leaf Spread (3D)"),
      N_("Magic Lamp 1"),
      N_("Magic Lamp 2"),
      N_("Razr (3D)"),
      N_("Sidekick"),
      N_("Zoom")
};

static AnimEffect minimizeEffectType[] = {
      AnimEffectNone,
      AnimEffectRandom,
      AnimEffectBeamUp,
      AnimEffectBurn,
      AnimEffectCurvedFold,
      AnimEffectDomino3D,
      AnimEffectDream,
      AnimEffectExplode3D,
      AnimEffectFade,
      AnimEffectGlide3D1,
      AnimEffectGlide3D2,
      AnimEffectHorizontalFolds,
      AnimEffectLeafSpread3D,
      AnimEffectMagicLamp1,
      AnimEffectMagicLamp2,
      AnimEffectRazr3D,
      AnimEffectSidekick,
      AnimEffectZoom
};

#define NUM_MINIMIZE_EFFECT (sizeof (minimizeEffectType) / sizeof (minimizeEffectType[0]))

static char *closeEffectName[] = {
      N_("None"),
      N_("Random"),
      N_("Beam Up"),
      N_("Burn"),
      N_("Curved Fold"),
      N_("Domino (3D)"),
      N_("Dream"),
      N_("Explode (3D)"),
      N_("Fade"),
      N_("Glide 1 (3D)"),
      N_("Glide 2 (3D)"),
      N_("Horizontal Folds"),
      N_("Leaf Spread (3D)"),
      N_("Magic Lamp 1"),
      N_("Magic Lamp 2"),
      N_("Magic Lamp 3 (Vacuum)"),
      N_("Razr (3D)"),
      N_("Sidekick"),
      N_("Wave"),
      N_("Zoom")
};

static AnimEffect closeEffectType[] = {
      AnimEffectNone,
      AnimEffectRandom,
      AnimEffectBeamUp,
      AnimEffectBurn,
      AnimEffectCurvedFold,
      AnimEffectDomino3D,
      AnimEffectDream,
      AnimEffectExplode3D,
      AnimEffectFade,
      AnimEffectGlide3D1,
      AnimEffectGlide3D2,
      AnimEffectHorizontalFolds,
      AnimEffectLeafSpread3D,
      AnimEffectMagicLamp1,
      AnimEffectMagicLamp2,
      AnimEffectMagicLamp3,
      AnimEffectRazr3D,
      AnimEffectSidekick,
      AnimEffectWave,
      AnimEffectZoom
};

#define NUM_CLOSE_EFFECT (sizeof (closeEffectType) / sizeof (closeEffectType[0]))

static char *focusEffectName[] = {
      N_("None"),
      N_("Wave")
};

static AnimEffect focusEffectType[] = {
      AnimEffectNone,
      AnimEffectWave
};

#define NUM_FOCUS_EFFECT (sizeof (focusEffectType) / sizeof (focusEffectType[0]))

static char *shadeEffectName[] = {
      N_("None"),
      N_("Random"),
      N_("Curved Fold"),
      N_("Horizontal Folds"),
      N_("Roll Up")
};

static AnimEffect shadeEffectType[] = {
      AnimEffectNone,
      AnimEffectRandom,
      AnimEffectCurvedFold,
      AnimEffectHorizontalFolds,
      AnimEffectRollUp
};

#define NUM_SHADE_EFFECT (sizeof (shadeEffectType) / sizeof (shadeEffectType[0]))

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

#define N_MINIMIZE_DEFAULT_WIN_TYPE (sizeof (minimizeDefaultWinType) / sizeof (minimizeDefaultWinType[0]))

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

#define N_CLOSE1_DEFAULT_WIN_TYPE (sizeof (close1DefaultWinType) / sizeof (close1DefaultWinType[0]))

static char *close2DefaultWinType[] = {
      N_("Unknown"),
      N_("Menu"),
      N_("PopupMenu"),
      N_("DropdownMenu"),
      N_("Tooltip")
};

#define N_CLOSE2_DEFAULT_WIN_TYPE (sizeof (close2DefaultWinType) / sizeof (close2DefaultWinType[0]))

static char *focusDefaultWinType[] = {
      N_("Normal"),
      N_("Dialog"),
      N_("ModalDialog"),
      N_("Dnd")
};

#define N_FOCUS_DEFAULT_WIN_TYPE (sizeof (focusDefaultWinType) / sizeof (focusDefaultWinType[0]))

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

#define N_SHADE_DEFAULT_WIN_TYPE (sizeof (shadeDefaultWinType) / sizeof (shadeDefaultWinType[0]))

static int displayPrivateIndex;

typedef struct _AnimDisplay
{
      int screenPrivateIndex;
      Atom wmHintsAtom;
      Atom winIconGeometryAtom;
      HandleEventProc handleEvent;
} AnimDisplay;

typedef enum
{
      // Event settings
      ANIM_SCREEN_OPTION_MINIMIZE_EFFECT = 0,
      ANIM_SCREEN_OPTION_MINIMIZE_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_MINIMIZE_DURATION,
      ANIM_SCREEN_OPTION_MINIMIZE_RANDOM_EFFECTS,
      ANIM_SCREEN_OPTION_UNMINIMIZE_EFFECT,
      ANIM_SCREEN_OPTION_UNMINIMIZE_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_UNMINIMIZE_DURATION,
      ANIM_SCREEN_OPTION_UNMINIMIZE_RANDOM_EFFECTS,
      ANIM_SCREEN_OPTION_CLOSE1_EFFECT,
      ANIM_SCREEN_OPTION_CLOSE1_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_CLOSE1_DURATION,
      ANIM_SCREEN_OPTION_CLOSE1_RANDOM_EFFECTS,
      ANIM_SCREEN_OPTION_CREATE1_EFFECT,
      ANIM_SCREEN_OPTION_CREATE1_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_CREATE1_DURATION,
      ANIM_SCREEN_OPTION_CREATE1_RANDOM_EFFECTS,
      ANIM_SCREEN_OPTION_CLOSE2_EFFECT,
      ANIM_SCREEN_OPTION_CLOSE2_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_CLOSE2_DURATION,
      ANIM_SCREEN_OPTION_CLOSE2_RANDOM_EFFECTS,
      ANIM_SCREEN_OPTION_CREATE2_EFFECT,
      ANIM_SCREEN_OPTION_CREATE2_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_CREATE2_DURATION,
      ANIM_SCREEN_OPTION_CREATE2_RANDOM_EFFECTS,
      ANIM_SCREEN_OPTION_FOCUS_EFFECT,
      ANIM_SCREEN_OPTION_FOCUS_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_FOCUS_DURATION,
      ANIM_SCREEN_OPTION_SHADE_EFFECT,
      ANIM_SCREEN_OPTION_SHADE_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_SHADE_DURATION,
      ANIM_SCREEN_OPTION_SHADE_RANDOM_EFFECTS,
      ANIM_SCREEN_OPTION_UNSHADE_EFFECT,
      ANIM_SCREEN_OPTION_UNSHADE_WINDOW_TYPE,
      ANIM_SCREEN_OPTION_UNSHADE_DURATION,
      ANIM_SCREEN_OPTION_UNSHADE_RANDOM_EFFECTS,
      ANIM_SCREEN_OPTION_ROLLUP_FIXED_INTERIOR,
      // Misc. settings
      ANIM_SCREEN_OPTION_ALL_RANDOM,
      ANIM_SCREEN_OPTION_ZOOM_FROM_CENTER,
      ANIM_SCREEN_OPTION_DISABLE_PP_FX,
      ANIM_SCREEN_OPTION_TIME_STEP,
      ANIM_SCREEN_OPTION_TIME_STEP_INTENSE,
      // Effect settings
      ANIM_SCREEN_OPTION_BEAMUP_SIZE,
      ANIM_SCREEN_OPTION_BEAMUP_SPACING,
      ANIM_SCREEN_OPTION_BEAMUP_COLOR,
      ANIM_SCREEN_OPTION_BEAMUP_SLOWDOWN,
      ANIM_SCREEN_OPTION_BEAMUP_LIFE,
      ANIM_SCREEN_OPTION_CURVED_FOLD_AMP,
      ANIM_SCREEN_OPTION_DOMINO_DIRECTION,
      ANIM_SCREEN_OPTION_RAZR_DIRECTION,
      ANIM_SCREEN_OPTION_FIRE_PARTICLES,
      ANIM_SCREEN_OPTION_FIRE_SIZE,
      ANIM_SCREEN_OPTION_FIRE_SLOWDOWN,
      ANIM_SCREEN_OPTION_FIRE_LIFE,
      ANIM_SCREEN_OPTION_FIRE_COLOR,
      ANIM_SCREEN_OPTION_FIRE_DIRECTION,
      ANIM_SCREEN_OPTION_FIRE_CONSTANT_SPEED,
      ANIM_SCREEN_OPTION_FIRE_SMOKE,
      ANIM_SCREEN_OPTION_FIRE_MYSTICAL,
      ANIM_SCREEN_OPTION_FOCUS_WAVE_WIDTH,
      ANIM_SCREEN_OPTION_FOCUS_WAVE_AMP,
      ANIM_SCREEN_OPTION_GLIDE1_AWAY_POS,
      ANIM_SCREEN_OPTION_GLIDE1_AWAY_ANGLE,
      ANIM_SCREEN_OPTION_GLIDE1_THICKNESS,
      ANIM_SCREEN_OPTION_GLIDE2_AWAY_POS,
      ANIM_SCREEN_OPTION_GLIDE2_AWAY_ANGLE,
      ANIM_SCREEN_OPTION_GLIDE2_THICKNESS,
      ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_AMP,
      ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_NUM_FOLDS,
      ANIM_SCREEN_OPTION_MAGIC_LAMP1_GRID_RES,
      ANIM_SCREEN_OPTION_MAGIC_LAMP1_MAX_WAVES,
      ANIM_SCREEN_OPTION_MAGIC_LAMP1_WAVE_AMP_MIN,
      ANIM_SCREEN_OPTION_MAGIC_LAMP1_WAVE_AMP_MAX,
      ANIM_SCREEN_OPTION_MAGIC_LAMP1_CREATE_START_WIDTH,
      ANIM_SCREEN_OPTION_MAGIC_LAMP2_GRID_RES,
      ANIM_SCREEN_OPTION_MAGIC_LAMP2_MAX_WAVES,
      ANIM_SCREEN_OPTION_MAGIC_LAMP2_WAVE_AMP_MIN,
      ANIM_SCREEN_OPTION_MAGIC_LAMP2_WAVE_AMP_MAX,
      ANIM_SCREEN_OPTION_MAGIC_LAMP2_CREATE_START_WIDTH,
      ANIM_SCREEN_OPTION_MAGIC_LAMP3_GRID_RES,
      ANIM_SCREEN_OPTION_MAGIC_LAMP3_MAX_WAVES,
      ANIM_SCREEN_OPTION_MAGIC_LAMP3_WAVE_AMP_MIN,
      ANIM_SCREEN_OPTION_MAGIC_LAMP3_WAVE_AMP_MAX,
      ANIM_SCREEN_OPTION_MAGIC_LAMP3_CREATE_START_WIDTH,
      ANIM_SCREEN_OPTION_SIDEKICK_NUM_ROTATIONS,

      ANIM_SCREEN_OPTION_NUM
} AnimScreenOptions;

typedef struct _AnimScreen
{
      int windowPrivateIndex;

      PreparePaintScreenProc preparePaintScreen;
      DonePaintScreenProc donePaintScreen;
      PaintScreenProc paintScreen;
      PaintWindowProc paintWindow;
      DamageWindowRectProc damageWindowRect;
      AddWindowGeometryProc addWindowGeometry;
      DrawWindowGeometryProc drawWindowGeometry;
      DrawWindowTextureProc drawWindowTexture;

      WindowResizeNotifyProc windowResizeNotify;
      WindowMoveNotifyProc windowMoveNotify;
      WindowGrabNotifyProc windowGrabNotify;
      WindowUngrabNotifyProc windowUngrabNotify;

      CompOption opt[ANIM_SCREEN_OPTION_NUM];

      PostprocessDisabling ppDisabling;

      ZoomFromCenter zoomFC;

      Bool animInProgress;
      AnimEffect minimizeEffect;
      AnimEffect unminimizeEffect;
      AnimEffect create1Effect;
      AnimEffect create2Effect;
      AnimEffect close1Effect;
      AnimEffect close2Effect;
      AnimEffect focusEffect;
      AnimEffect shadeEffect;
      AnimEffect unshadeEffect;
      unsigned int minimizeWMask;
      unsigned int unminimizeWMask;
      unsigned int create1WMask;
      unsigned int create2WMask;
      unsigned int close1WMask;
      unsigned int close2WMask;
      unsigned int focusWMask;
      unsigned int shadeWMask;
      unsigned int unshadeWMask;

      AnimEffect close1RandomEffects[NUM_CLOSE_EFFECT];
      AnimEffect close2RandomEffects[NUM_CLOSE_EFFECT];
      AnimEffect create1RandomEffects[NUM_CLOSE_EFFECT];
      AnimEffect create2RandomEffects[NUM_CLOSE_EFFECT];
      AnimEffect minimizeRandomEffects[NUM_MINIMIZE_EFFECT];
      AnimEffect unminimizeRandomEffects[NUM_MINIMIZE_EFFECT];
      AnimEffect shadeRandomEffects[NUM_SHADE_EFFECT];
      AnimEffect unshadeRandomEffects[NUM_SHADE_EFFECT];
      unsigned int nClose1RandomEffects;
      unsigned int nClose2RandomEffects;
      unsigned int nCreate1RandomEffects;
      unsigned int nCreate2RandomEffects;
      unsigned int nMinimizeRandomEffects;
      unsigned int nUnminimizeRandomEffects;
      unsigned int nShadeRandomEffects;
      unsigned int nUnshadeRandomEffects;
} AnimScreen;

typedef struct _AnimWindow
{
      Model *model;
      int numPs;
      ParticleSystem *ps;
      unsigned int state;
      unsigned int newState;

      PolygonSet *polygonSet;
      float mvm[16];

      Region drawRegion;
      Bool useDrawRegion;

      XRectangle icon;
      XRectangle origWindowRect;

      XRectangle lastKnownCoords;   // used to determine if paintWindow is drawing
                                                // on the viewport that the animation started

      float numZoomRotations;
      GLushort storedOpacity;
      float timestep;                     // to be used in updateWindowAttribFunc

      int nDrawGeometryCalls;

      Bool animInitialized;         // whether the animation effect (not the window) is initialized
      float animTotalTime;
      float animRemainingTime;
      int animOverrideProgressDir;  // 0: default dir, 1: forward, 2: backward

      Bool nowShaded;
      Bool grabbed;

      WindowEvent curWindowEvent;
      AnimEffect curAnimEffect;

      int unmapCnt;
      int destroyCnt;

      int nClipsPassed;             // # of clips passed to animAddWindowGeometry so far
      // in this draw step
      Bool clipsUpdated;          // whether stored clips are updated in this anim step
      WindowPaintAttrib curPaintAttrib;
      CompTexture *curTexture;
      int curTextureFilter;
      int animatedAtom;

      int animFireDirection;
      int subEffectNo;              // For effects that share same functions
      Bool deceleratingMotion;      // For effects that have decel. motion
} AnimWindow;

typedef struct _AnimEffectProperties
{
      void (*updateWindowAttribFunc) (AnimScreen *, AnimWindow *,
                                                      WindowPaintAttrib *);
      void (*prePaintWindowFunc) (CompScreen *, CompWindow *);
      void (*postPaintWindowFunc) (CompScreen *, CompWindow *);
      void (*animStepFunc) (CompScreen *, CompWindow *, float time);
      void (*initFunc) (CompScreen *, CompWindow *);
      void (*initGridFunc) (AnimScreen *, WindowEvent, int *, int *);
      void (*addCustomGeometryFunc) (CompScreen *, CompWindow *, int, Box *,
                                                   int, CompMatrix *);
      void (*drawCustomGeometryFunc) (CompScreen *, CompWindow *);
      Bool dontUseQTexCoord;        // TRUE if effect doesn't need Q coord.
      void (*animStepPolygonFunc) (CompWindow *, PolygonObject *, float);
      int subEffectNo;              // For effects that share same functions
} AnimEffectProperties;

AnimEffectProperties *animEffectPropertiesTmp;

#define GET_ANIM_DISPLAY(d)                                       \
    ((AnimDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define ANIM_DISPLAY(d)                       \
    AnimDisplay *ad = GET_ANIM_DISPLAY (d)

#define GET_ANIM_SCREEN(s, ad)                                   \
    ((AnimScreen *) (s)->privates[(ad)->screenPrivateIndex].ptr)

#define ANIM_SCREEN(s)                                                      \
    AnimScreen *as = GET_ANIM_SCREEN (s, GET_ANIM_DISPLAY (s->display))

#define GET_ANIM_WINDOW(w, as)                                   \
    ((AnimWindow *) (w)->privates[(as)->windowPrivateIndex].ptr)

#define ANIM_WINDOW(w)                                         \
    AnimWindow *aw = GET_ANIM_WINDOW  (w,                         \
            GET_ANIM_SCREEN  (w->screen,                 \
                GET_ANIM_DISPLAY (w->screen->display)))

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

#define LIST_SIZE(l) (sizeof (l) / sizeof (l[0]))


// iterate over given list
// check if given effect name matches any implemented effect
// Check if it was already in the stored list
// if not, store the effect
// if no valid effect is given, use the default effect

#define STORE_RANDOM_EFFECT_LIST(option, nFxChoices, defaultValue, nStoredRandomFx, storedRandomFx, fxNames, fxIds) \
{ \
      CompOptionValue *effect = as->opt[(option)].value.list.value; \
      int nListItems = as->opt[(option)].value.list.nValue; \
      \
      as->nStoredRandomFx = 0; \
      int j; \
      for (j = 0; j < nListItems; j++, effect++) \
      { \
            int i; \
            for (i = 2; i < (nFxChoices); i++) \
            { \
                  if (strcmp(effect->s, fxNames[i]) == 0) \
                  { \
                        Bool duplicate = FALSE; \
                        int k; \
                        for (k = 0; k < as->nStoredRandomFx; k++) \
                              if (as->storedRandomFx[k] == \
                                    fxIds[i]) \
                              { \
                                    duplicate = TRUE; \
                                    break; \
                              } \
                        if (!duplicate) \
                        { \
                              as->storedRandomFx[as->nStoredRandomFx] \
                                    = fxIds[i]; \
                              as->nStoredRandomFx++; \
                        } \
                        break; \
                  } \
            } \
      } \
      if (nListItems == 0 || as->nStoredRandomFx == 0) \
      { \
            as->storedRandomFx[0] = (defaultValue); \
            as->nStoredRandomFx = 1; \
      } \
}

#define INIT_RANDOM_EFFECT_LIST(poption, pname, pgroup, psubgroup, nFxChoices, fxNames) \
{ \
      o = &as->opt[(poption)]; \
      o->advanced = False; \
      o->name = pname "_random_effects"; \
      o->group = N_(pgroup); \
      o->subGroup = N_(psubgroup); \
      o->displayHints = ""; \
      o->shortDesc = N_("Random Effect Pool"); \
      o->longDesc = \
                  N_("Pool of effects to be chosen from if Random effect is " \
                     "selected. Click reset to restore full list. If the list " \
                     "is empty, the default effect will be used."); \
      o->type = CompOptionTypeList; \
      o->value.list.type = CompOptionTypeString; \
      o->value.list.nValue = (nFxChoices) - 2; \
      o->value.list.value = \
            malloc(sizeof(CompOptionValue) * ((nFxChoices) - 2)); \
      for (i = 2; i < (nFxChoices); i++) \
            o->value.list.value[i - 2].s = strdup(fxNames[i]); \
      o->rest.s.string = fxNames+2; \
      o->rest.s.nString = (nFxChoices) - 2; \
}

#define sigmoid(fx) (1.0f/(1.0f+exp(-5.0f*2*((fx)-0.5))))
#define sigmoid2(fx, s) (1.0f/(1.0f+exp(-(s)*2*((fx)-0.5))))

static void modelCalcBounds(Model * model)
{
      int i;

      model->topLeft.x = MAXSHORT;
      model->topLeft.y = MAXSHORT;
      model->bottomRight.x = MINSHORT;
      model->bottomRight.y = MINSHORT;

      for (i = 0; i < model->numObjects; i++)
      {
            if (model->objects[i].position.x < model->topLeft.x)
                  model->topLeft.x = model->objects[i].position.x;
            else if (model->objects[i].position.x > model->bottomRight.x)
                  model->bottomRight.x = model->objects[i].position.x;

            if (model->objects[i].position.y < model->topLeft.y)
                  model->topLeft.y = model->objects[i].position.y;
            else if (model->objects[i].position.y > model->bottomRight.y)
                  model->bottomRight.y = model->objects[i].position.y;
      }
}

static Bool ensureLargerClipCapacity(PolygonSet * pset)
{
      if (pset->clipCapacity == pset->nClips)   // if list full
      {
            Clip4Polygons *newList = realloc
                        (pset->clips, sizeof(Clip4Polygons) *
                         (pset->clipCapacity + ANIM_CLIP_LIST_INCREMENT));
            if (!newList)
                  return FALSE;
            // reset newly allocated part of this memory to 0
            memset(newList + pset->clipCapacity,
                     0, sizeof(Clip4Polygons) * ANIM_CLIP_LIST_INCREMENT);

            int *newList2 = realloc
                        (pset->lastClipInGroup, sizeof(int) *
                         (pset->clipCapacity + ANIM_CLIP_LIST_INCREMENT));
            if (!newList2)
            {
                  free(newList);
                  pset->clips = 0;
                  pset->lastClipInGroup = 0;
                  return FALSE;
            }
            // reset newly allocated part of this memory to 0
            memset(newList2 + pset->clipCapacity,
                     0, sizeof(int) * ANIM_CLIP_LIST_INCREMENT);

            pset->clips = newList;
            pset->clipCapacity += ANIM_CLIP_LIST_INCREMENT;
            pset->lastClipInGroup = newList2;
      }
      return TRUE;
}

static float defaultAnimProgress(AnimWindow * aw)
{
      float forwardProgress =
                  1 - (aw->animRemainingTime - aw->timestep) /
                  (aw->animTotalTime - aw->timestep);
      if (aw->curWindowEvent == WindowEventCreate ||
            aw->curWindowEvent == WindowEventUnminimize ||
            aw->curWindowEvent == WindowEventUnshade)
            forwardProgress = 1 - forwardProgress;

      return forwardProgress;
}

// Converts animation direction string to an integer direction
// (up, down, left, or right)
static AnimDirection getAnimationDirection(CompWindow * w,
                                                               char *optionValue, Bool openDir)
{
      ANIM_WINDOW(w);

      AnimDirection dir = AnimDirectionUp;
      int i;

      for (i = 0; i < LIST_SIZE(animDirectionName); i++)
      {
            if (strcmp(optionValue, animDirectionName[i]) == 0)
            {
                  dir = i;
            }
      }
      if (dir == AnimDirectionRandom)
      {
            dir = rand() % 4;
      }
      else if (dir == AnimDirectionAuto)
      {
            // away from icon
            int centerX = BORDER_X(w) + BORDER_W(w) / 2;
            int centerY = BORDER_Y(w) + BORDER_H(w) / 2;
            float relDiffX = ((float)centerX - aw->icon.x) / BORDER_W(w);
            float relDiffY = ((float)centerY - aw->icon.y) / BORDER_H(w);

            if (openDir)
            {
                  if (aw->curWindowEvent == WindowEventMinimize ||
                        aw->curWindowEvent == WindowEventUnminimize)
                        // min/unmin. should always result in +/- y direction
                        dir = aw->icon.y < w->screen->height - aw->icon.y ?
                                    AnimDirectionDown : AnimDirectionUp;
                  else if (fabs(relDiffY) > fabs(relDiffX))
                        dir = relDiffY > 0 ? AnimDirectionDown : AnimDirectionUp;
                  else
                        dir = relDiffX > 0 ? AnimDirectionRight : AnimDirectionLeft;
            }
            else
            {
                  if (aw->curWindowEvent == WindowEventMinimize ||
                        aw->curWindowEvent == WindowEventUnminimize)
                        // min/unmin. should always result in +/- y direction
                        dir = aw->icon.y < w->screen->height - aw->icon.y ?
                                    AnimDirectionUp : AnimDirectionDown;
                  else if (fabs(relDiffY) > fabs(relDiffX))
                        dir = relDiffY > 0 ? AnimDirectionUp : AnimDirectionDown;
                  else
                        dir = relDiffX > 0 ? AnimDirectionLeft : AnimDirectionRight;
            }
      }
      return dir;
}

// Gives some acceleration (when closing a window)
// or deceleration (when opening a window)
// Applies a sigmoid with slope s,
// where minx and maxx are the
// starting and ending points on the sigmoid
static float decelerateProgressCustom(float progress, float minx, float maxx)
{
      float x = 1 - progress;
      float s = 8;

      return (1 -
                  ((sigmoid2(minx + (x * (maxx - minx)), s) - sigmoid2(minx, s)) /
                   (sigmoid2(maxx, s) - sigmoid2(minx, s))));
}

// default x limits for deceleration sigmoid
#define DECEL_SIG_DEF_START 0.9
#define DECEL_SIG_DEF_END 1.3

/*
static float decelerateProgress(float progress)
{
      return decelerateProgressCustom
            (progress, DECEL_SIG_DEF_START, DECEL_SIG_DEF_END);
}
*/

static float decelerateProgress2(float progress)
{
      return decelerateProgressCustom(progress, 0.5, 0.75);
}


static void
applyTransformToObject(Object *obj, GLfloat *mat)
{
      Point3d o = obj->posRel3d;

      float x =
            o.x * mat[0] + o.y * mat[4] + o.z * mat[8] + 1 * mat[12];
      float y =
            o.x * mat[1] + o.y * mat[5] + o.z * mat[9] + 1 * mat[13];
      // ignore z
      float w =
            o.x * mat[3] + o.y * mat[7] + o.z * mat[11] + 1 * mat[15];

      obj->position.x = x / w;
      obj->position.y = y / w;
}

static void
obtainTransformMatrix (CompScreen *s, GLfloat *mat,
                                 float rotAngle, Vector3d rotAxis,
                                 Point3d translation)
{
      glPushMatrix();
      glLoadIdentity();

      // column-major order
      GLfloat perspMat[16] =
                  {1, 0, 0, 0,
                   0, 1, 0, 0,
                   0, 0, 1, -1.0/s->width,
                   0, 0, 1, 1};

      glTranslatef(0, 0, DEFAULT_Z_CAMERA * s->width);

      glMultMatrixf(perspMat);

      glTranslatef(translation.x, translation.y, translation.z);

      glRotatef(rotAngle, rotAxis.x, rotAxis.y, rotAxis.z);

      glGetFloatv(GL_MODELVIEW_MATRIX, mat);
      glPopMatrix();
}


static void polygonsAnimStep(CompScreen * s, CompWindow * w, float time)
{
      int i, j, steps;

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      aw->timestep = timestep;

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;
      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            float forwardProgress = defaultAnimProgress(aw);

            if (aw->polygonSet)
            {
                  if (animEffectPropertiesTmp[aw->curAnimEffect].
                        animStepPolygonFunc)
                  {
                        for (i = 0; i < aw->polygonSet->nPolygons; i++)
                              animEffectPropertiesTmp[aw->curAnimEffect].
                                          animStepPolygonFunc
                                          (w, &aw->polygonSet->polygons[i],
                                           forwardProgress);
                  }
            }
            else
                  fprintf(stderr, "%s: pset null at line %d\n", __FILE__, __LINE__);
            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}


// =====================  Effect: Magic Lamp  =========================

static void
fxMagicLamp1InitGrid(AnimScreen * as,
                               WindowEvent forWindowEvent,
                               int *gridWidth, int *gridHeight)
{
      *gridWidth = 2;
      *gridHeight = as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_GRID_RES].value.i;
}

static void
fxMagicLamp2InitGrid(AnimScreen * as,
                               WindowEvent forWindowEvent,
                               int *gridWidth, int *gridHeight)
{
      *gridWidth = 2;
      *gridHeight = as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_GRID_RES].value.i;
}
static void
fxMagicLamp3InitGrid(AnimScreen * as, WindowEvent forWindowEvent,
                               int *gridWidth, int *gridHeight)
{
      *gridWidth = 2;
      *gridHeight = as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_GRID_RES].value.i;
}

static void fxMagicLampInit(CompScreen * s, CompWindow * w)
{
      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      int screenHeight = s->height;
      Bool minimizeToTop = (WIN_Y(w) + WIN_H(w) / 2) >
                  (aw->icon.y + aw->icon.height / 2);
      int maxWaves;
      float waveAmpMin, waveAmpMax;

      if (aw->curAnimEffect == AnimEffectMagicLamp1)
      {
            maxWaves = as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_MAX_WAVES].value.i;
            waveAmpMin =
                        as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_WAVE_AMP_MIN].value.f;
            waveAmpMax =
                        as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_WAVE_AMP_MAX].value.f;
      }
      else if (aw->curAnimEffect == AnimEffectMagicLamp2)
      {
            maxWaves = as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_MAX_WAVES].value.i;
            waveAmpMin =
                        as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_WAVE_AMP_MIN].value.f;
            waveAmpMax =
                        as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_WAVE_AMP_MAX].value.f;
      }
      else
      {
            maxWaves = as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_MAX_WAVES].value.i;
            waveAmpMin =
                        as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_WAVE_AMP_MIN].value.f;
            waveAmpMax =
                        as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_WAVE_AMP_MAX].value.f;
      }
      if (waveAmpMax < waveAmpMin)
            waveAmpMax = waveAmpMin;

      if (maxWaves > 0)
      {
            float distance;

            if (minimizeToTop)
                  distance = WIN_Y(w) + WIN_H(w) - aw->icon.y;
            else
                  distance = aw->icon.y - WIN_Y(w);

            // Initialize waves
            model->magicLampWaveCount =
                        1 + (float)maxWaves *distance / screenHeight;

            if (!(model->magicLampWaves))
                  model->magicLampWaves =
                              calloc(1, model->magicLampWaveCount * sizeof(WaveParam));

            int ampDirection = (RAND_FLOAT() < 0.5 ? 1 : -1);
            int i;
            float minHalfWidth = 0.22f;
            float maxHalfWidth = 0.38f;

            for (i = 0; i < model->magicLampWaveCount; i++)
            {
                  model->magicLampWaves[i].amp =
                              ampDirection * (waveAmpMax - waveAmpMin) *
                              rand() / RAND_MAX + ampDirection * waveAmpMin;
                  model->magicLampWaves[i].halfWidth =
                              RAND_FLOAT() * (maxHalfWidth -
                                                      minHalfWidth) + minHalfWidth;

                  // avoid offset at top and bottom part by added waves
                  float availPos = 1 - 2 * model->magicLampWaves[i].halfWidth;
                  float posInAvailSegment = 0;

                  if (i > 0)
                        posInAvailSegment =
                                    (availPos /
                                     model->magicLampWaveCount) * rand() / RAND_MAX;

                  model->magicLampWaves[i].pos =
                              (posInAvailSegment +
                               i * availPos / model->magicLampWaveCount +
                               model->magicLampWaves[i].halfWidth);

                  // switch wave direction
                  ampDirection *= -1;
            }
      }
      else
            model->magicLampWaveCount = 0;
}

static void
fxMagicLampModelStepObject(CompWindow * w,
                                       Model * model,
                                       Object * object,
                                       float forwardProgress, Bool minimizeToTop)
{
      ANIM_WINDOW(w);

      float iconCloseEndY;
      float iconFarEndY;
      float winFarEndY;
      float winVisibleCloseEndY;

      if (minimizeToTop)
      {
            iconFarEndY = aw->icon.y;
            iconCloseEndY = aw->icon.y + aw->icon.height;
            winFarEndY = WIN_Y(w) + WIN_H(w);
            winVisibleCloseEndY = WIN_Y(w);
            if (winVisibleCloseEndY < iconCloseEndY)
                  winVisibleCloseEndY = iconCloseEndY;
      }
      else
      {
            iconFarEndY = aw->icon.y + aw->icon.height;
            iconCloseEndY = aw->icon.y;
            winFarEndY = WIN_Y(w);
            winVisibleCloseEndY = WIN_Y(w) + WIN_H(w);
            if (winVisibleCloseEndY > iconCloseEndY)
                  winVisibleCloseEndY = iconCloseEndY;
      }

      float preShapePhaseEnd = 0.17f;
      float stretchPhaseEnd =
                  preShapePhaseEnd + (1 - preShapePhaseEnd) *
                  (iconCloseEndY -
                   winVisibleCloseEndY) / ((iconCloseEndY - winFarEndY) +
                                                       (iconCloseEndY - winVisibleCloseEndY));
      if (stretchPhaseEnd < preShapePhaseEnd + 0.1)
            stretchPhaseEnd = preShapePhaseEnd + 0.1;

      float origx = w->attrib.x + (WIN_W(w) * object->gridPosition.x -
                                                 w->output.left) * model->scale.x;
      float origy = w->attrib.y + (WIN_H(w) * object->gridPosition.y -
                                                 w->output.top) * model->scale.y;

      float iconShadowLeft =
                  ((float)(w->output.left - w->input.left)) * aw->icon.width /
                  w->width;
      float iconShadowRight =
                  ((float)(w->output.right - w->input.right)) * aw->icon.width /
                  w->width;
      float iconx =
                  (aw->icon.x - iconShadowLeft) + (aw->icon.width + iconShadowLeft +
                                                                   iconShadowRight) *
                  object->gridPosition.x;
      float icony = aw->icon.y + aw->icon.height * object->gridPosition.y;

      if (forwardProgress < preShapePhaseEnd)
      {
            float preShapeProgress = forwardProgress / preShapePhaseEnd;
            float fx =
                        (iconCloseEndY - object->position.y) / (iconCloseEndY -
                                                                                    winFarEndY);
            float fy = (sigmoid(fx) - sigmoid(0)) / (sigmoid(1) - sigmoid(0));
            int i;
            float targetx = fy * (origx - iconx) + iconx;

            for (i = 0; i < model->magicLampWaveCount; i++)
            {
                  float cosfx =
                              (fx -
                               model->magicLampWaves[i].pos) /
                              model->magicLampWaves[i].halfWidth;
                  if (cosfx < -1 || cosfx > 1)
                        continue;
                  targetx +=
                              model->magicLampWaves[i].amp * model->scale.x *
                              (cos(cosfx * M_PI) + 1) / 2;
            }
            object->position.x =
                        (1 - preShapeProgress) * origx + preShapeProgress * targetx;
            object->position.y = origy;
      }
      else
      {
            float stretchedPos;

            if (minimizeToTop)
                  stretchedPos =
                              object->gridPosition.y * origy +
                              (1 - object->gridPosition.y) * icony;
            else
                  stretchedPos =
                              (1 - object->gridPosition.y) * origy +
                              object->gridPosition.y * icony;

            if (forwardProgress < stretchPhaseEnd)
            {
                  float stretchProgress =
                              (forwardProgress - preShapePhaseEnd) /
                              (stretchPhaseEnd - preShapePhaseEnd);

                  object->position.y =
                              (1 - stretchProgress) * origy +
                              stretchProgress * stretchedPos;
            }
            else
            {
                  float postStretchProgress =
                              (forwardProgress - stretchPhaseEnd) / (1 -
                                                                                       stretchPhaseEnd);

                  object->position.y =
                              (1 - postStretchProgress) *
                              stretchedPos +
                              postStretchProgress *
                              (stretchedPos + (iconCloseEndY - winFarEndY));
            }
            float fx =
                        (iconCloseEndY - object->position.y) / (iconCloseEndY -
                                                                                    winFarEndY);
            float fy = (sigmoid(fx) - sigmoid(0)) / (sigmoid(1) - sigmoid(0));

            int i;

            object->position.x = fy * (origx - iconx) + iconx;
            for (i = 0; i < model->magicLampWaveCount; i++)
            {
                  float cosfx =
                              (fx -
                               model->magicLampWaves[i].pos) /
                              model->magicLampWaves[i].halfWidth;
                  if (cosfx < -1 || cosfx > 1)
                        continue;
                  object->position.x +=
                              model->magicLampWaves[i].amp * model->scale.x *
                              (cos(cosfx * M_PI) + 1) / 2;
            }
      }
      if (minimizeToTop)
      {
            if (object->position.y < iconFarEndY)
                  object->position.y = iconFarEndY;
      }
      else
      {
            if (object->position.y > iconFarEndY)
                  object->position.y = iconFarEndY;
      }
}

static void fxMagicLampModelStep(CompScreen * s, CompWindow * w, float time)
{
      int i, j, steps;

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;

      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      Bool minimizeToTop = (WIN_Y(w) + WIN_H(w) / 2) >
                  (aw->icon.y + aw->icon.height / 2);

      for (j = 0; j < steps; j++)
      {
            float forwardProgress =
                        1 - (aw->animRemainingTime - timestep) /
                        (aw->animTotalTime - timestep);
            if (aw->curWindowEvent == WindowEventUnminimize ||
                  aw->curWindowEvent == WindowEventCreate)
                  forwardProgress = 1 - forwardProgress;

            for (i = 0; i < model->numObjects; i++)
            {
                  fxMagicLampModelStepObject(w, model,
                                                         &model->objects[i],
                                                         forwardProgress, minimizeToTop);
            }
            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}


// =====================  Effect: Dream  =========================

static void fxDreamInit(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(s);

      // store window opacity
      aw->storedOpacity = w->paint.opacity;

      aw->timestep = (s->slowAnimations ? 2 :   // For smooth slow-mo (refer to display.c)
                              as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);
}

static void
fxDreamModelStepObject(CompWindow * w,
                                 Model * model, Object * object, float forwardProgress)
{
      float waveAmpMax = MIN(WIN_H(w), WIN_W(w)) * 0.125f;
      float waveWidth = 10.0f;
      float waveSpeed = 7.0f;

      float origx = w->attrib.x + (WIN_W(w) * object->gridPosition.x -
                                                 w->output.left) * model->scale.x;
      float origy = w->attrib.y + (WIN_H(w) * object->gridPosition.y -
                                                 w->output.top) * model->scale.y;

      object->position.y = origy;
      object->position.x =
                  origx +
                  forwardProgress * waveAmpMax * model->scale.x *
                  sin(object->gridPosition.y * M_PI * waveWidth +
                        waveSpeed * forwardProgress);

}

static void fxDreamModelStep(CompScreen * s, CompWindow * w, float time)
{
      int i, j, steps;

      ANIM_WINDOW(w);
      ANIM_SCREEN(s);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      aw->timestep = timestep;

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;

      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            float forwardProgress =
                        1 - (aw->animRemainingTime - timestep) /
                        (aw->animTotalTime - timestep);
            if (aw->curWindowEvent == WindowEventCreate ||
                  aw->curWindowEvent == WindowEventUnminimize)
                  forwardProgress = 1 - forwardProgress;

            for (i = 0; i < model->numObjects; i++)
            {
                  fxDreamModelStepObject(w,
                                                   model,
                                                   &model->objects[i], forwardProgress);
            }
            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}

static void
fxDreamUpdateWindowAttrib(AnimScreen * as,
                                      AnimWindow * aw, WindowPaintAttrib * wAttrib)
{
      float forwardProgress =
                  1 - (aw->animRemainingTime - aw->timestep) /
                  (aw->animTotalTime - aw->timestep);
      forwardProgress = MIN(forwardProgress, 1);
      forwardProgress = MAX(forwardProgress, 0);

      if (aw->curWindowEvent == WindowEventCreate ||
            aw->curWindowEvent == WindowEventUnminimize)
            forwardProgress = 1 - forwardProgress;

      wAttrib->opacity = (GLushort) (aw->storedOpacity * (1 - forwardProgress));
}


// =====================  Effect: Wave  =========================

static void
fxWaveModelStepObject(CompWindow * w,
                                Model * model,
                                Object * object,
                                float forwardProgress,
                                float waveAmp, float waveHalfWidth)
{
      float origx = w->attrib.x + (WIN_W(w) * object->gridPosition.x -
                                                 w->output.left) * model->scale.x;
      float origy = w->attrib.y + (WIN_H(w) * object->gridPosition.y -
                                                 w->output.top) * model->scale.y;

      float wavePosition =
                  WIN_Y(w) - waveHalfWidth +
                  forwardProgress * (WIN_H(w) * model->scale.y + 2 * waveHalfWidth);

      object->position.y = origy;
      object->position.x = origx;

      if (fabs(object->position.y - wavePosition) < waveHalfWidth)
            object->position.x +=
                        (object->gridPosition.x - 0.5) * waveAmp *
                        (cos
                         ((object->position.y -
                           wavePosition) * M_PI / waveHalfWidth) + 1) / 2;
}

static void fxWaveModelStep(CompScreen * s, CompWindow * w, float time)
{
      int i, j, steps;

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;

      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            float forwardProgress =
                        1 - (aw->animRemainingTime - timestep) /
                        (aw->animTotalTime - timestep);

            for (i = 0; i < model->numObjects; i++)
            {
                  fxWaveModelStepObject(w,
                                                  model,
                                                  &model->objects[i],
                                                  forwardProgress,
                                                  WIN_H(w) * model->scale.y *
                                                  as->
                                                  opt
                                                  [ANIM_SCREEN_OPTION_FOCUS_WAVE_AMP].
                                                  value.f,
                                                  WIN_H(w) * model->scale.y *
                                                  as->
                                                  opt
                                                  [ANIM_SCREEN_OPTION_FOCUS_WAVE_WIDTH].
                                                  value.f / 2);
            }
            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}


// =====================  Effect: Zoom and Sidekick  =========================

static void fxSidekickInit(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(s);

      // determine number of rotations randomly in [0.75, 1.25] range
      aw->numZoomRotations =
                  as->opt[ANIM_SCREEN_OPTION_SIDEKICK_NUM_ROTATIONS].value.f *
                  (1.0 + 0.2 * rand() / RAND_MAX - 0.1);

      // store window opacity
      aw->storedOpacity = w->paint.opacity;
      aw->timestep = (s->slowAnimations ? 2 :   // For smooth slow-mo (refer to display.c)
                              as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);
}

static void fxZoomInit(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(s);

      // store window opacity
      aw->storedOpacity = w->paint.opacity;
      aw->timestep = (s->slowAnimations ? 2 :   // For smooth slow-mo (refer to display.c)
                              as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);
}

// Provides the animation progress for Zoom
static float fxZoomAnimProgressDirCustom(AnimWindow * aw,
                                                             float maxSigX)
{
      // maxSig is the max x for the deceleration sigmoid

      float forwardProgress =
                  1 - (aw->animRemainingTime - aw->timestep) /
                  (aw->animTotalTime - aw->timestep);

      int animProgressDir = 1;

      if (aw->curWindowEvent == WindowEventUnminimize ||
            aw->curWindowEvent == WindowEventCreate)
            animProgressDir = 2;

      if (aw->animOverrideProgressDir != 0)
            animProgressDir = aw->animOverrideProgressDir;

      float x = forwardProgress;

      if ((animProgressDir == 1 &&
             (aw->curWindowEvent == WindowEventUnminimize ||
              aw->curWindowEvent == WindowEventCreate)) ||
            (animProgressDir == 2 &&
             (aw->curWindowEvent == WindowEventMinimize ||
              aw->curWindowEvent == WindowEventClose)))
            x = 1 - forwardProgress;

      forwardProgress = decelerateProgressCustom
            (1 - x, DECEL_SIG_DEF_START, maxSigX);

      if (animProgressDir == 1)
            forwardProgress = 1 - forwardProgress;

      return forwardProgress;
}

static float fxZoomAnimProgressDir(AnimWindow * aw)
{
      return fxZoomAnimProgressDirCustom(aw, DECEL_SIG_DEF_END);
}

static void
fxSidekickModelStepObject(CompWindow * w,
                                      Model * model,
                                      Object * object,
                                      Point currentCenter, Point currentSize,
                                      float sinRot, float cosRot)
{
      float x =
            currentCenter.x - currentSize.x / 2 +
            currentSize.x * object->gridPosition.x;
      float y =
            currentCenter.y - currentSize.y / 2 +
            currentSize.y * object->gridPosition.y;

      x -= currentCenter.x;
      y -= currentCenter.y;

      object->position.x = cosRot * x - sinRot * y;
      object->position.y = sinRot * x + cosRot * y;

      object->position.x += currentCenter.x;
      object->position.y += currentCenter.y;
}

static void
fxZoomModelStepObject(CompScreen *s, CompWindow * w,
                                Model * model, Object * object,
                                Point currentCenter, Point currentSize)
{
      object->position.x =
            currentCenter.x - currentSize.x / 2 +
            currentSize.x * object->gridPosition.x;
      object->position.y =
            currentCenter.y - currentSize.y / 2 +
            currentSize.y * object->gridPosition.y;
}

static void fxZoomModelStep(CompScreen * s, CompWindow * w, float time)
{
      int i, j, steps;

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      aw->timestep = timestep;

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;

      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            float sinRot = 0;
            float cosRot = 0;

            Point winCenter =
                  {(WIN_X(w) + WIN_W(w) * model->scale.x / 2),
                   (WIN_Y(w) + WIN_H(w) * model->scale.y / 2)};
            Point iconCenter =
                  {aw->icon.x + aw->icon.width / 2,
                   aw->icon.y + aw->icon.height / 2};

            Point winSize =
                  {WIN_W(w) * model->scale.x,
                   WIN_H(w) * model->scale.y};

            //float xDiff = fabs(winCenter.x - iconCenter.x);
            //float yDiff = fabs(winCenter.y - iconCenter.y);
            float xMaxSig;
            float yMaxSig;

            xMaxSig = DECEL_SIG_DEF_END;
            yMaxSig = DECEL_SIG_DEF_END;

            float xProgress = fxZoomAnimProgressDirCustom(aw, xMaxSig);
            float yProgress = fxZoomAnimProgressDirCustom(aw, yMaxSig);
            float progress = (xProgress + yProgress) / 2;

            Point currentCenter =
                  {(1 - xProgress) * winCenter.x + xProgress * iconCenter.x,
                   (1 - yProgress) * winCenter.y + yProgress * iconCenter.y};
            Point currentSize =
                  {(1 - progress) * winSize.x + progress * aw->icon.width,
                   (1 - progress) * winSize.y + progress * aw->icon.height};

            if (aw->curAnimEffect == AnimEffectSidekick)
            {
                  sinRot = sin(2 * M_PI * progress * aw->numZoomRotations);
                  cosRot = cos(2 * M_PI * progress * aw->numZoomRotations);

                  for (i = 0; i < model->numObjects; i++)
                        fxSidekickModelStepObject(w, model,
                                                              &model->
                                                              objects[i],
                                                              currentCenter, currentSize,
                                                              sinRot, cosRot);
            }
            else
                  for (i = 0; i < model->numObjects; i++)
                        fxZoomModelStepObject(s, w, model,
                                                        &model->objects[i],
                                                        currentCenter, currentSize);

            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}

static void
fxZoomUpdateWindowAttrib(AnimScreen * as,
                                     AnimWindow * aw, WindowPaintAttrib * wAttrib)
{
      if (aw->model->scale.x < 1.0 &&     // if Scale plugin in progress
            aw->curWindowEvent == WindowEventUnminimize)    // and if unminimizing
            return;                             // then allow Fade to take over opacity
      float forwardProgress = fxZoomAnimProgressDir(aw);

      forwardProgress = MIN(forwardProgress, 1);
      forwardProgress = MAX(forwardProgress, 0);

      wAttrib->opacity =
                  (GLushort) (aw->storedOpacity * pow(1 - forwardProgress, 0.75));
}


// =====================  Effect: Glide  =========================

static void fxGlideGetParams
      (AnimScreen *as, AnimWindow *aw,
       float *finalDistFac, float *finalRotAng, float *thickness)
{
      // Sub effects:
      // 1: Glide 1
      // 2: Glide 2
      if (aw->subEffectNo == 1)
      {
            *finalDistFac = as->opt[ANIM_SCREEN_OPTION_GLIDE1_AWAY_POS].value.f;
            *finalRotAng = as->opt[ANIM_SCREEN_OPTION_GLIDE1_AWAY_ANGLE].value.f;
            *thickness = as->opt[ANIM_SCREEN_OPTION_GLIDE1_THICKNESS].value.f;
      }
      else
      {
            *finalDistFac = as->opt[ANIM_SCREEN_OPTION_GLIDE2_AWAY_POS].value.f;
            *finalRotAng = as->opt[ANIM_SCREEN_OPTION_GLIDE2_AWAY_ANGLE].value.f;
            *thickness = as->opt[ANIM_SCREEN_OPTION_GLIDE2_THICKNESS].value.f;
      }

}

static float fxGlideAnimProgress(AnimWindow * aw)
{
      float forwardProgress =
                  1 - (aw->animRemainingTime - aw->timestep) /
                  (aw->animTotalTime - aw->timestep);
      if (aw->curWindowEvent == WindowEventCreate ||
            aw->curWindowEvent == WindowEventUnminimize ||
            aw->curWindowEvent == WindowEventUnshade)
            forwardProgress = 1 - forwardProgress;

      return decelerateProgress2(forwardProgress);
}

static void
fxGlideModelStepObject(CompWindow * w,
                                 Model * model,
                                 Object * obj,
                                 GLfloat *mat,
                                 Point3d rotAxisOffset)
{
      float origx = w->attrib.x + (WIN_W(w) * obj->gridPosition.x -
                                                 w->output.left) * model->scale.x;
      float origy = w->attrib.y + (WIN_H(w) * obj->gridPosition.y -
                                                 w->output.top) * model->scale.y;

      obj->posRel3d.x = origx - rotAxisOffset.x;
      obj->posRel3d.y = origy - rotAxisOffset.y;

      applyTransformToObject(obj, mat);
      obj->position.x += rotAxisOffset.x;
      obj->position.y += rotAxisOffset.y;
}

static void fxGlideAnimStep(CompScreen * s, CompWindow * w, float time)
{
      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      float finalDistFac;
      float finalRotAng;
      float thickness;

      fxGlideGetParams(as, aw, &finalDistFac, &finalRotAng, &thickness);

      if (thickness > 1e-5) // the effect is 3D
      {
            polygonsAnimStep(s, w, time); // do 3D anim step instead
            return;
      }

      // for 2D glide effect
      // ------------------------

      int i, j, steps;
      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      aw->timestep = timestep;

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;

      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            float forwardProgress = fxGlideAnimProgress(aw);
            float finalz = finalDistFac * 0.8 * DEFAULT_Z_CAMERA * s->width;

            Vector3d rotAxis = {1, 0, 0};
            Point3d rotAxisOffset =
                  {WIN_X(w) + WIN_W(w) * model->scale.x / 2,
                   WIN_Y(w) + WIN_H(w) * model->scale.y / 2,
                   0};
            Point3d modelPos = {0, 0, finalz * forwardProgress};

            GLfloat mat[16];
            obtainTransformMatrix(s, mat, finalRotAng * forwardProgress,
                                            rotAxis, modelPos);
            for (i = 0; i < model->numObjects; i++)
                  fxGlideModelStepObject(w, model,
                                                   &model->objects[i],
                                                   mat,
                                                   rotAxisOffset);

            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}

static void
fxGlideUpdateWindowAttrib(AnimScreen * as,
                                     AnimWindow * aw, WindowPaintAttrib * wAttrib)
{
      float finalDistFac;
      float finalRotAng;
      float thickness;

      fxGlideGetParams(as, aw, &finalDistFac, &finalRotAng, &thickness);

      if (thickness > 1e-5) // the effect is 3D
            return;

      // the effect is 2D

      if (aw->model->scale.x < 1.0 &&     // if Scale plugin in progress
            aw->curWindowEvent == WindowEventUnminimize)    // and if unminimizing
            return;                             // then allow Fade to take over opacity
      float forwardProgress = fxGlideAnimProgress(aw);

      wAttrib->opacity = aw->storedOpacity * (1 - forwardProgress);
}


// =====================  Effect: Curved Fold  =========================

static void
fxCurvedFoldModelStepObject(CompWindow * w,
                                          Model * model,
                                          Object * object,
                                          float forwardProgress, float curveMaxAmp)
{
      ANIM_WINDOW(w);

      float origx = w->attrib.x + (WIN_W(w) * object->gridPosition.x -
                                                 w->output.left) * model->scale.x;
      float origy = w->attrib.y + (WIN_H(w) * object->gridPosition.y -
                                                 w->output.top) * model->scale.y;

      if (aw->curWindowEvent == WindowEventShade ||
            aw->curWindowEvent == WindowEventUnshade)
      {
            // Execute shade mode

            // find position in window contents
            // (window contents correspond to 0.0-1.0 range)
            float relPosInWinContents =
                        (object->gridPosition.y * WIN_H(w) -
                         model->topHeight) / w->height;
            float relDistToCenter = fabs(relPosInWinContents - 0.5);

            if (object->gridPosition.y == 0)
            {
                  object->position.x = origx;
                  object->position.y = WIN_Y(w);
            }
            else if (object->gridPosition.y == 1)
            {
                  object->position.x = origx;
                  object->position.y =
                              (1 - forwardProgress) *
                              origy +
                              forwardProgress *
                              (WIN_Y(w) + model->topHeight + model->bottomHeight);
            }
            else
            {
                  object->position.x =
                              origx + sin(forwardProgress * M_PI / 2) *
                              (0.5 -
                               object->gridPosition.x) * 2 * model->scale.x *
                              (curveMaxAmp -
                               curveMaxAmp * 4 * relDistToCenter * relDistToCenter);
                  object->position.y =
                              (1 - forwardProgress) * origy +
                              forwardProgress * (WIN_Y(w) + model->topHeight);
            }
      }
      else
      {                                         // Execute normal mode

            // find position within window borders
            // (border contents correspond to 0.0-1.0 range)
            float relPosInWinBorders =
                        (object->gridPosition.y * WIN_H(w) -
                         (w->output.top - w->input.top)) / BORDER_H(w);
            float relDistToCenter = fabs(relPosInWinBorders - 0.5);

            // prevent top & bottom shadows from extending too much
            if (relDistToCenter > 0.5)
                  relDistToCenter = 0.5;

            object->position.x =
                        origx + sin(forwardProgress * M_PI / 2) *
                        (0.5 - object->gridPosition.x) * 2 * model->scale.x *
                        (curveMaxAmp -
                         curveMaxAmp * 4 * relDistToCenter * relDistToCenter);
            object->position.y =
                        (1 - forwardProgress) * origy + forwardProgress * BORDER_Y(w);
      }
}

static void fxCurvedFoldModelStep(CompScreen * s, CompWindow * w, float time)
{
      int i, j, steps;

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;

      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            float forwardProgress =
                        1 - (aw->animRemainingTime - timestep) /
                        (aw->animTotalTime - timestep);
            if (aw->curWindowEvent == WindowEventCreate ||
                  aw->curWindowEvent == WindowEventUnminimize ||
                  aw->curWindowEvent == WindowEventUnshade)
                  forwardProgress = 1 - forwardProgress;

            for (i = 0; i < model->numObjects; i++)
                  fxCurvedFoldModelStepObject(w, model,
                                                            &model->objects[i],
                                                            forwardProgress,
                                                            as->
                                                            opt
                                                            [ANIM_SCREEN_OPTION_CURVED_FOLD_AMP].
                                                            value.f * WIN_W(w));

            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}


// =====================  Effect: Horizontal Folds  =========================


static void
fxHorizontalFoldsInitGrid(AnimScreen * as,
                                      WindowEvent forWindowEvent,
                                      int *gridWidth, int *gridHeight)
{
      *gridWidth = 2;
      if (forWindowEvent == WindowEventShade ||
            forWindowEvent == WindowEventUnshade)
            *gridHeight =
                        3 + 2 *
                        as->opt[ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_NUM_FOLDS].value.
                        i;
      else
            *gridHeight =
                        1 + 2 *
                        as->opt[ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_NUM_FOLDS].value.
                        i;
}

static void
fxHorizontalFoldsModelStepObject(CompWindow * w,
                                                 Model * model,
                                                 Object * object,
                                                 float forwardProgress,
                                                 float curveMaxAmp, int rowNo)
{
      ANIM_WINDOW(w);

      float origx = w->attrib.x + (WIN_W(w) * object->gridPosition.x -
                                                 w->output.left) * model->scale.x;
      float origy = w->attrib.y + (WIN_H(w) * object->gridPosition.y -
                                                 w->output.top) * model->scale.y;

      if (aw->curWindowEvent == WindowEventShade ||
            aw->curWindowEvent == WindowEventUnshade)
      {
            // Execute shade mode

            float relDistToFoldCenter = (rowNo % 2 == 1 ? 0.5 : 0);

            if (object->gridPosition.y == 0)
            {
                  object->position.x = origx;
                  object->position.y = WIN_Y(w);
            }
            else if (object->gridPosition.y == 1)
            {
                  object->position.x = origx;
                  object->position.y =
                              (1 - forwardProgress) * origy +
                              forwardProgress *
                              (WIN_Y(w) + model->topHeight + model->bottomHeight);
            }
            else
            {
                  object->position.x =
                              origx + sin(forwardProgress * M_PI / 2) *
                              (0.5 -
                               object->gridPosition.x) * 2 * model->scale.x *
                              (curveMaxAmp -
                               curveMaxAmp * 4 * relDistToFoldCenter *
                               relDistToFoldCenter);
                  object->position.y =
                              (1 - forwardProgress) * origy +
                              forwardProgress * (WIN_Y(w) + model->topHeight);
            }
      }
      else
      {                                         // Execute normal mode

            float relDistToFoldCenter;

            relDistToFoldCenter = (rowNo % 2 == 0 ? 0.5 : 0);

            object->position.x =
                        origx + sin(forwardProgress * M_PI / 2) *
                        (0.5 - object->gridPosition.x) * 2 * model->scale.x *
                        (curveMaxAmp - curveMaxAmp * 4 *
                         relDistToFoldCenter * relDistToFoldCenter);
            object->position.y =
                        (1 - forwardProgress) * origy + forwardProgress * BORDER_Y(w);
      }
}

static void
fxHorizontalFoldsModelStep(CompScreen * s, CompWindow * w, float time)
{
      int i, j, steps;

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;
      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            float forwardProgress =
                        1 - (aw->animRemainingTime - timestep) /
                        (aw->animTotalTime - timestep);
            if (aw->curWindowEvent == WindowEventCreate ||
                  aw->curWindowEvent == WindowEventUnminimize ||
                  aw->curWindowEvent == WindowEventUnshade)
                  forwardProgress = 1 - forwardProgress;

            for (i = 0; i < model->numObjects; i++)
                  fxHorizontalFoldsModelStepObject(w, model,
                                                                   &model->
                                                                   objects[i],
                                                                   forwardProgress,
                                                                   as->
                                                                   opt
                                                                   [ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_AMP].
                                                                   value.f *
                                                                   WIN_W(w), i / model->gridWidth);

            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}


// =====================  Effect: Roll Up  =========================

static void
fxRollUpInitGrid(AnimScreen * as,
                         WindowEvent forWindowEvent, int *gridWidth, int *gridHeight)
{
      *gridWidth = 2;
      if (forWindowEvent == WindowEventShade ||
            forWindowEvent == WindowEventUnshade)
            *gridHeight = 4;
      else
            *gridHeight = 2;
}

static void
fxRollUpModelStepObject(CompWindow * w,
                                    Model * model,
                                    Object * object,
                                    float forwardProgress, Bool fixedInterior)
{
      ANIM_WINDOW(w);

      float origx = WIN_X(w) + WIN_W(w) * object->gridPosition.x;

      if (aw->curWindowEvent == WindowEventShade ||
            aw->curWindowEvent == WindowEventUnshade)
      {
            // Execute shade mode

            // find position in window contents
            // (window contents correspond to 0.0-1.0 range)
            float relPosInWinContents =
                        (object->gridPosition.y * WIN_H(w) -
                         model->topHeight) / w->height;

            if (object->gridPosition.y == 0)
            {
                  object->position.x = origx;
                  object->position.y = WIN_Y(w);
            }
            else if (object->gridPosition.y == 1)
            {
                  object->position.x = origx;
                  object->position.y =
                              (1 - forwardProgress) *
                              (WIN_Y(w) +
                               WIN_H(w) * object->gridPosition.y) +
                              forwardProgress * (WIN_Y(w) +
                                                         model->topHeight +
                                                         model->bottomHeight);
            }
            else
            {
                  object->position.x = origx;

                  if (relPosInWinContents > forwardProgress)
                  {
                        object->position.y =
                                    (1 - forwardProgress) *
                                    (WIN_Y(w) +
                                     WIN_H(w) * object->gridPosition.y) +
                                    forwardProgress * (WIN_Y(w) + model->topHeight);

                        if (fixedInterior)
                              object->offsetTexCoordForQuadBefore.y =
                                          -forwardProgress * w->height;
                  }
                  else
                  {
                        object->position.y = WIN_Y(w) + model->topHeight;
                        if (!fixedInterior)
                              object->offsetTexCoordForQuadAfter.
                                          y =
                                          (forwardProgress -
                                           relPosInWinContents) * w->height;
                  }
            }
      }
}

static void fxRollUpModelStep(CompScreen * s, CompWindow * w, float time)
{
      int i, j, steps;

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;
      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            float forwardProgress =
                        1 - (aw->animRemainingTime - timestep) /
                        (aw->animTotalTime - timestep);

            forwardProgress =
                  (sigmoid(forwardProgress) - sigmoid(0)) /
                  (sigmoid(1) - sigmoid(0));

            if (aw->curWindowEvent == WindowEventCreate ||
                  aw->curWindowEvent == WindowEventUnminimize ||
                  aw->curWindowEvent == WindowEventUnshade)
                  forwardProgress = 1 - forwardProgress;

            for (i = 0; i < model->numObjects; i++)
                  fxRollUpModelStepObject(w, model,
                                                      &model->objects[i],
                                                      forwardProgress,
                                                      as->
                                                      opt
                                                      [ANIM_SCREEN_OPTION_ROLLUP_FIXED_INTERIOR].
                                                      value.b);

            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
      modelCalcBounds(model);
}


// =====================  Effect: Fade  =========================

static void fxFadeInit(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(s);

      // store window opacity
      aw->storedOpacity = w->paint.opacity;

      aw->timestep = (s->slowAnimations ? 2 :   // For smooth slow-mo (refer to display.c)
                              as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);
}

static void fxFadeModelStep(CompScreen * s, CompWindow * w, float time)
{
      int j, steps;

      ANIM_WINDOW(w);
      ANIM_SCREEN(s);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

      aw->timestep = timestep;

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;

      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      for (j = 0; j < steps; j++)
      {
            aw->animRemainingTime -= timestep;
            if (aw->animRemainingTime <= 0)
            {
                  aw->animRemainingTime = 0;    // avoid sub-zero values
                  break;
            }
      }
}

static void
fxFadeUpdateWindowAttrib(AnimScreen * as,
                                     AnimWindow * aw, WindowPaintAttrib * wAttrib)
{
      float forwardProgress =
                  1 - (aw->animRemainingTime - aw->timestep) /
                  (aw->animTotalTime - aw->timestep);
      forwardProgress = MIN(forwardProgress, 1);
      forwardProgress = MAX(forwardProgress, 0);

      if (aw->curWindowEvent == WindowEventCreate ||
            aw->curWindowEvent == WindowEventUnminimize)
            forwardProgress = 1 - forwardProgress;

      wAttrib->opacity = (GLushort) (aw->storedOpacity * (1 - forwardProgress));
}


// =====================  Effect: Burn  =========================

static void fxBurnInit(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(s);

      modelInitObjects(aw->model, WIN_X(w), WIN_Y(w), WIN_W(w), WIN_H(w));
      if (!aw->numPs)
      {
            aw->ps = calloc(1, 2 * sizeof(ParticleSystem));
            if (!aw->ps)
            {
                  postAnimationCleanup(w, TRUE);
                  return;
            }
            aw->numPs = 2;
      }
      initParticles(as->opt[ANIM_SCREEN_OPTION_FIRE_PARTICLES].value.i /
                          10, &aw->ps[0]);
      initParticles(as->opt[ANIM_SCREEN_OPTION_FIRE_PARTICLES].value.i,
                          &aw->ps[1]);
      aw->ps[1].slowdown = as->opt[ANIM_SCREEN_OPTION_FIRE_SLOWDOWN].value.f;
      aw->ps[1].darken = 0.5;
      aw->ps[1].blendMode = GL_ONE;

      aw->ps[0].slowdown =
                  as->opt[ANIM_SCREEN_OPTION_FIRE_SLOWDOWN].value.f / 2.0;
      aw->ps[0].darken = 0.0;
      aw->ps[0].blendMode = GL_ONE_MINUS_SRC_ALPHA;

      if (!aw->ps[0].tex)
            glGenTextures(1, &aw->ps[0].tex);
      glBindTexture(GL_TEXTURE_2D, aw->ps[0].tex);

      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0,
                         GL_RGBA, GL_UNSIGNED_BYTE, fireTex);
      glBindTexture(GL_TEXTURE_2D, 0);

      if (!aw->ps[1].tex)
            glGenTextures(1, &aw->ps[1].tex);
      glBindTexture(GL_TEXTURE_2D, aw->ps[1].tex);

      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0,
                         GL_RGBA, GL_UNSIGNED_BYTE, fireTex);
      glBindTexture(GL_TEXTURE_2D, 0);

      aw->animFireDirection = getAnimationDirection
                  (w, as->opt[ANIM_SCREEN_OPTION_FIRE_DIRECTION].value.s, FALSE);

      if (as->opt[ANIM_SCREEN_OPTION_FIRE_CONSTANT_SPEED].value.b)
      {
            aw->animTotalTime *= WIN_H(w) / 500.0;
            aw->animRemainingTime *= WIN_H(w) / 500.0;
      }
}

static void
fxBurnGenNewFire(CompScreen * s, ParticleSystem * ps, int x, int y,
                         int width, int height, float size, float time)
{
      ANIM_SCREEN(s);

      float max_new =
                  ps->numParticles * (time / 50) * (1.05 -
                                                                    as->
                                                                    opt
                                                                    [ANIM_SCREEN_OPTION_FIRE_LIFE].
                                                                    value.f);
      int i;
      Particle *part;
      float rVal;

      for (i = 0; i < ps->numParticles && max_new > 0; i++)
      {
            part = &ps->particles[i];
            if (part->life <= 0.0f)
            {
                  // give gt new life
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->life = 1.0f;
                  part->fade = (rVal * (1 - as->opt[ANIM_SCREEN_OPTION_FIRE_LIFE].value.f)) + (0.2f * (1.01 - as->opt[ANIM_SCREEN_OPTION_FIRE_LIFE].value.f));    // Random Fade Value

                  // set size
                  part->width = as->opt[ANIM_SCREEN_OPTION_FIRE_SIZE].value.f;
                  part->height =
                              as->opt[ANIM_SCREEN_OPTION_FIRE_SIZE].value.f * 1.5;
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->w_mod = size * rVal;
                  part->h_mod = size * rVal;

                  // choose random position
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->x = x + ((width > 1) ? (rVal * width) : 0);
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->y = y + ((height > 1) ? (rVal * height) : 0);
                  part->z = 0.0;
                  part->xo = part->x;
                  part->yo = part->y;
                  part->zo = part->z;

                  // set speed and direction
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->xi = ((rVal * 20.0) - 10.0f);
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->yi = ((rVal * 20.0) - 15.0f);
                  part->zi = 0.0f;
                  rVal = (float)(random() & 0xff) / 255.0;

                  if (as->opt[ANIM_SCREEN_OPTION_FIRE_MYSTICAL].value.b)
                  {
                        // Random colors! (aka Mystical Fire)
                        rVal = (float)(random() & 0xff) / 255.0;
                        part->r = rVal;
                        rVal = (float)(random() & 0xff) / 255.0;
                        part->g = rVal;
                        rVal = (float)(random() & 0xff) / 255.0;
                        part->b = rVal;
                  }
                  else
                  {
                        // set color ABAB as->opt[ANIM_SCREEN_OPTION_FIRE_COLOR].value.f
                        part->r =
                                    (float)as->
                                    opt[ANIM_SCREEN_OPTION_FIRE_COLOR].
                                    value.c[0] / 0xffff -
                                    (rVal / 1.7 *
                                     (float)as->
                                     opt[ANIM_SCREEN_OPTION_FIRE_COLOR].value.c[0] /
                                     0xffff);
                        part->g =
                                    (float)as->opt[ANIM_SCREEN_OPTION_FIRE_COLOR].value.
                                    c[1] / 0xffff -
                                    (rVal / 1.7 *
                                     (float)as->opt[ANIM_SCREEN_OPTION_FIRE_COLOR].value.
                                     c[1] / 0xffff);
                        part->b =
                                    (float)as->opt[ANIM_SCREEN_OPTION_FIRE_COLOR].value.
                                    c[2] / 0xffff -
                                    (rVal / 1.7 *
                                     (float)as->opt[ANIM_SCREEN_OPTION_FIRE_COLOR].value.
                                     c[2] / 0xffff);
                  }
                  // set transparancy
                  part->a =
                              (float)as->opt[ANIM_SCREEN_OPTION_FIRE_COLOR].
                              value.c[3] / 0xffff;

                  // set gravity
                  part->xg = (part->x < part->xo) ? 1.0 : -1.0;
                  part->yg = -3.0f;
                  part->zg = 0.0f;

                  ps->active = TRUE;
                  max_new -= 1;
            }
            else
            {
                  part->xg = (part->x < part->xo) ? 1.0 : -1.0;
            }
      }

}

static void
fxBurnGenNewSmoke(CompScreen * s, ParticleSystem * ps, int x, int y,
                          int width, int height, float size, float time)
{
      ANIM_SCREEN(s);

      float max_new =
                  ps->numParticles * (time / 50) * (1.05 -
                                                                    as->
                                                                    opt
                                                                    [ANIM_SCREEN_OPTION_FIRE_LIFE].
                                                                    value.f);
      int i;
      Particle *part;
      float rVal;

      for (i = 0; i < ps->numParticles && max_new > 0; i++)
      {
            part = &ps->particles[i];
            if (part->life <= 0.0f)
            {
                  // give gt new life
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->life = 1.0f;
                  part->fade = (rVal * (1 - as->opt[ANIM_SCREEN_OPTION_FIRE_LIFE].value.f)) + (0.2f * (1.01 - as->opt[ANIM_SCREEN_OPTION_FIRE_LIFE].value.f));    // Random Fade Value

                  // set size
                  part->width =
                              as->opt[ANIM_SCREEN_OPTION_FIRE_SIZE].value.f * size * 5;
                  part->height =
                              as->opt[ANIM_SCREEN_OPTION_FIRE_SIZE].value.f * size * 5;
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->w_mod = -0.8;
                  part->h_mod = -0.8;

                  // choose random position
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->x = x + ((width > 1) ? (rVal * width) : 0);
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->y = y + ((height > 1) ? (rVal * height) : 0);
                  part->z = 0.0;
                  part->xo = part->x;
                  part->yo = part->y;
                  part->zo = part->z;

                  // set speed and direction
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->xi = ((rVal * 20.0) - 10.0f);
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->yi = (rVal + 0.2) * -size;
                  part->zi = 0.0f;

                  // set color
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->r = rVal / 4.0;
                  part->g = rVal / 4.0;
                  part->b = rVal / 4.0;
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->a = 0.5 + (rVal / 2.0);

                  // set gravity
                  part->xg = (part->x < part->xo) ? size : -size;
                  part->yg = -size;
                  part->zg = 0.0f;

                  ps->active = TRUE;
                  max_new -= 1;
            }
            else
            {
                  part->xg = (part->x < part->xo) ? size : -size;
            }
      }

}

static void fxBurnModelStep(CompScreen * s, CompWindow * w, float time)
{
      int steps;

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      Bool smoke = as->opt[ANIM_SCREEN_OPTION_FIRE_SMOKE].value.b;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP_INTENSE].value.i);
      float old = 1 - (aw->animRemainingTime) / (aw->animTotalTime);
      float stepSize;

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;
      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      aw->animRemainingTime -= timestep;
      if (aw->animRemainingTime <= 0)
            aw->animRemainingTime = 0;    // avoid sub-zero values
      float new = 1 - (aw->animRemainingTime) / (aw->animTotalTime);

      stepSize = new - old;

      if (aw->curWindowEvent == WindowEventCreate ||
            aw->curWindowEvent == WindowEventUnminimize ||
            aw->curWindowEvent == WindowEventUnshade)
      {
            old = 1 - old;
            new = 1 - new;
      }

      if (!aw->drawRegion)
            aw->drawRegion = XCreateRegion();
      if (aw->animRemainingTime > 0)
      {
            XRectangle rect;

            switch (aw->animFireDirection)
            {
            case AnimDirectionUp:
                  rect.x = 0;
                  rect.y = 0;
                  rect.width = WIN_W(w);
                  rect.height = WIN_H(w) - (old * WIN_H(w));
                  break;
            case AnimDirectionRight:
                  rect.x = (old * WIN_W(w));
                  rect.y = 0;
                  rect.width = WIN_W(w) - (old * WIN_W(w));
                  rect.height = WIN_H(w);
                  break;
            case AnimDirectionLeft:
                  rect.x = 0;
                  rect.y = 0;
                  rect.width = WIN_W(w) - (old * WIN_W(w));
                  rect.height = WIN_H(w);
                  break;
            case AnimDirectionDown:
            default:
                  rect.x = 0;
                  rect.y = (old * WIN_H(w));
                  rect.width = WIN_W(w);
                  rect.height = WIN_H(w) - (old * WIN_H(w));
                  break;
            }
            XUnionRectWithRegion(&rect, getEmptyRegion(), aw->drawRegion);
      }
      else
      {
            XUnionRegion(getEmptyRegion(), getEmptyRegion(), aw->drawRegion);
      }
      if (new != 0)
            aw->useDrawRegion = TRUE;
      else
            aw->useDrawRegion = FALSE;

      if (aw->animRemainingTime > 0 && aw->ps)
      {
            switch (aw->animFireDirection)
            {
            case AnimDirectionUp:
                  if (smoke)
                        fxBurnGenNewSmoke(s, &aw->ps[0], WIN_X(w),
                                                  WIN_Y(w) +
                                                  ((1 - old) * WIN_H(w)),
                                                  WIN_W(w), 1, WIN_W(w) / 40.0, time);
                  fxBurnGenNewFire(s, &aw->ps[1], WIN_X(w),
                                           WIN_Y(w) + ((1 - old) * WIN_H(w)),
                                           WIN_W(w), (stepSize) * WIN_H(w),
                                           WIN_W(w) / 40.0, time);
                  break;
            case AnimDirectionLeft:
                  if (smoke)
                        fxBurnGenNewSmoke(s, &aw->ps[0],
                                                  WIN_X(w) +
                                                  ((1 - old) * WIN_W(w)),
                                                  WIN_Y(w),
                                                  (stepSize) * WIN_W(w),
                                                  WIN_H(w), WIN_H(w) / 40.0, time);
                  fxBurnGenNewFire(s, &aw->ps[1],
                                           WIN_X(w) + ((1 - old) * WIN_W(w)),
                                           WIN_Y(w), (stepSize) * WIN_W(w),
                                           WIN_H(w), WIN_H(w) / 40.0, time);
                  break;
            case AnimDirectionRight:
                  if (smoke)
                        fxBurnGenNewSmoke(s, &aw->ps[0],
                                                  WIN_X(w) +
                                                  (old * WIN_W(w)),
                                                  WIN_Y(w),
                                                  (stepSize) * WIN_W(w),
                                                  WIN_H(w), WIN_H(w) / 40.0, time);
                  fxBurnGenNewFire(s, &aw->ps[1],
                                           WIN_X(w) + (old * WIN_W(w)),
                                           WIN_Y(w), (stepSize) * WIN_W(w),
                                           WIN_H(w), WIN_H(w) / 40.0, time);
                  break;
            case AnimDirectionDown:
            default:
                  if (smoke)
                        fxBurnGenNewSmoke(s, &aw->ps[0], WIN_X(w),
                                                  WIN_Y(w) +
                                                  (old * WIN_H(w)),
                                                  WIN_W(w), 1, WIN_W(w) / 40.0, time);
                  fxBurnGenNewFire(s, &aw->ps[1], WIN_X(w),
                                           WIN_Y(w) + (old * WIN_H(w)),
                                           WIN_W(w), (stepSize) * WIN_H(w),
                                           WIN_W(w) / 40.0, time);
                  break;
            }

      }
      if (aw->animRemainingTime <= 0 && aw->numPs
            && (aw->ps[0].active || aw->ps[1].active))
            aw->animRemainingTime = timestep;

      if (!aw->numPs)
      {
            if (aw->ps)
            {
                  finiParticles(aw->ps);
                  free(aw->ps);
                  aw->ps = NULL;
            }
            return;                             // FIXME - is this correct behaviour?
      }

      int i;
      Particle *part;

      for (i = 0;
             i < aw->ps[0].numParticles && aw->animRemainingTime > 0
             && smoke; i++)
      {
            part = &aw->ps[0].particles[i];
            part->xg = (part->x < part->xo) ? WIN_W(w) / 40.0 : -WIN_W(w) / 40.0;
      }
      aw->ps[0].x = WIN_X(w);
      aw->ps[0].y = WIN_Y(w);

      for (i = 0; i < aw->ps[1].numParticles && aw->animRemainingTime > 0; i++)
      {
            part = &aw->ps[1].particles[i];
            part->xg = (part->x < part->xo) ? 1.0 : -1.0;
      }
      aw->ps[1].x = WIN_X(w);
      aw->ps[1].y = WIN_Y(w);

      modelCalcBounds(model);
}

static void drawParticleSystems(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);

      if (aw->numPs)
      {
            int i = 0;

            for (i = 0; i < aw->numPs; i++)
            {
                  if (aw->ps[i].active && !WINDOW_INVISIBLE(w))
                  {
                        drawParticles(s, w, &aw->ps[i]);
                  }
            }
      }
}


// =====================  Effect: Beam Up  =========================

static void fxBeamUpInit(CompScreen * s, CompWindow * w)
{
      int particles = WIN_W(w);

      fxFadeInit(s, w);
      ANIM_WINDOW(w);
      ANIM_SCREEN(s);
      if (!aw->numPs)
      {
            aw->ps = calloc(1, 2 * sizeof(ParticleSystem));
            if (!aw->ps)
            {
                  postAnimationCleanup(w, TRUE);
                  return;
            }
            aw->numPs = 2;
      }
      initParticles(particles / 10, &aw->ps[0]);
      initParticles(particles, &aw->ps[1]);
      aw->ps[1].slowdown = as->opt[ANIM_SCREEN_OPTION_BEAMUP_SLOWDOWN].value.f;
      aw->ps[1].darken = 0.5;
      aw->ps[1].blendMode = GL_ONE;

      aw->ps[0].slowdown =
                  as->opt[ANIM_SCREEN_OPTION_BEAMUP_SLOWDOWN].value.f / 2.0;
      aw->ps[0].darken = 0.0;
      aw->ps[0].blendMode = GL_ONE_MINUS_SRC_ALPHA;

      if (!aw->ps[0].tex)
            glGenTextures(1, &aw->ps[0].tex);
      glBindTexture(GL_TEXTURE_2D, aw->ps[0].tex);

      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0,
                         GL_RGBA, GL_UNSIGNED_BYTE, fireTex);
      glBindTexture(GL_TEXTURE_2D, 0);

      if (!aw->ps[1].tex)
            glGenTextures(1, &aw->ps[1].tex);
      glBindTexture(GL_TEXTURE_2D, aw->ps[1].tex);

      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0,
                         GL_RGBA, GL_UNSIGNED_BYTE, fireTex);
      glBindTexture(GL_TEXTURE_2D, 0);

}

static void
fxBeamUpGenNewFire(CompScreen * s, ParticleSystem * ps, int x, int y,
                           int width, int height, float size, float time)
{
      ANIM_SCREEN(s);

      ps->numParticles =
                  width / as->opt[ANIM_SCREEN_OPTION_BEAMUP_SPACING].value.i;
      float max_new =
                  ps->numParticles * (time / 50) * (1.05 -
                                                                    as->
                                                                    opt
                                                                    [ANIM_SCREEN_OPTION_BEAMUP_LIFE].
                                                                    value.f);
      int i;
      Particle *part;
      float rVal;

      for (i = 0; i < ps->numParticles && max_new > 0; i++)
      {
            part = &ps->particles[i];
            if (part->life <= 0.0f)
            {
                  // give gt new life
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->life = 1.0f;
                  part->fade = (rVal * (1 - as->opt[ANIM_SCREEN_OPTION_BEAMUP_LIFE].value.f)) + (0.2f * (1.01 - as->opt[ANIM_SCREEN_OPTION_BEAMUP_LIFE].value.f));      // Random Fade Value

                  // set size
                  part->width =
                              2.5 * as->opt[ANIM_SCREEN_OPTION_BEAMUP_SIZE].value.f;
                  part->height = height;
                  part->w_mod = size * 0.2;
                  part->h_mod = size * 0.02;

                  // choose random x position
                  rVal = (float)(random() & 0xff) / 255.0;
                  part->x = x + ((width > 1) ? (rVal * width) : 0);
                  part->y = y;
                  part->z = 0.0;
                  part->xo = part->x;
                  part->yo = part->y;
                  part->zo = part->z;

                  // set speed and direction
                  part->xi = 0.0f;
                  part->yi = 0.0f;
                  part->zi = 0.0f;

                  // set color ABAB as->opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR].value.f
                  part->r =
                              (float)as->
                              opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR].value.
                              c[0] / 0xffff -
                              (rVal / 1.7 *
                               (float)as->
                               opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR].value.c[0] /
                               0xffff);
                  part->g =
                              (float)as->opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR].value.
                              c[1] / 0xffff -
                              (rVal / 1.7 *
                               (float)as->opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR].value.
                               c[1] / 0xffff);
                  part->b =
                              (float)as->opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR].value.
                              c[2] / 0xffff -
                              (rVal / 1.7 *
                               (float)as->opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR].value.
                               c[2] / 0xffff);;
                  part->a =
                              (float)as->opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR].value.
                              c[3] / 0xffff;

                  // set gravity
                  part->xg = 0.0f;
                  part->yg = 0.0f;
                  part->zg = 0.0f;

                  ps->active = TRUE;
                  max_new -= 1;
            }
            else
            {
                  part->xg = (part->x < part->xo) ? 1.0 : -1.0;
            }
      }

}

static void fxBeamUpModelStep(CompScreen * s, CompWindow * w, float time)
{
      int steps;
      int creating = 0;

      fxFadeModelStep(s, w, time);

      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      Model *model = aw->model;

      float timestep = (s->slowAnimations ? 2 : // For smooth slow-mo (refer to display.c)
                                as->opt[ANIM_SCREEN_OPTION_TIME_STEP_INTENSE].value.i);
      float old = 1 - (aw->animRemainingTime) / (aw->animTotalTime);
      float stepSize;

      model->remainderSteps += time / timestep;
      steps = floor(model->remainderSteps);
      model->remainderSteps -= steps;
      if (!steps && aw->animRemainingTime < aw->animTotalTime)
            return;
      steps = MAX(1, steps);

      aw->animRemainingTime -= timestep;
      if (aw->animRemainingTime <= 0)
            aw->animRemainingTime = 0;    // avoid sub-zero values
      float new = 1 - (aw->animRemainingTime) / (aw->animTotalTime);

      stepSize = new - old;

      if (aw->curWindowEvent == WindowEventCreate ||
            aw->curWindowEvent == WindowEventUnminimize ||
            aw->curWindowEvent == WindowEventUnshade)
      {
            old = 1 - old;
            new = 1 - new;
            creating = 1;
      }

      if (!aw->drawRegion)
            aw->drawRegion = XCreateRegion();
      if (aw->animRemainingTime > 0)
      {
            XRectangle rect;

            rect.x = ((old / 2) * WIN_W(w));
            rect.width = WIN_W(w) - (old * WIN_W(w));
            rect.y = ((old / 2) * WIN_H(w));
            rect.height = WIN_H(w) - (old * WIN_H(w));
            XUnionRectWithRegion(&rect, getEmptyRegion(), aw->drawRegion);
      }
      else
      {
            XUnionRegion(getEmptyRegion(), getEmptyRegion(), aw->drawRegion);
      }
      if (new != 0)
            aw->useDrawRegion = TRUE;
      else
            aw->useDrawRegion = FALSE;

      if (aw->animRemainingTime > 0 && aw->ps)
      {
            fxBeamUpGenNewFire(s, &aw->ps[1], WIN_X(w),
                                       WIN_Y(w) + (WIN_H(w) / 2), WIN_W(w),
                                       creating ? WIN_H(w) -
                                       (old / 2 * WIN_H(w)) : (WIN_H(w) -
                                                                           (old *
                                                                              WIN_H(w))),
                                       WIN_W(w) / 40.0, time);

      }
      if (aw->animRemainingTime <= 0 && aw->numPs
            && (aw->ps[0].active || aw->ps[1].active))
            aw->animRemainingTime = timestep;

      if (!aw->numPs)
      {
            if (aw->ps)
            {
                  finiParticles(aw->ps);
                  free(aw->ps);
                  aw->ps = NULL;
            }
            return;                             // FIXME - is this correct behaviour?
      }

      int i;
      Particle *part;

      aw->ps[0].x = WIN_X(w);
      aw->ps[0].y = WIN_Y(w);

      for (i = 0; i < aw->ps[1].numParticles && aw->animRemainingTime > 0; i++)
      {
            part = &aw->ps[1].particles[i];
            part->xg = (part->x < part->xo) ? 1.0 : -1.0;
      }
      aw->ps[1].x = WIN_X(w);
      aw->ps[1].y = WIN_Y(w);

      modelCalcBounds(model);
}

static void
fxBeamupUpdateWindowAttrib(AnimScreen * as,
                                       AnimWindow * aw, WindowPaintAttrib * wAttrib)
{
      float forwardProgress =
                  1 - (aw->animRemainingTime - aw->timestep) /
                  (aw->animTotalTime - aw->timestep);
      forwardProgress = MIN(forwardProgress, 1);
      forwardProgress = MAX(forwardProgress, 0);

      if (aw->curWindowEvent == WindowEventCreate ||
            aw->curWindowEvent == WindowEventUnminimize)
      {
            forwardProgress = forwardProgress * forwardProgress;
            forwardProgress = forwardProgress * forwardProgress;
            forwardProgress = 1 - forwardProgress;
      }

      wAttrib->opacity = (GLushort) (aw->storedOpacity * (1 - forwardProgress));
}


// =====================  Effect: 3D Spread  =========================

// Frees up polygon objects in pset
static void freePolygonObjects(PolygonSet * pset)
{
      PolygonObject *p = pset->polygons;

      if (!p)
      {
            pset->nPolygons = 0;
            return;
      }
      int i;

      for (i = 0; i < pset->nPolygons; i++, p++)
      {
            if (p->nVertices > 0)
            {
                  if (p->vertices)
                        free(p->vertices);
                  if (p->sideIndices)
                        free(p->sideIndices);
                  if (p->normals)
                        free(p->normals);
                  p->vertices = 0;
                  p->sideIndices = 0;
                  p->normals = 0;
                  p->nVertices = 0;
            }
            /*if (p->nShadowQuads > 0)
               {
               if (p->shadowVertices)
               free(p->shadowVertices);
               if (p->shadowTexCoords)
               free(p->shadowTexCoords);
               p->shadowVertices = 0;
               p->shadowTexCoords = 0;
               p->nShadowQuads = 0;
               } */
            p->nSides = 0;
      }
      free(pset->polygons);
      pset->polygons = 0;
      pset->nPolygons = 0;
}

// Frees up intersecting polygon info of PolygonSet clips
static void freeClipsPolygons(PolygonSet * pset)
{
      int k;

      for (k = 0; k < pset->clipCapacity; k++)
      {
            if (pset->clips[k].intersectingPolygons)
            {
                  free(pset->clips[k].intersectingPolygons);
                  pset->clips[k].intersectingPolygons = 0;
            }
            if (pset->clips[k].polygonVertexTexCoords)
            {
                  free(pset->clips[k].polygonVertexTexCoords);
                  pset->clips[k].polygonVertexTexCoords = 0;
            }
            pset->clips[k].nIntersectingPolygons = 0;
      }
}

// Frees up the whole polygon set
static void freePolygonSet(AnimWindow * aw)
{
      PolygonSet *pset = aw->polygonSet;

      freePolygonObjects(pset);
      if (pset->clipCapacity > 0)
      {
            freeClipsPolygons(pset);
            free(pset->clips);
            pset->clips = 0;
            pset->nClips = 0;
            pset->firstNondrawnClip = 0;
            pset->clipCapacity = 0;
      }
      free(pset);
      aw->polygonSet = 0;
}

/*
// Tessellates window into extruded rectangular objects
// in a brick wall formation
static Bool
tessellateIntoBricks(CompWindow * w,
                     int gridSizeX, int gridSizeY, float thickness)
{
}

// Tessellates window into extruded hexagon objects
static Bool
tessellateIntoHexagons(CompWindow * w,
                     int gridSizeX, int gridSizeY, float thickness)
{
}

// Tessellates window into Voronoi segments
static Bool
tessellateVoronoi(CompWindow * w,
                  float thickness)
{
}
*/

// Tessellates window into extruded rectangular objects
static Bool
tessellateIntoRectangles(CompWindow * w,
                                     int gridSizeX, int gridSizeY, float thickness)
{
      ANIM_WINDOW(w);

      PolygonSet *pset = aw->polygonSet;

      if (!pset)
            return FALSE;

      int winLimitsX;                     // boundaries of polygon tessellation
      int winLimitsY;
      int winLimitsW;
      int winLimitsH;

      if (pset->includeShadows)
      {
            winLimitsX = WIN_X(w);
            winLimitsY = WIN_Y(w);
            winLimitsW = WIN_W(w) - 1; // avoid artifact on right edge
            winLimitsH = WIN_H(w);
      }
      else
      {
            winLimitsX = BORDER_X(w);
            winLimitsY = BORDER_Y(w);
            winLimitsW = BORDER_W(w);
            winLimitsH = BORDER_H(w);
      }
      float minRectSize = 20;
      float rectW = winLimitsW / (float)gridSizeX;
      float rectH = winLimitsH / (float)gridSizeY;

      if (rectW < minRectSize)
            gridSizeX = winLimitsW / minRectSize;     // int div.
      if (rectH < minRectSize)
            gridSizeY = winLimitsH / minRectSize;     // int div.

      if (pset->nPolygons != gridSizeX * gridSizeY)
      {
            if (pset->nPolygons > 0)
                  freePolygonObjects(pset);

            pset->nPolygons = gridSizeX * gridSizeY;

            pset->polygons = calloc(1, sizeof(PolygonObject) * pset->nPolygons);
            if (!pset->polygons)
            {
                  fprintf(stderr, "%s: Not enough memory at line %d!\n",
                              __FILE__, __LINE__);
                  pset->nPolygons = 0;
                  return FALSE;
            }
      }

      thickness /= w->screen->width;
      pset->thickness = thickness;
      pset->nTotalFrontVertices = 0;

      float cellW = (float)winLimitsW / gridSizeX;
      float cellH = (float)winLimitsH / gridSizeY;
      float halfW = cellW / 2;
      float halfH = cellH / 2;

      float halfThick = pset->thickness / 2;
      PolygonObject *p = pset->polygons;
      int x, y;

      //float vec = 1 / sqrt(3); // vector component for normals

      for (y = 0; y < gridSizeY; y++)
      {
            float posY = winLimitsY + cellH * (y + 0.5);

            for (x = 0; x < gridSizeX; x++, p++)
            {
                  p->centerPos.x = p->centerPosStart.x =
                              winLimitsX + cellW * (x + 0.5);
                  p->centerPos.y = p->centerPosStart.y = posY;
                  p->centerPos.z = p->centerPosStart.z = -halfThick;
                  p->rotAngle = p->rotAngleStart = 0;

                  p->centerRelPos.x = (x + 0.5) / gridSizeX;
                  p->centerRelPos.y = (y + 0.5) / gridSizeY;

                  p->nSides = 4;
                  p->nVertices = 2 * 4;
                  pset->nTotalFrontVertices += 4;

                  // 4 front, 4 back vertices
                  if (!p->vertices)
                  {
                        p->vertices = calloc(1, sizeof(GLfloat) * 8 * 3);
                  }
                  //if (!p->vertexOnEdge)
                  //  p->vertexOnEdge = calloc (1, sizeof (int) * p->nSides);
                  //p->vertexTexCoords4Clips = calloc (1, sizeof (GLfloat) * 4 * 2 * 2);
                  if (!p->vertices) // || !p->vertexOnEdge)// || !p->vertexTexCoords4Clips)
                  {
                        fprintf(stderr, "%s: Not enough memory at line %d!\n",
                                    __FILE__, __LINE__);
                        freePolygonObjects(pset);
                        return FALSE;
                  }

                  GLfloat *pv = p->vertices;

                  // Determine 4 front vertices in ccw direction
                  pv[0] = -halfW;
                  pv[1] = -halfH;
                  pv[2] = halfThick;

                  pv[3] = -halfW;
                  pv[4] = halfH;
                  pv[5] = halfThick;

                  pv[6] = halfW;
                  pv[7] = halfH;
                  pv[8] = halfThick;

                  pv[9] = halfW;
                  pv[10] = -halfH;
                  pv[11] = halfThick;

                  // Determine 4 back vertices in cw direction
                  pv[12] = halfW;
                  pv[13] = -halfH;
                  pv[14] = -halfThick;

                  pv[15] = halfW;
                  pv[16] = halfH;
                  pv[17] = -halfThick;

                  pv[18] = -halfW;
                  pv[19] = halfH;
                  pv[20] = -halfThick;

                  pv[21] = -halfW;
                  pv[22] = -halfH;
                  pv[23] = -halfThick;

                  // 10 indices for 4 sides (for quad strip)
                  if (!p->sideIndices)
                  {
                        //p->sideIndices = calloc(1, sizeof(GLushort) * 2 * (4 + 1));
                        p->sideIndices = calloc(1, sizeof(GLushort) * 4 * 4);
                  }
                  if (!p->sideIndices)
                  {
                        fprintf(stderr, "%s: Not enough memory at line %d!\n",
                                    __FILE__, __LINE__);
                        freePolygonObjects(pset);
                        return FALSE;
                  }

                  GLushort *ind = p->sideIndices;

                  /*
                     ind[0] = 0;
                     ind[1] = 7;
                     ind[2] = 1;
                     ind[3] = 6;
                     ind[4] = 2;
                     ind[5] = 5;
                     ind[6] = 3;
                     ind[7] = 4;
                     ind[8] = 0;
                     ind[9] = 7;
                   */
                  int id = 0;

                  ind[id++] = 0;
                  ind[id++] = 7;
                  ind[id++] = 6;
                  ind[id++] = 1;

                  ind[id++] = 1;
                  ind[id++] = 6;
                  ind[id++] = 5;
                  ind[id++] = 2;

                  ind[id++] = 2;
                  ind[id++] = 5;
                  ind[id++] = 4;
                  ind[id++] = 3;

                  ind[id++] = 3;
                  ind[id++] = 4;
                  ind[id++] = 7;
                  ind[id++] = 0;

                  // 4 front, 4 back vertex normals
                  if (!p->normals)
                  {
                        p->normals = calloc(1, sizeof(GLfloat) * (2 + 4) * 3);
                  }
                  if (!p->normals)
                  {
                        fprintf(stderr, "%s: Not enough memory at line %d!\n",
                                    __FILE__, __LINE__);
                        freePolygonObjects(pset);
                        return FALSE;
                  }

                  // Surface normals
                  GLfloat *nor = p->normals;

                  // Front
                  nor[0] = 0;
                  nor[1] = 0;
                  nor[2] = -1;

                  // Back
                  nor[3] = 0;
                  nor[4] = 0;
                  nor[5] = 1;

                  // Sides
                  nor[6] = -1;
                  nor[7] = 0;
                  nor[8] = 0;

                  nor[9] = 0;
                  nor[10] = 1;
                  nor[11] = 0;

                  nor[12] = 1;
                  nor[13] = 0;
                  nor[14] = 0;

                  nor[15] = 0;
                  nor[16] = -1;
                  nor[17] = 0;

                  // Determine bounding box (to test intersection with clips)
                  p->boundingBox.x1 = -halfW + p->centerPos.x;
                  p->boundingBox.y1 = -halfH + p->centerPos.y;
                  p->boundingBox.x2 = ceil(halfW + p->centerPos.x);
                  p->boundingBox.y2 = ceil(halfH + p->centerPos.y);
                  /*
                     // Determine edge/corner status
                     if (x == 0)
                     {
                     p->nShadowQuads++;
                     if (y == 0)
                     p->vertexOnEdge[0] = 5;
                     if (y == gridSizeY - 1)
                     p->nShadowQuads++;
                     }
                     if (x == gridSizeX - 1)
                     {
                     p->nShadowQuads++;
                     if (y == 0)
                     p->nShadowQuads++;
                     if (y == gridSizeY - 1)
                     p->nShadowQuads++;
                     }
                     if (y == 0)
                     p->nShadowQuads++;
                     if (y == gridSizeY - 1)
                     p->nShadowQuads++; */
            }
      }
      return TRUE;
}

static void
polygonsStoreClips(CompScreen * s, CompWindow * w,
                           int nClip, BoxPtr pClip, int nMatrix, CompMatrix * matrix)
{
      ANIM_WINDOW(w);

      PolygonSet *pset = aw->polygonSet;

      // if polygon set is not valid or effect is not 3D (glide w/thickness=0)
      if (!pset)
            return;

      // only draw windows on current viewport
      if (w->attrib.x > s->width || w->attrib.x + w->width < 0 ||
            w->attrib.y > s->height || w->attrib.y + w->height < 0 ||
            (aw->lastKnownCoords.x != NOT_INITIALIZED &&
             (aw->lastKnownCoords.x != w->attrib.x ||
              aw->lastKnownCoords.y != w->attrib.y)))
      {
            return;
            // since this is not the viewport the window was drawn
            // just before animation started
      }

      Bool dontStoreClips = TRUE;

      // If this clip doesn't match the corresponding stored clip,
      // clear the stored clips from this point (aw->nClipsPassed)
      // to the end and store the new ones instead.

      if (aw->nClipsPassed < pset->nClips) // if we have clips stored earlier
      {
            Clip4Polygons *c = pset->clips + aw->nClipsPassed;
            // the stored clip at position aw->nClipsPassed

            // if either clip coordinates or texture matrix is different
            if (memcmp(pClip, &c->box, sizeof(Box)) ||
                  memcmp(matrix, &c->texMatrix, sizeof(CompMatrix)))
            {
                  // get rid of the clips from here (aw->nClipsPassed) to the end
                  pset->nClips = aw->nClipsPassed;
                  dontStoreClips = FALSE;
            }
      }
      else
            dontStoreClips = FALSE;

      if (dontStoreClips)
      {
            aw->nClipsPassed += nClip;
            return;
      }
      // For each clip passed to this function
      for (; nClip--; pClip++, aw->nClipsPassed++)
      {
            // New clip

            if (!ensureLargerClipCapacity(pset))
            {
                  fprintf(stderr, "%s: Not enough memory at line %d!\n",
                              __FILE__, __LINE__);
                  return;
            }

            Clip4Polygons *newClip = &pset->clips[pset->nClips];

            newClip->id = aw->nClipsPassed;
            memcpy(&newClip->box, pClip, sizeof(Box));
            memcpy(&newClip->texMatrix, matrix, sizeof(CompMatrix));
            // nMatrix is not used for now
            // (i.e. only first texture matrix is considered)

            // avoid clipping along window edge
            // for the "window contents" clip
            if (pClip->x1 == BORDER_X(w) &&
                pClip->y1 == BORDER_Y(w) &&
                  pClip->x2 == BORDER_X(w) + BORDER_W(w) &&
                  pClip->y2 == BORDER_Y(w) + BORDER_H(w))
            {
                  newClip->boxf.x1 = pClip->x1 - 0.1f;
                  newClip->boxf.y1 = pClip->y1 - 0.1f;
                  newClip->boxf.x2 = pClip->x2 + 0.1f;
                  newClip->boxf.y2 = pClip->y2 + 0.1f;
            }
            else
            {
                  newClip->boxf.x1 = pClip->x1;
                  newClip->boxf.y1 = pClip->y1;
                  newClip->boxf.x2 = pClip->x2;
                  newClip->boxf.y2 = pClip->y2;
            }

            pset->nClips++;
            aw->clipsUpdated = TRUE;

            /*
               // Look for a container / contained clip to minimize # of clips.
               // Go backward, since such a clip is likely to be one of the last ones.
               // Go until you hit the clips corresponding to the previous texture.
               int i;
               for (i=pset->nClips-1; i>=pset->firstClipWithNoTex; i--)
               {
               Clip4Polygons *clip = &pset->clips[i];

               // if tex matrices are different
               if (clip->texMatrix.xx != matrix->xx ||
               clip->texMatrix.yy != matrix->yy ||
               clip->texMatrix.x0 != matrix->x0 ||
               clip->texMatrix.y0 != matrix->y0)
               continue;

               // if an old clip contains the new clip
               if (x1 >= clip->box.x1 && y1 >= clip->box.y1 &&
               x2 <= clip->box.x2 && y2 <= clip->box.y2)
               {
               newClipNo = i;
               updateCoords = FALSE;
               break;
               }
               // if the new clip contains an old clip
               if (x1 <= clip->box.x1 && y1 <= clip->box.y1 &&
               x2 >= clip->box.x2 && y2 >= clip->box.y2)
               {
               // update the old clip in this case
               newClipNo = i;
               break;
               }
               }
               if (pset->firstClipWithNoTex > 0)
               {
               // Go through clip's of earlier textures to look for
               // an identical clip. If such clips are found, their
               // texture should be updated with the latest one
               for (i=pset->firstClipWithNoTex-1; i>=0; i--)
               {
               Clip4Polygons *clip = &pset->clips[i];

               // if tex matrices are different
               if (clip->texMatrix.xx != matrix->xx ||
               clip->texMatrix.yy != matrix->yy ||
               clip->texMatrix.x0 != matrix->x0 ||
               clip->texMatrix.y0 != matrix->y0)
               continue;

               // if an old clip == the new clip
               if (x1 == clip->box.x1 && y1 == clip->box.y1 &&
               x2 == clip->box.x2 && y2 == clip->box.y2)
               {
               newClipNo = i;
               updateCoords = FALSE;
               clip->textureNo = aw->nTextures;
               break;
               }
               }
               }

               if (newClipNo == pset->nClips && // if no such old clip is found
               !ensureLargerClipCapacity (pset))
               {
               fprintf(stderr, "%s: Not enough memory at line %d!\n",
               __FILE__, __LINE__);
               return;
               }
               Clip4Polygons *newClip = &pset->clips[newClipNo];
               if (updateCoords)
               {
               memcpy (&newClip->box, pClip, sizeof (Box));
               memcpy (&newClip->texMatrix, matrix, sizeof (CompMatrix));
               // nMatrix is not used for now
               // (i.e. only first texture matrix is considered)
               }
               if (newClipNo == pset->nClips) // if new clip is being added
               {
               //printf("adding x1: %4d, y1: %4d, x2: %4d, y2: %4d\n",
               //       pClip->x1, pClip->y1, pClip->x2, pClip->y2);

               newClip->textureNo = aw->nTextures;
               pset->nClips++;
               } */
      }
}

// For each rectangular clip, this function finds polygons which
// have a bounding box that intersects the clip. For intersecting
// polygons, it computes the texture coordinates for the vertices
// of that polygon (to draw the clip texture).
static Bool processIntersectingPolygons(PolygonSet * pset)
{
      int j;

      for (j = pset->firstNondrawnClip; j < pset->nClips; j++)
      {
            Clip4Polygons *c = pset->clips + j;
            Box *cb = &c->box;
            int nFrontVerticesTilThisPoly = 0;

            c->nIntersectingPolygons = 0;

            // TODO: If it doesn't affect speed much, for each clip,
            // consider doing 2 passes, counting the intersecting polygons
            // in the 1st pass and allocating just enough space for those
            // polygons instead of all polygons in the 2nd pass.
            int i;

            for (i = 0; i < pset->nPolygons; i++)
            {
                  PolygonObject *p = pset->polygons + i;

                  Box *bb = &p->boundingBox;

                  if (bb->x2 <= cb->x1)
                        continue;         // no intersection
                  if (bb->y2 <= cb->y1)
                        continue;         // no intersection
                  if (bb->x1 >= cb->x2)
                        continue;         // no intersection
                  if (bb->y1 >= cb->y2)
                        continue;         // no intersection

                  // There is intersection, add clip info

                  if (!c->intersectingPolygons)
                  {
                        c->intersectingPolygons = calloc(1, sizeof(int) *
                                                                         pset->nPolygons);
                  }
                  // allocate tex coords
                  // 2 {x, y} * 2 {front, back} * <total # of polygon front vertices>
                  if (!c->polygonVertexTexCoords)
                  {
                        c->polygonVertexTexCoords =
                                    calloc(1, sizeof(GLfloat) * 2 * 2 *
                                             pset->nTotalFrontVertices);
                  }
                  if (!c->intersectingPolygons || !c->polygonVertexTexCoords)
                  {
                        fprintf(stderr, "%s: Not enough memory at line %d!\n",
                                    __FILE__, __LINE__);
                        freeClipsPolygons(pset);
                        return FALSE;
                  }
                  c->intersectingPolygons[c->nIntersectingPolygons] = i;

                  int k;

                  for (k = 0; k < p->nSides; k++)
                  {
                        float x = p->vertices[3 * k] +
                                      p->centerPosStart.x;
                        float y = p->vertices[3 * k + 1] +
                                      p->centerPosStart.y;
                        GLfloat tx;
                        GLfloat ty;
                        if (c->texMatrix.xy != 0.0f || c->texMatrix.yx != 0.0f)
                        {     // "non-rect" coordinates
                              tx = COMP_TEX_COORD_XY(&c->texMatrix, x, y);
                              ty = COMP_TEX_COORD_YX(&c->texMatrix, x, y);
                        }
                        else
                        {
                              tx = COMP_TEX_COORD_X(&c->texMatrix, x);
                              ty = COMP_TEX_COORD_Y(&c->texMatrix, y);
                        }
                        // for front vertices
                        int ti = 2 * (2 * nFrontVerticesTilThisPoly + k);

                        c->polygonVertexTexCoords[ti] = tx;
                        c->polygonVertexTexCoords[ti + 1] = ty;

                        // for back vertices
                        ti = 2 * (2 * nFrontVerticesTilThisPoly +
                                      (2 * p->nSides - 1 - k));
                        c->polygonVertexTexCoords[ti] = tx;
                        c->polygonVertexTexCoords[ti + 1] = ty;
                  }
                  c->nIntersectingPolygons++;
                  nFrontVerticesTilThisPoly += p->nSides;
            }
      }

      return TRUE;
}

/*
static Bool computePolygonShadows(PolygonSet * pset)
{
         PolygonObject *p = pset->polygons;
         int i;
         for (i=0; i<pset->nPolygons; i++, p++)
         {
         p->nShadowQuads = 0;

         // If an edge polygon, determine shadow quads
         // First count them
         if (x == 0)
         {
         p->nShadowQuads++;
         if (y == 0)
         p->nShadowQuads++;
         if (y == gridSizeY - 1)
         p->nShadowQuads++;
         }
         if (x == gridSizeX - 1)
         {
         p->nShadowQuads++;
         if (y == 0)
         p->nShadowQuads++;
         if (y == gridSizeY - 1)
         p->nShadowQuads++;
         }
         if (y == 0)
         p->nShadowQuads++;
         if (y == gridSizeY - 1)
         p->nShadowQuads++;

         // Allocate them
         p->shadowVertices = calloc (1, sizeof (GLfloat) * 3 *
         4 * p->nShadowQuads);
         if (!p->shadowVertices)
         {
         fprintf(stderr, "%s: Not enough memory at line %d!\n",
         __FILE__, __LINE__);
         freePolygonObjects (pset);
         return FALSE;
         }
         // Store them
         int n = 0;
         GLfloat *v = p->shadowVertices;
         if (x == 0)
         {
         // Left quad
         v[12 * n]     = w->attrib.x - w->output.left;
         v[12 * n + 1] = w->attrib.x - w->output.left;
         v[12 * n + 2] = w->attrib.x - w->output.left;
         n++;
         if (y == 0)
         n++;
         if (y == gridSizeY - 1)
         n++;
         }
         if (x == gridSizeX - 1)
         {
         n++;
         if (y == 0)
         n++;
         if (y == gridSizeY - 1)
         n++;
         }
         if (y == 0)
         n++;
         if (y == gridSizeY - 1)
         n++;

         p->nShadowQuads = n;
         }
      return TRUE;
}
*/

static void polygonsDrawCustomGeometry(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);

      if (                                // only draw windows on current viewport
               w->attrib.x > s->width || w->attrib.x + w->width < 0 ||
               w->attrib.y > s->height || w->attrib.y + w->height < 0 ||
               (aw->lastKnownCoords.x != NOT_INITIALIZED &&
                  (aw->lastKnownCoords.x != w->attrib.x ||
                   aw->lastKnownCoords.y != w->attrib.y)))
      {
            return;
            // since this is not the viewport the window was drawn
            // just before animation started
      }
      PolygonSet *pset = aw->polygonSet;

      // if polygon set is not valid or effect is not 3D (glide w/thickness=0)
      if (!pset)
            return;

      // TODO: Fix the source of the crash problem
      // (uninitialized lastClipInGroup)
      // instead of doing this uninitialized value check
      if (pset->firstNondrawnClip < 0 ||
            pset->firstNondrawnClip > pset->nClips ||
            (!aw->clipsUpdated &&
             (pset->lastClipInGroup[aw->nDrawGeometryCalls - 1] < 0 ||
              pset->lastClipInGroup[aw->nDrawGeometryCalls - 1] >= pset->nClips)))
      {
            return;
      }

      if (aw->clipsUpdated && aw->nDrawGeometryCalls > 0)
      {
            if (!processIntersectingPolygons(pset))
            {
                  return;
            }
      }

      int lastClip;                       // last clip to draw

      if (aw->clipsUpdated)
      {
            lastClip = pset->nClips - 1;
      }
      else
      {
            lastClip = pset->lastClipInGroup[aw->nDrawGeometryCalls - 1];
      }

      float forwardProgress = defaultAnimProgress(aw);

      // OpenGL stuff starts here

      if (pset->doLighting)
      {
            glPushAttrib(GL_LIGHT0);
            glPushAttrib(GL_COLOR_MATERIAL);
            glPushAttrib(GL_LIGHTING);
            glEnable(GL_COLOR_MATERIAL);
            glEnable(GL_LIGHTING);

            GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 0.3f };
            GLfloat diffuseLight[] = { 0.9f, 0.9f, 0.9f, 0.9f };
            GLfloat light0Position[] = { -0.5f, 0.5f, -9.0f, 0.0f };

            glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
            glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
            glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
      }
      glPushMatrix();

      // Store old blend values
      GLint blendSrcWas, blendDstWas;

      glGetIntegerv(GL_BLEND_SRC, &blendSrcWas);
      glGetIntegerv(GL_BLEND_DST, &blendDstWas);
      GLboolean blendWas = glIsEnabled(GL_BLEND);

      glPushAttrib(GL_STENCIL_BUFFER_BIT);
      glDisable(GL_STENCIL_TEST);

      //GLboolean normalArrayWas = glIsEnabled(GL_NORMAL_ARRAY);
      //glShadeModel(GL_FLAT);

      if (pset->doDepthTest)
      {
            // Depth test
            glPushAttrib(GL_DEPTH_FUNC);
            glPushAttrib(GL_DEPTH_TEST);
            glDepthFunc(GL_LEQUAL);
            glEnable(GL_DEPTH_TEST);
      }

      // Clip planes
      GLdouble clipPlane0[] = { 1, 0, 0, 0 };
      GLdouble clipPlane1[] = { 0, 1, 0, 0 };
      GLdouble clipPlane2[] = { -1, 0, 0, 0 };
      GLdouble clipPlane3[] = { 0, -1, 0, 0 };

      // Save old color values
      GLfloat oldColor[4];

      glGetFloatv(GL_CURRENT_COLOR, oldColor);


      // Determine where we are called from in paint.c's drawWindowTexture
      // to find out how we should change the opacity
      GLint prevActiveTexture = GL_TEXTURE0_ARB;
      Bool saturationFull = TRUE;

      if (w->screen->canDoSaturated && aw->curPaintAttrib.saturation != COLOR)
      {
            saturationFull = FALSE;
            if (w->screen->canDoSlightlySaturated &&
                  aw->curPaintAttrib.saturation > 0)
            {
                  if (aw->curPaintAttrib.opacity < OPAQUE ||
                        aw->curPaintAttrib.brightness != BRIGHT)
                        prevActiveTexture = GL_TEXTURE3_ARB;
                  else
                        prevActiveTexture = GL_TEXTURE2_ARB;
            }
            else
                  prevActiveTexture = GL_TEXTURE1_ARB;
      }

      float brightness = aw->curPaintAttrib.brightness / 65535.0;
      float opacity = aw->curPaintAttrib.opacity / 65535.0;

      float newOpacity = opacity;
      float fadePassedBy;

      if (!blendWas)                      // if translucency is not already turned on in paint.c
      {
            glEnable(GL_BLEND);
      }
      if (saturationFull)
      {
            screenTexEnvMode(w->screen, GL_MODULATE);
      }
      else if (prevActiveTexture == GL_TEXTURE2_ARB)
      {
            w->screen->activeTexture(prevActiveTexture + 1);
            enableTexture(w->screen, aw->curTexture, aw->curTextureFilter);
      }
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

      // if fade-out duration is not specified per polygon
      if (pset->allFadeDuration > -1.0f)
      {
            fadePassedBy = forwardProgress - (1 - pset->allFadeDuration);

            // if "fade out starting point" is passed
            if (fadePassedBy > 1e-5)      // if true, allFadeDuration should be > 0
            {
                  float opacityFac;

                  if (aw->deceleratingMotion)
                        opacityFac = 1 - decelerateProgress2
                                    (fadePassedBy / pset->allFadeDuration);
                  else
                        opacityFac = 1 - fadePassedBy / pset->allFadeDuration;
                  if (opacityFac < 0)
                        opacityFac = 0;
                  if (opacityFac > 1)
                        opacityFac = 1;
                  newOpacity = opacity * opacityFac;
            }
      }

      int pass;
      // 0: draw opaque ones
      // 2: draw transparent ones
      for (pass = 0; pass < 2; pass++)
      {
            int j;

            for (j = pset->firstNondrawnClip; j <= lastClip; j++)
            {
                  Clip4Polygons *c = pset->clips + j;
                  int nFrontVerticesTilThisPoly = 0;
                  int nNewSides = 0;
                  int i;

                  for (i = 0; i < c->nIntersectingPolygons;
                         i++, nFrontVerticesTilThisPoly += nNewSides)
                  {
                        PolygonObject *p =
                                    pset->polygons + c->intersectingPolygons[i];
                        nNewSides = p->nSides;

                        float newOpacityPolygon = newOpacity;

                        // if fade-out duration is specified per polygon
                        if (pset->allFadeDuration == -1.0f)
                        {
                              fadePassedBy = forwardProgress - p->fadeStartTime;
                              // if "fade out starting point" is passed
                              if (fadePassedBy > 1e-5)      // if true, then allFadeDuration > 0
                              {
                                    float opacityFac;

                                    if (aw->deceleratingMotion)
                                          opacityFac = 1 - decelerateProgress2
                                                      (fadePassedBy / p->fadeDuration);
                                    else
                                          opacityFac = 1 - fadePassedBy / p->fadeDuration;
                                    if (opacityFac < 0)
                                          opacityFac = 0;
                                    if (opacityFac > 1)
                                          opacityFac = 1;
                                    newOpacityPolygon = newOpacity * opacityFac;
                              }
                        }

                        if (newOpacityPolygon < 1e-5) // if polygon object is invisible
                              continue;

                        if (pass == 0)
                        {
                              if (newOpacityPolygon < 0.9999)     // if not fully opaque
                                    continue;   // draw only opaque ones in pass 0
                        }
                        else if (newOpacityPolygon > 0.9999)      // if fully opaque
                              continue;   // draw only non-opaque ones in pass 1

                        glPushMatrix();

                        if (pset->correctPerspective)
                        {
                              // Correct perspective appearance by skewing

                              GLfloat skewx = -((p->centerPos.x - s->width / 2) * 1.15);
                              GLfloat skewy = -((p->centerPos.y - s->height / 2) * 1.15);

                              // column-major order
                              GLfloat skewMat[16] =
                                          {1,0,0,0,
                                           0,1,0,0,
                                           skewx,skewy,1,0,
                                           0,0,0,1};
                              glMultMatrixf( skewMat);
                        }

                        // Center
                        glTranslatef(p->centerPos.x, p->centerPos.y, p->centerPos.z);

                        // Scale z first
                        glScalef(1.0f, 1.0f, 1.0f / s->width);

                        // Move by "rotation axis offset"
                        glTranslatef(p->rotAxisOffset.x, p->rotAxisOffset.y,
                                           p->rotAxisOffset.z);

                        // Rotate by desired angle
                        glRotatef(p->rotAngle, p->rotAxis.x, p->rotAxis.y,
                                      p->rotAxis.z);

                        // Move back to center
                        glTranslatef(-p->rotAxisOffset.x, -p->rotAxisOffset.y,
                                           -p->rotAxisOffset.z);

                        // Scale back
                        glScalef(1.0f, 1.0f, s->width);


                        clipPlane0[3] = -(c->boxf.x1 - p->centerPosStart.x);
                        clipPlane1[3] = -(c->boxf.y1 - p->centerPosStart.y);
                        clipPlane2[3] = (c->boxf.x2 - p->centerPosStart.x);
                        clipPlane3[3] = (c->boxf.y2 - p->centerPosStart.y);
                        glClipPlane(GL_CLIP_PLANE0, clipPlane0);
                        glClipPlane(GL_CLIP_PLANE1, clipPlane1);
                        glClipPlane(GL_CLIP_PLANE2, clipPlane2);
                        glClipPlane(GL_CLIP_PLANE3, clipPlane3);

                        int k;

                        for (k = 0; k < 4; k++)
                              glEnable(GL_CLIP_PLANE0 + k);
                        Bool fadeBackAndSides =
                                    pset->backAndSidesFadeDur > 0 &&
                                    forwardProgress <= pset->backAndSidesFadeDur;

                        float newOpacityPolygon2 = newOpacityPolygon;

                        if (fadeBackAndSides)
                        {
                              // Fade-in opacity for back face and sides
                              newOpacityPolygon2 *=
                                          (forwardProgress / pset->backAndSidesFadeDur);
                        }

                        if (saturationFull)
                              glColor4f(brightness, brightness, brightness,
                                            newOpacityPolygon2);
                        else if (prevActiveTexture == GL_TEXTURE1_ARB)
                        {
                              // From paint.c

                              GLfloat constant2[4] =
                                          { 0.5f +
                                    0.5f * RED_SATURATION_WEIGHT * brightness,
                                    0.5f + 0.5f * GREEN_SATURATION_WEIGHT * brightness,
                                    0.5f + 0.5f * BLUE_SATURATION_WEIGHT * brightness,
                                    newOpacityPolygon2
                              };

                              glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
                                             constant2);
                        }
                        else              //if (prevActiveTexture >= GL_TEXTURE2_ARB)
                        {
                              GLfloat constant2[4] = { brightness,
                                    brightness,
                                    brightness,
                                    newOpacityPolygon2
                              };

                              // From paint.c

                              glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
                                             constant2);

                              glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
                                            GL_COMBINE);

                              glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
                              glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
                              glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
                              glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
                              glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

                              glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
                              glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
                              glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT);
                              glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA,
                                            GL_SRC_ALPHA);
                              glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA,
                                            GL_SRC_ALPHA);
                        }

                        //glEnableClientState(GL_NORMAL_ARRAY);
                        //glEnable(GL_NORMALIZE);

                        // Draw back face
                        glVertexPointer(3, GL_FLOAT, 0, p->vertices + 3 * p->nSides);
                        //glNormalPointer(GL_FLOAT, 0,
                        //              p->normals + 3 * p->nSides);
                        glTexCoordPointer(2, GL_FLOAT, 0,
                                                  c->polygonVertexTexCoords +
                                                  2 * (2 * nFrontVerticesTilThisPoly +
                                                         p->nSides));

                        glNormal3f(p->normals[3], p->normals[4], p->normals[5]);
                        glDrawArrays(GL_POLYGON, 0, p->nSides);

                        // Front vertex coords
                        glVertexPointer(3, GL_FLOAT, 0, p->vertices);
                        //glNormalPointer(GL_FLOAT, 0, p->normals);
                        glTexCoordPointer(2, GL_FLOAT, 0,
                                                  c->polygonVertexTexCoords +
                                                  2 * 2 * nFrontVerticesTilThisPoly);

                        //if (pset->thickness > 1e-5)
                        {
                              // TODO: Surface normals for sides
                              // Draw quad strip for sides
                              if (TRUE)
                              {
                                    // Do each quad separately to be able to specify
                                    // different normals
                                    for (k = 0; k < p->nSides; k++)
                                    {
                                          /*
                                             int k2 = (k + 2) * 3;
                                             glNormal3f(p->normals[k2 + 0],
                                             p->normals[k2 + 1],
                                             p->normals[k2 + 2]); */
                                          glNormal3f(p->normals[0],     // front face normal for now
                                                         p->normals[1], p->normals[2]);
                                          glDrawElements(GL_QUADS, 4,
                                                               GL_UNSIGNED_SHORT,
                                                               p->sideIndices + k * 4);
                                    }
                              }
                              else              // no need for separate quad rendering
                                    glDrawElements(GL_QUAD_STRIP, 2 * (p->nSides + 1),
                                                         GL_UNSIGNED_SHORT, p->sideIndices);
                        }
                        if (fadeBackAndSides)
                              // if opacity was changed just above
                        {
                              // Go back to normal opacity for front face

                              if (saturationFull)
                                    glColor4f(brightness, brightness, brightness,
                                                  newOpacityPolygon);
                              else if (prevActiveTexture == GL_TEXTURE1_ARB)
                              {
                                    GLfloat constant[4] =
                                                { 0.5f +
                                          0.5f * RED_SATURATION_WEIGHT * brightness,
                                          0.5f + 0.5f * GREEN_SATURATION_WEIGHT *
                                                      brightness,
                                          0.5f + 0.5f * BLUE_SATURATION_WEIGHT * brightness,
                                          newOpacityPolygon
                                    };

                                    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
                                                   constant);
                              }
                              else
                              {
                                    GLfloat constant[4] = { brightness,
                                          brightness,
                                          brightness,
                                          newOpacityPolygon
                                    };
                                    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
                                                   constant);
                              }
                        }
                        // Draw front face

                        glNormal3f(p->normals[0], p->normals[1], p->normals[2]);
                        glDrawArrays(GL_POLYGON, 0, p->nSides);

                        for (k = 0; k < 4; k++)
                              glDisable(GL_CLIP_PLANE0 + k);

                        glPopMatrix();
                  }
            }
      }
      // Restore
      // -----------------------------------------

      // Restore old color values
      glColor4f(oldColor[0], oldColor[1], oldColor[2], oldColor[3]);
      //screenTexEnvMode(w->screen, GL_REPLACE);

      glPopAttrib();
      if (pset->doDepthTest)
      {
            glPopAttrib();
            glPopAttrib();
      }

      //if (!normalArrayWas)
      //  glDisableClientState(GL_NORMAL_ARRAY);

      // Restore texture stuff
      if (saturationFull)
            screenTexEnvMode(w->screen, GL_REPLACE);
      else if (prevActiveTexture == GL_TEXTURE2_ARB)
      {
            disableTexture(w->screen, aw->curTexture);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            w->screen->activeTexture(prevActiveTexture);
      }
      // Restore blend values
      if (!blendWas)
            glDisable(GL_BLEND);
      glBlendFunc(blendSrcWas, blendDstWas);

      glPopMatrix();

      if (pset->doLighting)         // && !s->lighting)
      {
            glPopAttrib();
            glPopAttrib();
            glPopAttrib();
      }
      if (aw->clipsUpdated)         // set end mark for this group of clips
            pset->lastClipInGroup[aw->nDrawGeometryCalls - 1] = lastClip;

      // Next time, start drawing from next group of clips
      pset->firstNondrawnClip =
                  pset->lastClipInGroup[aw->nDrawGeometryCalls - 1] + 1;
}

static void polygonsPrePaintWindow(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);
      if (aw->polygonSet)
            aw->polygonSet->firstNondrawnClip = 0;
}

static void polygonsPostPaintWindow(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);
      if (aw->clipsUpdated && // clips should be dropped only in the 1st step
            aw->polygonSet && aw->nDrawGeometryCalls == 0)  // if clips not drawn
      {
            // drop these unneeded clips (e.g. ones passed by blurfx)
            aw->polygonSet->nClips = aw->polygonSet->firstNondrawnClip;
      }
}

static void fxExplode3DInit(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);

      if (!tessellateIntoRectangles(w, 20, 14, 15.0f))
            return;

      PolygonSet *pset = aw->polygonSet;
      PolygonObject *p = pset->polygons;
      float sqrt2 = sqrt(2);

      int i;

      for (i = 0; i < pset->nPolygons; i++, p++)
      {
            p->rotAxis.x = RAND_FLOAT();
            p->rotAxis.y = RAND_FLOAT();
            p->rotAxis.z = RAND_FLOAT();

            float screenSizeFactor = (0.8 * DEFAULT_Z_CAMERA * s->width);
            float speed = screenSizeFactor / 10 * (0.2 + RAND_FLOAT());

            float xx = 2 * (p->centerRelPos.x - 0.5);
            float yy = 2 * (p->centerRelPos.y - 0.5);

            float x = speed * (xx + 0.5 * (RAND_FLOAT() - 0.5));
            float y = speed * (yy + 0.5 * (RAND_FLOAT() - 0.5));

            float zbias = 0.3;
            float z =
                        speed * 10 * (zbias +
                                            (sqrt2 - sqrt(xx * xx + yy * yy)) / sqrt2);

            p->finalRelPos.x = x;
            p->finalRelPos.y = y;
            p->finalRelPos.z = z;
            p->finalRotAng = 270;
      }
      pset->allFadeDuration = 0.2f;
      pset->doDepthTest = TRUE;
      pset->doLighting = TRUE;
      pset->correctPerspective = TRUE;
      pset->backAndSidesFadeDur = 0.2f;
}

static void fxLeafSpread3DInit(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);

      if (!tessellateIntoRectangles(w, 20, 14, 15.0f))
            return;

      PolygonSet *pset = aw->polygonSet;
      PolygonObject *p = pset->polygons;
      float fadeDuration = 0.26;
      float life = 0.4;
      float spreadFac = 3.5;
      float randYMax = 0.07;
      float winFacX = WIN_W(w) / 800.0;
      float winFacY = WIN_H(w) / 800.0;
      float winFacZ = (WIN_H(w) + WIN_W(w)) / 2.0 / 800.0;

      int i;

      for (i = 0; i < pset->nPolygons; i++, p++)
      {
            p->rotAxis.x = RAND_FLOAT();
            p->rotAxis.y = RAND_FLOAT();
            p->rotAxis.z = RAND_FLOAT();

            float screenSizeFactor = (0.8 * DEFAULT_Z_CAMERA * s->width);
            float speed = screenSizeFactor / 10 * (0.2 + RAND_FLOAT());

            float xx = 2 * (p->centerRelPos.x - 0.5);
            float yy = 2 * (p->centerRelPos.y - 0.5);

            float x =
                        speed * winFacX * spreadFac * (xx +
                                                                     0.5 * (RAND_FLOAT() - 0.5));
            float y =
                        speed * winFacY * spreadFac * (yy +
                                                                     0.5 * (RAND_FLOAT() - 0.5));
            float z = speed * winFacZ * 7 * ((RAND_FLOAT() - 0.5) / 0.5);

            p->finalRelPos.x = x;
            p->finalRelPos.y = y;
            p->finalRelPos.z = z;

            p->moveStartTime =
                        p->centerRelPos.y * (1 - fadeDuration - randYMax) +
                        randYMax * RAND_FLOAT();
            p->moveDuration = 1;

            p->fadeStartTime = p->moveStartTime + life;
            if (p->fadeStartTime > 1 - fadeDuration)
                  p->fadeStartTime = 1 - fadeDuration;
            p->fadeDuration = fadeDuration;

            p->finalRotAng = 150;
      }
      pset->doDepthTest = TRUE;
      pset->doLighting = TRUE;
      pset->correctPerspective = TRUE;
}

static void fxDomino3DInit(CompScreen * s, CompWindow * w)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(s);

      // Sub effects:
      // 1: Domino
      // 2: Razr
      int subEffectNo = aw->subEffectNo;

      int fallDir;

      if (subEffectNo == 2)
            fallDir = getAnimationDirection
                        (w, as->opt[ANIM_SCREEN_OPTION_RAZR_DIRECTION].value.s, TRUE);
      else
            fallDir = getAnimationDirection
                        (w, as->opt[ANIM_SCREEN_OPTION_DOMINO_DIRECTION].value.s,
                         TRUE);

      int defaultGridSize = 20;
      float minCellSize = 30;
      int gridSizeX;
      int gridSizeY;
      int fallDirGridSize;
      float minDistStartEdge;       // half piece size in [0,1] range
      float gridCellW;
      float gridCellH;
      float cellAspectRatio = 1.25;

      if (subEffectNo == 2)
            cellAspectRatio = 1;

      // Determine sensible domino piece sizes
      if (fallDir == AnimDirectionDown || fallDir == AnimDirectionUp)
      {
            if (minCellSize > BORDER_W(w))
                  minCellSize = BORDER_W(w);
            if (BORDER_W(w) / defaultGridSize < minCellSize)
                  gridSizeX = (int)(BORDER_W(w) / minCellSize);
            else
                  gridSizeX = defaultGridSize;
            gridCellW = BORDER_W(w) / gridSizeX;
            gridSizeY = (int)(BORDER_H(w) / (gridCellW * cellAspectRatio));
            if (gridSizeY == 0)
                  gridSizeY = 1;
            gridCellH = BORDER_H(w) / gridSizeY;
            fallDirGridSize = gridSizeY;
      }
      else
      {
            if (minCellSize > BORDER_H(w))
                  minCellSize = BORDER_H(w);
            if (BORDER_H(w) / defaultGridSize < minCellSize)
                  gridSizeY = (int)(BORDER_H(w) / minCellSize);
            else
                  gridSizeY = defaultGridSize;
            gridCellH = BORDER_H(w) / gridSizeY;
            gridSizeX = (int)(BORDER_W(w) / (gridCellH * cellAspectRatio));
            if (gridSizeX == 0)
                  gridSizeX = 1;
            gridCellW = BORDER_W(w) / gridSizeX;
            fallDirGridSize = gridSizeX;
      }
      minDistStartEdge = (1.0 / fallDirGridSize) / 2;

      float thickness = MIN(gridCellW, gridCellH) / 3.5;

      if (!tessellateIntoRectangles(w, gridSizeX, gridSizeY, thickness))
            return;

      float rotAxisX = 0;
      float rotAxisY = 0;
      Point3d rotAxisOff = { 0, 0, thickness / 2 };
      float posX = 0;                     // position of standing piece
      float posY = 0;
      float posZ = 0;
      int nFallingColumns = gridSizeX;
      float gridCellHalfW = gridCellW / 2;
      float gridCellHalfH = gridCellH / 2;

      switch (fallDir)
      {
      case AnimDirectionDown:
            rotAxisX = -1;
            if (subEffectNo == 2)
                  rotAxisOff.y = -gridCellHalfH;
            else
            {
                  posY = -(gridCellHalfH + thickness);
                  posZ = gridCellHalfH - thickness / 2;
            }
            break;
      case AnimDirectionLeft:
            rotAxisY = -1;
            if (subEffectNo == 2)
                  rotAxisOff.x = gridCellHalfW;
            else
            {
                  posX = gridCellHalfW + thickness;
                  posZ = gridCellHalfW - thickness / 2;
            }
            nFallingColumns = gridSizeY;
            break;
      case AnimDirectionUp:
            rotAxisX = 1;
            if (subEffectNo == 2)
                  rotAxisOff.y = gridCellHalfH;
            else
            {
                  posY = gridCellHalfH + thickness;
                  posZ = gridCellHalfH - thickness / 2;
            }
            break;
      case AnimDirectionRight:
            rotAxisY = 1;
            if (subEffectNo == 2)
                  rotAxisOff.x = -gridCellHalfW;
            else
            {
                  posX = -(gridCellHalfW + thickness);
                  posZ = gridCellHalfW - thickness / 2;
            }
            nFallingColumns = gridSizeY;
            break;
      }

      float fadeDuration;
      float riseDuration;
      float riseTimeRandMax = 0.2;

      if (subEffectNo == 2)
      {
            riseDuration = (1 - riseTimeRandMax) / fallDirGridSize;
            fadeDuration = riseDuration / 2;
      }
      else
      {
            fadeDuration = 0.18;
            riseDuration = 0.2;
      }
      float *riseTimeRandSeed = calloc(1, nFallingColumns * sizeof(float));

      if (!riseTimeRandSeed)
            // TODO: log error here
            return;
      int i;

      for (i = 0; i < nFallingColumns; i++)
            riseTimeRandSeed[i] = RAND_FLOAT();

      PolygonSet *pset = aw->polygonSet;
      PolygonObject *p = pset->polygons;

      for (i = 0; i < pset->nPolygons; i++, p++)
      {
            p->rotAxis.x = rotAxisX;
            p->rotAxis.y = rotAxisY;
            p->rotAxis.z = 0;

            p->finalRelPos.x = posX;
            p->finalRelPos.y = posY;
            p->finalRelPos.z = posZ;

            // dist. from rise-start / fall-end edge in window ([0,1] range)
            float distStartEdge = 0;

            // dist. from edge perpendicular to movement (for move start time)
            // so that same the blocks in same row/col. appear to knock down
            // the next one
            float distPerpEdge = 0;

            switch (fallDir)
            {
            case AnimDirectionUp:
                  distStartEdge = p->centerRelPos.y;
                  distPerpEdge = p->centerRelPos.x;
                  break;
            case AnimDirectionRight:
                  distStartEdge = 1 - p->centerRelPos.x;
                  distPerpEdge = p->centerRelPos.y;
                  break;
            case AnimDirectionDown:
                  distStartEdge = 1 - p->centerRelPos.y;
                  distPerpEdge = p->centerRelPos.x;
                  break;
            case AnimDirectionLeft:
                  distStartEdge = p->centerRelPos.x;
                  distPerpEdge = p->centerRelPos.y;
                  break;
            }

            float riseTimeRand =
                        riseTimeRandSeed[(int)(distPerpEdge * nFallingColumns)] *
                        riseTimeRandMax;

            p->moveDuration = riseDuration;

            float mult = 1;
            if (fallDirGridSize > 1)
                  mult = ((distStartEdge - minDistStartEdge) /
                              (1 - 2 * minDistStartEdge));
            if (subEffectNo == 2)
            {
                  p->moveStartTime =
                              mult *
                              (1 - riseDuration - riseTimeRandMax) + riseTimeRand;
                  p->fadeStartTime = p->moveStartTime + riseDuration / 2;
                  p->finalRotAng = -180;

                  p->rotAxisOffset.x = rotAxisOff.x;
                  p->rotAxisOffset.y = rotAxisOff.y;
                  p->rotAxisOffset.z = rotAxisOff.z;
            }
            else
            {
                  p->moveStartTime =
                              mult *
                              (1 - riseDuration - riseTimeRandMax) +
                              riseTimeRand;
                  p->fadeStartTime =
                              p->moveStartTime + riseDuration - riseTimeRand + 0.03;
                  p->finalRotAng = -90;
            }
            if (p->fadeStartTime > 1 - fadeDuration)
                  p->fadeStartTime = 1 - fadeDuration;
            p->fadeDuration = fadeDuration;
      }
      free(riseTimeRandSeed);
      pset->doDepthTest = TRUE;
      pset->doLighting = TRUE;
      pset->correctPerspective = TRUE;
}

/*
static void fxTornado3DInit(CompScreen * s, CompWindow * w)
{
      //ANIM_WINDOW(w);

      if (!tessellateIntoRectangles(w, 20, 14, 15.0f))
            return;
}
*/

static void fxGlideInit(CompScreen * s, CompWindow * w)
{
      ANIM_SCREEN(s);
      ANIM_WINDOW(w);

      float finalDistFac;
      float finalRotAng;
      float thickness;

      fxGlideGetParams(as, aw, &finalDistFac, &finalRotAng, &thickness);

      if (thickness < 1e-5) // if thicknes is 0, we'll make the effect 2D
      {
            // store window opacity
            aw->storedOpacity = w->paint.opacity;
            aw->timestep = (s->slowAnimations ? 2 :   // For smooth slow-mo
                                    as->opt[ANIM_SCREEN_OPTION_TIME_STEP].value.i);

            return; // we're done with 2D initialization
      }

      // for 3D glide effect
      // ------------------------

      PolygonSet *pset = aw->polygonSet;

      pset->includeShadows = (thickness < 1e-5);

      if (!tessellateIntoRectangles(w, 1, 1, thickness))
            return;

      PolygonObject *p = pset->polygons;

      int i;

      for (i = 0; i < pset->nPolygons; i++, p++)
      {
            p->rotAxis.x = 1;
            p->rotAxis.y = 0;
            p->rotAxis.z = 0;

            p->finalRelPos.x = 0;
            p->finalRelPos.y = 0;
            p->finalRelPos.z = finalDistFac * 0.8 * DEFAULT_Z_CAMERA * s->width;

            p->finalRotAng = finalRotAng;
      }
      pset->allFadeDuration = 1.0f;
      pset->backAndSidesFadeDur = 0.2f;
      pset->doLighting = TRUE;
      pset->correctPerspective = TRUE;
}

// Computes polygon's new position and orientation
// with linear movement
static void
polygonsLinearAnimStepPolygon(CompWindow * w,
                                            PolygonObject * p, float forwardProgress)
{
      float moveProgress = forwardProgress - p->moveStartTime;

      if (p->moveDuration > 0)
            moveProgress /= p->moveDuration;
      if (moveProgress < 0)
            moveProgress = 0;
      else if (moveProgress > 1)
            moveProgress = 1;

      p->centerPos.x = moveProgress * p->finalRelPos.x + p->centerPosStart.x;
      p->centerPos.y = moveProgress * p->finalRelPos.y + p->centerPosStart.y;
      p->centerPos.z = 1.0f / w->screen->width *
                  moveProgress * p->finalRelPos.z + p->centerPosStart.z;

      p->rotAngle = moveProgress * p->finalRotAng + p->rotAngleStart;
}

// Similar to polygonsLinearAnimStepPolygon,
// but slightly ac/decelerates movement
static void
polygonsDeceleratingAnimStepPolygon(CompWindow * w,
                                                      PolygonObject * p, float forwardProgress)
{
      float moveProgress = forwardProgress - p->moveStartTime;

      if (p->moveDuration > 0)
            moveProgress /= p->moveDuration;
      if (moveProgress < 0)
            moveProgress = 0;
      else if (moveProgress > 1)
            moveProgress = 1;

      moveProgress = decelerateProgress2(moveProgress);

      p->centerPos.x = moveProgress * p->finalRelPos.x + p->centerPosStart.x;
      p->centerPos.y = moveProgress * p->finalRelPos.y + p->centerPosStart.y;
      p->centerPos.z = 1.0f / w->screen->width *
                  moveProgress * p->finalRelPos.z + p->centerPosStart.z;

      p->rotAngle = moveProgress * p->finalRotAng + p->rotAngleStart;
}

// =====================  End of Effects  =========================


AnimEffectProperties animEffectProperties[AnimEffectNum] = {
      // AnimEffectNone
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
      ,
      // AnimEffectRandom
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
      ,
      // AnimEffectBeamUp
      {fxBeamupUpdateWindowAttrib, 0, drawParticleSystems, fxBeamUpModelStep,
       fxBeamUpInit, 0, 0, 0, 1, 0, 0}
      ,
      // AnimEffectBurn
      {0, 0, drawParticleSystems, fxBurnModelStep, fxBurnInit, 0, 0, 0, 1, 0, 0}
      ,
      // AnimEffectCurvedFold
      {0, 0, 0, fxCurvedFoldModelStep, 0, fxMagicLamp1InitGrid, 0, 0, 0, 0, 0}
      ,
      // AnimEffectDomino3D
      {0, polygonsPrePaintWindow, polygonsPostPaintWindow, polygonsAnimStep,
       fxDomino3DInit, 0, polygonsStoreClips, polygonsDrawCustomGeometry, 0,
       polygonsLinearAnimStepPolygon, 1}
      ,
      // AnimEffectDream
      {fxDreamUpdateWindowAttrib, 0, 0, fxDreamModelStep, fxDreamInit,
       fxMagicLamp1InitGrid, 0, 0, 0, 0, 0}
      ,
      // AnimEffectExplode3D
      {0, polygonsPrePaintWindow, polygonsPostPaintWindow, polygonsAnimStep,
       fxExplode3DInit, 0, polygonsStoreClips, polygonsDrawCustomGeometry, 0,
       polygonsLinearAnimStepPolygon, 0}
      ,
      // AnimEffectFade
      {fxFadeUpdateWindowAttrib, 0, 0, fxFadeModelStep, fxFadeInit, 0, 0, 0, 0,
       0, 0}
      ,
      // AnimEffectGlide3D1
      {fxGlideUpdateWindowAttrib, polygonsPrePaintWindow,
       polygonsPostPaintWindow, fxGlideAnimStep,
       fxGlideInit, 0, polygonsStoreClips, polygonsDrawCustomGeometry, 0,
       polygonsDeceleratingAnimStepPolygon, 1}
      ,
      // AnimEffectGlide3D2
      {fxGlideUpdateWindowAttrib, polygonsPrePaintWindow,
       polygonsPostPaintWindow, fxGlideAnimStep,
       fxGlideInit, 0, polygonsStoreClips, polygonsDrawCustomGeometry, 0,
       polygonsDeceleratingAnimStepPolygon, 2}
      ,
      // AnimEffectHorizontalFolds
      {0, 0, 0, fxHorizontalFoldsModelStep, 0, fxHorizontalFoldsInitGrid,
       0, 0, 0, 0, 0}
      ,
      // AnimEffectLeafSpread3D
      {0, polygonsPrePaintWindow, polygonsPostPaintWindow, polygonsAnimStep,
       fxLeafSpread3DInit, 0, polygonsStoreClips, polygonsDrawCustomGeometry, 0,
       polygonsLinearAnimStepPolygon, 0}
      ,
      // AnimEffectMagicLamp1
      {0, 0, 0, fxMagicLampModelStep, fxMagicLampInit, fxMagicLamp1InitGrid,
       0, 0, 0, 0, 0}
      ,
      // AnimEffectMagicLamp2
      {0, 0, 0, fxMagicLampModelStep, fxMagicLampInit, fxMagicLamp2InitGrid,
       0, 0, 0, 0, 0}
      ,
      // AnimEffectMagicLamp3
      {0, 0, 0, fxMagicLampModelStep, fxMagicLampInit,
       fxMagicLamp3InitGrid, 0}
      ,
      // AnimEffectRazr3D
      {0, polygonsPrePaintWindow, polygonsPostPaintWindow, polygonsAnimStep,
       fxDomino3DInit, 0, polygonsStoreClips, polygonsDrawCustomGeometry, 0,
       polygonsLinearAnimStepPolygon, 2}
      ,
      // AnimEffectRollUp
      {0, 0, 0, fxRollUpModelStep, 0, fxRollUpInitGrid, 0, 0, 1, 0, 0}
      ,
      // AnimEffectSidekick
      {fxZoomUpdateWindowAttrib, 0, 0, fxZoomModelStep, fxSidekickInit,
       0, 0, 0, 1, 0, 0}
      ,
      // AnimEffectWave
      {0, 0, 0, fxWaveModelStep, 0, fxMagicLamp1InitGrid, 0, 0, 0, 0, 0}
      ,
      // AnimEffectZoom
      {fxZoomUpdateWindowAttrib, 0, 0, fxZoomModelStep, fxZoomInit, 0, 0, 0, 1,
       0, 0}
};


static Bool getMousePointerXY(CompScreen * s, short *x, short *y)
{
      Window w1, w2;
      int xp, yp, xj, yj;
      unsigned int m;

      if (XQueryPointer
            (s->display->display, s->root, &w1, &w2, &xj, &yj, &xp, &yp, &m))
      {
            *x = xp;
            *y = yp;
            return TRUE;
      }
      return FALSE;
}

static int animGetWindowState(CompWindow * w)
{
      Atom actual;
      int result, format;
      unsigned long n, left;
      unsigned char *data;

      result = XGetWindowProperty(w->screen->display->display, w->id,
                                                w->screen->display->wmStateAtom, 0L,
                                                1L, FALSE,
                                                w->screen->display->wmStateAtom,
                                                &actual, &format, &n, &left, &data);

      if (result == Success && n && data)
      {
            int state;

            memcpy(&state, data, sizeof(int));
            XFree((void *)data);

            return state;
      }

      return WithdrawnState;
}

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

      ANIM_SCREEN(screen);

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

      switch (index)
      {
      case ANIM_SCREEN_OPTION_SIDEKICK_NUM_ROTATIONS:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_ZOOM_FROM_CENTER:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < 4; i++)
                  {
                        if (strcmp(o->value.s, zoomFromCenterOption[i]) == 0)
                        {
                              as->zoomFC = i;
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_TIME_STEP:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_TIME_STEP_INTENSE:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_DISABLE_PP_FX:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < 3; i++)
                  {
                        if (strcmp(o->value.s, ppDisablingName[i]) == 0)
                        {
                              as->ppDisabling = i;
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP1_GRID_RES:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP1_MAX_WAVES:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP1_WAVE_AMP_MIN:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP1_WAVE_AMP_MAX:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP1_CREATE_START_WIDTH:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP2_GRID_RES:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP2_MAX_WAVES:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP2_WAVE_AMP_MIN:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP2_WAVE_AMP_MAX:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP2_CREATE_START_WIDTH:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP3_GRID_RES:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP3_MAX_WAVES:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP3_WAVE_AMP_MIN:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP3_WAVE_AMP_MAX:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MAGIC_LAMP3_CREATE_START_WIDTH:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FOCUS_WAVE_WIDTH:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FOCUS_WAVE_AMP:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CURVED_FOLD_AMP:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_AMP:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_NUM_FOLDS:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_ROLLUP_FIXED_INTERIOR:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;
      case ANIM_SCREEN_OPTION_ALL_RANDOM:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;
      case ANIM_SCREEN_OPTION_MINIMIZE_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_MINIMIZE_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, minimizeEffectName[i]) == 0)
                        {
                              as->minimizeEffect = minimizeEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_UNMINIMIZE_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_MINIMIZE_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, minimizeEffectName[i]) == 0)
                        {
                              as->unminimizeEffect = minimizeEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_CLOSE1_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_CLOSE_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, closeEffectName[i]) == 0)
                        {
                              as->close1Effect = closeEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_CLOSE2_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_CLOSE_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, closeEffectName[i]) == 0)
                        {
                              as->close2Effect = closeEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_CREATE1_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_CLOSE_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, closeEffectName[i]) == 0)
                        {
                              as->create1Effect = closeEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_CREATE2_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_CLOSE_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, closeEffectName[i]) == 0)
                        {
                              as->create2Effect = closeEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_FOCUS_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_FOCUS_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, focusEffectName[i]) == 0)
                        {
                              as->focusEffect = focusEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_SHADE_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_SHADE_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, shadeEffectName[i]) == 0)
                        {
                              as->shadeEffect = shadeEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_UNSHADE_EFFECT:
            if (compSetStringOption(o, value))
            {
                  int i;

                  for (i = 0; i < NUM_SHADE_EFFECT; i++)
                  {
                        if (strcmp(o->value.s, shadeEffectName[i]) == 0)
                        {
                              as->unshadeEffect = shadeEffectType[i];
                              return TRUE;
                        }
                  }
            }
            break;
      case ANIM_SCREEN_OPTION_MINIMIZE_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->minimizeWMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_UNMINIMIZE_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->unminimizeWMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CLOSE1_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->close1WMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CLOSE2_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->close2WMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CREATE1_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->create1WMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CREATE2_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->create2WMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FOCUS_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->focusWMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_SHADE_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->shadeWMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_UNSHADE_WINDOW_TYPE:
            if (compSetOptionList(o, value))
            {
                  as->unshadeWMask = compWindowTypeMaskFromStringList(&o->value);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MINIMIZE_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_UNMINIMIZE_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CLOSE1_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CLOSE2_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CREATE1_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CREATE2_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FOCUS_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_SHADE_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_UNSHADE_DURATION:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FIRE_PARTICLES:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FIRE_SIZE:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FIRE_SLOWDOWN:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FIRE_LIFE:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FIRE_COLOR:
            if (compSetColorOption(o, value))
            {
                  return TRUE;
            }
      case ANIM_SCREEN_OPTION_FIRE_DIRECTION:
            if (compSetStringOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_FIRE_CONSTANT_SPEED:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;
      case ANIM_SCREEN_OPTION_FIRE_SMOKE:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;
      case ANIM_SCREEN_OPTION_FIRE_MYSTICAL:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;
      case ANIM_SCREEN_OPTION_BEAMUP_SIZE:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_BEAMUP_SPACING:
            if (compSetIntOption(o, value))
            {
                  return TRUE;
            }
      case ANIM_SCREEN_OPTION_BEAMUP_COLOR:
            if (compSetColorOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_BEAMUP_SLOWDOWN:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_BEAMUP_LIFE:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_DOMINO_DIRECTION:
            if (compSetStringOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_RAZR_DIRECTION:
            if (compSetStringOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_GLIDE1_AWAY_POS:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_GLIDE1_AWAY_ANGLE:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_GLIDE1_THICKNESS:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_GLIDE2_AWAY_POS:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_GLIDE2_AWAY_ANGLE:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_GLIDE2_THICKNESS:
            if (compSetFloatOption(o, value))
            {
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_MINIMIZE_RANDOM_EFFECTS:
            if (compSetOptionList(o, value))
            {
                  STORE_RANDOM_EFFECT_LIST
                              (ANIM_SCREEN_OPTION_MINIMIZE_RANDOM_EFFECTS,
                               NUM_MINIMIZE_EFFECT,
                               ANIM_MINIMIZE_DEFAULT,
                               nMinimizeRandomEffects,
                               minimizeRandomEffects,
                               minimizeEffectName, minimizeEffectType);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_UNMINIMIZE_RANDOM_EFFECTS:
            if (compSetOptionList(o, value))
            {
                  STORE_RANDOM_EFFECT_LIST
                              (ANIM_SCREEN_OPTION_UNMINIMIZE_RANDOM_EFFECTS,
                               NUM_MINIMIZE_EFFECT,
                               ANIM_UNMINIMIZE_DEFAULT,
                               nUnminimizeRandomEffects,
                               unminimizeRandomEffects,
                               minimizeEffectName, minimizeEffectType);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CLOSE1_RANDOM_EFFECTS:
            if (compSetOptionList(o, value))
            {
                  STORE_RANDOM_EFFECT_LIST
                              (ANIM_SCREEN_OPTION_CLOSE1_RANDOM_EFFECTS,
                               NUM_CLOSE_EFFECT,
                               ANIM_CLOSE1_DEFAULT,
                               nClose1RandomEffects,
                               close1RandomEffects, closeEffectName, closeEffectType);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CLOSE2_RANDOM_EFFECTS:
            if (compSetOptionList(o, value))
            {
                  STORE_RANDOM_EFFECT_LIST
                              (ANIM_SCREEN_OPTION_CLOSE2_RANDOM_EFFECTS,
                               NUM_CLOSE_EFFECT,
                               ANIM_CLOSE2_DEFAULT,
                               nClose2RandomEffects,
                               close2RandomEffects, closeEffectName, closeEffectType);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CREATE1_RANDOM_EFFECTS:
            if (compSetOptionList(o, value))
            {
                  STORE_RANDOM_EFFECT_LIST
                              (ANIM_SCREEN_OPTION_CREATE1_RANDOM_EFFECTS,
                               NUM_CLOSE_EFFECT,
                               ANIM_CREATE1_DEFAULT,
                               nCreate1RandomEffects,
                               create1RandomEffects, closeEffectName, closeEffectType);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_CREATE2_RANDOM_EFFECTS:
            if (compSetOptionList(o, value))
            {
                  STORE_RANDOM_EFFECT_LIST
                              (ANIM_SCREEN_OPTION_CREATE2_RANDOM_EFFECTS,
                               NUM_CLOSE_EFFECT,
                               ANIM_CREATE2_DEFAULT,
                               nCreate2RandomEffects,
                               create2RandomEffects, closeEffectName, closeEffectType);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_SHADE_RANDOM_EFFECTS:
            if (compSetOptionList(o, value))
            {
                  STORE_RANDOM_EFFECT_LIST
                              (ANIM_SCREEN_OPTION_SHADE_RANDOM_EFFECTS,
                               NUM_SHADE_EFFECT,
                               ANIM_SHADE_DEFAULT,
                               nShadeRandomEffects,
                               shadeRandomEffects, shadeEffectName, shadeEffectType);
                  return TRUE;
            }
            break;
      case ANIM_SCREEN_OPTION_UNSHADE_RANDOM_EFFECTS:
            if (compSetOptionList(o, value))
            {
                  STORE_RANDOM_EFFECT_LIST
                              (ANIM_SCREEN_OPTION_UNSHADE_RANDOM_EFFECTS,
                               NUM_SHADE_EFFECT,
                               ANIM_UNSHADE_DEFAULT,
                               nUnshadeRandomEffects,
                               unshadeRandomEffects, shadeEffectName, shadeEffectType);
                  return TRUE;
            }
            break;
      default:
            break;
      }

      return FALSE;
}

static void animScreenInitOptions(AnimScreen * as)
{
      CompOption *o;
      int i;

      o = &as->opt[ANIM_SCREEN_OPTION_ALL_RANDOM];
      o->name = "all_random";
      o->group = N_("Misc. Settings");
      o->subGroup = N_("");
      o->shortDesc = N_("Random Animations For All Events");
      o->advanced = False;
      o->longDesc =
                  N_
                  ("All effects are chosen randomly, ignoring the selected effect. If None is selected for an event, that event won't be animated.");
      o->displayHints = "";
      o->type = CompOptionTypeBool;
      o->value.b = ANIM_ALL_RANDOM_DEFAULT;

      o = &as->opt[ANIM_SCREEN_OPTION_ROLLUP_FIXED_INTERIOR];
      o->name = "rollup_fixed_interior";
      o->group = N_("(Un)Shade");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Rollup Fixed Interior");
      o->longDesc = N_("Fixed window interior during the Rollup animation.");
      o->displayHints = "";
      o->type = CompOptionTypeBool;
      o->value.b = ANIM_ROLLUP_FIXED_INTERIOR_DEFAULT;

      o = &as->opt[ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_NUM_FOLDS];
      o->name = "horizontal_folds_num_folds";
      o->group = N_("Horizontal Folds");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Number of Horizontal Folds");
      o->longDesc =
                  N_
                  ("The number of horizontal folds that occur in the Horizontal Fold animation.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_HORIZONTAL_FOLDS_NUM_FOLDS_DEFAULT;
      o->rest.i.min = ANIM_HORIZONTAL_FOLDS_NUM_FOLDS_MIN;
      o->rest.i.max = ANIM_HORIZONTAL_FOLDS_NUM_FOLDS_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_HORIZONTAL_FOLDS_AMP];
      o->name = "horizontal_folds_amp";
      o->group = N_("Horizontal Folds");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Horizontal Fold Amplitude");
      o->longDesc =
                  N_
                  ("Amplitude (size of the waves in the fold) of the Horizontal Folds relative to the window width. Negative values fold outward.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_HORIZONTAL_FOLDS_AMP_DEFAULT;
      o->rest.f.min = ANIM_HORIZONTAL_FOLDS_AMP_MIN;
      o->rest.f.max = ANIM_HORIZONTAL_FOLDS_AMP_MAX;
      o->rest.f.precision = ANIM_HORIZONTAL_FOLDS_AMP_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_CURVED_FOLD_AMP];
      o->name = "curved_fold_amp";
      o->group = N_("Curved Folds");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Curved Fold Amplitude");
      o->longDesc =
                  N_
                  ("Amplitude (size of the waves in the fold) of the curved fold relative to window width. Negative values fold outward.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_CURVED_FOLD_AMP_DEFAULT;
      o->rest.f.min = ANIM_CURVED_FOLD_AMP_MIN;
      o->rest.f.max = ANIM_CURVED_FOLD_AMP_MAX;
      o->rest.f.precision = ANIM_CURVED_FOLD_AMP_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_ZOOM_FROM_CENTER];
      o->name = "zoom_from_center";
      o->group = N_("Misc. Settings");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Zoom from Center");
      o->longDesc =
                  N_
                  ("Zoom from center when playing the Zoom and Sidekick animations.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(zoomFromCenterOption[ANIM_ZOOM_FROM_CENTER_DEFAULT]);
      o->rest.s.string = zoomFromCenterOption;
      o->rest.s.nString = 4;

      o = &as->opt[ANIM_SCREEN_OPTION_TIME_STEP];
      o->name = "time_step";
      o->group = N_("Misc. Settings");
      o->subGroup = N_("Advanced");
      o->advanced = False;
      o->shortDesc = N_("Animation Time Step");
      o->longDesc =
                  N_
                  ("The amount of time in milliseconds between each render of the animation. The higher the number, the jerkier the movements become.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_TIME_STEP_DEFAULT;
      o->rest.i.min = ANIM_TIME_STEP_MIN;
      o->rest.i.max = ANIM_TIME_STEP_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_TIME_STEP_INTENSE];
      o->name = "time_step_intense";
      o->group = N_("Misc. Settings");
      o->subGroup = N_("Advanced");
      o->advanced = False;
      o->shortDesc = N_("Animation Time Step For Intense Effects");
      o->longDesc =
                  N_
                  ("The amount of time in milliseconds between each render of the intense animation (Ex. Burn, Beam). The higher the number, the jerkier the movements become.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_TIME_STEP_INTENSE_DEFAULT;
      o->rest.i.min = ANIM_TIME_STEP_INTENSE_MIN;
      o->rest.i.max = ANIM_TIME_STEP_INTENSE_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_DISABLE_PP_FX];
      o->name = "disable_pp_fx";
      o->group = N_("Misc. Settings");
      o->subGroup = N_("Advanced");
      o->advanced = False;
      o->shortDesc = N_("Disable Post-processing Effects During Animation");
      o->longDesc = N_("Disables Blur effects during animations.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(ppDisablingName[ANIM_DISABLE_PP_FX_DEFAULT]);
      o->rest.s.string = ppDisablingName;
      o->rest.s.nString = 3;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_GRID_RES];
      o->name = "magic_lamp1_grid_res";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #1");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #1 Grid Y Resolution");
      o->longDesc =
                  N_
                  ("Vertex grid resolution for Magic Lamp #1 (Y dimension only). This is the number of points used to define the curves. The higher the number, the smoother the curves. However there will be a loss of performance (CPU usage increases).");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP1_GRID_RES_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP1_GRID_RES_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP1_GRID_RES_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_MAX_WAVES];
      o->name = "magic_lamp1_max_waves";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #1");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #1 Max Waves");
      o->longDesc = N_("The maximum number of waves for Magic Lamp #1.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP1_MAX_WAVES_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP1_MAX_WAVES_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP1_MAX_WAVES_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_WAVE_AMP_MIN];
      o->name = "magic_lamp1_wave_amp_min";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #1");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #1 Wave Min Amplitude");
      o->longDesc =
                  N_
                  ("The minimum amplitude (size of the waves) Magic Lamp #1 will have.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_MAGIC_LAMP1_WAVE_AMP_MIN_DEFAULT;
      o->rest.f.min = ANIM_MAGIC_LAMP1_WAVE_AMP_MIN_MIN;
      o->rest.f.max = ANIM_MAGIC_LAMP1_WAVE_AMP_MIN_MAX;
      o->rest.f.precision = ANIM_MAGIC_LAMP1_WAVE_AMP_MIN_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_WAVE_AMP_MAX];
      o->name = "magic_lamp1_wave_amp_max";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #1");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #1 Wave Max Amplitude");
      o->longDesc =
                  N_
                  ("The maxmimum amplitude (size of the waves) Magic Lamp #1 will have.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_MAGIC_LAMP1_WAVE_AMP_MAX_DEFAULT;
      o->rest.f.min = ANIM_MAGIC_LAMP1_WAVE_AMP_MAX_MIN;
      o->rest.f.max = ANIM_MAGIC_LAMP1_WAVE_AMP_MAX_MAX;
      o->rest.f.precision = ANIM_MAGIC_LAMP1_WAVE_AMP_MAX_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_CREATE_START_WIDTH];
      o->name = "magic_lamp1_create_start_width";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #1");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #1 Create Start Width");
      o->longDesc =
                  N_
                  ("Starting width of create effect and ending width of close effect for Magic Lamp #1.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP1_CREATE_START_WIDTH_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP1_CREATE_START_WIDTH_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP1_CREATE_START_WIDTH_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_GRID_RES];
      o->name = "magic_lamp2_grid_res";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #2");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #2 Grid Y Resolution");
      o->longDesc =
                  N_
                  ("Vertex grid resolution for Magic Lamp #2 (Y dimension only). This is the number of points used to define the curves. The higher the number, the smoother the curves. However there will be a loss of performance (CPU usage increases).");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP2_GRID_RES_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP2_GRID_RES_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP2_GRID_RES_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_MAX_WAVES];
      o->name = "magic_lamp2_max_waves";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #2");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #2 Max Waves");
      o->longDesc = N_("The maximum number of waves for Magic Lamp #2.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP2_MAX_WAVES_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP2_MAX_WAVES_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP2_MAX_WAVES_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_WAVE_AMP_MIN];
      o->name = "magic_lamp2_wave_amp_min";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #2");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #2 Wave Min Amplitude");
      o->longDesc =
                  N_
                  ("The minimum amplitude (size of the waves) Magic Lamp #2 will have.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_MAGIC_LAMP2_WAVE_AMP_MIN_DEFAULT;
      o->rest.f.min = ANIM_MAGIC_LAMP2_WAVE_AMP_MIN_MIN;
      o->rest.f.max = ANIM_MAGIC_LAMP2_WAVE_AMP_MIN_MAX;
      o->rest.f.precision = ANIM_MAGIC_LAMP2_WAVE_AMP_MIN_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_WAVE_AMP_MAX];
      o->name = "magic_lamp2_wave_amp_max";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #2");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #2 Wave Max Amplitude");
      o->longDesc =
                  N_
                  ("The maxmimum amplitude (size of the waves) Magic Lamp #2 will have.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_MAGIC_LAMP2_WAVE_AMP_MAX_DEFAULT;
      o->rest.f.min = ANIM_MAGIC_LAMP2_WAVE_AMP_MAX_MIN;
      o->rest.f.max = ANIM_MAGIC_LAMP2_WAVE_AMP_MAX_MAX;
      o->rest.f.precision = ANIM_MAGIC_LAMP2_WAVE_AMP_MAX_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP2_CREATE_START_WIDTH];
      o->name = "magic_lamp2_create_start_width";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #2");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #2 Create Start Width");
      o->longDesc =
                  N_
                  ("Starting width of create effect and ending width of close effect for Magic Lamp #2.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP2_CREATE_START_WIDTH_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP2_CREATE_START_WIDTH_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP2_CREATE_START_WIDTH_MAX;


      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_GRID_RES];
      o->name = "magic_lamp3_grid_res";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #3");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #3 Grid Y Resolution");
      o->longDesc =
                  N_
                  ("Vertex grid resolution for Magic Lamp #3 (Y dimension only). This is the number of points used to define the curves. The higher the number, the smoother the curves. However there will be a loss of performance (CPU usage increases).");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP3_GRID_RES_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP3_GRID_RES_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP3_GRID_RES_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_MAX_WAVES];
      o->name = "magic_lamp3_max_waves";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #3");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #3 Max Waves");
      o->longDesc = N_("The maximum number of waves for Magic Lamp #3.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP3_MAX_WAVES_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP3_MAX_WAVES_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP3_MAX_WAVES_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_WAVE_AMP_MIN];
      o->name = "magic_lamp3_wave_amp_min";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #3");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #3 Wave Min Amplitude");
      o->longDesc =
                  N_
                  ("The minimum amplitude (size of the waves) Magic Lamp #3 will have.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_MAGIC_LAMP3_WAVE_AMP_MIN_DEFAULT;
      o->rest.f.min = ANIM_MAGIC_LAMP3_WAVE_AMP_MIN_MIN;
      o->rest.f.max = ANIM_MAGIC_LAMP3_WAVE_AMP_MIN_MAX;
      o->rest.f.precision = ANIM_MAGIC_LAMP3_WAVE_AMP_MIN_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_WAVE_AMP_MAX];
      o->name = "magic_lamp3_wave_amp_max";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #3");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #3 Wave Max Amplitude");
      o->longDesc =
                  N_
                  ("The maxmimum amplitude (size of the waves) Magic Lamp #3 will have.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_MAGIC_LAMP3_WAVE_AMP_MAX_DEFAULT;
      o->rest.f.min = ANIM_MAGIC_LAMP3_WAVE_AMP_MAX_MIN;
      o->rest.f.max = ANIM_MAGIC_LAMP3_WAVE_AMP_MAX_MAX;
      o->rest.f.precision = ANIM_MAGIC_LAMP3_WAVE_AMP_MAX_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_MAGIC_LAMP3_CREATE_START_WIDTH];
      o->name = "magic_lamp3_create_start_width";
      o->group = N_("Magic Lamp");
      o->subGroup = N_("Magic Lamp #3");
      o->advanced = False;
      o->shortDesc = N_("Magic Lamp #3 Create Start Width");
      o->longDesc =
                  N_
                  ("Starting width of create effect and ending width of close effect for Magic Lamp #3.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_MAGIC_LAMP3_CREATE_START_WIDTH_DEFAULT;
      o->rest.i.min = ANIM_MAGIC_LAMP3_CREATE_START_WIDTH_MIN;
      o->rest.i.max = ANIM_MAGIC_LAMP3_CREATE_START_WIDTH_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_FOCUS_WAVE_WIDTH];
      o->name = "focus_wave_width";
      o->group = N_("Focus Wave");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Focus Wave Width");
      o->longDesc =
                  N_("The width of the focus wave relative to the window height.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_FOCUS_WAVE_WIDTH_DEFAULT;
      o->rest.f.min = ANIM_FOCUS_WAVE_WIDTH_MIN;
      o->rest.f.max = ANIM_FOCUS_WAVE_WIDTH_MAX;
      o->rest.f.precision = ANIM_FOCUS_WAVE_WIDTH_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_FOCUS_WAVE_AMP];
      o->name = "focus_wave_amp";
      o->group = N_("Focus Wave");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Focus Wave Amplitude");
      o->longDesc =
                  N_
                  ("The wave amplitude (size of the waves) relative to the window height.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_FOCUS_WAVE_AMP_DEFAULT;
      o->rest.f.min = ANIM_FOCUS_WAVE_AMP_MIN;
      o->rest.f.max = ANIM_FOCUS_WAVE_AMP_MAX;
      o->rest.f.precision = ANIM_FOCUS_WAVE_AMP_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_SIDEKICK_NUM_ROTATIONS];
      o->name = "sidekick_num_rotations";
      o->group = N_("Sidekick");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Sidekick Number of Rotations");
      o->longDesc =
                  N_
                  ("The number of rotations plus or minus 10% (for randomness) that Sidekick has.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_SIDEKICK_NUM_ROTATIONS_DEFAULT;
      o->rest.f.min = ANIM_SIDEKICK_NUM_ROTATIONS_MIN;
      o->rest.f.max = ANIM_SIDEKICK_NUM_ROTATIONS_MAX;
      o->rest.f.precision = ANIM_SIDEKICK_NUM_ROTATIONS_PRECISION;

      // Minimize

      o = &as->opt[ANIM_SCREEN_OPTION_MINIMIZE_EFFECT];
      o->name = "minimize_effect";
      o->group = N_("(Un)Minimize");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Minimize Animation");
      o->longDesc = N_("The animation shown when minimizing a window.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_MINIMIZE_DEFAULT]);
      o->rest.s.string = minimizeEffectName;
      o->rest.s.nString = NUM_MINIMIZE_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_MINIMIZE_WINDOW_TYPE];
      o->name = "minimize_window_types";
      o->group = N_("(Un)Minimize");
      o->subGroup = N_("Minimize");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc = N_("The window types that will be animated.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_MINIMIZE_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_MINIMIZE_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_MINIMIZE_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(minimizeDefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->minimizeWMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_MINIMIZE_DURATION];
      o->name = "minimize_duration";
      o->group = N_("(Un)Minimize");
      o->subGroup = N_("Minimize");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc =
                  N_
                  ("The number of seconds that the Minimize animation will last.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_MINIMIZE_DURATION_DEFAULT;
      o->rest.f.min = ANIM_MINIMIZE_DURATION_MIN;
      o->rest.f.max = ANIM_MINIMIZE_DURATION_MAX;
      o->rest.f.precision = ANIM_MINIMIZE_DURATION_PRECISION;

      INIT_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_MINIMIZE_RANDOM_EFFECTS,
                   "minimize", "(Un)Minimize", "Minimize", NUM_MINIMIZE_EFFECT,
                   minimizeEffectName);
      STORE_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_MINIMIZE_RANDOM_EFFECTS,
                   NUM_MINIMIZE_EFFECT,
                   ANIM_MINIMIZE_DEFAULT,
                   nMinimizeRandomEffects,
                   minimizeRandomEffects, minimizeEffectName, minimizeEffectType);

      // Unminimize

      o = &as->opt[ANIM_SCREEN_OPTION_UNMINIMIZE_EFFECT];
      o->name = "unminimize_effect";
      o->group = N_("(Un)Minimize");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Unminimize Animation");
      o->longDesc = N_("The animation shown when unminimizing a window.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_UNMINIMIZE_DEFAULT]);
      o->rest.s.string = minimizeEffectName;
      o->rest.s.nString = NUM_MINIMIZE_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_UNMINIMIZE_WINDOW_TYPE];
      o->name = "unminimize_window_types";
      o->group = N_("(Un)Minimize");
      o->subGroup = N_("Unminimize");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc = N_("The window types that will be animated.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_MINIMIZE_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_MINIMIZE_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_MINIMIZE_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(minimizeDefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->unminimizeWMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_UNMINIMIZE_DURATION];
      o->name = "unminimize_duration";
      o->group = N_("(Un)Minimize");
      o->subGroup = N_("Unminimize");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc =
                  N_
                  ("The number of seconds that the Unminimize animation will last.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_UNMINIMIZE_DURATION_DEFAULT;
      o->rest.f.min = ANIM_UNMINIMIZE_DURATION_MIN;
      o->rest.f.max = ANIM_UNMINIMIZE_DURATION_MAX;
      o->rest.f.precision = ANIM_UNMINIMIZE_DURATION_PRECISION;

      INIT_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_UNMINIMIZE_RANDOM_EFFECTS,
                   "unminimize", "(Un)Minimize", "Unminimize", NUM_MINIMIZE_EFFECT,
                   minimizeEffectName);
      STORE_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_UNMINIMIZE_RANDOM_EFFECTS,
                   NUM_MINIMIZE_EFFECT,
                   ANIM_UNMINIMIZE_DEFAULT,
                   nUnminimizeRandomEffects,
                   unminimizeRandomEffects, minimizeEffectName, minimizeEffectType);

      // Close 1

      o = &as->opt[ANIM_SCREEN_OPTION_CLOSE1_EFFECT];
      o->name = "close1_effect";
      o->group = N_("Close");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Close #1 Animation");
      o->longDesc = N_("The animation shown when closing a window.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_CLOSE1_DEFAULT]);
      o->rest.s.string = closeEffectName;
      o->rest.s.nString = NUM_CLOSE_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_CLOSE1_WINDOW_TYPE];
      o->name = "close1_window_types";
      o->group = N_("Close");
      o->subGroup = N_("Close #1");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc = N_("The window types that will be animated.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_CLOSE1_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_CLOSE1_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_CLOSE1_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(close1DefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->close1WMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_CLOSE1_DURATION];
      o->name = "close1_duration";
      o->group = N_("Close");
      o->subGroup = N_("Close #1");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc =
                  N_
                  ("The number of seconds that the Close #1 animation will last.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_CLOSE1_DURATION_DEFAULT;
      o->rest.f.min = ANIM_CLOSE1_DURATION_MIN;
      o->rest.f.max = ANIM_CLOSE1_DURATION_MAX;
      o->rest.f.precision = ANIM_CLOSE1_DURATION_PRECISION;

      INIT_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_CLOSE1_RANDOM_EFFECTS,
                   "close1", "Close", "Close #1", NUM_CLOSE_EFFECT,
                   closeEffectName);
      STORE_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_CLOSE1_RANDOM_EFFECTS,
                   NUM_CLOSE_EFFECT,
                   ANIM_CLOSE1_DEFAULT,
                   nClose1RandomEffects,
                   close1RandomEffects, closeEffectName, closeEffectType);

      // Close 2

      o = &as->opt[ANIM_SCREEN_OPTION_CLOSE2_EFFECT];
      o->name = "close2_effect";
      o->group = N_("Close");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Close #2 Animation");
      o->longDesc = N_("The animation shown when closing a window.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_CLOSE2_DEFAULT]);
      o->rest.s.string = closeEffectName;
      o->rest.s.nString = NUM_CLOSE_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_CLOSE2_WINDOW_TYPE];
      o->name = "close2_window_types";
      o->group = N_("Close");
      o->subGroup = N_("Close #2");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc = N_("The window types that will be animated.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_CLOSE2_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_CLOSE2_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_CLOSE2_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(close2DefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->close2WMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_CLOSE2_DURATION];
      o->name = "close2_duration";
      o->group = N_("Close");
      o->subGroup = N_("Close #2");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc =
                  N_
                  ("The number of seconds that the Close #2 animation will last.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_CLOSE2_DURATION_DEFAULT;
      o->rest.f.min = ANIM_CLOSE2_DURATION_MIN;
      o->rest.f.max = ANIM_CLOSE2_DURATION_MAX;
      o->rest.f.precision = ANIM_CLOSE2_DURATION_PRECISION;

      INIT_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_CLOSE2_RANDOM_EFFECTS,
                   "close2", "Close", "Close #2", NUM_CLOSE_EFFECT,
                   closeEffectName);
      STORE_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_CLOSE2_RANDOM_EFFECTS,
                   NUM_CLOSE_EFFECT,
                   ANIM_CLOSE2_DEFAULT,
                   nClose2RandomEffects,
                   close2RandomEffects, closeEffectName, closeEffectType);

      // Create 1

      o = &as->opt[ANIM_SCREEN_OPTION_CREATE1_EFFECT];
      o->name = "create1_effect";
      o->group = N_("Create");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Create #1 Animation");
      o->longDesc = N_("The animation shown when creating a window.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_CREATE1_DEFAULT]);
      o->rest.s.string = closeEffectName;
      o->rest.s.nString = NUM_CLOSE_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_CREATE1_WINDOW_TYPE];
      o->name = "create1_window_types";
      o->group = N_("Create");
      o->subGroup = N_("Create #1");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc =
                  N_
                  ("Window types that should animate with this effect when created.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_CLOSE1_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_CLOSE1_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_CLOSE1_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(close1DefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->create1WMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_CREATE1_DURATION];
      o->name = "create1_duration";
      o->group = N_("Create");
      o->subGroup = N_("Create #1");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc = N_("Animation duration in seconds for create effect #1.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_CREATE1_DURATION_DEFAULT;
      o->rest.f.min = ANIM_CREATE1_DURATION_MIN;
      o->rest.f.max = ANIM_CREATE1_DURATION_MAX;
      o->rest.f.precision = ANIM_CREATE1_DURATION_PRECISION;

      INIT_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_CREATE1_RANDOM_EFFECTS,
                   "create1", "Create", "Create #1", NUM_CLOSE_EFFECT,
                   closeEffectName);
      STORE_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_CREATE1_RANDOM_EFFECTS,
                   NUM_CLOSE_EFFECT,
                   ANIM_CREATE1_DEFAULT,
                   nCreate1RandomEffects,
                   create1RandomEffects, closeEffectName, closeEffectType);

      // Create 2

      o = &as->opt[ANIM_SCREEN_OPTION_CREATE2_EFFECT];
      o->name = "create2_effect";
      o->group = N_("Create");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Create #2 Animation");
      o->longDesc = N_("The animation shown when creating a window.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_CREATE2_DEFAULT]);
      o->rest.s.string = closeEffectName;
      o->rest.s.nString = NUM_CLOSE_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_CREATE2_WINDOW_TYPE];
      o->name = "create2_window_types";
      o->group = N_("Create");
      o->subGroup = N_("Create #2");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc =
                  N_
                  ("Window types that should animate with this effect when created.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_CLOSE2_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_CLOSE2_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_CLOSE2_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(close2DefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->create2WMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_CREATE2_DURATION];
      o->name = "create2_duration";
      o->group = N_("Create");
      o->subGroup = N_("Create #2");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc = N_("Animation duration in seconds for create effect #2.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_CREATE2_DURATION_DEFAULT;
      o->rest.f.min = ANIM_CREATE2_DURATION_MIN;
      o->rest.f.max = ANIM_CREATE2_DURATION_MAX;
      o->rest.f.precision = ANIM_CREATE2_DURATION_PRECISION;

      INIT_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_CREATE2_RANDOM_EFFECTS,
                   "create2", "Create", "Create #2", NUM_CLOSE_EFFECT,
                   closeEffectName);
      STORE_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_CREATE2_RANDOM_EFFECTS,
                   NUM_CLOSE_EFFECT,
                   ANIM_CREATE2_DEFAULT,
                   nCreate2RandomEffects,
                   create2RandomEffects, closeEffectName, closeEffectType);

      // Focus

      o = &as->opt[ANIM_SCREEN_OPTION_FOCUS_EFFECT];
      o->name = "focus_effect";
      o->group = N_("Focus");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Animation");
      o->longDesc = N_("Focus window effect.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_FOCUS_DEFAULT]);
      o->rest.s.string = focusEffectName;
      o->rest.s.nString = NUM_FOCUS_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_FOCUS_WINDOW_TYPE];
      o->name = "focus_window_types";
      o->group = N_("Focus");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc =
                  N_
                  ("Window types that should animate with this effect when focused.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_FOCUS_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_FOCUS_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_FOCUS_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(focusDefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->focusWMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_FOCUS_DURATION];
      o->name = "focus_duration";
      o->group = N_("Focus");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc = N_("Focus animation duration in seconds.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_FOCUS_DURATION_DEFAULT;
      o->rest.f.min = ANIM_FOCUS_DURATION_MIN;
      o->rest.f.max = ANIM_FOCUS_DURATION_MAX;
      o->rest.f.precision = ANIM_FOCUS_DURATION_PRECISION;

      // Shade

      o = &as->opt[ANIM_SCREEN_OPTION_SHADE_EFFECT];
      o->name = "shade_effect";
      o->group = N_("(Un)Shade");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Shade Animation");
      o->longDesc = N_("Shade window effect.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_SHADE_DEFAULT]);
      o->rest.s.string = shadeEffectName;
      o->rest.s.nString = NUM_SHADE_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_SHADE_WINDOW_TYPE];
      o->name = "shade_window_types";
      o->group = N_("(Un)Shade");
      o->subGroup = N_("Shade");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc =
                  N_
                  ("Window types that should animate with this effect when shaded.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_SHADE_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_SHADE_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_SHADE_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(shadeDefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->shadeWMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_SHADE_DURATION];
      o->name = "shade_duration";
      o->group = N_("(Un)Shade");
      o->subGroup = N_("Shade");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc = N_("Shade animation duration in seconds.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_SHADE_DURATION_DEFAULT;
      o->rest.f.min = ANIM_SHADE_DURATION_MIN;
      o->rest.f.max = ANIM_SHADE_DURATION_MAX;
      o->rest.f.precision = ANIM_SHADE_DURATION_PRECISION;

      INIT_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_SHADE_RANDOM_EFFECTS,
                   "shade", "(Un)Shade", "Shade", NUM_SHADE_EFFECT,
                   shadeEffectName);
      STORE_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_SHADE_RANDOM_EFFECTS,
                   NUM_SHADE_EFFECT,
                   ANIM_SHADE_DEFAULT,
                   nShadeRandomEffects,
                   shadeRandomEffects, shadeEffectName, shadeEffectType);

      // Unshade

      o = &as->opt[ANIM_SCREEN_OPTION_UNSHADE_EFFECT];
      o->name = "unshade_effect";
      o->group = N_("(Un)Shade");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Unshade Animation");
      o->longDesc = N_("Unshade window effect.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(allEffectName[ANIM_UNSHADE_DEFAULT]);
      o->rest.s.string = shadeEffectName;
      o->rest.s.nString = NUM_SHADE_EFFECT;

      o = &as->opt[ANIM_SCREEN_OPTION_UNSHADE_WINDOW_TYPE];
      o->name = "unshade_window_types";
      o->group = N_("(Un)Shade");
      o->subGroup = N_("Unshade");
      o->advanced = False;
      o->shortDesc = N_("Window Types");
      o->longDesc =
                  N_
                  ("Window types that should animate with this effect when unshaded.");
      o->displayHints = "";
      o->type = CompOptionTypeList;
      o->value.list.type = CompOptionTypeString;
      o->value.list.nValue = N_SHADE_DEFAULT_WIN_TYPE;
      o->value.list.value =
                  malloc(sizeof(CompOptionValue) * N_SHADE_DEFAULT_WIN_TYPE);
      for (i = 0; i < N_SHADE_DEFAULT_WIN_TYPE; i++)
            o->value.list.value[i].s = strdup(shadeDefaultWinType[i]);
      o->rest.s.string = (char **)windowTypeString;
      o->rest.s.nString = nWindowTypeString;

      as->unshadeWMask = compWindowTypeMaskFromStringList(&o->value);

      o = &as->opt[ANIM_SCREEN_OPTION_UNSHADE_DURATION];
      o->name = "unshade_duration";
      o->group = N_("(Un)Shade");
      o->subGroup = N_("Unshade");
      o->advanced = False;
      o->shortDesc = N_("Animation Duration");
      o->longDesc = N_("Unshade animation duration in seconds.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_UNSHADE_DURATION_DEFAULT;
      o->rest.f.min = ANIM_UNSHADE_DURATION_MIN;
      o->rest.f.max = ANIM_UNSHADE_DURATION_MAX;
      o->rest.f.precision = ANIM_UNSHADE_DURATION_PRECISION;

      INIT_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_UNSHADE_RANDOM_EFFECTS,
                   "unshade", "(Un)Shade", "Unshade", NUM_SHADE_EFFECT,
                   shadeEffectName);
      STORE_RANDOM_EFFECT_LIST
                  (ANIM_SCREEN_OPTION_UNSHADE_RANDOM_EFFECTS,
                   NUM_SHADE_EFFECT,
                   ANIM_UNSHADE_DEFAULT,
                   nUnshadeRandomEffects,
                   unshadeRandomEffects, shadeEffectName, shadeEffectType);

      // Fire (Burn)

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_PARTICLES];
      o->name = "fire_particles";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Number Of Fire Particles");
      o->longDesc = N_("Number of fire particles.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_FIRE_PARTICLES_DEFAULT;
      o->rest.i.min = ANIM_FIRE_PARTICLES_MIN;
      o->rest.i.max = ANIM_FIRE_PARTICLES_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_SIZE];
      o->name = "fire_size";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Fire Particle Size");
      o->longDesc = N_("Fire particle size.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_FIRE_SIZE_DEFAULT;
      o->rest.f.min = ANIM_FIRE_SIZE_MIN;
      o->rest.f.max = ANIM_FIRE_SIZE_MAX;
      o->rest.f.precision = ANIM_FIRE_SIZE_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_SLOWDOWN];
      o->name = "fire_slowdown";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Fire Particle Slowdown");
      o->longDesc = N_("Fire particle slowdown.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_FIRE_SLOWDOWN_DEFAULT;
      o->rest.f.min = ANIM_FIRE_SLOWDOWN_MIN;
      o->rest.f.max = ANIM_FIRE_SLOWDOWN_MAX;
      o->rest.f.precision = ANIM_FIRE_SLOWDOWN_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_LIFE];
      o->name = "fire_life";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Fire Particle Life");
      o->longDesc = N_("Fire particle life.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_FIRE_LIFE_DEFAULT;
      o->rest.f.min = ANIM_FIRE_LIFE_MIN;
      o->rest.f.max = ANIM_FIRE_LIFE_MAX;
      o->rest.f.precision = ANIM_FIRE_LIFE_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_COLOR];
      o->name = "fire_color";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Fire Particle Color");
      o->longDesc = N_("Fire particle color.");
      o->displayHints = "";
      o->type = CompOptionTypeColor;
      o->value.c[0] = ANIM_FIRE_COLOR_RED_DEFAULT;
      o->value.c[1] = ANIM_FIRE_COLOR_GREEN_DEFAULT;
      o->value.c[2] = ANIM_FIRE_COLOR_BLUE_DEFAULT;
      o->value.c[3] = ANIM_FIRE_COLOR_ALPHA_DEFAULT;

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_DIRECTION];
      o->name = "fire_direction";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Fire direction");
      o->longDesc = N_("Fire direction.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(animDirectionName[ANIM_FIRE_DIRECTION_DEFAULT]);
      o->rest.s.string = animDirectionName;
      o->rest.s.nString = LIST_SIZE(animDirectionName);

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_CONSTANT_SPEED];
      o->name = "fire_constant_speed";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Fire constant speed");
      o->longDesc =
                  N_("Make fire effect duration be dependent on window height.");
      o->displayHints = "";
      o->type = CompOptionTypeBool;
      o->value.b = ANIM_FIRE_CONSTANT_SPEED_DEFAULT;

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_SMOKE];
      o->name = "fire_smoke";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Fire Smoke");
      o->longDesc = N_("Fire smoke.");
      o->displayHints = "";
      o->type = CompOptionTypeBool;
      o->value.b = ANIM_FIRE_SMOKE_DEFAULT;

      o = &as->opt[ANIM_SCREEN_OPTION_FIRE_MYSTICAL];
      o->name = "fire_mystical";
      o->group = N_("Fire (A.K.A Burn)");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Randomly Colored Fire");
      o->longDesc =
                  N_
                  ("Have random colors for the fire effect, also known as Mystical Fire.");
      o->displayHints = "";
      o->type = CompOptionTypeBool;
      o->value.b = ANIM_FIRE_MYSTICAL_DEFAULT;

      // Beam Up

      o = &as->opt[ANIM_SCREEN_OPTION_BEAMUP_SIZE];
      o->name = "beam_size";
      o->group = N_("Beam");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Beam Width");
      o->longDesc = N_("Beam width.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_BEAMUP_SIZE_DEFAULT;
      o->rest.f.min = ANIM_BEAMUP_SIZE_MIN;
      o->rest.f.max = ANIM_BEAMUP_SIZE_MAX;
      o->rest.f.precision = ANIM_BEAMUP_SIZE_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_BEAMUP_SPACING];
      o->name = "beam_spacing";
      o->group = N_("Beam");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Beam spacing");
      o->longDesc = N_("Spacing between beams.");
      o->displayHints = "";
      o->type = CompOptionTypeInt;
      o->value.i = ANIM_BEAMUP_SPACING_DEFAULT;
      o->rest.i.min = ANIM_BEAMUP_SPACING_MIN;
      o->rest.i.max = ANIM_BEAMUP_SPACING_MAX;

      o = &as->opt[ANIM_SCREEN_OPTION_BEAMUP_COLOR];
      o->name = "beam_color";
      o->group = N_("Beam");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Beam Color");
      o->longDesc = N_("Beam color.");
      o->displayHints = "";
      o->type = CompOptionTypeColor;
      o->value.c[0] = ANIM_BEAMUP_COLOR_RED_DEFAULT;
      o->value.c[1] = ANIM_BEAMUP_COLOR_GREEN_DEFAULT;
      o->value.c[2] = ANIM_BEAMUP_COLOR_BLUE_DEFAULT;
      o->value.c[3] = ANIM_BEAMUP_COLOR_ALPHA_DEFAULT;

      o = &as->opt[ANIM_SCREEN_OPTION_BEAMUP_SLOWDOWN];
      o->name = "beam_slowdown";
      o->group = N_("Beam");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Beam Slowdown");
      o->longDesc = N_("Beam slowdown.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_BEAMUP_SLOWDOWN_DEFAULT;
      o->rest.f.min = ANIM_BEAMUP_SLOWDOWN_MIN;
      o->rest.f.max = ANIM_BEAMUP_SLOWDOWN_MAX;
      o->rest.f.precision = ANIM_BEAMUP_SLOWDOWN_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_BEAMUP_LIFE];
      o->name = "beam_life";
      o->group = N_("Beam");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Beam Life");
      o->longDesc = N_("Beam life.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_BEAMUP_LIFE_DEFAULT;
      o->rest.f.min = ANIM_BEAMUP_LIFE_MIN;
      o->rest.f.max = ANIM_BEAMUP_LIFE_MAX;
      o->rest.f.precision = ANIM_BEAMUP_LIFE_PRECISION;

      // Domino / Razr

      o = &as->opt[ANIM_SCREEN_OPTION_DOMINO_DIRECTION];
      o->name = "domino_direction";
      o->group = N_("Domino/Razr");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Domino Piece Falling Direction");
      o->longDesc = N_("Falling direction for Domino pieces.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(animDirectionName[ANIM_DOMINO_DIRECTION_DEFAULT]);
      o->rest.s.string = animDirectionName;
      o->rest.s.nString = LIST_SIZE(animDirectionName);

      o = &as->opt[ANIM_SCREEN_OPTION_RAZR_DIRECTION];
      o->name = "razr_direction";
      o->group = N_("Domino/Razr");
      o->subGroup = N_("");
      o->advanced = False;
      o->shortDesc = N_("Razr Fold Opening Direction");
      o->longDesc = N_("Fold opening direction for pieces in Razr effect.");
      o->displayHints = "";
      o->type = CompOptionTypeString;
      o->value.s = strdup(animDirectionName[ANIM_RAZR_DIRECTION_DEFAULT]);
      o->rest.s.string = animDirectionName;
      o->rest.s.nString = LIST_SIZE(animDirectionName);

      // Glide 1

      o = &as->opt[ANIM_SCREEN_OPTION_GLIDE1_AWAY_POS];
      o->name = "glide1_away_position";
      o->group = N_("Glide");
      o->subGroup = N_("Glide 1");
      o->advanced = False;
      o->shortDesc = N_("Away Position");
      o->longDesc = N_("Closeness of window to camera at the end of the "
                               "animation "
                               "(1.0: Close to camera, -2.0: Away from camera).");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_GLIDE1_AWAY_POS_DEFAULT;
      o->rest.f.min = ANIM_GLIDE1_AWAY_POS_MIN;
      o->rest.f.max = ANIM_GLIDE1_AWAY_POS_MAX;
      o->rest.f.precision = ANIM_GLIDE1_AWAY_POS_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_GLIDE1_AWAY_ANGLE];
      o->name = "glide1_away_angle";
      o->group = N_("Glide");
      o->subGroup = N_("Glide 1");
      o->advanced = False;
      o->shortDesc = N_("Away Angle");
      o->longDesc = N_("Angle of window at the end of the animation.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_GLIDE1_AWAY_ANGLE_DEFAULT;
      o->rest.f.min = ANIM_GLIDE1_AWAY_ANGLE_MIN;
      o->rest.f.max = ANIM_GLIDE1_AWAY_ANGLE_MAX;
      o->rest.f.precision = ANIM_GLIDE1_AWAY_ANGLE_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_GLIDE1_THICKNESS];
      o->name = "glide1_thickness";
      o->group = N_("Glide");
      o->subGroup = N_("Glide 1");
      o->advanced = False;
      o->shortDesc = N_("Thickness");
      o->longDesc = N_("Window thickness in pixels. Setting this to larger "
                               "than 0 will disable shadow, blur, and reflection "
                               "during the animation.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_GLIDE1_THICKNESS_DEFAULT;
      o->rest.f.min = ANIM_GLIDE1_THICKNESS_MIN;
      o->rest.f.max = ANIM_GLIDE1_THICKNESS_MAX;
      o->rest.f.precision = ANIM_GLIDE1_THICKNESS_PRECISION;

      // Glide 2

      o = &as->opt[ANIM_SCREEN_OPTION_GLIDE2_AWAY_POS];
      o->name = "glide2_away_position";
      o->group = N_("Glide");
      o->subGroup = N_("Glide 2");
      o->advanced = False;
      o->shortDesc = N_("Away Position");
      o->longDesc = N_("Closeness of window to camera at the end of the "
                               "animation "
                               "(1.0: Close to camera, -2.0: Away from camera).");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_GLIDE2_AWAY_POS_DEFAULT;
      o->rest.f.min = ANIM_GLIDE2_AWAY_POS_MIN;
      o->rest.f.max = ANIM_GLIDE2_AWAY_POS_MAX;
      o->rest.f.precision = ANIM_GLIDE2_AWAY_POS_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_GLIDE2_AWAY_ANGLE];
      o->name = "glide2_away_angle";
      o->group = N_("Glide");
      o->subGroup = N_("Glide 2");
      o->advanced = False;
      o->shortDesc = N_("Away Angle");
      o->longDesc = N_("Angle of window at the end of the animation.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_GLIDE2_AWAY_ANGLE_DEFAULT;
      o->rest.f.min = ANIM_GLIDE2_AWAY_ANGLE_MIN;
      o->rest.f.max = ANIM_GLIDE2_AWAY_ANGLE_MAX;
      o->rest.f.precision = ANIM_GLIDE2_AWAY_ANGLE_PRECISION;

      o = &as->opt[ANIM_SCREEN_OPTION_GLIDE2_THICKNESS];
      o->name = "glide2_thickness";
      o->group = N_("Glide");
      o->subGroup = N_("Glide 2");
      o->advanced = False;
      o->shortDesc = N_("Thickness");
      o->longDesc = N_("Window thickness in pixels. Setting this to larger "
                               "than 0 will disable shadow, blur, and reflection "
                               "during the animation.");
      o->displayHints = "";
      o->type = CompOptionTypeFloat;
      o->value.f = ANIM_GLIDE2_THICKNESS_DEFAULT;
      o->rest.f.min = ANIM_GLIDE2_THICKNESS_MIN;
      o->rest.f.max = ANIM_GLIDE2_THICKNESS_MAX;
      o->rest.f.precision = ANIM_GLIDE2_THICKNESS_PRECISION;
}

static CompOption *animGetScreenOptions(CompScreen * screen, int *count)
{
      if (screen)
      {
            ANIM_SCREEN(screen);

            *count = NUM_OPTIONS(as);
            return as->opt;
      }
      else
      {
            AnimScreen *as = calloc(1, sizeof(AnimScreen));

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

static void
objectInit(Object * object,
               float positionX, float positionY,
               float gridPositionX, float gridPositionY)
{
      object->gridPosition.x = gridPositionX;
      object->gridPosition.y = gridPositionY;

      object->position.x = positionX;
      object->position.y = positionY;

      object->offsetTexCoordForQuadBefore.x = 0;
      object->offsetTexCoordForQuadBefore.y = 0;
      object->offsetTexCoordForQuadAfter.x = 0;
      object->offsetTexCoordForQuadAfter.y = 0;
}

static void
modelInitObjects(Model * model, int x, int y, int width, int height)
{
      int gridX, gridY;
      int nGridCellsX, nGridCellsY;
      float x0, y0;

      x0 = model->scaleOrigin.x;
      y0 = model->scaleOrigin.y;

      // number of grid cells in x direction
      nGridCellsX = model->gridWidth - 1;

      if (model->forWindowEvent == WindowEventShade ||
            model->forWindowEvent == WindowEventUnshade)
      {
            // number of grid cells in y direction
            nGridCellsY = model->gridHeight - 3;      // One allocated for top, one for bottom

            float winContentsHeight =
                        height - model->topHeight - model->bottomHeight;

            //Top
            float objectY = y + (0 - y0) * model->scale.y + y0;

            for (gridX = 0; gridX < model->gridWidth; gridX++)
            {
                  objectInit(&model->objects[gridX],
                                 x + ((gridX * width / nGridCellsX) -
                                          x0) * model->scale.x + x0, objectY,
                                 (float)gridX / nGridCellsX, 0);
            }

            // Window contents
            for (gridY = 1; gridY < model->gridHeight - 1; gridY++)
            {
                  float inWinY =
                              (gridY - 1) * winContentsHeight / nGridCellsY +
                              model->topHeight;
                  float gridPosY = inWinY / height;

                  objectY = y + (inWinY - y0) * model->scale.y + y0;

                  for (gridX = 0; gridX < model->gridWidth; gridX++)
                  {
                        objectInit(&model->
                                       objects[gridY *
                                                   model->gridWidth +
                                                   gridX],
                                       x +
                                       ((gridX * width / nGridCellsX) -
                                          x0) * model->scale.x + x0,
                                       objectY, (float)gridX / nGridCellsX, gridPosY);
                  }
            }

            // Bottom (gridY is model->gridHeight-1 now)
            objectY = y + (height - y0) * model->scale.y + y0;

            for (gridX = 0; gridX < model->gridWidth; gridX++)
            {
                  objectInit(&model->
                                 objects[gridY * model->gridWidth +
                                             gridX],
                                 x + ((gridX * width / nGridCellsX) -
                                          x0) * model->scale.x + x0, objectY,
                                 (float)gridX / nGridCellsX, 1);
            }
      }
      else
      {
            // number of grid cells in y direction
            nGridCellsY = model->gridHeight - 1;

            int i = 0;

            for (gridY = 0; gridY < model->gridHeight; gridY++)
            {
                  float objectY =
                              y + ((gridY * height / nGridCellsY) -
                                     y0) * model->scale.y + y0;
                  for (gridX = 0; gridX < model->gridWidth; gridX++)
                  {
                        objectInit(&model->objects[i],
                                       x +
                                       ((gridX * width / nGridCellsX) -
                                          x0) * model->scale.x + x0,
                                       objectY,
                                       (float)gridX / nGridCellsX,
                                       (float)gridY / nGridCellsY);
                        i++;
                  }
            }
      }
}

static Model *createModel(CompWindow * w,
                                      WindowEvent forWindowEvent,
                                      AnimEffect forAnimEffect, int gridWidth,
                                      int gridHeight)
{
      int x = WIN_X(w);
      int y = WIN_Y(w);
      int width = WIN_W(w);
      int height = WIN_H(w);

      Model *model;

      model = calloc(1, sizeof(Model));
      if (!model)
      {
            fprintf(stderr, "%s: Not enough memory at line %d!\n",
                        __FILE__, __LINE__);
            return 0;
      }
      model->magicLampWaveCount = 0;
      model->magicLampWaves = NULL;

      model->gridWidth = gridWidth;
      model->gridHeight = gridHeight;
      model->numObjects = gridWidth * gridHeight;
      model->objects = calloc(1, sizeof(Object) * model->numObjects);
      if (!model->objects)
      {
            fprintf(stderr, "%s: Not enough memory at line %d!\n",
                        __FILE__, __LINE__);
            free(model);
            return 0;
      }

      // Store win. size to check later
      model->winWidth = width;
      model->winHeight = height;

      // For shading
      model->forWindowEvent = forWindowEvent;
      model->topHeight = w->output.top;
      model->bottomHeight = w->output.bottom;

      model->remainderSteps = 0;

      model->scale.x = 1.0f;
      model->scale.y = 1.0f;

      model->scaleOrigin.x = 0.0f;
      model->scaleOrigin.y = 0.0f;

      modelInitObjects(model, x, y, width, height);

      modelCalcBounds(model);

      return model;
}

static Bool
animEnsureModel(CompWindow * w,
                        WindowEvent forWindowEvent, AnimEffect forAnimEffect)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(w->screen);

      int gridWidth = 2;
      int gridHeight = 2;

      if (animEffectProperties[forAnimEffect].initGridFunc)
            animEffectProperties[forAnimEffect].initGridFunc(as,
                                                                                     forWindowEvent,
                                                                                     &gridWidth,
                                                                                     &gridHeight);

      Bool isShadeUnshadeEvent =
                  (forWindowEvent == WindowEventShade ||
                   forWindowEvent == WindowEventUnshade);

      Bool wasShadeUnshadeEvent = aw->model &&
                  (aw->model->forWindowEvent == WindowEventShade ||
                   aw->model->forWindowEvent == WindowEventUnshade);

      if (!aw->model ||
            gridWidth != aw->model->gridWidth ||
            gridHeight != aw->model->gridHeight ||
            (isShadeUnshadeEvent != wasShadeUnshadeEvent) ||
            aw->model->winWidth != WIN_W(w) || aw->model->winHeight != WIN_H(w))
      {
            if (aw->model)
            {
                  if (aw->model->magicLampWaves)
                        free(aw->model->magicLampWaves);
                  free(aw->model->objects);
                  free(aw->model);
            }
            aw->model = createModel(w,
                                                forWindowEvent, forAnimEffect,
                                                gridWidth, gridHeight);
            if (!aw->model)
                  return FALSE;
      }

      return TRUE;
}

static Bool playingPolygonEffect(AnimScreen *as, AnimWindow *aw)
{
      if (!animEffectProperties[aw->curAnimEffect].
             addCustomGeometryFunc)
            return FALSE;

      if (!(aw->curAnimEffect == AnimEffectGlide3D1 ||
              aw->curAnimEffect == AnimEffectGlide3D2))
            return TRUE;

      float finalDistFac;
      float finalRotAng;
      float thickness;

      fxGlideGetParams(as, aw, &finalDistFac, &finalRotAng, &thickness);

      return (thickness > 1e-5); // glide is 3D if thickness > 0
}

static void postAnimationCleanup(CompWindow * w, Bool resetAnimation)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(w->screen);

      IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, FALSE);

      if (resetAnimation)
      {
            // if 3d polygon fx
            if (playingPolygonEffect(as, aw))
            {
                  IPCS_SetBoolN(IPCS_OBJECT(w), "DISABLE_BLUR", FALSE);
                  IPCS_SetBoolN(IPCS_OBJECT(w), "DISABLE_REFLECTION", FALSE);
            }
            if (as->ppDisabling == PostprocessDisablingWindow)
                  IPCS_SetBoolN(IPCS_OBJECT(w), "DISABLE_BLUR", FALSE);
            if (!(as->animInProgress)
                  && as->ppDisabling == PostprocessDisablingScreen)
                  IPCS_SetBoolN(IPCS_OBJECT(w->screen), "DISABLE_BLUR", FALSE);

            aw->curWindowEvent = WindowEventNone;
            aw->curAnimEffect = AnimEffectNone;
            aw->animOverrideProgressDir = 0;

            if (aw->model)
            {
                  if (aw->model->magicLampWaves)
                        free(aw->model->magicLampWaves);
                  aw->model->magicLampWaves = 0;
            }
      }

      aw->state = aw->newState;

      if (aw->drawRegion)
            XDestroyRegion(aw->drawRegion);
      aw->drawRegion = NULL;
      aw->useDrawRegion = FALSE;

      if (aw->numPs)
      {
            int i = 0;

            for (i = 0; i < aw->numPs; i++)
            {
                  free(aw->ps[i].particles);
                  if (aw->ps[i].tex)
                        glDeleteTextures(1, &aw->ps[i].tex);
            }
            free(aw->ps);
            aw->ps = NULL;
            aw->numPs = 0;
      }

      if (aw->polygonSet)
      {
            freePolygonSet(aw);
            //aw->polygonSet->nClips = 0;
      }
      aw->animInitialized = FALSE;

      //if (aw->unmapCnt || aw->destroyCnt)
      //    releaseWindow (w);
      while (aw->unmapCnt)
      {
            unmapWindow(w);
            aw->unmapCnt--;
      }
      while (aw->destroyCnt)
      {
            destroyWindow(w);
            aw->destroyCnt--;
      }
}

static void animPreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
      CompWindow *w;

      ANIM_SCREEN(s);

      if (as->animInProgress)
      {
            AnimWindow *aw;
            BoxRec box;
            Point topLeft, bottomRight;

            if (as->ppDisabling == PostprocessDisablingScreen)
                  IPCS_SetBoolN(IPCS_OBJECT(s), "DISABLE_BLUR", TRUE);
            else
                  IPCS_SetBoolN(IPCS_OBJECT(s), "DISABLE_BLUR", FALSE);

            as->animInProgress = FALSE;
            for (w = s->windows; w; w = w->next)
            {
                  aw = GET_ANIM_WINDOW(w, as);

                  if (aw->numPs)
                  {
                        int i = 0;

                        for (i = 0; i < aw->numPs; i++)
                        {
                              if (aw->ps[i].active)
                              {
                                    updateParticles(&aw->ps[i], msSinceLastPaint);
                                    as->animInProgress = TRUE;
                              }
                        }
                  }

                  if (aw->animRemainingTime > 0)
                  {
                        if (!aw->animInitialized)     // if animation is just starting
                        {
                              aw->subEffectNo =
                                          animEffectProperties[aw->curAnimEffect].
                                          subEffectNo;

                              aw->deceleratingMotion =
                                          animEffectProperties[aw->curAnimEffect].
                                          animStepPolygonFunc ==
                                          polygonsDeceleratingAnimStepPolygon;

                              if (playingPolygonEffect(as, aw))
                              {
                                    // Allocate polygon set if null
                                    if (!aw->polygonSet)
                                    {
                                          //printf("calloc aw->polygonSet\n");
                                          aw->polygonSet = calloc(1, sizeof(PolygonSet));
                                    }
                                    if (!aw->polygonSet)
                                    {
                                          fprintf(stderr,
                                                      "%s: Not enough memory at line %d!\n",
                                                      __FILE__, __LINE__);
                                          return;
                                    }
                                    aw->polygonSet->allFadeDuration = -1.0f;
                              }
                        }

                        //aw->drawCount = 0;
                        //printf("aw->drawCount init: %d\n", aw->drawCount);

                        if (as->ppDisabling == PostprocessDisablingWindow)
                              IPCS_SetBoolN(IPCS_OBJECT(w), "DISABLE_BLUR", TRUE);
                        else
                              IPCS_SetBoolN(IPCS_OBJECT(w), "DISABLE_BLUR", FALSE);

                        // if 3d polygon fx
                        if (playingPolygonEffect(as, aw))
                        {
                              aw->nClipsPassed = 0;
                              aw->clipsUpdated = FALSE;

                              IPCS_SetBoolN(IPCS_OBJECT(w), "DISABLE_BLUR", TRUE);
                              IPCS_SetBoolN(IPCS_OBJECT(w), "DISABLE_REFLECTION", TRUE);
                        }
                        if (aw->model)
                        {
                              topLeft = aw->model->topLeft;
                              bottomRight = aw->model->bottomRight;

                              // If just starting, call fx init func.
                              if (!aw->animInitialized &&
                                    animEffectProperties[aw->curAnimEffect].initFunc)
                                    animEffectProperties[aw->
                                                                   curAnimEffect].initFunc(s, w);
                              aw->animInitialized = TRUE;

                              if (aw->model->winWidth != WIN_W(w) ||
                                    aw->model->winHeight != WIN_H(w))
                              {
                                    // model needs update

                                    // keep this value
                                    float remainderSteps = aw->model->remainderSteps;

                                    // re-create model
                                    animEnsureModel
                                                (w, aw->curWindowEvent, aw->curAnimEffect);
                                    if (aw->model == 0)
                                          continue;   // skip this window
                                    aw->model->remainderSteps = remainderSteps;
                              }
                              // Call fx step func.
                              if (animEffectProperties[aw->curAnimEffect].animStepFunc)
                              {
                                    animEffectProperties[aw->
                                                                   curAnimEffect].
                                                animStepFunc(s, w, msSinceLastPaint);
                              }
                              if (aw->animRemainingTime <= 0)
                              {
                                    // Animation done
                                    postAnimationCleanup(w, TRUE);
                              }

                              if (!(s->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK))
                              {
                                    if (aw->animRemainingTime > 0)
                                    {
                                          if (aw->model->topLeft.x < topLeft.x)
                                                topLeft.x = aw->model->topLeft.x;
                                          if (aw->model->topLeft.y < topLeft.y)
                                                topLeft.y = aw->model->topLeft.y;
                                          if (aw->model->bottomRight.x > bottomRight.x)
                                                bottomRight.x = aw->model->bottomRight.x;
                                          if (aw->model->bottomRight.y > bottomRight.y)
                                                bottomRight.y = aw->model->bottomRight.y;
                                    }
                                    else
                                          addWindowDamage(w);

                                    box.x1 = topLeft.x;
                                    box.y1 = topLeft.y;
                                    box.x2 = bottomRight.x + 0.5f;
                                    box.y2 = bottomRight.y + 0.5f;

                                    box.x1 -= w->attrib.x + w->attrib.border_width;
                                    box.y1 -= w->attrib.y + w->attrib.border_width;
                                    box.x2 -= w->attrib.x + w->attrib.border_width;
                                    box.y2 -= w->attrib.y + w->attrib.border_width;

                                    addWindowDamageRect(w, &box);
                              }
                        }
                        as->animInProgress |= (aw->animRemainingTime > 0);
                  }

                  if (aw->animRemainingTime <= 0)
                  {
                        if (aw->curAnimEffect != AnimEffectNone ||
                              aw->unmapCnt > 0 || aw->destroyCnt > 0)
                              postAnimationCleanup(w, TRUE);
                        aw->curWindowEvent = WindowEventNone;
                        aw->curAnimEffect = AnimEffectNone;
                  }
            }
      }

      UNWRAP(as, s, preparePaintScreen);
      (*s->preparePaintScreen) (s, msSinceLastPaint);
      WRAP(as, s, preparePaintScreen, animPreparePaintScreen);
}

static void animDonePaintScreen(CompScreen * s)
{
      ANIM_SCREEN(s);

      if (as->animInProgress)
      {
            damageScreen(s);
      }
      if (!(as->animInProgress) &&
            as->ppDisabling == PostprocessDisablingScreen)
            IPCS_SetBoolN(IPCS_OBJECT(s), "DISABLE_BLUR", FALSE);

      UNWRAP(as, s, donePaintScreen);
      (*s->donePaintScreen) (s);
      WRAP(as, s, donePaintScreen, animDonePaintScreen);
}

static void
animAddWindowGeometry(CompWindow * w,
                                CompMatrix * matrix,
                                int nMatrix, Region region, Region clip)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(w->screen);

      // if model is lost during animation (e.g. when plugin just reloaded)
      if (aw->animRemainingTime > 0 && !aw->model)
      {
            aw->animRemainingTime = 0;
            postAnimationCleanup(w, TRUE);
      }
      // if window is being animated
      if (aw->animRemainingTime > 0 && aw->model)
      {
            BoxPtr pClip;
            int nClip;
            int nVertices, nIndices;
            GLushort *i;
            GLfloat *v;
            int x1, y1, x2, y2;
            float width, height;
            float winContentsY, winContentsHeight;
            float deformedX, deformedY;
            int nVertX, nVertY, wx, wy;
            int vSize, it;
            float gridW, gridH, x, y;
            Bool rect = TRUE;
            Bool useTextureQ = TRUE;
            Model *model = aw->model;
            Region awRegion = NULL;

            // Use Q texture coordinate to avoid jagged-looking quads
            // http://www.r3.nu/~cass/qcoord/
            if (animEffectProperties[aw->curAnimEffect].dontUseQTexCoord)
                  useTextureQ = FALSE;

            if (aw->useDrawRegion)
            {
                  awRegion = XCreateRegion();
                  XOffsetRegion(aw->drawRegion, WIN_X(w), WIN_Y(w));
                  XIntersectRegion(region, aw->drawRegion, awRegion);
                  XOffsetRegion(aw->drawRegion, -WIN_X(w), -WIN_Y(w));
                  nClip = awRegion->numRects;
                  pClip = awRegion->rects;
            }
            else
            {
                  nClip = region->numRects;
                  pClip = region->rects;
            }

            if (nClip == 0)               // nothing to do
            {
                  if (awRegion)
                        XDestroyRegion(awRegion);
                  return;
            }

            for (it = 0; it < nMatrix; it++)
            {
                  if (matrix[it].xy != 0.0f || matrix[it].yx != 0.0f)
                  {
                        rect = FALSE;
                        break;
                  }
            }

            if (aw->polygonSet &&
                  animEffectProperties[aw->curAnimEffect].addCustomGeometryFunc)
            {
                  /*int nClip2 = nClip;
                     BoxPtr pClip2 = pClip;

                     // For each clip passed to this function
                     for (; nClip2--; pClip2++)
                     {
                     x1 = pClip2->x1;
                     y1 = pClip2->y1;
                     x2 = pClip2->x2;
                     y2 = pClip2->y2;

                     printf("x1: %4d, y1: %4d, x2: %4d, y2: %4d", x1, y1, x2, y2);
                     printf("\tm: %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
                     matrix[0].xx, matrix[0].xy, matrix[0].yx, matrix[0].yy,
                     matrix[0].x0, matrix[0].y0);
                     } */

                  animEffectProperties[aw->curAnimEffect].
                              addCustomGeometryFunc(w->screen, w, nClip, pClip,
                                                              nMatrix, matrix);

                  // If addGeometryFunc exists, it is expected to do everthing
                  // to add geometries (instead of the rest of this function).

                  if (w->vCount == 0)     // if there is no vertex
                  {
                        // put a dummy quad in vertices and indices
                        if (4 > w->indexSize)
                        {
                              if (!moreWindowIndices(w, 4))
                                    return;
                        }
                        if (4 > w->vertexSize)
                        {
                              if (!moreWindowVertices(w, 4))
                                    return;
                        }
                        w->vCount = 4;
                        w->indexCount = 4;
                        w->texUnits = 1;
                        memset(w->vertices, 0, sizeof(GLfloat) * 4);
                        memset(w->indices, 0, sizeof(GLushort) * 4);
                  }
                  return;                       // We're done here.
            }

            // window coordinates and size
            wx = WIN_X(w);
            wy = WIN_Y(w);
            width = WIN_W(w);
            height = WIN_H(w);

            // to be used if event is shade/unshade
            winContentsY = w->attrib.y;
            winContentsHeight = w->height;

            w->texUnits = nMatrix;

            if (w->vCount == 0)
            {
                  // reset
                  w->indexCount = 0;
                  w->texCoordSize = 4;
            }
            vSize = 2 + w->texUnits * w->texCoordSize;

            nVertices = w->vCount;
            nIndices = w->indexCount;

            v = w->vertices + (nVertices * vSize);
            i = w->indices + nIndices;

            // For each clip passed to this function
            for (; nClip--; pClip++)
            {
                  x1 = pClip->x1;
                  y1 = pClip->y1;
                  x2 = pClip->x2;
                  y2 = pClip->y2;
                  /*
                     printf("x1: %4d, y1: %4d, x2: %4d, y2: %4d", x1, y1, x2, y2);
                     printf("\tm: %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
                     matrix[0].xx, matrix[0].xy, matrix[0].yx, matrix[0].yy,
                     matrix[0].x0, matrix[0].y0);
                   */
                  gridW = (float)width / (model->gridWidth - 1);

                  if (aw->curWindowEvent == WindowEventShade ||
                        aw->curWindowEvent == WindowEventUnshade)
                  {
                        if (y1 < w->attrib.y)   // if at top part
                        {
                              gridH = model->topHeight;
                        }
                        else if (y2 > w->attrib.y + w->height)    // if at bottom
                        {
                              gridH = model->bottomHeight;
                        }
                        else              // in window contents (only in Y coords)
                        {
                              float winContentsHeight =
                                          height - model->topHeight - model->bottomHeight;
                              gridH = winContentsHeight / (model->gridHeight - 3);
                        }
                  }
                  else
                        gridH = (float)height / (model->gridHeight - 1);

                  // nVertX, nVertY: number of vertices for this clip in x and y dimensions
                  // + 2 to avoid running short of vertices in some cases
                  nVertX = ceil((x2 - x1) / gridW) + 2;
                  nVertY = (gridH ? ceil((y2 - y1) / gridH) : 0) + 2;

                  // Allocate 4 indices for each quad
                  int newIndexSize = nIndices + ((nVertX - 1) * (nVertY - 1) * 4);

                  if (newIndexSize > w->indexSize)
                  {
                        if (!moreWindowIndices(w, newIndexSize))
                              return;

                        i = w->indices + nIndices;
                  }
                  // Assign quad vertices to indices
                  int jx, jy;

                  for (jy = 0; jy < nVertY - 1; jy++)
                  {
                        for (jx = 0; jx < nVertX - 1; jx++)
                        {
                              *i++ = nVertices + nVertX * (2 * jy + 1) + jx;
                              *i++ = nVertices + nVertX * (2 * jy + 1) + jx + 1;
                              *i++ = nVertices + nVertX * 2 * jy + jx + 1;
                              *i++ = nVertices + nVertX * 2 * jy + jx;

                              nIndices += 4;
                        }
                  }

                  // Allocate vertices
                  int newVertexSize =
                              (nVertices + nVertX * (2 * nVertY - 2)) * vSize;
                  if (newVertexSize > w->vertexSize)
                  {
                        if (!moreWindowVertices(w, newVertexSize))
                              return;

                        v = w->vertices + (nVertices * vSize);
                  }

                  float rowTexCoordQ = 1;
                  float prevRowCellWidth = 0;   // this initial value won't be used
                  float rowCellWidth = 0;

                  // For each vertex
                  for (jy = 0, y = y1; jy < nVertY; jy++)
                  {
                        if (y > y2)
                              y = y2;

                        // Do calculations for y here to avoid repeating
                        // them unnecessarily in the x loop

                        float topiyFloat;
                        Bool applyOffsets = TRUE;

                        if (aw->curWindowEvent == WindowEventShade
                              || aw->curWindowEvent == WindowEventUnshade)
                        {
                              if (y1 < w->attrib.y)   // if at top part
                              {
                                    topiyFloat = (y - WIN_Y(w)) / model->topHeight;
                                    topiyFloat = MIN(topiyFloat, 0.999);      // avoid 1.0
                                    applyOffsets = FALSE;
                              }
                              else if (y2 > w->attrib.y + w->height)    // if at bottom
                              {
                                    topiyFloat = (model->gridHeight - 2) +
                                                (model->
                                                 bottomHeight ? (y - winContentsY -
                                                                         winContentsHeight) /
                                                 model->bottomHeight : 0);
                                    applyOffsets = FALSE;
                              }
                              else        // in window contents (only in Y coords)
                              {
                                    topiyFloat =
                                                (model->gridHeight -
                                                 3) * (y - winContentsY) / winContentsHeight +
                                                1;
                              }
                        }
                        else
                        {
                              topiyFloat = (model->gridHeight - 1) * (y - wy) / height;
                        }
                        // topiy should be at most (model->gridHeight - 2)
                        int topiy = (int)(topiyFloat + 1e-4);

                        if (topiy == model->gridHeight - 1)
                              topiy--;
                        int bottomiy = topiy + 1;
                        float iny = topiyFloat - topiy;

                        // End of calculations for y

                        for (jx = 0, x = x1; jx < nVertX; jx++)
                        {
                              if (x > x2)
                                    x = x2;

                              // find containing grid cell (leftix rightix) x (topiy bottomiy)
                              float leftixFloat =
                                          (model->gridWidth - 1) * (x - wx) / width;
                              int leftix = (int)(leftixFloat + 1e-4);

                              if (leftix == model->gridWidth - 1)
                                    leftix--;
                              int rightix = leftix + 1;

                              // Objects that are at top, bottom, left, right corners of quad
                              Object *objToTopLeft =
                                          &(model->
                                            objects[topiy * model->gridWidth + leftix]);
                              Object *objToTopRight =
                                          &(model->
                                            objects[topiy * model->gridWidth + rightix]);
                              Object *objToBottomLeft =
                                          &(model->
                                            objects[bottomiy * model->gridWidth + leftix]);
                              Object *objToBottomRight =
                                          &(model->
                                            objects[bottomiy * model->gridWidth + rightix]);

                              // find position in cell by taking remainder of flooring
                              float inx = leftixFloat - leftix;

                              // Interpolate to find deformed coordinates

                              float hor1x =
                                          (1 -
                                           inx) *
                                          objToTopLeft->position.x +
                                          inx * objToTopRight->position.x;
                              float hor1y =
                                          (1 -
                                           inx) *
                                          objToTopLeft->position.y +
                                          inx * objToTopRight->position.y;
                              float hor2x =
                                          (1 -
                                           inx) *
                                          objToBottomLeft->position.x +
                                          inx * objToBottomRight->position.x;
                              float hor2y =
                                          (1 -
                                           inx) *
                                          objToBottomLeft->position.y +
                                          inx * objToBottomRight->position.y;

                              deformedX = (1 - iny) * hor1x + iny * hor2x;
                              deformedY = (1 - iny) * hor1y + iny * hor2y;

                              if (useTextureQ)
                              {
                                    if (jx == 1)
                                          rowCellWidth = deformedX - v[-2];

                                    if (jy > 0 && jx == 1)  // do only once per row for all rows except row 0
                                    {
                                          rowTexCoordQ = (rowCellWidth / prevRowCellWidth);

                                          v[-3] = rowTexCoordQ;   // update first column
                                          v[-6] *= rowTexCoordQ;  // (since we didn't know rowTexCoordQ before)
                                          v[-5] *= rowTexCoordQ;
                                    }
                              }
                              if (rect)
                              {
                                    for (it = 0; it < nMatrix; it++)
                                    {
                                          float offsetY = 0;

                                          if (applyOffsets && y < y2)
                                                offsetY =
                                                            objToTopLeft->
                                                            offsetTexCoordForQuadAfter.y;

                                          *v++ = COMP_TEX_COORD_X(&matrix[it], x);
                                          *v++ = COMP_TEX_COORD_Y(&matrix[it], y + offsetY);
                                          *v++ = 0;
                                          if (useTextureQ)
                                          {
                                                *v++ = rowTexCoordQ;    // Q texture coordinate

                                                if (0 < jy && jy < nVertY - 1)
                                                {
                                                      // copy first 3 texture coords to duplicate row
                                                      memcpy(v
                                                               -
                                                               4
                                                               +
                                                               nVertX
                                                               *
                                                               vSize, v - 4, 3 * sizeof(GLfloat));
                                                      *(v - 1 + nVertX * vSize) = 1;      // Q texture coordinate
                                                }
                                                if (applyOffsets &&     /*0 < jy && */
                                                      objToTopLeft->
                                                      offsetTexCoordForQuadBefore.y != 0)
                                                {
                                                      // After copying to next row, update texture y coord
                                                      // by following object's offset
                                                      offsetY =
                                                                  objToTopLeft->
                                                                  offsetTexCoordForQuadBefore.y;
                                                      v[-3] = COMP_TEX_COORD_Y(&matrix[it],
                                                                                           y + offsetY);
                                                }
                                                if (jx > 0) // since column 0 is updated when jx == 1
                                                {
                                                      v[-4] *= rowTexCoordQ;
                                                      v[-3] *= rowTexCoordQ;
                                                }
                                          }
                                          else
                                          {
                                                *v++ = 1;

                                                if (0 < jy && jy < nVertY - 1)
                                                {
                                                      // copy first 3 texture coords to duplicate row
                                                      memcpy(v
                                                               -
                                                               4
                                                               +
                                                               nVertX
                                                               *
                                                               vSize, v - 4, 3 * sizeof(GLfloat));

                                                      *(v - 1 + nVertX * vSize) = 1;      // Q texture coordinate
                                                }
                                                if (applyOffsets
                                                      && objToTopLeft->
                                                      offsetTexCoordForQuadBefore.y != 0)
                                                {
                                                      // After copying to next row, update texture y coord
                                                      // by following object's offset
                                                      offsetY =
                                                                  objToTopLeft->
                                                                  offsetTexCoordForQuadBefore.y;
                                                      v[-3] = COMP_TEX_COORD_Y(&matrix[it],
                                                                                           y + offsetY);
                                                }
                                          }
                                    }
                              }
                              else
                              {
                                    for (it = 0; it < nMatrix; it++)
                                    {
                                          float offsetY = 0;

                                          if (applyOffsets && y < y2)
                                                offsetY =
                                                            objToTopLeft->
                                                            offsetTexCoordForQuadAfter.y;

                                          *v++ = COMP_TEX_COORD_XY
                                                      (&matrix[it], x, y + offsetY);
                                          *v++ = COMP_TEX_COORD_YX
                                                      (&matrix[it], x, y + offsetY);
                                          *v++ = 0;
                                          if (useTextureQ)
                                          {
                                                *v++ = rowTexCoordQ;    // Q texture coordinate

                                                if (0 < jy && jy < nVertY - 1)
                                                {
                                                      // copy first 3 texture coords to duplicate row
                                                      memcpy(v
                                                               -
                                                               4
                                                               +
                                                               nVertX
                                                               *
                                                               vSize, v - 4, 3 * sizeof(GLfloat));
                                                      *(v - 1 + nVertX * vSize) = 1;      // Q texture coordinate
                                                }
                                                if (applyOffsets
                                                      && objToTopLeft->
                                                      offsetTexCoordForQuadBefore.y != 0)
                                                {
                                                      // After copying to next row, update texture y coord
                                                      // by following object's offset
                                                      offsetY =
                                                                  objToTopLeft->
                                                                  offsetTexCoordForQuadBefore.y;
                                                      v[-4] = COMP_TEX_COORD_XY(&matrix[it], x,
                                                                                            y + offsetY);
                                                      v[-3] = COMP_TEX_COORD_YX(&matrix[it], x,
                                                                                            y + offsetY);
                                                }
                                                if (jx > 0) // column t should be updated when jx is t+1
                                                {
                                                      v[-4] *= rowTexCoordQ;
                                                      v[-3] *= rowTexCoordQ;
                                                }
                                          }
                                          else
                                          {
                                                *v++ = 1;

                                                if (0 < jy && jy < nVertY - 1)
                                                {
                                                      // copy first 3 texture coords to duplicate row
                                                      memcpy(v
                                                               -
                                                               4
                                                               +
                                                               nVertX
                                                               *
                                                               vSize, v - 4, 3 * sizeof(GLfloat));
                                                      *(v - 1 + nVertX * vSize) = 1;      // Q texture coordinate
                                                }
                                                if (applyOffsets
                                                      && objToTopLeft->
                                                      offsetTexCoordForQuadBefore.y != 0)
                                                {
                                                      // After copying to next row, update texture y coord
                                                      // by following object's offset
                                                      offsetY =
                                                                  objToTopLeft->
                                                                  offsetTexCoordForQuadBefore.y;
                                                      v[-4] = COMP_TEX_COORD_XY(&matrix[it], x,
                                                                                            y + offsetY);
                                                      v[-3] = COMP_TEX_COORD_YX(&matrix[it], x,
                                                                                            y + offsetY);
                                                }
                                          }
                                    }
                              }
                              *v++ = deformedX;
                              *v++ = deformedY;

                              if (0 < jy && jy < nVertY - 1)
                                    memcpy(v - 2 +
                                             nVertX * vSize, v - 2, 2 * sizeof(GLfloat));

                              nVertices++;

                              // increment x properly (so that coordinates fall on grid intersections)
                              x = rightix * gridW + wx;
                        }
                        if (useTextureQ)
                              prevRowCellWidth = rowCellWidth;

                        if (0 < jy && jy < nVertY - 1)
                        {
                              v += nVertX * vSize;    // skip the duplicate row
                              nVertices += nVertX;
                        }
                        // increment y properly (so that coordinates fall on grid intersections)
                        if (aw->curWindowEvent == WindowEventShade
                              || aw->curWindowEvent == WindowEventUnshade)
                        {
                              y += gridH;
                        }
                        else
                        {
                              y = bottomiy * gridH + wy;
                        }
                  }
            }
            w->vCount = nVertices;
            w->indexCount = nIndices;
            if (awRegion)
            {
                  XDestroyRegion(awRegion);
                  awRegion = NULL;
            }
      }
      else
      {
            UNWRAP(as, w->screen, addWindowGeometry);
            (*w->screen->addWindowGeometry) (w, matrix, nMatrix, region, clip);
            WRAP(as, w->screen, addWindowGeometry, animAddWindowGeometry);
      }
}

static void
animDrawWindowTexture(CompWindow * w, CompTexture * texture,
                                const WindowPaintAttrib * attrib, unsigned int mask)
{
      ANIM_WINDOW(w);
      ANIM_SCREEN(w->screen);

      if (aw->animRemainingTime > 0)      // if animation in progress, store texture
      {
            //printf("%X animDrawWindowTexture, texture: %X\n",
            //     (unsigned)aw, (unsigned)texture);
            aw->curTexture = texture;
            aw->curPaintAttrib = *attrib;
      }

      UNWRAP(as, w->screen, drawWindowTexture);
      (*w->screen->drawWindowTexture) (w, texture, attrib, mask);
      WRAP(as, w->screen, drawWindowTexture, animDrawWindowTexture);
}


static void animDrawWindowGeometry(CompWindow * w)
{
      ANIM_WINDOW(w);

      if (aw->animRemainingTime > 0)
      {
            //printf("animDrawWindowGeometry: %X: coords: %d, %d, %f\n",
            //       (unsigned)aw, WIN_X (w), WIN_Y (w), aw->animRemainingTime);
            aw->nDrawGeometryCalls++;

            ANIM_SCREEN(w->screen);

            if (playingPolygonEffect(as, aw) &&
                  animEffectProperties[aw->curAnimEffect].drawCustomGeometryFunc)
            {
                  animEffectProperties[aw->curAnimEffect].drawCustomGeometryFunc
                              (w->screen, w);
                  return;
            }
            int texUnit = w->texUnits;
            int currentTexUnit = 0;
            int stride = 2 + texUnit * w->texCoordSize;
            GLfloat *vertices = w->vertices + (stride - 2);

            stride *= sizeof(GLfloat);

            glVertexPointer(2, GL_FLOAT, stride, vertices);

            while (texUnit--)
            {
                  if (texUnit != currentTexUnit)
                  {
                        w->screen->clientActiveTexture(GL_TEXTURE0_ARB + texUnit);
                        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
                        currentTexUnit = texUnit;
                  }
                  vertices -= w->texCoordSize;
                  glTexCoordPointer(w->texCoordSize, GL_FLOAT, stride, vertices);
            }

            glDrawElements(GL_QUADS, w->indexCount, GL_UNSIGNED_SHORT,
                                 w->indices);

            // disable all texture coordinate arrays except 0
            texUnit = w->texUnits;
            if (texUnit > 1)
            {
                  while (--texUnit)
                  {
                        (*w->screen->clientActiveTexture) (GL_TEXTURE0_ARB + texUnit);
                        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
                  }

                  (*w->screen->clientActiveTexture) (GL_TEXTURE0_ARB);
            }
      }
      else
      {
            ANIM_SCREEN(w->screen);

            UNWRAP(as, w->screen, drawWindowGeometry);
            (*w->screen->drawWindowGeometry) (w);
            WRAP(as, w->screen, drawWindowGeometry, animDrawWindowGeometry);
      }
}

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

      ANIM_SCREEN(w->screen);
      ANIM_WINDOW(w);

      if (aw->animRemainingTime > 0)
      {
            //printf("animPaintWindow: %X: coords: %d, %d\n",
            //       (unsigned)aw, WIN_X (w), WIN_X (w));

            if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
            {
                  aw->curTextureFilter = w->screen->filter[WINDOW_TRANS_FILTER];
            }
            else if (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)
            {
                  aw->curTextureFilter = w->screen->filter[SCREEN_TRANS_FILTER];
            }
            else
            {
                  aw->curTextureFilter = w->screen->filter[NOTHING_TRANS_FILTER];
            }

            w->indexCount = 0;

            WindowPaintAttrib wAttrib = *attrib;

            if (mask & PAINT_WINDOW_SOLID_MASK)
                  return FALSE;

            mask |= PAINT_WINDOW_TRANSFORMED_MASK;

            wAttrib.xScale = 1.0f;
            wAttrib.yScale = 1.0f;

            aw->nDrawGeometryCalls = 0;

            if (animEffectProperties[aw->curAnimEffect].updateWindowAttribFunc)
                  animEffectProperties[aw->curAnimEffect].
                              updateWindowAttribFunc(as, aw, &wAttrib);

            if (animEffectProperties[aw->curAnimEffect].prePaintWindowFunc)
                  animEffectProperties[aw->curAnimEffect].
                              prePaintWindowFunc(w->screen, w);

            UNWRAP(as, w->screen, paintWindow);
            status = (*w->screen->paintWindow) (w, &wAttrib, region, mask);
            WRAP(as, w->screen, paintWindow, animPaintWindow);

            if (animEffectProperties[aw->curAnimEffect].postPaintWindowFunc)
                  animEffectProperties[aw->curAnimEffect].
                              postPaintWindowFunc(w->screen, w);
      }
      else
      {
            UNWRAP(as, w->screen, paintWindow);
            status = (*w->screen->paintWindow) (w, attrib, region, mask);
            WRAP(as, w->screen, paintWindow, animPaintWindow);
      }

      return status;
}

static Bool animGetWindowIconGeometry(CompWindow * w, XRectangle * rect)
{
      Atom actual;
      int result, format;
      unsigned long n, left;
      unsigned char *data;

      ANIM_DISPLAY(w->screen->display);

      result = XGetWindowProperty(w->screen->display->display, w->id,
                                                ad->winIconGeometryAtom,
                                                0L, 4L, FALSE, XA_CARDINAL, &actual,
                                                &format, &n, &left, &data);

      if (result == Success && n && data)
      {
            if (n == 4)
            {
                  unsigned long *geometry = (unsigned long *)data;

                  rect->x = geometry[0];
                  rect->y = geometry[1];
                  rect->width = geometry[2];
                  rect->height = geometry[3];

                  XFree(data);

                  return TRUE;
            }

            XFree(data);
      }

      return FALSE;
}

static void animHandleEvent(CompDisplay * d, XEvent * event)
{
      Window activeWindow = 0;
      CompWindow *w;

      ANIM_DISPLAY(d);

      switch (event->type)
      {
      case PropertyNotify:
            if (event->xproperty.atom == d->winActiveAtom)
                  activeWindow = d->activeWindow;
            break;
      case MapNotify:
            w = findWindowAtDisplay(d, event->xmap.window);
            if (w)
            {
                  ANIM_WINDOW(w);

                  if (aw->animRemainingTime > 0)
                  {
                        aw->state = aw->newState;
                  }
                  while (aw->unmapCnt)
                  {
                        unmapWindow(w);
                        aw->unmapCnt--;
                  }
            }
            break;
      case DestroyNotify:
            w = findWindowAtDisplay(d, event->xunmap.window);
            if (w)
            {
                  ANIM_WINDOW(w);
                  aw->destroyCnt++;
                  w->destroyRefCnt++;
                  addWindowDamage(w);
            }
            break;
      case UnmapNotify:
            w = findWindowAtDisplay(d, event->xunmap.window);
            if (w)
            {
                  ANIM_SCREEN(w->screen);

                  if (w->pendingUnmaps && onCurrentDesktop(w))    // Normal -> Iconic
                  {
                        ANIM_WINDOW(w);
                        if (w->shaded)
                        {
                              // SHADE event!

                              //printf("SHADE event! %X\n", (unsigned)aw);

                              aw->nowShaded = TRUE;

                              if (as->shadeEffect && (as->shadeWMask & w->type))
                              {
                                    IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, TRUE);
                                    Bool startingNew = TRUE;

                                    if (aw->curWindowEvent != WindowEventNone)
                                    {
                                          if (aw->curWindowEvent != WindowEventUnshade)
                                                postAnimationCleanup(w, TRUE);
                                          else
                                          {
                                                // Play the unshade effect backwards from where it left
                                                aw->animRemainingTime =
                                                            aw->animTotalTime -
                                                            aw->animRemainingTime;

                                                // avoid window remains
                                                if (aw->animRemainingTime <= 0)
                                                      aw->animRemainingTime = 1;

                                                startingNew = FALSE;
                                                if (aw->animOverrideProgressDir == 0)
                                                      aw->animOverrideProgressDir = 2;
                                                else if (aw->animOverrideProgressDir == 1)
                                                      aw->animOverrideProgressDir = 0;
                                          }
                                    }

                                    as->animInProgress = TRUE;
                                    aw->curWindowEvent = WindowEventShade;

                                    if (startingNew)
                                    {
                                          if (as->shadeEffect == AnimEffectRandom
                                                || as->
                                                opt[ANIM_SCREEN_OPTION_ALL_RANDOM].value.b)
                                                aw->curAnimEffect =     // choose a random effect
                                                            as->shadeRandomEffects[(int)
                                                                                             (as->
                                                                                                nShadeRandomEffects
                                                                                                *
                                                                                                (double)rand()
                                                                                                / RAND_MAX)];
                                          else
                                                aw->curAnimEffect = as->shadeEffect;

                                          aw->animTotalTime =
                                                      as->
                                                      opt
                                                      [ANIM_SCREEN_OPTION_SHADE_DURATION].
                                                      value.f * 1000;
                                          aw->animRemainingTime = aw->animTotalTime;
                                    }

                                    // Store coords in this viewport to omit 3d effect
                                    // painting in other viewports
                                    aw->lastKnownCoords.x = w->attrib.x;
                                    aw->lastKnownCoords.y = w->attrib.y;

                                    if (!animEnsureModel
                                          (w, WindowEventShade, aw->curAnimEffect))
                                    {
                                          postAnimationCleanup(w, TRUE);
                                    }

                                    aw->unmapCnt++;
                                    w->unmapRefCnt++;

                                    addWindowDamage(w);
                              }
                        }
                        else if (w->minimized
                                     && as->minimizeEffect
                                     && (as->minimizeWMask & w->type))
                        {
                              // MINIMIZE event!

                              //printf("MINIMIZE event! %X\n", (unsigned)aw);

                              IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, TRUE);
                              Bool startingNew = TRUE;

                              if (aw->curWindowEvent != WindowEventNone)
                              {
                                    if (aw->curWindowEvent != WindowEventUnminimize)
                                          postAnimationCleanup(w, TRUE);
                                    else
                                    {
                                          // Play the unminimize effect backwards from where it left
                                          aw->animRemainingTime =
                                                      aw->animTotalTime - aw->animRemainingTime;

                                          // avoid window remains
                                          if (aw->animRemainingTime == 0)
                                                aw->animRemainingTime = 1;

                                          startingNew = FALSE;
                                          if (aw->animOverrideProgressDir == 0)
                                                aw->animOverrideProgressDir = 2;
                                          else if (aw->animOverrideProgressDir == 1)
                                                aw->animOverrideProgressDir = 0;
                                    }
                              }

                              aw->newState = IconicState;
                              as->animInProgress = TRUE;
                              aw->curWindowEvent = WindowEventMinimize;

                              if (startingNew)
                              {
                                    if (as->minimizeEffect ==
                                          AnimEffectRandom
                                          || as->opt[ANIM_SCREEN_OPTION_ALL_RANDOM].value.b)
                                          aw->curAnimEffect =     // choose a random effect
                                                      as->minimizeRandomEffects[(int)
                                                                                            (as->
                                                                                             nMinimizeRandomEffects
                                                                                             *
                                                                                             (double)rand()
                                                                                             / RAND_MAX)];
                                    else
                                          aw->curAnimEffect = as->minimizeEffect;

                                    aw->animTotalTime =
                                                as->
                                                opt
                                                [ANIM_SCREEN_OPTION_MINIMIZE_DURATION].
                                                value.f * 1000;
                                    aw->animRemainingTime = aw->animTotalTime;
                              }

                              // Store coords in this viewport to omit 3d effect
                              // painting in other viewports
                              aw->lastKnownCoords.x = w->attrib.x;
                              aw->lastKnownCoords.y = w->attrib.y;

                              if (!animEnsureModel
                                    (w, WindowEventMinimize, aw->curAnimEffect))
                              {
                                    postAnimationCleanup(w, TRUE);
                              }
                              else
                              {
                                    if (!animGetWindowIconGeometry(w, &aw->icon))
                                    {
                                          // minimize to bottom-center if there is no window list
                                          aw->icon.x = w->screen->width / 2;
                                          aw->icon.y = w->screen->height;
                                          aw->icon.width = 100;
                                          aw->icon.height = 20;
                                    }
                                    if ((aw->curAnimEffect ==
                                           AnimEffectZoom
                                           || aw->
                                           curAnimEffect ==
                                           AnimEffectSidekick)
                                          &&
                                          (as->zoomFC == ZoomFromCenterOn ||
                                           as->zoomFC == ZoomFromCenterMin))
                                    {
                                          aw->icon.x =
                                                      WIN_X(w) +
                                                      WIN_W(w) / 2 - aw->icon.width / 2;
                                          aw->icon.y =
                                                      WIN_Y(w) +
                                                      WIN_H(w) / 2 - aw->icon.height / 2;
                                    }

                                    aw->unmapCnt++;
                                    w->unmapRefCnt++;

                                    addWindowDamage(w);
                              }
                        }
                  }
                  else                    // X -> Withdrawn
                  {
                        ANIM_WINDOW(w);

                        AnimEffect windowsCloseEffect = AnimEffectNone;
                        int whichClose = 1;     // either 1 or 2

                        if (as->close1Effect && (as->close1WMask & w->type))
                              windowsCloseEffect = as->close1Effect;
                        else if (as->close2Effect && (as->close2WMask & w->type))
                        {
                              windowsCloseEffect = as->close2Effect;
                              whichClose = 2;
                        }
                        // CLOSE event!

                        if (windowsCloseEffect)
                        {
                              IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, TRUE);
                              int tmpSteps = 0;

                              //printf("CLOSE event! %X\n", (unsigned)aw);

                              Bool startingNew = TRUE;

                              if (aw->animRemainingTime > 0 &&
                                    aw->curWindowEvent != WindowEventCreate)
                              {
                                    tmpSteps = aw->animRemainingTime;
                                    aw->animRemainingTime = 0;
                              }
                              if (aw->curWindowEvent != WindowEventNone)
                              {
                                    if (aw->curWindowEvent == WindowEventCreate)
                                    {
                                          // Play the create effect backward from where it left
                                          aw->animRemainingTime =
                                                      aw->animTotalTime - aw->animRemainingTime;

                                          // avoid window remains
                                          if (aw->animRemainingTime <= 0)
                                                aw->animRemainingTime = 1;

                                          startingNew = FALSE;
                                          if (aw->animOverrideProgressDir == 0)
                                                aw->animOverrideProgressDir = 2;
                                          else if (aw->animOverrideProgressDir == 1)
                                                aw->animOverrideProgressDir = 0;
                                    }
                                    else if (aw->curWindowEvent == WindowEventClose)
                                    {
                                          if (aw->animOverrideProgressDir == 2)
                                          {
                                                aw->animRemainingTime = tmpSteps;
                                                startingNew = FALSE;
                                          }
                                    }
                                    else
                                    {
                                          postAnimationCleanup(w, TRUE);
                                    }
                              }

                              aw->state = NormalState;
                              aw->newState = WithdrawnState;
                              as->animInProgress = TRUE;
                              aw->curWindowEvent = WindowEventClose;

                              if (startingNew)
                              {
                                    if (windowsCloseEffect ==
                                          AnimEffectRandom
                                          || as->opt[ANIM_SCREEN_OPTION_ALL_RANDOM].value.b)
                                    {
                                          if (whichClose == 1)
                                                aw->curAnimEffect =     // choose a random effect
                                                            as->close1RandomEffects[(int)
                                                                                                (as->
                                                                                                 nClose1RandomEffects
                                                                                                 *
                                                                                                 (double)
                                                                                                 rand() /
                                                                                                 RAND_MAX)];
                                          else
                                                aw->curAnimEffect =     // choose a random effect
                                                            as->close2RandomEffects[(int)
                                                                                                (as->
                                                                                                 nClose2RandomEffects
                                                                                                 *
                                                                                                 (double)
                                                                                                 rand() /
                                                                                                 RAND_MAX)];
                                    }
                                    else
                                          aw->curAnimEffect = windowsCloseEffect;

                                    aw->animTotalTime =
                                                as->opt[whichClose ==
                                                            1 ?
                                                            ANIM_SCREEN_OPTION_CLOSE1_DURATION
                                                            :
                                                            ANIM_SCREEN_OPTION_CLOSE2_DURATION].
                                                value.f * 1000;
                                    aw->animRemainingTime = aw->animTotalTime;
                              }

                              // Store coords in this viewport to omit 3d effect
                              // painting in other viewports
                              aw->lastKnownCoords.x = w->attrib.x;
                              aw->lastKnownCoords.y = w->attrib.y;

                              if (!animEnsureModel
                                    (w, WindowEventClose, aw->curAnimEffect))
                              {
                                    postAnimationCleanup(w, TRUE);
                              }
                              else if (getMousePointerXY
                                           (w->screen, &aw->icon.x, &aw->icon.y))
                              {
                                    aw->icon.width = FAKE_ICON_SIZE;
                                    aw->icon.height = FAKE_ICON_SIZE;

                                    if (aw->curAnimEffect == AnimEffectMagicLamp1 &&
                                          aw->icon.width <
                                          as->
                                          opt
                                          [ANIM_SCREEN_OPTION_MAGIC_LAMP1_CREATE_START_WIDTH].
                                          value.i)
                                    {
                                          aw->icon.width =
                                                      as->
                                                      opt
                                                      [ANIM_SCREEN_OPTION_MAGIC_LAMP1_CREATE_START_WIDTH].
                                                      value.i;
                                    }
                                    else if (aw->curAnimEffect == AnimEffectMagicLamp2 &&
                                                 aw->icon.width <
                                                 as->
                                                 opt
                                                 [ANIM_SCREEN_OPTION_MAGIC_LAMP2_CREATE_START_WIDTH].
                                                 value.i)
                                    {
                                          aw->icon.width =
                                                      as->
                                                      opt
                                                      [ANIM_SCREEN_OPTION_MAGIC_LAMP2_CREATE_START_WIDTH].
                                                      value.i;
                                    }

                                    else if (aw->curAnimEffect == AnimEffectMagicLamp3 &&
                                                 aw->icon.width <
                                                 as->
                                                 opt
                                                 [ANIM_SCREEN_OPTION_MAGIC_LAMP3_CREATE_START_WIDTH].
                                                 value.i)
                                    {
                                          aw->icon.width =
                                                      as->
                                                      opt
                                                      [ANIM_SCREEN_OPTION_MAGIC_LAMP3_CREATE_START_WIDTH].
                                                      value.i;
                                    }

                                    if ((aw->curAnimEffect ==
                                           AnimEffectZoom
                                           || aw->
                                           curAnimEffect ==
                                           AnimEffectSidekick)
                                          &&
                                          (as->zoomFC == ZoomFromCenterOn ||
                                           as->zoomFC == ZoomFromCenterCreate))
                                    {
                                          aw->icon.x =
                                                      WIN_X(w) +
                                                      WIN_W(w) / 2 - aw->icon.width / 2;
                                          aw->icon.y =
                                                      WIN_Y(w) +
                                                      WIN_H(w) / 2 - aw->icon.height / 2;
                                    }

                                    aw->unmapCnt++;
                                    w->unmapRefCnt++;

                                    addWindowDamage(w);
                              }
                        }
                        else if ((as->create1Effect
                                      && (as->create1WMask & w->type))
                                     || (as->create2Effect
                                           && (as->create2WMask & w->type)))
                        {
                              // stop the current animation and prevent it from rewinding

                              if (aw->animRemainingTime > 0 &&
                                    aw->curWindowEvent != WindowEventCreate)
                              {
                                    aw->animRemainingTime = 0;
                              }
                              if ((aw->curWindowEvent !=
                                     WindowEventNone)
                                    && (aw->curWindowEvent != WindowEventClose))
                              {
                                    postAnimationCleanup(w, TRUE);
                              }
                              // set some properties to make sure this window will use the
                              // correct create effect the next time it's "created"

                              aw->state = NormalState;
                              aw->newState = WithdrawnState;
                              as->animInProgress = TRUE;
                              aw->curWindowEvent = WindowEventClose;

                              aw->unmapCnt++;
                              w->unmapRefCnt++;

                              addWindowDamage(w);
                        }
                  }
            }
            break;
      default:
            break;
      }

      UNWRAP(ad, d, handleEvent);
      (*d->handleEvent) (d, event);
      WRAP(ad, d, handleEvent, animHandleEvent);

      switch (event->type)
      {
      case PropertyNotify:
            if (event->xproperty.atom == d->winActiveAtom)
            {
                  if (d->activeWindow != activeWindow)
                  {
                        w = findWindowAtDisplay(d, d->activeWindow);
                        if (w)
                        {
                              ANIM_WINDOW(w);
                              ANIM_SCREEN(w->screen);

                              if (aw->curWindowEvent != WindowEventNone)
                                    return;

                              if ((as->focusWMask & w->type) && as->focusEffect &&
                                    // On unminimization, focus event is fired first.
                                    // When this happens and minimize is in progress,
                                    // don't prevent rewinding of minimize when unminimize is fired
                                    // right after this focus event.
                                    aw->curWindowEvent !=
                                    WindowEventMinimize
                                    && animEnsureModel(w,
                                                               WindowEventFocus, as->focusEffect))
                              {
                                    // FOCUS event!

                                    //printf("FOCUS event! %X\n", (unsigned)aw);

                                    IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, TRUE);
                                    if (aw->curWindowEvent != WindowEventNone)
                                    {
                                          postAnimationCleanup(w, TRUE);
                                    }

                                    as->animInProgress = TRUE;
                                    aw->curWindowEvent = WindowEventFocus;
                                    aw->curAnimEffect = as->focusEffect;
                                    aw->animTotalTime =
                                                as->
                                                opt
                                                [ANIM_SCREEN_OPTION_FOCUS_DURATION].
                                                value.f * 1000;
                                    aw->animRemainingTime = aw->animTotalTime;

                                    // Store coords in this viewport to omit 3d effect
                                    // painting in other viewports
                                    aw->lastKnownCoords.x = w->attrib.x;
                                    aw->lastKnownCoords.y = w->attrib.y;

                                    damageScreen(w->screen);
                              }
                        }
                  }
            }

      default:
            break;
      }
}

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

      ANIM_SCREEN(w->screen);

      if (initial)                        // Unminimize or Create
      {
            ANIM_WINDOW(w);

            if (aw->state == IconicState)
            {
                  if (!w->invisible && as->unminimizeEffect
                        && (as->unminimizeWMask & w->type))
                  {
                        // UNMINIMIZE event!

                        //printf("UNMINIMIZE event! %X\n", (unsigned)aw);

                        IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, TRUE);
                        Bool startingNew = TRUE;

                        if (aw->curWindowEvent != WindowEventNone)
                        {
                              if (aw->curWindowEvent != WindowEventMinimize)
                                    postAnimationCleanup(w, TRUE);
                              else
                              {
                                    // Play the minimize effect backwards from where it left
                                    aw->animRemainingTime =
                                                aw->animTotalTime - aw->animRemainingTime;

                                    // avoid window remains
                                    if (aw->animRemainingTime <= 0)
                                          aw->animRemainingTime = 1;

                                    startingNew = FALSE;
                                    if (aw->animOverrideProgressDir == 0)
                                          aw->animOverrideProgressDir = 1;
                                    else if (aw->animOverrideProgressDir == 2)
                                          aw->animOverrideProgressDir = 0;
                              }
                        }
                        as->animInProgress = TRUE;
                        aw->curWindowEvent = WindowEventUnminimize;

                        if (startingNew)
                        {
                              if (as->unminimizeEffect ==
                                    AnimEffectRandom
                                    || as->opt[ANIM_SCREEN_OPTION_ALL_RANDOM].value.b)
                                    aw->curAnimEffect =     // choose a random effect
                                                as->unminimizeRandomEffects[(int)
                                                                                          (as->
                                                                                           nUnminimizeRandomEffects
                                                                                           *
                                                                                           (double)rand() /
                                                                                           RAND_MAX)];
                              else
                                    aw->curAnimEffect = as->unminimizeEffect;

                              aw->animTotalTime =
                                          as->
                                          opt
                                          [ANIM_SCREEN_OPTION_UNMINIMIZE_DURATION].
                                          value.f * 1000;
                              aw->animRemainingTime = aw->animTotalTime;
                        }

                        // Store coords in this viewport to omit 3d effect
                        // painting in other viewports
                        aw->lastKnownCoords.x = w->attrib.x;
                        aw->lastKnownCoords.y = w->attrib.y;

                        if (animEnsureModel
                              (w, WindowEventUnminimize, aw->curAnimEffect))
                        {
                              if (!animGetWindowIconGeometry(w, &aw->icon))
                              {
                                    // minimize to bottom-center if there is no window list
                                    aw->icon.x = w->screen->width / 2;
                                    aw->icon.y = w->screen->height;
                                    aw->icon.width = 100;
                                    aw->icon.height = 20;
                              }
                              if ((aw->curAnimEffect ==
                                     AnimEffectZoom
                                     || aw->curAnimEffect ==
                                     AnimEffectSidekick)
                                    &&
                                    (as->zoomFC == ZoomFromCenterOn ||
                                     as->zoomFC == ZoomFromCenterMin))
                              {
                                    aw->icon.x =
                                                WIN_X(w) + WIN_W(w) / 2 - aw->icon.width / 2;
                                    aw->icon.y =
                                                WIN_Y(w) + WIN_H(w) / 2 - aw->icon.height / 2;
                              }
                              addWindowDamage(w);
                        }
                        else
                              postAnimationCleanup(w, TRUE);
                  }
            }
            else if (aw->nowShaded)
            {
                  // UNSHADE event!
                  //printf("UNSHADE event! %X\n", (unsigned)aw);

                  IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, TRUE);
                  aw->nowShaded = FALSE;

                  if (as->unshadeEffect && (as->unshadeWMask & w->type))
                  {
                        Bool startingNew = TRUE;

                        if (aw->curWindowEvent != WindowEventNone)
                        {
                              if (aw->curWindowEvent != WindowEventShade)
                                    postAnimationCleanup(w, TRUE);
                              else
                              {
                                    // Play the shade effect backwards from where it left
                                    aw->animRemainingTime =
                                                aw->animTotalTime - aw->animRemainingTime;

                                    // avoid window remains
                                    if (aw->animRemainingTime <= 0)
                                          aw->animRemainingTime = 1;

                                    startingNew = FALSE;
                                    if (aw->animOverrideProgressDir == 0)
                                          aw->animOverrideProgressDir = 1;
                                    else if (aw->animOverrideProgressDir == 2)
                                          aw->animOverrideProgressDir = 0;
                              }
                        }
                        as->animInProgress = TRUE;
                        aw->curWindowEvent = WindowEventUnshade;

                        if (startingNew)
                        {
                              if (as->unshadeEffect ==
                                    AnimEffectRandom
                                    || as->opt[ANIM_SCREEN_OPTION_ALL_RANDOM].value.b)
                                    aw->curAnimEffect =     // choose a random effect
                                                as->unshadeRandomEffects[(int)
                                                                                     (as->
                                                                                      nUnshadeRandomEffects
                                                                                      * (double)rand() /
                                                                                      RAND_MAX)];
                              else
                                    aw->curAnimEffect = as->unshadeEffect;

                              aw->animTotalTime =
                                          as->
                                          opt
                                          [ANIM_SCREEN_OPTION_UNSHADE_DURATION].value.f *
                                          1000;
                              aw->animRemainingTime = aw->animTotalTime;
                        }

                        // Store coords in this viewport to omit 3d effect
                        // painting in other viewports
                        aw->lastKnownCoords.x = w->attrib.x;
                        aw->lastKnownCoords.y = w->attrib.y;

                        if (animEnsureModel(w, WindowEventUnshade, aw->curAnimEffect))
                              addWindowDamage(w);
                        else
                              postAnimationCleanup(w, TRUE);
                  }
            }
            else if (aw->state != NormalState && !w->invisible)
            {
                  AnimEffect windowsCreateEffect = AnimEffectNone;

                  int whichCreate = 1;    // either 1 or 2

                  if (as->create1Effect && (as->create1WMask & w->type))
                        windowsCreateEffect = as->create1Effect;
                  else if (as->create2Effect && (as->create2WMask & w->type))
                  {
                        windowsCreateEffect = as->create2Effect;
                        whichCreate = 2;
                  }

                  if (windowsCreateEffect &&
                        getMousePointerXY(w->screen, &aw->icon.x, &aw->icon.y))
                  {
                        // CREATE event!

                        //printf("CREATE event! %X\n", (unsigned)aw);

                        IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, TRUE);
                        Bool startingNew = TRUE;

                        if (aw->curWindowEvent != WindowEventNone)
                        {
                              if (aw->curWindowEvent != WindowEventClose)
                                    postAnimationCleanup(w, TRUE);
                              else
                              {
                                    // Play the close effect backwards from where it left
                                    aw->animRemainingTime =
                                                aw->animTotalTime - aw->animRemainingTime;

                                    // avoid window remains
                                    if (aw->animRemainingTime == 0)
                                          aw->animRemainingTime = 1;

                                    startingNew = FALSE;
                                    if (aw->animOverrideProgressDir == 0)
                                          aw->animOverrideProgressDir = 1;
                                    else if (aw->animOverrideProgressDir == 2)
                                          aw->animOverrideProgressDir = 0;
                              }
                        }
                        as->animInProgress = TRUE;
                        aw->curWindowEvent = WindowEventCreate;

                        if (startingNew)
                        {
                              if (windowsCreateEffect ==
                                    AnimEffectRandom
                                    || as->opt[ANIM_SCREEN_OPTION_ALL_RANDOM].value.b)
                              {
                                    if (whichCreate == 1)
                                          aw->curAnimEffect =     // choose a random effect
                                                      as->create1RandomEffects[(int)
                                                                                           (as->
                                                                                            nCreate1RandomEffects
                                                                                            *
                                                                                            (double)rand() /
                                                                                            RAND_MAX)];
                                    else
                                          aw->curAnimEffect =     // choose a random effect
                                                      as->create2RandomEffects[(int)
                                                                                           (as->
                                                                                            nCreate2RandomEffects
                                                                                            *
                                                                                            (double)rand() /
                                                                                            RAND_MAX)];
                              }
                              else
                                    aw->curAnimEffect = windowsCreateEffect;

                              aw->animTotalTime =
                                          as->opt[whichCreate == 1 ?
                                                      ANIM_SCREEN_OPTION_CREATE1_DURATION
                                                      :
                                                      ANIM_SCREEN_OPTION_CREATE2_DURATION].
                                          value.f * 1000;
                              aw->animRemainingTime = aw->animTotalTime;
                        }

                        aw->icon.width = FAKE_ICON_SIZE;
                        aw->icon.height = FAKE_ICON_SIZE;

                        if (aw->curAnimEffect == AnimEffectMagicLamp1 &&
                              aw->icon.width <
                              as->
                              opt[ANIM_SCREEN_OPTION_MAGIC_LAMP1_CREATE_START_WIDTH].
                              value.i)
                        {
                              aw->icon.width =
                                          as->
                                          opt
                                          [ANIM_SCREEN_OPTION_MAGIC_LAMP1_CREATE_START_WIDTH].
                                          value.i;
                        }
                        else if (aw->curAnimEffect == AnimEffectMagicLamp2 &&
                                     aw->icon.width <
                                     as->
                                     opt
                                     [ANIM_SCREEN_OPTION_MAGIC_LAMP2_CREATE_START_WIDTH].
                                     value.i)
                        {
                              aw->icon.width =
                                          as->
                                          opt
                                          [ANIM_SCREEN_OPTION_MAGIC_LAMP2_CREATE_START_WIDTH].
                                          value.i;
                        }
                        else if (aw->curAnimEffect == AnimEffectMagicLamp3 &&
                                     aw->icon.width <
                                     as->
                                     opt
                                     [ANIM_SCREEN_OPTION_MAGIC_LAMP3_CREATE_START_WIDTH].
                                     value.i)
                        {
                              aw->icon.width =
                                          as->
                                          opt
                                          [ANIM_SCREEN_OPTION_MAGIC_LAMP3_CREATE_START_WIDTH].
                                          value.i;
                        }
                        aw->icon.x -= aw->icon.width / 2;
                        aw->icon.y -= aw->icon.height / 2;

                        if ((aw->curAnimEffect == AnimEffectZoom ||
                               aw->curAnimEffect == AnimEffectSidekick) &&
                              (as->zoomFC == ZoomFromCenterOn ||
                               as->zoomFC == ZoomFromCenterCreate))
                        {
                              aw->icon.x = WIN_X(w) + WIN_W(w) / 2 - aw->icon.width / 2;
                              aw->icon.y =
                                          WIN_Y(w) + WIN_H(w) / 2 - aw->icon.height / 2;
                        }
                        aw->state = IconicState;      // we're doing this as a hack, it may not be necessary

                        // Store coords in this viewport to omit 3d effect
                        // painting in other viewports
                        if (aw->lastKnownCoords.x != NOT_INITIALIZED)
                        {
                              aw->lastKnownCoords.x = w->attrib.x;
                              aw->lastKnownCoords.y = w->attrib.y;
                        }
                        if (animEnsureModel(w, WindowEventCreate, aw->curAnimEffect))
                              addWindowDamage(w);
                        else
                              postAnimationCleanup(w, TRUE);
                  }
            }

            aw->newState = NormalState;
      }

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

      return status;
}

static void animWindowResizeNotify(CompWindow * w, int dx, int dy, int dwidth,
                                                   int dheight, Bool preview)
{
      ANIM_SCREEN(w->screen);
      ANIM_WINDOW(w);

      if (aw->polygonSet && !aw->animInitialized)
      {
            // to refresh polygon coords
            freePolygonSet(aw);
      }

      if (!preview && (dx || dy || dwidth || dheight))
      {
            if (aw->animRemainingTime > 0)
            {
                  aw->animRemainingTime = 0;
                  postAnimationCleanup(w, TRUE);
            }

            if (aw->model)
            {
                  modelInitObjects(aw->model, WIN_X(w), WIN_Y(w), WIN_W(w),
                                           WIN_H(w));
            }
      }

      aw->state = w->state;

      UNWRAP(as, w->screen, windowResizeNotify);
      (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight, preview);
      WRAP(as, w->screen, windowResizeNotify, animWindowResizeNotify);
}

static void
animWindowMoveNotify(CompWindow * w, int dx, int dy, Bool immediate)
{
      ANIM_SCREEN(w->screen);
      ANIM_WINDOW(w);

      CompWindow *w2;

      if (aw->polygonSet && !aw->animInitialized)
      {
            // to refresh polygon coords
            freePolygonSet(aw);
      }
      if (aw->animRemainingTime > 0 && aw->grabbed)
      {
            aw->animRemainingTime = 0;
            if (as->animInProgress)
            {
                  as->animInProgress = FALSE;
                  for (w2 = w->screen->windows; w2; w2 = w2->next)
                  {
                        AnimWindow *aw2;

                        aw2 = GET_ANIM_WINDOW(w2, as);
                        if (aw2->animRemainingTime > 0)
                        {
                              as->animInProgress = TRUE;
                              break;
                        }
                  }
            }
            postAnimationCleanup(w, TRUE);
      }
      if (aw->model)
      {
            modelInitObjects(aw->model, WIN_X(w), WIN_Y(w), WIN_W(w),
                                     WIN_H(w));
      }

      UNWRAP(as, w->screen, windowMoveNotify);
      (*w->screen->windowMoveNotify) (w, dx, dy, immediate);
      WRAP(as, w->screen, windowMoveNotify, animWindowMoveNotify);
}

static void
animWindowGrabNotify(CompWindow * w,
                               int x, int y, unsigned int state, unsigned int mask)
{
      ANIM_SCREEN(w->screen);
      ANIM_WINDOW(w);

      aw->grabbed = TRUE;

      UNWRAP(as, w->screen, windowGrabNotify);
      (*w->screen->windowGrabNotify) (w, x, y, state, mask);
      WRAP(as, w->screen, windowGrabNotify, animWindowGrabNotify);
}

static void animWindowUngrabNotify(CompWindow * w)
{
      ANIM_SCREEN(w->screen);
      ANIM_WINDOW(w);

      aw->grabbed = FALSE;

      UNWRAP(as, w->screen, windowUngrabNotify);
      (*w->screen->windowUngrabNotify) (w);
      WRAP(as, w->screen, windowUngrabNotify, animWindowUngrabNotify);
}

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

      ANIM_SCREEN(s);

      if (as->animInProgress)
      {
            // Find out if an animation running now uses depth test
            Bool depthUsed = FALSE;
            CompWindow *w;
            for (w = s->windows; w; w = w->next)
            {
                  ANIM_WINDOW(w);
                  if (aw->animRemainingTime > 0 &&
                        aw->polygonSet &&
                        aw->polygonSet->doDepthTest)
                  {
                        depthUsed = TRUE;
                        break;
                  }
            }
            if (depthUsed)
            {
                  glClearDepth(1000.0f);
                  glClear(GL_DEPTH_BUFFER_BIT);
            }
            mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
      }
      UNWRAP(as, s, paintScreen);
      status = (*s->paintScreen) (s, sAttrib, region, output, mask);
      WRAP(as, s, paintScreen, animPaintScreen);

      return status;
}

static Bool animInitDisplay(CompPlugin * p, CompDisplay * d)
{
      AnimDisplay *ad;

      ad = calloc(1, sizeof(AnimDisplay));
      if (!ad)
            return FALSE;

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

      ad->wmHintsAtom = XInternAtom(d->display, "WM_HINTS", FALSE);
      ad->winIconGeometryAtom =
                  XInternAtom(d->display, "_NET_WM_ICON_GEOMETRY", 0);

      WRAP(ad, d, handleEvent, animHandleEvent);

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

      return TRUE;
}

static void animFiniDisplay(CompPlugin * p, CompDisplay * d)
{
      ANIM_DISPLAY(d);

      freeScreenPrivateIndex(d, ad->screenPrivateIndex);

      UNWRAP(ad, d, handleEvent);

      free(ad);
}

static Bool animInitScreen(CompPlugin * p, CompScreen * s)
{
      AnimScreen *as;

      ANIM_DISPLAY(s->display);

      as = calloc(1, sizeof(AnimScreen));
      if (!as)
            return FALSE;

      as->windowPrivateIndex = allocateWindowPrivateIndex(s);
      if (as->windowPrivateIndex < 0)
      {
            free(as);
            return FALSE;
      }
      as->animInProgress = FALSE;
      as->minimizeEffect = ANIM_MINIMIZE_DEFAULT;
      as->unminimizeEffect = ANIM_UNMINIMIZE_DEFAULT;
      as->create1Effect = ANIM_CREATE1_DEFAULT;
      as->create2Effect = ANIM_CREATE2_DEFAULT;
      as->close1Effect = ANIM_CLOSE1_DEFAULT;
      as->close2Effect = ANIM_CLOSE2_DEFAULT;
      as->focusEffect = ANIM_FOCUS_DEFAULT;
      as->shadeEffect = ANIM_SHADE_DEFAULT;
      as->unshadeEffect = ANIM_UNSHADE_DEFAULT;

      as->zoomFC = ANIM_ZOOM_FROM_CENTER_DEFAULT;

      as->ppDisabling = ANIM_DISABLE_PP_FX_DEFAULT;

      animScreenInitOptions(as);

      WRAP(as, s, preparePaintScreen, animPreparePaintScreen);
      WRAP(as, s, donePaintScreen, animDonePaintScreen);
      WRAP(as, s, paintScreen, animPaintScreen);
      WRAP(as, s, paintWindow, animPaintWindow);
      WRAP(as, s, damageWindowRect, animDamageWindowRect);
      WRAP(as, s, addWindowGeometry, animAddWindowGeometry);
      WRAP(as, s, drawWindowTexture, animDrawWindowTexture);
      WRAP(as, s, drawWindowGeometry, animDrawWindowGeometry);
      WRAP(as, s, windowResizeNotify, animWindowResizeNotify);
      WRAP(as, s, windowMoveNotify, animWindowMoveNotify);
      WRAP(as, s, windowGrabNotify, animWindowGrabNotify);
      WRAP(as, s, windowUngrabNotify, animWindowUngrabNotify);

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

      return TRUE;
}

static void animFiniScreen(CompPlugin * p, CompScreen * s)
{
      ANIM_SCREEN(s);

      freeWindowPrivateIndex(s, as->windowPrivateIndex);

      free(as->opt[ANIM_SCREEN_OPTION_MINIMIZE_EFFECT].value.s);
      free(as->opt[ANIM_SCREEN_OPTION_UNMINIMIZE_EFFECT].value.s);
      free(as->opt[ANIM_SCREEN_OPTION_CREATE1_EFFECT].value.s);
      free(as->opt[ANIM_SCREEN_OPTION_CREATE2_EFFECT].value.s);
      free(as->opt[ANIM_SCREEN_OPTION_CLOSE1_EFFECT].value.s);
      free(as->opt[ANIM_SCREEN_OPTION_CLOSE2_EFFECT].value.s);
      free(as->opt[ANIM_SCREEN_OPTION_FOCUS_EFFECT].value.s);
      free(as->opt[ANIM_SCREEN_OPTION_SHADE_EFFECT].value.s);
      free(as->opt[ANIM_SCREEN_OPTION_UNSHADE_EFFECT].value.s);

      UNWRAP(as, s, preparePaintScreen);
      UNWRAP(as, s, donePaintScreen);
      UNWRAP(as, s, paintScreen);
      UNWRAP(as, s, paintWindow);
      UNWRAP(as, s, damageWindowRect);
      UNWRAP(as, s, addWindowGeometry);
      UNWRAP(as, s, drawWindowTexture);
      UNWRAP(as, s, drawWindowGeometry);
      UNWRAP(as, s, windowResizeNotify);
      UNWRAP(as, s, windowMoveNotify);
      UNWRAP(as, s, windowGrabNotify);
      UNWRAP(as, s, windowUngrabNotify);

      free(as);
}

static Bool animInitWindow(CompPlugin * p, CompWindow * w)
{
      AnimWindow *aw;

      ANIM_SCREEN(w->screen);

      aw = calloc(1, sizeof(AnimWindow));
      if (!aw)
            return FALSE;

      aw->animatedAtom = IPCS_GetAtom(IPCS_OBJECT(w), IPCS_BOOL,
                                                      "IS_ANIMATED", TRUE);
      IPCS_SetBool(IPCS_OBJECT(w), aw->animatedAtom, FALSE);
      aw->model = 0;
      aw->state = w->state;
      aw->animRemainingTime = 0;
      aw->animInitialized = FALSE;
      aw->curAnimEffect = AnimEffectNone;
      aw->curWindowEvent = WindowEventNone;
      aw->animOverrideProgressDir = 0;
      w->indexCount = 0;

      aw->polygonSet = NULL;
      aw->lastKnownCoords.x = NOT_INITIALIZED;
      aw->lastKnownCoords.y = NOT_INITIALIZED;

      aw->unmapCnt = 0;
      aw->destroyCnt = 0;

      aw->grabbed = FALSE;

      aw->useDrawRegion = FALSE;
      aw->drawRegion = NULL;

      if (w->shaded)
      {
            aw->state = aw->newState = NormalState;
            aw->nowShaded = TRUE;
      }
      else
      {
            aw->state = aw->newState = animGetWindowState(w);
            aw->nowShaded = FALSE;
      }

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

      return TRUE;
}

static void animFiniWindow(CompPlugin * p, CompWindow * w)
{
      ANIM_WINDOW(w);

      postAnimationCleanup(w, FALSE);

      if (aw->model)
      {
            if (aw->model->magicLampWaves)
                  free(aw->model->magicLampWaves);
            aw->model->magicLampWaves = 0;
            free(aw->model->objects);
            aw->model->objects = 0;
            free(aw->model);
            aw->model = 0;
      }

      while (aw->unmapCnt--)
            unmapWindow(w);

      free(aw);
}

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

      animEffectPropertiesTmp = animEffectProperties;
      return TRUE;
}

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

CompPluginDep animDeps[] = {
      {CompPluginRuleAfter, "decoration"}
};

CompPluginVTable animVTable = {
      "animation",
      N_("Animations"),
      N_("Use various animations as window effects"),
      animInit,
      animFini,
      animInitDisplay,
      animFiniDisplay,
      animInitScreen,
      animFiniScreen,
      animInitWindow,
      animFiniWindow,
      0,
      0,
      animGetScreenOptions,
      animSetScreenOption,
      animDeps,
      sizeof(animDeps) / sizeof(animDeps[0]),
      0,
      0,
      BERYL_ABI_INFO,
      "beryl-plugins",
      "effects",
      0,
      0,
      True,
};

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

Generated by  Doxygen 1.6.0   Back to index