d1ef57198af245212cfc971fa17e2c4998507e67
[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->dpy,
213                         st->xgwa.visual,
214                         st->mycmap, &st->mycolors[1], &ncolors, True, True, 0, True);
215   if (ncolors < tailmax - 1)
216     {
217       int c;
218       for (c = 1; c < tailmax; c++)
219         st->mycolors[c].pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
220     }
221 }
222
223 static void
224 gridupdate (struct state *st, Bool interruptible)
225 {
226   int x, y;
227   if (st->gridden > 0)
228     for (x = 0; x <= xmax && !(wasakeypressed (st) && interruptible); x += st->boxw)
229       for (y = 0; y <= ymax; y += st->boxh)
230         {
231           if (random1 (15) < st->gridden)
232             {
233 #define lesser(A,B) ( ((A)<(B)) ? (A) : (B) )
234               int max = lesser (x + st->boxw, xmax);
235               int xc;
236               for (xc = x; xc <= max; xc++)
237                 sp (st, xc, y, 1);
238             }
239           if (random1 (15) < st->gridden)
240             {
241               int max = lesser (y + st->boxh, ymax);
242               int yc;
243               for (yc = y; yc <= max; yc++)
244                 sp (st, x, yc, 1);
245             }
246         }
247 }
248
249 static void
250 bordupdate (struct state *st)
251 {
252   int xbord, ybord;
253
254   if (st->bordcorn == 0 || st->bordcorn == 1)
255     ybord = ymin;
256   else
257     ybord = ymax;
258   if (st->bordcorn == 0 || st->bordcorn == 3)
259     xbord = xmin;
260   else
261     xbord = xmax;
262   {
263     int x, y;
264     for (x = xmin; x <= xmax; x++)
265       sp (st, x, ybord, st->bordcol);
266     for (y = ymin; y <= ymax; y++)
267       sp (st, xbord, y, st->bordcol);
268   }
269 }
270
271 static Bool
272 inbank (struct state *st, unsigned char thr)
273 {
274   int c;
275   if (st->bnkt > 0)
276     for (c = 1; c <= st->bnkt; c++)
277       if (st->bank[c - 1] == thr)
278         return True;
279   return False;
280 }
281
282 static void
283 pickbank (struct state *st)
284 {
285   unsigned char thr = 1;
286   st->bnkt = 0;
287   st->ch = '\0';
288   do
289     {
290       while (inbank (st, thr))
291         thr = thr % st->threads + 1;
292
293       palupdate (st, False);
294       st->ch = readkey (st);
295       palupdate (st, False); 
296       switch (st->ch)
297         {
298         case '+':
299         case '-':
300           do
301             {
302               if (st->ch == '+')
303                 thr++;
304               else
305                 thr--;
306               wraparound (thr, 1, st->threads + 1);
307             }
308           while (inbank (st, thr));
309           break;
310         case ' ':
311           st->bank[++st->bnkt - 1] = thr;
312           break;
313         case '1': case '2': case '3':
314         case '4': case '5': case '6':
315         case '7': case '8': case '9':
316
317           st->bank[++st->bnkt - 1] = st->ch - '0';
318           if (st->bank[st->bnkt - 1] > st->threads)
319             st->bnkt--;
320           break;
321         case 'I':
322           {
323             banktype tbank;
324             int tbankt = 0;
325             int c;
326             for (c = 1; c <= st->threads; c++)
327               if (!inbank (st, c))
328                 tbank[++tbankt - 1] = c;
329             st->bnkt = tbankt;
330             arrcpy (st->bank, tbank);
331           }
332           break;
333         case 'T':
334           st->ch = readkey (st);
335           switch (st->ch)
336             {
337             case '1': case '2': case '3':
338             case '4': case '5': case '6':
339             case '7': case '8': case '9':
340
341               {
342                 int c;
343                 for (c = 1; c <= st->threads; c++)
344                   if (st->thread[c - 1].tmode == st->ch - '0')
345                     st->bank[++st->bnkt - 1] = c;
346               }
347               break;
348             }
349           break;
350         case 'A':
351           for (st->bnkt = 1; st->bnkt <= st->threads; st->bnkt++)
352             st->bank[st->bnkt - 1] = st->bnkt;
353           st->bnkt = st->threads;
354           break;
355         case 'E':
356           for (st->bnkt = 1; st->bnkt <= thrmax; st->bnkt++)
357             st->bank[st->bnkt - 1] = st->bnkt;
358           st->bnkt = thrmax;
359           break;
360         }
361     }
362   while (!(st->bnkt >= st->threads || st->ch == 'N' || st->ch == '\15' || st->ch == '#'));
363   if (st->bnkt == 0 && st->ch != 'N')
364     {
365       st->bnkt = 1;
366       st->bank[0] = thr;
367     }
368   palupdate (st, False);
369 }
370
371 static void
372 bankmod (char boolop, Bool * Bool_)
373 {
374   switch (boolop)
375     {
376     case 'T':
377       *Bool_ = !*Bool_;
378       break;
379     case 'Y':
380       *Bool_ = True;
381       break;
382     case 'N':
383       *Bool_ = False;
384       break;
385     }
386 }
387
388 static void
389 newonscreen (struct state *st, unsigned char thr)
390 {
391   linedata *LP = &st->thread[thr - 1];
392   LP->filled = False;
393   LP->dead = False;
394   LP->reclen = (LP->little) ? 
395         random1 (10) + 5 : random1 (rlmax - 30) + 30;
396   LP->deg = random1 (degs);
397   LP->y = random1 (st->hei);
398   LP->x = random1 (st->wid);
399   LP->recpos = 0;
400   LP->turnco = 2;
401   LP->turnsize = random1 (4) + 2;
402 }
403
404 static void
405 firstinit (struct state *st, unsigned char thr)
406 {
407   linedata *LP = &st->thread[thr - 1];
408   LP->col = thr + 1;
409   LP->prey = 0;
410   LP->tmode = 1;
411   LP->slice = degs / 3;
412   LP->orichar = 'R';
413   LP->spiturn = 5;
414   LP->selfbounce = False;
415   LP->realbounce = False;
416   LP->vhfollow = False;
417   LP->tailfollow = False;
418   LP->killwalls = False;
419   LP->little = False;
420   LP->ctinc = random1 (2) * 2 - 1;
421   LP->circturn = ((thr % 2) * 2 - 1) * ((thr - 1) % 7 + 1);
422   LP->tsc = 1;
423   LP->tslen = 6;
424   LP->turnseq[0] = 6;
425   LP->turnseq[1] = -6;
426   LP->turnseq[2] = 6;
427   LP->turnseq[3] = 6;
428   LP->turnseq[4] = -6;
429   LP->turnseq[5] = 6;
430   LP->tclim = (unsigned char) (((real) degs) / 2 / 12);
431 }
432
433 static void
434 maininit (struct state *st)
435 {
436   if (!st->instring)
437     {
438       int n = random1 (sizeof (sampleStrings) / sizeof (sampleStrings[0]));
439       st->instring = sampleStrings[n].str;
440       st->speed = sampleStrings[n].speed;
441     }
442   if (st->speed == 0)
443     {
444           st->speed = 200;
445     }
446   st->boxh = 10;
447   st->boxw = 10;
448   st->gridden = 0;
449   st->bordcorn = 0;
450   st->threads = 4;
451   st->curviness = 30;
452   st->bordcol = 1;
453   st->ogd = 8;
454   st->ch = '\0';
455   st->erasing = True;
456   {
457     unsigned char thr;
458     for (thr = 1; thr <= thrmax; thr++)
459       {
460         firstinit (st, thr);
461         newonscreen (st, thr);
462       }
463   }
464   {
465     int d;
466     for (d = degs - 1; d >= 0; d--)
467       {
468         st->sinof[d] = sin (d * dtor);
469         st->cosof[d] = cos (d * dtor);
470         if (d % degs4 == 0)
471           st->tanof[d] = st->tanof[d + 1];
472         else
473           st->tanof[d] = tan (d * dtor);
474       }
475   }
476   randpal (st);
477 }
478
479 static Bool
480 move (struct state *st, unsigned char thr)
481 {
482   linedata *LP = &st->thread[thr - 1];
483   if (LP->dead)
484     return (False);
485   if (LP->prey == 0)
486     switch (LP->tmode)
487       {
488       case 1:
489         LP->deg += random1 (2 * LP->turnsize + 1) - LP->turnsize;
490         break;
491       case 2:
492         if (LP->slice == degs || LP->slice == degs2 || LP->slice == degs4)
493           {
494             if (LP->orichar == 'D')
495               {
496                 if (LP->deg % degs4 != degs8)
497                   LP->deg = degs4 * random1 (4) + degs8;
498               }
499             else if (LP->orichar == 'V')
500               if (LP->deg % degs4 != 0)
501                 LP->deg = degs4 * random1 (4);
502           }
503         if (random1 (100) == 0)
504           {
505             if (LP->slice == 0)
506               LP->deg = LP->deg - degs4 + random1 (degs2);
507             else
508               LP->deg += (random1 (2) * 2 - 1) * LP->slice;
509           }
510         break;
511       case 3:
512         LP->deg += LP->circturn;
513         break;
514       case 4:
515         if (abs (LP->spiturn) > 11)
516           LP->spiturn = 5;
517         else
518           LP->deg += LP->spiturn;
519         if (random1 (15 - abs (LP->spiturn)) == 0)
520           {
521             LP->spiturn += LP->ctinc;
522             if (abs (LP->spiturn) > 10)
523               LP->ctinc *= -1;
524           }
525         break;
526       case 5:
527         LP->turnco = abs (LP->turnco) - 1;
528         if (LP->turnco == 0)
529           {
530             LP->turnco = st->curviness + random1 (10);
531             LP->circturn *= -1;
532           }
533         LP->deg += LP->circturn;
534         break;
535       case 6:
536         if (abs (LP->turnco) == 1)
537           LP->turnco *= -1 * (random1 (degs2 / abs (LP->circturn)) + 5);
538         else if (LP->turnco == 0)
539           LP->turnco = 2;
540         else if (LP->turnco > 0)
541           {
542             LP->turnco--;
543             LP->deg += LP->circturn;
544           }
545         else
546           LP->turnco++;
547         break;
548       case 7:
549         LP->turnco++;
550         if (LP->turnco > LP->tclim)
551           {
552             LP->turnco = 1;
553             LP->tsc = (LP->tsc % LP->tslen) + 1;
554           }
555         LP->deg += LP->turnseq[LP->tsc - 1];
556         break;
557       }
558   else
559     {
560       int desdeg;
561       real dy, dx;
562       if (LP->tailfollow || LP->prey == thr)
563         {
564           dx = st->thread[LP->prey - 1].xrec[st->thread[LP->prey - 1].recpos] - LP->x;
565           dy = st->thread[LP->prey - 1].yrec[st->thread[LP->prey - 1].recpos] - LP->y;
566         }
567       else
568         {
569           dx = st->thread[LP->prey - 1].x - LP->x;
570           dy = st->thread[LP->prey - 1].y - LP->y;
571         }
572       desdeg =
573         (LP->vhfollow) ?
574         ((fabs (dx) > fabs (dy)) ?
575          ((dx > 0) ?
576           0 * degs4
577           :
578           2 * degs4)
579          :
580          ((dy > 0) ?
581           1 * degs4
582           :
583           3 * degs4))
584         :
585         ((dx > 0) ?
586          ((dy > 0) ?
587           1 * degs8 : 7 * degs8) : ((dy > 0) ? 3 * degs8 : 5 * degs8));
588       if (desdeg - desdeg % degs4 != LP->deg - LP->deg % degs4
589           || LP->vhfollow)
590         {
591           if (!LP->vhfollow)
592            { 
593               /* Using atan2 here doesn't seem to slow things down: */
594               desdeg = atan2 (dy, dx) / dtor;
595               wraparound (desdeg, 0, degs);
596            }
597           if (abs (desdeg - LP->deg) <= abs (LP->circturn))
598             LP->deg = desdeg;
599           else
600             LP->deg +=
601               (desdeg > LP->deg) ?
602               ((desdeg - LP->deg > degs2) ?
603                -abs (LP->circturn) : abs (LP->circturn))
604               : ((LP->deg - desdeg > degs2) ?
605                  abs (LP->circturn) : -abs (LP->circturn));
606         }
607       else
608         LP->deg +=
609           (st->tanof[LP->deg] >
610            dy / dx) ? -abs (LP->circturn) : abs (LP->circturn);
611     }
612
613   wraparound (LP->deg, 0, degs);
614   {
615     unsigned char oldcol;
616     real oldy = LP->y, oldx = LP->x;
617     LP->x += st->cosof[LP->deg];
618     wraparound (LP->x, xmin, xmax + 1);
619     LP->y += st->sinof[LP->deg];
620     wraparound (LP->y, ymin, ymax + 1);
621 #define xi ((int) LP->x)
622 #define yi ((int) LP->y)
623
624     oldcol = gp (st, xi, yi);
625     if (oldcol != 0)
626       {
627         Bool vertwall = False, horiwall = False;
628         if (oldcol == 1 && ((LP->killwalls && st->gridden > 0) || LP->realbounce))
629           {
630             vertwall = (gp (st, xi, (int) oldy) == 1);
631             horiwall = (gp (st, (int) oldx, yi) == 1);
632           }
633         if (oldcol == 1 && LP->realbounce && (vertwall || horiwall))
634           {
635             if (vertwall)
636               LP->deg = -LP->deg + degs2;
637             else
638               LP->deg = -LP->deg;
639           }
640         else
641           {
642             if ((oldcol != LP->col && LP->realbounce)
643                 || (oldcol == LP->col && LP->selfbounce))
644               LP->deg += degs4 * (random1 (2) * 2 - 1);
645             else if (oldcol != LP->col)
646               LP->deg += degs2;
647           }
648         if (LP->killwalls && st->gridden > 0 && oldcol == 1)
649           {
650             if (vertwall && xi + 1 <= xmax)
651               {
652                 int yy;
653                 for (yy = yi - yi % st->boxh;
654                      yy <= yi - yi % st->boxh + st->boxh && yy <= ymax; yy++)
655                   if (gp (st, xi + 1, yy) != 1 || yy == ymax)
656                     sp (st, xi, yy, 0);
657               }
658             if (horiwall && yi + 1 <= ymax)
659               {
660                 int xx;
661                 for (xx = xi - xi % st->boxw;
662                      xx <= xi - xi % st->boxw + st->boxw && xx <= xmax; xx++)
663                   if (gp (st, xx, yi + 1) != 1 || xx == xmax)
664                     sp (st, xx, yi, 0);
665               }
666           }
667         if (oldcol != LP->col || LP->selfbounce)
668           {
669             LP->x = oldx;
670             LP->y = oldy;
671           }
672         wraparound (LP->deg, 0, degs);
673       }
674   }
675
676   sp (st, xi, yi, LP->col);
677   if (LP->filled)
678     {
679       if (st->erasing)
680         sp (st, LP->xrec[LP->recpos], LP->yrec[LP->recpos], 0);
681       else
682         sp (st, LP->xrec[LP->recpos], LP->yrec[LP->recpos], LP->col + thrmax);
683     }
684   LP->yrec[LP->recpos] = yi;
685   LP->xrec[LP->recpos] = xi;
686   if (LP->recpos == LP->reclen - 1)
687     LP->filled = True;
688   if (LP->filled && !st->erasing)
689     {
690       int co = LP->recpos;
691       LP->dead = True;
692       do
693         {
694           int nextco = co + 1;
695           wraparound (nextco, 0, LP->reclen);
696           if (LP->yrec[co] != LP->yrec[nextco]
697               || LP->xrec[co] != LP->xrec[nextco])
698             LP->dead = False;
699           co = nextco;
700         }
701       while (!(!LP->dead || co == LP->recpos));
702     }
703   LP->recpos++;
704   wraparound (LP->recpos, 0, LP->reclen);
705   return (!LP->dead);
706 }
707
708 static unsigned long
709 vermiculate_draw (Display *dpy, Window window, void *closure)
710 {
711   struct state *st = (struct state *) closure;
712   int had_instring = (st->instring != 0);
713   int tick = 0;
714   int this_delay = 0;
715   int loop = 0;
716
717   
718  AGAIN:
719   if (st->reset_p) 
720     {
721       st->reset_p = 0;
722
723       clearscreen (st);
724       {
725         unsigned char thr;
726         for (thr = 1; thr <= st->threads; thr++)
727           newonscreen (st, thr);
728       }
729       if (st->autopal)
730         {
731           randpal (st);
732           palupdate (st, False);
733         }
734       bordupdate (st);
735       gridupdate (st, False);
736     }
737
738   {
739     Bool alltrap = True;
740     unsigned char thr;
741     for (thr = 1; thr <= st->threads; thr++)
742       if (move (st, thr))
743         alltrap = False;
744     if (alltrap)        /* all threads are trapped */
745       st->reset_p = True;
746     if (st->speed != SPEEDMAX)
747       this_delay = waitabit (st);
748   }
749
750   if (tick++ > st->max_ticks && !had_instring)
751     {
752       tick = 0;
753       st->instring = 0;
754       maininit(st);
755       st->reset_p = True;
756       st->autopal = False;
757     }
758
759   if (this_delay == 0 && loop++ < 1000)
760     goto AGAIN;
761
762   return this_delay;
763 }
764
765 static void *
766 vermiculate_init (Display *d, Window w)
767 {
768   struct state *st = (struct state *) calloc (1, sizeof(*st));
769   st->dpy = d;
770   st->window = w;
771   st->reset_p = 1;
772   st->instring = get_string_resource (st->dpy, "instring", "Instring");
773   if (st->instring && !*st->instring)
774     st->instring = 0;
775
776   st->max_ticks = get_integer_resource (st->dpy, "ticks", "Integer");
777   {
778     int temp = get_integer_resource (st->dpy, "speed", "Speed");
779     if (temp != 0)
780       st->speed = temp;
781   }
782
783   st->mycolors[0].pixel = BlackPixel (st->dpy, DefaultScreen (st->dpy));
784
785   XSetWindowBackground (st->dpy, st->window,
786                         BlackPixel (st->dpy, DefaultScreen (st->dpy)));
787   {
788     XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
789     st->wid = st->xgwa.width;
790     st->hei = st->xgwa.height;
791     st->mycmap = st->xgwa.colormap;
792   }
793   {
794     XGCValues mygcv;
795     st->mygc = XCreateGC (st->dpy, st->window, 0, &mygcv);
796   }
797
798   st->point = (unsigned char *) calloc (1, st->wid * st->hei);
799   maininit (st);
800   palupdate (st, True);
801   consume_instring(st);
802   return st;
803 }
804
805
806 static void
807 vermiculate_reshape (Display *dpy, Window window, void *closure, 
808                  unsigned int w, unsigned int h)
809 {
810   struct state *st = (struct state *) closure;
811   st->wid = w;
812   st->hei = h;
813   free (st->point);
814   st->point = (unsigned char *) calloc (1, st->wid * st->hei);
815 }
816
817 static Bool
818 vermiculate_event (Display *dpy, Window window, void *closure, XEvent *event)
819 {
820   return False;
821 }
822
823 static void
824 consume_instring(struct state *st)
825 {
826   char boolop;
827
828           while (wasakeypressed (st))
829             {
830               st->ch = readkey (st);
831               switch (st->ch)
832                 {
833                 case 'M':
834                   st->ch = readkey (st);
835                   switch (st->ch)
836                     {
837                     case 'A':
838                     case 'N':
839                       {
840                         unsigned char othreads = st->threads;
841                         if (st->ch == 'N')
842                           st->threads = 0;
843                         do
844                           {
845                             st->ch = readkey (st);
846                             switch (st->ch)
847                               {
848                               case '1': case '2': case '3':
849                               case '4': case '5': case '6':
850                               case '7': case '8': case '9':
851                                 st->thread[++st->threads - 1].tmode = st->ch - '0';
852                                 break;
853                               case 'R':
854                                 st->thread[++st->threads - 1].tmode =
855                                   random1 (tmodes - '0') + 1;
856                                 break;
857                               }
858                           }
859                         while (!(st->ch == '\15' || st->ch == '#'
860                                  || st->threads == thrmax));
861                         if (st->threads == 0)
862                           st->threads = othreads;
863                         st->reset_p = 1;
864                       }
865                       break;
866                     }
867                   break;
868                 case 'C':
869                   pickbank (st);
870                   if (st->bnkt > 0)
871                     {
872                       st->ch = readkey (st);
873                       switch (st->ch)
874                         {
875                         case 'D':
876                           st->ch = readkey (st);
877                           switch (st->ch)
878                             {
879                             case '1': case '2': case '3':
880                             case '4': case '5': case '6':
881                             case '7': case '8': case '9':
882 /* Careful!  The following macro needs to be at the beginning of any
883 block in which it's invoked, since it declares variables: */
884 #define forallinbank(LDP) linedata *LDP; int bankc; \
885                 for (bankc = 1; \
886                 (LDP = &st->thread[st->bank[bankc - 1] - 1],    \
887                 bankc <= st->bnkt); bankc++)
888                               {
889                                 forallinbank (L) L->slice = degs / (st->ch - '0');
890                               }
891                               break;
892                             case 'M':
893                               {
894                                 forallinbank (L) L->slice = 0;
895                               }
896                               break;
897                             }
898                           break;
899                         case 'S':
900                           {
901                             forallinbank (L)
902                             {
903                               L->otslen = L->tslen;
904                               L->tslen = 0;
905                             }
906                           }
907                           do
908                             {
909                               char oldch = st->ch;
910                               st->ch = readkey (st);
911                               {
912                                 forallinbank (L)
913                                 {
914                                   switch (st->ch)
915                                     {
916                                     case '0':
917                                     case '1': case '2': case '3':
918                                     case '4': case '5': case '6':
919                                     case '7': case '8': case '9':
920                                       L->tslen++;
921                                       L->turnseq[L->tslen - 1] = st->ch - '0';
922                                       if (oldch == '-')
923                                         L->turnseq[L->tslen - 1] *= -1;
924                                       if (bankc % 2 == 0)
925                                         L->turnseq[L->tslen - 1] *= -1;
926                                       break;
927                                     }
928                                 }
929                               }
930                             }
931                           while (!(st->ch == '\15' || st->ch == '#'
932                                    || st->thread[st->bank[0] - 1].tslen == 50));
933                           {
934                             forallinbank (L)
935                             {
936                               int seqSum = 0, c;
937
938                               if (L->tslen == 0)
939                                 L->tslen = L->otslen;
940                               for (c = 1; c <= L->tslen; c++)
941                                 seqSum += L->turnseq[c - 1];
942                               if (seqSum == 0)
943                                 L->tclim = 1;
944                               else
945                                 L->tclim =
946                                   (int) (((real) degs2) / abs (seqSum));
947                               L->tsc = random1 (L->tslen) + 1;
948                             }
949                           }
950                           break;
951                         case 'T':
952                           {
953                             st->ch = readkey (st);
954                             {
955                               forallinbank (L)
956                               {
957                                 switch (st->ch)
958                                   {
959                                   case '1': case '2': case '3':
960                                   case '4': case '5': case '6':
961                                   case '7': case '8': case '9':
962                                     L->tmode = st->ch - '0';
963                                     break;
964                                   case 'R':
965                                     L->tmode = random1 (tmodes - '0') + 1;
966                                     break;
967                                   }
968                               }
969                             }
970                           }
971                           break;
972                         case 'O':
973                           st->ch = readkey (st);
974                           {
975                             forallinbank (L) L->orichar = st->ch;
976                           }
977                           break;
978                         case 'F':
979                           {
980                             banktype fbank;
981                             arrcpy (fbank, st->bank);
982                             {
983                               int fbnkt = st->bnkt;
984                               int bankc;
985                               pickbank (st);
986                               for (bankc = 1; bankc <= fbnkt; bankc++)
987                                 {
988                                   linedata *L = &st->thread[fbank[bankc - 1] - 1];
989                                   if (st->ch == 'N')
990                                     L->prey = 0;
991                                   else
992                                     L->prey = st->bank[0 + (bankc - 1) % st->bnkt];
993                                 }
994                             }
995                           }
996                           break;
997                         case 'L':
998                           {
999                             forallinbank (L) L->prey = st->bank[bankc % st->bnkt];
1000                           }
1001                           break;
1002                         case 'R':
1003                           st->ch = readkey (st);
1004                           {
1005                             forallinbank (L) switch (st->ch)
1006                               {
1007                               case '1': case '2': case '3':
1008                               case '4': case '5': case '6':
1009                               case '7': case '8': case '9':
1010                                 L->circturn = 10 - (st->ch - '0');
1011                                 break;
1012                               case 'R':
1013                                 L->circturn = random1 (7) + 1;
1014                                 break;
1015                               }
1016                           }
1017                           break;
1018                         }
1019                     }
1020                   break;
1021                 case 'T':
1022                 case 'Y':
1023                 case 'N':
1024                   boolop = st->ch;
1025                   pickbank (st);
1026                   if (st->bnkt > 0)
1027                     {
1028                       st->ch = readkey (st);
1029                       {
1030                         forallinbank (L)
1031                         {
1032                           switch (st->ch)
1033                             {
1034                             case 'S':
1035                               bankmod (boolop, &L->selfbounce);
1036                               break;
1037                             case 'V':
1038                               bankmod (boolop, &L->vhfollow);
1039                               break;
1040                             case 'R':
1041                               bankmod (boolop, &L->realbounce);
1042                               break;
1043                             case 'L':
1044                               bankmod (boolop, &L->little);
1045                               st->cleared = True;
1046                               break;
1047                             case 'T':
1048                               bankmod (boolop, &L->tailfollow);
1049                               break;
1050                             case 'K':
1051                               bankmod (boolop, &L->killwalls);
1052                               break;
1053                             }
1054                         }
1055                       }
1056                     }
1057                   break;
1058                 case 'R':
1059                   if (st->bordcol == 1)
1060                     {
1061                       st->bordcol = 0;
1062                       bordupdate (st);
1063                       st->bordcorn = (st->bordcorn + 1) % 4;
1064                       st->bordcol = 1;
1065                       bordupdate (st);
1066                     }
1067                   break;
1068                 case '1': case '2': case '3':
1069                 case '4': case '5': case '6':
1070                 case '7': case '8': case '9':
1071                   {
1072                     int c;
1073                     for (c = 1; c <= thrmax; c++)
1074                       st->thread[c - 1].tmode = st->ch - '0';
1075                   }
1076                   break;
1077                 case '\40':
1078                   st->cleared = True;
1079                   break;
1080                 case 'E':
1081                   st->erasing = !st->erasing;
1082                   break;
1083                 case 'P':
1084                   randpal (st);
1085                   palupdate (st, True);
1086                   break;
1087                 case 'G':
1088                   {
1089                     char dimch = 'B';
1090                     Bool gridchanged = True;
1091                     if (st->gridden == 0)
1092                       st->gridden = st->ogd;
1093                     do
1094                       {
1095                         int msize = 0;
1096                         if (gridchanged)
1097                           {
1098                             clearscreen (st);
1099                             gridupdate (st, True);
1100                           }
1101                         st->ch = readkey (st);
1102                         gridchanged = True;
1103                         switch (st->ch)
1104                           {
1105                           case '+':
1106                             msize = 1;
1107                             break;
1108                           case '-':
1109                             msize = -1;
1110                             break;
1111                           case ']':
1112                             if (st->gridden < 15)
1113                               st->gridden++;
1114                             break;
1115                           case '[':
1116                             if (st->gridden > 0)
1117                               st->gridden--;
1118                             break;
1119                           case 'O':
1120                             st->ogd = st->gridden;
1121                             st->gridden = 0;
1122                             break;
1123                           case 'S':
1124                             st->boxw = st->boxh;
1125                           case 'W':
1126                           case 'H':
1127                           case 'B':
1128                             dimch = st->ch;
1129                             break;
1130                           default:
1131                             gridchanged = False;
1132                           }
1133                         if (dimch == 'W' || dimch == 'B')
1134                           st->boxw += msize;
1135                         if (dimch == 'H' || dimch == 'B')
1136                           st->boxh += msize;
1137                         if (st->boxw == 0)
1138                           st->boxw = 1;
1139                         if (st->boxh == 0)
1140                           st->boxh = 1;
1141                       }
1142                     while (!(st->ch == '\15' || st->ch == '#' || st->ch == 'O'));
1143                     st->cleared = True;
1144                   }
1145                   break;
1146                 case 'A':
1147                   st->autopal = !st->autopal;
1148                   break;
1149                 case 'B':
1150                   st->bordcol = 1 - st->bordcol;
1151                   bordupdate (st);
1152                   break;
1153                 case '-':
1154                   st->speed -= SPEEDINC;
1155                   if (st->speed < 1)
1156                     st->speed = 1;
1157                   break;
1158                 case '+':
1159                   st->speed += SPEEDINC;
1160                   if (st->speed > SPEEDMAX)
1161                     st->speed = SPEEDMAX;
1162                   break;
1163                 case '/':
1164                   if (st->curviness > 5)
1165                     st->curviness -= 5;
1166                   break;
1167                 case '*':
1168                   if (st->curviness < 50)
1169                     st->curviness += 5;
1170                   break;
1171                 case ']':
1172                   if (st->threads < thrmax)
1173                     newonscreen (st, ++st->threads);
1174                   break;
1175                 case '[':
1176                   if (st->threads > 1)
1177                     {
1178                       linedata *L = &st->thread[st->threads - 1];
1179                       int lastpos = (L->filled) ? L->reclen - 1 : L->recpos;
1180                       int c;
1181                       for (c = 0; c <= lastpos; c++)
1182                         sp (st, L->xrec[c], L->yrec[c], 0);
1183                       st->threads--;
1184                     }
1185                   break;
1186                 }
1187             }
1188 }
1189
1190 static void
1191 vermiculate_free (Display *dpy, Window window, void *closure)
1192 {
1193   struct state *st = (struct state *) closure;
1194
1195   if (st->point)
1196     {
1197       free(st->point);
1198     }
1199   free (st);
1200 }
1201
1202
1203 static const char *vermiculate_defaults[] = {
1204   ".background: Black",
1205   "*ticks: 20000",
1206   "*fpsSolid:   true",
1207   "*speed: 0",
1208   "*instring: ",
1209   0
1210 };
1211
1212 static XrmOptionDescRec vermiculate_options[] = {
1213   {"-speed", ".speed", XrmoptionSepArg, 0},
1214   {"-instring", ".instring", XrmoptionSepArg, 0},
1215   {0, 0, 0, 0}
1216 };
1217
1218
1219 XSCREENSAVER_MODULE ("Vermiculate", vermiculate)