gdkcolor-x11.c 38.4 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* 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
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * 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/. 
 */

27
#include <time.h>
Owen Taylor's avatar
Started  
Owen Taylor committed
28 29

#include "gdkcolor.h"
30
#include "gdkinternals.h"
31
#include "gdkx.h"
32
#include "gdkprivate-x11.h"
33
#include "gdkscreen-x11.h"
Elliot Lee's avatar
Elliot Lee committed
34

35 36 37 38
typedef struct _GdkColormapPrivateX11  GdkColormapPrivateX11;

struct _GdkColormapPrivateX11
{
39
  GdkScreen *screen;
40 41 42 43 44 45 46
  Colormap xcolormap;
  Display *xdisplay;
  gint private_val;

  GHashTable *hash;
  GdkColorInfo *info;
  time_t last_sync_time;
47 48

  guint foreign : 1;
49 50
};

51 52
#define GDK_COLORMAP_PRIVATE_DATA(cmap) ((GdkColormapPrivateX11 *) GDK_COLORMAP (cmap)->windowing_data)

53 54 55 56 57
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);
58 59 60 61

static GdkColormap *gdk_colormap_lookup   (GdkScreen   *screen,
					   Colormap     xcolormap);

62 63 64
static guint    gdk_colormap_hash        (Colormap    *cmap);
static gboolean gdk_colormap_equal       (Colormap    *a,
					  Colormap    *b);
65 66
static void     gdk_colormap_sync        (GdkColormap *colormap,
                                          gboolean     force);
Elliot Lee's avatar
Elliot Lee committed
67

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
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",
96
                                            &object_info, 0);
97 98 99 100 101 102 103 104 105 106 107 108 109 110
    }
  
  return object_type;
}

static void
gdk_colormap_init (GdkColormap *colormap)
{
  GdkColormapPrivateX11 *private;

  private = g_new (GdkColormapPrivateX11, 1);

  colormap->windowing_data = private;
  
111
  private->screen = NULL;
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  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);

138 139
  if (!private->screen->closed)
    XFreeColormap (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap);
140 141 142 143 144 145 146 147 148

  if (private->hash)
    g_hash_table_destroy (private->hash);
  
  g_free (private->info);
  g_free (colormap->colors);
  
  G_OBJECT_CLASS (parent_class)->finalize (object);
}
Elliot Lee's avatar
Elliot Lee committed
149

Matthias Clasen's avatar
Matthias Clasen committed
150 151 152 153 154 155 156 157 158 159 160
/**
 * 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.
 **/
Elliot Lee's avatar
Elliot Lee committed
161 162
GdkColormap*
gdk_colormap_new (GdkVisual *visual,
Matthias Clasen's avatar
Matthias Clasen committed
163
		  gboolean   allocate)
