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
22 #include "screenhack.h"
25 #define degs2 (degs/2)
26 #define degs4 (degs/4)
27 #define degs8 (degs/8)
28 #define dtor 0.0174532925 /* pi / degs2; */
30 #define tailmax (thrmax * 2 + 1)
32 #define ymax (st->hei - 1)
34 #define xmax (st->wid - 1)
39 #define wraparound(VAL,LOWER,UPPER) { \
41 VAL -= UPPER - LOWER; \
42 else if (VAL < LOWER) \
43 VAL += UPPER - LOWER; }
44 #define arrcpy(DEST,SRC) memcpy (DEST, SRC, sizeof(DEST))
47 typedef unsigned char banktype[thrmax];
49 typedef struct linedata
51 int deg, spiturn, turnco, turnsize;
57 int tmode, tsc, tslen, tclim, otslen, ctinc, reclen, recpos, circturn, prey,
59 int xrec[rlmax + 1], yrec[rlmax + 1];
61 Bool filled, killwalls, vhfollow,
62 selfbounce, tailfollow, realbounce, little;
66 static const struct stringAndSpeed
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 }
97 XWindowAttributes xgwa;
98 XColor mycolors[tailmax];
101 Bool erasing, cleared, autopal;
105 real sinof[degs], cosof[degs], tanof[degs];
106 unsigned char *point;
108 linedata thread[thrmax];
111 int boxw, boxh, curviness, gridden, ogd, bordcorn;
112 unsigned char bordcol, threads;
122 consume_instring(struct state *st);
125 wasakeypressed (struct state *st)
127 if (*st->instring != 0)
134 readkey (struct state *st)
137 if (*st->instring == 0)
139 readkey_result = '#';
143 readkey_result = *st->instring;
146 return toupper (readkey_result);
150 random1 (unsigned int i)
152 return (ya_random () % i);
156 waitabit (struct state *st)
159 st->cyc += st->threads;
160 while (st->cyc > st->speed)
163 st->cyc -= st->speed;
169 clearscreen (struct state *st)
171 XClearWindow (st->dpy, st->window);
172 memset (st->point, 0, st->wid * st->hei);
176 sp (struct state *st, int x, int y, int c)
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;
184 gp (struct state *st, int x, int y)
186 return st->point[(st->wid * y) + x];
190 redraw (struct state *st, int x, int y, int width, int height)
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]);
200 palupdate (struct state *st, Bool forceUpdate)
202 if (forceUpdate || *st->instring == 0)
204 redraw (st, xmin, ymin, st->wid, st->hei);
209 randpal (struct state *st)
211 int ncolors = tailmax - 1;
212 make_random_colormap (st->xgwa.screen, st->xgwa.visual, st->mycmap,
213 &st->mycolors[1], &ncolors, True, True, 0, True);
214 if (ncolors < tailmax - 1)
217 for (c = 1; c < tailmax; c++)
218 st->mycolors[c].pixel = WhitePixel (st->dpy, DefaultScreen (st->dpy));
223 gridupdate (struct state *st, Bool interruptible)
227 for (x = 0; x <= xmax && !(wasakeypressed (st) && interruptible); x += st->boxw)
228 for (y = 0; y <= ymax; y += st->boxh)
230 if (random1 (15) < st->gridden)
232 #define lesser(A,B) ( ((A)<(B)) ? (A) : (B) )
233 int max = lesser (x + st->boxw, xmax);
235 for (xc = x; xc <= max; xc++)
238 if (random1 (15) < st->gridden)
240 int max = lesser (y + st->boxh, ymax);
242 for (yc = y; yc <= max; yc++)
249 bordupdate (struct state *st)
253 if (st->bordcorn == 0 || st->bordcorn == 1)
257 if (st->bordcorn == 0 || st->bordcorn == 3)
263 for (x = xmin; x <= xmax; x++)
264 sp (st, x, ybord, st->bordcol);
265 for (y = ymin; y <= ymax; y++)
266 sp (st, xbord, y, st->bordcol);
271 inbank (struct state *st, unsigned char thr)
275 for (c = 1; c <= st->bnkt; c++)
276 if (st->bank[c - 1] == thr)
282 pickbank (struct state *st)
284 unsigned char thr = 1;
289 while (inbank (st, thr))
290 thr = thr % st->threads + 1;
292 palupdate (st, False);
293 st->ch = readkey (st);
294 palupdate (st, False);
305 wraparound (thr, 1, st->threads + 1);
307 while (inbank (st, thr));
310 st->bank[++st->bnkt - 1] = thr;
312 case '1': case '2': case '3':
313 case '4': case '5': case '6':
314 case '7': case '8': case '9':
316 st->bank[++st->bnkt - 1] = st->ch - '0';
317 if (st->bank[st->bnkt - 1] > st->threads)
325 for (c = 1; c <= st->threads; c++)
327 tbank[++tbankt - 1] = c;
329 arrcpy (st->bank, tbank);
333 st->ch = readkey (st);
336 case '1': case '2': case '3':
337 case '4': case '5': case '6':
338 case '7': case '8': case '9':
342 for (c = 1; c <= st->threads; c++)
343 if (st->thread[c - 1].tmode == st->ch - '0')
344 st->bank[++st->bnkt - 1] = c;
350 for (st->bnkt = 1; st->bnkt <= st->threads; st->bnkt++)
351 st->bank[st->bnkt - 1] = st->bnkt;
352 st->bnkt = st->threads;
355 for (st->bnkt = 1; st->bnkt <= thrmax; st->bnkt++)
356 st->bank[st->bnkt - 1] = st->bnkt;
361 while (!(st->bnkt >= st->threads || st->ch == 'N' || st->ch == '\15' || st->ch == '#'));
362 if (st->bnkt == 0 && st->ch != 'N')
367 palupdate (st, False);
371 bankmod (char boolop, Bool * Bool_)
388 newonscreen (struct state *st, unsigned char thr)
390 linedata *LP = &st->thread[thr - 1];
393 LP->reclen = (LP->little) ?
394 random1 (10) + 5 : random1 (rlmax - 30) + 30;
395 LP->deg = random1 (degs);
396 LP->y = random1 (st->hei);
397 LP->x = random1 (st->wid);
400 LP->turnsize = random1 (4) + 2;
404 firstinit (struct state *st, unsigned char thr)
406 linedata *LP = &st->thread[thr - 1];
410 LP->slice = degs / 3;
413 LP->selfbounce = False;
414 LP->realbounce = False;
415 LP->vhfollow = False;
416 LP->tailfollow = False;
417 LP->killwalls = False;
419 LP->ctinc = random1 (2) * 2 - 1;
420 LP->circturn = ((thr % 2) * 2 - 1) * ((thr - 1) % 7 + 1);
429 LP->tclim = (unsigned char) (((real) degs) / 2 / 12);
433 maininit (struct state *st)
437 int n = random1 (sizeof (sampleStrings) / sizeof (sampleStrings[0]));
438 st->instring = sampleStrings[n].str;
439 st->speed = sampleStrings[n].speed;
453 for (thr = 1; thr <= thrmax; thr++)
456 newonscreen (st, thr);
461 for (d = degs - 1; d >= 0; d--)
463 st->sinof[d] = sin (d * dtor);
464 st->cosof[d] = cos (d * dtor);
466 st->tanof[d] = st->tanof[d + 1];
468 st->tanof[d] = tan (d * dtor);
475 move (struct state *st, unsigned char thr)
477 linedata *LP = &st->thread[thr - 1];
484 LP->deg += random1 (2 * LP->turnsize + 1) - LP->turnsize;
487 if (LP->slice == degs || LP->slice == degs2 || LP->slice == degs4)
489 if (LP->orichar == 'D')
491 if (LP->deg % degs4 != degs8)
492 LP->deg = degs4 * random1 (4) + degs8;
494 else if (LP->orichar == 'V')
495 if (LP->deg % degs4 != 0)
496 LP->deg = degs4 * random1 (4);
498 if (random1 (100) == 0)
501 LP->deg = LP->deg - degs4 + random1 (degs2);
503 LP->deg += (random1 (2) * 2 - 1) * LP->slice;
507 LP->deg += LP->circturn;
510 if (abs (LP->spiturn) > 11)
513 LP->deg += LP->spiturn;
514 if (random1 (15 - abs (LP->spiturn)) == 0)
516 LP->spiturn += LP->ctinc;
517 if (abs (LP->spiturn) > 10)
522 LP->turnco = abs (LP->turnco) - 1;
525 LP->turnco = st->curviness + random1 (10);
528 LP->deg += LP->circturn;
531 if (abs (LP->turnco) == 1)
532 LP->turnco *= -1 * (random1 (degs2 / abs (LP->circturn)) + 5);
533 else if (LP->turnco == 0)
535 else if (LP->turnco > 0)
538 LP->deg += LP->circturn;
545 if (LP->turnco > LP->tclim)
548 LP->tsc = (LP->tsc % LP->tslen) + 1;
550 LP->deg += LP->turnseq[LP->tsc - 1];
557 if (LP->tailfollow || LP->prey == thr)
559 dx = st->thread[LP->prey - 1].xrec[st->thread[LP->prey - 1].recpos] - LP->x;
560 dy = st->thread[LP->prey - 1].yrec[st->thread[LP->prey - 1].recpos] - LP->y;
564 dx = st->thread[LP->prey - 1].x - LP->x;
565 dy = st->thread[LP->prey - 1].y - LP->y;
569 ((fabs (dx) > fabs (dy)) ?
582 1 * degs8 : 7 * degs8) : ((dy > 0) ? 3 * degs8 : 5 * degs8));
583 if (desdeg - desdeg % degs4 != LP->deg - LP->deg % degs4
588 /* Using atan2 here doesn't seem to slow things down: */
589 desdeg = atan2 (dy, dx) / dtor;
590 wraparound (desdeg, 0, degs);
592 if (abs (desdeg - LP->deg) <= abs (LP->circturn))
597 ((desdeg - LP->deg > degs2) ?
598 -abs (LP->circturn) : abs (LP->circturn))
599 : ((LP->deg - desdeg > degs2) ?
600 abs (LP->circturn) : -abs (LP->circturn));
604 (st->tanof[LP->deg] >
605 dy / dx) ? -abs (LP->circturn) : abs (LP->circturn);
608 wraparound (LP->deg, 0, degs);
610 unsigned char oldcol;
611 real oldy = LP->y, oldx = LP->x;
612 LP->x += st->cosof[LP->deg];
613 wraparound (LP->x, xmin, xmax + 1);
614 LP->y += st->sinof[LP->deg];
615 wraparound (LP->y, ymin, ymax + 1);
616 #define xi ((int) LP->x)
617 #define yi ((int) LP->y)
619 oldcol = gp (st, xi, yi);
622 Bool vertwall = False, horiwall = False;
623 if (oldcol == 1 && ((LP->killwalls && st->gridden > 0) || LP->realbounce))
625 vertwall = (gp (st, xi, (int) oldy) == 1);
626 horiwall = (gp (st, (int) oldx, yi) == 1);
628 if (oldcol == 1 && LP->realbounce && (vertwall || horiwall))
631 LP->deg = -LP->deg + degs2;
637 if ((oldcol != LP->col && LP->realbounce)
638 || (oldcol == LP->col && LP->selfbounce))
639 LP->deg += degs4 * (random1 (2) * 2 - 1);
640 else if (oldcol != LP->col)
643 if (LP->killwalls && st->gridden > 0 && oldcol == 1)
645 if (vertwall && xi + 1 <= xmax)
648 for (yy = yi - yi % st->boxh;
649 yy <= yi - yi % st->boxh + st->boxh && yy <= ymax; yy++)
650 if (gp (st, xi + 1, yy) != 1 || yy == ymax)
653 if (horiwall && yi + 1 <= ymax)
656 for (xx = xi - xi % st->boxw;
657 xx <= xi - xi % st->boxw + st->boxw && xx <= xmax; xx++)
658 if (gp (st, xx, yi + 1) != 1 || xx == xmax)
662 if (oldcol != LP->col || LP->selfbounce)
667 wraparound (LP->deg, 0, degs);
671 sp (st, xi, yi, LP->col);
675 sp (st, LP->xrec[LP->recpos], LP->yrec[LP->recpos], 0);
677 sp (st, LP->xrec[LP->recpos], LP->yrec[LP->recpos], LP->col + thrmax);
679 LP->yrec[LP->recpos] = yi;
680 LP->xrec[LP->recpos] = xi;
681 if (LP->recpos == LP->reclen - 1)
683 if (LP->filled && !st->erasing)
690 wraparound (nextco, 0, LP->reclen);
691 if (LP->yrec[co] != LP->yrec[nextco]
692 || LP->xrec[co] != LP->xrec[nextco])
696 while (!(!LP->dead || co == LP->recpos));
699 wraparound (LP->recpos, 0, LP->reclen);
704 vermiculate_draw (Display *dpy, Window window, void *closure)
706 struct state *st = (struct state *) closure;
707 int had_instring = (st->instring != 0);
721 for (thr = 1; thr <= st->threads; thr++)
722 newonscreen (st, thr);
727 palupdate (st, False);
730 gridupdate (st, False);
736 for (thr = 1; thr <= st->threads; thr++)
739 if (alltrap) /* all threads are trapped */
741 if (st->speed != SPEEDMAX)
742 this_delay = waitabit (st);
745 if (tick++ > st->max_ticks && !had_instring)
754 if (this_delay == 0 && loop++ < 1000)
761 vermiculate_init (Display *d, Window w)
763 struct state *st = (struct state *) calloc (1, sizeof(*st));
767 st->instring = get_string_resource (st->dpy, "instring", "Instring");
768 if (st->instring && !*st->instring)
771 st->max_ticks = get_integer_resource (st->dpy, "ticks", "Integer");
772 st->speed = get_integer_resource (st->dpy, "speed", "Speed");
773 if (st->speed <= 0) st->speed = 1;
774 st->mycolors[0].pixel = BlackPixel (st->dpy, DefaultScreen (st->dpy));
776 XSetWindowBackground (st->dpy, st->window,
777 BlackPixel (st->dpy, DefaultScreen (st->dpy)));
779 XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
780 st->wid = st->xgwa.width;
781 st->hei = st->xgwa.height;
782 st->mycmap = st->xgwa.colormap;
786 st->mygc = XCreateGC (st->dpy, st->window, 0, &mygcv);
789 st->point = (unsigned char *) calloc (1, st->wid * st->hei);
791 palupdate (st, True);
792 consume_instring(st);
798 vermiculate_reshape (Display *dpy, Window window, void *closure,
799 unsigned int w, unsigned int h)
801 struct state *st = (struct state *) closure;
805 st->point = (unsigned char *) calloc (1, st->wid * st->hei);
809 vermiculate_event (Display *dpy, Window window, void *closure, XEvent *event)
811 struct state *st = (struct state *) closure;
812 if (screenhack_event_helper (dpy, window, event))
821 consume_instring(struct state *st)
825 while (wasakeypressed (st))
827 st->ch = readkey (st);
831 st->ch = readkey (st);
837 unsigned char othreads = st->threads;
842 st->ch = readkey (st);
845 case '1': case '2': case '3':
846 case '4': case '5': case '6':
847 case '7': case '8': case '9':
848 st->thread[++st->threads - 1].tmode = st->ch - '0';
851 st->thread[++st->threads - 1].tmode =
852 random1 (tmodes - '0') + 1;
856 while (!(st->ch == '\15' || st->ch == '#'
857 || st->threads == thrmax));
858 if (st->threads == 0)
859 st->threads = othreads;
869 st->ch = readkey (st);
873 st->ch = readkey (st);
876 case '1': case '2': case '3':
877 case '4': case '5': case '6':
878 case '7': case '8': case '9':
879 /* Careful! The following macro needs to be at the beginning of any
880 block in which it's invoked, since it declares variables: */
881 #define forallinbank(LDP) linedata *LDP; int bankc; \
883 ((bankc <= st->bnkt) ? ( \
884 (LDP = &st->thread[st->bank[bankc - 1] - 1], 1) \
887 forallinbank (L) L->slice = degs / (st->ch - '0');
892 forallinbank (L) L->slice = 0;
901 L->otslen = L->tslen;
908 st->ch = readkey (st);
915 case '1': case '2': case '3':
916 case '4': case '5': case '6':
917 case '7': case '8': case '9':
919 L->turnseq[L->tslen - 1] = st->ch - '0';
921 L->turnseq[L->tslen - 1] *= -1;
923 L->turnseq[L->tslen - 1] *= -1;
929 while (!(st->ch == '\15' || st->ch == '#'
930 || st->thread[st->bank[0] - 1].tslen == 50));
937 L->tslen = L->otslen;
938 for (c = 1; c <= L->tslen; c++)
939 seqSum += L->turnseq[c - 1];
944 (int) (((real) degs2) / abs (seqSum));
945 L->tsc = random1 (L->tslen) + 1;
951 st->ch = readkey (st);
957 case '1': case '2': case '3':
958 case '4': case '5': case '6':
959 case '7': case '8': case '9':
960 L->tmode = st->ch - '0';
963 L->tmode = random1 (tmodes - '0') + 1;
971 st->ch = readkey (st);
973 forallinbank (L) L->orichar = st->ch;
979 arrcpy (fbank, st->bank);
981 int fbnkt = st->bnkt;
984 for (bankc = 1; bankc <= fbnkt; bankc++)
986 linedata *L = &st->thread[fbank[bankc - 1] - 1];
990 L->prey = st->bank[0 + (bankc - 1) % st->bnkt];
997 forallinbank (L) L->prey = st->bank[bankc % st->bnkt];
1001 st->ch = readkey (st);
1003 forallinbank (L) switch (st->ch)
1005 case '1': case '2': case '3':
1006 case '4': case '5': case '6':
1007 case '7': case '8': case '9':
1008 L->circturn = 10 - (st->ch - '0');
1011 L->circturn = random1 (7) + 1;
1026 st->ch = readkey (st);
1033 bankmod (boolop, &L->selfbounce);
1036 bankmod (boolop, &L->vhfollow);
1039 bankmod (boolop, &L->realbounce);
1042 bankmod (boolop, &L->little);
1046 bankmod (boolop, &L->tailfollow);
1049 bankmod (boolop, &L->killwalls);
1057 if (st->bordcol == 1)
1061 st->bordcorn = (st->bordcorn + 1) % 4;
1066 case '1': case '2': case '3':
1067 case '4': case '5': case '6':
1068 case '7': case '8': case '9':
1071 for (c = 1; c <= thrmax; c++)
1072 st->thread[c - 1].tmode = st->ch - '0';
1079 st->erasing = !st->erasing;
1083 palupdate (st, True);
1088 Bool gridchanged = True;
1089 if (st->gridden == 0)
1090 st->gridden = st->ogd;
1097 gridupdate (st, True);
1099 st->ch = readkey (st);
1110 if (st->gridden < 15)
1114 if (st->gridden > 0)
1118 st->ogd = st->gridden;
1122 st->boxw = st->boxh;
1129 gridchanged = False;
1131 if (dimch == 'W' || dimch == 'B')
1133 if (dimch == 'H' || dimch == 'B')
1140 while (!(st->ch == '\15' || st->ch == '#' || st->ch == 'O'));
1145 st->autopal = !st->autopal;
1148 st->bordcol = 1 - st->bordcol;
1152 st->speed -= SPEEDINC;
1157 st->speed += SPEEDINC;
1158 if (st->speed > SPEEDMAX)
1159 st->speed = SPEEDMAX;
1162 if (st->curviness > 5)
1166 if (st->curviness < 50)
1170 if (st->threads < thrmax)
1171 newonscreen (st, ++st->threads);
1174 if (st->threads > 1)
1176 linedata *L = &st->thread[st->threads - 1];
1177 int lastpos = (L->filled) ? L->reclen - 1 : L->recpos;
1179 for (c = 0; c <= lastpos; c++)
1180 sp (st, L->xrec[c], L->yrec[c], 0);
1189 vermiculate_free (Display *dpy, Window window, void *closure)
1191 struct state *st = (struct state *) closure;
1201 static const char *vermiculate_defaults[] = {
1203 ".background: Black",
1209 "*ignoreRotation: True",
1214 static XrmOptionDescRec vermiculate_options[] = {
1215 {"-speed", ".speed", XrmoptionSepArg, 0},
1216 {"-instring", ".instring", XrmoptionSepArg, 0},
1221 XSCREENSAVER_MODULE ("Vermiculate", vermiculate)