From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / lockward.c
1 /*
2  * lockward.c:  First attempt at an Xscreensaver.
3  *
4  * Leo L. Schwab                                        2007.08.17
5  ****
6  * Copyright (c) 2007 Leo L. Schwab
7  *
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:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  * 
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.
26  */
27 #include <ctype.h>
28 #include <strings.h>
29
30 #include "xlockmore.h"
31 #include "colors.h"
32
33
34 /***************************************************************************
35  * #defines
36  */
37 #ifdef USE_GL /* whole file */
38
39 #define DEFAULTS        "*delay:        20000   \n"\
40                         "*showFPS:      False   \n"
41
42 #define refresh_lockward        0
43 #define release_lockward        0
44
45
46 #define NUMOF(x)        (sizeof ((x)) / sizeof ((*x)))
47
48 #define NBLADES         12
49 #define NSPINNERS       4
50 #define NRADII          8
51 #define COLORIDX_SHF    4
52 #define SUBDIV          6
53
54 #undef countof
55 #define countof(x) (sizeof((x))/sizeof((*x)))
56
57
58 /***************************************************************************
59  * Structure definitions.
60  */
61 struct lockward_context;                        /*  Forward declaration.  */
62
63 #define int8_t   char
64 #define int16_t  short
65 #define int32_t  int
66 #define uint8_t  unsigned char
67 #define uint16_t unsigned short
68 #define uint32_t unsigned int
69
70 typedef struct bladestate {
71         uint8_t         outer, inner;   /*  Radii  */
72 } bladestate;
73
74 typedef struct spinnerstate {
75         GLfloat         rot;    /*  Terminal rotation after count expires */
76         GLfloat         rotinc; /*  Per-frame increment to rot.  */
77         XColor          *colors;
78         bladestate      *bladeidx;
79         int             ncolors;        /*  n.4 fixed-point  */
80         int             ccolor;         /*  n.4 fixed-point  */
81         int             colorinc;       /*  n.4 fixed-point  */
82         int             rotcount;
83         uint8_t         nblades;
84 } spinnerstate;
85
86 typedef struct blinkstate {
87         int             (*drawfunc) (struct lockward_context *ctx,
88                                      struct blinkstate *bs);
89         uint32_t        *noise; /*  For draw_blink_segment_scatter()  */
90         GLfloat         color[4];
91         uint32_t        val;
92         int16_t         dwell;  /*  <0: sharp  >0: decay  */
93         int16_t         dwellcnt;
94         uint8_t         type;
95         int8_t          counter;
96         int8_t          direction;
97         int8_t          radius;
98 } blinkstate;
99
100 enum blinktype {
101         BTYPE_RADIAL_SINGLE = 0,
102         BTYPE_RADIAL_RANDOM,
103         BTYPE_RADIAL_SEQ,
104         BTYPE_RADIAL_DOUBLESEQ,
105         BTYPE_SEGMENT_SINGLE,
106         BTYPE_SEGMENT_RANDOM,
107         BTYPE_CONCENTRIC_SINGLE,
108         BTYPE_CONCENTRIC_RANDOM,
109         BTYPE_CONCENTRIC_SEQ,
110         BTYPE_SEGMENT_SCATTER,
111         MAX_BTYPE
112 };
113
114 typedef struct { GLfloat x,y,z; } XYZ;
115
116 typedef struct lockward_context {
117         GLXContext      *glx_context;
118
119         spinnerstate    spinners[NSPINNERS];
120         blinkstate      blink;
121
122         /* This used to put vertexes into lists without putting begin/end
123            into the same list!  I didn't even know that worked.  Well, it
124            doesn't work with jwzgles, so I changed it to not do that. */
125      /* GLuint          blades_outer, blades_inner; */
126         XYZ points_outer[NRADII][SUBDIV+1];
127         XYZ points_inner[NRADII][SUBDIV+1];
128
129         GLuint          rings;
130         Bool            blendmode;
131         int             nextblink;
132         int             fps;
133
134 } lockward_context;
135
136
137 /***************************************************************************
138  * Prototypes.
139  */
140 static void free_lockward (ModeInfo *mi);
141
142
143 /***************************************************************************
144  * Global variables.
145  */
146 static lockward_context *g_ctx = NULL;
147 static Bool             g_blink_p = True;
148 static int              g_blades = NBLADES;
149 static int              g_rotateidle_min,
150                         g_rotateidle_max;
151 static int              g_blinkidle_min,
152                         g_blinkidle_max;
153 static int              g_blinkdwell_min,
154                         g_blinkdwell_max;
155
156 #define DEF_BLINK               "True"
157 #define DEF_ROTATEIDLEMIN       "1000"
158 #define DEF_ROTATEIDLEMAX       "6000"
159 #define DEF_BLINKIDLEMIN        "1000"
160 #define DEF_BLINKIDLEMAX        "9000"
161 #define DEF_BLINKDWELLMIN       "100"
162 #define DEF_BLINKDWELLMAX       "600"
163
164
165 static XrmOptionDescRec opts[] = {
166         { "-blink",     ".blink",  XrmoptionNoArg, "on" },
167         { "+blink",     ".blink",  XrmoptionNoArg, "off" },
168         { "-rotateidle-min",    ".rotateidlemin",       XrmoptionSepArg,        0 },
169         { "-rotateidle-max",    ".rotateidlemax",       XrmoptionSepArg,        0 },
170         { "-blinkidle-min",     ".blinkidlemin",        XrmoptionSepArg,        0 },
171         { "-blinkidle-max",     ".blinkidlemax",        XrmoptionSepArg,        0 },
172         { "-blinkdwell-min",    ".blinkdwellmin",       XrmoptionSepArg,        0 },
173         { "-blinkdwell-max",    ".blinkdwellmax",       XrmoptionSepArg,        0 },
174 };
175
176 static argtype vars[] = {
177         { &g_blink_p, "blink",  "Blink", DEF_BLINK, t_Bool      },
178         { &g_rotateidle_min, "rotateidlemin",   "Rotateidlemin", DEF_ROTATEIDLEMIN, t_Int       },
179         { &g_rotateidle_max, "rotateidlemax",   "Rotateidlemax", DEF_ROTATEIDLEMAX, t_Int       },
180         { &g_blinkidle_min, "blinkidlemin", "Blinkidlemin", DEF_BLINKIDLEMIN, t_Int },
181         { &g_blinkidle_max, "blinkidlemax", "Blinkidlemax", DEF_BLINKIDLEMAX, t_Int },
182         { &g_blinkdwell_min, "blinkdwellmin", "Blinkdwellmin", DEF_BLINKDWELLMIN, t_Int },
183         { &g_blinkdwell_max, "blinkdwellmax", "Blinkdwellmax", DEF_BLINKDWELLMAX, t_Int },
184 };
185
186 static OptionStruct desc[] = {
187         { "-/+blink", "Turn on/off blinking effects." },
188         { "-rotateidle-min", "Minimum idle time for rotators, in milliseconds." },
189         { "-rotateidle-max", "Maximum idle time for rotators, in milliseconds." },
190         { "-blinkidle-min", "Minimum idle time between blink effects, in milliseconds." },
191         { "-blinkidle-max", "Maximum idle time between blink effects, in milliseconds." },
192         { "-blinkdwell-min", "Minimum dwell time for blink effects, in milliseconds." },
193         { "-blinkdwell-max", "Maximum dwell time for blink effects, in milliseconds." },
194 };
195
196 ENTRYPOINT ModeSpecOpt lockward_opts = {
197         NUMOF(opts), opts, NUMOF(vars), vars, desc
198 };
199
200
201 /***************************************************************************
202  * Window management.
203  */
204 ENTRYPOINT void
205 reshape_lockward (ModeInfo *mi, int width, int height)
206 {
207         lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
208         GLfloat h = (GLfloat) height / (GLfloat) width;
209
210         glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
211                         *(ctx->glx_context));
212
213         glViewport (0, 0, (GLint) width, (GLint) height);
214
215         glMatrixMode (GL_PROJECTION);
216         glLoadIdentity ();
217         if (height > width)
218                 glOrtho (-8.0, 8.0, -8.0 * h, 8.0 * h, -1, 1);
219         else
220                 glOrtho (-8.0 / h, 8.0 / h, -8.0, 8.0, -1, 1);
221
222         glMatrixMode (GL_MODELVIEW);
223 }
224
225 ENTRYPOINT Bool
226 lockward_handle_event (ModeInfo *mi, XEvent *event)
227 {
228         lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
229
230         if (event->xany.type == KeyPress) {
231                 KeySym  keysym;
232                 char    c = 0;
233
234                 XLookupString (&event->xkey, &c, 1, &keysym, 0);
235                 if (c == ' ' || c == '\t') {
236                         ctx->blendmode ^= 1;
237                         return True;
238                 }
239         }
240
241         return False;
242 }
243
244
245 /***************************************************************************
246  * "Blade" routines.
247  */
248 static void
249 random_blade_rot (lockward_context *ctx, struct spinnerstate *ss)
250 {
251         /*
252          * The circle is divided up in to g_blades divisions.  The idea here
253          * is to rotate to an exact division point.
254          *
255          * The target rotation is computed via random numbers.
256          *
257          * The time it takes to get there is a maximum of six seconds per
258          * division, and a minimum of one second (no matter how far away it
259          * is), and is selected by random numbers.
260          *
261          * The time value is converted into frames, and a per-frame rotation
262          * is computed.
263          *
264          * During rendering, we approach the target rotation by subtracting
265          * from it the per-frame rotation times the number of outstanding
266          * ticks.  Doing it this way means we'll hit the target rotation
267          * exactly, without low-order errors creeping in to the values (at
268          * least not nearly as quickly).
269          */
270         GLfloat d;
271         int     dist;
272
273         dist = random() % g_blades + 1;
274
275         ss->rotcount = random() % (6 * dist * ctx->fps - ctx->fps)
276                      + ctx->fps;
277
278         if (random() & 4)
279                 dist = -dist;
280         d = dist * 360.0 / (GLfloat) g_blades;
281         ss->rot += d;
282         ss->rotinc = d / (GLfloat) ss->rotcount;
283 }
284
285
286 /*
287  * A "blade" is pie-wedge shaped flat thing that is rotated around where the
288  * apex is/would be.  Initially envisioned as 1/12th of a circle, but that
289  * could be configurable.  The inner and outer edges are rounded off using
290  * six subdivisions so that, when multiple blades are assembled, it looks
291  * more like a circle and less like a polygon.
292  *
293  * The blade is assembled as a tri-fan.  It is oriented centered at 3
294  * o'clock.  The blade is composed of two display lists -- arcs, essentially
295  * -- the outer and the inner one.  The outer one *must* be called before
296  * the inner one, or the blade clockwise-ness will be wrong, and become
297  * invisible.  Arcs of various radii are compiled.
298  */
299
300 static void
301 gen_blade_arcs (lockward_context *ctx)
302 {
303         GLfloat here, there, step;
304         int     i, n;
305
306         here = 0;
307         there = M_PI * 2.0 / g_blades;
308         step = there / SUBDIV;
309         here -= SUBDIV * step / 2.0;
310
311         /*
312          * Build outer blade arcs.
313          * Start at left side of outer radius.  Strike all its vertices.
314          */
315         for (n = 0;  n < NRADII;  ++n) {
316           /* glNewList (ctx->blades_outer + n, GL_COMPILE); */
317           XYZ *a = ctx->points_outer[n];
318           int j = 0;
319           for (i = SUBDIV;  i >= 0;  --i) {
320             /* glVertex3f (cos (here + step * i) * (n + 1.0),
321                            sin (here + step * i) * (n + 1.0), 0); */
322             a[j].x = cos (here + step * i) * (n + 1.0);
323             a[j].y = sin (here + step * i) * (n + 1.0);
324             a[j].z = 0;
325             j++;
326           }
327           if (j != SUBDIV+1) abort();
328           /* glEndList (); */
329         }
330
331         /*
332          * Build inner blade arcs.
333          * Move to inner radius, strike all vertices in opposite order.
334          */
335         for (n = 0;  n < NRADII;  ++n) {
336           /* glNewList (ctx->blades_inner + n, GL_COMPILE); */
337           XYZ *a = ctx->points_inner[n];
338           int j = 0;
339           for (i = 0;  i <= SUBDIV;  ++i) {
340             /* glVertex3f (cos (here + step * i) * (n + 1.0),
341                            sin (here + step * i) * (n + 1.0), 0); */
342             a[j].x = cos (here + step * i) * (n + 1.0);
343             a[j].y = sin (here + step * i) * (n + 1.0);
344             a[j].z = 0;
345             j++;
346           }
347           if (j != SUBDIV+1) abort();
348           /* glEndList (); */
349         }
350 }
351
352 static void
353 gen_rings (lockward_context *ctx)
354 {
355         GLfloat step;
356         int     i, n;
357
358         step = M_PI * 2.0 / (g_blades * SUBDIV);
359
360         for (n = 0;  n < NRADII - 1;  ++n) {
361                 glNewList (ctx->rings + n, GL_COMPILE);
362                 glBegin (GL_TRIANGLE_STRIP);
363                 for (i = g_blades * SUBDIV;  i >= 0;  --i) {
364                         glVertex3f (cos (step * i) * (n + 1.0),
365                                     sin (step * i) * (n + 1.0), 0);
366                         glVertex3f (cos (step * i) * (n + 2.0),
367                                     sin (step * i) * (n + 2.0), 0);
368                 }
369                 glEnd();
370                 glEndList ();
371         }
372 }
373
374
375 /***************************************************************************
376  * "Blink" routines.
377  */
378 static int
379 calc_interval_frames (lockward_context *ctx, int min, int max)
380 {
381         /*
382          * Compute random interval between min and max milliseconds.
383          * Returned value is in frames.
384          */
385         register int i;
386
387         i = min;
388         if (max > min)
389                 i += random() % (max - min);
390
391         return i * ctx->fps / 1000;
392 }
393
394 static void
395 set_alpha_by_dwell (struct blinkstate *bs)
396 {
397         if (bs->dwell > 0)
398                 bs->color[3] = (GLfloat) bs->dwellcnt / (GLfloat) bs->dwell;
399         else
400                 bs->color[3] = bs->dwellcnt > (-bs->dwell >> 2)  ?  1.0  : 0.0;
401 }
402
403
404 static void
405 draw_blink_blade (lockward_context *ctx, int inner, int outer,
406                   Bool begin_p)
407 {
408   int i;
409   if (begin_p) glBegin (GL_TRIANGLE_FAN);
410   /* glCallList (ctx->blades_outer + outer); */
411   for (i = 0; i < countof(*ctx->points_outer); i++)
412     glVertex3f(ctx->points_outer[outer][i].x,
413                ctx->points_outer[outer][i].y,
414                ctx->points_outer[outer][i].z);
415
416   /* glCallList (ctx->blades_inner + inner); */
417   for (i = 0; i < countof(*ctx->points_inner); i++)
418     glVertex3f(ctx->points_inner[inner][i].x,
419                ctx->points_inner[inner][i].y,
420                ctx->points_inner[inner][i].z);
421   if (begin_p) glEnd();
422 }
423
424 static int
425 draw_blink_radial_random (lockward_context *ctx, struct blinkstate *bs)
426 {
427         int i;
428
429         /*
430          * There is no sense of direction in a random sweep, so re-use the
431          * 'direction' field to hold the current blade we're messing with.
432          */
433         if (bs->dwellcnt < 0) {
434                 if (bs->counter <= 0) {
435                         bs->drawfunc = NULL;
436                         return 0;
437                 }
438
439                 /*
440                  * Find available blade.  Potentially very slow, depending on
441                  * how unlucky we are.
442                  */
443                 do {
444                         i = random() % g_blades;
445                 } while (bs->val & (1 << i));
446                 bs->val |= (1 << i);    /*  Mark as used.  */
447                 bs->direction = i;
448                 if ((bs->dwellcnt = bs->dwell) < 0)
449                         bs->dwellcnt = -bs->dwellcnt;
450                 
451                 if (    bs->type == BTYPE_SEGMENT_SINGLE
452                     ||  bs->type == BTYPE_SEGMENT_RANDOM)
453                         bs->radius = random() % (NRADII - 1);
454
455                 --bs->counter;
456         }
457
458         set_alpha_by_dwell (bs);
459         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
460         glColor4fv (bs->color);
461         glRotatef (bs->direction * 360.0 / (GLfloat) g_blades, 0, 0, 1);
462         if (bs->radius >= 0)
463                 draw_blink_blade (ctx, bs->radius, bs->radius + 1, True);
464         else
465                 draw_blink_blade (ctx, 0, NRADII - 1, True);
466
467         --bs->dwellcnt;
468
469         return SUBDIV + SUBDIV;
470 }
471
472 static int
473 draw_blink_radial_sequential (lockward_context *ctx, struct blinkstate *bs)
474 {
475         if (bs->dwellcnt < 0) {
476                 if (bs->counter <= 0) {
477                         bs->drawfunc = NULL;
478                         return 0;
479                 }
480                 if ((bs->dwellcnt = bs->dwell) < 0)
481                         bs->dwellcnt = -bs->dwellcnt;
482                 --bs->counter;
483         }
484
485         set_alpha_by_dwell (bs);
486         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
487         glColor4fv (bs->color);
488         glRotatef ((bs->counter * bs->direction + (int) bs->val)
489                     * 360.0 / (GLfloat) g_blades,
490                    0, 0, 1);
491         draw_blink_blade (ctx, 0, NRADII - 1, True);
492
493         --bs->dwellcnt;
494
495         return SUBDIV + SUBDIV;
496 }
497
498 static int
499 draw_blink_radial_doubleseq (lockward_context *ctx, struct blinkstate *bs)
500 {
501         int polys;
502
503         if (bs->dwellcnt < 0) {
504                 if (bs->counter <= 0) {
505                         bs->drawfunc = NULL;
506                         return 0;
507                 }
508                 if ((bs->dwellcnt = bs->dwell) < 0)
509                         bs->dwellcnt = -bs->dwellcnt;
510                 --bs->counter;
511         }
512
513         set_alpha_by_dwell (bs);
514         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
515         glColor4fv (bs->color);
516
517         glPushMatrix ();
518         glRotatef (((int) bs->val + bs->counter) * 360.0 / (GLfloat) g_blades,
519                    0, 0, 1);
520         draw_blink_blade (ctx, 0, NRADII - 1, True);
521         glPopMatrix ();
522         polys = SUBDIV + SUBDIV;
523
524         if (bs->counter  &&  bs->counter < g_blades / 2) {
525                 glRotatef (((int) bs->val - bs->counter)
526                             * 360.0 / (GLfloat) g_blades,
527                            0, 0, 1);
528                 draw_blink_blade (ctx, 0, NRADII - 1, True);
529                 polys += SUBDIV + SUBDIV;
530         }
531
532         --bs->dwellcnt;
533
534         return polys;
535 }
536
537 static int
538 draw_blink_concentric_random (lockward_context *ctx, struct blinkstate *bs)
539 {
540         int i;
541
542         if (bs->dwellcnt < 0) {
543                 if (bs->counter <= 0) {
544                         bs->drawfunc = NULL;
545                         return 0;
546                 }
547
548                 do {
549                         i = random() % (NRADII - 1);
550                 } while (bs->val & (1 << i));
551                 bs->val |= (1 << i);
552                 bs->direction = i;
553                 if ((bs->dwellcnt = bs->dwell) < 0)
554                         bs->dwellcnt = -bs->dwellcnt;
555
556                 --bs->counter;
557         }
558
559         set_alpha_by_dwell (bs);
560         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
561         glColor4fv (bs->color);
562         glCallList (ctx->rings + bs->direction);
563
564         --bs->dwellcnt;
565
566         return g_blades * SUBDIV * 2;
567 }
568
569 static int
570 draw_blink_concentric_sequential (lockward_context *ctx, struct blinkstate *bs)
571 {
572         if (bs->dwellcnt < 0) {
573                 if (bs->counter <= 0) {
574                         bs->drawfunc = NULL;
575                         return 0;
576                 }
577                 if ((bs->dwellcnt = bs->dwell) < 0)
578                         bs->dwellcnt = -bs->dwellcnt;
579                 --bs->counter;
580         }
581
582         set_alpha_by_dwell (bs);
583         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
584         glColor4fv (bs->color);
585         if (bs->direction > 0)
586                 glCallList (ctx->rings + (NRADII - 2) - bs->counter);
587         else
588                 glCallList (ctx->rings + bs->counter);
589
590         --bs->dwellcnt;
591
592         return g_blades * SUBDIV * 2;
593 }
594
595 static int
596 draw_blink_segment_scatter (lockward_context *ctx, struct blinkstate *bs)
597 {
598         int i, polys = 0;
599
600         if (bs->dwellcnt < 0) {
601                 if (bs->counter <= 0) {
602                         bs->drawfunc = NULL;
603                         return 0;
604                 }
605
606                 /*
607                  * Init random noise array.  On average, 1/4 of the bits will
608                  * be set, which should look nice.  (1/2 looks too busy.)
609                  */
610                 for (i = g_blades;  --i >= 0; )
611                         bs->noise[i] = random() & random()
612                                      & ((1 << (NRADII - 1)) - 1);
613
614                 if ((bs->dwellcnt = bs->dwell) < 0)
615                         bs->dwellcnt = -bs->dwellcnt;
616                 --bs->counter;
617         }
618
619         set_alpha_by_dwell (bs);
620         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
621         glColor4fv (bs->color);
622
623         for (i = g_blades;  --i >= 0; ) {
624                 register uint32_t       bits;
625                 int                     inner, outer;
626
627                 /*
628                  * Find consecutive runs of 1 bits.  Keep going until we run
629                  * out of them.
630                  */
631                 for (bits = bs->noise[i];  bits; ) {
632                         inner = ffs (bits) - 1;
633                         bits = ~bits & ~((1 << inner) - 1);
634                         outer = ffs (bits) - 1;
635                         bits = ~bits & ~((1 << outer) - 1);
636
637                         glPushMatrix ();
638                         glRotatef (i * 360.0 / (GLfloat) g_blades, 0, 0, 1);
639                         draw_blink_blade (ctx, inner, outer, True);
640                         glPopMatrix ();
641
642                         polys += SUBDIV + SUBDIV;
643                 }
644         }
645
646         --bs->dwellcnt;
647
648         return polys;
649 }
650
651 static void
652 random_blink (lockward_context *ctx, struct blinkstate *bs)
653 {
654         bs->color[0]    =
655         bs->color[1]    =
656         bs->color[2]    =
657         bs->color[3]    = 1.0;
658         bs->dwellcnt    = -1;
659         bs->radius      = -1;
660         bs->dwell       = calc_interval_frames
661                           (ctx, g_blinkdwell_min, g_blinkdwell_max);
662         if (random() & 2)
663                 bs->dwell = -bs->dwell;
664
665         bs->type = random() % MAX_BTYPE;
666
667         switch (bs->type) {
668         case BTYPE_RADIAL_SINGLE:
669         case BTYPE_SEGMENT_SINGLE:
670                 bs->drawfunc = draw_blink_radial_random;
671                 bs->val = 0;
672                 bs->counter = 1;
673                 break;
674         case BTYPE_RADIAL_RANDOM:
675         case BTYPE_SEGMENT_RANDOM:
676                 bs->drawfunc = draw_blink_radial_random;
677                 bs->val = 0;
678                 bs->counter = g_blades;
679                 break;
680         case BTYPE_RADIAL_SEQ:
681                 bs->drawfunc = draw_blink_radial_sequential;
682                 bs->val = random() % g_blades;  /*  Initial offset  */
683                 bs->direction = random() & 8  ?  1 :  -1;
684                 bs->counter = g_blades;
685                 break;
686         case BTYPE_RADIAL_DOUBLESEQ:
687                 bs->drawfunc = draw_blink_radial_doubleseq;
688                 bs->val = random() % g_blades;  /*  Initial offset  */
689                 bs->counter = g_blades / 2 + 1;
690                 break;
691         case BTYPE_CONCENTRIC_SINGLE:
692                 bs->drawfunc = draw_blink_concentric_random;
693                 bs->val = 0;
694                 bs->counter = 1;
695                 break;
696         case BTYPE_CONCENTRIC_RANDOM:
697                 bs->drawfunc = draw_blink_concentric_random;
698                 bs->val = 0;
699                 bs->counter = NRADII - 1;
700                 break;
701         case BTYPE_CONCENTRIC_SEQ:
702                 bs->drawfunc = draw_blink_concentric_sequential;
703                 bs->direction = random() & 8  ?  1 :  -1;
704                 bs->counter = NRADII - 1;
705                 break;
706         case BTYPE_SEGMENT_SCATTER:
707                 bs->drawfunc = draw_blink_segment_scatter;
708                 bs->counter = random() % (g_blades / 2) + (g_blades / 2) + 1;
709                 break;
710         }
711 }
712
713
714 /***************************************************************************
715  * Main rendering routine.
716  */
717 ENTRYPOINT void
718 draw_lockward (ModeInfo *mi)
719 {
720         lockward_context        *ctx = &g_ctx[MI_SCREEN (mi)];
721         spinnerstate    *ss;
722         Display         *dpy = MI_DISPLAY(mi);
723         Window          window = MI_WINDOW(mi);
724         int             i, n;
725
726         GLfloat scolor[4] = {0.0, 0.0, 0.0, 0.5};
727
728         if (!ctx->glx_context)
729                 return;
730
731         glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi), *(ctx->glx_context));
732
733
734         glClear (GL_COLOR_BUFFER_BIT);
735
736         if (ctx->blendmode)
737                 glBlendFunc (GL_ONE, GL_ONE);
738         else
739                 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
740
741         glPushMatrix ();
742         glLoadIdentity ();
743
744         mi->polygon_count = 0;
745
746         for (n = NSPINNERS;  --n >= 0; ) {
747                 ss = &ctx->spinners[n];
748
749                 /*  Set color.  */
750                 i = ss->ccolor >> COLORIDX_SHF;
751                 scolor[0] = ss->colors[i].red   / 65535.0;
752                 scolor[1] = ss->colors[i].green / 65535.0;
753                 scolor[2] = ss->colors[i].blue  / 65535.0;
754                 glColor4fv (scolor);
755
756                 glPushMatrix ();
757                 glRotatef (ss->rot - ss->rotcount * ss->rotinc, 0, 0, 1);
758                 for (i = ss->nblades;  --i >= 0; ) {
759                   glPushMatrix ();
760                   glRotatef (360.0 * i / ss->nblades, 0, 0, 1);
761
762                   glBegin (GL_TRIANGLE_FAN);
763                   /* glCallList (ctx->blades_outer + ss->bladeidx[i].outer); */
764                   /* glCallList (ctx->blades_inner + ss->bladeidx[i].inner); */
765                   draw_blink_blade (ctx,
766                                     ss->bladeidx[i].inner,
767                                     ss->bladeidx[i].outer,
768                                     False);
769                   glEnd ();
770
771                   glPopMatrix ();
772                   mi->polygon_count += SUBDIV + SUBDIV;
773                 }
774                 glPopMatrix ();
775
776                 /*  Advance rotation.  */
777                 if (ss->rotcount) {
778                         if (ss->rotcount > 0)
779                                 --ss->rotcount;
780                 } else {
781                         if (ss->rotinc == 0.0)
782                                 random_blade_rot (ctx, ss);
783                         else {
784                                 /*  Compute # of ticks to sit idle.  */
785                                 ss->rotinc = 0.0;
786                                 ss->rotcount =
787                                  calc_interval_frames (ctx,
788                                                        g_rotateidle_min,
789                                                        g_rotateidle_max);
790                         }
791                 }
792
793                 /*  Advance colors.  */
794                 if ((ss->ccolor += ss->colorinc) >= ss->ncolors)
795                         ss->ccolor -= ss->ncolors;
796                 else if (ss->ccolor < 0)
797                         ss->ccolor += ss->ncolors;
798         }
799
800         if (g_blink_p) {
801                 if (ctx->blink.drawfunc) {
802                         mi->polygon_count +=
803                          ctx->blink.drawfunc (ctx, &ctx->blink);
804                 } else {
805                         if (ctx->nextblink > 0)
806                                 --ctx->nextblink;
807                         else {
808                                 /* Compute # of frames for blink idle time. */
809                                 ctx->nextblink =
810                                  calc_interval_frames (ctx,
811                                                        g_blinkidle_min,
812                                                        g_blinkidle_max);
813                                 random_blink (ctx, &ctx->blink);
814                         }
815                 }
816         }
817         glPopMatrix ();
818
819         if (MI_IS_FPS (mi)) do_fps (mi);
820         glFinish();
821
822         glXSwapBuffers (dpy, window);
823 }
824
825
826 /***************************************************************************
827  * Initialization/teardown.
828  */
829 ENTRYPOINT void 
830 init_lockward (ModeInfo *mi)
831 {
832         lockward_context        *ctx;
833         int             i, n;
834
835         MI_INIT (mi, g_ctx, free_lockward);
836         ctx = &g_ctx[MI_SCREEN (mi)];
837
838         ctx->glx_context = init_GL (mi);
839
840         reshape_lockward (mi, MI_WIDTH (mi), MI_HEIGHT (mi));
841
842         glEnable (GL_CULL_FACE);
843         glEnable (GL_BLEND);
844         glDisable (GL_DEPTH_TEST);
845
846         glShadeModel (GL_FLAT);
847         glFrontFace (GL_CW);
848
849         /* ctx->blades_outer    = glGenLists (NRADII); */
850         /* ctx->blades_inner    = glGenLists (NRADII); */
851         ctx->rings              = glGenLists (NRADII - 1);
852         ctx->blendmode          = 0;
853         ctx->fps                = 1000000 / MI_DELAY (mi);
854         ctx->nextblink          = calc_interval_frames
855                                    (ctx, g_blinkidle_min, g_blinkidle_max);
856         ctx->blink.drawfunc     = NULL;
857         ctx->blink.noise        = malloc (sizeof (uint32_t) * g_blades);
858         if (!ctx->blink.noise) {
859                 fprintf (stderr, "Can't allocate noise array.\n");
860                 exit (1);
861         }
862
863         gen_blade_arcs (ctx);
864         gen_rings (ctx);
865
866         for (i = NSPINNERS;  --i >= 0; ) {
867                 spinnerstate *ss = &ctx->spinners[i];
868
869                 ss->rot         = 0.0;
870                 ss->rotcount    = -1;
871
872                 /*  Establish rotation  */
873                 random_blade_rot (ctx, ss);
874
875                 /*
876                  * Establish color cycling path and rate.  Rate avoids zero.
877                  */
878                 ss->ncolors = 128;
879                 ss->colorinc = (random() & ((2 << COLORIDX_SHF) - 1))
880                              - (1 << COLORIDX_SHF);
881                 if (ss->colorinc >= 0)
882                         ++ss->colorinc;
883
884                 ss->colors = (XColor *) calloc (ss->ncolors, sizeof (XColor));
885                 if (!ss->colors) {
886                         fprintf (stderr,
887                                  "Can't allocate XColors for spinner %d.\n",
888                                  i);
889                         exit (1);
890                 }
891                 make_smooth_colormap (0, 0, 0,
892                                       ss->colors, &ss->ncolors,
893                                       False, 0, False);
894                 ss->ncolors <<= COLORIDX_SHF;
895
896                 /*
897                  * Create blades.
898                  */
899                 ss->nblades     = g_blades;
900                 ss->bladeidx    = malloc (sizeof (bladestate) * g_blades);
901                 if (!ss->bladeidx) {
902                         fprintf (stderr, "Can't allocate blades.\n");
903                         exit (1);
904                 }
905                 for (n = g_blades;  --n >= 0; ) {
906                         /*
907                          * Establish blade radii.  Can't be equal.  Ensure
908                          * outer > inner.
909                          */
910                         do {
911                                 ss->bladeidx[n].outer = random() & 7;
912                                 ss->bladeidx[n].inner = random() & 7;
913                         } while (ss->bladeidx[n].outer ==
914                                  ss->bladeidx[n].inner);
915
916                         if (ss->bladeidx[n].outer < ss->bladeidx[n].inner) {
917                                 uint8_t tmp;
918
919                                 tmp = ss->bladeidx[n].outer;
920                                 ss->bladeidx[n].outer = ss->bladeidx[n].inner;
921                                 ss->bladeidx[n].inner = tmp;
922                         }
923                 }
924         }
925 }
926
927 static void
928 free_lockward (ModeInfo *mi)
929 {
930         lockward_context        *ctx = &g_ctx[MI_SCREEN (mi)];
931         int i;
932
933         if (!ctx->glx_context)
934                 return;
935
936         glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
937                         *(ctx->glx_context));
938
939         if (ctx->blink.noise)
940                 free (ctx->blink.noise);
941         if (glIsList (ctx->rings))
942                 glDeleteLists (ctx->rings, NRADII - 1);
943         /* if (glIsList (ctx->blades_outer))
944                 glDeleteLists (ctx->blades_outer, NRADII);
945         if (glIsList (ctx->blades_inner))
946                 glDeleteLists (ctx->blades_inner, NRADII); */
947
948         for (i = NSPINNERS;  --i >= 0; ) {
949                 spinnerstate *ss = &ctx->spinners[i];
950
951                 if (ss->colors)
952                         free (ss->colors);
953                 if (ss->bladeidx)
954                         free (ss->bladeidx);
955         }
956 }
957
958
959 XSCREENSAVER_MODULE ("Lockward", lockward)
960
961 #endif /* USE_GL */
962
963 /*  vim:se ts=8 sts=8 sw=8:  */