http://slackware.bholcomb.com/slackware/slackware-11.0/source/xap/xscreensaver/xscree...
[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 typedef struct _muncher {
26   int width;
27   int atX, atY;
28   int kX, kT, kY;
29   int grav;
30   XColor fgc;
31   int yshadow, xshadow;
32   int x, y, t;
33   int doom;
34   int done;
35 } muncher;
36
37
38 struct state {
39   Display *dpy;
40   Window window;
41
42   GC gc;
43   int delay, simul, clear, xor;
44   int logminwidth, logmaxwidth;
45   int restart, window_width, window_height;
46
47   int draw_n;  /* number of squares before we have to clear */
48   int draw_i;
49
50   muncher **munchers;
51 };
52
53
54 /*
55  * dumb way to get # of digits in number.  Probably faster than actually
56  * doing a log and a division, maybe.
57  */
58 static int dumb_log_2(int k) 
59 {
60   int r = -1;
61   while (k > 0) {
62     k >>= 1; r++;
63   }
64   return r;
65 }
66
67
68 static void calc_logwidths (struct state *st) 
69 {
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
74      look too noisy. */
75
76   if (st->window_height < st->window_width) {
77     st->logmaxwidth = (int)dumb_log_2(st->window_height * 0.8);
78   } else {
79     st->logmaxwidth = (int)dumb_log_2(st->window_width * 0.8);
80   }
81
82   if (st->logmaxwidth < 2) {
83     st->logmaxwidth = 2;
84   }
85
86   /* we always want three sizes of squares */
87   st->logminwidth = st->logmaxwidth - 2;
88
89   if (st->logminwidth < 2) {
90     st->logminwidth = 2;
91   }
92 }
93
94
95
96 static muncher *make_muncher (struct state *st) 
97 {
98   int logwidth;
99   XWindowAttributes xgwa;
100   muncher *m = (muncher *) malloc(sizeof(muncher));
101
102   XGetWindowAttributes(st->dpy, st->window, &xgwa);
103
104   /* choose size -- power of two */
105   logwidth = (st->logminwidth +
106               (random() % (1 + st->logmaxwidth - st->logminwidth)));
107
108   m->width = 1 << logwidth;
109
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));
115
116   /* wrap-around by these values; no need to % as we end up doing that
117      later anyway */
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);
124
125   /* set the gravity of the munch, or rather, which direction we draw
126      stuff in. */
127   m->grav = random() % 2;
128
129   /* I like this color scheme better than random colors. */
130   switch (random() % 4) {
131     case 0:
132       m->fgc.red = random() % 65536;
133       m->fgc.blue = random() % 32768;
134       m->fgc.green = random() % 16384;
135       break;
136
137     case 1:
138       m->fgc.red = 0;
139       m->fgc.blue = random() % 65536;
140       m->fgc.green = random() % 16384;
141       break;
142
143     case 2:
144       m->fgc.red = random() % 8192;
145       m->fgc.blue = random() % 8192;
146       m->fgc.green = random() % 49152;
147       break;
148
149     case 3:
150       m->fgc.red = random() % 65536;
151       m->fgc.green = m->fgc.red;
152       m->fgc.blue = m->fgc.red;
153       break;
154   }
155
156   /* Sometimes draw a mostly-overlapping copy of the square.  This
157      generates all kinds of neat blocky graphics when drawing in xor
158      mode. */
159   if (random() % 4) {
160     m->xshadow = 0;
161     m->yshadow = 0;
162   } else {
163     m->xshadow = (random() % (m->width/3)) - (m->width/6);
164     m->yshadow = (random() % (m->width/3)) - (m->width/6);
165   }
166
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;
170
171   m->t = 0;
172
173   /*
174     Doom each square to be aborted at some random point.
175     (When doom == (width - 1), the entire square will be drawn.)
176   */
177   m->doom = random() % m->width;
178
179   m->done = 0;
180
181   return m;
182 }
183
184
185 static void munch (struct state *st, muncher *m) 
186 {
187   int drawX, drawY;
188   XWindowAttributes xgwa;
189
190   if (m->done) {
191     return;
192   }
193
194   XGetWindowAttributes(st->dpy, st->window, &xgwa);
195
196   if (!mono_p) {
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);
200     }
201   }
202
203   /* Finally draw this pass of the munching error. */
204
205   for(m->x = 0; m->x < m->width; m->x++) {
206     /* figure out the next point */
207
208     /*
209       The ordinary Munching Squares calculation is:
210       m->y = ((m->x ^ ((m->t + m->kT) % m->width)) + m->kY) % m->width;
211
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.
215     */
216     m->y = ((-m->y ^ ((-m->t + m->kT) % m->width)) + m->kY) % m->width;
217
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);
220
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);
225     }
226     /* XXX may want to change this to XDrawPoints,
227        but it's fast enough without it for the moment. */
228
229   }
230
231   m->t++;
232   if (m->t > m->doom) {
233     m->done = 1;
234   }
235 }
236
237
238 static void *
239 mismunch_init (Display *dpy, Window w)
240 {
241   struct state *st = (struct state *) calloc (1, sizeof(*st));
242   XWindowAttributes xgwa;
243   XGCValues gcv;
244   int i;
245
246   st->dpy = dpy;
247   st->window = w;
248   st->restart = 0;
249
250   /* get the dimensions of the window */
251   XGetWindowAttributes(st->dpy, w, &xgwa);
252
253   /* create the gc */
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");
258
259   st->gc = XCreateGC(st->dpy, w, GCForeground|GCBackground, &gcv);
260
261   st->delay = get_integer_resource(st->dpy, "delay", "Integer");
262   if (st->delay < 0) st->delay = 0;
263
264   st->simul = get_integer_resource(st->dpy, "simul", "Integer");
265   if (st->simul < 1) st->simul = 1;
266
267   st->clear = get_integer_resource(st->dpy, "clear", "Integer");
268   if (st->clear < 0) st->clear = 0;
269
270   st->xor = get_boolean_resource(st->dpy, "xor", "Boolean");
271
272   st->window_width = xgwa.width;
273   st->window_height = xgwa.height;
274
275   calc_logwidths(st);
276
277   /* always draw xor on mono. */
278   if (mono_p || st->xor) {
279     XSetFunction(st->dpy, st->gc, GXxor);
280   }
281
282   st->munchers = (muncher **) calloc(st->simul, sizeof(muncher *));
283   for (i = 0; i < st->simul; i++) {
284     st->munchers[i] = make_muncher(st);
285   }
286
287   return st;
288 }
289
290 static unsigned long
291 mismunch_draw (Display *dpy, Window w, void *closure)
292 {
293   struct state *st = (struct state *) closure;
294
295   /* for (draw_i = 0; draw_i < simul; draw_i++)  */
296   {
297     munch(st, st->munchers[st->draw_i]);
298
299     if (st->munchers[st->draw_i]->done) {
300       st->draw_n++;
301
302       free(st->munchers[st->draw_i]);
303       st->munchers[st->draw_i] = make_muncher(st);
304     }
305   }
306
307   st->draw_i++;
308   if (st->draw_i >= st->simul) {
309     int i = 0;
310     st->draw_i = 0;
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);
315       }
316
317       XClearWindow(st->dpy, w);
318       st->draw_n = 0;
319       st->restart = 0;
320     }
321   }
322
323   return st->delay;
324 }
325
326
327 static void
328 mismunch_reshape (Display *dpy, Window window, void *closure, 
329                  unsigned int w, unsigned int h)
330 {
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;
336     calc_logwidths(st);
337     st->restart = 1;
338     st->draw_i = 0;
339   }
340 }
341
342 static Bool
343 mismunch_event (Display *dpy, Window window, void *closure, XEvent *event)
344 {
345   return False;
346 }
347
348 static void
349 mismunch_free (Display *dpy, Window window, void *closure)
350 {
351   struct state *st = (struct state *) closure;
352   free (st);
353 }
354
355
356 static const char *mismunch_defaults [] = {
357   ".background:       black",
358   ".foreground:       white",
359   "*delay:            2500",
360   "*simul:            5",
361   "*clear:            65",
362   "*xor:              True",
363   0
364 };
365
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" },
372   { 0, 0, 0, 0 }
373 };
374
375
376 XSCREENSAVER_MODULE ("Mismunch", mismunch)