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->logmaxwidth = (int)dumb_log_2(st->window_height * 0.8);
133 st->logmaxwidth = (int)dumb_log_2(st->window_width * 0.8);
136 if (st->logmaxwidth < 2) {
140 /* we always want three sizes of squares */
141 st->logminwidth = st->logmaxwidth - 2;
143 if (st->logminwidth < 2) {
150 static muncher *make_muncher (struct state *st)
153 XWindowAttributes xgwa;
154 muncher *m = (muncher *) malloc(sizeof(muncher));
156 XGetWindowAttributes(st->dpy, st->window, &xgwa);
158 m->mismunch = st->mismunch;
160 /* choose size -- power of two */
161 logwidth = (st->logminwidth +
162 (random() % (1 + st->logmaxwidth - st->logminwidth)));
164 m->width = 1 << logwidth;
166 /* draw at this location */
167 m->atX = (random() % (xgwa.width <= m->width ? 1
168 : xgwa.width - m->width));
169 m->atY = (random() % (xgwa.height <= m->width ? 1
170 : xgwa.width - m->width));
172 /* wrap-around by these values; no need to % as we end up doing that
174 m->kX = ((random() % 2)
175 ? (random() % m->width) : 0);
176 m->kT = ((random() % 2)
177 ? (random() % m->width) : 0);
178 m->kY = ((random() % 2)
179 ? (random() % m->width) : 0);
181 /* set the gravity of the munch, or rather, which direction we draw
183 m->grav = random() % 2;
185 /* I like this color scheme better than random colors. */
186 switch (random() % 4) {
188 m->fgc.red = random() % 65536;
189 m->fgc.blue = random() % 32768;
190 m->fgc.green = random() % 16384;
195 m->fgc.blue = random() % 65536;
196 m->fgc.green = random() % 16384;
200 m->fgc.red = random() % 8192;
201 m->fgc.blue = random() % 8192;
202 m->fgc.green = random() % 49152;
206 m->fgc.red = random() % 65536;
207 m->fgc.green = m->fgc.red;
208 m->fgc.blue = m->fgc.red;
212 /* Sometimes draw a mostly-overlapping copy of the square. This
213 generates all kinds of neat blocky graphics when drawing in xor
215 if (!m->mismunch || (random() % 4)) {
219 m->xshadow = (random() % (m->width/3)) - (m->width/6);
220 m->yshadow = (random() % (m->width/3)) - (m->width/6);
223 /* Start with a random y value -- this sort of controls the type of
224 deformities seen in the squares. */
225 m->y = random() % 256;
230 Doom each square to be aborted at some random point.
231 (When doom == (width - 1), the entire square will be drawn.)
233 m->doom = (m->mismunch ? (random() % m->width) : (m->width - 1));
240 static void munch (struct state *st, muncher *m)
243 XWindowAttributes xgwa;
249 XGetWindowAttributes(st->dpy, st->window, &xgwa);
252 /* XXX there are probably bugs with this. */
253 if (XAllocColor(st->dpy, xgwa.colormap, &m->fgc)) {
254 XSetForeground(st->dpy, st->gc, m->fgc.pixel);
258 /* Finally draw this pass of the munching error. */
260 for(m->x = 0; m->x < m->width; m->x++) {
261 /* figure out the next point */
264 The ordinary Munching Squares calculation is:
265 m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width;
267 We create some feedback by plugging in y in place of x, and
268 make a couple of values negative so that some parts of some
269 squares get drawn in the wrong place.
272 m->y = ((-m->y ^ ((-m->t + m->kT) % m->width)) + m->kY) % m->width;
274 m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width;
276 drawX = ((m->x + m->kX) % m->width) + m->atX;
277 drawY = (m->grav ? m->y + m->atY : m->atY + m->width - 1 - m->y);
279 XDrawPoint(st->dpy, st->window, st->gc, drawX, drawY);
280 if ((m->xshadow != 0) || (m->yshadow != 0)) {
281 /* draw the corresponding shadow point */
282 XDrawPoint(st->dpy, st->window, st->gc, drawX + m->xshadow, drawY + m->yshadow);
284 /* XXX may want to change this to XDrawPoints,
285 but it's fast enough without it for the moment. */
290 if (m->t > m->doom) {
297 munch_init (Display *dpy, Window w)
299 struct state *st = (struct state *) calloc (1, sizeof(*st));
300 XWindowAttributes xgwa;
309 /* get the dimensions of the window */
310 XGetWindowAttributes(st->dpy, w, &xgwa);
313 gcv.foreground= get_pixel_resource(st->dpy, xgwa.colormap,
314 "foreground","Foreground");
315 gcv.background= get_pixel_resource(st->dpy, xgwa.colormap,
316 "background","Background");
318 st->gc = XCreateGC(st->dpy, w, GCForeground|GCBackground, &gcv);
320 st->delay = get_integer_resource(st->dpy, "delay", "Integer");
321 if (st->delay < 0) st->delay = 0;
323 st->simul = get_integer_resource(st->dpy, "simul", "Integer");
324 if (st->simul < 1) st->simul = 1;
326 st->clear = get_integer_resource(st->dpy, "clear", "Integer");
327 if (st->clear < 0) st->clear = 0;
329 st->xor = get_boolean_resource(st->dpy, "xor", "Boolean");
331 mm = get_string_resource (st->dpy, "mismunch", "Mismunch");
332 if (!mm || !*mm || !strcmp(mm, "random"))
333 st->mismunch = random() & 1;
335 st->mismunch = get_boolean_resource (st->dpy, "mismunch", "Mismunch");
337 st->window_width = xgwa.width;
338 st->window_height = xgwa.height;
342 /* always draw xor on mono. */
343 if (mono_p || st->xor) {
344 XSetFunction(st->dpy, st->gc, GXxor);
347 st->munchers = (muncher **) calloc(st->simul, sizeof(muncher *));
348 for (i = 0; i < st->simul; i++) {
349 st->munchers[i] = make_muncher(st);
356 munch_draw (Display *dpy, Window w, void *closure)
358 struct state *st = (struct state *) closure;
361 for (i = 0; i < 5; i++) {
363 /* for (draw_i = 0; draw_i < simul; draw_i++) */
365 munch(st, st->munchers[st->draw_i]);
367 if (st->munchers[st->draw_i]->done) {
370 free(st->munchers[st->draw_i]);
371 st->munchers[st->draw_i] = make_muncher(st);
376 if (st->draw_i >= st->simul) {
379 if (st->restart || (st->clear && st->draw_n >= st->clear)) {
381 char *mm = get_string_resource (st->dpy, "mismunch", "Mismunch");
382 if (!mm || !*mm || !strcmp(mm, "random"))
383 st->mismunch = random() & 1;
385 for (i = 0; i < st->simul; i++) {
386 free(st->munchers[i]);
387 st->munchers[i] = make_muncher(st);
390 XClearWindow(st->dpy, w);
403 munch_reshape (Display *dpy, Window window, void *closure,
404 unsigned int w, unsigned int h)
406 struct state *st = (struct state *) closure;
407 if (w != st->window_width ||
408 h != st->window_height) {
409 st->window_width = w;
410 st->window_height = h;
418 munch_event (Display *dpy, Window window, void *closure, XEvent *event)
420 struct state *st = (struct state *) closure;
421 if (screenhack_event_helper (dpy, window, event))
425 munch_reshape(dpy, window, closure, st->window_width, st->window_height);
426 st->mismunch = random() & 1;
427 for (i = 0; i < st->simul; i++) {
428 free (st->munchers[i]);
429 st->munchers[i] = make_muncher(st);
431 XClearWindow(dpy, window);
438 munch_free (Display *dpy, Window window, void *closure)
440 struct state *st = (struct state *) closure;
445 static const char *munch_defaults [] = {
446 ".background: black",
447 ".foreground: white",
455 "*ignoreRotation: True",
460 static XrmOptionDescRec munch_options [] = {
461 { "-delay", ".delay", XrmoptionSepArg, 0 },
462 { "-simul", ".simul", XrmoptionSepArg, 0 },
463 { "-clear", ".clear", XrmoptionSepArg, "true" },
464 { "-xor", ".xor", XrmoptionNoArg, "true" },
465 { "-no-xor", ".xor", XrmoptionNoArg, "false" },
466 { "-classic", ".mismunch", XrmoptionNoArg, "false" },
467 { "-mismunch", ".mismunch", XrmoptionNoArg, "true" },
468 { "-random", ".mismunch", XrmoptionNoArg, "random" },
473 XSCREENSAVER_MODULE ("Munch", munch)