Elliot Lee's avatar
Elliot Lee committed
164 165
{
  GdkColormap *colormap;
166
  GdkColormapPrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
167
  Visual *xvisual;
168 169
  Display *xdisplay;
  Window xrootwin;
Elliot Lee's avatar
Elliot Lee committed
170 171 172
  int size;
  int i;

173 174 175 176
  /* FIXME when object properties settle down, there needs to be some
   * kind of default construction (and construct-only arguments)
   */
  
Elliot Lee's avatar
Elliot Lee committed
177 178
  g_return_val_if_fail (visual != NULL, NULL);

179
  colormap = g_object_new (GDK_TYPE_COLORMAP, NULL);
180
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
Elliot Lee's avatar
Elliot Lee committed
181

182
  colormap->visual = visual;
183
  private->screen = gdk_visual_get_screen (visual);
184
  
Elliot Lee's avatar
Elliot Lee committed
185
  xvisual = ((GdkVisualPrivate*) visual)->xvisual;
186 187
  xdisplay = GDK_SCREEN_XDISPLAY (private->screen);
  xrootwin = GDK_SCREEN_XROOTWIN (private->screen);
Elliot Lee's avatar
Elliot Lee committed
188

189 190
  colormap->size = visual->colormap_size;

Elliot Lee's avatar
Elliot Lee committed
191 192 193 194
  switch (visual->type)
    {
    case GDK_VISUAL_GRAYSCALE:
    case GDK_VISUAL_PSEUDO_COLOR:
195
      private->info = g_new0 (GdkColorInfo, colormap->size);
196
      colormap->colors = g_new (GdkColor, colormap->size);
197
      
198
      private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
199
					(GEqualFunc) gdk_color_equal);
200
      
Matthias Clasen's avatar
Matthias Clasen committed
201
      private->private_val = allocate;
202
      private->xcolormap = XCreateColormap (xdisplay, xrootwin,
Matthias Clasen's avatar
Matthias Clasen committed
203
					    xvisual, (allocate) ? (AllocAll) : (AllocNone));
Elliot Lee's avatar
Elliot Lee committed
204

Matthias Clasen's avatar
Matthias Clasen committed
205
      if (allocate)
Elliot Lee's avatar
Elliot Lee committed
206
	{
207 208
	  XColor *default_colors;

209
 	  default_colors = g_new (XColor, colormap->size);
210 211

	  for (i = 0; i < colormap->size; i++)
Elliot Lee's avatar
Elliot Lee committed
212
	    default_colors[i].pixel = i;
213 214
	  
	  XQueryColors (xdisplay,
215
			DefaultColormapOfScreen (GDK_SCREEN_X11 (private->screen)->xscreen),
216
			default_colors, colormap->size);
Elliot Lee's avatar
Elliot Lee committed
217

218
	  for (i = 0; i < colormap->size; i++)
Elliot Lee's avatar
Elliot Lee committed
219 220 221 222 223 224 225
	    {
	      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;
	    }

226 227 228
	  gdk_colormap_change (colormap, colormap->size);
	  
	  g_free (default_colors);
Elliot Lee's avatar
Elliot Lee committed
229 230 231 232 233
	}
      break;

    case GDK_VISUAL_DIRECT_COLOR:
      private->private_val = TRUE;
234
      private->xcolormap = XCreateColormap (xdisplay, xrootwin,
Elliot Lee's avatar
Elliot Lee committed
235
					    xvisual, AllocAll);
236
      colormap->colors = g_new (GdkColor, colormap->size);
Elliot Lee's avatar
Elliot Lee committed
237 238 239 240 241 242 243 244 245 246 247 248 249

      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);

250
      gdk_colormap_change (colormap, colormap->size);
Elliot Lee's avatar
Elliot Lee committed
251 252 253 254
      break;

    case GDK_VISUAL_STATIC_GRAY:
    case GDK_VISUAL_STATIC_COLOR:
255
      private->private_val = FALSE;
256
      private->xcolormap = XCreateColormap (xdisplay, xrootwin,
257 258 259
					    xvisual, AllocNone);
      
      colormap->colors = g_new (GdkColor, colormap->size);
260
      gdk_colormap_sync (colormap, TRUE);
261 262
      break;
      
Elliot Lee's avatar
Elliot Lee committed
263 264
    case GDK_VISUAL_TRUE_COLOR:
      private->private_val = FALSE;
265
      private->xcolormap = XCreateColormap (xdisplay, xrootwin,
Elliot Lee's avatar
Elliot Lee committed
266 267 268 269 270 271 272 273 274
					    xvisual, AllocNone);
      break;
    }

  gdk_colormap_add (colormap);

  return colormap;
}

275
static void
276
gdk_colormap_sync_palette (GdkColormap *colormap)
277
{
278
  GdkColormapPrivateX11 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
279 280 281 282 283 284 285 286 287
  XColor *xpalette;
  gint nlookup;
  gint i;

  nlookup = 0;
  xpalette = g_new (XColor, colormap->size);
  
  for (i = 0; i < colormap->size; i++)
    {
288
      if (!private->info || private->info[i].ref_count == 0)
289 290 291 292 293 294 295 296
	{
	  xpalette[nlookup].pixel = i;
	  xpalette[nlookup].red = 0;
	  xpalette[nlookup].green = 0;
	  xpalette[nlookup].blue = 0;
	  nlookup++;
	}
    }
297

298
  XQueryColors (GDK_SCREEN_XDISPLAY (private->screen),
299
		private->xcolormap, xpalette, nlookup);
300 301 302 303 304 305 306 307 308 309 310 311
  
  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);
}
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

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);
353

354 355
  g_return_if_fail (GDK_IS_COLORMAP (colormap));

356 357 358
  if (private->screen->closed)
    return;

359 360 361 362 363 364 365 366 367 368 369
  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);
}
370
		   
