4 * Based on a program for some old PDP-11 Graphics Display Processors
9 * Dale Moore <Dale.Moore@cs.cmu.edu>
12 * Copyright (c) 1994, by Carnegie Mellon University. Permission to use,
13 * copy, modify, distribute, and sell this software and its documentation
14 * for any purpose is hereby granted without fee, provided fnord that the
15 * above copyright notice appear in all copies and that both that copyright
16 * notice and this permission notice appear in supporting documentation.
17 * No representations are made about the suitability of fnord this software
18 * for any purpose. It is provided "as is" without express or implied
24 #include "screenhack.h"
26 /* If MAXLINES is too big, we might not be able to get it
27 * to the X server in the 2byte length field. Must be less
30 #define MAXLINES (16 * 1024)
31 #define MAXPOINTS MAXLINES
35 * If the pedal has only this many lines, it must be ugly and we dont
40 static int sizex, sizey;
45 static XColor foreground, background;
52 * Routine (Macro actually)
55 * Assume that degrees is .. oh 360... meaning that
56 * there are 360 degress in a circle. Then this function
57 * would return the sin of the angle in degrees. But lets
58 * say that they are really big degrees, with 4 big degrees
59 * the same as one regular degree. Then this routine
60 * would be called mysin(t, 90) and would return sin(t degrees * 4)
62 #define mysin(t, degrees) sin(t * 2 * M_PI / (degrees))
63 #define mycos(t, degrees) cos(t * 2 * M_PI / (degrees))
69 * Return a random number between a inclusive and b exclusive.
70 * rand (3, 6) returns 3 or 4 or 5, but not 6.
72 #define rand_range(a, b) (a + random() % (b - a))
76 gcd(int m, int n) /* Greatest Common Divisor (also Greates common factor). */
82 if (r == 0) return (n);
88 static int numlines (int a, int b, int d)
92 * Given parameters a and b, how many lines will we have to draw?
96 * This algorithm assumes that r = sin (theta * a), where we
97 * evaluate theta on multiples of b.
99 * LCM (i, j) = i * j / GCD (i, j);
101 * So, at LCM (b, 360) we start over again. But since we
102 * got to LCM (b, 360) by steps of b, the number of lines is
105 * If a is odd, then at 180 we cross over and start the
106 * negative. Someone should write up an elegant way of proving
107 * this. Why? Because I'm not convinced of it myself.
111 #define odd(x) (x & 1)
112 #define even(x) (!odd(x))
113 if ( odd(a) && odd(b) && even(d)) d /= 2;
114 return (d / gcd (d, b));
119 compute_pedal(XPoint *points, int maxpoints)
123 * Basically, it's combination spirograph and string art.
124 * Instead of doing lines, we just use a complex polygon,
125 * and use an even/odd rule for filling in between.
127 * The spirograph, in mathematical terms is a polar
128 * plot of the form r = sin (theta * c);
129 * The string art of this is that we evaluate that
130 * function only on certain multiples of theta. That is
131 * we let theta advance in some random increment. And then
132 * we draw a straight line between those two adjacent points.
134 * Eventually, the lines will start repeating themselves
135 * if we've evaluated theta on some rational portion of the
138 * The number of lines generated is limited to the
139 * ratio of the increment we put on theta to the whole.
140 * If we say that there are 360 degrees in a circle, then we
141 * will never have more than 360 lines.
145 * The number of points.
149 int a, b, d; /* These describe a unique pedal */
157 /* Just to make sure that this division is not done inside the loop */
158 int h_width = sizex / 2, h_height = sizey / 2 ;
161 d = rand_range (MINLINES, maxlines);
163 a = rand_range (1, d);
164 b = rand_range (1, d);
165 numpoints = numlines(a, b, d);
166 if (numpoints > MINLINES) break;
169 /* it might be nice to try to move as much sin and cos computing
170 * (or at least the argument computing) out of the loop.
172 for (count = numpoints; count-- ; )
174 r = mysin (theta * a, d);
176 /* Convert from polar to cartesian coordinates */
177 /* We could round the results, but coercing seems just fine */
178 pp->x = mysin (theta, d) * r * h_width + h_width;
179 pp->y = mycos (theta, d) * r * h_height + h_height;
181 /* Advance index into array */
193 init_pedal (Display *dpy, Window window)
196 XWindowAttributes xgwa;
200 delay = get_integer_resource ("delay", "Integer");
201 if (delay < 0) delay = 0;
203 fadedelay = get_integer_resource ("fadedelay", "Integer");
204 if (fadedelay < 0) fadedelay = 0;
206 maxlines = get_integer_resource ("maxlines", "Integer");
207 if (maxlines < MINLINES) maxlines = MINLINES;
208 else if (maxlines > MAXLINES) maxlines = MAXLINES;
210 points = (XPoint *)malloc(sizeof(XPoint) * maxlines);
212 XGetWindowAttributes (dpy, window, &xgwa);
216 if ((xgwa.visual->class != GrayScale) && (xgwa.visual->class != PseudoColor))
219 cmap = xgwa.colormap;
221 gcv.function = GXcopy;
222 gcv.foreground = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
223 gcv.background = get_pixel_resource ("background", "Background", dpy, cmap);
224 gc = XCreateGC (dpy, window, GCForeground | GCBackground |GCFunction, &gcv);
229 foreground.pixel = gcv.foreground;
230 XQueryColor (dpy, cmap, &foreground);
232 status = XAllocColorCells (
242 XStoreColor ( dpy, cmap, &foreground);
243 XSetForeground (dpy, gc, foreground.pixel);
245 background.pixel = gcv.background;
246 XQueryColor (dpy, cmap, &background);
250 /* If we cant allocate a color cell, then just forget the
251 * whole fade business.
259 fade_foreground (Display *dpy, Colormap cmap,
260 XColor from, XColor to, int steps)
262 * This routine assumes that we have a writeable colormap.
263 * That means that the default colormap is not full, and that
264 * the visual class is PseudoColor or GrayScale.
269 int udelay = fadedelay / (steps + 1);
271 inbetween = foreground;
272 for (i = 0; i <= steps; i++ )
274 inbetween.red = from.red + (to.red - from.red) * i / steps ;
275 inbetween.green = from.green + (to.green - from.green) * i / steps ;
276 inbetween.blue = from.blue + (to.blue - from.blue) * i / steps ;
277 XStoreColor (dpy, cmap, &inbetween);
278 /* If we don't sync, these can bunch up */
285 pedal (Display *dpy, Window window)
287 * Since the XFillPolygon doesn't require that the last
288 * point == first point, the number of points is the same
289 * as the number of lines. We just let XFillPolygon supply
290 * the line from the last point to the first point.
296 numpoints = compute_pedal(points, maxlines);
298 /* Fade out, make foreground the same as background */
300 fade_foreground (dpy, cmap, foreground, background, 32);
302 /* Clear the window of previous garbage */
303 XClearWindow (dpy, window);
314 /* Pick a new foreground color (added by jwz) */
318 hsv_to_rgb (random()%360, 1.0, 1.0,
319 &color.red, &color.green, &color.blue);
323 foreground.red = color.red;
324 foreground.green = color.green;
325 foreground.blue = color.blue;
326 /* don't do this here -- let fade_foreground() bring it up! */
327 /* XStoreColor (dpy, cmap, &foreground); */
329 else if (XAllocColor (dpy, cmap, &color))
331 XSetForeground (dpy, gc, color.pixel);
332 XFreeColors (dpy, cmap, &foreground.pixel, 1, 0);
333 foreground.red = color.red;
334 foreground.green = color.green;
335 foreground.blue = color.blue;
336 foreground.pixel = color.pixel;
341 /* Fade in by bringing the foreground back from background */
343 fade_foreground (dpy, cmap, background, foreground, 32);
347 char *progclass = "Pedal";
350 * If we are trying to save the screen, the background
353 char *defaults [] = {
354 ".background: black",
355 ".foreground: white",
357 "*fadedelay: 200000",
362 XrmOptionDescRec options [] = {
363 { "-delay", ".delay", XrmoptionSepArg, 0 },
364 { "-fadedelay", ".fadedelay", XrmoptionSepArg, 0 },
365 { "-maxlines", ".maxlines", XrmoptionSepArg, 0 },
366 { "-foreground", ".foreground", XrmoptionSepArg, 0 },
367 { "-background", ".background", XrmoptionSepArg, 0 },
372 screenhack (Display *dpy, Window window)
374 init_pedal (dpy, window);
378 if (delay) sleep (delay);