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