/*
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1