[comp.sources.x] v02i072: A demo of continuous graphics in a Toolkit window, Part01/02

mikew@wyse.wyse.com (Mike Wexler) (01/05/89)

Submitted-by: moraes@csri.toronto.edu (Mark Moraes)
Posting-number: Volume 2, Issue 72
Archive-name: life/part01

#! /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 archive 1 (of 2)."
# Contents:  README AUTHOR Imakefile Makefile object.h xlife.c
#   xlife.man xlife.pat xlife2.c
# Wrapped by mikew@wyse on Wed Jan  4 09:57:36 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2375 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis is an interactive demo of John Conway's game of Life. This
Ximplementation was inspired by the one on a DEC Firefly. (We have only
Xone Firefly, but lots of X workstations:-)
X
XI wrote it as a skeleton framework/demo on which people could base
Xapplications that they wanted to port quickly to X, which were
Xbasically single window things that took a few key/mouse commands,
Xand did mostly continuous output. Usually, these applications had
Xtheir own main loop, or suchlike, and couldn't call XtMainLoop. To
Xdo all the WM/Normal/IconHints, options, defaults stuff right
Xwithout the Toolkit is cumbersome, but trying to do basic graphics
Xrequires Xlib calls. And everyone whom I gave the Xt documents came
Xback shaking their heads saying "No thanks - we'll use the Tek
Xwindow".
X
XAnyway, this attempts to answer the question
X	How do I put up a window and draw stuff to it?
Xbut uses the Toolkit.
X
Xxlife.c is interesting as a simple Tookit demo which does CONTINUOUS
Xgraphic output, and uses the SIGIO signal to process events. (expose,
Xmouse). Much code stolen from contrib/gnuplot.
X
Xxlife2.c uses ordinary event handling, processing events only between 
Xiterations. Simpler, but less responsive.
X
XMakes a neat game. Start it off, and try to kill off all life by
Xadding cells while the game is running - you're not allowed to stop it
Xin between except to answer the phone or suchlike :-).
X
XTo compile, edit the Imakefile to define HAVE_RANDOM if you have it, and to
Xuncomment the lines for xlife2 if you want that as well, make Makefile, make
Xdepend, and make. Try it with 'xlife -pat xlife.pat'. If you want, then
X'make install'.
X
XHave fun. Send additions to the xlife.pat file, enhancements, fixes,
Xcomments, criticism etc. to 
X
X                                        Mark Moraes
X                                        Computer Systems Research Institute,
X                                        University of Toronto,
X                                        Toronto, Canada M5S 1A4
X                                        
X                                        {your favourite backbone}!utcsri!moraes
X                                        moraes@utcsri.UUCP (new style)
X                                        moraes@csri.toronto.edu
X                                        moraes@csri.utoronto.ca
X                                        moraes@csri.utoronto (bitnet)
END_OF_FILE
if test 2375 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'AUTHOR' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'AUTHOR'\"
else
echo shar: Extracting \"'AUTHOR'\" \(658 characters\)
sed "s/^X//" >'AUTHOR' <<'END_OF_FILE'
X(Message inbox:688)
XReturn-Path: moraes@church.csri.toronto.edu
XReceived:  by wyse.wyse.com (5.58/Wyse master/5-13-88)
X	id AA07721; Sat, 31 Dec 88 03:05:38 PST
XReceived: from [128.100.1.6] by uunet.UU.NET (5.59/1.14) 
X	id AA14417; Sat, 31 Dec 88 02:48:26 EST
XReceived: by church.csri.toronto.edu id 487; Sat, 31 Dec 88 02:48:59 EST
XFrom: Mark Moraes <moraes@csri.toronto.edu>
XTo: mikew@wyse.wyse.com
XSubject: xlife
XMessage-Id: <88Dec31.024859est.487@church.csri.toronto.edu>
XDate: 	Sat, 31 Dec 88 02:48:48 EST
X
XA demo of continuous graphics in a Toolkit window, using SIGIO to process
Xevents. Reasonably entertaining game too - exploring Life is always fun.
END_OF_FILE
if test 658 -ne `wc -c <'AUTHOR'`; then
    echo shar: \"'AUTHOR'\" unpacked with wrong size!
fi
# end of 'AUTHOR'
fi
if test -f 'Imakefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Imakefile'\"
else
echo shar: Extracting \"'Imakefile'\" \(526 characters\)
sed "s/^X//" >'Imakefile' <<'END_OF_FILE'
XLOCAL_LIBRARIES = $(XTOOLLIB) $(XLIB)
X
X# Define HAVE_RANDOM if you have the improved random() call in libc.
X# If you haven't, then xlife will use rand().
X
XDEFINES = -DPATFILE=\"$(LIBDIR)/xlife.pat\" # -DHAVE_RANDOM -DWINDOWDEBUG
XCDEBUGFLAGS = -O #-g
X
X# Uncomment if you want xlife2 as well.
X
XPROGRAMS = xlife xlife2
X
XOBJS1 = xlife.o
XSRCS1 = xlife.c
X
XOBJS2 = xlife2.o
XSRCS2 = xlife2.c
X
XComplexProgramTarget_1(xlife, $(LOCAL_LIBRARIES),)
XInstallNonExec(xlife.pat, $(LIBDIR))
X
XComplexProgramTarget_2(xlife2, $(LOCAL_LIBRARIES),)
END_OF_FILE
if test 526 -ne `wc -c <'Imakefile'`; then
    echo shar: \"'Imakefile'\" unpacked with wrong size!
