Back to index

kdeartwork  4.3.2
xs_colors.c
Go to the documentation of this file.
00001 /* xscreensaver, Copyright (c) 1997 Jamie Zawinski <jwz@jwz.org>
00002  *
00003  * Permission to use, copy, modify, distribute, and sell this software and its
00004  * documentation for any purpose is hereby granted without fee, provided that
00005  * the above copyright notice appear in all copies and that both that
00006  * copyright notice and this permission notice appear in supporting
00007  * documentation.  No representations are made about the suitability of this
00008  * software for any purpose.  It is provided "as is" without express or 
00009  * implied warranty.
00010  */
00011 
00012 /* This file contains some utility routines for randomly picking the colors
00013    to hack the screen with.
00014  */
00015 
00016 #include <stdlib.h>
00017 #include <stdio.h>
00018 #include <math.h>
00019 
00020 #include <X11/Xlib.h>
00021 #include <X11/Xutil.h>
00022 #include <X11/Xos.h>
00023 #include <X11/Xresource.h>
00024 
00025 #include "xs_visual.h"
00026 #include "xs_yarandom.h"
00027 #include "xs_hsv.h"
00028 #include "xs_colors.h"
00029 
00030 /* extern char *progname; */
00031 
00032 void
00033 free_colors(Display *dpy, Colormap cmap, XColor *colors, int ncolors)
00034 {
00035   int i;
00036   if (ncolors > 0)
00037     {
00038       unsigned long *pixels = (unsigned long *)
00039        malloc(sizeof(*pixels) * ncolors);
00040       for (i = 0; i < ncolors; i++)
00041        pixels[i] = colors[i].pixel;
00042       XFreeColors (dpy, cmap, pixels, ncolors, 0L);
00043       free(pixels);
00044     }
00045 }
00046 
00047 
00048 void
00049 allocate_writable_colors (Display *dpy, Colormap cmap,
00050                        unsigned long *pixels, int *ncolorsP)
00051 {
00052   int desired = *ncolorsP;
00053   int got = 0;
00054   int requested = desired;
00055   unsigned long *new_pixels = pixels;
00056 
00057   *ncolorsP = 0;
00058   while (got < desired
00059         && requested > 0)
00060     {
00061       if (desired - got < requested)
00062        requested = desired - got;
00063 
00064       if (XAllocColorCells (dpy, cmap, False, 0, 0, new_pixels, requested))
00065        {
00066          /* Got all the pixels we asked for. */
00067          new_pixels += requested;
00068          got += requested;
00069        }
00070       else
00071        {
00072          /* We didn't get all/any of the pixels we asked for.  This time, ask
00073             for half as many.  (If we do get all that we ask for, we ask for
00074             the same number again next time, so we only do O(log(n)) server
00075             roundtrips.)
00076          */
00077          requested = requested / 2;
00078        }
00079     }
00080   *ncolorsP += got;
00081 }
00082 
00083 
00084 
00085 void
00086 make_color_ramp (Display *dpy, Colormap cmap,
00087                int h1, double s1, double v1,   /* 0-360, 0-1.0, 0-1.0 */
00088                int h2, double s2, double v2,   /* 0-360, 0-1.0, 0-1.0 */
00089                XColor *colors, int *ncolorsP,
00090                Bool closed_p,
00091                Bool allocate_p,
00092                Bool writable_p)
00093 {
00094   int i;
00095   int ncolors = *ncolorsP;
00096   double dh, ds, dv;        /* deltas */
00097 
00098  AGAIN:
00099 
00100   memset (colors, 0, (*ncolorsP) * sizeof(*colors));
00101 
00102   if (closed_p)
00103     ncolors = (ncolors / 2) + 1;
00104 
00105   /* Note: unlike other routines in this module, this function assumes that
00106      if h1 and h2 are more than 180 degrees apart, then the desired direction
00107      is always from h1 to h2 (rather than the shorter path.)  make_uniform
00108      depends on this.
00109    */
00110   dh = ((double)h2 - (double)h1) / ncolors;
00111   ds = (s2 - s1) / ncolors;
00112   dv = (v2 - v1) / ncolors;
00113 
00114   for (i = 0; i < ncolors; i++)
00115     {
00116       colors[i].flags = DoRed|DoGreen|DoBlue;
00117       hsv_to_rgb ((int) (h1 + (i*dh)), (s1 + (i*ds)), (v1 + (i*dv)),
00118                 &colors[i].red, &colors[i].green, &colors[i].blue);
00119     }
00120 
00121   if (closed_p)
00122     for (i = ncolors; i < *ncolorsP; i++)
00123       colors[i] = colors[(*ncolorsP)-i];
00124 
00125   if (!allocate_p)
00126     return;
00127 
00128   if (writable_p)
00129     {
00130       unsigned long *pixels = (unsigned long *)
00131        malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
00132 
00133       /* allocate_writable_colors() won't do here, because we need exactly this
00134         number of cells, or the color sequence we've chosen won't fit. */
00135       if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
00136        {
00137          free(pixels);
00138          goto FAIL;
00139        }
00140 
00141       for (i = 0; i < *ncolorsP; i++)
00142        colors[i].pixel = pixels[i];
00143       free (pixels);
00144 
00145       XStoreColors (dpy, cmap, colors, *ncolorsP);
00146     }
00147   else
00148     {
00149       for (i = 0; i < *ncolorsP; i++)
00150        {
00151          XColor color;
00152          color = colors[i];
00153          if (XAllocColor (dpy, cmap, &color))
00154            {
00155              colors[i].pixel = color.pixel;
00156            }
00157          else
00158            {
00159              free_colors (dpy, cmap, colors, i);
00160              goto FAIL;
00161            }
00162        }
00163     }
00164 
00165   return;
00166 
00167  FAIL:
00168   /* we weren't able to allocate all the colors we wanted;
00169      decrease the requested number and try again.
00170    */
00171   ncolors = (ncolors > 170 ? ncolors - 20 :
00172             ncolors > 100 ? ncolors - 10 :
00173             ncolors >  75 ? ncolors -  5 :
00174             ncolors >  25 ? ncolors -  3 :
00175             ncolors >  10 ? ncolors -  2 :
00176             ncolors >   2 ? ncolors -  1 :
00177             0);
00178   *ncolorsP = ncolors;
00179   if (ncolors > 0)
00180     goto AGAIN;
00181 }
00182 
00183 
00184 #define MAXPOINTS 50 /* yeah, so I'm lazy */
00185 
00186 
00187 static void
00188 make_color_path (Display *dpy, Colormap cmap,
00189                int npoints, int *h, double *s, double *v,
00190                XColor *colors, int *ncolorsP,
00191                Bool allocate_p,
00192                Bool writable_p)
00193 {
00194   int i, j, k;
00195   int total_ncolors = *ncolorsP;
00196 
00197   int ncolors[MAXPOINTS];  /* number of pixels per edge */
00198   double dh[MAXPOINTS];    /* distance between pixels, per edge (0 - 360.0) */
00199   double ds[MAXPOINTS];    /* distance between pixels, per edge (0 - 1.0) */
00200   double dv[MAXPOINTS];    /* distance between pixels, per edge (0 - 1.0) */
00201 
00202   if (npoints == 0)
00203     {
00204       *ncolorsP = 0;
00205       return;
00206     }
00207   else if (npoints == 2)    /* using make_color_ramp() will be faster */
00208     {
00209       make_color_ramp (dpy, cmap,
00210                      h[0], s[0], v[0], h[1], s[1], v[1],
00211                      colors, ncolorsP,
00212                      True,  /* closed_p */
00213                      allocate_p, writable_p);
00214       return;
00215     }
00216   else if (npoints >= MAXPOINTS)
00217     {
00218       npoints = MAXPOINTS-1;
00219     }
00220 
00221  AGAIN:
00222 
00223   {
00224     double DH[MAXPOINTS];   /* Distance between H values in the shortest
00225                                direction around the circle, that is, the
00226                                distance between 10 and 350 is 20.
00227                                (Range is 0 - 360.0.)
00228                             */
00229     double edge[MAXPOINTS]; /* lengths of edges in unit HSV space. */
00230     double ratio[MAXPOINTS];       /* proportions of the edges (total 1.0) */
00231     double circum = 0;
00232     double one_point_oh = 0;       /* (debug) */
00233 
00234     for (i = 0; i < npoints; i++)
00235       {
00236        int j = (i+1) % npoints;
00237        double d = ((double) (h[i] - h[j])) / 360;
00238        if (d < 0) d = -d;
00239        if (d > 0.5) d = 0.5 - (d - 0.5);
00240        DH[i] = d;
00241       }
00242 
00243     for (i = 0; i < npoints; i++)
00244       {
00245        int j = (i+1) % npoints;
00246        edge[i] = sqrt((DH[i] * DH[j]) +
00247                      ((s[j] - s[i]) * (s[j] - s[i])) +
00248                      ((v[j] - v[i]) * (v[j] - v[i])));
00249        circum += edge[i];
00250       }
00251 
00252 #ifdef DEBUG
00253     fprintf(stderr, "\ncolors:");
00254     for (i=0; i < npoints; i++)
00255       fprintf(stderr, " (%d, %.3f, %.3f)", h[i], s[i], v[i]);
00256     fprintf(stderr, "\nlengths:");
00257     for (i=0; i < npoints; i++)
00258       fprintf(stderr, " %.3f", edge[i]);
00259 #endif /* DEBUG */
00260 
00261     if (circum < 0.0001)
00262       goto FAIL;
00263 
00264     for (i = 0; i < npoints; i++)
00265       {
00266        ratio[i] = edge[i] / circum;
00267        one_point_oh += ratio[i];
00268       }
00269 
00270 #ifdef DEBUG
00271     fprintf(stderr, "\nratios:");
00272     for (i=0; i < npoints; i++)
00273       fprintf(stderr, " %.3f", ratio[i]);
00274 #endif /* DEBUG */
00275 
00276     if (one_point_oh < 0.99999 || one_point_oh > 1.00001)
00277       abort();
00278 
00279     /* space the colors evenly along the circumference -- that means that the
00280        number of pixels on a edge is proportional to the length of that edge
00281        (relative to the lengths of the other edges.)
00282      */
00283     for (i = 0; i < npoints; i++)
00284       ncolors[i] = total_ncolors * ratio[i];
00285 
00286 
00287 #ifdef DEBUG
00288     fprintf(stderr, "\npixels:");
00289     for (i=0; i < npoints; i++)
00290       fprintf(stderr, " %d", ncolors[i]);
00291     fprintf(stderr, "  (%d)\n", total_ncolors);
00292 #endif /* DEBUG */
00293 
00294     for (i = 0; i < npoints; i++)
00295       {
00296        int j = (i+1) % npoints;
00297 
00298        if (ncolors[i] > 0)
00299          {
00300            dh[i] = 360 * (DH[i] / ncolors[i]);
00301            ds[i] = (s[j] - s[i]) / ncolors[i];
00302            dv[i] = (v[j] - v[i]) / ncolors[i];
00303          }
00304       }
00305   }
00306 
00307   memset (colors, 0, (*ncolorsP) * sizeof(*colors));
00308 
00309   k = 0;
00310   for (i = 0; i < npoints; i++)
00311     {
00312       int distance, direction;
00313       distance = h[(i+1) % npoints] - h[i];
00314       direction = (distance >= 0 ? -1 : 1);
00315 
00316       if (distance > 180)
00317        distance = 180 - (distance - 180);
00318       else if (distance < -180)
00319        distance = -(180 - ((-distance) - 180));
00320       else
00321        direction = -direction;
00322 
00323 #ifdef DEBUG
00324       fprintf (stderr, "point %d: %3d %.2f %.2f\n",
00325               i, h[i], s[i], v[i]);
00326       fprintf(stderr, "  h[i]=%d  dh[i]=%.2f  ncolors[i]=%d\n",
00327              h[i], dh[i], ncolors[i]);
00328 #endif /* DEBUG */
00329       for (j = 0; j < ncolors[i]; j++, k++)
00330        {
00331          double hh = (h[i] + (j * dh[i] * direction));
00332          if (hh < 0) hh += 360;
00333          else if (hh > 360) hh -= 0;
00334          colors[k].flags = DoRed|DoGreen|DoBlue;
00335          hsv_to_rgb ((int)
00336                     hh,
00337                     (s[i] + (j * ds[i])),
00338                     (v[i] + (j * dv[i])),
00339                     &colors[k].red, &colors[k].green, &colors[k].blue);
00340 #ifdef DEBUG
00341          fprintf (stderr, "point %d+%d: %.2f %.2f %.2f  %04X %04X %04X\n",
00342                  i, j,
00343                  hh,
00344                  (s[i] + (j * ds[i])),
00345                  (v[i] + (j * dv[i])),
00346                  colors[k].red, colors[k].green, colors[k].blue);
00347 #endif /* DEBUG */
00348        }
00349     }
00350 
00351   /* Floating-point round-off can make us decide to use fewer colors. */
00352   if (k < *ncolorsP)
00353     {
00354       *ncolorsP = k;
00355       if (k <= 0)
00356        return;
00357     }
00358 
00359   if (!allocate_p)
00360     return;
00361 
00362   if (writable_p)
00363     {
00364       unsigned long *pixels = (unsigned long *)
00365        malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
00366 
00367       /* allocate_writable_colors() won't do here, because we need exactly this
00368         number of cells, or the color sequence we've chosen won't fit. */
00369       if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
00370        {
00371          free(pixels);
00372          goto FAIL;
00373        }
00374 
00375       for (i = 0; i < *ncolorsP; i++)
00376        colors[i].pixel = pixels[i];
00377       free (pixels);
00378 
00379       XStoreColors (dpy, cmap, colors, *ncolorsP);
00380     }
00381   else
00382     {
00383       for (i = 0; i < *ncolorsP; i++)
00384        {
00385          XColor color;
00386          color = colors[i];
00387          if (XAllocColor (dpy, cmap, &color))
00388            {
00389              colors[i].pixel = color.pixel;
00390            }
00391          else
00392            {
00393              free_colors (dpy, cmap, colors, i);
00394              goto FAIL;
00395            }
00396        }
00397     }
00398 
00399   return;
00400 
00401  FAIL:
00402   /* we weren't able to allocate all the colors we wanted;
00403      decrease the requested number and try again.
00404    */
00405   total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
00406                  total_ncolors > 100 ? total_ncolors - 10 :
00407                  total_ncolors >  75 ? total_ncolors -  5 :
00408                  total_ncolors >  25 ? total_ncolors -  3 :
00409                  total_ncolors >  10 ? total_ncolors -  2 :
00410                  total_ncolors >   2 ? total_ncolors -  1 :
00411                  0);
00412   *ncolorsP = total_ncolors;
00413   if (total_ncolors > 0)
00414     goto AGAIN;
00415 }
00416 
00417 
00418 void
00419 make_color_loop (Display *dpy, Colormap cmap,
00420                int h0, double s0, double v0,   /* 0-360, 0-1.0, 0-1.0 */
00421                int h1, double s1, double v1,   /* 0-360, 0-1.0, 0-1.0 */
00422                int h2, double s2, double v2,   /* 0-360, 0-1.0, 0-1.0 */
00423                XColor *colors, int *ncolorsP,
00424                Bool allocate_p,
00425                Bool writable_p)
00426 {
00427   int h[3];
00428   double s[3], v[3];
00429   h[0] = h0; h[1] = h1; h[2] = h2;
00430   s[0] = s0; s[1] = s1; s[2] = s2;
00431   v[0] = v0; v[1] = v1; v[2] = v2;
00432   make_color_path(dpy, cmap,
00433                 3, h, s, v,
00434                 colors, ncolorsP,
00435                 allocate_p, writable_p);
00436 }
00437 
00438 
00439 static void
00440 complain (int wanted_colors, int got_colors,
00441          Bool wanted_writable, Bool got_writable)
00442 {
00443   if (wanted_writable && !got_writable)
00444     fprintf(stderr,
00445            "%s: wanted %d writable colors; got %d read-only colors.\n",
00446            "colors (kscreensaver)", wanted_colors, got_colors);
00447 
00448   else if (wanted_colors > (got_colors + 10))
00449     /* don't bother complaining if we're within ten pixels. */
00450     fprintf(stderr, "%s: wanted %d%s colors; got %d.\n",
00451            "colors (kscreensaver)", wanted_colors, (got_writable ? " writable" : ""),
00452            got_colors);
00453 }
00454 
00455 
00456 void
00457 make_smooth_colormap (Display *dpy, Visual *visual, Colormap cmap,
00458                     XColor *colors, int *ncolorsP,
00459                     Bool allocate_p,
00460                     Bool *writable_pP,
00461                     Bool verbose_p)
00462 {
00463   int npoints;
00464   int ncolors = *ncolorsP;
00465   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
00466   int i;
00467   int h[MAXPOINTS];
00468   double s[MAXPOINTS];
00469   double v[MAXPOINTS];
00470   double total_s = 0;
00471   double total_v = 0;
00472   Screen *screen = DefaultScreenOfDisplay(dpy); /* #### WRONG! */
00473 
00474   if (*ncolorsP <= 0) return;
00475 
00476   {
00477     int n = random() % 20;
00478     if      (n <= 5)  npoints = 2; /* 30% of the time */
00479     else if (n <= 15) npoints = 3; /* 50% of the time */
00480     else if (n <= 18) npoints = 4; /* 15% of the time */
00481     else             npoints = 5;  /*  5% of the time */
00482   }
00483 
00484  REPICK_ALL_COLORS:
00485   for (i = 0; i < npoints; i++)
00486     {
00487     REPICK_THIS_COLOR:
00488       h[i] = random() % 360;
00489       s[i] = frand(1.0);
00490       v[i] = frand(0.8) + 0.2;
00491 
00492       /* Make sure that no two adjascent colors are *too* close together.
00493         If they are, try again.
00494        */
00495       if (i > 0)
00496        {
00497          int j = (i+1 == npoints) ? 0 : (i-1);
00498          double hi = ((double) h[i]) / 360;
00499          double hj = ((double) h[j]) / 360;
00500          double dh = hj - hi;
00501          double distance;
00502          if (dh < 0) dh = -dh;
00503          if (dh > 0.5) dh = 0.5 - (dh - 0.5);
00504          distance = sqrt ((dh * dh) +
00505                         ((s[j] - s[i]) * (s[j] - s[i])) +
00506                         ((v[j] - v[i]) * (v[j] - v[i])));
00507          if (distance < 0.2)
00508            goto REPICK_THIS_COLOR;
00509        }
00510       total_s += s[i];
00511       total_v += v[i];
00512     }
00513 
00514   /* If the average saturation or intensity are too low, repick the colors,
00515      so that we don't end up with a black-and-white or too-dark map.
00516    */
00517   if (total_s / npoints < 0.2)
00518     goto REPICK_ALL_COLORS;
00519   if (total_v / npoints < 0.3)
00520     goto REPICK_ALL_COLORS;
00521 
00522   /* If this visual doesn't support writable cells, don't bother trying.
00523    */
00524   if (wanted_writable && !has_writable_cells(screen, visual))
00525     *writable_pP = False;
00526 
00527  RETRY_NON_WRITABLE:
00528   make_color_path (dpy, cmap, npoints, h, s, v, colors, &ncolors,
00529                  allocate_p, (writable_pP && *writable_pP));
00530 
00531   /* If we tried for writable cells and got none, try for non-writable. */
00532   if (allocate_p && *ncolorsP == 0 && *writable_pP)
00533     {
00534       *writable_pP = False;
00535       goto RETRY_NON_WRITABLE;
00536     }
00537 
00538   if (verbose_p)
00539     complain(*ncolorsP, ncolors, wanted_writable,
00540             wanted_writable && *writable_pP);
00541 
00542   *ncolorsP = ncolors;
00543 }
00544 
00545 
00546 void
00547 make_uniform_colormap (Display *dpy, Visual *visual, Colormap cmap,
00548                      XColor *colors, int *ncolorsP,
00549                      Bool allocate_p,
00550                      Bool *writable_pP,
00551                      Bool verbose_p)
00552 {
00553   int ncolors = *ncolorsP;
00554   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
00555   Screen *screen = DefaultScreenOfDisplay(dpy); /* #### WRONG! */
00556 
00557   double S = ((double) (random() % 34) + 66) / 100.0;   /* range 66%-100% */
00558   double V = ((double) (random() % 34) + 66) / 100.0;   /* range 66%-100% */
00559 
00560   if (*ncolorsP <= 0) return;
00561 
00562   /* If this visual doesn't support writable cells, don't bother trying. */
00563   if (wanted_writable && !has_writable_cells(screen, visual))
00564     *writable_pP = False;
00565 
00566  RETRY_NON_WRITABLE:
00567   make_color_ramp(dpy, cmap,
00568                 0,   S, V,
00569                 359, S, V,
00570                 colors, &ncolors,
00571                 False, True, wanted_writable);
00572 
00573   /* If we tried for writable cells and got none, try for non-writable. */
00574   if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
00575     {
00576       ncolors = *ncolorsP;
00577       *writable_pP = False;
00578       goto RETRY_NON_WRITABLE;
00579     }
00580 
00581   if (verbose_p)
00582     complain(*ncolorsP, ncolors, wanted_writable,
00583             wanted_writable && *writable_pP);
00584 
00585   *ncolorsP = ncolors;
00586 }
00587 
00588 
00589 void
00590 make_random_colormap (Display *dpy, Visual *visual, Colormap cmap,
00591                     XColor *colors, int *ncolorsP,
00592                     Bool bright_p,
00593                     Bool allocate_p,
00594                     Bool *writable_pP,
00595                     Bool verbose_p)
00596 {
00597   Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
00598   int ncolors = *ncolorsP;
00599   int i;
00600   Screen *screen = DefaultScreenOfDisplay(dpy); /* #### WRONG! */
00601 
00602   if (*ncolorsP <= 0) return;
00603 
00604   /* If this visual doesn't support writable cells, don't bother trying. */
00605   if (wanted_writable && !has_writable_cells(screen, visual))
00606     *writable_pP = False;
00607 
00608   for (i = 0; i < ncolors; i++)
00609     {
00610       colors[i].flags = DoRed|DoGreen|DoBlue;
00611       if (bright_p)
00612        {
00613          int H = random() % 360;                    /* range 0-360    */
00614          double S = ((double) (random()%70) + 30)/100.0;  /* range 30%-100% */
00615          double V = ((double) (random()%34) + 66)/100.0;  /* range 66%-100% */
00616          hsv_to_rgb (H, S, V,
00617                     &colors[i].red, &colors[i].green, &colors[i].blue);
00618        }
00619       else
00620        {
00621          colors[i].red   = random() % 0xFFFF;
00622          colors[i].green = random() % 0xFFFF;
00623          colors[i].blue  = random() % 0xFFFF;
00624        }
00625     }
00626 
00627   if (!allocate_p)
00628     return;
00629 
00630  RETRY_NON_WRITABLE:
00631   if (writable_pP && *writable_pP)
00632     {
00633       unsigned long *pixels = (unsigned long *)
00634        malloc(sizeof(*pixels) * (ncolors + 1));
00635 
00636       allocate_writable_colors (dpy, cmap, pixels, &ncolors);
00637       if (ncolors > 0)
00638        for (i = 0; i < ncolors; i++)
00639          colors[i].pixel = pixels[i];
00640       free (pixels);
00641       if (ncolors > 0)
00642        XStoreColors (dpy, cmap, colors, ncolors);
00643     }
00644   else
00645     {
00646       for (i = 0; i < ncolors; i++)
00647        {
00648          XColor color;
00649          color = colors[i];
00650          if (!XAllocColor (dpy, cmap, &color))
00651            break;
00652          colors[i].pixel = color.pixel;
00653        }
00654       ncolors = i;
00655     }
00656 
00657   /* If we tried for writable cells and got none, try for non-writable. */
00658   if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
00659     {
00660       ncolors = *ncolorsP;
00661       *writable_pP = False;
00662       goto RETRY_NON_WRITABLE;
00663     }
00664 
00665   if (verbose_p)
00666     complain(*ncolorsP, ncolors, wanted_writable,
00667             wanted_writable && *writable_pP);
00668 
00669   *ncolorsP = ncolors;
00670 }
00671 
00672 
00673 void
00674 rotate_colors (Display *dpy, Colormap cmap,
00675               XColor *colors, int ncolors, int distance)
00676 {
00677   int i;
00678   XColor *colors2 = (XColor *) malloc(sizeof(*colors2) * ncolors);
00679   if (ncolors < 2) return;
00680   distance = distance % ncolors;
00681   for (i = 0; i < ncolors; i++)
00682     {
00683       int j = i - distance;
00684       if (j >= ncolors) j -= ncolors;
00685       if (j < 0) j += ncolors;
00686       colors2[i] = colors[j];
00687       colors2[i].pixel = colors[i].pixel;
00688     }
00689   XStoreColors (dpy, cmap, colors2, ncolors);
00690   XFlush(dpy);
00691   memcpy(colors, colors2, sizeof(*colors) * ncolors);
00692   free(colors2);
00693 }