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 | } |
---|