ftp://ftp.jp.xemacs.org/pub/NetBSD/packages/distfiles/xscreensaver-4.15.tar.gz
[xscreensaver] / hacks / mismunch.c
1 /* mismunch.c
2  * Munch Errors
3  * Copyright (c) 2004 Steven Hazel <sah@thalassocracy.org>
4  *
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
11  * implied warranty.
12  *
13  * Based on munch.c
14  * A munching squares implementation for X
15  * Copyright 1997, Tim Showalter <tjs@andrew.cmu.edu>
16  *
17  * Some code stolen from / This is meant to work with
18  * xscreensaver, Copyright (c) 1992, 1995, 1996 Jamie Zawinski <jwz@jwz.org>
19  *
20  */
21
22 #include <math.h>
23 #include "screenhack.h"
24
25 char *progclass = "Mismunch";
26
27 char *defaults [] = {
28   ".background:       black",
29   ".foreground:       white",
30   "*delay:            2500",
31   "*simul:            5",
32   "*clear:            65",
33   "*xor:              True",
34   0
35 };
36
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" },
43   { 0, 0, 0, 0 }
44 };
45
46
47 static GC gc;
48 static int delay, simul, clear, xor;
49 static int logminwidth, logmaxwidth;
50 static int restart, window_width, window_height;
51
52 typedef struct _muncher {
53   int width;
54   int atX, atY;
55   int kX, kT, kY;
56   int grav;
57   XColor fgc;
58   int yshadow, xshadow;
59   int x, y, t;
60   int doom;
61   int done;
62 } muncher;
63
64
65 /*
66  * dumb way to get # of digits in number.  Probably faster than actually
67  * doing a log and a division, maybe.
68  */
69 static int dumb_log_2(int k) {
70   int r = -1;
71   while (k > 0) {
72     k >>= 1; r++;
73   }
74   return r;
75 }
76
77
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
83      look too noisy. */
84
85   if (window_height < window_width) {
86     logmaxwidth = (int)dumb_log_2(window_height * 0.8);
87   } else {
88     logmaxwidth = (int)dumb_log_2(window_width * 0.8);
89   }
90
91   if (logmaxwidth < 2) {
92     logmaxwidth = 2;
93   }
94
95   /* we always want three sizes of squares */
96   logminwidth = logmaxwidth - 2;
97
98   if (logminwidth < 2) {
99     logminwidth = 2;
100   }
101 }
102
103
104 void mismunch_handle_events (Display *dpy) {
105   XEvent e;
106
107   while (XPending(dpy)) {
108     XNextEvent(dpy, &e);
109
110     switch (e.type) {
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;
116           calc_logwidths();
117           restart = 1;
118         }
119         break;
120
121       default:
122         screenhack_handle_event(dpy, &e);
123     }
124   }
125 }
126
127
128 static muncher *make_muncher (Display *dpy, Window w) {
129   int logwidth;
130   XWindowAttributes xgwa;
131   muncher *m = (muncher *) malloc(sizeof(muncher));
132
133   XGetWindowAttributes(dpy, w, &xgwa);
134
135   /* choose size -- power of two */
136   logwidth = (logminwidth +
137               (random() % (1 + logmaxwidth - logminwidth)));
138
139   m->width = 1 << logwidth;
140
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));
146
147   /* wrap-around by these values; no need to % as we end up doing that
148      later anyway */
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);
155
156   /* set the gravity of the munch, or rather, which direction we draw
157      stuff in. */
158   m->grav = random() % 2;
159
160   /* I like this color scheme better than random colors. */
161   switch (random() % 4) {
162     case 0:
163       m->fgc.red = random() % 65536;
164       m->fgc.blue = random() % 32768;
165       m->fgc.green = random() % 16384;
166       break;
167
168     case 1:
169       m->fgc.red = 0;
170       m->fgc.blue = random() % 65536;
171       m->fgc.green = random() % 16384;
172       break;
173
174     case 2:
175       m->fgc.red = random() % 8192;
176       m->fgc.blue = random() % 8192;
177       m->fgc.green = random() % 49152;
178       break;
179
180     case 3:
181       m->fgc.red = random() % 65536;
182       m->fgc.green = m->fgc.red;
183       m->fgc.blue = m->fgc.red;
184       break;
185   }
186
187   /* Sometimes draw a mostly-overlapping copy of the square.  This
188      generates all kinds of neat blocky graphics when drawing in xor
189      mode. */
190   if (random() % 4) {
191     m->xshadow = 0;
192     m->yshadow = 0;
193   } else {
194     m->xshadow = (random() % (m->width/3)) - (m->width/6);
195     m->yshadow = (random() % (m->width/3)) - (m->width/6);
196   }
197
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;
201
202   m->t = 0;
203
204   /*
205     Doom each square to be aborted at some random point.
206     (When doom == (width - 1), the entire square will be drawn.)
207   */
208   m->doom = random() % m->width;
209
210   m->done = 0;
211
212   return m;
213 }
214
215
216 static void munch (Display *dpy, Window w, muncher *m) {
217   int drawX, drawY;
218   XWindowAttributes xgwa;
219   static Colormap cmap;
220
221   if (m->done) {
222     return;
223   }
224
225   XGetWindowAttributes(dpy, w, &xgwa);
226
227   if (!mono_p) {
228     /* XXX there are probably bugs with this. */
229     cmap = xgwa.colormap;
230
231     if (XAllocColor(dpy, cmap, &m->fgc)) {
232       XSetForeground(dpy, gc, m->fgc.pixel);
233     }
234   }
235
236   /* Finally draw this pass of the munching error. */
237
238   for(m->x = 0; m->x < m->width; m->x++) {
239     /* figure out the next point */
240
241     /*
242       The ordinary Munching Squares calculation is:
243       m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width;
244
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.
248     */
249     m->y = ((-m->y ^ ((-m->t + m->kT) % m->width)) + m->kY) % m->width;
250
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);
253
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);
258     }
259     /* XXX may want to change this to XDrawPoints,
260        but it's fast enough without it for the moment. */
261
262   }
263
264   /*
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)
269   */
270   XSync(dpy, False);
271   mismunch_handle_events(dpy);
272
273   if (delay) usleep(delay);
274
275   m->t++;
276   if (m->t > m->doom) {
277     m->done = 1;
278     return;
279   }
280
281   return;
282 }
283
284
285 void screenhack (Display *dpy, Window w)
286 {
287   XWindowAttributes xgwa;
288   Colormap cmap;
289   XGCValues gcv;
290   int n = 0;  /* number of squares before we have to clear */
291   muncher **munchers;
292   int i;
293
294   restart = 0;
295
296   /* get the dimensions of the window */
297   XGetWindowAttributes(dpy, w, &xgwa);
298
299   /* create the gc */
300   cmap = xgwa.colormap;
301   gcv.foreground= get_pixel_resource("foreground","Foreground",
302                                      dpy, cmap);
303   gcv.background= get_pixel_resource("background","Background",
304                                      dpy, cmap);
305
306   gc = XCreateGC(dpy, w, GCForeground|GCBackground, &gcv);
307
308   delay = get_integer_resource("delay", "Integer");
309   if (delay < 0) delay = 0;
310
311   simul = get_integer_resource("simul", "Integer");
312   if (simul < 1) simul = 1;
313
314   clear = get_integer_resource("clear", "Integer");
315   if (clear < 0) clear = 0;
316
317   xor = get_boolean_resource("xor", "Boolean");
318
319   window_width = xgwa.width;
320   window_height = xgwa.height;
321
322   calc_logwidths();
323
324   /* always draw xor on mono. */
325   if (mono_p || xor) {
326     XSetFunction(dpy, gc, GXxor);
327   }
328
329   munchers = (muncher **) calloc(simul, sizeof(muncher *));
330   for (i = 0; i < simul; i++) {
331     munchers[i] = make_muncher(dpy, w);
332   }
333
334   for(;;) {
335     for (i = 0; i < simul; i++) {
336       munch(dpy, w, munchers[i]);
337
338       if (munchers[i]->done) {
339         n++;
340
341         free(munchers[i]);
342         munchers[i] = make_muncher(dpy, w);
343
344         mismunch_handle_events(dpy);
345       }
346     }
347
348     if (restart || (clear && n >= clear)) {
349       for (i = 0; i < simul; i++) {
350         free(munchers[i]);
351         munchers[i] = make_muncher(dpy, w);
352       }
353
354       XClearWindow(dpy, w);
355       n = 0;
356       restart = 0;
357     }
358
359     XSync(dpy, False);
360   }
361 }