|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T x
Length: 14221 (0x378d)
Types: TextFile
Names: »xlife2.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/X/Xlife/xlife2.c«
/*
* John Conway's Game of Life. With some simple interaction that makes it a
* neat game - Mark Moraes.
*/
/*
* Possible enhancements: Load, Save, Go through patterns, etc Do it
* the right way with Translation management
*/
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Command.h>
#include <X11/cursorfont.h>
#include <X11/Xutil.h>
#include "random.h"
#include "object.h"
#ifndef PATFILE
#define PATFILE "xlife.pat"
#endif PATFILE
#define RightButton Button3
#define MiddleButton Button2
#define LeftButton Button1
#define RightButtonMask Button3Mask
#define MiddleButtonMask Button2Mask
#define LeftButtonMask Button1Mask
#define CELLWIDTH 4
#define CELLHEIGHT 4
#define XCELLS 150
#define YCELLS 150
#define xcell(i) ((i)*CELLWIDTH)
#define ycell(i) ((i)*CELLHEIGHT)
/* Storage for the old board and new board */
static int **boardrows1, **boardrows2;
/*
* Pointers to the current board for the iteration, and the new board
* for the next iteration - these pointers point to boardrows[12]
* alternately.
*/
static int **nboard, **oboard;
/* Number of columns and rows of cells */
static int nxcells, nycells;
static int nrandom;
/* Boolean variables */
static int running = 0; /* Are we running the simulation or not ? */
static int notover = 1; /* zero if user wants to quit */
static int changed = 0;
static void do_life();
static void makeboard();
static void fill_rectangle();
static void setcell();
static void redraw();
static void readpatterns();
static void randomize();
static void clear();
/*
* This is my favourite way of handling X Toolkit arguments -
* startargs() and then, setarg(name, value) for all the args I want to
* set
*/
#define MAXARGS 32
static int nargs;
static Arg wargs[MAXARGS];
#define startargs() nargs = 0
#define setarg(name, value) XtSetArg(wargs[nargs], name, value), nargs++
/* X Windows related variables */
static Cursor WorkingCursor;
static Display *dpy;
static Window win;
static GC gc;
static GC cleargc;
/* X Defaults */
static int defaultWidth = CELLWIDTH * XCELLS;
static int defaultHeight = CELLWIDTH * YCELLS;
static int zero = 0;
static int Width, Height;
static Pixel fg, bg;
static char *progname;
static char *patfile;
/* Application Resources - no particular widget */
static XtResource application_resources[] = {
{"name", "Name", XtRString, sizeof(char *),
(Cardinal)&progname, XtRString, "xlife"},
{"width", "Width", XtRInt, sizeof(int),
(Cardinal)&Width, XtRInt, (caddr_t) &defaultWidth},
{"height", "Height", XtRInt, sizeof(int),
(Cardinal)&Height, XtRInt, (caddr_t) &defaultHeight},
{"random", "Random", XtRInt, sizeof(int),
(Cardinal)&nrandom, XtRInt, (caddr_t) &zero},
{"foreground", "Foreground", XtRPixel, sizeof(Pixel),
(Cardinal)&fg, XtRString, (caddr_t) "Black"},
{"background", "Background", XtRPixel, sizeof(Pixel),
(Cardinal)&bg, XtRString, (caddr_t) "White"},
{"patternfile", "PatternFile", XtRString, sizeof(char *),
(Cardinal)&patfile, XtRString, PATFILE},
};
/*
* Command line options table. The command line is parsed for these,
* and it sets/overrides the appropriate values in the resource
* database
*/
static XrmOptionDescRec optionDescList[] = {
{"-width", "*width", XrmoptionSepArg, (caddr_t) NULL},
{"-height", "*height", XrmoptionSepArg, (caddr_t) NULL},
{"-fg", "*foreground", XrmoptionSepArg, (caddr_t) NULL},
{"-bg", "*background", XrmoptionSepArg, (caddr_t) NULL},
{"-random", "*random", XrmoptionSepArg, (caddr_t) NULL},
{"-patfile", "*patternfile", XrmoptionSepArg, (caddr_t) NULL},
};
main(argc, argv)
int argc;
char **argv;
{
static void RepaintCanvas();
static void RecordMapStatus();
static void MouseInput();
Widget toplevel;
Widget w;
XGCValues gcv;
XEvent event;
/*
* Create the top level Widget that represents encloses the
* application.
*/
toplevel = XtInitialize(argv[0], "XLife",
optionDescList, XtNumber(optionDescList), &argc, argv);
XtGetApplicationResources(toplevel, 0, application_resources,
XtNumber(application_resources), NULL, 0 );
if (argc != 1) {
(void) fprintf(stderr, "Usage: %s [Xt options]\n", argv[0]);
exit(-1);
}
makeboard(Width/CELLWIDTH, Height/CELLHEIGHT);
readpatterns(patfile);
/*
* Create a simple Core class widget which we'll use for the actual
* game. A Core class widget is basically just a window, with a
* simple Xt "wrapper" around it.
*/
startargs();
setarg(XtNwidth, xcell(nxcells));
setarg(XtNheight, xcell(nycells));
w = XtCreateManagedWidget(argv[0], widgetClass, toplevel,
wargs, nargs);
/*
* Set the procedures for various X Windows actions - exposure events
* which arrive when a window needs to be redrawn. The map event lets
* you know that the window is now on the screen so you can actually
* do stuff. The ButtonPress event lets you know that a mouse button
* was pressed.
*/
XtAddEventHandler(w, (Cardinal) ExposureMask, NULL,
RepaintCanvas, "redraw_data");
XtAddEventHandler(w, (Cardinal) StructureNotifyMask, NULL,
RecordMapStatus, "map_data");
/* One day, we'll use the translation manager here */
XtAddEventHandler(w,
(Cardinal) ButtonPressMask | ButtonMotionMask | KeyPressMask, NULL,
MouseInput, "input_data");
/*
* Create the windows, and set their attributes according to the Widget
* data.
*/
XtRealizeWidget(toplevel);
/* We need these for the raw Xlib calls */
win = XtWindow(w);
dpy = XtDisplay(w);
WorkingCursor = XCreateFontCursor(dpy, XC_top_left_arrow);
XDefineCursor(dpy, win, WorkingCursor);
/*
* make the X Graphic Contexts here - one for copy (setting the
* square to foreground colour), one for erase (setting the
* square to background colour).
*/
gcv.foreground = fg;
gcv.background = bg;
gcv.function = GXcopy;
gc = XCreateGC(dpy, win, GCForeground | GCBackground
| GCFunction, &gcv);
gcv.foreground = bg;
cleargc = XCreateGC(dpy, win, GCForeground | GCBackground
| GCFunction, &gcv);
(void) seedrnd(getpid());
randomize(nrandom);
XFlush(dpy);
/*
* Now process the events. This is a straightforward eventloop
* where we do one iteration and update the display, and then check
* for events
*/
while(notover) {
/* Process any events if they're waiting */
while (XtPending() > 0) {
XtNextEvent (&event);
XtDispatchEvent(&event);
}
if (running)
do_life();
else {
/* Block for the next event since we aren't doing anything else */
XtNextEvent (&event);
XtDispatchEvent(&event);
}
}
}
/* Actual game of life iteration */
static void
do_life()
{
int **tmp;
register int *nrow, *orow, *orowprev, *orownext;
register int i, j;
register int neighbours;
#ifdef DEBUG
int done = 0;
int filled = 0;
#endif
changed = 0;
orow = oboard[0];
orownext = oboard[1];
for(i = 1; i <= nycells; i++) {
orowprev = orow;
orow = orownext; /* orow = oboard[i] */
orownext = oboard[i+1];
nrow = nboard[i];
for(j = 1; j <= nxcells; j++) {
/* calculate cell from oboard to new board */
neighbours =
orowprev[j-1] + orowprev[j] + orowprev[j+1] +
orow[j-1] + orow[j+1] +
orownext[j-1] + orownext[j] + orownext[j+1];
if (neighbours == 3 && orow[j] == 0) {
nrow[j] = 1;
fill_rectangle(j, i, 1);
#ifdef DEBUG
done++;
#endif
} else if (neighbours != 2 && neighbours != 3 &&
orow[j] != 0) {
nrow[j] = 0;
fill_rectangle(j, i, 0);
#ifdef DEBUG
done++;
#endif
} else {
nrow[j] = orow[j];
/* Since it hasn't changed, we don't update */
}
#ifdef DEBUG
if (nrow[j])
filled++;
#endif
}
}
/* Stop running if we haven't had a change in the cell */
if (changed == 0)
running = 0;
tmp = nboard;
nboard = oboard;
oboard = tmp;
/* reentrant_redraw();*/
#ifdef DEBUG
printf("Done an iteration - changed = %d, filled = %d, running = %d\n",
done, filled, running);
#endif
}
/*
* Add a rectangle to the display_list. If the last rectangle was the
* same colour as the one now (determined by 'set'), then add it to the
* current rect_list element by incrementing the count, and putting it
* in rect_buf. If it is different, then start a new rect_list
* element.
*/
static void
fill_rectangle(x, y, set)
{
changed++;
XFillRectangle(dpy, win, set ? gc : cleargc, xcell(x), ycell(y),
CELLWIDTH, CELLHEIGHT);
}
static int isMapped = 0;
/*ARGSUSED*/
static void
RepaintCanvas(w, data, ev)
Widget w;
caddr_t data;
XEvent *ev;
{
if (!isMapped)
return;
/*
* Redraw the array
*/
if (ev && ev->xexpose.count == 0) {
XEvent event;
/* Skip all excess redraws */
while (XCheckTypedEvent(dpy, Expose, &event))
;
redraw();
}
#ifdef WINDOWDEBUG
printf("repaint\n");
#endif
XFlush(dpy);
}
/*ARGSUSED*/
static void
RecordMapStatus(w, data, ev)
Widget w;
caddr_t data;
XEvent *ev;
{
if (ev->type == MapNotify) {
#ifdef WINDOWDEBUG
printf("window mapped\n");
#endif
isMapped = TRUE;
} else if (ev->type = ConfigureNotify) {
#ifdef WINDOWDEBUG
printf("window resized\n");
#endif
}
}
/*ARGSUSED*/
static void
MouseInput(w, data, ev)
Widget w;
caddr_t data;
XEvent *ev;
{
char ch;
#ifdef WINDOWDEBUG
printf("Input to canvas - %d (0x%x)\n", ev->xany.type, ev->xany.type);
#endif
switch (ev->xany.type) {
case ButtonPress:
if (ev->xbutton.button == LeftButton) {
/* Turn on cell */
setcell((int) (ev->xbutton.x / CELLWIDTH),
(int) (ev->xbutton.y / CELLHEIGHT), 1);
} else if (ev->xbutton.button == MiddleButton) {
/* Toggle simulation on/off */
running = !running;
} else if (ev->xbutton.button == RightButton && !running) {
/* Turn off cell */
setcell((int) (ev->xbutton.x / CELLWIDTH),
(int) (ev->xbutton.y / CELLHEIGHT), 0);
}
break;
case MotionNotify:
if (ev->xmotion.state & LeftButtonMask)
/* Turn on cell */
setcell((int) (ev->xmotion.x / CELLWIDTH),
(int) (ev->xmotion.y / CELLHEIGHT), 1);
else if ((ev->xmotion.state & RightButtonMask) && !running)
/* Turn off cell */
setcell((int) (ev->xmotion.x / CELLWIDTH),
(int) (ev->xmotion.y / CELLHEIGHT), 0);
break;
case KeyPress:
XLookupString(ev, &ch, 1, NULL, NULL);
#ifdef WINDOWDEBUG
printf("Keypress - %c (0x%x)\n", ch, ch);
#endif
switch(ch) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break;
case 'c':
clear();
break;
case 'r':
redraw();
break;
case 'q':
exit(0);
/*NOTREACHED*/
default:
break;
}
break;
default:
(void) printf("Got an event of type %d (%x).\n",
ev->xany.type, ev->xany.type);
break;
}
}
static void
setcell(x, y, set)
int x, y, set;
{
int xreal = x * CELLWIDTH;
int yreal = y * CELLHEIGHT;
register int i = y + 1;
register int j = x + 1;
if (x < 0 || x >= nxcells || y < 0 || y >= nycells)
return;
if (set)
oboard[i][j] = 1;
else
oboard[i][j] = 0;
XFillRectangle(dpy, win, set ? gc : cleargc, xreal, yreal,
CELLWIDTH, CELLHEIGHT);
}
static void
makeboard(width, height)
int width, height;
{
register int i;
nxcells = width;
nycells = height;
boardrows1 = (int **) XtCalloc(height+2, sizeof(int *));
boardrows2 = (int **) XtCalloc(height+2, sizeof(int *));
for(i = 0; i < height+2; i++) {
boardrows1[i] = (int *) XtCalloc(width+2, sizeof(int));
boardrows2[i] = (int *) XtCalloc(width+2, sizeof(int));
}
oboard = boardrows1;
nboard = boardrows2;
}
/* If signals aren't off when redraw is called, bad things may happen */
static void
redraw()
{
int i, j;
for(i = 1; i <= nycells; i++) {
for(j = 1; j <= nxcells; j++) {
if(oboard[i][j])
XFillRectangle(dpy, win, gc,
xcell(j-1), ycell(i-1),
CELLWIDTH, CELLHEIGHT);
}
}
}
/* Typical usage of object.h */
typedef struct {
int i, j;
} Coord;
#define PATTERN_BUF_CHUNK 256
static Coord *pattern_buf = NULL;
static int pattern_buf_size = 0;
static int pattern_buf_count = 0;
/* yet another array of objects */
typedef struct {
Coord *coord;
int ncoords;
} Pattern;
#define PATTERN_CHUNK 32
static Pattern *patterns = NULL;
static int patterns_size = 0;
static int patterns_count = 0;
/* Opens file and appends patterns to list of predefined patterns. */
static void
readpatterns(file)
char *file;
{
FILE *fp;
char buf[128];
int i, j;
int newpattern = 0;
if ((fp = fopen(file, "r")) == NULL) {
(void) fprintf(stderr, "%s: Couldn't open file %s\n",
progname, file);
return;
}
test_and_grow_object(patterns, patterns_count,
patterns_size, PATTERN_CHUNK, Pattern);
test_and_grow_object(pattern_buf, pattern_buf_count,
pattern_buf_size, PATTERN_BUF_CHUNK, Coord);
patterns[patterns_count].coord = &pattern_buf[pattern_buf_count];
patterns[patterns_count].ncoords = 0;
while(fgets(buf, sizeof(buf), fp) != NULL) {
if (sscanf(buf, " %d %d", &i, &j) == 2) {
newpattern++;
test_and_grow_object(pattern_buf, pattern_buf_count,
pattern_buf_size, PATTERN_BUF_CHUNK, Coord);
pattern_buf[pattern_buf_count].i = i;
pattern_buf[pattern_buf_count].j = j;
pattern_buf_count++;
} else if (newpattern != 0) {
/* Lousy parsing - we assume end of pattern */
patterns[patterns_count].ncoords = newpattern;
patterns_count++;
test_and_grow_object(patterns, patterns_count,
patterns_size, PATTERN_CHUNK, Pattern);
patterns[patterns_count].coord =
&pattern_buf[pattern_buf_count];
patterns[patterns_count].ncoords = 0;
newpattern = 0;
}
}
}
/* Puts pattern m with origin at col i, row j */
static void
putpattern(m, x, y)
{
register int k;
Coord *p;
register int yoffset = y;
register int xoffset = x;
p = patterns[m].coord;
k = patterns[m].ncoords;
while(--k >= 0)
setcell(xoffset + p[k].j, yoffset + p[k].i, 1);
}
static void
randomize(nrandom)
{
int x, y;
int i;
int nx, ny;
if (patterns_count == 0)
return;
nx = nxcells - 2;
ny = nycells - 2;
if (nrandom == 0)
nrandom = (nxcells + nycells) / 5;
while(nrandom-- > 0) {
x = rnd(nx) + 1;
y = rnd(ny) + 1;
i = rnd(patterns_count);
putpattern(i, x, y);
}
}
static void
clear()
{
register int i, j;
for(i = 0; i < nycells; i++)
for(j = 0; j < nxcells; j++)
oboard[i][j] = nboard[i][j] = 0;
XClearWindow(dpy, win);
}