ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-1.27.tar.Z
[xscreensaver] / hacks / halo.c
1 /* xscreensaver, Copyright (c) 1993, 1995 Jamie Zawinski <jwz@netscape.com>
2  *
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 
9  * implied warranty.
10  */
11
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
15    a real drag...
16
17    This would probably look good with shapes other than circles as well.
18
19  */
20
21 #include "screenhack.h"
22 #include <stdio.h>
23
24 struct circle {
25   int x, y, radius;
26   int increment;
27   int dx, dy;
28 };
29
30 static enum color_mode {
31   seuss_mode, ramp_mode, random_mode
32 } cmode;
33
34
35 static struct circle *circles;
36 static int count, global_count;
37 static Pixmap pixmap, buffer;
38 static int width, height, global_inc;
39 static int delay;
40 static unsigned long fg_pixel, bg_pixel;
41 static XColor fgc, bgc;
42 static GC draw_gc, erase_gc, copy_gc, merge_gc;
43 static Bool anim_p;
44 static Colormap cmap;
45
46 #define min(x,y) ((x)<(y)?(x):(y))
47 #define max(x,y) ((x)>(y)?(x):(y))
48
49 static void
50 init_circles_1 (dpy, window)
51      Display *dpy;
52      Window window;
53 {
54   int i;
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++)
60     {
61       circles [i].x = 10 + random () % (width - 20);
62       circles [i].y = 10 + random () % (height - 20);
63       if (global_inc)
64       circles [i].increment = global_inc;
65       else
66         { /* prefer smaller increments to larger ones */
67           int j = 8;
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;
71         }
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);
75     }
76 }
77
78 static void
79 init_circles (dpy, window)
80      Display *dpy;
81      Window window;
82 {
83   XGCValues gcv;
84   XWindowAttributes xgwa;
85   char *mode_str = 0;
86   XGetWindowAttributes (dpy, window, &xgwa);
87   cmap = xgwa.colormap;
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;
99   else {
100     fprintf (stderr,
101              "%s: colorMode must be seuss, ramp, or random, not \"%s\"\n",
102              progname, mode_str);
103     exit (1);
104   }
105
106   if (mono_p) cmode = seuss_mode;
107   if (cmode == random_mode)
108     cmode = ((random()&3) == 1) ? ramp_mode : seuss_mode;
109
110   if (cmode == ramp_mode)
111     anim_p = False;    /* This combo doesn't work right... */
112
113   if (mono_p)
114     {
115       fg_pixel = get_pixel_resource ("foreground","Foreground", dpy, cmap);
116       bg_pixel = get_pixel_resource ("background","Background", dpy, cmap);
117     }
118   else
119     {
120       int r  = random() % 360;
121       int r2 = (random() % 180) + 45;
122       double fs, bs;
123       if (cmode == seuss_mode)
124         fs = 0.5, bs = 1.0;
125       else
126         fs = 1.0, bs = 0.1;
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;
133     }
134
135   width = max (50, xgwa.width);
136   height = max (50, xgwa.height);
137
138 #ifdef DEBUG
139   width/=2; height/=2;
140 #endif
141
142   pixmap = XCreatePixmap (dpy, window, width, height, 1);
143   if (cmode == seuss_mode)
144     buffer = XCreatePixmap (dpy, window, width, height, 1);
145   else
146     buffer = 0;
147
148   gcv.foreground = 1;
149   gcv.background = 0;
150   draw_gc = XCreateGC (dpy, pixmap, GCForeground | GCBackground, &gcv);
151   gcv.foreground = 0;
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);
156
157   if (cmode == seuss_mode)
158     {
159       gcv.foreground = 1;
160       gcv.background = 0;
161       gcv.function = GXxor;
162       merge_gc = XCreateGC (dpy, pixmap,
163                             GCForeground | GCBackground | GCFunction, &gcv);
164     }
165   else
166     {
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);
172     }
173
174   init_circles_1 (dpy, window);
175   XClearWindow (dpy, window);
176   if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
177 }
178
179 static void
180 run_circles (dpy, window)
181      Display *dpy;
182      Window window;
183 {
184   int i;
185   static int iterations = 0;
186   static int oiterations = 0;
187   static Bool first_time_p = True;
188   Bool done = False;
189   Bool inhibit_sleep = False;
190   XFillRectangle (dpy, pixmap, erase_gc, 0, 0, width, height);
191   for (i = 0; i < count; i++)
192     {
193       int radius = circles [i].radius;
194       int inc = circles [i].increment;
195       if (! (iterations & 1))
196         ;
197       else if (radius == 0)
198         ;
199       else if (radius < 0)
200         done = True;
201       else
202         {
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)
213             done = True;
214         }
215       if (radius > 0 &&
216           (cmode == seuss_mode || circles [0].increment < 0))
217         XFillArc (dpy,
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;
223     }
224
225   if (anim_p && !first_time_p)
226     inhibit_sleep = !done;
227
228   if (done)
229     {
230       if (anim_p)
231         {
232           first_time_p = False;
233           for (i = 0; i < count; i++)
234             {
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)
239                 {
240                   circles [i].dx = -circles [i].dx;
241                   circles [i].x += (2 * circles [i].dx);
242                 }
243               if (circles [i].y < 0 || circles [i].y >= height)
244                 {
245                   circles [i].dy = -circles [i].dy;
246                   circles [i].y += (2 * circles [i].dy);
247                 }
248             }
249         }
250       else if (circles [0].increment < 0)
251         {
252           free (circles);
253           init_circles_1 (dpy, window);
254           if (! mono_p)
255             {
256               XColor d1, d2;
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);
261               d1 = fgc;
262               d2 = bgc;
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);
269             }
270         }
271 #if 0
272       else if ((random () % 2) == 0)
273         {
274           iterations = 0; /* ick */
275           for (i = 0; i < count; i++)
276             circles [i].radius %= circles [i].increment;
277         }
278 #endif
279       else
280         {
281           oiterations = iterations;
282           for (i = 0; i < count; i++)
283             {
284               circles [i].increment = -circles [i].increment;
285               circles [i].radius += (2 * circles [i].increment);
286             }
287         }
288     }
289
290   if (buffer)
291     XCopyPlane (dpy, pixmap, buffer, merge_gc, 0, 0, width, height, 0, 0, 1);
292   else if (cmode != seuss_mode)
293     {
294       static int ncolors = 0;
295       static XColor *colors = 0;
296       if (circles [0].increment >= 0)
297         inhibit_sleep = True;
298       else if (done)
299         {
300           int fgh, bgh;
301           double fgs, fgv, bgs, bgv;
302           if (colors)
303             for (i = 0; i < ncolors; i++)
304               XFreeColors (dpy, cmap, &colors [i].pixel, 1, 0);
305
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;
309           colors = ((XColor *)
310                     (colors
311                      ? realloc (colors, sizeof (XColor) * ncolors)
312                      : malloc (sizeof (XColor) * ncolors)));
313           
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);
318         }
319       else
320         {
321           XSetForeground (dpy, merge_gc, colors [iterations].pixel);
322         }
323     }
324   else
325     XCopyPlane (dpy, pixmap, window, merge_gc, 0, 0, width, height, 0, 0, 1);
326
327   if (buffer && (anim_p
328                  ? (done || (first_time_p && (iterations & 1)))
329                  : (iterations & 1)))
330     {
331       XCopyPlane (dpy, buffer, window, copy_gc, 0, 0, width, height, 0, 0, 1);
332       XSync (dpy, True);
333       if (anim_p && done)
334         XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
335     }
336 #ifdef DEBUG
337   XCopyPlane (dpy, pixmap, window, copy_gc, 0,0,width,height,width,height, 1);
338   if (buffer)
339     XCopyPlane (dpy, buffer, window, copy_gc, 0,0,width,height,0,height, 1);
340   XSync (dpy, True);
341 #endif
342
343   if (done)
344     iterations = 0;
345   else
346     iterations++;
347
348   if (delay && !inhibit_sleep) usleep (delay);
349 }
350
351 \f
352 char *progclass = "Halo";
353
354 char *defaults [] = {
355   "Halo.background:     black",         /* to placate SGI */
356   "Halo.foreground:     white",
357   "*colorMode:          random",
358   "*count:              0",
359   "*delay:              100000",
360   0
361 };
362
363 XrmOptionDescRec options [] = {
364   { "-count",           ".count",       XrmoptionSepArg, 0 },
365   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
366   { "-animate",         ".animate",     XrmoptionNoArg, "True" },
367   { "-mode",            ".colorMode",   XrmoptionSepArg, 0 }
368 };
369 int options_size = (sizeof (options) / sizeof (options[0]));
370
371 void
372 screenhack (dpy, window)
373      Display *dpy;
374      Window window;
375 {
376   init_circles (dpy, window);
377   while (1)
378     run_circles (dpy, window);
379 }