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