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

water.c

/*
 * Copyright © 2006 Novell, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Author: David Reveman <davidr@novell.com>
 */

#ifdef HAVE_CONFIG_H
#  include "../config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <beryl.h>

#define TEXTURE_SIZE 256

#define PROGRAM_BUMP          0
#define PROGRAM_BUMP_RECT     1
#define PROGRAM_BUMP_SAT      2
#define PROGRAM_BUMP_SAT_RECT 3
#define PROGRAM_WATER         4
#define PROGRAM_NUM           5

#define TEXTURE_NUM  3

#define TINDEX(ws, i) (((ws)->tIndex + (i)) % TEXTURE_NUM)

#define CLAMP(v, min, max) \
    if ((v) > (max))       \
(v) = (max);         \
else if ((v) < (min))  \
(v) = (min)

#define WATER_INITIATE_MODIFIERS_DEFAULT (ControlMask | CompSuperMask)

#define WATER_TOGGLE_RAIN_KEY_DEFAULT       "F9"
#define WATER_TOGGLE_RAIN_MODIFIERS_DEFAULT ShiftMask

#define WATER_TOGGLE_WIPER_KEY_DEFAULT       "F8"
#define WATER_TOGGLE_WIPER_MODIFIERS_DEFAULT ShiftMask

#define WATER_OFFSET_SCALE_DEFAULT    1.0f
#define WATER_OFFSET_SCALE_MIN        0.0f
#define WATER_OFFSET_SCALE_MAX       10.0f
#define WATER_OFFSET_SCALE_PRECISION  0.1f

#define WATER_TITLE_AMPL_DEFAULT      0.1f
#define WATER_TITLE_AMPL_MIN          0.01f
#define WATER_TITLE_AMPL_MAX          1.0f
#define WATER_TITLE_AMPL_PRECISION    0.01f

#define WATER_RAIN_AMPL_DEFAULT       0.8f
#define WATER_RAIN_AMPL_MIN           0.01f
#define WATER_RAIN_AMPL_MAX           1.0f
#define WATER_RAIN_AMPL_PRECISION     0.01f

#define WATER_VISCOS_DEFAULT          0.2f
#define WATER_VISCOS_MIN              0.01f
#define WATER_VISCOS_MAX              0.6f
#define WATER_VISCOS_PRECISION        0.01f

#define WATER_INIT_VERTICES_DEFAULT   3000
#define WATER_INIT_VERTICES_MIN       100
#define WATER_INIT_VERTICES_MAX       10000

#define WATER_RAIN_DELAY_DEFAULT 250
#define WATER_RAIN_DELAY_MIN   10
#define WATER_RAIN_DELAY_MAX   60000      // max of 1 minute...hope this is good

#define WATER_TITLE_WAVE_BELL_DEFAULT FALSE
#define WATER_UNGRAB_WAVE_DEFAULT     FALSE
#define WATER_SHIVER_WIN_DEFAULT      TRUE

static int displayPrivateIndex;

static int waterLastPointerX = 0;
static int waterLastPointerY = 0;

#define WATER_DISPLAY_OPTION_INITIATE      0
#define WATER_DISPLAY_OPTION_TOGGLE_RAIN   1
#define WATER_DISPLAY_OPTION_TOGGLE_WIPER  2
#define WATER_DISPLAY_OPTION_OFFSET_SCALE  3
#define WATER_DISPLAY_OPTION_RAIN_DELAY      4
#define WATER_DISPLAY_OPTION_TITLE_WAVE    5
#define WATER_DISPLAY_OPTION_POINT         6
#define WATER_DISPLAY_OPTION_LINE          7
#define WATER_DISPLAY_OPTION_QUAD          8
#define WATER_DISPLAY_OPTION_WIN           9
#define WATER_DISPLAY_OPTION_UNGRAB_WAVE   10
#define WATER_DISPLAY_OPTION_SHIVER_WIN    11
#define WATER_DISPLAY_OPTION_TITLE_AMPL    12
#define WATER_DISPLAY_OPTION_RAIN_AMPL     13
#define WATER_DISPLAY_OPTION_VISCOSITY     14
#define WATER_DISPLAY_OPTION_INIT_VERTICES 15
#define WATER_DISPLAY_OPTION_NUM           16

typedef struct _WaterDisplay
{
      int screenPrivateIndex;

      CompOption opt[WATER_DISPLAY_OPTION_NUM];

      HandleEventProc handleEvent;
} WaterDisplay;

typedef struct _WaterScreen
{
      PreparePaintScreenProc preparePaintScreen;
      DonePaintScreenProc donePaintScreen;
      DrawWindowTextureProc drawWindowTexture;
      WindowUngrabNotifyProc windowUngrabNotify;

      int grabIndex;
      int width, height;

      GLuint program[PROGRAM_NUM];
      GLuint texture[TEXTURE_NUM];

      int tIndex;
      GLenum target;
      GLfloat tx, ty;

      int count;

      GLuint fbo;
      GLint fboStatus;

      void *data;
      float *d0;
      float *d1;
      unsigned char *t0;

      CompTimeoutHandle rainHandle;
      CompTimeoutHandle wiperHandle;

      float wiperAngle;
      float wiperSpeed;

      Window current;

} WaterScreen;

