From http://www.jwz.org/xscreensaver/xscreensaver-5.38.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 release_lockward        0
43
44
45 #define NUMOF(x)        (sizeof ((x)) / sizeof ((*x)))
46
47 #define NBLADES         12
48 #define NSPINNERS       4
49 #define NRADII          8
50 #define COLORIDX_SHF    4
51 #define SUBDIV          6
52
53 #undef countof
54 #define countof(x) (sizeof((x))/sizeof((*x)))
55
56
57 /***************************************************************************
58  * Structure definitions.
59  */
60 struct lockward_context;                        /*  Forward declaration.  */
61
62 #define int8_t   char
63 #define int16_t  short
64 #define int32_t  int
65 #define uint8_t  unsigned char
66 #define uint16_t unsigned short
67 #define uint32_t unsigned int
68
69 typedef struct bladestate {
70         uint8_t         outer, inner;   /*  Radii  */
71 } bladestate;
72
73 typedef struct spinnerstate {
74         GLfloat         rot;    /*  Terminal rotation after count expires */
75         GLfloat         rotinc; /*  Per-frame increment to rot.  */
76         XColor          *colors;
77         bladestate      *bladeidx;
78         int             ncolors;        /*  n.4 fixed-point  */
79         int             ccolor;         /*  n.4 fixed-point  */
80         int             colorinc;       /*  n.4 fixed-point  */
81         int             rotcount;
82         uint8_t         nblades;
83 } spinnerstate;
84
85 typedef struct blinkstate {
86         int             (*drawfunc) (struct lockward_context *ctx,
87                                      struct blinkstate *bs);
88         uint32_t        *noise; /*  For draw_blink_segment_scatter()  */
89         GLfloat         color[4];
90         uint32_t        val;
91         int16_t         dwell;  /*  <0: sharp  >0: decay  */
92         int16_t         dwellcnt;
93         uint8_t         type;
94         int8_t          counter;
95         int8_t          direction;
96         int8_t          radius;
97 } blinkstate;
98
99 enum blinktype {
100         BTYPE_RADIAL_SINGLE = 0,
101         BTYPE_RADIAL_RANDOM,
102         BTYPE_RADIAL_SEQ,
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,
110         MAX_BTYPE
111 };
112
113 typedef struct { GLfloat x,y,z; } XYZ;
114
115 typedef struct lockward_context {
116         GLXContext      *glx_context;
117
118         spinnerstate    spinners[NSPINNERS];
119         blinkstate      blink;
120
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];
127
128         GLuint          rings;
129         Bool            blendmode;
130         int             nextblink;
131         int             fps;
132
133 } lockward_context;
134
135
136 /***************************************************************************
137  * Prototypes.
138  */
139 ENTRYPOINT void free_lockward (ModeInfo *mi);
140
141
142 /***************************************************************************
143  * Global variables.
144  */
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,
149                         g_rotateidle_max;
150 static int              g_blinkidle_min,
151                         g_blinkidle_max;
152 static int              g_blinkdwell_min,
153                         g_blinkdwell_max;
154
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"
162
163
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 },
173 };
174
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 },
183 };
184
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." },
193 };
194
195 ENTRYPOINT ModeSpecOpt lockward_opts = {
196         NUMOF(opts), opts, NUMOF(vars), vars, desc
197 };
198
199
200 /***************************************************************************
201  * Window management.
202  */
203 ENTRYPOINT void
204 reshape_lockward (ModeInfo *mi, int width, int height)
205 {
206         lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
207         GLfloat h = (GLfloat) height / (GLfloat) width;
208         int y = 0;
209
210         if (width > height * 5) {   /* tiny window: show middle */
211           height = width * 9/16;
212           y = -height/2;
213           h = height / (GLfloat) width;
214         }
215
216         glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
217                         *(ctx->glx_context));
218
219         glViewport (0, y, (GLint) width, (GLint) height);
220
221         glMatrixMode (GL_PROJECTION);
222         glLoadIdentity ();
223         if (height > width)
224                 glOrtho (-8.0, 8.0, -8.0 * h, 8.0 * h, -1, 1);
225         else
226                 glOrtho (-8.0 / h, 8.0 / h, -8.0, 8.0, -1, 1);
227
228         glMatrixMode (GL_MODELVIEW);
229 }
230
231 ENTRYPOINT Bool
232 lockward_handle_event (ModeInfo *mi, XEvent *event)
233 {
234         lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
235
236         if (event->xany.type == KeyPress) {
237                 KeySym  keysym;
238                 char    c = 0;
239
240                 XLookupString (&event->xkey, &c, 1, &keysym, 0);
241                 if (c == ' ' || c == '\t') {
242                         ctx->blendmode ^= 1;
243                         return True;
244                 }
245         }
246
247         return False;
248 }
249
250
251 /***************************************************************************
252  * "Blade" routines.
253  */
254 static void
255 random_blade_rot (lockward_context *ctx, struct spinnerstate *ss)
256 {
257         /*
258          * The circle is divided up in to g_blades divisions.  The idea here
259          * is to rotate to an exact division point.
260          *
261          * The target rotation is computed via random numbers.
262          *
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.
266          *
267          * The time value is converted into frames, and a per-frame rotation
268          * is computed.
269          *
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).
275          */
276         GLfloat d;
277         int     dist;
278
279         dist = random() % g_blades + 1;
280
281         ss->rotcount = random() % (6 * dist * ctx->fps - ctx->fps)
282                      + ctx->fps;
283
284         if (random() & 4)
285                 dist = -dist;
286         d = dist * 360.0 / (GLfloat) g_blades;
287         ss->rot += d;
288         ss->rotinc = d / (GLfloat) ss->rotcount;
289 }
290
291
292 /*
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.
298  *
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.
304  */
305
306 static void
307 gen_blade_arcs (lockward_context *ctx)
308 {
309         GLfloat here, there, step;
310         int     i, n;
311
312         here = 0;
313         there = M_PI * 2.0 / g_blades;
314         step = there / SUBDIV;
315         here -= SUBDIV * step / 2.0;
316
317         /*
318          * Build outer blade arcs.
319          * Start at left side of outer radius.  Strike all its vertices.
320          */
321         for (n = 0;  n < NRADII;  ++n) {
322           /* glNewList (ctx->blades_outer + n, GL_COMPILE); */
323           XYZ *a = ctx->points_outer[n];
324           int j = 0;
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);
330             a[j].z = 0;
331             j++;
332           }
333           if (j != SUBDIV+1) abort();
334           /* glEndList (); */
335         }
336
337         /*
338          * Build inner blade arcs.
339          * Move to inner radius, strike all vertices in opposite order.
340          */
341         for (n = 0;  n < NRADII;  ++n) {
342           /* glNewList (ctx->blades_inner + n, GL_COMPILE); */
343           XYZ *a = ctx->points_inner[n];
344           int j = 0;
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);
350             a[j].z = 0;
351             j++;
352           }
353           if (j != SUBDIV+1) abort();
354           /* glEndList (); */
355         }
356 }
357
358 static void
359 gen_rings (lockward_context *ctx)
360 {
361         GLfloat step;
362         int     i, n;
363
364         step = M_PI * 2.0 / (g_blades * SUBDIV);
365
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);
374                 }
375                 glEnd();
376                 glEndList ();
377         }
378 }
379
380
381 /***************************************************************************
382  * "Blink" routines.
383  */
384 static int
385 calc_interval_frames (lockward_context *ctx, int min, int max)
386 {
387         /*
388          * Compute random interval between min and max milliseconds.
389          * Returned value is in frames.
390          */
391         register int i;
392
393         i = min;
394         if (max > min)
395                 i += random() % (max - min);
396
397         return i * ctx->fps / 1000;
398 }
399
400 static void
401 set_alpha_by_dwell (struct blinkstate *bs)
402 {
403         if (bs->dwell > 0)
404                 bs->color[3] = (GLfloat) bs->dwellcnt / (GLfloat) bs->dwell;
405         else
406                 bs->color[3] = bs->dwellcnt > (-bs->dwell >> 2)  ?  1.0  : 0.0;
407 }
408
409
410 static void
411 draw_blink_blade (lockward_context *ctx, int inner, int outer,
412                   Bool begin_p)
413 {
414   int i;
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);
421
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();
428 }
429
430 static int
431 draw_blink_radial_random (lockward_context *ctx, struct blinkstate *bs)
432 {
433         int i;
434
435         /*
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.
438          */
439         if (bs->dwellcnt < 0) {
440                 if (bs->counter <= 0) {
441                         bs->drawfunc = NULL;
442                         return 0;
443                 }
444
445                 /*
446                  * Find available blade.  Potentially very slow, depending on
447                  * how unlucky we are.
448                  */
449                 do {
450                         i = random() % g_blades;
451                 } while (bs->val & (1 << i));
452                 bs->val |= (1 << i);    /*  Mark as used.  */
453                 bs->direction = i;
454                 if ((bs->dwellcnt = bs->dwell) < 0)
455                         bs->dwellcnt = -bs->dwellcnt;
456                 
457                 if (    bs->type == BTYPE_SEGMENT_SINGLE
458                     ||  bs->type == BTYPE_SEGMENT_RANDOM)
459                         bs->radius = random() % (NRADII - 1);
460
461                 --bs->counter;
462         }
463
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);
468         if (bs->radius >= 0)
469                 draw_blink_blade (ctx, bs->radius, bs->radius + 1, True);
470         else
471                 draw_blink_blade (ctx, 0, NRADII - 1, True);
472
473         --bs->dwellcnt;
474
475         return SUBDIV + SUBDIV;
476 }
477
478 static int
479 draw_blink_radial_sequential (lockward_context *ctx, struct blinkstate *bs)
480 {
481         if (bs->dwellcnt < 0) {
482                 if (bs->counter <= 0) {
483                         bs->drawfunc = NULL;
484                         return 0;
485                 }
486                 if ((bs->dwellcnt = bs->dwell) < 0)
487                         bs->dwellcnt = -bs->dwellcnt;
488                 --bs->counter;
489         }
490
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,
496                    0, 0, 1);
497         draw_blink_blade (ctx, 0, NRADII - 1, True);
498
499         --bs->dwellcnt;
500
501         return SUBDIV + SUBDIV;
502 }
503
504 static int
505 draw_blink_radial_doubleseq (lockward_context *ctx, struct blinkstate *bs)
506 {
507         int polys;
508
509         if (bs->dwellcnt < 0) {
510                 if (bs->counter <= 0) {
511                         bs->drawfunc = NULL;
512                         return 0;
513                 }
514                 if ((bs->dwellcnt = bs->dwell) < 0)
515                         bs->dwellcnt = -bs->dwellcnt;
516                 --bs->counter;
517         }
518
519         set_alpha_by_dwell (bs);
520         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
521         glColor4fv (bs->color);
522
523         glPushMatrix ();
524         glRotatef (((int) bs->val + bs->counter) * 360.0 / (GLfloat) g_blades,
525                    0, 0, 1);
526         draw_blink_blade (ctx, 0, NRADII - 1, True);
527         glPopMatrix ();
528         polys = SUBDIV + SUBDIV;
529
530         if (bs->counter  &&  bs->counter < g_blades / 2) {
531                 glRotatef (((int) bs->val - bs->counter)
532                             * 360.0 / (GLfloat) g_blades,
533                            0, 0, 1);
534                 draw_blink_blade (ctx, 0, NRADII - 1, True);
535                 polys += SUBDIV + SUBDIV;
536         }
537
538         --bs->dwellcnt;
539
540         return polys;
541 }
542
543 static int
544 draw_blink_concentric_random (lockward_context *ctx, struct blinkstate *bs)
545 {
546         int i;
547
548         if (bs->dwellcnt < 0) {
549                 if (bs->counter <= 0) {
550                         bs->drawfunc = NULL;
551                         return 0;
552                 }
553
554                 do {
555                         i = random() % (NRADII - 1);
556                 } while (bs->val & (1 << i));
557                 bs->val |= (1 << i);
558                 bs->direction = i;
559                 if ((bs->dwellcnt = bs->dwell) < 0)
560                         bs->dwellcnt = -bs->dwellcnt;
561
562                 --bs->counter;
563         }
564
565         set_alpha_by_dwell (bs);
566         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
567         glColor4fv (bs->color);
568         glCallList (ctx->rings + bs->direction);
569
570         --bs->dwellcnt;
571
572         return g_blades * SUBDIV * 2;
573 }
574
575 static int
576 draw_blink_concentric_sequential (lockward_context *ctx, struct blinkstate *bs)
577 {
578         if (bs->dwellcnt < 0) {
579                 if (bs->counter <= 0) {
580                         bs->drawfunc = NULL;
581                         return 0;
582                 }
583                 if ((bs->dwellcnt = bs->dwell) < 0)
584                         bs->dwellcnt = -bs->dwellcnt;
585                 --bs->counter;
586         }
587
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);
593         else
594                 glCallList (ctx->rings + bs->counter);
595
596         --bs->dwellcnt;
597
598         return g_blades * SUBDIV * 2;
599 }
600
601 static int
602 draw_blink_segment_scatter (lockward_context *ctx, struct blinkstate *bs)
603 {
604         int i, polys = 0;
605
606         if (bs->dwellcnt < 0) {
607                 if (bs->counter <= 0) {
608                         bs->drawfunc = NULL;
609                         return 0;
610                 }
611
612                 /*
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.)
615                  */
616                 for (i = g_blades;  --i >= 0; )
617                         bs->noise[i] = random() & random()
618                                      & ((1 << (NRADII - 1)) - 1);
619
620                 if ((bs->dwellcnt = bs->dwell) < 0)
621                         bs->dwellcnt = -bs->dwellcnt;
622                 --bs->counter;
623         }
624
625         set_alpha_by_dwell (bs);
626         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
627         glColor4fv (bs->color);
628
629         for (i = g_blades;  --i >= 0; ) {
630                 register uint32_t       bits;
631                 int                     inner, outer;
632
633                 /*
634                  * Find consecutive runs of 1 bits.  Keep going until we run
635                  * out of them.
636                  */
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);
642
643                         glPushMatrix ();
644                         glRotatef (i * 360.0 / (GLfloat) g_blades, 0, 0, 1);
645                         draw_blink_blade (ctx, inner, outer, True);
646                         glPopMatrix ();
647
648                         polys += SUBDIV + SUBDIV;
649                 }
650         }
651
652         --bs->dwellcnt;
653
654         return polys;
655 }
656
657 static void
658 random_blink (lockward_context *ctx, struct blinkstate *bs)
659 {
660         bs->color[0]    =
661         bs->color[1]    =
662         bs->color[2]    =
663         bs->color[3]    = 1.0;
664         bs->dwellcnt    = -1;
665         bs->radius      = -1;
666         bs->dwell       = calc_interval_frames
667                           (ctx, g_blinkdwell_min, g_blinkdwell_max);
668         if (random() & 2)
669                 bs->dwell = -bs->dwell;
670
671         bs->type = random() % MAX_BTYPE;
672
673         switch (bs->type) {
674         case BTYPE_RADIAL_SINGLE:
675         case BTYPE_SEGMENT_SINGLE:
676                 bs->drawfunc = draw_blink_radial_random;
677                 bs->val = 0;
678                 bs->counter = 1;
679                 break;
680         case BTYPE_RADIAL_RANDOM:
681         case BTYPE_SEGMENT_RANDOM:
682                 bs->drawfunc = draw_blink_radial_random;
683                 bs->val = 0;
684                 bs->counter = g_blades;
685                 break;
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;
691                 break;
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;
696                 break;
697         case BTYPE_CONCENTRIC_SINGLE:
698                 bs->drawfunc = draw_blink_concentric_random;
699                 bs->val = 0;
700                 bs->counter = 1;
701                 break;
702         case BTYPE_CONCENTRIC_RANDOM:
703                 bs->drawfunc = draw_blink_concentric_random;
704                 bs->val = 0;
705                 bs->counter = NRADII - 1;
706                 break;
707         case BTYPE_CONCENTRIC_SEQ:
708                 bs->drawfunc = draw_blink_concentric_sequential;
709                 bs->direction = random() & 8  ?  1 :  -1;
710                 bs->counter = NRADII - 1;
711                 break;
712         case BTYPE_SEGMENT_SCATTER:
713                 bs->drawfunc = draw_blink_segment_scatter;
714                 bs->counter = random() % (g_blades / 2) + (g_blades / 2) + 1;
715                 break;
716         }
717 }
718
719
720 /***************************************************************************
721  * Main rendering routine.
722  */
723 ENTRYPOINT void
724 draw_lockward (ModeInfo *mi)
725 {
726         lockward_context        *ctx = &g_ctx[MI_SCREEN (mi)];
727         spinnerstate    *ss;
728         Display         *dpy = MI_DISPLAY(mi);
729         Window          window = MI_WINDOW(mi);
730         int             i, n;
731
732         GLfloat scolor[4] = {0.0, 0.0, 0.0, 0.5};
733
734         if (!ctx->glx_context)
735                 return;
736
737         glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi), *(ctx->glx_context));
738
739
740         glClear (GL_COLOR_BUFFER_BIT);
741
742         if (ctx->blendmode)
743                 glBlendFunc (GL_ONE, GL_ONE);
744         else
745                 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
746
747         glPushMatrix ();
748         glLoadIdentity ();
749
750         mi->polygon_count = 0;
751
752         for (n = NSPINNERS;  --n >= 0; ) {
753                 ss = &ctx->spinners[n];
754
755                 /*  Set color.  */
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;
760                 glColor4fv (scolor);
761
762                 glPushMatrix ();
763                 glRotatef (ss->rot - ss->rotcount * ss->rotinc, 0, 0, 1);
764                 for (i = ss->nblades;  --i >= 0; ) {
765                   glPushMatrix ();
766                   glRotatef (360.0 * i / ss->nblades, 0, 0, 1);
767
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,
774                                     False);
775                   glEnd ();
776
777                   glPopMatrix ();
778                   mi->polygon_count += SUBDIV + SUBDIV;
779                 }
780                 glPopMatrix ();
781
782                 /*  Advance rotation.  */
783                 if (ss->rotcount) {
784                         if (ss->rotcount > 0)
785                                 --ss->rotcount;
786                 } else {
787                         if (ss->rotinc == 0.0)
788                                 random_blade_rot (ctx, ss);
789                         else {
790                                 /*  Compute # of ticks to sit idle.  */
791                                 ss->rotinc = 0.0;
792                                 ss->rotcount =
793                                  calc_interval_frames (ctx,
794                                                        g_rotateidle_min,
795                                                        g_rotateidle_max);
796                         }
797                 }
798
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;
804         }
805
806         if (g_blink_p) {
807                 if (ctx->blink.drawfunc) {
808                         mi->polygon_count +=
809                          ctx->blink.drawfunc (ctx, &ctx->blink);
810                 } else {
811                         if (ctx->nextblink > 0)
812                                 --ctx->nextblink;
813                         else {
814                                 /* Compute # of frames for blink idle time. */
815                                 ctx->nextblink =
816                                  calc_interval_frames (ctx,
817                                                        g_blinkidle_min,
818                                                        g_blinkidle_max);
819                                 random_blink (ctx, &ctx->blink);
820                         }
821                 }
822         }
823         glPopMatrix ();
824
825         if (MI_IS_FPS (mi)) do_fps (mi);
826         glFinish();
827
828         glXSwapBuffers (dpy, window);
829 }
830
831
832 /***************************************************************************
833  * Initialization/teardown.
834  */
835 ENTRYPOINT void 
836 init_lockward (ModeInfo *mi)
837 {
838         lockward_context        *ctx;
839         int             i, n;
840
841         MI_INIT (mi, g_ctx);
842         ctx = &g_ctx[MI_SCREEN (mi)];
843
844         ctx->glx_context = init_GL (mi);
845
846         reshape_lockward (mi, MI_WIDTH (mi), MI_HEIGHT (mi));
847
848         glEnable (GL_CULL_FACE);
849         glEnable (GL_BLEND);
850         glDisable (GL_DEPTH_TEST);
851
852         glShadeModel (GL_FLAT);
853         glFrontFace (GL_CW);
854
855         /* ctx->blades_outer    = glGenLists (NRADII); */
856         /* ctx->blades_inner    = glGenLists (NRADII); */
857         ctx->rings              = glGenLists (NRADII - 1);
858         ctx->blendmode          = 0;
859 /* WTF?         ctx->fps                = 1000000 / MI_DELAY (mi); */
860         ctx->fps = 60;
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");
867                 exit (1);
868         }
869
870         gen_blade_arcs (ctx);
871         gen_rings (ctx);
872
873         for (i = NSPINNERS;  --i >= 0; ) {
874                 spinnerstate *ss = &ctx->spinners[i];
875
876                 ss->rot         = 0.0;
877                 ss->rotcount    = -1;
878
879                 /*  Establish rotation  */
880                 random_blade_rot (ctx, ss);
881
882                 /*
883                  * Establish color cycling path and rate.  Rate avoids zero.
884                  */
885                 ss->ncolors = 128;
886                 ss->colorinc = (random() & ((2 << COLORIDX_SHF) - 1))
887                              - (1 << COLORIDX_SHF);
888                 if (ss->colorinc >= 0)
889                         ++ss->colorinc;
890
891                 ss->colors = (XColor *) calloc (ss->ncolors, sizeof (XColor));
892                 if (!ss->colors) {
893                         fprintf (stderr,
894                                  "Can't allocate XColors for spinner %d.\n",
895                                  i);
896                         exit (1);
897                 }
898                 make_smooth_colormap (0, 0, 0,
899                                       ss->colors, &ss->ncolors,
900                                       False, 0, False);
901                 ss->ncolors <<= COLORIDX_SHF;
902
903                 /*
904                  * Create blades.
905                  */
906                 ss->nblades     = g_blades;
907                 ss->bladeidx    = malloc (sizeof (bladestate) * g_blades);
908                 if (!ss->bladeidx) {
909                         fprintf (stderr, "Can't allocate blades.\n");
910                         exit (1);
911                 }
912                 for (n = g_blades;  --n >= 0; ) {
913                         /*
914                          * Establish blade radii.  Can't be equal.  Ensure
915                          * outer > inner.
916                          */
917                         do {
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);
922
923                         if (ss->bladeidx[n].outer < ss->bladeidx[n].inner) {
924                                 uint8_t tmp;
925
926                                 tmp = ss->bladeidx[n].outer;
927                                 ss->bladeidx[n].outer = ss->bladeidx[n].inner;
928                                 ss->bladeidx[n].inner = tmp;
929                         }
930                 }
931         }
932 }
933
934 ENTRYPOINT void
935 free_lockward (ModeInfo *mi)
936 {
937         lockward_context        *ctx = &g_ctx[MI_SCREEN (mi)];
938         int i;
939
940         if (!ctx->glx_context)
941                 return;
942
943         glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
944                         *(ctx->glx_context));
945
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); */
954
955         for (i = NSPINNERS;  --i >= 0; ) {
956                 spinnerstate *ss = &ctx->spinners[i];
957
958                 if (ss->colors)
959                         free (ss->colors);
960                 if (ss->bladeidx)
961                         free (ss->bladeidx);
962         }
963 }
964
965
966 XSCREENSAVER_MODULE ("Lockward", lockward)
967
968 #endif /* USE_GL */
969
970 /*  vim:se ts=8 sts=8 sw=8:  */