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