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