371 372 373 374 375 376 377 378 379 380
/**
 * 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)
Elliot Lee's avatar
Elliot Lee committed
381
{
382
  GdkColormap *colormap = NULL;
383
  GdkColormapPrivateX11 *private;
384
  GdkScreenX11 *screen_x11;
Elliot Lee's avatar
Elliot Lee committed
385

386 387
  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
  screen_x11 = GDK_SCREEN_X11 (screen);
Elliot Lee's avatar
Elliot Lee committed
388

389 390
  if (screen_x11->system_colormap)
    return screen_x11->system_colormap;
Elliot Lee's avatar
Elliot Lee committed
391

392
  colormap = g_object_new (GDK_TYPE_COLORMAP, NULL);
393
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
394

395
  private->screen = screen;
396 397 398 399
  colormap->visual = gdk_screen_get_system_visual (screen);
  
  private->xcolormap = DefaultColormapOfScreen (screen_x11->xscreen);
  private->private_val = FALSE;
400

401 402 403
  private->hash = NULL;
  private->last_sync_time = 0;
  private->info = NULL;
Elliot Lee's avatar
Elliot Lee committed
404

405 406
  colormap->colors = NULL;
  colormap->size = colormap->visual->colormap_size;
Elliot Lee's avatar
Elliot Lee committed
407

408 409 410 411 412 413 414 415 416 417
  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:
418
    case GDK_VISUAL_DIRECT_COLOR:
419 420 421 422 423 424 425 426 427 428
      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;
  
Elliot Lee's avatar
Elliot Lee committed
429 430 431
  return colormap;
}

Matthias Clasen's avatar
Matthias Clasen committed
432 433 434 435 436 437 438 439 440
/**
 * 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.
 **/
Elliot Lee's avatar
Elliot Lee committed
441 442 443
gint
gdk_colormap_get_system_size (void)
{
Owen Taylor's avatar
Owen Taylor committed
444 445
  return DisplayCells (GDK_SCREEN_XDISPLAY (gdk_screen_get_default()),
		       GDK_SCREEN_X11 (gdk_screen_get_default())->screen_num);
Elliot Lee's avatar
Elliot Lee committed
446 447
}

Matthias Clasen's avatar
Matthias Clasen committed
448 449 450 451 452 453 454 455 456 457
/**
 * 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().
 **/
Elliot Lee's avatar
Elliot Lee committed
458 459 460 461
void
gdk_colormap_change (GdkColormap *colormap,
		     gint         ncolors)
{
462
  GdkColormapPrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
463
  GdkVisual *visual;
464
  XColor *palette;
465
  Display *xdisplay;
Elliot Lee's avatar
Elliot Lee committed
466 467 468 469 470
  gint shift;
  int max_colors;
  int size;
  int i;

471
  g_return_if_fail (GDK_IS_COLORMAP (colormap));
Elliot Lee's avatar
Elliot Lee committed
472

473 474
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);

475 476 477
  if (private->screen->closed)
    return;

478
  xdisplay = GDK_SCREEN_XDISPLAY (private->screen);
479 480
  palette = g_new (XColor, ncolors);

481
  switch (colormap->visual->type)
Elliot Lee's avatar
Elliot Lee committed
482 483 484 485 486 487 488 489 490 491 492 493
    {
    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;
	}

494
      XStoreColors (xdisplay, private->xcolormap, palette, ncolors);
Elliot Lee's avatar
Elliot Lee committed
495 496 497
      break;

    case GDK_VISUAL_DIRECT_COLOR:
498
      visual = colormap->visual;
Elliot Lee's avatar
Elliot Lee committed
499 500 501 502 503 504 505 506 507 508 509 510

      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;
	}

511
      XStoreColors (xdisplay, private->xcolormap, palette, size);
Elliot Lee's avatar
Elliot Lee committed
512 513 514 515 516 517 518 519 520 521 522 523

      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;
	}

524
      XStoreColors (xdisplay, private->xcolormap, palette, size);
Elliot Lee's avatar
Elliot Lee committed
525 526 527 528 529 530 531 532 533 534 535 536

      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;
	}

537
      XStoreColors (xdisplay, private->xcolormap, palette, size);
Elliot Lee's avatar
Elliot Lee committed
538 539 540 541 542
      break;

    default:
      break;
    }
543 544

  g_free (palette);
Elliot Lee's avatar
Elliot Lee committed
545 546
}

