obsolete.computer

vermiculate-standalone/vermiculate.c

File Type: text/x-c

/*
 *  @(#) vermiculate.c
 *  @(#) Copyright (C) 2001 Tyler Pierce (email redacted)
 *  The full program, with documentation, is available at:
 *    http://freshmeat.net/projects/fdm
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/time.h>
#include <math.h>

#ifdef VERMICULATE_STANDALONE
#include "yarandom.h"
#include "usleep.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>
#else
#include "screenhack.h"
#include "config.h"
#endif /* VERMICULATE_STANDALONE */

#define degs 360
#define degs2 (degs/2)
#define degs4 (degs/4)
#define degs8 (degs/8)
#define dtor 0.0174532925   /*  pi / degs2; */
#define thrmax 120
#define tailmax (thrmax * 2 + 1)
#define tmodes '7'
#define ymax (hei - 1)
#define ymin 0
#define xmax (wid - 1)
#define xmin 0
#define rlmax 200
#define SPEEDINC 10
#define SPEEDMAX 1000
#define wraparound(VAL,LOWER,UPPER) {   \
            if (VAL >= UPPER)   \
              VAL -= UPPER - LOWER; \
            else if (VAL < LOWER)   \
              VAL += UPPER - LOWER; }
#define arrcpy(DEST,SRC) memcpy (DEST, SRC, sizeof(DEST))

typedef double real;
typedef unsigned char banktype[thrmax];

typedef struct linedata
{
  int deg, spiturn, turnco, turnsize;
  unsigned char col;
  Bool dead;

  char orichar;
  real x, y;
  int tmode, tsc, tslen, tclim, otslen, ctinc, reclen, recpos, circturn, prey,
    slice;
  int xrec[rlmax + 1], yrec[rlmax + 1];
  int turnseq[50];
  Bool filled, killwalls, vhfollow,
    selfbounce, tailfollow, realbounce, little;
}
linedata;

#ifdef    VERMICULATE_STANDALONE
static XEvent myevent;
static Bool use_root = False;
static unsigned char rgb[256][3];

#else
char *progclass = "Vermiculate";

char *defaults[] = {
  ".ticks: 20000",
  0
};

XrmOptionDescRec options[] = {
  {"-speed", ".speed", XrmoptionSepArg, 0},
  {"-instring", ".instring", XrmoptionSepArg, 0},
  {0, 0, 0, 0}
};
#endif /* VERMICULATE_STANDALONE */

static Display *mydpy;
static Window mywindow;
static GC mygc;
static Colormap mycmap;
static XWindowAttributes xgwa;
static Bool neednewkey = True;
static XColor mycolors[tailmax];

static int hei = 600, wid = 800, speed = 100;
static Bool erasing = True;
static char *instring = 0;
static int max_ticks;

static struct stringAndSpeed
{
  char *str;
  int speed;
}
sampleStrings[] =
{
/*
  { "]]]]]]]]7ces123400-9-8#c123456#s9998880004#ma3#car9ma6#c-#r1", 600} ,
  { "bmarrrr#c1234#lc5678#lyet]", 600} ,
  { "AEBMN222222223#CAR9CAD4CAOV", 150} ,
  { "mn333#c23#f1#]]]]]]]]]]]3bc9#r9#c78#f9#ma4#", 600} ,
  { "AEBMN22222#CAD4CAORc1#f2#c1#r6", 100} ,
  { "mn6666666#c1i#f1#y2#sy2#vyety1#ry13i#l", 40} ,
  { "aebmnrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr#", 500} ,
  { "bg+++++++++++++++++++++++#mnrrrrrrrrrrrrrrrrrrrrrrr#y1#k", 500} ,
  { "BMN22222223#CAD4CAOVYAS", 150} ,
  { "aebmnrrrrrrrrrrrrrrrr#yaryakg--#", 100} ,
  { "mn6rrrrrrrrrrrrrrr#by1i#lcalc1#fnyav", 200 } ,
  { "mn1rrrrrrrrrrrrrrr#by1i#lcalc1#fn", 200 } ,
*/
  { "bmn1111#", 50 }
};

static real sinof[degs], cosof[degs], tanof[degs];
static unsigned char *point;

static linedata thread[thrmax];
static banktype bank;
static int bankt, boxw, boxh, curviness, gridden, ogd, bordcorn;
static unsigned char bordcol, threads;
static char ch, boolop;

static Bool
wasakeypressed (void)
{
  if (!neednewkey || *instring != 0)
    return True;
  else
#ifdef VERMICULATE_STANDALONE
    return !(neednewkey =
         !XCheckWindowEvent (mydpy, mywindow, KeyPressMask, &myevent));
#else
    return False;
#endif /* VERMICULATE_STANDALONE */
}