fi
# end of 'Imakefile'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(7121 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile generated by imake - do not edit!
X# $XConsortium: imake.c,v 1.37 88/10/08 20:08:30 jim Exp $
X#
X# The cpp used on this machine replaces all newlines and multiple tabs and
X# spaces in a macro expansion with a single space.  Imake tries to compensate
X# for this, but is not always successful.
X#
X
X###########################################################################
X# X Window System Makefile generated from template file Imake.tmpl
X# $XConsortium: Imake.tmpl,v 1.91 88/10/23 22:37:10 jim Exp $
X#
X# Do not change the body of the imake template file.  Server-specific
X# parameters may be set in the appropriate .macros file; site-specific
X# parameters (but shared by all servers) may be set in site.def.  If you
X# make any changes, you'll need to rebuild the makefiles using
X# "make World" (at best) or "make Makefile; make Makefiles" (at least) in
X# the top level directory.
X#
X# If your C preprocessor doesn't define any unique symbols, you'll need
X# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing
X# "make Makefile", "make Makefiles", or "make World").
X#
X# If you absolutely can't get imake to work, you'll need to set the
X# variables at the top of each Makefile as well as the dependencies at the
X# bottom (makedepend will do this automatically).
X#
X
X###########################################################################
X# platform-specific configuration parameters - edit Sun.macros to change
X
X# platform:  $XConsortium: Sun.macros,v 1.52 88/10/23 11:00:55 jim Exp $
X# operating system:   SunOS 3.4
X
XBOOTSTRAPCFLAGS =
X             AS = as
X             CC = cc
X            CPP = /lib/cpp
X             LD = ld
X           LINT = lint
X        INSTALL = install
X           TAGS = ctags
X             RM = rm -f
X             MV = mv
X             LN = ln -s
X         RANLIB = ranlib
XRANLIBINSTFLAGS = -t
X             AR = ar clq
X             LS = ls
X       LINTOPTS = -axz
X    LINTLIBFLAG = -C
X           MAKE = make
XSTD_CPP_DEFINES =
X    STD_DEFINES =
X
X###########################################################################
X# site-specific configuration parameters - edit site.def to change
X
X# site:  $XConsortium: site.def,v 1.16 88/10/12 10:30:24 jim Exp $
X
X###########################################################################
X# definitions common to all Makefiles - do not edit
X
X          SHELL =  /bin/sh
X
X        DESTDIR = /global
X      USRLIBDIR = $(DESTDIR)/lib
X         BINDIR = $(DESTDIR)/bin/X11
X         INCDIR = $(DESTDIR)/include
X         ADMDIR = $(DESTDIR)/usr/adm
X         LIBDIR = $(USRLIBDIR)/X11
X     LINTLIBDIR = $(USRLIBDIR)/lint
X        FONTDIR = $(LIBDIR)/fonts
X       XINITDIR = $(LIBDIR)/xinit
X         XDMDIR = $(LIBDIR)/xdm
X         UWMDIR = $(LIBDIR)/uwm
X         AWMDIR = $(LIBDIR)/awm
X         TWMDIR = $(LIBDIR)/twm
X          DTDIR = $(LIBDIR)/dt
X        MANPATH = /usr/man
X  MANSOURCEPATH = $(MANPATH)/man
X         MANDIR = $(MANSOURCEPATH)n
X      LIBMANDIR = $(MANSOURCEPATH)n3
X    XAPPLOADDIR = $(LIBDIR)/app-defaults
X
X   INSTBINFLAGS = -m 0755
X   INSTUIDFLAGS = -m 4755
X   INSTLIBFLAGS = -m 0664
X   INSTINCFLAGS = -m 0444
X   INSTMANFLAGS = -m 0444
X   INSTAPPFLAGS = -m 0444
X  INSTKMEMFLAGS = -m 4755
X        FCFLAGS = -t
X    CDEBUGFLAGS = -O
X
X        PATHSEP = /
X         DEPEND = $(BINDIR)/makedepend
X          IMAKE = $(BINDIR)/imake
X            RGB = $(LIBDIR)/rgb
X             FC = $(BINDIR)/bdftosnf
X      MKFONTDIR = $(BINDIR)/mkfontdir
X      MKDIRHIER = $(BINDIR)/mkdirhier.sh
X
X         CFLAGS = $(CDEBUGFLAGS) $(INCLUDES) $(STD_DEFINES) $(DEFINES)
X      LINTFLAGS = $(LINTOPTS) $(INCLUDES) $(STD_DEFINES) $(DEFINES) -DLINT
X        LDFLAGS = $(CDEBUGFLAGS) -L$(USRLIBDIR) $(SYS_LIBRARIES) $(SYSAUX_LIBRARIES)
X
X       IRULESRC = $(LIBDIR)/imake.includes
X
X   EXTENSIONLIB = $(USRLIBDIR)/libext.a
X           XLIB = $(USRLIBDIR)/libX11.a
X         XMULIB = $(USRLIBDIR)/libXmu.a
X        OLDXLIB = $(USRLIBDIR)/liboldX.a
X       XTOOLLIB = $(USRLIBDIR)/libXt.a
X         XAWLIB = $(USRLIBDIR)/libXaw.a
X       LINTXLIB = $(USRLIBDIR)/llib-lX11.ln
X        LINTXMU = $(USRLIBDIR)/llib-lXmu.ln
X      LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln
X        LINTXAW = $(USRLIBDIR)/llib-lXaw.ln
X       INCLUDES = -I$(INCDIR)
X      MACROFILE = Sun.macros
X   ICONFIGFILES = $(IRULESRC)/Imake.tmpl \
X			$(IRULESRC)/$(MACROFILE) $(IRULESRC)/site.def
X  IMAKE_DEFINES =
X      IMAKE_CMD = $(NEWTOP)$(IMAKE) -TImake.tmpl -I$(NEWTOP)$(IRULESRC) \
X			-s Makefile $(IMAKE_DEFINES)
X         RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a \
X			.emacs_* tags TAGS make.log MakeOut
X
X###########################################################################
X# rules:  $XConsortium: Imake.rules,v 1.71 88/10/23 22:46:34 jim Exp $
X
X###########################################################################
X# start of Imakefile
X
XLOCAL_LIBRARIES = $(XTOOLLIB) $(XLIB)
X
X# Define HAVE_RANDOM if you have the improved random() call in libc.
X# If you haven't, then xlife will use rand().
X
XDEFINES = -DPATFILE=\"$(LIBDIR)/xlife.pat\" # -DHAVE_RANDOM -DWINDOWDEBUG
XCDEBUGFLAGS = -O #-g
X
X# Uncomment if you want xlife2 as well.
X
XPROGRAMS = xlife xlife2
X
XOBJS1 = xlife.o
XSRCS1 = xlife.c
X
XOBJS2 = xlife2.o
XSRCS2 = xlife2.c
X
X OBJS = $(OBJS1) $(OBJS2) $(OBJS3)
X SRCS = $(SRCS1) $(SRCS2) $(SRCS3)
X
Xall:: $(PROGRAMS)
X
Xxlife: $(OBJS1)  $(LOCAL_LIBRARIES)
X	$(RM) $@
X	$(CC) -o $@ $(OBJS1)  $(LOCAL_LIBRARIES) $(LDFLAGS)  $(SYSLAST_LIBRARIES)
X
Xinstall:: xlife
X	$(INSTALL) -c $(INSTALLFLAGS) xlife $(BINDIR)
X
Xinstall.man:: xlife.man
X	$(INSTALL) -c $(INSTMANFLAGS) xlife.man $(MANDIR)/xlife.n
X
Xrelink::
X	$(RM) $(PROGRAMS)
X	$(MAKE) $(MFLAGS) $(PROGRAMS)
X
Xdepend:: $(DEPEND)
X
Xdepend::
X	$(DEPEND) -s "# DO NOT DELETE" -- $(CFLAGS) -- $(SRCS)
X
X$(DEPEND):
X	@echo "making $@"; \
X	cd $(DEPENDSRC); $(MAKE)
X
Xclean::
X	$(RM) $(PROGRAMS)
X
Xinstall:: xlife.pat
X	$(INSTALL) -c $(INSTALLFLAGS) xlife.pat  $(LIBDIR)
X
Xxlife2: $(OBJS2)  $(LOCAL_LIBRARIES)
X	$(RM) $@
X	$(CC) -o $@ $(OBJS2)  $(LOCAL_LIBRARIES) $(LDFLAGS)  $(SYSLAST_LIBRARIES)
X
Xinstall:: xlife2
X	$(INSTALL) -c $(INSTALLFLAGS) xlife2 $(BINDIR)
X
Xinstall.man:: xlife2.man
X	$(INSTALL) -c $(INSTMANFLAGS) xlife2.man $(MANDIR)/xlife2.n
X
X###########################################################################
X# Imake.tmpl common rules for all Makefiles - do not edit
X
Xemptyrule::
X
Xclean::
X	$(RM_CMD) \#*
X
XMakefile:: $(IMAKE)
X
XMakefile:: Imakefile \
X	$(IRULESRC)/Imake.tmpl \
X	$(IRULESRC)/Imake.rules \
X	$(IRULESRC)/site.def \
X	$(IRULESRC)/$(MACROFILE)
X	-@if [ -f Makefile ]; then \
X	echo "$(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \
X	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
X	else exit 0; fi
X	$(IMAKE_CMD) -DTOPDIR=$(TOP)
X
X$(IMAKE):
X	@echo "making $@"; \
X	cd $(IMAKESRC); $(MAKE) BOOTSTRAPCFLAGS=$(BOOTSTRAPCFLAGS)
X
Xtags::
X	$(TAGS) -w *.[ch]
X	$(TAGS) -xw *.[ch] > TAGS
X
X###########################################################################
X# empty rules for directories that do not have SUBDIRS - do not edit
X
Xinstall::
X	@echo "install done"
X
Xinstall.man::
X	@echo "install.man done"
X
XMakefiles::
X
X###########################################################################
X# dependencies generated by makedepend
X
END_OF_FILE
if test 7121 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'object.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'object.h'\"
else
echo shar: Extracting \"'object.h'\" \(912 characters\)
sed "s/^X//" >'object.h' <<'END_OF_FILE'
X/* 
X *  This is a simple way to maintain a dynamically allocated array that
X *  grows in chunks. Essentially, obj is a a pointer to the object
X *  (starts off as NULL), and count is the number of elements in it.
X *  size is the currently allocated size, and incr is the size to
X *  increase it by if count == size. type is the type of the object - it
X *  callocs size*sizeof(type) elements for it, and casts this to (type
X *  *). Type is the reason this is a macro and not a procedure.
X *  basically you call this before you store something in obj[count].
X *  Greate for adding things at the end of lists
X */
X/* Mark Moraes */
X#define test_and_grow_object(obj, count, size, incr, type)\
X	if((count) == (size) || (obj) == NULL) { \
X		size += (incr);	\
X		if ((obj) == NULL) \
X			obj = (type *) XtCalloc(size, sizeof(type));\
X		else \
X			obj = (type *) XtRealloc((char *) obj, \
X			 (size)*sizeof(type));\
X	} else
X
END_OF_FILE
if test 912 -ne `wc -c <'object.h'`; then
    echo shar: \"'object.h'\" unpacked with wrong size!
