/*
* Compiz reflection effect plugin
*
* reflex.c
*
* Copyright : (C) 2007 by Dennis Kasprzyk
* E-mail : onestone@beryl-project.org
*
*
* 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.
*
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <compiz.h>
#include "reflex_options.h"
static int displayPrivateIndex = 0;
typedef struct _ReflexDisplay
{
MatchExpHandlerChangedProc matchExpHandlerChanged;
MatchPropertyChangedProc matchPropertyChanged;
int screenPrivateIndex;
}
ReflexDisplay;
typedef struct _ReflexFunction
{
int handle;
int target;
int param;
int unit;
} ReflexFunction;
typedef struct _ReflexScreen
{
int windowPrivateIndex;
DrawWindowTextureProc drawWindowTexture;
Bool imageLoaded;
CompTexture image;
unsigned int width;
unsigned int height;
ReflexFunction function;
}
ReflexScreen;
typedef struct _ReflexWindow {
Bool active;
} ReflexWindow;
#define GET_REFLEX_DISPLAY(d) \
((ReflexDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define REFLEX_DISPLAY(d) \
ReflexDisplay *rd = GET_REFLEX_DISPLAY (d)
#define GET_REFLEX_SCREEN(s, rd) \
((ReflexScreen *) (s)->privates[(rd)->screenPrivateIndex].ptr)
#define REFLEX_SCREEN(s) \
ReflexScreen *rs = GET_REFLEX_SCREEN (s, GET_REFLEX_DISPLAY (s->display))
#define GET_REFLEX_WINDOW(w, rs) \
((ReflexWindow *) (w)->privates[(rs)->windowPrivateIndex].ptr)
#define REFLEX_WINDOW(w) \
ReflexWindow *rw = GET_REFLEX_WINDOW (w, \
GET_REFLEX_SCREEN (w->screen, \
GET_REFLEX_DISPLAY (w->screen->display)))
static int
getReflexFragmentFunction (CompScreen *s,
CompTexture *texture,
int param,
int unit)
{
CompFunctionData *data;
REFLEX_SCREEN (s);
int target;
char *targetString;
if (texture->target == GL_TEXTURE_2D)
{
target = COMP_FETCH_TARGET_2D;
}
else
{
target = COMP_FETCH_TARGET_RECT;
}
if (rs->image.target == GL_TEXTURE_2D)
{
targetString = "2D";
}
else
{
targetString = "RECT";
}
if (rs->function.handle &&
rs->function.param == param &&
rs->function.target == target &&
rs->function.unit == unit)
return rs->function.handle;
data = createFunctionData ();
if (data)
{
Bool ok = TRUE;
int handle = 0;
char str[1024];
ok &= addTempHeaderOpToFunctionData (data, "image");
ok &= addTempHeaderOpToFunctionData (data, "tmp");
ok &= addFetchOpToFunctionData (data, "output", NULL, target);
ok &= addColorOpToFunctionData (data, "output", "output");
snprintf (str, 1024,
"MAD tmp, fragment.position, program.env[%d],"
" program.env[%d];", param, param + 1);
ok &= addDataOpToFunctionData (data, str);
snprintf (str, 1024,
"TEX image, tmp, texture[%d], %s;", unit, targetString);
ok &= addDataOpToFunctionData (data, str);
snprintf (str, 1024,
"MUL_SAT tmp, output.a, program.env[%d].b;"
"MAD image, -output.a, image, image;"
"MAD output, image, tmp.a, output;", param + 1);
ok &= addDataOpToFunctionData (data, str);
if (!ok)
{
destroyFunctionData (data);
return 0;
}
handle = createFragmentFunction (s, "reflex", data);
rs->function.handle = handle;
rs->function.target = target;
rs->function.param = param;
rs->function.unit = unit;
destroyFunctionData (data);
return handle;
}
return 0;
}
static void
reflexUpdateWindowMatch (CompWindow *w)
{
Bool active;
REFLEX_WINDOW (w);
active = matchEval (reflexGetMatch (w->screen), w);
if (active != rw->active)
{
rw->active = active;
addWindowDamage (w);
}
}
static void
reflexScreenOptionChanged (CompScreen *s,
CompOption *opt,
ReflexScreenOptions num)
{
CompWindow *w;
REFLEX_SCREEN (s);
switch (num)
{
case ReflexScreenOptionFile:
if (rs->imageLoaded)
{
finiTexture (s, &rs->image);
initTexture (s, &rs->image);
}
rs->imageLoaded = readImageToTexture (s, &rs->image, reflexGetFile (s),
&rs->width, &rs->height);
damageScreen (s);
break;
case ReflexScreenOptionMatch:
for (w = s->windows; w; w = w->next)
reflexUpdateWindowMatch (w);
damageScreen (s);
break;
default:
damageScreen (s);
break;
}
}
static void
reflexDrawWindowTexture (CompWindow *w,
CompTexture *texture,
const FragmentAttrib *attrib,
unsigned int mask)
{
CompScreen *s = w->screen;
REFLEX_SCREEN (s);
REFLEX_WINDOW (w);
Bool enabled = (texture == w->texture) ?
reflexGetWindow (s) : reflexGetDecoration (s);
if (enabled && rw->active && rs->imageLoaded &&
w->screen->fragmentProgram)
{
FragmentAttrib fa = *attrib;
int function;
int unit = 0;
int param;
float tx, ty, dx, mx;
if (reflexGetMoving (s) )
{
mx = w->attrib.x + (w->width / 2);
mx /= s->width / 2.0;
mx -= 1.0;
mx *= -0.065;
}
else
mx = 0.0;
if (rs->image.target == GL_TEXTURE_2D)
{
tx = 1.0 / s->width;
ty = 1.0 / s->height;
dx = mx;
}
else
{
tx = 1.0 / s->width * rs->width;
ty = 1.0 / s->height * rs->height;
dx = mx * rs->width;
}
unit = allocFragmentTextureUnits (&fa, 1);
param = allocFragmentParameters (&fa, 2);
function = getReflexFragmentFunction (w->screen, texture, param, unit);
if (function)
{
addFragmentFunction (&fa, function);
(*s->activeTexture) (GL_TEXTURE0_ARB + unit);
enableTexture (s, &rs->image, COMP_TEXTURE_FILTER_GOOD);
(*s->activeTexture) (GL_TEXTURE0_ARB);
(*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, param,
tx, ty, 0.0f, 0.0f);
(*s->programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, param + 1,
dx, 0.0f,
reflexGetThreshold (s), 0.0f);
}
UNWRAP (rs, w->screen, drawWindowTexture);
(*w->screen->drawWindowTexture) (w, texture, &fa, mask);
WRAP (rs, w->screen, drawWindowTexture, reflexDrawWindowTexture);
if (unit)
{
(*s->activeTexture) (GL_TEXTURE0_ARB + unit);
disableTexture (s, &rs->image);
(*s->activeTexture) (GL_TEXTURE0_ARB);
}
}
else
{
UNWRAP (rs, w->screen, drawWindowTexture);
(*w->screen->drawWindowTexture) (w, texture, attrib, mask);
WRAP (rs, w->screen, drawWindowTexture, reflexDrawWindowTexture);
}
}
static void
reflexMatchExpHandlerChanged (CompDisplay *d)
{
CompScreen *s;
CompWindow *w;
REFLEX_DISPLAY (d);
UNWRAP (rd, d, matchExpHandlerChanged);
(*d->matchExpHandlerChanged) (d);
WRAP (rd, d, matchExpHandlerChanged, reflexMatchExpHandlerChanged);
/* match options are up to date after the call to matchExpHandlerChanged */
for (s = d->screens; s; s = s->next)
{
for (w = s->windows; w; w = w->next)
reflexUpdateWindowMatch (w);
}
}
static void
reflexMatchPropertyChanged (CompDisplay *d,
CompWindow *w)
{
REFLEX_DISPLAY (d);
reflexUpdateWindowMatch (w);
UNWRAP (rd, d, matchPropertyChanged);
(*d->matchPropertyChanged) (d, w);
WRAP (rd, d, matchPropertyChanged, reflexMatchPropertyChanged);
}
static Bool
reflexInitDisplay (CompPlugin *p,
CompDisplay *d)
{
ReflexDisplay *rd;
rd = malloc (sizeof (ReflexDisplay) );
if (!rd)
return FALSE;
rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
if (rd->screenPrivateIndex < 0)
{
free (rd);
return FALSE;
}
d->privates[displayPrivateIndex].ptr = rd;
WRAP (rd, d, matchExpHandlerChanged, reflexMatchExpHandlerChanged);
WRAP (rd, d, matchPropertyChanged, reflexMatchPropertyChanged);
return TRUE;
}
static void
reflexFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
REFLEX_DISPLAY (d);
freeScreenPrivateIndex (d, rd->screenPrivateIndex);
UNWRAP (rd, d, matchExpHandlerChanged);
UNWRAP (rd, d, matchPropertyChanged);
free (rd);
}
static Bool
reflexInitScreen (CompPlugin *p,
CompScreen *s)
{
ReflexScreen *rs;
REFLEX_DISPLAY (s->display);
rs = malloc (sizeof (ReflexScreen) );
if (!rs)
return FALSE;
rs->windowPrivateIndex = allocateWindowPrivateIndex (s);
if (rs->windowPrivateIndex < 0)
{
free (rs);
return FALSE;
}
initTexture (s, &rs->image);
rs->imageLoaded = readImageToTexture (s, &rs->image, reflexGetFile (s),
&rs->width, &rs->height);
reflexSetFileNotify (s, reflexScreenOptionChanged);
reflexSetMatchNotify (s, reflexScreenOptionChanged);
s->privates[rd->screenPrivateIndex].ptr = rs;
rs->function.handle = 0;
WRAP (rs, s, drawWindowTexture, reflexDrawWindowTexture);
return TRUE;
}
static void
reflexFiniScreen (CompPlugin *p,
CompScreen *s)
{
REFLEX_SCREEN (s);
freeWindowPrivateIndex (s, rs->windowPrivateIndex);
UNWRAP (rs, s, drawWindowTexture);
if (rs->function.handle)
destroyFragmentFunction (s, rs->function.handle);
free (rs);
}
static Bool
reflexInitWindow (CompPlugin *p,
CompWindow *w)
{
ReflexWindow *rw;
REFLEX_SCREEN (w->screen);
rw = malloc (sizeof (ReflexWindow));
if (!rw)
return FALSE;
rw->active = FALSE;
w->privates[rs->windowPrivateIndex].ptr = rw;
reflexUpdateWindowMatch (w);
return TRUE;
}
static void
reflexFiniWindow (CompPlugin *p,
CompWindow *w)
{
REFLEX_WINDOW (w);
free (rw);
}
static Bool
reflexInit (CompPlugin *p)
{
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void
reflexFini (CompPlugin *p)
{
if (displayPrivateIndex >= 0)
freeDisplayPrivateIndex (displayPrivateIndex);
}
static int
reflexGetVersion (CompPlugin *plugin,
int version)
{
return ABIVERSION;
}
CompPluginVTable reflexVTable = {
"reflex",
reflexGetVersion,
0,
reflexInit,
reflexFini,
reflexInitDisplay,
reflexFiniDisplay,
reflexInitScreen,
reflexFiniScreen,
reflexInitWindow,
reflexFiniWindow,
0,
0,
0,
0
};
CompPluginVTable *
getCompPluginInfo (void)
{
return &reflexVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1