static char
readkey (void)
{
  char readkey_result;
  if (*instring == 0)
    {
#ifdef VERMICULATE_STANDALONE
      char key_buffer[1];
      KeySym key_sym;
      if (neednewkey)
    XWindowEvent (mydpy, mywindow, KeyPressMask, &myevent);
      XLookupString (&myevent.xkey, key_buffer, 1, &key_sym, NULL);
      readkey_result = key_sym;
#else
      readkey_result = '#';
#endif /* VERMICULATE_STANDALONE */
      neednewkey = True;
    }
  else
    {
      readkey_result = *instring;
      instring++;
    };
  return toupper (readkey_result);
}

static unsigned int
random1 (unsigned int i)
{
  return (ya_random () % i);
}

static void
waitabit (void)
{
  static int cyc = 0;
  cyc += threads;
  while (cyc > speed)
    {
      usleep (10000);
      cyc -= speed;
    }
}

static void
clearscreen (void)
{
  XClearWindow (mydpy, mywindow);
  memset (point, 0, wid * hei);
}

static void
sp (int x, int y, int c)
{
  XSetForeground (mydpy, mygc, mycolors[c].pixel);
  XDrawPoint (mydpy, mywindow, mygc, x, y);
  point[(wid * y) + x] = c;
}

static int
gp (int x, int y)
{
  return point[(wid * y) + x];
}

static void
redraw (int x, int y, int width, int height)
{
  int xc, yc;
  for (xc = x; xc <= x + width - 1; xc++)
    for (yc = y; yc <= y + height - 1; yc++)
      if (point[wid * yc + xc] != 0)
    sp (xc, yc, point[wid * yc + xc]);
}

static void
palupdate (Bool forceUpdate)
{
  if (forceUpdate || *instring == 0)
    {
#ifdef VERMICULATE_STANDALONE
      int colnum;
      for (colnum = 0; colnum < tailmax; colnum++)
    {
      mycolors[colnum].red = rgb[colnum][0] << 10;
      mycolors[colnum].green = rgb[colnum][1] << 10;
      mycolors[colnum].blue = rgb[colnum][2] << 10;
      mycolors[colnum].flags = DoRed | DoBlue | DoGreen;
      XAllocColor (mydpy, mycmap, &mycolors[colnum]);
    };
#endif /* VERMICULATE_STANDALONE */
      redraw (xmin, ymin, wid, hei);
    }
}

static void
randpal (void)
{
#ifdef VERMICULATE_STANDALONE
  int co, ro;
  for (co = 1; co <= 255; co++)
    for (ro = 0; ro <= 2; ro++)
      if (co > tailmax)
    rgb[co][ro] = random1 (20);
      else
    rgb[co][ro] = random1 (64);
  for (ro = 0; ro <= 2; ro++)
    rgb[0][ro] = 0;
#else
  int ncolors = tailmax - 1;
  make_random_colormap (mydpy,
            xgwa.visual,
            mycmap, &mycolors[1], &ncolors, True, True, 0, True);
  if (ncolors < tailmax - 1)
    {
      int c;
      for (c = 1; c < tailmax; c++)
    mycolors[c].pixel = WhitePixel (mydpy, DefaultScreen (mydpy));
    }
#endif /* VERMICULATE_STANDALONE */
}

static void
gridupdate (Bool interruptible)
{
  int x, y;
  if (gridden > 0)
    for (x = 0; x <= xmax && !(wasakeypressed () && interruptible); x += boxw)
      for (y = 0; y <= ymax; y += boxh)
    {
      if (random1 (15) < gridden)
        {
#define lesser(A,B) ( ((A)<(B)) ? (A) : (B) )
          int max = lesser (x + boxw, xmax);
          int xc;
          for (xc = x; xc <= max; xc++)
        sp (xc, y, 1);
        }
      if (random1 (15) < gridden)
        {
          int max = lesser (y + boxh, ymax);
          int yc;
          for (yc = y; yc <= max; yc++)
        sp (x, yc, 1);
        }
    }
}

static void
bordupdate (void)
{
  int xbord, ybord;

  if (bordcorn == 0 || bordcorn == 1)
    ybord = ymin;
  else
    ybord = ymax;
  if (bordcorn == 0 || bordcorn == 3)
    xbord = xmin;
  else
    xbord = xmax;
  {
    int x, y;
    for (x = xmin; x <= xmax; x++)
      sp (x, ybord, bordcol);
    for (y = ymin; y <= ymax; y++)
      sp (ybord, y, bordcol);
  }
}

