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