#define GET_WATER_DISPLAY(d)                          \
    ((WaterDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define WATER_DISPLAY(d)                 \
    WaterDisplay *wd = GET_WATER_DISPLAY (d)

#define GET_WATER_SCREEN(s, wd)                               \
    ((WaterScreen *) (s)->privates[(wd)->screenPrivateIndex].ptr)

#define WATER_SCREEN(s)                                        \
    WaterScreen *ws = GET_WATER_SCREEN (s, GET_WATER_DISPLAY (s->display))

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

static Bool waterRainTimeout(void *closure);

static Bool waterWiperTimeout(void *closure);

static const char *saturateFpString =
            "MUL temp, rgb, { 1.0, 1.0, 1.0, 0.0 };"
            "DP3 temp, temp, program.local[1];"
            "LRP rgb.xyz, program.local[1].w, rgb, temp;";

static const char *bumpFpString =
            "!!ARBfp1.0"
            "PARAM light0color = state.light[0].diffuse;"
            "TEMP rgb, normal, temp, total, bump, offset;"
/* get normal from normal map */
            "TEX normal, fragment.texcoord[1], texture[1], %s;"
/* save height */
            "MOV offset, normal;"
/* remove scale and bias from normal */
            "SUB normal, normal, 0.5;" "MUL normal, normal, 2.0;"
/* normalize the normal map */
            "DP3 temp, normal, normal;"
            "RSQ temp, temp.x;" "MUL normal, normal, temp;"
/* scale down normal by height and constant and use as offset in texture */
            "MUL offset, normal, offset.w;"
            "MUL offset, offset, program.local[0];"
            "ADD offset, fragment.texcoord[0].xyzw, offset.yxzz;"
/* get texture data */
            "TEX rgb, offset, texture[0], %s;"
/* normal dot lightdir, this should eventually be changed to a real light
   vector */
            "DP3 bump, normal, { 0.707, 0.707, 0.0, 0.0 };"
            "MUL bump, bump, light0color;"
/* diffuse per-vertex lighting, opacity and brightness
   and add lightsource bump color */
            "MAD rgb, fragment.color, rgb, bump;"
/* saturation */
            "%s"
/* output */
            "MOV result.color, rgb;" "END";

static const char *waterFpString =
            "!!ARBfp1.0"
            "PARAM param = program.local[0];"
            "ATTRIB t11  = fragment.texcoord[0];"
            "TEMP t01, t21, t10, t12;"
            "TEMP c11, c01, c21, c10, c12;"
            "TEMP prev, v, temp, accel;"
            "TEX prev, t11, texture[0], %s;" "TEX c11,  t11, texture[1], %s;"
/* sample offsets */
            "ADD t01, t11, { - %f, 0.0, 0.0, 0.0 };"
            "ADD t21, t11, {   %f, 0.0, 0.0, 0.0 };"
            "ADD t10, t11, { 0.0, - %f, 0.0, 0.0 };"
            "ADD t12, t11, { 0.0,   %f, 0.0, 0.0 };"
/* fetch nesseccary samples */
            "TEX c01, t01, texture[1], %s;"
            "TEX c21, t21, texture[1], %s;"
            "TEX c10, t10, texture[1], %s;" "TEX c12, t12, texture[1], %s;"
/* x/y normals from height */
            "MOV v, { 0.0, 0.0, 0.75, 0.0 };"
            "SUB v.x, c12.w, c10.w;" "SUB v.y, c01.w, c21.w;"
/* bumpiness */
            "MUL v, v, 1.5;"
/* normalize */
            "MAD temp, v.x, v.x, 1.0;"
            "MAD temp, v.y, v.y, temp;" "RSQ temp, temp.x;" "MUL v, v, temp;"
/* add scale and bias to normal */
            "MAD v, v, 0.5, 0.5;"
/* done with computing the normal, continue with computing the next
   height value */
            "ADD accel, c10, c12;"
            "ADD accel, c01, accel;"
            "ADD accel, c21, accel;" "MAD accel, -4.0, c11, accel;"
/* store new height in alpha component */
            "MAD v.w, 2.0, c11, -prev.w;" "MAD v.w, accel, param.x, v.w;"
/* fade out height */
            "MUL v.w, v.w, param.y;" "MOV result.color, v;" "END";

static int
loadFragmentProgram(CompScreen * s, GLuint * program, const char *string)
{
      GLint errorPos;

      /* clear errors */
      glGetError();

      if (!*program)
            (*s->genPrograms) (1, program);

      (*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, *program);
      (*s->programString) (GL_FRAGMENT_PROGRAM_ARB,
                                     GL_PROGRAM_FORMAT_ASCII_ARB, strlen(string), string);

      glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
      if (glGetError() != GL_NO_ERROR || errorPos != -1)
      {
            fprintf(stderr,
                        "%s: water: failed to load bump map program\n",
                        getProgramName());

            (*s->deletePrograms) (1, program);
            *program = 0;

            return 0;
      }

      return 1;
}

static int loadWaterProgram(CompScreen * s)
{
      char buffer[1024];

      WATER_SCREEN(s);

      if (ws->target == GL_TEXTURE_2D)
            sprintf(buffer, waterFpString,
                        "2D", "2D",
                        1.0f / ws->width, 1.0f / ws->width,
                        1.0f / ws->height, 1.0f / ws->height, "2D", "2D", "2D", "2D");
      else
            sprintf(buffer, waterFpString,
                        "RECT", "RECT",
                        1.0f, 1.0f, 1.0f, 1.0f, "RECT", "RECT", "RECT", "RECT");

      return loadFragmentProgram(s, &ws->program[PROGRAM_WATER], buffer);
}

static int loadBumpMapProgram(CompScreen * s, int program)
{
      const char *normalTarget = "2D";
      const char *textureTarget = "2D";
      const char *saturation = "";
      char buffer[1024];

      WATER_SCREEN(s);

      if (!s->fragmentProgram)
      {
            fprintf(stderr,
                        "%s: water: GL_ARB_fragment_program is missing\n",
                        getProgramName());
            return 0;
      }

      switch (program)
      {
      case PROGRAM_BUMP_SAT:
            saturation = saturateFpString;
            /* fall-through */
      case PROGRAM_BUMP:
            break;
      case PROGRAM_BUMP_SAT_RECT:
            saturation = saturateFpString;
            /* fall-through */
      case PROGRAM_BUMP_RECT:
            textureTarget = "RECT";
            break;
      }

      if (ws->target != GL_TEXTURE_2D)
            normalTarget = "RECT";

      sprintf(buffer, bumpFpString, normalTarget, textureTarget, saturation);

      return loadFragmentProgram(s, &ws->program[program], buffer);
}

static int ensureBumpMapProgram(CompScreen * s, int program)
{
      WATER_SCREEN(s);

      if (!ws->program[program])
            return loadBumpMapProgram(s, program);

      return 1;
}

static void allocTexture(CompScreen * s, int index)
{
      WATER_SCREEN(s);

      glGenTextures(1, &ws->texture[index]);
      glBindTexture(ws->target, ws->texture[index]);

      glTexParameteri(ws->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(ws->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(ws->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameteri(ws->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

      glTexImage2D(ws->target, 0, GL_RGBA, ws->width, ws->height, 0, GL_BGRA,
#if IMAGE_BYTE_ORDER == MSBFirst
                         GL_UNSIGNED_INT_8_8_8_8_REV,
#else
                         GL_UNSIGNED_BYTE,
#endif
                         ws->t0);
}

static int fboPrologue(CompScreen * s, int tIndex)
{
      WATER_SCREEN(s);

      if (!ws->fbo)
            return 0;

      if (!ws->texture[tIndex])
            allocTexture(s, tIndex);

      (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, ws->fbo);

      (*s->framebufferTexture2D) (GL_FRAMEBUFFER_EXT,
                                                GL_COLOR_ATTACHMENT0_EXT,
                                                ws->target, ws->texture[tIndex], 0);

      glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
      glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);

      /* check status the first time */
      if (!ws->fboStatus)
      {
            ws->fboStatus = (*s->checkFramebufferStatus) (GL_FRAMEBUFFER_EXT);
            if (ws->fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT)
            {
                  fprintf(stderr,
                              "%s: water: framebuffer incomplete\n", getProgramName());

                  (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0);
                  (*s->deleteFramebuffers) (1, &ws->fbo);

                  glDrawBuffer(GL_BACK);
                  glReadBuffer(GL_BACK);

                  ws->fbo = 0;

                  return 0;
            }
      }

      glViewport(0, 0, ws->width, ws->height);
      glMatrixMode(GL_PROJECTION);
      glPushMatrix();
      glLoadIdentity();
      glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
      glMatrixMode(GL_MODELVIEW);
      glPushMatrix();
      glLoadIdentity();

      return 1;
}

static void fboEpilogue(CompScreen * s)
{
      (*s->bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0);

      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      glDepthRange(0, 1);
      glViewport(-1, -1, 2, 2);
      glRasterPos2f(0, 0);

      s->rasterX = s->rasterY = 0;

      setDefaultViewport(s);
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);
      glPopMatrix();

      glDrawBuffer(GL_BACK);
      glReadBuffer(GL_BACK);
}

static int fboUpdate(CompScreen * s, float dt, float fade)
{
      WATER_SCREEN(s);
      WATER_DISPLAY(s->display);

      if (!fboPrologue(s, TINDEX(ws, 1)))
            return 0;

      if (!ws->texture[TINDEX(ws, 2)])
            allocTexture(s, TINDEX(ws, 2));

      if (!ws->texture[TINDEX(ws, 0)])
            allocTexture(s, TINDEX(ws, 0));

      glEnable(ws->target);

      (*s->activeTexture) (GL_TEXTURE0_ARB);
      glBindTexture(ws->target, ws->texture[TINDEX(ws, 2)]);

      glTexParameteri(ws->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexParameteri(ws->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      (*s->activeTexture) (GL_TEXTURE1_ARB);
      glBindTexture(ws->target, ws->texture[TINDEX(ws, 0)]);
      glTexParameteri(ws->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexParameteri(ws->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

      glEnable(GL_FRAGMENT_PROGRAM_ARB);
      (*s->bindProgram) (GL_FRAGMENT_PROGRAM_ARB, ws->program[PROGRAM_WATER]);

      (*s->programLocalParameter4f) (GL_FRAGMENT_PROGRAM_ARB, 0,
                                                   dt *
                                                   wd->
                                                   opt[WATER_DISPLAY_OPTION_VISCOSITY].
                                                   value.f, fade, 1.0f, 1.0f);

      glBegin(GL_QUADS);

      glTexCoord2f(0.0f, 0.0f);
      glVertex2f(0.0f, 0.0f);
      glTexCoord2f(ws->tx, 0.0f);
      glVertex2f(1.0f, 0.0f);
      glTexCoord2f(ws->tx, ws->ty);
      glVertex2f(1.0f, 1.0f);
      glTexCoord2f(0.0f, ws->ty);
      glVertex2f(0.0f, 1.0f);

      glEnd();

      glDisable(GL_FRAGMENT_PROGRAM_ARB);

      glBindTexture(ws->target, 0);
      glTexParameteri(ws->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(ws->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      (*s->activeTexture) (GL_TEXTURE0_ARB);
      glTexParameteri(ws->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(ws->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glBindTexture(ws->target, 0);

      fboEpilogue(s);

      /* increment texture index */
      ws->tIndex = TINDEX(ws, 1);

      return 1;
}

static int
fboVertices(CompScreen * s, GLenum type, XPoint * p, int n, float v)
{
      WATER_SCREEN(s);

      if (!fboPrologue(s, TINDEX(ws, 0)))
            return 0;

      glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
      glColor4f(0.0f, 0.0f, 0.0f, v);

      glPointSize(3.0f);
      glLineWidth(1.0f);

      glScalef(1.0f / ws->width, 1.0f / ws->height, 1.0);
      glTranslatef(0.5f, 0.5f, 0.0f);

      glBegin(type);

      while (n--)
      {
            glVertex2i(p->x, p->y);
            p++;
      }

      glEnd();

      glColor4usv(defaultColor);
      glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

      fboEpilogue(s);

      return 1;
}

static void softwareUpdate(CompScreen * s, float dt, float fade)
{
      float *dTmp;
      int i, j;
      float v0, v1, inv;
      float accel, value;
      unsigned char *t0, *t;
      int dWidth, dHeight;
      float *d01, *d10, *d11, *d12;

      WATER_SCREEN(s);
      WATER_DISPLAY(s->display);

      if (!ws->texture[TINDEX(ws, 0)])
            allocTexture(s, TINDEX(ws, 0));

      dt *= wd->opt[WATER_DISPLAY_OPTION_VISCOSITY].value.f * 2.0f;
      fade *= 0.99f;

      dWidth = ws->width + 2;
      dHeight = ws->height + 2;

#define D(d, j) (*((d) + (j)))

      d01 = ws->d0 + dWidth;
      d10 = ws->d1;
      d11 = d10 + dWidth;
      d12 = d11 + dWidth;

      for (i = 1; i < dHeight - 1; i++)
      {
            for (j = 1; j < dWidth - 1; j++)
            {
                  accel = dt * (D(d10, j) +
                                      D(d12, j) +
                                      D(d11, j - 1) + D(d11, j + 1) - 4.0f * D(d11, j));

                  value = (2.0f * D(d11, j) - D(d01, j) + accel) * fade;

                  CLAMP(value, 0.0f, 1.0f);

                  D(d01, j) = value;
            }

            d01 += dWidth;
            d10 += dWidth;
            d11 += dWidth;
            d12 += dWidth;
      }

      /* update border */
      memcpy(ws->d0, ws->d0 + dWidth, dWidth * sizeof(GLfloat));
      memcpy(ws->d0 + dWidth * (dHeight - 1),
               ws->d0 + dWidth * (dHeight - 2), dWidth * sizeof(GLfloat));

      d01 = ws->d0 + dWidth;

      for (i = 1; i < dHeight - 1; i++)
      {
            D(d01, 0) = D(d01, 1);
            D(d01, dWidth - 1) = D(d01, dWidth - 2);

            d01 += dWidth;
      }

      d10 = ws->d1;
      d11 = d10 + dWidth;
      d12 = d11 + dWidth;

      t0 = ws->t0;

      /* update texture */
      for (i = 0; i < ws->height; i++)
      {
            for (j = 0; j < ws->width; j++)
            {
                  v0 = (D(d12, j) - D(d10, j)) * 1.5f;
                  v1 = (D(d11, j - 1) - D(d11, j + 1)) * 1.5f;

                  /* 0.5 for scale */
                  inv = 0.5f / sqrtf(v0 * v0 + v1 * v1 + 1.0f);

                  /* add scale and bias to normal */
                  v0 = v0 * inv + 0.5f;
                  v1 = v1 * inv + 0.5f;

                  /* store normal map in RGB components */
                  t = t0 + (j * 4);
                  t[0] = (unsigned char)((inv + 0.5f) * 255.0f);
                  t[1] = (unsigned char)(v1 * 255.0f);
                  t[2] = (unsigned char)(v0 * 255.0f);

                  /* store height in A component */
                  t[3] = (unsigned char)(D(d11, j) * 255.0f);
            }

            d10 += dWidth;
            d11 += dWidth;
            d12 += dWidth;

            t0 += ws->width * 4;
      }

#undef D

      /* swap height maps */
      dTmp = ws->d0;
      ws->d0 = ws->d1;
      ws->d1 = dTmp;

      if (ws->texture[TINDEX(ws, 0)])
      {
            glBindTexture(ws->target, ws->texture[TINDEX(ws, 0)]);
            glTexImage2D(ws->target,
                               0, GL_RGBA, ws->width, ws->height, 0, GL_BGRA,
#if IMAGE_BYTE_ORDER == MSBFirst
                               GL_UNSIGNED_INT_8_8_8_8_REV,
#else
                               GL_UNSIGNED_BYTE,
#endif
                               ws->t0);
      }
}


#define SET(x, y, v) *((ws->d1) + (ws->width + 2) * (y + 1) + (x + 1)) = (v)

static void softwarePoints(CompScreen * s, XPoint * p, int n, float add)
{
      WATER_SCREEN(s);

      while (n--)
      {
            SET(p->x - 1, p->y - 1, add);
            SET(p->x, p->y - 1, add);
            SET(p->x + 1, p->y - 1, add);

            SET(p->x - 1, p->y, add);
            SET(p->x, p->y, add);
            SET(p->x + 1, p->y, add);

            SET(p->x - 1, p->y + 1, add);
            SET(p->x, p->y + 1, add);
            SET(p->x + 1, p->y + 1, add);

            p++;
      }
}

/* bresenham */
static void softwareLines(CompScreen * s, XPoint * p, int n, float v)
{
      int x1, y1, x2, y2;
      Bool steep;
      int tmp;
      int deltaX, deltaY;
      int error = 0;
      int yStep;
      int x, y;

      WATER_SCREEN(s);

#define SWAP(v0, v1) \
    tmp = v0;          \
    v0 = v1;           \
    v1 = tmp

      while (n > 1)
      {
            x1 = p->x;
            y1 = p->y;

            p++;
            n--;

            x2 = p->x;
            y2 = p->y;

            p++;
            n--;

            steep = abs(y2 - y1) > abs(x2 - x1);
            if (steep)
            {
                  SWAP(x1, y1);
                  SWAP(x2, y2);
            }

            if (x1 > x2)
            {
                  SWAP(x1, x2);
                  SWAP(y1, y2);
            }
#undef SWAP

            deltaX = x2 - x1;
            deltaY = abs(y2 - y1);

            y = y1;
            if (y1 < y2)
                  yStep = 1;
            else
                  yStep = -1;

            for (x = x1; x <= x2; x++)
            {
                  if (steep)
                  {
                        SET(y, x, v);
                  }
                  else
                  {
                        SET(x, y, v);
                  }

                  error += deltaY;
                  if (2 * error >= deltaX)
                  {
                        y += yStep;
                        error -= deltaX;
                  }
            }
      }
}

#undef SET
static void softwareQuads(CompScreen * s, XPoint * p, int n, float v)
{
      /* this seemed the most logical way to handle this */

      /* the quad is defined by its four corners
       * rather than redesign a new algorithm to
       * handlle this, I just munge the data to work
       * with softwareLines
       */
      XPoint pq[8];

      pq[0] = p[0];
      pq[1] = p[1];

      pq[2] = p[1];
      pq[3] = p[2];

      pq[4] = p[2];
      pq[5] = p[3];

      pq[6] = p[3];
      pq[7] = p[0];

      softwareLines(s, pq, 8, v);
}

static void
softwareVertices(CompScreen * s, GLenum type, XPoint * p, int n, float v)
{
      switch (type)
      {
      case GL_POINTS:
            softwarePoints(s, p, n, v);
            break;
      case GL_LINES:
            softwareLines(s, p, n, v);
            break;
      case GL_QUADS:
            softwareQuads(s, p, n, v);
            break;
      }
}

static void waterUpdate(CompScreen * s, float dt)
{
      GLfloat fade = 1.0f;

      WATER_SCREEN(s);

      if (ws->count < 1000)
      {
            if (ws->count > 1)
                  fade = 0.90f + ws->count / 10000.0f;
            else
                  fade = 0.0f;
      }

      if (!fboUpdate(s, dt, fade))
            softwareUpdate(s, dt, fade);
}

static void scaleVertices(CompScreen * s, XPoint * p, int n)
{
      WATER_SCREEN(s);

      while (n--)
      {
            p[n].x = (ws->width * p[n].x) / s->width;
            p[n].y = (ws->height * p[n].y) / s->height;
      }
}

static void
waterVertices(CompScreen * s, GLenum type, XPoint * p, int n, float v)
{
      WATER_SCREEN(s);
      WATER_DISPLAY(s->display);

      if (!ensureBumpMapProgram(s, PROGRAM_BUMP))
            return;

      scaleVertices(s, p, n);

      if (!fboVertices(s, type, p, n, v))
            softwareVertices(s, type, p, n, v);

      if (ws->count < wd->opt[WATER_DISPLAY_OPTION_INIT_VERTICES].value.i)
            ws->count = wd->opt[WATER_DISPLAY_OPTION_INIT_VERTICES].value.i;
}

static Bool waterRainTimeout(void *closure)
{
      CompScreen *s = closure;
      XPoint p;

      WATER_SCREEN(s);
      WATER_DISPLAY(s->display);

      if (ws->current)
            ws->current = 0;

      p.x = (int)(s->width * (rand() / (float)RAND_MAX));
      p.y = (int)(s->height * (rand() / (float)RAND_MAX));

      waterVertices(s, GL_POINTS, &p, 1,
                          wd->opt[WATER_DISPLAY_OPTION_RAIN_AMPL].value.f *
                          (rand() / (float)RAND_MAX));

      damageScreen(s);

      return TRUE;
}

static Bool waterWiperTimeout(void *closure)
{
      CompScreen *s = closure;

      WATER_SCREEN(s);

      if (ws->count)
      {
            if (ws->wiperAngle == 0.0f)
                  ws->wiperSpeed = 2.5f;
            else if (ws->wiperAngle == 180.0f)
                  ws->wiperSpeed = -2.5f;
            if (ws->current)
                  ws->current = 0;
      }

      return TRUE;
}

static void waterReset(CompScreen * s)
{
      int size, i, j;

      WATER_SCREEN(s);

      ws->height = TEXTURE_SIZE;
      ws->width = (ws->height * s->width) / s->height;

      if (s->textureNonPowerOfTwo ||
            (POWER_OF_TWO(ws->width) && POWER_OF_TWO(ws->height)))
      {
            ws->target = GL_TEXTURE_2D;
            ws->tx = ws->ty = 1.0f;
      }
      else
      {
            ws->target = GL_TEXTURE_RECTANGLE_NV;
            ws->tx = ws->width;
            ws->ty = ws->height;
      }

      if (!loadBumpMapProgram(s, PROGRAM_BUMP))
            return;

      if (s->fbo)
      {
            loadWaterProgram(s);
            if (!ws->fbo)
                  (*s->genFramebuffers) (1, &ws->fbo);
      }

      ws->fboStatus = 0;

      for (i = 0; i < TEXTURE_NUM; i++)
      {
            if (ws->texture[i])
            {
                  glDeleteTextures(1, &ws->texture[i]);
                  ws->texture[i] = 0;
            }
      }

      if (ws->data)
            free(ws->data);

      size = (ws->width + 2) * (ws->height + 2);

      ws->data = calloc(1, (sizeof(float) * size * 2) +
                                (sizeof(GLubyte) * ws->width * ws->height * 4));
      if (!ws->data)
            return;

      ws->d0 = ws->data;
      ws->d1 = (ws->d0 + (size));
      ws->t0 = (unsigned char *)(ws->d1 + (size));

      for (i = 0; i < ws->height; i++)
      {
            for (j = 0; j < ws->width; j++)
            {
                  (ws->t0 + (ws->width * 4 * i + j * 4))[0] = 0xff;
            }
      }
}

static void
waterDrawWindowTexture(CompWindow * w,
                                 CompTexture * texture,
                                 const WindowPaintAttrib * attrib, unsigned int mask)
{
      WATER_DISPLAY(w->screen->display);
      WATER_SCREEN(w->screen);

      if (w->id == ws->current)
      {
            UNWRAP(ws, w->screen, drawWindowTexture);
            (*w->screen->drawWindowTexture) (w, texture, attrib, mask);
            WRAP(ws, w->screen, drawWindowTexture, waterDrawWindowTexture);

            return;
      }

      if (ws->count)
      {
            Bool lighting = w->screen->lighting;
            GLfloat plane[4];
            int program;

            screenLighting(w->screen, TRUE);

            glPushMatrix();

            enableTexture(w->screen, texture,
                                w->screen->filter[WINDOW_TRANS_FILTER]);

            (*w->screen->activeTexture) (GL_TEXTURE1_ARB);

            glBindTexture(ws->target, ws->texture[TINDEX(ws, 0)]);

            plane[1] = plane[2] = 0.0f;
            plane[0] = ws->tx / (GLfloat) w->screen->width;
            plane[3] = 0.0f;

            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGenfv(GL_S, GL_EYE_PLANE, plane);
            glEnable(GL_TEXTURE_GEN_S);

            plane[0] = plane[2] = 0.0f;
            plane[1] = ws->ty / (GLfloat) w->screen->height;
            plane[3] = 0.0f;

            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGenfv(GL_T, GL_EYE_PLANE, plane);
            glEnable(GL_TEXTURE_GEN_T);

            if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
            {
                  glTranslatef(w->attrib.x, w->attrib.y, 0.0f);
                  glScalef(attrib->xScale, attrib->yScale, 0.0f);
                  glTranslatef(attrib->xTranslate / attrib->xScale -
                                     w->attrib.x,
                                     attrib->yTranslate / attrib->yScale -
                                     w->attrib.y, 0.0f);
            }

            glEnable(GL_FRAGMENT_PROGRAM_ARB);

            if (w->screen->canDoSaturated && attrib->saturation != COLOR)
            {
                  if (texture->target == GL_TEXTURE_2D)
                        program = PROGRAM_BUMP_SAT;
                  else
                        program = PROGRAM_BUMP_SAT_RECT;

                  ensureBumpMapProgram(w->screen, program);

                  (*w->screen->bindProgram) (GL_FRAGMENT_PROGRAM_ARB,
                                                         ws->program[program]);

                  (*w->screen->
                   programLocalParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
                                                         0,
                                                         texture->matrix.yy *
                                                         wd->
                                                         opt
                                                         [WATER_DISPLAY_OPTION_OFFSET_SCALE].
                                                         value.f * 50.0f,
                                                         -texture->matrix.xx *
                                                         wd->
                                                         opt
                                                         [WATER_DISPLAY_OPTION_OFFSET_SCALE].
                                                         value.f * 50.0f, 0.0f, 0.0f);

                  (*w->screen->
                   programLocalParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
                                                         1,
                                                         RED_SATURATION_WEIGHT,
                                                         GREEN_SATURATION_WEIGHT,
                                                         BLUE_SATURATION_WEIGHT,
                                                         attrib->saturation / 65535.0f);
            }
            else
            {
                  if (texture->target == GL_TEXTURE_2D)
                        program = PROGRAM_BUMP;
                  else
                        program = PROGRAM_BUMP_RECT;

                  ensureBumpMapProgram(w->screen, program);

                  (*w->screen->bindProgram) (GL_FRAGMENT_PROGRAM_ARB,
                                                         ws->program[program]);

                  (*w->screen->
                   programLocalParameter4f) (GL_FRAGMENT_PROGRAM_ARB,
                                                         0,
                                                         texture->matrix.yy *
                                                         wd->
                                                         opt
                                                         [WATER_DISPLAY_OPTION_OFFSET_SCALE].
                                                         value.f * 50.0f,
                                                         -texture->matrix.xx *
                                                         wd->
                                                         opt
                                                         [WATER_DISPLAY_OPTION_OFFSET_SCALE].
                                                         value.f * 50.0f, 0.0f, 0.0f);
            }

            if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
            {
                  glEnable(GL_BLEND);
                  if (attrib->opacity != OPAQUE || attrib->brightness != BRIGHT)
                  {
                        GLushort color;

                        color = (attrib->opacity * attrib->brightness) >> 16;

                        glColor4us(color, color, color, attrib->opacity);

                        (*w->screen->drawWindowGeometry) (w);

                        glColor4usv(defaultColor);
                  }
                  else
                  {
                        (*w->screen->drawWindowGeometry) (w);
                  }

                  glDisable(GL_BLEND);
            }
            else if (attrib->brightness != BRIGHT)
            {
                  screenTexEnvMode(w->screen, GL_MODULATE);
                  glColor4us(attrib->brightness, attrib->brightness,
                                 attrib->brightness, BRIGHT);

                  (*w->screen->drawWindowGeometry) (w);

                  glColor4usv(defaultColor);
                  screenTexEnvMode(w->screen, GL_REPLACE);
            }
            else
            {
                  (*w->screen->drawWindowGeometry) (w);
            }

            glDisable(GL_FRAGMENT_PROGRAM_ARB);

            glDisable(GL_TEXTURE_GEN_T);
            glDisable(GL_TEXTURE_GEN_S);

            glBindTexture(ws->target, 0);
            (*w->screen->activeTexture) (GL_TEXTURE0_ARB);
            disableTexture(w->screen, texture);

            glPopMatrix();

            screenLighting(w->screen, lighting);
      }
      else
      {
            UNWRAP(ws, w->screen, drawWindowTexture);
            (*w->screen->drawWindowTexture) (w, texture, attrib, mask);
            WRAP(ws, w->screen, drawWindowTexture, waterDrawWindowTexture);
      }
}

/* TODO: a way to control the speed */
static void waterPreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
      WATER_SCREEN(s);

      if (ws->count)
      {
            ws->count -= 10;
            if (ws->count < 0)
                  ws->count = 0;

            if (ws->wiperHandle)
            {
                  float step, angle0, angle1;
                  Bool wipe = FALSE;
                  XPoint p[3];

                  p[1].x = s->width / 2;
                  p[1].y = s->height;

                  step = ws->wiperSpeed * msSinceLastPaint / 20.0f;

                  if (ws->wiperSpeed > 0.0f)
                  {
                        if (ws->wiperAngle < 180.0f)
                        {
                              angle0 = ws->wiperAngle;

                              ws->wiperAngle += step;
                              ws->wiperAngle = MIN(ws->wiperAngle, 180.0f);

                              angle1 = ws->wiperAngle;

                              wipe = TRUE;
                        }
                  }
                  else
                  {
                        if (ws->wiperAngle > 0.0f)
                        {
                              angle1 = ws->wiperAngle;

                              ws->wiperAngle += step;
                              ws->wiperAngle = MAX(ws->wiperAngle, 0.0f);

                              angle0 = ws->wiperAngle;

                              wipe = TRUE;
                        }
                  }

#define TAN(a) (tanf ((a) * (M_PI / 180.0f)))

                  if (wipe)
                  {
                        if (angle0 > 0.0f)
                        {
                              p[2].x = s->width / 2 - s->height / TAN(angle0);
                              p[2].y = 0;
                        }
                        else
                        {
                              p[2].x = 0;
                              p[2].y = s->height;
                        }

                        if (angle1 < 180.0f)
                        {
                              p[0].x = s->width / 2 - s->height / TAN(angle1);
                              p[0].y = 0;
                        }
                        else
                        {
                              p[0].x = s->width;
                              p[0].y = s->height;
                        }

                        /* software rasterizer doesn't support triangles yet so wiper
                           effect will only work with FBOs right now */
                        waterVertices(s, GL_TRIANGLES, p, 3, 0.0f);
                  }
#undef TAN

            }

            waterUpdate(s, 0.8f);
      }

      UNWRAP(ws, s, preparePaintScreen);
      (*s->preparePaintScreen) (s, msSinceLastPaint);
      WRAP(ws, s, preparePaintScreen, waterPreparePaintScreen);
}

static void waterDonePaintScreen(CompScreen * s)
{
      WATER_SCREEN(s);

      if (ws->count)
            damageScreen(s);
      else
            ws->current = 0;

      UNWRAP(ws, s, donePaintScreen);
      (*s->donePaintScreen) (s);
      WRAP(ws, s, donePaintScreen, waterDonePaintScreen);
}

static void waterHandleMotionEvent(CompDisplay * d, Window root)
{
      CompScreen *s;

      s = findScreenAtDisplay(d, root);
      if (s)
      {
            WATER_SCREEN(s);

            if (ws->grabIndex)
            {
                  XPoint p[2];

                  p[0].x = waterLastPointerX;
                  p[0].y = waterLastPointerY;

                  p[1].x = waterLastPointerX = d->pointerX;
                  p[1].y = waterLastPointerY = d->pointerY;

                  waterVertices(s, GL_LINES, p, 2, 0.2f);

                  damageScreen(s);
            }
      }
}

static Bool
waterInitiate(CompDisplay * d,
                    CompAction * action,
                    CompActionState state, CompOption * option, int nOption)
{
      CompScreen *s;
      unsigned int ui;
      Window root, child;
      int xRoot, yRoot, i;

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

            if (otherScreenGrabExist(s, "water", 0))
                  continue;

            if (!ws->grabIndex)
                  ws->grabIndex = pushScreenGrab(s, None, "water");

            if (ws->current)
                  ws->current = 0;

            if (XQueryPointer
                  (d->display, s->root, &root, &child, &xRoot, &yRoot, &i, &i, &ui))
            {
                  XPoint p;

                  p.x = waterLastPointerX = xRoot;
                  p.y = waterLastPointerY = yRoot;

                  waterVertices(s, GL_POINTS, &p, 1, 0.8f);

                  damageScreen(s);
            }
      }

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

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

      return FALSE;
}

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

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

            if (ws->grabIndex)
            {
                  removeScreenGrab(s, ws->grabIndex, 0);
                  ws->grabIndex = 0;
            }
            ws->current = 0;
      }

      return FALSE;
}

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

      WATER_DISPLAY(d);

      s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
      if (s)
      {
            WATER_SCREEN(s);

            if (!ws->rainHandle)
            {
                  int delay;

                  delay = wd->opt[WATER_DISPLAY_OPTION_RAIN_DELAY].value.i;
                  ws->rainHandle = compAddTimeout(delay, waterRainTimeout, s);
            }
            else
            {
                  compRemoveTimeout(ws->rainHandle);
                  ws->rainHandle = 0;
            }
      }

      return FALSE;
}

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

      s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
      if (s)
      {
            WATER_SCREEN(s);

            if (!ws->wiperHandle)
            {
                  int delay;

                  delay = 2000;
                  ws->wiperHandle = compAddTimeout(delay, waterWiperTimeout, s);
            }
            else
            {
                  compRemoveTimeout(ws->wiperHandle);
                  ws->wiperHandle = 0;
            }
      }

      return FALSE;
}

static Bool
waterTitleWave(CompDisplay * d,
                     CompAction * action,
                     CompActionState state, CompOption * option, int nOption)
{
      CompWindow *w;
      int xid;

      xid = getIntOptionNamed(option, nOption, "window", d->activeWindow);

      w = findWindowAtDisplay(d, xid);
      if (w)
      {
            WATER_DISPLAY(w->screen->display);
            XPoint p[2];

            p[0].x = w->attrib.x - w->input.left;
            p[0].y = w->attrib.y - (w->input.top >> 2);

            p[1].x = w->attrib.x + w->width + w->input.right;
            p[1].y = p[0].y;

            waterVertices(w->screen, GL_LINES, p, 2,
                                wd->opt[WATER_DISPLAY_OPTION_TITLE_AMPL].value.f);

            damageScreen(w->screen);
      }

      return FALSE;
}

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

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

      s = findScreenAtDisplay(d, xid);
      if (s)
      {
            XPoint p;
            float amp;

            p.x = getIntOptionNamed(option, nOption, "x", s->width / 2);
            p.y = getIntOptionNamed(option, nOption, "y", s->height / 2);

            amp = getFloatOptionNamed(option, nOption, "amplitude", 0.5f);

            waterVertices(s, GL_POINTS, &p, 1, amp);

            damageScreen(s);
      }

      return FALSE;
}

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

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

      s = findScreenAtDisplay(d, xid);
      if (s)
      {
            XPoint p[2];
            float amp;

            p[0].x = getIntOptionNamed(option, nOption, "x0", s->width / 4);
            p[0].y = getIntOptionNamed(option, nOption, "y0", s->height / 2);

            p[1].x = getIntOptionNamed(option, nOption, "x1",
                                                   s->width - s->width / 4);
            p[1].y = getIntOptionNamed(option, nOption, "y1", s->height / 2);


            amp = getFloatOptionNamed(option, nOption, "amplitude", 0.25f);

            waterVertices(s, GL_LINES, p, 2, amp);

            damageScreen(s);
      }

      return FALSE;
}



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

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

      s = findScreenAtDisplay(d, xid);
      if (s)
      {
            XPoint p[4];
            float amp;

            /* top left corner */
            p[0].x = getIntOptionNamed(option, nOption, "x0", s->width / 4);
            p[0].y = getIntOptionNamed(option, nOption, "y0", s->height / 2);

            /* top right corner */
            p[1].x = getIntOptionNamed(option, nOption, "x1",
                                                   s->width - s->width / 4);
            p[1].y = getIntOptionNamed(option, nOption, "y1", s->height / 2);

            /* bottom right corner */
            p[2].x = getIntOptionNamed(option, nOption, "x2", s->width / 4);
            p[2].y = getIntOptionNamed(option, nOption, "y2", s->height / 2);

            /* bottom left corner */
            p[3].x = getIntOptionNamed(option, nOption, "x3",
                                                   s->width - s->width / 4);
            p[3].y = getIntOptionNamed(option, nOption, "y3", s->height / 2);

            amp = getFloatOptionNamed(option, nOption, "amplitude", 0.25f);

            waterVertices(s, GL_QUADS, p, 4, amp);

            damageScreen(s);
      }

      return FALSE;
}


static Bool
waterWin(CompDisplay * d,
             CompAction * action,
             CompActionState state, CompOption * option, int nOption)
{
      CompWindow *w;
      int xid;

      xid = getIntOptionNamed(option, nOption, "window", d->activeWindow);

      if (!xid)
            xid = d->activeWindow;

      w = findWindowAtDisplay(d, xid);
      if (w)
      {
            CompOption o[10];

            WATER_DISPLAY(d);
            WATER_SCREEN(w->screen);

            ws->current = xid;

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

            /* top left */
            o[1].type = CompOptionTypeInt;
            o[1].name = "x0";
            o[1].value.i = w->attrib.x - w->input.left;

            o[2].type = CompOptionTypeInt;
            o[2].name = "y0";
            o[2].value.i = w->attrib.y - w->input.top;

            /* top right */
            o[3].type = CompOptionTypeInt;
            o[3].name = "x1";
            o[3].value.i = w->attrib.x + w->width + w->input.right;

            o[4].type = CompOptionTypeInt;
            o[4].name = "y1";
            o[4].value.i = w->attrib.y - w->input.top;

            /* bottom right */
            o[5].type = CompOptionTypeInt;
            o[5].name = "x2";
            o[5].value.i = w->attrib.x + w->width + w->input.right;

            o[6].type = CompOptionTypeInt;
            o[6].name = "y2";
            o[6].value.i = w->attrib.y + w->height + w->input.bottom;

            /* bottom left */
            o[7].type = CompOptionTypeInt;
            o[7].name = "x3";
            o[7].value.i = w->attrib.x - w->input.left;

            o[8].type = CompOptionTypeInt;
            o[8].name = "y3";
            o[8].value.i = w->attrib.y + w->height + w->input.bottom;

            o[9].type = CompOptionTypeFloat;
            o[9].name = "amplitude";
            o[9].value.f =
                        getFloatOptionNamed(option, nOption, "amplitude", 0.25f);

            if (wd->opt[WATER_DISPLAY_OPTION_SHIVER_WIN].value.b)
            {
                  /* Send a client message to wobbly */
                  /* I'm not sure if this is the best way to do this, but it is simple and it works */
                  XClientMessageEvent xev;

                  xev.type = ClientMessage;
                  xev.window = w->id;
                  xev.message_type =
                              XInternAtom(w->screen->display->display,
                                                "_BERYL_WOBBLY_SHIVER", 0);
                  xev.format = 32;

                  XSendEvent(w->screen->display->display,
                                 w->screen->root, 0, StructureNotifyMask,
                                 (XEvent *) & xev);
            }

            waterQuad(d, NULL, 0, o, 10);
      }

      return FALSE;
}

static void waterWindowUngrabNotify(CompWindow * w)
{

      WATER_DISPLAY(w->screen->display);
      WATER_SCREEN(w->screen);

      if (wd->opt[WATER_DISPLAY_OPTION_UNGRAB_WAVE].value.b &&
            !(w->state & CompWindowStateOffscreenMask))
      {
            /* generate a wave from the windows edges */
            CompOption o[2];

            o[0].type = CompOptionTypeInt;
            o[0].name = "window";
            o[0].value.i = w->id;

            o[1].type = CompOptionTypeFloat;
            o[1].name = "amplitude";
            o[1].value.f = wd->opt[WATER_DISPLAY_OPTION_TITLE_AMPL].value.f;

            waterWin(w->screen->display, NULL, 0, o, 2);
      }

      UNWRAP(ws, w->screen, windowUngrabNotify);
      (*w->screen->windowUngrabNotify) (w);
      WRAP(ws, w->screen, windowUngrabNotify, waterWindowUngrabNotify);
}

static void waterHandleEvent(CompDisplay * d, XEvent * event)
{
      CompScreen *s;

      WATER_DISPLAY(d);

      switch (event->type)
      {
      case ButtonPress:
            s = findScreenAtDisplay(d, event->xbutton.root);
            if (s)
            {
                  WATER_SCREEN(s);

                  if (ws->grabIndex)
                  {
                        XPoint p;

                        p.x = d->pointerX;
                        p.y = d->pointerY;

                        waterVertices(s, GL_POINTS, &p, 1, 0.8f);
                        damageScreen(s);
                  }
            }
            break;
      case EnterNotify:
      case LeaveNotify:
            waterHandleMotionEvent(d, event->xcrossing.root);
            break;
      case MotionNotify:
            waterHandleMotionEvent(d, event->xmotion.root);
      default:
            break;
      }

      UNWRAP(wd, d, handleEvent);
      (*d->handleEvent) (d, event);
      WRAP(wd, d, handleEvent, waterHandleEvent);
}

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

      WATER_DISPLAY(display);

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

      switch (index)
      {
      case WATER_DISPLAY_OPTION_INITIATE:
      case WATER_DISPLAY_OPTION_TOGGLE_RAIN:
      case WATER_DISPLAY_OPTION_TOGGLE_WIPER:
      case WATER_DISPLAY_OPTION_TITLE_WAVE:
      case WATER_DISPLAY_OPTION_POINT:
      case WATER_DISPLAY_OPTION_LINE:
      case WATER_DISPLAY_OPTION_QUAD:
      case WATER_DISPLAY_OPTION_WIN:
            if (setDisplayAction(display, o, value))
                  return TRUE;
            break;
      case WATER_DISPLAY_OPTION_OFFSET_SCALE:
      case WATER_DISPLAY_OPTION_TITLE_AMPL:
      case WATER_DISPLAY_OPTION_RAIN_AMPL:
      case WATER_DISPLAY_OPTION_VISCOSITY:
            if (compSetFloatOption(o, value))
                  return TRUE;
            break;
      case WATER_DISPLAY_OPTION_INIT_VERTICES:
            if (compSetIntOption(o, value))
                  return TRUE;
            break;
      case WATER_DISPLAY_OPTION_RAIN_DELAY:
            if (compSetIntOption(o, value))
            {
                  CompScreen *s;

                  for (s = display->screens; s; s = s->next)
                  {
                        WATER_SCREEN(s);

                        if (!ws->rainHandle)
                              continue;

                        compRemoveTimeout(ws->rainHandle);
                        ws->rainHandle =
                                    compAddTimeout(o->value.i, waterRainTimeout, s);
                  }
                  return TRUE;
            }
            break;
      case WATER_DISPLAY_OPTION_UNGRAB_WAVE:
      case WATER_DISPLAY_OPTION_SHIVER_WIN:
            if (compSetBoolOption(o, value))
                  return TRUE;
            break;
      default:
            break;
      }

      return FALSE;
}

static void waterDisplayInitOptions(WaterDisplay * wd)
{
      CompOption *o;

      o = &wd->opt[WATER_DISPLAY_OPTION_INITIATE];
      o->advanced = False;
      o->name = "initiate";
      o->group = N_("Pointer wave");
      o->subGroup = N_("Initiate");
      o->displayHints = "";
      o->shortDesc = N_("Initiate");
      o->longDesc = N_("Enable pointer Water Effects.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = waterInitiate;
      o->value.action.terminate = waterTerminate;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeKey;
      o->value.action.state = CompActionStateInitKey;
      o->value.action.key.modifiers = WATER_INITIATE_MODIFIERS_DEFAULT;
      o->value.action.key.keysym = 0;

      o = &wd->opt[WATER_DISPLAY_OPTION_TOGGLE_RAIN];
      o->advanced = False;
      o->name = "toggle_rain";
      o->group = N_("Rain");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Toggle Rain");
      o->longDesc = N_("Toggle Rain Effect (random drops of water).");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = waterToggleRain;
      o->value.action.terminate = 0;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeKey;
      o->value.action.state = CompActionStateInitKey;
      o->value.action.key.modifiers = WATER_TOGGLE_RAIN_MODIFIERS_DEFAULT;
      o->value.action.key.keysym =
                  XStringToKeysym(WATER_TOGGLE_RAIN_KEY_DEFAULT);

      o = &wd->opt[WATER_DISPLAY_OPTION_TOGGLE_WIPER];
      o->advanced = False;
      o->name = "toggle_wiper";
      o->group = N_("Rain");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Toggle Wiper");
      o->longDesc = N_("Toggle Wiper Effect.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = waterToggleWiper;
      o->value.action.terminate = 0;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeKey;
      o->value.action.state = CompActionStateInitKey;
      o->value.action.key.modifiers = WATER_TOGGLE_WIPER_MODIFIERS_DEFAULT;
      o->value.action.key.keysym =
                  XStringToKeysym(WATER_TOGGLE_WIPER_KEY_DEFAULT);

      o = &wd->opt[WATER_DISPLAY_OPTION_OFFSET_SCALE];
      o->advanced = False;
      o->name = "offset_scale";
      o->group = N_("Misc. Options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Offset Scale");
      o->longDesc = N_("Adjusts the blur effect under the wave.");
      o->type = CompOptionTypeFloat;
      o->value.f = WATER_OFFSET_SCALE_DEFAULT;
      o->rest.f.min = WATER_OFFSET_SCALE_MIN;
      o->rest.f.max = WATER_OFFSET_SCALE_MAX;
      o->rest.f.precision = WATER_OFFSET_SCALE_PRECISION;

      o = &wd->opt[WATER_DISPLAY_OPTION_TITLE_AMPL];
      o->advanced = False;
      o->name = "title_wave_amplitude";
      o->group = N_("Title wave");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Title Wave Amplitude");
      o->longDesc = N_("Amplitude of title and window Wave.");
      o->type = CompOptionTypeFloat;
      o->value.f = WATER_TITLE_AMPL_DEFAULT;
      o->rest.f.min = WATER_TITLE_AMPL_MIN;
      o->rest.f.max = WATER_TITLE_AMPL_MAX;
      o->rest.f.precision = WATER_TITLE_AMPL_PRECISION;

      o = &wd->opt[WATER_DISPLAY_OPTION_RAIN_AMPL];
      o->advanced = False;
      o->name = "rain_wave_amplitude";
      o->group = N_("Rain");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Rain Wave Maximum Amplitude");
      o->longDesc = N_("Amplitude of Rain Wave.");
      o->type = CompOptionTypeFloat;
      o->value.f = WATER_RAIN_AMPL_DEFAULT;
      o->rest.f.min = WATER_RAIN_AMPL_MIN;
      o->rest.f.max = WATER_RAIN_AMPL_MAX;
      o->rest.f.precision = WATER_RAIN_AMPL_PRECISION;

      o = &wd->opt[WATER_DISPLAY_OPTION_VISCOSITY];
      o->advanced = False;
      o->name = "water_viscosity";
      o->group = N_("Misc. Options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Water Viscosity");
      o->longDesc =
                  N_("Water Viscosity (higher number results in larger wave).");
      o->type = CompOptionTypeFloat;
      o->value.f = WATER_VISCOS_DEFAULT;
      o->rest.f.min = WATER_VISCOS_MIN;
      o->rest.f.max = WATER_VISCOS_MAX;
      o->rest.f.precision = WATER_VISCOS_PRECISION;

      o = &wd->opt[WATER_DISPLAY_OPTION_INIT_VERTICES];
      o->advanced = False;
      o->name = "initial_vertices";
      o->group = N_("Misc. Options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Initial Vertices");
      o->longDesc =
                  N_
                  ("Initial number of Water Vertices (larger number results in larger wave duration).");
      o->type = CompOptionTypeInt;
      o->value.i = WATER_INIT_VERTICES_DEFAULT;
      o->rest.i.min = WATER_INIT_VERTICES_MIN;
      o->rest.i.max = WATER_INIT_VERTICES_MAX;

      o = &wd->opt[WATER_DISPLAY_OPTION_RAIN_DELAY];
      o->advanced = False;
      o->name = "rain_delay";
      o->group = N_("Rain");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Rain Delay");
      o->longDesc = N_("Delay (in ms) between each Raindrop.");
      o->type = CompOptionTypeInt;
      o->value.i = WATER_RAIN_DELAY_DEFAULT;
      o->rest.i.min = WATER_RAIN_DELAY_MIN;
      o->rest.i.max = WATER_RAIN_DELAY_MAX;

      o = &wd->opt[WATER_DISPLAY_OPTION_TITLE_WAVE];
      o->advanced = False;
      o->name = "title_wave";
      o->group = N_("Title wave");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Title Wave");
      o->longDesc = N_("Wave effect from window Title.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = waterTitleWave;
      o->value.action.terminate = 0;
      o->value.action.bell = WATER_TITLE_WAVE_BELL_DEFAULT;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeNone;
      o->value.action.state = CompActionStateInitBell;

      o = &wd->opt[WATER_DISPLAY_OPTION_POINT];
      o->advanced = False;
      o->name = "point";
      o->group = N_("Misc. Options");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Point");
      o->longDesc = N_("Add Point.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = waterPoint;
      o->value.action.terminate = 0;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeNone;
      o->value.action.state = 0;

      o = &wd->opt[WATER_DISPLAY_OPTION_LINE];
      o->advanced = False;
      o->name = "line";
      o->group = N_("");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Line");
      o->longDesc = N_("Add Line.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = waterLine;
      o->value.action.terminate = 0;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeNone;
      o->value.action.state = 0;

      o = &wd->opt[WATER_DISPLAY_OPTION_QUAD];
      o->advanced = False;
      o->name = "quad";
      o->group = N_("");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Quad");
      o->longDesc = N_("Add Quad.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = waterQuad;
      o->value.action.terminate = 0;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.type = CompBindingTypeNone;
      o->value.action.state = 0;

      o = &wd->opt[WATER_DISPLAY_OPTION_WIN];
      o->advanced = False;
      o->name = "window_wave";
      o->group = N_("Window wave");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Window Wave");
      o->longDesc = N_("Wave effect from Window.");
      o->type = CompOptionTypeAction;
      o->value.action.initiate = waterWin;
      o->value.action.terminate = 0;
      o->value.action.bell = FALSE;
      o->value.action.edgeMask = 0;
      o->value.action.state = CompActionStateInitKey;
      o->value.action.state |= CompActionStateInitButton;
      o->value.action.state |= CompActionStateInitBell;
      o->value.action.type = CompBindingTypeNone;

      o = &wd->opt[WATER_DISPLAY_OPTION_UNGRAB_WAVE];
      o->advanced = False;
      o->name = "ungrab_wave";
      o->group = N_("Window wave");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Ungrab Wave Effect");
      o->longDesc = N_("Wave when Ungrabing windows.");
      o->type = CompOptionTypeBool;
      o->value.b = WATER_UNGRAB_WAVE_DEFAULT;

      o = &wd->opt[WATER_DISPLAY_OPTION_SHIVER_WIN];
      o->advanced = False;
      o->name = "shiver_window";
      o->group = N_("Window wave");
      o->subGroup = N_("");
      o->displayHints = "";
      o->shortDesc = N_("Shiver Window");
      o->longDesc = N_("Wobbly Shiver when doing Window or ungrab wave.");
      o->type = CompOptionTypeBool;
      o->value.b = WATER_SHIVER_WIN_DEFAULT;
}

static CompOption *waterGetDisplayOptions(CompDisplay * display, int *count)
{
      if (display)
      {
            WATER_DISPLAY(display);

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

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


static Bool waterInitDisplay(CompPlugin * p, CompDisplay * d)
{
      WaterDisplay *wd;

      wd = malloc(sizeof(WaterDisplay));
      if (!wd)
            return FALSE;

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

      waterDisplayInitOptions(wd);

      WRAP(wd, d, handleEvent, waterHandleEvent);

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

      return TRUE;
}

static void waterFiniDisplay(CompPlugin * p, CompDisplay * d)
{
      WATER_DISPLAY(d);

      freeScreenPrivateIndex(d, wd->screenPrivateIndex);

      UNWRAP(wd, d, handleEvent);

      free(wd);
}

static Bool waterInitScreen(CompPlugin * p, CompScreen * s)
{
      WaterScreen *ws;

      WATER_DISPLAY(s->display);

      ws = calloc(1, sizeof(WaterScreen));
      if (!ws)
            return FALSE;

      ws->grabIndex = 0;
      ws->current = 0;

      addScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_INITIATE].value.action);
      addScreenAction(s,
                              &wd->opt[WATER_DISPLAY_OPTION_TOGGLE_RAIN].value.action);
      addScreenAction(s,
                              &wd->opt[WATER_DISPLAY_OPTION_TOGGLE_WIPER].value.action);
      addScreenAction(s,
                              &wd->opt[WATER_DISPLAY_OPTION_TITLE_WAVE].value.action);
      addScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_POINT].value.action);
      addScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_LINE].value.action);
      addScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_QUAD].value.action);
      addScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_WIN].value.action);

      WRAP(ws, s, preparePaintScreen, waterPreparePaintScreen);
      WRAP(ws, s, donePaintScreen, waterDonePaintScreen);
      WRAP(ws, s, drawWindowTexture, waterDrawWindowTexture);
      WRAP(ws, s, windowUngrabNotify, waterWindowUngrabNotify);

      s->privates[wd->screenPrivateIndex].ptr = ws;

      waterReset(s);

      return TRUE;
}