static Bool
inbank (unsigned char thr)
{
  int c;
  if (bankt > 0)
    for (c = 1; c <= bankt; c++)
      if (bank[c - 1] == thr)
    return True;
  return False;
}

static void
pickbank (void)
{
  unsigned char thr = 1;
#ifdef VERMICULATE_STANDALONE
  int co, ro;
  unsigned char orgb[256][3];

  arrcpy (orgb, rgb);
  for (co = 2; co <= tailmax; co++)
    for (ro = 0; ro <= 2; ro++)
      rgb[co][ro] = 25;
#endif /* VERMICULATE_STANDALONE */
  bankt = 0;
  ch = '\0';
  do
    {
      while (inbank (thr))
    thr = thr % threads + 1;
#ifdef VERMICULATE_STANDALONE
      for (co = 1; co <= threads; co++)
    {
      for (ro = 0; ro <= 2; ro++)
        rgb[co + 1][ro] = 25;
      if (inbank (co))
        for (ro = 0; ro <= 1; ro++)
          rgb[co + 1][ro] = 60;
    }
      for (ro = 0; ro <= 2; ro++)
    rgb[thr + 1][ro] = 60;
#endif /* VERMICULATE_STANDALONE */
      palupdate (False);
      ch = readkey ();
      palupdate (False);
      switch (ch)
    {
    case '+':
    case '-':
      do
        {
          if (ch == '+')
        thr++;
          else
        thr--;
          wraparound (thr, 1, threads + 1);
        }
      while (inbank (thr));
      break;
    case ' ':
      bank[++bankt - 1] = thr;
      break;
    case '1': case '2': case '3':
        case '4': case '5': case '6':
        case '7': case '8': case '9':

      bank[++bankt - 1] = ch - '0';
      if (bank[bankt - 1] > threads)
        bankt--;
      break;
    case 'I':
      {
        banktype tbank;
        int tbankt = 0;
        int c;
        for (c = 1; c <= threads; c++)
          if (!inbank (c))
        tbank[++tbankt - 1] = c;
        bankt = tbankt;
        arrcpy (bank, tbank);
      }
      break;
    case 'T':
      ch = readkey ();
      switch (ch)
        {
        case '1': case '2': case '3':
            case '4': case '5': case '6':
            case '7': case '8': case '9':

          {
        int c;
        for (c = 1; c <= threads; c++)
          if (thread[c - 1].tmode == ch - '0')
            bank[++bankt - 1] = c;
          }
          break;
        }
      break;
    case 'A':
      for (bankt = 1; bankt <= threads; bankt++)
        bank[bankt - 1] = bankt;
      bankt = threads;
      break;
    case 'E':
      for (bankt = 1; bankt <= thrmax; bankt++)
        bank[bankt - 1] = bankt;
      bankt = thrmax;
      break;
    }
    }
  while (!(bankt >= threads || ch == 'N' || ch == '\15' || ch == '#'));
  if (bankt == 0 && ch != 'N')
    {
      bankt = 1;
      bank[0] = thr;
    }
#ifdef VERMICULATE_STANDALONE
  arrcpy (rgb, orgb);
#endif /* VERMICULATE_STANDALONE */
  palupdate (False);
}

static void
bankmod (Bool * Bool_)
{
  switch (boolop)
    {
    case 'T':
      *Bool_ = !*Bool_;
      break;
    case 'Y':
      *Bool_ = True;
      break;
    case 'N':
      *Bool_ = False;
      break;
    }
}

static void
newonscreen (unsigned char thr)
{
  linedata *LP = &thread[thr - 1];
  LP->filled = False;
  LP->dead = False;
  LP->reclen = (LP->little) ? 
    random1 (10) + 5 : random1 (rlmax - 30) + 30;
  LP->deg = random1 (degs);
  LP->y = random1 (hei);
  LP->x = random1 (wid);
  LP->recpos = 0;
  LP->turnco = 2;
  LP->turnsize = random1 (4) + 2;
}

static void
firstinit (unsigned char thr)
{
  linedata *LP = &thread[thr - 1];
  LP->col = thr + 1;
  LP->prey = 0;
  LP->tmode = 1;
  LP->slice = degs / 3;
  LP->orichar = 'R';
  LP->spiturn = 5;
  LP->selfbounce = False;
  LP->realbounce = False;
  LP->vhfollow = False;
  LP->tailfollow = False;
  LP->killwalls = False;
  LP->little = False;
  LP->ctinc = random1 (2) * 2 - 1;
  LP->circturn = ((thr % 2) * 2 - 1) * ((thr - 1) % 7 + 1);
  LP->tsc = 1;
  LP->tslen = 6;
  LP->turnseq[0] = 6;
  LP->turnseq[1] = -6;
  LP->turnseq[2] = 6;
  LP->turnseq[3] = 6;
  LP->turnseq[4] = -6;
  LP->turnseq[5] = 6;
  LP->tclim = (unsigned char) (((real) degs) / 2 / 12);
}

