ftp://ftp.smr.ru/pub/0/FreeBSD/releases/distfiles/xscreensaver-3.16.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@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 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         screenhack_handle_events (dpy);
137         if (delay) usleep(delay);
138     }
139 }
140
141 /*
142  * dumb way to get # of digits in number.  Probably faster than actually
143  * doing a log and a division, maybe.
144  */
145 static int dumb_log_2(int k) {
146     int r = -1;
147     while (k > 0) {
148         k >>= 1; r++;
149     }
150     return r;
151 }
152
153 /* This parses arguments, initializes the window, etc., and finally starts
154  * calling munchOnce ad infinitum.
155  */
156 void
157 screenhack (dpy, w) Display *dpy; Window w;
158 {
159     int logmaxwidth;
160     int maxwidth;
161     XWindowAttributes xgwa;
162     Colormap cmap;
163     XGCValues gcv;
164     int n = 0;  /* number of squares before we have to clear */
165     int randflags;
166     int thiswidth;
167     
168     /* get the dimensions of the window */
169     XGetWindowAttributes (dpy, w, &xgwa);
170     
171     /* We need a square; limit on screen size? */
172     /* we want a power of 2 for the width or the munch doesn't fill up.
173      */
174     logmaxwidth = (int)
175         dumb_log_2(xgwa.height < xgwa.width ? xgwa.height : xgwa.width);
176
177     maxwidth = 1 << logmaxwidth;
178
179     if (logmaxwidth < logminwidth) {
180         /* off-by-one error here?  Anyone running on < 640x480? */
181         fprintf(stderr, "munch: screen too small; use -logminwidth\n");
182         fprintf(stderr, "\t(width is %d; log is %d; log must be at least "
183                 "%d)\n", 
184                 (xgwa.height < xgwa.width ? xgwa.height : xgwa.width),
185                 logmaxwidth, logminwidth);
186         exit(0);
187     }
188     
189     /* create the gc */
190     cmap = xgwa.colormap;
191     gcv.foreground= get_pixel_resource("foreground","Foreground",
192                                        dpy, cmap);
193     gcv.background= get_pixel_resource("background","Background",
194                                        dpy, cmap);
195     
196     gc = XCreateGC(dpy, w, GCForeground|GCBackground, &gcv);
197     
198     delay = get_integer_resource ("delay", "Integer");
199     if (delay < 0) delay = 0;
200     
201     hold = get_integer_resource ("hold", "Integer");
202     if (hold < 0) hold = 0;
203     
204     clear = get_integer_resource ("clear", "Integer");
205     if (clear < 0) clear = 0;
206
207     logminwidth = get_integer_resource ("logminwidth", "Integer");
208     if (logminwidth < 2) logminwidth = 2;
209
210     shiftk = get_boolean_resource("shift", "Boolean");
211
212     xor = get_boolean_resource("xor", "Boolean");
213
214     /* always draw xor on mono. */
215     if (mono_p || xor) {
216         XSetFunction(dpy, gc, GXxor);
217     }
218
219     for(;;) {
220         /* saves some calls to random.  big deal */
221         randflags = random();
222
223         /* choose size -- power of two */
224         thiswidth = 1 << (logminwidth +
225                           (random() % (1 + logmaxwidth - logminwidth)));
226
227         munchOnce(dpy, w,
228                   thiswidth, /* Width, in pixels */
229                   
230                   /* draw at this location */
231                   (random() % (xgwa.width <= thiswidth ? 1
232                                : xgwa.width - thiswidth)),
233                   (random() % (xgwa.height <= thiswidth ? 1
234                                : xgwa.width - thiswidth)),
235                   
236                   /* wrap-around by these values; no need to %
237                      as we end up doing that later anyway*/
238                   ((shiftk && (randflags & SHIFT_KX))
239                    ? (random() % thiswidth) : 0),
240                   ((shiftk && (randflags & SHIFT_KT))
241                    ? (random() % thiswidth) : 0),
242                   ((shiftk && (randflags & SHIFT_KY))
243                    ? (random() % thiswidth) : 0),
244                   
245                   /* set the gravity of the munch, or rather,
246                      which direction we draw stuff in. */
247                   (randflags & GRAV)
248                   );
249         
250         screenhack_handle_events (dpy);
251         if (hold) usleep(hold);
252         
253         if (clear && ++n >= clear) {
254             XClearWindow(dpy, w);
255             n = 0;
256         }
257     }
258 }