1 /* Munching Squares and Mismunch
3 * Portions copyright 1992-2014 Jamie Zawinski <jwz@jwz.org>
5 * Permission to use, copy, modify, distribute, and sell this
6 * software and its documentation for any purpose is hereby
7 * granted without fee, provided that the above copyright notice
8 * appear in all copies and that both that copyright notice and
9 * this permission notice appear in supporting documentation. No
10 * representations are made about the suitability of this software
11 * for any purpose. It is provided "as is" without express or
14 * Portions Copyright 1997, Tim Showalter
16 * Permission is granted to copy, modify, and use this as long
17 * as this notice remains intact. No warranties are expressed or
20 * Portions Copyright 2004 Steven Hazel <sah@thalassocracy.org>
22 * (The "mismunch" part).
24 * "munch.c" and "mismunch.c" merged by jwz, 29-Aug-2008.
28 ***********************************************************************
32 * MIT AI Memo 239, Feb. 29, 1972.
33 * Beeler, M., Gosper, R.W., and Schroeppel, R.
35 * http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html#item146
37 ***********************************************************************
39 * ITEM 146: MUNCHING SQUARES
41 * Another simple display program. It is thought that this was
42 * discovered by Jackson Wright on the RLE PDP-1 circa 1962.
50 * 2=X, 3=Y. Try things like 1001002 in data switches. This also
51 * does * interesting things with operations other than XOR, and
52 * rotations * other than -22. (Try IOR; AND; TSC; FADR; FDV(!);
53 * ROT * -14, -9, -20, * ...)
55 * ITEM 147 (Schroeppel):
57 * Munching squares is just views of the graph Y = X XOR T for
58 * consecutive values of T = time.
60 * ITEM 147 (Cohen, Beeler):
62 * A modification to munching squares which reveals them in frozen
63 * states through opening and closing curtains: insert FADR 2,1
64 * before the XOR. Try data switches =
66 * 4000,,4 1000,,2002 2000,,4 0,,1002
68 * (Notation: <left half>,,<right half>)
69 * Also try the FADR after the XOR, switches = 1001,,1.
71 ***********************************************************************
75 #include "screenhack.h"
77 typedef struct _muncher {
96 int delay, simul, clear, xor;
97 int logminwidth, logmaxwidth;
98 int restart, window_width, window_height;
100 int draw_n; /* number of squares before we have to clear */
109 * dumb way to get # of digits in number. Probably faster than actually
110 * doing a log and a division, maybe.
112 static int dumb_log_2(int k)
122 static void calc_logwidths (struct state *st)
124 /* Choose a range of square sizes based on the window size. We want
125 a power of 2 for the square width or the munch doesn't fill up.
126 Also, if a square doesn't fit inside an area 20% smaller than the
127 window, it's too big. Mismunched squares that big make things
130 if (st->window_height < st->window_width &&
131 st->window_width < st->window_height * 5) {
132 st->logmaxwidth = (int)dumb_log_2(st->window_height * 0.8);
134 st->logmaxwidth = (int)dumb_log_2(st->window_width * 0.8);
137 if (st->logmaxwidth < 2) {
141 /* we always want three sizes of squares */
142 st->logminwidth = st->logmaxwidth - 2;
144 if (st->logminwidth < 2) {
151 static muncher *make_muncher (struct state *st)
154 XWindowAttributes xgwa;
155 muncher *m = (muncher *) malloc(sizeof(muncher));
157 XGetWindowAttributes(st->dpy, st->window, &xgwa);
159 m->mismunch = st->mismunch;
161 /* choose size -- power of two */
162 logwidth = (st->logminwidth +
163 (random() % (1 + st->logmaxwidth - st->logminwidth)));
165 m->width = 1 << logwidth;
167 /* draw at this location */
168 m->atX = (random() % (xgwa.width <= m->width ? 1
169 : xgwa.width - m->width));
170 m->atY = (random() % (xgwa.height <= m->width ? 1
171 : xgwa.height - m->width));
173 /* wrap-around by these values; no need to % as we end up doing that
175 m->kX = ((random() % 2)
176 ? (random() % m->width) : 0);
177 m->kT = ((random() % 2)
178 ? (random() % m->width) : 0);
179 m->kY = ((random() % 2)
180 ? (random() % m->width) : 0);
182 /* set the gravity of the munch, or rather, which direction we draw
184 m->grav = random() % 2;
186 /* I like this color scheme better than random colors. */
187 switch (random() % 4) {
189 m->fgc.red = random() % 65536;
190 m->fgc.blue = random() % 32768;
191 m->fgc.green = random() % 16384;
196 m->fgc.blue = random() % 65536;
197 m->fgc.green = random() % 16384;
201 m->fgc.red = random() % 8192;
202 m->fgc.blue = random() % 8192;
203 m->fgc.green = random() % 49152;
207 m->fgc.red = random() % 65536;
208 m->fgc.green = m->fgc.red;
209 m->fgc.blue = m->fgc.red;
213 /* Sometimes draw a mostly-overlapping copy of the square. This
214 generates all kinds of neat blocky graphics when drawing in xor
216 if (!m->mismunch || (random() % 4)) {
220 m->xshadow = (random() % (m->width/3)) - (m->width/6);
221 m->yshadow = (random() % (m->width/3)) - (m->width/6);
224 /* Start with a random y value -- this sort of controls the type of
225 deformities seen in the squares. */
226 m->y = random() % 256;
231 Doom each square to be aborted at some random point.
232 (When doom == (width - 1), the entire square will be drawn.)
234 m->doom = (m->mismunch ? (random() % m->width) : (m->width - 1));
241 static void munch (struct state *st, muncher *m)
244 XWindowAttributes xgwa;
250 XGetWindowAttributes(st->dpy, st->window, &xgwa);
253 /* XXX there are probably bugs with this. */
254 if (XAllocColor(st->dpy, xgwa.colormap, &m->fgc)) {
255 XSetForeground(st->dpy, st->gc, m->fgc.pixel);
259 /* Finally draw this pass of the munching error. */
261 for(m->x = 0; m->x < m->width; m->x++) {
262 /* figure out the next point */
265 The ordinary Munching Squares calculation is:
266 m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width;
268 We create some feedback by plugging in y in place of x, and
269 make a couple of values negative so that some parts of some
270 squares get drawn in the wrong place.
273 m->y = ((-m->y ^ ((-m->t + m->kT) % m->width)) + m->kY) % m->width;
275 m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width;
277 drawX = ((m->x + m->kX) % m->width) + m->atX;
278 drawY = (m->grav ? m->y + m->atY : m->atY + m->width - 1 - m->y);
280 XDrawPoint(st->dpy, st->window, st->gc, drawX, drawY);
281 if ((m->xshadow != 0) || (m->yshadow != 0)) {
282 /* draw the corresponding shadow point */
283 XDrawPoint(st->dpy, st->window, st->gc, drawX + m->xshadow, drawY + m->yshadow);
285 /* XXX may want to change this to XDrawPoints,
286 but it's fast enough without it for the moment. */
291 if (m->t > m->doom) {
298 munch_init (Display *dpy, Window w)
300 struct state *st = (struct state *) calloc (1, sizeof(*st));
301 XWindowAttributes xgwa;
310 /* get the dimensions of the window */
311 XGetWindowAttributes(st->dpy, w, &xgwa);
314 gcv.foreground= get_pixel_resource(st->dpy, xgwa.colormap,
315 "foreground","Foreground");
316 gcv.background= get_pixel_resource(st->dpy, xgwa.colormap,
317 "background","Background");
319 st->gc = XCreateGC(st->dpy, w, GCForeground|GCBackground, &gcv);
321 st->delay = get_integer_resource(st->dpy, "delay", "Integer");
322 if (st->delay < 0) st->delay = 0;
324 st->simul = get_integer_resource(st->dpy, "simul", "Integer");
325 if (st->simul < 1) st->simul = 1;
327 st->clear = get_integer_resource(st->dpy, "clear", "Integer");
328 if (st->clear < 0) st->clear = 0;
330 st->xor = get_boolean_resource(st->dpy, "xor", "Boolean");
332 mm = get_string_resource (st->dpy, "mismunch", "Mismunch");
333 if (!mm || !*mm || !strcmp(mm, "random"))
334 st->mismunch = random() & 1;
336 st->mismunch = get_boolean_resource (st->dpy, "mismunch", "Mismunch");
338 st->window_width = xgwa.width;
339 st->window_height = xgwa.height;
343 /* always draw xor on mono. */
344 if (mono_p || st->xor) {
345 XSetFunction(st->dpy, st->gc, GXxor);
348 st->munchers = (muncher **) calloc(st->simul, sizeof(muncher *));
349 for (i = 0; i < st->simul; i++) {
350 st->munchers[i] = make_muncher(st);
357 munch_draw (Display *dpy, Window w, void *closure)
359 struct state *st = (struct state *) closure;
362 for (i = 0; i < 5; i++) {
364 /* for (draw_i = 0; draw_i < simul; draw_i++) */
366 munch(st, st->munchers[st->draw_i]);
368 if (st->munchers[st->draw_i]->done) {
371 free(st->munchers[st->draw_i]);
372 st->munchers[st->draw_i] = make_muncher(st);
377 if (st->draw_i >= st->simul) {
380 if (st->restart || (st->clear && st->draw_n >= st->clear)) {
382 char *mm = get_string_resource (st->dpy, "mismunch", "Mismunch");
383 if (!mm || !*mm || !strcmp(mm, "random"))
384 st->mismunch = random() & 1;
386 for (i = 0; i < st->simul; i++) {
387 free(st->munchers[i]);
388 st->munchers[i] = make_muncher(st);
391 XClearWindow(st->dpy, w);
404 munch_reshape (Display *dpy, Window window, void *closure,
405 unsigned int w, unsigned int h)
407 struct state *st = (struct state *) closure;
408 if (w != st->window_width ||
409 h != st->window_height) {
410 st->window_width = w;
411 st->window_height = h;
419 munch_event (Display *dpy, Window window, void *closure, XEvent *event)
421 struct state *st = (struct state *) closure;
422 if (screenhack_event_helper (dpy, window, event))
426 munch_reshape(dpy, window, closure, st->window_width, st->window_height);
427 st->mismunch = random() & 1;
428 for (i = 0; i < st->simul; i++) {
429 free (st->munchers[i]);
430 st->munchers[i] = make_muncher(st);
432 XClearWindow(dpy, window);
439 munch_free (Display *dpy, Window window, void *closure)
441 struct state *st = (struct state *) closure;
446 static const char *munch_defaults [] = {
448 ".background: black",
449 ".foreground: white",
457 "*ignoreRotation: True",
463 static XrmOptionDescRec munch_options [] = {
464 { "-delay", ".delay", XrmoptionSepArg, 0 },
465 { "-simul", ".simul", XrmoptionSepArg, 0 },
466 { "-clear", ".clear", XrmoptionSepArg, "true" },
467 { "-xor", ".xor", XrmoptionNoArg, "true" },
468 { "-no-xor", ".xor", XrmoptionNoArg, "false" },
469 { "-classic", ".mismunch", XrmoptionNoArg, "false" },
470 { "-mismunch", ".mismunch", XrmoptionNoArg, "true" },
471 { "-random", ".mismunch", XrmoptionNoArg, "random" },
476 XSCREENSAVER_MODULE ("Munch", munch)