static void
maininit (void)
{
  if (!instring)
    {
      int n = random1 (sizeof (sampleStrings) / sizeof (sampleStrings[0]));
      instring = sampleStrings[n].str;
      speed = sampleStrings[n].speed;
    }
  boxh = 10;
  boxw = 10;
  gridden = 0;
  bordcorn = 0;
  threads = 4;
  curviness = 30;
  bordcol = 1;
  ogd = 8;
  ch = '\0';
  erasing = True;
  {
    unsigned char thr;
    for (thr = 1; thr <= thrmax; thr++)
      {
    firstinit (thr);
    newonscreen (thr);
      }
  }
  {
    int d;
    for (d = degs - 1; d >= 0; d--)
      {
    sinof[d] = sin (d * dtor);
    cosof[d] = cos (d * dtor);
    if (d % degs4 == 0)
      tanof[d] = tanof[d + 1];
    else
      tanof[d] = tan (d * dtor);
      }
  }
  randpal ();
}

static Bool
move (unsigned char thr)
{
  linedata *LP = &thread[thr - 1];
  if (LP->dead)
    return (False);
  if (LP->prey == 0)
    switch (LP->tmode)
      {
      case 1:
    LP->deg += random1 (2 * LP->turnsize + 1) - LP->turnsize;
    break;
      case 2:
    if (LP->slice == degs || LP->slice == degs2 || LP->slice == degs4)
      {
        if (LP->orichar == 'D')
          {
        if (LP->deg % degs4 != degs8)
          LP->deg = degs4 * random1 (4) + degs8;
          }
        else if (LP->orichar == 'V')
          if (LP->deg % degs4 != 0)
        LP->deg = degs4 * random1 (4);
      }
    if (random1 (100) == 0)
      {
        if (LP->slice == 0)
          LP->deg = LP->deg - degs4 + random1 (degs2);
        else
          LP->deg += (random1 (2) * 2 - 1) * LP->slice;
      }
    break;
      case 3:
    LP->deg += LP->circturn;
    break;
      case 4:
    if (abs (LP->spiturn) > 11)
      LP->spiturn = 5;
    else
      LP->deg += LP->spiturn;
    if (random1 (15 - abs (LP->spiturn)) == 0)
      {
        LP->spiturn += LP->ctinc;
        if (abs (LP->spiturn) > 10)
          LP->ctinc *= -1;
      }
    break;
      case 5:
    LP->turnco = abs (LP->turnco) - 1;
    if (LP->turnco == 0)
      {
        LP->turnco = curviness + random1 (10);
        LP->circturn *= -1;
      }
    LP->deg += LP->circturn;
    break;
      case 6:
    if (abs (LP->turnco) == 1)
      LP->turnco *= -1 * (random1 (degs2 / abs (LP->circturn)) + 5);
    else if (LP->turnco == 0)
      LP->turnco = 2;
    else if (LP->turnco > 0)
      {
        LP->turnco--;
        LP->deg += LP->circturn;
      }
    else
      LP->turnco++;
    break;
      case 7:
    LP->turnco++;
    if (LP->turnco > LP->tclim)
      {
        LP->turnco = 1;
        LP->tsc = (LP->tsc % LP->tslen) + 1;
      }
    LP->deg += LP->turnseq[LP->tsc - 1];
    break;
      }
  else
    {
      int desdeg;
      real dy, dx;
      if (LP->tailfollow || LP->prey == thr)
    {
      dx = thread[LP->prey - 1].xrec[thread[LP->prey - 1].recpos] - LP->x;
      dy = thread[LP->prey - 1].yrec[thread[LP->prey - 1].recpos] - LP->y;
    }
      else
    {
      dx = thread[LP->prey - 1].x - LP->x;
      dy = thread[LP->prey - 1].y - LP->y;
    }
      desdeg =
    (LP->vhfollow) ?
    ((fabs (dx) > fabs (dy)) ?
     ((dx > 0) ?
      0 * degs4
      :
      2 * degs4)
     :
     ((dy > 0) ?
      1 * degs4
      :
      3 * degs4))
    :
    ((dx > 0) ?
     ((dy > 0) ?
      1 * degs8 : 7 * degs8) : ((dy > 0) ? 3 * degs8 : 5 * degs8));
      if (desdeg - desdeg % degs4 != LP->deg - LP->deg % degs4
      || LP->vhfollow)
    {
      if (!LP->vhfollow)
           { 
              /* Using atan2 here doesn't seem to slow things down: */
              desdeg = atan2 (dy, dx) / dtor;
              wraparound (desdeg, 0, degs);
       }
      if (abs (desdeg - LP->deg) <= abs (LP->circturn))
        LP->deg = desdeg;
      else
        LP->deg +=
          (desdeg > LP->deg) ?
          ((desdeg - LP->deg > degs2) ?
           -abs (LP->circturn) : abs (LP->circturn))
          : ((LP->deg - desdeg > degs2) ?
         abs (LP->circturn) : -abs (LP->circturn));
    }
      else
    LP->deg +=
      (tanof[LP->deg] >
       dy / dx) ? -abs (LP->circturn) : abs (LP->circturn);
    }

  wraparound (LP->deg, 0, degs);
  {
    unsigned char oldcol;
    real oldy = LP->y, oldx = LP->x;
    LP->x += cosof[LP->deg];
    wraparound (LP->x, xmin, xmax + 1);
    LP->y += sinof[LP->deg];
    wraparound (LP->y, ymin, ymax + 1);
#define xi ((int) LP->x)
#define yi ((int) LP->y)

    oldcol = gp (xi, yi);
    if (oldcol != 0)
      {
    Bool vertwall = False, horiwall = False;
    if (oldcol == 1 && ((LP->killwalls && gridden > 0) || LP->realbounce))
      {
        vertwall = (gp (xi, (int) oldy) == 1);
        horiwall = (gp ((int) oldx, yi) == 1);
      }
    if (oldcol == 1 && LP->realbounce && (vertwall || horiwall))
      {
        if (vertwall)
          LP->deg = -LP->deg + degs2;
        else
          LP->deg = -LP->deg;
      }
    else
      {
        if ((oldcol != LP->col && LP->realbounce)
        || (oldcol == LP->col && LP->selfbounce))
          LP->deg += degs4 * (random1 (2) * 2 - 1);
        else if (oldcol != LP->col)
          LP->deg += degs2;
      }
    if (LP->killwalls && gridden > 0 && oldcol == 1)
      {
        if (vertwall && xi + 1 <= xmax)
          {
        int yy;
        for (yy = yi - yi % boxh;
             yy <= yi - yi % boxh + boxh && yy <= ymax; yy++)
          if (gp (xi + 1, yy) != 1 || yy == ymax)
            sp (xi, yy, 0);
          }
        if (horiwall && yi + 1 <= ymax)
          {
        int xx;
        for (xx = xi - xi % boxw;
             xx <= xi - xi % boxw + boxw && xx <= xmax; xx++)
          if (gp (xx, yi + 1) != 1 || xx == xmax)
            sp (xx, yi, 0);
          }
      }
    if (oldcol != LP->col || LP->selfbounce)
      {
        LP->x = oldx;
        LP->y = oldy;
      }
    wraparound (LP->deg, 0, degs);
      }
  }

  sp (xi, yi, LP->col);
  if (LP->filled)
    {
      if (erasing)
    sp (LP->xrec[LP->recpos], LP->yrec[LP->recpos], 0);
      else
    sp (LP->xrec[LP->recpos], LP->yrec[LP->recpos], LP->col + thrmax);
    }
  LP->yrec[LP->recpos] = yi;
  LP->xrec[LP->recpos] = xi;
  if (LP->recpos == LP->reclen - 1)
    LP->filled = True;
  if (LP->filled && !erasing)
    {
      int co = LP->recpos;
      LP->dead = True;
      do
    {
      int nextco = co + 1;
      wraparound (nextco, 0, LP->reclen);
      if (LP->yrec[co] != LP->yrec[nextco]
          || LP->xrec[co] != LP->xrec[nextco])
        LP->dead = False;
      co = nextco;
    }
      while (!(!LP->dead || co == LP->recpos));
    }
  LP->recpos++;
  wraparound (LP->recpos, 0, LP->reclen);
  return (!LP->dead);
}

