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