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