/* cynosure --- draw some rectangles
*
* 01-aug-96: written in Java by ozymandias G desiderata <ogd@organic.com>
* 25-dec-97: ported to C and XScreenSaver by Jamie Zawinski <jwz@jwz.org>
*
* Original version:
* http://www.organic.com/staff/ogd/java/cynosure.html
* http://www.organic.com/staff/ogd/java/source/cynosure/Cynosure-java.txt
*
* Original comments and copyright:
*
* Cynosure.java
* A Java implementation of Stephen Linhart's Cynosure screen-saver as a
* drop-in class.
*
* Header: /home/ogd/lib/cvs/aoaioxxysz/graphics/Cynosure.java,v 1.2 1996/08/02 02:41:21 ogd Exp
*
* ozymandias G desiderata <ogd@organic.com>
* Thu Aug 1 1996
*
* COPYRIGHT NOTICE
*
* Copyright 1996 ozymandias G desiderata. Title, ownership rights, and
* intellectual property rights in and to this software remain with
* ozymandias G desiderata. This software may be copied, modified,
* or used as long as this copyright is retained. Use this code at your
* own risk.
*
* Revision: 1.2
*
* Log: Cynosure.java,v
* Revision 1.2 1996/08/02 02:41:21 ogd
* Added a few more comments, fixed messed-up header.
*
* Revision 1.1.1.1 1996/08/02 02:30:45 ogd
* First version
*/
#include "screenhack.h"
/* #define DO_STIPPLE */
struct state {
Display *dpy;
Window window;
XColor *colors;
int ncolors;
#ifndef DO_STIPPLE
XColor *colors2;
int ncolors2;
#endif
int fg_pixel, bg_pixel;
GC fg_gc, bg_gc, shadow_gc;
int curColor;
int curBase; /* color progression */
int shadowWidth;
int elevation; /* offset of dropshadow */
int sway; /* time until base color changed */
int timeLeft; /* until base color used */
int tweak; /* amount of color variance */
int gridSize;
int iterations, i, delay;
XWindowAttributes xgwa;
};
/**
* The smallest size for an individual cell.
**/
#define MINCELLSIZE 16
/**
* The narrowest a rectangle can be.
**/
#define MINRECTSIZE 6
/**
* Every so often genNewColor() generates a completely random
* color. This variable sets how frequently that happens. It's
* currently set to happen 1% of the time.
*
* @see #genNewColor
**/
#define THRESHOLD 100 /*0.01*/
static void paint(struct state *st);
static int genNewColor(struct state *st);
static int genConstrainedColor(struct state *st, int base, int tweak);
static int c_tweak(struct state *st, int base, int tweak);
static void *
cynosure_init (Display *d, Window w)
{
struct state *st = (struct state *) calloc (1, sizeof(*st));
XGCValues gcv;
st->dpy = d;
st->window = w;
st->curColor = 0;
st->curBase = st->curColor;
st->shadowWidth = get_integer_resource (st->dpy, "shadowWidth", "Integer");
st->elevation = get_integer_resource (st->dpy, "elevation", "Integer");
st->sway = get_integer_resource (st->dpy, "sway", "Integer");
st->tweak = get_integer_resource (st->dpy, "tweak", "Integer");
st->gridSize = get_integer_resource (st->dpy, "gridSize", "Integer");
st->timeLeft = 0;
XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
if (st->ncolors < 2) st->ncolors = 2;
if (st->ncolors <= 2) mono_p = True;
if (mono_p)
st->colors = 0;
else
st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
if (mono_p)
;
else {
make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
st->colors, &st->ncolors,
True, 0, True);
if (st->ncolors <= 2) {
mono_p = True;
st->ncolors = 2;
if (st->colors) free(st->colors);
st->colors = 0;
}
}
st->bg_pixel = get_pixel_resource(st->dpy,
st->xgwa.colormap, "background", "Background");
st->fg_pixel = get_pixel_resource(st->dpy,
st->xgwa.colormap, "foreground", "Foreground");
gcv.foreground = st->fg_pixel;
st->fg_gc = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
gcv.foreground = st->bg_pixel;
st->bg_gc = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
#ifdef DO_STIPPLE
gcv.fill_style = FillStippled;
gcv.stipple = XCreateBitmapFromData(st->dpy, st->window, "\125\252", 8, 2);
st->shadow_gc = XCreateGC(st->dpy, st->window, GCForeground|GCFillStyle|GCStipple, &gcv);
XFreePixmap(st->dpy, gcv.stipple);
#else /* !DO_STIPPLE */
st->shadow_gc = XCreateGC(st->dpy, st->window, GCForeground, &gcv);
# ifdef HAVE_JWXYZ /* allow non-opaque alpha components in pixel values */
jwxyz_XSetAlphaAllowed (st->dpy, st->shadow_gc, True);
# endif
if (st->colors)
{
int i;
st->ncolors2 = st->ncolors;
st->colors2 = (XColor *) malloc(sizeof(*st->colors2) * (st->ncolors2+1));
for (i = 0; i < st->ncolors2; i++)
{
# ifdef HAVE_JWXYZ
/* give a non-opaque alpha to the shadow colors */
unsigned long pixel = st->colors[i].pixel;
unsigned long amask = BlackPixelOfScreen (st->xgwa.screen);
unsigned long a = (0x77777777 & amask);
pixel = (pixel & (~amask)) | a;
st->colors2[i].pixel = pixel;
# else /* !HAVE_JWXYZ */
int h;
double s, v;
rgb_to_hsv (st->colors[i].red,
st->colors[i].green,
st->colors[i].blue,
&h, &s, &v);
v *= 0.4;
hsv_to_rgb (h, s, v,
&st->colors2[i].red,
&st->colors2[i].green,
&st->colors2[i].blue);
st->colors2[i].pixel = st->colors[i].pixel;
XAllocColor (st->dpy, st->xgwa.colormap, &st->colors2[i]);
# endif /* !HAVE_JWXYZ */
}
}
# endif /* !DO_STIPPLE */
st->delay = get_integer_resource (st->dpy, "delay", "Delay");
st->iterations = get_integer_resource (st->dpy, "iterations", "Iterations");
return st;
}
static unsigned long
cynosure_draw (Display *dpy, Window window, void *closure)
{
struct state *st = (struct state *) closure;
if (st->iterations > 0 && ++st->i >= st->iterations)
{
st->i = 0;
if (!mono_p)
XSetWindowBackground(st->dpy, st->window,
st->colors[random() % st->ncolors].pixel);
XClearWindow(st->dpy, st->window);
}
paint(st);
return st->delay;
}
/**
* paint adds a new layer of multicolored rectangles within a grid of
* randomly generated size. Each row of rectangles is the same color,
* but colors vary slightly from row to row. Each rectangle is placed
* within a regularly-sized cell, but each rectangle is sized and
* placed randomly within that cell.
*
* @param g the Graphics coordinate in which to draw
* @see #genNewColor
**/
static void paint(struct state *st)
{
int i;
int cellsWide, cellsHigh, cellWidth, cellHeight;
int width = st->xgwa.width;
int height = st->xgwa.height;
/* How many cells wide the grid is (equal to gridSize +/- (gridSize / 2))
*/
cellsWide = c_tweak(st, st->gridSize, st->gridSize / 2);
/* How many cells high the grid is (equal to gridSize +/- (gridSize / 2))
*/
cellsHigh = c_tweak(st, st->gridSize, st->gridSize / 2);
/* How wide each cell in the grid is */
cellWidth = width / cellsWide;
/* How tall each cell in the grid is */
cellHeight = height / cellsHigh;
/* Ensure that each cell is above a certain minimum size */
if (cellWidth < MINCELLSIZE) {
cellWidth = MINCELLSIZE;
cellsWide = width / cellWidth;
}
if (cellHeight < MINCELLSIZE) {
cellHeight = MINCELLSIZE;
cellsHigh = width / cellWidth;
}
/* fill the grid with randomly-generated cells */
for(i = 0; i < cellsHigh; i++) {
int j;
/* Each row is a different color, randomly generated (but constrained) */
if (!mono_p)
{
int c = genNewColor(st);
XSetForeground(st->dpy, st->fg_gc, st->colors[c].pixel);
# ifndef DO_STIPPLE
if (st->colors2)
XSetForeground(st->dpy, st->shadow_gc, st->colors2[c].pixel);
# endif
}
for(j = 0; j < cellsWide; j++) {
int curWidth, curHeight, curX, curY;
/* Generate a random height for a rectangle and make sure that */
/* it's above a certain minimum size */
curHeight = random() % (cellHeight - st->shadowWidth);
if (curHeight < MINRECTSIZE)
curHeight = MINRECTSIZE;
/* Generate a random width for a rectangle and make sure that
it's above a certain minimum size */
curWidth = random() % (cellWidth - st->shadowWidth);
if (curWidth < MINRECTSIZE)
curWidth = MINRECTSIZE;
/* Figure out a random place to locate the rectangle within the
cell */
curY = (i * cellHeight) + (random() % ((cellHeight - curHeight) -
st->shadowWidth));
curX = (j * cellWidth) + (random() % ((cellWidth - curWidth) -
st->shadowWidth));
/* Draw the shadow */
if (st->elevation > 0)
XFillRectangle(st->dpy, st->window, st->shadow_gc,
curX + st->elevation, curY + st->elevation,
curWidth, curHeight);
/* Draw the edge */
if (st->shadowWidth > 0)
XFillRectangle(st->dpy, st->window, st->bg_gc,
curX + st->shadowWidth, curY + st->shadowWidth,
curWidth, curHeight);
XFillRectangle(st->dpy, st->window, st->fg_gc, curX, curY, curWidth, curHeight);
/* Draw a 1-pixel black border around the rectangle */
XDrawRectangle(st->dpy, st->window, st->bg_gc, curX, curY, curWidth, curHeight);
}
}
}
/**
* genNewColor returns a new color, gradually mutating the colors and
* occasionally returning a totally random color, just for variety.
*
* @return the new color
**/
static int genNewColor(struct state *st)
{
/* These lines handle "sway", or the gradual random changing of */
/* colors. After genNewColor() has been called a given number of */
/* times (specified by a random permutation of the tweak variable), */
/* take whatever color has been most recently randomly generated and */
/* make it the new base color. */
if (st->timeLeft == 0) {
st->timeLeft = c_tweak(st, st->sway, st->sway / 3);
st->curColor = st->curBase;
} else {
st->timeLeft--;
}
/* If a randomly generated number is less than the threshold value,
produce a "sport" color value that is completely unrelated to the
current palette. */
if (0 == (random() % THRESHOLD)) {
return (random() % st->ncolors);
} else {
st->curBase = genConstrainedColor(st, st->curColor, st->tweak);
return st->curBase;
}
}
/**
* genConstrainedColor creates a random new color within a certain
* range of an existing color. Right now this works with RGB color
* values, but a future version of the program will most likely use HSV
* colors, which should generate a more pleasing progression of values.
*
* @param base the color on which the new color will be based
* @param tweak the amount that the new color can be tweaked
* @return a new constrained color
* @see #genNewColor
**/
static int genConstrainedColor(struct state *st, int base, int tweak)
{
int i = 1 + (random() % st->tweak);
if (random() & 1)
i = -i;
i = (base + i) % st->ncolors;
while (i < 0)
i += st->ncolors;
return i;
}
/**
* Utility function to generate a tweaked color value
*
* @param base the byte value on which the color is based
* @param tweak the amount the value will be skewed
* @see #tweak
* @return the tweaked byte
**/
static int c_tweak(struct state *st, int base, int tweak)
{
int ranTweak = (random() % (2 * tweak));
int n = (base + (ranTweak - tweak));
if (n < 0) n = -n;
return (n < 255 ? n : 255);
}
static void
cynosure_reshape (Display *dpy, Window window, void *closure,
unsigned int w, unsigned int h)
{
struct state *st = (struct state *) closure;
st->xgwa.width = w;
st->xgwa.height = h;
}
static Bool
cynosure_event (Display *dpy, Window window, void *closure, XEvent *event)
{
struct state *st = (struct state *) closure;
if (screenhack_event_helper (dpy, window, event))
{
st->i = st->iterations;
return True;
}
return False;
}
static void
cynosure_free (Display *dpy, Window window, void *closure)
{
struct state *st = (struct state *) closure;
XFreeGC (dpy, st->fg_gc);
XFreeGC (dpy, st->bg_gc);
XFreeGC (dpy, st->shadow_gc);
if (st->colors) free (st->colors);
if (st->colors2) free (st->colors2);
free (st);
}
static const char *cynosure_defaults [] = {
".background: black",
".foreground: white",
".lowrez: true",
"*fpsSolid: true",
"*delay: 500000",
"*colors: 128",
"*iterations: 100",
"*shadowWidth: 2",
"*elevation: 5",
"*sway: 30",
"*tweak: 20",
"*gridSize: 12",
#ifdef HAVE_MOBILE
"*ignoreRotation: True",
#endif
0
};
static XrmOptionDescRec cynosure_options [] = {
{ "-delay", ".delay", XrmoptionSepArg, 0 },
{ "-ncolors", ".colors", XrmoptionSepArg, 0 },
{ "-iterations", ".iterations", XrmoptionSepArg, 0 },
{ 0, 0, 0, 0 }
};
XSCREENSAVER_MODULE ("Cynosure", cynosure)