http://ftp.x.org/contrib/applications/xscreensaver-2.23.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  * Revision History:
13  * 24-aug-92: jwz: hacked.
14  * 17-May-97: jwz: hacked more.
15  */
16
17 #include <stdio.h>
18 #include <math.h>
19 #include <sys/time.h> /* for gettimeofday() */
20
21 #include <X11/Xlib.h>
22 #include <X11/Xutil.h>
23
24 #include "screenhack.h"
25
26 #define NSTEPS 7
27 #define COUNT (1 << NSTEPS)
28 #define CELL(c, r) cell[((unsigned int)(c)) + ((unsigned int) (r)) * xmax]
29
30 static enum mode_t { MODE_H, MODE_S, MODE_V, MODE_RANDOM } mode;
31
32 static GC gc, gc2;
33 static XWindowAttributes xgwa;
34
35 #if defined(sun) && !__STDC__   /* sun cc doesn't know "signed char" */
36 #define signed /**/
37 #endif
38
39 static Colormap cmap;
40 static int ncolors;
41 static XColor *colors;
42 static Bool cycle_p;
43 static int cycle_direction;
44 static Bool extra_krinkly_p;
45
46 static int delay, cycle_delay;
47 static signed char *cell = NULL;
48 static int xmax, ymax;
49 static int iterations;
50
51 static void
52 init_map (Display *dpy, Window window)
53 {
54   unsigned long fg_pixel = 0, bg_pixel = 0;
55   int fg_h, bg_h;
56   double fg_s, fg_v, bg_s, bg_v;
57
58   enum mode_t this_mode;
59   static Bool rv_p;
60     
61   XGCValues gcv;
62
63   XGetWindowAttributes (dpy, window, &xgwa);
64   cmap = xgwa.colormap;
65
66   if (!ncolors)
67     {
68       char *mode_str = get_string_resource ("mode", "Mode");
69       rv_p = get_boolean_resource ("reverseVideo", "ReverseVideo");
70       cycle_p = get_boolean_resource ("cycle", "Cycle");
71       ncolors = get_integer_resource ("ncolors", "Integer");
72       delay = get_integer_resource ("delay", "Integer");
73       cycle_delay = get_integer_resource ("cycleDelay", "Integer");
74       iterations = get_integer_resource ("iterations", "Integer");
75       if (iterations < 0) iterations = 0;
76       else if (iterations > 7) iterations = 7;
77
78       if (ncolors <= 2) ncolors = 0;
79       if (ncolors == 0) mono_p = True;
80
81       fg_pixel = get_pixel_resource ("background", "Background", dpy, cmap);
82       bg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
83
84       if (fg_pixel == bg_pixel)
85         {
86           XColor black, white;
87           black.red = black.green = black.blue = 0;
88           white.red = white.green = white.blue = 0xFFFF;
89           black.flags = white.flags = DoRed|DoGreen|DoBlue;
90           XAllocColor(dpy, cmap, &black);
91           XAllocColor(dpy, cmap, &white);
92           if (bg_pixel == black.pixel)
93             fg_pixel = white.pixel;
94           else
95             fg_pixel = black.pixel;
96         }
97
98       if (mono_p) cycle_p = False;
99
100       gcv.foreground = fg_pixel;
101       gcv.background = bg_pixel;
102       gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
103       gcv.foreground = bg_pixel;
104       gc2 = XCreateGC (dpy, window, GCForeground, &gcv);
105
106       if (!mode_str || !strcmp (mode_str, "random"))
107         mode = MODE_RANDOM;
108       else if (!strcmp (mode_str, "h") || !strcmp (mode_str, "hue"))
109         mode = MODE_H;
110       else if (!strcmp (mode_str, "s") || !strcmp (mode_str, "saturation"))
111         mode = MODE_S;
112       else if (!strcmp (mode_str, "v") || !strcmp (mode_str, "value"))
113         mode = MODE_V;
114       else
115         {
116           fprintf (stderr,
117            "%s: mode must be hue, saturation, value, or random, not \"%s\"\n",
118                    progname, mode_str);
119           mode = MODE_RANDOM;
120         }
121     }
122
123   this_mode = mode;
124   if (!mono_p && mode == MODE_RANDOM)
125     switch (random () % 6) {
126     case 0: this_mode = MODE_H; break;
127     case 1: this_mode = MODE_S; break;
128     case 2: this_mode = MODE_V; break;
129     default: break;
130     }
131
132   if (mono_p)
133     extra_krinkly_p = !(random() % 15);
134   else
135     extra_krinkly_p = !(random() % 5);
136
137   if (!mono_p)
138     {
139       double distance, fg_H, bg_H, dh;
140
141     RETRY:
142       fg_h = random() % 360;
143       fg_s = frand(1.0);
144       fg_v = frand(1.0);
145
146       bg_h = fg_h;
147       bg_s = fg_s;
148       bg_v = fg_v;
149
150       switch (this_mode)
151         {
152         case MODE_H:
153           bg_h = random() % 360;
154           if (fg_v < 0.4)
155             goto RETRY;
156           distance = fg_h - bg_h;
157           if (distance < 0)
158             distance = -distance;
159           if (distance > 360)
160             distance = 180 - (distance - 180);
161           if (distance < 30)
162             goto RETRY;
163           break;
164
165         case MODE_S:
166           bg_s = frand(1.0);
167           if (fg_v < 0.4)
168             goto RETRY;
169           distance = fg_s - bg_s;
170           if (distance < 0)
171             distance = -distance;
172           if (distance < 0.2)
173             goto RETRY;
174           break;
175
176         case MODE_V:
177           bg_v = frand(1.0);
178           distance = fg_v - bg_v;
179           if (distance < 0)
180             distance = -distance;
181           if (distance < 0.4)
182             goto RETRY;
183           break;
184
185         default:
186           bg_h = random() % 360;
187           bg_s = frand(1.0);
188           bg_v = frand(1.0);
189
190           fg_H = ((double) fg_h) / 360;
191           bg_H = ((double) bg_h) / 360;
192           dh = fg_H - bg_H;
193           if (dh < 0) dh = -dh;
194           if (dh > 0.5) dh = 0.5 - (dh - 0.5);
195           distance = sqrt ((dh * dh) +
196                            ((fg_s - bg_s) * (fg_s - bg_s)) +
197                            ((fg_v - bg_v) * (fg_v - bg_v)));
198           if (distance < 0.2)
199             goto RETRY;
200         }
201
202       cycle_p = True;
203       if (colors)
204         free_colors (dpy, cmap, colors, ncolors);
205       else
206         colors = (XColor *) malloc (ncolors * sizeof(*colors));
207
208       cycle_direction = (random() & 1 ? 1 : -1);
209
210     RETRY_NON_WRITABLE:
211       {
212         int n = ncolors;
213         make_color_ramp (dpy, cmap,
214                          fg_h, fg_s, fg_v,
215                          bg_h, bg_s, bg_v,
216                          colors, &n,
217                          True, True, cycle_p);
218         if (n == 0 && cycle_p)
219           {
220             cycle_p = False;
221             goto RETRY_NON_WRITABLE;
222           }
223         ncolors = n;
224       }
225
226       if (ncolors <= 0)
227         mono_p = 1;
228     }
229
230   if (mono_p)
231     {
232       static Bool done = False;
233       static XColor c[50];
234           colors = c;
235       cycle_p = False;
236       ncolors = sizeof(c)/sizeof(*c);
237       if (!done)
238         {
239           int i;
240           done = True;
241           colors[0].pixel = fg_pixel;
242           for (i = 1; i < ncolors; i++)
243             colors[i].pixel = bg_pixel;
244         }
245     }
246
247   XSetForeground (dpy, gc, colors[1].pixel);
248   XFillRectangle (dpy, window, gc, 0, 0, xgwa.width, xgwa.height);
249 }
250
251
252 #define HEIGHT_TO_PIXEL(height)                         \
253         ((height) < 0                                   \
254          ? (extra_krinkly_p                             \
255             ? ncolors - ((-(height)) % ncolors)         \
256             : 0)                                        \
257          : ((height) >= ncolors                         \
258             ? (extra_krinkly_p                          \
259                ? (height) % ncolors                     \
260                : ncolors-1)                             \
261             : (height)))
262
263
264 static unsigned int
265 set (unsigned int l,
266      unsigned int c,
267      unsigned int size,
268      int height)
269 {
270   int rang = 1 << (NSTEPS - size);
271   height = height + (random () % rang) - rang / 2;
272   height = HEIGHT_TO_PIXEL(height);
273   CELL (l, c) = height;
274   return colors[height].pixel;
275 }
276
277 static void
278 floyd_steinberg (Display *dpy, Window window)
279 {
280   int x, y, err;
281
282   /* Instead of repeatedly calling XPutPixel(), we make an Image and then
283      send its bits over all at once.  This consumes much less network
284      bandwidth.  The image we create is Wx1 intead of WxH, so that we
285      don't use enormous amounts of memory.
286    */
287   XImage *image =
288     XCreateImage (dpy, xgwa.visual,
289                   1, XYBitmap, 0,               /* depth, format, offset */
290                   (char *) calloc ((xmax + 8) / 8, 1),  /* data */
291                   xmax, 1, 8, 0);               /* w, h, pad, bpl */
292
293   XSetForeground (dpy, gc, colors[0].pixel);
294   XSetBackground (dpy, gc, colors[1].pixel);
295
296   for (y = 0; y < ymax - 1; y++)
297     {
298       for (x = 0; x < xmax - 1; x++)
299         {
300           if (CELL(x, y) < 0)
301             {
302               err = CELL (x, y);
303               XPutPixel (image, x, 0, 1);
304             }
305           else
306             {
307               err = CELL (x, y) - 1;
308               XPutPixel (image, x, 0, 0);
309             }
310           /* distribute error */
311           CELL (x,   y+1) += (int) (((float) err) * 3.0/8.0);
312           CELL (x+1, y)   += (int) (((float) err) * 3.0/8.0);
313           CELL (x+1, y+1) += (int) (((float) err) * 1.0/4.0);
314         }
315       XPutImage (dpy, window, gc, image, 0, 0, 0, y, xmax, 1);
316     }
317   XDestroyImage (image);
318 }
319
320 static void
321 draw (Display *dpy, Window window,
322       int x, int y, unsigned long pixel, int grid_size)
323 {
324   static unsigned int last_pixel, last_valid = 0;
325   if (! (last_valid && pixel == last_pixel))
326     XSetForeground (dpy, gc, pixel);
327   last_valid = 1, last_pixel = pixel;
328   if (grid_size == 1)
329     XDrawPoint (dpy, window, gc, x, y);
330   else
331     XFillRectangle (dpy, window, gc, x, y, grid_size, grid_size);
332 }
333
334
335 static void 
336 draw_map (Display *dpy, Window window)
337 {
338   int xstep, ystep, xnextStep, ynextStep;
339   int x, y, i, x1, x2, y1, y2;
340   unsigned int pixel, qpixels [4];
341
342   int backwards = random() & 1;
343
344   xmax = xgwa.width;
345   ymax = xgwa.height;
346
347   cell = (signed char *) calloc (xmax * ymax, 1);
348   if (cell == NULL)
349     exit (1);
350
351   CELL (0, 0) = 0;
352   xstep = (backwards ? -COUNT : COUNT);
353   ystep = COUNT;
354   for (i = 0; i < iterations; i++)
355     {
356       xnextStep = xstep / 2;
357       ynextStep = ystep / 2;
358       for (x = (backwards ? xmax-1 : 0);
359            (backwards ? x >= 0 : x < xmax);
360            x += xstep)
361         {
362           x1 = x + xnextStep;
363           if (x1 < 0)
364             x1 = xmax-1;
365           else if (x1 >= xmax)
366             x1 = 0;
367
368           x2 = x + xstep;
369           if (x2 < 0)
370             x2 = xmax-1;
371           else if (x2 >= xmax)
372             x2 = 0;
373
374           for (y = 0; y < ymax; y += ystep)
375             {
376               y1 = y + ynextStep;
377               if (y1 < 0)
378                 y1 = ymax-1;
379               else if (y1 >= ymax)
380                 y1 = 0;
381
382               y2 = y + ystep;
383               if (y2 < 0)
384                 y2 = ymax-1;
385               else if (y2 >= ymax)
386                 y2 = 0;
387
388               qpixels [0] = colors [HEIGHT_TO_PIXEL (CELL (x, y))].pixel;
389               qpixels [1] = colors [HEIGHT_TO_PIXEL (CELL (x, y2))].pixel;
390               qpixels [2] = colors [HEIGHT_TO_PIXEL (CELL (x2, y))].pixel;
391               qpixels [3] = colors [HEIGHT_TO_PIXEL (CELL (x2, y2))].pixel;
392
393               pixel = set (x, y1, i,
394                            ((int) CELL (x, y) + (int) CELL (x, y2) + 1) / 2);
395               if (! mono_p &&
396                   (pixel != qpixels[0] || pixel != qpixels[1] || 
397                    pixel != qpixels[2] || pixel != qpixels[3]))
398                 draw (dpy, window, x, y1, pixel, ynextStep);
399
400               pixel = set (x1, y, i,
401                            ((int) CELL (x, y) + (int) CELL (x2, y) + 1) / 2);
402               if (! mono_p &&
403                   (pixel != qpixels[0] || pixel != qpixels[1] || 
404                    pixel != qpixels[2] || pixel != qpixels[3]))
405                 draw (dpy, window, x1, y, pixel, ynextStep);
406
407               pixel = set (x1, y1, i,
408                            ((int) CELL (x, y) + (int) CELL (x, y2) +
409                             (int) CELL (x2, y) + (int) CELL (x2, y2) + 2)
410                            / 4);
411               if (! mono_p &&
412                   (pixel != qpixels[0] || pixel != qpixels[1] || 
413                    pixel != qpixels[2] || pixel != qpixels[3]))
414                 draw (dpy, window, x1, y1, pixel, ynextStep);
415
416
417               if (cycle_p)
418                 {
419                   struct timeval now;
420                   static struct timeval then = { 0, };
421                   unsigned long diff;
422 #ifdef GETTIMEOFDAY_TWO_ARGS
423                   struct timezone tzp;
424                   gettimeofday(&now, &tzp);
425 #else
426                   gettimeofday(&now);
427 #endif
428                   diff = (((now.tv_sec - then.tv_sec)  * 1000000) +
429                           (now.tv_usec - then.tv_usec));
430                   if (diff > cycle_delay)
431                     {
432                       rotate_colors (dpy, cmap, colors, ncolors,
433                                      cycle_direction);
434                       then = now;
435                     }
436                 }
437             }
438         }
439       xstep = xnextStep;
440       ystep = ynextStep;
441       if (!mono_p)
442         XSync (dpy, True);
443     }
444   if (mono_p)
445     /* in mono-mode, we do all the drawing at the end */
446     floyd_steinberg (dpy, window);
447   
448   free (cell);
449   XSync (dpy, True);
450 }
451
452
453 char *progclass = "Imsmap";
454
455 char *defaults [] = {
456   ".background: black",
457   ".foreground: black",
458   "*mode:       random",
459   "*ncolors:    50",
460   "*iterations: 7",
461   "*delay:      10",
462   "*cycleDelay: 100000",
463   "*cycle:      true",
464   0
465 };
466
467 XrmOptionDescRec options [] = {
468   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
469   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
470   { "-cycle-delay",     ".cycleDelay",  XrmoptionSepArg, 0 },
471   { "-mode",            ".mode",        XrmoptionSepArg, 0 },
472   { "-iterations",      ".iterations",  XrmoptionSepArg, 0 },
473   { "-cycle",           ".cycle",       XrmoptionNoArg, "True"  },
474   { "-no-cycle",        ".cycle",       XrmoptionNoArg, "False" },
475   { 0, 0, 0, 0 }
476 };
477
478
479 void
480 screenhack (Display *dpy, Window window)
481 {
482     while (1)
483       {
484         init_map (dpy, window);
485         draw_map (dpy, window);
486         if (delay)
487           {
488             if (cycle_p)
489               {
490                 time_t start = time((time_t) 0);
491                 while (start + delay > time((time_t) 0))
492                   {
493                     rotate_colors (dpy, cmap, colors, ncolors,
494                                    cycle_direction);
495                     if (cycle_delay) usleep(cycle_delay);
496                   }
497               }
498             else
499               sleep (delay);
500           }
501       }
502 }