ftp://ftp.ntnu.no/old/pub/X11/R5contrib/xscreensaver-1.17.tar.gz
[xscreensaver] / hacks / halo.c
1 /* xscreensaver, Copyright (c) 1993 Jamie Zawinski <jwz@lucid.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
18 #include "screenhack.h"
19
20 struct circle {
21   int x, y, radius;
22   int increment;
23   int dx, dy;
24 };
25
26 static struct circle *circles;
27 static int count, global_count;
28 static Pixmap pixmap, buffer;
29 static int width, height, global_inc;
30 static int delay;
31 static unsigned long fg_pixel, bg_pixel;
32 static XColor fgc, bgc;
33 static Bool xor_p;
34 static GC draw_gc, erase_gc, copy_gc, merge_gc;
35 static Bool anim_p;
36 static Colormap cmap;
37
38 #define min(x,y) ((x)<(y)?(x):(y))
39 #define max(x,y) ((x)>(y)?(x):(y))
40
41 static void
42 init_circles_1 (dpy, window)
43      Display *dpy;
44      Window window;
45 {
46   int i;
47   count = (global_count ? global_count
48            : (3 + (random () % max (1, (min (width, height) / 50)))
49                 + (random () % max (1, (min (width, height) / 50)))));
50   circles = (struct circle *) malloc (count * sizeof (struct circle));
51   for (i = 0; i < count; i++)
52     {
53       circles [i].x = 10 + random () % (width - 20);
54       circles [i].y = 10 + random () % (height - 20);
55       if (global_inc)
56       circles [i].increment = global_inc;
57       else
58         { /* prefer smaller increments to larger ones */
59           int j = 8;
60           int inc = ((random()%j) + (random()%j) + (random()%j)) - ((j*3)/2);
61           if (inc < 0) inc = -inc + 3;
62           circles [i].increment = inc + 3;
63         }
64       circles [i].radius = random () % circles [i].increment;
65       circles [i].dx = ((random () % 3) - 1) * (1 + random () % 5);
66       circles [i].dy = ((random () % 3) - 1) * (1 + random () % 5);
67     }
68 }
69
70 static void
71 init_circles (dpy, window)
72      Display *dpy;
73      Window window;
74 {
75   XGCValues gcv;
76   XWindowAttributes xgwa;
77   XGetWindowAttributes (dpy, window, &xgwa);
78   cmap = xgwa.colormap;
79   global_count = get_integer_resource ("count", "Integer");
80   if (global_count < 0) global_count = 0;
81   global_inc = get_integer_resource ("increment", "Integer");
82   if (global_inc < 0) global_inc = 0;
83   xor_p = get_boolean_resource ("xor", "Boolean");
84 /*  if (mono_p) */ xor_p = True;
85   anim_p = get_boolean_resource ("animate", "Boolean");
86   delay = get_integer_resource ("delay", "Integer");
87   if (mono_p)
88     {
89       fg_pixel = get_pixel_resource ("foreground","Foreground", dpy, cmap);
90       bg_pixel = get_pixel_resource ("background","Background", dpy, cmap);
91     }
92   else
93     {
94       hsv_to_rgb (0,   0.5, 1.0, &fgc.red, &fgc.green, &fgc.blue);
95       hsv_to_rgb (180, 1.0, 0.7, &bgc.red, &bgc.green, &bgc.blue);
96       XAllocColor (dpy, cmap, &fgc);
97       XAllocColor (dpy, cmap, &bgc);
98       fg_pixel = fgc.pixel;
99       bg_pixel = bgc.pixel;
100     }
101
102   width = max (50, xgwa.width);
103   height = max (50, xgwa.height);
104
105 #ifdef DEBUG
106   width/=2; height/=2;
107 #endif
108
109   pixmap = XCreatePixmap (dpy, window, width, height, 1);
110   if (xor_p)
111     buffer = XCreatePixmap (dpy, window, width, height, 1);
112   else
113     buffer = 0;
114
115   gcv.foreground = 1;
116   gcv.background = 0;
117   draw_gc = XCreateGC (dpy, pixmap, GCForeground | GCBackground, &gcv);
118   gcv.foreground = 0;
119   erase_gc = XCreateGC (dpy, pixmap, GCForeground, &gcv);
120   gcv.foreground = fg_pixel;
121   gcv.background = bg_pixel;
122   copy_gc = XCreateGC (dpy, window, GCForeground | GCBackground, &gcv);
123
124   if (xor_p)
125     {
126       gcv.foreground = 1;
127       gcv.background = 0;
128       gcv.function = GXxor;
129       merge_gc = XCreateGC (dpy, pixmap,
130                             GCForeground | GCBackground | GCFunction, &gcv);
131     }
132   else
133     {
134       gcv.foreground = fg_pixel;
135       gcv.background = bg_pixel;
136       gcv.function = GXcopy;
137       merge_gc = XCreateGC (dpy, window,
138                             GCForeground | GCBackground | GCFunction, &gcv);
139     }
140
141   init_circles_1 (dpy, window);
142   XClearWindow (dpy, window);
143   if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
144 }
145
146 static void
147 run_circles (dpy, window)
148      Display *dpy;
149      Window window;
150 {
151   int i;
152   static int iterations = 0;
153   static int oiterations = 0;
154   static Bool first_time_p = True;
155   Bool done = False;
156   Bool inhibit_sleep = False;
157   XFillRectangle (dpy, pixmap, erase_gc, 0, 0, width, height);
158   for (i = 0; i < count; i++)
159     {
160       int radius = circles [i].radius;
161       int inc = circles [i].increment;
162       if (! (iterations & 1))
163         ;
164       else if (radius == 0)
165         ;
166       else if (radius < 0)
167         done = True;
168       else
169         {
170           /* Probably there's a simpler way to ask the musical question,
171              "is this square completely enclosed by this circle," but I've
172              forgotten too much trig to know it...  (That's not really the
173              right question anyway, but the right question is too hard.) */
174           double x1 = ((double) (-circles [i].x)) / ((double) radius);
175           double y1 = ((double) (-circles [i].y)) / ((double) radius);
176           double x2 = ((double) (width - circles [i].x)) / ((double) radius);
177           double y2 = ((double) (height - circles [i].y)) / ((double) radius);
178           x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2;
179           if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1)
180             done = True;
181         }
182       if (radius > 0 &&
183           (xor_p || circles [0].increment < 0))
184         XFillArc (dpy,
185                   (xor_p ? pixmap : window),
186                   (xor_p ? draw_gc : merge_gc),
187                   circles [i].x - radius, circles [i].y - radius,
188                   radius * 2, radius * 2, 0, 360*64);
189       circles [i].radius += inc;
190     }
191
192   if (anim_p && !first_time_p)
193     inhibit_sleep = !done;
194
195   if (done)
196     {
197       if (anim_p)
198         {
199           first_time_p = False;
200           for (i = 0; i < count; i++)
201             {
202               circles [i].x += circles [i].dx;
203               circles [i].y += circles [i].dy;
204               circles [i].radius %= circles [i].increment;
205               if (circles [i].x < 0 || circles [i].x >= width)
206                 {
207                   circles [i].dx = -circles [i].dx;
208                   circles [i].x += (2 * circles [i].dx);
209                 }
210               if (circles [i].y < 0 || circles [i].y >= height)
211                 {
212                   circles [i].dy = -circles [i].dy;
213                   circles [i].y += (2 * circles [i].dy);
214                 }
215             }
216         }
217       else if (circles [0].increment < 0)
218         {
219           free (circles);
220           init_circles_1 (dpy, window);
221           if (! mono_p)
222             {
223               cycle_hue (&fgc, 10);
224               cycle_hue (&bgc, 10);
225               XFreeColors (dpy, cmap, &fgc.pixel, 1, 0);
226               XFreeColors (dpy, cmap, &bgc.pixel, 1, 0);
227               XAllocColor (dpy, cmap, &fgc);
228               XAllocColor (dpy, cmap, &bgc);
229               XSetForeground (dpy, copy_gc, fgc.pixel);
230               XSetBackground (dpy, copy_gc, bgc.pixel);
231             }
232         }
233 #if 0
234       else if ((random () % 2) == 0)
235         {
236           iterations = 0; /* ick */
237           for (i = 0; i < count; i++)
238             circles [i].radius %= circles [i].increment;
239         }
240 #endif
241       else
242         {
243           oiterations = iterations;
244           for (i = 0; i < count; i++)
245             {
246               circles [i].increment = -circles [i].increment;
247               circles [i].radius += (2 * circles [i].increment);
248             }
249         }
250     }
251
252   if (buffer)
253     XCopyPlane (dpy, pixmap, buffer, merge_gc, 0, 0, width, height, 0, 0, 1);
254   else if (!xor_p)
255     {
256       static int ncolors = 0;
257       static XColor *colors = 0;
258       if (circles [0].increment >= 0)
259         inhibit_sleep = True;
260       else if (done)
261         {
262           int fgh, bgh;
263           double fgs, fgv, bgs, bgv;
264           if (colors)
265             for (i = 0; i < ncolors; i++)
266               XFreeColors (dpy, cmap, &colors [i].pixel, 1, 0);
267
268           rgb_to_hsv (fgc.red, fgc.green, fgc.blue, &fgh, &fgs, &fgv);
269           rgb_to_hsv (bgc.red, bgc.green, bgc.blue, &bgh, &bgs, &bgv);
270           ncolors = oiterations;
271           colors = ((XColor *)
272                     (colors
273                      ? realloc (colors, sizeof (XColor) * ncolors)
274                      : malloc (sizeof (XColor) * ncolors)));
275           
276           make_color_ramp (bgh, bgs, bgv, fgh, fgs, fgv, colors, ncolors);
277           for (i = 0; i < ncolors; i++)
278             XAllocColor (dpy, cmap, &colors [i]);
279           XSetForeground (dpy, merge_gc, colors [0].pixel);
280         }
281       else
282         {
283           XSetForeground (dpy, merge_gc, colors [iterations].pixel);
284         }
285     }
286   else
287     XCopyPlane (dpy, pixmap, window, merge_gc, 0, 0, width, height, 0, 0, 1);
288
289   if (buffer && (anim_p
290                  ? (done || (first_time_p && (iterations & 1)))
291                  : (iterations & 1)))
292     {
293       XCopyPlane (dpy, buffer, window, copy_gc, 0, 0, width, height, 0, 0, 1);
294       XSync (dpy, True);
295       if (anim_p && done)
296         XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
297     }
298 #ifdef DEBUG
299   XCopyPlane (dpy, pixmap, window, copy_gc, 0,0,width,height,width,height, 1);
300   if (buffer)
301     XCopyPlane (dpy, buffer, window, copy_gc, 0,0,width,height,0,height, 1);
302   XSync (dpy, True);
303 #endif
304
305   if (done)
306     iterations = 0;
307   else
308     iterations++;
309
310   if (delay && !inhibit_sleep) usleep (delay);
311 }
312
313 \f
314 char *progclass = "Halo";
315
316 char *defaults [] = {
317   "*background: black",
318   "*foreground: white",
319 /*  "*xor:      false", */
320   "*count:      0",
321   "*delay:      100000",
322   0
323 };
324
325 XrmOptionDescRec options [] = {
326   { "-count",           ".count",       XrmoptionSepArg, 0 },
327   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
328   { "-animate",         ".animate",     XrmoptionNoArg, "True" } /* ,
329   { "-xor",             ".xor",         XrmoptionNoArg, "True" },
330   { "-no-xor",          ".xor",         XrmoptionNoArg, "False" } */
331 };
332 int options_size = (sizeof (options) / sizeof (options[0]));
333
334 void
335 screenhack (dpy, window)
336      Display *dpy;
337      Window window;
338 {
339   init_circles (dpy, window);
340   while (1)
341     run_circles (dpy, window);
342 }