fi
# end of 'object.h'
fi
if test -f 'xlife.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xlife.c'\"
else
echo shar: Extracting \"'xlife.c'\" \(20707 characters\)
sed "s/^X//" >'xlife.c' <<'END_OF_FILE'
X/*
X * John Conway's Game of Life. With some simple interaction that makes it a
X * neat game - Mark Moraes.
X */
X/*
X *  Possible enhancements: Load, Save, Go through patterns, etc Do it
X *  the right way with Translation management
X */
X#include <stdio.h>
X#include <X11/Xlib.h>
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/Command.h>
X#include <X11/cursorfont.h>
X#include <X11/Xutil.h>
X#include <fcntl.h>
X#include <signal.h>
X#include "random.h"
X#include "object.h"
X
X#ifndef PATFILE
X#define PATFILE "xlife.pat"
X#endif PATFILE
X
X#define RightButton		Button3
X#define MiddleButton		Button2
X#define LeftButton		Button1
X#define RightButtonMask		Button3Mask
X#define MiddleButtonMask	Button2Mask
X#define LeftButtonMask		Button1Mask
X
X#define CELLWIDTH 4
X#define CELLHEIGHT 4
X
X#define XCELLS	150
X#define YCELLS	150
X
X#define xcell(i)		((i)*CELLWIDTH)
X#define ycell(i)		((i)*CELLHEIGHT)
X
X/* Storage for the old board and new board */
Xstatic int **boardrows1, **boardrows2;
X/*
X *  Pointers to the curent board for the iteration, and the new board
X *  for the next iteration - these pointers point to boardrows[12]
X *  alternately.
X */
Xstatic int **nboard, **oboard;
X/* Number of columns and rows of cells */
Xstatic int nxcells, nycells;
Xstatic int nrandom;
X
X/* Boolean variables */
Xstatic int running = 0;	/* Are we running the simulation or not ? */
Xstatic int notover = 1;	/* zero if user wants to quit */
X
X/* 
X *  These structures comprise the display list - instead of drawing as
X *  we change the cell, we store it in the display list, and update in
X *  batches - see the comments on the interrupt strategy at
X *  xw_setup_intr(). 
X */
X
X
Xtypedef struct {
X	int set;
X	int x, y;
X} RectList;
X
X#define RECT_LIST_CHUNK 1024
XRectList *rect_list = NULL;
Xint rect_list_size = 0;
Xint rect_list_count = 0;;
X
Xstatic void do_life();
Xstatic void makeboard();
Xstatic void fill_rectangle();
Xstatic void setcell();
Xstatic void xw_setup_intr();
Xstatic int xw_handle_intr();
Xstatic int enable_sigio();
Xstatic int disable_sigio();
Xstatic int set_sigio_state();
Xstatic void reentrant_xflush();
Xstatic void redraw();
Xstatic void xw_update();
Xstatic void readpatterns();
Xstatic void randomize();
Xstatic void clear();
X
X
X/*
X *  This is my favourite way of handling X Toolkit arguments -
X *  startargs() and then, setarg(name, value) for all the args I want to
X *  set
X */
X#define MAXARGS 32
Xstatic int nargs;
Xstatic Arg wargs[MAXARGS];
X#define startargs()		nargs = 0
X#define setarg(name, value)	XtSetArg(wargs[nargs], name, value), nargs++
X
X/* X Windows related variables */
Xstatic Cursor WorkingCursor;
Xstatic Display *dpy;
Xstatic Window win;
Xstatic GC gc;
Xstatic GC cleargc;
X
X/* X Defaults */
Xstatic int defaultWidth = CELLWIDTH * XCELLS;
Xstatic int defaultHeight = CELLWIDTH * YCELLS;
X
Xstatic int zero = 0;
Xstatic int Width, Height;
Xstatic Pixel fg, bg;
Xstatic char *progname;
Xstatic char *patfile;
X
X/* Application Resources - no particular widget */
Xstatic XtResource application_resources[] = {
X	{"name", "Name", XtRString, sizeof(char *),
X		(Cardinal)&progname, XtRString, "xlife"},
X	{"width", "Width", XtRInt, sizeof(int),
X		(Cardinal)&Width, XtRInt, (caddr_t) &defaultWidth},
X	{"height", "Height", XtRInt, sizeof(int),
X		(Cardinal)&Height, XtRInt, (caddr_t) &defaultHeight},
X	{"random", "Random", XtRInt, sizeof(int),
X		(Cardinal)&nrandom, XtRInt, (caddr_t) &zero},
X	{"foreground", "Foreground", XtRPixel, sizeof(Pixel),
X		(Cardinal)&fg, XtRString, (caddr_t) "Black"},
X	{"background", "Background", XtRPixel, sizeof(Pixel),
X		(Cardinal)&bg, XtRString, (caddr_t) "White"},
X	{"patternfile", "PatternFile", XtRString, sizeof(char *),
X		(Cardinal)&patfile, XtRString, PATFILE},
X};
X
X/*
X *  Command line options table. The command line is parsed for these,
X *  and it sets/overrides the appropriate values in the resource
X *  database
X */
Xstatic XrmOptionDescRec optionDescList[] = {
X{"-width",	"*width",	XrmoptionSepArg, 	(caddr_t) NULL},
X{"-height",	"*height",	XrmoptionSepArg,	(caddr_t) NULL},
X{"-fg",		"*foreground",	XrmoptionSepArg,	(caddr_t) NULL},
X{"-bg",		"*background",	XrmoptionSepArg,	(caddr_t) NULL},
X{"-random",	"*random",	XrmoptionSepArg,	(caddr_t) NULL},
X{"-patfile",	"*patternfile",	XrmoptionSepArg,	(caddr_t) NULL},
X};
X
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	static void RepaintCanvas();
X	static void RecordMapStatus();
X	static void MouseInput();
X	Widget toplevel;
X	Widget w;
X	XGCValues gcv;
X
X	/*
X 	 * Create the top level Widget that represents encloses the
X	 * application.
X	 */
X	toplevel = XtInitialize(argv[0], "XLife",
X		optionDescList, XtNumber(optionDescList), &argc, argv);
X
X	XtGetApplicationResources(toplevel, 0, application_resources,
X		XtNumber(application_resources), NULL, 0 );
X
X
X	if (argc != 1) {
X		(void) fprintf(stderr, "Usage: %s [Xt options]\n", argv[0]);
X		exit(-1);
X	}
X
X	makeboard(Width/CELLWIDTH, Height/CELLHEIGHT);
X	readpatterns(patfile);
X	
X	/*
X	 * Create a simple Core class widget which we'll use for the actual
X	 * game.  A Core class widget is basically just a window, with a
X	 * simple Xt "wrapper" around it.
X	 */
X	startargs();
X	setarg(XtNwidth, xcell(nxcells));
X	setarg(XtNheight, xcell(nycells));
X	w = XtCreateManagedWidget(argv[0], widgetClass, toplevel, 
X		wargs, nargs);
X
X	/*
X	 * Set the procedures for various X Windows actions - exposure events
X	 * which arrive when a window needs to be redrawn. The map event lets
X	 * you know that the window is now on the screen so you can actually
X	 * do stuff. The ButtonPress event lets you know that a mouse button
X	 * was pressed.
X	 */
X	XtAddEventHandler(w, (Cardinal) ExposureMask, NULL, 
X	 RepaintCanvas, "redraw_data");
X	XtAddEventHandler(w, (Cardinal) StructureNotifyMask, NULL, 
X	 RecordMapStatus, "map_data");
X	/* One day, we'll use the translation manager here */
X	XtAddEventHandler(w, 
X	 (Cardinal) ButtonPressMask | ButtonMotionMask | KeyPressMask, NULL,
X	 MouseInput, "input_data"); 
X
X	/*
X	 * Create the windows, and set their attributes according to the Widget
X	 * data.
X	 */
X	XtRealizeWidget(toplevel);
X	
X	/* We need these for the raw Xlib calls */
X	win = XtWindow(w);
X	dpy = XtDisplay(w);
X
X	WorkingCursor = XCreateFontCursor(dpy, XC_top_left_arrow);
X	XDefineCursor(dpy, win, WorkingCursor);
X
X	/* 
X	 *  make the X Graphic Contexts here - one for copy (setting the
X	 *  square to foreground colour), one for erase (setting the
X	 *  square to background colour).
X	 */
X	gcv.foreground = fg;
X	gcv.background = bg;
X	gcv.function = GXcopy;
X	gc = XCreateGC(dpy, win, GCForeground | GCBackground 
X	 | GCFunction, &gcv);
X	gcv.foreground = bg;
X	cleargc = XCreateGC(dpy, win, GCForeground | GCBackground 
X	 | GCFunction, &gcv);
X
X	
X	(void) seedrnd(getpid());
X	randomize(nrandom);
X	XFlush(dpy);
X
X	/* 
X	 *  Now process the events. Our event processing is
X	 *  asynchronous, like GNUPLOT, from which the event code is
X	 *  adapted. This is unlike most X applications which are
X	 *  interactive, and therefore go into a XtMainLoop here. We go
X	 *  into a "display loop", where we start doing output, and get
X	 *  interrupted when there's an event to be processed.  as we
X	 *  can.
X	 */
X	
X	xw_setup_intr();
X	while(notover) {
X		if (running)
X			do_life();
X		else
X			pause();
X	}
X}
X
X/* Actual game of life iteration */
Xstatic void
Xdo_life()
X{
X	int **tmp;
X	register int *nrow, *orow, *orowprev, *orownext;
X	register int i, j;
X	register int neighbours;
X#ifdef DEBUG
X	int done = 0;
X	int filled = 0;
X#endif
X
X	rect_list_count = 0;
X	orow = oboard[0];
X	orownext = oboard[1];
X	for(i = 1; i <= nycells; i++) {
X		orowprev = orow;
X		orow = orownext;	/* orow = oboard[i] */
X		orownext = oboard[i+1];
X		nrow = nboard[i];
X		for(j = 1; j <= nxcells; j++) {
X			/* calculate cell from oboard to new board */
X			neighbours =
X			 orowprev[j-1] + orowprev[j] + orowprev[j+1] +
X			 orow[j-1] + orow[j+1] +
X			 orownext[j-1] + orownext[j] + orownext[j+1];
X			if (neighbours == 3 && orow[j] == 0) {
X				nrow[j] = 1;
X				fill_rectangle(j, i, 1);
X#ifdef DEBUG
X				done++;
X#endif
X			} else if (neighbours != 2 && neighbours != 3 &&
X			 orow[j] != 0) {
X			 	nrow[j] = 0;
X				fill_rectangle(j, i, 0);
X#ifdef DEBUG
X				done++;
X#endif
X			} else {
X				nrow[j] = orow[j];
X				/* Since it hasn't changed, we don't update */
X			}
X#ifdef DEBUG
X			if (nrow[j])
X				filled++;
X#endif
X		}
X	}
X	/* Stop running if we haven't had a change in the cell */
X	if (rect_list_count == 0)
X		running = 0;
X	xw_update();
X	tmp = nboard;
X	nboard = oboard;
X	oboard = tmp;
X/*	reentrant_redraw();*/
X#ifdef DEBUG
X	printf("Done an iteration - changed = %d, filled = %d, running = %d\n",
X	 done, filled, running);
X#endif
X}
X
X
X/*
X *  Add a rectangle to the display_list. If the last rectangle was the
X *  same colour as the one now (determined by 'set'), then add it to the
X *  current rect_list element by incrementing the count, and putting it
X *  in rect_buf. If it is different, then start a new rect_list
X *  element.
X */
Xstatic void
Xfill_rectangle(x, y, set)
X{
X	register RectList *cur_rect;
X
X	test_and_grow_object(rect_list, rect_list_count,
X	 rect_list_size, RECT_LIST_CHUNK, RectList);
X	
X	cur_rect = &rect_list[rect_list_count];
X	cur_rect->x = xcell(x-1);
X	cur_rect->y = ycell(y-1);
X	cur_rect->set = set;
X	rect_list_count++;
X}
X
Xstatic int isMapped = 0;
X
X/*ARGSUSED*/
Xstatic void 
XRepaintCanvas(w, data, ev)
XWidget w;
Xcaddr_t data;
XXEvent *ev;
X{
X	if (!isMapped)
X		return;
X	/*
X	 * Redraw the array
X	 */
X	if (ev && ev->xexpose.count == 0) {
X		XEvent event;
X		/* Skip all excess redraws */
X		while (XCheckTypedEvent(dpy, Expose, &event))
X			;
X		redraw();
X	}
X
X		
X#ifdef WINDOWDEBUG
X	printf("repaint\n");
X#endif
X	XFlush(dpy);
X}
X
X/*ARGSUSED*/
Xstatic void 
XRecordMapStatus(w, data, ev)
XWidget w;
Xcaddr_t data;
XXEvent *ev;
X{
X	if (ev->type == MapNotify) {
X#ifdef WINDOWDEBUG
X		printf("window mapped\n");
X#endif
X		isMapped = TRUE;
X	} else if (ev->type = ConfigureNotify) {
X#ifdef WINDOWDEBUG
X		printf("window resized\n");
X#endif
X	}
X}
X
X
X/*ARGSUSED*/
Xstatic void 
XMouseInput(w, data, ev)
XWidget w;
Xcaddr_t data;
XXEvent *ev;
X{
X	char ch;
X	
X#ifdef WINDOWDEBUG
X	printf("Input to canvas - %d (0x%x)\n", ev->xany.type, ev->xany.type);
X#endif
X	switch (ev->xany.type) {
X	case ButtonPress:
X		if (ev->xbutton.button == LeftButton) {
X			/* Turn on cell */
X			setcell((int) (ev->xbutton.x / CELLWIDTH),
X				(int) (ev->xbutton.y / CELLHEIGHT), 1);
X		} else if (ev->xbutton.button == MiddleButton) {
X			/* Toggle simulation on/off */
X			running = !running;
X		} else if (ev->xbutton.button == RightButton && !running) {
X			/* Turn off cell */
X			setcell((int) (ev->xbutton.x / CELLWIDTH),
X				(int) (ev->xbutton.y / CELLHEIGHT), 0);
X		}
X		break;
X	case MotionNotify:
X		if (ev->xmotion.state & LeftButtonMask) 
X			/* Turn on cell */
X			setcell((int) (ev->xmotion.x / CELLWIDTH),
X				(int) (ev->xmotion.y / CELLHEIGHT), 1);
X		else if ((ev->xmotion.state & RightButtonMask) && !running)
X			/* Turn off cell */
X			setcell((int) (ev->xmotion.x / CELLWIDTH),
X				(int) (ev->xmotion.y / CELLHEIGHT), 0);
X		break;
X	case KeyPress:
X		XLookupString(ev, &ch, 1, NULL, NULL);
X#ifdef WINDOWDEBUG
X		printf("Keypress - %c (0x%x)\n", ch, ch);
X#endif
X		switch(ch) {
X		case '0': case '1': case '2': case '3': case '4': 
X		case '5': case '6': case '7': case '8': case '9':
X			break;
X		case 'c':
X			clear();
X			break;
X		case 'r':
X			redraw();
X			break;
X		case 'q':
X			exit(0);
X			/*NOTREACHED*/
X		default:
X			break;
X		}
X		break;
X	default:
X		(void) fprintf(stderr, "Got an event of type %d (%x).\n",
X		       ev->xany.type, ev->xany.type);
X		break;
X	}
X}
X
Xstatic void
Xsetcell(x, y, set)
Xint x, y, set;
X{
X	int xreal = x * CELLWIDTH;
X	int yreal = y * CELLHEIGHT;
X	register int i = y + 1;
X	register int j = x + 1;
X
X	if (x < 0 || x >= nxcells || y < 0 || y >= nycells)
X		return;
X	if (set)
X		oboard[i][j] = nboard[i][j] = 1;
X	else
X		oboard[i][j] = nboard[i][j] = 0;
X
X	XFillRectangle(dpy, win, set ? gc : cleargc, xreal, yreal,
X	 CELLWIDTH, CELLHEIGHT);
X}
X
Xstatic void
Xmakeboard(width, height)
Xint width, height;
X{
X	register int i;
X	
X	nxcells = width;
X	nycells = height;
X	boardrows1 = (int **) XtCalloc(height+2, sizeof(int *));
X	boardrows2 = (int **) XtCalloc(height+2, sizeof(int *));
X	for(i = 0; i < height+2; i++) {
X		boardrows1[i] = (int *) XtCalloc(width+2, sizeof(int));
X		boardrows2[i] = (int *) XtCalloc(width+2, sizeof(int));
X	}
X	oboard = boardrows1;
X	nboard = boardrows2;
X}
X
X
Xstatic int display_socket_flags = 0;	/*
X					 *  nonzero=> server input will
X					 *  cause a SIGIO interrupt
X					 */
X
X/*
X *  The following code is adapted from GNUPLOT, and uses a pretty neat
X *  scheme for using X events properly in applications that aren't event
X *  driven, but do continuous graphic output, responding asynchronously
X *  to keyboard input. The X and Xt library calls are not known to be
X *  reentrant.  Therefore, because expose events must cause a SIGIO
X *  interrupt handler to redraw the screen and these interrupts can
X *  arrive in the middle of X/Xt library calls, the interrupts must be
X *  disabled while the calls are taking place.  In order to not take a
X *  bad performance hit from calling sigmask on every library request,
X *  the requests are batched.  xw_update then flushes the batched
X *  requests.  It passes the requests off to xw_display, which disables
X *  SIGIO and then processes each item in a specified part of the
X *  display list.  xw_display is also called by the interrupt handler to
X *  redraw the entire window.  No attempt is made to process any
X *  exposure events other than EXPOSE.
X */
X/*
X * The next routine sets up the interrupt handler for asynchronous
X *  refresh on expose events.  Method:
X *	1. Remember previous status flags for socket, as these flags
X *	   will be diddled to request and unrequest SIGIO interrupts later.
X *	2. Use signal() to catch SIGIO interrupts with the interrupt
X *		 handler.  We must do this before calling enable_sigio,
X *	   otherwise some signals could happen with nobody to catch
X *		 them, causing a core dump.
X *	3. Use fcntl() to request that the current process be the one
X *	   to which SIGIO interrupts are sent when I/O generates them.
X *	   Otherwise, they are sent to process 0 for some reason.
X *	4. Call enable_sigio(), which uses fcntl() again to request a SIGIO
X *	   when data appears on the X server socket.  Supposedly this
X *	   will work in 4.3 and in Ultrix 2.0, even though certain BSD
X *	   doc says that this call only works when the channel passed it
X *	   is a tty channel.  (Other docs say it works on ttys and
X *	   sockets).  So it is possible that this will lose on some old
X *	   4.2bsd systems.  I am not sure what to do then.
X */
Xstatic void
Xxw_setup_intr ()
X{
X	static int xw_handle_intr ();
X
X	int socket = XConnectionNumber(dpy); /* 
X				       *  channel number of the tcp
X				       *  socket to the server
X				       */
X	
X	display_socket_flags = fcntl (socket, F_GETFL, 0);
X
X	(void) signal (SIGIO, xw_handle_intr);
X	(void) fcntl (socket, F_SETOWN, getpid());
X	(void) enable_sigio ();
X}
X
X/*
X *  Request a SIGIO interrupt to be sent whenever input is available on
X *  the socket connected to the X server.  Return old state.
X */
Xstatic int
Xenable_sigio ()
X{
X	return set_sigio_state (display_socket_flags | FASYNC);
X}
X 
X/*
X *  Cancel request for SIGIO interrupt on input from the server.  The
X *  interrupt should be turned off whenever "normal" traffic (i.e.,
X *  plotting and anything else that does not have to asynchronously
X *  notify xlife that something is happening) is taking place.  Return
X *  old state.
X */
Xstatic int
Xdisable_sigio ()
X{
X	return set_sigio_state (display_socket_flags & ~FASYNC);
X}
X
X/*
X *  Set state of sigio to VALUE, if it was something different.  Return
X *  old value so it can be restored.
X */
Xstatic int
Xset_sigio_state (value)
Xint value;
X{
X	int socket = XConnectionNumber(dpy);/*
X					    *  channel number of the tcp
X					    *  socket to the server
X					    */
X	int oldval = display_socket_flags;
X
X	/* Only call fcntl if we're asking for a real state change (old
X	 state different from new state). */
X	if (display_socket_flags != value) {
X		(void) fcntl (socket, F_SETFL, value);
X		display_socket_flags = value;
X	}
X	return oldval;
X}
X
X/*
X *  actually handle the interrupt by simply repeating everything on the
X *  display list up to the last xw_flush().
X */
Xstatic int
Xxw_handle_intr()
X{
X	XEvent event;
X	int old_intr;
X
X	/*
X	 *  first, we need to get rid of the X events on the queue.
X	 *  However, since reading the events in order to throw them
X	 *  away involves Xlib calls, we need to disable SIGIO while it
X	 *  is happening.
X	 */
X
X	old_intr = disable_sigio ();
X
X	/* Check the X queue for actual exposure events.  This is necessary
X	 for two reasons:
X	 1. SIGIO interrupts seem to get sent here even though the SIGIO
X		request is carefully turned off during any activity that can
X		cause traffic on the socket (a kernel bug?).
X	 2. The Xlib documentation lies when it says XSelectInput
X		masks events other than the ones selected.  I.e., sometimes
X		events can arrive that were not asked for.  Therefore,
X		we must make sure the purported exposure event really is one.
X	*/
X
X	while (XtPending() > 0) {
X		XtNextEvent (&event);
X		XtDispatchEvent(&event);
X	}
X	(void) set_sigio_state (old_intr);
X}
X
X/*
X *  Cause an XFlush() call, with interrupts disabled.
X */
Xstatic void
Xreentrant_xflush ()
X{
X	int old_sigio = disable_sigio ();
X	XFlush (dpy);
X	(void) set_sigio_state (old_sigio);
X}
X
X/* If signals aren't off when redraw is called, bad things may happen */
Xstatic void
Xredraw()
X{
X	int i, j;
X	for(i = 1; i <= nycells; i++) {
X		for(j = 1; j <= nxcells; j++) {
X			if(oboard[i][j])
X				XFillRectangle(dpy, win, gc,
X				 xcell(j-1), ycell(i-1),
X				 CELLWIDTH, CELLHEIGHT);
X		}
X	}
X}
X
Xstatic void
Xreentrant_redraw ()
X{
X	int old_sigio = disable_sigio ();
X	XClearWindow(dpy, win);
X	redraw();
X	XFlush(dpy);
X	(void) set_sigio_state (old_sigio);
X}
X
Xstatic void
Xxw_update()
X{
X	int old_sigio = disable_sigio ();
X	register int i;
X	register RectList *cur_rect = rect_list;
X
X#ifdef DEBUG
X	printf("-- update: %d rects\n", rect_list_count);
X#endif
X	for(i = 0; i < rect_list_count; i++, cur_rect++) {
X		XFillRectangle(dpy, win, rect_list[i].set ? gc : cleargc,
X		 cur_rect->x, cur_rect->y, CELLWIDTH, CELLHEIGHT);
X	}
X	rect_list_count = 0;
X	XFlush(dpy);
X	
X	(void) set_sigio_state (old_sigio);
X}
X
Xtypedef struct {
X	int i, j;
X} Coord;
X
X#define PATTERN_BUF_CHUNK 256
Xstatic Coord *pattern_buf = NULL;
Xstatic int pattern_buf_size = 0;
Xstatic int pattern_buf_count = 0;
X
Xtypedef struct {
X	Coord *coord;
X	int ncoords;
X} Pattern;
X
X#define PATTERN_CHUNK 32
Xstatic Pattern *patterns = NULL;
Xstatic int patterns_size = 0;
Xstatic int patterns_count = 0;
X
X/* Opens file and appends patterns to list of predefined patterns. */
Xstatic void
Xreadpatterns(file)
Xchar *file;
X{
X	FILE *fp;
X	char buf[128];
X	int i, j;
X	int newpattern = 0;
X
X	if ((fp = fopen(file, "r")) == NULL) {
X		(void) fprintf(stderr, "%s: Couldn't open file %s\n", 
X		 progname, file);
X		return;
X	}
X	test_and_grow_object(patterns, patterns_count,
X	 patterns_size, PATTERN_CHUNK, Pattern);
X	test_and_grow_object(pattern_buf, pattern_buf_count,
X	 pattern_buf_size, PATTERN_BUF_CHUNK, Coord);
X	patterns[patterns_count].coord = &pattern_buf[pattern_buf_count];
X	patterns[patterns_count].ncoords = 0;
X	while(fgets(buf, sizeof(buf), fp) != NULL) {
X		if (sscanf(buf, " %d %d", &i, &j) == 2) {
X			newpattern++;
X			test_and_grow_object(pattern_buf, pattern_buf_count,
X			 pattern_buf_size, PATTERN_BUF_CHUNK, Coord);
X			pattern_buf[pattern_buf_count].i = i;
X			pattern_buf[pattern_buf_count].j = j;
X			pattern_buf_count++;
X		} else if (newpattern != 0) {
X			/* Lousy parsing - we assume end of pattern */
X			patterns[patterns_count].ncoords = newpattern;
X			patterns_count++;
X			test_and_grow_object(patterns, patterns_count,
X			 patterns_size, PATTERN_CHUNK, Pattern);
X			patterns[patterns_count].coord =
X			 &pattern_buf[pattern_buf_count];
X			patterns[patterns_count].ncoords = 0;
X			newpattern = 0;
X		}
X	}
X}
X
X
X/* Puts pattern m with origin at col i, row j */
Xstatic void
Xputpattern(m, x, y)
X{
X	register int k;
X	Coord *p;
X	register int yoffset = y;
X	register int xoffset = x;
X
X	p = patterns[m].coord;
X	k = patterns[m].ncoords;
X	while(--k >= 0)
X		setcell(xoffset + p[k].j, yoffset + p[k].i, 1);
X}
X
X
Xstatic void
Xrandomize(nrandom)
X{
X	int x, y;
X	int i;
X	int nx, ny;
X
X	if (patterns_count == 0)
X		return;
X	nx = nxcells - 2;
X	ny = nycells - 2;
X	if (nrandom == 0)
X		nrandom = (nxcells + nycells) / 5;
X	while(nrandom-- > 0) {
X		x = rnd(nx) + 1;
X		y = rnd(ny) + 1;
X		i = rnd(patterns_count);
X		putpattern(i, x, y);
X	}
X}
X
Xstatic void
Xclear()
X{
X	register int i, j;
X	int old_sigio = disable_sigio ();
X	for(i = 0; i < nycells; i++)
X		for(j = 0; j < nxcells; j++)
X			oboard[i][j] = nboard[i][j] = 0;
X	XClearWindow(dpy, win);
X	XSync(dpy, True);
X	(void) set_sigio_state (old_sigio);
X}
END_OF_FILE
if test 20707 -ne `wc -c <'xlife.c'`; then
    echo shar: \"'xlife.c'\" unpacked with wrong size!
