home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume26 / scamper / part04 / automata.c
Encoding:
C/C++ Source or Header  |  1991-11-20  |  48.3 KB  |  1,775 lines

  1. /*
  2.  
  3.  
  4.  
  5.    *****                 *****                   *****                 *****
  6.   *     *               *     *                 *     *               *     *
  7.   *                     *     *                 *     *               *     *
  8.    **         *****     *     *    *       *    *     *     ******    *     *
  9.      *       *     *    *******    **     **    ******     *          ******
  10.       **     *          *     *    * *   * *    *          *          * *
  11.         *    *          *     *    *  * *  *    *          *          *  *
  12.   *     *    *          *     *    *   *   *    *           *****     *   *
  13.    *****     *          *     *    *       *    *          *          *    *
  14.              *                     *       *               *
  15.              *     *               *       *               *
  16.               *****                *       *                ******
  17.  
  18.  
  19.  
  20.                                  by Ian Palmer
  21.  
  22.                                       at
  23.  
  24.               Imperial College of Science, Technology and Medicine
  25.                              University of London
  26.  
  27.  
  28.  
  29.  
  30. --------------------------------------------------------------------------------
  31. | Scamper is supplied "as is", without express or implied warranty.            |
  32. |                                                                              |
  33. | Permission to use, copy, modify and distribute this software, and any        |
  34. | documentation, for any non-commercial purpose is hereby granted without fee, |
  35. | provided that all copyright messages, and permission notices, remain intact. |
  36. |                                                                              |
  37. | Copyright 1991 by Ian Palmer of Imperial College of Science, Technology      |
  38. | and Medicine, University of London.                                          |
  39. --------------------------------------------------------------------------------
  40.  
  41.    _____
  42.   /_  _/  Let Total Chaos reign forever !       I.J.Palmer,
  43.    / /  ___      ______________                 Department of Computing,
  44.   / /  / __\      |    _                        Imperial College,
  45.  /_/  / /         | <> | /-\ |_                 180 Queen's Gate,
  46.       \ \__           /             _           London SW7 2BZ.
  47.        \___/          \ |-| /-\ <> <
  48.                       ______________>           ijp@doc.ic.ac.uk
  49.   PANIC NOW
  50.   and avoid       23. Add to Celt's pub meal and produce utter turmoil. (6,8)
  51.   the rush.       -----------------------------------------------------------
  52.  
  53.  
  54. */
  55.  
  56. /*
  57.       Program Segment      : automata.c
  58.  
  59.       Task                 : Provide the main finctions of Scamper, including :
  60.                              - Main loop
  61.                              - X Event handling
  62.                              - Displaying domain
  63.                              - Scrolling and resizing the domain
  64.                              - Main menu (display and option selection)
  65.                              - Handling file options' selection
  66.                              - Cell allocation and user actions on domain
  67. */
  68.  
  69. #include <stdlib.h>
  70. #include <stdio.h>
  71. #include <X11/Xlib.h>
  72. #include "windows.h"
  73. #include "domain.h"
  74. #include "calscan.h"
  75. #include "automata.h"
  76. #include "stats.h"
  77. #include "colour.h"
  78. #include "rules.h"
  79. #define offset 28
  80.  
  81. struct box boxes[MaxBoxes] ;
  82. struct abox askbox[8] ;
  83. struct state states[max_states];
  84. CELL array[MAX_X][MAX_Y] ;
  85.  
  86. int running , generation , display , update , start_x , update , updateX ;
  87. int updateC , fnamel , shape, stopat , displaychange , torus ;
  88. int max_x , max_y , new, old , mode , start_y , Asking , greyscale ;
  89. int scale , size_x , end_x , end_y , size_y, updatesmall ;
  90. int old_x , old_y , counter, old_op, dscale , tscale , cropX =0 , cropY =0 ;
  91. int step , Exit = 0 , updown = 0 , AskVal , AskOp, mk[max_neigh] ;
  92. int sccol , bycol , scnum , numdir , numloop , dirtype , loopnum , change ;
  93.  
  94. float hscale , wscale ;
  95.  
  96. char FileName[110] , message[100] ;
  97. char theauthor[] ="b y  I.  J.  P a l m e r";             /* The author, to be placed at the top of the main menu */
  98.  
  99. struct button {
  100.   int value ;
  101.   int state ;
  102. } buttons[3];
  103.  
  104. /* ------------------------------------------------------------------------- */
  105.  
  106. void change_domain(change_to) int change_to ;
  107. /* Toggles the logical handling of the domain between a torus and a flat plane.
  108.   
  109.    Status changes : torus
  110. */
  111. {
  112.   torus = change_to ;                       /* Set the new value of torus */
  113.   if (torus)
  114.     link_cells(shape,0);                    /* If we now have a torus domain, link the cells */
  115.   else {
  116.     setup_array(0,0,max_x+1,max_y+1,1);     /* Otherwise, enlarge the domain by 1 in each direction */
  117.     link_cells(shape,0);                    /* and then link the cells */
  118.     };
  119. }
  120.  
  121. /* ------------------------------------------------------------------------- */
  122.  
  123. void setup_array ( sx,sy,fx,fy,clear ) int sx,sy,fx,fy,clear ;
  124. /* Allocates space for unallocated cells in the domain. Called when expanding the size of the domain.
  125.  
  126.    Pre : sx < fx < MAX_X   &   sy < fy < MAX_Y
  127.    Post: for all x,y : sx <= x < fx  &  sy <= y < fy   then   array[x][y] exists
  128.    
  129.    Status changes : none
  130. */
  131. {
  132.  
  133.   int x,y;
  134.   if (!torus) {
  135.     ++fx ;
  136.     ++fy ;
  137.     };
  138.   XSetForeground( dpy,smallgc,colours[states[0].colour].mapno);   /* Set the colour to black */
  139.   for( x=sx ; x<fx ; x+=1 ) 
  140.     for( y=sy ; y < fy ; y+=1)                                    /* Go through all new cell positions */
  141.       if (array[x][y] == NULL) {                                  /* If the cell is unallocated */  
  142.         array[x][y] = (CELL) malloc(sizeof(struct cell));         /* then allocate it some space */
  143.         if ((clear) && updatesmall)
  144.           XDrawPoint( dpy, small, smallgc, x, y );                /* Make sure it starts off dead */
  145.         array[x][y] -> state[1] = 0 ;
  146.         array[x][y] -> state[0] = 0 ;
  147.         };
  148. }
  149.  
  150. /* ------------------------------------------------------------------------- */
  151.  
  152. void resize()
  153. /* Sets global variables dictating start and end cells for displayed domain.
  154.    If using hexagonal neighbourhood then a template hexagon is set up to speed up drawing.
  155.  
  156.    Status changes : none
  157. */
  158. {
  159.   int z ;
  160.   if (shape == 2) {
  161.     end_y = start_y + (size_y = (DispY/scale/3.4)+2);       /* If using hexagonal cells, work out the last cells to */  
  162.     end_x = start_x + (size_x = (DispX/scale/3)+2);         /* which can be displayed when viewing the domain */
  163.     z = 1.7320508*scale+1 ;
  164.     hexagon[1].x = 2 * scale ;                              /* Create the right size of hexagon for drawing */
  165.     hexagon[1].y = 0 ;
  166.     hexagon[2].x = scale ;
  167.     hexagon[2].y = -z ;
  168.     hexagon[3].x = - scale ;
  169.     hexagon[3].y = -z ;
  170.     hexagon[4].x = -2*scale ;
  171.     hexagon[4].y = 0 ;
  172.     hexagon[5].x = -scale ;
  173.     hexagon[5].y = z ;
  174.     }
  175.   else {
  176.     end_y = start_y + (size_y = (DispY/scale/2)+2);         /* If square cells, work out the last cells to */
  177.     end_x = start_x + (size_x = (DispX/scale/2)+2);         /* which can be displayed when viewing the domain */
  178.     };
  179.   if (size_x > max_x) {                                     /* Check for overflow (wrap around) on the above numbers */
  180.     size_x = max_x ;
  181.     end_x = start_x - 1;
  182.     if (end_x == -1)
  183.       end_x = max_x  -1;
  184.     };
  185.   if (end_x > max_x)
  186.     end_x -= max_x ;
  187.   if (size_y > max_y) {
  188.     size_y = max_y ;
  189.     end_y = start_y - 1;
  190.     if (end_y == -1)
  191.       end_y = max_y  -1;
  192.     };
  193.   if (end_y > max_y)
  194.     end_y -= max_y ;
  195.   dscale = scale*2 ;                                        /* Work out some useful `constants' for this size of cell */
  196.   tscale = scale*3 ;
  197.   hscale = scale*1.73205808 ;
  198.   wscale = 3.4641016*scale ;
  199. }
  200.  
  201. /* ------------------------------------------------------------------------- */
  202.  
  203. void link_cells ( n , all) int n, all;
  204. /* Links cells to their neighbours for a given neighbourhood :
  205.     1 - 4 square
  206.     2 - hexagonal
  207.     3 - 8 square
  208.  
  209.    Status changes : shape
  210. */
  211. {
  212.   shape = n ;
  213.   cropX = cropY = 0;
  214.   resize();
  215.   switch (n) {
  216.     case 1: init4(all); break;
  217.     case 2: init6(all); break;
  218.     case 3: init8(all); break;
  219.     default: init8(all);
  220.   };
  221. }
  222.  
  223. /* ------------------------------------------------------------------------- */
  224.  
  225. void init8 (all) int all ;
  226. /* Links up neighbours for an 8 square arrangement */
  227. {
  228.  
  229.   int x,y,mx,my;
  230.   mx = max_x + 1 - torus ;
  231.   my = max_y + 1 - torus ;
  232.  
  233.   for( x=0 ; x<mx ; x+=1 ) {
  234.     for( y=0 ; y< my ; y+=1 ) {
  235.       array[x][y]->dir[0] = get(x,y-1);                 /* North */
  236.       array[x][y]->dir[4] = get(x+1,y-1);               /* North East */
  237.       array[x][y]->dir[1] = get(x+1,y);                 /* East */
  238.       array[x][y]->dir[5] = get(x+1,y+1);               /* South East */
  239.       array[x][y]->dir[2] = get(x,y+1);                 /* South */
  240.       array[x][y]->dir[6] = get(x-1,y+1);               /* South West */
  241.       array[x][y]->dir[3] = get(x-1,y);                 /* West */
  242.       array[x][y]->dir[7] = get(x-1,y-1);               /* North West */
  243.       if ((y==0) && (x < mx-2) && (x>0) && !all)
  244.         y = my-3 ;
  245.     };
  246.   };
  247. }
  248.  
  249. /* ------------------------------------------------------------------------- */
  250.  
  251. void init4 (all) int all ;
  252. /* Links up neighbours for a 4 square arrangement */
  253. {
  254.  
  255.   int x,y,mx,my ;
  256.   mx = max_x + 1 - torus ;
  257.   my = max_y + 1 - torus ;
  258.  
  259.   for( x=0 ; x<mx ; x+=1 ) {
  260.     for( y=0 ; y< my ; y+=1 ) {
  261.       array[x][y] -> dir[0] = get(x,y-1);               /* North */
  262.       array[x][y] -> dir[1] = get(x+1,y);               /* East */
  263.       array[x][y] -> dir[2] = get(x,y+1);               /* South */
  264.       array[x][y] -> dir[3] = get(x-1,y);               /* West */
  265.       if ((y==0) && (x < mx-2) && (x>0) && !all)
  266.         y = my-3 ;
  267.     };
  268.   };
  269. }
  270.  
  271. /* ------------------------------------------------------------------------- */
  272.  
  273. void init6 (all) int all ;
  274. /* Links up neighbours for a hexagonal arrangement */
  275. {
  276.  
  277.   int x,y,mx,my ;
  278.   mx = max_x + 1 - torus ;
  279.   my = max_y + 1 - torus ;
  280.  
  281.   for( x=0 ; x<mx ; x+=1 ) {
  282.     for( y=0 ; y< my ; y+=1 ) {
  283.       array[x][y] -> dir[0] = get(x,y-1);               /* North */
  284.       array[x][y] -> dir[1] = get(x+1,y-even(x));       /* North East-ish */
  285.       array[x][y] -> dir[2] = get(x+1,y+odd(x));        /* South East-ish */
  286.       array[x][y] -> dir[3] = get(x,y+1);               /* South */
  287.       array[x][y] -> dir[4] = get(x-1,y+odd(x));        /* South West-ish */
  288.       array[x][y] -> dir[5] = get(x-1,y-even(x));       /* North West-ist */
  289.       if ((y==0) && (x < mx-2) && (x > 0) && !all)
  290.         y = my-3 ;
  291.     };
  292.   };
  293. }
  294.  
  295. /* ------------------------------------------------------------------------- */
  296.  
  297. int odd (x)
  298. /*
  299.  
  300.    Pre : x >= 0
  301.    Post: 1 if x is odd
  302.          0 otherwise
  303. */
  304. int x ;
  305.  
  306. {
  307.   return ((x % 2) == 1);
  308. }
  309.  
  310. /* ------------------------------------------------------------------------- */
  311.  
  312. int even (x)
  313. /*
  314.  
  315.    Pre : x >= 0
  316.    Post: 1 if x is even
  317.          0 otherwise
  318. */
  319. int x;
  320.  
  321. {
  322.   return ((x % 2) == 0);
  323. }
  324.  
  325. /* ------------------------------------------------------------------------- */
  326.  
  327. CELL get( x,y )
  328. /* Given an x,y coordinate, returns a pointer to that cells structure, checking for overflow and underflow. */
  329.  
  330. int x,y ;
  331.  
  332. {
  333.   int mx,my ;
  334.   mx = max_x + 1 - torus ;
  335.   my = max_y + 1 - torus ;
  336.  
  337.   if (x == -1)                    /* X underflow */
  338.     x = mx-1 ;
  339.  
  340.   if (x == mx)                    /* X overflow */
  341.     x = 0 ;
  342.  
  343.   if (y == -1)                    /* Y underflow */
  344.     y = my-1 ;
  345.  
  346.   if (y == my)                    /* Y overflow */
  347.     y = 0 ;
  348.  
  349.   return array[x][y];
  350. }
  351.  
  352. /* ------------------------------------------------------------------------- */
  353.  
  354. int test_inside ( x,y )
  355. /* Tests to see if a given X,Y coordinate is inside the displayable domain, returns true if it is, false otherwise */
  356.  
  357. int x,y ;
  358.  
  359. {
  360.  
  361.   int sx,sy,ex,ey ;
  362.  
  363.   sx = start_x ;
  364.   sy = start_y ;
  365.   ex = end_x ;
  366.   ey = end_y ;
  367.  
  368.   if (ex < sx) {
  369.     sx = sx - ex ;
  370.     if ( x > ex )
  371.       x = x - ex ;
  372.     else
  373.       x = max_x - (ex - x) ;
  374.     ex = max_x ;
  375.     };
  376.  
  377.   if (ey < sy) {
  378.     sy = sy - ey ;
  379.     if ( y > ey )
  380.       y = y - ey ;
  381.     else
  382.       y = max_y - (ey - y) ;
  383.     ey = max_y ;
  384.     };
  385.  
  386.   if ((x >= sx) && (x <= ex) && (y >= sy) && (y <= ey))
  387.     return 1 ;
  388.   else
  389.     return 0 ;
  390.  
  391. }
  392.  
  393. /* ------------------------------------------------------------------------- */
  394.  
  395. void display_cells ()
  396. /* Displays the displayable cells of the domain in the `display' window (only if the Status variable `display' = 1).
  397.  
  398.    Status changes : update updateX updateC
  399. */
  400. {
  401.   int cx,cy,x,y,d ;
  402.   update = 1;
  403.   if (display == 1) {                                 /* Check we are supposed to draw the cells (check status) */
  404.     SetWindow(Display_Window);                        /* Select the correct window */
  405.     x = updateX ;                                     /* Start off where left off */
  406.     for( cx = updateC ; cx < size_x ; ++cx) {
  407.       y = start_y ;
  408.       for( cy = 0 ; cy < size_y ; ++cy ) {
  409.         draw_cell(x,y);
  410.         y = (y+1)%max_y ;
  411.       };
  412.       x = (x+1)%max_x ;
  413.       if (EventPending()) {                           /* Check for any X events */
  414.         updateX = x;                                  /* If there are any, make a note of where you are */
  415.         updateC = cx+1;                               /* and stop it */
  416.         return ;
  417.         };
  418.     };
  419.     FlushQueue();
  420.     };
  421.   updateX = start_x;                                  /* If you managed to finish the display, make a note of it */
  422.   updateC = 0;
  423.   update = 0;
  424. }
  425.  
  426. /* ------------------------------------------------------------------------- */
  427.  
  428. void draw_cell(x,y)
  429. /* Given an X,Y coordinate, displays the cell for that coordinate.
  430.  
  431.    Pre : display window selected
  432.    
  433.    Status changes : cropX cropY
  434. */
  435. int x,y;
  436. {
  437.   int dx,dy ;
  438.   dx = x ;
  439.   dy = y ;
  440.  
  441.   if (array[x][y]) {
  442.     SetColour(states[array[x][y] -> state[new]].colour);                    /* Set the correct colour */
  443.     if (x < start_x)                                                        /* Check for wrap around */
  444.       dx += max_x ;
  445.     if (y < start_y)
  446.       dy += max_y ;
  447.     if (shape != 2) {                                                       /* If 4 or 8 neighbours, draw a box */
  448.       dx = (dx-start_x)*dscale ;
  449.       dy = (dy-start_y)*dscale ;
  450.       XFillRectangle( dpy, win, gc, dx-scale , dy-scale,dscale,dscale);
  451.       if ((dx+dscale)>cropX)
  452.         cropX = dx+dscale ;
  453.       if ((dy+scale)>cropY)
  454.         cropY = dy+scale ;
  455.     }
  456.     else {
  457.       dy = (dy-start_y)*wscale+(1+odd(dx))*hscale ;                         /* Otherwise a hexagon */
  458.       dx = (dx-start_x)*tscale-scale ;
  459.       hexagon[0].x = dx ;
  460.       hexagon[0].y = dy+1 ;
  461.       XFillPolygon(dpy,win,gc,hexagon,6,Convex,CoordModePrevious);
  462.       if (dy>cropY)
  463.         cropY = dy+1 ;
  464.       if ((dx+dscale)>cropX)
  465.         cropX = dx+tscale ;
  466.     };
  467.   };
  468. }
  469.  
  470. /* ------------------------------------------------------------------------- */
  471.  
  472. void AutoResize()
  473. /* Tries to resize the domain to just fit inside the `display' window.
  474.  
  475.    Status changes : scale cropX cropY
  476. */
  477. {
  478.   cropX = cropY = 0;
  479.   if (shape == 2) {
  480.     scale = DispY/(max_x*3.4) ;
  481.     if (scale > (DispX/(max_x*3)))
  482.       scale = DispX/(max_x*3) ;
  483.     }
  484.   else {
  485.     scale = DispY/(max_x*2) ;
  486.     if (scale > (DispX/(max_x*2)))
  487.       scale = DispX/(max_x*2) ;
  488.     };
  489.   if (scale == 0)
  490.     scale = 1 ;
  491.   resize();
  492. }
  493.  
  494. /* ------------------------------------------------------------------------- */
  495.  
  496. void enlarge()
  497. /* Enlarges the size of displayed cells
  498.  
  499.    Status changes : scale cropX cropY
  500. */
  501. {
  502.   if (display >1)
  503.     return ;
  504.   cropX = cropY = 0;
  505.   scale += 1;
  506.   resize();
  507.   clear_display();
  508.   updateX = start_x;
  509.   updateC = 0;
  510.   display_cells();
  511. }
  512.  
  513. /* ------------------------------------------------------------------------- */
  514.  
  515. void decrease()
  516. /* Decreases the size of displayed cells
  517.  
  518.    Status changes : scale cropX cropY
  519. */
  520. {
  521.   if (display >1)
  522.     return ;
  523.   cropX = cropY = 0;
  524.   if (scale > 1) {
  525.     scale -= 1;
  526.     resize();
  527.     clear_display();
  528.     updateX = start_x;
  529.     updateC = 0;
  530.     display_cells();
  531.     }
  532.   else
  533.     Message("Can't shrink any more");
  534. }
  535.  
  536. /* ------------------------------------------------------------------------- */
  537.  
  538. void move_left()
  539. /* Scrolls the display window to the left
  540.  
  541.    Status changes : start_x 
  542. */
  543. {
  544.   int y,cy,x2 ;
  545.   if (display >1) {
  546.     if (display == 4)
  547.       ColourLeft();
  548.     return ;
  549.     };
  550.   if (start_x > 0)
  551.     start_x -= 1 ;
  552.   else
  553.     start_x = max_x-1 ;
  554.   resize();
  555.   SetWindow(Display_Window);
  556.   if (shape != 2)
  557.     XCopyArea(dpy,win,win,gc,0,0,cropX-dscale,cropY,dscale,0);
  558.   else
  559.     XCopyArea(dpy,win,win,gc,0,0,cropX-tscale+1,cropY,tscale,0);
  560.   y = start_y ;
  561.   x2 = (start_x+1)%max_x ;
  562.   for( cy = 0 ; cy < size_y ; ++cy ) {
  563.     draw_cell(start_x,y);
  564.     draw_cell(x2,y);
  565.     y = (y+1)%max_y ;
  566.     };
  567. }
  568.  
  569. /* ------------------------------------------------------------------------- */
  570.  
  571. void move_right()
  572. /* Scrolls the display window to the right
  573.  
  574.    Status changes : start_x 
  575. */
  576. {
  577.   int y,x,cy,cx ;
  578.   if (display >1) {
  579.     if (display == 4)
  580.       ColourRight();
  581.     return ;
  582.     };
  583.   start_x += 1;
  584.   if (start_x == max_x)
  585.     start_x = 0 ;
  586.   resize();
  587.   SetWindow(Display_Window);
  588.   if (shape != 2)
  589.     XCopyArea(dpy,win,win,gc,dscale,0,cropX,cropY,0,0);
  590.   else
  591.     XCopyArea(dpy,win,win,gc,tscale,0,cropX+1,cropY,0,0);
  592.   x = end_x;
  593.   if (x==-1)
  594.     x = max_x-1 ;
  595.   for( cx=0 ; cx<4 ; ++cx) {
  596.     y = start_y ;
  597.     for( cy = 0 ; cy < size_y ; ++cy ) {
  598.       draw_cell(x,y);
  599.       y = (y+1)%max_y ;
  600.       };
  601.     if ((x = x-1)==-1)
  602.       x = max_x -1 ;
  603.     };
  604. }
  605.  
  606. /* ------------------------------------------------------------------------- */
  607.  
  608. void move_up()
  609. /* Scrolls the display window up
  610.  
  611.    Status changes : start_y 
  612. */
  613. {
  614.   int x,y2,cx ;
  615.   if (display >1) {
  616.     if (display == 4)
  617.       ColourUp();
  618.     return ;
  619.     };
  620.   if (start_y > 0)
  621.     start_y -= 1 ;
  622.   else
  623.     start_y = max_y -1 ;
  624.   resize();
  625.   SetWindow(Display_Window);
  626.   if (shape !=2)
  627.     XCopyArea(dpy,win,win,gc,0,0,cropX,cropY-dscale,0,dscale);
  628.   else
  629.     XCopyArea(dpy,win,win,gc,0,0,cropX,cropY- (int) wscale,0,(int) wscale);
  630.   y2 = (start_y+1)%max_y ;
  631.   x = start_x ;
  632.   for( cx=0 ; cx < size_x ; ++cx ) {
  633.     draw_cell(x,start_y);
  634.     draw_cell(x,y2);
  635.     x = (x+1)%max_x ;
  636.     };
  637. }
  638.  
  639. /* ------------------------------------------------------------------------- */
  640.  
  641. void move_down()
  642. /* Scrolls the display window down
  643.  
  644.    Status changes : start_y 
  645. */
  646. {
  647.   int y,x,cy,cx ;
  648.   if (display >1) {
  649.     if (display == 4)
  650.       ColourDown();
  651.     return ;
  652.     };
  653.   start_y += 1;
  654.   if (start_y == max_y)
  655.     start_y = 0;
  656.   resize();
  657.   SetWindow(Display_Window);
  658.   if (shape !=2)
  659.     XCopyArea(dpy,win,win,gc,0,dscale,cropX,cropY,0,0);
  660.   else
  661.     XCopyArea(dpy,win,win,gc,0,(int) wscale,cropX,cropY+1,0,0);
  662.   y = end_y ;
  663.   for( cy=0 ; cy<4 ; ++cy ) {
  664.     x = start_x ;
  665.     for( cx=0 ; cx < size_x ; ++cx ) {
  666.       draw_cell(x,y);
  667.       x = (x+1)%max_x ;
  668.       };
  669.     if ((y = y-1) == -1)
  670.       y = max_y ;
  671.     };
  672. }
  673.  
  674. /* ------------------------------------------------------------------------- */
  675.  
  676. void clear_display()
  677. /* Clears the `display' window
  678. */
  679. {
  680.   SetWindow(Display_Window);
  681.   ClearScreen();
  682. }
  683.  
  684. /* ------------------------------------------------------------------------- */
  685.  
  686. void InitMulti(n) int n ;
  687. /* Sets the labels of the boxes (3 to 8) for the bottom row, depending on display type
  688.  
  689.    Pre : n = 0 or 1
  690.    Post: n = 0 - normal setup
  691.          n = 1 - built in Cal program setup
  692. */
  693. {
  694.   if (n) {
  695.     sprintf(boxes[3].text,"Grey");
  696.     sprintf(boxes[4].text,"Sharp");
  697.     sprintf(boxes[5].text,"Soft");
  698.     sprintf(boxes[6].text,"Apply");
  699.     sprintf(boxes[7].text,"Update");
  700.     sprintf(boxes[8].text,"Undo");
  701.   } else {
  702.     sprintf(boxes[3].text,"+");
  703.     sprintf(boxes[4].text,"-");
  704.     sprintf(boxes[5].text,"Left");
  705.     sprintf(boxes[6].text,"Right");
  706.     sprintf(boxes[7].text,"Up");
  707.     sprintf(boxes[8].text,"Down");
  708.   };
  709. }
  710.  
  711. /* ------------------------------------------------------------------------- */
  712.  
  713. void InitBoxes()
  714. /* Initializes all main menu `boxes', positions and labels
  715. */
  716. {
  717.   int x,y ;
  718.   for (x=0;x<max_x;++x)
  719.     for (y=0;y<max_y;++y)
  720.       array[x][y] = NULL ;
  721.   sprintf(message,"Scamper message window");
  722.   for (x=0 ; x<max_states ; ++x) 
  723.     states[x].rules = NULL ;
  724.   for ( x = 0 ; x<MaxBoxes ; ++x )
  725.     boxes[x].draw = 0 ;
  726.   for ( x=0 ; x<10 ; ++x ) {
  727.     boxes[x].draw = 1 ;
  728.     boxes[x].sx = 10+(x*70) ;
  729.     boxes[x].sy = DispY+60 ;
  730.     boxes[x].fx = 60+(x*70) ;
  731.     boxes[x].fy = DispY+90 ;
  732.     };
  733.   for ( x=10 ; x<16 ; ++x ) {
  734.     boxes[x].draw = 1 ;
  735.     boxes[x].sx = 80 ;
  736.     boxes[x].fx = 180 ;
  737.     boxes[x].sy = 80+50*(x-10+(x>12)) ;
  738.     boxes[x].fy = 110+50*(x-10+(x>12)) ;
  739.     };
  740.   for ( x=16 ; x<22 ; ++x ) {
  741.     boxes[x].draw = 1 ;
  742.     boxes[x].sx = 200 ;
  743.     boxes[x].fx = 300 ;
  744.     boxes[x].sy = 80+50*(x-16+(x>18)) ;
  745.     boxes[x].fy = 110+50*(x-16+(x>18)) ;
  746.     };
  747.   for ( x=22 ; x<28 ; ++x ) {
  748.     boxes[x].draw = 1 ;
  749.     boxes[x].sx = 320 ;
  750.     boxes[x].fx = 420 ;
  751.     boxes[x].sy = 80+50*(x-22+(x>24)) ;
  752.     boxes[x].fy = 110+50*(x-22+(x>24)) ;
  753.     };
  754.   for ( x=28 ; x<34 ; ++x ) {
  755.     boxes[x].draw = 1 ;
  756.     boxes[x].sx = 440 ;
  757.     boxes[x].fx = DispX+20 ;
  758.     boxes[x].sy = 80+50*(x-28+(x>30)) ;
  759.     boxes[x].fy = 110+50*(x-28+(x>30)) ;
  760.     if (x < 31) {
  761.       buttons[x-28].value = x-28 ;
  762.       buttons[x-28].state = 1 ;
  763.       };
  764.     };
  765.  
  766.   for( x=0 ; x<8 ; ++x) {
  767.     askbox[x].draw = 0 ;
  768.     askbox[x].set = 0 ;
  769.     askbox[x].reset = 0 ;
  770.     };
  771.  
  772.   for( x= 0 ; x<3 ; ++x ) {
  773.     askbox[x].draw = 1 ;
  774.     askbox[x].sx = 158 ;
  775.     askbox[x].fx = 226 ;
  776.     askbox[x].sy = 35 + x*50 ;
  777.     askbox[x].fy = 60 + x*50 ;
  778.     };
  779.   
  780.   for(x=3 ; x<8 ; ++x) {
  781.     askbox[x].draw = 1 ;
  782.     askbox[x].sx = 20 ;
  783.     askbox[x].fx = 35 ;
  784.     askbox[x].sy = 10 + 40*(x-3) ;
  785.     askbox[x].fy = 25 + 40*(x-3) ;
  786.     };
  787.  
  788.   sprintf(askbox[1].text,"Cancel");
  789.   sprintf(askbox[2].text,"Clear");
  790.  
  791.   sprintf(boxes[0].text,"Run");
  792.   sprintf(boxes[1].text,"Stop");
  793.   sprintf(boxes[2].text,"Step");
  794.   InitMulti(0);
  795.   sprintf(boxes[9].text,"Swap");
  796.   sprintf(boxes[10].text,"4 neighbours");
  797.   sprintf(boxes[11].text,"6 neighbours");
  798.   sprintf(boxes[12].text,"8 neighbours");
  799.   sprintf(boxes[13].text,"Home");
  800.   sprintf(boxes[14].text,"Set Home");
  801.   sprintf(boxes[15].text,"QUIT");
  802.   sprintf(boxes[16].text,"Load Domain");
  803.   sprintf(boxes[17].text,"Insert Domain");
  804.   sprintf(boxes[18].text,"Save Domain");
  805.   sprintf(boxes[19].text,"Auto Resize");
  806.   sprintf(boxes[20].text,"View Small");
  807.   sprintf(boxes[21].text,"View Stats");
  808.   sprintf(boxes[22].text,"Load Bitmap");
  809.   sprintf(boxes[23].text,"Save Bitmap");
  810.   sprintf(boxes[24].text,"Clear Domain");
  811.   sprintf(boxes[25].text,"Load Rules");
  812.   sprintf(boxes[26].text,"Clear Rules");
  813.   sprintf(boxes[27].text,"Reset Time");
  814.   sprintf(boxes[28].text,"Left Button");
  815.   sprintf(boxes[29].text,"Middle Button");
  816.   sprintf(boxes[30].text,"Right Button");
  817.   sprintf(boxes[31].text,"Change Colours");
  818.   sprintf(boxes[32].text,"");
  819.   sprintf(boxes[33].text,"");
  820. }
  821.   
  822. /* ------------------------------------------------------------------------- */
  823.  
  824. void DrawBox(x,invert) int x,invert ;
  825. /* Draws a givan box. If invert = 0 - normal, otherwise inverted colours.
  826. */
  827. {
  828.   char string[25];
  829.  
  830.   SetWindow(Main_Window);                                                         /* Select main menu window */
  831.   SelectFont(Small_Font);                                                         /* and small font */
  832.   SetColour(cols[fore]);
  833.   if ((x>27) && (x<31))
  834.     SetColour(states[buttons[x-28].value].colour);
  835.   FillRectangle(boxes[x].sx,boxes[x].sy,boxes[x].fx,boxes[x].fy);
  836.   FillRectangle(boxes[x].sx-1,boxes[x].sy+1,boxes[x].fx+1,boxes[x].fy-1);         /* Bounding box */
  837.   FillRectangle(boxes[x].sx+1,boxes[x].sy-1,boxes[x].fx-1,boxes[x].fy+1);
  838.   FillRectangle(boxes[x].sx-2,boxes[x].sy+2,boxes[x].fx+2,boxes[x].fy-2);
  839.   FillRectangle(boxes[x].sx+2,boxes[x].sy-2,boxes[x].fx-2,boxes[x].fy+2);
  840.   SetColour(cols[back]);
  841.   if (! invert) {
  842.     FillRectangle(boxes[x].sx+3,boxes[x].sy+3,boxes[x].fx-3,boxes[x].fy-3);       /* Clear the inside (if not invert) */
  843.     FillRectangle(boxes[x].sx+2,boxes[x].sy+4,boxes[x].fx-2,boxes[x].fy-4);
  844.     FillRectangle(boxes[x].sx+4,boxes[x].sy+2,boxes[x].fx-4,boxes[x].fy-2);
  845.     FillRectangle(boxes[x].sx+1,boxes[x].sy+5,boxes[x].fx-1,boxes[x].fy-5);
  846.     FillRectangle(boxes[x].sx+5,boxes[x].sy+1,boxes[x].fx-5,boxes[x].fy-1);
  847.     SetColour(cols[fore]);
  848.     };
  849.   if ((x<28) || (x==31))
  850.       Centre(boxes[x].text,boxes[x].sx,boxes[x].sy,boxes[x].fx,boxes[x].fy);      /* Place text */
  851.   else {
  852.     if (x==32)
  853.       sprintf(string,"X = %d",max_x);
  854.     if (x==33)
  855.       sprintf(string,"Y = %d",max_y);
  856.     if ((x>27) && (x<31))
  857.       if (buttons[x-28].state)
  858.         sprintf(string,"%d - %s",buttons[x-28].value,ColourName(states[buttons[x-28].value].colour));
  859.       else
  860.         sprintf(string,boxes[x].text);
  861.     Centre(string,boxes[x].sx,boxes[x].sy,boxes[x].fx,boxes[x].fy);
  862.     };
  863. }
  864.  
  865. /* ------------------------------------------------------------------------- */
  866.  
  867. void MainScreen()
  868. /* Draws the main menu
  869. */
  870. {
  871. int x,y,sx,sy ;
  872.  
  873.   change = 1 ;
  874.   SelectFont(Small_Font);
  875.   for( x=0 ; x<MaxBoxes ; ++x ) {
  876.     if (boxes[x].draw) 
  877.       DrawBox(x,0);
  878.   };
  879. }
  880.  
  881. /* ------------------------------------------------------------------------- */
  882.  
  883. void DisplayFileName(clear) int clear ;
  884. /* Displays the current file name in the file name window */
  885. {
  886.   if (display == 0) {
  887.     SetWindow(File_Window);
  888.     SetBackColour(cols[filebg]);
  889.     if (clear) {
  890.       SetColour(cols[filefg]);
  891.       ClearScreen();
  892.       Centre("File name :",0,0,256,40);
  893.       }
  894.     else {
  895.       SetColour(cols[filebg]) ;
  896.       FillRectangle(0,40,256,80) ;
  897.       SetColour(cols[filefg]);
  898.       };
  899.     FileName[fnamel] = '_' ;
  900.     FileName[fnamel+1] = 0 ;
  901.     Centre(FileName,0,40,256,80);
  902.     };
  903. }
  904.  
  905. /* ------------------------------------------------------------------------- */
  906.  
  907. void Centre( text , sx,sy,fx,fy ) char *text ; int sx,sy,fx,fy ;
  908. /* 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
  909.    bounding box, then it is right justified to the right hand side of the box.
  910. */
  911. {
  912.   int x,y ;
  913.   TextSize(text,&x,&y);
  914.   if (sx == fx)
  915.     x = fx + 10 ;
  916.   else if (x>(fx-sx))
  917.     x = fx - x ;
  918.   else
  919.     x = sx + ((fx-sx-x)/2) ;
  920.   y = (fy+sy+y-4)/2 ;
  921.   PrintString(x,y,text) ;
  922. }
  923.  
  924. /* ------------------------------------------------------------------------- */
  925.  
  926. void service()
  927. /* Given that there is an X11 event pending, this procedure reads the event and takes necessary action.
  928. */
  929. {
  930. int x,y,b,w ;
  931. EventType ev ;
  932. char c ;
  933.  
  934.   GetNextEvent(&ev,&c,&x,&y,&b,&w);
  935.  
  936.   if ( ev == EventExpose) {                     /* Expose event, a window needs redisplaying */
  937.     if (w==0)
  938.       MainScreen();
  939.     if (w==1) {
  940.       updateX = start_x;
  941.       updateC = 0;
  942.       display_cells();
  943.       if (display == 4)
  944.         ColourEvent(ev,c,x,y,b);
  945.       };
  946.     if (w==2)
  947.       Message(message);
  948.     if (w == 3)
  949.       DisplayFileName(1);
  950.     if ((w == 4) && (Asking == 1))
  951.       AskBoxes();
  952.     if ((w == 4) && (Asking == 2))
  953.       DrawGreyScale();
  954.     if ((w == 5) && error)
  955.       Error(errormess[0],errormess[1]);
  956.     if (w == 6)
  957.       change = 1 ;
  958.     };
  959.  
  960.   if (ev == EventMouseDown) {                   /* A mouse button has been pressed down */
  961.     updown = b+1 ;
  962.     old_x = x ;
  963.     old_y = y ;
  964.     counter = 0;
  965.     if (w==2)
  966.       Message("");
  967.     };
  968.  
  969.   if (ev == EventMouseUp) {                     /* A mouse button has been released */
  970.     updown = 0 ;
  971.     old_op = 0 ;
  972.     if (Asking == 2)
  973.       GreyScaleMouseUp();
  974.     };
  975.  
  976.   if (ev == EventKeypress) {                    /* A key has been pressed */
  977.     if ((c > 31) && (c != 127) && (fnamel < 100))
  978.       FileName[fnamel++] = c ;
  979.     else if (((c == 127) || (c == 8)) && (fnamel > 0))
  980.       --fnamel ;
  981.     else if (c == 13) 
  982.       CallAsk();
  983.     else if (c == 21)
  984.       fnamel = 0;
  985.     else if (c == 18)
  986.       Total_Refresh();
  987.     DisplayFileName(0);
  988.     };
  989.  
  990.   if (updown && (w==1))
  991.     cellop(x,y,updown-1);
  992.   if (updown && (w==0))
  993.     operation(old_x,old_y,updown-1);
  994.   if (updown && (w==4) && (Asking == 1))
  995.     ButtonAsk(x,y);
  996.   if (updown && (w==4) && (Asking == 2))
  997.     GreyScaleMove(x,y);
  998.   if (updown && (w==5) && error)
  999.     ErrorWindow( error = 0 );
  1000. }
  1001.  
  1002. /* ------------------------------------------------------------------------- */
  1003.  
  1004. void Total_Refresh()
  1005. /* Refresh all windows that are visible */
  1006. {
  1007.   SetWindow(Main_Window);
  1008.   ClearScreen();
  1009.   if (display) {
  1010.     SetWindow(Display_Window);
  1011.     ClearScreen();
  1012.     };
  1013.   if (Asking) {
  1014.     SetWindow(Multi_Window);
  1015.     ClearScreen();
  1016.     };
  1017.   if (error) {
  1018.     SetWindow(Error_Window);
  1019.     ClearScreen();
  1020.     };
  1021.   SetWindow(Title_Window);
  1022.     ClearScreen();
  1023.   if (! display) {
  1024.     SetWindow(Message_Window);
  1025.     ClearScreen();
  1026.     SetWindow(File_Window);
  1027.     ClearScreen();
  1028.     };
  1029.   MainScreen();
  1030.   Message(message);
  1031.   DisplayFileName(1);
  1032.   updateX = start_x;
  1033.   updateC = 0;
  1034.   display_cells();
  1035.   if (display == 4)
  1036.     ColourEvent(EventExpose,' ',0,0,0);
  1037.   if (display == 3)
  1038.     display_stats(0,1);
  1039.   if (display == 2)
  1040.     SmallView();
  1041.   if (Asking == 1)
  1042.     AskBoxes();
  1043.   if (Asking == 2)
  1044.     DrawGreyScale();
  1045.   if (error)
  1046.     Error(errormess[0],errormess[1]);
  1047.   change = 1 ;
  1048. }
  1049.  
  1050. /* ------------------------------------------------------------------------- */
  1051.  
  1052. void StartRun(n) int n ;
  1053. /* Force a higlight of option box n
  1054. */
  1055. {
  1056.   DrawBox(n,1);
  1057.   FlushQueue();
  1058. }
  1059.  
  1060. /* ------------------------------------------------------------------------- */
  1061.  
  1062. void operation(x,y,b)
  1063. /* Given an x,y coordinate of the mouse pointer, and the button number pressed this procedure works out which menu option
  1064.    was selected, and acts accordingly.
  1065. */
  1066. int x,y,b ;
  1067. {
  1068. int n,op,cx,cy = -1 ;
  1069.  
  1070.   if (error) return ;
  1071.  
  1072.   op = -1;
  1073.   for( n=0 ; n<MaxBoxes ; ++n )
  1074.     if ((x >= boxes[n].sx) && (x <= boxes[n].fx) && (y >= boxes[n].sy) && (y <= boxes[n].fy))
  1075.       op = n ;
  1076.   
  1077.   SetWindow(Main_Window);
  1078.   DrawBox(op,1);
  1079.   FlushQueue();
  1080.  
  1081.   if (op==15) {                     /* Quit */
  1082.     Exit = 1 ;
  1083.     Message("Goodbye");
  1084.     };
  1085.  
  1086.   if ((op >9) && (op<13)) {         /* 4, 6, or 8 neighbours */
  1087.     link_cells(op-9,1) ;
  1088.     };
  1089.  
  1090.   if (op==13) {                     /* Home */
  1091.     start_x = 0;
  1092.     start_y = 0;
  1093.     resize();
  1094.     };
  1095.  
  1096.   if (op==14)                       /* Set Home */
  1097.     SetHome();
  1098.  
  1099.   if (op==19)                       /* Auto Resize */
  1100.     AutoResize();
  1101.  
  1102.   if (op==21) {                     /* Stats */
  1103.     InitMulti(1);
  1104.     for(cy = 3; cy < 9 ; ++cy)
  1105.       DrawBox(cy,0);
  1106.     SetWindow(Display_Window);
  1107.     display_stats(0,1);
  1108.     };
  1109.  
  1110.   if (op == 26)                     /* Clear Rules */
  1111.     ClearRules();
  1112.  
  1113.   if (op == 27) {                   /* Reset Time */
  1114.     generation = 0;
  1115.     Shift = 0 ;
  1116.     reset_stats();
  1117.     Message("Time Reset");
  1118.     };
  1119.  
  1120.   if (AskVal) {                     /* Don't allow any of the below when file operation initiated */
  1121.     if (op != AskOp)
  1122.       DrawBox(op,0);
  1123.     return ;
  1124.     };
  1125.  
  1126.   if (op == 0) {                    /* Run */
  1127.     stopnum = 0 ;
  1128.     running = 1 ;
  1129.     };
  1130.  
  1131.   if (op == 1)                      /* Stop */
  1132.     running = 0 ;
  1133.  
  1134.   if (op == 2)                      /* Step */
  1135.     step = 1 ;
  1136.  
  1137.   if ((display != 2) && (display != 3)) {
  1138.     if (op == 3)                    /* Enlarge `+' */
  1139.       enlarge();
  1140.     if (op == 4)                    /* Decrease `-' */
  1141.       decrease();
  1142.     if (op == 5)                    /* Scroll Left */
  1143.       move_left();
  1144.     if (op == 6)                    /* Scroll Right */
  1145.       move_right();
  1146.     if (op == 7)                    /* Scroll Up */
  1147.       move_up();
  1148.     if (op == 8)                    /* Scroll Down */
  1149.       move_down();
  1150.     } 
  1151.   else if ((op > 2) && (op < 9))    /* Built in Cal program */
  1152.     AlternateRun(op-2) ;
  1153.  
  1154.   if (op == 9) {                    /* Swap */
  1155.     DrawBox(28,0);
  1156.     DrawBox(29,0);
  1157.     DrawBox(30,0);
  1158.     if (Asking == 2) {
  1159.       AskOff();
  1160.       Asking = 0 ;
  1161.       };
  1162.     if ((display == 2) || (display == 3)) {
  1163.       InitMulti(0);
  1164.       for(cy = 3; cy < 9 ; ++cy)
  1165.         DrawBox(cy,0);
  1166.       };
  1167.     display = 1 - display ;
  1168.     if (display < 0)
  1169.       display = 0 ;
  1170.     if (display == 1) {
  1171.       DisplayOn();
  1172.       update = 1;
  1173.       updateX = start_x;
  1174.       updateC = 0;
  1175.       }
  1176.     else {
  1177.       DisplayOff();
  1178.       };
  1179.     };
  1180.  
  1181.   if (op==16)                       /* Load Domain */
  1182.     Ask(4);
  1183.  
  1184.   if (op==17)                       /* Insert Domain */
  1185.     Ask(6);
  1186.  
  1187.   if (op==18)                       /* Save Domain */
  1188.     Ask(5);
  1189.  
  1190.   if (op==20) {                     /* View Small */
  1191.     InitMulti(1);
  1192.     for(cy = 3; cy < 9 ; ++cy)
  1193.       DrawBox(cy,0);
  1194.     SmallView();
  1195.     };
  1196.  
  1197.   if (op==22) {                     /* Load Bitmap */
  1198.     Ask(2);
  1199.     };
  1200.  
  1201.   if (op==23)                       /* Save Bitmap */
  1202.     Ask(3);
  1203.  
  1204.   if (op==24) {                     /* Clear Domain */
  1205.     XSetForeground( dpy,smallgc,colours[states[0].colour].mapno);
  1206.     for( cy=0 ; cy<max_y ; ++cy )
  1207.       for( cx=0 ; cx<max_x ; ++cx) {
  1208.         array[cx][cy]->state[new] = array[cx][cy]->state[old] = 0 ;
  1209.         if (updatesmall)
  1210.           XDrawPoint( dpy, small, smallgc, cx, cy );
  1211.         };
  1212.     };
  1213.  
  1214.   if (op == 25)                     /* Load Rules */
  1215.     Ask(1);
  1216.  
  1217.   if (((op<28) && ((op<5) || (op>8))) || (display == 2) || (display == 3))
  1218.     updown=0 ;
  1219.   else {
  1220.     counter = 0 ;                   /* Decide if repeat allowed if button held down long enough */
  1221.     old_op = op ;
  1222.     };
  1223.  
  1224.   if ((op>27) && (op<31)) {         /* Mouse Buttons' Options */
  1225.     if (b==1) {
  1226.       --buttons[op-28].value ;
  1227.       if (buttons[op-28].value == -1)
  1228.         buttons[op-28].value = max_states-1 ;
  1229.     };
  1230.     if (b==3) {
  1231.       ++buttons[op-28].value ;
  1232.       if (buttons[op-28].value == max_states)
  1233.         buttons[op-28].value = 0 ;
  1234.     };
  1235.     if (b==2)
  1236.       buttons[op-28].state = 1 - buttons[op-28].state ;
  1237.   };
  1238.  
  1239.   if (op == 31) {                   /* Change Colours */
  1240.     ColourOps();
  1241.     };
  1242.  
  1243.   if (op==32) {                     /* X size of domain */
  1244.     if ((b==1) && (max_x >5))
  1245.       --max_x ;
  1246.     if ((b==3) && (max_x < MAX_X)) {
  1247.       ++max_x ;
  1248.       setup_array(max_x-2,0,max_x,max_y,1);
  1249.       };
  1250.     link_cells(shape,0);
  1251.     };
  1252.  
  1253.   if (op==33) {                     /* Y size of domain */
  1254.     if ((b==1) && (max_y >5))
  1255.       --max_y ;
  1256.     if ((b==3) && (max_y < MAX_Y)) {
  1257.       ++max_y ;
  1258.       setup_array(0,max_y-2,max_x,max_y,1);
  1259.       };
  1260.     link_cells(shape,0);
  1261.     };
  1262.  
  1263.   if ((op != 0) && (op != 2) && (AskVal == 0))
  1264.     DrawBox(op,0);                  /* Un highlight box, if necessary */
  1265.   if (AskVal)                       /* Set file operation selected (if any) */
  1266.     AskOp = op ;
  1267.   if (op == 1)                      /* If `Stop' unhighlight `Run' */
  1268.     DrawBox(0,0);
  1269.   FlushQueue();                     /* Make sure X is up to date */
  1270.  
  1271. }
  1272.  
  1273. /* ------------------------------------------------------------------------- */
  1274.  
  1275. void cellop(px,py,b) int px,py,b ;
  1276. /* Given the x,y coordinate of the mouse pointer, and the button pressed, works out which cell to change the state of, and
  1277.    sets the state accordind to which button was pressed.
  1278. */
  1279. {
  1280.  
  1281.   int cx,cy,x,y,fx,fy,dx,dy ;
  1282.   unsigned long td,md = 100000000 ;
  1283.  
  1284.   if (display == 3) 
  1285.     StatButton(px,py);
  1286.  
  1287.   if ((display != 1) || (px > cropX) || (py > cropY)) return ;
  1288.   SetWindow(Display_Window);
  1289.   if (shape == 2)
  1290.     x = start_x + (px/tscale) -1 ;          /* Calculate the coordinates of the cell clicked on */
  1291.   else
  1292.     x = start_x + (px/dscale) -1 ;
  1293.   if (x < start_x)
  1294.     x = start_x ;
  1295.   if (x >= max_x)
  1296.     x -= max_x ;
  1297.   if (x >= max_x) {
  1298.     Message("Outside cell area");
  1299.     return ;
  1300.     };
  1301.   for( cx = 0 ; cx < 4 ; ++cx ) {
  1302.     if (shape == 2)
  1303.       y = start_y + (py/wscale) -1 ;
  1304.     else
  1305.       y = start_y + (py/dscale) -1 ;
  1306.     if (y < start_y)
  1307.       y = start_y ;
  1308.     if (y >= max_y)
  1309.       y -= max_y ;
  1310.     if (y >= max_y) {
  1311.       Message("Outside cell area");
  1312.       return ;
  1313.       };
  1314.     for( cy = 0 ; cy < 4 ; ++cy ) {
  1315.       dx = x ;
  1316.       dy = y ;
  1317.       if (x < start_x)
  1318.         dx += max_x ;
  1319.       if (y < start_y)
  1320.         dy += max_y ;
  1321.       if (shape != 2) {
  1322.         dx = (dx-start_x)*dscale ;
  1323.         dy = (dy-start_y)*dscale ;
  1324.       }
  1325.       else {
  1326.         dy = (dy-start_y)*wscale+(odd(dx))*hscale ;
  1327.         dx = (dx-start_x)*tscale ;
  1328.       };
  1329.       td = (dx-px)*(dx-px) + (dy-py)*(dy-py) ;
  1330.       if (td < md) {
  1331.         md = td ;
  1332.         fx = x ;
  1333.         fy = y ;
  1334.       };
  1335.       y = (y+1)%max_y ;
  1336.     };
  1337.     x = (x+1)%max_x ;
  1338.   };
  1339.   (array[fx][fy]->state[old]) = buttons[b-1].value ;      /* Set it's state */
  1340.   (array[fx][fy]->state[new]) = buttons[b-1].value ;
  1341.   draw_cell(fx,fy);
  1342.   if (updatesmall) {
  1343.     XSetForeground( dpy,smallgc,colours[states[array[fx][fy] -> state[new]].colour].mapno);
  1344.     XDrawPoint( dpy, small, smallgc, fx, fy );
  1345.     };                                                    /* Update Small View Image */
  1346. }
  1347.  
  1348. /* ------------------------------------------------------------------------- */
  1349.  
  1350. void Message( mess ) char* mess ;
  1351. /* Place message `mess' in the message window */
  1352. {
  1353.   SetWindow(Message_Window);
  1354.   SetColour(cols[verbfg]);
  1355.   ClearScreen();
  1356.   Centre(mess,0,0,256,80);
  1357.   sprintf(message,mess);
  1358.   FlushQueue();
  1359. }
  1360.  
  1361. /* ------------------------------------------------------------------------- */
  1362.  
  1363. void SetHome()
  1364. /* Set the `Home' position to the current start_x start_y position */
  1365. {
  1366. int x,y,X,Y ;
  1367.  
  1368.   x = start_x ;
  1369.   for( X=0 ; X<max_x ; ++X) {
  1370.     y = start_y ;
  1371.     for(Y=0 ; Y<max_y ; ++Y) {
  1372.       array[X][Y] -> state[old] = array[x][y] -> state[new] ;
  1373.       y = (y+1)%max_y ;
  1374.       };
  1375.     x = (x+1)%max_x ;
  1376.     };
  1377.   new = old ;
  1378.   old = 1-old ;
  1379.   start_x = 0 ;
  1380.   start_y = 0 ;
  1381.   resize();
  1382. }
  1383.     
  1384. /* ------------------------------------------------------------------------- */
  1385.  
  1386. void Ask(n) int n ;
  1387. /* Set up the boxes for file operations. 
  1388.   1 -> Read File (Cal program)
  1389.   2 -> Load Bitmap
  1390.   3 -> Save Bitmap
  1391.   4 -> Load domain
  1392.   5 -> Save domain
  1393.   6 -> Insert domain
  1394. */
  1395. {
  1396. int x ;
  1397.  
  1398.   for(x = 3 ; x<8 ; ++x ) {
  1399.     askbox[x].draw = 0 ;
  1400.     askbox[x].set = 0 ;
  1401.     askbox[x].reset = 0 ;
  1402.     };
  1403.  
  1404.   switch(n) {
  1405.     case 1 :  sprintf(askbox[0].text,"Read"); 
  1406.               sprintf(askbox[3].text,"Display On"); askbox[3].draw = 1 ;
  1407.               sprintf(askbox[4].text,"Display Off"); askbox[4].draw = 1 ;
  1408.               askbox[3].set = 1 ;
  1409.               askbox[3].reset = 4 ;
  1410.               askbox[4].reset = 3 ;
  1411.               break ;
  1412.     case 2 :  sprintf(askbox[0].text,"Load");
  1413.               sprintf(askbox[3].text,"Full Size"); askbox[3].draw = 1 ;
  1414.               sprintf(askbox[4].text,"Half Size"); askbox[4].draw = 1 ;
  1415.               sprintf(askbox[5].text,"Double Size"); askbox[5].draw = 1 ;
  1416.               sprintf(askbox[6].text,"Invert"); askbox[6].draw = 1 ;
  1417.               sprintf(askbox[7].text,"Normal"); askbox[7].draw = 1 ;
  1418.               askbox[3].set = 1 ;
  1419.               askbox[7].set = 1 ;
  1420.               askbox[3].reset = 4 ;
  1421.               askbox[4].reset = 5 ;
  1422.               askbox[5].reset = 3 ;
  1423.               askbox[6].reset = 7 ;
  1424.               askbox[7].reset = 6 ;
  1425.               break ;
  1426.     case 3 :  sprintf(askbox[0].text,"Save");
  1427.               sprintf(askbox[3].text,"Sub-directory"); askbox[3].draw = 1 ;
  1428.               sprintf(askbox[4].text,"Current directory"); askbox[4].draw = 1 ;
  1429.               askbox[3].set = 1 ;
  1430.               askbox[3].reset = 4 ;
  1431.               askbox[4].reset = 3 ;
  1432.               break ;
  1433.     case 4 :  sprintf(askbox[0].text,"Load");
  1434.               sprintf(askbox[3].text,"Full Size"); askbox[3].draw = 1 ;
  1435.               sprintf(askbox[4].text,"Half Size"); askbox[4].draw = 1 ;
  1436.               sprintf(askbox[5].text,"Double Size"); askbox[5].draw = 1 ;
  1437.               askbox[3].set = 1 ;
  1438.               askbox[3].reset = 4 ;
  1439.               askbox[4].reset = 5 ;
  1440.               askbox[5].reset = 3 ;
  1441.               break ;
  1442.     case 5 :  sprintf(askbox[0].text,"Save");
  1443.               sprintf(askbox[3].text,"Sub-directory"); askbox[3].draw = 1 ;
  1444.               sprintf(askbox[4].text,"Current directory"); askbox[4].draw = 1 ;
  1445.               sprintf(askbox[5].text,"Scamper format"); askbox[5].draw = 1 ;
  1446.               sprintf(askbox[6].text,"Postscript"); askbox[6].draw = 1 ;
  1447.               askbox[3].set = 1 ;
  1448.               askbox[5].set = 1 ;
  1449.               askbox[3].reset = 4 ;
  1450.               askbox[4].reset = 3 ;
  1451.               askbox[5].reset = 6 ;
  1452.               askbox[6].reset = 5 ;
  1453.               break ;
  1454.     case 6 :  sprintf(askbox[0].text,"Insert");
  1455.               sprintf(askbox[3].text,"Add"); askbox[3].draw = 1 ;
  1456.               sprintf(askbox[4].text,"And"); askbox[4].draw = 1 ;
  1457.               sprintf(askbox[5].text,"Or"); askbox[5].draw = 1 ;
  1458.               sprintf(askbox[6].text,"Not"); askbox[6].draw = 1 ;
  1459.               askbox[3].set = 1 ;
  1460.               askbox[3].reset = 4 ;
  1461.               askbox[4].reset = 5 ;
  1462.               askbox[5].reset = 6 ;
  1463.               askbox[6].reset = 3 ;
  1464.               break ;
  1465.     default: ;
  1466.     };
  1467.   AskOn(1);
  1468.   Asking = 1 ;
  1469.   FlushQueue();
  1470.   AskVal = n ;
  1471.   SetWindow(Multi_Window);
  1472.   SetBackColour(cols[filebg]);
  1473.   ClearScreen();
  1474.   SetColour(cols[filefg]);
  1475. }
  1476.  
  1477. /* ------------------------------------------------------------------------- */
  1478.  
  1479. void CallAsk()
  1480. /* Either `Return' has been pressed or the commit option for a file operation selected. So activate the
  1481. file operation which is selected */
  1482. {
  1483. int n , op , on , off, temp;
  1484.  
  1485.   AskOff();
  1486.   Asking = 0 ;
  1487.   BusyCursor();
  1488.   FlushQueue();
  1489.   temp = AskVal ;
  1490.   AskVal = 0 ;
  1491.   switch(temp) {
  1492.     case 1 : displaychange = askbox[3].set ;      /* Read File */
  1493.              ReadFile(); break ;
  1494.     case 2 : for( op=3 ; op<6 ; ++op)             /* Load Bitmap */
  1495.                if (askbox[op].set)
  1496.                  n = op - 2 ;
  1497.              on = askbox[7].set ;
  1498.              off = askbox[6].set ;
  1499.              LoadBitmap(n,on,off);
  1500.              break ;
  1501.     case 3 : if (askbox[3].set)                   /* Save Bitmap */
  1502.                SaveBitmap(1);
  1503.              else
  1504.                SaveBitmap(2);
  1505.              break ;
  1506.     case 4 : for( op=3 ; op<6 ; ++op)             /* Load Domain */
  1507.                if (askbox[op].set)
  1508.                  n = op - 2 ;
  1509.              LoadScreen(n); break ;
  1510.     case 5 : n = 1 + askbox[4].set ;              /* Save Domain */
  1511.              if (askbox[5].set)
  1512.                SaveScreen(n); 
  1513.              else
  1514.                SavePS(n);
  1515.              break ;
  1516.     case 6 : for( op=3 ; op<7 ; ++op)             /* Insert Domain */
  1517.                if (askbox[op].set)
  1518.                  n = op - 2 ;
  1519.              InsertScreen(n); break ;
  1520.     default: ;
  1521.     };
  1522.   SetWindow(Main_Window);
  1523.   DrawBox(AskOp,0);
  1524.   OnCursor();
  1525. }
  1526.  
  1527. /* ------------------------------------------------------------------------- */
  1528.  
  1529. void UpdateButtons( n ) int n ;
  1530. /* A state colour has been changed (state = n), see if any of the `Mouse Button' options needs updating, if so update them
  1531. */
  1532. {
  1533.   int b ;
  1534.  
  1535.   for(b = 0 ; b<3 ; ++b)
  1536.     if (buttons[b].value == n)
  1537.       DrawBox(28+b,0);
  1538. }
  1539.  
  1540. /* ------------------------------------------------------------------------- */
  1541.  
  1542. void AskBoxes()
  1543. /* Draw all the Ask Boxes (those of file operations)
  1544. */
  1545. {
  1546.   int x ;
  1547.   SetWindow(Multi_Window);
  1548.   for( x=0 ; x < 8 ; ++x ) 
  1549.     if (askbox[x].draw) 
  1550.       DrawAskBox(x);
  1551. }
  1552.  
  1553. /* ------------------------------------------------------------------------- */
  1554.  
  1555. void DrawAskBox(x) int x ;
  1556. /* Given the number of an Ask Box, draw it in the Multi_Window
  1557. */
  1558. {
  1559.  
  1560.   SetWindow(Multi_Window);
  1561.   SetColour(cols[filefg]);
  1562.   FillRectangle(askbox[x].sx,askbox[x].sy,askbox[x].fx,askbox[x].fy);
  1563.   FillRectangle(askbox[x].sx-1,askbox[x].sy+1,askbox[x].fx+1,askbox[x].fy-1);
  1564.   FillRectangle(askbox[x].sx+1,askbox[x].sy-1,askbox[x].fx-1,askbox[x].fy+1);
  1565.   if (x<3) {
  1566.     FillRectangle(askbox[x].sx-2,askbox[x].sy+2,askbox[x].fx+2,askbox[x].fy-2);
  1567.     FillRectangle(askbox[x].sx+2,askbox[x].sy-2,askbox[x].fx-2,askbox[x].fy+2);
  1568.     };
  1569.   if ((x<3) || (askbox[x].set==0)) {
  1570.     SetColour(cols[filebg]);
  1571.     FillRectangle(askbox[x].sx+3,askbox[x].sy+3,askbox[x].fx-3,askbox[x].fy-3);
  1572.     FillRectangle(askbox[x].sx+2,askbox[x].sy+4,askbox[x].fx-2,askbox[x].fy-4);
  1573.     FillRectangle(askbox[x].sx+4,askbox[x].sy+2,askbox[x].fx-4,askbox[x].fy-2);
  1574.     if (x<3) {
  1575.       FillRectangle(askbox[x].sx+1,askbox[x].sy+5,askbox[x].fx-1,askbox[x].fy-5);
  1576.       FillRectangle(askbox[x].sx+5,askbox[x].sy+1,askbox[x].fx-5,askbox[x].fy-1);
  1577.       };
  1578.     };
  1579.   SetColour(cols[filefg]);
  1580.   if (x<3)
  1581.     Centre(askbox[x].text,askbox[x].sx,askbox[x].sy,askbox[x].fx,askbox[x].fy);
  1582.   else
  1583.     Centre(askbox[x].text,askbox[x].fx,askbox[x].sy,askbox[x].fx,askbox[x].fy);
  1584. }
  1585.   
  1586. /* ------------------------------------------------------------------------- */
  1587.  
  1588. void ButtonAsk(x,y) int x,y ;
  1589. /* A button has been pressed in the Multi-Window whilst a file operation has been initiated. Act on the
  1590. button press...
  1591. */
  1592. {
  1593.   int n , op;
  1594.  
  1595.   updown = 0;
  1596.   op = -1 ;
  1597.   for( n=0 ; n<8 ; ++n )
  1598.     if ((x >= askbox[n].sx) && (x <= askbox[n].fx) && (y >= askbox[n].sy) && (y <= askbox[n].fy))
  1599.       op = n ;
  1600.   if (op == 0)
  1601.     CallAsk();
  1602.   if (op == 1) {
  1603.     AskVal = 0 ;
  1604.     Asking = 0 ;
  1605.     AskOff();
  1606.     DrawBox(AskOp,0);
  1607.     FlushQueue();
  1608.     };
  1609.   if (op == 2) {
  1610.     fnamel = 0;
  1611.     DisplayFileName(0);
  1612.     };
  1613.   if (op > 2) {
  1614.     askbox[op].set = 1 ; DrawAskBox(op) ;
  1615.     n = op ;
  1616.     do {
  1617.       n = askbox[n].reset ;
  1618.       if ((n != 0) && (n != op)) {
  1619.         askbox[n].set = 0 ; DrawAskBox(n) ;
  1620.         };
  1621.       } while ((n != 0) && (n != op));
  1622.     };
  1623. }
  1624.  
  1625. /* ------------------------------------------------------------------------- */
  1626.  
  1627. void SmallView()
  1628. /* Display the `Small View' image
  1629. */
  1630. {
  1631.   int sx,sy ;
  1632.  
  1633.   DisplayOn();
  1634.   SetWindow(Display_Window);
  1635.   display = 2 ;
  1636.   sx = (DispX - max_x) / 2 ;
  1637.   sy = (DispY - max_y) / 2 ;
  1638.   XCopyArea(dpy,small,win,gc,0,0,max_x,max_y,sx,sy);
  1639. }
  1640.  
  1641. /* ------------------------------------------------------------------------- */
  1642.  
  1643. void Initialise()
  1644. /* Initialise the program, operation include :
  1645.  
  1646.     Open windows
  1647.     Initialise the main menu options
  1648.     Set up status
  1649.     Set all states colours to either balck or white
  1650.     Create initial domain
  1651.     Draw main menu
  1652. */
  1653. {
  1654.   int s ;
  1655.  
  1656.   InitialiseWindows();
  1657.   MakeWindows();
  1658.   InitBoxes();
  1659.   max_x = 20 ;
  1660.   max_y = 20 ;
  1661.   start_x = 0;
  1662.   start_y = 0;
  1663.   new = 0 ;
  1664.   old = 1 ;
  1665.   scale = 30;
  1666.   shape = 1;
  1667.   Shift = 0 ;
  1668.   mode = 0 ;
  1669.   stopat = 1 ;
  1670.   torus = 1 ;
  1671.   stopnum = 0 ;
  1672.   displaychange = 1 ;
  1673.   fnamel = 0 ;
  1674.   AskVal = 0 ;
  1675.   Asking = 0 ;
  1676.   generation = 0;
  1677.   error = 0 ;
  1678.   display = 0;
  1679.   updateX = start_x;
  1680.   updateC = 0;
  1681.   updatesmall = 1 ;
  1682.  
  1683.   for( s=0 ; s<max_states ; ++s)
  1684.     states[s].colour = AllocColour(s % 2,-1,-1) ;
  1685.   setup_array(0,0,max_x,max_y,1);
  1686.   link_cells(1,1);
  1687.   init_stats();
  1688.   InitGreyScale();
  1689.   FlushQueue();
  1690.   MainScreen();
  1691.   AutoResize();
  1692.   ClearRules();
  1693.  
  1694.   sccol = cols[title] ;
  1695.   scnum = 0 ;
  1696.   numdir = -1 ;
  1697.   numloop = 0 ;
  1698.   dirtype = 1 ;
  1699.   change = 1 ;
  1700.   greyscale = 1 ;
  1701.   loopnum = 75 ;
  1702. }
  1703.  
  1704. /* ------------------------------------------------------------------------- */
  1705.  
  1706. main()
  1707. /* Main loop for Scamper */
  1708. {
  1709.  
  1710.   Initialise();                                           /* Initialise ScAmPeR */
  1711.  
  1712.   do {                                                    /* Repeat */
  1713.     if (update)                                             /* if domain is still being drawn, then carry on */
  1714.       display_cells();
  1715.     while (EventPending())                                  /* While there is an X event waiting, serve it */
  1716.       service();
  1717.     if (updown)                                             /* If mouse button held down, then repeat selection */
  1718.       if ((++counter == 800) && (old_op != 0))
  1719.         operation(old_x,old_y,updown-1);
  1720.     if (running || step) {                                  /* Perform a generation if required */
  1721.       run();
  1722.       if (step != 0)
  1723.         DrawBox(2,0);
  1724.       step = 0;
  1725.       }
  1726.     else if (greyscale || (scnum != 50))                    /* If nothing else to do, update the trendy title bar */
  1727.       if (++numloop == 512) {
  1728.         if ((scnum == 0) || (scnum == loopnum)) {
  1729.           numdir = -numdir ;
  1730.           if (scnum == 0) {
  1731.             loopnum = 75 ;
  1732.             change = 1 ;
  1733.             dirtype = 1 - dirtype ;
  1734.             if (dirtype == 0)
  1735.               loopnum = 256 ;
  1736.             };
  1737.           };
  1738.         scnum += numdir ;
  1739.         if ((scnum == 1) && (! greyscale)) {
  1740.           scnum = 50 ;
  1741.           dirtype = 0 ;
  1742.           change = 1 ;
  1743.           }
  1744.         else if (! greyscale) {
  1745.           scnum = 0 ;
  1746.           numdir = -1 ;
  1747.           };
  1748.         if (scnum <= 50) {
  1749.           bycol = colours[sccol].mapno ;
  1750.           sccol = fade(scnum,sccol);
  1751.           if ((bycol != colours[sccol].mapno) || change) {
  1752.             change = 0 ;
  1753.             SetWindow(Title_Window) ; 
  1754.             SelectFont(Large_Font);
  1755.             SetColour(sccol);
  1756.             if (dirtype == 0) {
  1757.               PrintString(23+offset,28,"S");
  1758.               PrintString(50+offset,33,"C");
  1759.               PrintString(80+offset,28,"A");
  1760.               PrintString(105+offset,33,"M");
  1761.               PrintString(136+offset,28,"P");
  1762.               PrintString(163+offset,33,"E");
  1763.               PrintString(191+offset,28,"R");
  1764.               }
  1765.             else
  1766.               Centre( theauthor , 0 , 0 , DispX-300 , 35);
  1767.             SelectFont(Small_Font);
  1768.             };
  1769.           };
  1770.         numloop = 0 ;
  1771.         };
  1772.   } while (! Exit);                                           /* Until `Quit' has been selected */
  1773.   CloseDownWindows();                                         /* Close all windows */
  1774. }
  1775.