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