From http://www.jwz.org/xscreensaver/xscreensaver-5.31.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
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 static void free_lockward (lockward_context *ctx);
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         GLfloat h = (GLfloat) height / (GLfloat) width;
207
208         glViewport (0, 0, (GLint) width, (GLint) height);
209
210         glMatrixMode (GL_PROJECTION);
211         glLoadIdentity ();
212         if (height > width)
213                 glOrtho (-8.0, 8.0, -8.0 * h, 8.0 * h, -1, 1);
214         else
215                 glOrtho (-8.0 / h, 8.0 / h, -8.0, 8.0, -1, 1);
216
217         glMatrixMode (GL_MODELVIEW);
218 }
219
220 ENTRYPOINT Bool
221 lockward_handle_event (ModeInfo *mi, XEvent *event)
222 {
223         lockward_context *ctx = &g_ctx[MI_SCREEN (mi)];
224
225         if (event->xany.type == KeyPress) {
226                 KeySym  keysym;
227                 char    c = 0;
228
229                 XLookupString (&event->xkey, &c, 1, &keysym, 0);
230                 if (c == ' ' || c == '\t') {
231                         ctx->blendmode ^= 1;
232                         return True;
233                 }
234         }
235
236         return False;
237 }
238
239
240 /***************************************************************************
241  * "Blade" routines.
242  */
243 static void
244 random_blade_rot (lockward_context *ctx, struct spinnerstate *ss)
245 {
246         /*
247          * The circle is divided up in to g_blades divisions.  The idea here
248          * is to rotate to an exact division point.
249          *
250          * The target rotation is computed via random numbers.
251          *
252          * The time it takes to get there is a maximum of six seconds per
253          * division, and a minimum of one second (no matter how far away it
254          * is), and is selected by random numbers.
255          *
256          * The time value is converted into frames, and a per-frame rotation
257          * is computed.
258          *
259          * During rendering, we approach the target rotation by subtracting
260          * from it the per-frame rotation times the number of outstanding
261          * ticks.  Doing it this way means we'll hit the target rotation
262          * exactly, without low-order errors creeping in to the values (at
263          * least not nearly as quickly).
264          */
265         GLfloat d;
266         int     dist;
267
268         dist = random() % g_blades + 1;
269
270         ss->rotcount = random() % (6 * dist * ctx->fps - ctx->fps)
271                      + ctx->fps;
272
273         if (random() & 4)
274                 dist = -dist;
275         d = dist * 360.0 / (GLfloat) g_blades;
276         ss->rot += d;
277         ss->rotinc = d / (GLfloat) ss->rotcount;
278 }
279
280
281 /*
282  * A "blade" is pie-wedge shaped flat thing that is rotated around where the
283  * apex is/would be.  Initially envisioned as 1/12th of a circle, but that
284  * could be configurable.  The inner and outer edges are rounded off using
285  * six subdivisions so that, when multiple blades are assembled, it looks
286  * more like a circle and less like a polygon.
287  *
288  * The blade is assembled as a tri-fan.  It is oriented centered at 3
289  * o'clock.  The blade is composed of two display lists -- arcs, essentially
290  * -- the outer and the inner one.  The outer one *must* be called before
291  * the inner one, or the blade clockwise-ness will be wrong, and become
292  * invisible.  Arcs of various radii are compiled.
293  */
294
295 static void
296 gen_blade_arcs (lockward_context *ctx)
297 {
298         GLfloat here, there, step;
299         int     i, n;
300
301         here = 0;
302         there = M_PI * 2.0 / g_blades;
303         step = there / SUBDIV;
304         here -= SUBDIV * step / 2.0;
305
306         /*
307          * Build outer blade arcs.
308          * Start at left side of outer radius.  Strike all its vertices.
309          */
310         for (n = 0;  n < NRADII;  ++n) {
311           /* glNewList (ctx->blades_outer + n, GL_COMPILE); */
312           XYZ *a = ctx->points_outer[n];
313           int j = 0;
314           for (i = SUBDIV;  i >= 0;  --i) {
315             /* glVertex3f (cos (here + step * i) * (n + 1.0),
316                            sin (here + step * i) * (n + 1.0), 0); */
317             a[j].x = cos (here + step * i) * (n + 1.0);
318             a[j].y = sin (here + step * i) * (n + 1.0);
319             a[j].z = 0;
320             j++;
321           }
322           if (j != SUBDIV+1) abort();
323           /* glEndList (); */
324         }
325
326         /*
327          * Build inner blade arcs.
328          * Move to inner radius, strike all vertices in opposite order.
329          */
330         for (n = 0;  n < NRADII;  ++n) {
331           /* glNewList (ctx->blades_inner + n, GL_COMPILE); */
332           XYZ *a = ctx->points_inner[n];
333           int j = 0;
334           for (i = 0;  i <= SUBDIV;  ++i) {
335             /* glVertex3f (cos (here + step * i) * (n + 1.0),
336                            sin (here + step * i) * (n + 1.0), 0); */
337             a[j].x = cos (here + step * i) * (n + 1.0);
338             a[j].y = sin (here + step * i) * (n + 1.0);
339             a[j].z = 0;
340             j++;
341           }
342           if (j != SUBDIV+1) abort();
343           /* glEndList (); */
344         }
345 }
346
347 static void
348 gen_rings (lockward_context *ctx)
349 {
350         GLfloat step;
351         int     i, n;
352
353         step = M_PI * 2.0 / (g_blades * SUBDIV);
354
355         for (n = 0;  n < NRADII - 1;  ++n) {
356                 glNewList (ctx->rings + n, GL_COMPILE);
357                 glBegin (GL_TRIANGLE_STRIP);
358                 for (i = g_blades * SUBDIV;  i >= 0;  --i) {
359                         glVertex3f (cos (step * i) * (n + 1.0),
360                                     sin (step * i) * (n + 1.0), 0);
361                         glVertex3f (cos (step * i) * (n + 2.0),
362                                     sin (step * i) * (n + 2.0), 0);
363                 }
364                 glEnd();
365                 glEndList ();
366         }
367 }
368
369
370 /***************************************************************************
371  * "Blink" routines.
372  */
373 static int
374 calc_interval_frames (lockward_context *ctx, int min, int max)
375 {
376         /*
377          * Compute random interval between min and max milliseconds.
378          * Returned value is in frames.
379          */
380         register int i;
381
382         i = min;
383         if (max > min)
384                 i += random() % (max - min);
385
386         return i * ctx->fps / 1000;
387 }
388
389 static void
390 set_alpha_by_dwell (struct blinkstate *bs)
391 {
392         if (bs->dwell > 0)
393                 bs->color[3] = (GLfloat) bs->dwellcnt / (GLfloat) bs->dwell;
394         else
395                 bs->color[3] = bs->dwellcnt > (-bs->dwell >> 2)  ?  1.0  : 0.0;
396 }
397
398
399 static void
400 draw_blink_blade (lockward_context *ctx, int inner, int outer,
401                   Bool begin_p)
402 {
403   int i;
404   if (begin_p) glBegin (GL_TRIANGLE_FAN);
405   /* glCallList (ctx->blades_outer + outer); */
406   for (i = 0; i < countof(*ctx->points_outer); i++)
407     glVertex3f(ctx->points_outer[outer][i].x,
408                ctx->points_outer[outer][i].y,
409                ctx->points_outer[outer][i].z);
410
411   /* glCallList (ctx->blades_inner + inner); */
412   for (i = 0; i < countof(*ctx->points_inner); i++)
413     glVertex3f(ctx->points_inner[inner][i].x,
414                ctx->points_inner[inner][i].y,
415                ctx->points_inner[inner][i].z);
416   if (begin_p) glEnd();
417 }
418
419 static int
420 draw_blink_radial_random (lockward_context *ctx, struct blinkstate *bs)
421 {
422         int i;
423
424         /*
425          * There is no sense of direction in a random sweep, so re-use the
426          * 'direction' field to hold the current blade we're messing with.
427          */
428         if (bs->dwellcnt < 0) {
429                 if (bs->counter <= 0) {
430                         bs->drawfunc = NULL;
431                         return 0;
432                 }
433
434                 /*
435                  * Find available blade.  Potentially very slow, depending on
436                  * how unlucky we are.
437                  */
438                 do {
439                         i = random() % g_blades;
440                 } while (bs->val & (1 << i));
441                 bs->val |= (1 << i);    /*  Mark as used.  */
442                 bs->direction = i;
443                 if ((bs->dwellcnt = bs->dwell) < 0)
444                         bs->dwellcnt = -bs->dwellcnt;
445                 
446                 if (    bs->type == BTYPE_SEGMENT_SINGLE
447                     ||  bs->type == BTYPE_SEGMENT_RANDOM)
448                         bs->radius = random() % (NRADII - 1);
449
450                 --bs->counter;
451         }
452
453         set_alpha_by_dwell (bs);
454         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
455         glColor4fv (bs->color);
456         glRotatef (bs->direction * 360.0 / (GLfloat) g_blades, 0, 0, 1);
457         if (bs->radius >= 0)
458                 draw_blink_blade (ctx, bs->radius, bs->radius + 1, True);
459         else
460                 draw_blink_blade (ctx, 0, NRADII - 1, True);
461
462         --bs->dwellcnt;
463
464         return SUBDIV + SUBDIV;
465 }
466
467 static int
468 draw_blink_radial_sequential (lockward_context *ctx, struct blinkstate *bs)
469 {
470         if (bs->dwellcnt < 0) {
471                 if (bs->counter <= 0) {
472                         bs->drawfunc = NULL;
473                         return 0;
474                 }
475                 if ((bs->dwellcnt = bs->dwell) < 0)
476                         bs->dwellcnt = -bs->dwellcnt;
477                 --bs->counter;
478         }
479
480         set_alpha_by_dwell (bs);
481         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
482         glColor4fv (bs->color);
483         glRotatef ((bs->counter * bs->direction + (int) bs->val)
484                     * 360.0 / (GLfloat) g_blades,
485                    0, 0, 1);
486         draw_blink_blade (ctx, 0, NRADII - 1, True);
487
488         --bs->dwellcnt;
489
490         return SUBDIV + SUBDIV;
491 }
492
493 static int
494 draw_blink_radial_doubleseq (lockward_context *ctx, struct blinkstate *bs)
495 {
496         int polys;
497
498         if (bs->dwellcnt < 0) {
499                 if (bs->counter <= 0) {
500                         bs->drawfunc = NULL;
501                         return 0;
502                 }
503                 if ((bs->dwellcnt = bs->dwell) < 0)
504                         bs->dwellcnt = -bs->dwellcnt;
505                 --bs->counter;
506         }
507
508         set_alpha_by_dwell (bs);
509         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
510         glColor4fv (bs->color);
511
512         glPushMatrix ();
513         glRotatef (((int) bs->val + bs->counter) * 360.0 / (GLfloat) g_blades,
514                    0, 0, 1);
515         draw_blink_blade (ctx, 0, NRADII - 1, True);
516         glPopMatrix ();
517         polys = SUBDIV + SUBDIV;
518
519         if (bs->counter  &&  bs->counter < g_blades / 2) {
520                 glRotatef (((int) bs->val - bs->counter)
521                             * 360.0 / (GLfloat) g_blades,
522                            0, 0, 1);
523                 draw_blink_blade (ctx, 0, NRADII - 1, True);
524                 polys += SUBDIV + SUBDIV;
525         }
526
527         --bs->dwellcnt;
528
529         return polys;
530 }
531
532 static int
533 draw_blink_concentric_random (lockward_context *ctx, struct blinkstate *bs)
534 {
535         int i;
536
537         if (bs->dwellcnt < 0) {
538                 if (bs->counter <= 0) {
539                         bs->drawfunc = NULL;
540                         return 0;
541                 }
542
543                 do {
544                         i = random() % (NRADII - 1);
545                 } while (bs->val & (1 << i));
546                 bs->val |= (1 << i);
547                 bs->direction = i;
548                 if ((bs->dwellcnt = bs->dwell) < 0)
549                         bs->dwellcnt = -bs->dwellcnt;
550
551                 --bs->counter;
552         }
553
554         set_alpha_by_dwell (bs);
555         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
556         glColor4fv (bs->color);
557         glCallList (ctx->rings + bs->direction);
558
559         --bs->dwellcnt;
560
561         return g_blades * SUBDIV * 2;
562 }
563
564 static int
565 draw_blink_concentric_sequential (lockward_context *ctx, struct blinkstate *bs)
566 {
567         if (bs->dwellcnt < 0) {
568                 if (bs->counter <= 0) {
569                         bs->drawfunc = NULL;
570                         return 0;
571                 }
572                 if ((bs->dwellcnt = bs->dwell) < 0)
573                         bs->dwellcnt = -bs->dwellcnt;
574                 --bs->counter;
575         }
576
577         set_alpha_by_dwell (bs);
578         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
579         glColor4fv (bs->color);
580         if (bs->direction > 0)
581                 glCallList (ctx->rings + (NRADII - 2) - bs->counter);
582         else
583                 glCallList (ctx->rings + bs->counter);
584
585         --bs->dwellcnt;
586
587         return g_blades * SUBDIV * 2;
588 }
589
590 static int
591 draw_blink_segment_scatter (lockward_context *ctx, struct blinkstate *bs)
592 {
593         int i, polys = 0;
594
595         if (bs->dwellcnt < 0) {
596                 if (bs->counter <= 0) {
597                         bs->drawfunc = NULL;
598                         return 0;
599                 }
600
601                 /*
602                  * Init random noise array.  On average, 1/4 of the bits will
603                  * be set, which should look nice.  (1/2 looks too busy.)
604                  */
605                 for (i = g_blades;  --i >= 0; )
606                         bs->noise[i] = random() & random()
607                                      & ((1 << (NRADII - 1)) - 1);
608
609                 if ((bs->dwellcnt = bs->dwell) < 0)
610                         bs->dwellcnt = -bs->dwellcnt;
611                 --bs->counter;
612         }
613
614         set_alpha_by_dwell (bs);
615         glBlendFunc (GL_DST_COLOR, GL_SRC_ALPHA);
616         glColor4fv (bs->color);
617
618         for (i = g_blades;  --i >= 0; ) {
619                 register uint32_t       bits;
620                 int                     inner, outer;
621
622                 /*
623                  * Find consecutive runs of 1 bits.  Keep going until we run
624                  * out of them.
625                  */
626                 for (bits = bs->noise[i];  bits; ) {
627                         inner = ffs (bits) - 1;
628                         bits = ~bits & ~((1 << inner) - 1);
629                         outer = ffs (bits) - 1;
630                         bits = ~bits & ~((1 << outer) - 1);
631
632                         glPushMatrix ();
633                         glRotatef (i * 360.0 / (GLfloat) g_blades, 0, 0, 1);
634                         draw_blink_blade (ctx, inner, outer, True);
635                         glPopMatrix ();
636
637                         polys += SUBDIV + SUBDIV;
638                 }
639         }
640
641         --bs->dwellcnt;
642
643         return polys;
644 }
645
646 static void
647 random_blink (lockward_context *ctx, struct blinkstate *bs)
648 {
649         bs->color[0]    =
650         bs->color[1]    =
651         bs->color[2]    =
652         bs->color[3]    = 1.0;
653         bs->dwellcnt    = -1;
654         bs->radius      = -1;
655         bs->dwell       = calc_interval_frames
656                           (ctx, g_blinkdwell_min, g_blinkdwell_max);
657         if (random() & 2)
658                 bs->dwell = -bs->dwell;
659
660         bs->type = random() % MAX_BTYPE;
661
662         switch (bs->type) {
663         case BTYPE_RADIAL_SINGLE:
664         case BTYPE_SEGMENT_SINGLE:
665                 bs->drawfunc = draw_blink_radial_random;
666                 bs->val = 0;
667                 bs->counter = 1;
668                 break;
669         case BTYPE_RADIAL_RANDOM:
670         case BTYPE_SEGMENT_RANDOM:
671                 bs->drawfunc = draw_blink_radial_random;
672                 bs->val = 0;
673                 bs->counter = g_blades;
674                 break;
675         case BTYPE_RADIAL_SEQ:
676                 bs->drawfunc = draw_blink_radial_sequential;
677                 bs->val = random() % g_blades;  /*  Initial offset  */
678                 bs->direction = random() & 8  ?  1 :  -1;
679                 bs->counter = g_blades;
680                 break;
681         case BTYPE_RADIAL_DOUBLESEQ:
682                 bs->drawfunc = draw_blink_radial_doubleseq;
683                 bs->val = random() % g_blades;  /*  Initial offset  */
684                 bs->counter = g_blades / 2 + 1;
685                 break;
686         case BTYPE_CONCENTRIC_SINGLE:
687                 bs->drawfunc = draw_blink_concentric_random;
688                 bs->val = 0;
689                 bs->counter = 1;
690                 break;
691         case BTYPE_CONCENTRIC_RANDOM:
692                 bs->drawfunc = draw_blink_concentric_random;
693                 bs->val = 0;
694                 bs->counter = NRADII - 1;
695                 break;
696         case BTYPE_CONCENTRIC_SEQ:
697                 bs->drawfunc = draw_blink_concentric_sequential;
698                 bs->direction = random() & 8  ?  1 :  -1;
699                 bs->counter = NRADII - 1;
700                 break;
701         case BTYPE_SEGMENT_SCATTER:
702                 bs->drawfunc = draw_blink_segment_scatter;
703                 bs->counter = random() % (g_blades / 2) + (g_blades / 2) + 1;
704                 break;
705         }
706 }
707
708
709 /***************************************************************************
710  * Main rendering routine.
711  */
712 ENTRYPOINT void
713 draw_lockward (ModeInfo *mi)
714 {
715         lockward_context        *ctx = &g_ctx[MI_SCREEN (mi)];
716         spinnerstate    *ss;
717         Display         *dpy = MI_DISPLAY(mi);
718         Window          window = MI_WINDOW(mi);
719         int             i, n;
720
721         GLfloat scolor[4] = {0.0, 0.0, 0.0, 0.5};
722
723         if (!ctx->glx_context)
724                 return;
725
726         glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi), *(ctx->glx_context));
727
728
729         glClear (GL_COLOR_BUFFER_BIT);
730
731         if (ctx->blendmode)
732                 glBlendFunc (GL_ONE, GL_ONE);
733         else
734                 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
735
736         glPushMatrix ();
737         glLoadIdentity ();
738
739         mi->polygon_count = 0;
740
741         for (n = NSPINNERS;  --n >= 0; ) {
742                 ss = &ctx->spinners[n];
743
744                 /*  Set color.  */
745                 i = ss->ccolor >> COLORIDX_SHF;
746                 scolor[0] = ss->colors[i].red   / 65535.0;
747                 scolor[1] = ss->colors[i].green / 65535.0;
748                 scolor[2] = ss->colors[i].blue  / 65535.0;
749                 glColor4fv (scolor);
750
751                 glPushMatrix ();
752                 glRotatef (ss->rot - ss->rotcount * ss->rotinc, 0, 0, 1);
753                 for (i = ss->nblades;  --i >= 0; ) {
754                   glPushMatrix ();
755                   glRotatef (360.0 * i / ss->nblades, 0, 0, 1);
756
757                   glBegin (GL_TRIANGLE_FAN);
758                   /* glCallList (ctx->blades_outer + ss->bladeidx[i].outer); */
759                   /* glCallList (ctx->blades_inner + ss->bladeidx[i].inner); */
760                   draw_blink_blade (ctx,
761                                     ss->bladeidx[i].inner,
762                                     ss->bladeidx[i].outer,
763                                     False);
764                   glEnd ();
765
766                   glPopMatrix ();
767                   mi->polygon_count += SUBDIV + SUBDIV;
768                 }
769                 glPopMatrix ();
770
771                 /*  Advance rotation.  */
772                 if (ss->rotcount) {
773                         if (ss->rotcount > 0)
774                                 --ss->rotcount;
775                 } else {
776                         if (ss->rotinc == 0.0)
777                                 random_blade_rot (ctx, ss);
778                         else {
779                                 /*  Compute # of ticks to sit idle.  */
780                                 ss->rotinc = 0.0;
781                                 ss->rotcount =
782                                  calc_interval_frames (ctx,
783                                                        g_rotateidle_min,
784                                                        g_rotateidle_max);
785                         }
786                 }
787
788                 /*  Advance colors.  */
789                 if ((ss->ccolor += ss->colorinc) >= ss->ncolors)
790                         ss->ccolor -= ss->ncolors;
791                 else if (ss->ccolor < 0)
792                         ss->ccolor += ss->ncolors;
793         }
794
795         if (g_blink_p) {
796                 if (ctx->blink.drawfunc) {
797                         mi->polygon_count +=
798                          ctx->blink.drawfunc (ctx, &ctx->blink);
799                 } else {
800                         if (ctx->nextblink > 0)
801                                 --ctx->nextblink;
802                         else {
803                                 /* Compute # of frames for blink idle time. */
804                                 ctx->nextblink =
805                                  calc_interval_frames (ctx,
806                                                        g_blinkidle_min,
807                                                        g_blinkidle_max);
808                                 random_blink (ctx, &ctx->blink);
809                         }
810                 }
811         }
812         glPopMatrix ();
813
814         if (MI_IS_FPS (mi)) do_fps (mi);
815         glFinish();
816
817         glXSwapBuffers (dpy, window);
818 }
819
820
821 /***************************************************************************
822  * Initialization/teardown.
823  */
824 ENTRYPOINT void 
825 init_lockward (ModeInfo *mi)
826 {
827         lockward_context        *ctx;
828         int             i, n;
829
830         if (!g_ctx) {
831                 g_ctx = (lockward_context *) calloc (MI_NUM_SCREENS (mi),
832                                                    sizeof (lockward_context));
833                 if (!g_ctx) {
834                         fprintf (stderr, "%s: can't allocate context.\n",
835                                  progname);
836                         exit (1);
837                 }
838         }
839         ctx = &g_ctx[MI_SCREEN (mi)];
840
841         ctx->glx_context = init_GL (mi);
842
843         reshape_lockward (mi, MI_WIDTH (mi), MI_HEIGHT (mi));
844
845         glEnable (GL_CULL_FACE);
846         glEnable (GL_BLEND);
847         glDisable (GL_DEPTH_TEST);
848
849         glShadeModel (GL_FLAT);
850         glFrontFace (GL_CW);
851
852         /* ctx->blades_outer    = glGenLists (NRADII); */
853         /* ctx->blades_inner    = glGenLists (NRADII); */
854         ctx->rings              = glGenLists (NRADII - 1);
855         ctx->blendmode          = 0;
856         ctx->fps                = 1000000 / MI_DELAY (mi);
857         ctx->nextblink          = calc_interval_frames
858                                    (ctx, g_blinkidle_min, g_blinkidle_max);
859         ctx->blink.drawfunc     = NULL;
860         ctx->blink.noise        = malloc (sizeof (uint32_t) * g_blades);
861         if (!ctx->blink.noise) {
862                 fprintf (stderr, "Can't allocate noise array.\n");
863                 exit (1);
864         }
865
866         gen_blade_arcs (ctx);
867         gen_rings (ctx);
868
869         for (i = NSPINNERS;  --i >= 0; ) {
870                 spinnerstate *ss = &ctx->spinners[i];
871
872                 ss->rot         = 0.0;
873                 ss->rotcount    = -1;
874
875                 /*  Establish rotation  */
876                 random_blade_rot (ctx, ss);
877
878                 /*
879                  * Establish color cycling path and rate.  Rate avoids zero.
880                  */
881                 ss->ncolors = 128;
882                 ss->colorinc = (random() & ((2 << COLORIDX_SHF) - 1))
883                              - (1 << COLORIDX_SHF);
884                 if (ss->colorinc >= 0)
885                         ++ss->colorinc;
886
887                 ss->colors = (XColor *) calloc (ss->ncolors, sizeof (XColor));
888                 if (!ss->colors) {
889                         fprintf (stderr,
890                                  "Can't allocate XColors for spinner %d.\n",
891                                  i);
892                         exit (1);
893                 }
894                 make_smooth_colormap (0, 0, 0,
895                                       ss->colors, &ss->ncolors,
896                                       False, 0, False);
897                 ss->ncolors <<= COLORIDX_SHF;
898
899                 /*
900                  * Create blades.
901                  */
902                 ss->nblades     = g_blades;
903                 ss->bladeidx    = malloc (sizeof (bladestate) * g_blades);
904                 if (!ss->bladeidx) {
905                         fprintf (stderr, "Can't allocate blades.\n");
906                         exit (1);
907                 }
908                 for (n = g_blades;  --n >= 0; ) {
909                         /*
910                          * Establish blade radii.  Can't be equal.  Ensure
911                          * outer > inner.
912                          */
913                         do {
914                                 ss->bladeidx[n].outer = random() & 7;
915                                 ss->bladeidx[n].inner = random() & 7;
916                         } while (ss->bladeidx[n].outer ==
917                                  ss->bladeidx[n].inner);
918
919                         if (ss->bladeidx[n].outer < ss->bladeidx[n].inner) {
920                                 uint8_t tmp;
921
922                                 tmp = ss->bladeidx[n].outer;
923                                 ss->bladeidx[n].outer = ss->bladeidx[n].inner;
924                                 ss->bladeidx[n].inner = tmp;
925                         }
926                 }
927         }
928 }
929
930 static void
931 free_lockward (lockward_context *ctx)
932 {
933         int i;
934
935         if (ctx->blink.noise)
936                 free (ctx->blink.noise);
937         if (glIsList (ctx->rings))
938                 glDeleteLists (ctx->rings, NRADII - 1);
939         /* if (glIsList (ctx->blades_outer))
940                 glDeleteLists (ctx->blades_outer, NRADII);
941         if (glIsList (ctx->blades_inner))
942                 glDeleteLists (ctx->blades_inner, NRADII); */
943
944         for (i = NSPINNERS;  --i >= 0; ) {
945                 spinnerstate *ss = &ctx->spinners[i];
946
947                 if (ss->colors)
948                         free (ss->colors);
949                 if (ss->bladeidx)
950                         free (ss->bladeidx);
951         }
952 }
953
954 ENTRYPOINT void
955 release_lockward (ModeInfo *mi)
956 {
957         int i;
958
959         if (!g_ctx)
960                 return;
961
962         for (i = MI_NUM_SCREENS (mi);  --i >= 0; ) {
963                 if (g_ctx[i].glx_context)
964                         glXMakeCurrent (MI_DISPLAY (mi), MI_WINDOW (mi),
965                                         *(g_ctx[i].glx_context));
966                 free_lockward (&g_ctx[i]);
967         }
968
969         FreeAllGL (mi);
970         free (g_ctx);  g_ctx = NULL;
971 }
972
973
974 XSCREENSAVER_MODULE ("Lockward", lockward)
975
976 #endif /* USE_GL */
977
978 /*  vim:se ts=8 sts=8 sw=8:  */