1 /* xscreensaver, Copyright (c) 1993, 1995, 1996, 1997, 1998, 1999, 2003, 2006
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
40 struct circle *circles;
41 int count, global_count;
42 Pixmap pixmap, buffer;
43 int width, height, global_inc;
45 unsigned long fg_pixel, bg_pixel;
46 GC draw_gc, erase_gc, copy_gc, merge_gc;
57 Bool done_once_no_really;
62 #define min(x,y) ((x)<(y)?(x):(y))
63 #define max(x,y) ((x)>(y)?(x):(y))
66 init_circles_1 (struct state *st)
69 st->count = (st->global_count ? st->global_count
70 : (3 + (random () % max (1, (min (st->width, st->height) / 50)))
71 + (random () % max (1, (min (st->width, st->height) / 50)))));
72 st->circles = (struct circle *) malloc (st->count * sizeof (struct circle));
73 for (i = 0; i < st->count; i++)
75 st->circles [i].x = 10 + random () % (st->width - 20);
76 st->circles [i].y = 10 + random () % (st->height - 20);
78 st->circles [i].increment = st->global_inc;
80 { /* prefer smaller increments to larger ones */
82 int inc = ((random()%j) + (random()%j) + (random()%j)) - ((j*3)/2);
83 if (inc < 0) inc = -inc + 3;
84 st->circles [i].increment = inc + 3;
86 st->circles [i].radius = random () % st->circles [i].increment;
87 st->circles [i].dx = ((random () % 3) - 1) * (1 + random () % 5);
88 st->circles [i].dy = ((random () % 3) - 1) * (1 + random () % 5);
93 halo_init (Display *dpy, Window window)
95 struct state *st = (struct state *) calloc (1, sizeof(*st));
97 XWindowAttributes xgwa;
101 XGetWindowAttributes (st->dpy, st->window, &xgwa);
102 st->cmap = xgwa.colormap;
103 st->global_count = get_integer_resource (st->dpy, "count", "Integer");
104 if (st->global_count < 0) st->global_count = 0;
105 st->global_inc = get_integer_resource (st->dpy, "increment", "Integer");
106 if (st->global_inc < 0) st->global_inc = 0;
107 st->anim_p = get_boolean_resource (st->dpy, "animate", "Boolean");
108 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
109 st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer") * 1000000;
110 mode_str = get_string_resource (st->dpy, "colorMode", "ColorMode");
111 if (! mode_str) cmode = random_mode;
112 else if (!strcmp (mode_str, "seuss")) cmode = seuss_mode;
113 else if (!strcmp (mode_str, "ramp")) cmode = ramp_mode;
114 else if (!strcmp (mode_str, "random")) cmode = random_mode;
117 "%s: colorMode must be seuss, ramp, or random, not \"%s\"\n",
122 if (mono_p) cmode = seuss_mode;
123 if (cmode == random_mode)
124 cmode = ((random()&3) == 1) ? ramp_mode : seuss_mode;
126 if (cmode == ramp_mode)
127 st->anim_p = False; /* This combo doesn't work right... */
129 st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
130 if (st->ncolors < 2) st->ncolors = 2;
131 if (st->ncolors <= 2) mono_p = True;
136 st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
141 else if (random() % (cmode == seuss_mode ? 2 : 10))
142 make_uniform_colormap (st->dpy, xgwa.visual, st->cmap, st->colors, &st->ncolors,
145 make_smooth_colormap (st->dpy, xgwa.visual, st->cmap, st->colors, &st->ncolors,
148 if (st->ncolors <= 2) mono_p = True;
149 if (mono_p) cmode = seuss_mode;
153 st->fg_pixel = get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
154 st->bg_pixel = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
159 st->bg_index = st->ncolors / 4;
160 if (st->fg_index == st->bg_index) st->bg_index++;
161 st->fg_pixel = st->colors[st->fg_index].pixel;
162 st->bg_pixel = st->colors[st->bg_index].pixel;
165 st->width = max (50, xgwa.width);
166 st->height = max (50, xgwa.height);
169 st->width/=2; st->height/=2;
172 st->pixmap = XCreatePixmap (st->dpy, st->window, st->width, st->height, 1);
173 if (cmode == seuss_mode)
174 st->buffer = XCreatePixmap (st->dpy, st->window, st->width, st->height, 1);
180 st->draw_gc = XCreateGC (st->dpy, st->pixmap, GCForeground | GCBackground, &gcv);
182 st->erase_gc = XCreateGC (st->dpy, st->pixmap, GCForeground, &gcv);
183 gcv.foreground = st->fg_pixel;
184 gcv.background = st->bg_pixel;
185 st->copy_gc = XCreateGC (st->dpy, st->window, GCForeground | GCBackground, &gcv);
188 jwxyz_XSetAntiAliasing (dpy, st->draw_gc, False);
189 jwxyz_XSetAntiAliasing (dpy, st->erase_gc, False);
190 jwxyz_XSetAntiAliasing (dpy, st->copy_gc, False);
193 if (cmode == seuss_mode)
197 gcv.function = GXxor;
198 st->merge_gc = XCreateGC (st->dpy, st->pixmap,
199 GCForeground | GCBackground | GCFunction, &gcv);
203 gcv.foreground = st->fg_pixel;
204 gcv.background = st->bg_pixel;
205 gcv.function = GXcopy;
206 st->merge_gc = XCreateGC (st->dpy, st->window,
207 GCForeground | GCBackground | GCFunction, &gcv);
211 XClearWindow (st->dpy, st->window);
212 if (st->buffer) XFillRectangle (st->dpy, st->buffer, st->erase_gc, 0, 0, st->width, st->height);
217 halo_draw (Display *dpy, Window window, void *closure)
219 struct state *st = (struct state *) closure;
222 Bool inhibit_sleep = False;
223 int this_delay = st->delay;
225 XFillRectangle (st->dpy, st->pixmap, st->erase_gc, 0, 0, st->width, st->height);
226 for (i = 0; i < st->count; i++)
228 int radius = st->circles [i].radius;
229 int inc = st->circles [i].increment;
231 if (! (st->iterations & 1)) /* never stop on an odd number of iterations */
233 else if (radius == 0) /* eschew inf */
235 else if (radius < 0) /* stop when the circles are points */
237 else /* stop when the circles fill the st->window */
239 /* Probably there's a simpler way to ask the musical question,
240 "is this square completely enclosed by this circle," but I've
241 forgotten too much trig to know it... (That's not really the
242 right question anyway, but the right question is too hard.) */
243 double x1 = ((double) (-st->circles [i].x)) / ((double) radius);
244 double y1 = ((double) (-st->circles [i].y)) / ((double) radius);
245 double x2 = ((double) (st->width - st->circles [i].x)) / ((double) radius);
246 double y2 = ((double) (st->height - st->circles [i].y)) / ((double) radius);
247 x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2;
248 if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1)
253 (cmode == seuss_mode || /* drawing all circles, or */
254 st->circles [0].increment < 0)) /* on the way back in */
257 (cmode == seuss_mode ? st->pixmap : st->window),
258 (cmode == seuss_mode ? st->draw_gc : st->merge_gc),
259 st->circles [i].x - radius, st->circles [i].y - radius,
260 radius * 2, radius * 2, 0, 360*64);
262 st->circles [i].radius += inc;
265 if (st->anim_p && !st->done_once)
266 inhibit_sleep = !done;
272 st->done_once = True;
273 for (i = 0; i < st->count; i++)
275 st->circles [i].x += st->circles [i].dx;
276 st->circles [i].y += st->circles [i].dy;
277 st->circles [i].radius %= st->circles [i].increment;
278 if (st->circles [i].x < 0 || st->circles [i].x >= st->width)
280 st->circles [i].dx = -st->circles [i].dx;
281 st->circles [i].x += (2 * st->circles [i].dx);
283 if (st->circles [i].y < 0 || st->circles [i].y >= st->height)
285 st->circles [i].dy = -st->circles [i].dy;
286 st->circles [i].y += (2 * st->circles [i].dy);
290 else if (st->circles [0].increment < 0)
292 /* We've zoomed out and the screen is blank -- re-pick the
293 center points, and shift the st->colors.
299 st->fg_index = (st->fg_index + 1) % st->ncolors;
300 st->bg_index = (st->fg_index + (st->ncolors/2)) % st->ncolors;
301 XSetForeground (st->dpy, st->copy_gc, st->colors [st->fg_index].pixel);
302 XSetBackground (st->dpy, st->copy_gc, st->colors [st->bg_index].pixel);
305 /* Sometimes go out from the inside instead of the outside */
306 else if (st->clear_tick == 0 && ((random () % 3) == 0))
308 st->iterations = 0; /* ick */
309 for (i = 0; i < st->count; i++)
310 st->circles [i].radius %= st->circles [i].increment;
312 st->clear_tick = ((random() % 8) + 4) | 1; /* must be odd */
316 for (i = 0; i < st->count; i++)
318 st->circles [i].increment = -st->circles [i].increment;
319 st->circles [i].radius += (2 * st->circles [i].increment);
325 XCopyPlane (st->dpy, st->pixmap, st->buffer, st->merge_gc, 0, 0, st->width, st->height, 0, 0, 1);
326 else if (cmode != seuss_mode)
333 if (st->fg_index >= st->ncolors) st->fg_index = 0;
334 if (st->bg_index >= st->ncolors) st->bg_index = 0;
335 XSetForeground (st->dpy, st->merge_gc, st->colors [st->fg_index].pixel);
338 if (st->circles [0].increment >= 0)
339 inhibit_sleep = True;
340 else if (done && cmode == seuss_mode)
341 XFillRectangle (st->dpy, st->window, st->merge_gc, 0, 0, st->width, st->height);
344 XCopyPlane (st->dpy, st->pixmap, st->window, st->merge_gc, 0, 0, st->width, st->height, 0, 0, 1);
346 /* st->buffer is only used in seuss-mode or anim-mode */
347 if (st->buffer && (st->anim_p
348 ? (done || (!st->done_once && (st->iterations & 1)))
349 : (st->iterations & 1)))
351 XCopyPlane (st->dpy, st->buffer, st->window, st->copy_gc, 0, 0, st->width, st->height, 0, 0, 1);
352 if (st->anim_p && done)
353 XFillRectangle (st->dpy, st->buffer, st->erase_gc, 0, 0, st->width, st->height);
357 XCopyPlane (st->dpy, st->pixmap, st->window, st->copy_gc, 0,0,st->width,st->height,st->width,st->height, 1);
359 XCopyPlane (st->dpy, st->buffer, st->window, st->copy_gc, 0,0,st->width,st->height,0,st->height, 1);
367 if (st->delay && !inhibit_sleep)
371 if (cmode == seuss_mode && st->anim_p)
377 st->done_once_no_really = True;
380 if (done && st->clear_tick > 0)
385 XClearWindow (st->dpy, st->window);
386 if (st->buffer) XFillRectangle (st->dpy, st->buffer, st->erase_gc, 0,0,st->width,st->height);
390 if (inhibit_sleep) this_delay = 0;
397 static const char *halo_defaults [] = {
398 ".background: black",
399 ".foreground: white",
400 "*colorMode: random",
410 static XrmOptionDescRec halo_options [] = {
411 { "-count", ".count", XrmoptionSepArg, 0 },
412 { "-delay", ".delay", XrmoptionSepArg, 0 },
413 { "-animate", ".animate", XrmoptionNoArg, "True" },
414 { "-mode", ".colorMode", XrmoptionSepArg, 0 },
415 { "-colors", ".colors", XrmoptionSepArg, 0 },
420 halo_reshape (Display *dpy, Window window, void *closure,
421 unsigned int w, unsigned int h)
426 halo_event (Display *dpy, Window window, void *closure, XEvent *event)
432 halo_free (Display *dpy, Window window, void *closure)
436 XSCREENSAVER_MODULE ("Halo", halo)