static void
vermiculate_main (void)
{
  int had_instring = (instring != 0);
  int tick = 0;
  Bool halted = False, autopal = False, cleared;
  point = (unsigned char *) malloc (wid * hei);
  maininit ();
  palupdate (True);

  do
    {
      clearscreen ();
      {
    unsigned char thr;
    for (thr = 1; thr <= threads; thr++)
      newonscreen (thr);
      }
      if (autopal)
    {
      randpal ();
      palupdate (False);
    }
      bordupdate ();
      gridupdate (False);
      cleared = False;
      do
    {
      while (wasakeypressed ())
        {
          ch = readkey ();
          switch (ch)
        {
        case 'M':
          ch = readkey ();
          switch (ch)
            {
            case 'A':
            case 'N':
              {
            unsigned char othreads = threads;
            if (ch == 'N')
              threads = 0;
            do
              {
                ch = readkey ();
                switch (ch)
                  {
                              case '1': case '2': case '3':
                              case '4': case '5': case '6':
                              case '7': case '8': case '9':
                thread[++threads - 1].tmode = ch - '0';
                break;
                  case 'R':
                thread[++threads - 1].tmode =
                  random1 (tmodes - '0') + 1;
                break;
                  }
              }
            while (!(ch == '\15' || ch == '#'
                 || threads == thrmax));
            if (threads == 0)
              threads = othreads;
            cleared = True;
              }
              break;
            }
          break;
        case 'C':
          pickbank ();
          if (bankt > 0)
            {
              ch = readkey ();
              switch (ch)
            {
            case 'D':
              ch = readkey ();
              switch (ch)
                {
                            case '1': case '2': case '3':
                            case '4': case '5': case '6':
                            case '7': case '8': case '9':
/* Careful!  The following macro needs to be at the beginning of any
block in which it's invoked, since it declares variables: */
#define forallinbank(LDP) linedata *LDP; int bankc; \
        for (bankc = 1; \
        (LDP = &thread[bank[bankc - 1] - 1],    \
        bankc <= bankt); bankc++)
                  {
                forallinbank (L) L->slice = degs / (ch - '0');
                  }
                  break;
                case 'M':
                  {
                forallinbank (L) L->slice = 0;
                  }
                  break;
                }
              break;
            case 'S':
              {
                forallinbank (L)
                {
                  L->otslen = L->tslen;
                  L->tslen = 0;
                }
              }
              do
                {
                  char oldch = ch;
                  ch = readkey ();
                  {
                forallinbank (L)
                {
                  switch (ch)
                    {
                                    case '0':
                                    case '1': case '2': case '3':
                                    case '4': case '5': case '6':
                                    case '7': case '8': case '9':
                      L->tslen++;
                      L->turnseq[L->tslen - 1] = ch - '0';
                      if (oldch == '-')
                    L->turnseq[L->tslen - 1] *= -1;
                      if (bankc % 2 == 0)
                    L->turnseq[L->tslen - 1] *= -1;
                      break;
                    }
                }
                  }
                }
              while (!(ch == '\15' || ch == '#'
                   || thread[bank[0] - 1].tslen == 50));
              {
                forallinbank (L)
                {
                  int seqSum = 0, c;

                  if (L->tslen == 0)
                L->tslen = L->otslen;
                  for (c = 1; c <= L->tslen; c++)
                seqSum += L->turnseq[c - 1];
                  if (seqSum == 0)
                L->tclim = 1;
                  else
                L->tclim =
                  (int) (((real) degs2) / abs (seqSum));
                  L->tsc = random1 (L->tslen) + 1;
                }
              }
              break;
            case 'T':
              {
                ch = readkey ();
                {
                  forallinbank (L)
                  {
                switch (ch)
                  {
                                  case '1': case '2': case '3':
                                  case '4': case '5': case '6':
                                  case '7': case '8': case '9':
                    L->tmode = ch - '0';
                    break;
                  case 'R':
                    L->tmode = random1 (tmodes - '0') + 1;
                    break;
                  }
                  }
                }
              }
              break;
            case 'O':
              ch = readkey ();
              {
                forallinbank (L) L->orichar = ch;
              }
              break;
            case 'F':
              {
                banktype fbank;
                arrcpy (fbank, bank);
                {
                  int fbankt = bankt;
                  int bankc;
                  pickbank ();
                  for (bankc = 1; bankc <= fbankt; bankc++)
                {
                  linedata *L = &thread[fbank[bankc - 1] - 1];
                  if (ch == 'N')
                    L->prey = 0;
                  else
                    L->prey = bank[0 + (bankc - 1) % bankt];
                }
                }
              }
              break;
            case 'L':
              {
                forallinbank (L) L->prey = bank[bankc % bankt];
              }
              break;
            case 'R':
              ch = readkey ();
              {
                forallinbank (L) switch (ch)
                  {
                              case '1': case '2': case '3':
                              case '4': case '5': case '6':
                              case '7': case '8': case '9':
                L->circturn = 10 - (ch - '0');
                break;
                  case 'R':
                L->circturn = random1 (7) + 1;
                break;
                  }
              }
              break;
            }
            }
          break;
        case 'T':
        case 'Y':
        case 'N':
          boolop = ch;
          pickbank ();
          if (bankt > 0)
            {
              ch = readkey ();
              {
            forallinbank (L)
            {
              switch (ch)
                {
                case 'S':
                  bankmod (&L->selfbounce);
                  break;
                case 'V':
                  bankmod (&L->vhfollow);
                  break;
                case 'R':
                  bankmod (&L->realbounce);
                  break;
                case 'L':
                  bankmod (&L->little);
                  cleared = True;
                  break;
                case 'T':
                  bankmod (&L->tailfollow);
                  break;
                case 'K':
                  bankmod (&L->killwalls);
                  break;
                }
            }
              }
            }
          break;
        case 'R':
          if (bordcol == 1)
            {
              bordcol = 0;
              bordupdate ();
              bordcorn = (bordcorn + 1) % 4;
              bordcol = 1;
              bordupdate ();
            }
          break;
        case '\33':
          halted = True;
          break;
        case '1': case '2': case '3':
        case '4': case '5': case '6':
        case '7': case '8': case '9':
          {
            int c;
            for (c = 1; c <= thrmax; c++)
              thread[c - 1].tmode = ch - '0';
          }
          break;
        case '\40':
          cleared = True;
          break;
        case 'E':
          erasing = !erasing;
          break;
        case 'P':
          randpal ();
          palupdate (True);
          break;
        case 'G':
          {
            char dimch = 'B';
            Bool gridchanged = True;
            if (gridden == 0)
              gridden = ogd;
            do
              {
            int msize = 0;
            if (gridchanged)
              {
                clearscreen ();
                gridupdate (True);
              }
            ch = readkey ();
            gridchanged = True;
            switch (ch)
              {
              case '+':
                msize = 1;
                break;
              case '-':
                msize = -1;
                break;
              case ']':
                if (gridden < 15)
                  gridden++;
                break;
              case '[':
                if (gridden > 0)
                  gridden--;
                break;
              case 'O':
                ogd = gridden;
                gridden = 0;
                break;
              case 'S':
                boxw = boxh;
              case 'W':
              case 'H':
              case 'B':
                dimch = ch;
                break;
              default:
                gridchanged = False;
              }
            if (dimch == 'W' || dimch == 'B')
              boxw += msize;
            if (dimch == 'H' || dimch == 'B')
              boxh += msize;
            if (boxw == 0)
              boxw = 1;
            if (boxh == 0)
              boxh = 1;
              }
            while (!(ch == '\15' || ch == '#' || ch == 'O'));
            cleared = True;
          }
          break;
        case 'A':
          autopal = !autopal;
          break;
        case 'B':
          bordcol = 1 - bordcol;
          bordupdate ();
          break;
        case '-':
          speed -= SPEEDINC;
          if (speed < 1)
            speed = 1;
          break;
        case '+':
          speed += SPEEDINC;
          if (speed > SPEEDMAX)
            speed = SPEEDMAX;
          break;
        case '/':
          if (curviness > 5)
            curviness -= 5;
          break;
        case '*':
          if (curviness < 50)
            curviness += 5;
          break;
        case ']':
          if (threads < thrmax)
            newonscreen (++threads);
          break;
        case '[':
          if (threads > 1)
            {
              linedata *L = &thread[threads - 1];
              int lastpos = (L->filled) ? L->reclen - 1 : L->recpos;
              int c;
              for (c = 0; c <= lastpos; c++)
            sp (L->xrec[c], L->yrec[c], 0);
              threads--;
            }
          break;
        }
        }

#ifdef VERMICULATE_STANDALONE
      {
        XEvent xe;
        while (XCheckWindowEvent
           (mydpy, mywindow, ExposureMask, &xe))
          switch (xe.type)
        {
        case ConfigureNotify:
          wid = xe.xconfigure.width;
          hei = xe.xconfigure.height;
          free (point);
          point = (unsigned char *) malloc (wid * hei);
          cleared = True;
          break;
        case Expose:
          if (!cleared)
            redraw (xe.xexpose.x,
                xe.xexpose.y, xe.xexpose.width,
                xe.xexpose.height);
          break;
        }
      }
#else
      screenhack_handle_events (mydpy);
#endif /* VERMICULATE_STANDALONE */

      if (!cleared)
        {
          Bool alltrap = True;
          unsigned char thr;
          for (thr = 1; thr <= threads; thr++)
        if (move (thr))
          alltrap = False;
          if (alltrap)  /* all threads are trapped */
        cleared = True;
          if (speed != SPEEDMAX)
        waitabit ();
        }

          if (tick++ > max_ticks && !had_instring)
            {
              tick = 0;
              instring = 0;
              maininit();
              cleared = True;
              autopal = False;
            }
    }
      while (!(halted || cleared));
    }
  while (!halted);
}

