1 /* xscreensaver, Copyright (c) 1993, 1995, 1996, 1997, 1998, 1999
2 * Jamie Zawinski <jwz@jwz.org>
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");
129 /* If the visual isn't color-indexed, don't bother trying to
130 allocate writable cells. */
131 if (cycle_p && !has_writable_cells (xgwa.screen, xgwa.visual))
137 else if (random() % (cmode == seuss_mode ? 2 : 10))
138 make_uniform_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
139 True, &cycle_p, True);
141 make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
142 True, &cycle_p, True);
144 if (ncolors <= 2) mono_p = True;
145 if (mono_p) cycle_p = False;
146 if (mono_p) cmode = seuss_mode;
150 fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
151 bg_pixel = get_pixel_resource ("background", "Background", dpy, cmap);
156 bg_index = ncolors / 4;
157 if (fg_index == bg_index) bg_index++;
158 fg_pixel = colors[fg_index].pixel;
159 bg_pixel = colors[bg_index].pixel;
162 width = max (50, xgwa.width);
163 height = max (50, xgwa.height);
169 pixmap = XCreatePixmap (dpy, window, width, height, 1);
170 if (cmode == seuss_mode)
171 buffer = XCreatePixmap (dpy, window, width, height, 1);
177 draw_gc = XCreateGC (dpy, pixmap, GCForeground | GCBackground, &gcv);
179 erase_gc = XCreateGC (dpy, pixmap, GCForeground, &gcv);
180 gcv.foreground = fg_pixel;
181 gcv.background = bg_pixel;
182 copy_gc = XCreateGC (dpy, window, GCForeground | GCBackground, &gcv);
184 if (cmode == seuss_mode)
188 gcv.function = GXxor;
189 merge_gc = XCreateGC (dpy, pixmap,
190 GCForeground | GCBackground | GCFunction, &gcv);
194 gcv.foreground = fg_pixel;
195 gcv.background = bg_pixel;
196 gcv.function = GXcopy;
197 merge_gc = XCreateGC (dpy, window,
198 GCForeground | GCBackground | GCFunction, &gcv);
201 init_circles_1 (dpy, window);
202 XClearWindow (dpy, window);
203 if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
207 run_circles (Display *dpy, Window window)
210 static int iterations = 0;
211 static int oiterations = 0;
212 static Bool first_time_p = True;
214 Bool inhibit_sleep = False;
215 static int clear_tick = 0;
217 XFillRectangle (dpy, pixmap, erase_gc, 0, 0, width, height);
218 for (i = 0; i < count; i++)
220 int radius = circles [i].radius;
221 int inc = circles [i].increment;
223 if (! (iterations & 1)) /* never stop on an odd number of iterations */
225 else if (radius == 0) /* eschew inf */
227 else if (radius < 0) /* stop when the circles are points */
229 else /* stop when the circles fill the window */
231 /* Probably there's a simpler way to ask the musical question,
232 "is this square completely enclosed by this circle," but I've
233 forgotten too much trig to know it... (That's not really the
234 right question anyway, but the right question is too hard.) */
235 double x1 = ((double) (-circles [i].x)) / ((double) radius);
236 double y1 = ((double) (-circles [i].y)) / ((double) radius);
237 double x2 = ((double) (width - circles [i].x)) / ((double) radius);
238 double y2 = ((double) (height - circles [i].y)) / ((double) radius);
239 x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2;
240 if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1)
245 (cmode == seuss_mode || /* drawing all circles, or */
246 circles [0].increment < 0)) /* on the way back in */
249 (cmode == seuss_mode ? pixmap : window),
250 (cmode == seuss_mode ? draw_gc : merge_gc),
251 circles [i].x - radius, circles [i].y - radius,
252 radius * 2, radius * 2, 0, 360*64);
254 circles [i].radius += inc;
257 if (cycle_p && cmode != seuss_mode)
260 static struct timeval then = { 0, };
262 #ifdef GETTIMEOFDAY_TWO_ARGS
264 gettimeofday(&now, &tzp);
268 diff = (((now.tv_sec - then.tv_sec) * 1000000) +
269 (now.tv_usec - then.tv_usec));
270 if (diff > cycle_delay)
272 rotate_colors (dpy, cmap, colors, ncolors, 1);
277 if (anim_p && !first_time_p)
278 inhibit_sleep = !done;
284 first_time_p = False;
285 for (i = 0; i < count; i++)
287 circles [i].x += circles [i].dx;
288 circles [i].y += circles [i].dy;
289 circles [i].radius %= circles [i].increment;
290 if (circles [i].x < 0 || circles [i].x >= width)
292 circles [i].dx = -circles [i].dx;
293 circles [i].x += (2 * circles [i].dx);
295 if (circles [i].y < 0 || circles [i].y >= height)
297 circles [i].dy = -circles [i].dy;
298 circles [i].y += (2 * circles [i].dy);
302 else if (circles [0].increment < 0)
304 /* We've zoomed out and the screen is blank -- re-pick the
305 center points, and shift the colors.
308 init_circles_1 (dpy, window);
311 fg_index = (fg_index + 1) % ncolors;
312 bg_index = (fg_index + (ncolors/2)) % ncolors;
313 XSetForeground (dpy, copy_gc, colors [fg_index].pixel);
314 XSetBackground (dpy, copy_gc, colors [bg_index].pixel);
317 /* Sometimes go out from the inside instead of the outside */
318 else if (clear_tick == 0 && ((random () % 3) == 0))
320 iterations = 0; /* ick */
321 for (i = 0; i < count; i++)
322 circles [i].radius %= circles [i].increment;
324 clear_tick = ((random() % 8) + 4) | 1; /* must be odd */
328 oiterations = iterations;
329 for (i = 0; i < count; i++)
331 circles [i].increment = -circles [i].increment;
332 circles [i].radius += (2 * circles [i].increment);
338 XCopyPlane (dpy, pixmap, buffer, merge_gc, 0, 0, width, height, 0, 0, 1);
339 else if (cmode != seuss_mode)
346 if (fg_index >= ncolors) fg_index = 0;
347 if (bg_index >= ncolors) bg_index = 0;
348 XSetForeground (dpy, merge_gc, colors [fg_index].pixel);
351 if (circles [0].increment >= 0)
352 inhibit_sleep = True;
353 else if (done && cmode == seuss_mode)
354 XFillRectangle (dpy, window, merge_gc, 0, 0, width, height);
357 XCopyPlane (dpy, pixmap, window, merge_gc, 0, 0, width, height, 0, 0, 1);
359 /* buffer is only used in seuss-mode or anim-mode */
360 if (buffer && (anim_p
361 ? (done || (first_time_p && (iterations & 1)))
364 XCopyPlane (dpy, buffer, window, copy_gc, 0, 0, width, height, 0, 0, 1);
367 XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
371 XCopyPlane (dpy, pixmap, window, copy_gc, 0,0,width,height,width,height, 1);
373 XCopyPlane (dpy, buffer, window, copy_gc, 0,0,width,height,0,height, 1);
382 if (delay && !inhibit_sleep)
384 static Bool really_first_p = True;
387 if (done && cycle_p && cmode != seuss_mode && !really_first_p)
390 if (! (random() % 10))
395 screenhack_handle_events (dpy);
397 if (cycle_p && cycle_delay)
402 rotate_colors (dpy, cmap, colors, ncolors, direction);
404 screenhack_handle_events (dpy);
408 else if (cmode != seuss_mode &&
409 done && !really_first_p && cycle_delay > 0)
410 usleep (cycle_delay * 50);
415 really_first_p = False;
418 if (done && clear_tick > 0)
423 XClearWindow (dpy, window);
424 if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0,0,width,height);
430 char *progclass = "Halo";
432 char *defaults [] = {
433 ".background: black",
434 ".foreground: white",
435 "*colorMode: random",
441 "*cycleDelay: 100000",
445 XrmOptionDescRec options [] = {
446 { "-count", ".count", XrmoptionSepArg, 0 },
447 { "-delay", ".delay", XrmoptionSepArg, 0 },
448 { "-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0 },
449 { "-animate", ".animate", XrmoptionNoArg, "True" },
450 { "-mode", ".colorMode", XrmoptionSepArg, 0 },
451 { "-colors", ".colors", XrmoptionSepArg, 0 },
452 { "-cycle", ".cycle", XrmoptionNoArg, "True" },
453 { "-no-cycle", ".cycle", XrmoptionNoArg, "False" },
458 screenhack (Display *dpy, Window window)
460 init_circles (dpy, window);
463 run_circles (dpy, window);
464 screenhack_handle_events (dpy);