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