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