Newsgroups: comp.sources.misc
From: ijp@doc.ic.ac.uk (Ian Palmer)
Subject:  v26i027:  scamper - Cellular Automata Simulator, Part04/10
Message-ID: <1991Nov20.233916.14380@sparky.imd.sterling.com>
X-Md4-Signature: d41e1ddff275ed76e7d8557a4eb19eb0
Date: Wed, 20 Nov 1991 23:39:16 GMT
Approved: kent@sparky.imd.sterling.com

Submitted-by: ijp@doc.ic.ac.uk (Ian Palmer)
Posting-number: Volume 26, Issue 27
Archive-name: scamper/part04
Environment: X11R4, SunOS

---- CUT HERE -------- CUT HERE -------- CUT HERE -------- CUT HERE ----
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  automata.c
# Wrapped by ijp@swan.doc.ic.ac.uk on Thu Nov  7 10:09:46 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'automata.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'automata.c'\"
else
echo shar: Extracting \"'automata.c'\" \(49495 characters\)
sed "s/^X//" >'automata.c' <<'END_OF_FILE'
X/*
X
X
X
X   *****                 *****                   *****                 *****
X  *     *               *     *                 *     *               *     *
X  *                     *     *                 *     *               *     *
X   **         *****     *     *    *       *    *     *     ******    *     *
X     *       *     *    *******    **     **    ******     *          ******
X      **     *          *     *    * *   * *    *          *          * *
X        *    *          *     *    *  * *  *    *          *          *  *
X  *     *    *          *     *    *   *   *    *           *****     *   *
X   *****     *          *     *    *       *    *          *          *    *
X             *                     *       *               *
X             *     *               *       *               *
X              *****                *       *                ******
X
X
X
X                                 by Ian Palmer
X
X                                      at
X
X              Imperial College of Science, Technology and Medicine
X                             University of London
X
X
X
X
X--------------------------------------------------------------------------------
X| Scamper is supplied "as is", without express or implied warranty.            |
X|                                                                              |
X| Permission to use, copy, modify and distribute this software, and any        |
X| documentation, for any non-commercial purpose is hereby granted without fee, |
X| provided that all copyright messages, and permission notices, remain intact. |
X|                                                                              |
X| Copyright 1991 by Ian Palmer of Imperial College of Science, Technology      |
X| and Medicine, University of London.                                          |
X--------------------------------------------------------------------------------
X
X   _____
X  /_  _/  Let Total Chaos reign forever !       I.J.Palmer,
X   / /  ___      ______________                 Department of Computing,
X  / /  / __\      |    _                        Imperial College,
X /_/  / /         | <> | /-\ |_                 180 Queen's Gate,
X      \ \__           /             _           London SW7 2BZ.
X       \___/          \ |-| /-\ <> <
X                      ______________>           ijp@doc.ic.ac.uk
X  PANIC NOW
X  and avoid       23. Add to Celt's pub meal and produce utter turmoil. (6,8)
X  the rush.       -----------------------------------------------------------
X
X
X*/
X
X/*
X      Program Segment      : automata.c
X
X      Task                 : Provide the main finctions of Scamper, including :
X                             - Main loop
X                             - X Event handling
X                             - Displaying domain
X                             - Scrolling and resizing the domain
X                             - Main menu (display and option selection)
X                             - Handling file options' selection
X                             - Cell allocation and user actions on domain
X*/
X
X#include <stdlib.h>
X#include <stdio.h>
X#include <X11/Xlib.h>
X#include "windows.h"
X#include "domain.h"
X#include "calscan.h"
X#include "automata.h"
X#include "stats.h"
X#include "colour.h"
X#include "rules.h"
X#define offset 28
X
Xstruct box boxes[MaxBoxes] ;
Xstruct abox askbox[8] ;
Xstruct state states[max_states];
XCELL array[MAX_X][MAX_Y] ;
X
Xint running , generation , display , update , start_x , update , updateX ;
Xint updateC , fnamel , shape, stopat , displaychange , torus ;
Xint max_x , max_y , new, old , mode , start_y , Asking , greyscale ;
Xint scale , size_x , end_x , end_y , size_y, updatesmall ;
Xint old_x , old_y , counter, old_op, dscale , tscale , cropX =0 , cropY =0 ;
Xint step , Exit = 0 , updown = 0 , AskVal , AskOp, mk[max_neigh] ;
Xint sccol , bycol , scnum , numdir , numloop , dirtype , loopnum , change ;
X
Xfloat hscale , wscale ;
X
Xchar FileName[110] , message[100] ;
Xchar theauthor[] ="b y  I.  J.  P a l m e r";             /* The author, to be placed at the top of the main menu */
X
Xstruct button {
X  int value ;
X  int state ;
X} buttons[3];
X
X/* ------------------------------------------------------------------------- */
X
Xvoid change_domain(change_to) int change_to ;
X/* Toggles the logical handling of the domain between a torus and a flat plane.
X  
X   Status changes : torus
X*/
X{
X  torus = change_to ;                       /* Set the new value of torus */
X  if (torus)
X    link_cells(shape,0);                    /* If we now have a torus domain, link the cells */
X  else {
X    setup_array(0,0,max_x+1,max_y+1,1);     /* Otherwise, enlarge the domain by 1 in each direction */
X    link_cells(shape,0);                    /* and then link the cells */
X    };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid setup_array ( sx,sy,fx,fy,clear ) int sx,sy,fx,fy,clear ;
X/* Allocates space for unallocated cells in the domain. Called when expanding the size of the domain.
X
X   Pre : sx < fx < MAX_X   &   sy < fy < MAX_Y
X   Post: for all x,y : sx <= x < fx  &  sy <= y < fy   then   array[x][y] exists
X   
X   Status changes : none
X*/
X{
X
X  int x,y;
X  if (!torus) {
X    ++fx ;
X    ++fy ;
X    };
X  XSetForeground( dpy,smallgc,colours[states[0].colour].mapno);   /* Set the colour to black */
X  for( x=sx ; x<fx ; x+=1 ) 
X    for( y=sy ; y < fy ; y+=1)                                    /* Go through all new cell positions */
X      if (array[x][y] == NULL) {                                  /* If the cell is unallocated */  
X        array[x][y] = (CELL) malloc(sizeof(struct cell));         /* then allocate it some space */
X        if ((clear) && updatesmall)
X          XDrawPoint( dpy, small, smallgc, x, y );                /* Make sure it starts off dead */
X        array[x][y] -> state[1] = 0 ;
X        array[x][y] -> state[0] = 0 ;
X        };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid resize()
X/* Sets global variables dictating start and end cells for displayed domain.
X   If using hexagonal neighbourhood then a template hexagon is set up to speed up drawing.
X
X   Status changes : none
X*/
X{
X  int z ;
X  if (shape == 2) {
X    end_y = start_y + (size_y = (DispY/scale/3.4)+2);       /* If using hexagonal cells, work out the last cells to */  
X    end_x = start_x + (size_x = (DispX/scale/3)+2);         /* which can be displayed when viewing the domain */
X    z = 1.7320508*scale+1 ;
X    hexagon[1].x = 2 * scale ;                              /* Create the right size of hexagon for drawing */
X    hexagon[1].y = 0 ;
X    hexagon[2].x = scale ;
X    hexagon[2].y = -z ;
X    hexagon[3].x = - scale ;
X    hexagon[3].y = -z ;
X    hexagon[4].x = -2*scale ;
X    hexagon[4].y = 0 ;
X    hexagon[5].x = -scale ;
X    hexagon[5].y = z ;
X    }
X  else {
X    end_y = start_y + (size_y = (DispY/scale/2)+2);         /* If square cells, work out the last cells to */
X    end_x = start_x + (size_x = (DispX/scale/2)+2);         /* which can be displayed when viewing the domain */
X    };
X  if (size_x > max_x) {                                     /* Check for overflow (wrap around) on the above numbers */
X    size_x = max_x ;
X    end_x = start_x - 1;
X    if (end_x == -1)
X      end_x = max_x  -1;
X    };
X  if (end_x > max_x)
X    end_x -= max_x ;
X  if (size_y > max_y) {
X    size_y = max_y ;
X    end_y = start_y - 1;
X    if (end_y == -1)
X      end_y = max_y  -1;
X    };
X  if (end_y > max_y)
X    end_y -= max_y ;
X  dscale = scale*2 ;                                        /* Work out some useful `constants' for this size of cell */
X  tscale = scale*3 ;
X  hscale = scale*1.73205808 ;
X  wscale = 3.4641016*scale ;
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid link_cells ( n , all) int n, all;
X/* Links cells to their neighbours for a given neighbourhood :
X    1 - 4 square
X    2 - hexagonal
X    3 - 8 square
X
X   Status changes : shape
X*/
X{
X  shape = n ;
X  cropX = cropY = 0;
X  resize();
X  switch (n) {
X    case 1: init4(all); break;
X    case 2: init6(all); break;
X    case 3: init8(all); break;
X    default: init8(all);
X  };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid init8 (all) int all ;
X/* Links up neighbours for an 8 square arrangement */
X{
X
X  int x,y,mx,my;
X  mx = max_x + 1 - torus ;
X  my = max_y + 1 - torus ;
X
X  for( x=0 ; x<mx ; x+=1 ) {
X    for( y=0 ; y< my ; y+=1 ) {
X      array[x][y]->dir[0] = get(x,y-1);                 /* North */
X      array[x][y]->dir[4] = get(x+1,y-1);               /* North East */
X      array[x][y]->dir[1] = get(x+1,y);                 /* East */
X      array[x][y]->dir[5] = get(x+1,y+1);               /* South East */
X      array[x][y]->dir[2] = get(x,y+1);                 /* South */
X      array[x][y]->dir[6] = get(x-1,y+1);               /* South West */
X      array[x][y]->dir[3] = get(x-1,y);                 /* West */
X      array[x][y]->dir[7] = get(x-1,y-1);               /* North West */
X      if ((y==0) && (x < mx-2) && (x>0) && !all)
X        y = my-3 ;
X    };
X  };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid init4 (all) int all ;
X/* Links up neighbours for a 4 square arrangement */
X{
X
X  int x,y,mx,my ;
X  mx = max_x + 1 - torus ;
X  my = max_y + 1 - torus ;
X
X  for( x=0 ; x<mx ; x+=1 ) {
X    for( y=0 ; y< my ; y+=1 ) {
X      array[x][y] -> dir[0] = get(x,y-1);               /* North */
X      array[x][y] -> dir[1] = get(x+1,y);               /* East */
X      array[x][y] -> dir[2] = get(x,y+1);               /* South */
X      array[x][y] -> dir[3] = get(x-1,y);               /* West */
X      if ((y==0) && (x < mx-2) && (x>0) && !all)
X        y = my-3 ;
X    };
X  };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid init6 (all) int all ;
X/* Links up neighbours for a hexagonal arrangement */
X{
X
X  int x,y,mx,my ;
X  mx = max_x + 1 - torus ;
X  my = max_y + 1 - torus ;
X
X  for( x=0 ; x<mx ; x+=1 ) {
X    for( y=0 ; y< my ; y+=1 ) {
X      array[x][y] -> dir[0] = get(x,y-1);               /* North */
X      array[x][y] -> dir[1] = get(x+1,y-even(x));       /* North East-ish */
X      array[x][y] -> dir[2] = get(x+1,y+odd(x));        /* South East-ish */
X      array[x][y] -> dir[3] = get(x,y+1);               /* South */
X      array[x][y] -> dir[4] = get(x-1,y+odd(x));        /* South West-ish */
X      array[x][y] -> dir[5] = get(x-1,y-even(x));       /* North West-ist */
X      if ((y==0) && (x < mx-2) && (x > 0) && !all)
X        y = my-3 ;
X    };
X  };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xint odd (x)
X/*
X
X   Pre : x >= 0
X   Post: 1 if x is odd
X         0 otherwise
X*/
Xint x ;
X
X{
X  return ((x % 2) == 1);
X}
X
X/* ------------------------------------------------------------------------- */
X
Xint even (x)
X/*
X
X   Pre : x >= 0
X   Post: 1 if x is even
X         0 otherwise
X*/
Xint x;
X
X{
X  return ((x % 2) == 0);
X}
X
X/* ------------------------------------------------------------------------- */
X
XCELL get( x,y )
X/* Given an x,y coordinate, returns a pointer to that cells structure, checking for overflow and underflow. */
X
Xint x,y ;
X
X{
X  int mx,my ;
X  mx = max_x + 1 - torus ;
X  my = max_y + 1 - torus ;
X
X  if (x == -1)                    /* X underflow */
X    x = mx-1 ;
X
X  if (x == mx)                    /* X overflow */
X    x = 0 ;
X
X  if (y == -1)                    /* Y underflow */
X    y = my-1 ;
X
X  if (y == my)                    /* Y overflow */
X    y = 0 ;
X
X  return array[x][y];
X}
X
X/* ------------------------------------------------------------------------- */
X
Xint test_inside ( x,y )
X/* Tests to see if a given X,Y coordinate is inside the displayable domain, returns true if it is, false otherwise */
X
Xint x,y ;
X
X{
X
X  int sx,sy,ex,ey ;
X
X  sx = start_x ;
X  sy = start_y ;
X  ex = end_x ;
X  ey = end_y ;
X
X  if (ex < sx) {
X    sx = sx - ex ;
X    if ( x > ex )
X      x = x - ex ;
X    else
X      x = max_x - (ex - x) ;
X    ex = max_x ;
X    };
X
X  if (ey < sy) {
X    sy = sy - ey ;
X    if ( y > ey )
X      y = y - ey ;
X    else
X      y = max_y - (ey - y) ;
X    ey = max_y ;
X    };
X
X  if ((x >= sx) && (x <= ex) && (y >= sy) && (y <= ey))
X    return 1 ;
X  else
X    return 0 ;
X
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid display_cells ()
X/* Displays the displayable cells of the domain in the `display' window (only if the Status variable `display' = 1).
X
X   Status changes : update updateX updateC
X*/
X{
X  int cx,cy,x,y,d ;
X  update = 1;
X  if (display == 1) {                                 /* Check we are supposed to draw the cells (check status) */
X    SetWindow(Display_Window);                        /* Select the correct window */
X    x = updateX ;                                     /* Start off where left off */
X    for( cx = updateC ; cx < size_x ; ++cx) {
X      y = start_y ;
X      for( cy = 0 ; cy < size_y ; ++cy ) {
X        draw_cell(x,y);
X        y = (y+1)%max_y ;
X      };
X      x = (x+1)%max_x ;
X      if (EventPending()) {                           /* Check for any X events */
X        updateX = x;                                  /* If there are any, make a note of where you are */
X        updateC = cx+1;                               /* and stop it */
X        return ;
X        };
X    };
X    FlushQueue();
X    };
X  updateX = start_x;                                  /* If you managed to finish the display, make a note of it */
X  updateC = 0;
X  update = 0;
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid draw_cell(x,y)
X/* Given an X,Y coordinate, displays the cell for that coordinate.
X
X   Pre : display window selected
X   
X   Status changes : cropX cropY
X*/
Xint x,y;
X{
X  int dx,dy ;
X  dx = x ;
X  dy = y ;
X
X  if (array[x][y]) {
X    SetColour(states[array[x][y] -> state[new]].colour);                    /* Set the correct colour */
X    if (x < start_x)                                                        /* Check for wrap around */
X      dx += max_x ;
X    if (y < start_y)
X      dy += max_y ;
X    if (shape != 2) {                                                       /* If 4 or 8 neighbours, draw a box */
X      dx = (dx-start_x)*dscale ;
X      dy = (dy-start_y)*dscale ;
X      XFillRectangle( dpy, win, gc, dx-scale , dy-scale,dscale,dscale);
X      if ((dx+dscale)>cropX)
X        cropX = dx+dscale ;
X      if ((dy+scale)>cropY)
X        cropY = dy+scale ;
X    }
X    else {
X      dy = (dy-start_y)*wscale+(1+odd(dx))*hscale ;                         /* Otherwise a hexagon */
X      dx = (dx-start_x)*tscale-scale ;
X      hexagon[0].x = dx ;
X      hexagon[0].y = dy+1 ;
X      XFillPolygon(dpy,win,gc,hexagon,6,Convex,CoordModePrevious);
X      if (dy>cropY)
X        cropY = dy+1 ;
X      if ((dx+dscale)>cropX)
X        cropX = dx+tscale ;
X    };
X  };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid AutoResize()
X/* Tries to resize the domain to just fit inside the `display' window.
X
X   Status changes : scale cropX cropY
X*/
X{
X  cropX = cropY = 0;
X  if (shape == 2) {
X    scale = DispY/(max_x*3.4) ;
X    if (scale > (DispX/(max_x*3)))
X      scale = DispX/(max_x*3) ;
X    }
X  else {
X    scale = DispY/(max_x*2) ;
X    if (scale > (DispX/(max_x*2)))
X      scale = DispX/(max_x*2) ;
X    };
X  if (scale == 0)
X    scale = 1 ;
X  resize();
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid enlarge()
X/* Enlarges the size of displayed cells
X
X   Status changes : scale cropX cropY
X*/
X{
X  if (display >1)
X    return ;
X  cropX = cropY = 0;
X  scale += 1;
X  resize();
X  clear_display();
X  updateX = start_x;
X  updateC = 0;
X  display_cells();
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid decrease()
X/* Decreases the size of displayed cells
X
X   Status changes : scale cropX cropY
X*/
X{
X  if (display >1)
X    return ;
X  cropX = cropY = 0;
X  if (scale > 1) {
X    scale -= 1;
X    resize();
X    clear_display();
X    updateX = start_x;
X    updateC = 0;
X    display_cells();
X    }
X  else
X    Message("Can't shrink any more");
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid move_left()
X/* Scrolls the display window to the left
X
X   Status changes : start_x 
X*/
X{
X  int y,cy,x2 ;
X  if (display >1) {
X    if (display == 4)
X      ColourLeft();
X    return ;
X    };
X  if (start_x > 0)
X    start_x -= 1 ;
X  else
X    start_x = max_x-1 ;
X  resize();
X  SetWindow(Display_Window);
X  if (shape != 2)
X    XCopyArea(dpy,win,win,gc,0,0,cropX-dscale,cropY,dscale,0);
X  else
X    XCopyArea(dpy,win,win,gc,0,0,cropX-tscale+1,cropY,tscale,0);
X  y = start_y ;
X  x2 = (start_x+1)%max_x ;
X  for( cy = 0 ; cy < size_y ; ++cy ) {
X    draw_cell(start_x,y);
X    draw_cell(x2,y);
X    y = (y+1)%max_y ;
X    };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid move_right()
X/* Scrolls the display window to the right
X
X   Status changes : start_x 
X*/
X{
X  int y,x,cy,cx ;
X  if (display >1) {
X    if (display == 4)
X      ColourRight();
X    return ;
X    };
X  start_x += 1;
X  if (start_x == max_x)
X    start_x = 0 ;
X  resize();
X  SetWindow(Display_Window);
X  if (shape != 2)
X    XCopyArea(dpy,win,win,gc,dscale,0,cropX,cropY,0,0);
X  else
X    XCopyArea(dpy,win,win,gc,tscale,0,cropX+1,cropY,0,0);
X  x = end_x;
X  if (x==-1)
X    x = max_x-1 ;
X  for( cx=0 ; cx<4 ; ++cx) {
X    y = start_y ;
X    for( cy = 0 ; cy < size_y ; ++cy ) {
X      draw_cell(x,y);
X      y = (y+1)%max_y ;
X      };
X    if ((x = x-1)==-1)
X      x = max_x -1 ;
X    };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid move_up()
X/* Scrolls the display window up
X
X   Status changes : start_y 
X*/
X{
X  int x,y2,cx ;
X  if (display >1) {
X    if (display == 4)
X      ColourUp();
X    return ;
X    };
X  if (start_y > 0)
X    start_y -= 1 ;
X  else
X    start_y = max_y -1 ;
X  resize();
X  SetWindow(Display_Window);
X  if (shape !=2)
X    XCopyArea(dpy,win,win,gc,0,0,cropX,cropY-dscale,0,dscale);
X  else
X    XCopyArea(dpy,win,win,gc,0,0,cropX,cropY- (int) wscale,0,(int) wscale);
X  y2 = (start_y+1)%max_y ;
X  x = start_x ;
X  for( cx=0 ; cx < size_x ; ++cx ) {
X    draw_cell(x,start_y);
X    draw_cell(x,y2);
X    x = (x+1)%max_x ;
X    };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid move_down()
X/* Scrolls the display window down
X
X   Status changes : start_y 
X*/
X{
X  int y,x,cy,cx ;
X  if (display >1) {
X    if (display == 4)
X      ColourDown();
X    return ;
X    };
X  start_y += 1;
X  if (start_y == max_y)
X    start_y = 0;
X  resize();
X  SetWindow(Display_Window);
X  if (shape !=2)
X    XCopyArea(dpy,win,win,gc,0,dscale,cropX,cropY,0,0);
X  else
X    XCopyArea(dpy,win,win,gc,0,(int) wscale,cropX,cropY+1,0,0);
X  y = end_y ;
X  for( cy=0 ; cy<4 ; ++cy ) {
X    x = start_x ;
X    for( cx=0 ; cx < size_x ; ++cx ) {
X      draw_cell(x,y);
X      x = (x+1)%max_x ;
X      };
X    if ((y = y-1) == -1)
X      y = max_y ;
X    };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid clear_display()
X/* Clears the `display' window
X*/
X{
X  SetWindow(Display_Window);
X  ClearScreen();
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid InitMulti(n) int n ;
X/* Sets the labels of the boxes (3 to 8) for the bottom row, depending on display type
X
X   Pre : n = 0 or 1
X   Post: n = 0 - normal setup
X         n = 1 - built in Cal program setup
X*/
X{
X  if (n) {
X    sprintf(boxes[3].text,"Grey");
X    sprintf(boxes[4].text,"Sharp");
X    sprintf(boxes[5].text,"Soft");
X    sprintf(boxes[6].text,"Apply");
X    sprintf(boxes[7].text,"Update");
X    sprintf(boxes[8].text,"Undo");
X  } else {
X    sprintf(boxes[3].text,"+");
X    sprintf(boxes[4].text,"-");
X    sprintf(boxes[5].text,"Left");
X    sprintf(boxes[6].text,"Right");
X    sprintf(boxes[7].text,"Up");
X    sprintf(boxes[8].text,"Down");
X  };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid InitBoxes()
X/* Initializes all main menu `boxes', positions and labels
X*/
X{
X  int x,y ;
X  for (x=0;x<max_x;++x)
X    for (y=0;y<max_y;++y)
X      array[x][y] = NULL ;
X  sprintf(message,"Scamper message window");
X  for (x=0 ; x<max_states ; ++x) 
X    states[x].rules = NULL ;
X  for ( x = 0 ; x<MaxBoxes ; ++x )
X    boxes[x].draw = 0 ;
X  for ( x=0 ; x<10 ; ++x ) {
X    boxes[x].draw = 1 ;
X    boxes[x].sx = 10+(x*70) ;
X    boxes[x].sy = DispY+60 ;
X    boxes[x].fx = 60+(x*70) ;
X    boxes[x].fy = DispY+90 ;
X    };
X  for ( x=10 ; x<16 ; ++x ) {
X    boxes[x].draw = 1 ;
X    boxes[x].sx = 80 ;
X    boxes[x].fx = 180 ;
X    boxes[x].sy = 80+50*(x-10+(x>12)) ;
X    boxes[x].fy = 110+50*(x-10+(x>12)) ;
X    };
X  for ( x=16 ; x<22 ; ++x ) {
X    boxes[x].draw = 1 ;
X    boxes[x].sx = 200 ;
X    boxes[x].fx = 300 ;
X    boxes[x].sy = 80+50*(x-16+(x>18)) ;
X    boxes[x].fy = 110+50*(x-16+(x>18)) ;
X    };
X  for ( x=22 ; x<28 ; ++x ) {
X    boxes[x].draw = 1 ;
X    boxes[x].sx = 320 ;
X    boxes[x].fx = 420 ;
X    boxes[x].sy = 80+50*(x-22+(x>24)) ;
X    boxes[x].fy = 110+50*(x-22+(x>24)) ;
X    };
X  for ( x=28 ; x<34 ; ++x ) {
X    boxes[x].draw = 1 ;
X    boxes[x].sx = 440 ;
X    boxes[x].fx = DispX+20 ;
X    boxes[x].sy = 80+50*(x-28+(x>30)) ;
X    boxes[x].fy = 110+50*(x-28+(x>30)) ;
X    if (x < 31) {
X      buttons[x-28].value = x-28 ;
X      buttons[x-28].state = 1 ;
X      };
X    };
X
X  for( x=0 ; x<8 ; ++x) {
X    askbox[x].draw = 0 ;
X    askbox[x].set = 0 ;
X    askbox[x].reset = 0 ;
X    };
X
X  for( x= 0 ; x<3 ; ++x ) {
X    askbox[x].draw = 1 ;
X    askbox[x].sx = 158 ;
X    askbox[x].fx = 226 ;
X    askbox[x].sy = 35 + x*50 ;
X    askbox[x].fy = 60 + x*50 ;
X    };
X  
X  for(x=3 ; x<8 ; ++x) {
X    askbox[x].draw = 1 ;
X    askbox[x].sx = 20 ;
X    askbox[x].fx = 35 ;
X    askbox[x].sy = 10 + 40*(x-3) ;
X    askbox[x].fy = 25 + 40*(x-3) ;
X    };
X
X  sprintf(askbox[1].text,"Cancel");
X  sprintf(askbox[2].text,"Clear");
X
X  sprintf(boxes[0].text,"Run");
X  sprintf(boxes[1].text,"Stop");
X  sprintf(boxes[2].text,"Step");
X  InitMulti(0);
X  sprintf(boxes[9].text,"Swap");
X  sprintf(boxes[10].text,"4 neighbours");
X  sprintf(boxes[11].text,"6 neighbours");
X  sprintf(boxes[12].text,"8 neighbours");
X  sprintf(boxes[13].text,"Home");
X  sprintf(boxes[14].text,"Set Home");
X  sprintf(boxes[15].text,"QUIT");
X  sprintf(boxes[16].text,"Load Domain");
X  sprintf(boxes[17].text,"Insert Domain");
X  sprintf(boxes[18].text,"Save Domain");
X  sprintf(boxes[19].text,"Auto Resize");
X  sprintf(boxes[20].text,"View Small");
X  sprintf(boxes[21].text,"View Stats");
X  sprintf(boxes[22].text,"Load Bitmap");
X  sprintf(boxes[23].text,"Save Bitmap");
X  sprintf(boxes[24].text,"Clear Domain");
X  sprintf(boxes[25].text,"Load Rules");
X  sprintf(boxes[26].text,"Clear Rules");
X  sprintf(boxes[27].text,"Reset Time");
X  sprintf(boxes[28].text,"Left Button");
X  sprintf(boxes[29].text,"Middle Button");
X  sprintf(boxes[30].text,"Right Button");
X  sprintf(boxes[31].text,"Change Colours");
X  sprintf(boxes[32].text,"");
X  sprintf(boxes[33].text,"");
X}
X  
X/* ------------------------------------------------------------------------- */
X
Xvoid DrawBox(x,invert) int x,invert ;
X/* Draws a givan box. If invert = 0 - normal, otherwise inverted colours.
X*/
X{
X  char string[25];
X
X  SetWindow(Main_Window);                                                         /* Select main menu window */
X  SelectFont(Small_Font);                                                         /* and small font */
X  SetColour(cols[fore]);
X  if ((x>27) && (x<31))
X    SetColour(states[buttons[x-28].value].colour);
X  FillRectangle(boxes[x].sx,boxes[x].sy,boxes[x].fx,boxes[x].fy);
X  FillRectangle(boxes[x].sx-1,boxes[x].sy+1,boxes[x].fx+1,boxes[x].fy-1);         /* Bounding box */
X  FillRectangle(boxes[x].sx+1,boxes[x].sy-1,boxes[x].fx-1,boxes[x].fy+1);
X  FillRectangle(boxes[x].sx-2,boxes[x].sy+2,boxes[x].fx+2,boxes[x].fy-2);
X  FillRectangle(boxes[x].sx+2,boxes[x].sy-2,boxes[x].fx-2,boxes[x].fy+2);
X  SetColour(cols[back]);
X  if (! invert) {
X    FillRectangle(boxes[x].sx+3,boxes[x].sy+3,boxes[x].fx-3,boxes[x].fy-3);       /* Clear the inside (if not invert) */
X    FillRectangle(boxes[x].sx+2,boxes[x].sy+4,boxes[x].fx-2,boxes[x].fy-4);
X    FillRectangle(boxes[x].sx+4,boxes[x].sy+2,boxes[x].fx-4,boxes[x].fy-2);
X    FillRectangle(boxes[x].sx+1,boxes[x].sy+5,boxes[x].fx-1,boxes[x].fy-5);
X    FillRectangle(boxes[x].sx+5,boxes[x].sy+1,boxes[x].fx-5,boxes[x].fy-1);
X    SetColour(cols[fore]);
X    };
X  if ((x<28) || (x==31))
X      Centre(boxes[x].text,boxes[x].sx,boxes[x].sy,boxes[x].fx,boxes[x].fy);      /* Place text */
X  else {
X    if (x==32)
X      sprintf(string,"X = %d",max_x);
X    if (x==33)
X      sprintf(string,"Y = %d",max_y);
X    if ((x>27) && (x<31))
X      if (buttons[x-28].state)
X        sprintf(string,"%d - %s",buttons[x-28].value,ColourName(states[buttons[x-28].value].colour));
X      else
X        sprintf(string,boxes[x].text);
X    Centre(string,boxes[x].sx,boxes[x].sy,boxes[x].fx,boxes[x].fy);
X    };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid MainScreen()
X/* Draws the main menu
X*/
X{
Xint x,y,sx,sy ;
X
X  change = 1 ;
X  SelectFont(Small_Font);
X  for( x=0 ; x<MaxBoxes ; ++x ) {
X    if (boxes[x].draw) 
X      DrawBox(x,0);
X  };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid DisplayFileName(clear) int clear ;
X/* Displays the current file name in the file name window */
X{
X  if (display == 0) {
X    SetWindow(File_Window);
X    SetBackColour(cols[filebg]);
X    if (clear) {
X      SetColour(cols[filefg]);
X      ClearScreen();
X      Centre("File name :",0,0,256,40);
X      }
X    else {
X      SetColour(cols[filebg]) ;
X      FillRectangle(0,40,256,80) ;
X      SetColour(cols[filefg]);
X      };
X    FileName[fnamel] = '_' ;
X    FileName[fnamel+1] = 0 ;
X    Centre(FileName,0,40,256,80);
X    };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid Centre( text , sx,sy,fx,fy ) char *text ; int sx,sy,fx,fy ;
X/* Given some text and a bounding box, places the text in the centre of the bounding box. If the text is too large from the
X   bounding box, then it is right justified to the right hand side of the box.
X*/
X{
X  int x,y ;
X  TextSize(text,&x,&y);
X  if (sx == fx)
X    x = fx + 10 ;
X  else if (x>(fx-sx))
X    x = fx - x ;
X  else
X    x = sx + ((fx-sx-x)/2) ;
X  y = (fy+sy+y-4)/2 ;
X  PrintString(x,y,text) ;
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid service()
X/* Given that there is an X11 event pending, this procedure reads the event and takes necessary action.
X*/
X{
Xint x,y,b,w ;
XEventType ev ;
Xchar c ;
X
X  GetNextEvent(&ev,&c,&x,&y,&b,&w);
X
X  if ( ev == EventExpose) {                     /* Expose event, a window needs redisplaying */
X    if (w==0)
X      MainScreen();
X    if (w==1) {
X      updateX = start_x;
X      updateC = 0;
X      display_cells();
X      if (display == 4)
X        ColourEvent(ev,c,x,y,b);
X      };
X    if (w==2)
X      Message(message);
X    if (w == 3)
X      DisplayFileName(1);
X    if ((w == 4) && (Asking == 1))
X      AskBoxes();
X    if ((w == 4) && (Asking == 2))
X      DrawGreyScale();
X    if ((w == 5) && error)
X      Error(errormess[0],errormess[1]);
X    if (w == 6)
X      change = 1 ;
X    };
X
X  if (ev == EventMouseDown) {                   /* A mouse button has been pressed down */
X    updown = b+1 ;
X    old_x = x ;
X    old_y = y ;
X    counter = 0;
X    if (w==2)
X      Message("");
X    };
X
X  if (ev == EventMouseUp) {                     /* A mouse button has been released */
X    updown = 0 ;
X    old_op = 0 ;
X    if (Asking == 2)
X      GreyScaleMouseUp();
X    };
X
X  if (ev == EventKeypress) {                    /* A key has been pressed */
X    if ((c > 31) && (c != 127) && (fnamel < 100))
X      FileName[fnamel++] = c ;
X    else if (((c == 127) || (c == 8)) && (fnamel > 0))
X      --fnamel ;
X    else if (c == 13) 
X      CallAsk();
X    else if (c == 21)
X      fnamel = 0;
X    else if (c == 18)
X      Total_Refresh();
X    DisplayFileName(0);
X    };
X
X  if (updown && (w==1))
X    cellop(x,y,updown-1);
X  if (updown && (w==0))
X    operation(old_x,old_y,updown-1);
X  if (updown && (w==4) && (Asking == 1))
X    ButtonAsk(x,y);
X  if (updown && (w==4) && (Asking == 2))
X    GreyScaleMove(x,y);
X  if (updown && (w==5) && error)
X    ErrorWindow( error = 0 );
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid Total_Refresh()
X/* Refresh all windows that are visible */
X{
X  SetWindow(Main_Window);
X  ClearScreen();
X  if (display) {
X    SetWindow(Display_Window);
X    ClearScreen();
X    };
X  if (Asking) {
X    SetWindow(Multi_Window);
X    ClearScreen();
X    };
X  if (error) {
X    SetWindow(Error_Window);
X    ClearScreen();
X    };
X  SetWindow(Title_Window);
X    ClearScreen();
X  if (! display) {
X    SetWindow(Message_Window);
X    ClearScreen();
X    SetWindow(File_Window);
X    ClearScreen();
X    };
X  MainScreen();
X  Message(message);
X  DisplayFileName(1);
X  updateX = start_x;
X  updateC = 0;
X  display_cells();
X  if (display == 4)
X    ColourEvent(EventExpose,' ',0,0,0);
X  if (display == 3)
X    display_stats(0,1);
X  if (display == 2)
X    SmallView();
X  if (Asking == 1)
X    AskBoxes();
X  if (Asking == 2)
X    DrawGreyScale();
X  if (error)
X    Error(errormess[0],errormess[1]);
X  change = 1 ;
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid StartRun(n) int n ;
X/* Force a higlight of option box n
X*/
X{
X  DrawBox(n,1);
X  FlushQueue();
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid operation(x,y,b)
X/* Given an x,y coordinate of the mouse pointer, and the button number pressed this procedure works out which menu option
X   was selected, and acts accordingly.
X*/
Xint x,y,b ;
X{
Xint n,op,cx,cy = -1 ;
X
X  if (error) return ;
X
X  op = -1;
X  for( n=0 ; n<MaxBoxes ; ++n )
X    if ((x >= boxes[n].sx) && (x <= boxes[n].fx) && (y >= boxes[n].sy) && (y <= boxes[n].fy))
X      op = n ;
X  
X  SetWindow(Main_Window);
X  DrawBox(op,1);
X  FlushQueue();
X
X  if (op==15) {                     /* Quit */
X    Exit = 1 ;
X    Message("Goodbye");
X    };
X
X  if ((op >9) && (op<13)) {         /* 4, 6, or 8 neighbours */
X    link_cells(op-9,1) ;
X    };
X
X  if (op==13) {                     /* Home */
X    start_x = 0;
X    start_y = 0;
X    resize();
X    };
X
X  if (op==14)                       /* Set Home */
X    SetHome();
X
X  if (op==19)                       /* Auto Resize */
X    AutoResize();
X
X  if (op==21) {                     /* Stats */
X    InitMulti(1);
X    for(cy = 3; cy < 9 ; ++cy)
X      DrawBox(cy,0);
X    SetWindow(Display_Window);
X    display_stats(0,1);
X    };
X
X  if (op == 26)                     /* Clear Rules */
X    ClearRules();
X
X  if (op == 27) {                   /* Reset Time */
X    generation = 0;
X    Shift = 0 ;
X    reset_stats();
X    Message("Time Reset");
X    };
X
X  if (AskVal) {                     /* Don't allow any of the below when file operation initiated */
X    if (op != AskOp)
X      DrawBox(op,0);
X    return ;
X    };
X
X  if (op == 0) {                    /* Run */
X    stopnum = 0 ;
X    running = 1 ;
X    };
X
X  if (op == 1)                      /* Stop */
X    running = 0 ;
X
X  if (op == 2)                      /* Step */
X    step = 1 ;
X
X  if ((display != 2) && (display != 3)) {
X    if (op == 3)                    /* Enlarge `+' */
X      enlarge();
X    if (op == 4)                    /* Decrease `-' */
X      decrease();
X    if (op == 5)                    /* Scroll Left */
X      move_left();
X    if (op == 6)                    /* Scroll Right */
X      move_right();
X    if (op == 7)                    /* Scroll Up */
X      move_up();
X    if (op == 8)                    /* Scroll Down */
X      move_down();
X    } 
X  else if ((op > 2) && (op < 9))    /* Built in Cal program */
X    AlternateRun(op-2) ;
X
X  if (op == 9) {                    /* Swap */
X    DrawBox(28,0);
X    DrawBox(29,0);
X    DrawBox(30,0);
X    if (Asking == 2) {
X      AskOff();
X      Asking = 0 ;
X      };
X    if ((display == 2) || (display == 3)) {
X      InitMulti(0);
X      for(cy = 3; cy < 9 ; ++cy)
X        DrawBox(cy,0);
X      };
X    display = 1 - display ;
X    if (display < 0)
X      display = 0 ;
X    if (display == 1) {
X      DisplayOn();
X      update = 1;
X      updateX = start_x;
X      updateC = 0;
X      }
X    else {
X      DisplayOff();
X      };
X    };
X
X  if (op==16)                       /* Load Domain */
X    Ask(4);
X
X  if (op==17)                       /* Insert Domain */
X    Ask(6);
X
X  if (op==18)                       /* Save Domain */
X    Ask(5);
X
X  if (op==20) {                     /* View Small */
X    InitMulti(1);
X    for(cy = 3; cy < 9 ; ++cy)
X      DrawBox(cy,0);
X    SmallView();
X    };
X
X  if (op==22) {                     /* Load Bitmap */
X    Ask(2);
X    };
X
X  if (op==23)                       /* Save Bitmap */
X    Ask(3);
X
X  if (op==24) {                     /* Clear Domain */
X    XSetForeground( dpy,smallgc,colours[states[0].colour].mapno);
X    for( cy=0 ; cy<max_y ; ++cy )
X      for( cx=0 ; cx<max_x ; ++cx) {
X        array[cx][cy]->state[new] = array[cx][cy]->state[old] = 0 ;
X        if (updatesmall)
X          XDrawPoint( dpy, small, smallgc, cx, cy );
X        };
X    };
X
X  if (op == 25)                     /* Load Rules */
X    Ask(1);
X
X  if (((op<28) && ((op<5) || (op>8))) || (display == 2) || (display == 3))
X    updown=0 ;
X  else {
X    counter = 0 ;                   /* Decide if repeat allowed if button held down long enough */
X    old_op = op ;
X    };
X
X  if ((op>27) && (op<31)) {         /* Mouse Buttons' Options */
X    if (b==1) {
X      --buttons[op-28].value ;
X      if (buttons[op-28].value == -1)
X        buttons[op-28].value = max_states-1 ;
X    };
X    if (b==3) {
X      ++buttons[op-28].value ;
X      if (buttons[op-28].value == max_states)
X        buttons[op-28].value = 0 ;
X    };
X    if (b==2)
X      buttons[op-28].state = 1 - buttons[op-28].state ;
X  };
X
X  if (op == 31) {                   /* Change Colours */
X    ColourOps();
X    };
X
X  if (op==32) {                     /* X size of domain */
X    if ((b==1) && (max_x >5))
X      --max_x ;
X    if ((b==3) && (max_x < MAX_X)) {
X      ++max_x ;
X      setup_array(max_x-2,0,max_x,max_y,1);
X      };
X    link_cells(shape,0);
X    };
X
X  if (op==33) {                     /* Y size of domain */
X    if ((b==1) && (max_y >5))
X      --max_y ;
X    if ((b==3) && (max_y < MAX_Y)) {
X      ++max_y ;
X      setup_array(0,max_y-2,max_x,max_y,1);
X      };
X    link_cells(shape,0);
X    };
X
X  if ((op != 0) && (op != 2) && (AskVal == 0))
X    DrawBox(op,0);                  /* Un highlight box, if necessary */
X  if (AskVal)                       /* Set file operation selected (if any) */
X    AskOp = op ;
X  if (op == 1)                      /* If `Stop' unhighlight `Run' */
X    DrawBox(0,0);
X  FlushQueue();                     /* Make sure X is up to date */
X
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid cellop(px,py,b) int px,py,b ;
X/* Given the x,y coordinate of the mouse pointer, and the button pressed, works out which cell to change the state of, and
X   sets the state accordind to which button was pressed.
X*/
X{
X
X  int cx,cy,x,y,fx,fy,dx,dy ;
X  unsigned long td,md = 100000000 ;
X
X  if (display == 3) 
X    StatButton(px,py);
X
X  if ((display != 1) || (px > cropX) || (py > cropY)) return ;
X  SetWindow(Display_Window);
X  if (shape == 2)
X    x = start_x + (px/tscale) -1 ;          /* Calculate the coordinates of the cell clicked on */
X  else
X    x = start_x + (px/dscale) -1 ;
X  if (x < start_x)
X    x = start_x ;
X  if (x >= max_x)
X    x -= max_x ;
X  if (x >= max_x) {
X    Message("Outside cell area");
X    return ;
X    };
X  for( cx = 0 ; cx < 4 ; ++cx ) {
X    if (shape == 2)
X      y = start_y + (py/wscale) -1 ;
X    else
X      y = start_y + (py/dscale) -1 ;
X    if (y < start_y)
X      y = start_y ;
X    if (y >= max_y)
X      y -= max_y ;
X    if (y >= max_y) {
X      Message("Outside cell area");
X      return ;
X      };
X    for( cy = 0 ; cy < 4 ; ++cy ) {
X      dx = x ;
X      dy = y ;
X      if (x < start_x)
X        dx += max_x ;
X      if (y < start_y)
X        dy += max_y ;
X      if (shape != 2) {
X        dx = (dx-start_x)*dscale ;
X        dy = (dy-start_y)*dscale ;
X      }
X      else {
X        dy = (dy-start_y)*wscale+(odd(dx))*hscale ;
X        dx = (dx-start_x)*tscale ;
X      };
X      td = (dx-px)*(dx-px) + (dy-py)*(dy-py) ;
X      if (td < md) {
X        md = td ;
X        fx = x ;
X        fy = y ;
X      };
X      y = (y+1)%max_y ;
X    };
X    x = (x+1)%max_x ;
X  };
X  (array[fx][fy]->state[old]) = buttons[b-1].value ;      /* Set it's state */
X  (array[fx][fy]->state[new]) = buttons[b-1].value ;
X  draw_cell(fx,fy);
X  if (updatesmall) {
X    XSetForeground( dpy,smallgc,colours[states[array[fx][fy] -> state[new]].colour].mapno);
X    XDrawPoint( dpy, small, smallgc, fx, fy );
X    };                                                    /* Update Small View Image */
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid Message( mess ) char* mess ;
X/* Place message `mess' in the message window */
X{
X  SetWindow(Message_Window);
X  SetColour(cols[verbfg]);
X  ClearScreen();
X  Centre(mess,0,0,256,80);
X  sprintf(message,mess);
X  FlushQueue();
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid SetHome()
X/* Set the `Home' position to the current start_x start_y position */
X{
Xint x,y,X,Y ;
X
X  x = start_x ;
X  for( X=0 ; X<max_x ; ++X) {
X    y = start_y ;
X    for(Y=0 ; Y<max_y ; ++Y) {
X      array[X][Y] -> state[old] = array[x][y] -> state[new] ;
X      y = (y+1)%max_y ;
X      };
X    x = (x+1)%max_x ;
X    };
X  new = old ;
X  old = 1-old ;
X  start_x = 0 ;
X  start_y = 0 ;
X  resize();
X}
X    
X/* ------------------------------------------------------------------------- */
X
Xvoid Ask(n) int n ;
X/* Set up the boxes for file operations. 
X  1 -> Read File (Cal program)
X  2 -> Load Bitmap
X  3 -> Save Bitmap
X  4 -> Load domain
X  5 -> Save domain
X  6 -> Insert domain
X*/
X{
Xint x ;
X
X  for(x = 3 ; x<8 ; ++x ) {
X    askbox[x].draw = 0 ;
X    askbox[x].set = 0 ;
X    askbox[x].reset = 0 ;
X    };
X
X  switch(n) {
X    case 1 :  sprintf(askbox[0].text,"Read"); 
X              sprintf(askbox[3].text,"Display On"); askbox[3].draw = 1 ;
X              sprintf(askbox[4].text,"Display Off"); askbox[4].draw = 1 ;
X              askbox[3].set = 1 ;
X              askbox[3].reset = 4 ;
X              askbox[4].reset = 3 ;
X              break ;
X    case 2 :  sprintf(askbox[0].text,"Load");
X              sprintf(askbox[3].text,"Full Size"); askbox[3].draw = 1 ;
X              sprintf(askbox[4].text,"Half Size"); askbox[4].draw = 1 ;
X              sprintf(askbox[5].text,"Double Size"); askbox[5].draw = 1 ;
X              sprintf(askbox[6].text,"Invert"); askbox[6].draw = 1 ;
X              sprintf(askbox[7].text,"Normal"); askbox[7].draw = 1 ;
X              askbox[3].set = 1 ;
X              askbox[7].set = 1 ;
X              askbox[3].reset = 4 ;
X              askbox[4].reset = 5 ;
X              askbox[5].reset = 3 ;
X              askbox[6].reset = 7 ;
X              askbox[7].reset = 6 ;
X              break ;
X    case 3 :  sprintf(askbox[0].text,"Save");
X              sprintf(askbox[3].text,"Sub-directory"); askbox[3].draw = 1 ;
X              sprintf(askbox[4].text,"Current directory"); askbox[4].draw = 1 ;
X              askbox[3].set = 1 ;
X              askbox[3].reset = 4 ;
X              askbox[4].reset = 3 ;
X              break ;
X    case 4 :  sprintf(askbox[0].text,"Load");
X              sprintf(askbox[3].text,"Full Size"); askbox[3].draw = 1 ;
X              sprintf(askbox[4].text,"Half Size"); askbox[4].draw = 1 ;
X              sprintf(askbox[5].text,"Double Size"); askbox[5].draw = 1 ;
X              askbox[3].set = 1 ;
X              askbox[3].reset = 4 ;
X              askbox[4].reset = 5 ;
X              askbox[5].reset = 3 ;
X              break ;
X    case 5 :  sprintf(askbox[0].text,"Save");
X              sprintf(askbox[3].text,"Sub-directory"); askbox[3].draw = 1 ;
X              sprintf(askbox[4].text,"Current directory"); askbox[4].draw = 1 ;
X              sprintf(askbox[5].text,"Scamper format"); askbox[5].draw = 1 ;
X              sprintf(askbox[6].text,"Postscript"); askbox[6].draw = 1 ;
X              askbox[3].set = 1 ;
X              askbox[5].set = 1 ;
X              askbox[3].reset = 4 ;
X              askbox[4].reset = 3 ;
X              askbox[5].reset = 6 ;
X              askbox[6].reset = 5 ;
X              break ;
X    case 6 :  sprintf(askbox[0].text,"Insert");
X              sprintf(askbox[3].text,"Add"); askbox[3].draw = 1 ;
X              sprintf(askbox[4].text,"And"); askbox[4].draw = 1 ;
X              sprintf(askbox[5].text,"Or"); askbox[5].draw = 1 ;
X              sprintf(askbox[6].text,"Not"); askbox[6].draw = 1 ;
X              askbox[3].set = 1 ;
X              askbox[3].reset = 4 ;
X              askbox[4].reset = 5 ;
X              askbox[5].reset = 6 ;
X              askbox[6].reset = 3 ;
X              break ;
X    default: ;
X    };
X  AskOn(1);
X  Asking = 1 ;
X  FlushQueue();
X  AskVal = n ;
X  SetWindow(Multi_Window);
X  SetBackColour(cols[filebg]);
X  ClearScreen();
X  SetColour(cols[filefg]);
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid CallAsk()
X/* Either `Return' has been pressed or the commit option for a file operation selected. So activate the
Xfile operation which is selected */
X{
Xint n , op , on , off, temp;
X
X  AskOff();
X  Asking = 0 ;
X  BusyCursor();
X  FlushQueue();
X  temp = AskVal ;
X  AskVal = 0 ;
X  switch(temp) {
X    case 1 : displaychange = askbox[3].set ;      /* Read File */
X             ReadFile(); break ;
X    case 2 : for( op=3 ; op<6 ; ++op)             /* Load Bitmap */
X               if (askbox[op].set)
X                 n = op - 2 ;
X             on = askbox[7].set ;
X             off = askbox[6].set ;
X             LoadBitmap(n,on,off);
X             break ;
X    case 3 : if (askbox[3].set)                   /* Save Bitmap */
X               SaveBitmap(1);
X             else
X               SaveBitmap(2);
X             break ;
X    case 4 : for( op=3 ; op<6 ; ++op)             /* Load Domain */
X               if (askbox[op].set)
X                 n = op - 2 ;
X             LoadScreen(n); break ;
X    case 5 : n = 1 + askbox[4].set ;              /* Save Domain */
X             if (askbox[5].set)
X               SaveScreen(n); 
X             else
X               SavePS(n);
X             break ;
X    case 6 : for( op=3 ; op<7 ; ++op)             /* Insert Domain */
X               if (askbox[op].set)
X                 n = op - 2 ;
X             InsertScreen(n); break ;
X    default: ;
X    };
X  SetWindow(Main_Window);
X  DrawBox(AskOp,0);
X  OnCursor();
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid UpdateButtons( n ) int n ;
X/* A state colour has been changed (state = n), see if any of the `Mouse Button' options needs updating, if so update them
X*/
X{
X  int b ;
X
X  for(b = 0 ; b<3 ; ++b)
X    if (buttons[b].value == n)
X      DrawBox(28+b,0);
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid AskBoxes()
X/* Draw all the Ask Boxes (those of file operations)
X*/
X{
X  int x ;
X  SetWindow(Multi_Window);
X  for( x=0 ; x < 8 ; ++x ) 
X    if (askbox[x].draw) 
X      DrawAskBox(x);
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid DrawAskBox(x) int x ;
X/* Given the number of an Ask Box, draw it in the Multi_Window
X*/
X{
X
X  SetWindow(Multi_Window);
X  SetColour(cols[filefg]);
X  FillRectangle(askbox[x].sx,askbox[x].sy,askbox[x].fx,askbox[x].fy);
X  FillRectangle(askbox[x].sx-1,askbox[x].sy+1,askbox[x].fx+1,askbox[x].fy-1);
X  FillRectangle(askbox[x].sx+1,askbox[x].sy-1,askbox[x].fx-1,askbox[x].fy+1);
X  if (x<3) {
X    FillRectangle(askbox[x].sx-2,askbox[x].sy+2,askbox[x].fx+2,askbox[x].fy-2);
X    FillRectangle(askbox[x].sx+2,askbox[x].sy-2,askbox[x].fx-2,askbox[x].fy+2);
X    };
X  if ((x<3) || (askbox[x].set==0)) {
X    SetColour(cols[filebg]);
X    FillRectangle(askbox[x].sx+3,askbox[x].sy+3,askbox[x].fx-3,askbox[x].fy-3);
X    FillRectangle(askbox[x].sx+2,askbox[x].sy+4,askbox[x].fx-2,askbox[x].fy-4);
X    FillRectangle(askbox[x].sx+4,askbox[x].sy+2,askbox[x].fx-4,askbox[x].fy-2);
X    if (x<3) {
X      FillRectangle(askbox[x].sx+1,askbox[x].sy+5,askbox[x].fx-1,askbox[x].fy-5);
X      FillRectangle(askbox[x].sx+5,askbox[x].sy+1,askbox[x].fx-5,askbox[x].fy-1);
X      };
X    };
X  SetColour(cols[filefg]);
X  if (x<3)
X    Centre(askbox[x].text,askbox[x].sx,askbox[x].sy,askbox[x].fx,askbox[x].fy);
X  else
X    Centre(askbox[x].text,askbox[x].fx,askbox[x].sy,askbox[x].fx,askbox[x].fy);
X}
X  
X/* ------------------------------------------------------------------------- */
X
Xvoid ButtonAsk(x,y) int x,y ;
X/* A button has been pressed in the Multi-Window whilst a file operation has been initiated. Act on the
Xbutton press...
X*/
X{
X  int n , op;
X
X  updown = 0;
X  op = -1 ;
X  for( n=0 ; n<8 ; ++n )
X    if ((x >= askbox[n].sx) && (x <= askbox[n].fx) && (y >= askbox[n].sy) && (y <= askbox[n].fy))
X      op = n ;
X  if (op == 0)
X    CallAsk();
X  if (op == 1) {
X    AskVal = 0 ;
X    Asking = 0 ;
X    AskOff();
X    DrawBox(AskOp,0);
X    FlushQueue();
X    };
X  if (op == 2) {
X    fnamel = 0;
X    DisplayFileName(0);
X    };
X  if (op > 2) {
X    askbox[op].set = 1 ; DrawAskBox(op) ;
X    n = op ;
X    do {
X      n = askbox[n].reset ;
X      if ((n != 0) && (n != op)) {
X        askbox[n].set = 0 ; DrawAskBox(n) ;
X        };
X      } while ((n != 0) && (n != op));
X    };
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid SmallView()
X/* Display the `Small View' image
X*/
X{
X  int sx,sy ;
X
X  DisplayOn();
X  SetWindow(Display_Window);
X  display = 2 ;
X  sx = (DispX - max_x) / 2 ;
X  sy = (DispY - max_y) / 2 ;
X  XCopyArea(dpy,small,win,gc,0,0,max_x,max_y,sx,sy);
X}
X
X/* ------------------------------------------------------------------------- */
X
Xvoid Initialise()
X/* Initialise the program, operation include :
X
X    Open windows
X    Initialise the main menu options
X    Set up status
X    Set all states colours to either balck or white
X    Create initial domain
X    Draw main menu
X*/
X{
X  int s ;
X
X  InitialiseWindows();
X  MakeWindows();
X  InitBoxes();
X  max_x = 20 ;
X  max_y = 20 ;
X  start_x = 0;
X  start_y = 0;
X  new = 0 ;
X  old = 1 ;
X  scale = 30;
X  shape = 1;
X  Shift = 0 ;
X  mode = 0 ;
X  stopat = 1 ;
X  torus = 1 ;
X  stopnum = 0 ;
X  displaychange = 1 ;
X  fnamel = 0 ;
X  AskVal = 0 ;
X  Asking = 0 ;
X  generation = 0;
X  error = 0 ;
X  display = 0;
X  updateX = start_x;
X  updateC = 0;
X  updatesmall = 1 ;
X
X  for( s=0 ; s<max_states ; ++s)
X    states[s].colour = AllocColour(s % 2,-1,-1) ;
X  setup_array(0,0,max_x,max_y,1);
X  link_cells(1,1);
X  init_stats();
X  InitGreyScale();
X  FlushQueue();
X  MainScreen();
X  AutoResize();
X  ClearRules();
X
X  sccol = cols[title] ;
X  scnum = 0 ;
X  numdir = -1 ;
X  numloop = 0 ;
X  dirtype = 1 ;
X  change = 1 ;
X  greyscale = 1 ;
X  loopnum = 75 ;
X}
X
X/* ------------------------------------------------------------------------- */
X
Xmain()
X/* Main loop for Scamper */
X{
X
X  Initialise();                                           /* Initialise ScAmPeR */
X
X  do {                                                    /* Repeat */
X    if (update)                                             /* if domain is still being drawn, then carry on */
X      display_cells();
X    while (EventPending())                                  /* While there is an X event waiting, serve it */
X      service();
X    if (updown)                                             /* If mouse button held down, then repeat selection */
X      if ((++counter == 800) && (old_op != 0))
X        operation(old_x,old_y,updown-1);
X    if (running || step) {                                  /* Perform a generation if required */
X      run();
X      if (step != 0)
X        DrawBox(2,0);
X      step = 0;
X      }
X    else if (greyscale || (scnum != 50))                    /* If nothing else to do, update the trendy title bar */
X      if (++numloop == 512) {
X        if ((scnum == 0) || (scnum == loopnum)) {
X          numdir = -numdir ;
X          if (scnum == 0) {
X            loopnum = 75 ;
X            change = 1 ;
X            dirtype = 1 - dirtype ;
X            if (dirtype == 0)
X              loopnum = 256 ;
X            };
X          };
X        scnum += numdir ;
X        if ((scnum == 1) && (! greyscale)) {
X          scnum = 50 ;
X          dirtype = 0 ;
X          change = 1 ;
X          }
X        else if (! greyscale) {
X          scnum = 0 ;
X          numdir = -1 ;
X          };
X        if (scnum <= 50) {
X          bycol = colours[sccol].mapno ;
X          sccol = fade(scnum,sccol);
X          if ((bycol != colours[sccol].mapno) || change) {
X            change = 0 ;
X            SetWindow(Title_Window) ; 
X            SelectFont(Large_Font);
X            SetColour(sccol);
X            if (dirtype == 0) {
X              PrintString(23+offset,28,"S");
X              PrintString(50+offset,33,"C");
X              PrintString(80+offset,28,"A");
X              PrintString(105+offset,33,"M");
X              PrintString(136+offset,28,"P");
X              PrintString(163+offset,33,"E");
X              PrintString(191+offset,28,"R");
X              }
X            else
X              Centre( theauthor , 0 , 0 , DispX-300 , 35);
X            SelectFont(Small_Font);
X            };
X          };
X        numloop = 0 ;
X        };
X  } while (! Exit);                                           /* Until `Quit' has been selected */
X  CloseDownWindows();                                         /* Close all windows */
X}
END_OF_FILE
if test 49495 -ne `wc -c <'automata.c'`; then
    echo shar: \"'automata.c'\" unpacked with wrong size!
fi
# end of 'automata.c'
fi
echo shar: End of shell archive.
exit 0
---- CUT HERE -------- CUT HERE -------- CUT HERE -------- CUT HERE ----


-- 
   _____            _____________________        Ian Palmer
  /_  _/           |                     |       Department of Computing,
   / /  ___        |                     |       Imperial College, 
  / /  / __\       | This space for rent |       180 Queen's Gate,
 /_/  / /          |                     |       London SW7 2BZ.
      \ \__        |_____________________|       
       \___/                 | |                 ijp@doc.ic.ac.uk
                            \|_|/                ijp=ack@doc.ic.ac.uk
  PANIC NOW
  and avoid       23. Add to Celt's pub meal and produce utter turmoil. (6,8)
  the rush.       -----------------------------------------------------------
If a camel is a horse designed by a committee, then a consensus
forecast is a camel's behind.
		-- Edgar R. Fiedler

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.
