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
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
24 #ifdef VERMICULATE_STANDALONE
28 #include <X11/Xutil.h>
29 #include <X11/Xatom.h>
30 #include <X11/Xresource.h>
32 #include "screenhack.h"
34 #endif /* VERMICULATE_STANDALONE */
37 #define degs2 (degs/2)
38 #define degs4 (degs/4)
39 #define degs8 (degs/8)
40 #define dtor 0.0174532925 /* pi / degs2; */
42 #define tailmax (thrmax * 2 + 1)
44 #define ymax (hei - 1)
46 #define xmax (wid - 1)
51 #define wraparound(VAL,LOWER,UPPER) { \
53 VAL -= UPPER - LOWER; \
54 else if (VAL < LOWER) \
55 VAL += UPPER - LOWER; }
56 #define arrcpy(DEST,SRC) memcpy (DEST, SRC, sizeof(DEST))
59 typedef unsigned char banktype[thrmax];
61 typedef struct linedata
63 int deg, spiturn, turnco, turnsize;
69 int tmode, tsc, tslen, tclim, otslen, ctinc, reclen, recpos, circturn, prey,
71 int xrec[rlmax + 1], yrec[rlmax + 1];
73 Bool filled, killwalls, vhfollow,
74 selfbounce, tailfollow, realbounce, little;
78 #ifdef VERMICULATE_STANDALONE
79 static XEvent myevent;
80 static Bool use_root = False;
81 static unsigned char rgb[256][3];
84 char *progclass = "Vermiculate";
91 XrmOptionDescRec options[] = {
92 {"-speed", ".speed", XrmoptionSepArg, 0},
93 {"-instring", ".instring", XrmoptionSepArg, 0},
96 #endif /* VERMICULATE_STANDALONE */
98 static Display *mydpy;
99 static Window mywindow;
101 static Colormap mycmap;
102 static XWindowAttributes xgwa;
103 static Bool neednewkey = True;
104 static XColor mycolors[tailmax];
106 static int hei = 500, wid = 500, speed = 100;
107 static Bool erasing = True;
108 static char *instring = 0;
109 static int max_ticks;
111 static struct stringAndSpeed
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 }
132 static real sinof[degs], cosof[degs], tanof[degs];
133 static unsigned char *point;
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;
142 wasakeypressed (void)
144 if (!neednewkey || *instring != 0)
147 #ifdef VERMICULATE_STANDALONE
148 return !(neednewkey =
149 !XCheckWindowEvent (mydpy, mywindow, KeyPressMask, &myevent));
152 #endif /* VERMICULATE_STANDALONE */
161 #ifdef VERMICULATE_STANDALONE
165 XWindowEvent (mydpy, mywindow, KeyPressMask, &myevent);
166 XLookupString (&myevent.xkey, key_buffer, 1, &key_sym, NULL);
167 readkey_result = key_sym;
169 readkey_result = '#';
170 #endif /* VERMICULATE_STANDALONE */
175 readkey_result = *instring;
178 return toupper (readkey_result);
182 random1 (unsigned int i)
184 return (ya_random () % i);
202 XClearWindow (mydpy, mywindow);
203 memset (point, 0, wid * hei);
207 sp (int x, int y, int c)
209 XSetForeground (mydpy, mygc, mycolors[c].pixel);
210 XDrawPoint (mydpy, mywindow, mygc, x, y);
211 point[(wid * y) + x] = c;
217 return point[(wid * y) + x];
221 redraw (int x, int y, int width, int height)
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]);
231 palupdate (Bool forceUpdate)
233 if (forceUpdate || *instring == 0)
235 #ifdef VERMICULATE_STANDALONE
237 for (colnum = 0; colnum < tailmax; colnum++)
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]);
245 #endif /* VERMICULATE_STANDALONE */
246 redraw (xmin, ymin, wid, hei);
253 #ifdef VERMICULATE_STANDALONE
255 for (co = 1; co <= 255; co++)
256 for (ro = 0; ro <= 2; ro++)
258 rgb[co][ro] = random1 (20);
260 rgb[co][ro] = random1 (64);
261 for (ro = 0; ro <= 2; ro++)
264 int ncolors = tailmax - 1;
265 make_random_colormap (mydpy,
267 mycmap, &mycolors[1], &ncolors, True, True, 0, True);
268 if (ncolors < tailmax - 1)
271 for (c = 1; c < tailmax; c++)
272 mycolors[c].pixel = WhitePixel (mydpy, DefaultScreen (mydpy));
274 #endif /* VERMICULATE_STANDALONE */
278 gridupdate (Bool interruptible)
282 for (x = 0; x <= xmax && !(wasakeypressed () && interruptible); x += boxw)
283 for (y = 0; y <= ymax; y += boxh)
285 if (random1 (15) < gridden)
287 #define lesser(A,B) ( ((A)<(B)) ? (A) : (B) )
288 int max = lesser (x + boxw, xmax);
290 for (xc = x; xc <= max; xc++)
293 if (random1 (15) < gridden)
295 int max = lesser (y + boxh, ymax);
297 for (yc = y; yc <= max; yc++)
308 if (bordcorn == 0 || bordcorn == 1)
312 if (bordcorn == 0 || bordcorn == 3)
318 for (x = xmin; x <= xmax; x++)
319 sp (x, ybord, bordcol);
320 for (y = ymin; y <= ymax; y++)
321 sp (ybord, y, bordcol);
326 inbank (unsigned char thr)
330 for (c = 1; c <= bankt; c++)
331 if (bank[c - 1] == thr)
339 unsigned char thr = 1;
340 #ifdef VERMICULATE_STANDALONE
342 unsigned char orgb[256][3];
345 for (co = 2; co <= tailmax; co++)
346 for (ro = 0; ro <= 2; ro++)
348 #endif /* VERMICULATE_STANDALONE */
354 thr = thr % threads + 1;
355 #ifdef VERMICULATE_STANDALONE
356 for (co = 1; co <= threads; co++)
358 for (ro = 0; ro <= 2; ro++)
359 rgb[co + 1][ro] = 25;
361 for (ro = 0; ro <= 1; ro++)
362 rgb[co + 1][ro] = 60;
364 for (ro = 0; ro <= 2; ro++)
365 rgb[thr + 1][ro] = 60;
366 #endif /* VERMICULATE_STANDALONE */
380 wraparound (thr, 1, threads + 1);
382 while (inbank (thr));
385 bank[++bankt - 1] = thr;
387 case '1': case '2': case '3':
388 case '4': case '5': case '6':
389 case '7': case '8': case '9':
391 bank[++bankt - 1] = ch - '0';
392 if (bank[bankt - 1] > threads)
400 for (c = 1; c <= threads; c++)
402 tbank[++tbankt - 1] = c;
404 arrcpy (bank, tbank);
411 case '1': case '2': case '3':
412 case '4': case '5': case '6':
413 case '7': case '8': case '9':
417 for (c = 1; c <= threads; c++)
418 if (thread[c - 1].tmode == ch - '0')
419 bank[++bankt - 1] = c;
425 for (bankt = 1; bankt <= threads; bankt++)
426 bank[bankt - 1] = bankt;
430 for (bankt = 1; bankt <= thrmax; bankt++)
431 bank[bankt - 1] = bankt;
436 while (!(bankt >= threads || ch == 'N' || ch == '\15' || ch == '#'));
437 if (bankt == 0 && ch != 'N')
442 #ifdef VERMICULATE_STANDALONE
444 #endif /* VERMICULATE_STANDALONE */
449 bankmod (Bool * Bool_)
466 newonscreen (unsigned char thr)
468 linedata *LP = &thread[thr - 1];
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);
478 LP->turnsize = random1 (4) + 2;
482 firstinit (unsigned char thr)
484 linedata *LP = &thread[thr - 1];
488 LP->slice = degs / 3;
491 LP->selfbounce = False;
492 LP->realbounce = False;
493 LP->vhfollow = False;
494 LP->tailfollow = False;
495 LP->killwalls = False;
497 LP->ctinc = random1 (2) * 2 - 1;
498 LP->circturn = ((thr % 2) * 2 - 1) * ((thr - 1) % 7 + 1);
507 LP->tclim = (unsigned char) (((real) degs) / 2 / 12);
515 int n = random1 (sizeof (sampleStrings) / sizeof (sampleStrings[0]));
516 instring = sampleStrings[n].str;
517 speed = sampleStrings[n].speed;
531 for (thr = 1; thr <= thrmax; thr++)
539 for (d = degs - 1; d >= 0; d--)
541 sinof[d] = sin (d * dtor);
542 cosof[d] = cos (d * dtor);
544 tanof[d] = tanof[d + 1];
546 tanof[d] = tan (d * dtor);
553 move (unsigned char thr)
555 linedata *LP = &thread[thr - 1];
562 LP->deg += random1 (2 * LP->turnsize + 1) - LP->turnsize;
565 if (LP->slice == degs || LP->slice == degs2 || LP->slice == degs4)
567 if (LP->orichar == 'D')
569 if (LP->deg % degs4 != degs8)
570 LP->deg = degs4 * random1 (4) + degs8;
572 else if (LP->orichar == 'V')
573 if (LP->deg % degs4 != 0)
574 LP->deg = degs4 * random1 (4);
576 if (random1 (100) == 0)
579 LP->deg = LP->deg - degs4 + random1 (degs2);
581 LP->deg += (random1 (2) * 2 - 1) * LP->slice;
585 LP->deg += LP->circturn;
588 if (abs (LP->spiturn) > 11)
591 LP->deg += LP->spiturn;
592 if (random1 (15 - abs (LP->spiturn)) == 0)
594 LP->spiturn += LP->ctinc;
595 if (abs (LP->spiturn) > 10)
600 LP->turnco = abs (LP->turnco) - 1;
603 LP->turnco = curviness + random1 (10);
606 LP->deg += LP->circturn;
609 if (abs (LP->turnco) == 1)
610 LP->turnco *= -1 * (random1 (degs2 / abs (LP->circturn)) + 5);
611 else if (LP->turnco == 0)
613 else if (LP->turnco > 0)
616 LP->deg += LP->circturn;
623 if (LP->turnco > LP->tclim)
626 LP->tsc = (LP->tsc % LP->tslen) + 1;
628 LP->deg += LP->turnseq[LP->tsc - 1];
635 if (LP->tailfollow || LP->prey == thr)
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;
642 dx = thread[LP->prey - 1].x - LP->x;
643 dy = thread[LP->prey - 1].y - LP->y;
647 ((fabs (dx) > fabs (dy)) ?
660 1 * degs8 : 7 * degs8) : ((dy > 0) ? 3 * degs8 : 5 * degs8));
661 if (desdeg - desdeg % degs4 != LP->deg - LP->deg % degs4
666 /* Using atan2 here doesn't seem to slow things down: */
667 desdeg = atan2 (dy, dx) / dtor;
668 wraparound (desdeg, 0, degs);
670 if (abs (desdeg - LP->deg) <= abs (LP->circturn))
675 ((desdeg - LP->deg > degs2) ?
676 -abs (LP->circturn) : abs (LP->circturn))
677 : ((LP->deg - desdeg > degs2) ?
678 abs (LP->circturn) : -abs (LP->circturn));
683 dy / dx) ? -abs (LP->circturn) : abs (LP->circturn);
686 wraparound (LP->deg, 0, degs);
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)
697 oldcol = gp (xi, yi);
700 Bool vertwall = False, horiwall = False;
701 if (oldcol == 1 && ((LP->killwalls && gridden > 0) || LP->realbounce))
703 vertwall = (gp (xi, (int) oldy) == 1);
704 horiwall = (gp ((int) oldx, yi) == 1);
706 if (oldcol == 1 && LP->realbounce && (vertwall || horiwall))
709 LP->deg = -LP->deg + degs2;
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)
721 if (LP->killwalls && gridden > 0 && oldcol == 1)
723 if (vertwall && xi + 1 <= xmax)
726 for (yy = yi - yi % boxh;
727 yy <= yi - yi % boxh + boxh && yy <= ymax; yy++)
728 if (gp (xi + 1, yy) != 1 || yy == ymax)
731 if (horiwall && yi + 1 <= ymax)
734 for (xx = xi - xi % boxw;
735 xx <= xi - xi % boxw + boxw && xx <= xmax; xx++)
736 if (gp (xx, yi + 1) != 1 || xx == xmax)
740 if (oldcol != LP->col || LP->selfbounce)
745 wraparound (LP->deg, 0, degs);
749 sp (xi, yi, LP->col);
753 sp (LP->xrec[LP->recpos], LP->yrec[LP->recpos], 0);
755 sp (LP->xrec[LP->recpos], LP->yrec[LP->recpos], LP->col + thrmax);
757 LP->yrec[LP->recpos] = yi;
758 LP->xrec[LP->recpos] = xi;
759 if (LP->recpos == LP->reclen - 1)
761 if (LP->filled && !erasing)
768 wraparound (nextco, 0, LP->reclen);
769 if (LP->yrec[co] != LP->yrec[nextco]
770 || LP->xrec[co] != LP->xrec[nextco])
774 while (!(!LP->dead || co == LP->recpos));
777 wraparound (LP->recpos, 0, LP->reclen);
782 vermiculate_main (void)
784 int had_instring = (instring != 0);
786 Bool halted = False, autopal = False, cleared;
787 point = (unsigned char *) malloc (wid * hei);
796 for (thr = 1; thr <= threads; thr++)
809 while (wasakeypressed ())
821 unsigned char othreads = threads;
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';
835 thread[++threads - 1].tmode =
836 random1 (tmodes - '0') + 1;
840 while (!(ch == '\15' || ch == '#'
841 || threads == thrmax));
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; \
867 (LDP = &thread[bank[bankc - 1] - 1], \
868 bankc <= bankt); bankc++)
870 forallinbank (L) L->slice = degs / (ch - '0');
875 forallinbank (L) L->slice = 0;
884 L->otslen = L->tslen;
898 case '1': case '2': case '3':
899 case '4': case '5': case '6':
900 case '7': case '8': case '9':
902 L->turnseq[L->tslen - 1] = ch - '0';
904 L->turnseq[L->tslen - 1] *= -1;
906 L->turnseq[L->tslen - 1] *= -1;
912 while (!(ch == '\15' || ch == '#'
913 || thread[bank[0] - 1].tslen == 50));
920 L->tslen = L->otslen;
921 for (c = 1; c <= L->tslen; c++)
922 seqSum += L->turnseq[c - 1];
927 (int) (((real) degs2) / abs (seqSum));
928 L->tsc = random1 (L->tslen) + 1;
940 case '1': case '2': case '3':
941 case '4': case '5': case '6':
942 case '7': case '8': case '9':
946 L->tmode = random1 (tmodes - '0') + 1;
956 forallinbank (L) L->orichar = ch;
962 arrcpy (fbank, bank);
967 for (bankc = 1; bankc <= fbankt; bankc++)
969 linedata *L = &thread[fbank[bankc - 1] - 1];
973 L->prey = bank[0 + (bankc - 1) % bankt];
980 forallinbank (L) L->prey = bank[bankc % bankt];
986 forallinbank (L) switch (ch)
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');
994 L->circturn = random1 (7) + 1;
1016 bankmod (&L->selfbounce);
1019 bankmod (&L->vhfollow);
1022 bankmod (&L->realbounce);
1025 bankmod (&L->little);
1029 bankmod (&L->tailfollow);
1032 bankmod (&L->killwalls);
1044 bordcorn = (bordcorn + 1) % 4;
1052 case '1': case '2': case '3':
1053 case '4': case '5': case '6':
1054 case '7': case '8': case '9':
1057 for (c = 1; c <= thrmax; c++)
1058 thread[c - 1].tmode = ch - '0';
1074 Bool gridchanged = True;
1115 gridchanged = False;
1117 if (dimch == 'W' || dimch == 'B')
1119 if (dimch == 'H' || dimch == 'B')
1126 while (!(ch == '\15' || ch == '#' || ch == 'O'));
1134 bordcol = 1 - bordcol;
1144 if (speed > SPEEDMAX)
1156 if (threads < thrmax)
1157 newonscreen (++threads);
1162 linedata *L = &thread[threads - 1];
1163 int lastpos = (L->filled) ? L->reclen - 1 : L->recpos;
1165 for (c = 0; c <= lastpos; c++)
1166 sp (L->xrec[c], L->yrec[c], 0);
1173 #ifdef VERMICULATE_STANDALONE
1176 while (XCheckWindowEvent
1177 (mydpy, mywindow, ExposureMask, &xe))
1180 case ConfigureNotify:
1181 wid = xe.xconfigure.width;
1182 hei = xe.xconfigure.height;
1184 point = (unsigned char *) malloc (wid * hei);
1189 redraw (xe.xexpose.x,
1190 xe.xexpose.y, xe.xexpose.width,
1196 screenhack_handle_events (mydpy);
1197 #endif /* VERMICULATE_STANDALONE */
1201 Bool alltrap = True;
1203 for (thr = 1; thr <= threads; thr++)
1206 if (alltrap) /* all threads are trapped */
1208 if (speed != SPEEDMAX)
1212 if (tick++ > max_ticks && !had_instring)
1221 while (!(halted || cleared));
1229 XSetWindowBackground (mydpy, mywindow,
1230 BlackPixel (mydpy, DefaultScreen (mydpy)));
1232 XGetWindowAttributes (mydpy, mywindow, &xgwa);
1235 mycmap = xgwa.colormap;
1239 XGetGCValues (mydpy, XDefaultGC (mydpy, XDefaultScreen (mydpy)),
1240 GCForeground, &mygcv);
1241 mygc = XCreateGC (mydpy, mywindow, GCForeground, &mygcv);
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
1252 GetVRoot (Display * dpy, int scr)
1254 Window rootReturn, parentReturn, *children;
1255 unsigned int numChildren;
1256 Window root = RootWindow (dpy, scr);
1257 Atom __SWM_VROOT = None;
1260 __SWM_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
1261 XQueryTree (dpy, root, &rootReturn, &parentReturn, &children, &numChildren);
1262 for (i = 0; i < numChildren; i++)
1266 unsigned long int nitems, bytesafter;
1267 Window *newRoot = NULL;
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
1280 XFree ((char *) children);
1285 main (int argc, char **argv)
1288 if ((mydpy = XOpenDisplay (NULL)) == NULL)
1290 fprintf (stderr, "%s: cannot connect to X server %s\n", argv[0],
1291 XDisplayName (NULL));
1295 for (argnum = 1; argnum < argc; argnum++)
1297 if (!strcmp (argv[argnum], "-geometry"))
1300 unsigned int uh, uw;
1301 XParseGeometry (argv[++argnum], &x, &y, &uw, &uh);
1305 else if (!strcmp (argv[argnum], "-instring"))
1306 instring = argv[++argnum];
1307 else if (!strcmp (argv[argnum], "-root"))
1309 else if (!strcmp (argv[argnum], "-speed"))
1310 speed = atoi (argv[++argnum]);
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);
1324 mywindow = GetVRoot (mydpy, DefaultScreen (mydpy));
1326 mywindow = XCreateSimpleWindow (mydpy, DefaultRootWindow (mydpy), 0, 0,
1327 wid, hei, 0, 0, BlackPixel (mydpy,
1330 XStoreName (mydpy, mywindow, "vermiculate");
1331 XMapWindow (mydpy, mywindow);
1333 XSelectInput (mydpy, mywindow, KeyPressMask | ExposureMask);
1338 vermiculate_main ();
1345 screenhack (Display * d, Window w)
1349 instring = get_string_resource ("instring", "Instring");
1350 max_ticks = get_integer_resource ("ticks", "Integer");
1352 int temp = get_integer_resource ("speed", "Speed");
1357 mycolors[0].pixel = BlackPixel (mydpy, DefaultScreen (mydpy));
1358 vermiculate_main ();
1360 #endif /* VERMICULATE_STANDALONE */