gtk/gdk/x11/gdkcolor-x11.c
Owen Taylor a1f5e821bf Fixes from #98358, Havoc Pennington.
Mon Nov 25 17:44:09 2002  Owen Taylor  <otaylor@redhat.com>

        Fixes from #98358, Havoc Pennington.

        * gdk/{x11,win32,linux-fb}/gdkcolor-*.c (gdk_colormap_finalize):
        Free the private structure.

        * gdk/x11,win32,linxu-fb}/gdkscreen-*.c (gdk_screen_set_default_colormap):
          gdk/x11/gdkwindow-x11.c (_gdk_windowing_window_init):
        Keep a ref to the default colormap.

        * gdk/x11/gdkscreen-x11.c (gdk_screen_x11_dispose): Unref
        the default colormap.

        * gdk/x11/gdkwindow-x11.c (gdk_window_impl_x11_set_colormap):
        Handle the CMAP == NULL case even when the window is
        destroyed.

        * gdk/x11/gdkwindow-x11.c (_gdk_windowing_window_init):
        Add a missing ref for the system colormap.
2002-11-25 22:52:36 +00:00

1510 lines
38 KiB
C

/* GDK - The GIMP Drawing Kit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include <time.h>
#include "gdkcolor.h"
#include "gdkinternals.h"
#include "gdkx.h"
#include "gdkprivate-x11.h"
#include "gdkscreen-x11.h"
typedef struct _GdkColormapPrivateX11 GdkColormapPrivateX11;
struct _GdkColormapPrivateX11
{
GdkScreen *screen;
Colormap xcolormap;
Display *xdisplay;
gint private_val;
GHashTable *hash;
GdkColorInfo *info;
time_t last_sync_time;
guint foreign : 1;
};
#define GDK_COLORMAP_PRIVATE_DATA(cmap) ((GdkColormapPrivateX11 *) GDK_COLORMAP (cmap)->windowing_data)
static gint gdk_colormap_match_color (GdkColormap *cmap,
GdkColor *color,
const gchar *available);
static void gdk_colormap_add (GdkColormap *cmap);
static void gdk_colormap_remove (GdkColormap *cmap);
static GdkColormap *gdk_colormap_lookup (GdkScreen *screen,
Colormap xcolormap);
static guint gdk_colormap_hash (Colormap *cmap);
static gboolean gdk_colormap_equal (Colormap *a,
Colormap *b);
static void gdk_colormap_sync (GdkColormap *colormap,
gboolean force);
static void gdk_colormap_init (GdkColormap *colormap);
static void gdk_colormap_class_init (GdkColormapClass *klass);
static void gdk_colormap_finalize (GObject *object);
static gpointer parent_class = NULL;
GType
gdk_colormap_get_type (void)
{
static GType object_type = 0;
if (!object_type)
{
static const GTypeInfo object_info =
{
sizeof (GdkColormapClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gdk_colormap_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GdkColormap),
0, /* n_preallocs */
(GInstanceInitFunc) gdk_colormap_init,
};
object_type = g_type_register_static (G_TYPE_OBJECT,
"GdkColormap",
&object_info, 0);
}
return object_type;
}
static void
gdk_colormap_init (GdkColormap *colormap)
{
GdkColormapPrivateX11 *private;
private = g_new (GdkColormapPrivateX11, 1);
colormap->windowing_data = private;
private->screen = NULL;
private->hash = NULL;
private->last_sync_time = 0;
private->info = NULL;
colormap->size = 0;
colormap->colors = NULL;
}
static void
gdk_colormap_class_init (GdkColormapClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->finalize = gdk_colormap_finalize;
}
static void
gdk_colormap_finalize (GObject *object)
{
GdkColormap *colormap = GDK_COLORMAP (object);
GdkColormapPrivateX11 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
gdk_colormap_remove (colormap);
if (!private->screen->closed)
XFreeColormap (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap);
if (private->hash)
g_hash_table_destroy (private->hash);
g_free (private->info);
g_free (colormap->colors);
g_free (private);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* gdk_colormap_new:
* @visual: a #GdkVisual.
* @allocate: if %TRUE, the newly created colormap will be
* a private colormap, and all colors in it will be
* allocated for the applications use.
*
* Creates a new colormap for the given visual.
*
* Return value: the new #GdkColormap.
**/
GdkColormap*
gdk_colormap_new (GdkVisual *visual,
gboolean allocate)
{
GdkColormap *colormap;
GdkColormapPrivateX11 *private;
Visual *xvisual;
Display *xdisplay;
Window xrootwin;
int size;
int i;
/* FIXME when object properties settle down, there needs to be some
* kind of default construction (and construct-only arguments)
*/
g_return_val_if_fail (visual != NULL, NULL);
colormap = g_object_new (GDK_TYPE_COLORMAP, NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
colormap->visual = visual;
private->screen = gdk_visual_get_screen (visual);
xvisual = ((GdkVisualPrivate*) visual)->xvisual;
xdisplay = GDK_SCREEN_XDISPLAY (private->screen);
xrootwin = GDK_SCREEN_XROOTWIN (private->screen);
colormap->size = visual->colormap_size;
switch (visual->type)
{
case GDK_VISUAL_GRAYSCALE:
case GDK_VISUAL_PSEUDO_COLOR:
private->info = g_new0 (GdkColorInfo, colormap->size);
colormap->colors = g_new (GdkColor, colormap->size);
private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
(GEqualFunc) gdk_color_equal);
private->private_val = allocate;
private->xcolormap = XCreateColormap (xdisplay, xrootwin,
xvisual, (allocate) ? (AllocAll) : (AllocNone));
if (allocate)
{
XColor *default_colors;
default_colors = g_new (XColor, colormap->size);
for (i = 0; i < colormap->size; i++)
default_colors[i].pixel = i;
XQueryColors (xdisplay,
DefaultColormapOfScreen (GDK_SCREEN_X11 (private->screen)->xscreen),
default_colors, colormap->size);
for (i = 0; i < colormap->size; i++)
{
colormap->colors[i].pixel = default_colors[i].pixel;
colormap->colors[i].red = default_colors[i].red;
colormap->colors[i].green = default_colors[i].green;
colormap->colors[i].blue = default_colors[i].blue;
}
gdk_colormap_change (colormap, colormap->size);
g_free (default_colors);
}
break;
case GDK_VISUAL_DIRECT_COLOR:
private->private_val = TRUE;
private->xcolormap = XCreateColormap (xdisplay, xrootwin,
xvisual, AllocAll);
colormap->colors = g_new (GdkColor, colormap->size);
size = 1 << visual->red_prec;
for (i = 0; i < size; i++)
colormap->colors[i].red = i * 65535 / (size - 1);
size = 1 << visual->green_prec;
for (i = 0; i < size; i++)
colormap->colors[i].green = i * 65535 / (size - 1);
size = 1 << visual->blue_prec;
for (i = 0; i < size; i++)
colormap->colors[i].blue = i * 65535 / (size - 1);
gdk_colormap_change (colormap, colormap->size);
break;
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_STATIC_COLOR:
private->private_val = FALSE;
private->xcolormap = XCreateColormap (xdisplay, xrootwin,
xvisual, AllocNone);
colormap->colors = g_new (GdkColor, colormap->size);
gdk_colormap_sync (colormap, TRUE);
break;
case GDK_VISUAL_TRUE_COLOR:
private->private_val = FALSE;
private->xcolormap = XCreateColormap (xdisplay, xrootwin,
xvisual, AllocNone);
break;
}
gdk_colormap_add (colormap);
return colormap;
}
static void
gdk_colormap_sync_palette (GdkColormap *colormap)
{
GdkColormapPrivateX11 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
XColor *xpalette;
gint nlookup;
gint i;
nlookup = 0;
xpalette = g_new (XColor, colormap->size);
for (i = 0; i < colormap->size; i++)
{
if (!private->info || private->info[i].ref_count == 0)
{
xpalette[nlookup].pixel = i;
xpalette[nlookup].red = 0;
xpalette[nlookup].green = 0;
xpalette[nlookup].blue = 0;
nlookup++;
}
}
XQueryColors (GDK_SCREEN_XDISPLAY (private->screen),
private->xcolormap, xpalette, nlookup);
for (i = 0; i < nlookup; i++)
{
gulong pixel = xpalette[i].pixel;
colormap->colors[pixel].pixel = pixel;
colormap->colors[pixel].red = xpalette[i].red;
colormap->colors[pixel].green = xpalette[i].green;
colormap->colors[pixel].blue = xpalette[i].blue;
}
g_free (xpalette);
}
static void
gdk_colormap_sync_direct_color (GdkColormap *colormap)
{
GdkColormapPrivateX11 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
GdkVisual *visual = colormap->visual;
XColor *xpalette;
gint i;
xpalette = g_new (XColor, colormap->size);
for (i = 0; i < colormap->size; i++)
{
xpalette[i].pixel =
(((i << visual->red_shift) & visual->red_mask) |
((i << visual->green_shift) & visual->green_mask) |
((i << visual->blue_shift) & visual->blue_mask));
}
XQueryColors (GDK_SCREEN_XDISPLAY (private->screen),
private->xcolormap, xpalette, colormap->size);
for (i = 0; i < colormap->size; i++)
{
colormap->colors[i].pixel = xpalette[i].pixel;
colormap->colors[i].red = xpalette[i].red;
colormap->colors[i].green = xpalette[i].green;
colormap->colors[i].blue = xpalette[i].blue;
}
g_free (xpalette);
}
#define MIN_SYNC_TIME 2
static void
gdk_colormap_sync (GdkColormap *colormap,
gboolean force)
{
time_t current_time;
GdkColormapPrivateX11 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
g_return_if_fail (GDK_IS_COLORMAP (colormap));
if (private->screen->closed)
return;
current_time = time (NULL);
if (!force && ((current_time - private->last_sync_time) < MIN_SYNC_TIME))
return;
private->last_sync_time = current_time;
if (colormap->visual->type == GDK_VISUAL_DIRECT_COLOR)
gdk_colormap_sync_direct_color (colormap);
else
gdk_colormap_sync_palette (colormap);
}
/**
* gdk_screen_get_system_colormap:
* @screen: a #GdkScreen
*
* Gets the system's default colormap for @screen
*
* Returns: the default colormap for @screen.
*/
GdkColormap *
gdk_screen_get_system_colormap (GdkScreen *screen)
{
GdkColormap *colormap = NULL;
GdkColormapPrivateX11 *private;
GdkScreenX11 *screen_x11;
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
screen_x11 = GDK_SCREEN_X11 (screen);
if (screen_x11->system_colormap)
return screen_x11->system_colormap;
colormap = g_object_new (GDK_TYPE_COLORMAP, NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
private->screen = screen;
colormap->visual = gdk_screen_get_system_visual (screen);
private->xcolormap = DefaultColormapOfScreen (screen_x11->xscreen);
private->private_val = FALSE;
private->hash = NULL;
private->last_sync_time = 0;
private->info = NULL;
colormap->colors = NULL;
colormap->size = colormap->visual->colormap_size;
switch (colormap->visual->type)
{
case GDK_VISUAL_GRAYSCALE:
case GDK_VISUAL_PSEUDO_COLOR:
private->info = g_new0 (GdkColorInfo, colormap->size);
private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
(GEqualFunc) gdk_color_equal);
/* Fall through */
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_STATIC_COLOR:
case GDK_VISUAL_DIRECT_COLOR:
colormap->colors = g_new (GdkColor, colormap->size);
gdk_colormap_sync (colormap, TRUE);
case GDK_VISUAL_TRUE_COLOR:
break;
}
gdk_colormap_add (colormap);
screen_x11->system_colormap = colormap;
return colormap;
}
/**
* gdk_colormap_get_system_size:
*
* Returns the size of the system's default colormap.
* (See the description of struct #GdkColormap for an
* explanation of the size of a colormap.)
*
* Return value: the size of the system's default colormap.
**/
gint
gdk_colormap_get_system_size (void)
{
return DisplayCells (GDK_SCREEN_XDISPLAY (gdk_screen_get_default()),
GDK_SCREEN_X11 (gdk_screen_get_default())->screen_num);
}
/**
* gdk_colormap_change:
* @colormap: a #GdkColormap.
* @ncolors: the number of colors to change.
*
* Changes the value of the first @ncolors in a private colormap
* to match the values in the <structfield>colors</structfield>
* array in the colormap. This function is obsolete and
* should not be used. See gdk_color_change().
**/
void
gdk_colormap_change (GdkColormap *colormap,
gint ncolors)
{
GdkColormapPrivateX11 *private;
GdkVisual *visual;
XColor *palette;
Display *xdisplay;
gint shift;
int max_colors;
int size;
int i;
g_return_if_fail (GDK_IS_COLORMAP (colormap));
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if (private->screen->closed)
return;
xdisplay = GDK_SCREEN_XDISPLAY (private->screen);
palette = g_new (XColor, ncolors);
switch (colormap->visual->type)
{
case GDK_VISUAL_GRAYSCALE:
case GDK_VISUAL_PSEUDO_COLOR:
for (i = 0; i < ncolors; i++)
{
palette[i].pixel = colormap->colors[i].pixel;
palette[i].red = colormap->colors[i].red;
palette[i].green = colormap->colors[i].green;
palette[i].blue = colormap->colors[i].blue;
palette[i].flags = DoRed | DoGreen | DoBlue;
}
XStoreColors (xdisplay, private->xcolormap, palette, ncolors);
break;
case GDK_VISUAL_DIRECT_COLOR:
visual = colormap->visual;
shift = visual->red_shift;
max_colors = 1 << visual->red_prec;
size = (ncolors < max_colors) ? (ncolors) : (max_colors);
for (i = 0; i < size; i++)
{
palette[i].pixel = i << shift;
palette[i].red = colormap->colors[i].red;
palette[i].flags = DoRed;
}
XStoreColors (xdisplay, private->xcolormap, palette, size);
shift = visual->green_shift;
max_colors = 1 << visual->green_prec;
size = (ncolors < max_colors) ? (ncolors) : (max_colors);
for (i = 0; i < size; i++)
{
palette[i].pixel = i << shift;
palette[i].green = colormap->colors[i].green;
palette[i].flags = DoGreen;
}
XStoreColors (xdisplay, private->xcolormap, palette, size);
shift = visual->blue_shift;
max_colors = 1 << visual->blue_prec;
size = (ncolors < max_colors) ? (ncolors) : (max_colors);
for (i = 0; i < size; i++)
{
palette[i].pixel = i << shift;
palette[i].blue = colormap->colors[i].blue;
palette[i].flags = DoBlue;
}
XStoreColors (xdisplay, private->xcolormap, palette, size);
break;
default:
break;
}
g_free (palette);
}
/**
* gdk_colors_alloc:
* @colormap: a #GdkColormap.
* @contiguous: if %TRUE, the colors should be allocated
* in contiguous color cells.
* @planes: an array in which to store the plane masks.
* @nplanes: the number of planes to allocate. (Or zero,
* to indicate that the color allocation should not be planar.)
* @pixels: an array into which to store allocated pixel values.
* @npixels: the number of pixels in each plane to allocate.
*
* Allocates colors from a colormap. This function
* is obsolete. See gdk_colormap_alloc_colors().
* For full documentation of the fields, see
* the Xlib documentation for <function>XAllocColorCells()</function>.
*
* Return value:
**/
gboolean
gdk_colors_alloc (GdkColormap *colormap,
gboolean contiguous,
gulong *planes,
gint nplanes,
gulong *pixels,
gint npixels)
{
GdkColormapPrivateX11 *private;
gint return_val;
gint i;
g_return_val_if_fail (GDK_IS_COLORMAP (colormap), 0);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if (private->screen->closed)
return FALSE;
return_val = XAllocColorCells (GDK_SCREEN_XDISPLAY (private->screen),
private->xcolormap,contiguous, planes,
nplanes, pixels, npixels);
if (return_val)
{
for (i=0; i<npixels; i++)
{
private->info[pixels[i]].ref_count++;
private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
}
}
return return_val != 0;
}
/* This is almost identical to gdk_colormap_free_colors.
* Keep them in sync!
*/
/**
* gdk_colors_free:
* @colormap: a #GdkColormap.
* @pixels: the pixel values of the colors to free.
* @npixels: the number of values in @pixels.
* @planes: the plane masks for all planes to free, OR'd together.
*
* Frees colors allocated with gdk_colors_alloc(). This
* function is obsolete. See gdk_colormap_free_colors().
**/
void
gdk_colors_free (GdkColormap *colormap,
gulong *pixels,
gint npixels,
gulong planes)
{
GdkColormapPrivateX11 *private;
gulong *pixels_to_free;
gint npixels_to_free = 0;
gint i;
g_return_if_fail (GDK_IS_COLORMAP (colormap));
g_return_if_fail (pixels != NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if ((colormap->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
(colormap->visual->type != GDK_VISUAL_GRAYSCALE))
return;
pixels_to_free = g_new (gulong, npixels);
for (i=0; i<npixels; i++)
{
gulong pixel = pixels[i];
if (private->info[pixel].ref_count)
{
private->info[pixel].ref_count--;
if (private->info[pixel].ref_count == 0)
{
pixels_to_free[npixels_to_free++] = pixel;
if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
g_hash_table_remove (private->hash, &colormap->colors[pixel]);
private->info[pixel].flags = 0;
}
}
}
if (npixels_to_free && !private->screen->closed)
XFreeColors (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
pixels_to_free, npixels_to_free, planes);
g_free (pixels_to_free);
}
/* This is almost identical to gdk_colors_free.
* Keep them in sync!
*/
/**
* gdk_colormap_free_colors:
* @colormap: a #GdkColormap.
* @colors: the colors to free.
* @ncolors: the number of colors in @colors.
*
* Frees previously allocated colors.
**/
void
gdk_colormap_free_colors (GdkColormap *colormap,
GdkColor *colors,
gint ncolors)
{
GdkColormapPrivateX11 *private;
gulong *pixels;
gint npixels = 0;
gint i;
g_return_if_fail (GDK_IS_COLORMAP (colormap));
g_return_if_fail (colors != NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if ((colormap->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
(colormap->visual->type != GDK_VISUAL_GRAYSCALE))
return;
pixels = g_new (gulong, ncolors);
for (i=0; i<ncolors; i++)
{
gulong pixel = colors[i].pixel;
if (private->info[pixel].ref_count)
{
private->info[pixel].ref_count--;
if (private->info[pixel].ref_count == 0)
{
pixels[npixels++] = pixel;
if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
g_hash_table_remove (private->hash, &colormap->colors[pixel]);
private->info[pixel].flags = 0;
}
}
}
if (npixels && !private->screen->closed)
XFreeColors (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
pixels, npixels, 0);
g_free (pixels);
}
/********************
* Color allocation *
********************/
/* Try to allocate a single color using XAllocColor. If it succeeds,
* cache the result in our colormap, and store in ret.
*/
static gboolean
gdk_colormap_alloc1 (GdkColormap *colormap,
GdkColor *color,
GdkColor *ret)
{
GdkColormapPrivateX11 *private;
XColor xcolor;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
xcolor.red = color->red;
xcolor.green = color->green;
xcolor.blue = color->blue;
xcolor.pixel = color->pixel;
xcolor.flags = DoRed | DoGreen | DoBlue;
if (XAllocColor (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap, &xcolor))
{
ret->pixel = xcolor.pixel;
ret->red = xcolor.red;
ret->green = xcolor.green;
ret->blue = xcolor.blue;
if (ret->pixel < colormap->size)
{
if (private->info[ret->pixel].ref_count) /* got a duplicate */
{
XFreeColors (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
&xcolor.pixel, 1, 0);
}
else
{
colormap->colors[ret->pixel] = *color;
colormap->colors[ret->pixel].pixel = ret->pixel;
private->info[ret->pixel].ref_count = 1;
g_hash_table_insert (private->hash,
&colormap->colors[ret->pixel],
&colormap->colors[ret->pixel]);
}
}
return TRUE;
}
else
{
return FALSE;
}
}
static gint
gdk_colormap_alloc_colors_writeable (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateX11 *private;
gulong *pixels;
Status status;
gint i, index;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if (private->private_val)
{
index = 0;
for (i=0; i<ncolors; i++)
{
while ((index < colormap->size) && (private->info[index].ref_count != 0))
index++;
if (index < colormap->size)
{
colors[i].pixel = index;
success[i] = TRUE;
private->info[index].ref_count++;
private->info[i].flags |= GDK_COLOR_WRITEABLE;
}
else
break;
}
return i;
}
else
{
pixels = g_new (gulong, ncolors);
/* Allocation of a writeable color cells */
status = XAllocColorCells (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
FALSE, NULL, 0, pixels, ncolors);
if (status)
{
for (i=0; i<ncolors; i++)
{
colors[i].pixel = pixels[i];
private->info[pixels[i]].ref_count++;
private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
}
}
g_free (pixels);
return status ? ncolors : 0;
}
}
static gint
gdk_colormap_alloc_colors_private (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateX11 *private;
gint i, index;
XColor *store = g_new (XColor, ncolors);
gint nstore = 0;
gint nremaining = 0;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
index = -1;
/* First, store the colors we have room for */
index = 0;
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
while ((index < colormap->size) && (private->info[index].ref_count != 0))
index++;
if (index < colormap->size)
{
store[nstore].red = colors[i].red;
store[nstore].blue = colors[i].blue;
store[nstore].green = colors[i].green;
store[nstore].pixel = index;
nstore++;
success[i] = TRUE;
colors[i].pixel = index;
private->info[index].ref_count++;
}
else
nremaining++;
}
}
XStoreColors (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
store, nstore);
g_free (store);
if (nremaining > 0 && best_match)
{
/* Get best matches for remaining colors */
gchar *available = g_new (gchar, colormap->size);
for (i = 0; i < colormap->size; i++)
available[i] = TRUE;
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
index = gdk_colormap_match_color (colormap,
&colors[i],
available);
if (index != -1)
{
colors[i] = colormap->colors[index];
private->info[index].ref_count++;
success[i] = TRUE;
nremaining--;
}
}
}
g_free (available);
}
return (ncolors - nremaining);
}
static gint
gdk_colormap_alloc_colors_shared (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateX11 *private;
gint i, index;
gint nremaining = 0;
gint nfailed = 0;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
index = -1;
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
if (gdk_colormap_alloc1 (colormap, &colors[i], &colors[i]))
success[i] = TRUE;
else
nremaining++;
}
}
if (nremaining > 0 && best_match)
{
gchar *available = g_new (gchar, colormap->size);
for (i = 0; i < colormap->size; i++)
available[i] = ((private->info[i].ref_count == 0) ||
!(private->info[i].flags & GDK_COLOR_WRITEABLE));
gdk_colormap_sync (colormap, FALSE);
while (nremaining > 0)
{
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
index = gdk_colormap_match_color (colormap, &colors[i], available);
if (index != -1)
{
if (private->info[index].ref_count)
{
private->info[index].ref_count++;
colors[i] = colormap->colors[index];
success[i] = TRUE;
nremaining--;
}
else
{
if (gdk_colormap_alloc1 (colormap,
&colormap->colors[index],
&colors[i]))
{
success[i] = TRUE;
nremaining--;
break;
}
else
{
available[index] = FALSE;
}
}
}
else
{
nfailed++;
nremaining--;
success[i] = 2; /* flag as permanent failure */
}
}
}
}
g_free (available);
}
/* Change back the values we flagged as permanent failures */
if (nfailed > 0)
{
for (i=0; i<ncolors; i++)
if (success[i] == 2)
success[i] = FALSE;
nremaining = nfailed;
}
return (ncolors - nremaining);
}
static gint
gdk_colormap_alloc_colors_pseudocolor (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateX11 *private;
GdkColor *lookup_color;
gint i;
gint nremaining = 0;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
/* Check for an exact match among previously allocated colors */
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
lookup_color = g_hash_table_lookup (private->hash, &colors[i]);
if (lookup_color)
{
private->info[lookup_color->pixel].ref_count++;
colors[i].pixel = lookup_color->pixel;
success[i] = TRUE;
}
else
nremaining++;
}
}
/* If that failed, we try to allocate a new color, or approxmiate
* with what we can get if best_match is TRUE.
*/
if (nremaining > 0)
{
if (private->private_val)
return gdk_colormap_alloc_colors_private (colormap, colors, ncolors, writeable, best_match, success);
else
return gdk_colormap_alloc_colors_shared (colormap, colors, ncolors, writeable, best_match, success);
}
else
return 0;
}
/**
* gdk_colormap_alloc_colors:
* @colormap: a #GdkColormap.
* @colors: The color values to allocate. On return, the pixel
* values for allocated colors will be filled in.
* @ncolors: The number of colors in @colors.
* @writeable: If %TRUE, the colors are allocated writeable
* (their values can later be changed using gdk_color_change()).
* Writeable colors cannot be shared between applications.
* @best_match: If %TRUE, GDK will attempt to do matching against
* existing colors if the colors cannot be allocated as requested.
* @success: An array of length @ncolors. On return, this
* indicates whether the corresponding color in @colors was
* sucessfully allocated or not.
*
* Allocates colors from a colormap.
*
* Return value: The number of colors that were not sucessfully
* allocated.
**/
gint
gdk_colormap_alloc_colors (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateX11 *private;
GdkVisual *visual;
gint i;
gint nremaining = 0;
XColor xcolor;
g_return_val_if_fail (GDK_IS_COLORMAP (colormap), ncolors);
g_return_val_if_fail (colors != NULL, ncolors);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if (private->screen->closed)
return ncolors;
for (i=0; i<ncolors; i++)
{
success[i] = FALSE;
}
switch (colormap->visual->type)
{
case GDK_VISUAL_PSEUDO_COLOR:
case GDK_VISUAL_GRAYSCALE:
if (writeable)
return gdk_colormap_alloc_colors_writeable (colormap, colors, ncolors,
writeable, best_match, success);
else
return gdk_colormap_alloc_colors_pseudocolor (colormap, colors, ncolors,
writeable, best_match, success);
break;
case GDK_VISUAL_DIRECT_COLOR:
case GDK_VISUAL_TRUE_COLOR:
visual = colormap->visual;
for (i=0; i<ncolors; i++)
{
colors[i].pixel = (((colors[i].red >> (16 - visual->red_prec)) << visual->red_shift) +
((colors[i].green >> (16 - visual->green_prec)) << visual->green_shift) +
((colors[i].blue >> (16 - visual->blue_prec)) << visual->blue_shift));
success[i] = TRUE;
}
break;
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_STATIC_COLOR:
for (i=0; i<ncolors; i++)
{
xcolor.red = colors[i].red;
xcolor.green = colors[i].green;
xcolor.blue = colors[i].blue;
xcolor.pixel = colors[i].pixel;
xcolor.flags = DoRed | DoGreen | DoBlue;
if (XAllocColor (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap, &xcolor))
{
colors[i].pixel = xcolor.pixel;
success[i] = TRUE;
}
else
nremaining++;
}
break;
}
return nremaining;
}
/**
* gdk_colormap_query_color:
* @colormap: a #GdkColormap
* @pixel: pixel value in hardware display format
* @result: #GdkColor with red, green, blue fields initialized
*
* Locates the RGB color in @colormap corresponding to the given
* hardware pixel @pixel. @pixel must be a valid pixel in the
* colormap; it's a programmer error to call this function with a
* pixel which is not in the colormap. Hardware pixels are normally
* obtained from gdk_colormap_alloc_colors(), or from a #GdkImage. (A
* #GdkImage contains image data in hardware format, a #GdkPixbuf
* contains image data in a canonical 24-bit RGB format.)
*
* This function is rarely useful; it's used for example to
* implement the eyedropper feature in #GtkColorSelection.
*
**/
void
gdk_colormap_query_color (GdkColormap *colormap,
gulong pixel,
GdkColor *result)
{
XColor xcolor;
GdkVisual *visual;
GdkColormapPrivateX11 *private;
g_return_if_fail (GDK_IS_COLORMAP (colormap));
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
visual = gdk_colormap_get_visual (colormap);
switch (visual->type) {
case GDK_VISUAL_DIRECT_COLOR:
case GDK_VISUAL_TRUE_COLOR:
result->red = 65535. * (double)((pixel & visual->red_mask) >> visual->red_shift) / ((1 << visual->red_prec) - 1);
result->green = 65535. * (double)((pixel & visual->green_mask) >> visual->green_shift) / ((1 << visual->green_prec) - 1);
result->blue = 65535. * (double)((pixel & visual->blue_mask) >> visual->blue_shift) / ((1 << visual->blue_prec) - 1);
break;
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_GRAYSCALE:
result->red = result->green = result->blue = 65535. * (double)pixel/((1<<visual->depth) - 1);
break;
case GDK_VISUAL_STATIC_COLOR:
xcolor.pixel = pixel;
if (!private->screen->closed)
{
XQueryColor (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap, &xcolor);
result->red = xcolor.red;
result->green = xcolor.green;
result->blue = xcolor.blue;
}
else
result->red = result->green = result->blue = 0;
break;
case GDK_VISUAL_PSEUDO_COLOR:
g_return_if_fail (pixel < colormap->size);
result->red = colormap->colors[pixel].red;
result->green = colormap->colors[pixel].green;
result->blue = colormap->colors[pixel].blue;
break;
default:
g_assert_not_reached ();
break;
}
}
/**
* gdk_color_change:
* @colormap: a #GdkColormap.
* @color: a #GdkColor, with the color to change
* in the <structfield>pixel</structfield> field,
* and the new value in the remaining fields.
*
* Changes the value of a color that has already
* been allocated. If @colormap is not a private
* colormap, then the color must have been allocated
* using gdk_colormap_alloc_colors() with the
* @writeable set to %TRUE.
*
* Return value: %TRUE if the color was successfully changed.
**/
gboolean
gdk_color_change (GdkColormap *colormap,
GdkColor *color)
{
GdkColormapPrivateX11 *private;
XColor xcolor;
g_return_val_if_fail (GDK_IS_COLORMAP (colormap), FALSE);
g_return_val_if_fail (color != NULL, FALSE);
xcolor.pixel = color->pixel;
xcolor.red = color->red;
xcolor.green = color->green;
xcolor.blue = color->blue;
xcolor.flags = DoRed | DoGreen | DoBlue;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if (!private->screen->closed)
XStoreColor (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap, &xcolor);
return TRUE;
}
/**
* gdk_x11_colormap_foreign_new:
* @visual: a #GdkVisual
* @xcolormap: The XID of a colormap with visual @visual
*
* If xcolormap refers to a colormap previously known to GTK+,
* returns a new reference to the existing #GdkColormap object,
* otherwise creates a new GdkColormap object and returns that
*
* Return value: the #GdkColormap object for @xcolormap.
* Free with g_object_unref(). Note that for colormap created
* with gdk_x11_colormap_foreign_new(), unref'ing the last
* reference to the object will only free the #GdkColoramp
* object and not call XFreeColormap()
**/
GdkColormap *
gdk_x11_colormap_foreign_new (GdkVisual *visual,
Colormap xcolormap)
{
GdkColormap *colormap;
GdkScreen *screen;
GdkColormapPrivateX11 *private;
g_return_val_if_fail (GDK_IS_VISUAL (visual), NULL);
g_return_val_if_fail (xcolormap != None, NULL);
screen = gdk_visual_get_screen (visual);
if (xcolormap == DefaultColormap (GDK_SCREEN_XDISPLAY (screen),
GDK_SCREEN_XNUMBER (screen)));
return g_object_ref (gdk_screen_get_system_colormap (screen));
colormap = gdk_colormap_lookup (screen, xcolormap);
if (colormap)
return g_object_ref (colormap);
colormap = g_object_new (GDK_TYPE_COLORMAP, NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
colormap->visual = visual;
private->screen = screen;
private->xcolormap = xcolormap;
private->xdisplay = GDK_SCREEN_XDISPLAY (screen);
private->private_val = FALSE;
colormap->size = visual->colormap_size;
switch (colormap->visual->type)
{
case GDK_VISUAL_GRAYSCALE:
case GDK_VISUAL_PSEUDO_COLOR:
private->info = g_new0 (GdkColorInfo, colormap->size);
private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
(GEqualFunc) gdk_color_equal);
/* Fall through */
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_STATIC_COLOR:
case GDK_VISUAL_DIRECT_COLOR:
colormap->colors = g_new (GdkColor, colormap->size);
gdk_colormap_sync (colormap, TRUE);
case GDK_VISUAL_TRUE_COLOR:
break;
}
gdk_colormap_add (colormap);
return colormap;
}
/**
* gdkx_colormap_get:
* @xcolormap: the XID of a colormap for the default screen.
*
* Returns a #GdkColormap corresponding to a X colormap;
* this function only works if the colormap is already
* known to GTK+ (a colormap created by GTK+ or the default
* colormap for the screen), since GTK+
*
* Always use gdk_x11_colormap_foreign_new() instead.
*
* Return value: the existing #GdkColormap object if it was
* already known to GTK+, otherwise warns and return
* %NULL.
**/
GdkColormap*
gdkx_colormap_get (Colormap xcolormap)
{
GdkScreen *screen = gdk_screen_get_default ();
GdkColormap *colormap;
if (xcolormap == DefaultColormap (GDK_SCREEN_XDISPLAY (screen),
GDK_SCREEN_XNUMBER (screen)));
return g_object_ref (gdk_screen_get_system_colormap (screen));
colormap = gdk_colormap_lookup (screen, xcolormap);
if (colormap)
return g_object_ref (colormap);
g_warning ("Colormap passed to gdkx_colormap_get\n"
"does not previously exist");
return NULL;
}
static gint
gdk_colormap_match_color (GdkColormap *cmap,
GdkColor *color,
const gchar *available)
{
GdkColor *colors;
guint sum, max;
gint rdiff, gdiff, bdiff;
gint i, index;
g_return_val_if_fail (cmap != NULL, 0);
g_return_val_if_fail (color != NULL, 0);
colors = cmap->colors;
max = 3 * (65536);
index = -1;
for (i = 0; i < cmap->size; i++)
{
if ((!available) || (available && available[i]))
{
rdiff = (color->red - colors[i].red);
gdiff = (color->green - colors[i].green);
bdiff = (color->blue - colors[i].blue);
sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
if (sum < max)
{
index = i;
max = sum;
}
}
}
return index;
}
static GdkColormap*
gdk_colormap_lookup (GdkScreen *screen,
Colormap xcolormap)
{
GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
if (screen_x11->colormap_hash)
return g_hash_table_lookup (screen_x11->colormap_hash, &xcolormap);
else
return NULL;
}
static void
gdk_colormap_add (GdkColormap *cmap)
{
GdkScreenX11 *screen_x11;
GdkColormapPrivateX11 *private;
private = GDK_COLORMAP_PRIVATE_DATA (cmap);
screen_x11 = GDK_SCREEN_X11 (private->screen);
if (!screen_x11->colormap_hash)
screen_x11->colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
(GEqualFunc) gdk_colormap_equal);
g_hash_table_insert (screen_x11->colormap_hash, &private->xcolormap, cmap);
}
static void
gdk_colormap_remove (GdkColormap *cmap)
{
GdkScreenX11 *screen_x11;
GdkColormapPrivateX11 *private;
private = GDK_COLORMAP_PRIVATE_DATA (cmap);
screen_x11 = GDK_SCREEN_X11 (private->screen);
if (screen_x11->colormap_hash)
g_hash_table_remove (screen_x11->colormap_hash, &private->xcolormap);
}
static guint
gdk_colormap_hash (Colormap *colormap)
{
return *colormap;
}
static gboolean
gdk_colormap_equal (Colormap *a,
Colormap *b)
{
return (*a == *b);
}
/**
* gdk_x11_colormap_get_xdisplay:
* @colormap: a #GdkColormap.
*
* Returns the display of a #GdkColormap.
*
* Return value: an Xlib <type>Display*</type>.
**/
Display *
gdk_x11_colormap_get_xdisplay (GdkColormap *colormap)
{
GdkColormapPrivateX11 *private;
g_return_val_if_fail (GDK_IS_COLORMAP (colormap), NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
return private->xdisplay;
}
/**
* gdk_x11_colormap_get_xcolormap:
* @colormap: a #GdkColormap.
*
* Returns the X colormap belonging to a #GdkColormap.
*
* Return value: an Xlib <type>Colormap</type>.
**/
Colormap
gdk_x11_colormap_get_xcolormap (GdkColormap *colormap)
{
GdkColormapPrivateX11 *private;
g_return_val_if_fail (GDK_IS_COLORMAP (colormap), None);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if (private->screen->closed)
return None;
else
return private->xcolormap;
}
/**
* gdk_colormap_get_screen:
* @cmap: a #GdkColormap
*
* Gets the screen for which this colormap was created.
*
* Return value: the screen for which this colormap was created.
**/
GdkScreen *
gdk_colormap_get_screen (GdkColormap *cmap)
{
g_return_val_if_fail (GDK_IS_COLORMAP (cmap), NULL);
return GDK_COLORMAP_PRIVATE_DATA (cmap)->screen;
}