From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / vermiculate.c
1 /*
2  *  @(#) vermiculate.c
3  *  @(#) Copyright (C) 2001 Tyler Pierce (tyler@alumni.brown.edu)
4  *  The full program, with documentation, is available at:
5  *    http://freshmeat.net/projects/fdm
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation.  No representations are made about the suitability of this
12  * software for any purpose.  It is provided "as is" without express or
13  * implied warranty.
14  */
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19
20 #include <ctype.h>
21 #include <math.h>
22 #include "screenhack.h"
23
24 #define degs 360
25 #define degs2 (degs/2)
26 #define degs4 (degs/4)
27 #define degs8 (degs/8)
28 #define dtor 0.0174532925       /*  pi / degs2; */
29 #define thrmax 120
30 #define tailmax (thrmax * 2 + 1)
31 #define tmodes '7'
32 #define ymax (st->hei - 1)
33 #define ymin 0
34 #define xmax (st->wid - 1)
35 #define xmin 0
36 #define rlmax 200
37 #define SPEEDINC 10
38 #define SPEEDMAX 1000
39 #define wraparound(VAL,LOWER,UPPER) {   \
40                     if (VAL >= UPPER)   \
41                       VAL -= UPPER - LOWER;     \
42                     else if (VAL < LOWER)       \
43                       VAL += UPPER - LOWER; }
44 #define arrcpy(DEST,SRC) memcpy (DEST, SRC, sizeof(DEST))
45
46 typedef double real;
47 typedef unsigned char banktype[thrmax];
48
49 typedef struct linedata
50 {
51   int deg, spiturn, turnco, turnsize;
52   unsigned char col;
53   Bool dead;
54
55   char orichar;
56   real x, y;
57   int tmode, tsc, tslen, tclim, otslen, ctinc, reclen, recpos, circturn, prey,
58     slice;
59   int xrec[rlmax + 1], yrec[rlmax + 1];
60   int turnseq[50];
61   Bool filled, killwalls, vhfollow,
62     selfbounce, tailfollow, realbounce, little;
63 }
64 linedata;
65
66 static const struct stringAndSpeed
67 {
68   char *str;
69   int speed;
70 }
71 sampleStrings[] =
72 {
73 /*  { "]]]]]]]]7ces123400-9-8#c123456#s9998880004#ma3#car9ma6#c-#r1", 600} , 
74         { "bmarrrr#c1234#lc5678#lyet]", 600} , */
75   { "AEBMN222222223#CAR9CAD4CAOV", 150} ,
76   { "mn333#c23#f1#]]]]]]]]]]]3bc9#r9#c78#f9#ma4#", 600} ,
77   { "AEBMN22222#CAD4CAORc1#f2#c1#r6", 100} , 
78 /*  { "mn6666666#c1i#f1#y2#sy2#vyety1#ry13i#l", 40} , */
79   { "aebmnrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr#", 500} , 
80 /*  { "bg+++++++++++++++++++++++#mnrrrrrrrrrrrrrrrrrrrrrrr#y1#k", 500},  */
81 /*  { "BMN22222223#CAD4CAOVYAS", 150}, */
82 /*  { "aebmnrrrrrrrrrrrrrrrr#yaryakg--#", 100} , */
83   { "mn6rrrrrrrrrrrrrrr#by1i#lcalc1#fnyav" , 200 } ,
84   { "mn1rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr#by1i#lcalc1#fn", 2000 },
85   { "baeMn333333333333333333333333#CerrYerCal", 800 },
86   { "baeMn1111111111111111111111111111111111111111111111111111111111#Cer9YesYevYerCal", 1200 },
87   { "baMn111111222222333333444444555555#Ct1#lCt2#lCt3#lCt4#lCt5#lCerrYerYet", 1400 },
88   { "baMn111111222222333333444444555555#Ct1#lCt2#lCt3#lCt4#lCt5#lCerrYerYetYt1i#lYt1i#sYt1#v", 1400 }
89 };
90
91 struct state {
92   Display *dpy;
93   Window window;
94
95   GC mygc;
96   Colormap mycmap;
97   XWindowAttributes xgwa;
98   XColor mycolors[tailmax];
99
100   int hei, wid, speed;
101   Bool erasing, cleared, autopal;
102   char *instring;
103   int max_ticks;
104
105   real sinof[degs], cosof[degs], tanof[degs];
106   unsigned char *point;
107
108   linedata thread[thrmax];
109   banktype bank;
110   int bnkt;
111   int boxw, boxh, curviness, gridden, ogd, bordcorn;
112   unsigned char bordcol, threads;
113   char ch, boolop;
114
115   int reset_p;
116   int cyc;
117 };
118
119
120
121 static void
122 consume_instring(struct state *st);
123
124 static Bool
125 wasakeypressed (struct state *st)
126 {
127   if (*st->instring != 0)
128     return True;
129   else
130     return False;
131 }
132
133 static char
134 readkey (struct state *st)
135 {
136   char readkey_result;
137   if (*st->instring == 0)
138     {
139       readkey_result = '#';
140     }
141   else
142     {
143       readkey_result = *st->instring;
144       st->instring++;
145     };
146   return toupper (readkey_result);
147 }
148
149 static unsigned int
150 random1 (unsigned int i)
151 {
152   return (ya_random () % i);
153 }
154
155 static unsigned long
156 waitabit (struct state *st)
157 {
158   int result = 0;
159   st->cyc += st->threads;
160   while (st->cyc > st->speed)
161     {
162       result += 10000;
163       st->cyc -= st->speed;
164     }
165   return result;
166 }
167
168 static void
169 clearscreen (struct state *st)
170 {
171   XClearWindow (st->dpy, st->window);
172   memset (st->point, 0, st->wid * st->hei);
173 }
174
175 static void
176 sp (struct state *st, int x, int y, int c)
177 {
178   XSetForeground (st->dpy, st->mygc, st->mycolors[c].pixel);
179   XDrawPoint (st->dpy, st->window, st->mygc, x, y);
180   st->point[(st->wid * y) + x] = c;
181 }
182
183 static int
184 gp (struct state *st, int x, int y)
185 {
186   return st->point[(st->wid * y) + x];
187 }
188
189 static void
190 redraw (struct state *st, int x, int y, int width, int height)
191 {
192   int xc, yc;
193   for (xc = x; xc <= x + width - 1; xc++)
194     for (yc = y; yc <= y + height - 1; yc++)
195       if (st->point[st->wid * yc + xc] != 0)
196         sp (st, xc, yc, st->point[st->wid * yc + xc]);
197 }
198
199 static void
200 palupdate (struct state *st, Bool forceUpdate)
201 {
202   if (forceUpdate || *st->instring == 0)
203     {
204       redraw (st, xmin, ymin, st->wid, st->hei);
205     }
206 }
207
208 static void
209 randpal (struct state *st)
210 {
211   int ncolors = tailmax - 1;
212   make_random_colormap (st->xgwa.screen, st->xgwa.visual, st->mycmap,
213                         &st->mycolors[1], &ncolors, True, True, 0, True);
214   if (ncolors < tailmax - 1)
215     {
216       int c;
217       for (c = 1; c < tailmax; c++)
218         st->mycolors[c].pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
219     }
220 }
221
222 static void
223 gridupdate (struct state *st, Bool interruptible)
224 {
225   int x, y;
226   if (st->gridden > 0)
227     for (x = 0; x <= xmax && !(wasakeypressed (st) && interruptible); x += st->boxw)
228       for (y = 0; y <= ymax; y += st->boxh)
229         {
230           if (random1 (15) < st->gridden)
231             {
232 #define lesser(A,B) ( ((A)<(B)) ? (A) : (B) )
233               int max = lesser (x + st->boxw, xmax);
234               int xc;
235               for (xc = x; xc <= max; xc++)
236                 sp (st, xc, y, 1);
237             }
238           if (random1 (15) < st->gridden)
239             {
240               int max = lesser (y + st->boxh, ymax);
241               int yc;
242               for (yc = y; yc <= max; yc++)
243                 sp (st, x, yc, 1);
244             }
245         }
246 }
247
248 static void
249 bordupdate (struct state *st)
250 {
251   int xbord, ybord;
252
253   if (st->bordcorn == 0 || st->bordcorn == 1)
254     ybord = ymin;
255   else
256     ybord = ymax;
257   if (st->bordcorn == 0 || st->bordcorn == 3)
258     xbord = xmin;
259   else
260     xbord = xmax;
261   {
262     int x, y;
263     for (x = xmin; x <= xmax; x++)
264       sp (st, x, ybord, st->bordcol);
265     for (y = ymin; y <= ymax; y++)
266       sp (st, xbord, y, st->bordcol);
267   }
268 }
269
270 static Bool
271 inbank (struct state *st, unsigned char thr)
272 {
273   int c;
274   if (st->bnkt > 0)
275     for (c = 1; c <= st->bnkt; c++)
276       if (st->bank[c - 1] == thr)
277         return True;
278   return False;
279 }
280
281 static void
282 pickbank (struct state *st)
283 {
284   unsigned char thr = 1;
285   st->bnkt = 0;
286   st->ch = '\0';
287   do
288     {
289       while (inbank (st, thr))
290         thr = thr % st->threads + 1;
291
292       palupdate (st, False);
293       st->ch = readkey (st);
294       palupdate (st, False); 
295       switch (st->ch)
296         {
297         case '+':
298         case '-':
299           do
300             {
301               if (st->ch == '+')
302                 thr++;
303               else
304                 thr--;
305               wraparound (thr, 1, st->threads + 1);
306             }
307           while (inbank (st, thr));
308           break;
309         case ' ':
310           st->bank[++st->bnkt - 1] = thr;
311           break;
312         case '1': case '2': case '3':
313         case '4': case '5': case '6':
314         case '7': case '8': case '9':
315
316           st->bank[++st->bnkt - 1] = st->ch - '0';
317           if (st->bank[st->bnkt - 1] > st->threads)
318             st->bnkt--;
319           break;
320         case 'I':
321           {
322             banktype tbank;
323             int tbankt = 0;
324             int c;
325             for (c = 1; c <= st->threads; c++)
326               if (!inbank (st, c))
327                 tbank[++tbankt - 1] = c;
328             st->bnkt = tbankt;
329             arrcpy (st->bank, tbank);
330           }
331           break;
332         case 'T':
333           st->ch = readkey (st);
334           switch (st->ch)
335             {
336             case '1': case '2': case '3':
337             case '4': case '5': case '6':
338             case '7': case '8': case '9':
339
340               {
341                 int c;
342                 for (c = 1; c <= st->threads; c++)
343                   if (st->thread[c - 1].tmode == st->ch - '0')
344                     st->bank[++st->bnkt - 1] = c;
345               }
346               break;
347             }
348           break;
349         case 'A':
350           for (st->bnkt = 1; st->bnkt <= st->threads; st->bnkt++)
351             st->bank[st->bnkt - 1] = st->bnkt;
352           st->bnkt = st->threads;
353           break;
354         case 'E':
355           for (st->bnkt = 1; st->bnkt <= thrmax; st->bnkt++)
356             st->bank[st->bnkt - 1] = st->bnkt;
357           st->bnkt = thrmax;
358           break;
359         }
360     }
361   while (!(st->bnkt >= st->threads || st->ch == 'N' || st->ch == '\15' || st->ch == '#'));
362   if (st->bnkt == 0 && st->ch != 'N')
363     {
364       st->bnkt = 1;
365       st->bank[0] = thr;
366     }
367   palupdate (st, False);
368 }
369
370 static void
371 bankmod (char boolop, Bool * Bool_)
372 {
373   switch (boolop)
374     {
375     case 'T':
376       *Bool_ = !*Bool_;
377       break;
378     case 'Y':
379       *Bool_ = True;
380       break;
381     case 'N':
382       *Bool_ = False;
383       break;
384     }
385 }
386
387 static void
388 newonscreen (struct state *st, unsigned char thr)
389 {
390   linedata *LP = &st->thread[thr - 1];
391   LP->filled = False;
392   LP->dead = False;
393   LP->reclen = (LP->little) ? 
394         random1 (10) + 5 : random1 (rlmax - 30) + 30;
395   LP->deg = random1 (degs);
396   LP->y = random1 (st->hei);
397   LP->x = random1 (st->wid);
398   LP->recpos = 0;
399   LP->turnco = 2;
400   LP->turnsize = random1 (4) + 2;
401 }
402
403 static void
404 firstinit (struct state *st, unsigned char thr)
405 {
406   linedata *LP = &st->thread[thr - 1];
407   LP->col = thr + 1;
408   LP->prey = 0;
409   LP->tmode = 1;
410   LP->slice = degs / 3;
411   LP->orichar = 'R';
412   LP->spiturn = 5;
413   LP->selfbounce = False;
414   LP->realbounce = False;
415   LP->vhfollow = False;
416   LP->tailfollow = False;
417   LP->killwalls = False;
418   LP->little = False;
419   LP->ctinc = random1 (2) * 2 - 1;
420   LP->circturn = ((thr % 2) * 2 - 1) * ((thr - 1) % 7 + 1);
421   LP->tsc = 1;
422   LP->tslen = 6;
423   LP->turnseq[0] = 6;
424   LP->turnseq[1] = -6;
425   LP->turnseq[2] = 6;
426   LP->turnseq[3] = 6;
427   LP->turnseq[4] = -6;
428   LP->turnseq[5] = 6;
429   LP->tclim = (unsigned char) (((real) degs) / 2 / 12);
430 }
431
432 static void
433 maininit (struct state *st)
434 {
435   if (!st->instring)
436     {
437       int n = random1 (sizeof (sampleStrings) / sizeof (sampleStrings[0]));
438       st->instring = sampleStrings[n].str;
439       st->speed = sampleStrings[n].speed;
440     }
441   if (st->speed == 0)
442     {
443           st->speed = 200;
444     }
445   st->boxh = 10;
446   st->boxw = 10;
447   st->gridden = 0;
448   st->bordcorn = 0;
449   st->threads = 4;
450   st->curviness = 30;
451   st->bordcol = 1;
452   st->ogd = 8;
453   st->ch = '\0';
454   st->erasing = True;
455   {
456     unsigned char thr;
457     for (thr = 1; thr <= thrmax; thr++)
458       {
459         firstinit (st, thr);
460         newonscreen (st, thr);
461       }
462   }
463   {
464     int d;
465     for (d = degs - 1; d >= 0; d--)
466       {
467         st->sinof[d] = sin (d * dtor);
468         st->cosof[d] = cos (d * dtor);
469         if (d % degs4 == 0)
470           st->tanof[d] = st->tanof[d + 1];
471         else
472           st->tanof[d] = tan (d * dtor);
473       }
474   }
475   randpal (st);
476 }
477
478 static Bool
479 move (struct state *st, unsigned char thr)
480 {
481   linedata *LP = &st->thread[thr - 1];
482   if (LP->dead)
483     return (False);
484   if (LP->prey == 0)
485     switch (LP->tmode)
486       {
487       case 1:
488         LP->deg += random1 (2 * LP->turnsize + 1) - LP->turnsize;
489         break;
490       case 2:
491         if (LP->slice == degs || LP->slice == degs2 || LP->slice == degs4)
492           {
493             if (LP->orichar == 'D')
494               {
495                 if (LP->deg % degs4 != degs8)
496                   LP->deg = degs4 * random1 (4) + degs8;
497               }
498             else if (LP->orichar == 'V')
499               if (LP->deg % degs4 != 0)
500                 LP->deg = degs4 * random1 (4);
501           }
502         if (random1 (100) == 0)
503           {
504             if (LP->slice == 0)
505               LP->deg = LP->deg - degs4 + random1 (degs2);
506             else
507               LP->deg += (random1 (2) * 2 - 1) * LP->slice;
508           }
509         break;
510       case 3:
511         LP->deg += LP->circturn;
512         break;
513       case 4:
514         if (abs (LP->spiturn) > 11)
515           LP->spiturn = 5;
516         else
517           LP->deg += LP->spiturn;
518         if (random1 (15 - abs (LP->spiturn)) == 0)
519           {
520             LP->spiturn += LP->ctinc;
521             if (abs (LP->spiturn) > 10)
522               LP->ctinc *= -1;
523           }
524         break;
525       case 5:
526         LP->turnco = abs (LP->turnco) - 1;
527         if (LP->turnco == 0)
528           {
529             LP->turnco = st->curviness + random1 (10);
530             LP->circturn *= -1;
531           }
532         LP->deg += LP->circturn;
533         break;
534       case 6:
535         if (abs (LP->turnco) == 1)
536           LP->turnco *= -1 * (random1 (degs2 / abs (LP->circturn)) + 5);
537         else if (LP->turnco == 0)
538           LP->turnco = 2;
539         else if (LP->turnco > 0)
540           {
541             LP->turnco--;
542             LP->deg += LP->circturn;
543           }
544         else
545           LP->turnco++;
546         break;
547       case 7:
548         LP->turnco++;
549         if (LP->turnco > LP->tclim)
550           {
551             LP->turnco = 1;
552             LP->tsc = (LP->tsc % LP->tslen) + 1;
553           }
554         LP->deg += LP->turnseq[LP->tsc - 1];
555         break;
556       }
557   else
558     {
559       int desdeg;
560       real dy, dx;
561       if (LP->tailfollow || LP->prey == thr)
562         {
563           dx = st->thread[LP->prey - 1].xrec[st->thread[LP->prey - 1].recpos] - LP->x;
564           dy = st->thread[LP->prey - 1].yrec[st->thread[LP->prey - 1].recpos] - LP->y;
565         }
566       else
567         {
568           dx = st->thread[LP->prey - 1].x - LP->x;
569           dy = st->thread[LP->prey - 1].y - LP->y;
570         }
571       desdeg =
572         (LP->vhfollow) ?
573         ((fabs (dx) > fabs (dy)) ?
574          ((dx > 0) ?
575           0 * degs4
576           :
577           2 * degs4)
578          :
579          ((dy > 0) ?
580           1 * degs4
581           :
582           3 * degs4))
583         :
584         ((dx > 0) ?
585          ((dy > 0) ?
586           1 * degs8 : 7 * degs8) : ((dy > 0) ? 3 * degs8 : 5 * degs8));
587       if (desdeg - desdeg % degs4 != LP->deg - LP->deg % degs4
588           || LP->vhfollow)
589         {
590           if (!LP->vhfollow)
591            { 
592               /* Using atan2 here doesn't seem to slow things down: */
593               desdeg = atan2 (dy, dx) / dtor;
594               wraparound (desdeg, 0, degs);
595            }
596           if (abs (desdeg - LP->deg) <= abs (LP->circturn))
597             LP->deg = desdeg;
598           else
599             LP->deg +=
600               (desdeg > LP->deg) ?
601               ((desdeg - LP->deg > degs2) ?
602                -abs (LP->circturn) : abs (LP->circturn))
603               : ((LP->deg - desdeg > degs2) ?
604                  abs (LP->circturn) : -abs (LP->circturn));
605         }
606       else
607         LP->deg +=
608           (st->tanof[LP->deg] >
609            dy / dx) ? -abs (LP->circturn) : abs (LP->circturn);
610     }
611
612   wraparound (LP->deg, 0, degs);
613   {
614     unsigned char oldcol;
615     real oldy = LP->y, oldx = LP->x;
616     LP->x += st->cosof[LP->deg];
617     wraparound (LP->x, xmin, xmax + 1);
618     LP->y += st->sinof[LP->deg];
619     wraparound (LP->y, ymin, ymax + 1);
620 #define xi ((int) LP->x)
621 #define yi ((int) LP->y)
622
623     oldcol = gp (st, xi, yi);
624     if (oldcol != 0)
625       {
626         Bool vertwall = False, horiwall = False;
627         if (oldcol == 1 && ((LP->killwalls && st->gridden > 0) || LP->realbounce))
628           {
629             vertwall = (gp (st, xi, (int) oldy) == 1);
630             horiwall = (gp (st, (int) oldx, yi) == 1);
631           }
632         if (oldcol == 1 && LP->realbounce && (vertwall || horiwall))
633           {
634             if (vertwall)
635               LP->deg = -LP->deg + degs2;
636             else
637               LP->deg = -LP->deg;
638           }
639         else
640           {
641             if ((oldcol != LP->col && LP->realbounce)
642                 || (oldcol == LP->col && LP->selfbounce))
643               LP->deg += degs4 * (random1 (2) * 2 - 1);
644             else if (oldcol != LP->col)
645               LP->deg += degs2;
646           }
647         if (LP->killwalls && st->gridden > 0 && oldcol == 1)
648           {
649             if (vertwall && xi + 1 <= xmax)
650               {
651                 int yy;
652                 for (yy = yi - yi % st->boxh;
653                      yy <= yi - yi % st->boxh + st->boxh && yy <= ymax; yy++)
654                   if (gp (st, xi + 1, yy) != 1 || yy == ymax)
655                     sp (st, xi, yy, 0);
656               }
657             if (horiwall && yi + 1 <= ymax)
658               {
659                 int xx;
660                 for (xx = xi - xi % st->boxw;
661                      xx <= xi - xi % st->boxw + st->boxw && xx <= xmax; xx++)
662                   if (gp (st, xx, yi + 1) != 1 || xx == xmax)
663                     sp (st, xx, yi, 0);
664               }
665           }
666         if (oldcol != LP->col || LP->selfbounce)
667           {
668             LP->x = oldx;
669             LP->y = oldy;
670           }
671         wraparound (LP->deg, 0, degs);
672       }
673   }
674
675   sp (st, xi, yi, LP->col);
676   if (LP->filled)
677     {
678       if (st->erasing)
679         sp (st, LP->xrec[LP->recpos], LP->yrec[LP->recpos], 0);
680       else
681         sp (st, LP->xrec[LP->recpos], LP->yrec[LP->recpos], LP->col + thrmax);
682     }
683   LP->yrec[LP->recpos] = yi;
684   LP->xrec[LP->recpos] = xi;
685   if (LP->recpos == LP->reclen - 1)
686     LP->filled = True;
687   if (LP->filled && !st->erasing)
688     {
689       int co = LP->recpos;
690       LP->dead = True;
691       do
692         {
693           int nextco = co + 1;
694           wraparound (nextco, 0, LP->reclen);
695           if (LP->yrec[co] != LP->yrec[nextco]
696               || LP->xrec[co] != LP->xrec[nextco])
697             LP->dead = False;
698           co = nextco;
699         }
700       while (!(!LP->dead || co == LP->recpos));
701     }
702   LP->recpos++;
703   wraparound (LP->recpos, 0, LP->reclen);
704   return (!LP->dead);
705 }
706
707 static unsigned long
708 vermiculate_draw (Display *dpy, Window window, void *closure)
709 {
710   struct state *st = (struct state *) closure;
711   int had_instring = (st->instring != 0);
712   int tick = 0;
713   int this_delay = 0;
714   int loop = 0;
715
716   
717  AGAIN:
718   if (st->reset_p) 
719     {
720       st->reset_p = 0;
721
722       clearscreen (st);
723       {
724         unsigned char thr;
725         for (thr = 1; thr <= st->threads; thr++)
726           newonscreen (st, thr);
727       }
728       if (st->autopal)
729         {
730           randpal (st);
731           palupdate (st, False);
732         }
733       bordupdate (st);
734       gridupdate (st, False);
735     }
736
737   {
738     Bool alltrap = True;
739     unsigned char thr;
740     for (thr = 1; thr <= st->threads; thr++)
741       if (move (st, thr))
742         alltrap = False;
743     if (alltrap)        /* all threads are trapped */
744       st->reset_p = True;
745     if (st->speed != SPEEDMAX)
746       this_delay = waitabit (st);
747   }
748
749   if (tick++ > st->max_ticks && !had_instring)
750     {
751       tick = 0;
752       st->instring = 0;
753       maininit(st);
754       st->reset_p = True;
755       st->autopal = False;
756     }
757
758   if (this_delay == 0 && loop++ < 1000)
759     goto AGAIN;
760
761   return this_delay;
762 }
763
764 static void *
765 vermiculate_init (Display *d, Window w)
766 {
767   struct state *st = (struct state *) calloc (1, sizeof(*st));
768   st->dpy = d;
769   st->window = w;
770   st->reset_p = 1;
771   st->instring = get_string_resource (st->dpy, "instring", "Instring");
772   if (st->instring && !*st->instring)
773     st->instring = 0;
774
775   st->max_ticks = get_integer_resource (st->dpy, "ticks", "Integer");
776   {
777     int temp = get_integer_resource (st->dpy, "speed", "Speed");
778     if (temp != 0)
779       st->speed = temp;
780   }
781
782   st->mycolors[0].pixel = BlackPixel (st->dpy, DefaultScreen (st->dpy));
783
784   XSetWindowBackground (st->dpy, st->window,
785                         BlackPixel (st->dpy, DefaultScreen (st->dpy)));
786   {
787     XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
788     st->wid = st->xgwa.width;
789     st->hei = st->xgwa.height;
790     st->mycmap = st->xgwa.colormap;
791   }
792   {
793     XGCValues mygcv;
794     st->mygc = XCreateGC (st->dpy, st->window, 0, &mygcv);
795   }
796
797   st->point = (unsigned char *) calloc (1, st->wid * st->hei);
798   maininit (st);
799   palupdate (st, True);
800   consume_instring(st);
801   return st;
802 }
803
804
805 static void
806 vermiculate_reshape (Display *dpy, Window window, void *closure, 
807                  unsigned int w, unsigned int h)
808 {
809   struct state *st = (struct state *) closure;
810   st->wid = w;
811   st->hei = h;
812   free (st->point);
813   st->point = (unsigned char *) calloc (1, st->wid * st->hei);
814 }
815
816 static Bool
817 vermiculate_event (Display *dpy, Window window, void *closure, XEvent *event)
818 {
819   struct state *st = (struct state *) closure;
820   if (screenhack_event_helper (dpy, window, event))
821     {
822       st->reset_p = 1;
823       return True;
824     }
825   return False;
826 }
827
828 static void
829 consume_instring(struct state *st)
830 {
831   char boolop;
832
833           while (wasakeypressed (st))
834             {
835               st->ch = readkey (st);
836               switch (st->ch)
837                 {
838                 case 'M':
839                   st->ch = readkey (st);
840                   switch (st->ch)
841                     {
842                     case 'A':
843                     case 'N':
844                       {
845                         unsigned char othreads = st->threads;
846                         if (st->ch == 'N')
847                           st->threads = 0;
848                         do
849                           {
850                             st->ch = readkey (st);
851                             switch (st->ch)
852                               {
853                               case '1': case '2': case '3':
854                               case '4': case '5': case '6':
855                               case '7': case '8': case '9':
856                                 st->thread[++st->threads - 1].tmode = st->ch - '0';
857                                 break;
858                               case 'R':
859                                 st->thread[++st->threads - 1].tmode =
860                                   random1 (tmodes - '0') + 1;
861                                 break;
862                               }
863                           }
864                         while (!(st->ch == '\15' || st->ch == '#'
865                                  || st->threads == thrmax));
866                         if (st->threads == 0)
867                           st->threads = othreads;
868                         st->reset_p = 1;
869                       }
870                       break;
871                     }
872                   break;
873                 case 'C':
874                   pickbank (st);
875                   if (st->bnkt > 0)
876                     {
877                       st->ch = readkey (st);
878                       switch (st->ch)
879                         {
880                         case 'D':
881                           st->ch = readkey (st);
882                           switch (st->ch)
883                             {
884                             case '1': case '2': case '3':
885                             case '4': case '5': case '6':
886                             case '7': case '8': case '9':
887 /* Careful!  The following macro needs to be at the beginning of any
888 block in which it's invoked, since it declares variables: */
889 #define forallinbank(LDP) linedata *LDP; int bankc; \
890                 for (bankc = 1; \
891                 ((bankc <= st->bnkt) ? ( \
892                         (LDP = &st->thread[st->bank[bankc - 1] - 1],    1) \
893                         ) : 0) ; bankc++)
894                               {
895                                 forallinbank (L) L->slice = degs / (st->ch - '0');
896                               }
897                               break;
898                             case 'M':
899                               {
900                                 forallinbank (L) L->slice = 0;
901                               }
902                               break;
903                             }
904                           break;
905                         case 'S':
906                           {
907                             forallinbank (L)
908                             {
909                               L->otslen = L->tslen;
910                               L->tslen = 0;
911                             }
912                           }
913                           do
914                             {
915                               char oldch = st->ch;
916                               st->ch = readkey (st);
917                               {
918                                 forallinbank (L)
919                                 {
920                                   switch (st->ch)
921                                     {
922                                     case '0':
923                                     case '1': case '2': case '3':
924                                     case '4': case '5': case '6':
925                                     case '7': case '8': case '9':
926                                       L->tslen++;
927                                       L->turnseq[L->tslen - 1] = st->ch - '0';
928                                       if (oldch == '-')
929                                         L->turnseq[L->tslen - 1] *= -1;
930                                       if (bankc % 2 == 0)
931                                         L->turnseq[L->tslen - 1] *= -1;
932                                       break;
933                                     }
934                                 }
935                               }
936                             }
937                           while (!(st->ch == '\15' || st->ch == '#'
938                                    || st->thread[st->bank[0] - 1].tslen == 50));
939                           {
940                             forallinbank (L)
941                             {
942                               int seqSum = 0, c;
943
944                               if (L->tslen == 0)
945                                 L->tslen = L->otslen;
946                               for (c = 1; c <= L->tslen; c++)
947                                 seqSum += L->turnseq[c - 1];
948                               if (seqSum == 0)
949                                 L->tclim = 1;
950                               else
951                                 L->tclim =
952                                   (int) (((real) degs2) / abs (seqSum));
953                               L->tsc = random1 (L->tslen) + 1;
954                             }
955                           }
956                           break;
957                         case 'T':
958                           {
959                             st->ch = readkey (st);
960                             {
961                               forallinbank (L)
962                               {
963                                 switch (st->ch)
964                                   {
965                                   case '1': case '2': case '3':
966                                   case '4': case '5': case '6':
967                                   case '7': case '8': case '9':
968                                     L->tmode = st->ch - '0';
969                                     break;
970                                   case 'R':
971                                     L->tmode = random1 (tmodes - '0') + 1;
972                                     break;
973                                   }
974                               }
975                             }
976                           }
977                           break;
978                         case 'O':
979                           st->ch = readkey (st);
980                           {
981                             forallinbank (L) L->orichar = st->ch;
982                           }
983                           break;
984                         case 'F':
985                           {
986                             banktype fbank;
987                             arrcpy (fbank, st->bank);
988                             {
989                               int fbnkt = st->bnkt;
990                               int bankc;
991                               pickbank (st);
992                               for (bankc = 1; bankc <= fbnkt; bankc++)
993                                 {
994                                   linedata *L = &st->thread[fbank[bankc - 1] - 1];
995                                   if (st->ch == 'N')
996                                     L->prey = 0;
997                                   else
998                                     L->prey = st->bank[0 + (bankc - 1) % st->bnkt];
999                                 }
1000                             }
1001                           }
1002                           break;
1003                         case 'L':
1004                           {
1005                             forallinbank (L) L->prey = st->bank[bankc % st->bnkt];
1006                           }
1007                           break;
1008                         case 'R':
1009                           st->ch = readkey (st);
1010                           {
1011                             forallinbank (L) switch (st->ch)
1012                               {
1013                               case '1': case '2': case '3':
1014                               case '4': case '5': case '6':
1015                               case '7': case '8': case '9':
1016                                 L->circturn = 10 - (st->ch - '0');
1017                                 break;
1018                               case 'R':
1019                                 L->circturn = random1 (7) + 1;
1020                                 break;
1021                               }
1022                           }
1023                           break;
1024                         }
1025                     }
1026                   break;
1027                 case 'T':
1028                 case 'Y':
1029                 case 'N':
1030                   boolop = st->ch;
1031                   pickbank (st);
1032                   if (st->bnkt > 0)
1033                     {
1034                       st->ch = readkey (st);
1035                       {
1036                         forallinbank (L)
1037                         {
1038                           switch (st->ch)
1039                             {
1040                             case 'S':
1041                               bankmod (boolop, &L->selfbounce);
1042                               break;
1043                             case 'V':
1044                               bankmod (boolop, &L->vhfollow);
1045                               break;
1046                             case 'R':
1047                               bankmod (boolop, &L->realbounce);
1048                               break;
1049                             case 'L':
1050                               bankmod (boolop, &L->little);
1051                               st->cleared = True;
1052                               break;
1053                             case 'T':
1054                               bankmod (boolop, &L->tailfollow);
1055                               break;
1056                             case 'K':
1057                               bankmod (boolop, &L->killwalls);
1058                               break;
1059                             }
1060                         }
1061                       }
1062                     }
1063                   break;
1064                 case 'R':
1065                   if (st->bordcol == 1)
1066                     {
1067                       st->bordcol = 0;
1068                       bordupdate (st);
1069                       st->bordcorn = (st->bordcorn + 1) % 4;
1070                       st->bordcol = 1;
1071                       bordupdate (st);
1072                     }
1073                   break;
1074                 case '1': case '2': case '3':
1075                 case '4': case '5': case '6':
1076                 case '7': case '8': case '9':
1077                   {
1078                     int c;
1079                     for (c = 1; c <= thrmax; c++)
1080                       st->thread[c - 1].tmode = st->ch - '0';
1081                   }
1082                   break;
1083                 case '\40':
1084                   st->cleared = True;
1085                   break;
1086                 case 'E':
1087                   st->erasing = !st->erasing;
1088                   break;
1089                 case 'P':
1090                   randpal (st);
1091                   palupdate (st, True);
1092                   break;
1093                 case 'G':
1094                   {
1095                     char dimch = 'B';
1096                     Bool gridchanged = True;
1097                     if (st->gridden == 0)
1098                       st->gridden = st->ogd;
1099                     do
1100                       {
1101                         int msize = 0;
1102                         if (gridchanged)
1103                           {
1104                             clearscreen (st);
1105                             gridupdate (st, True);
1106                           }
1107                         st->ch = readkey (st);
1108                         gridchanged = True;
1109                         switch (st->ch)
1110                           {
1111                           case '+':
1112                             msize = 1;
1113                             break;
1114                           case '-':
1115                             msize = -1;
1116                             break;
1117                           case ']':
1118                             if (st->gridden < 15)
1119                               st->gridden++;
1120                             break;
1121                           case '[':
1122                             if (st->gridden > 0)
1123                               st->gridden--;
1124                             break;
1125                           case 'O':
1126                             st->ogd = st->gridden;
1127                             st->gridden = 0;
1128                             break;
1129                           case 'S':
1130                             st->boxw = st->boxh;
1131                           case 'W':
1132                           case 'H':
1133                           case 'B':
1134                             dimch = st->ch;
1135                             break;
1136                           default:
1137                             gridchanged = False;
1138                           }
1139                         if (dimch == 'W' || dimch == 'B')
1140                           st->boxw += msize;
1141                         if (dimch == 'H' || dimch == 'B')
1142                           st->boxh += msize;
1143                         if (st->boxw == 0)
1144                           st->boxw = 1;
1145                         if (st->boxh == 0)
1146                           st->boxh = 1;
1147                       }
1148                     while (!(st->ch == '\15' || st->ch == '#' || st->ch == 'O'));
1149                     st->cleared = True;
1150                   }
1151                   break;
1152                 case 'A':
1153                   st->autopal = !st->autopal;
1154                   break;
1155                 case 'B':
1156                   st->bordcol = 1 - st->bordcol;
1157                   bordupdate (st);
1158                   break;
1159                 case '-':
1160                   st->speed -= SPEEDINC;
1161                   if (st->speed < 1)
1162                     st->speed = 1;
1163                   break;
1164                 case '+':
1165                   st->speed += SPEEDINC;
1166                   if (st->speed > SPEEDMAX)
1167                     st->speed = SPEEDMAX;
1168                   break;
1169                 case '/':
1170                   if (st->curviness > 5)
1171                     st->curviness -= 5;
1172                   break;
1173                 case '*':
1174                   if (st->curviness < 50)
1175                     st->curviness += 5;
1176                   break;
1177                 case ']':
1178                   if (st->threads < thrmax)
1179                     newonscreen (st, ++st->threads);
1180                   break;
1181                 case '[':
1182                   if (st->threads > 1)
1183                     {
1184                       linedata *L = &st->thread[st->threads - 1];
1185                       int lastpos = (L->filled) ? L->reclen - 1 : L->recpos;
1186                       int c;
1187                       for (c = 0; c <= lastpos; c++)
1188                         sp (st, L->xrec[c], L->yrec[c], 0);
1189                       st->threads--;
1190                     }
1191                   break;
1192                 }
1193             }
1194 }
1195
1196 static void
1197 vermiculate_free (Display *dpy, Window window, void *closure)
1198 {
1199   struct state *st = (struct state *) closure;
1200
1201   if (st->point)
1202     {
1203       free(st->point);
1204     }
1205   free (st);
1206 }
1207
1208
1209 static const char *vermiculate_defaults[] = {
1210   ".background: Black",
1211   "*ticks: 20000",
1212   "*fpsSolid:   true",
1213   "*speed: 0",
1214   "*instring: ",
1215 #ifdef USE_IPHONE
1216   "*ignoreRotation: True",
1217 #endif
1218   0
1219 };
1220
1221 static XrmOptionDescRec vermiculate_options[] = {
1222   {"-speed", ".speed", XrmoptionSepArg, 0},
1223   {"-instring", ".instring", XrmoptionSepArg, 0},
1224   {0, 0, 0, 0}
1225 };
1226
1227
1228 XSCREENSAVER_MODULE ("Vermiculate", vermiculate)