1 /* xscreensaver, Copyright (c) 1993, 1995 Jamie Zawinski <jwz@netscape.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
17 This would probably look good with shapes other than circles as well.
21 #include "screenhack.h"
30 static enum color_mode {
31 seuss_mode, ramp_mode, random_mode
35 static struct circle *circles;
36 static int count, global_count;
37 static Pixmap pixmap, buffer;
38 static int width, height, global_inc;
40 static unsigned long fg_pixel, bg_pixel;
41 static XColor fgc, bgc;
42 static GC draw_gc, erase_gc, copy_gc, merge_gc;
46 #define min(x,y) ((x)<(y)?(x):(y))
47 #define max(x,y) ((x)>(y)?(x):(y))
50 init_circles_1 (dpy, window)
55 count = (global_count ? global_count
56 : (3 + (random () % max (1, (min (width, height) / 50)))
57 + (random () % max (1, (min (width, height) / 50)))));
58 circles = (struct circle *) malloc (count * sizeof (struct circle));
59 for (i = 0; i < count; i++)
61 circles [i].x = 10 + random () % (width - 20);
62 circles [i].y = 10 + random () % (height - 20);
64 circles [i].increment = global_inc;
66 { /* prefer smaller increments to larger ones */
68 int inc = ((random()%j) + (random()%j) + (random()%j)) - ((j*3)/2);
69 if (inc < 0) inc = -inc + 3;
70 circles [i].increment = inc + 3;
72 circles [i].radius = random () % circles [i].increment;
73 circles [i].dx = ((random () % 3) - 1) * (1 + random () % 5);
74 circles [i].dy = ((random () % 3) - 1) * (1 + random () % 5);
79 init_circles (dpy, window)
84 XWindowAttributes xgwa;
86 XGetWindowAttributes (dpy, window, &xgwa);
88 global_count = get_integer_resource ("count", "Integer");
89 if (global_count < 0) global_count = 0;
90 global_inc = get_integer_resource ("increment", "Integer");
91 if (global_inc < 0) global_inc = 0;
92 anim_p = get_boolean_resource ("animate", "Boolean");
93 delay = get_integer_resource ("delay", "Integer");
94 mode_str = get_string_resource ("colorMode", "ColorMode");
95 if (! mode_str) cmode = random_mode;
96 else if (!strcmp (mode_str, "seuss")) cmode = seuss_mode;
97 else if (!strcmp (mode_str, "ramp")) cmode = ramp_mode;
98 else if (!strcmp (mode_str, "random")) cmode = random_mode;
101 "%s: colorMode must be seuss, ramp, or random, not \"%s\"\n",
106 if (mono_p) cmode = seuss_mode;
107 if (cmode == random_mode)
108 cmode = ((random()&3) == 1) ? ramp_mode : seuss_mode;
110 if (cmode == ramp_mode)
111 anim_p = False; /* This combo doesn't work right... */
115 fg_pixel = get_pixel_resource ("foreground","Foreground", dpy, cmap);
116 bg_pixel = get_pixel_resource ("background","Background", dpy, cmap);
120 int r = random() % 360;
121 int r2 = (random() % 180) + 45;
123 if (cmode == seuss_mode)
127 hsv_to_rgb (r, fs, 1.0, &fgc.red, &fgc.green, &fgc.blue);
128 hsv_to_rgb ((r+r2)%360, bs, 0.7, &bgc.red, &bgc.green, &bgc.blue);
129 XAllocColor (dpy, cmap, &fgc);
130 XAllocColor (dpy, cmap, &bgc);
131 fg_pixel = fgc.pixel;
132 bg_pixel = bgc.pixel;
135 width = max (50, xgwa.width);
136 height = max (50, xgwa.height);
142 pixmap = XCreatePixmap (dpy, window, width, height, 1);
143 if (cmode == seuss_mode)
144 buffer = XCreatePixmap (dpy, window, width, height, 1);
150 draw_gc = XCreateGC (dpy, pixmap, GCForeground | GCBackground, &gcv);
152 erase_gc = XCreateGC (dpy, pixmap, GCForeground, &gcv);
153 gcv.foreground = fg_pixel;
154 gcv.background = bg_pixel;
155 copy_gc = XCreateGC (dpy, window, GCForeground | GCBackground, &gcv);
157 if (cmode == seuss_mode)
161 gcv.function = GXxor;
162 merge_gc = XCreateGC (dpy, pixmap,
163 GCForeground | GCBackground | GCFunction, &gcv);
167 gcv.foreground = fg_pixel;
168 gcv.background = bg_pixel;
169 gcv.function = GXcopy;
170 merge_gc = XCreateGC (dpy, window,
171 GCForeground | GCBackground | GCFunction, &gcv);
174 init_circles_1 (dpy, window);
175 XClearWindow (dpy, window);
176 if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
180 run_circles (dpy, window)
185 static int iterations = 0;
186 static int oiterations = 0;
187 static Bool first_time_p = True;
189 Bool inhibit_sleep = False;
190 XFillRectangle (dpy, pixmap, erase_gc, 0, 0, width, height);
191 for (i = 0; i < count; i++)
193 int radius = circles [i].radius;
194 int inc = circles [i].increment;
195 if (! (iterations & 1))
197 else if (radius == 0)
203 /* Probably there's a simpler way to ask the musical question,
204 "is this square completely enclosed by this circle," but I've
205 forgotten too much trig to know it... (That's not really the
206 right question anyway, but the right question is too hard.) */
207 double x1 = ((double) (-circles [i].x)) / ((double) radius);
208 double y1 = ((double) (-circles [i].y)) / ((double) radius);
209 double x2 = ((double) (width - circles [i].x)) / ((double) radius);
210 double y2 = ((double) (height - circles [i].y)) / ((double) radius);
211 x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2;
212 if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1)
216 (cmode == seuss_mode || circles [0].increment < 0))
218 (cmode == seuss_mode ? pixmap : window),
219 (cmode == seuss_mode ? draw_gc : merge_gc),
220 circles [i].x - radius, circles [i].y - radius,
221 radius * 2, radius * 2, 0, 360*64);
222 circles [i].radius += inc;
225 if (anim_p && !first_time_p)
226 inhibit_sleep = !done;
232 first_time_p = False;
233 for (i = 0; i < count; i++)
235 circles [i].x += circles [i].dx;
236 circles [i].y += circles [i].dy;
237 circles [i].radius %= circles [i].increment;
238 if (circles [i].x < 0 || circles [i].x >= width)
240 circles [i].dx = -circles [i].dx;
241 circles [i].x += (2 * circles [i].dx);
243 if (circles [i].y < 0 || circles [i].y >= height)
245 circles [i].dy = -circles [i].dy;
246 circles [i].y += (2 * circles [i].dy);
250 else if (circles [0].increment < 0)
253 init_circles_1 (dpy, window);
257 cycle_hue (&fgc, 10);
258 cycle_hue (&bgc, 10);
259 XFreeColors (dpy, cmap, &fgc.pixel, 1, 0);
260 XFreeColors (dpy, cmap, &bgc.pixel, 1, 0);
263 XAllocColor (dpy, cmap, &fgc);
264 XAllocColor (dpy, cmap, &bgc);
265 fgc.red = d1.red; fgc.green = d1.green; fgc.blue = d1.blue;
266 bgc.red = d2.red; bgc.green = d2.green; bgc.blue = d2.blue;
267 XSetForeground (dpy, copy_gc, fgc.pixel);
268 XSetBackground (dpy, copy_gc, bgc.pixel);
272 else if ((random () % 2) == 0)
274 iterations = 0; /* ick */
275 for (i = 0; i < count; i++)
276 circles [i].radius %= circles [i].increment;
281 oiterations = iterations;
282 for (i = 0; i < count; i++)
284 circles [i].increment = -circles [i].increment;
285 circles [i].radius += (2 * circles [i].increment);
291 XCopyPlane (dpy, pixmap, buffer, merge_gc, 0, 0, width, height, 0, 0, 1);
292 else if (cmode != seuss_mode)
294 static int ncolors = 0;
295 static XColor *colors = 0;
296 if (circles [0].increment >= 0)
297 inhibit_sleep = True;
301 double fgs, fgv, bgs, bgv;
303 for (i = 0; i < ncolors; i++)
304 XFreeColors (dpy, cmap, &colors [i].pixel, 1, 0);
306 rgb_to_hsv (fgc.red, fgc.green, fgc.blue, &fgh, &fgs, &fgv);
307 rgb_to_hsv (bgc.red, bgc.green, bgc.blue, &bgh, &bgs, &bgv);
308 ncolors = oiterations;
311 ? realloc (colors, sizeof (XColor) * ncolors)
312 : malloc (sizeof (XColor) * ncolors)));
314 make_color_ramp (bgh, bgs, bgv, fgh, fgs, fgv, colors, ncolors);
315 for (i = 0; i < ncolors; i++)
316 XAllocColor (dpy, cmap, &colors [i]);
317 XSetForeground (dpy, merge_gc, colors [0].pixel);
321 XSetForeground (dpy, merge_gc, colors [iterations].pixel);
325 XCopyPlane (dpy, pixmap, window, merge_gc, 0, 0, width, height, 0, 0, 1);
327 if (buffer && (anim_p
328 ? (done || (first_time_p && (iterations & 1)))
331 XCopyPlane (dpy, buffer, window, copy_gc, 0, 0, width, height, 0, 0, 1);
334 XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
337 XCopyPlane (dpy, pixmap, window, copy_gc, 0,0,width,height,width,height, 1);
339 XCopyPlane (dpy, buffer, window, copy_gc, 0,0,width,height,0,height, 1);
348 if (delay && !inhibit_sleep) usleep (delay);
352 char *progclass = "Halo";
354 char *defaults [] = {
355 "Halo.background: black", /* to placate SGI */
356 "Halo.foreground: white",
357 "*colorMode: random",
363 XrmOptionDescRec options [] = {
364 { "-count", ".count", XrmoptionSepArg, 0 },
365 { "-delay", ".delay", XrmoptionSepArg, 0 },
366 { "-animate", ".animate", XrmoptionNoArg, "True" },
367 { "-mode", ".colorMode", XrmoptionSepArg, 0 }
369 int options_size = (sizeof (options) / sizeof (options[0]));
372 screenhack (dpy, window)
376 init_circles (dpy, window);
378 run_circles (dpy, window);