3 * Copyright (c) 2004 Steven Hazel <sah@thalassocracy.org>
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation. No representations are made about the suitability of this
10 * software for any purpose. It is provided "as is" without express or
14 * A munching squares implementation for X
15 * Copyright 1997, Tim Showalter <tjs@andrew.cmu.edu>
17 * Some code stolen from / This is meant to work with
18 * xscreensaver, Copyright (c) 1992, 1995, 1996 Jamie Zawinski <jwz@jwz.org>
23 #include "screenhack.h"
25 typedef struct _muncher {
43 int delay, simul, clear, xor;
44 int logminwidth, logmaxwidth;
45 int restart, window_width, window_height;
47 int draw_n; /* number of squares before we have to clear */
55 * dumb way to get # of digits in number. Probably faster than actually
56 * doing a log and a division, maybe.
58 static int dumb_log_2(int k)
68 static void calc_logwidths (struct state *st)
70 /* Choose a range of square sizes based on the window size. We want
71 a power of 2 for the square width or the munch doesn't fill up.
72 Also, if a square doesn't fit inside an area 20% smaller than the
73 window, it's too big. Mismunched squares that big make things
76 if (st->window_height < st->window_width) {
77 st->logmaxwidth = (int)dumb_log_2(st->window_height * 0.8);
79 st->logmaxwidth = (int)dumb_log_2(st->window_width * 0.8);
82 if (st->logmaxwidth < 2) {
86 /* we always want three sizes of squares */
87 st->logminwidth = st->logmaxwidth - 2;
89 if (st->logminwidth < 2) {
96 static muncher *make_muncher (struct state *st)
99 XWindowAttributes xgwa;
100 muncher *m = (muncher *) malloc(sizeof(muncher));
102 XGetWindowAttributes(st->dpy, st->window, &xgwa);
104 /* choose size -- power of two */
105 logwidth = (st->logminwidth +
106 (random() % (1 + st->logmaxwidth - st->logminwidth)));
108 m->width = 1 << logwidth;
110 /* draw at this location */
111 m->atX = (random() % (xgwa.width <= m->width ? 1
112 : xgwa.width - m->width));
113 m->atY = (random() % (xgwa.height <= m->width ? 1
114 : xgwa.width - m->width));
116 /* wrap-around by these values; no need to % as we end up doing that
118 m->kX = ((random() % 2)
119 ? (random() % m->width) : 0);
120 m->kT = ((random() % 2)
121 ? (random() % m->width) : 0);
122 m->kY = ((random() % 2)
123 ? (random() % m->width) : 0);
125 /* set the gravity of the munch, or rather, which direction we draw
127 m->grav = random() % 2;
129 /* I like this color scheme better than random colors. */
130 switch (random() % 4) {
132 m->fgc.red = random() % 65536;
133 m->fgc.blue = random() % 32768;
134 m->fgc.green = random() % 16384;
139 m->fgc.blue = random() % 65536;
140 m->fgc.green = random() % 16384;
144 m->fgc.red = random() % 8192;
145 m->fgc.blue = random() % 8192;
146 m->fgc.green = random() % 49152;
150 m->fgc.red = random() % 65536;
151 m->fgc.green = m->fgc.red;
152 m->fgc.blue = m->fgc.red;
156 /* Sometimes draw a mostly-overlapping copy of the square. This
157 generates all kinds of neat blocky graphics when drawing in xor
163 m->xshadow = (random() % (m->width/3)) - (m->width/6);
164 m->yshadow = (random() % (m->width/3)) - (m->width/6);
167 /* Start with a random y value -- this sort of controls the type of
168 deformities seen in the squares. */
169 m->y = random() % 256;
174 Doom each square to be aborted at some random point.
175 (When doom == (width - 1), the entire square will be drawn.)
177 m->doom = random() % m->width;
185 static void munch (struct state *st, muncher *m)
188 XWindowAttributes xgwa;
194 XGetWindowAttributes(st->dpy, st->window, &xgwa);
197 /* XXX there are probably bugs with this. */
198 if (XAllocColor(st->dpy, xgwa.colormap, &m->fgc)) {
199 XSetForeground(st->dpy, st->gc, m->fgc.pixel);
203 /* Finally draw this pass of the munching error. */
205 for(m->x = 0; m->x < m->width; m->x++) {
206 /* figure out the next point */
209 The ordinary Munching Squares calculation is:
210 m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width;
212 We create some feedback by plugging in y in place of x, and
213 make a couple of values negative so that some parts of some
214 squares get drawn in the wrong place.
216 m->y = ((-m->y ^ ((-m->t + m->kT) % m->width)) + m->kY) % m->width;
218 drawX = ((m->x + m->kX) % m->width) + m->atX;
219 drawY = (m->grav ? m->y + m->atY : m->atY + m->width - 1 - m->y);
221 XDrawPoint(st->dpy, st->window, st->gc, drawX, drawY);
222 if ((m->xshadow != 0) || (m->yshadow != 0)) {
223 /* draw the corresponding shadow point */
224 XDrawPoint(st->dpy, st->window, st->gc, drawX + m->xshadow, drawY + m->yshadow);
226 /* XXX may want to change this to XDrawPoints,
227 but it's fast enough without it for the moment. */
232 if (m->t > m->doom) {
239 mismunch_init (Display *dpy, Window w)
241 struct state *st = (struct state *) calloc (1, sizeof(*st));
242 XWindowAttributes xgwa;
250 /* get the dimensions of the window */
251 XGetWindowAttributes(st->dpy, w, &xgwa);
254 gcv.foreground= get_pixel_resource(st->dpy, xgwa.colormap,
255 "foreground","Foreground");
256 gcv.background= get_pixel_resource(st->dpy, xgwa.colormap,
257 "background","Background");
259 st->gc = XCreateGC(st->dpy, w, GCForeground|GCBackground, &gcv);
261 st->delay = get_integer_resource(st->dpy, "delay", "Integer");
262 if (st->delay < 0) st->delay = 0;
264 st->simul = get_integer_resource(st->dpy, "simul", "Integer");
265 if (st->simul < 1) st->simul = 1;
267 st->clear = get_integer_resource(st->dpy, "clear", "Integer");
268 if (st->clear < 0) st->clear = 0;
270 st->xor = get_boolean_resource(st->dpy, "xor", "Boolean");
272 st->window_width = xgwa.width;
273 st->window_height = xgwa.height;
277 /* always draw xor on mono. */
278 if (mono_p || st->xor) {
279 XSetFunction(st->dpy, st->gc, GXxor);
282 st->munchers = (muncher **) calloc(st->simul, sizeof(muncher *));
283 for (i = 0; i < st->simul; i++) {
284 st->munchers[i] = make_muncher(st);
291 mismunch_draw (Display *dpy, Window w, void *closure)
293 struct state *st = (struct state *) closure;
295 /* for (draw_i = 0; draw_i < simul; draw_i++) */
297 munch(st, st->munchers[st->draw_i]);
299 if (st->munchers[st->draw_i]->done) {
302 free(st->munchers[st->draw_i]);
303 st->munchers[st->draw_i] = make_muncher(st);
308 if (st->draw_i >= st->simul) {
311 if (st->restart || (st->clear && st->draw_n >= st->clear)) {
312 for (i = 0; i < st->simul; i++) {
313 free(st->munchers[i]);
314 st->munchers[i] = make_muncher(st);
317 XClearWindow(st->dpy, w);
328 mismunch_reshape (Display *dpy, Window window, void *closure,
329 unsigned int w, unsigned int h)
331 struct state *st = (struct state *) closure;
332 if (w != st->window_width ||
333 h != st->window_height) {
334 st->window_width = w;
335 st->window_height = h;
343 mismunch_event (Display *dpy, Window window, void *closure, XEvent *event)
349 mismunch_free (Display *dpy, Window window, void *closure)
351 struct state *st = (struct state *) closure;
356 static const char *mismunch_defaults [] = {
357 ".background: black",
358 ".foreground: white",
366 static XrmOptionDescRec mismunch_options [] = {
367 { "-delay", ".delay", XrmoptionSepArg, 0 },
368 { "-simul", ".simul", XrmoptionSepArg, 0 },
369 { "-clear", ".clear", XrmoptionSepArg, "true" },
370 { "-xor", ".xor", XrmoptionNoArg, "true" },
371 { "-no-xor", ".xor", XrmoptionNoArg, "false" },
376 XSCREENSAVER_MODULE ("Mismunch", mismunch)