X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fmunch.c;h=a76e4e42ca3445f94265839e558f23c459525e62;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hp=b3cd73517d2c477dc638eb727a72a7bdd1586476;hpb=df7adbee81405e2849728a24b498ad2117784b1f;p=xscreensaver diff --git a/hacks/munch.c b/hacks/munch.c index b3cd7351..a76e4e42 100644 --- a/hacks/munch.c +++ b/hacks/munch.c @@ -1,258 +1,473 @@ -/* munch.c - * A munching squares implementation for X - * Tim Showalter +/* Munching Squares and Mismunch + * + * Portions copyright 1992-2014 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. No + * representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or + * implied warranty. + * + * Portions Copyright 1997, Tim Showalter + * + * Permission is granted to copy, modify, and use this as long + * as this notice remains intact. No warranties are expressed or + * implied. CMU Sucks. * - * Copyright 1997, Tim Showalter - * Permission is granted to copy, modify, and use this as long - * as this notice remains intact. No warranties are expressed or implied. - * CMU Sucks. + * Portions Copyright 2004 Steven Hazel + * + * (The "mismunch" part). * - * Some code stolen from / This is meant to work with - * xscreensaver, Copyright (c) 1992, 1995, 1996 - * Jamie Zawinski + * "munch.c" and "mismunch.c" merged by jwz, 29-Aug-2008. * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -/* Munching Squares is this simplistic, silly screen hack (according - to HAKMEM, discovered by Jackson Wright in 1962) where you take - Y = X XOR T and graph it over and over. According to HAKMEM, it - takes 5 instructions of PDP-1 assembly. This is a little more - complicated than that, mostly X's fault, but it does some other - random things. - - http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html#item146 + * + * + *********************************************************************** + * + * HAKMEM + * + * MIT AI Memo 239, Feb. 29, 1972. + * Beeler, M., Gosper, R.W., and Schroeppel, R. + * + * http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html#item146 + * + *********************************************************************** + * + * ITEM 146: MUNCHING SQUARES + * + * Another simple display program. It is thought that this was + * discovered by Jackson Wright on the RLE PDP-1 circa 1962. + * + * DATAI 2 + * ADDB 1,2 + * ROTC 2,-22 + * XOR 1,2 + * JRST .-4 + * + * 2=X, 3=Y. Try things like 1001002 in data switches. This also + * does * interesting things with operations other than XOR, and + * rotations * other than -22. (Try IOR; AND; TSC; FADR; FDV(!); + * ROT * -14, -9, -20, * ...) + * + * ITEM 147 (Schroeppel): + * + * Munching squares is just views of the graph Y = X XOR T for + * consecutive values of T = time. + * + * ITEM 147 (Cohen, Beeler): + * + * A modification to munching squares which reveals them in frozen + * states through opening and closing curtains: insert FADR 2,1 + * before the XOR. Try data switches = + * + * 4000,,4 1000,,2002 2000,,4 0,,1002 + * + * (Notation: ,,) + * Also try the FADR after the XOR, switches = 1001,,1. + * + *********************************************************************** */ #include -/*#include */ #include "screenhack.h" -/* flags for random things. Must be < log2(random's maximum), incidentially. - */ -#define SHIFT_KX (0x01) -#define SHIFT_KT (0x02) -#define SHIFT_KY (0x04) -#define GRAV (0x08) - -char *progclass = "Munch"; - -char *defaults [] = { - ".background: black", - ".foreground: white", - "*delay: 5000", - "*hold: 100000", - "*clear: 50", - "*logminwidth: 7", - "*shift: True", - "*xor: True", - 0 -}; +typedef struct _muncher { + int mismunch; + int width; + int atX, atY; + int kX, kT, kY; + int grav; + XColor fgc; + int yshadow, xshadow; + int x, y, t; + int doom; + int done; +} muncher; -XrmOptionDescRec options [] = { - { "-delay", ".delay", XrmoptionSepArg, 0 }, - { "-hold", ".hold", XrmoptionSepArg, 0 }, - { "-clear", ".clear", XrmoptionSepArg, "true" }, - { "-shift", ".shift", XrmoptionNoArg, "true" }, - { "-no-shift", ".shift", XrmoptionNoArg, "false" }, - { "-logminwidth", ".logminwidth", XrmoptionSepArg, 0 }, - { "-xor", ".xor", XrmoptionNoArg, "true" }, - { "-no-xor", ".xor", XrmoptionNoArg, "false" }, - { 0, 0, 0, 0 } -}; +struct state { + Display *dpy; + Window window; -static GC gc; -/* only configurable things right now */ -static int delay, hold, clear, logminwidth, shiftk, xor; + GC gc; + int delay, simul, clear, xor; + int logminwidth, logmaxwidth; + int restart, window_width, window_height; -static void munchOnce (Display* dpy, Window w, - int width, /* pixels */ - int atX, int atY, /* pixels */ - int kX, int kT, int kY, /* pixels */ - int grav /* 0 or not */) { + int draw_n; /* number of squares before we have to clear */ + int draw_i; + int mismunch; - int x, y, t; - static Colormap cmap; - XWindowAttributes xgwa; - XColor fgc; - int drawX, drawY; + muncher **munchers; +}; - /* - fprintf(stderr,"Doing width %d at %d %d shift %d %d %d grav %d\n", - width, atX, atY, kX, kT, kY, grav); - */ - - XGetWindowAttributes (dpy, w, &xgwa); - - if (!mono_p) { - /* XXX there are probably bugs with this. */ - cmap = xgwa.colormap; - - fgc.red = random() % 65535; - fgc.green = random() % 65535; - fgc.blue = random() % 65535; - - if (XAllocColor(dpy, cmap, &fgc)) { - XSetForeground(dpy, gc, fgc.pixel); - } - } - - /* Finally draw this munching square. */ - for(t = 0; t < width; t++) { - for(x = 0; x < width; x++) { - /* figure out the next point */ - y = ((x ^ ((t + kT) % width)) + kY) % width; - - drawX = ((x + kX) % width) + atX; - drawY = (grav ? y + atY : atY + width - 1 - y); - - /* used to be bugs where it would draw partially offscreen. - while that might be a pretty feature, I didn't want it to do - that yet. if these trigger, please let me know. - */ -/* assert(drawX >= 0 && drawX < xgwa.width); - assert(drawY >= 0 && drawY < xgwa.height); -*/ - - XDrawPoint(dpy, w, gc, drawX, drawY); - /* XXX may want to change this to XDrawPoints, - but it's fast enough without it for the moment. */ - - } - - /* we've drawn one pass' worth of points. let the server catch up - or this won't be interesting to watch at all. we call this here - in the hopes of having one whole sequence of dots appear at the - same time (one for each value of x, surprisingly enough) - */ - XSync(dpy, False); - screenhack_handle_events (dpy); - if (delay) usleep(delay); - } -} /* * dumb way to get # of digits in number. Probably faster than actually * doing a log and a division, maybe. */ -static int dumb_log_2(int k) { - int r = -1; - while (k > 0) { - k >>= 1; r++; +static int dumb_log_2(int k) +{ + int r = -1; + while (k > 0) { + k >>= 1; r++; + } + return r; +} + + +static void calc_logwidths (struct state *st) +{ + /* Choose a range of square sizes based on the window size. We want + a power of 2 for the square width or the munch doesn't fill up. + Also, if a square doesn't fit inside an area 20% smaller than the + window, it's too big. Mismunched squares that big make things + look too noisy. */ + + if (st->window_height < st->window_width) { + st->logmaxwidth = (int)dumb_log_2(st->window_height * 0.8); + } else { + st->logmaxwidth = (int)dumb_log_2(st->window_width * 0.8); + } + + if (st->logmaxwidth < 2) { + st->logmaxwidth = 2; + } + + /* we always want three sizes of squares */ + st->logminwidth = st->logmaxwidth - 2; + + if (st->logminwidth < 2) { + st->logminwidth = 2; + } +} + + + +static muncher *make_muncher (struct state *st) +{ + int logwidth; + XWindowAttributes xgwa; + muncher *m = (muncher *) malloc(sizeof(muncher)); + + XGetWindowAttributes(st->dpy, st->window, &xgwa); + + m->mismunch = st->mismunch; + + /* choose size -- power of two */ + logwidth = (st->logminwidth + + (random() % (1 + st->logmaxwidth - st->logminwidth))); + + m->width = 1 << logwidth; + + /* draw at this location */ + m->atX = (random() % (xgwa.width <= m->width ? 1 + : xgwa.width - m->width)); + m->atY = (random() % (xgwa.height <= m->width ? 1 + : xgwa.width - m->width)); + + /* wrap-around by these values; no need to % as we end up doing that + later anyway */ + m->kX = ((random() % 2) + ? (random() % m->width) : 0); + m->kT = ((random() % 2) + ? (random() % m->width) : 0); + m->kY = ((random() % 2) + ? (random() % m->width) : 0); + + /* set the gravity of the munch, or rather, which direction we draw + stuff in. */ + m->grav = random() % 2; + + /* I like this color scheme better than random colors. */ + switch (random() % 4) { + case 0: + m->fgc.red = random() % 65536; + m->fgc.blue = random() % 32768; + m->fgc.green = random() % 16384; + break; + + case 1: + m->fgc.red = 0; + m->fgc.blue = random() % 65536; + m->fgc.green = random() % 16384; + break; + + case 2: + m->fgc.red = random() % 8192; + m->fgc.blue = random() % 8192; + m->fgc.green = random() % 49152; + break; + + case 3: + m->fgc.red = random() % 65536; + m->fgc.green = m->fgc.red; + m->fgc.blue = m->fgc.red; + break; + } + + /* Sometimes draw a mostly-overlapping copy of the square. This + generates all kinds of neat blocky graphics when drawing in xor + mode. */ + if (!m->mismunch || (random() % 4)) { + m->xshadow = 0; + m->yshadow = 0; + } else { + m->xshadow = (random() % (m->width/3)) - (m->width/6); + m->yshadow = (random() % (m->width/3)) - (m->width/6); + } + + /* Start with a random y value -- this sort of controls the type of + deformities seen in the squares. */ + m->y = random() % 256; + + m->t = 0; + + /* + Doom each square to be aborted at some random point. + (When doom == (width - 1), the entire square will be drawn.) + */ + m->doom = (m->mismunch ? (random() % m->width) : (m->width - 1)); + m->done = 0; + + return m; +} + + +static void munch (struct state *st, muncher *m) +{ + int drawX, drawY; + XWindowAttributes xgwa; + + if (m->done) { + return; + } + + XGetWindowAttributes(st->dpy, st->window, &xgwa); + + if (!mono_p) { + /* XXX there are probably bugs with this. */ + if (XAllocColor(st->dpy, xgwa.colormap, &m->fgc)) { + XSetForeground(st->dpy, st->gc, m->fgc.pixel); + } + } + + /* Finally draw this pass of the munching error. */ + + for(m->x = 0; m->x < m->width; m->x++) { + /* figure out the next point */ + + /* + The ordinary Munching Squares calculation is: + m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width; + + We create some feedback by plugging in y in place of x, and + make a couple of values negative so that some parts of some + squares get drawn in the wrong place. + */ + if (m->mismunch) + m->y = ((-m->y ^ ((-m->t + m->kT) % m->width)) + m->kY) % m->width; + else + m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width; + + drawX = ((m->x + m->kX) % m->width) + m->atX; + drawY = (m->grav ? m->y + m->atY : m->atY + m->width - 1 - m->y); + + XDrawPoint(st->dpy, st->window, st->gc, drawX, drawY); + if ((m->xshadow != 0) || (m->yshadow != 0)) { + /* draw the corresponding shadow point */ + XDrawPoint(st->dpy, st->window, st->gc, drawX + m->xshadow, drawY + m->yshadow); } - return r; + /* XXX may want to change this to XDrawPoints, + but it's fast enough without it for the moment. */ + + } + + m->t++; + if (m->t > m->doom) { + m->done = 1; + } } -/* This parses arguments, initializes the window, etc., and finally starts - * calling munchOnce ad infinitum. - */ -void -screenhack (dpy, w) Display *dpy; Window w; + +static void * +munch_init (Display *dpy, Window w) +{ + struct state *st = (struct state *) calloc (1, sizeof(*st)); + XWindowAttributes xgwa; + XGCValues gcv; + int i; + char *mm; + + st->dpy = dpy; + st->window = w; + st->restart = 0; + + /* get the dimensions of the window */ + XGetWindowAttributes(st->dpy, w, &xgwa); + + /* create the gc */ + gcv.foreground= get_pixel_resource(st->dpy, xgwa.colormap, + "foreground","Foreground"); + gcv.background= get_pixel_resource(st->dpy, xgwa.colormap, + "background","Background"); + + st->gc = XCreateGC(st->dpy, w, GCForeground|GCBackground, &gcv); + + st->delay = get_integer_resource(st->dpy, "delay", "Integer"); + if (st->delay < 0) st->delay = 0; + + st->simul = get_integer_resource(st->dpy, "simul", "Integer"); + if (st->simul < 1) st->simul = 1; + + st->clear = get_integer_resource(st->dpy, "clear", "Integer"); + if (st->clear < 0) st->clear = 0; + + st->xor = get_boolean_resource(st->dpy, "xor", "Boolean"); + + mm = get_string_resource (st->dpy, "mismunch", "Mismunch"); + if (!mm || !*mm || !strcmp(mm, "random")) + st->mismunch = random() & 1; + else + st->mismunch = get_boolean_resource (st->dpy, "mismunch", "Mismunch"); + + st->window_width = xgwa.width; + st->window_height = xgwa.height; + + calc_logwidths(st); + + /* always draw xor on mono. */ + if (mono_p || st->xor) { + XSetFunction(st->dpy, st->gc, GXxor); + } + + st->munchers = (muncher **) calloc(st->simul, sizeof(muncher *)); + for (i = 0; i < st->simul; i++) { + st->munchers[i] = make_muncher(st); + } + + return st; +} + +static unsigned long +munch_draw (Display *dpy, Window w, void *closure) { - int logmaxwidth; - int maxwidth; - XWindowAttributes xgwa; - Colormap cmap; - XGCValues gcv; - int n = 0; /* number of squares before we have to clear */ - int randflags; - int thiswidth; - - /* get the dimensions of the window */ - XGetWindowAttributes (dpy, w, &xgwa); - - /* We need a square; limit on screen size? */ - /* we want a power of 2 for the width or the munch doesn't fill up. - */ - logmaxwidth = (int) - dumb_log_2(xgwa.height < xgwa.width ? xgwa.height : xgwa.width); - - maxwidth = 1 << logmaxwidth; - - if (logmaxwidth < logminwidth) { - /* off-by-one error here? Anyone running on < 640x480? */ - fprintf(stderr, "munch: screen too small; use -logminwidth\n"); - fprintf(stderr, "\t(width is %d; log is %d; log must be at least " - "%d)\n", - (xgwa.height < xgwa.width ? xgwa.height : xgwa.width), - logmaxwidth, logminwidth); - exit(0); + struct state *st = (struct state *) closure; + int i; + + for (i = 0; i < 5; i++) { + + /* for (draw_i = 0; draw_i < simul; draw_i++) */ + { + munch(st, st->munchers[st->draw_i]); + + if (st->munchers[st->draw_i]->done) { + st->draw_n++; + + free(st->munchers[st->draw_i]); + st->munchers[st->draw_i] = make_muncher(st); } - - /* create the gc */ - cmap = xgwa.colormap; - gcv.foreground= get_pixel_resource("foreground","Foreground", - dpy, cmap); - gcv.background= get_pixel_resource("background","Background", - dpy, cmap); - - gc = XCreateGC(dpy, w, GCForeground|GCBackground, &gcv); - - delay = get_integer_resource ("delay", "Integer"); - if (delay < 0) delay = 0; - - hold = get_integer_resource ("hold", "Integer"); - if (hold < 0) hold = 0; - - clear = get_integer_resource ("clear", "Integer"); - if (clear < 0) clear = 0; - - logminwidth = get_integer_resource ("logminwidth", "Integer"); - if (logminwidth < 2) logminwidth = 2; - - shiftk = get_boolean_resource("shift", "Boolean"); - - xor = get_boolean_resource("xor", "Boolean"); - - /* always draw xor on mono. */ - if (mono_p || xor) { - XSetFunction(dpy, gc, GXxor); + } + + st->draw_i++; + if (st->draw_i >= st->simul) { + int i = 0; + st->draw_i = 0; + if (st->restart || (st->clear && st->draw_n >= st->clear)) { + + char *mm = get_string_resource (st->dpy, "mismunch", "Mismunch"); + if (!mm || !*mm || !strcmp(mm, "random")) + st->mismunch = random() & 1; + + for (i = 0; i < st->simul; i++) { + free(st->munchers[i]); + st->munchers[i] = make_muncher(st); + } + + XClearWindow(st->dpy, w); + st->draw_n = 0; + st->restart = 0; } + } + + } + + return st->delay; +} - for(;;) { - /* saves some calls to random. big deal */ - randflags = random(); - - /* choose size -- power of two */ - thiswidth = 1 << (logminwidth + - (random() % (1 + logmaxwidth - logminwidth))); - - munchOnce(dpy, w, - thiswidth, /* Width, in pixels */ - - /* draw at this location */ - (random() % (xgwa.width <= thiswidth ? 1 - : xgwa.width - thiswidth)), - (random() % (xgwa.height <= thiswidth ? 1 - : xgwa.width - thiswidth)), - - /* wrap-around by these values; no need to % - as we end up doing that later anyway*/ - ((shiftk && (randflags & SHIFT_KX)) - ? (random() % thiswidth) : 0), - ((shiftk && (randflags & SHIFT_KT)) - ? (random() % thiswidth) : 0), - ((shiftk && (randflags & SHIFT_KY)) - ? (random() % thiswidth) : 0), - - /* set the gravity of the munch, or rather, - which direction we draw stuff in. */ - (randflags & GRAV) - ); - - screenhack_handle_events (dpy); - if (hold) usleep(hold); - - if (clear && ++n >= clear) { - XClearWindow(dpy, w); - n = 0; - } + +static void +munch_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) +{ + struct state *st = (struct state *) closure; + if (w != st->window_width || + h != st->window_height) { + st->window_width = w; + st->window_height = h; + calc_logwidths(st); + st->restart = 1; + st->draw_i = 0; + } +} + +static Bool +munch_event (Display *dpy, Window window, void *closure, XEvent *event) +{ + struct state *st = (struct state *) closure; + if (screenhack_event_helper (dpy, window, event)) + { + int i; + st->window_height--; + munch_reshape(dpy, window, closure, st->window_width, st->window_height); + st->mismunch = random() & 1; + for (i = 0; i < st->simul; i++) { + free (st->munchers[i]); + st->munchers[i] = make_muncher(st); + } + XClearWindow(dpy, window); + return True; } + return False; +} + +static void +munch_free (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + free (st); } + + +static const char *munch_defaults [] = { + ".background: black", + ".foreground: white", + "*fpsSolid: true", + "*delay: 10000", + "*mismunch: random", + "*simul: 5", + "*clear: 65", + "*xor: True", +#ifdef HAVE_MOBILE + "*ignoreRotation: True", +#endif + 0 +}; + +static XrmOptionDescRec munch_options [] = { + { "-delay", ".delay", XrmoptionSepArg, 0 }, + { "-simul", ".simul", XrmoptionSepArg, 0 }, + { "-clear", ".clear", XrmoptionSepArg, "true" }, + { "-xor", ".xor", XrmoptionNoArg, "true" }, + { "-no-xor", ".xor", XrmoptionNoArg, "false" }, + { "-classic", ".mismunch", XrmoptionNoArg, "false" }, + { "-mismunch", ".mismunch", XrmoptionNoArg, "true" }, + { "-random", ".mismunch", XrmoptionNoArg, "random" }, + { 0, 0, 0, 0 } +}; + + +XSCREENSAVER_MODULE ("Munch", munch)