2 * lockward.c: First attempt at an Xscreensaver.
4 * Leo L. Schwab 2007.08.17
6 * Copyright (c) 2007 Leo L. Schwab
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to permit
13 * persons to whom the Software is furnished to do so, subject to the
14 * following conditions:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
22 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #include "xlockmore.h"
34 /***************************************************************************
37 #ifdef USE_GL /* whole file */
39 #define DEFAULTS "*delay: 20000 \n"\
42 #define release_lockward 0
45 #define NUMOF(x) (sizeof ((x)) / sizeof ((*x)))
50 #define COLORIDX_SHF 4
54 #define countof(x) (sizeof((x))/sizeof((*x)))
57 /***************************************************************************
58 * Structure definitions.
60 struct lockward_context; /* Forward declaration. */
65 #define uint8_t unsigned char
66 #define uint16_t unsigned short
67 #define uint32_t unsigned int
69 typedef struct bladestate {
70 uint8_t outer, inner; /* Radii */
73 typedef struct spinnerstate {
74 GLfloat rot; /* Terminal rotation after count expires */
75 GLfloat rotinc; /* Per-frame increment to rot. */
78 int ncolors; /* n.4 fixed-point */
79 int ccolor; /* n.4 fixed-point */
80 int colorinc; /* n.4 fixed-point */
85 typedef struct blinkstate {
86 int (*drawfunc) (struct lockward_context *ctx,
87 struct blinkstate *bs);
88 uint32_t *noise; /* For draw_blink_segment_scatter() */
91 int16_t dwell; /* <0: sharp >0: decay */
100 BTYPE_RADIAL_SINGLE = 0,
103 BTYPE_RADIAL_DOUBLESEQ,
104 BTYPE_SEGMENT_SINGLE,
105 BTYPE_SEGMENT_RANDOM,
106 BTYPE_CONCENTRIC_SINGLE,
107 BTYPE_CONCENTRIC_RANDOM,
108 BTYPE_CONCENTRIC_SEQ,
109 BTYPE_SEGMENT_SCATTER,
113 typedef struct { GLfloat x,y,z; } XYZ;
115 typedef struct lockward_context {
116 GLXContext *glx_context;
118 spinnerstate spinners[NSPINNERS];
121 /* This used to put vertexes into lists without putting begin/end
122 into the same list! I didn't even know that worked. Well, it
123 doesn't work with jwzgles, so I changed it to not do that. */
124 /* GLuint blades_outer, blades_inner; */
125 XYZ points_outer[NRADII][SUBDIV+1];
126 XYZ points_inner[NRADII][SUBDIV+1];
136 /***************************************************************************
139 ENTRYPOINT void free_lockward (ModeInfo *mi);
142 /***************************************************************************
145 static lockward_context *g_ctx = NULL;
146 static Bool g_blink_p = True;
147 static int g_blades = NBLADES;
148 static int g_rotateidle_min,
150 static int g_blinkidle_min,
152 static int g_blinkdwell_min,
155 #define DEF_BLINK "True"
156 #define DEF_ROTATEIDLEMIN "1000"
157 #define DEF_ROTATEIDLEMAX "6000"
158 #define DEF_BLINKIDLEMIN "1000"
159 #define DEF_BLINKIDLEMAX "9000"
160 #define DEF_BLINKDWELLMIN "100"
161 #define DEF_BLINKDWELLMAX "600"
164 static XrmOptionDescRec opts[] = {
165 { "-blink", ".blink", XrmoptionNoArg, "on" },
166 { "+blink", ".blink", XrmoptionNoArg, "off" },
167 { "-rotateidle-min", ".rotateidlemin", XrmoptionSepArg, 0 },
168 { "-rotateidle-max", ".rotateidlemax", XrmoptionSepArg, 0 },
169 { "-blinkidle-min", ".blinkidlemin", XrmoptionSepArg, 0 },
170 { "-blinkidle-max", ".blinkidlemax", XrmoptionSepArg, 0 },
171 { "-blinkdwell-min", ".blinkdwellmin", XrmoptionSepArg, 0 },
172 { "-blinkdwell-max", ".blinkdwellmax", XrmoptionSepArg, 0 },
175 static argtype vars[] = {
176 { &g_blink_p, "blink", "Blink", DEF_BLINK, t_Bool },
177 { &g_rotateidle_min, "rotateidlemin", "Rotateidlemin", DEF_ROTATEIDLEMIN, t_Int },
178 { &g_rotateidle_max, "rotateidlemax", "Rotateidlemax", DEF_ROTATEIDLEMAX, t_Int },
179 { &g_blinkidle_min, "blinkidlemin", "Blinkidlemin", DEF_BLINKIDLEMIN, t_Int },
180 { &g_blinkidle_max, "blinkidlemax", "Blinkidlemax", DEF_BLINKIDLEMAX, t_Int },
181 { &g_blinkdwell_min, "blinkdwellmin", "Blinkdwellmin", DEF_BLINKDWELLMIN, t_Int },
182 { &g_blinkdwell_max, "blinkdwellmax", "Blinkdwellmax", DEF_BLINKDWELLMAX, t_Int },
185 static OptionStruct desc[] = {
186 { "-/+blink", "Turn on/off blinking effects." },
187 { "-rotateidle-min", "Minimum idle time for rotators, in milliseconds." },
188 { "-rotateidle-max", "Maximum idle time for rotators, in milliseconds." },
189 { "-blinkidle-min", "Minimum idle time between blink effects, in milliseconds." },
190 { "-blinkidle-max", "Maximum idle time between blink effects, in milliseconds." },
191 { "-blinkdwell-min", "Minimum dwell time for blink effects, in milliseconds." },
192 { "-blinkdwell-max", "Maximum dwell time for blink effects, in milliseconds." },
195 ENTRYPOINT ModeSpecOpt lockward_opts = {
196 NUMOF(opts), opts, NUMOF(vars), vars, desc
200 /***************************************************************************
204 reshape_lockward (ModeInfo *mi, int width, int height)
206 lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
207 GLfloat h = (GLfloat) height / (GLfloat) width;
210 if (width > height * 5) { /* tiny window: show middle */
211 height = width * 9/16;
213 h = height / (GLfloat) width;
216 glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
217 *(ctx->glx_context));
219 glViewport (0, y, (GLint) width, (GLint) height);
221 glMatrixMode (GL_PROJECTION);
224 glOrtho (-8.0, 8.0, -8.0 * h, 8.0 * h, -1, 1);
226 glOrtho (-8.0 / h, 8.0 / h, -8.0, 8.0, -1, 1);
228 glMatrixMode (GL_MODELVIEW);
232 lockward_handle_event (ModeInfo *mi, XEvent *event)
234 lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
236 if (event->xany.type == KeyPress) {
240 XLookupString (&event->xkey, &c, 1, &keysym, 0);
241 if (c == ' ' || c == '\t') {
251 /***************************************************************************
255 random_blade_rot (lockward_context *ctx, struct spinnerstate *ss)
258 * The circle is divided up in to g_blades divisions. The idea here
259 * is to rotate to an exact division point.
261 * The target rotation is computed via random numbers.
263 * The time it takes to get there is a maximum of six seconds per
264 * division, and a minimum of one second (no matter how far away it
265 * is), and is selected by random numbers.
267 * The time value is converted into frames, and a per-frame rotation
270 * During rendering, we approach the target rotation by subtracting
271 * from it the per-frame rotation times the number of outstanding
272 * ticks. Doing it this way means we'll hit the target rotation
273 * exactly, without low-order errors creeping in to the values (at
274 * least not nearly as quickly).
279 dist = random() % g_blades + 1;
281 ss->rotcount = random() % (6 * dist * ctx->fps - ctx->fps)
286 d = dist * 360.0 / (GLfloat) g_blades;
288 ss->rotinc = d / (GLfloat) ss->rotcount;
293 * A "blade" is pie-wedge shaped flat thing that is rotated around where the
294 * apex is/would be. Initially envisioned as 1/12th of a circle, but that
295 * could be configurable. The inner and outer edges are rounded off using
296 * six subdivisions so that, when multiple blades are assembled, it looks
297 * more like a circle and less like a polygon.
299 * The blade is assembled as a tri-fan. It is oriented centered at 3
300 * o'clock. The blade is composed of two display lists -- arcs, essentially
301 * -- the outer and the inner one. The outer one *must* be called before
302 * the inner one, or the blade clockwise-ness will be wrong, and become
303 * invisible. Arcs of various radii are compiled.
307 gen_blade_arcs (lockward_context *ctx)
309 GLfloat here, there, step;
313 there = M_PI * 2.0 / g_blades;
314 step = there / SUBDIV;
315 here -= SUBDIV * step / 2.0;
318 * Build outer blade arcs.
319 * Start at left side of outer radius. Strike all its vertices.
321 for (n = 0; n < NRADII; ++n) {
322 /* glNewList (ctx->blades_outer + n, GL_COMPILE); */
323 XYZ *a = ctx->points_outer[n];
325 for (i = SUBDIV; i >= 0; --i) {
326 /* glVertex3f (cos (here + step * i) * (n + 1.0),
327 sin (here + step * i) * (n + 1.0), 0); */
328 a[j].x = cos (here + step * i) * (n + 1.0);
329 a[j].y = sin (here + step * i) * (n + 1.0);
333 if (j != SUBDIV+1) abort();
338 * Build inner blade arcs.
339 * Move to inner radius, strike all vertices in opposite order.
341 for (n = 0; n < NRADII; ++n) {
342 /* glNewList (ctx->blades_inner + n, GL_COMPILE); */
343 XYZ *a = ctx->points_inner[n];
345 for (i = 0; i <= SUBDIV; ++i) {
346 /* glVertex3f (cos (here + step * i) * (n + 1.0),
347 sin (here + step * i) * (n + 1.0), 0); */
348 a[j].x = cos (here + step * i) * (n + 1.0);
349 a[j].y = sin (here + step * i) * (n + 1.0);
353 if (j != SUBDIV+1) abort();
359 gen_rings (lockward_context *ctx)
364 step = M_PI * 2.0 / (g_blades * SUBDIV);
366 for (n = 0; n < NRADII - 1; ++n) {
367 glNewList (ctx->rings + n, GL_COMPILE);
368 glBegin (GL_TRIANGLE_STRIP);
369 for (i = g_blades * SUBDIV; i >= 0; --i) {
370 glVertex3f (cos (step * i) * (n + 1.0),
371 sin (step * i) * (n + 1.0), 0);
372 glVertex3f (cos (step * i) * (n + 2.0),
373 sin (step * i) * (n + 2.0), 0);
381 /***************************************************************************
385 calc_interval_frames (lockward_context *ctx, int min, int max)
388 * Compute random interval between min and max milliseconds.
389 * Returned value is in frames.
395 i += random() % (max - min);
397 return i * ctx->fps / 1000;
401 set_alpha_by_dwell (struct blinkstate *bs)
404 bs->color[3] = (GLfloat) bs->dwellcnt / (GLfloat) bs->dwell;
406 bs->color[3] = bs->dwellcnt > (-bs->dwell >> 2) ? 1.0 : 0.0;
411 draw_blink_blade (lockward_context *ctx, int inner, int outer,
415 if (begin_p) glBegin (GL_TRIANGLE_FAN);
416 /* glCallList (ctx->blades_outer + outer); */
417 for (i = 0; i < countof(*ctx->points_outer); i++)
418 glVertex3f(ctx->points_outer[outer][i].x,
419 ctx->points_outer[outer][i].y,
420 ctx->points_outer[outer][i].z);
422 /* glCallList (ctx->blades_inner + inner); */
423 for (i = 0; i < countof(*ctx->points_inner); i++)
424 glVertex3f(ctx->points_inner[inner][i].x,
425 ctx->points_inner[inner][i].y,
426 ctx->points_inner[inner][i].z);
427 if (begin_p) glEnd();
431 draw_blink_radial_random (lockward_context *ctx, struct blinkstate *bs)
436 * There is no sense of direction in a random sweep, so re-use the
437 * 'direction' field to hold the current blade we're messing with.
439 if (bs->dwellcnt < 0) {
440 if (bs->counter <= 0) {
446 * Find available blade. Potentially very slow, depending on
447 * how unlucky we are.
450 i = random() % g_blades;
451 } while (bs->val & (1 << i));
452 bs->val |= (1 << i); /* Mark as used. */
454 if ((bs->dwellcnt = bs->dwell) < 0)
455 bs->dwellcnt = -bs->dwellcnt;
457 if ( bs->type == BTYPE_SEGMENT_SINGLE
458 || bs->type == BTYPE_SEGMENT_RANDOM)
459 bs->radius = random() % (NRADII - 1);
464 set_alpha_by_dwell (bs);
465 glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
466 glColor4fv (bs->color);
467 glRotatef (bs->direction * 360.0 / (GLfloat) g_blades, 0, 0, 1);
469 draw_blink_blade (ctx, bs->radius, bs->radius + 1, True);
471 draw_blink_blade (ctx, 0, NRADII - 1, True);
475 return SUBDIV + SUBDIV;
479 draw_blink_radial_sequential (lockward_context *ctx, struct blinkstate *bs)
481 if (bs->dwellcnt < 0) {
482 if (bs->counter <= 0) {
486 if ((bs->dwellcnt = bs->dwell) < 0)
487 bs->dwellcnt = -bs->dwellcnt;
491 set_alpha_by_dwell (bs);
492 glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
493 glColor4fv (bs->color);
494 glRotatef ((bs->counter * bs->direction + (int) bs->val)
495 * 360.0 / (GLfloat) g_blades,
497 draw_blink_blade (ctx, 0, NRADII - 1, True);
501 return SUBDIV + SUBDIV;
505 draw_blink_radial_doubleseq (lockward_context *ctx, struct blinkstate *bs)
509 if (bs->dwellcnt < 0) {
510 if (bs->counter <= 0) {
514 if ((bs->dwellcnt = bs->dwell) < 0)
515 bs->dwellcnt = -bs->dwellcnt;
519 set_alpha_by_dwell (bs);
520 glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
521 glColor4fv (bs->color);
524 glRotatef (((int) bs->val + bs->counter) * 360.0 / (GLfloat) g_blades,
526 draw_blink_blade (ctx, 0, NRADII - 1, True);
528 polys = SUBDIV + SUBDIV;
530 if (bs->counter && bs->counter < g_blades / 2) {
531 glRotatef (((int) bs->val - bs->counter)
532 * 360.0 / (GLfloat) g_blades,
534 draw_blink_blade (ctx, 0, NRADII - 1, True);
535 polys += SUBDIV + SUBDIV;
544 draw_blink_concentric_random (lockward_context *ctx, struct blinkstate *bs)
548 if (bs->dwellcnt < 0) {
549 if (bs->counter <= 0) {
555 i = random() % (NRADII - 1);
556 } while (bs->val & (1 << i));
559 if ((bs->dwellcnt = bs->dwell) < 0)
560 bs->dwellcnt = -bs->dwellcnt;
565 set_alpha_by_dwell (bs);
566 glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
567 glColor4fv (bs->color);
568 glCallList (ctx->rings + bs->direction);
572 return g_blades * SUBDIV * 2;
576 draw_blink_concentric_sequential (lockward_context *ctx, struct blinkstate *bs)
578 if (bs->dwellcnt < 0) {
579 if (bs->counter <= 0) {
583 if ((bs->dwellcnt = bs->dwell) < 0)
584 bs->dwellcnt = -bs->dwellcnt;
588 set_alpha_by_dwell (bs);
589 glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
590 glColor4fv (bs->color);
591 if (bs->direction > 0)
592 glCallList (ctx->rings + (NRADII - 2) - bs->counter);
594 glCallList (ctx->rings + bs->counter);
598 return g_blades * SUBDIV * 2;
602 draw_blink_segment_scatter (lockward_context *ctx, struct blinkstate *bs)
606 if (bs->dwellcnt < 0) {
607 if (bs->counter <= 0) {
613 * Init random noise array. On average, 1/4 of the bits will
614 * be set, which should look nice. (1/2 looks too busy.)
616 for (i = g_blades; --i >= 0; )
617 bs->noise[i] = random() & random()
618 & ((1 << (NRADII - 1)) - 1);
620 if ((bs->dwellcnt = bs->dwell) < 0)
621 bs->dwellcnt = -bs->dwellcnt;
625 set_alpha_by_dwell (bs);
626 glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
627 glColor4fv (bs->color);
629 for (i = g_blades; --i >= 0; ) {
630 register uint32_t bits;
634 * Find consecutive runs of 1 bits. Keep going until we run
637 for (bits = bs->noise[i]; bits; ) {
638 inner = ffs (bits) - 1;
639 bits = ~bits & ~((1 << inner) - 1);
640 outer = ffs (bits) - 1;
641 bits = ~bits & ~((1 << outer) - 1);
644 glRotatef (i * 360.0 / (GLfloat) g_blades, 0, 0, 1);
645 draw_blink_blade (ctx, inner, outer, True);
648 polys += SUBDIV + SUBDIV;
658 random_blink (lockward_context *ctx, struct blinkstate *bs)
666 bs->dwell = calc_interval_frames
667 (ctx, g_blinkdwell_min, g_blinkdwell_max);
669 bs->dwell = -bs->dwell;
671 bs->type = random() % MAX_BTYPE;
674 case BTYPE_RADIAL_SINGLE:
675 case BTYPE_SEGMENT_SINGLE:
676 bs->drawfunc = draw_blink_radial_random;
680 case BTYPE_RADIAL_RANDOM:
681 case BTYPE_SEGMENT_RANDOM:
682 bs->drawfunc = draw_blink_radial_random;
684 bs->counter = g_blades;
686 case BTYPE_RADIAL_SEQ:
687 bs->drawfunc = draw_blink_radial_sequential;
688 bs->val = random() % g_blades; /* Initial offset */
689 bs->direction = random() & 8 ? 1 : -1;
690 bs->counter = g_blades;
692 case BTYPE_RADIAL_DOUBLESEQ:
693 bs->drawfunc = draw_blink_radial_doubleseq;
694 bs->val = random() % g_blades; /* Initial offset */
695 bs->counter = g_blades / 2 + 1;
697 case BTYPE_CONCENTRIC_SINGLE:
698 bs->drawfunc = draw_blink_concentric_random;
702 case BTYPE_CONCENTRIC_RANDOM:
703 bs->drawfunc = draw_blink_concentric_random;
705 bs->counter = NRADII - 1;
707 case BTYPE_CONCENTRIC_SEQ:
708 bs->drawfunc = draw_blink_concentric_sequential;
709 bs->direction = random() & 8 ? 1 : -1;
710 bs->counter = NRADII - 1;
712 case BTYPE_SEGMENT_SCATTER:
713 bs->drawfunc = draw_blink_segment_scatter;
714 bs->counter = random() % (g_blades / 2) + (g_blades / 2) + 1;
720 /***************************************************************************
721 * Main rendering routine.
724 draw_lockward (ModeInfo *mi)
726 lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
728 Display *dpy = MI_DISPLAY(mi);
729 Window window = MI_WINDOW(mi);
732 GLfloat scolor[4] = {0.0, 0.0, 0.0, 0.5};
734 if (!ctx->glx_context)
737 glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi), *(ctx->glx_context));
740 glClear (GL_COLOR_BUFFER_BIT);
743 glBlendFunc (GL_ONE, GL_ONE);
745 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
750 mi->polygon_count = 0;
752 for (n = NSPINNERS; --n >= 0; ) {
753 ss = &ctx->spinners[n];
756 i = ss->ccolor >> COLORIDX_SHF;
757 scolor[0] = ss->colors[i].red / 65535.0;
758 scolor[1] = ss->colors[i].green / 65535.0;
759 scolor[2] = ss->colors[i].blue / 65535.0;
763 glRotatef (ss->rot - ss->rotcount * ss->rotinc, 0, 0, 1);
764 for (i = ss->nblades; --i >= 0; ) {
766 glRotatef (360.0 * i / ss->nblades, 0, 0, 1);
768 glBegin (GL_TRIANGLE_FAN);
769 /* glCallList (ctx->blades_outer + ss->bladeidx[i].outer); */
770 /* glCallList (ctx->blades_inner + ss->bladeidx[i].inner); */
771 draw_blink_blade (ctx,
772 ss->bladeidx[i].inner,
773 ss->bladeidx[i].outer,
778 mi->polygon_count += SUBDIV + SUBDIV;
782 /* Advance rotation. */
784 if (ss->rotcount > 0)
787 if (ss->rotinc == 0.0)
788 random_blade_rot (ctx, ss);
790 /* Compute # of ticks to sit idle. */
793 calc_interval_frames (ctx,
799 /* Advance colors. */
800 if ((ss->ccolor += ss->colorinc) >= ss->ncolors)
801 ss->ccolor -= ss->ncolors;
802 else if (ss->ccolor < 0)
803 ss->ccolor += ss->ncolors;
807 if (ctx->blink.drawfunc) {
809 ctx->blink.drawfunc (ctx, &ctx->blink);
811 if (ctx->nextblink > 0)
814 /* Compute # of frames for blink idle time. */
816 calc_interval_frames (ctx,
819 random_blink (ctx, &ctx->blink);
825 if (MI_IS_FPS (mi)) do_fps (mi);
828 glXSwapBuffers (dpy, window);
832 /***************************************************************************
833 * Initialization/teardown.
836 init_lockward (ModeInfo *mi)
838 lockward_context *ctx;
842 ctx = &g_ctx[MI_SCREEN (mi)];
844 ctx->glx_context = init_GL (mi);
846 reshape_lockward (mi, MI_WIDTH (mi), MI_HEIGHT (mi));
848 glEnable (GL_CULL_FACE);
850 glDisable (GL_DEPTH_TEST);
852 glShadeModel (GL_FLAT);
855 /* ctx->blades_outer = glGenLists (NRADII); */
856 /* ctx->blades_inner = glGenLists (NRADII); */
857 ctx->rings = glGenLists (NRADII - 1);
859 /* WTF? ctx->fps = 1000000 / MI_DELAY (mi); */
861 ctx->nextblink = calc_interval_frames
862 (ctx, g_blinkidle_min, g_blinkidle_max);
863 ctx->blink.drawfunc = NULL;
864 ctx->blink.noise = malloc (sizeof (uint32_t) * g_blades);
865 if (!ctx->blink.noise) {
866 fprintf (stderr, "Can't allocate noise array.\n");
870 gen_blade_arcs (ctx);
873 for (i = NSPINNERS; --i >= 0; ) {
874 spinnerstate *ss = &ctx->spinners[i];
879 /* Establish rotation */
880 random_blade_rot (ctx, ss);
883 * Establish color cycling path and rate. Rate avoids zero.
886 ss->colorinc = (random() & ((2 << COLORIDX_SHF) - 1))
887 - (1 << COLORIDX_SHF);
888 if (ss->colorinc >= 0)
891 ss->colors = (XColor *) calloc (ss->ncolors, sizeof (XColor));
894 "Can't allocate XColors for spinner %d.\n",
898 make_smooth_colormap (0, 0, 0,
899 ss->colors, &ss->ncolors,
901 ss->ncolors <<= COLORIDX_SHF;
906 ss->nblades = g_blades;
907 ss->bladeidx = malloc (sizeof (bladestate) * g_blades);
909 fprintf (stderr, "Can't allocate blades.\n");
912 for (n = g_blades; --n >= 0; ) {
914 * Establish blade radii. Can't be equal. Ensure
918 ss->bladeidx[n].outer = random() & 7;
919 ss->bladeidx[n].inner = random() & 7;
920 } while (ss->bladeidx[n].outer ==
921 ss->bladeidx[n].inner);
923 if (ss->bladeidx[n].outer < ss->bladeidx[n].inner) {
926 tmp = ss->bladeidx[n].outer;
927 ss->bladeidx[n].outer = ss->bladeidx[n].inner;
928 ss->bladeidx[n].inner = tmp;
935 free_lockward (ModeInfo *mi)
937 lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
940 if (!ctx->glx_context)
943 glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
944 *(ctx->glx_context));
946 if (ctx->blink.noise)
947 free (ctx->blink.noise);
948 if (glIsList (ctx->rings))
949 glDeleteLists (ctx->rings, NRADII - 1);
950 /* if (glIsList (ctx->blades_outer))
951 glDeleteLists (ctx->blades_outer, NRADII);
952 if (glIsList (ctx->blades_inner))
953 glDeleteLists (ctx->blades_inner, NRADII); */
955 for (i = NSPINNERS; --i >= 0; ) {
956 spinnerstate *ss = &ctx->spinners[i];
966 XSCREENSAVER_MODULE ("Lockward", lockward)
970 /* vim:se ts=8 sts=8 sw=8: */