source: trunk/third/xscreensaver/hacks/imsmap.c @ 20148

Revision 20148, 12.2 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
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
31static enum imsmap_mode { MODE_H, MODE_S, MODE_V, MODE_RANDOM } mode;
32
33static GC gc, gc2;
34static XWindowAttributes xgwa;
35
36#if defined(sun) && !__STDC__   /* sun cc doesn't know "signed char" */
37#define signed /**/
38#endif
39
40static Colormap cmap;
41static int ncolors;
42static XColor *colors;
43static Bool cycle_p;
44static int cycle_direction;
45static Bool extra_krinkly_p;
46
47static int delay, cycle_delay;
48static signed char *cell = NULL;
49static int xmax, ymax;
50static int iterations;
51
52static void
53init_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
266static unsigned int
267set (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
279static void
280floyd_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
322static void
323draw (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
337static void
338draw_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
456char *progclass = "Imsmap";
457
458char *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
470XrmOptionDescRec 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
482void
483screenhack (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}
Note: See TracBrowser for help on using the repository browser.