/* * Copyright (c) 1988-91 by Patrick J. Naughton. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. */ /* * Copyright (c) 1994, 2015, Oracle and/or its affiliates. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /*- * life.c - Conway's game of Life for xlock, the X Window System lockscreen. * * Copyright (c) 1991 by Patrick J. Naughton. * * See xlock.c for copying information. * * Revision History: * 24-May-91: Added wraparound code from johnson@bugs.comm.mot.com. * Made old cells stay blue. * Made batchcount control the number of generations till restart. * 29-Jul-90: support for multiple screens. * 07-Feb-90: remove bogus semi-colon after #include line. * 15-Dec-89: Fix for proper skipping of {White,Black}Pixel() in colors. * 08-Oct-89: Moved seconds() to an extern. * 20-Sep-89: Written (life algorithm courtesy of Jim Graham, flar@sun.com). */ #include "xlock.h" #include "lifeicon.bit" static XImage logo = { 0, 0, /* width, height */ 0, XYBitmap, 0, /* xoffset, format, data */ LSBFirst, 8, /* byte-order, bitmap-unit */ LSBFirst, 8, 1 /* bitmap-bit-order, bitmap-pad, depth */ }; #define min(a, b) ((a)<(b)?(a):(b)) #define MAXROWS 155 #define MAXCOLS 144 #define TIMEOUT 30 typedef struct { int pixelmode; int xs; int ys; int xb; int yb; int generation; long shooterTime; int nrows; int ncols; int width; int height; unsigned char buffer[(MAXROWS + 2) * (MAXCOLS + 2) + 2]; unsigned char tempbuf[MAXCOLS * 2]; unsigned char lastbuf[MAXCOLS]; unsigned char agebuf[(MAXROWS + 2) * (MAXCOLS + 2)]; } lifestruct; static lifestruct lifes[MAXSCREENS]; static int icon_width, icon_height; /* Buffer stores the data for each cell. Each cell is stored as * 8 bits representing the presence of a critter in each of it's * surrounding 8 cells. There is an empty row and column around * the whole array to allow stores without bounds checking as well * as an extra row at the end for the fetches into tempbuf. * * Tempbuf stores the data for the next two rows so that we know * the state of those critter before he was modified by the fate * of the critters that have already been processed. * * Agebuf stores the age of each critter. */ #define UPLT 0x01 #define UP 0x02 #define UPRT 0x04 #define LT 0x08 #define RT 0x10 #define DNLT 0x20 #define DN 0x40 #define DNRT 0x80 /* Fates is a lookup table for the fate of a critter. The 256 * entries represent the 256 possible combinations of the 8 * neighbor cells. Each entry is one of BIRTH (create a cell * or leave one alive), SAME (leave the cell alive or dead), * or DEATH (kill anything in the cell). */ #define BIRTH 0 #define SAME 1 #define DEATH 2 static unsigned char fates[256]; static int initialized = 0; static int patterns[][128] = { { /* EIGHT */ -3, -3, -2, -3, -1, -3, -3, -2, -2, -2, -1, -2, -3, -1, -2, -1, -1, -1, 0, 0, 1, 0, 2, 0, 0, 1, 1, 1, 2, 1, 0, 2, 1, 2, 2, 2, 99 }, { /* PULSAR */ 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 1, 2, 5, 2, 99 }, { /* BARBER */ -7, -7, -6, -7, -7, -6, -5, -6, -5, -4, -3, -4, -3, -2, -1, -2, -1, 0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 4, 5, 5, 5, 99 }, { /* HERTZ */ -2, -6, -1, -6, -2, -5, -1, -5, -7, -3, -6, -3, -2, -3, -1, -3, 0, -3, 1, -3, 5, -3, 6, -3, -7, -2, -5, -2, -3, -2, 2, -2, 4, -2, 6, -2, -5, -1, -3, -1, -2, -1, 2, -1, 4, -1, -7, 0, -5, 0, -3, 0, 2, 0, 4, 0, 6, 0, -7, 1, -6, 1, -2, 1, -1, 1, 0, 1, 1, 1, 5, 1, 6, 1, -2, 3, -1, 3, -2, 4, -1, 4, 99 }, { /* TUMBLER */ -6, -6, -5, -6, 6, -6, 7, -6, -6, -5, -5, -5, 6, -5, 7, -5, -5, 5, 6, 5, -7, 6, -5, 6, 6, 6, 8, 6, -7, 7, -5, 7, 6, 7, 8, 7, -7, 8, -6, 8, 7, 8, 8, 8, 99 }, { /* PERIOD4 */ -5, -8, -4, -8, -7, -7, -5, -7, -8, -6, -2, -6, -7, -5, -3, -5, -2, -5, -5, -3, -3, -3, -4, -2, 99 }, { /* PERIOD5 */ -5, -8, -4, -8, -6, -7, -3, -7, -7, -6, -2, -6, -8, -5, -1, -5, -8, -4, -1, -4, -7, -3, -2, -3, -6, -2, -3, -2, -5, -1, -4, -1, 99 }, { /* PERIOD6 */ -4, -8, -3, -8, -8, -7, -7, -7, -5, -7, -8, -6, -7, -6, -4, -6, -1, -6, -3, -5, -1, -5, -2, -4, -3, -2, -2, -2, -3, -1, -2, -1, 99 }, { /* PINWHEEL */ -4, -8, -3, -8, -4, -7, -3, -7, -4, -5, -3, -5, -2, -5, -1, -5, -5, -4, -3, -4, 0, -4, 2, -4, 3, -4, -5, -3, -1, -3, 0, -3, 2, -3, 3, -3, -8, -2, -7, -2, -5, -2, -2, -2, 0, -2, -8, -1, -7, -1, -5, -1, 0, -1, -4, 0, -3, 0, -2, 0, -1, 0, -2, 2, -1, 2, -2, 3, -1, 3, 99 }, { /* ] */ -1, -1, 0, -1, 1, -1, 0, 0, 1, 0, -1, 1, 0, 1, 1, 1, 99 }, { /* cc: */ -3, -1, -2, -1, -1, -1, 1, -1, 2, -1, 3, -1, -3, 0, -2, 0, 1, 0, 2, 0, -3, 1, -2, 1, -1, 1, 1, 1, 2, 1, 3, 1, 99 }, { /* DOLBY */ -3, -1, -2, -1, -1, -1, 1, -1, 2, -1, 3, -1, -3, 0, -2, 0, 2, 0, 3, 0, -3, 1, -2, 1, -1, 1, 1, 1, 2, 1, 3, 1, 99 }, { /* HORIZON */ -15, 0, -14, 0, -13, 0, -12, 0, -11, 0, -10, 0, -9, 0, -8, 0, -7, 0, -6, 0, -5, 0, -4, 0, -3, 0, -2, 0, -1, 0, 4, 0, 3, 0, 2, 0, 1, 0, 0, 0, 9, 0, 8, 0, 7, 0, 6, 0, 5, 0, 14, 0, 13, 0, 12, 0, 11, 0, 10, 0, 99 }, { /* SHEAR */ -7, -2, -6, -2, -5, -2, -4, -2, -3, -2, -2, -2, -1, -2, 0, -2, 1, -2, 2, -2, -5, -1, -4, -1, -3, -1, -2, -1, -1, -1, 0, -1, 1, -1, 2, -1, 3, -1, 4, -1, -3, 0, -2, 0, -1, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, -10, 1, -9, 1, -8, 1, -7, 1, -6, 1, -5, 1, -4, 1, -3, 1, -2, 1, -1, 1, -10, 2, -9, 2, -8, 2, -7, 2, -6, 2, -5, 2, -4, 2, -3, 2, -2, 2, -1, 2, 99 }, { /* VERTIGO */ 0, -7, 0, -6, 0, -5, 0, -4, 0, -3, 0, -2, 0, -1, 0, 0, 0, 7, 0, 6, 0, 5, 0, 4, 0, 3, 0, 2, 0, 1, 99 }, { /* CROSSBAR */ -5, 0, -4, 0, -3, 0, -2, 0, -1, 0, 4, 0, 3, 0, 2, 0, 1, 0, 0, 0, 99 }, { /* GOALPOSTS */ -8, -7, 8, -7, -8, -6, 8, -6, -8, -5, 8, -5, -8, -4, 8, -4, -8, -3, 8, -3, -8, -2, 8, -2, -8, -1, 8, -1, -8, 0, 8, 0, -8, 1, 8, 1, -8, 2, 8, 2, -8, 3, 8, 3, -8, 4, 8, 4, -8, 5, 8, 5, -8, 6, 8, 6, -8, 7, 8, 7, 99 }, { /* \ */ -8, -8, -7, -8, -7, -7, -6, -7, -6, -6, -5, -6, -5, -5, -4, -5, -4, -4, -3, -4, -3, -3, -2, -3, -2, -2, -1, -2, -1, -1, 0, -1, 0, 0, 1, 0, 1, 1, 2, 1, 2, 2, 3, 2, 3, 3, 4, 3, 4, 4, 5, 4, 5, 5, 6, 5, 6, 6, 7, 6, 7, 7, 8, 7, 99 }, { /* LABYRINTH */ -4, -4, -3, -4, -2, -4, -1, -4, 0, -4, 1, -4, 2, -4, 3, -4, 4, -4, -4, -3, 0, -3, 4, -3, -4, -2, -2, -2, -1, -2, 0, -2, 1, -2, 2, -2, 4, -2, -4, -1, -2, -1, 2, -1, 4, -1, -4, 0, -2, 0, -1, 0, 0, 0, 1, 0, 2, 0, 4, 0, -4, 1, -2, 1, 2, 1, 4, 1, -4, 2, -2, 2, -1, 2, 0, 2, 1, 2, 2, 2, 4, 2, -4, 3, 0, 3, 4, 3, -4, 4, -3, 4, -2, 4, -1, 4, 0, 4, 1, 4, 2, 4, 3, 4, 4, 4, 99 } }; #define NPATS (sizeof patterns / sizeof patterns[0]) static void drawcell( Window win, int row, int col ) { lifestruct *lp = &lifes[screen]; XSetForeground(dsp, Scr[screen].gc, sswhite[screen].pixel); if (!mono && Scr[screen].npixels > 2) { int off = ((row + 1) * (lp->ncols + 2)) + col + 1; unsigned char *ageptr = lp->agebuf + off; unsigned char age = *ageptr; /* if we aren't up to blue yet, then keep aging the cell. */ if (age < Scr[screen].npixels * 0.7) ++age; XSetForeground(dsp, Scr[screen].gc, Scr[screen].pixels[age]); *ageptr = age; } if (lp->pixelmode) XFillRectangle(dsp, win, Scr[screen].gc, lp->xb + lp->xs * col, lp->yb + lp->ys * row, lp->xs, lp->ys); else XPutImage(dsp, win, Scr[screen].gc, &logo, 0, 0, lp->xb + lp->xs * col, lp->yb + lp->ys * row, icon_width, icon_height); } static void erasecell( Window win, int row, int col ) { lifestruct *lp = &lifes[screen]; XSetForeground(dsp, Scr[screen].gc, ssblack[screen].pixel); XFillRectangle(dsp, win, Scr[screen].gc, lp->xb + lp->xs * col, lp->yb + lp->ys * row, lp->xs, lp->ys); } static void spawn(unsigned char *loc) { lifestruct *lp = &lifes[screen]; unsigned char *ulloc, *ucloc, *urloc, *clloc, *crloc, *llloc, *lcloc, *lrloc, *arloc; int off, row, col, lastrow; lastrow = (lp->nrows) * (lp->ncols + 2); off = (int) (loc - lp->buffer); col = off % (lp->ncols + 2); row = (off - col) / (lp->ncols + 2); ulloc = loc - lp->ncols - 3; ucloc = loc - lp->ncols - 2; urloc = loc - lp->ncols - 1; clloc = loc - 1; crloc = loc + 1; arloc = loc + 1; llloc = loc + lp->ncols + 1; lcloc = loc + lp->ncols + 2; lrloc = loc + lp->ncols + 3; if (row == 1) { ulloc += lastrow; ucloc += lastrow; urloc += lastrow; } if (row == lp->nrows) { llloc -= lastrow; lcloc -= lastrow; lrloc -= lastrow; } if (col == 1) { ulloc += lp->ncols; clloc += lp->ncols; llloc += lp->ncols; } if (col == lp->ncols) { urloc -= lp->ncols; crloc -= lp->ncols; lrloc -= lp->ncols; } *ulloc |= UPLT; *ucloc |= UP; *urloc |= UPRT; *clloc |= LT; *crloc |= RT; *arloc |= RT; *llloc |= DNLT; *lcloc |= DN; *lrloc |= DNRT; *(lp->agebuf + (loc - lp->buffer)) = 0; } static void killcell(unsigned char *loc) { lifestruct *lp = &lifes[screen]; unsigned char *ulloc, *ucloc, *urloc, *clloc, *crloc, *llloc, *lcloc, *lrloc, *arloc; int off, row, col, lastrow; lastrow = (lp->nrows) * (lp->ncols + 2); off = (int) (loc - lp->buffer); row = off / (lp->ncols + 2); col = off % (lp->ncols + 2); row = (off - col) / (lp->ncols + 2); ulloc = loc - lp->ncols - 3; ucloc = loc - lp->ncols - 2; urloc = loc - lp->ncols - 1; clloc = loc - 1; crloc = loc + 1; arloc = loc + 1; llloc = loc + lp->ncols + 1; lcloc = loc + lp->ncols + 2; lrloc = loc + lp->ncols + 3; if (row == 1) { ulloc += lastrow; ucloc += lastrow; urloc += lastrow; } if (row == lp->nrows) { llloc -= lastrow; lcloc -= lastrow; lrloc -= lastrow; } if (col == 1) { ulloc += lp->ncols; clloc += lp->ncols; llloc += lp->ncols; } if (col == lp->ncols) { urloc -= lp->ncols; crloc -= lp->ncols; lrloc -= lp->ncols; } *ulloc &= ~UPLT; *ucloc &= ~UP; *urloc &= ~UPRT; *clloc &= ~LT; *crloc &= ~RT; *arloc &= ~RT; *llloc &= ~DNLT; *lcloc &= ~DN; *lrloc &= ~DNRT; } static void setcell( Window win, int row, int col ) { lifestruct *lp = &lifes[screen]; unsigned char *loc; loc = lp->buffer + ((row + 1) * (lp->ncols + 2)) + col + 1; spawn(loc); drawcell(win, row, col); } static void init_fates(void) { int i, bits, neighbors; for (i = 0; i < 256; i++) { neighbors = 0; for (bits = i; bits; bits &= (bits - 1)) neighbors++; if (neighbors == 3) fates[i] = BIRTH; else if (neighbors == 2) fates[i] = SAME; else fates[i] = DEATH; } } void initlife(Window win) { int row, col; int *patptr; XWindowAttributes xgwa; lifestruct *lp = &lifes[screen]; lp->generation = 0; lp->shooterTime = seconds(); icon_width = lifeicon_width; icon_height = lifeicon_height; if (!initialized) { initialized = 1; init_fates(); logo.data = (char *) lifeicon_bits; logo.width = icon_width; logo.height = icon_height; logo.bytes_per_line = (icon_width + 7) / 8; } XGetWindowAttributes(dsp, win, &xgwa); lp->width = xgwa.width; lp->height = xgwa.height; lp->pixelmode = (lp->width < 4 * icon_width); if (lp->pixelmode) { lp->ncols = 32; lp->nrows = 32; } else { lp->ncols = min(lp->width / icon_width, MAXCOLS); lp->nrows = min(lp->height / icon_height, MAXROWS); } lp->xs = lp->width / lp->ncols; lp->ys = lp->height / lp->nrows; lp->xb = (lp->width - lp->xs * lp->ncols) / 2; lp->yb = (lp->height - lp->ys * lp->nrows) / 2; XSetForeground(dsp, Scr[screen].gc, ssblack[screen].pixel); XFillRectangle(dsp, win, Scr[screen].gc, 0, 0, lp->width, lp->height); bzero(lp->buffer, sizeof(lp->buffer)); patptr = &patterns[random() % NPATS][0]; while ((col = *patptr++) != 99) { row = *patptr++; col += lp->ncols / 2; row += lp->nrows / 2; setcell(win, row, col); } } void drawlife(Window win) { unsigned char *loc, *temploc, *lastloc; int row, col; unsigned char fate; lifestruct *lp = &lifes[screen]; loc = lp->buffer + lp->ncols + 2 + 1; temploc = lp->tempbuf; /* copy the first 2 rows to the tempbuf */ bcopy(loc, temploc, lp->ncols); bcopy(loc + lp->ncols + 2, temploc + lp->ncols, lp->ncols); lastloc = lp->lastbuf; /* copy the last row to another buffer for wraparound */ bcopy(loc + ((lp->nrows - 1) * (lp->ncols + 2)), lastloc, lp->ncols); for (row = 0; row < lp->nrows; ++row) { for (col = 0; col < lp->ncols; ++col) { fate = fates[*temploc]; *temploc = (row == (lp->nrows - 3)) ? *(lastloc + col) : *(loc + (lp->ncols + 2) * 2); switch (fate) { case BIRTH: if (!(*(loc + 1) & RT)) { spawn(loc); } /* FALLTHROUGH */ case SAME: if (*(loc + 1) & RT) { drawcell(win, row, col); } break; case DEATH: if (*(loc + 1) & RT) { killcell(loc); erasecell(win, row, col); } break; } loc++; temploc++; } loc += 2; if (temploc >= lp->tempbuf + lp->ncols * 2) temploc = lp->tempbuf; } if (++lp->generation > batchcount) initlife(win); /* * generate a randomized shooter aimed roughly toward the center of the * screen after timeout. */ if (seconds() - lp->shooterTime > TIMEOUT) { int hsp = (int) random() % (lp->ncols - 5) + 3; int vsp = (int) random() % (lp->nrows - 5) + 3; int hoff = 1; int voff = 1; if (vsp > lp->nrows / 2) voff = -1; if (hsp > lp->ncols / 2) hoff = -1; setcell(win, vsp + 0 * voff, hsp + 2 * hoff); setcell(win, vsp + 1 * voff, hsp + 2 * hoff); setcell(win, vsp + 2 * voff, hsp + 2 * hoff); setcell(win, vsp + 2 * voff, hsp + 1 * hoff); setcell(win, vsp + 1 * voff, hsp + 0 * hoff); lp->shooterTime = seconds(); } }