source: trunk/third/xscreensaver/hacks/halo.c @ 20148

Revision 20148, 12.8 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/* xscreensaver, Copyright (c) 1993, 1995, 1996, 1997, 1998, 1999, 2003
2 *  Jamie Zawinski <jwz@jwz.org>
3 *
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
10 * implied warranty.
11 */
12
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
16   a real drag...
17
18   This would probably look good with shapes other than circles as well.
19
20 */
21
22#include "screenhack.h"
23#include <stdio.h>
24
25struct circle {
26  int x, y, radius;
27  int increment;
28  int dx, dy;
29};
30
31static enum color_mode {
32  seuss_mode, ramp_mode, random_mode
33} cmode;
34
35
36static struct circle *circles;
37static int count, global_count;
38static Pixmap pixmap, buffer;
39static int width, height, global_inc;
40static int delay, delay2, cycle_delay;
41static unsigned long fg_pixel, bg_pixel;
42static GC draw_gc, erase_gc, copy_gc, merge_gc;
43static Bool anim_p;
44static Colormap cmap;
45
46static int ncolors;
47static XColor *colors;
48static Bool cycle_p;
49static int fg_index;
50static int bg_index;
51
52
53#define min(x,y) ((x)<(y)?(x):(y))
54#define max(x,y) ((x)>(y)?(x):(y))
55
56static void
57init_circles_1 (Display *dpy, Window window)
58{
59  int i;
60  count = (global_count ? global_count
61           : (3 + (random () % max (1, (min (width, height) / 50)))
62                + (random () % max (1, (min (width, height) / 50)))));
63  circles = (struct circle *) malloc (count * sizeof (struct circle));
64  for (i = 0; i < count; i++)
65    {
66      circles [i].x = 10 + random () % (width - 20);
67      circles [i].y = 10 + random () % (height - 20);
68      if (global_inc)
69      circles [i].increment = global_inc;
70      else
71        { /* prefer smaller increments to larger ones */
72          int j = 8;
73          int inc = ((random()%j) + (random()%j) + (random()%j)) - ((j*3)/2);
74          if (inc < 0) inc = -inc + 3;
75          circles [i].increment = inc + 3;
76        }
77      circles [i].radius = random () % circles [i].increment;
78      circles [i].dx = ((random () % 3) - 1) * (1 + random () % 5);
79      circles [i].dy = ((random () % 3) - 1) * (1 + random () % 5);
80    }
81}
82
83static void
84init_circles (Display *dpy, Window window)
85{
86  XGCValues gcv;
87  XWindowAttributes xgwa;
88  char *mode_str = 0;
89  XGetWindowAttributes (dpy, window, &xgwa);
90  cmap = xgwa.colormap;
91  global_count = get_integer_resource ("count", "Integer");
92  if (global_count < 0) global_count = 0;
93  global_inc = get_integer_resource ("increment", "Integer");
94  if (global_inc < 0) global_inc = 0;
95  anim_p = get_boolean_resource ("animate", "Boolean");
96  delay = get_integer_resource ("delay", "Integer");
97  delay2 = get_integer_resource ("delay2", "Integer") * 1000000;
98  cycle_delay = get_integer_resource ("cycleDelay", "Integer");
99  mode_str = get_string_resource ("colorMode", "ColorMode");
100  if (! mode_str) cmode = random_mode;
101  else if (!strcmp (mode_str, "seuss"))  cmode = seuss_mode;
102  else if (!strcmp (mode_str, "ramp"))   cmode = ramp_mode;
103  else if (!strcmp (mode_str, "random")) cmode = random_mode;
104  else {
105    fprintf (stderr,
106             "%s: colorMode must be seuss, ramp, or random, not \"%s\"\n",
107             progname, mode_str);
108    exit (1);
109  }
110
111  if (mono_p) cmode = seuss_mode;
112  if (cmode == random_mode)
113    cmode = ((random()&3) == 1) ? ramp_mode : seuss_mode;
114
115  if (cmode == ramp_mode)
116    anim_p = False;    /* This combo doesn't work right... */
117
118  ncolors = get_integer_resource ("colors", "Colors");
119  if (ncolors < 2) ncolors = 2;
120  if (ncolors <= 2) mono_p = True;
121
122  if (mono_p)
123    colors = 0;
124  else
125    colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
126
127  cycle_p = mono_p ? False : get_boolean_resource ("cycle", "Cycle");
128
129  /* If the visual isn't color-indexed, don't bother trying to
130     allocate writable cells. */
131  if (cycle_p && !has_writable_cells (xgwa.screen, xgwa.visual))
132    cycle_p = False;
133
134
135  if (mono_p)
136    ;
137  else if (random() % (cmode == seuss_mode ? 2 : 10))
138    make_uniform_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
139                           True, &cycle_p, True);
140  else
141    make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
142                          True, &cycle_p, True);
143
144  if (ncolors <= 2) mono_p = True;
145  if (mono_p) cycle_p = False;
146  if (mono_p) cmode = seuss_mode;
147
148  if (mono_p)
149    {
150      fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
151      bg_pixel = get_pixel_resource ("background", "Background", dpy, cmap);
152    }
153  else
154    {
155      fg_index = 0;
156      bg_index = ncolors / 4;
157      if (fg_index == bg_index) bg_index++;
158      fg_pixel = colors[fg_index].pixel;
159      bg_pixel = colors[bg_index].pixel;
160    }
161
162  width = max (50, xgwa.width);
163  height = max (50, xgwa.height);
164
165#ifdef DEBUG
166  width/=2; height/=2;
167#endif
168
169  pixmap = XCreatePixmap (dpy, window, width, height, 1);
170  if (cmode == seuss_mode)
171    buffer = XCreatePixmap (dpy, window, width, height, 1);
172  else
173    buffer = 0;
174
175  gcv.foreground = 1;
176  gcv.background = 0;
177  draw_gc = XCreateGC (dpy, pixmap, GCForeground | GCBackground, &gcv);
178  gcv.foreground = 0;
179  erase_gc = XCreateGC (dpy, pixmap, GCForeground, &gcv);
180  gcv.foreground = fg_pixel;
181  gcv.background = bg_pixel;
182  copy_gc = XCreateGC (dpy, window, GCForeground | GCBackground, &gcv);
183
184  if (cmode == seuss_mode)
185    {
186      gcv.foreground = 1;
187      gcv.background = 0;
188      gcv.function = GXxor;
189      merge_gc = XCreateGC (dpy, pixmap,
190                            GCForeground | GCBackground | GCFunction, &gcv);
191    }
192  else
193    {
194      gcv.foreground = fg_pixel;
195      gcv.background = bg_pixel;
196      gcv.function = GXcopy;
197      merge_gc = XCreateGC (dpy, window,
198                            GCForeground | GCBackground | GCFunction, &gcv);
199    }
200
201  init_circles_1 (dpy, window);
202  XClearWindow (dpy, window);
203  if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
204}
205
206static void
207run_circles (Display *dpy, Window window)
208{
209  int i;
210  static int iterations = 0;
211  static int oiterations = 0;
212  static Bool first_time_p = True;
213  Bool done = False;
214  Bool inhibit_sleep = False;
215  static int clear_tick = 0;
216
217  XFillRectangle (dpy, pixmap, erase_gc, 0, 0, width, height);
218  for (i = 0; i < count; i++)
219    {
220      int radius = circles [i].radius;
221      int inc = circles [i].increment;
222
223      if (! (iterations & 1))  /* never stop on an odd number of iterations */
224        ;
225      else if (radius == 0)     /* eschew inf */
226        ;
227      else if (radius < 0)      /* stop when the circles are points */
228        done = True;
229      else                      /* stop when the circles fill the window */
230        {
231          /* Probably there's a simpler way to ask the musical question,
232             "is this square completely enclosed by this circle," but I've
233             forgotten too much trig to know it...  (That's not really the
234             right question anyway, but the right question is too hard.) */
235          double x1 = ((double) (-circles [i].x)) / ((double) radius);
236          double y1 = ((double) (-circles [i].y)) / ((double) radius);
237          double x2 = ((double) (width - circles [i].x)) / ((double) radius);
238          double y2 = ((double) (height - circles [i].y)) / ((double) radius);
239          x1 *= x1; x2 *= x2; y1 *= y1; y2 *= y2;
240          if ((x1 + y1) < 1 && (x2 + y2) < 1 && (x1 + y2) < 1 && (x2 + y1) < 1)
241            done = True;
242        }
243
244      if (radius > 0 &&
245          (cmode == seuss_mode ||       /* drawing all circles, or */
246           circles [0].increment < 0))  /* on the way back in */
247        {
248          XFillArc (dpy,
249                    (cmode == seuss_mode ? pixmap : window),
250                    (cmode == seuss_mode ? draw_gc : merge_gc),
251                    circles [i].x - radius, circles [i].y - radius,
252                    radius * 2, radius * 2, 0, 360*64);
253        }
254      circles [i].radius += inc;
255    }
256
257  if (cycle_p && cmode != seuss_mode)
258    {
259      struct timeval now;
260      static struct timeval then = { 0, 0 };
261      unsigned long diff;
262#ifdef GETTIMEOFDAY_TWO_ARGS
263      struct timezone tzp;
264      gettimeofday(&now, &tzp);
265#else
266      gettimeofday(&now);
267#endif
268      diff = (((now.tv_sec - then.tv_sec)  * 1000000) +
269              (now.tv_usec - then.tv_usec));
270      if (diff > cycle_delay)
271        {
272          rotate_colors (dpy, cmap, colors, ncolors, 1);
273          then = now;
274        }
275    }
276
277  if (anim_p && !first_time_p)
278    inhibit_sleep = !done;
279
280  if (done)
281    {
282      if (anim_p)
283        {
284          first_time_p = False;
285          for (i = 0; i < count; i++)
286            {
287              circles [i].x += circles [i].dx;
288              circles [i].y += circles [i].dy;
289              circles [i].radius %= circles [i].increment;
290              if (circles [i].x < 0 || circles [i].x >= width)
291                {
292                  circles [i].dx = -circles [i].dx;
293                  circles [i].x += (2 * circles [i].dx);
294                }
295              if (circles [i].y < 0 || circles [i].y >= height)
296                {
297                  circles [i].dy = -circles [i].dy;
298                  circles [i].y += (2 * circles [i].dy);
299                }
300            }
301        }
302      else if (circles [0].increment < 0)
303        {
304          /* We've zoomed out and the screen is blank -- re-pick the
305             center points, and shift the colors.
306           */
307          free (circles);
308          init_circles_1 (dpy, window);
309          if (! mono_p)
310            {
311              fg_index = (fg_index + 1) % ncolors;
312              bg_index = (fg_index + (ncolors/2)) % ncolors;
313              XSetForeground (dpy, copy_gc, colors [fg_index].pixel);
314              XSetBackground (dpy, copy_gc, colors [bg_index].pixel);
315            }
316        }
317      /* Sometimes go out from the inside instead of the outside */
318      else if (clear_tick == 0 && ((random () % 3) == 0))
319        {
320          iterations = 0; /* ick */
321          for (i = 0; i < count; i++)
322            circles [i].radius %= circles [i].increment;
323
324          clear_tick = ((random() % 8) + 4) | 1;   /* must be odd */
325        }
326      else
327        {
328          oiterations = iterations;
329          for (i = 0; i < count; i++)
330            {
331              circles [i].increment = -circles [i].increment;
332              circles [i].radius += (2 * circles [i].increment);
333            }
334        }
335    }
336
337  if (buffer)
338    XCopyPlane (dpy, pixmap, buffer, merge_gc, 0, 0, width, height, 0, 0, 1);
339  else if (cmode != seuss_mode)
340    {
341
342      if (!mono_p)
343        {
344          fg_index++;
345          bg_index++;
346          if (fg_index >= ncolors) fg_index = 0;
347          if (bg_index >= ncolors) bg_index = 0;
348          XSetForeground (dpy, merge_gc, colors [fg_index].pixel);
349        }
350
351      if (circles [0].increment >= 0)
352        inhibit_sleep = True;
353      else if (done && cmode == seuss_mode)
354        XFillRectangle (dpy, window, merge_gc, 0, 0, width, height);
355    }
356  else
357    XCopyPlane (dpy, pixmap, window, merge_gc, 0, 0, width, height, 0, 0, 1);
358
359  /* buffer is only used in seuss-mode or anim-mode */
360  if (buffer && (anim_p
361                 ? (done || (first_time_p && (iterations & 1)))
362                 : (iterations & 1)))
363    {
364      XCopyPlane (dpy, buffer, window, copy_gc, 0, 0, width, height, 0, 0, 1);
365      XSync (dpy, False);
366      if (anim_p && done)
367        XFillRectangle (dpy, buffer, erase_gc, 0, 0, width, height);
368    }
369
370#ifdef DEBUG
371  XCopyPlane (dpy, pixmap, window, copy_gc, 0,0,width,height,width,height, 1);
372  if (buffer)
373    XCopyPlane (dpy, buffer, window, copy_gc, 0,0,width,height,0,height, 1);
374  XSync (dpy, False);
375#endif
376
377  if (done)
378    iterations = 0;
379  else
380    iterations++;
381
382  if (delay && !inhibit_sleep)
383    {
384      static Bool really_first_p = True;
385      int direction = 1;
386      int d = delay;
387      if (done && cycle_p && cmode != seuss_mode && !really_first_p)
388        {
389          d = delay2;
390          if (! (random() % 10))
391            direction = -1;
392        }
393
394      XSync(dpy, False);
395      screenhack_handle_events (dpy);
396
397      if (cycle_p && cycle_delay)
398        {
399          i = 0;
400          while (i < d)
401            {
402              rotate_colors (dpy, cmap, colors, ncolors, direction);
403              usleep(cycle_delay);
404              screenhack_handle_events (dpy);
405              i += cycle_delay;
406            }
407        }
408      else if (cmode != seuss_mode &&
409               done && !really_first_p && cycle_delay > 0)
410        usleep (cycle_delay * 50);
411      else
412        usleep (d);
413
414      if (done)
415        really_first_p = False;
416    }
417
418  if (done && clear_tick > 0)
419    {
420      clear_tick--;
421      if (!clear_tick)
422        {
423          XClearWindow (dpy, window);
424          if (buffer) XFillRectangle (dpy, buffer, erase_gc, 0,0,width,height);
425        }
426    }
427}
428
429
430char *progclass = "Halo";
431
432char *defaults [] = {
433  ".background:         black",
434  ".foreground:         white",
435  "*colorMode:          random",
436  "*colors:             100",
437  "*cycle:              true",
438  "*count:              0",
439  "*delay:              100000",
440  "*delay2:             20",
441  "*cycleDelay:         100000",
442  0
443};
444
445XrmOptionDescRec options [] = {
446  { "-count",           ".count",       XrmoptionSepArg, 0 },
447  { "-delay",           ".delay",       XrmoptionSepArg, 0 },
448  { "-cycle-delay",     ".cycleDelay",  XrmoptionSepArg, 0 },
449  { "-animate",         ".animate",     XrmoptionNoArg, "True" },
450  { "-mode",            ".colorMode",   XrmoptionSepArg, 0 },
451  { "-colors",          ".colors",      XrmoptionSepArg, 0 },
452  { "-cycle",           ".cycle",       XrmoptionNoArg, "True" },
453  { "-no-cycle",        ".cycle",       XrmoptionNoArg, "False" },
454  { 0, 0, 0, 0 }
455};
456
457void
458screenhack (Display *dpy, Window window)
459{
460  init_circles (dpy, window);
461  while (1)
462    {
463      run_circles (dpy, window);
464      screenhack_handle_events (dpy);
465    }
466}
Note: See TracBrowser for help on using the repository browser.