void
commonXinit (void)
{
  XSetWindowBackground (mydpy, mywindow,
            BlackPixel (mydpy, DefaultScreen (mydpy)));
  {
    XGetWindowAttributes (mydpy, mywindow, &xgwa);
    wid = xgwa.width;
    hei = xgwa.height;
    mycmap = xgwa.colormap;
  }
  {
    XGCValues mygcv;
    XGetGCValues (mydpy, XDefaultGC (mydpy, XDefaultScreen (mydpy)),
          GCForeground, &mygcv);
    mygc = XCreateGC (mydpy, mywindow, GCForeground, &mygcv);
  }
}

#ifdef VERMICULATE_STANDALONE
/* Function Name: GetVRoot (slightly changed from the X Windows FAQ)
 * Description: Gets the root window, even if it's a virtual root
 * Arguments: the display and the screen
 * Returns: the root window for the client
 */
static Window
GetVRoot (Display * dpy, int scr)
{
  Window rootReturn, parentReturn, *children;
  unsigned int numChildren;
  Window root = RootWindow (dpy, scr);
  Atom __SWM_VROOT = None;
  int i;

  __SWM_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
  XQueryTree (dpy, root, &rootReturn, &parentReturn, &children, &numChildren);
  for (i = 0; i < numChildren; i++)
    {
      Atom actual_type;
      int actual_format;
      unsigned long int nitems, bytesafter;
      Window *newRoot = NULL;

      if (XGetWindowProperty (dpy, children[i], __SWM_VROOT, 0, 1,
                  False, XA_WINDOW, &actual_type, &actual_format,
                  &nitems, &bytesafter,
                  (unsigned char **) &newRoot) == Success
      && newRoot)
    {
      root = *newRoot;
      break;
    }
    }

  XFree ((char *) children);
  return root;
}