static void waterFiniScreen(CompPlugin * p, CompScreen * s)
{
      int i;

      WATER_SCREEN(s);
      WATER_DISPLAY(s->display);

      if (ws->rainHandle)
            compRemoveTimeout(ws->rainHandle);

      if (ws->wiperHandle)
            compRemoveTimeout(ws->wiperHandle);

      if (ws->fbo)
            (*s->deleteFramebuffers) (1, &ws->fbo);

      for (i = 0; i < TEXTURE_NUM; i++)
      {
            if (ws->texture[i])
                  glDeleteTextures(1, &ws->texture[i]);
      }

      for (i = 0; i < PROGRAM_NUM; i++)
      {
            if (ws->program[i])
                  (*s->deletePrograms) (1, &ws->program[i]);
      }

      if (ws->data)
            free(ws->data);

      UNWRAP(ws, s, preparePaintScreen);
      UNWRAP(ws, s, donePaintScreen);
      UNWRAP(ws, s, drawWindowTexture);
      UNWRAP(ws, s, windowUngrabNotify);

      removeScreenAction(s,
                                 &wd->opt[WATER_DISPLAY_OPTION_INITIATE].value.action);
      removeScreenAction(s,
                                 &wd->opt[WATER_DISPLAY_OPTION_TOGGLE_RAIN].
                                 value.action);
      removeScreenAction(s,
                                 &wd->opt[WATER_DISPLAY_OPTION_TOGGLE_WIPER].
                                 value.action);
      removeScreenAction(s,
                                 &wd->opt[WATER_DISPLAY_OPTION_TITLE_WAVE].value.
                                 action);
      removeScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_POINT].value.action);
      removeScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_LINE].value.action);
      removeScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_QUAD].value.action);
      removeScreenAction(s, &wd->opt[WATER_DISPLAY_OPTION_WIN].value.action);

      free(ws);
}

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

      return TRUE;
}

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

static CompPluginVTable waterVTable = {
      "water",
      N_("Water Effect"),
      N_("Adds water effects to different desktop actions"),
      waterInit,
      waterFini,
      waterInitDisplay,
      waterFiniDisplay,
      waterInitScreen,
      waterFiniScreen,
      0,                                        /* InitWindow */
      0,                                        /* FiniWindow */
      waterGetDisplayOptions,
      waterSetDisplayOption,
      0,                                        /* GetScreenOptions */
      0,                                        /* SetScreenOption */
      NULL,
      0,
      0,
      0,
      BERYL_ABI_INFO,
      "beryl-plugins",
      "misc",
      0,
      0,
      False,
};

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

Generated by  Doxygen 1.6.0   Back to index