diff options
Diffstat (limited to 'hacks/glx/lockward.c')
-rw-r--r-- | hacks/glx/lockward.c | 970 |
1 files changed, 970 insertions, 0 deletions
diff --git a/hacks/glx/lockward.c b/hacks/glx/lockward.c new file mode 100644 index 0000000..f15374b --- /dev/null +++ b/hacks/glx/lockward.c @@ -0,0 +1,970 @@ +/* + * 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: */ |