http://ftp.x.org/contrib/applications/xscreensaver-2.23.tar.gz
[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@netscape.com>
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 char *progclass = "Munch";
45
46 char *defaults [] = {
47     ".background:       black",
48     ".foreground:       white",
49     "*delay:            5000",
50     "*hold:             100000",
51     "*clear:            50",
52     "*logminwidth:      7",
53     "*shift:            True",
54     "*xor:              True",
55     0
56 };
57
58 XrmOptionDescRec options [] = {
59     { "-delay",         ".delay",       XrmoptionSepArg, 0 },
60     { "-hold",          ".hold",        XrmoptionSepArg, 0 },
61     { "-clear",         ".clear",       XrmoptionSepArg, "true" },
62     { "-shift",         ".shift",       XrmoptionNoArg, "true" },
63     { "-no-shift",      ".shift",       XrmoptionNoArg, "false" },
64     { "-logminwidth",   ".logminwidth", XrmoptionSepArg, 0 },
65     { "-xor",           ".xor",         XrmoptionNoArg, "true" },
66     { "-no-xor",        ".xor",         XrmoptionNoArg, "false" },
67     { 0, 0, 0, 0 }
68 };
69
70
71 static GC gc;
72 /* only configurable things right now */
73 static int delay, hold, clear, logminwidth, shiftk, xor;
74
75 static void munchOnce (Display* dpy, Window w,
76                        int width, /* pixels */
77                        int atX, int atY, /* pixels */
78                        int kX, int kT, int kY, /* pixels */
79                        int grav /* 0 or not */) {
80
81     int x, y, t;
82     static Colormap cmap;
83     XWindowAttributes xgwa;
84     XColor fgc;
85     int drawX, drawY;
86
87     /*
88     fprintf(stderr,"Doing width %d at %d %d shift %d %d %d grav %d\n",
89             width, atX, atY, kX, kT, kY, grav);
90             */
91     
92     XGetWindowAttributes (dpy, w, &xgwa);
93
94     if (!mono_p) {
95         /* XXX there are probably bugs with this. */
96         cmap = xgwa.colormap;
97    
98         fgc.red = random() % 65535;
99         fgc.green = random() % 65535;
100         fgc.blue = random() % 65535;
101     
102         if (XAllocColor(dpy, cmap, &fgc)) {
103             XSetForeground(dpy, gc, fgc.pixel);
104         }
105     }
106     
107     /* Finally draw this munching square. */
108     for(t = 0; t < width; t++) {
109         for(x = 0; x < width; x++) {
110             /* figure out the next point */
111             y = ((x ^ ((t + kT) % width)) + kY) % width;
112
113             drawX = ((x + kX) % width) + atX;
114             drawY = (grav ? y + atY : atY + width - 1 - y);
115
116             /* used to be bugs where it would draw partially offscreen.
117                while that might be a pretty feature, I didn't want it to do
118                that yet.  if these trigger, please let me know.
119                */
120 /*          assert(drawX >= 0 && drawX < xgwa.width);
121             assert(drawY >= 0 && drawY < xgwa.height);
122 */
123
124             XDrawPoint(dpy, w, gc, drawX, drawY);
125             /* XXX may want to change this to XDrawPoints,
126                but it's fast enough without it for the moment. */
127             
128         }
129         
130         /* we've drawn one pass' worth of points.  let the server catch up
131            or this won't be interesting to watch at all.  we call this here
132            in the hopes of having one whole sequence of dots appear at the
133            same time (one for each value of x, surprisingly enough)
134            */
135         XSync(dpy, False);
136         if (delay) usleep(delay);
137     }
138 }
139
140 /*
141  * dumb way to get # of digits in number.  Probably faster than actually
142  * doing a log and a division, maybe.
143  */
144 static int dumb_log_2(int k) {
145     int r = -1;
146     while (k > 0) {
147         k >>= 1; r++;
148     }
149     return r;
150 }
151
152 /* This parses arguments, initializes the window, etc., and finally starts
153  * calling munchOnce ad infinitum.
154  */
155 void
156 screenhack (dpy, w) Display *dpy; Window w;
157 {
158     int logmaxwidth;
159     int maxwidth;
160     XWindowAttributes xgwa;
161     Colormap cmap;
162     XGCValues gcv;
163     int n = 0;  /* number of squares before we have to clear */
164     int randflags;
165     int thiswidth;
166     
167     /* get the dimensions of the window */
168     XGetWindowAttributes (dpy, w, &xgwa);
169     
170     /* We need a square; limit on screen size? */
171     /* we want a power of 2 for the width or the munch doesn't fill up.
172      */
173     logmaxwidth = (int)
174         dumb_log_2(xgwa.height < xgwa.width ? xgwa.height : xgwa.width);
175
176     maxwidth = 1 << logmaxwidth;
177
178     if (logmaxwidth < logminwidth) {
179         /* off-by-one error here?  Anyone running on < 640x480? */
180         fprintf(stderr, "munch: screen too small; use -logminwidth\n");
181         fprintf(stderr, "\t(width is %d; log is %d; log must be at least "
182                 "%d)\n", 
183                 (xgwa.height < xgwa.width ? xgwa.height : xgwa.width),
184                 logmaxwidth, logminwidth);
185         exit(0);
186     }
187     
188     /* create the gc */
189     cmap = xgwa.colormap;
190     gcv.foreground= get_pixel_resource("foreground","Foreground",
191                                        dpy, cmap);
192     gcv.background= get_pixel_resource("background","Background",
193                                        dpy, cmap);
194     
195     gc = XCreateGC(dpy, w, GCForeground|GCBackground, &gcv);
196     
197     delay = get_integer_resource ("delay", "Integer");
198     if (delay < 0) delay = 0;
199     
200     hold = get_integer_resource ("hold", "Integer");
201     if (hold < 0) hold = 0;
202     
203     clear = get_integer_resource ("clear", "Integer");
204     if (clear < 0) clear = 0;
205
206     logminwidth = get_integer_resource ("logminwidth", "Integer");
207     if (logminwidth < 2) logminwidth = 2;
208
209     shiftk = get_boolean_resource("shift", "Boolean");
210
211     xor = get_boolean_resource("xor", "Boolean");
212
213     /* always draw xor on mono. */
214     if (mono_p || xor) {
215         XSetFunction(dpy, gc, GXxor);
216     }
217
218     for(;;) {
219         /* saves some calls to random.  big deal */
220         randflags = random();
221
222         /* choose size -- power of two */
223         thiswidth = 1 << (logminwidth +
224                           (random() % (1 + logmaxwidth - logminwidth)));
225
226         munchOnce(dpy, w,
227                   thiswidth, /* Width, in pixels */
228                   
229                   /* draw at this location */
230                   (random() % (xgwa.width <= thiswidth ? 1
231                                : xgwa.width - thiswidth)),
232                   (random() % (xgwa.height <= thiswidth ? 1
233                                : xgwa.width - thiswidth)),
234                   
235                   /* wrap-around by these values; no need to %
236                      as we end up doing that later anyway*/
237                   ((shiftk && (randflags & SHIFT_KX))
238                    ? (random() % thiswidth) : 0),
239                   ((shiftk && (randflags & SHIFT_KT))
240                    ? (random() % thiswidth) : 0),
241                   ((shiftk && (randflags & SHIFT_KY))
242                    ? (random() % thiswidth) : 0),
243                   
244                   /* set the gravity of the munch, or rather,
245                      which direction we draw stuff in. */
246                   (randflags & GRAV)
247                   );
248         
249         if (hold) usleep(hold);
250         
251         if (clear && ++n >= clear) {
252             XClearWindow(dpy, w);
253             n = 0;
254         }
255     }
256 }