]> Pileus Git - ~andy/gtk/blob - gdk/gdkcolor.c
assert ref_count>0.
[~andy/gtk] / gdk / gdkcolor.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include <time.h>
20 #include <X11/Xlib.h>
21 #include "gdk.h"
22 #include "gdkprivate.h"
23 #include "gdkx.h"
24
25
26 static gint  gdk_colormap_match_color (GdkColormap *cmap,
27                                        GdkColor    *color,
28                                        const gchar *available);
29 static void  gdk_colormap_add         (GdkColormap *cmap);
30 static void  gdk_colormap_remove      (GdkColormap *cmap);
31 static guint gdk_colormap_hash        (Colormap    *cmap);
32 static gint  gdk_colormap_cmp         (Colormap    *a,
33                                        Colormap    *b);
34 static void gdk_colormap_real_destroy (GdkColormap *colormap);
35
36 static GHashTable *colormap_hash = NULL;
37
38
39 GdkColormap*
40 gdk_colormap_new (GdkVisual *visual,
41                   gint       private_cmap)
42 {
43   GdkColormap *colormap;
44   GdkColormapPrivate *private;
45   Visual *xvisual;
46   int size;
47   int i;
48
49   g_return_val_if_fail (visual != NULL, NULL);
50
51   private = g_new (GdkColormapPrivate, 1);
52   colormap = (GdkColormap*) private;
53
54   private->xdisplay = gdk_display;
55   private->visual = visual;
56   private->ref_count = 1;
57
58   private->hash = NULL;
59   private->last_sync_time = 0;
60   private->info = NULL;
61   
62   xvisual = ((GdkVisualPrivate*) visual)->xvisual;
63
64   colormap->size = visual->colormap_size;
65   colormap->colors = g_new (GdkColor, colormap->size);
66
67   switch (visual->type)
68     {
69     case GDK_VISUAL_GRAYSCALE:
70     case GDK_VISUAL_PSEUDO_COLOR:
71       private->info = g_new0 (GdkColorInfo, colormap->size);
72       colormap->colors = g_new (GdkColor, colormap->size);
73       
74       private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
75                                         (GCompareFunc) gdk_color_equal);
76       
77       private->private_val = private_cmap;
78       private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
79                                             xvisual, (private_cmap) ? (AllocAll) : (AllocNone));
80
81       if (private_cmap)
82         {
83           XColor *default_colors;
84
85           default_colors = g_new (XColor, colormap->size);
86
87           for (i = 0; i < colormap->size; i++)
88             default_colors[i].pixel = i;
89
90           XQueryColors (private->xdisplay,
91                         DefaultColormap (private->xdisplay, gdk_screen),
92                         default_colors, colormap->size);
93
94           for (i = 0; i < colormap->size; i++)
95             {
96               colormap->colors[i].pixel = default_colors[i].pixel;
97               colormap->colors[i].red = default_colors[i].red;
98               colormap->colors[i].green = default_colors[i].green;
99               colormap->colors[i].blue = default_colors[i].blue;
100             }
101
102           gdk_colormap_change (colormap, colormap->size);
103           
104           g_free (default_colors);
105         }
106       break;
107
108     case GDK_VISUAL_DIRECT_COLOR:
109       private->private_val = TRUE;
110       private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
111                                             xvisual, AllocAll);
112
113       size = 1 << visual->red_prec;
114       for (i = 0; i < size; i++)
115         colormap->colors[i].red = i * 65535 / (size - 1);
116
117       size = 1 << visual->green_prec;
118       for (i = 0; i < size; i++)
119         colormap->colors[i].green = i * 65535 / (size - 1);
120
121       size = 1 << visual->blue_prec;
122       for (i = 0; i < size; i++)
123         colormap->colors[i].blue = i * 65535 / (size - 1);
124
125       gdk_colormap_change (colormap, colormap->size);
126       break;
127
128     case GDK_VISUAL_STATIC_GRAY:
129     case GDK_VISUAL_STATIC_COLOR:
130     case GDK_VISUAL_TRUE_COLOR:
131       private->private_val = FALSE;
132       private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
133                                             xvisual, AllocNone);
134       break;
135     }
136
137   gdk_colormap_add (colormap);
138
139   return colormap;
140 }
141
142 static void
143 gdk_colormap_real_destroy (GdkColormap *colormap)
144 {
145   GdkColormapPrivate *private = (GdkColormapPrivate*) colormap;
146
147   g_return_if_fail (colormap != NULL);
148   g_return_if_fail (private->ref_count == 0);
149
150   gdk_colormap_remove (colormap);
151   XFreeColormap (private->xdisplay, private->xcolormap);
152
153   if (private->hash)
154     g_hash_table_destroy (private->hash);
155   
156   g_free (private->info);
157   g_free (colormap->colors);
158   g_free (colormap);
159 }
160
161 GdkColormap*
162 gdk_colormap_ref (GdkColormap *cmap)
163 {
164   GdkColormapPrivate *private = (GdkColormapPrivate *)cmap;
165
166   g_return_val_if_fail (cmap != NULL, NULL);
167
168   private->ref_count += 1;
169   return cmap;
170 }
171
172 void
173 gdk_colormap_unref (GdkColormap *cmap)
174 {
175   GdkColormapPrivate *private = (GdkColormapPrivate *)cmap;
176
177   g_return_if_fail (cmap != NULL);
178   g_return_if_fail (private->ref_count > 0);
179
180   private->ref_count -= 1;
181   if (private->ref_count == 0)
182     gdk_colormap_real_destroy (cmap);
183 }
184
185 GdkVisual *
186 gdk_colormap_get_visual (GdkColormap *colormap)
187 {
188   GdkColormapPrivate *private;
189
190   g_return_val_if_fail (colormap != NULL, NULL);
191   
192   private = (GdkColormapPrivate *)colormap;
193
194   return private->visual;
195 }
196      
197 #define MIN_SYNC_TIME 2
198
199 void
200 gdk_colormap_sync (GdkColormap *colormap,
201                    gboolean     force)
202 {
203   time_t current_time;
204   GdkColormapPrivate *private = (GdkColormapPrivate *)colormap;
205   XColor *xpalette;
206   gint nlookup;
207   gint i;
208   
209   g_return_if_fail (colormap != NULL);
210
211   current_time = time (NULL);
212   if (!force && ((current_time - private->last_sync_time) < MIN_SYNC_TIME))
213     return;
214
215   private->last_sync_time = current_time;
216
217   nlookup = 0;
218   xpalette = g_new (XColor, colormap->size);
219   
220   for (i = 0; i < colormap->size; i++)
221     {
222       if (private->info[i].ref_count == 0)
223         {
224           xpalette[nlookup].pixel = i;
225           xpalette[nlookup].red = 0;
226           xpalette[nlookup].green = 0;
227           xpalette[nlookup].blue = 0;
228           nlookup++;
229         }
230     }
231   
232   XQueryColors (gdk_display, private->xcolormap, xpalette, nlookup);
233   
234   for (i = 0; i < nlookup; i++)
235     {
236       gulong pixel = xpalette[i].pixel;
237       colormap->colors[pixel].pixel = pixel;
238       colormap->colors[pixel].red = xpalette[i].red;
239       colormap->colors[pixel].green = xpalette[i].green;
240       colormap->colors[pixel].blue = xpalette[i].blue;
241     }
242   
243   g_free (xpalette);
244 }
245                    
246
247 GdkColormap*
248 gdk_colormap_get_system (void)
249 {
250   static GdkColormap *colormap = NULL;
251   GdkColormapPrivate *private;
252
253   if (!colormap)
254     {
255       private = g_new (GdkColormapPrivate, 1);
256       colormap = (GdkColormap*) private;
257
258       private->xdisplay = gdk_display;
259       private->xcolormap = DefaultColormap (gdk_display, gdk_screen);
260       private->visual = gdk_visual_get_system ();
261       private->private_val = FALSE;
262       private->ref_count = 1;
263
264       private->hash = NULL;
265       private->last_sync_time = 0;
266       private->info = NULL;
267
268       colormap->colors = NULL;
269       colormap->size = private->visual->colormap_size;
270
271       if ((private->visual->type == GDK_VISUAL_GRAYSCALE) ||
272           (private->visual->type == GDK_VISUAL_PSEUDO_COLOR))
273         {
274           private->info = g_new0 (GdkColorInfo, colormap->size);
275           colormap->colors = g_new (GdkColor, colormap->size);
276           
277           private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
278                                             (GCompareFunc) gdk_color_equal);
279
280           gdk_colormap_sync (colormap, TRUE);
281         }
282
283       gdk_colormap_add (colormap);
284     }
285
286   return colormap;
287 }
288
289 gint
290 gdk_colormap_get_system_size (void)
291 {
292   return DisplayCells (gdk_display, gdk_screen);
293 }
294
295 void
296 gdk_colormap_change (GdkColormap *colormap,
297                      gint         ncolors)
298 {
299   GdkColormapPrivate *private;
300   GdkVisual *visual;
301   XColor *palette;
302   gint shift;
303   int max_colors;
304   int size;
305   int i;
306
307   g_return_if_fail (colormap != NULL);
308
309   palette = g_new (XColor, ncolors);
310
311   private = (GdkColormapPrivate*) colormap;
312   switch (private->visual->type)
313     {
314     case GDK_VISUAL_GRAYSCALE:
315     case GDK_VISUAL_PSEUDO_COLOR:
316       for (i = 0; i < ncolors; i++)
317         {
318           palette[i].pixel = colormap->colors[i].pixel;
319           palette[i].red = colormap->colors[i].red;
320           palette[i].green = colormap->colors[i].green;
321           palette[i].blue = colormap->colors[i].blue;
322           palette[i].flags = DoRed | DoGreen | DoBlue;
323         }
324
325       XStoreColors (private->xdisplay, private->xcolormap, palette, ncolors);
326       break;
327
328     case GDK_VISUAL_DIRECT_COLOR:
329       visual = private->visual;
330
331       shift = visual->red_shift;
332       max_colors = 1 << visual->red_prec;
333       size = (ncolors < max_colors) ? (ncolors) : (max_colors);
334
335       for (i = 0; i < size; i++)
336         {
337           palette[i].pixel = i << shift;
338           palette[i].red = colormap->colors[i].red;
339           palette[i].flags = DoRed;
340         }
341
342       XStoreColors (private->xdisplay, private->xcolormap, palette, size);
343
344       shift = visual->green_shift;
345       max_colors = 1 << visual->green_prec;
346       size = (ncolors < max_colors) ? (ncolors) : (max_colors);
347
348       for (i = 0; i < size; i++)
349         {
350           palette[i].pixel = i << shift;
351           palette[i].green = colormap->colors[i].green;
352           palette[i].flags = DoGreen;
353         }
354
355       XStoreColors (private->xdisplay, private->xcolormap, palette, size);
356
357       shift = visual->blue_shift;
358       max_colors = 1 << visual->blue_prec;
359       size = (ncolors < max_colors) ? (ncolors) : (max_colors);
360
361       for (i = 0; i < size; i++)
362         {
363           palette[i].pixel = i << shift;
364           palette[i].blue = colormap->colors[i].blue;
365           palette[i].flags = DoBlue;
366         }
367
368       XStoreColors (private->xdisplay, private->xcolormap, palette, size);
369       break;
370
371     default:
372       break;
373     }
374
375   g_free (palette);
376 }
377
378 void
379 gdk_colors_store (GdkColormap   *colormap,
380                   GdkColor      *colors,
381                   gint           ncolors)
382 {
383   gint i;
384
385   for (i = 0; i < ncolors; i++)
386     {
387       colormap->colors[i].pixel = colors[i].pixel;
388       colormap->colors[i].red = colors[i].red;
389       colormap->colors[i].green = colors[i].green;
390       colormap->colors[i].blue = colors[i].blue;
391     }
392
393   gdk_colormap_change (colormap, ncolors);
394 }
395
396 gboolean
397 gdk_colors_alloc (GdkColormap   *colormap,
398                   gint           contiguous,
399                   gulong        *planes,
400                   gint           nplanes,
401                   gulong        *pixels,
402                   gint           npixels)
403 {
404   GdkColormapPrivate *private;
405   gint return_val;
406   gint i;
407
408   g_return_val_if_fail (colormap != NULL, 0);
409
410   private = (GdkColormapPrivate*) colormap;
411
412   return_val = XAllocColorCells (private->xdisplay, private->xcolormap,
413                                  contiguous, planes, nplanes, pixels, npixels);
414
415   if (return_val)
416     {
417       for (i=0; i<npixels; i++)
418         {
419           private->info[pixels[i]].ref_count++;
420           private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
421         }
422     }
423
424   return return_val;
425 }
426
427 /* This is almost identical to gdk_colormap_free_colors.
428  * Keep them in sync!
429  */
430 void
431 gdk_colors_free (GdkColormap *colormap,
432                  gulong      *in_pixels,
433                  gint         in_npixels,
434                  gulong       planes)
435 {
436   GdkColormapPrivate *private;
437   gulong *pixels;
438   gint npixels = 0;
439   gint i;
440
441   g_return_if_fail (colormap != NULL);
442   g_return_if_fail (in_pixels != NULL);
443
444   private = (GdkColormapPrivate*) colormap;
445
446   if ((private->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
447       (private->visual->type != GDK_VISUAL_GRAYSCALE))
448     return;
449   
450   pixels = g_new (gulong, in_npixels);
451
452   for (i=0; i<in_npixels; i++)
453     {
454       gulong pixel = in_pixels[i];
455       
456       if (private->info[pixel].ref_count)
457         {
458           private->info[pixel].ref_count--;
459
460           if (private->info[pixel].ref_count == 0)
461             {
462               pixels[npixels++] = pixel;
463               if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
464                 g_hash_table_remove (private->hash, &colormap->colors[in_pixels[i]]);
465               private->info[pixel].flags = 0;
466             }
467         }
468     }
469
470   if (npixels)
471     XFreeColors (private->xdisplay, private->xcolormap,
472                  pixels, npixels, planes);
473   g_free (pixels);
474 }
475
476 /*
477  *--------------------------------------------------------------
478  * gdk_color_copy
479  *
480  *   Copy a color structure into new storage.
481  *
482  * Arguments:
483  *   "color" is the color struct to copy.
484  *
485  * Results:
486  *   A new color structure.  Free it with gdk_color_free.
487  *
488  *--------------------------------------------------------------
489  */
490
491 static GMemChunk *color_chunk;
492
493 GdkColor*
494 gdk_color_copy (GdkColor *color)
495 {
496   GdkColor *new_color;
497   
498   g_return_val_if_fail (color != NULL, NULL);
499
500   if (color_chunk == NULL)
501     color_chunk = g_mem_chunk_new ("colors",
502                                    sizeof (GdkColor),
503                                    4096,
504                                    G_ALLOC_AND_FREE);
505
506   new_color = g_chunk_new (GdkColor, color_chunk);
507   *new_color = *color;
508   return new_color;
509 }
510
511 /*
512  *--------------------------------------------------------------
513  * gdk_color_free
514  *
515  *   Free a color structure obtained from gdk_color_copy.  Do not use
516  *   with other color structures.
517  *
518  * Arguments:
519  *   "color" is the color struct to free.
520  *
521  *-------------------------------------------------------------- */
522
523 void
524 gdk_color_free (GdkColor *color)
525 {
526   g_assert (color_chunk != NULL);
527   g_return_if_fail (color != NULL);
528
529   g_mem_chunk_free (color_chunk, color);
530 }
531
532 gint
533 gdk_color_white (GdkColormap *colormap,
534                  GdkColor    *color)
535 {
536   gint return_val;
537
538   g_return_val_if_fail (colormap != NULL, FALSE);
539
540   if (color)
541     {
542       color->pixel = WhitePixel (gdk_display, gdk_screen);
543       color->red = 65535;
544       color->green = 65535;
545       color->blue = 65535;
546
547       return_val = gdk_color_alloc (colormap, color);
548     }
549   else
550     return_val = FALSE;
551
552   return return_val;
553 }
554
555 gint
556 gdk_color_black (GdkColormap *colormap,
557                  GdkColor    *color)
558 {
559   gint return_val;
560
561   g_return_val_if_fail (colormap != NULL, FALSE);
562
563   if (color)
564     {
565       color->pixel = BlackPixel (gdk_display, gdk_screen);
566       color->red = 0;
567       color->green = 0;
568       color->blue = 0;
569
570       return_val = gdk_color_alloc (colormap, color);
571     }
572   else
573     return_val = FALSE;
574
575   return return_val;
576 }
577
578 gboolean
579 gdk_color_parse (const gchar *spec,
580                  GdkColor *color)
581 {
582   Colormap xcolormap;
583   XColor xcolor;
584   gboolean return_val;
585
586   g_return_val_if_fail (spec != NULL, FALSE);
587   g_return_val_if_fail (color != NULL, FALSE);
588
589   xcolormap = DefaultColormap (gdk_display, gdk_screen);
590
591   if (XParseColor (gdk_display, xcolormap, spec, &xcolor))
592     {
593       return_val = TRUE;
594       color->red = xcolor.red;
595       color->green = xcolor.green;
596       color->blue = xcolor.blue;
597     }
598   else
599     return_val = FALSE;
600
601   return return_val;
602 }
603
604 /********************
605  * Color allocation *
606  ********************/
607
608 /* Try to allocate a single color using XAllocColor. If it succeeds,
609  * cache the result in our colormap, and store in ret.
610  */
611 static gboolean 
612 gdk_colormap_alloc1 (GdkColormap *colormap,
613                      GdkColor    *color,
614                      GdkColor    *ret)
615 {
616   GdkColormapPrivate *private;
617   XColor xcolor;
618
619   private = (GdkColormapPrivate*) colormap;
620
621   xcolor.red = color->red;
622   xcolor.green = color->green;
623   xcolor.blue = color->blue;
624   xcolor.pixel = color->pixel;
625   xcolor.flags = DoRed | DoGreen | DoBlue;
626
627   if (XAllocColor (private->xdisplay, private->xcolormap, &xcolor))
628     {
629       ret->pixel = xcolor.pixel;
630       ret->red = xcolor.red;
631       ret->green = xcolor.green;
632       ret->blue = xcolor.blue;
633       
634       if (ret->pixel < colormap->size)
635         {
636           if (private->info[ret->pixel].ref_count) /* got a duplicate */
637             {
638               XFreeColors (private->xdisplay, private->xcolormap,
639                            &ret->pixel, 1, 0);
640             }
641           else
642             {
643               colormap->colors[ret->pixel] = *color;
644               private->info[ret->pixel].ref_count = 1;
645
646               g_hash_table_insert (private->hash,
647                                    &colormap->colors[ret->pixel],
648                                    &colormap->colors[ret->pixel]);
649             }
650         }
651       return TRUE;
652     }
653   else
654     {
655       return FALSE;
656     }
657 }
658
659 static gint
660 gdk_colormap_alloc_colors_writeable (GdkColormap *colormap,
661                                      GdkColor    *colors,
662                                      gint         ncolors,
663                                      gboolean     writeable,
664                                      gboolean     best_match,
665                                      gboolean    *success)
666 {
667   GdkColormapPrivate *private;
668   gulong *pixels;
669   Status status;
670   gint i, index;
671
672   private = (GdkColormapPrivate*) colormap;
673
674   if (private->private_val)
675     {
676       index = 0;
677       for (i=0; i<ncolors; i++)
678         {
679           while ((index < colormap->size) && (private->info[index].ref_count != 0))
680             index++;
681           
682           if (index < colormap->size)
683             {
684               colors[i].pixel = index;
685               success[i] = TRUE;
686               private->info[index].ref_count++;
687               private->info[i].flags |= GDK_COLOR_WRITEABLE;
688             }
689           else
690             break;
691         }
692       return i;
693     }
694   else
695     {
696       pixels = g_new (gulong, ncolors);
697       /* Allocation of a writeable color cells */
698       
699       status =  XAllocColorCells (private->xdisplay, private->xcolormap,
700                                   FALSE, NULL, 0, pixels, ncolors);
701       if (status)
702         {
703           for (i=0; i<ncolors; i++)
704             {
705               colors[i].pixel = pixels[i];
706               private->info[pixels[i]].ref_count++;
707               private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
708             }
709         }
710       
711       g_free (pixels);
712
713       return status ? ncolors : 0; 
714     }
715 }
716
717 static gint
718 gdk_colormap_alloc_colors_private (GdkColormap *colormap,
719                                    GdkColor    *colors,
720                                    gint         ncolors,
721                                    gboolean     writeable,
722                                    gboolean     best_match,
723                                    gboolean    *success)
724 {
725   GdkColormapPrivate *private;
726   gint i, index;
727   XColor *store = g_new (XColor, ncolors);
728   gint nstore = 0;
729   gint nremaining = 0;
730   
731   private = (GdkColormapPrivate*) colormap;
732   index = -1;
733
734   /* First, store the colors we have room for */
735
736   index = 0;
737   for (i=0; i<ncolors; i++)
738     {
739       if (!success[i])
740         {
741           while ((index < colormap->size) && (private->info[index].ref_count != 0))
742             index++;
743
744           if (index < colormap->size)
745             {
746               store[nstore].red = colors[i].red;
747               store[nstore].blue = colors[i].blue;
748               store[nstore].green = colors[i].green;
749               store[nstore].pixel = index;
750               nstore++;
751
752               success[i] = TRUE;
753
754               colors[i].pixel = index;
755               private->info[index].ref_count++;
756             }
757           else
758             nremaining++;
759         }
760     }
761   
762   XStoreColors (private->xdisplay, private->xcolormap, store, nstore);
763   g_free (store);
764
765   if (nremaining > 0 && best_match)
766     {
767       /* Get best matches for remaining colors */
768
769       gchar *available = g_new (gchar, colormap->size);
770       for (i = 0; i < colormap->size; i++)
771         available[i] = TRUE;
772
773       for (i=0; i<ncolors; i++)
774         {
775           if (!success[i])
776             {
777               index = gdk_colormap_match_color (colormap, 
778                                                 &colors[i], 
779                                                 available);
780               if (index != -1)
781                 {
782                   colors[i] = colormap->colors[index];
783                   private->info[index].ref_count++;
784
785                   success[i] = TRUE;
786                   nremaining--;
787                 }
788             }
789         }
790       g_free (available);
791     }
792
793   return (ncolors - nremaining);
794 }
795
796 static gint
797 gdk_colormap_alloc_colors_shared (GdkColormap *colormap,
798                                   GdkColor    *colors,
799                                   gint         ncolors,
800                                   gboolean     writeable,
801                                   gboolean     best_match,
802                                   gboolean    *success)
803 {
804   GdkColormapPrivate *private;
805   gint i, index;
806   gint nremaining = 0;
807   gint nfailed = 0;
808
809   private = (GdkColormapPrivate*) colormap;
810   index = -1;
811
812   for (i=0; i<ncolors; i++)
813     {
814       if (!success[i])
815         {
816           if (gdk_colormap_alloc1 (colormap, &colors[i], &colors[i]))
817             success[i] = TRUE;
818           else
819             nremaining++;
820         }
821     }
822
823
824   if (nremaining > 0 && best_match)
825     {
826       gchar *available = g_new (gchar, colormap->size);
827       for (i = 0; i < colormap->size; i++)
828         available[i] = ((private->info[i].ref_count == 0) ||
829                         !(private->info[i].flags && GDK_COLOR_WRITEABLE));
830       gdk_colormap_sync (colormap, FALSE);
831       
832       while (nremaining > 0)
833         {
834           for (i=0; i<ncolors; i++)
835             {
836               if (!success[i])
837                 {
838                   index = gdk_colormap_match_color (colormap, &colors[i], available);
839                   if (index != -1)
840                     {
841                       if (private->info[index].ref_count)
842                         {
843                           private->info[index].ref_count++;
844                           colors[i] = colormap->colors[index];
845                           success[i] = TRUE;
846                           nremaining--;
847                         }
848                       else
849                         {
850                           if (gdk_colormap_alloc1 (colormap, 
851                                                    &colormap->colors[index],
852                                                    &colors[i]))
853                             {
854                               success[i] = TRUE;
855                               nremaining--;
856                               break;
857                             }
858                           else
859                             {
860                               available[index] = FALSE;
861                             }
862                         }
863                     }
864                   else
865                     {
866                       nfailed++;
867                       nremaining--;
868                       success[i] = 2; /* flag as permanent failure */
869                     }
870                 }
871             }
872         }
873       g_free (available);
874     }
875
876   /* Change back the values we flagged as permanent failures */
877   if (nfailed > 0)
878     {
879       for (i=0; i<ncolors; i++)
880         if (success[i] == 2)
881           success[i] = FALSE;
882       nremaining = nfailed;
883     }
884   
885   return (ncolors - nremaining);
886 }
887
888 static gint
889 gdk_colormap_alloc_colors_pseudocolor (GdkColormap *colormap,
890                                        GdkColor    *colors,
891                                        gint         ncolors,
892                                        gboolean     writeable,
893                                        gboolean     best_match,
894                                        gboolean    *success)
895 {
896   GdkColormapPrivate *private;
897   GdkColor *lookup_color;
898   gint i;
899   gint nremaining = 0;
900
901   private = (GdkColormapPrivate*) colormap;
902
903   /* Check for an exact match among previously allocated colors */
904
905   for (i=0; i<ncolors; i++)
906     {
907       if (!success[i])
908         {
909           lookup_color = g_hash_table_lookup (private->hash, &colors[i]);
910           if (lookup_color)
911             {
912               private->info[lookup_color->pixel].ref_count++;
913               colors[i].pixel = lookup_color->pixel;
914               success[i] = TRUE;
915             }
916           else
917             nremaining++;
918         }
919     }
920
921   /* If that failed, we try to allocate a new color, or approxmiate
922    * with what we can get if best_match is TRUE.
923    */
924   if (nremaining > 0)
925     {
926       if (private->private_val)
927         return gdk_colormap_alloc_colors_private (colormap, colors, ncolors, writeable, best_match, success);
928       else
929         return gdk_colormap_alloc_colors_shared (colormap, colors, ncolors, writeable, best_match, success);
930     }
931   else
932     return 0;
933 }
934
935 gint
936 gdk_colormap_alloc_colors (GdkColormap *colormap,
937                            GdkColor    *colors,
938                            gint         ncolors,
939                            gboolean     writeable,
940                            gboolean     best_match,
941                            gboolean    *success)
942 {
943   GdkColormapPrivate *private;
944   GdkVisual *visual;
945   gint i;
946   gint nremaining = 0;
947   XColor xcolor;
948
949   g_return_val_if_fail (colormap != NULL, FALSE);
950   g_return_val_if_fail (colors != NULL, FALSE);
951
952   private = (GdkColormapPrivate*) colormap;
953
954   for (i=0; i<ncolors; i++)
955     {
956       success[i] = FALSE;
957     }
958
959   switch (private->visual->type)
960     {
961     case GDK_VISUAL_PSEUDO_COLOR:
962     case GDK_VISUAL_GRAYSCALE:
963       if (writeable)
964         return gdk_colormap_alloc_colors_writeable (colormap, colors, ncolors,
965                                                     writeable, best_match, success);
966       else
967         return gdk_colormap_alloc_colors_pseudocolor (colormap, colors, ncolors,
968                                                     writeable, best_match, success);
969       break;
970
971     case GDK_VISUAL_DIRECT_COLOR:
972     case GDK_VISUAL_TRUE_COLOR:
973       visual = private->visual;
974
975       for (i=0; i<ncolors; i++)
976         {
977           colors[i].pixel = (((colors[i].red >> (16 - visual->red_prec)) << visual->red_shift) +
978                              ((colors[i].green >> (16 - visual->green_prec)) << visual->green_shift) +
979                              ((colors[i].blue >> (16 - visual->blue_prec)) << visual->blue_shift));
980           success[i] = TRUE;
981         }
982       break;
983
984     case GDK_VISUAL_STATIC_GRAY:
985     case GDK_VISUAL_STATIC_COLOR:
986       for (i=0; i<ncolors; i++)
987         {
988           xcolor.red = colors[i].red;
989           xcolor.green = colors[i].green;
990           xcolor.blue = colors[i].blue;
991           xcolor.pixel = colors[i].pixel;
992           xcolor.flags = DoRed | DoGreen | DoBlue;
993
994           if (XAllocColor (private->xdisplay, private->xcolormap, &xcolor))
995             {
996               colors[i].pixel = xcolor.pixel;
997               success[i] = TRUE;
998             }
999           else
1000             nremaining++;
1001         }
1002       break;
1003     }
1004   return nremaining;
1005 }
1006
1007 gboolean
1008 gdk_colormap_alloc_color (GdkColormap *colormap,
1009                           GdkColor    *color,
1010                           gboolean     writeable,
1011                           gboolean     best_match)
1012 {
1013   gboolean success;
1014
1015   gdk_colormap_alloc_colors (colormap, color, 1, writeable, best_match,
1016                              &success);
1017
1018   return success;
1019 }
1020
1021 /* This is almost identical to gdk_colors_free.
1022  * Keep them in sync!
1023  */
1024 void
1025 gdk_colormap_free_colors (GdkColormap *colormap,
1026                           GdkColor    *colors,
1027                           gint         ncolors)
1028 {
1029   GdkColormapPrivate *private;
1030   gulong *pixels;
1031   gint npixels = 0;
1032   gint i;
1033
1034   g_return_if_fail (colormap != NULL);
1035   g_return_if_fail (colors != NULL);
1036
1037   private = (GdkColormapPrivate*) colormap;
1038
1039   if ((private->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
1040       (private->visual->type != GDK_VISUAL_GRAYSCALE))
1041     return;
1042
1043   pixels = g_new (gulong, ncolors);
1044
1045   for (i=0; i<ncolors; i++)
1046     {
1047       gulong pixel = colors[i].pixel;
1048       
1049       if (private->info[pixel].ref_count)
1050         {
1051           private->info[pixel].ref_count--;
1052
1053           if (private->info[pixel].ref_count == 0)
1054             {
1055               pixels[npixels++] = pixel;
1056               if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
1057                 g_hash_table_remove (private->hash, &colors[i]);
1058               private->info[pixel].flags = 0;
1059             }
1060         }
1061     }
1062
1063   if (npixels)
1064     XFreeColors (private->xdisplay, private->xcolormap,
1065                  pixels, npixels, 0);
1066
1067   g_free (pixels);
1068 }
1069
1070 gboolean
1071 gdk_color_alloc (GdkColormap *colormap,
1072                  GdkColor    *color)
1073 {
1074   gboolean success;
1075
1076   gdk_colormap_alloc_colors (colormap, color, 1, FALSE, TRUE, &success);
1077
1078   return success;
1079 }
1080
1081 gboolean
1082 gdk_color_change (GdkColormap *colormap,
1083                   GdkColor    *color)
1084 {
1085   GdkColormapPrivate *private;
1086   XColor xcolor;
1087
1088   g_return_val_if_fail (colormap != NULL, FALSE);
1089   g_return_val_if_fail (color != NULL, FALSE);
1090
1091   xcolor.pixel = color->pixel;
1092   xcolor.red = color->red;
1093   xcolor.green = color->green;
1094   xcolor.blue = color->blue;
1095   xcolor.flags = DoRed | DoGreen | DoBlue;
1096
1097   private = (GdkColormapPrivate*) colormap;
1098   XStoreColor (private->xdisplay, private->xcolormap, &xcolor);
1099
1100   return TRUE;
1101 }
1102
1103 guint
1104 gdk_color_hash (const GdkColor *colora,
1105                 const GdkColor *colorb)
1106 {
1107   return ((colora->red) +
1108           (colora->green << 11) +
1109           (colora->blue << 22) +
1110           (colora->blue >> 6));
1111 }
1112
1113 gint
1114 gdk_color_equal (const GdkColor *colora,
1115                  const GdkColor *colorb)
1116 {
1117   g_return_val_if_fail (colora != NULL, FALSE);
1118   g_return_val_if_fail (colorb != NULL, FALSE);
1119
1120   return ((colora->red == colorb->red) &&
1121           (colora->green == colorb->green) &&
1122           (colora->blue == colorb->blue));
1123 }
1124
1125 /* XXX: Do not use this function until it is fixed. An X Colormap
1126  *      is useless unless we also have the visual.
1127  */
1128 GdkColormap*
1129 gdkx_colormap_get (Colormap xcolormap)
1130 {
1131   GdkColormap *colormap;
1132   GdkColormapPrivate *private;
1133
1134   colormap = gdk_colormap_lookup (xcolormap);
1135   if (colormap)
1136     return colormap;
1137
1138   if (xcolormap == DefaultColormap (gdk_display, gdk_screen))
1139     return gdk_colormap_get_system ();
1140
1141   private = g_new (GdkColormapPrivate, 1);
1142   colormap = (GdkColormap*) private;
1143
1144   private->xdisplay = gdk_display;
1145   private->xcolormap = xcolormap;
1146   private->visual = NULL;
1147   private->private_val = TRUE;
1148
1149   /* To do the following safely, we would have to have some way of finding
1150    * out what the size or visual of the given colormap is. It seems
1151    * X doesn't allow this
1152    */
1153
1154 #if 0
1155   for (i = 0; i < 256; i++)
1156     {
1157       xpalette[i].pixel = i;
1158       xpalette[i].red = 0;
1159       xpalette[i].green = 0;
1160       xpalette[i].blue = 0;
1161     }
1162
1163   XQueryColors (gdk_display, private->xcolormap, xpalette, 256);
1164
1165   for (i = 0; i < 256; i++)
1166     {
1167       colormap->colors[i].pixel = xpalette[i].pixel;
1168       colormap->colors[i].red = xpalette[i].red;
1169       colormap->colors[i].green = xpalette[i].green;
1170       colormap->colors[i].blue = xpalette[i].blue;
1171     }
1172 #endif
1173
1174   colormap->colors = NULL;
1175   colormap->size = 0;
1176
1177   gdk_colormap_add (colormap);
1178
1179   return colormap;
1180 }
1181
1182
1183 static gint
1184 gdk_colormap_match_color (GdkColormap *cmap,
1185                           GdkColor    *color,
1186                           const gchar *available)
1187 {
1188   GdkColor *colors;
1189   guint sum, max;
1190   gint rdiff, gdiff, bdiff;
1191   gint i, index;
1192
1193   g_return_val_if_fail (cmap != NULL, 0);
1194   g_return_val_if_fail (color != NULL, 0);
1195
1196   colors = cmap->colors;
1197   max = 3 * (65536);
1198   index = -1;
1199
1200   for (i = 0; i < cmap->size; i++)
1201     {
1202       if ((!available) || (available && available[i]))
1203         {
1204           rdiff = (color->red - colors[i].red);
1205           gdiff = (color->green - colors[i].green);
1206           bdiff = (color->blue - colors[i].blue);
1207
1208           sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
1209
1210           if (sum < max)
1211             {
1212               index = i;
1213               max = sum;
1214             }
1215         }
1216     }
1217
1218   return index;
1219 }
1220
1221
1222 GdkColormap*
1223 gdk_colormap_lookup (Colormap xcolormap)
1224 {
1225   GdkColormap *cmap;
1226
1227   if (!colormap_hash)
1228     return NULL;
1229
1230   cmap = g_hash_table_lookup (colormap_hash, &xcolormap);
1231   return cmap;
1232 }
1233
1234 static void
1235 gdk_colormap_add (GdkColormap *cmap)
1236 {
1237   GdkColormapPrivate *private;
1238
1239   if (!colormap_hash)
1240     colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
1241                                       (GCompareFunc) gdk_colormap_cmp);
1242
1243   private = (GdkColormapPrivate*) cmap;
1244
1245   g_hash_table_insert (colormap_hash, &private->xcolormap, cmap);
1246 }
1247
1248 static void
1249 gdk_colormap_remove (GdkColormap *cmap)
1250 {
1251   GdkColormapPrivate *private;
1252
1253   if (!colormap_hash)
1254     colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
1255                                       (GCompareFunc) gdk_colormap_cmp);
1256
1257   private = (GdkColormapPrivate*) cmap;
1258
1259   g_hash_table_remove (colormap_hash, &private->xcolormap);
1260 }
1261
1262 static guint
1263 gdk_colormap_hash (Colormap *cmap)
1264 {
1265   return *cmap;
1266 }
1267
1268 static gint
1269 gdk_colormap_cmp (Colormap *a,
1270                   Colormap *b)
1271 {
1272   return (*a == *b);
1273 }