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