9ab053fa2066b37cb625ef37f07eb267faf0e876
[xscreensaver] / hacks / munch.c
1 /* munch.c
2  * A munching squares implementation for X
3  * Tim Showalter <tjs@andrew.cmu.edu>
4  * 
5  * Copyright 1997, Tim Showalter
6  * Permission is granted to copy, modify, and use this as long
7  * as this notice remains intact.  No warranties are expressed or implied.
8  * CMU Sucks.
9  * 
10  * Some code stolen from / This is meant to work with
11  * xscreensaver, Copyright (c) 1992, 1995, 1996
12  * Jamie Zawinski <jwz@jwz.org>
13  *
14  * Permission to use, copy, modify, distribute, and sell this software and its
15  * documentation for any purpose is hereby granted without fee, provided that
16  * the above copyright notice appear in all copies and that both that
17  * copyright notice and this permission notice appear in supporting
18  * documentation.  No representations are made about the suitability of this
19  * software for any purpose.  It is provided "as is" without express or 
20  * implied warranty.
21  */
22
23 /* Munching Squares is this simplistic, silly screen hack (according
24    to HAKMEM, discovered by Jackson Wright in 1962) where you take
25    Y = X XOR T and graph it over and over.  According to HAKMEM, it 
26    takes 5 instructions of PDP-1 assembly.  This is a little more
27    complicated than that, mostly X's fault, but it does some other
28    random things.
29
30    http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html#item146
31  */
32
33 #include <math.h>
34 /*#include <assert.h>*/
35 #include "screenhack.h"
36
37 /* flags for random things.  Must be < log2(random's maximum), incidentially.
38  */
39 #define SHIFT_KX (0x01)
40 #define SHIFT_KT (0x02)
41 #define SHIFT_KY (0x04)
42 #define GRAV     (0x08)
43
44
45 struct state {
46   Display *dpy;
47   Window window;
48
49   GC gc;
50   int delay, hold, clear, logminwidth, shiftk, xor;
51
52   int logmaxwidth;
53   int maxwidth;
54   int randflags;
55   int thiswidth;
56   XWindowAttributes xgwa;
57
58   int munch_t;
59
60   int reset;
61   int atX, atY, kX, kT, kY, grav;
62   int square_count;
63 };
64
65
66 static int munchOnce (struct state *st, int width)
67 {
68
69     if (st->munch_t == 0) {
70       /*
71         fprintf(stderr,"Doing width %d at %d %d shift %d %d %d grav %d\n",
72         width, atX, atY, kX, kT, kY, grav);
73       */
74     
75       if (!mono_p) {
76         XColor fgc;
77         fgc.red = random() % 65535;
78         fgc.green = random() % 65535;
79         fgc.blue = random() % 65535;
80     
81         if (XAllocColor(st->dpy, st->xgwa.colormap, &fgc)) {
82           XSetForeground(st->dpy, st->gc, fgc.pixel);
83         }
84       }
85     }
86     
87     /* Finally draw this munching square. */
88     /* for(munch_t = 0; munch_t < width; munch_t++) */
89     {
90       int x;
91       for(x = 0; x < width; x++) {
92         /* figure out the next point */
93         int y = ((x ^ ((st->munch_t + st->kT) % width)) + st->kY) % width;
94         int drawX = ((x + st->kX) % width) + st->atX;
95         int drawY = (st->grav ? y + st->atY : st->atY + width - 1 - y);
96
97         /* used to be bugs where it would draw partially offscreen.
98            while that might be a pretty feature, I didn'munch_t want it to do
99            that yet.  if these trigger, please let me know.
100         */
101         /*          assert(drawX >= 0 && drawX < xgwa.width);
102           assert(drawY >= 0 && drawY < xgwa.height);
103         */
104
105         XDrawPoint(st->dpy, st->window, st->gc, drawX, drawY);
106         /* XXX may want to change this to XDrawPoints,
107            but it's fast enough without it for the moment. */
108             
109       }
110     }
111
112     st->munch_t++;
113     if (st->munch_t >= width) {
114       st->munch_t = 0;
115       return 1;
116     }
117
118     return 0;
119 }
120
121 /*
122  * dumb way to get # of digits in number.  Probably faster than actually
123  * doing a log and a division, maybe.
124  */
125 static int dumb_log_2(int k)
126 {
127     int r = -1;
128     while (k > 0) {
129         k >>= 1; r++;
130     }
131     return r;
132 }
133
134 static void *
135 munch_init (Display *dpy, Window w)
136 {
137     struct state *st = (struct state *) calloc (1, sizeof(*st));
138     XGCValues gcv;
139     
140     st->dpy = dpy;
141     st->window = w;
142
143     /* get the dimensions of the window */
144     XGetWindowAttributes (st->dpy, w, &st->xgwa);
145     
146     /* We need a square; limit on screen size? */
147     /* we want a power of 2 for the width or the munch doesn't fill up.
148      */
149     st->logmaxwidth = (int)
150         dumb_log_2(st->xgwa.height < st->xgwa.width ? st->xgwa.height : st->xgwa.width);
151
152     st->maxwidth = 1 << st->logmaxwidth;
153
154     if (st->logmaxwidth < st->logminwidth) {
155         /* off-by-one error here?  Anyone running on < 640x480? */
156         fprintf(stderr, "munch: screen too small; use -logminwidth\n");
157         fprintf(stderr, "\t(width is %d; log is %d; log must be at least "
158                 "%d)\n", 
159                 (st->xgwa.height < st->xgwa.width ? st->xgwa.height : st->xgwa.width),
160                 st->logmaxwidth, st->logminwidth);
161         exit(0);
162     }
163     
164     /* create the gc */
165     gcv.foreground= get_pixel_resource(st->dpy, st->xgwa.colormap,
166                                        "foreground","Foreground");
167     gcv.background= get_pixel_resource(st->dpy, st->xgwa.colormap,
168                                        "background","Background");
169     
170     st->gc = XCreateGC(st->dpy, w, GCForeground|GCBackground, &gcv);
171     
172     st->delay = get_integer_resource (st->dpy, "delay", "Integer");
173     if (st->delay < 0) st->delay = 0;
174     
175     st->hold = get_integer_resource (st->dpy, "hold", "Integer");
176     if (st->hold < 0) st->hold = 0;
177     
178     st->clear = get_integer_resource (st->dpy, "clear", "Integer");
179     if (st->clear < 0) st->clear = 0;
180
181     st->logminwidth = get_integer_resource (st->dpy, "logminwidth", "Integer");
182     if (st->logminwidth < 2) st->logminwidth = 2;
183
184     st->shiftk = get_boolean_resource(st->dpy, "shift", "Boolean");
185
186     st->xor = get_boolean_resource(st->dpy, "xor", "Boolean");
187
188     /* always draw xor on mono. */
189     if (mono_p || st->xor) {
190         XSetFunction(st->dpy, st->gc, GXxor);
191     }
192
193     st->reset = 1;
194
195     return st;
196 }
197
198 static unsigned long
199 munch_draw (Display *dpy, Window w, void *closure)
200 {
201   struct state *st = (struct state *) closure;
202   int this_delay = st->delay;
203
204   if (st->reset)
205     {
206       st->reset = 0;
207
208       this_delay = st->hold;
209       /* saves some calls to random.  big deal */
210       st->randflags = random();
211
212       /* choose size -- power of two */
213       st->thiswidth = 1 << (st->logminwidth +
214                         (random() % (1 + st->logmaxwidth - st->logminwidth)));
215
216       if (st->clear && ++st->square_count >= st->clear) {
217         XClearWindow(st->dpy, w);
218         st->square_count = 0;
219       }
220
221       /* draw at this location */
222       st->atX = (random() % (st->xgwa.width <= st->thiswidth ? 1
223                          : st->xgwa.width - st->thiswidth));
224       st->atY = (random() % (st->xgwa.height <= st->thiswidth ? 1
225                          : st->xgwa.width - st->thiswidth));
226                   
227       /* wrap-around by these values; no need to %
228          as we end up doing that later anyway*/
229       st->kX = ((st->shiftk && (st->randflags & SHIFT_KX))
230             ? (random() % st->thiswidth) : 0);
231       st->kT = ((st->shiftk && (st->randflags & SHIFT_KT))
232             ? (random() % st->thiswidth) : 0);
233       st->kY = ((st->shiftk && (st->randflags & SHIFT_KY))
234             ? (random() % st->thiswidth) : 0);
235                   
236       /* set the gravity of the munch, or rather,
237          which direction we draw stuff in. */
238       st->grav = (st->randflags & GRAV);
239     }
240
241   if (munchOnce (st, st->thiswidth))
242     st->reset = 1;
243
244 /*  printf("%d\n",this_delay);*/
245   return this_delay;
246 }
247
248 static void
249 munch_reshape (Display *dpy, Window window, void *closure, 
250                  unsigned int w, unsigned int h)
251 {
252   struct state *st = (struct state *) closure;
253   st->xgwa.width = w;
254   st->xgwa.height = h;
255   st->logmaxwidth = (int)
256     dumb_log_2(st->xgwa.height < st->xgwa.width ? st->xgwa.height : st->xgwa.width);
257   st->maxwidth = 1 << st->logmaxwidth;
258 }
259
260 static Bool
261 munch_event (Display *dpy, Window window, void *closure, XEvent *event)
262 {
263   return False;
264 }
265
266 static void
267 munch_free (Display *dpy, Window window, void *closure)
268 {
269   struct state *st = (struct state *) closure;
270   free (st);
271 }
272
273 \f
274 static const char *munch_defaults [] = {
275     ".background:       black",
276     ".foreground:       white",
277     "*fpsSolid:         true",
278     "*delay:            10000",
279     "*hold:             100000",
280     "*clear:            50",
281     "*logminwidth:      7",
282     "*shift:            True",
283     "*xor:              True",
284     0
285 };
286
287 static XrmOptionDescRec munch_options [] = {
288     { "-delay",         ".delay",       XrmoptionSepArg, 0 },
289     { "-hold",          ".hold",        XrmoptionSepArg, 0 },
290     { "-clear",         ".clear",       XrmoptionSepArg, "true" },
291     { "-shift",         ".shift",       XrmoptionNoArg, "true" },
292     { "-no-shift",      ".shift",       XrmoptionNoArg, "false" },
293     { "-logminwidth",   ".logminwidth", XrmoptionSepArg, 0 },
294     { "-xor",           ".xor",         XrmoptionNoArg, "true" },
295     { "-no-xor",        ".xor",         XrmoptionNoArg, "false" },
296     { 0, 0, 0, 0 }
297 };
298
299 XSCREENSAVER_MODULE ("Munch", munch)