1 /* xscreensaver, Copyright (c) 1993 Jamie Zawinski <jwz@lucid.com>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* I wanted to lay down new circles with TV:ALU-ADD instead of TV:ALU-XOR,
13 but X doesn't support arithmetic combinations of pixmaps!! What losers.
14 I suppose I could crank out the 2's compliment math by hand, but that's
18 #include "screenhack.h"
26 static struct circle *circles;
27 static int count, global_count;
28 static Pixmap pixmap, buffer;
29 static int width, height, global_inc;
31 static unsigned long fg_pixel, bg_pixel;
32 static XColor fgc, bgc;
34 static GC draw_gc, erase_gc, copy_gc, merge_gc;
38 #define min(x,y) ((x)<(y)?(x):(y))
39 #define max(x,y) ((x)>(y)?(x):(y))
42 init_circles_1 (dpy, window)
47 count = (global_count ? global_count
48 : (3 + (random () % max (1, (min (width, height) / 50)))
49 + (random () % max (1, (min (width, height) / 50)))));
50 circles = (struct circle *) malloc (count * sizeof (struct circle));
51 for (i = 0; i < count; i++)
53 circles [i].x = 10 + random () % (width - 20);
54 circles [i].y = 10 + random () % (height - 20);
56 circles [i].increment = global_inc;
58 { /* prefer smaller increments to larger ones */
60 int inc = ((random()%j) + (random()%j) + (random()%j)) - ((j*3)/2);
61 if (inc < 0) inc = -inc + 3;
62 circles [i].increment = inc + 3;
64 circles [i].radius = random () % circles [i].increment;
65 circles [i].dx = ((random () % 3) - 1) * (1 + random () % 5);
66 circles [i].dy = ((random () % 3) - 1) * (1 + random () % 5);
71 init_circles (dpy, window)
76 XWindowAttributes xgwa;
77 XGetWindowAttributes (dpy, window, &xgwa);
79 global_count = get_integer_resource ("count", "Integer");
80 if (global_count < 0) global_count = 0;
81 global_inc = get_integer_resource ("increment", "Integer");
82 if (global_inc < 0) global_inc = 0;
83 xor_p = get_boolean_resource ("xor", "Boolean");
84 /* if (mono_p) */ xor_p = True;
85 anim_p = get_boolean_resource ("animate", "Boolean");
86 delay = get_integer_resource ("delay", "Integer");
89 fg_pixel = get_pixel_resource ("foreground","Foreground", dpy, cmap);
90 bg_pixel = get_pixel_resource ("background","Background", dpy, cmap);
94 hsv_to_rgb (0, 0.5, 1.0, &fgc.red, &fgc.green, &fgc.blue);
95 hsv_to_rgb (180, 1.0, 0.7, &bgc.red, &bgc.green, &bgc.blue);
96 XAllocColor (dpy, cmap, &fgc);
97 XAllocColor (dpy, cmap, &bgc);
102 width = max (50, xgwa.width);
103 height = max (50, xgwa.height);
109 pixmap = XCreatePixmap (dpy, window, width, height, 1);
111 buffer = XCreatePixmap (dpy, window, width, height, 1);
117 draw_gc = XCreateGC (dpy, pixmap, GCForeground | GCBackground, &gcv);
119 erase_gc = XCreateGC (dpy, pixmap, GCForeground, &gcv);
120 gcv.foreground = fg_pixel;
121 gcv.background = bg_pixel;
122 copy_gc = XCreateGC (dpy, window, GCForeground | GCBackground, &gcv);
128 gcv.function = GXxor;
129 merge_gc = XCreateGC (dpy, pixmap,
130 GCForeground | GCBackground | GCFunction, &gcv);
134 gcv.foreground = fg_pixel;
135 gcv.background = bg_pixel;
136 gcv.function = GXcopy;
137 merge_gc = XCreateGC (dpy, window,
138 GCForeground | GCBackground | GCFunction, &gcv);
141 init_circles_1 (dpy, window);
142 XClearWindow (dpy, window);
143 if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
147 run_circles (dpy, window)
152 static int iterations = 0;
153 static int oiterations = 0;
154 static Bool first_time_p = True;
156 Bool inhibit_sleep = False;
157 XFillRectangle (dpy, pixmap, erase_gc, 0, 0, width, height);
158 for (i = 0; i < count; i++)
160 int radius = circles [i].radius;
161 int inc = circles [i].increment;
162 if (! (iterations & 1))
164 else if (radius == 0)
170 /* Probably there's a simpler way to ask the musical question,
171 "is this square completely enclosed by this circle," but I've
172 forgotten too much trig to know it... (That's not really the
173 right question anyway, but the right question is too hard.) */
174 double x1 = ((double) (-circles [i].x)) / ((double) radius);
175 double y1 = ((double) (-circles [i].y)) / ((double) radius);
176 double x2 = ((double) (width - circles [i].x)) / ((double) radius);
177 double y2 = ((double) (height - circles [i].y)) / ((double) radius);
178 x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2;
179 if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1)
183 (xor_p || circles [0].increment < 0))
185 (xor_p ? pixmap : window),
186 (xor_p ? draw_gc : merge_gc),
187 circles [i].x - radius, circles [i].y - radius,
188 radius * 2, radius * 2, 0, 360*64);
189 circles [i].radius += inc;
192 if (anim_p && !first_time_p)
193 inhibit_sleep = !done;
199 first_time_p = False;
200 for (i = 0; i < count; i++)
202 circles [i].x += circles [i].dx;
203 circles [i].y += circles [i].dy;
204 circles [i].radius %= circles [i].increment;
205 if (circles [i].x < 0 || circles [i].x >= width)
207 circles [i].dx = -circles [i].dx;
208 circles [i].x += (2 * circles [i].dx);
210 if (circles [i].y < 0 || circles [i].y >= height)
212 circles [i].dy = -circles [i].dy;
213 circles [i].y += (2 * circles [i].dy);
217 else if (circles [0].increment < 0)
220 init_circles_1 (dpy, window);
223 cycle_hue (&fgc, 10);
224 cycle_hue (&bgc, 10);
225 XFreeColors (dpy, cmap, &fgc.pixel, 1, 0);
226 XFreeColors (dpy, cmap, &bgc.pixel, 1, 0);
227 XAllocColor (dpy, cmap, &fgc);
228 XAllocColor (dpy, cmap, &bgc);
229 XSetForeground (dpy, copy_gc, fgc.pixel);
230 XSetBackground (dpy, copy_gc, bgc.pixel);
234 else if ((random () % 2) == 0)
236 iterations = 0; /* ick */
237 for (i = 0; i < count; i++)
238 circles [i].radius %= circles [i].increment;
243 oiterations = iterations;
244 for (i = 0; i < count; i++)
246 circles [i].increment = -circles [i].increment;
247 circles [i].radius += (2 * circles [i].increment);
253 XCopyPlane (dpy, pixmap, buffer, merge_gc, 0, 0, width, height, 0, 0, 1);
256 static int ncolors = 0;
257 static XColor *colors = 0;
258 if (circles [0].increment >= 0)
259 inhibit_sleep = True;
263 double fgs, fgv, bgs, bgv;
265 for (i = 0; i < ncolors; i++)
266 XFreeColors (dpy, cmap, &colors [i].pixel, 1, 0);
268 rgb_to_hsv (fgc.red, fgc.green, fgc.blue, &fgh, &fgs, &fgv);
269 rgb_to_hsv (bgc.red, bgc.green, bgc.blue, &bgh, &bgs, &bgv);
270 ncolors = oiterations;
273 ? realloc (colors, sizeof (XColor) * ncolors)
274 : malloc (sizeof (XColor) * ncolors)));
276 make_color_ramp (bgh, bgs, bgv, fgh, fgs, fgv, colors, ncolors);
277 for (i = 0; i < ncolors; i++)
278 XAllocColor (dpy, cmap, &colors [i]);
279 XSetForeground (dpy, merge_gc, colors [0].pixel);
283 XSetForeground (dpy, merge_gc, colors [iterations].pixel);
287 XCopyPlane (dpy, pixmap, window, merge_gc, 0, 0, width, height, 0, 0, 1);
289 if (buffer && (anim_p
290 ? (done || (first_time_p && (iterations & 1)))
293 XCopyPlane (dpy, buffer, window, copy_gc, 0, 0, width, height, 0, 0, 1);
296 XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
299 XCopyPlane (dpy, pixmap, window, copy_gc, 0,0,width,height,width,height, 1);
301 XCopyPlane (dpy, buffer, window, copy_gc, 0,0,width,height,0,height, 1);
310 if (delay && !inhibit_sleep) usleep (delay);
314 char *progclass = "Halo";
316 char *defaults [] = {
317 "*background: black",
318 "*foreground: white",
325 XrmOptionDescRec options [] = {
326 { "-count", ".count", XrmoptionSepArg, 0 },
327 { "-delay", ".delay", XrmoptionSepArg, 0 },
328 { "-animate", ".animate", XrmoptionNoArg, "True" } /* ,
329 { "-xor", ".xor", XrmoptionNoArg, "True" },
330 { "-no-xor", ".xor", XrmoptionNoArg, "False" } */
332 int options_size = (sizeof (options) / sizeof (options[0]));
335 screenhack (dpy, window)
339 init_circles (dpy, window);
341 run_circles (dpy, window);