Matthias Clasen's avatar
Matthias Clasen committed
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
/**
 * 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: 
 **/
565
gboolean
Elliot Lee's avatar
Elliot Lee committed
566
gdk_colors_alloc (GdkColormap   *colormap,
Owen Taylor's avatar
Owen Taylor committed
567
		  gboolean       contiguous,
Elliot Lee's avatar
Elliot Lee committed
568 569 570 571 572
		  gulong        *planes,
		  gint           nplanes,
		  gulong        *pixels,
		  gint           npixels)
{
573
  GdkColormapPrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
574
  gint return_val;
575
  gint i;
Elliot Lee's avatar
Elliot Lee committed
576

577
  g_return_val_if_fail (GDK_IS_COLORMAP (colormap), 0);
Elliot Lee's avatar
Elliot Lee committed
578

579
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
Elliot Lee's avatar
Elliot Lee committed
580

581 582 583
  if (private->screen->closed)
    return FALSE;

584
  return_val = XAllocColorCells (GDK_SCREEN_XDISPLAY (private->screen),
585 586
				 private->xcolormap,contiguous, planes,
				 nplanes, pixels, npixels);
587 588 589 590 591 592 593 594 595
  if (return_val)
    {
      for (i=0; i<npixels; i++)
	{
	  private->info[pixels[i]].ref_count++;
	  private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
	}
    }

596
  return return_val != 0;
Elliot Lee's avatar
Elliot Lee committed
597 598
}

599 600 601
/* This is almost identical to gdk_colormap_free_colors.
 * Keep them in sync!
 */
Matthias Clasen's avatar
Matthias Clasen committed
602 603 604 605 606


/**
 * gdk_colors_free:
 * @colormap: a #GdkColormap.
Matthias Clasen's avatar
Matthias Clasen committed
607 608
 * @pixels: the pixel values of the colors to free.
 * @npixels: the number of values in @pixels.
Matthias Clasen's avatar
Matthias Clasen committed
609 610 611 612 613
 * @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().
 **/
614 615
void
gdk_colors_free (GdkColormap *colormap,
Matthias Clasen's avatar
Matthias Clasen committed
616 617
		 gulong      *pixels,
		 gint         npixels,
618 619
		 gulong       planes)
{
620
  GdkColormapPrivateX11 *private;
Matthias Clasen's avatar
Matthias Clasen committed
621 622
  gulong *pixels_to_free;
  gint npixels_to_free = 0;
623 624
  gint i;

625
  g_return_if_fail (GDK_IS_COLORMAP (colormap));
Matthias Clasen's avatar
Matthias Clasen committed
626
  g_return_if_fail (pixels != NULL);
627

628
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
629

630 631
  if ((colormap->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
      (colormap->visual->type != GDK_VISUAL_GRAYSCALE))
632 633
    return;
  
Matthias Clasen's avatar
Matthias Clasen committed
634
  pixels_to_free = g_new (gulong, npixels);
635

Matthias Clasen's avatar
Matthias Clasen committed
636
  for (i=0; i<npixels; i++)
637
    {
Matthias Clasen's avatar
Matthias Clasen committed
638
      gulong pixel = pixels[i];
639 640 641 642 643 644 645
      
      if (private->info[pixel].ref_count)
	{
	  private->info[pixel].ref_count--;

	  if (private->info[pixel].ref_count == 0)
	    {
Matthias Clasen's avatar
Matthias Clasen committed
646
	      pixels_to_free[npixels_to_free++] = pixel;
647 648 649 650 651 652 653
	      if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
		g_hash_table_remove (private->hash, &colormap->colors[pixel]);
	      private->info[pixel].flags = 0;
	    }
	}
    }

Matthias Clasen's avatar
Matthias Clasen committed
654
  if (npixels_to_free && !private->screen->closed)
655
    XFreeColors (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
Matthias Clasen's avatar
Matthias Clasen committed
656 657
		 pixels_to_free, npixels_to_free, planes);
  g_free (pixels_to_free);
658 659 660 661 662
}

/* This is almost identical to gdk_colors_free.
 * Keep them in sync!
 */
Matthias Clasen's avatar
Matthias Clasen committed
663 664 665 666 667 668 669 670 671

/**
 * gdk_colormap_free_colors:
 * @colormap: a #GdkColormap.
 * @colors: the colors to free.
 * @ncolors: the number of colors in @colors.
 * 
 * Frees previously allocated colors.
 **/
672 673 674 675 676
void
gdk_colormap_free_colors (GdkColormap *colormap,
			  GdkColor    *colors,
			  gint         ncolors)
{
677
  GdkColormapPrivateX11 *private;
678 679 680 681
  gulong *pixels;
  gint npixels = 0;
  gint i;

682
  g_return_if_fail (GDK_IS_COLORMAP (colormap));
683 684
  g_return_if_fail (colors != NULL);

685
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
686

687 688
  if ((colormap->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
      (colormap->visual->type != GDK_VISUAL_GRAYSCALE))
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
    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;
	    }
	}
    }

