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