fi
# end of 'xlife.c'
fi
if test -f 'xlife.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xlife.man'\"
else
echo shar: Extracting \"'xlife.man'\" \(2583 characters\)
sed "s/^X//" >'xlife.man' <<'END_OF_FILE'
X.TH XLIFE 6 "13 July 1988" "X Version 11"
X.SH NAME
Xxlife - game of Life in a window with interaction
X.SH SYNTAX
X\fBxlife\fP [\fI-toolkitoption\fP ...] [\fI-width\fP width] 
X[\fI-height\fP height] [\fI-random number-of-random-patterns]
X[\fI-patfile\fP patternfile]
X.SH OPTIONS
X.I Xlife
Xaccepts all of the standard X Toolkit command line options, plus:
X.TP 8
X.IR -width " width"
XSets the width of the window
X.TP 8
X.IR -height " height"
Xsets the height of the window
X.TP 8
X.IR -random " number"
Xstarts the game off with a suitable number of random patterns.
XZero means a program default.
X.TP 8
X.IR -patfile " patternfile"
Xreads in the list of patterns to use when generating the random
Xstartup screen. The default is 
X.I LIBDIR/xlife.pat.
X.SH DESCRIPTION
X.I Xlife
Xsimulates the game of Life, described as follows:
X.IP \-
Xif a "live" cell has one neighbour among the eight adjacent cells, it "dies".
X(Isolation)
X.IP \-
Xif a "live" cell has two neighbours, it continues to "live". (Survival)
X.IP \-
Xif an empty cell has three neighbours, it comes "alive". (Reproduction)
X.IP \-
Xif a "live" cell has four or more neighbours, it "dies" (Suffocation)
X.PP
XThe following commands are available:
X.IP "Set"
XBound to the left mouse button, it makes a cell come alive.
X.IP "Start/Stop"
XBound to the middle mouse button, it starts and stops the simulation.
X.IP "Kill"
XBound to the right mouse button, it clears a cell. This is only
Xavailable when the simulation is stopped.
X.IP "Quit"
XBound to the 'q' key.
X.IP "Redraw" 
XBound to the 'r' key.
X.IP "Clear"
XBound to the 'c' key, it clears the screen and board.
X.PP
XWhen played as a game, the objective is to clear the board of all life
Xusing the Set command only (Stopping, using the Kill key, and restarting is
Xnot fair!). 
X.PP
XLife randomly initializes the board using a set of standard patterns that
Xcause reasonable carnage - they are well known growing, quasi-static
Xor moving patterns. The patterns are found in the patterns file
X.I xlife.pat.
X.SH X DEFAULTS
XFor
X.I xlife
Xthe available class identifiers are:
X.sp
X.nf
XXLife
X.fi
X.PP
XFor
X.I xlife,
Xthe available name identifiers are:
X.sp
X.nf
Xxlife
X.fi
X.sp
X.LP
XFor
X.I xlife,
Xthe available resources are:
X.IP background
XSpecifies the background color to be displayed.
XThe default is white.
X.IP foreground
XSpecifies the foreground color of the
X.I xlife
Xwindow.
XThe default is black.
X.IP width
Xwidth of the window.
X.IP height
Xheight of the window.
X.IP random
Xnumber of random cells to initialize with
X.SH SEE ALSO
XX(1), xrdb(1)
X.SH FILES
XLIBDIR/xlife.pat
X.SH AUTHOR
XMark Moraes, University of Toronto
END_OF_FILE
if test 2583 -ne `wc -c <'xlife.man'`; then
    echo shar: \"'xlife.man'\" unpacked with wrong size!
