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