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