Added missing newline in NEDsim error message.
[screensavers] / screenhack / colors.c
CommitLineData
3144ee8a
AT
1/* xscreensaver, Copyright (c) 1997-2018 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
22extern char *progname;
23
24void
25free_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
41void
42allocate_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
78static void
79complain (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
98void
99make_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
223static void
224make_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 /* We used to just return the smaller set of colors, but that meant
387 that after re-generating the color map repeatedly, the number of
388 colors in use would tend toward 0, which not only looked bad but
389 also often caused crashes. So instead, just duplicate the last
390 color to pad things out. */
391# if 0
392 *ncolorsP = k;
393 if (k <= 0)
394 return;
395# else
396 if (k <= 0)
397 return;
398 for (i = k; i < *ncolorsP; i++)
399 /* #### Should duplicate the allocation of the color cell here
400 to avoid a double-color-free on PseudoColor, but it's 2018
401 and I don't care, */
402 colors[i] = colors[i-1];
403# endif
404 }
405
406 if (!allocate_p)
407 return;
408
409 if (writable_pP && *writable_pP)
410 {
411 unsigned long *pixels = (unsigned long *)
412 malloc(sizeof(*pixels) * ((*ncolorsP) + 1));
413
414 /* allocate_writable_colors() won't do here, because we need exactly this
415 number of cells, or the color sequence we've chosen won't fit. */
416 if (! XAllocColorCells(dpy, cmap, False, 0, 0, pixels, *ncolorsP))
417 {
418 free(pixels);
419 goto FAIL;
420 }
421
422 for (i = 0; i < *ncolorsP; i++)
423 colors[i].pixel = pixels[i];
424 free (pixels);
425
426 XStoreColors (dpy, cmap, colors, *ncolorsP);
427 }
428 else
429 {
430 for (i = 0; i < *ncolorsP; i++)
431 {
432 XColor color;
433 color = colors[i];
434 if (XAllocColor (dpy, cmap, &color))
435 {
436 colors[i].pixel = color.pixel;
437 }
438 else
439 {
440 free_colors (screen, cmap, colors, i);
441 goto FAIL;
442 }
443 }
444 }
445
446 return;
447
448 FAIL:
449 /* we weren't able to allocate all the colors we wanted;
450 decrease the requested number and try again.
451 */
452 total_ncolors = (total_ncolors > 170 ? total_ncolors - 20 :
453 total_ncolors > 100 ? total_ncolors - 10 :
454 total_ncolors > 75 ? total_ncolors - 5 :
455 total_ncolors > 25 ? total_ncolors - 3 :
456 total_ncolors > 10 ? total_ncolors - 2 :
457 total_ncolors > 2 ? total_ncolors - 1 :
458 0);
459 *ncolorsP = total_ncolors;
460 if (total_ncolors > 0)
461 goto AGAIN;
462}
463
464
465void
466make_color_loop (Screen *screen, Visual *visual, Colormap cmap,
467 int h0, double s0, double v0, /* 0-360, 0-1.0, 0-1.0 */
468 int h1, double s1, double v1, /* 0-360, 0-1.0, 0-1.0 */
469 int h2, double s2, double v2, /* 0-360, 0-1.0, 0-1.0 */
470 XColor *colors, int *ncolorsP,
471 Bool allocate_p,
472 Bool *writable_pP)
473{
474 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
475
476 int h[3];
477 double s[3], v[3];
478 h[0] = h0; h[1] = h1; h[2] = h2;
479 s[0] = s0; s[1] = s1; s[2] = s2;
480 v[0] = v0; v[1] = v1; v[2] = v2;
481
482 /* If this visual doesn't support writable cells, don't bother trying.
483 */
484 if (wanted_writable && !has_writable_cells(screen, visual))
485 *writable_pP = False;
486
487 make_color_path (screen, visual, cmap,
488 3, h, s, v,
489 colors, ncolorsP,
490 allocate_p, writable_pP);
491}
492
493
494void
495make_smooth_colormap (Screen *screen, Visual *visual, Colormap cmap,
496 XColor *colors, int *ncolorsP,
497 Bool allocate_p,
498 Bool *writable_pP,
499 Bool verbose_p)
500{
501 int npoints;
502 int ncolors = *ncolorsP;
503 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
504 int i;
505 int h[MAXPOINTS];
506 double s[MAXPOINTS];
507 double v[MAXPOINTS];
508 double total_s = 0;
509 double total_v = 0;
510 int loop = 0;
511
512 if (*ncolorsP <= 0) return;
513
514 {
515 int n = random() % 20;
516 if (n <= 5) npoints = 2; /* 30% of the time */
517 else if (n <= 15) npoints = 3; /* 50% of the time */
518 else if (n <= 18) npoints = 4; /* 15% of the time */
519 else npoints = 5; /* 5% of the time */
520 }
521
522 REPICK_ALL_COLORS:
523 for (i = 0; i < npoints; i++)
524 {
525 REPICK_THIS_COLOR:
526 if (++loop > 10000) abort();
527 h[i] = random() % 360;
528 s[i] = frand(1.0);
529 v[i] = frand(0.8) + 0.2;
530
531 /* Make sure that no two adjascent colors are *too* close together.
532 If they are, try again.
533 */
534 if (i > 0)
535 {
536 int j = (i+1 == npoints) ? 0 : (i-1);
537 double hi = ((double) h[i]) / 360;
538 double hj = ((double) h[j]) / 360;
539 double dh = hj - hi;
540 double distance;
541 if (dh < 0) dh = -dh;
542 if (dh > 0.5) dh = 0.5 - (dh - 0.5);
543 distance = sqrt ((dh * dh) +
544 ((s[j] - s[i]) * (s[j] - s[i])) +
545 ((v[j] - v[i]) * (v[j] - v[i])));
546 if (distance < 0.2)
547 goto REPICK_THIS_COLOR;
548 }
549 total_s += s[i];
550 total_v += v[i];
551 }
552
553 /* If the average saturation or intensity are too low, repick the colors,
554 so that we don't end up with a black-and-white or too-dark map.
555 */
556 if (total_s / npoints < 0.2)
557 goto REPICK_ALL_COLORS;
558 if (total_v / npoints < 0.3)
559 goto REPICK_ALL_COLORS;
560
561 /* If this visual doesn't support writable cells, don't bother trying.
562 */
563 if (wanted_writable && !has_writable_cells(screen, visual))
564 *writable_pP = False;
565
566 RETRY_NON_WRITABLE:
567 make_color_path (screen, visual, cmap, npoints, h, s, v, colors, &ncolors,
568 allocate_p, writable_pP);
569
570 /* If we tried for writable cells and got none, try for non-writable. */
571 if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
572 {
573 *writable_pP = False;
574 goto RETRY_NON_WRITABLE;
575 }
576
577 if (verbose_p)
578 complain(*ncolorsP, ncolors, wanted_writable,
579 wanted_writable && *writable_pP);
580
581 *ncolorsP = ncolors;
582}
583
584
585void
586make_uniform_colormap (Screen *screen, Visual *visual, Colormap cmap,
587 XColor *colors, int *ncolorsP,
588 Bool allocate_p,
589 Bool *writable_pP,
590 Bool verbose_p)
591{
592 int ncolors = *ncolorsP;
593 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
594
595 double S = ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
596 double V = ((double) (random() % 34) + 66) / 100.0; /* range 66%-100% */
597
598 if (*ncolorsP <= 0) return;
599
600 /* If this visual doesn't support writable cells, don't bother trying. */
601 if (wanted_writable && !has_writable_cells(screen, visual))
602 *writable_pP = False;
603
604 RETRY_NON_WRITABLE:
605 make_color_ramp(screen, visual, cmap,
606 0, S, V,
607 359, S, V,
608 colors, &ncolors,
609 False, allocate_p, writable_pP);
610
611 /* If we tried for writable cells and got none, try for non-writable. */
612 if (allocate_p && *ncolorsP == 0 && writable_pP && *writable_pP)
613 {
614 ncolors = *ncolorsP;
615 *writable_pP = False;
616 goto RETRY_NON_WRITABLE;
617 }
618
619 if (verbose_p)
620 complain(*ncolorsP, ncolors, wanted_writable,
621 wanted_writable && *writable_pP);
622
623 *ncolorsP = ncolors;
624}
625
626
627void
628make_random_colormap (Screen *screen, Visual *visual, Colormap cmap,
629 XColor *colors, int *ncolorsP,
630 Bool bright_p,
631 Bool allocate_p,
632 Bool *writable_pP,
633 Bool verbose_p)
634{
635 Display *dpy = screen ? DisplayOfScreen (screen) : 0;
636 Bool wanted_writable = (allocate_p && writable_pP && *writable_pP);
637 int ncolors = *ncolorsP;
638 int i;
639
640 if (*ncolorsP <= 0) return;
641
642 /* If this visual doesn't support writable cells, don't bother trying. */
643 if (wanted_writable && !has_writable_cells(screen, visual))
644 *writable_pP = False;
645
646 RETRY_ALL:
647 for (i = 0; i < ncolors; i++)
648 {
649 colors[i].flags = DoRed|DoGreen|DoBlue;
650 if (bright_p)
651 {
652 int H = random() % 360; /* range 0-360 */
653 double S = ((double) (random()%70) + 30)/100.0; /* range 30%-100% */
654 double V = ((double) (random()%34) + 66)/100.0; /* range 66%-100% */
655 hsv_to_rgb (H, S, V,
656 &colors[i].red, &colors[i].green, &colors[i].blue);
657 }
658 else
659 {
660 colors[i].red = random() % 0xFFFF;
661 colors[i].green = random() % 0xFFFF;
662 colors[i].blue = random() % 0xFFFF;
663 }
664 }
665
666 /* If there are a small number of colors, make sure at least the first
667 two contrast well.
668 */
669 if (!bright_p && ncolors <= 4)
670 {
671 int h0, h1;
672 double s0, s1, v0, v1;
673 rgb_to_hsv (colors[0].red, colors[0].green, colors[0].blue, &h0,&s0,&v0);
674 rgb_to_hsv (colors[1].red, colors[1].green, colors[1].blue, &h1,&s1,&v1);
675 if (fabs (v1-v0) < 0.5)
676 goto RETRY_ALL;
677 }
678
679 if (!allocate_p)
680 return;
681
682 RETRY_NON_WRITABLE:
683 if (writable_pP && *writable_pP)
684 {
685 unsigned long *pixels = (unsigned long *)
686 malloc(sizeof(*pixels) * (ncolors + 1));
687
688 allocate_writable_colors (screen, cmap, pixels, &ncolors);
689 if (ncolors > 0)
690 for (i = 0; i < ncolors; i++)
691 colors[i].pixel = pixels[i];
692 free (pixels);
693 if (ncolors > 0)
694 XStoreColors (dpy, cmap, colors, ncolors);
695 }
696 else
697 {
698 for (i = 0; i < ncolors; i++)
699 {
700 XColor color;
701 color = colors[i];
702 if (!XAllocColor (dpy, cmap, &color))
703 break;
704 colors[i].pixel = color.pixel;
705 }
706 ncolors = i;
707 }
708
709 /* If we tried for writable cells and got none, try for non-writable. */
710 if (allocate_p && ncolors == 0 && writable_pP && *writable_pP)
711 {
712 ncolors = *ncolorsP;
713 *writable_pP = False;
714 goto RETRY_NON_WRITABLE;
715 }
716
717 if (verbose_p)
718 complain(*ncolorsP, ncolors, wanted_writable,
719 wanted_writable && *writable_pP);
720
721 *ncolorsP = ncolors;
722}
723
724
725void
726rotate_colors (Screen *screen, Colormap cmap,
727 XColor *colors, int ncolors, int distance)
728{
729 Display *dpy = screen ? DisplayOfScreen (screen) : 0;
730 int i;
731 XColor *colors2;
732 if (ncolors < 2) return;
733 colors2 = (XColor *) malloc(sizeof(*colors2) * ncolors);
734 distance = distance % ncolors;
735 for (i = 0; i < ncolors; i++)
736 {
737 int j = i - distance;
738 if (j >= ncolors) j -= ncolors;
739 if (j < 0) j += ncolors;
740 colors2[i] = colors[j];
741 colors2[i].pixel = colors[i].pixel;
742 }
743 XStoreColors (dpy, cmap, colors2, ncolors);
744 XFlush(dpy);
745 memcpy(colors, colors2, sizeof(*colors) * ncolors);
746 free(colors2);
747}