summaryrefslogtreecommitdiff
path: root/plugins/water
diff options
context:
space:
mode:
authorDennis Kasprzyk <onestone@compiz-fusion.org>2009-03-15 06:09:18 +0100
committerDennis kasprzyk <onestone@compiz-fusion.org>2009-03-15 06:09:18 +0100
commit163f6b6f3c3b7764987cbdf8e03cc355edeaa499 (patch)
tree4278afde195343dcb8277b0bb0b6efc926ce8907 /plugins/water
parent28e45c55c11e20206d3bc9056aea8fc7f1b7a0e7 (diff)
downloadcompiz-with-glib-mainloop-163f6b6f3c3b7764987cbdf8e03cc355edeaa499.tar.gz
compiz-with-glib-mainloop-163f6b6f3c3b7764987cbdf8e03cc355edeaa499.tar.bz2
New generalized build system.
Diffstat (limited to 'plugins/water')
-rw-r--r--plugins/water/CMakeLists.txt5
-rw-r--r--plugins/water/src/water.cpp1392
-rw-r--r--plugins/water/src/water.h193
-rw-r--r--plugins/water/water.xml.in60
4 files changed, 1650 insertions, 0 deletions
diff --git a/plugins/water/CMakeLists.txt b/plugins/water/CMakeLists.txt
new file mode 100644
index 0000000..2d2542b
--- /dev/null
+++ b/plugins/water/CMakeLists.txt
@@ -0,0 +1,5 @@
+find_package (Compiz REQUIRED)
+
+include (CompizPlugin)
+
+compiz_plugin(water PLUGINDEPS composite opengl) \ No newline at end of file
diff --git a/plugins/water/src/water.cpp b/plugins/water/src/water.cpp
new file mode 100644
index 0000000..30970cb
--- /dev/null
+++ b/plugins/water/src/water.cpp
@@ -0,0 +1,1392 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include "water.h"
+
+COMPIZ_PLUGIN_20081216 (water, WaterPluginVTable)
+
+static int waterLastPointerX = 0;
+static int waterLastPointerY = 0;
+
+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 bool
+loadFragmentProgram (GLuint *program,
+ const char *string)
+{
+ GLint errorPos;
+
+ /* clear errors */
+ glGetError ();
+
+ if (!*program)
+ GL::genPrograms (1, program);
+
+ GL::bindProgram (GL_FRAGMENT_PROGRAM_ARB, *program);
+ GL::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)
+ {
+ compLogMessage ("water", CompLogLevelError,
+ "failed to load bump map program");
+
+ GL::deletePrograms (1, program);
+ *program = 0;
+
+ return false;
+ }
+
+ return true;
+}
+
+static int
+loadWaterProgram ()
+{
+ char buffer[1024];
+
+ WATER_SCREEN (screen);
+
+ 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 (&ws->program, buffer);
+}
+
+GLFragment::FunctionId
+WaterScreen::getBumpMapFragmentFunction (GLTexture *texture,
+ int unit,
+ int param)
+{
+ GLFragment::FunctionData data;
+ int target;
+ WaterFunction function;
+
+ if (texture->target () == GL_TEXTURE_2D)
+ target = COMP_FETCH_TARGET_2D;
+ else
+ target = COMP_FETCH_TARGET_RECT;
+
+ foreach (WaterFunction &f, bumpMapFunctions)
+ {
+ if (f.param == param &&
+ f.unit == unit &&
+ f.target == target)
+ return f.id;
+ }
+
+ static const char *temp[] = { "normal", "temp", "total", "bump", "offset" };
+ char str[1024];
+
+ for (unsigned int i = 0; i < sizeof (temp) / sizeof (temp[0]); i++)
+ data.addTempHeaderOp (temp[i]);
+
+ data.addDataOp (
+ /* get normal from normal map */
+ "TEX normal, fragment.texcoord[%d], texture[%d], %s;"
+
+ /* save height */
+ "MOV offset, normal;"
+
+ /* remove scale and bias from normal */
+ "MAD normal, normal, 2.0, -1.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.env[%d];",
+
+ unit, unit,
+ (this->target == GL_TEXTURE_2D) ? "2D" : "RECT",
+ param);
+
+ data.addFetchOp ("output", "offset.yxzz", target);
+
+ data.addDataOp (
+ /* 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, state.light[0].diffuse;");
+
+
+ data.addColorOp ("output", "output");
+
+ data.addDataOp (
+ /* diffuse per-vertex lighting, opacity and brightness
+ and add lightsource bump color */
+ "ADD output, output, bump;");
+
+ if (!data.status ())
+ return 0;
+
+
+ function.id = data.createFragmentFunction ("water");
+
+ function.target = target;
+ function.param = param;
+ function.unit = unit;
+
+ bumpMapFunctions.push_back (function);
+
+ return function.id;
+}
+
+void
+WaterScreen::allocTexture (int index)
+{
+ glGenTextures (1, &texture[index]);
+ glBindTexture (target, texture[index]);
+
+ glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D (target, 0, GL_RGBA, width, height, 0, GL_BGRA,
+#if IMAGE_BYTE_ORDER == MSBFirst
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+#else
+ GL_UNSIGNED_BYTE,
+#endif
+ t0);
+
+ glBindTexture (target, 0);
+}
+
+bool
+WaterScreen::fboPrologue (int tIndex)
+{
+ if (!fbo)
+ return false;
+
+ if (!texture[tIndex])
+ allocTexture (tIndex);
+
+ GL::bindFramebuffer (GL_FRAMEBUFFER_EXT, fbo);
+
+ GL::framebufferTexture2D (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ target, texture[tIndex], 0);
+
+ glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);
+ glReadBuffer (GL_COLOR_ATTACHMENT0_EXT);
+
+ /* check status the first time */
+ if (!fboStatus)
+ {
+ fboStatus = GL::checkFramebufferStatus (GL_FRAMEBUFFER_EXT);
+ if (fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ compLogMessage ("water", CompLogLevelError,
+ "framebuffer incomplete");
+
+ GL::bindFramebuffer (GL_FRAMEBUFFER_EXT, 0);
+ GL::deleteFramebuffers (1, &fbo);
+
+ glDrawBuffer (GL_BACK);
+ glReadBuffer (GL_BACK);
+
+ fbo = 0;
+
+ return false;
+ }
+ }
+
+ glViewport (0, 0, width, 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 true;
+}
+
+void
+WaterScreen::fboEpilogue ()
+{
+ GL::bindFramebuffer (GL_FRAMEBUFFER_EXT, 0);
+
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ glDepthRange (0, 1);
+ glViewport (-1, -1, 2, 2);
+
+ gScreen->resetRasterPos ();
+
+ gScreen->setDefaultViewport ();
+
+ glMatrixMode (GL_PROJECTION);
+ glPopMatrix ();
+ glMatrixMode (GL_MODELVIEW);
+ glPopMatrix ();
+
+ glDrawBuffer (GL_BACK);
+ glReadBuffer (GL_BACK);
+}
+
+bool
+WaterScreen::fboUpdate (float dt, float fade)
+{
+ if (!fboPrologue (TINDEX (this, 1)))
+ return false;
+
+ if (!texture[TINDEX (this, 2)])
+ allocTexture (TINDEX (this, 2));
+
+ if (!texture[TINDEX (this, 0)])
+ allocTexture (TINDEX (this, 0));
+
+ glEnable (target);
+
+ GL::activeTexture (GL_TEXTURE0_ARB);
+ glBindTexture (target, texture[TINDEX (this, 2)]);
+
+ glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ GL::activeTexture (GL_TEXTURE1_ARB);
+ glBindTexture (target, texture[TINDEX (this, 0)]);
+ glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glEnable (GL_FRAGMENT_PROGRAM_ARB);
+ GL::bindProgram (GL_FRAGMENT_PROGRAM_ARB, program);
+
+ GL::programLocalParameter4f (GL_FRAGMENT_PROGRAM_ARB, 0,
+ dt * K, fade, 1.0f, 1.0f);
+
+ glBegin (GL_QUADS);
+
+ glTexCoord2f (0.0f, 0.0f);
+ glVertex2f (0.0f, 0.0f);
+ glTexCoord2f (tx, 0.0f);
+ glVertex2f (1.0f, 0.0f);
+ glTexCoord2f (tx, ty);
+ glVertex2f (1.0f, 1.0f);
+ glTexCoord2f (0.0f, ty);
+ glVertex2f (0.0f, 1.0f);
+
+ glEnd ();
+
+ glDisable (GL_FRAGMENT_PROGRAM_ARB);
+
+ glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glBindTexture (target, 0);
+ GL::activeTexture (GL_TEXTURE0_ARB);
+ glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glBindTexture (target, 0);
+
+ glDisable (target);
+
+ fboEpilogue ();
+
+ /* increment texture index */
+ tIndex = TINDEX (this, 1);
+
+ return true;
+}
+
+bool
+WaterScreen::fboVertices (GLenum type,
+ XPoint *p,
+ int n,
+ float v)
+{
+ if (!fboPrologue (TINDEX (this, 0)))
+ return false;
+
+ 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 / width, 1.0f / 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 ();
+
+ return true;
+}
+
+void
+WaterScreen::softwareUpdate (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;
+
+ if (!texture[TINDEX (this, 0)])
+ allocTexture (TINDEX (this, 0));
+
+ dt *= K * 2.0f;
+ fade *= 0.99f;
+
+ dWidth = width + 2;
+ dHeight = height + 2;
+
+#define D(d, j) (*((d) + (j)))
+
+ d01 = d0 + dWidth;
+ d10 = 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 (d0, d0 + dWidth, dWidth * sizeof (GLfloat));
+ memcpy (d0 + dWidth * (dHeight - 1),
+ d0 + dWidth * (dHeight - 2),
+ dWidth * sizeof (GLfloat));
+
+ d01 = 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 = d1;
+ d11 = d10 + dWidth;
+ d12 = d11 + dWidth;
+
+ t0 = this->t0;
+
+ /* update texture */
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < 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 += width * 4;
+ }
+
+#undef D
+
+ /* swap height maps */
+ dTmp = d0;
+ d0 = d1;
+ d1 = dTmp;
+
+ if (texture[TINDEX (this, 0)])
+ {
+ glBindTexture (target, texture[TINDEX (this, 0)]);
+ glTexImage2D (target, 0, GL_RGBA, width, height, 0, GL_BGRA,
+#if IMAGE_BYTE_ORDER == MSBFirst
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+#else
+ GL_UNSIGNED_BYTE,
+#endif
+ this->t0);
+ }
+}
+
+
+#define SET(x, y, v) *((d1) + (width + 2) * (y + 1) + (x + 1)) = (v)
+
+void
+WaterScreen::softwarePoints (XPoint *p,
+ int n,
+ float add)
+{
+ 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 */
+void
+WaterScreen::softwareLines (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;
+
+#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
+
+void
+WaterScreen::softwareVertices (GLenum type,
+ XPoint *p,
+ int n,
+ float v)
+{
+ switch (type) {
+ case GL_POINTS:
+ softwarePoints (p, n, v);
+ break;
+ case GL_LINES:
+ softwareLines (p, n, v);
+ break;
+ }
+}
+
+void
+WaterScreen::waterUpdate (float dt)
+{
+ GLfloat fade = 1.0f;
+
+ if (count < 1000)
+ {
+ if (count > 1)
+ fade = 0.90f + count / 10000.0f;
+ else
+ fade = 0.0f;
+ }
+
+ if (!fboUpdate (dt, fade))
+ softwareUpdate (dt, fade);
+}
+
+void
+WaterScreen::scaleVertices (XPoint *p, int n)
+{
+ while (n--)
+ {
+ p[n].x = (width * p[n].x) / screen->width ();
+ p[n].y = (height * p[n].y) / screen->height ();
+ }
+}
+
+void
+WaterScreen::waterVertices (GLenum type,
+ XPoint *p,
+ int n,
+ float v)
+{
+ if (!GL::fragmentProgram)
+ return;
+
+ scaleVertices (p, n);
+
+ if (!fboVertices (type, p, n, v))
+ softwareVertices (type, p, n, v);
+
+ if (count <= 0)
+ {
+ WaterWindow *ww;
+
+ cScreen->preparePaintSetEnabled (this, true);
+ cScreen->donePaintSetEnabled (this, true);
+ foreach (CompWindow *w, screen->windows ())
+ {
+ ww = WaterWindow::get (w);
+ ww->gWindow->glDrawTextureSetEnabled (ww, true);
+ }
+ }
+
+ if (count < 3000)
+ count = 3000;
+}
+
+bool
+WaterScreen::rainTimeout ()
+{
+ XPoint p;
+
+ p.x = (int) (screen->width () * (rand () / (float) RAND_MAX));
+ p.y = (int) (screen->height () * (rand () / (float) RAND_MAX));
+
+ waterVertices (GL_POINTS, &p, 1, 0.8f * (rand () / (float) RAND_MAX));
+
+ cScreen->damageScreen ();
+
+ return true;
+}
+
+bool
+WaterScreen::wiperTimeout ()
+{
+ if (count)
+ {
+ if (wiperAngle == 0.0f)
+ wiperSpeed = 2.5f;
+ else if (wiperAngle == 180.0f)
+ wiperSpeed = -2.5f;
+ }
+
+ return true;
+}
+
+void
+WaterScreen::waterReset ()
+{
+ int size, i, j;
+
+ height = TEXTURE_SIZE;
+ width = (height * screen->width ()) / screen->height ();
+
+ if (GL::textureNonPowerOfTwo ||
+ (POWER_OF_TWO (width) && POWER_OF_TWO (height)))
+ {
+ target = GL_TEXTURE_2D;
+ tx = ty = 1.0f;
+ }
+ else
+ {
+ target = GL_TEXTURE_RECTANGLE_NV;
+ tx = width;
+ ty = height;
+ }
+
+ if (!GL::fragmentProgram)
+ return;
+
+ if (GL::fbo)
+ {
+ loadWaterProgram ();
+ if (!fbo)
+ GL::genFramebuffers (1, &fbo);
+ }
+
+ fboStatus = 0;
+
+ for (i = 0; i < TEXTURE_NUM; i++)
+ {
+ if (texture[i])
+ {
+ glDeleteTextures (1, &texture[i]);
+ texture[i] = 0;
+ }
+ }
+
+ if (data)
+ free (data);
+
+ size = (width + 2) * (height + 2);
+
+ data = calloc (1, (sizeof (float) * size * 2) +
+ (sizeof (GLubyte) * width * height * 4));
+ if (!data)
+ return;
+
+ d0 = (float *)data;
+ d1 = (d0 + (size));
+ t0 = (unsigned char *) (d1 + (size));
+
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ (t0 + (width * 4 * i + j * 4))[0] = 0xff;
+ }
+ }
+}
+
+void
+WaterWindow::glDrawTexture (GLTexture *texture,
+ GLFragment::Attrib &attrib,
+ unsigned int mask)
+{
+ if (wScreen->count)
+ {
+ GLFragment::Attrib fa (attrib);
+ bool lighting = wScreen->gScreen->lighting ();
+ int param, unit;
+ GLFragment::FunctionId function;
+ GLfloat plane[4];
+
+ param = fa.allocParameters (1);
+ unit = fa.allocTextureUnits (1);
+
+ function = wScreen->getBumpMapFragmentFunction (texture, unit, param);
+ if (function)
+ {
+ fa.addFunction (function);
+
+ gScreen->setLighting (true);
+
+ GL::activeTexture (GL_TEXTURE0_ARB + unit);
+
+ glBindTexture (wScreen->target,
+ wScreen->texture[TINDEX (wScreen, 0)]);
+
+ plane[1] = plane[2] = 0.0f;
+ plane[0] = wScreen->tx / (GLfloat) 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] = wScreen->ty / (GLfloat) 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);
+
+ GL::activeTexture (GL_TEXTURE0_ARB);
+
+ GL::programEnvParameter4f (GL_FRAGMENT_PROGRAM_ARB, param,
+ texture->matrix ().yy *
+ wScreen->offsetScale,
+ -texture->matrix ().xx *
+ wScreen->offsetScale,
+ 0.0f, 0.0f);
+ }
+
+ /* to get appropriate filtering of texture */
+ mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK;
+
+ gWindow->glDrawTexture (texture, fa, mask);
+
+ if (function)
+ {
+ GL::activeTexture (GL_TEXTURE0_ARB + unit);
+ glDisable (GL_TEXTURE_GEN_T);
+ glDisable (GL_TEXTURE_GEN_S);
+ glBindTexture (wScreen->target, 0);
+ GL::activeTexture (GL_TEXTURE0_ARB);
+
+ gScreen->setLighting (lighting);
+ }
+ }
+ else
+ {
+ gWindow->glDrawTexture (texture, attrib, mask);
+ }
+}
+
+/* TODO: a way to control the speed */
+void
+WaterScreen::preparePaint (int msSinceLastPaint)
+{
+ if (count)
+ {
+ count -= 10;
+ if (count < 0)
+ count = 0;
+
+ if (wiperTimer.active ())
+ {
+ float step, angle0, angle1;
+ bool wipe = false;
+ XPoint p[3];
+
+ p[1].x = screen->width () / 2;
+ p[1].y = screen->height ();
+
+ step = wiperSpeed * msSinceLastPaint / 20.0f;
+
+ if (wiperSpeed > 0.0f)
+ {
+ if (wiperAngle < 180.0f)
+ {
+ angle0 = wiperAngle;
+
+ wiperAngle += step;
+ wiperAngle = MIN (wiperAngle, 180.0f);
+
+ angle1 = wiperAngle;
+
+ wipe = true;
+ }
+ }
+ else
+ {
+ if (wiperAngle > 0.0f)
+ {
+ angle1 = wiperAngle;
+
+ wiperAngle += step;
+ wiperAngle = MAX (wiperAngle, 0.0f);
+
+ angle0 = wiperAngle;
+
+ wipe = true;
+ }
+ }
+
+#define TAN(a) (tanf ((a) * (M_PI / 180.0f)))
+
+ if (wipe)
+ {
+ if (angle0 > 0.0f)
+ {
+ p[2].x = screen->width () / 2 -
+ screen->height () / TAN (angle0);
+ p[2].y = 0;
+ }
+ else
+ {
+ p[2].x = 0;
+ p[2].y = screen->height ();
+ }
+
+ if (angle1 < 180.0f)
+ {
+ p[0].x = screen->width () / 2 -
+ screen->height () / TAN (angle1);
+ p[0].y = 0;
+ }
+ else
+ {
+ p[0].x = screen->width ();
+ p[0].y = screen->height ();
+ }
+
+ /* software rasterizer doesn't support triangles yet so wiper
+ effect will only work with FBOs right now */
+ waterVertices (GL_TRIANGLES, p, 3, 0.0f);
+ }
+
+#undef TAN
+
+ }
+
+ waterUpdate (0.8f);
+ }
+
+ cScreen->preparePaint (msSinceLastPaint);
+}
+
+void
+WaterScreen::donePaint ()
+{
+ if (count)
+ cScreen->damageScreen ();
+ else
+ {
+ WaterWindow *ww;
+
+ cScreen->preparePaintSetEnabled (this, false);
+ cScreen->donePaintSetEnabled (this, false);
+ foreach (CompWindow *w, screen->windows ())
+ {
+ ww = WaterWindow::get (w);
+ ww->gWindow->glDrawTextureSetEnabled (ww, true);
+ }
+ }
+
+ cScreen->donePaint ();
+}
+
+void
+WaterScreen::handleMotionEvent ()
+{
+ if (grabIndex)
+ {
+ XPoint p[2];
+
+ p[0].x = waterLastPointerX;
+ p[0].y = waterLastPointerY;
+
+ p[1].x = waterLastPointerX = pointerX;
+ p[1].y = waterLastPointerY = pointerY;
+
+ waterVertices (GL_LINES, p, 2, 0.2f);
+
+ cScreen->damageScreen ();
+ }
+
+}
+
+static bool
+waterInitiate (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector &options)
+{
+ CompScreen *s;
+ unsigned int ui;
+ Window root, child;
+ int xRoot, yRoot, i;
+
+ WATER_SCREEN (screen);
+
+ if (!screen->otherGrabExist ("water", 0))
+ {
+ if (!ws->grabIndex)
+ {
+ ws->grabIndex = screen->pushGrab (None, "water");
+ screen->handleEventSetEnabled (ws, true);
+ }
+
+ if (XQueryPointer (screen->dpy (), screen->root (), &root, &child,
+ &xRoot, &yRoot, &i, &i, &ui))
+ {
+ XPoint p;
+
+ p.x = waterLastPointerX = xRoot;
+ p.y = waterLastPointerY = yRoot;
+
+ ws->waterVertices (GL_POINTS, &p, 1, 0.8f);
+
+ ws->cScreen->damageScreen ();
+ }
+ }
+
+ if (state & CompAction::StateInitButton)
+ action->setState (action->state () | CompAction::StateTermButton);
+
+ if (state & CompAction::StateInitKey)
+ action->setState (action->state () | CompAction::StateTermKey);
+
+ return false;
+}
+
+static bool
+waterTerminate (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector &options)
+{
+ WATER_SCREEN (screen);
+
+ if (ws->grabIndex)
+ {
+ screen->removeGrab (ws->grabIndex, 0);
+ ws->grabIndex = 0;
+ screen->handleEventSetEnabled (ws, false);
+ }
+
+ return false;
+}
+
+static bool
+waterToggleRain (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector &options)
+{
+ WATER_SCREEN (screen);
+
+ if (!ws->rainTimer.active ())
+ {
+ int delay;
+
+ delay = ws->opt[WATER_OPTION_RAIN_DELAY].value ().i ();
+ ws->rainTimer.start (delay, (float) delay * 1.2);
+ }
+ else
+ {
+ ws->rainTimer.stop ();
+ }
+
+ return false;
+}
+
+static bool
+waterToggleWiper (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector &options)
+{
+ WATER_SCREEN (screen);
+
+ if (!ws->wiperTimer.active ())
+ {
+ ws->wiperTimer.start (2000, 2400);
+ }
+ else
+ {
+ ws->wiperTimer.stop ();
+ }
+
+ return false;
+}
+
+static bool
+waterTitleWave (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector &options)
+{
+ CompWindow *w;
+ int xid;
+
+ WATER_SCREEN (screen);
+
+ xid = CompOption::getIntOptionNamed (options, "window",
+ screen->activeWindow ());
+
+ w = screen->findWindow (xid);
+ if (w)
+ {
+ CompWindow::Geometry &g = w->geometry ();
+ XPoint p[2];
+
+ p[0].x = g.x () - w->input ().left;
+ p[0].y = g.y () - w->input ().top / 2;
+
+ p[1].x = g.x () + g.width () + w->input ().right;
+ p[1].y = p[0].y;
+
+ ws->waterVertices (GL_LINES, p, 2, 0.15f);
+
+ ws->cScreen->damageScreen ();
+ }
+
+ return false;
+}
+
+static bool
+waterPoint (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector &options)
+{
+ XPoint p;
+ float amp;
+
+ WATER_SCREEN (screen);
+
+ p.x = CompOption::getIntOptionNamed (options, "x",
+ screen->width () / 2);
+ p.y = CompOption::getIntOptionNamed (options, "y",
+ screen->height () / 2);
+
+ amp = CompOption::getFloatOptionNamed (options, "amplitude", 0.5f);
+
+ ws->waterVertices (GL_POINTS, &p, 1, amp);
+
+ ws->cScreen->damageScreen ();
+
+ return false;
+}
+
+static bool
+waterLine (CompAction *action,
+ CompAction::State state,
+ CompOption::Vector &options)
+{
+ XPoint p[2];
+ float amp;
+
+ WATER_SCREEN (screen);
+
+ p[0].x = CompOption::getIntOptionNamed (options, "x0",
+ screen->width () / 4);
+ p[0].y = CompOption::getIntOptionNamed (options, "y0",
+ screen->height () / 2);
+
+ p[1].x = CompOption::getIntOptionNamed (options, "x1",
+ screen->width () -
+ screen->width () / 4);
+ p[1].y = CompOption::getIntOptionNamed (options, "y1",
+ screen->height () / 2);
+
+ amp = CompOption::getFloatOptionNamed (options, "amplitude", 0.25f);
+
+ ws->waterVertices (GL_LINES, p, 2, amp);
+
+ ws->cScreen->damageScreen ();
+
+ return false;
+}
+
+void
+WaterScreen::handleEvent (XEvent *event)
+{
+ switch (event->type) {
+ case ButtonPress:
+ if (event->xbutton.root == screen->root () && grabIndex)
+ {
+ XPoint p;
+
+ p.x = pointerX;
+ p.y = pointerY;
+
+ waterVertices (GL_POINTS, &p, 1, 0.8f);
+ cScreen->damageScreen ();
+ }
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ if (event->xcrossing.root == screen->root () && grabIndex)
+ handleMotionEvent ();
+ break;
+ case MotionNotify:
+ if (event->xmotion.root == screen->root () && grabIndex)
+ handleMotionEvent ();
+ default:
+ break;
+ }
+
+ screen->handleEvent (event);
+}
+
+CompOption::Vector &
+WaterScreen::getOptions ()
+{
+ return opt;
+}
+
+bool
+WaterScreen::setOption (const char *name,
+ CompOption::Value &value)
+{
+ CompOption *o;
+ unsigned int index;
+
+ o = CompOption::findOption (opt, name, &index);
+ if (!o)
+ return false;
+
+ switch (index) {
+ case WATER_OPTION_OFFSET_SCALE:
+ if (o->set (value))
+ {
+ offsetScale = o->value ().f () * 50.0f;
+ return true;
+ }
+ break;
+ case WATER_OPTION_RAIN_DELAY:
+ if (o->set (value))
+ {
+ if (rainTimer.active ())
+ {
+ rainTimer.setTimes (value.i (), (float)value.i () * 1.2);
+ }
+ return true;
+ }
+ break;
+ default:
+ return CompOption::setOption (*o, value);
+ }
+
+ return false;
+}
+
+static const CompMetadata::OptionInfo waterOptionInfo[] = {
+ { "initiate_key", "key", 0, waterInitiate, waterTerminate },
+ { "toggle_rain_key", "key", 0, waterToggleRain, 0 },
+ { "toggle_wiper_key", "key", 0, waterToggleWiper, 0 },
+ { "offset_scale", "float", "<min>0</min>", 0, 0 },
+ { "rain_delay", "int", "<min>1</min>", 0, 0 },
+ { "title_wave", "bell", 0, waterTitleWave, 0 },
+ { "point", "action", 0, waterPoint, 0 },
+ { "line", "action", 0, waterLine, 0 }
+};
+
+WaterScreen::WaterScreen (CompScreen *screen) :
+ PrivateHandler<WaterScreen,CompScreen> (screen),
+ cScreen (CompositeScreen::get (screen)),
+ gScreen (GLScreen::get (screen)),
+ opt(WATER_OPTION_NUM),
+ grabIndex (0),
+ width (0),
+ height (0),
+
+ program (0),
+
+ tIndex (0),
+ target (0),
+ tx (0),
+ ty (0),
+
+ count (0),
+
+ fbo (0),
+ fboStatus (0),
+
+ data (NULL),
+ d0 (NULL),
+ d1 (NULL),
+ t0 (NULL),
+
+ wiperAngle (0),
+ wiperSpeed (0),
+
+ bumpMapFunctions ()
+{
+ if (!waterVTable->getMetadata ()->initOptions (waterOptionInfo,
+ WATER_OPTION_NUM, opt))
+ {
+ setFailed ();
+ return;
+ }
+
+ offsetScale = opt[WATER_OPTION_OFFSET_SCALE].value ().f () * 50.0f;
+
+ memset (texture, 0, sizeof (GLuint) * TEXTURE_NUM);
+
+ wiperTimer.setCallback (boost::bind (&WaterScreen::wiperTimeout, this));
+ rainTimer.setCallback (boost::bind (&WaterScreen::rainTimeout, this));
+
+ waterReset ();
+
+ ScreenInterface::setHandler (screen, false);
+ CompositeScreenInterface::setHandler (cScreen, false);
+}
+
+WaterScreen::~WaterScreen ()
+{
+ if (fbo)
+ GL::deleteFramebuffers (1, &fbo);
+
+ for (unsigned int i = 0; i < TEXTURE_NUM; i++)
+ {
+ if (texture[i])
+ glDeleteTextures (1, &texture[i]);
+ }
+
+ if (program)
+ GL::deletePrograms (1, &program);
+
+ if (data)
+ free (data);
+
+ foreach (WaterFunction &f, bumpMapFunctions)
+ {
+ GLFragment::destroyFragmentFunction (f.id);
+ }
+}
+
+bool
+WaterPluginVTable::init ()
+{
+ if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) |
+ !CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) |
+ !CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI))
+ return false;
+
+ getMetadata ()->addFromOptionInfo (waterOptionInfo, WATER_OPTION_NUM);
+ getMetadata ()->addFromFile (name ());
+
+ return true;
+}
+
diff --git a/plugins/water/src/water.h b/plugins/water/src/water.h
new file mode 100644
index 0000000..c53026a
--- /dev/null
+++ b/plugins/water/src/water.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Novell, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ * Novell, Inc. makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+ * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sys/time.h>
+
+#include <X11/cursorfont.h>
+
+#include <core/core.h>
+#include <core/privatehandler.h>
+
+#include <composite/composite.h>
+#include <opengl/opengl.h>
+
+#define WATER_OPTION_INITIATE_KEY 0
+#define WATER_OPTION_TOGGLE_RAIN_KEY 1
+#define WATER_OPTION_TOGGLE_WIPER_KEY 2
+#define WATER_OPTION_OFFSET_SCALE 3
+#define WATER_OPTION_RAIN_DELAY 4
+#define WATER_OPTION_TITLE_WAVE 5
+#define WATER_OPTION_POINT 6
+#define WATER_OPTION_LINE 7
+#define WATER_OPTION_NUM 8
+
+#define WATER_SCREEN(s) \
+ WaterScreen *ws = WaterScreen::get (s)
+
+#define TEXTURE_SIZE 256
+
+#define K 0.1964f
+
+#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)
+
+struct WaterFunction {
+ GLFragment::FunctionId id;
+
+ int target;
+ int param;
+ int unit;
+};
+
+class WaterScreen :
+ public ScreenInterface,
+ public CompositeScreenInterface,
+ public PrivateHandler<WaterScreen,CompScreen>
+{
+ public:
+
+ WaterScreen (CompScreen *screen);
+ ~WaterScreen ();
+
+ CompOption::Vector & getOptions ();
+ bool setOption (const char *name, CompOption::Value &value);
+
+ void handleEvent (XEvent *);
+
+ void preparePaint (int);
+ void donePaint ();
+
+ GLFragment::FunctionId
+ getBumpMapFragmentFunction (GLTexture *texture,
+ int unit,
+ int param);
+
+ void allocTexture (int index);
+
+ bool fboPrologue (int tIndex);
+ void fboEpilogue ();
+ bool fboUpdate (float dt, float fade);
+ bool fboVertices (GLenum type, XPoint *p, int n, float v);
+
+ void softwareUpdate (float dt, float fade);
+ void softwarePoints (XPoint *p, int n, float add);
+ void softwareLines (XPoint *p, int n, float v);
+ void softwareVertices (GLenum type, XPoint *p, int n, float v);
+
+ void waterUpdate (float dt);
+ void scaleVertices (XPoint *p, int n);
+ void waterVertices (GLenum type, XPoint *p, int n, float v);
+
+ bool rainTimeout ();
+ bool wiperTimeout ();
+
+ void waterReset ();
+
+ void handleMotionEvent ();
+
+ CompositeScreen *cScreen;
+ GLScreen *gScreen;
+
+ CompOption::Vector opt;
+
+ float offsetScale;
+
+ CompScreen::GrabHandle grabIndex;
+
+ int width, height;
+
+ GLuint program;
+ 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;
+
+ CompTimer rainTimer;
+ CompTimer wiperTimer;
+
+ float wiperAngle;
+ float wiperSpeed;
+
+ std::vector<WaterFunction> bumpMapFunctions;
+};
+
+class WaterWindow :
+ public GLWindowInterface,
+ public PrivateHandler<WaterWindow,CompWindow>
+{
+ public:
+ WaterWindow (CompWindow *window) :
+ PrivateHandler<WaterWindow,CompWindow> (window),
+ window (window),
+ gWindow (GLWindow::get (window)),
+ wScreen (WaterScreen::get (screen)),
+ gScreen (GLScreen::get (screen))
+ {
+ GLWindowInterface::setHandler (gWindow, false);
+ }
+
+ void glDrawTexture (GLTexture *texture, GLFragment::Attrib &,
+ unsigned int);
+
+ CompWindow *window;
+ GLWindow *gWindow;
+ WaterScreen *wScreen;
+ GLScreen *gScreen;
+};
+
+class WaterPluginVTable :
+ public CompPlugin::VTableForScreenAndWindow<WaterScreen,WaterWindow>
+{
+ public:
+
+ bool init ();
+
+ PLUGIN_OPTION_HELPER (WaterScreen);
+
+};
diff --git a/plugins/water/water.xml.in b/plugins/water/water.xml.in
new file mode 100644
index 0000000..af41bc9
--- /dev/null
+++ b/plugins/water/water.xml.in
@@ -0,0 +1,60 @@
+<compiz>
+ <plugin name="water">
+ <_short>Water Effect</_short>
+ <_long>Adds water effects to different desktop actions</_long>
+ <deps>
+ <relation type="before">
+ <plugin>blur</plugin>
+ <plugin>video</plugin>
+ </relation>
+ <requirement>
+ <plugin>opengl</plugin>
+ </requirement>
+ </deps>
+ <options>
+ <option name="initiate_key" type="key">
+ <_short>Initiate</_short>
+ <_long>Enable pointer water effects</_long>
+ <default>&lt;Control&gt;&lt;Super&gt;</default>
+ </option>
+ <option name="toggle_rain_key" type="key">
+ <_short>Toggle rain</_short>
+ <_long>Toggle rain effect</_long>
+ <default>&lt;Shift&gt;F9</default>
+ </option>
+ <option name="toggle_wiper_key" type="key">
+ <_short>Toggle wiper</_short>
+ <_long>Toggle wiper effect</_long>
+ <default>&lt;Shift&gt;F8</default>
+ </option>
+ <option name="offset_scale" type="float">
+ <_short>Offset Scale</_short>
+ <_long>Water offset scale</_long>
+ <default>1</default>
+ <min>0</min>
+ <max>10</max>
+ <precision>0.1</precision>
+ </option>
+ <option name="rain_delay" type="int">
+ <_short>Rain Delay</_short>
+ <_long>Delay (in ms) between each rain-drop</_long>
+ <default>250</default>
+ <min>1</min>
+ <max>3600000</max>
+ </option>
+ <option name="title_wave" type="bell">
+ <_short>Title wave</_short>
+ <_long>Wave effect from window title</_long>
+ <default>false</default>
+ </option>
+ <option name="point" type="action">
+ <_short>Point</_short>
+ <_long>Add point</_long>
+ </option>
+ <option name="line" type="action">
+ <_short>Line</_short>
+ <_long>Add line</_long>
+ </option>
+ </options>
+ </plugin>
+</compiz>