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