1 /* xscreensaver, Copyright (c) 1993, 1995, 1996, 1997
2 * Jamie Zawinski <jwz@netscape.com>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 /* I wanted to lay down new circles with TV:ALU-ADD instead of TV:ALU-XOR,
14 but X doesn't support arithmetic combinations of pixmaps!! What losers.
15 I suppose I could crank out the 2's compliment math by hand, but that's
18 This would probably look good with shapes other than circles as well.
22 #include "screenhack.h"
31 static enum color_mode {
32 seuss_mode, ramp_mode, random_mode
36 static struct circle *circles;
37 static int count, global_count;
38 static Pixmap pixmap, buffer;
39 static int width, height, global_inc;
40 static int delay, delay2, cycle_delay;
41 static unsigned long fg_pixel, bg_pixel;
42 static GC draw_gc, erase_gc, copy_gc, merge_gc;
47 static XColor *colors;
53 #define min(x,y) ((x)<(y)?(x):(y))
54 #define max(x,y) ((x)>(y)?(x):(y))
57 init_circles_1 (Display *dpy, Window window)
60 count = (global_count ? global_count
61 : (3 + (random () % max (1, (min (width, height) / 50)))
62 + (random () % max (1, (min (width, height) / 50)))));
63 circles = (struct circle *) malloc (count * sizeof (struct circle));
64 for (i = 0; i < count; i++)
66 circles [i].x = 10 + random () % (width - 20);
67 circles [i].y = 10 + random () % (height - 20);
69 circles [i].increment = global_inc;
71 { /* prefer smaller increments to larger ones */
73 int inc = ((random()%j) + (random()%j) + (random()%j)) - ((j*3)/2);
74 if (inc < 0) inc = -inc + 3;
75 circles [i].increment = inc + 3;
77 circles [i].radius = random () % circles [i].increment;
78 circles [i].dx = ((random () % 3) - 1) * (1 + random () % 5);
79 circles [i].dy = ((random () % 3) - 1) * (1 + random () % 5);
84 init_circles (Display *dpy, Window window)
87 XWindowAttributes xgwa;
89 XGetWindowAttributes (dpy, window, &xgwa);
91 global_count = get_integer_resource ("count", "Integer");
92 if (global_count < 0) global_count = 0;
93 global_inc = get_integer_resource ("increment", "Integer");
94 if (global_inc < 0) global_inc = 0;
95 anim_p = get_boolean_resource ("animate", "Boolean");
96 delay = get_integer_resource ("delay", "Integer");
97 delay2 = get_integer_resource ("delay2", "Integer") * 1000000;
98 cycle_delay = get_integer_resource ("cycleDelay", "Integer");
99 mode_str = get_string_resource ("colorMode", "ColorMode");
100 if (! mode_str) cmode = random_mode;
101 else if (!strcmp (mode_str, "seuss")) cmode = seuss_mode;
102 else if (!strcmp (mode_str, "ramp")) cmode = ramp_mode;
103 else if (!strcmp (mode_str, "random")) cmode = random_mode;
106 "%s: colorMode must be seuss, ramp, or random, not \"%s\"\n",
111 if (mono_p) cmode = seuss_mode;
112 if (cmode == random_mode)
113 cmode = ((random()&3) == 1) ? ramp_mode : seuss_mode;
115 if (cmode == ramp_mode)
116 anim_p = False; /* This combo doesn't work right... */
118 ncolors = get_integer_resource ("colors", "Colors");
119 if (ncolors < 2) ncolors = 2;
120 if (ncolors <= 2) mono_p = True;
125 colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
127 cycle_p = mono_p ? False : get_boolean_resource ("cycle", "Cycle");
132 else if (random() % (cmode == seuss_mode ? 2 : 10))
133 make_uniform_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
134 True, &cycle_p, True);
136 make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
137 True, &cycle_p, True);
139 if (ncolors <= 2) mono_p = True;
140 if (mono_p) cycle_p = False;
141 if (mono_p) cmode = seuss_mode;
145 fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
146 bg_pixel = get_pixel_resource ("background", "Background", dpy, cmap);
151 bg_index = ncolors / 4;
152 if (fg_index == bg_index) bg_index++;
153 fg_pixel = colors[fg_index].pixel;
154 bg_pixel = colors[bg_index].pixel;
157 width = max (50, xgwa.width);
158 height = max (50, xgwa.height);
164 pixmap = XCreatePixmap (dpy, window, width, height, 1);
165 if (cmode == seuss_mode)
166 buffer = XCreatePixmap (dpy, window, width, height, 1);
172 draw_gc = XCreateGC (dpy, pixmap, GCForeground | GCBackground, &gcv);
174 erase_gc = XCreateGC (dpy, pixmap, GCForeground, &gcv);
175 gcv.foreground = fg_pixel;
176 gcv.background = bg_pixel;
177 copy_gc = XCreateGC (dpy, window, GCForeground | GCBackground, &gcv);
179 if (cmode == seuss_mode)
183 gcv.function = GXxor;
184 merge_gc = XCreateGC (dpy, pixmap,
185 GCForeground | GCBackground | GCFunction, &gcv);
189 gcv.foreground = fg_pixel;
190 gcv.background = bg_pixel;
191 gcv.function = GXcopy;
192 merge_gc = XCreateGC (dpy, window,
193 GCForeground | GCBackground | GCFunction, &gcv);
196 init_circles_1 (dpy, window);
197 XClearWindow (dpy, window);
198 if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
202 run_circles (Display *dpy, Window window)
205 static int iterations = 0;
206 static int oiterations = 0;
207 static Bool first_time_p = True;
209 Bool inhibit_sleep = False;
210 XFillRectangle (dpy, pixmap, erase_gc, 0, 0, width, height);
211 for (i = 0; i < count; i++)
213 int radius = circles [i].radius;
214 int inc = circles [i].increment;
216 if (! (iterations & 1)) /* never stop on an odd number of iterations */
218 else if (radius == 0) /* eschew inf */
220 else if (radius < 0) /* stop when the circles are points */
222 else /* stop when the circles fill the window */
224 /* Probably there's a simpler way to ask the musical question,
225 "is this square completely enclosed by this circle," but I've
226 forgotten too much trig to know it... (That's not really the
227 right question anyway, but the right question is too hard.) */
228 double x1 = ((double) (-circles [i].x)) / ((double) radius);
229 double y1 = ((double) (-circles [i].y)) / ((double) radius);
230 double x2 = ((double) (width - circles [i].x)) / ((double) radius);
231 double y2 = ((double) (height - circles [i].y)) / ((double) radius);
232 x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2;
233 if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1)
238 (cmode == seuss_mode || /* drawing all circles, or */
239 circles [0].increment < 0)) /* on the way back in */
242 (cmode == seuss_mode ? pixmap : window),
243 (cmode == seuss_mode ? draw_gc : merge_gc),
244 circles [i].x - radius, circles [i].y - radius,
245 radius * 2, radius * 2, 0, 360*64);
247 circles [i].radius += inc;
250 if (cycle_p && cmode != seuss_mode)
253 static struct timeval then = { 0, };
255 #ifdef GETTIMEOFDAY_TWO_ARGS
257 gettimeofday(&now, &tzp);
261 diff = (((now.tv_sec - then.tv_sec) * 1000000) +
262 (now.tv_usec - then.tv_usec));
263 if (diff > cycle_delay)
265 rotate_colors (dpy, cmap, colors, ncolors, 1);
270 if (anim_p && !first_time_p)
271 inhibit_sleep = !done;
277 first_time_p = False;
278 for (i = 0; i < count; i++)
280 circles [i].x += circles [i].dx;
281 circles [i].y += circles [i].dy;
282 circles [i].radius %= circles [i].increment;
283 if (circles [i].x < 0 || circles [i].x >= width)
285 circles [i].dx = -circles [i].dx;
286 circles [i].x += (2 * circles [i].dx);
288 if (circles [i].y < 0 || circles [i].y >= height)
290 circles [i].dy = -circles [i].dy;
291 circles [i].y += (2 * circles [i].dy);
295 else if (circles [0].increment < 0)
297 /* We've zoomed out and the screen is blank -- re-pick the
298 center points, and shift the colors.
301 init_circles_1 (dpy, window);
304 fg_index = (fg_index + 1) % ncolors;
305 bg_index = (fg_index + (ncolors/2)) % ncolors;
306 XSetForeground (dpy, copy_gc, colors [fg_index].pixel);
307 XSetBackground (dpy, copy_gc, colors [bg_index].pixel);
311 /* Sometimes go out from the inside instead of the outside */
312 else if ((random () % 10) == 0)
317 unsigned long swap = fg_index;
320 XSetForeground (dpy, copy_gc, colors [fg_index].pixel);
321 XSetBackground (dpy, copy_gc, colors [bg_index].pixel);
324 iterations = 0; /* ick */
325 for (i = 0; i < count; i++)
326 circles [i].radius %= circles [i].increment;
331 oiterations = iterations;
332 for (i = 0; i < count; i++)
334 circles [i].increment = -circles [i].increment;
335 circles [i].radius += (2 * circles [i].increment);
341 XCopyPlane (dpy, pixmap, buffer, merge_gc, 0, 0, width, height, 0, 0, 1);
342 else if (cmode != seuss_mode)
349 if (fg_index >= ncolors) fg_index = 0;
350 if (bg_index >= ncolors) bg_index = 0;
351 XSetForeground (dpy, merge_gc, colors [fg_index].pixel);
354 if (circles [0].increment >= 0)
355 inhibit_sleep = True;
356 else if (done && cmode == seuss_mode)
357 XFillRectangle (dpy, window, merge_gc, 0, 0, width, height);
360 XCopyPlane (dpy, pixmap, window, merge_gc, 0, 0, width, height, 0, 0, 1);
362 /* buffer is only used in seuss-mode or anim-mode */
363 if (buffer && (anim_p
364 ? (done || (first_time_p && (iterations & 1)))
367 XCopyPlane (dpy, buffer, window, copy_gc, 0, 0, width, height, 0, 0, 1);
370 XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
374 XCopyPlane (dpy, pixmap, window, copy_gc, 0,0,width,height,width,height, 1);
376 XCopyPlane (dpy, buffer, window, copy_gc, 0,0,width,height,0,height, 1);
385 if (delay && !inhibit_sleep)
387 static Bool really_first_p = True;
390 if (done && cycle_p && cmode != seuss_mode && !really_first_p)
393 if (! (random() % 10))
397 really_first_p = False;
401 if (cycle_p && cycle_delay)
406 rotate_colors (dpy, cmap, colors, ncolors, direction);
417 char *progclass = "Halo";
419 char *defaults [] = {
420 "*background: black",
421 "*foreground: white",
422 "*colorMode: random",
428 "*cycleDelay: 100000",
432 XrmOptionDescRec options [] = {
433 { "-count", ".count", XrmoptionSepArg, 0 },
434 { "-delay", ".delay", XrmoptionSepArg, 0 },
435 { "-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0 },
436 { "-animate", ".animate", XrmoptionNoArg, "True" },
437 { "-mode", ".colorMode", XrmoptionSepArg, 0 },
438 { "-colors", ".colors", XrmoptionSepArg, 0 },
439 { "-cycle", ".cycle", XrmoptionNoArg, "True" },
440 { "-no-cycle", ".cycle", XrmoptionNoArg, "False" },
445 screenhack (Display *dpy, Window window)
447 init_circles (dpy, window);
449 run_circles (dpy, window);