forked from AuroraMiddleware/gtk
47ee996198
Also add a priv pointer to GdkVisual and use it for the GdkVisualPrivate structure. Then Make GdkVisualPrivate actually private to gdkvisual-x11.c and make other callers use proper function calls to access it.
1236 lines
31 KiB
C
1236 lines
31 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 "config.h"
|
|
#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;
|
|
gint private_val;
|
|
|
|
GHashTable *hash;
|
|
GdkColorInfo *info;
|
|
time_t last_sync_time;
|
|
|
|
gboolean foreign;
|
|
};
|
|
|
|
#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_finalize (GObject *object);
|
|
|
|
static void gdk_colormap_change (GdkColormap *colormap,
|
|
gint ncolors);
|
|
|
|
G_DEFINE_TYPE (GdkColormap, gdk_colormap, G_TYPE_OBJECT)
|
|
|
|
static void
|
|
gdk_colormap_init (GdkColormap *colormap)
|
|
{
|
|
GdkColormapPrivateX11 *private;
|
|
|
|
private = G_TYPE_INSTANCE_GET_PRIVATE (colormap, GDK_TYPE_COLORMAP,
|
|
GdkColormapPrivateX11);
|
|
|
|
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);
|
|
|
|
object_class->finalize = gdk_colormap_finalize;
|
|
|
|
g_type_class_add_private (object_class, sizeof (GdkColormapPrivateX11));
|
|
}
|
|
|
|
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 && !private->foreign)
|
|
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_OBJECT_CLASS (gdk_colormap_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 (GDK_IS_VISUAL (visual), 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 = gdk_x11_visual_get_xvisual (visual);
|
|
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)
|
|
{
|
|
GdkVisual *system_visual;
|
|
XColor *default_colors;
|
|
gint n_default_colors;
|
|
|
|
system_visual = gdk_screen_get_system_visual (private->screen);
|
|
n_default_colors = MIN (system_visual->colormap_size, colormap->size);
|
|
|
|
default_colors = g_new (XColor, colormap->size);
|
|
|
|
for (i = 0; i < n_default_colors; i++)
|
|
default_colors[i].pixel = i;
|
|
|
|
XQueryColors (xdisplay,
|
|
DefaultColormapOfScreen (GDK_SCREEN_X11 (private->screen)->xscreen),
|
|
default_colors, n_default_colors);
|
|
|
|
for (i = 0; i < n_default_colors; 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, n_default_colors);
|
|
|
|
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: (transfer none): the default colormap for @screen.
|
|
*
|
|
* Since: 2.2
|
|
*/
|
|
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_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.
|
|
*/
|
|
static 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_colormap_free_colors:
|
|
* @colormap: a #GdkColormap.
|
|
* @colors: the colors to free.
|
|
* @n_colors: the number of colors in @colors.
|
|
*
|
|
* Frees previously allocated colors.
|
|
**/
|
|
void
|
|
gdk_colormap_free_colors (GdkColormap *colormap,
|
|
const GdkColor *colors,
|
|
gint n_colors)
|
|
{
|
|
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, n_colors);
|
|
|
|
for (i = 0; i < n_colors; 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->private_val && !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,
|
|
const 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);
|
|
private->info[ret->pixel].ref_count++;
|
|
}
|
|
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 ncolors - 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];
|
|
success[i] = TRUE;
|
|
private->info[pixels[i]].ref_count++;
|
|
private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
|
|
}
|
|
}
|
|
|
|
g_free (pixels);
|
|
|
|
return status ? 0 : ncolors;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
/* 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;
|
|
store[nstore].flags = DoRed | DoGreen | DoBlue;
|
|
nstore++;
|
|
|
|
success[i] = TRUE;
|
|
colors[i].pixel = index;
|
|
|
|
colormap->colors[index] = colors[i];
|
|
private->info[index].ref_count++;
|
|
|
|
g_hash_table_insert (private->hash,
|
|
&colormap->colors[index],
|
|
&colormap->colors[index]);
|
|
}
|
|
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] = !(private->info[i].flags & GDK_COLOR_WRITEABLE);
|
|
|
|
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 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);
|
|
|
|
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 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.
|
|
* @n_colors: The number of colors in @colors.
|
|
* @writeable: this parameter has no effect, and it's here for mere
|
|
* compatibility.
|
|
* @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
|
|
* successfully allocated or not.
|
|
*
|
|
* Allocates colors from a colormap.
|
|
*
|
|
* Return value: The number of colors that were not successfully
|
|
* allocated.
|
|
**/
|
|
gint
|
|
gdk_colormap_alloc_colors (GdkColormap *colormap,
|
|
GdkColor *colors,
|
|
gint n_colors,
|
|
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), n_colors);
|
|
g_return_val_if_fail (colors != NULL, n_colors);
|
|
g_return_val_if_fail (success != NULL, n_colors);
|
|
|
|
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
|
|
|
|
if (private->screen->closed)
|
|
return n_colors;
|
|
|
|
for (i = 0; i < n_colors; 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, n_colors,
|
|
writeable, best_match, success);
|
|
else
|
|
return gdk_colormap_alloc_colors_pseudocolor (colormap, colors, n_colors,
|
|
writeable, best_match, success);
|
|
break;
|
|
|
|
case GDK_VISUAL_DIRECT_COLOR:
|
|
case GDK_VISUAL_TRUE_COLOR:
|
|
visual = colormap->visual;
|
|
|
|
for (i = 0; i < n_colors; i++)
|
|
{
|
|
/* If bits not used for color are used for something other than padding,
|
|
* it's likely alpha, so we set them to 1s.
|
|
*/
|
|
guint padding, unused;
|
|
|
|
/* Shifting by >= width-of-type isn't defined in C */
|
|
if (visual->depth >= 32)
|
|
padding = 0;
|
|
else
|
|
padding = ((~(guint32)0)) << visual->depth;
|
|
|
|
unused = ~ (visual->red_mask | visual->green_mask | visual->blue_mask | padding);
|
|
|
|
colors[i].pixel = (unused +
|
|
((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 < n_colors; 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_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()
|
|
*
|
|
* Since: 2.2
|
|
**/
|
|
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->private_val = FALSE;
|
|
private->foreign = TRUE;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
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 GDK_SCREEN_XDISPLAY (private->screen);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Since: 2.2
|
|
**/
|
|
GdkScreen *
|
|
gdk_colormap_get_screen (GdkColormap *cmap)
|
|
{
|
|
g_return_val_if_fail (GDK_IS_COLORMAP (cmap), NULL);
|
|
|
|
return GDK_COLORMAP_PRIVATE_DATA (cmap)->screen;
|
|
}
|