/*
* lockward.c: First attempt at an Xscreensaver.
*
* Leo L. Schwab 2007.08.17
****
* Copyright (c) 2007 Leo L. Schwab
*
* 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 or substantial portions of the Software.
*
* 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 OR COPYRIGHT HOLDERS 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 <ctype.h>
#include <strings.h>
#include "xlockmore.h"
#include "colors.h"
/***************************************************************************
* #defines
*/
#ifdef USE_GL /* whole file */
#define DEFAULTS "*delay: 20000 \n"\
"*showFPS: False \n"
#define release_lockward 0
#define NUMOF(x) (sizeof ((x)) / sizeof ((*x)))
#define NBLADES 12
#define NSPINNERS 4
#define NRADII 8
#define COLORIDX_SHF 4
#define SUBDIV 6
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
/***************************************************************************
* Structure definitions.
*/
struct lockward_context; /* Forward declaration. */
#define int8_t char
#define int16_t short
#define int32_t int
#define uint8_t unsigned char
#define uint16_t unsigned short
#define uint32_t unsigned int
typedef struct bladestate {
uint8_t outer, inner; /* Radii */
} bladestate;
typedef struct spinnerstate {
GLfloat rot; /* Terminal rotation after count expires */
GLfloat rotinc; /* Per-frame increment to rot. */
XColor *colors;
bladestate *bladeidx;
int ncolors; /* n.4 fixed-point */
int ccolor; /* n.4 fixed-point */
int colorinc; /* n.4 fixed-point */
int rotcount;
uint8_t nblades;
} spinnerstate;
typedef struct blinkstate {
int (*drawfunc) (struct lockward_context *ctx,
struct blinkstate *bs);
uint32_t *noise; /* For draw_blink_segment_scatter() */
GLfloat color[4];
uint32_t val;
int16_t dwell; /* <0: sharp >0: decay */
int16_t dwellcnt;
uint8_t type;
int8_t counter;
int8_t direction;
int8_t radius;
} blinkstate;
enum blinktype {
BTYPE_RADIAL_SINGLE = 0,
BTYPE_RADIAL_RANDOM,
BTYPE_RADIAL_SEQ,
BTYPE_RADIAL_DOUBLESEQ,
BTYPE_SEGMENT_SINGLE,
BTYPE_SEGMENT_RANDOM,
BTYPE_CONCENTRIC_SINGLE,
BTYPE_CONCENTRIC_RANDOM,
BTYPE_CONCENTRIC_SEQ,
BTYPE_SEGMENT_SCATTER,
MAX_BTYPE
};
typedef struct { GLfloat x,y,z; } XYZ;
typedef struct lockward_context {
GLXContext *glx_context;
spinnerstate spinners[NSPINNERS];
blinkstate blink;
/* This used to put vertexes into lists without putting begin/end
into the same list! I didn't even know that worked. Well, it
doesn't work with jwzgles, so I changed it to not do that. */
/* GLuint blades_outer, blades_inner; */
XYZ points_outer[NRADII][SUBDIV+1];
XYZ points_inner[NRADII][SUBDIV+1];
GLuint rings;
Bool blendmode;
int nextblink;
int fps;
} lockward_context;
/***************************************************************************
* Prototypes.
*/
ENTRYPOINT void free_lockward (ModeInfo *mi);
/***************************************************************************
* Global variables.
*/
static lockward_context *g_ctx = NULL;
static Bool g_blink_p = True;
static int g_blades = NBLADES;
static int g_rotateidle_min,
g_rotateidle_max;
static int g_blinkidle_min,
g_blinkidle_max;
static int g_blinkdwell_min,
g_blinkdwell_max;
#define DEF_BLINK "True"
#define DEF_ROTATEIDLEMIN "1000"
#define DEF_ROTATEIDLEMAX "6000"
#define DEF_BLINKIDLEMIN "1000"
#define DEF_BLINKIDLEMAX "9000"
#define DEF_BLINKDWELLMIN "100"
#define DEF_BLINKDWELLMAX "600"
static XrmOptionDescRec opts[] = {
{ "-blink", ".blink", XrmoptionNoArg, "on" },
{ "+blink", ".blink", XrmoptionNoArg, "off" },
{ "-rotateidle-min", ".rotateidlemin", XrmoptionSepArg, 0 },
{ "-rotateidle-max", ".rotateidlemax", XrmoptionSepArg, 0 },
{ "-blinkidle-min", ".blinkidlemin", XrmoptionSepArg, 0 },
{ "-blinkidle-max", ".blinkidlemax", XrmoptionSepArg, 0 },
{ "-blinkdwell-min", ".blinkdwellmin", XrmoptionSepArg, 0 },
{ "-blinkdwell-max", ".blinkdwellmax", XrmoptionSepArg, 0 },
};
static argtype vars[] = {
{ &g_blink_p, "blink", "Blink", DEF_BLINK, t_Bool },
{ &g_rotateidle_min, "rotateidlemin", "Rotateidlemin", DEF_ROTATEIDLEMIN, t_Int },
{ &g_rotateidle_max, "rotateidlemax", "Rotateidlemax", DEF_ROTATEIDLEMAX, t_Int },
{ &g_blinkidle_min, "blinkidlemin", "Blinkidlemin", DEF_BLINKIDLEMIN, t_Int },
{ &g_blinkidle_max, "blinkidlemax", "Blinkidlemax", DEF_BLINKIDLEMAX, t_Int },
{ &g_blinkdwell_min, "blinkdwellmin", "Blinkdwellmin", DEF_BLINKDWELLMIN, t_Int },
{ &g_blinkdwell_max, "blinkdwellmax", "Blinkdwellmax", DEF_BLINKDWELLMAX, t_Int },
};
static OptionStruct desc[] = {
{ "-/+blink", "Turn on/off blinking effects." },
{ "-rotateidle-min", "Minimum idle time for rotators, in milliseconds." },
{ "-rotateidle-max", "Maximum idle time for rotators, in milliseconds." },
{ "-blinkidle-min", "Minimum idle time between blink effects, in milliseconds." },
{ "-blinkidle-max", "Maximum idle time between blink effects, in milliseconds." },
{ "-blinkdwell-min", "Minimum dwell time for blink effects, in milliseconds." },
{ "-blinkdwell-max", "Maximum dwell time for blink effects, in milliseconds." },
};
ENTRYPOINT ModeSpecOpt lockward_opts = {
NUMOF(opts), opts, NUMOF(vars), vars, desc
};
/***************************************************************************
* Window management.
*/
ENTRYPOINT void
reshape_lockward (ModeInfo *mi, int width, int height)
{
lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
GLfloat h = (GLfloat) height / (GLfloat) width;
int y = 0;
if (width > height * 5) { /* tiny window: show middle */
height = width * 9/16;
y = -height/2;
h = height / (GLfloat) width;
}
glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
*(ctx->glx_context));
glViewport (0, y, (GLint) width, (GLint) height);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
if (height > width)
glOrtho (-8.0, 8.0, -8.0 * h, 8.0 * h, -1, 1);
else
glOrtho (-8.0 / h, 8.0 / h, -8.0, 8.0, -1, 1);
glMatrixMode (GL_MODELVIEW);
}
ENTRYPOINT Bool
lockward_handle_event (ModeInfo *mi, XEvent *event)
{
lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
if (event->xany.type == KeyPress) {
KeySym keysym;
char c = 0;
XLookupString (&event->xkey, &c, 1, &keysym, 0);
if (c == ' ' || c == '\t') {
ctx->blendmode ^= 1;
return True;
}
}
return False;
}
/***************************************************************************
* "Blade" routines.
*/
static void
random_blade_rot (lockward_context *ctx, struct spinnerstate *ss)
{
/*
* The circle is divided up in to g_blades divisions. The idea here
* is to rotate to an exact division point.
*
* The target rotation is computed via random numbers.
*
* The time it takes to get there is a maximum of six seconds per
* division, and a minimum of one second (no matter how far away it
* is), and is selected by random numbers.
*
* The time value is converted into frames, and a per-frame rotation
* is computed.
*
* During rendering, we approach the target rotation by subtracting
* from it the per-frame rotation times the number of outstanding
* ticks. Doing it this way means we'll hit the target rotation
* exactly, without low-order errors creeping in to the values (at
* least not nearly as quickly).
*/
GLfloat d;
int dist;
dist = random() % g_blades + 1;
ss->rotcount = random() % (6 * dist * ctx->fps - ctx->fps)
+ ctx->fps;
if (random() & 4)
dist = -dist;
d = dist * 360.0 / (GLfloat) g_blades;
ss->rot += d;
ss->rotinc = d / (GLfloat) ss->rotcount;
}
/*
* A "blade" is pie-wedge shaped flat thing that is rotated around where the
* apex is/would be. Initially envisioned as 1/12th of a circle, but that
* could be configurable. The inner and outer edges are rounded off using
* six subdivisions so that, when multiple blades are assembled, it looks
* more like a circle and less like a polygon.
*
* The blade is assembled as a tri-fan. It is oriented centered at 3
* o'clock. The blade is composed of two display lists -- arcs, essentially
* -- the outer and the inner one. The outer one *must* be called before
* the inner one, or the blade clockwise-ness will be wrong, and become
* invisible. Arcs of various radii are compiled.
*/
static void
gen_blade_arcs (lockward_context *ctx)
{
GLfloat here, there, step;
int i, n;
here = 0;
there = M_PI * 2.0 / g_blades;
step = there / SUBDIV;
here -= SUBDIV * step / 2.0;
/*
* Build outer blade arcs.
* Start at left side of outer radius. Strike all its vertices.
*/
for (n = 0; n < NRADII; ++n) {
/* glNewList (ctx->blades_outer + n, GL_COMPILE); */
XYZ *a = ctx->points_outer[n];
int j = 0;
for (i = SUBDIV; i >= 0; --i) {
/* glVertex3f (cos (here + step * i) * (n + 1.0),
sin (here + step * i) * (n + 1.0), 0); */
a[j].x = cos (here + step * i) * (n + 1.0);
a[j].y = sin (here + step * i) * (n + 1.0);
a[j].z = 0;
j++;
}
if (j != SUBDIV+1) abort();
/* glEndList (); */
}
/*
* Build inner blade arcs.
* Move to inner radius, strike all vertices in opposite order.
*/
for (n = 0; n < NRADII; ++n) {
/* glNewList (ctx->blades_inner + n, GL_COMPILE); */
XYZ *a = ctx->points_inner[n];
int j = 0;
for (i = 0; i <= SUBDIV; ++i) {
/* glVertex3f (cos (here + step * i) * (n + 1.0),
sin (here + step * i) * (n + 1.0), 0); */
a[j].x = cos (here + step * i) * (n + 1.0);
a[j].y = sin (here + step * i) * (n + 1.0);
a[j].z = 0;
j++;
}
if (j != SUBDIV+1) abort();
/* glEndList (); */
}
}
static void
gen_rings (lockward_context *ctx)
{
GLfloat step;
int i, n;
step = M_PI * 2.0 / (g_blades * SUBDIV);
for (n = 0; n < NRADII - 1; ++n) {
glNewList (ctx->rings + n, GL_COMPILE);
glBegin (GL_TRIANGLE_STRIP);
for (i = g_blades * SUBDIV; i >= 0; --i) {
glVertex3f (cos (step * i) * (n + 1.0),
sin (step * i) * (n + 1.0), 0);
glVertex3f (cos (step * i) * (n + 2.0),
sin (step * i) * (n + 2.0), 0);
}
glEnd();
glEndList ();
}
}
/***************************************************************************
* "Blink" routines.
*/
static int
calc_interval_frames (lockward_context *ctx, int min, int max)
{
/*
* Compute random interval between min and max milliseconds.
* Returned value is in frames.
*/
register int i;
i = min;
if (max > min)
i += random() % (max - min);
return i * ctx->fps / 1000;
}
static void
set_alpha_by_dwell (struct blinkstate *bs)
{
if (bs->dwell > 0)
bs->color[3] = (GLfloat) bs->dwellcnt / (GLfloat) bs->dwell;
else
bs->color[3] = bs->dwellcnt > (-bs->dwell >> 2) ? 1.0 : 0.0;
}
static void
draw_blink_blade (lockward_context *ctx, int inner, int outer,
Bool begin_p)
{
int i;
if (begin_p) glBegin (GL_TRIANGLE_FAN);
/* glCallList (ctx->blades_outer + outer); */
for (i = 0; i < countof(*ctx->points_outer); i++)
glVertex3f(ctx->points_outer[outer][i].x,
ctx->points_outer[outer][i].y,
ctx->points_outer[outer][i].z);
/* glCallList (ctx->blades_inner + inner); */
for (i = 0; i < countof(*ctx->points_inner); i++)
glVertex3f(ctx->points_inner[inner][i].x,
ctx->points_inner[inner][i].y,
ctx->points_inner[inner][i].z);
if (begin_p) glEnd();
}
static int
draw_blink_radial_random (lockward_context *ctx, struct blinkstate *bs)
{
int i;
/*
* There is no sense of direction in a random sweep, so re-use the
* 'direction' field to hold the current blade we're messing with.
*/
if (bs->dwellcnt < 0) {
if (bs->counter <= 0) {
bs->drawfunc = NULL;
return 0;
}
/*
* Find available blade. Potentially very slow, depending on
* how unlucky we are.
*/
do {
i = random() % g_blades;
} while (bs->val & (1 << i));
bs->val |= (1 << i); /* Mark as used. */
bs->direction = i;
if ((bs->dwellcnt = bs->dwell) < 0)
bs->dwellcnt = -bs->dwellcnt;
if ( bs->type == BTYPE_SEGMENT_SINGLE
|| bs->type == BTYPE_SEGMENT_RANDOM)
bs->radius = random() % (NRADII - 1);
--bs->counter;
}
set_alpha_by_dwell (bs);
glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
glColor4fv (bs->color);
glRotatef (bs->direction * 360.0 / (GLfloat) g_blades, 0, 0, 1);
if (bs->radius >= 0)
draw_blink_blade (ctx, bs->radius, bs->radius + 1, True);
else
draw_blink_blade (ctx, 0, NRADII - 1, True);
--bs->dwellcnt;
return SUBDIV + SUBDIV;
}
static int
draw_blink_radial_sequential (lockward_context *ctx, struct blinkstate *bs)
{
if (bs->dwellcnt < 0) {
if (bs->counter <= 0) {
bs->drawfunc = NULL;
return 0;
}
if ((bs->dwellcnt = bs->dwell) < 0)
bs->dwellcnt = -bs->dwellcnt;
--bs->counter;
}
set_alpha_by_dwell (bs);
glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
glColor4fv (bs->color);
glRotatef ((bs->counter * bs->direction + (int) bs->val)
* 360.0 / (GLfloat) g_blades,
0, 0, 1);
draw_blink_blade (ctx, 0, NRADII - 1, True);
--bs->dwellcnt;
return SUBDIV + SUBDIV;
}
static int
draw_blink_radial_doubleseq (lockward_context *ctx, struct blinkstate *bs)
{
int polys;
if (bs->dwellcnt < 0) {
if (bs->counter <= 0) {
bs->drawfunc = NULL;
return 0;
}
if ((bs->dwellcnt = bs->dwell) < 0)
bs->dwellcnt = -bs->dwellcnt;
--bs->counter;
}
set_alpha_by_dwell (bs);
glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
glColor4fv (bs->color);
glPushMatrix ();
glRotatef (((int) bs->val + bs->counter) * 360.0 / (GLfloat) g_blades,
0, 0, 1);
draw_blink_blade (ctx, 0, NRADII - 1, True);
glPopMatrix ();
polys = SUBDIV + SUBDIV;
if (bs->counter && bs->counter < g_blades / 2) {
glRotatef (((int) bs->val - bs->counter)
* 360.0 / (GLfloat) g_blades,
0, 0, 1);
draw_blink_blade (ctx, 0, NRADII - 1, True);
polys += SUBDIV + SUBDIV;
}
--bs->dwellcnt;
return polys;
}
static int
draw_blink_concentric_random (lockward_context *ctx, struct blinkstate *bs)
{
int i;
if (bs->dwellcnt < 0) {
if (bs->counter <= 0) {
bs->drawfunc = NULL;
return 0;
}
do {
i = random() % (NRADII - 1);
} while (bs->val & (1 << i));
bs->val |= (1 << i);
bs->direction = i;
if ((bs->dwellcnt = bs->dwell) < 0)
bs->dwellcnt = -bs->dwellcnt;
--bs->counter;
}
set_alpha_by_dwell (bs);
glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
glColor4fv (bs->color);
glCallList (ctx->rings + bs->direction);
--bs->dwellcnt;
return g_blades * SUBDIV * 2;
}
static int
draw_blink_concentric_sequential (lockward_context *ctx, struct blinkstate *bs)
{
if (bs->dwellcnt < 0) {
if (bs->counter <= 0) {
bs->drawfunc = NULL;
return 0;
}
if ((bs->dwellcnt = bs->dwell) < 0)
bs->dwellcnt = -bs->dwellcnt;
--bs->counter;
}
set_alpha_by_dwell (bs);
glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
glColor4fv (bs->color);
if (bs->direction > 0)
glCallList (ctx->rings + (NRADII - 2) - bs->counter);
else
glCallList (ctx->rings + bs->counter);
--bs->dwellcnt;
return g_blades * SUBDIV * 2;
}
static int
draw_blink_segment_scatter (lockward_context *ctx, struct blinkstate *bs)
{
int i, polys = 0;
if (bs->dwellcnt < 0) {
if (bs->counter <= 0) {
bs->drawfunc = NULL;
return 0;
}
/*
* Init random noise array. On average, 1/4 of the bits will
* be set, which should look nice. (1/2 looks too busy.)
*/
for (i = g_blades; --i >= 0; )
bs->noise[i] = random() & random()
& ((1 << (NRADII - 1)) - 1);
if ((bs->dwellcnt = bs->dwell) < 0)
bs->dwellcnt = -bs->dwellcnt;
--bs->counter;
}
set_alpha_by_dwell (bs);
glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
glColor4fv (bs->color);
for (i = g_blades; --i >= 0; ) {
register uint32_t bits;
int inner, outer;
/*
* Find consecutive runs of 1 bits. Keep going until we run
* out of them.
*/
for (bits = bs->noise[i]; bits; ) {
inner = ffs (bits) - 1;
bits = ~bits & ~((1 << inner) - 1);
outer = ffs (bits) - 1;
bits = ~bits & ~((1 << outer) - 1);
glPushMatrix ();
glRotatef (i * 360.0 / (GLfloat) g_blades, 0, 0, 1);
draw_blink_blade (ctx, inner, outer, True);
glPopMatrix ();
polys += SUBDIV + SUBDIV;
}
}
--bs->dwellcnt;
return polys;
}
static void
random_blink (lockward_context *ctx, struct blinkstate *bs)
{
bs->color[0] =
bs->color[1] =
bs->color[2] =
bs->color[3] = 1.0;
bs->dwellcnt = -1;
bs->radius = -1;
bs->dwell = calc_interval_frames
(ctx, g_blinkdwell_min, g_blinkdwell_max);
if (random() & 2)
bs->dwell = -bs->dwell;
bs->type = random() % MAX_BTYPE;
switch (bs->type) {
case BTYPE_RADIAL_SINGLE:
case BTYPE_SEGMENT_SINGLE:
bs->drawfunc = draw_blink_radial_random;
bs->val = 0;
bs->counter = 1;
break;
case BTYPE_RADIAL_RANDOM:
case BTYPE_SEGMENT_RANDOM:
bs->drawfunc = draw_blink_radial_random;
bs->val = 0;
bs->counter = g_blades;
break;
case BTYPE_RADIAL_SEQ:
bs->drawfunc = draw_blink_radial_sequential;
bs->val = random() % g_blades; /* Initial offset */
bs->direction = random() & 8 ? 1 : -1;
bs->counter = g_blades;
break;
case BTYPE_RADIAL_DOUBLESEQ:
bs->drawfunc = draw_blink_radial_doubleseq;
bs->val = random() % g_blades; /* Initial offset */
bs->counter = g_blades / 2 + 1;
break;
case BTYPE_CONCENTRIC_SINGLE:
bs->drawfunc = draw_blink_concentric_random;
bs->val = 0;
bs->counter = 1;
break;
case BTYPE_CONCENTRIC_RANDOM:
bs->drawfunc = draw_blink_concentric_random;
bs->val = 0;
bs->counter = NRADII - 1;
break;
case BTYPE_CONCENTRIC_SEQ:
bs->drawfunc = draw_blink_concentric_sequential;
bs->direction = random() & 8 ? 1 : -1;
bs->counter = NRADII - 1;
break;
case BTYPE_SEGMENT_SCATTER:
bs->drawfunc = draw_blink_segment_scatter;
bs->counter = random() % (g_blades / 2) + (g_blades / 2) + 1;
break;
}
}
/***************************************************************************
* Main rendering routine.
*/
ENTRYPOINT void
draw_lockward (ModeInfo *mi)
{
lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
spinnerstate *ss;
Display *dpy = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
int i, n;
GLfloat scolor[4] = {0.0, 0.0, 0.0, 0.5};
if (!ctx->glx_context)
return;
glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi), *(ctx->glx_context));
glClear (GL_COLOR_BUFFER_BIT);
if (ctx->blendmode)
glBlendFunc (GL_ONE, GL_ONE);
else
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPushMatrix ();
glLoadIdentity ();
mi->polygon_count = 0;
for (n = NSPINNERS; --n >= 0; ) {
ss = &ctx->spinners[n];
/* Set color. */
i = ss->ccolor >> COLORIDX_SHF;
scolor[0] = ss->colors[i].red / 65535.0;
scolor[1] = ss->colors[i].green / 65535.0;
scolor[2] = ss->colors[i].blue / 65535.0;
glColor4fv (scolor);
glPushMatrix ();
glRotatef (ss->rot - ss->rotcount * ss->rotinc, 0, 0, 1);
for (i = ss->nblades; --i >= 0; ) {
glPushMatrix ();
glRotatef (360.0 * i / ss->nblades, 0, 0, 1);
glBegin (GL_TRIANGLE_FAN);
/* glCallList (ctx->blades_outer + ss->bladeidx[i].outer); */
/* glCallList (ctx->blades_inner + ss->bladeidx[i].inner); */
draw_blink_blade (ctx,
ss->bladeidx[i].inner,
ss->bladeidx[i].outer,
False);
glEnd ();
glPopMatrix ();
mi->polygon_count += SUBDIV + SUBDIV;
}
glPopMatrix ();
/* Advance rotation. */
if (ss->rotcount) {
if (ss->rotcount > 0)
--ss->rotcount;
} else {
if (ss->rotinc == 0.0)
random_blade_rot (ctx, ss);
else {
/* Compute # of ticks to sit idle. */
ss->rotinc = 0.0;
ss->rotcount =
calc_interval_frames (ctx,
g_rotateidle_min,
g_rotateidle_max);
}
}
/* Advance colors. */
if ((ss->ccolor += ss->colorinc) >= ss->ncolors)
ss->ccolor -= ss->ncolors;
else if (ss->ccolor < 0)
ss->ccolor += ss->ncolors;
}
if (g_blink_p) {
if (ctx->blink.drawfunc) {
mi->polygon_count +=
ctx->blink.drawfunc (ctx, &ctx->blink);
} else {
if (ctx->nextblink > 0)
--ctx->nextblink;
else {
/* Compute # of frames for blink idle time. */
ctx->nextblink =
calc_interval_frames (ctx,
g_blinkidle_min,
g_blinkidle_max);
random_blink (ctx, &ctx->blink);
}
}
}
glPopMatrix ();
if (MI_IS_FPS (mi)) do_fps (mi);
glFinish();
glXSwapBuffers (dpy, window);
}
/***************************************************************************
* Initialization/teardown.
*/
ENTRYPOINT void
init_lockward (ModeInfo *mi)
{
lockward_context *ctx;
int i, n;
MI_INIT (mi, g_ctx);
ctx = &g_ctx[MI_SCREEN (mi)];
ctx->glx_context = init_GL (mi);
reshape_lockward (mi, MI_WIDTH (mi), MI_HEIGHT (mi));
glEnable (GL_CULL_FACE);
glEnable (GL_BLEND);
glDisable (GL_DEPTH_TEST);
glShadeModel (GL_FLAT);
glFrontFace (GL_CW);
/* ctx->blades_outer = glGenLists (NRADII); */
/* ctx->blades_inner = glGenLists (NRADII); */
ctx->rings = glGenLists (NRADII - 1);
ctx->blendmode = 0;
/* WTF? ctx->fps = 1000000 / MI_DELAY (mi); */
ctx->fps = 60;
ctx->nextblink = calc_interval_frames
(ctx, g_blinkidle_min, g_blinkidle_max);
ctx->blink.drawfunc = NULL;
ctx->blink.noise = malloc (sizeof (uint32_t) * g_blades);
if (!ctx->blink.noise) {
fprintf (stderr, "Can't allocate noise array.\n");
exit (1);
}
gen_blade_arcs (ctx);
gen_rings (ctx);
for (i = NSPINNERS; --i >= 0; ) {
spinnerstate *ss = &ctx->spinners[i];
ss->rot = 0.0;
ss->rotcount = -1;
/* Establish rotation */
random_blade_rot (ctx, ss);
/*
* Establish color cycling path and rate. Rate avoids zero.
*/
ss->ncolors = 128;
ss->colorinc = (random() & ((2 << COLORIDX_SHF) - 1))
- (1 << COLORIDX_SHF);
if (ss->colorinc >= 0)
++ss->colorinc;
ss->colors = (XColor *) calloc (ss->ncolors, sizeof (XColor));
if (!ss->colors) {
fprintf (stderr,
"Can't allocate XColors for spinner %d.\n",
i);
exit (1);
}
make_smooth_colormap (0, 0, 0,
ss->colors, &ss->ncolors,
False, 0, False);
ss->ncolors <<= COLORIDX_SHF;
/*
* Create blades.
*/
ss->nblades = g_blades;
ss->bladeidx = malloc (sizeof (bladestate) * g_blades);
if (!ss->bladeidx) {
fprintf (stderr, "Can't allocate blades.\n");
exit (1);
}
for (n = g_blades; --n >= 0; ) {
/*
* Establish blade radii. Can't be equal. Ensure
* outer > inner.
*/
do {
ss->bladeidx[n].outer = random() & 7;
ss->bladeidx[n].inner = random() & 7;
} while (ss->bladeidx[n].outer ==
ss->bladeidx[n].inner);
if (ss->bladeidx[n].outer < ss->bladeidx[n].inner) {
uint8_t tmp;
tmp = ss->bladeidx[n].outer;
ss->bladeidx[n].outer = ss->bladeidx[n].inner;
ss->bladeidx[n].inner = tmp;
}
}
}
}
ENTRYPOINT void
free_lockward (ModeInfo *mi)
{
lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
int i;
if (!ctx->glx_context)
return;
glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
*(ctx->glx_context));
if (ctx->blink.noise)
free (ctx->blink.noise);
if (glIsList (ctx->rings))
glDeleteLists (ctx->rings, NRADII - 1);
/* if (glIsList (ctx->blades_outer))
glDeleteLists (ctx->blades_outer, NRADII);
if (glIsList (ctx->blades_inner))
glDeleteLists (ctx->blades_inner, NRADII); */
for (i = NSPINNERS; --i >= 0; ) {
spinnerstate *ss = &ctx->spinners[i];
if (ss->colors)
free (ss->colors);
if (ss->bladeidx)
free (ss->bladeidx);
}
}
XSCREENSAVER_MODULE ("Lockward", lockward)
#endif /* USE_GL */
/* vim:se ts=8 sts=8 sw=8: */