711
  if (npixels && !private->screen->closed)
712
    XFreeColors (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
713 714 715 716 717
		 pixels, npixels, 0);

  g_free (pixels);
}

718 719 720 721 722 723 724 725 726 727 728
/********************
 * 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)
Elliot Lee's avatar
Elliot Lee committed
729
{
730
  GdkColormapPrivateX11 *private;
Elliot Lee's avatar
Elliot Lee committed
731 732
  XColor xcolor;

733
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
Elliot Lee's avatar
Elliot Lee committed
734 735 736 737 738 739 740

  xcolor.red = color->red;
  xcolor.green = color->green;
  xcolor.blue = color->blue;
  xcolor.pixel = color->pixel;
  xcolor.flags = DoRed | DoGreen | DoBlue;

741
  if (XAllocColor (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap, &xcolor))
742 743 744 745 746 747 748 749 750 751
    {
      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 */
	    {
752
	      XFreeColors (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
753
			   &xcolor.pixel, 1, 0);
754 755 756 757
	    }
	  else
	    {
	      colormap->colors[ret->pixel] = *color;
758
	      colormap->colors[ret->pixel].pixel = ret->pixel;
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
	      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)
{
782
  GdkColormapPrivateX11 *private;
783 784 785 786
  gulong *pixels;
  Status status;
  gint i, index;

787
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
Elliot Lee's avatar
Elliot Lee committed
788

789
  if (private->private_val)
Elliot Lee's avatar
Elliot Lee committed
790
    {
791 792
      index = 0;
      for (i=0; i<ncolors; i++)
Elliot Lee's avatar
Elliot Lee committed
793
	{
794 795 796 797
	  while ((index < colormap->size) && (private->info[index].ref_count != 0))
	    index++;
	  
	  if (index < colormap->size)
Elliot Lee's avatar
Elliot Lee committed
798
	    {
799 800 801 802
	      colors[i].pixel = index;
	      success[i] = TRUE;
	      private->info[index].ref_count++;
	      private->info[i].flags |= GDK_COLOR_WRITEABLE;
Elliot Lee's avatar
Elliot Lee committed
803 804
	    }
	  else
805 806 807 808 809 810 811 812 813
	    break;
	}
      return i;
    }
  else
    {
      pixels = g_new (gulong, ncolors);
      /* Allocation of a writeable color cells */
      
814
      status =  XAllocColorCells (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
815 816 817 818
				  FALSE, NULL, 0, pixels, ncolors);
      if (status)
	{
	  for (i=0; i<ncolors; i++)
Elliot Lee's avatar
Elliot Lee committed
819
	    {
820 821 822 823 824 825 826 827 828 829 830
	      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; 
    }
}
Elliot Lee's avatar
Elliot Lee committed
831

832 833 834 835 836 837 838 839
static gint
gdk_colormap_alloc_colors_private (GdkColormap *colormap,
				   GdkColor    *colors,
				   gint         ncolors,
				   gboolean     writeable,
				   gboolean     best_match,
				   gboolean    *success)
{
840
  GdkColormapPrivateX11 *private;
841 842 843 844 845
  gint i, index;
  XColor *store = g_new (XColor, ncolors);
  gint nstore = 0;
  gint nremaining = 0;
  
846
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
  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++;
Elliot Lee's avatar
Elliot Lee committed
871
	    }
872 873
	  else
	    nremaining++;
Elliot Lee's avatar
Elliot Lee committed
874
	}
875 876
    }
  
877
  XStoreColors (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap,
878
		store, nstore);
879 880 881 882 883 884 885 886 887 888 889
  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++)
