1 /* xscreensaver, Copyright (c) 1993-2013 Jamie Zawinski <jwz@jwz.org>
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
39 struct circle *circles;
40 int count, global_count;
41 Pixmap pixmap, buffer;
42 int width, height, global_inc;
44 unsigned long fg_pixel, bg_pixel;
45 GC draw_gc, erase_gc, copy_gc, merge_gc;
56 Bool done_once_no_really;
61 #define min(x,y) ((x)<(y)?(x):(y))
62 #define max(x,y) ((x)>(y)?(x):(y))
65 init_circles_1 (struct state *st)
68 st->count = (st->global_count ? st->global_count
69 : (3 + (random () % max (1, (min (st->width, st->height) / 50)))
70 + (random () % max (1, (min (st->width, st->height) / 50)))));
71 st->circles = (struct circle *) malloc (st->count * sizeof (struct circle));
72 for (i = 0; i < st->count; i++)
74 st->circles [i].x = 10 + random () % (st->width - 20);
75 st->circles [i].y = 10 + random () % (st->height - 20);
77 st->circles [i].increment = st->global_inc;
79 { /* prefer smaller increments to larger ones */
81 int inc = ((random()%j) + (random()%j) + (random()%j)) - ((j*3)/2);
82 if (inc < 0) inc = -inc + 3;
83 st->circles [i].increment = inc + 3;
85 st->circles [i].radius = random () % st->circles [i].increment;
86 st->circles [i].dx = ((random () % 3) - 1) * (1 + random () % 5);
87 st->circles [i].dy = ((random () % 3) - 1) * (1 + random () % 5);
92 halo_init (Display *dpy, Window window)
94 struct state *st = (struct state *) calloc (1, sizeof(*st));
96 XWindowAttributes xgwa;
100 XGetWindowAttributes (st->dpy, st->window, &xgwa);
101 st->cmap = xgwa.colormap;
102 st->global_count = get_integer_resource (st->dpy, "count", "Integer");
103 if (st->global_count < 0) st->global_count = 0;
104 st->global_inc = get_integer_resource (st->dpy, "increment", "Integer");
105 if (st->global_inc < 0) st->global_inc = 0;
106 st->anim_p = get_boolean_resource (st->dpy, "animate", "Boolean");
107 st->delay = get_integer_resource (st->dpy, "delay", "Integer");
108 st->delay2 = get_integer_resource (st->dpy, "delay2", "Integer") * 1000000;
109 mode_str = get_string_resource (st->dpy, "colorMode", "ColorMode");
110 if (! mode_str) cmode = random_mode;
111 else if (!strcmp (mode_str, "seuss")) cmode = seuss_mode;
112 else if (!strcmp (mode_str, "ramp")) cmode = ramp_mode;
113 else if (!strcmp (mode_str, "random")) cmode = random_mode;
116 "%s: colorMode must be seuss, ramp, or random, not \"%s\"\n",
121 if (mono_p) cmode = seuss_mode;
122 if (cmode == random_mode)
123 cmode = ((random()&3) == 1) ? ramp_mode : seuss_mode;
125 if (cmode == ramp_mode)
126 st->anim_p = False; /* This combo doesn't work right... */
128 st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
129 if (st->ncolors < 2) st->ncolors = 2;
130 if (st->ncolors <= 2) mono_p = True;
135 st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
140 else if (random() % (cmode == seuss_mode ? 2 : 10))
141 make_uniform_colormap (xgwa.screen, xgwa.visual, st->cmap,
142 st->colors, &st->ncolors,
145 make_smooth_colormap (xgwa.screen, xgwa.visual, st->cmap,
146 st->colors, &st->ncolors,
149 if (st->ncolors <= 2) mono_p = True;
150 if (mono_p) cmode = seuss_mode;
154 st->fg_pixel = get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
155 st->bg_pixel = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
160 st->bg_index = st->ncolors / 4;
161 if (st->fg_index == st->bg_index) st->bg_index++;
162 st->fg_pixel = st->colors[st->fg_index].pixel;
163 st->bg_pixel = st->colors[st->bg_index].pixel;
166 st->width = max (50, xgwa.width);
167 st->height = max (50, xgwa.height);
170 st->width/=2; st->height/=2;
173 st->pixmap = XCreatePixmap (st->dpy, st->window, st->width, st->height, 1);
174 if (cmode == seuss_mode)
175 st->buffer = XCreatePixmap (st->dpy, st->window, st->width, st->height, 1);
181 st->draw_gc = XCreateGC (st->dpy, st->pixmap, GCForeground | GCBackground, &gcv);
183 st->erase_gc = XCreateGC (st->dpy, st->pixmap, GCForeground, &gcv);
184 gcv.foreground = st->fg_pixel;
185 gcv.background = st->bg_pixel;
186 st->copy_gc = XCreateGC (st->dpy, st->window, GCForeground | GCBackground, &gcv);
189 jwxyz_XSetAntiAliasing (dpy, st->draw_gc, False);
190 jwxyz_XSetAntiAliasing (dpy, st->erase_gc, False);
191 jwxyz_XSetAntiAliasing (dpy, st->copy_gc, False);
194 if (cmode == seuss_mode)
198 gcv.function = GXxor;
199 st->merge_gc = XCreateGC (st->dpy, st->pixmap,
200 GCForeground | GCBackground | GCFunction, &gcv);
204 gcv.foreground = st->fg_pixel;
205 gcv.background = st->bg_pixel;
206 gcv.function = GXcopy;
207 st->merge_gc = XCreateGC (st->dpy, st->window,
208 GCForeground | GCBackground | GCFunction, &gcv);
212 XClearWindow (st->dpy, st->window);
213 if (st->buffer) XFillRectangle (st->dpy, st->buffer, st->erase_gc, 0, 0, st->width, st->height);
218 halo_draw (Display *dpy, Window window, void *closure)
220 struct state *st = (struct state *) closure;
223 Bool inhibit_sleep = False;
224 int this_delay = st->delay;
226 XFillRectangle (st->dpy, st->pixmap, st->erase_gc, 0, 0, st->width, st->height);
227 for (i = 0; i < st->count; i++)
229 int radius = st->circles [i].radius;
230 int inc = st->circles [i].increment;
232 if (! (st->iterations & 1)) /* never stop on an odd number of iterations */
234 else if (radius == 0) /* eschew inf */
236 else if (radius < 0) /* stop when the circles are points */
238 else /* stop when the circles fill the st->window */
240 /* Probably there's a simpler way to ask the musical question,
241 "is this square completely enclosed by this circle," but I've
242 forgotten too much trig to know it... (That's not really the
243 right question anyway, but the right question is too hard.) */
244 double x1 = ((double) (-st->circles [i].x)) / ((double) radius);
245 double y1 = ((double) (-st->circles [i].y)) / ((double) radius);
246 double x2 = ((double) (st->width - st->circles [i].x)) / ((double) radius);
247 double y2 = ((double) (st->height - st->circles [i].y)) / ((double) radius);
248 x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2;
249 if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1)
254 (cmode == seuss_mode || /* drawing all circles, or */
255 st->circles [0].increment < 0)) /* on the way back in */
258 (cmode == seuss_mode ? st->pixmap : st->window),
259 (cmode == seuss_mode ? st->draw_gc : st->merge_gc),
260 st->circles [i].x - radius, st->circles [i].y - radius,
261 radius * 2, radius * 2, 0, 360*64);
263 st->circles [i].radius += inc;
266 if (st->anim_p && !st->done_once)
267 inhibit_sleep = !done;
273 st->done_once = True;
274 for (i = 0; i < st->count; i++)
276 st->circles [i].x += st->circles [i].dx;
277 st->circles [i].y += st->circles [i].dy;
278 st->circles [i].radius %= st->circles [i].increment;
279 if (st->circles [i].x < 0 || st->circles [i].x >= st->width)
281 st->circles [i].dx = -st->circles [i].dx;
282 st->circles [i].x += (2 * st->circles [i].dx);
284 if (st->circles [i].y < 0 || st->circles [i].y >= st->height)
286 st->circles [i].dy = -st->circles [i].dy;
287 st->circles [i].y += (2 * st->circles [i].dy);
291 else if (st->circles [0].increment < 0)
293 /* We've zoomed out and the screen is blank -- re-pick the
294 center points, and shift the st->colors.
300 st->fg_index = (st->fg_index + 1) % st->ncolors;
301 st->bg_index = (st->fg_index + (st->ncolors/2)) % st->ncolors;
302 XSetForeground (st->dpy, st->copy_gc, st->colors [st->fg_index].pixel);
303 XSetBackground (st->dpy, st->copy_gc, st->colors [st->bg_index].pixel);
306 /* Sometimes go out from the inside instead of the outside */
307 else if (st->clear_tick == 0 && ((random () % 3) == 0))
309 st->iterations = 0; /* ick */
310 for (i = 0; i < st->count; i++)
311 st->circles [i].radius %= st->circles [i].increment;
313 st->clear_tick = ((random() % 8) + 4) | 1; /* must be odd */
317 for (i = 0; i < st->count; i++)
319 st->circles [i].increment = -st->circles [i].increment;
320 st->circles [i].radius += (2 * st->circles [i].increment);
326 XCopyPlane (st->dpy, st->pixmap, st->buffer, st->merge_gc, 0, 0, st->width, st->height, 0, 0, 1);
327 else if (cmode != seuss_mode)
334 if (st->fg_index >= st->ncolors) st->fg_index = 0;
335 if (st->bg_index >= st->ncolors) st->bg_index = 0;
336 XSetForeground (st->dpy, st->merge_gc, st->colors [st->fg_index].pixel);
339 if (st->circles [0].increment >= 0)
340 inhibit_sleep = True;
341 else if (done && cmode == seuss_mode)
342 XFillRectangle (st->dpy, st->window, st->merge_gc, 0, 0, st->width, st->height);
345 XCopyPlane (st->dpy, st->pixmap, st->window, st->merge_gc, 0, 0, st->width, st->height, 0, 0, 1);
347 /* st->buffer is only used in seuss-mode or anim-mode */
348 if (st->buffer && (st->anim_p
349 ? (done || (!st->done_once && (st->iterations & 1)))
350 : (st->iterations & 1)))
352 XCopyPlane (st->dpy, st->buffer, st->window, st->copy_gc, 0, 0, st->width, st->height, 0, 0, 1);
353 if (st->anim_p && done)
354 XFillRectangle (st->dpy, st->buffer, st->erase_gc, 0, 0, st->width, st->height);
358 XCopyPlane (st->dpy, st->pixmap, st->window, st->copy_gc, 0,0,st->width,st->height,st->width,st->height, 1);
360 XCopyPlane (st->dpy, st->buffer, st->window, st->copy_gc, 0,0,st->width,st->height,0,st->height, 1);
368 if (st->delay && !inhibit_sleep)
372 if (cmode == seuss_mode && st->anim_p)
378 st->done_once_no_really = True;
381 if (done && st->clear_tick > 0)
386 XClearWindow (st->dpy, st->window);
387 if (st->buffer) XFillRectangle (st->dpy, st->buffer, st->erase_gc, 0,0,st->width,st->height);
391 if (inhibit_sleep) this_delay = 0;
398 static const char *halo_defaults [] = {
399 ".background: black",
400 ".foreground: white",
401 "*colorMode: random",
409 "*ignoreRotation: True",
414 static XrmOptionDescRec halo_options [] = {
415 { "-count", ".count", XrmoptionSepArg, 0 },
416 { "-delay", ".delay", XrmoptionSepArg, 0 },
417 { "-animate", ".animate", XrmoptionNoArg, "True" },
418 { "-mode", ".colorMode", XrmoptionSepArg, 0 },
419 { "-colors", ".colors", XrmoptionSepArg, 0 },
424 halo_reshape (Display *dpy, Window window, void *closure,
425 unsigned int w, unsigned int h)
430 halo_event (Display *dpy, Window window, void *closure, XEvent *event)
436 halo_free (Display *dpy, Window window, void *closure)
440 XSCREENSAVER_MODULE ("Halo", halo)