From http://www.jwz.org/xscreensaver/xscreensaver-5.22.tar.gz
[xscreensaver] / utils / colors.c
1 /* xscreensaver, Copyright (c) 1997-2013 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* This file contains some utility routines for randomly picking the colors
13    to hack the screen with.
14  */
15
16 #include "utils.h"
17 #include "hsv.h"
18 #include "yarandom.h"
19 #include "visual.h"
20 #include "colors.h"
21
22 extern char *progname;
23
24 void
25 free_colors (Screen *screen, Colormap cmap, XColor *colors, int ncolors)
26 {
27   Display *dpy = screen ? DisplayOfScreen (screen) : 0;
28   int i;
29   if (ncolors > 0)
30     {
31       unsigned long *pixels = (unsigned long *)
32         malloc(sizeof(*pixels) * ncolors);
33       for (i = 0; i < ncolors; i++)
34         pixels[i] = colors[i].pixel;
35       XFreeColors (dpy, cmap, pixels, ncolors, 0L);
36       free(pixels);
37     }
38 }
39
40
41 void
42 allocate_writable_colors (Screen *screen, Colormap cmap,
43                           unsigned long *pixels, int *ncolorsP)
44 {
45   Display *dpy = screen ? DisplayOfScreen (screen) : 0;
46   int desired = *ncolorsP;
47   int got = 0;
48   int requested = desired;
49   unsigned long *new_pixels = pixels;
50
51   *ncolorsP = 0;
52   while (got < desired
53          && requested > 0)
54     {
55       if (desired - got < requested)
56         requested = desired - got;
57
58       if (XAllocColorCells (dpy, cmap, False, 0, 0, new_pixels, requested))
59         {
60           /* Got all the pixels we asked for. */
61           new_pixels += requested;
62           got += requested;
63         }
64       else
65         {
66           /* We didn't get all/any of the pixels we asked for.  This time, ask
67              for half as many.  (If we do get all that we ask for, we ask for
68              the same number again next time, so we only do O(log(n)) server
69              roundtrips.)
70           */
71           requested = requested / 2;
72         }
73     }
74   *ncolorsP += got;
75 }
76
77
78 static void
79 complain (int wanted_colors, int got_colors,
80           Bool wanted_writable, Bool got_writable)
81 {
82   if (got_colors > wanted_colors - 10)
83     /* don't bother complaining if we're within ten pixels. */
84     return;
85
86   if (wanted_writable && !got_writable)
87     fprintf (stderr,
88              "%s: wanted %d writable colors; got %d read-only colors.\n",
89              progname, wanted_colors, got_colors);
90   else
91     fprintf (stderr, "%s: wanted %d%s colors; got %d.\n",
92              progname, wanted_colors, (got_writable ? " writable" : ""),
93              got_colors);
94 }
95
96
97
98 void
99 make_color_ramp (Screen *screen, Visual *visual, Colormap cmap,
100                  int h1, double s1, double v1,   /* 0-360, 0-1.0, 0-1.0 */
101                  int h2, double s2, double v2,   /* 0-360, 0-1.0, 0-1.0 */
102                  XColor *colors, int *ncolorsP,
103                  Bool closed_p,
104                  Bool allocate_p,
105                  Bool *writable_pP)
106 {
107   Display *dpy = screen ? DisplayOfScreen (screen) : 0;
108   Bool verbose_p = True;  /* argh. */
109   int i;
110   int total_ncolors = *ncolorsP;
111   int ncolors, wanted;
112   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
113   double dh, ds, dv;            /* deltas */
114
115   wanted = total_ncolors;
116   if (closed_p)
117     wanted = (wanted / 2) + 1;
118
119   /* If this visual doesn't support writable cells, don't bother trying.
120    */
121   if (wanted_writable && !has_writable_cells(screen, visual))
122     *writable_pP = False;
123
124  AGAIN:
125   ncolors = total_ncolors;
126
127   memset (colors, 0, (*ncolorsP) * sizeof(*colors));
128
129   if (closed_p)
130     ncolors = (ncolors / 2) + 1;
131
132   /* Note: unlike other routines in this module, this function assumes that
133      if h1 and h2 are more than 180 degrees apart, then the desired direction
134      is always from h1 to h2 (rather than the shorter path.)  make_uniform
135      depends on this.
136    */
137   dh = ((double)h2 - (double)h1) / ncolors;
138   ds = (s2 - s1) / ncolors;
139   dv = (v2 - v1) / ncolors;
140
141   for (i = 0; i < ncolors; i++)
142     {
143       colors[i].flags = DoRed|DoGreen|DoBlue;
144       hsv_to_rgb ((int) (h1 + (i*dh)), (s1 + (i*ds)), (v1 + (i*dv)),
145                   &colors[i].red, &colors[i].green, &colors[i].blue);
146     }
147
148   if (closed_p)
149     for (i = ncolors; i < *ncolorsP; i++)
150       colors[i] = colors[(*ncolorsP)-i];
151
152   if (!allocate_p)
153     return;
154
155   if (writable_pP && *writable_pP)
156     {
157       unsigned long *pixels = (unsigned long *)
158         malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
159
160       /* allocate_writable_colors() won't do here, because we need exactly this
161          number of cells, or the color sequence we've chosen won't fit. */
162       if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
163         {
164           free(pixels);
165           goto FAIL;
166         }
167
168       for (i = 0; i < *ncolorsP; i++)
169         colors[i].pixel = pixels[i];
170       free (pixels);
171
172       XStoreColors (dpy, cmap, colors, *ncolorsP);
173     }
174   else
175     {
176       for (i = 0; i < *ncolorsP; i++)
177         {
178           XColor color;
179           color = colors[i];
180           if (XAllocColor (dpy, cmap, &color))
181             {
182               colors[i].pixel = color.pixel;
183             }
184           else
185             {
186               free_colors (screen, cmap, colors, i);
187               goto FAIL;
188             }
189         }
190     }
191
192   goto WARN;
193
194  FAIL:
195   /* we weren't able to allocate all the colors we wanted;
196      decrease the requested number and try again.
197    */
198   total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
199                    total_ncolors > 100 ? total_ncolors - 10 :
200                    total_ncolors >  75 ? total_ncolors -  5 :
201                    total_ncolors >  25 ? total_ncolors -  3 :
202                    total_ncolors >  10 ? total_ncolors -  2 :
203                    total_ncolors >   2 ? total_ncolors -  1 :
204                    0);
205   *ncolorsP = total_ncolors;
206   ncolors = total_ncolors;
207   if (total_ncolors > 0)
208     goto AGAIN;
209
210  WARN:
211   
212   if (verbose_p &&
213       /* don't warn if we got 0 writable colors -- probably TrueColor. */
214       (ncolors != 0 || !wanted_writable))
215     complain (wanted, ncolors, wanted_writable, 
216               (wanted_writable && writable_pP && *writable_pP));
217 }
218
219
220 #define MAXPOINTS 50    /* yeah, so I'm lazy */
221
222
223 static void
224 make_color_path (Screen *screen, Visual *visual, Colormap cmap,
225                  int npoints, int *h, double *s, double *v,
226                  XColor *colors, int *ncolorsP,
227                  Bool allocate_p,
228                  Bool *writable_pP)
229 {
230   Display *dpy = screen ? DisplayOfScreen (screen) : 0;
231   int i, j, k;
232   int total_ncolors = *ncolorsP;
233
234   int ncolors[MAXPOINTS];  /* number of pixels per edge */
235   double dh[MAXPOINTS];    /* distance between pixels, per edge (0 - 360.0) */
236   double ds[MAXPOINTS];    /* distance between pixels, per edge (0 - 1.0) */
237   double dv[MAXPOINTS];    /* distance between pixels, per edge (0 - 1.0) */
238
239   if (npoints == 0)
240     {
241       *ncolorsP = 0;
242       return;
243     }
244   else if (npoints == 2)        /* using make_color_ramp() will be faster */
245     {
246       make_color_ramp (screen, visual, cmap,
247                        h[0], s[0], v[0], h[1], s[1], v[1],
248                        colors, ncolorsP,
249                        True,  /* closed_p */
250                        allocate_p, writable_pP);
251       return;
252     }
253   else if (npoints >= MAXPOINTS)
254     {
255       npoints = MAXPOINTS-1;
256     }
257
258  AGAIN:
259
260   {
261     double DH[MAXPOINTS];       /* Distance between H values in the shortest
262                                    direction around the circle, that is, the
263                                    distance between 10 and 350 is 20.
264                                    (Range is 0 - 360.0.)
265                                 */
266     double edge[MAXPOINTS];     /* lengths of edges in unit HSV space. */
267     double ratio[MAXPOINTS];    /* proportions of the edges (total 1.0) */
268     double circum = 0;
269     double one_point_oh = 0;    /* (debug) */
270
271     for (i = 0; i < npoints; i++)
272       {
273         int j = (i+1) % npoints;
274         double d = ((double) (h[i] - h[j])) / 360;
275         if (d < 0) d = -d;
276         if (d > 0.5) d = 0.5 - (d - 0.5);
277         DH[i] = d;
278       }
279
280     for (i = 0; i < npoints; i++)
281       {
282         int j = (i+1) % npoints;
283         edge[i] = sqrt((DH[i] * DH[j]) +
284                        ((s[j] - s[i]) * (s[j] - s[i])) +
285                        ((v[j] - v[i]) * (v[j] - v[i])));
286         circum += edge[i];
287       }
288
289 #ifdef DEBUG
290     fprintf(stderr, "\ncolors:");
291     for (i=0; i < npoints; i++)
292       fprintf(stderr, " (%d, %.3f, %.3f)", h[i], s[i], v[i]);
293     fprintf(stderr, "\nlengths:");
294     for (i=0; i < npoints; i++)
295       fprintf(stderr, " %.3f", edge[i]);
296 #endif /* DEBUG */
297
298     if (circum < 0.0001)
299       goto FAIL;
300
301     for (i = 0; i < npoints; i++)
302       {
303         ratio[i] = edge[i] / circum;
304         one_point_oh += ratio[i];
305       }
306
307 #ifdef DEBUG
308     fprintf(stderr, "\nratios:");
309     for (i=0; i < npoints; i++)
310       fprintf(stderr, " %.3f", ratio[i]);
311 #endif /* DEBUG */
312
313     if (one_point_oh < 0.99999 || one_point_oh > 1.00001)
314       abort();
315
316     /* space the colors evenly along the circumference -- that means that the
317        number of pixels on a edge is proportional to the length of that edge
318        (relative to the lengths of the other edges.)
319      */
320     for (i = 0; i < npoints; i++)
321       ncolors[i] = total_ncolors * ratio[i];
322
323
324 #ifdef DEBUG
325     fprintf(stderr, "\npixels:");
326     for (i=0; i < npoints; i++)
327       fprintf(stderr, " %d", ncolors[i]);
328     fprintf(stderr, "  (%d)\n", total_ncolors);
329 #endif /* DEBUG */
330
331     for (i = 0; i < npoints; i++)
332       {
333         int j = (i+1) % npoints;
334
335         if (ncolors[i] > 0)
336           {
337             dh[i] = 360 * (DH[i] / ncolors[i]);
338             ds[i] = (s[j] - s[i]) / ncolors[i];
339             dv[i] = (v[j] - v[i]) / ncolors[i];
340           }
341       }
342   }
343
344   memset (colors, 0, (*ncolorsP) * sizeof(*colors));
345
346   k = 0;
347   for (i = 0; i < npoints; i++)
348     {
349       int distance = h[(i+1) % npoints] - h[i];
350       int direction = (distance >= 0 ? -1 : 1);
351
352       if (distance <= 180 && distance >= -180)
353         direction = -direction;
354
355 #ifdef DEBUG
356       fprintf (stderr, "point %d: %3d %.2f %.2f\n",
357                i, h[i], s[i], v[i]);
358       fprintf(stderr, "  h[i]=%d  dh[i]=%.2f  ncolors[i]=%d\n",
359               h[i], dh[i], ncolors[i]);
360 #endif /* DEBUG */
361       for (j = 0; j < ncolors[i]; j++, k++)
362         {
363           double hh = (h[i] + (j * dh[i] * direction));
364           if (hh < 0) hh += 360;
365           else if (hh > 360) hh -= 0;
366           colors[k].flags = DoRed|DoGreen|DoBlue;
367           hsv_to_rgb ((int)
368                       hh,
369                       (s[i] + (j * ds[i])),
370                       (v[i] + (j * dv[i])),
371                       &colors[k].red, &colors[k].green, &colors[k].blue);
372 #ifdef DEBUG
373           fprintf (stderr, "point %d+%d: %.2f %.2f %.2f  %04X %04X %04X\n",
374                    i, j,
375                    hh,
376                    (s[i] + (j * ds[i])),
377                    (v[i] + (j * dv[i])),
378                    colors[k].red, colors[k].green, colors[k].blue);
379 #endif /* DEBUG */
380         }
381     }
382
383   /* Floating-point round-off can make us decide to use fewer colors. */
384   if (k < *ncolorsP)
385     {
386       *ncolorsP = k;
387       if (k <= 0)
388         return;
389     }
390
391   if (!allocate_p)
392     return;
393
394   if (writable_pP && *writable_pP)
395     {
396       unsigned long *pixels = (unsigned long *)
397         malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
398
399       /* allocate_writable_colors() won't do here, because we need exactly this
400          number of cells, or the color sequence we've chosen won't fit. */
401       if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
402         {
403           free(pixels);
404           goto FAIL;
405         }
406
407       for (i = 0; i < *ncolorsP; i++)
408         colors[i].pixel = pixels[i];
409       free (pixels);
410
411       XStoreColors (dpy, cmap, colors, *ncolorsP);
412     }
413   else
414     {
415       for (i = 0; i < *ncolorsP; i++)
416         {
417           XColor color;
418           color = colors[i];
419           if (XAllocColor (dpy, cmap, &color))
420             {
421               colors[i].pixel = color.pixel;
422             }
423           else
424             {
425               free_colors (screen, cmap, colors, i);
426               goto FAIL;
427             }
428         }
429     }
430
431   return;
432
433  FAIL:
434   /* we weren't able to allocate all the colors we wanted;
435      decrease the requested number and try again.
436    */
437   total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
438                    total_ncolors > 100 ? total_ncolors - 10 :
439                    total_ncolors >  75 ? total_ncolors -  5 :
440                    total_ncolors >  25 ? total_ncolors -  3 :
441                    total_ncolors >  10 ? total_ncolors -  2 :
442                    total_ncolors >   2 ? total_ncolors -  1 :
443                    0);
444   *ncolorsP = total_ncolors;
445   if (total_ncolors > 0)
446     goto AGAIN;
447 }
448
449
450 void
451 make_color_loop (Screen *screen, Visual *visual, Colormap cmap,
452                  int h0, double s0, double v0,   /* 0-360, 0-1.0, 0-1.0 */
453                  int h1, double s1, double v1,   /* 0-360, 0-1.0, 0-1.0 */
454                  int h2, double s2, double v2,   /* 0-360, 0-1.0, 0-1.0 */
455                  XColor *colors, int *ncolorsP,
456                  Bool allocate_p,
457                  Bool *writable_pP)
458 {
459   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
460
461   int h[3];
462   double s[3], v[3];
463   h[0] = h0; h[1] = h1; h[2] = h2;
464   s[0] = s0; s[1] = s1; s[2] = s2;
465   v[0] = v0; v[1] = v1; v[2] = v2;
466
467   /* If this visual doesn't support writable cells, don't bother trying.
468    */
469   if (wanted_writable && !has_writable_cells(screen, visual))
470     *writable_pP = False;
471
472   make_color_path (screen, visual, cmap,
473                    3, h, s, v,
474                    colors, ncolorsP,
475                    allocate_p, writable_pP);
476 }
477
478
479 void
480 make_smooth_colormap (Screen *screen, Visual *visual, Colormap cmap,
481                       XColor *colors, int *ncolorsP,
482                       Bool allocate_p,
483                       Bool *writable_pP,
484                       Bool verbose_p)
485 {
486   int npoints;
487   int ncolors = *ncolorsP;
488   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
489   int i;
490   int h[MAXPOINTS];
491   double s[MAXPOINTS];
492   double v[MAXPOINTS];
493   double total_s = 0;
494   double total_v = 0;
495   int loop = 0;
496
497   if (*ncolorsP <= 0) return;
498
499   {
500     int n = random() % 20;
501     if      (n <= 5)  npoints = 2;      /* 30% of the time */
502     else if (n <= 15) npoints = 3;      /* 50% of the time */
503     else if (n <= 18) npoints = 4;      /* 15% of the time */
504     else             npoints = 5;       /*  5% of the time */
505   }
506
507  REPICK_ALL_COLORS:
508   for (i = 0; i < npoints; i++)
509     {
510     REPICK_THIS_COLOR:
511       if (++loop > 10000) abort();
512       h[i] = random() % 360;
513       s[i] = frand(1.0);
514       v[i] = frand(0.8) + 0.2;
515
516       /* Make sure that no two adjascent colors are *too* close together.
517          If they are, try again.
518        */
519       if (i > 0)
520         {
521           int j = (i+1 == npoints) ? 0 : (i-1);
522           double hi = ((double) h[i]) / 360;
523           double hj = ((double) h[j]) / 360;
524           double dh = hj - hi;
525           double distance;
526           if (dh < 0) dh = -dh;
527           if (dh > 0.5) dh = 0.5 - (dh - 0.5);
528           distance = sqrt ((dh * dh) +
529                            ((s[j] - s[i]) * (s[j] - s[i])) +
530                            ((v[j] - v[i]) * (v[j] - v[i])));
531           if (distance < 0.2)
532             goto REPICK_THIS_COLOR;
533         }
534       total_s += s[i];
535       total_v += v[i];
536     }
537
538   /* If the average saturation or intensity are too low, repick the colors,
539      so that we don't end up with a black-and-white or too-dark map.
540    */
541   if (total_s / npoints < 0.2)
542     goto REPICK_ALL_COLORS;
543   if (total_v / npoints < 0.3)
544     goto REPICK_ALL_COLORS;
545
546   /* If this visual doesn't support writable cells, don't bother trying.
547    */
548   if (wanted_writable && !has_writable_cells(screen, visual))
549     *writable_pP = False;
550
551  RETRY_NON_WRITABLE:
552   make_color_path (screen, visual, cmap, npoints, h, s, v, colors, &ncolors,
553                    allocate_p, writable_pP);
554
555   /* If we tried for writable cells and got none, try for non-writable. */
556   if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
557     {
558       *writable_pP = False;
559       goto RETRY_NON_WRITABLE;
560     }
561
562   if (verbose_p)
563     complain(*ncolorsP, ncolors, wanted_writable,
564              wanted_writable && *writable_pP);
565
566   *ncolorsP = ncolors;
567 }
568
569
570 void
571 make_uniform_colormap (Screen *screen, Visual *visual, Colormap cmap,
572                        XColor *colors, int *ncolorsP,
573                        Bool allocate_p,
574                        Bool *writable_pP,
575                        Bool verbose_p)
576 {
577   int ncolors = *ncolorsP;
578   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
579
580   double S = ((double) (random() % 34) + 66) / 100.0;   /* range 66%-100% */
581   double V = ((double) (random() % 34) + 66) / 100.0;   /* range 66%-100% */
582
583   if (*ncolorsP <= 0) return;
584
585   /* If this visual doesn't support writable cells, don't bother trying. */
586   if (wanted_writable && !has_writable_cells(screen, visual))
587     *writable_pP = False;
588
589  RETRY_NON_WRITABLE:
590   make_color_ramp(screen, visual, cmap,
591                   0,   S, V,
592                   359, S, V,
593                   colors, &ncolors,
594                   False, allocate_p, writable_pP);
595
596   /* If we tried for writable cells and got none, try for non-writable. */
597   if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
598     {
599       ncolors = *ncolorsP;
600       *writable_pP = False;
601       goto RETRY_NON_WRITABLE;
602     }
603
604   if (verbose_p)
605     complain(*ncolorsP, ncolors, wanted_writable,
606              wanted_writable && *writable_pP);
607
608   *ncolorsP = ncolors;
609 }
610
611
612 void
613 make_random_colormap (Screen *screen, Visual *visual, Colormap cmap,
614                       XColor *colors, int *ncolorsP,
615                       Bool bright_p,
616                       Bool allocate_p,
617                       Bool *writable_pP,
618                       Bool verbose_p)
619 {
620   Display *dpy = screen ? DisplayOfScreen (screen) : 0;
621   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
622   int ncolors = *ncolorsP;
623   int i;
624
625   if (*ncolorsP <= 0) return;
626
627   /* If this visual doesn't support writable cells, don't bother trying. */
628   if (wanted_writable && !has_writable_cells(screen, visual))
629     *writable_pP = False;
630
631  RETRY_ALL:
632   for (i = 0; i < ncolors; i++)
633     {
634       colors[i].flags = DoRed|DoGreen|DoBlue;
635       if (bright_p)
636         {
637           int H = random() % 360;                          /* range 0-360    */
638           double S = ((double) (random()%70) + 30)/100.0;  /* range 30%-100% */
639           double V = ((double) (random()%34) + 66)/100.0;  /* range 66%-100% */
640           hsv_to_rgb (H, S, V,
641                       &colors[i].red, &colors[i].green, &colors[i].blue);
642         }
643       else
644         {
645           colors[i].red   = random() % 0xFFFF;
646           colors[i].green = random() % 0xFFFF;
647           colors[i].blue  = random() % 0xFFFF;
648         }
649     }
650
651   /* If there are a small number of colors, make sure at least the first
652      two contrast well.
653   */
654   if (!bright_p && ncolors <= 4)
655     {
656       int h0, h1;
657       double s0, s1, v0, v1;
658       rgb_to_hsv (colors[0].red, colors[0].green, colors[0].blue, &h0,&s0,&v0);
659       rgb_to_hsv (colors[1].red, colors[1].green, colors[1].blue, &h1,&s1,&v1);
660       if (fabs (v1-v0) < 0.5)
661         goto RETRY_ALL;
662     }
663
664   if (!allocate_p)
665     return;
666
667  RETRY_NON_WRITABLE:
668   if (writable_pP && *writable_pP)
669     {
670       unsigned long *pixels = (unsigned long *)
671         malloc(sizeof(*pixels) * (ncolors + 1));
672
673       allocate_writable_colors (screen, cmap, pixels, &ncolors);
674       if (ncolors > 0)
675         for (i = 0; i < ncolors; i++)
676           colors[i].pixel = pixels[i];
677       free (pixels);
678       if (ncolors > 0)
679         XStoreColors (dpy, cmap, colors, ncolors);
680     }
681   else
682     {
683       for (i = 0; i < ncolors; i++)
684         {
685           XColor color;
686           color = colors[i];
687           if (!XAllocColor (dpy, cmap, &color))
688             break;
689           colors[i].pixel = color.pixel;
690         }
691       ncolors = i;
692     }
693
694   /* If we tried for writable cells and got none, try for non-writable. */
695   if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
696     {
697       ncolors = *ncolorsP;
698       *writable_pP = False;
699       goto RETRY_NON_WRITABLE;
700     }
701
702   if (verbose_p)
703     complain(*ncolorsP, ncolors, wanted_writable,
704              wanted_writable && *writable_pP);
705
706   *ncolorsP = ncolors;
707 }
708
709
710 void
711 rotate_colors (Screen *screen, Colormap cmap,
712                XColor *colors, int ncolors, int distance)
713 {
714   Display *dpy = screen ? DisplayOfScreen (screen) : 0;
715   int i;
716   XColor *colors2;
717   if (ncolors < 2) return;
718   colors2 = (XColor *) malloc(sizeof(*colors2) * ncolors);
719   distance = distance % ncolors;
720   for (i = 0; i < ncolors; i++)
721     {
722       int j = i - distance;
723       if (j >= ncolors) j -= ncolors;
724       if (j < 0) j += ncolors;
725       colors2[i] = colors[j];
726       colors2[i].pixel = colors[i].pixel;
727     }
728   XStoreColors (dpy, cmap, colors2, ncolors);
729   XFlush(dpy);
730   memcpy(colors, colors2, sizeof(*colors) * ncolors);
731   free(colors2);
732 }