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