fi
# end of 'xlife.man'
fi
if test -f 'xlife.pat' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xlife.pat'\"
else
echo shar: Extracting \"'xlife.pat'\" \(872 characters\)
sed "s/^X//" >'xlife.pat' <<'END_OF_FILE'
X# Diagonal egg (LR)
X0 2
X1 1
X1 3
X2 0
X2 3
X3 1
X3 2
X# Diagonal egg (RL)
X0 1
X1 0
X1 2
X2 0
X2 3
X3 1
X3 2
X# Explodes into a quasi static pattern
X0 0
X0 2
X0 4
X1 0
X1 4
X2 0
X2 4
X3 0
X3 4
X4 0
X4 2
X4 4
X# Long life pattern that ends up stable but covers a lot of area (U)
X0 0
X0 2
X1 0
X1 2
X2 0
X2 1
X2 2
X# Long life pattern that ends up stable but covers a lot of area (D)
X0 0
X0 1
X0 2
X1 0
X1 2
X2 0
X2 2
X# Long life pattern that ends up stable but covers a lot of area (R)
X0 0
X0 1
X0 2
X1 0
X2 0
X2 1
X2 2
X# Long life pattern that ends up stable but covers a lot of area (L)
X0 0
X0 1
X0 2
X1 2
X2 0
X2 1
X2 2
X# Glider R-D
X0 0
X0 1
X0 2
X1 2
X2 1
X# Glider L-D
X0 0
X0 1
X0 2
X1 0
X2 1
X# Glider R-U
X0 0
X0 1
X1 0
X1 2
X2 0
X# Glider L-U
X0 1
X0 2
X1 0
X1 2
X2 2
X# exploding pattern
X0 1
X1 0
X1 1
X1 2
X2 0
X2 2
X3 1
X#
X0 0
X0 1
X1 0
X1 2
X2 1
X2 2
X3 1
X# 
X0 1
X1 0
X1 1
X2 0
X2 2
X3 1
X3 2
X#
X0 1
X0 2
X1 0
X1 1
X1 3
X2 2
X2 3
X#
X0 0
X0 1
X1 0
X1 2
X1 3
X2 1
X2 2
END_OF_FILE
if test 872 -ne `wc -c <'xlife.pat'`; then
    echo shar: \"'xlife.pat'\" unpacked with wrong size!
