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 char *progclass = "Mismunch";
37 XrmOptionDescRec options [] = {
38 { "-delay", ".delay", XrmoptionSepArg, 0 },
39 { "-simul", ".simul", XrmoptionSepArg, 0 },
40 { "-clear", ".clear", XrmoptionSepArg, "true" },
41 { "-xor", ".xor", XrmoptionNoArg, "true" },
42 { "-no-xor", ".xor", XrmoptionNoArg, "false" },
48 static int delay, simul, clear, xor;
49 static int logminwidth, logmaxwidth;
50 static int restart, window_width, window_height;
52 typedef struct _muncher {
66 * dumb way to get # of digits in number. Probably faster than actually
67 * doing a log and a division, maybe.
69 static int dumb_log_2(int k) {
78 void calc_logwidths (void) {
79 /* Choose a range of square sizes based on the window size. We want
80 a power of 2 for the square width or the munch doesn't fill up.
81 Also, if a square doesn't fit inside an area 20% smaller than the
82 window, it's too big. Mismunched squares that big make things
85 if (window_height < window_width) {
86 logmaxwidth = (int)dumb_log_2(window_height * 0.8);
88 logmaxwidth = (int)dumb_log_2(window_width * 0.8);
91 if (logmaxwidth < 2) {
95 /* we always want three sizes of squares */
96 logminwidth = logmaxwidth - 2;
98 if (logminwidth < 2) {
104 void mismunch_handle_events (Display *dpy) {
107 while (XPending(dpy)) {
111 case ConfigureNotify:
112 if (e.xconfigure.width != window_width ||
113 e.xconfigure.height != window_height) {
114 window_width = e.xconfigure.width;
115 window_height = e.xconfigure.height;
122 screenhack_handle_event(dpy, &e);
128 static muncher *make_muncher (Display *dpy, Window w) {
130 XWindowAttributes xgwa;
131 muncher *m = (muncher *) malloc(sizeof(muncher));
133 XGetWindowAttributes(dpy, w, &xgwa);
135 /* choose size -- power of two */
136 logwidth = (logminwidth +
137 (random() % (1 + logmaxwidth - logminwidth)));
139 m->width = 1 << logwidth;
141 /* draw at this location */
142 m->atX = (random() % (xgwa.width <= m->width ? 1
143 : xgwa.width - m->width));
144 m->atY = (random() % (xgwa.height <= m->width ? 1
145 : xgwa.width - m->width));
147 /* wrap-around by these values; no need to % as we end up doing that
149 m->kX = ((random() % 2)
150 ? (random() % m->width) : 0);
151 m->kT = ((random() % 2)
152 ? (random() % m->width) : 0);
153 m->kY = ((random() % 2)
154 ? (random() % m->width) : 0);
156 /* set the gravity of the munch, or rather, which direction we draw
158 m->grav = random() % 2;
160 /* I like this color scheme better than random colors. */
161 switch (random() % 4) {
163 m->fgc.red = random() % 65536;
164 m->fgc.blue = random() % 32768;
165 m->fgc.green = random() % 16384;
170 m->fgc.blue = random() % 65536;
171 m->fgc.green = random() % 16384;
175 m->fgc.red = random() % 8192;
176 m->fgc.blue = random() % 8192;
177 m->fgc.green = random() % 49152;
181 m->fgc.red = random() % 65536;
182 m->fgc.green = m->fgc.red;
183 m->fgc.blue = m->fgc.red;
187 /* Sometimes draw a mostly-overlapping copy of the square. This
188 generates all kinds of neat blocky graphics when drawing in xor
194 m->xshadow = (random() % (m->width/3)) - (m->width/6);
195 m->yshadow = (random() % (m->width/3)) - (m->width/6);
198 /* Start with a random y value -- this sort of controls the type of
199 deformities seen in the squares. */
200 m->y = random() % 256;
205 Doom each square to be aborted at some random point.
206 (When doom == (width - 1), the entire square will be drawn.)
208 m->doom = random() % m->width;
216 static void munch (Display *dpy, Window w, muncher *m) {
218 XWindowAttributes xgwa;
219 static Colormap cmap;
225 XGetWindowAttributes(dpy, w, &xgwa);
228 /* XXX there are probably bugs with this. */
229 cmap = xgwa.colormap;
231 if (XAllocColor(dpy, cmap, &m->fgc)) {
232 XSetForeground(dpy, gc, m->fgc.pixel);
236 /* Finally draw this pass of the munching error. */
238 for(m->x = 0; m->x < m->width; m->x++) {
239 /* figure out the next point */
242 The ordinary Munching Squares calculation is:
243 m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width;
245 We create some feedback by plugging in y in place of x, and
246 make a couple of values negative so that some parts of some
247 squares get drawn in the wrong place.
249 m->y = ((-m->y ^ ((-m->t + m->kT) % m->width)) + m->kY) % m->width;
251 drawX = ((m->x + m->kX) % m->width) + m->atX;
252 drawY = (m->grav ? m->y + m->atY : m->atY + m->width - 1 - m->y);
254 XDrawPoint(dpy, w, gc, drawX, drawY);
255 if ((m->xshadow != 0) || (m->yshadow != 0)) {
256 /* draw the corresponding shadow point */
257 XDrawPoint(dpy, w, gc, drawX + m->xshadow, drawY + m->yshadow);
259 /* XXX may want to change this to XDrawPoints,
260 but it's fast enough without it for the moment. */
265 we've drawn one pass' worth of points. let the server catch up or
266 this won't be interesting to watch at all. we call this here in
267 the hopes of having one whole sequence of dots appear at the same
268 time (one for each value of x, surprisingly enough)
271 mismunch_handle_events(dpy);
273 if (delay) usleep(delay);
276 if (m->t > m->doom) {
285 void screenhack (Display *dpy, Window w)
287 XWindowAttributes xgwa;
290 int n = 0; /* number of squares before we have to clear */
296 /* get the dimensions of the window */
297 XGetWindowAttributes(dpy, w, &xgwa);
300 cmap = xgwa.colormap;
301 gcv.foreground= get_pixel_resource("foreground","Foreground",
303 gcv.background= get_pixel_resource("background","Background",
306 gc = XCreateGC(dpy, w, GCForeground|GCBackground, &gcv);
308 delay = get_integer_resource("delay", "Integer");
309 if (delay < 0) delay = 0;
311 simul = get_integer_resource("simul", "Integer");
312 if (simul < 1) simul = 1;
314 clear = get_integer_resource("clear", "Integer");
315 if (clear < 0) clear = 0;
317 xor = get_boolean_resource("xor", "Boolean");
319 window_width = xgwa.width;
320 window_height = xgwa.height;
324 /* always draw xor on mono. */
326 XSetFunction(dpy, gc, GXxor);
329 munchers = (muncher **) calloc(simul, sizeof(muncher *));
330 for (i = 0; i < simul; i++) {
331 munchers[i] = make_muncher(dpy, w);
335 for (i = 0; i < simul; i++) {
336 munch(dpy, w, munchers[i]);
338 if (munchers[i]->done) {
342 munchers[i] = make_muncher(dpy, w);
344 mismunch_handle_events(dpy);
348 if (restart || (clear && n >= clear)) {
349 for (i = 0; i < simul; i++) {
351 munchers[i] = make_muncher(dpy, w);
354 XClearWindow(dpy, w);