/* Copyright (c) 2003 Levi Burton <donburton@sbcglobal.net>
*
* 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. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
#include "screenhack.h"
#include "colors.h"
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
# include "xdbe.h"
#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
typedef struct _square {
int x, y, w, h;
int color;
} square;
static void
randomize_square_colors(square *squares, int nsquares, int ncolors)
{
int i;
square *s = squares;
for (i = 0; i < nsquares; i++)
s[i].color = random() % ncolors;
}
struct state {
Display *dpy;
Window window;
int delay, subdivisionx, subdivisiony, border, ncolors, twitch, dbuf;
XWindowAttributes xgwa;
GC gc;
XColor *colors;
int sw, sh, gw, gh, nsquares;
square *squares;
Pixmap b, ba, bb;
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
XdbeBackBuffer backb;
#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
};
static void
popsquares_reshape (Display *dpy, Window window, void *closure,
unsigned int w, unsigned int h)
{
struct state *st = (struct state *) closure;
int s = get_integer_resource(st->dpy, "subdivision", "Integer");
int x, y;
XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
if (st->xgwa.width < 100 || st->xgwa.height < 100) /* tiny window */
{
int ss = (st->xgwa.width < st->xgwa.height
? st->xgwa.width : st->xgwa.height);
s = ss / 15;
if (s < 1) s = 1;
}
if (st->xgwa.width > st->xgwa.height * 5 || /* weird aspect ratio */
st->xgwa.height > st->xgwa.width * 5)
{
double r = st->xgwa.width / (double) st->xgwa.height;
if (r > 1)
{
st->subdivisiony = s;
st->subdivisionx = s * r;
}
else
{
st->subdivisionx = s;
st->subdivisiony = s / r;
}
}
else
{
st->subdivisionx = st->subdivisiony = s;
}
st->sw = st->xgwa.width / st->subdivisionx;
st->sh = st->xgwa.height / st->subdivisiony;
st->gw = st->sw ? st->xgwa.width / st->sw : 0;
st->gh = st->sh ? st->xgwa.height / st->sh : 0;
st->nsquares = st->gw * st->gh;
free (st->squares);
if (st->nsquares < 1) st->nsquares = 1;
st->squares = (square *) calloc (st->nsquares, sizeof(square));
for (y = 0; y < st->gh; y++)
for (x = 0; x < st->gw; x++)
{
square *s = (square *) &st->squares[st->gw * y + x];
s->w = st->sw;
s->h = st->sh;
s->x = x * st->sw;
s->y = y * st->sh;
}
randomize_square_colors(st->squares, st->nsquares, st->ncolors);
if (st->dbuf) {
XFreePixmap (dpy, st->ba);
XFreePixmap (dpy, st->bb);
st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth);
st->bb = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth);
st->b = st->ba;
}
}
static void *
popsquares_init (Display *dpy, Window window)
{
struct state *st = (struct state *) calloc (1, sizeof(*st));
int x, y;
double s1, v1, s2, v2 = 0;
int h1, h2 = 0;
/* Not sure how to use DBEClear */
/* Bool dbeclear_p = get_boolean_resource(dpy, "useDBEClear", "Boolean"); */
XColor fg, bg;
XGCValues gcv;
st->dpy = dpy;
st->window = window;
st->delay = get_integer_resource (st->dpy, "delay", "Integer");
st->subdivisionx = get_integer_resource(st->dpy, "subdivision", "Integer");
st->subdivisiony = st->subdivisionx;
st->border = get_integer_resource(st->dpy, "border", "Integer");
st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer");
st->twitch = get_boolean_resource(st->dpy, "twitch", "Boolean");
st->dbuf = get_boolean_resource(st->dpy, "doubleBuffer", "Boolean");
# ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */
st->dbuf = False;
# endif
XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
fg.pixel = get_pixel_resource (st->dpy, st->xgwa.colormap, "foreground", "Foreground");
bg.pixel = get_pixel_resource (st->dpy, st->xgwa.colormap, "background", "Background");
XQueryColor (st->dpy, st->xgwa.colormap, &fg);
XQueryColor (st->dpy, st->xgwa.colormap, &bg);
st->sw = st->xgwa.width / st->subdivisionx;
st->sh = st->xgwa.height / st->subdivisiony;
st->gw = st->sw ? st->xgwa.width / st->sw : 0;
st->gh = st->sh ? st->xgwa.height / st->sh : 0;
st->nsquares = st->gw * st->gh;
if (st->nsquares < 1) st->nsquares = 1;
if (st->ncolors < 1) st->ncolors = 1;
gcv.foreground = fg.pixel;
gcv.background = bg.pixel;
st->gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
st->colors = (XColor *) calloc (st->ncolors, sizeof(XColor));
st->squares = (square *) calloc (st->nsquares, sizeof(square));
rgb_to_hsv (fg.red, fg.green, fg.blue, &h1, &s1, &v1);
rgb_to_hsv (bg.red, bg.green, bg.blue, &h2, &s2, &v2);
make_color_ramp (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
h1, s1, v1,
h2, s2, v2,
st->colors, &st->ncolors, /* would this be considered a value-result argument? */
True, True, False);
if (st->ncolors < 2)
{
fprintf (stderr, "%s: insufficient colors!\n", progname);
exit (1);
}
for (y = 0; y < st->gh; y++)
for (x = 0; x < st->gw; x++)
{
square *s = (square *) &st->squares[st->gw * y + x];
s->w = st->sw;
s->h = st->sh;
s->x = x * st->sw;
s->y = y * st->sh;
}
randomize_square_colors(st->squares, st->nsquares, st->ncolors);
if (st->dbuf)
{
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeUndefined);
st->backb = st->b;
#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
if (!st->b)
{
st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth);
st->bb = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height, st->xgwa.depth);
st->b = st->ba;
}
}
else
{
st->b = st->window;
}
popsquares_reshape (dpy, window, st, st->xgwa.width, st->xgwa.height);
return st;
}
static unsigned long
popsquares_draw (Display *dpy, Window window, void *closure)
{
struct state *st = (struct state *) closure;
int x, y;
for (y = 0; y < st->gh; y++)
for (x = 0; x < st->gw; x++)
{
square *s = (square *) &st->squares[st->gw * y + x];
XSetForeground (st->dpy, st->gc, st->colors[s->color].pixel);
XFillRectangle (st->dpy, st->b, st->gc, s->x, s->y,
st->border ? s->w - st->border : s->w,
st->border ? s->h - st->border : s->h);
s->color++;
if (s->color == st->ncolors)
{
if (st->twitch && ((random() % 4) == 0))
randomize_square_colors (st->squares, st->nsquares, st->ncolors);
else
s->color = random() % st->ncolors;
}
}
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
if (st->backb)
{
XdbeSwapInfo info[1];
info[0].swap_window = st->window;
info[0].swap_action = XdbeUndefined;
XdbeSwapBuffers (st->dpy, info, 1);
}
else
#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
if (st->dbuf)
{
XCopyArea (st->dpy, st->b, st->window, st->gc, 0, 0,
st->xgwa.width, st->xgwa.height, 0, 0);
st->b = (st->b == st->ba ? st->bb : st->ba);
}
return st->delay;
}
static Bool
popsquares_event (Display *dpy, Window window, void *closure, XEvent *event)
{
return False;
}
static void
popsquares_free (Display *dpy, Window window, void *closure)
{
struct state *st = (struct state *) closure;
XFreeGC (dpy, st->gc);
free (st->colors);
free (st->squares);
free (st);
}
static const char *popsquares_defaults [] = {
".background: #0000FF",
".foreground: #00008B",
"*delay: 25000",
"*subdivision: 5",
"*border: 1",
"*ncolors: 128",
"*twitch: False",
"*doubleBuffer: False",
#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
"*useDBE: True",
"*useDBEClear: True",
#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
#ifdef HAVE_MOBILE
"*ignoreRotation: True",
#endif
0
};
static XrmOptionDescRec popsquares_options [] = {
{ "-fg", ".foreground", XrmoptionSepArg, 0},
{ "-bg", ".background", XrmoptionSepArg, 0},
{ "-delay", ".delay", XrmoptionSepArg, 0 },
{ "-subdivision", ".subdivision", XrmoptionSepArg, 0 },
{ "-border", ".border", XrmoptionSepArg, 0},
{ "-ncolors", ".ncolors", XrmoptionSepArg, 0 },
{ "-twitch", ".twitch", XrmoptionNoArg, "True" },
{ "-no-twitch", ".twitch", XrmoptionNoArg, "False" },
{ "-db", ".doubleBuffer", XrmoptionNoArg, "True" },
{ "-no-db", ".doubleBuffer", XrmoptionNoArg, "False" },
{ 0, 0, 0, 0 }
};
XSCREENSAVER_MODULE ("PopSquares", popsquares)