|
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); }