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