e16/src/fx.c

570 lines
14 KiB
C

/*
* Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
* Copyright (C) 2004-2021 Kim Woelders
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of the Software, its documentation and marketing & publicity
* materials, and acknowledgment shall be given in the documentation, materials
* and software packages that this Software was used.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <math.h>
#include <X11/Xlib.h>
#if USE_XRENDER
#define FX_USE_RENDER 1
#include <X11/extensions/Xrender.h>
#endif
#include "E.h"
#include "animation.h"
#include "desktops.h"
#include "dialog.h"
#include "ecompmgr.h"
#include "eimage.h"
#include "emodule.h"
#include "settings.h"
#include "util.h"
#include "xwin.h"
#define M_2PI_F ((float)(2 * M_PI))
#define FX_OP_ENABLE 1 /* Enable, start */
#define FX_OP_DISABLE 2 /* Disable, stop */
#define FX_OP_START 3 /* Start (if enabled) */
#define FX_OP_PAUSE 4
#define FX_OP_DESK 5
typedef struct {
const char *name;
void (*init_func)(const char *name);
void (*ops_func)(int op);
char enabled;
char active;
} FXHandler;
typedef struct {
Win win;
EX_Window root;
EX_Pixmap above;
int count;
float incv, inch, incx;
#if FX_USE_RENDER
EX_Picture pict_buf;
EX_Picture pict_win;
#if USE_COMPOSITE
EX_Picture pict_win_clip;
#endif
#else
GC gc1;
#endif
} FXData;
static void
_FxSetup(FXData *d, unsigned int height)
{
EObj *bgeo;
if (!d->above)
{
bgeo = DeskGetBackgroundObj(DesksGetCurrent());
d->win = EobjGetWin(bgeo);
#if USE_COMPOSITE
if (ECompMgrIsActive() && !Mode.wm.window)
d->root = ECompMgrRootWin();
else
#endif
d->root = EobjGetXwin(bgeo);
d->above = ECreatePixmap(d->win, WinGetW(VROOT), height, 0);
#if FX_USE_RENDER
XRenderPictureAttributes pa;
d->pict_buf = EPictureCreate(NULL, d->above);
d->pict_win = EPictureCreate(NULL, d->root);
#if USE_COMPOSITE
d->pict_win_clip = EPictureCreate(NULL, d->root);
#endif
pa.subwindow_mode = ClipByChildren;
XRenderChangePicture(disp, d->pict_win, CPSubwindowMode, &pa);
#else
XGCValues xgcv;
xgcv.subwindow_mode = ClipByChildren;
if (!d->gc1)
d->gc1 = EXCreateGC(d->root, GCSubwindowMode, &xgcv);
#endif
}
#if USE_COMPOSITE
#if FX_USE_RENDER
EPictureSetClip(d->pict_win_clip, ECompMgrChildClipRegion());
#else
/* As of composite 0.4 we need to set the clip region */
EGCSetClip(d->gc1, ECompMgrChildClipRegion());
#endif
#endif
}
static void
_FxCleanup(FXData *d, int h)
{
if (h == 0)
{
EFreePixmap(d->above);
d->above = NoXID;
d->count = 0;
}
else
{
#if FX_USE_RENDER
EPictureDestroy(d->pict_buf);
EPictureDestroy(d->pict_win);
#if USE_COMPOSITE
EPictureDestroy(d->pict_win_clip);
#endif
#else
EXFreeGC(d->gc1);
#endif
EClearArea(d->win, 0, WinGetH(VROOT) - h, WinGetW(VROOT), h);
#if USE_COMPOSITE
ECompMgrDamageArea(0, WinGetH(VROOT) - h, WinGetW(VROOT), h);
#endif
}
}
static void
_FxCopyArea(FXData *d, int out, int src_x, int src_y, int dst_x, int dst_y,
unsigned int width, unsigned int height)
{
EX_ID src, dst;
#if FX_USE_RENDER
if (out)
{
src = d->pict_buf;
#if USE_COMPOSITE
dst = d->pict_win_clip;
#else
dst = d->pict_win;
#endif
}
else
{
src = d->pict_win;
dst = d->pict_buf;
}
XRenderComposite(disp, PictOpSrc, src, NoXID, dst,
src_x, src_y, 0, 0, dst_x, dst_y, width, height);
#else
if (out)
{
src = d->above;
dst = d->root;
EXCopyAreaGC(src, dst, d->gc1, src_x, src_y, width, height,
dst_x, dst_y);
}
else
{
src = d->root;
dst = d->above;
EXCopyArea(src, dst, src_x, src_y, width, height, dst_x, dst_y);
}
#endif
}
/****************************** RIPPLES *************************************/
#define FX_RIPPLE_WATERH 64
static Animator *fx_ripple = NULL;
static int
FX_ripple_timeout(EObj *eo __UNUSED__, int run __UNUSED__, void *state)
{
FXData *d = (FXData *) state;
int y;
_FxSetup(d, FX_RIPPLE_WATERH * 2);
if (d->count == 0)
_FxCopyArea(d, 0, 0, WinGetH(VROOT) - (FX_RIPPLE_WATERH * 3), 0, 0,
WinGetW(VROOT), FX_RIPPLE_WATERH * 2);
d->count++;
if (d->count > 32)
d->count = 0;
d->incv += 0.40f;
if (d->incv > M_2PI_F)
d->incv = 0;
d->inch += 0.32f;
if (d->inch > M_2PI_F)
d->inch = 0;
for (y = 0; y < FX_RIPPLE_WATERH; y++)
{
float a, p;
int xoff, yoff, yy;
p = (float)(FX_RIPPLE_WATERH - y) / (float)FX_RIPPLE_WATERH;
a = p * p * 48 + d->incv;
yoff = y + (int)(sinf(a) * 7) + 8; /* 0:63 + -7:7 + 8 = 1:78 */
yy = (FX_RIPPLE_WATERH * 2) - yoff; /* 128 - 1:78 = 127:50 */
a = p * p * 64 + d->inch;
xoff = (int)(sinf(a) * 10 * (1 - p));
_FxCopyArea(d, 1, 0, yy, xoff, WinGetH(VROOT) - FX_RIPPLE_WATERH + y,
WinGetW(VROOT), 1);
}
return 4;
}
static void
FX_Ripple_Init(const char *name __UNUSED__)
{
FXData fxd;
memset(&fxd, 0, sizeof(fxd));
fx_ripple = AnimatorAdd(NULL, ANIM_FX_RIPPLES, FX_ripple_timeout, -1, 0,
sizeof(fxd), &fxd);
}
static void
FX_Ripple_Ops(int op)
{
FXData *d;
d = (FXData *) AnimatorGetData(fx_ripple);
if (!d)
return;
_FxCleanup(d, 0);
if (op != FX_OP_DISABLE)
return;
_FxCleanup(d, FX_RIPPLE_WATERH);
AnimatorDel(NULL, fx_ripple);
fx_ripple = NULL;
}
/****************************** WAVES ***************************************/
/* by tsade :) */
/****************************************************************************/
#define FX_WAVE_WATERH 64
#define FX_WAVE_WATERW 64
#define FX_WAVE_DEPTH 10
#define FX_WAVE_CROSSPERIOD 0.42f
static Animator *fx_waves = NULL;
static int
FX_Wave_timeout(EObj *eo __UNUSED__, int run __UNUSED__, void *state)
{
FXData *d = (FXData *) state;
float incx2;
int y;
_FxSetup(d, FX_WAVE_WATERH * 2);
/* On the zero, grab the desktop again. */
if (d->count == 0)
_FxCopyArea(d, 0, 0, WinGetH(VROOT) - (FX_WAVE_WATERH * 3), 0, 0,
WinGetW(VROOT), FX_WAVE_WATERH * 2);
/* Increment and roll the counter */
d->count++;
if (d->count > 32)
d->count = 0;
/* Increment and roll some other variables */
d->incv += 0.40f;
if (d->incv > M_2PI_F)
d->incv = 0;
d->inch += 0.32f;
if (d->inch > M_2PI_F)
d->inch = 0;
d->incx += 0.32f;
if (d->incx > M_2PI_F)
d->incx = 0;
/* Go through the bottom couple (FX_WAVE_WATERH) lines of the window */
for (y = 0; y < FX_WAVE_WATERH; y++)
{
/* Variables */
float a, p;
int xoff, yoff, yy;
int x;
/* Figure out the side-to-side movement */
p = (float)(FX_WAVE_WATERH - y) / (float)FX_WAVE_WATERH;
a = p * p * 48 + d->incv;
yoff = y + (int)(sinf(a) * 7) + 8; /* 0:63 + -7:7 + 8 = 1:78 */
yy = (FX_WAVE_WATERH * 2) - yoff; /* 128 - 1:78 = 127:50 */
a = p * p * 64 + d->inch;
xoff = (int)(sinf(a) * 10 * (1 - p));
/* Set up the next part */
incx2 = d->incx;
/* Go through the width of the screen, in block sizes */
for (x = 0; x < WinGetW(VROOT); x += FX_WAVE_WATERW)
{
/* Variables */
int sx;
/* Add something to incx2 and roll it */
incx2 += FX_WAVE_CROSSPERIOD;
if (incx2 > M_2PI_F)
incx2 = 0;
/* Figure it out */
sx = (int)(sinf(incx2) * FX_WAVE_DEPTH);
/* Display this block */
_FxCopyArea(d, 1, x, yy, xoff + x,
WinGetH(VROOT) - FX_WAVE_WATERH + y + sx,
FX_WAVE_WATERW, 1);
}
}
return 4;
}
static void
FX_Waves_Init(const char *name __UNUSED__)
{
FXData fxd;
memset(&fxd, 0, sizeof(fxd));
fx_waves = AnimatorAdd(NULL, ANIM_FX_WAVES, FX_Wave_timeout, -1, 0,
sizeof(fxd), &fxd);
}
static void
FX_Waves_Ops(int op)
{
FXData *d;
d = (FXData *) AnimatorGetData(fx_waves);
if (!d)
return;
_FxCleanup(d, 0);
if (op != FX_OP_DISABLE)
return;
_FxCleanup(d, 2 * FX_WAVE_WATERH + FX_WAVE_DEPTH);
AnimatorDel(NULL, fx_waves);
fx_waves = NULL;
}
/****************************************************************************/
#define fx_rip fx_handlers[0]
#define fx_wav fx_handlers[1]
static FXHandler fx_handlers[] = {
{ "ripples", FX_Ripple_Init, FX_Ripple_Ops, 0, 0 },
{ "waves", FX_Waves_Init, FX_Waves_Ops, 0, 0 },
};
/****************************** Effect handlers *****************************/
static void
FX_Op(FXHandler *fxh, int op)
{
switch (op)
{
case FX_OP_ENABLE:
if (fxh->enabled)
break;
fxh->enabled = 1;
goto do_start;
case FX_OP_DISABLE:
if (!fxh->enabled)
break;
fxh->enabled = 0;
goto do_stop;
case FX_OP_START:
if (!fxh->enabled)
break;
do_start:
if (fxh->active)
break;
fxh->init_func(fxh->name);
fxh->active = 1;
break;
case FX_OP_PAUSE:
if (!fxh->enabled)
break;
do_stop:
if (!fxh->active)
break;
fxh->ops_func(FX_OP_DISABLE);
fxh->active = 0;
break;
case FX_OP_DESK:
if (!fxh->enabled)
break;
fxh->ops_func(FX_OP_DESK);
break;
}
}
static void
FX_OpForEach(int op)
{
unsigned int i;
for (i = 0; i < E_ARRAY_SIZE(fx_handlers); i++)
FX_Op(&fx_handlers[i], op);
}
static void
FxCfgFunc(void *item __UNUSED__, const char *value)
{
FXHandler *fxh = NULL;
if (item == &fx_rip.enabled)
fxh = &fx_rip;
else if (item == &fx_wav.enabled)
fxh = &fx_wav;
if (!fxh)
return;
FX_Op(fxh, atoi(value) ? FX_OP_ENABLE : FX_OP_DISABLE);
}
/****************************************************************************/
/*
* Fx Module
*/
static void
FxSighan(int sig, void *prm __UNUSED__)
{
switch (sig)
{
case ESIGNAL_START:
FX_OpForEach(FX_OP_START);
break;
case ESIGNAL_DESK_SWITCH_START:
break;
case ESIGNAL_DESK_SWITCH_DONE:
FX_OpForEach(FX_OP_DESK);
break;
case ESIGNAL_ANIMATION_SUSPEND:
FX_OpForEach(FX_OP_PAUSE);
break;
case ESIGNAL_ANIMATION_RESUME:
FX_OpForEach(FX_OP_START);
break;
}
}
#if ENABLE_DIALOGS
static char tmp_effect_ripples;
static char tmp_effect_waves;
static void
_DlgApplyFx(Dialog *d __UNUSED__, int val __UNUSED__, void *data __UNUSED__)
{
FX_Op(&fx_rip, tmp_effect_ripples ? FX_OP_ENABLE : FX_OP_DISABLE);
FX_Op(&fx_wav, tmp_effect_waves ? FX_OP_ENABLE : FX_OP_DISABLE);
autosave();
}
static void
_DlgFillFx(Dialog *d __UNUSED__, DItem *table, void *data __UNUSED__)
{
DItem *di;
tmp_effect_ripples = fx_rip.enabled;
tmp_effect_waves = fx_wav.enabled;
DialogItemTableSetOptions(table, 1, 0, 0, 0);
/* Effects */
di = DialogAddItem(table, DITEM_TEXT);
DialogItemSetText(di, _("Effects"));
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetText(di, _("Ripples"));
DialogItemCheckButtonSetPtr(di, &tmp_effect_ripples);
di = DialogAddItem(table, DITEM_CHECKBUTTON);
DialogItemSetText(di, _("Waves"));
DialogItemCheckButtonSetPtr(di, &tmp_effect_waves);
}
const DialogDef DlgFx = {
"CONFIGURE_FX",
N_("FX"), N_("Special FX Settings"),
0,
SOUND_SETTINGS_FX,
"pix/fx.png",
N_("Enlightenment Special Effects\n" "Settings Dialog"),
_DlgFillFx,
DLG_OAC, _DlgApplyFx, NULL
};
#endif /* ENABLE_DIALOGS */
#define CFR_FUNC_BOOL(conf, name, dflt, func) \
{ #name, &conf, ITEM_TYPE_BOOL, dflt, func }
static const CfgItem FxCfgItems[] = {
CFR_FUNC_BOOL(fx_handlers[0].enabled, ripples.enabled, 0, FxCfgFunc),
CFR_FUNC_BOOL(fx_handlers[1].enabled, waves.enabled, 0, FxCfgFunc),
};
/*
* Module descriptor
*/
extern const EModule ModEffects;
const EModule ModEffects = {
"effects", "fx",
FxSighan,
{ 0, NULL },
MOD_ITEMS(FxCfgItems)
};