Elliot Lee's avatar
Elliot Lee committed
890
	{
891
	  if (!success[i])
Elliot Lee's avatar
Elliot Lee committed
892
	    {
893 894 895 896
	      index = gdk_colormap_match_color (colormap, 
						&colors[i], 
						available);
	      if (index != -1)
Elliot Lee's avatar
Elliot Lee committed
897
		{
898 899
		  colors[i] = colormap->colors[index];
		  private->info[index].ref_count++;
Elliot Lee's avatar
Elliot Lee committed
900

901 902
		  success[i] = TRUE;
		  nremaining--;
Elliot Lee's avatar
Elliot Lee committed
903
		}
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
	    }
	}
      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)
{
920
  GdkColormapPrivateX11 *private;
921 922 923 924
  gint i, index;
  gint nremaining = 0;
  gint nfailed = 0;

925
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
926
  index = -1;
Elliot Lee's avatar
Elliot Lee committed
927

928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
  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) ||
945
			!(private->info[i].flags & GDK_COLOR_WRITEABLE));
946 947 948 949 950 951 952 953 954
      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);
Elliot Lee's avatar
Elliot Lee committed
955 956
		  if (index != -1)
		    {
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
		      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;
			    }
			}
Elliot Lee's avatar
Elliot Lee committed
979 980 981
		    }
		  else
		    {
982 983 984
		      nfailed++;
		      nremaining--;
		      success[i] = 2; /* flag as permanent failure */
Elliot Lee's avatar
Elliot Lee committed
985 986 987 988
		    }
		}
	    }
	}
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
      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)
{
1012
  GdkColormapPrivateX11 *private;
1013 1014 1015 1016
  GdkColor *lookup_color;
  gint i;
  gint nremaining = 0;

1017
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050

  /* 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;
}

Matthias Clasen's avatar
Matthias Clasen committed
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
/**
 * 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.
 **/
1071 1072 1073 1074 1075 1076 1077 1078
gint
gdk_colormap_alloc_colors (GdkColormap *colormap,
			   GdkColor    *colors,
			   gint         ncolors,
			   gboolean     writeable,
			   gboolean     best_match,
			   gboolean    *success)
{
1079
  GdkColormapPrivateX11 *private;
1080 1081 1082 1083 1084
  GdkVisual *visual;
  gint i;
  gint nremaining = 0;
  XColor xcolor;

1085 1086
  g_return_val_if_fail (GDK_IS_COLORMAP (colormap), ncolors);
  g_return_val_if_fail (colors != NULL, ncolors);
1087

1088
  private = GDK_COLORMAP_PRIVATE_DATA (colormap);
1089

1090 1091 1092
  if (private->screen->closed)
    return ncolors;

1093 1094 1095 1096 1097
  for (i=0; i<ncolors; i++)
    {
      success[i] = FALSE;
    }

1098
  switch (colormap->visual->type)
1099 1100 1101 1102 1103 1104 1105 1106 1107
    {
    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);
Elliot Lee's avatar
Elliot Lee committed
1108 1109 1110
      break;

    case GDK_VISUAL_DIRECT_COLOR:
1111
    case GDK_VISUAL_TRUE_COLOR:
1112
      visual = colormap->visual;
1113 1114 1115 1116 1117 1118 1119 1120

      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;
	}
Elliot Lee's avatar
Elliot Lee committed
1121 1122 1123
      break;
    case GDK_VISUAL_STATIC_GRAY:
    case GDK_VISUAL_STATIC_COLOR:
1124
      for (i=0; i<ncolors; i++)
Elliot Lee's avatar
Elliot Lee committed
1125
	{
1126 1127 1128 1129 1130 1131
	  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;

1132
	  if (XAllocColor (GDK_SCREEN_XDISPLAY (private->screen), private->xcolormap, &xcolor))
1133 1134 1135 1136 1137 1138
	    {
	      colors[i].pixel = xcolor.pixel;
	      success[i] = TRUE;
	    }
	  else
	    nremaining++;
Elliot Lee's avatar
Elliot Lee committed
1139 1140 1141
	}
      break;
    }
1142 1143
  return nremaining;
}
Elliot Lee's avatar
Elliot Lee committed
1144

Havoc Pennington's avatar
Havoc Pennington committed
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
/**
 * 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.)
 *
1159
 * This function is rarely useful; it's used for example to
Havoc Pennington's avatar
Havoc Pennington committed
1160 1161