fi
# end of 'xlife.pat'
fi
if test -f 'xlife2.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xlife2.c'\"
else
echo shar: Extracting \"'xlife2.c'\" \(14221 characters\)
sed "s/^X//" >'xlife2.c' <<'END_OF_FILE'
X/*
X * John Conway's Game of Life. With some simple interaction that makes it a
X * neat game - Mark Moraes.
X */
X/*
X *  Possible enhancements: Load, Save, Go through patterns, etc Do it
X *  the right way with Translation management
X */
X#include <stdio.h>
X#include <X11/Xlib.h>
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/Command.h>
X#include <X11/cursorfont.h>
X#include <X11/Xutil.h>
X#include "random.h"
X#include "object.h"
X
X#ifndef PATFILE
X#define PATFILE "xlife.pat"
X#endif PATFILE
X
X#define RightButton		Button3
X#define MiddleButton		Button2
X#define LeftButton		Button1
X#define RightButtonMask		Button3Mask
X#define MiddleButtonMask	Button2Mask
X#define LeftButtonMask		Button1Mask
X
X#define CELLWIDTH 4
X#define CELLHEIGHT 4
X
X#define XCELLS	150
X#define YCELLS	150
X
X#define xcell(i)		((i)*CELLWIDTH)
X#define ycell(i)		((i)*CELLHEIGHT)
X
X/* Storage for the old board and new board */
Xstatic int **boardrows1, **boardrows2;
X/*
X *  Pointers to the current board for the iteration, and the new board
X *  for the next iteration - these pointers point to boardrows[12]
X *  alternately.
X */
Xstatic int **nboard, **oboard;
X/* Number of columns and rows of cells */
Xstatic int nxcells, nycells;
Xstatic int nrandom;
X
X/* Boolean variables */
Xstatic int running = 0;	/* Are we running the simulation or not ? */
Xstatic int notover = 1;	/* zero if user wants to quit */
Xstatic int changed = 0;
X
Xstatic void do_life();
Xstatic void makeboard();
Xstatic void fill_rectangle();
Xstatic void setcell();
Xstatic void redraw();
Xstatic void readpatterns();
Xstatic void randomize();
Xstatic void clear();
X
X/*
X *  This is my favourite way of handling X Toolkit arguments -
X *  startargs() and then, setarg(name, value) for all the args I want to
X *  set
X */
X#define MAXARGS 32
Xstatic int nargs;
Xstatic Arg wargs[MAXARGS];
X#define startargs()		nargs = 0
X#define setarg(name, value)	XtSetArg(wargs[nargs], name, value), nargs++
X
X/* X Windows related variables */
Xstatic Cursor WorkingCursor;
Xstatic Display *dpy;
Xstatic Window win;
Xstatic GC gc;
Xstatic GC cleargc;
X
X/* X Defaults */
Xstatic int defaultWidth = CELLWIDTH * XCELLS;
Xstatic int defaultHeight = CELLWIDTH * YCELLS;
X
Xstatic int zero = 0;
Xstatic int Width, Height;
Xstatic Pixel fg, bg;
Xstatic char *progname;
Xstatic char *patfile;
X
X/* Application Resources - no particular widget */
Xstatic XtResource application_resources[] = {
X	{"name", "Name", XtRString, sizeof(char *),
X		(Cardinal)&progname, XtRString, "xlife"},
X	{"width", "Width", XtRInt, sizeof(int),
X		(Cardinal)&Width, XtRInt, (caddr_t) &defaultWidth},
X	{"height", "Height", XtRInt, sizeof(int),
X		(Cardinal)&Height, XtRInt, (caddr_t) &defaultHeight},
X	{"random", "Random", XtRInt, sizeof(int),
X		(Cardinal)&nrandom, XtRInt, (caddr_t) &zero},
X	{"foreground", "Foreground", XtRPixel, sizeof(Pixel),
X		(Cardinal)&fg, XtRString, (caddr_t) "Black"},
X	{"background", "Background", XtRPixel, sizeof(Pixel),
X		(Cardinal)&bg, XtRString, (caddr_t) "White"},
X	{"patternfile", "PatternFile", XtRString, sizeof(char *),
X		(Cardinal)&patfile, XtRString, PATFILE},
X};
X
X/*
X *  Command line options table. The command line is parsed for these,
X *  and it sets/overrides the appropriate values in the resource
X *  database
X */
Xstatic XrmOptionDescRec optionDescList[] = {
X{"-width",	"*width",	XrmoptionSepArg, 	(caddr_t) NULL},
X{"-height",	"*height",	XrmoptionSepArg,	(caddr_t) NULL},
X{"-fg",		"*foreground",	XrmoptionSepArg,	(caddr_t) NULL},
X{"-bg",		"*background",	XrmoptionSepArg,	(caddr_t) NULL},
X{"-random",	"*random",	XrmoptionSepArg,	(caddr_t) NULL},
X{"-patfile",	"*patternfile",	XrmoptionSepArg,	(caddr_t) NULL},
X};
X
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	static void RepaintCanvas();
X	static void RecordMapStatus();
X	static void MouseInput();
X	Widget toplevel;
X	Widget w;
X	XGCValues gcv;
X	XEvent event;
X
X	/*
X 	 * Create the top level Widget that represents encloses the
X	 * application.
X	 */
X	toplevel = XtInitialize(argv[0], "XLife",
X		optionDescList, XtNumber(optionDescList), &argc, argv);
X
X	XtGetApplicationResources(toplevel, 0, application_resources,
X		XtNumber(application_resources), NULL, 0 );
X
X
X	if (argc != 1) {
X		(void) fprintf(stderr, "Usage: %s [Xt options]\n", argv[0]);
X		exit(-1);
X	}
X
X	makeboard(Width/CELLWIDTH, Height/CELLHEIGHT);
X	readpatterns(patfile);
X	
X	/*
X	 * Create a simple Core class widget which we'll use for the actual
X	 * game.  A Core class widget is basically just a window, with a
X	 * simple Xt "wrapper" around it.
X	 */
X	startargs();
X	setarg(XtNwidth, xcell(nxcells));
X	setarg(XtNheight, xcell(nycells));
X	w = XtCreateManagedWidget(argv[0], widgetClass, toplevel, 
X		wargs, nargs);
X
X	/*
X	 * Set the procedures for various X Windows actions - exposure events
X	 * which arrive when a window needs to be redrawn. The map event lets
X	 * you know that the window is now on the screen so you can actually
X	 * do stuff. The ButtonPress event lets you know that a mouse button
X	 * was pressed.
X	 */
X	XtAddEventHandler(w, (Cardinal) ExposureMask, NULL, 
X	 RepaintCanvas, "redraw_data");
X	XtAddEventHandler(w, (Cardinal) StructureNotifyMask, NULL, 
X	 RecordMapStatus, "map_data");
X	/* One day, we'll use the translation manager here */
X	XtAddEventHandler(w, 
X	 (Cardinal) ButtonPressMask | ButtonMotionMask | KeyPressMask, NULL,
X	 MouseInput, "input_data"); 
X
X	/*
X	 * Create the windows, and set their attributes according to the Widget
X	 * data.
X	 */
X	XtRealizeWidget(toplevel);
X	
X	/* We need these for the raw Xlib calls */
X	win = XtWindow(w);
X	dpy = XtDisplay(w);
X
X	WorkingCursor = XCreateFontCursor(dpy, XC_top_left_arrow);
X	XDefineCursor(dpy, win, WorkingCursor);
X
X	/* 
X	 *  make the X Graphic Contexts here - one for copy (setting the
X	 *  square to foreground colour), one for erase (setting the
X	 *  square to background colour).
X	 */
X	gcv.foreground = fg;
X	gcv.background = bg;
X	gcv.function = GXcopy;
X	gc = XCreateGC(dpy, win, GCForeground | GCBackground 
X	 | GCFunction, &gcv);
X	gcv.foreground = bg;
X	cleargc = XCreateGC(dpy, win, GCForeground | GCBackground 
X	 | GCFunction, &gcv);
X
X	
X	(void) seedrnd(getpid());
X	randomize(nrandom);
X	XFlush(dpy);
X
X	/*
X	 *  Now process the events. This is a straightforward eventloop
X	 *  where we do one iteration and update the display, and then check
X	 *  for events
X	 */
X	
X	while(notover) {
X		/* Process any events if they're waiting */
X		while (XtPending() > 0) {
X			XtNextEvent (&event);
X			XtDispatchEvent(&event);
X		}
X		if (running)
X			do_life();
X		else {
X			/* Block for the next event since we aren't doing anything else */
X			XtNextEvent (&event);
X			XtDispatchEvent(&event);
X		}
X	}
X}
X
X/* Actual game of life iteration */
Xstatic void
Xdo_life()
X{
X	int **tmp;
X	register int *nrow, *orow, *orowprev, *orownext;
X	register int i, j;
X	register int neighbours;
X#ifdef DEBUG
X	int done = 0;
X	int filled = 0;
X#endif
X
X	changed = 0;
X	orow = oboard[0];
X	orownext = oboard[1];
X	for(i = 1; i <= nycells; i++) {
X		orowprev = orow;
X		orow = orownext;	/* orow = oboard[i] */
X		orownext = oboard[i+1];
X		nrow = nboard[i];
X		for(j = 1; j <= nxcells; j++) {
X			/* calculate cell from oboard to new board */
X			neighbours =
X			 orowprev[j-1] + orowprev[j] + orowprev[j+1] +
X			 orow[j-1] + orow[j+1] +
X			 orownext[j-1] + orownext[j] + orownext[j+1];
X			if (neighbours == 3 && orow[j] == 0) {
X				nrow[j] = 1;
X				fill_rectangle(j, i, 1);
X#ifdef DEBUG
X				done++;
X#endif
X			} else if (neighbours != 2 && neighbours != 3 &&
X			 orow[j] != 0) {
X			 	nrow[j] = 0;
X				fill_rectangle(j, i, 0);
X#ifdef DEBUG
X				done++;
X#endif
X			} else {
X				nrow[j] = orow[j];
X				/* Since it hasn't changed, we don't update */
X			}
X#ifdef DEBUG
X			if (nrow[j])
X				filled++;
X#endif
X		}
X	}
X	/* Stop running if we haven't had a change in the cell */
X	if (changed == 0)
X		running = 0;
X	tmp = nboard;
X	nboard = oboard;
X	oboard = tmp;
X/*	reentrant_redraw();*/
X#ifdef DEBUG
X	printf("Done an iteration - changed = %d, filled = %d, running = %d\n",
X	 done, filled, running);
X#endif
X}
X
X
X/*
X *  Add a rectangle to the display_list. If the last rectangle was the
X *  same colour as the one now (determined by 'set'), then add it to the
X *  current rect_list element by incrementing the count, and putting it
X *  in rect_buf. If it is different, then start a new rect_list
X *  element.
X */
Xstatic void
Xfill_rectangle(x, y, set)
X{
X	changed++;
X	XFillRectangle(dpy, win, set ? gc : cleargc, xcell(x), ycell(y),
X	 CELLWIDTH, CELLHEIGHT);
X}
X
Xstatic int isMapped = 0;
X
X/*ARGSUSED*/
Xstatic void 
XRepaintCanvas(w, data, ev)
XWidget w;
Xcaddr_t data;
XXEvent *ev;
X{
X	if (!isMapped)
X		return;
X	/*
X	 * Redraw the array
X	 */
X	if (ev && ev->xexpose.count == 0) {
X		XEvent event;
X		/* Skip all excess redraws */
X		while (XCheckTypedEvent(dpy, Expose, &event))
X			;
X		redraw();
X	}
X
X		
X#ifdef WINDOWDEBUG
X	printf("repaint\n");
X#endif
X	XFlush(dpy);
X}
X
X/*ARGSUSED*/
Xstatic void 
XRecordMapStatus(w, data, ev)
XWidget w;
Xcaddr_t data;
XXEvent *ev;
X{
X	if (ev->type == MapNotify) {
X#ifdef WINDOWDEBUG
X		printf("window mapped\n");
X#endif
X		isMapped = TRUE;
X	} else if (ev->type = ConfigureNotify) {
X#ifdef WINDOWDEBUG
X		printf("window resized\n");
X#endif
X	}
X}
X
X
X/*ARGSUSED*/
Xstatic void 
XMouseInput(w, data, ev)
XWidget w;
Xcaddr_t data;
XXEvent *ev;
X{
X	char ch;
X	
X#ifdef WINDOWDEBUG
X	printf("Input to canvas - %d (0x%x)\n", ev->xany.type, ev->xany.type);
X#endif
X	switch (ev->xany.type) {
X	case ButtonPress:
X		if (ev->xbutton.button == LeftButton) {
X			/* Turn on cell */
X			setcell((int) (ev->xbutton.x / CELLWIDTH),
X				(int) (ev->xbutton.y / CELLHEIGHT), 1);
X		} else if (ev->xbutton.button == MiddleButton) {
X			/* Toggle simulation on/off */
X			running = !running;
X		} else if (ev->xbutton.button == RightButton && !running) {
X			/* Turn off cell */
X			setcell((int) (ev->xbutton.x / CELLWIDTH),
X				(int) (ev->xbutton.y / CELLHEIGHT), 0);
X		}
X		break;
X	case MotionNotify:
X		if (ev->xmotion.state & LeftButtonMask) 
X			/* Turn on cell */
X			setcell((int) (ev->xmotion.x / CELLWIDTH),
X				(int) (ev->xmotion.y / CELLHEIGHT), 1);
X		else if ((ev->xmotion.state & RightButtonMask) && !running)
X			/* Turn off cell */
X			setcell((int) (ev->xmotion.x / CELLWIDTH),
X				(int) (ev->xmotion.y / CELLHEIGHT), 0);
X		break;
X	case KeyPress:
X		XLookupString(ev, &ch, 1, NULL, NULL);
X#ifdef WINDOWDEBUG
X		printf("Keypress - %c (0x%x)\n", ch, ch);
X#endif
X		switch(ch) {
X		case '0': case '1': case '2': case '3': case '4': 
X		case '5': case '6': case '7': case '8': case '9':
X			break;
X		case 'c':
X			clear();
X			break;
X		case 'r':
X			redraw();
X			break;
X		case 'q':
X			exit(0);
X			/*NOTREACHED*/
X		default:
X			break;
X		}
X		break;
X	default:
X		(void) printf("Got an event of type %d (%x).\n",
X		       ev->xany.type, ev->xany.type);
X		break;
X	}
X}
X
Xstatic void
Xsetcell(x, y, set)
Xint x, y, set;
X{
X	int xreal = x * CELLWIDTH;
X	int yreal = y * CELLHEIGHT;
X	register int i = y + 1;
X	register int j = x + 1;
X
X	if (x < 0 || x >= nxcells || y < 0 || y >= nycells)
X		return;
X	if (set)
X		oboard[i][j] = 1;
X	else
X		oboard[i][j] = 0;
X
X	XFillRectangle(dpy, win, set ? gc : cleargc, xreal, yreal,
X	 CELLWIDTH, CELLHEIGHT);
X}
X
Xstatic void
Xmakeboard(width, height)
Xint width, height;
X{
X	register int i;
X	
X	nxcells = width;
X	nycells = height;
X	boardrows1 = (int **) XtCalloc(height+2, sizeof(int *));
X	boardrows2 = (int **) XtCalloc(height+2, sizeof(int *));
X	for(i = 0; i < height+2; i++) {
X		boardrows1[i] = (int *) XtCalloc(width+2, sizeof(int));
X		boardrows2[i] = (int *) XtCalloc(width+2, sizeof(int));
X	}
X	oboard = boardrows1;
X	nboard = boardrows2;
X}
X
X
X/* If signals aren't off when redraw is called, bad things may happen */
Xstatic void
Xredraw()
X{
X	int i, j;
X	for(i = 1; i <= nycells; i++) {
X		for(j = 1; j <= nxcells; j++) {
X			if(oboard[i][j])
X				XFillRectangle(dpy, win, gc,
X				 xcell(j-1), ycell(i-1),
X				 CELLWIDTH, CELLHEIGHT);
X		}
X	}
X}
X
X/* Typical usage of object.h */
Xtypedef struct {
X	int i, j;
X} Coord;
X
X#define PATTERN_BUF_CHUNK 256
Xstatic Coord *pattern_buf = NULL;
Xstatic int pattern_buf_size = 0;
Xstatic int pattern_buf_count = 0;
X
X/* yet another array of objects */
Xtypedef struct {
X	Coord *coord;
X	int ncoords;
X} Pattern;
X
X#define PATTERN_CHUNK 32
Xstatic Pattern *patterns = NULL;
Xstatic int patterns_size = 0;
Xstatic int patterns_count = 0;
X
X/* Opens file and appends patterns to list of predefined patterns. */
Xstatic void
Xreadpatterns(file)
Xchar *file;
X{
X	FILE *fp;
X	char buf[128];
X	int i, j;
X	int newpattern = 0;
X
X	if ((fp = fopen(file, "r")) == NULL) {
X		(void) fprintf(stderr, "%s: Couldn't open file %s\n", 
X		 progname, file);
X		return;
X	}
X	test_and_grow_object(patterns, patterns_count,
X	 patterns_size, PATTERN_CHUNK, Pattern);
X	test_and_grow_object(pattern_buf, pattern_buf_count,
X	 pattern_buf_size, PATTERN_BUF_CHUNK, Coord);
X	patterns[patterns_count].coord = &pattern_buf[pattern_buf_count];
X	patterns[patterns_count].ncoords = 0;
X	while(fgets(buf, sizeof(buf), fp) != NULL) {
X		if (sscanf(buf, " %d %d", &i, &j) == 2) {
X			newpattern++;
X			test_and_grow_object(pattern_buf, pattern_buf_count,
X			 pattern_buf_size, PATTERN_BUF_CHUNK, Coord);
X			pattern_buf[pattern_buf_count].i = i;
X			pattern_buf[pattern_buf_count].j = j;
X			pattern_buf_count++;
X		} else if (newpattern != 0) {
X			/* Lousy parsing - we assume end of pattern */
X			patterns[patterns_count].ncoords = newpattern;
X			patterns_count++;
X			test_and_grow_object(patterns, patterns_count,
X			 patterns_size, PATTERN_CHUNK, Pattern);
X			patterns[patterns_count].coord =
X			 &pattern_buf[pattern_buf_count];
X			patterns[patterns_count].ncoords = 0;
X			newpattern = 0;
X		}
X	}
X}
X
X
X/* Puts pattern m with origin at col i, row j */
Xstatic void
Xputpattern(m, x, y)
X{
X	register int k;
X	Coord *p;
X	register int yoffset = y;
X	register int xoffset = x;
X
X	p = patterns[m].coord;
X	k = patterns[m].ncoords;
X	while(--k >= 0)
X		setcell(xoffset + p[k].j, yoffset + p[k].i, 1);
X}
X
X
Xstatic void
Xrandomize(nrandom)
X{
X	int x, y;
X	int i;
X	int nx, ny;
X
X	if (patterns_count == 0)
X		return;
X	nx = nxcells - 2;
X	ny = nycells - 2;
X	if (nrandom == 0)
X		nrandom = (nxcells + nycells) / 5;
X	while(nrandom-- > 0) {
X		x = rnd(nx) + 1;
X		y = rnd(ny) + 1;
X		i = rnd(patterns_count);
X		putpattern(i, x, y);
X	}
X}
X
Xstatic void
Xclear()
X{
X	register int i, j;
X	for(i = 0; i < nycells; i++)
X		for(j = 0; j < nxcells; j++)
X			oboard[i][j] = nboard[i][j] = 0;
X	XClearWindow(dpy, win);
X}
END_OF_FILE
if test 14221 -ne `wc -c <'xlife2.c'`; then
    echo shar: \"'xlife2.c'\" unpacked with wrong size!
fi
# end of 'xlife2.c'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mike Wexler(wyse!mikew)    Phone: (408)433-1000 x1330
Moderator of comp.sources.x