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