ftp://ftp.ntnu.no/old/pub/X11/R5contrib/xscreensaver-1.17.tar.gz
[xscreensaver] / hacks / qix.c
1 /* xscreensaver, Copyright (c) 1992 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 #include "screenhack.h"
13 #include <stdio.h>
14
15 struct qpoint {
16   int x, y;
17   int dx, dy;
18 };
19
20 struct qline {
21   struct qpoint p1, p2;
22   XColor color;
23   Bool dead;
24 };
25
26 struct qix {
27   int id;
28   int fp;
29   int nlines;
30   struct qline *lines;
31 };
32
33 static GC draw_gc, erase_gc;
34 static unsigned int default_fg_pixel;
35 static int maxx, maxy, max_spread, max_size, color_shift;
36 static Bool random_p, solid_p, xor_p, transparent_p;
37 static int delay;
38 static int count;
39 static Colormap cmap;
40 static unsigned long base_pixel;
41
42 static GC *gcs[2];
43
44 static void
45 get_geom (dpy, window)
46      Display *dpy;
47      Window window;
48 {
49   XWindowAttributes xgwa;
50   XGetWindowAttributes (dpy, window, &xgwa);
51   maxx = xgwa.width;
52   maxy = xgwa.height;
53 }
54
55 static struct qix *
56 init_one_qix (dpy, window, nlines)
57      Display *dpy;
58      Window window;
59      int nlines;
60 {
61   int i;
62   struct qix *qix = (struct qix *) calloc (1, sizeof (struct qix));
63   qix->nlines = nlines;
64   qix->lines = (struct qline *) calloc (qix->nlines, sizeof (struct qline));
65   
66   if (!mono_p && !transparent_p)
67     {
68       hsv_to_rgb (random () % 360, frand (1.0), frand (0.5) + 0.5,
69                   &qix->lines[0].color.red, &qix->lines[0].color.green,
70                   &qix->lines[0].color.blue);
71       if (!XAllocColor (dpy, cmap, &qix->lines[0].color))
72         {
73           qix->lines[0].color.pixel = default_fg_pixel;
74           if (!XQueryColor (dpy, cmap, &qix->lines[0].color))
75             abort ();
76           if (!XAllocColor (dpy, cmap, &qix->lines[0].color))
77             abort ();
78         }
79     }
80   qix->lines[0].p1.x = random () % maxx;
81   qix->lines[0].p1.y = random () % maxy;
82   if (max_size == 0)
83     {
84       qix->lines[0].p2.x = random () % maxx;
85       qix->lines[0].p2.y = random () % maxy;
86     }
87   else
88     {
89       qix->lines[0].p2.x = qix->lines[0].p1.x + (random () % (max_size/2));
90       qix->lines[0].p2.y = qix->lines[0].p1.y + (random () % (max_size/2));
91       if (qix->lines[0].p2.x > maxx) qix->lines[0].p2.x = maxx;
92       if (qix->lines[0].p2.y > maxy) qix->lines[0].p2.y = maxy;
93     }
94   qix->lines[0].p1.dx = (random () % (max_spread + 1)) - (max_spread / 2);
95   qix->lines[0].p1.dy = (random () % (max_spread + 1)) - (max_spread / 2);
96   qix->lines[0].p2.dx = (random () % (max_spread + 1)) - (max_spread / 2);
97   qix->lines[0].p2.dy = (random () % (max_spread + 1)) - (max_spread / 2);
98   qix->lines[0].dead = True;
99   for (i = 1; i < qix->nlines; i++)
100     {
101       qix->lines[i] = qix->lines[0];
102       if (!mono_p && !transparent_p)
103         if (!XAllocColor (dpy, cmap, &qix->lines[i].color))
104           abort ();
105     }
106   return qix;
107 }
108
109 /* I don't believe this fucking language doesn't have builtin exponentiation.
110    I further can't believe that the fucking ^ character means fucking XOR!! */
111 static int i_exp(i,j)
112      int i, j;
113 {
114   int k = 1;
115   while (j--) k *= i;
116   return k;
117 }
118
119
120 static void
121 merge_colors (argc, argv, into_color, mask, increment_p)
122      int argc;
123      XColor **argv;
124      XColor *into_color;
125      int mask;
126      Bool increment_p;
127 {
128   int j;
129   *into_color = *argv [0];
130   into_color->pixel |= mask;
131 #define SHORT_INC(x,y) (x = ((((x)+(y)) > 0xFFFF) ? 0xFFFF : ((x)+(y))))
132 #define SHORT_DEC(x,y) (x = ((((x)-(y)) < 0)      ? 0      : ((x)-(y))))
133   for (j = 1; j < argc; j++)
134     if (increment_p)
135       {
136         SHORT_INC (into_color->red,   argv[j]->red);
137         SHORT_INC (into_color->green, argv[j]->green);
138         SHORT_INC (into_color->blue,  argv[j]->blue);
139       }
140     else
141       {
142         SHORT_DEC (into_color->red,   argv[j]->red);
143         SHORT_DEC (into_color->green, argv[j]->green);
144         SHORT_DEC (into_color->blue,  argv[j]->blue);
145       }
146 #undef SHORT_INC
147 #undef SHORT_DEC
148 }
149
150 /* fill in all the permutations of colors that XAllocColorCells() has 
151    allocated for us.  Thanks Ron, you're an additive kind of guy. */
152 static void
153 permute_colors (pcolors, colors, count, plane_masks, increment_p)
154      XColor *pcolors, *colors;
155      int count;
156      unsigned long *plane_masks;
157      Bool increment_p;
158 {
159   int out = 0;
160   int max = i_exp (2, count);
161   if (count > 31) abort ();
162   for (out = 1; out < max; out++)
163     {
164       XColor *argv [32];
165       int this_mask = 0;
166       int argc = 0;
167       int bit;
168       for (bit = 0; bit < 32; bit++)
169         if (out & (1<<bit))
170           {
171             argv [argc++] = &pcolors [bit];
172             this_mask |= plane_masks [bit];
173           }
174       merge_colors (argc, argv, &colors [out-1], this_mask, increment_p);
175     }
176 }
177
178
179 static struct qix **
180 init_qix (dpy, window)
181      Display *dpy;
182      Window window;
183 {
184   int nlines;
185   struct qix **qixes;
186   XGCValues gcv;
187   XWindowAttributes xgwa;
188   XGetWindowAttributes (dpy, window, &xgwa);
189   cmap = xgwa.colormap;
190   count = get_integer_resource ("count", "Integer");
191   if (count <= 0) count = 1;
192   nlines = get_integer_resource ("segments", "Integer");
193   if (nlines <= 0) nlines = 20;
194   get_geom (dpy, window);
195   max_spread = get_integer_resource ("spread", "Integer");
196   if (max_spread <= 0) max_spread = 10;
197   max_size = get_integer_resource ("size", "Integer");
198   if (max_size < 0) max_size = 0;
199   random_p = get_boolean_resource ("random", "Boolean");
200   solid_p = get_boolean_resource ("solid", "Boolean");
201   xor_p = get_boolean_resource ("xor", "Boolean");
202   transparent_p = get_boolean_resource ("transparent", "Boolean");
203   delay = get_integer_resource ("delay", "Integer");
204   color_shift = get_integer_resource ("colorShift", "Integer");
205   if (color_shift < 0 || color_shift >= 360) color_shift = 5;
206   if (delay < 0) delay = 0;
207
208   if (count == 1 && transparent_p)
209     transparent_p = False; /* it's a no-op */
210
211   if (transparent_p && CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
212     {
213       fprintf (stderr, "%s: -transparent only works on color displays.\n",
214                progname);
215       transparent_p = False;
216     }
217
218   if (xor_p && !transparent_p)
219     mono_p = True;
220
221   gcs[0] = gcs[1] = 0;
222   gcv.foreground = default_fg_pixel =
223     get_pixel_resource ("foreground", "Foreground", dpy, cmap);
224
225   if (transparent_p)
226     {
227       Bool increment_p = get_boolean_resource ("additive", "Boolean");
228       unsigned long plane_masks [32];
229       XColor *pcolors, *colors;
230       int nplanes = count;
231       int i, total_colors;
232
233       /* permutations would be harder if the number of planes didn't fit
234          in an int.  Who has >32-bit displays anyway... */
235       if (nplanes > 31) nplanes = 31;
236
237       while (nplanes > 1 &&
238              !XAllocColorCells (dpy, cmap, False, plane_masks, nplanes,
239                                 &base_pixel, 1))
240         nplanes--;
241
242       if (nplanes <= 1)
243         {
244           fprintf (stderr,
245          "%s: couldn't allocate any color planes; turning -transparent off.\n",
246                    progname);
247           transparent_p = False;
248           if (xor_p)
249             goto NON_TRANSPARENT_XOR;
250           else
251             goto NON_TRANSPARENT;
252         }
253       else if (nplanes != count)
254         {
255           fprintf (stderr,
256                    "%s: only allocated %d color planes (instead of %d).\n",
257                    progname, nplanes, count);
258           count = nplanes;
259         }
260
261       gcs[0] = (GC *) malloc (count * sizeof (GC));
262       gcs[1] = xor_p ? gcs[0] : (GC *) malloc (count * sizeof (GC));
263       total_colors = i_exp (2, count);
264       pcolors = (XColor *) calloc (count, sizeof (XColor));
265       colors = (XColor *) calloc (total_colors, sizeof (XColor));
266       for (i = 0; i < count; i++)
267         {
268           gcv.plane_mask = plane_masks [i];
269           gcv.foreground = ~0;
270           if (xor_p)
271             {
272               gcv.function = GXxor;
273               gcs [0][i] = XCreateGC (dpy, window,
274                                       GCForeground|GCFunction|GCPlaneMask,
275                                       &gcv);
276             }
277           else
278             {
279               gcs [0][i] = XCreateGC (dpy, window, GCForeground|GCPlaneMask,
280                                       &gcv);
281               gcv.foreground = 0;
282               gcs [1][i] = XCreateGC (dpy, window, GCForeground|GCPlaneMask,
283                                       &gcv);
284             }
285
286           /* pick the "primary" (not in that sense) colors.
287              If we are in subtractive mode, pick higher intensities. */
288           hsv_to_rgb (random () % 360, frand (1.0),
289                       frand (0.5) + (increment_p ? 0.2 : 0.5),
290                       &pcolors[i].red, &pcolors[i].green, &pcolors[i].blue);
291
292           pcolors [i].flags = DoRed | DoGreen | DoBlue;
293           pcolors [i].pixel = base_pixel | plane_masks [i];
294         }
295       permute_colors (pcolors, colors, count, plane_masks, increment_p);
296       /* clone the default background of the window into our "base" pixel */
297       colors [total_colors - 1].pixel =
298         get_pixel_resource ("background", "Background", dpy, cmap);
299       XQueryColor (dpy, cmap, &colors [total_colors - 1]);
300       colors [total_colors - 1].pixel = base_pixel;
301       XStoreColors (dpy, cmap, colors, total_colors);
302       XSetWindowBackground (dpy, window, base_pixel);
303       XClearWindow (dpy, window);
304     }
305   else if (xor_p)
306     {
307     NON_TRANSPARENT_XOR:
308       gcv.function = GXxor;
309       gcv.foreground =
310         (default_fg_pixel ^ get_pixel_resource ("background", "Background",
311                                                 dpy, cmap));
312       draw_gc = erase_gc = XCreateGC(dpy,window,GCForeground|GCFunction,&gcv);
313     }
314   else
315     {
316     NON_TRANSPARENT:
317       draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
318       gcv.foreground = get_pixel_resource ("background", "Background",
319                                            dpy, cmap);
320       erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
321     }
322
323   qixes = (struct qix **) malloc ((count + 1) * sizeof (struct qix *));
324   qixes [count] = 0;
325   while (count--)
326     {
327       qixes [count] = init_one_qix (dpy, window, nlines);
328       qixes [count]->id = count;
329     }
330   return qixes;
331 }
332
333 static void
334 free_qline (dpy, window, cmap, qline, prev, qix)
335      Display *dpy;
336      Window window;
337      Colormap cmap;
338      struct qline *qline, *prev;
339      struct qix *qix;
340 {
341   if (qline->dead || !prev)
342     ;
343   else if (solid_p)
344     {
345       XPoint points [4];
346       points [0].x = qline->p1.x; points [0].y = qline->p1.y;
347       points [1].x = qline->p2.x; points [1].y = qline->p2.y;
348       points [2].x = prev->p2.x; points [2].y = prev->p2.y;
349       points [3].x = prev->p1.x; points [3].y = prev->p1.y;
350       XFillPolygon (dpy, window, (transparent_p ? gcs[1][qix->id] : erase_gc),
351                     points, 4, Complex, CoordModeOrigin);
352     }
353   else
354     XDrawLine (dpy, window, (transparent_p ? gcs[1][qix->id] : erase_gc),
355                qline->p1.x, qline->p1.y, qline->p2.x, qline->p2.y);
356
357   if (!mono_p && !transparent_p)
358     XFreeColors (dpy, cmap, &qline->color.pixel, 1, 0);
359
360   qline->dead = True;
361 }
362
363 static void
364 add_qline (dpy, window, cmap, qline, prev_qline, qix)
365      Display *dpy;
366      Window window;
367      Colormap cmap;
368      struct qline *qline, *prev_qline;
369      struct qix *qix;
370 {
371   *qline = *prev_qline;
372
373 #define wiggle(point,delta,max)                                         \
374   if (random_p) delta += (random () % 3) - 1;                           \
375   if (delta > max_spread) delta = max_spread;                           \
376   else if (delta < -max_spread) delta = -max_spread;                    \
377   point += delta;                                                       \
378   if (point < 0) point = 0, delta = -delta, point += delta<<1;          \
379   else if (point > max) point = max, delta = -delta, point += delta<<1;
380   
381   wiggle (qline->p1.x, qline->p1.dx, maxx);
382   wiggle (qline->p1.y, qline->p1.dy, maxy);
383   wiggle (qline->p2.x, qline->p2.dx, maxx);
384   wiggle (qline->p2.y, qline->p2.dy, maxy);
385
386   if (max_size)
387     {
388       if (qline->p1.x - qline->p2.x > max_size)
389         qline->p1.x = qline->p2.x + max_size
390           - (random_p ? random() % max_spread : 0);
391       else if (qline->p2.x - qline->p1.x > max_size)
392         qline->p2.x = qline->p1.x + max_size
393           - (random_p ? random() % max_spread : 0);
394       if (qline->p1.y - qline->p2.y > max_size)
395         qline->p1.y = qline->p2.y + max_size
396           - (random_p ? random() % max_spread : 0);
397       else if (qline->p2.y - qline->p1.y > max_size)
398         qline->p2.y = qline->p1.y + max_size
399           - (random_p ? random() % max_spread : 0);
400     }
401
402   if (!mono_p && !transparent_p)
403     {
404       cycle_hue (&qline->color, color_shift);
405       qline->color.flags = DoRed | DoGreen | DoBlue;
406       if (!XAllocColor (dpy, cmap, &qline->color))
407         {
408           qline->color = prev_qline->color;
409           if (!XAllocColor (dpy, cmap, &qline->color))
410             abort (); /* same color should work */
411         }
412       XSetForeground (dpy, draw_gc, qline->color.pixel);
413     }
414   if (! solid_p)
415     XDrawLine (dpy, window, (transparent_p ? gcs[0][qix->id] : draw_gc),
416                qline->p1.x, qline->p1.y, qline->p2.x, qline->p2.y);
417   else if (!prev_qline->dead)
418     {
419       XPoint points [4];
420       points [0].x = qline->p1.x; points [0].y = qline->p1.y;
421       points [1].x = qline->p2.x; points [1].y = qline->p2.y;
422       points [2].x = prev_qline->p2.x; points [2].y = prev_qline->p2.y;
423       points [3].x = prev_qline->p1.x; points [3].y = prev_qline->p1.y;
424       XFillPolygon (dpy, window, (transparent_p ? gcs[0][qix->id] : draw_gc),
425                     points, 4, Complex, CoordModeOrigin);
426     }
427
428   qline->dead = False;
429 }
430
431 static void
432 qix1 (dpy, window, qix)
433      Display *dpy;
434      Window window;
435      struct qix *qix;
436 {
437   int ofp = qix->fp - 1;
438   static int gtick = 0;
439   if (gtick++ == 500)
440     get_geom (dpy, window), gtick = 0;
441   if (ofp < 0) ofp = qix->nlines - 1;
442   free_qline (dpy, window, cmap, &qix->lines [qix->fp],
443               &qix->lines[(qix->fp + 1) % qix->nlines], qix);
444   add_qline (dpy, window, cmap, &qix->lines[qix->fp], &qix->lines[ofp], qix);
445   if ((++qix->fp) >= qix->nlines)
446     qix->fp = 0;
447 }
448
449 \f
450 char *progclass = "Qix";
451
452 char *defaults [] = {
453   "*background: black",
454   "*foreground: white",
455   "*count:      1",
456   "*segments:   50",
457   "*spread:     8",
458   "*size:       0",
459   "*colorShift: 3",
460   "*solid:      false",
461   "*delay:      10000",
462   "*random:     true",
463   "*xor:        false",
464   "*transparent:false",
465   "*additive:   true",
466   0
467 };
468
469 XrmOptionDescRec options [] = {
470   { "-count",           ".count",       XrmoptionSepArg, 0 },
471   { "-segments",        ".segments",    XrmoptionSepArg, 0 },
472   { "-spread",          ".spread",      XrmoptionSepArg, 0 },
473   { "-size",            ".size",        XrmoptionSepArg, 0 },
474   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
475   { "-color-shift",     ".colorShift",  XrmoptionSepArg, 0 },
476   { "-random",          ".random",      XrmoptionNoArg, "true" },
477   { "-linear",          ".random",      XrmoptionNoArg, "false" },
478   { "-solid",           ".solid",       XrmoptionNoArg, "true" },
479   { "-hollow",          ".solid",       XrmoptionNoArg, "false" },
480   { "-xor",             ".xor",         XrmoptionNoArg, "true" },
481   { "-no-xor",          ".xor",         XrmoptionNoArg, "false" },
482   { "-transparent",     ".transparent", XrmoptionNoArg, "true" },
483   { "-non-transparent", ".transparent", XrmoptionNoArg, "false" },
484   { "-additive",        ".additive",    XrmoptionNoArg, "true" },
485   { "-subtractive",     ".additive",    XrmoptionNoArg, "false" },
486 };
487 int options_size = (sizeof (options) / sizeof (options[0]));
488
489 void
490 screenhack (dpy, window)
491      Display *dpy;
492      Window window;
493 {
494   struct qix **q1 = init_qix (dpy, window);
495   struct qix **qn;
496   while (1)
497     for (qn = q1; *qn; qn++)
498       {
499         qix1 (dpy, window, *qn);
500         XSync (dpy, True);
501         if (delay) usleep (delay);
502       }
503 }