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