ftp://ftp.ntnu.no/old/pub/X11/R5contrib/xscreensaver-1.17.tar.gz
[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           fg_color.pixel = fg_pixel;
143           if (! XQueryColor (dsp, cmap, &fg_color))
144             abort ();
145           bg_color.pixel = bg_pixel;
146           if (! XQueryColor (dsp, cmap, &bg_color))
147             abort ();
148         }
149       fg_color.flags = DoRed|DoGreen|DoBlue;
150       bg_color.flags = DoRed|DoGreen|DoBlue;
151
152       rgb_to_hsv (fg_color.red, fg_color.green, fg_color.blue,
153                   &fg_h, &fg_s, &fg_v);
154       rgb_to_hsv (bg_color.red, bg_color.green, bg_color.blue,
155                   &bg_h, &bg_s, &bg_v);
156
157       if (/*mode == MODE_RANDOM &&*/
158           ((this_mode == MODE_S && (fg_s-bg_s) > -0.3 && (fg_s-bg_s) < 0.3) ||
159            (this_mode == MODE_V && (fg_v-bg_v) > -0.3 && (fg_v-bg_v) < 0.3) ||
160            (this_mode == MODE_H && (fg_h-bg_h) > -30 && (fg_h-bg_h) < 30)))
161         goto HSV_AGAIN;
162
163       switch (this_mode) {
164       case MODE_H:  shift = (bg_h - fg_h) / ncolors; break;
165       case MODE_S: dshift = (bg_s - fg_s) / ncolors; break;
166       case MODE_V: dshift = (bg_v - fg_v) / ncolors; break;
167       default: abort ();
168       }
169       
170       if (mode == MODE_RANDOM &&
171           ((this_mode == MODE_H)
172            ? ((shift > -2 && shift < 2) || fg_s < 0.3 || fg_v < 0.3)
173            : (dshift > -0.005 && dshift < 0.005)))
174         goto HSV_AGAIN;
175
176       if (mode == MODE_RANDOM && this_mode == MODE_S && fg_v < 0.5)
177         goto V_AGAIN;
178
179       for (npixels = 0; npixels < ncolors; npixels++)
180         {
181           if (cycle_p)
182             {
183               unsigned long plane_masks;
184               /* allocate the writable color cells, one at a time. */
185               if (! XAllocColorCells (dsp, cmap, False, &plane_masks, 0,
186                                       &fg_color.pixel, 1))
187                 {
188                   fprintf (stderr,
189    "%s: couldn't allocate %s writable color cells.  Turning off -cycle.\n",
190                            progname, (npixels ? "enough" : "any"));
191                   cycle_p = 0;
192                   goto NON_CYCLE;
193                 }
194               XStoreColor (dsp, cmap, &fg_color);
195             }
196           else
197             {
198             NON_CYCLE:
199               if (!XAllocColor (dsp, cmap, &fg_color))
200                 break;
201             }
202           pixels[npixels] = fg_color.pixel;
203         
204           switch (this_mode)
205             {
206             case MODE_H: fg_h = (fg_h + shift) % 360; break;
207             case MODE_S: fg_s += dshift; break;
208             case MODE_V: fg_v += dshift; break;
209             default: abort ();
210             }
211           hsv_to_rgb (fg_h, fg_s, fg_v,
212                       &fg_color.red, &fg_color.green, &fg_color.blue);
213         }
214     }
215   XSetForeground (dsp, gc, pixels [0]);
216   XFillRectangle (dsp, win, gc, 0, 0, wattrs.width, wattrs.height);
217 }
218
219
220 #define HEIGHT_TO_PIXEL(height) \
221         (((int) (height)) < 0 ? 0 : \
222          ((int) (height)) >= npixels ? npixels - 3 : ((int) (height)))
223
224 static unsigned int
225 set (l, c, size, height)
226      unsigned int l, c, size;
227      int height;
228 {
229   int rang = 1 << (NSTEPS - size);
230   height = height + (random () % rang) - rang / 2;
231   CELL (l, c) = height;
232
233   return pixels [HEIGHT_TO_PIXEL (height)];
234 }
235
236 static void
237 floyd_steinberg ()
238 {
239   int x, y, err;
240
241   /* Instead of repeatedly calling XPutPixel(), we make an Image and then
242      send its bits over all at once.  This consumes much less network
243      bandwidth.  The image we create is Wx1 intead of WxH, so that we
244      don't use enormous amounts of memory.
245    */
246   XImage *image =
247     XCreateImage (disp, DefaultVisual(disp,DefaultScreen(disp)),
248                   1, XYBitmap, 0,               /* depth, format, offset */
249                   (char *) calloc ((xmax + 1) / 8, 1),  /* data */
250                   xmax, 1, 8, 0);               /* w, h, pad, bpl */
251
252   for (y = 0; y < ymax - 1; y++)
253     {
254       for (x = 0; x < xmax - 1; x++)
255         {
256           if (CELL(x, y) < 0)
257             {
258               err = CELL (x, y);
259               XPutPixel (image, x, 0, 1);
260             }
261           else
262             {
263               err = CELL (x, y) - 1;
264               XPutPixel (image, x, 0, 0);
265             }
266           /* distribute error */
267           CELL (x,   y+1) += (int) (((float) err) * 3.0/8.0);
268           CELL (x+1, y)   += (int) (((float) err) * 3.0/8.0);
269           CELL (x+1, y+1) += (int) (((float) err) * 1.0/4.0);
270         }
271       XPutImage (disp, wind, gc, image, 0, 0, 0, y, xmax, 1);
272     }
273   XDestroyImage (image);
274 }
275
276 static void
277 draw (x, y, pixel, grid_size)   /* not called in mono mode */
278      int x, y, grid_size;
279      unsigned long pixel;
280 {
281   static unsigned int last_pixel, last_valid = 0;
282   if (! (last_valid && pixel == last_pixel))
283     XSetForeground (disp, gc, pixel);
284   last_valid = 1, last_pixel = pixel;
285   if (grid_size == 1)
286     XDrawPoint (disp, wind, gc, x, y);
287   else
288     XFillRectangle (disp, wind, gc, x, y, grid_size, grid_size);
289 }
290
291
292 static void 
293 drawmap ()
294 {
295   unsigned int x, y, i, step, nextStep, x1, x2, y1, y2;
296   unsigned int pixel, qpixels [4];
297
298   xmax = wattrs.width;
299   ymax = wattrs.height;
300
301   cell = (signed char *) calloc (xmax * ymax, 1);
302   if (cell == NULL)
303     exit (1);
304
305   CELL (0, 0) = 0;
306   step = COUNT;
307   for (i = 0; i < iterations; i++)
308     {
309       nextStep = step / 2;
310       for (x = 0; x < xmax; x += step)
311         {
312           x1 = x + nextStep;
313           if (x1 >= xmax)
314             x1 = 0;
315           x2 = x + step;
316           if (x2 >= xmax)
317             x2 = 0;
318           for (y = 0; y < ymax; y += step)
319             {
320               y1 = y + nextStep;
321               if (y1 >= ymax)
322                 y1 = 0;
323               y2 = y + step;
324               if (y2 >= ymax)
325                 y2 = 0;
326
327               qpixels [0] = pixels [HEIGHT_TO_PIXEL (CELL (x, y))];
328               qpixels [1] = pixels [HEIGHT_TO_PIXEL (CELL (x, y2))];
329               qpixels [2] = pixels [HEIGHT_TO_PIXEL (CELL (x2, y))];
330               qpixels [3] = pixels [HEIGHT_TO_PIXEL (CELL (x2, y2))];
331
332               pixel = set (x, y1, i,
333                            ((int) CELL (x, y) + (int) CELL (x, y2) + 1) / 2);
334               if (! mono_p &&
335                   (pixel != qpixels[0] || pixel != qpixels[1] || 
336                    pixel != qpixels[2] || pixel != qpixels[3]))
337                 draw (x, y1, pixel, nextStep);
338
339               pixel = set (x1, y, i,
340                            ((int) CELL (x, y) + (int) CELL (x2, y) + 1) / 2);
341               if (! mono_p &&
342                   (pixel != qpixels[0] || pixel != qpixels[1] || 
343                    pixel != qpixels[2] || pixel != qpixels[3]))
344                 draw (x1, y, pixel, nextStep);
345
346               pixel = set (x1, y1, i,
347                            ((int) CELL (x, y) + (int) CELL (x, y2) +
348                             (int) CELL (x2, y) + (int) CELL (x2, y2) + 2)
349                            / 4);
350               if (! mono_p &&
351                   (pixel != qpixels[0] || pixel != qpixels[1] || 
352                    pixel != qpixels[2] || pixel != qpixels[3]))
353                 draw (x1, y1, pixel, nextStep);
354             }
355         }
356       step = nextStep;
357       if (!mono_p)
358         XSync (disp, True);
359     }
360   if (mono_p)
361     /* in mono-mode, we do all the drawing at the end */
362     floyd_steinberg ();
363   
364   free (cell);
365   XSync (disp, True);
366 }
367
368 static void
369 cycle (dpy)
370      Display *dpy;
371 {
372   XColor *colors = malloc (npixels * sizeof (XColor));
373   time_t stop;
374   int i;
375   for (i = 0; i < npixels; i++)
376     colors [i].pixel = pixels [i];
377   XQueryColors (dpy, cmap, colors, npixels);
378   stop = (time_t) ((time ((time_t) 0)) + timeout);
379   while (stop >= (time_t) time ((time_t) 0))
380     {
381       unsigned long scratch = colors [npixels-1].pixel;
382       for (i = npixels-1; i > 0; i--)
383         colors [i].pixel = colors [i-1].pixel;
384       colors [0].pixel = scratch;
385       XStoreColors (dpy, cmap, colors, npixels);
386       XSync (dpy, True);
387       if (cycle_delay) usleep (cycle_delay);
388     }
389   XSync (dpy, True);
390   free (colors);
391 }
392
393
394 char *progclass = "Imsmap";
395
396 char *defaults [] = {
397   "*background: black",
398   "*foreground: black",
399   "*mode:       random",
400   "*ncolors:    50",
401   "*iterations: 7",
402   "*timeout:    10",
403   "*cycleDelay: 100000",
404   "*cycle:      true",
405   0
406 };
407
408 XrmOptionDescRec options [] = {
409   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
410   { "-timeout",         ".timeout",     XrmoptionSepArg, 0 },
411   { "-cycle-delay",     ".cycleDelay",  XrmoptionSepArg, 0 },
412   { "-mode",            ".mode",        XrmoptionSepArg, 0 },
413   { "-iterations",      ".iterations",  XrmoptionSepArg, 0 },
414   { "-cycle",           ".cycle",       XrmoptionNoArg, "True"  },
415   { "-no-cycle",        ".cycle",       XrmoptionNoArg, "False" }
416 };
417 int options_size = (sizeof (options) / sizeof (options[0]));
418
419
420 void
421 screenhack (dpy, window)
422      Display *dpy;
423      Window window;
424 {
425     disp = dpy;
426     wind = window;
427     while (1)
428       {
429         initwin (dpy, window);
430         drawmap ();
431         if (timeout)
432           {
433             if (cycle_p)
434               cycle (dpy);
435             else
436               sleep (timeout);
437           }
438       }
439 }