int
main (int argc, char **argv)
{
  int argnum;
  if ((mydpy = XOpenDisplay (NULL)) == NULL)
    {
      fprintf (stderr, "%s: cannot connect to X server %s\n", argv[0],
           XDisplayName (NULL));
      exit (1);
    }

  for (argnum = 1; argnum < argc; argnum++)
    {
      if (!strcmp (argv[argnum], "-geometry"))
    {
      int x, y;
      unsigned int uh, uw;
      XParseGeometry (argv[++argnum], &x, &y, &uw, &uh);
      hei = (int) uh;
      wid = (int) uw;
    }
      else if (!strcmp (argv[argnum], "-instring"))
    instring = argv[++argnum];
      else if (!strcmp (argv[argnum], "-root"))
    use_root = True;
      else if (!strcmp (argv[argnum], "-speed"))
    speed = atoi (argv[++argnum]);
      else
    {
      fprintf (stderr,
           "\nvermiculate options are:"
           "\n -speed NUMBER:  set speed, can be from 1 to %d."
           "\n -root:  use root window."
           "\n -instring STRING:  put STRING in kbd buffer."
           "\n -geometry WIDTHxHEIGHT \n", SPEEDMAX);
      exit (1);
    }
    }

  if (use_root)
    mywindow = GetVRoot (mydpy, DefaultScreen (mydpy));
  else
    mywindow = XCreateSimpleWindow (mydpy, DefaultRootWindow (mydpy), 0, 0,
                    wid, hei, 0, 0, BlackPixel (mydpy,
                                DefaultScreen
                                (mydpy)));
  XStoreName (mydpy, mywindow, "vermiculate");
  XMapWindow (mydpy, mywindow);
  commonXinit ();
  XSelectInput (mydpy, mywindow, KeyPressMask | ExposureMask);

#undef ya_rand_init
  ya_rand_init (0);

  vermiculate_main ();
  return 0;
}

#else

void
screenhack (Display * d, Window w)
{
  mydpy = d;
  mywindow = w;
  instring = get_string_resource ("instring", "Instring");
  max_ticks = get_integer_resource ("ticks", "Integer");
  {
    int temp = get_integer_resource ("speed", "Speed");
    if (temp != 0)
      speed = temp;
  }
  commonXinit ();
  mycolors[0].pixel = BlackPixel (mydpy, DefaultScreen (mydpy));
  vermiculate_main ();
}
#endif /* VERMICULATE_STANDALONE */

Meta