http://se.aminet.net/pub/X11/ftp.x.org/contrib/vms/xscreensaver-124.zip
[xscreensaver] / hacks / imsmap.c
1 /* imsmap, Copyright (c) 1992 Juergen Nickelsen <nickel@cs.tu-berlin.de>
2  * Derived from code by Markus Schirmer, TU Berlin.
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 <stdio.h>
15 #include <X11/Xutil.h>
16
17 #define NSTEPS 7
18 #define COUNT (1 << NSTEPS)
19 #define CELL(c, r) cell[((unsigned int)(c)) + ((unsigned int) (r)) * xmax]
20
21 static enum mode_t { MODE_H, MODE_S, MODE_V, MODE_RANDOM } mode;
22
23 static GC gc, gc2;
24 static Display *disp;
25 static Window wind;
26 static XWindowAttributes wattrs;
27
28 #if defined(sun) && !__STDC__   /* sun cc doesn't know "signed char" */
29 #define signed /**/
30 #endif
31
32 #ifdef VMS
33 #ifndef __DECC
34 #define signed /**/              /* for VAX C */
35 #endif
36 #endif
37 static unsigned long *pixels = 0, fg_pixel, bg_pixel;
38 static int npixels = 0;
39 static Colormap cmap;
40 static int timeout, cycle_delay;
41 static int cycle_p;
42 static signed char *cell = NULL;
43 static int xmax, ymax;
44 static int iterations;
45
46 static void
47 initwin (dsp, win)
48      Display *dsp;
49      Window win;
50 {
51   int fg_h, bg_h;
52   double fg_s, fg_v, bg_s, bg_v;
53
54   enum mode_t this_mode;
55   static Bool rv_p;
56   static int ncolors = 0;
57   int shift;
58   double dshift;
59     
60   XGCValues gcv;
61
62   XGetWindowAttributes (dsp, win, &wattrs);
63   cmap = wattrs.colormap;
64
65   if (!ncolors)
66     {
67       char *mode_str = get_string_resource ("mode", "Mode");
68       rv_p = get_boolean_resource ("reverseVideo", "ReverseVideo");
69       cycle_p = get_boolean_resource ("cycle", "Cycle");
70       ncolors = get_integer_resource ("ncolors", "Integer");
71       timeout = get_integer_resource ("timeout", "Integer");
72       cycle_delay = get_integer_resource ("cycleDelay", "Integer");
73       iterations = get_integer_resource ("iterations", "Integer");
74       if (iterations < 0) iterations = 0;
75       else if (iterations > 7) iterations = 7;
76       pixels = (unsigned long *) calloc (ncolors, sizeof (unsigned int));
77       fg_pixel = get_pixel_resource ("background", "Background", dsp, cmap);
78       bg_pixel = get_pixel_resource ("foreground", "Foreground", dsp, cmap);
79
80       if (mono_p && fg_pixel == bg_pixel)
81         bg_pixel = !bg_pixel;
82
83       if (mono_p) cycle_p = False;
84
85       gcv.foreground = fg_pixel;
86       gcv.background = bg_pixel;
87       gc = XCreateGC (dsp, win, GCForeground|GCBackground, &gcv);
88       gcv.foreground = bg_pixel;
89       gc2 = XCreateGC (dsp, win, GCForeground, &gcv);
90
91       if (!mode_str || !strcmp (mode_str, "random"))
92         mode = MODE_RANDOM;
93       else if (!strcmp (mode_str, "h") || !strcmp (mode_str, "hue"))
94         mode = MODE_H;
95       else if (!strcmp (mode_str, "s") || !strcmp (mode_str, "saturation"))
96         mode = MODE_S;
97       else if (!strcmp (mode_str, "v") || !strcmp (mode_str, "value"))
98         mode = MODE_V;
99       else
100         {
101           fprintf (stderr,
102            "%s: mode must be hue, saturation, value, or random, not \"%s\"\n",
103                    progname, mode_str);
104           mode = MODE_RANDOM;
105         }
106     }
107     else if (! mono_p)
108       XFreeColors (dsp, cmap, pixels, npixels, 0);
109
110   this_mode = mode;
111   if (!mono_p && mode == MODE_RANDOM)
112     switch (random () % 3) {
113     case 0: this_mode = MODE_H; break;
114     case 1: this_mode = MODE_S; break;
115     case 2: this_mode = MODE_V; break;
116     }
117
118   if (mono_p)
119     {
120       npixels = ncolors;
121       pixels [0] = fg_pixel;
122       pixels [1] = bg_pixel;
123     }
124   else
125     {
126       XColor fg_color, bg_color;
127
128       if (fg_pixel == bg_pixel)
129         {
130         HSV_AGAIN:
131           fg_h = random () % 360;
132           bg_h = random () % 360;
133           fg_s = frand (1.0);
134           bg_s = frand (1.0);
135         V_AGAIN:
136           fg_v = frand (1.0);
137           bg_v = frand (1.0);
138           if ((fg_v - bg_v) > -0.4 && (fg_v - bg_v) < 0.4)
139             goto V_AGAIN;
140           hsv_to_rgb (fg_h, fg_s, fg_v, 
141                       &fg_color.red, &fg_color.green, &fg_color.blue);
142           hsv_to_rgb (bg_h, bg_s, bg_v, 
143                       &bg_color.red, &bg_color.green, &bg_color.blue);
144         }
145       else
146         {
147           XQueryColor (dsp, cmap, &fg_color);
148           XQueryColor (dsp, cmap, &bg_color);
149           fg_color.pixel = fg_pixel;
150           bg_color.pixel = bg_pixel;
151         }
152       fg_color.flags = DoRed|DoGreen|DoBlue;
153       bg_color.flags = DoRed|DoGreen|DoBlue;
154
155       rgb_to_hsv (fg_color.red, fg_color.green, fg_color.blue,
156                   &fg_h, &fg_s, &fg_v);
157       rgb_to_hsv (bg_color.red, bg_color.green, bg_color.blue,
158                   &bg_h, &bg_s, &bg_v);
159
160       if (/*mode == MODE_RANDOM &&*/
161           ((this_mode == MODE_S && (fg_s-bg_s) > -0.3 && (fg_s-bg_s) < 0.3) ||
162            (this_mode == MODE_V && (fg_v-bg_v) > -0.3 && (fg_v-bg_v) < 0.3) ||
163            (this_mode == MODE_H && (fg_h-bg_h) > -30 && (fg_h-bg_h) < 30)))
164         goto HSV_AGAIN;
165
166       switch (this_mode) {
167       case MODE_H:  shift = (bg_h - fg_h) / ncolors; break;
168       case MODE_S: dshift = (bg_s - fg_s) / ncolors; break;
169       case MODE_V: dshift = (bg_v - fg_v) / ncolors; break;
170       default: abort ();
171       }
172       
173       if (mode == MODE_RANDOM &&
174           ((this_mode == MODE_H)
175            ? ((shift > -2 && shift < 2) || fg_s < 0.3 || fg_v < 0.3)
176            : (dshift > -0.005 && dshift < 0.005)))
177         goto HSV_AGAIN;
178
179       if (mode == MODE_RANDOM && this_mode == MODE_S && fg_v < 0.5)
180         goto V_AGAIN;
181
182       for (npixels = 0; npixels < ncolors; npixels++)
183         {
184           if (cycle_p)
185             {
186               unsigned long plane_masks;
187               /* allocate the writable color cells, one at a time. */
188               if (! XAllocColorCells (dsp, cmap, False, &plane_masks, 0,
189                                       &fg_color.pixel, 1))
190                 {
191                   fprintf (stderr,
192    "%s: couldn't allocate %s writable color cells.  Turning off -cycle.\n",
193                            progname, (npixels ? "enough" : "any"));
194                   cycle_p = 0;
195                   goto NON_CYCLE;
196                 }
197               XStoreColor (dsp, cmap, &fg_color);
198             }
199           else
200             {
201             NON_CYCLE:
202               if (!XAllocColor (dsp, cmap, &fg_color))
203                 break;
204             }
205           pixels[npixels] = fg_color.pixel;
206         
207           switch (this_mode)
208             {
209             case MODE_H: fg_h = (fg_h + shift) % 360; break;
210             case MODE_S: fg_s += dshift; break;
211             case MODE_V: fg_v += dshift; break;
212             default: abort ();
213             }
214           hsv_to_rgb (fg_h, fg_s, fg_v,
215                       &fg_color.red, &fg_color.green, &fg_color.blue);
216         }
217     }
218   XSetForeground (dsp, gc, pixels [0]);
219   XFillRectangle (dsp, win, gc, 0, 0, wattrs.width, wattrs.height);
220 }
221
222
223 #define HEIGHT_TO_PIXEL(height) \
224         (((int) (height)) < 0 ? 0 : \
225          ((int) (height)) >= npixels ? npixels - 3 : ((int) (height)))
226
227 static unsigned int
228 set (l, c, size, height)
229      unsigned int l, c, size;
230      int height;
231 {
232   int rang = 1 << (NSTEPS - size);
233   height = height + (random () % rang) - rang / 2;
234   CELL (l, c) = height;
235
236   return pixels [HEIGHT_TO_PIXEL (height)];
237 }
238
239 static void
240 floyd_steinberg ()
241 {
242   int x, y, err;
243
244   /* Instead of repeatedly calling XPutPixel(), we make an Image and then
245      send its bits over all at once.  This consumes much less network
246      bandwidth.  The image we create is Wx1 intead of WxH, so that we
247      don't use enormous amounts of memory.
248    */
249   XImage *image =
250     XCreateImage (disp, DefaultVisual(disp,DefaultScreen(disp)),
251                   1, XYBitmap, 0,               /* depth, format, offset */
252                   (char *) calloc ((xmax + 1) / 8, 1),  /* data */
253                   xmax, 1, 8, 0);               /* w, h, pad, bpl */
254
255   for (y = 0; y < ymax - 1; y++)
256     {
257       for (x = 0; x < xmax - 1; x++)
258         {
259           if (CELL(x, y) < 0)
260             {
261               err = CELL (x, y);
262               XPutPixel (image, x, 0, 1);
263             }
264           else
265             {
266               err = CELL (x, y) - 1;
267               XPutPixel (image, x, 0, 0);
268             }
269           /* distribute error */
270           CELL (x,   y+1) += (int) (((float) err) * 3.0/8.0);
271           CELL (x+1, y)   += (int) (((float) err) * 3.0/8.0);
272           CELL (x+1, y+1) += (int) (((float) err) * 1.0/4.0);
273         }
274       XPutImage (disp, wind, gc, image, 0, 0, 0, y, xmax, 1);
275     }
276   XDestroyImage (image);
277 }
278
279 static void
280 draw (x, y, pixel, grid_size)   /* not called in mono mode */
281      int x, y, grid_size;
282      unsigned long pixel;
283 {
284   static unsigned int last_pixel, last_valid = 0;
285   if (! (last_valid && pixel == last_pixel))
286     XSetForeground (disp, gc, pixel);
287   last_valid = 1, last_pixel = pixel;
288   if (grid_size == 1)
289     XDrawPoint (disp, wind, gc, x, y);
290   else
291     XFillRectangle (disp, wind, gc, x, y, grid_size, grid_size);
292 }
293
294
295 static void 
296 drawmap ()
297 {
298   unsigned int x, y, i, step, nextStep, x1, x2, y1, y2;
299   unsigned int pixel, qpixels [4];
300
301   xmax = wattrs.width;
302   ymax = wattrs.height;
303
304   cell = (signed char *) calloc (xmax * ymax, 1);
305   if (cell == NULL)
306     exit (1);
307
308   CELL (0, 0) = 0;
309   step = COUNT;
310   for (i = 0; i < iterations; i++)
311     {
312       nextStep = step / 2;
313       for (x = 0; x < xmax; x += step)
314         {
315           x1 = x + nextStep;
316           if (x1 >= xmax)
317             x1 = 0;
318           x2 = x + step;
319           if (x2 >= xmax)
320             x2 = 0;
321           for (y = 0; y < ymax; y += step)
322             {
323               y1 = y + nextStep;
324               if (y1 >= ymax)
325                 y1 = 0;
326               y2 = y + step;
327               if (y2 >= ymax)
328                 y2 = 0;
329
330               qpixels [0] = pixels [HEIGHT_TO_PIXEL (CELL (x, y))];
331               qpixels [1] = pixels [HEIGHT_TO_PIXEL (CELL (x, y2))];
332               qpixels [2] = pixels [HEIGHT_TO_PIXEL (CELL (x2, y))];
333               qpixels [3] = pixels [HEIGHT_TO_PIXEL (CELL (x2, y2))];
334
335               pixel = set (x, y1, i,
336                            ((int) CELL (x, y) + (int) CELL (x, y2) + 1) / 2);
337               if (! mono_p &&
338                   (pixel != qpixels[0] || pixel != qpixels[1] || 
339                    pixel != qpixels[2] || pixel != qpixels[3]))
340                 draw (x, y1, pixel, nextStep);
341
342               pixel = set (x1, y, i,
343                            ((int) CELL (x, y) + (int) CELL (x2, y) + 1) / 2);
344               if (! mono_p &&
345                   (pixel != qpixels[0] || pixel != qpixels[1] || 
346                    pixel != qpixels[2] || pixel != qpixels[3]))
347                 draw (x1, y, pixel, nextStep);
348
349               pixel = set (x1, y1, i,
350                            ((int) CELL (x, y) + (int) CELL (x, y2) +
351                             (int) CELL (x2, y) + (int) CELL (x2, y2) + 2)
352                            / 4);
353               if (! mono_p &&
354                   (pixel != qpixels[0] || pixel != qpixels[1] || 
355                    pixel != qpixels[2] || pixel != qpixels[3]))
356                 draw (x1, y1, pixel, nextStep);
357             }
358         }
359       step = nextStep;
360       if (!mono_p)
361         XSync (disp, True);
362     }
363   if (mono_p)
364     /* in mono-mode, we do all the drawing at the end */
365     floyd_steinberg ();
366   
367   free (cell);
368   XSync (disp, True);
369 }
370
371 static void
372 cycle (dpy)
373      Display *dpy;
374 {
375   XColor *colors = (XColor *) malloc (npixels * sizeof (XColor));
376   time_t stop;
377   int i;
378   for (i = 0; i < npixels; i++)
379     colors [i].pixel = pixels [i];
380   XQueryColors (dpy, cmap, colors, npixels);
381   stop = (time_t) ((time ((time_t) 0)) + timeout);
382   while (stop >= (time_t) time ((time_t) 0))
383     {
384       unsigned long scratch = colors [npixels-1].pixel;
385       for (i = npixels-1; i > 0; i--)
386         colors [i].pixel = colors [i-1].pixel;
387       colors [0].pixel = scratch;
388       XStoreColors (dpy, cmap, colors, npixels);
389       XSync (dpy, True);
390       if (cycle_delay) usleep (cycle_delay);
391     }
392   XSync (dpy, True);
393   free (colors);
394 }
395
396
397 char *progclass = "Imsmap";
398
399 char *defaults [] = {
400   "Imsmap.background:   black",         /* to placate SGI */
401   "Imsmap.foreground:   black",
402   "*mode:       random",
403   "*ncolors:    50",
404   "*iterations: 7",
405   "*timeout:    10",
406   "*cycleDelay: 100000",
407   "*cycle:      true",
408   0
409 };
410
411 XrmOptionDescRec options [] = {
412   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
413   { "-timeout",         ".timeout",     XrmoptionSepArg, 0 },
414   { "-cycle-delay",     ".cycleDelay",  XrmoptionSepArg, 0 },
415   { "-mode",            ".mode",        XrmoptionSepArg, 0 },
416   { "-iterations",      ".iterations",  XrmoptionSepArg, 0 },
417   { "-cycle",           ".cycle",       XrmoptionNoArg, "True"  },
418   { "-no-cycle",        ".cycle",       XrmoptionNoArg, "False" }
419 };
420 int options_size = (sizeof (options) / sizeof (options[0]));
421
422
423 void
424 screenhack (dpy, window)
425      Display *dpy;
426      Window window;
427 {
428     disp = dpy;
429     wind = window;
430     while (1)
431       {
432         initwin (dpy, window);
433         drawmap ();
434         if (timeout)
435           {
436             if (cycle_p)
437               cycle (dpy);
438             else
439               sleep (timeout);
440           }
441       }
442 }