/* $Id: map.c,v 1.1.1.1 90/11/28 17:02:44 altenhof Exp $ */

/*
 * Copyright (C) 1990 by Digital Equipment Corporation.
 * 
 * Author: Michael P. Altenhofen, CEC Karlsruhe e-mail:
 * Altenhofen@kampus.enet.dec.com
 * 
 * This file ist part of Shared X
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation without fee is hereby granted, but only for non-profit  use
 * and distribution,  and provided  that the copyright notice and this notice
 * is preserved on all copies.
 * 
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */
/*
 * $Log:	map.c,v $
 * Revision 1.1.1.1  90/11/28  17:02:44  altenhof
 * First public release
 * 
 * Revision 1.1  90/11/28  17:02:42  altenhof
 * Initial revision
 * 
 * Revision 1.1.1.1  90/11/28  16:25:51  spanachi
 * First public version of shX
 * 
 * Revision 1.1  90/11/28  16:25:49  spanachi
 * Initial revision
 * 
 * 
 * Revision 1.3  90/08/29  11:04:53  altenhof Fixed 'BUG': Included the SUN
 * patches in ReallocPixelValues (SUN doesn't allow to realloc NULL pointer)
 * 
 * Revision 1.2  90/06/07  11:24:31  altenhof Changed code in GetServerMask
 * 
 * Revision 1.1.1.1  90/04/23  10:13:12  spanachi First version of 'shared X'
 * library (X11R3).
 * 
 * Revision 1.1  90/04/23  10:13:07  spanachi Initial revision
 * 
 * Revision 1.2  90/04/04  20:31:10  altenhof FIXED bug: hashing on negative
 * numbers
 * 
 * Revision 1.1  90/04/04  09:40:53  altenhof Initial revision
 * 
 * Revision 1.1  90/03/21  15:24:34  altenhof Initial revision
 * 
 * Revision 1.1  89/11/03  14:45:28  neideck Original send to X consortium
 * 
 * Revision 2.3  89/09/27  19:25:42  michael New routine added to handle
 * FreeColors request ( map pixels and planes ) FIXED BUGS: InsertID was
 * wrong (overwriting values of unused entries) RemoveID was wrong (return
 * with an error instead of removing the alias) Bugs were not detected until
 * now, since ID removal has not been used!
 * 
 * Revision 2.2  89/09/07  19:06:42  michael Code to manage private cells from
 * XAllocColorPlanes added ( but not tested!) FIXED BUGS: Pixel mapping for
 * private cells was wrong ( too many planes!) XmuXMapPixelsAndPlane now has
 * in and out parameters ( overriding was bad!)
 * 
 * Revision 2.1  89/09/05  17:23:35  michael FIXED BUG: Plane generation was
 * wrong.
 * 
 */

#include "XmuXlibint.h"
#include "macros.h"

#ifndef IsPixmap
#define IsWindow  0
#define IsPixmap  1
#define DontKnow  2
#endif

/* local definitions for hashing */

#define ID_HASHSIZE 127		/* fits in one page */

/*
 * the hash function: ( resource ID ) modulo ID_HASHSIZE
 */
#define HashID( id )   ( int ) ( ( (id) >= 0 ) ? ( id ) % ID_HASHSIZE : \
				 ( -(id)) % ID_HASHSIZE )

#define NullTable    ( IDMapPtr * ) ( 0 )
#define NullIDMapPtr ( IDMapPtr ) ( 0 )

/*
 * typedef's and declarations for ID mapping
 */
typedef struct _IDMapRec {	/* one entry in our ID map */
  int whose;			/* where does this ID come from (FromServer
				 * or FromClient) */
  Bool used;			/* is this entry currently used */
  XID id,			/* the id ... */
   alias;			/* ... and it's equivalence */
  struct _IDMapRec *next;	/* we use hashing with collision resolution
				 * by linking */
} IDMapRec, *IDMapPtr;

static IDMapPtr *MUXIDMaps[MAXSOCKS];	/* one hash table per connection */

static IDMapPtr *FreeMaps[10];	/* list of allocated but unused hash tables */

static int free_index = -1;

static IDMapPtr FreeEntries = NullIDMapPtr;	/* list of allocated but
						 * unused hash entries */

/*
 * typedef's and declarations for Colormap Hashing
 */

#define W_HASHSIZE 7

#define HashW( w )   ( int ) ( ( (w) >= 0 ) ? ( w ) % W_HASHSIZE : \
			       ( -(w) ) %W_HASHSIZE )

#define NullWindowCmapTable  ( WindowCmapPtr * ) ( 0 )
#define NullWindowCmapPtr    ( WindowCmapPtr ) ( 0 )

typedef struct _WindowCmapEntry {

  XID window,			/* the window ... */
   cmap;			/* ... and it's color map */
  struct _WindowCmapEntry *next;/* collision resolution */

} WindowCmapEntry, *WindowCmapPtr;

static WindowCmapPtr *MUXWindowCmapTable[MAXSOCKS];


/*
 * typedef's and declarations for PIXEL mapping
 */

typedef struct _PixelMapValues {/* one entry in a pixel map array */

  unsigned long client_value,	/* the client's pixel/plane value */
   server_value;		/* the server's pixel/plane value */

} PixelMapValues;

/***
 * Next declarations need some explanations:
 *
 * The most time-consuming job is pixel mapping, because you have three
 * different ways to allocate colors:
 *    -> You may use read-only color cells
 *    -> You may allocate private color cells via XAllocColorCells
 *    -> You may allocate private color cells via XAllocColorPlanes
 *
 * Read-only cells are easy: they are uniquely determined by a pixel value.
 *
 * Private color cells from XAllocColorCells are hard: They are uniquely
 * determined by a so-called "base-pixel" value (one of the pixel values
 * XAllocColorCells returns to you) and an OR-ed combination of plane masks
 * returned by XAllocColorCells. What you get from XAllocColorCells is an
 * array of plane masks, where every mask has exactly one bit set (if you are
 * using a PseudoColor Visual) or exactly three bits set (if you are using a
 * DirectColor Visual) and no mask value having any bit in common with any
 * other. To map such a private cell, you'll have to reconstruct the
 * base-pixel value by stripping off the plane masks, map this pixel and OR
 * it with the mapped plane masks.
 *
 * Private color cells from XAllocColorPlanes are even harder:
 * XAllocColorPlanes is intended to be used on DirectColor workstations
 * (although it works on PseudoColor workstations), since the three plane
 * sets returned can be used as primary subfields (red,green,blue) in
 * DirectColor colormaps. What XAllocColorCells returns as an array is merged
 * into single masks here; e.g. if you need 4 planes in the red subfield, the
 * "red_mask" will have 4 bits set. To map such a private color cells, you'll
 * have to use a different technique to generate and map the used plane
 * masks.
 *
 * To deal with all three color cell types we use an array of five different
 * lists (called maps): One storing the read-only cells (indexed by
 * SharedPixels) One storing the base-pixel values returned by
 * XAllocColorCells (indexed by PseudoPixels)
 *
 * One storing the plane mask values returned by XAllocColorCells (indexed by
 * PseudoPlanes)
 * One storing the base-pixel values returned by XAllocColorPlanes (indexed by
 * DirectPixels)
 * One storing the plane mask values returned by XAllocColorPlanes (indexed by
 * DirectPlanes)
 *
 * The index array is used to determine the base-pixels/plane masks from one
 * call to XAllocColorCells/Planes.  You must use a "per-call" mapping
 * algorithm, since plane masks are only unique in one call!
 ***/

typedef struct _PixelMap {	/* describes a pixel map for one connection */
  Colormap cmap;		/* identifies this pixel map */
  Bool sorted;			/* can we use binary search ? */
  int length[5],		/* length of the arrays */
   allocated[5],		/* number of allocated values in the arrays */
   pr_calls[2],			/* num of calls to XAllocColorCells/Planes */
  *index[3];
  PixelMapValues *maps[5];	/* the map arrays */

  struct _PixelMap *next;	/* hook to the next map */
} PixelMap, *PixelMapPtr;

static PixelMapPtr MUXPixelMaps[MAXSOCKS];	/* one pixel map list per
						 * connection */

/* local definitions for pixel mapping */

#define ALLOC_AT_ONCE 5
#define PLANES_RGB     3

#define NullPixelMapValues ( PixelMapValues * ) ( 0 )
#define NullPixelMapPtr ( PixelMapPtr ) ( 0 )

/* indices to maps */
#define SharedPixels 0
#define PseudoPixels 1
#define PseudoPlanes 2
#define DirectPixels 3
#define DirectPlanes 4

/* indices to index */
#define ACCPixels    0		/* ACC means (X)A_lloc_C_olor_C_ells */
#define ACCPlanes    1
#define ACPPixels    2		/* ACP means (X)A_lloc_C_olor_P_lanes */

/* indices to pr_calls */
#define ACCCalls     0
#define ACPCalls     1

#define INDEX( ptr, i, which ) ptr->index[ which ][ i ]

#define ALLOCATED( ptr, which ) ptr->allocated[ which ]

#define MUST_REALLOC( ptr, n, which ) \
           ( ( ptr->allocated[ which ] + n ) >= ptr->length[ which ] )

#define CLIENT_VALUE( ptr, i, which ) \
         ptr->maps[ which ][ i ].client_value

#define SERVER_VALUE( ptr, i, which ) \
         ptr->maps[ which ][ i ].server_value

#define SERVER_RGBS( ptr, k ) \
         ( ( ptr->maps[ DirectPlanes ][ k * PLANES_RGB ].server_value ) |\
	   ( ptr->maps[ DirectPlanes ][ k * PLANES_RGB + 1 ].server_value ) |\
	   ( ptr->maps[ DirectPlanes ][ k * PLANES_RGB + 2 ].server_value ) )

#define CLIENT_RGBS( ptr, k ) \
         ( ( ptr->maps[ DirectPlanes ][ k * PLANES_RGB ].client_value ) |\
	   ( ptr->maps[ DirectPlanes ][ k * PLANES_RGB + 1 ].client_value ) |\
	   ( ptr->maps[ DirectPlanes ][ k * PLANES_RGB + 2 ].client_value ) )

#define SetPseudoPixelIndex( ptr, idx ) \
           SetIndex( ptr, idx, ACCPixels, ACCCalls )

#define SetDirectPixelIndex( ptr, idx ) \
           SetIndex( ptr, idx, ACPPixels, ACPCalls )

#define SetPseudoPlaneIndex( ptr, idx ) \
           SetIndex( ptr, idx, ACCPlanes, ACCCalls )

void
FreeIDMap (map)
  IDMapPtr *map;

{
  IDMapPtr entry;
  register int i;

  if (map == NullTable)
    XmuXFatalError ("FreeIDMap: Try to free an empty map!\n");
  for (i = 0; i < ID_HASHSIZE; i++) {
    if ((entry = map[i]) != NullIDMapPtr) {
      while (entry->next != NullIDMapPtr)
	entry = entry->next;
      entry->next = FreeEntries;
      FreeEntries = map[i];
      map[i] = NullIDMapPtr;
    }

  }
  if (++free_index > 9) {
    free_index = 9;
    Xfree ((char *) map);
  }
  else
    FreeMaps[free_index] = map;
}

IDMapPtr *
AllocIDMap ()
{
  IDMapPtr *map;
  register int i;

  if (free_index < 0) {
    free_index = -1;
    map = (IDMapPtr *) Xmalloc (ID_HASHSIZE * sizeof (IDMapPtr));

  }
  else
    map = FreeMaps[free_index--];

  for (i = 0; i < ID_HASHSIZE; i++)
    map[i] = NullIDMapPtr;

  return (map);
}

IDMapPtr
AllocEntry ()
{
  IDMapPtr newentry;

  if (FreeEntries == NullIDMapPtr)
    return ((IDMapPtr) Xmalloc (sizeof (IDMapRec)));
  else {
    newentry = FreeEntries;
    newentry->next = NullIDMapPtr;
    FreeEntries = FreeEntries->next;
    return (newentry);
  }
}

void
InitIDMaps ()
{
  register int i;

  for (i = 0; i < MAXSOCKS; i++)
    MUXIDMaps[i] = NullTable;
}

void
InstallIDMap (dpy)
  Display *dpy;

{
  register int i;

#ifdef DEBUG
  CheckDisplay (dpy);
#endif

  if ((MUXIDMaps[ConnectionNumber (dpy)] = AllocIDMap ()) ==
      NullTable)
    XmuXFatalError ("InstallIDMap: Out of memory (conn %d)!\n",
		    ConnectionNumber (dpy));

  for (i = 0; i < ID_HASHSIZE; i++)
    MUXIDMaps[ConnectionNumber (dpy)][i] = NullIDMapPtr;
}

void
XmuXInsertID (dpy, id, alias, whose)
  Display *dpy;
  XID id, alias;
  int whose;			/* FromServer or FromClient */

{
  IDMapPtr *bucket,		/* pointer to hash table bucket */
   newentry;			/* pointer to new map entry */

  /* don't want to point to nowhere */

#ifdef DEBUG
  CheckDisplay (dpy, "InsertID");
#endif

  /* the "primary" server need no hash table at all */
  if (IsClientDisplay (dpy))
    return;

  /*
   * Multiplex servers must use a hash table
   */

  if (MUXIDMaps[ConnectionNumber (dpy)] == NullTable)
    /* create it */
    InstallIDMap (dpy);

  /* find the right place */
  bucket = MUXIDMaps[ConnectionNumber (dpy)] + HashID (id);

  for (newentry = *bucket; newentry != NullIDMapPtr;
       newentry = newentry->next)

    /*
     * it has been inserted before or entry is unused,  overwrite old values
     */
    if (!newentry->used ||
	(newentry->id == id && newentry->whose == whose))
      break;

  if (newentry == NullIDMapPtr) {
    /* create, fill and insert a new entry */
    if ((newentry = AllocEntry ()) == NullIDMapPtr)
      XmuXFatalError ("XmuXInsertID: Out of memory (conn %d)!\n",
		      ConnectionNumber (dpy));
    newentry->next = *bucket;
    *bucket = newentry;
  }

  newentry->id = id;
  newentry->alias = alias;
  newentry->whose = whose;
  newentry->used = True;

}

/*
 * We do not really remove here, because its to complicated (Hint:
 * XDestroyWindow destroys all subwindows too. We would only be able to
 * remove these IDs when removing the resource info; but that's the wrong
 * place, since the request XDetroyWindow for multiplex'ed connections comes
 * later. Which IDs should we use then ? ). Instead, we mark the entry as
 * "not used".
 */

void
XmuXRemoveID (dpy, id, whose)
  Display *dpy;
  XID id;
  int whose;			/* FromClient or FromServer */

{
  IDMapPtr curr;		/* pointer to the entry that has to be
				 * "deleted" */
  register int i;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXRemoveID");
#endif

  /* the "primary" server does not store any id at all */
  if (IsClientDisplay (dpy))
    return;

  /* VAX can "handle" NULL pointers !!! */
  if (MUXIDMaps[ConnectionNumber (dpy)] == NullTable) {
    XmuXErrorF ("XmuXRemoveID: Empty map %d!\n", ConnectionNumber (dpy));
    return;
  }

  /*
   * we remove the ID and it's alias!!!
   */
  for (i = 0; i < 2; i++) {
    /* find the right place in the table */
    curr = MUXIDMaps[ConnectionNumber (dpy)][HashID (id)];

    if (curr == NullIDMapPtr) {
      XmuXErrorF ("XmuXRemoveID: No entry (0x%lx ID map %d)!\n",
		  id, ConnectionNumber (dpy));
      return;
    }

    /* search through the list */
    for (; curr != NullIDMapPtr; curr = curr->next)
      if (curr->id == id && curr->whose == whose) {	/* we've found it -->
							 * update the list */
	/* set values for second round */
	id = curr->alias;
	whose = (whose == FromClient) ? FromServer : FromClient;
	/* mark entry as unused */
	curr->used = False;
	break;
      }
    if (curr == NullIDMapPtr) {
      /* we failed! */
      XmuXErrorF ("XmuXRemoveID: ID 0x%lx not found (ID map %d)!\n",
		  id, ConnectionNumber (dpy));
      return;
    }
  }
}

void
XmuXRemoveWindowID (dpy, window)
  Display *dpy;
  Window window;

{
  register Display *curr_dpy;
  register int i;

  for (i = 1; (curr_dpy = XmuXGetDisplay (dpy, i)) != NULL; i++)
    XmuXRemoveID (curr_dpy, window, FromClient);

}

static
void
DeleteIDMap (dpy)
  Display *dpy;

{

#ifdef DEBUG
  CheckDisplay (dpy, "DeleteIDMap");
#endif

  if (MUXIDMaps[ConnectionNumber (dpy)] != NullTable)
    FreeIDMap (MUXIDMaps[ConnectionNumber (dpy)]);
  MUXIDMaps[ConnectionNumber (dpy)] = NullTable;
}

int
XmuXIsRegistered (dpy, id, whose)
  Display *dpy;
  XID id;
  int whose;
{
  IDMapPtr entry;

  /* Check the connection number (Don't want to point to nowhere) */
#ifdef DEBUG
  CheckDisplay (dpy, "XmuXMapID");
#endif

  /* id None is not registered */
  if (id == None)
    return (False);

  /* the "primary" server doesn't use a hash table */
  if (IsClientDisplay (dpy))
    return (False);

  /*
   * All other ID "pairs" are stored in a hash table
   */

  if (MUXIDMaps[ConnectionNumber (dpy)] == NullTable)
    XmuXFatalError ("XmuXMapID: ID map %d empty!\n",
		    ConnectionNumber (dpy));

  /* find the right place */
  entry = MUXIDMaps[ConnectionNumber (dpy)][HashID (id)];

  /* scan the list */
  for (; entry != NullIDMapPtr; entry = entry->next)
    if (entry->id == id && entry->whose == whose)
      /* we've found it */
      return (True);

  /* not found */
  return (False);
}

XID
XmuXMapID (dpy, id, whose)
  Display *dpy;
  XID id;
  int whose;

{
  IDMapPtr entry;

  /* Check the connection number (Don't want to point to nowhere) */
#ifdef DEBUG
  CheckDisplay (dpy, "XmuXMapID");
#endif

  /* don't do anything if id == None */
  if (id == None)
    return (None);

  /* the "primary" server need no hash table at all */
  if (IsClientDisplay (dpy))
    return (id);

  /*
   * All other ID "pairs" are stored in a hash table
   */

  if (MUXIDMaps[ConnectionNumber (dpy)] == NullTable)
    XmuXFatalError ("XmuXMapID: ID map %d empty!\n",
		    ConnectionNumber (dpy));

  /* find the right place */
  entry = MUXIDMaps[ConnectionNumber (dpy)][HashID (id)];

  /* scan the list */
  for (; entry != NullIDMapPtr; entry = entry->next)
    if (entry->id == id && entry->whose == whose)
      /* we've found it */
      return (entry->alias);

  /*
   * We've failed! Maybe it's an id from the wm or another client. Pass it
   * back without changing.
   */
  return (id);
}

static
 Colormap
FindWindowCmap (dpy, window)
  Display *dpy;
  Window window;

{
  register Colormap cmap;
  register WindowCmapPtr entry;

  /* we know that the display is valid here */
  /* this is the default if we fail */
  cmap = DefaultColormap (dpy, DefaultScreen (dpy));
  /* window == None means, use the default */
  if (window == None)
    return (cmap);
  if (MUXWindowCmapTable[ConnectionNumber (dpy)] != NullWindowCmapTable) {
    entry = MUXWindowCmapTable[ConnectionNumber (dpy)][HashW (window)];

    for (; entry != NullWindowCmapPtr; entry = entry->next)
      if (entry->window == window) {
	cmap = entry->cmap;
	break;
      }
  }

  return (cmap);
}

static
 PixelMapPtr
FindPixelMapPtr (dpy, window)
  Display *dpy;
  Window window;

{
  register Colormap cmap;
  register PixelMapPtr ptr;

  cmap = FindWindowCmap (dpy, window);
  ptr = MUXPixelMaps[ConnectionNumber (dpy)];
  while (ptr != NullPixelMapPtr && cmap != ptr->cmap)
    ptr = ptr->next;

  return (ptr);
}

void
InitWindowCmapTables ()
{
  register int i;

  for (i = 0; i < MAXSOCKS; i++)
    MUXWindowCmapTable[i] = NullWindowCmapTable;
}

void
InstallWindowCmapTable (dpy)
  Display *dpy;

{
  register WindowCmapPtr *new_map;
  register int i;

  new_map = (WindowCmapPtr *)
    Xmalloc (W_HASHSIZE * sizeof (WindowCmapPtr));

  if (new_map == NullWindowCmapTable)
    XmuXFatalError ("InstallWindowCmapTable: Out of memory (conn %d)!\n",
		    ConnectionNumber (dpy));
  for (i = 0; i < W_HASHSIZE; i++)
    new_map[i] = NullWindowCmapPtr;

  MUXWindowCmapTable[ConnectionNumber (dpy)] = new_map;
}

static
void
DeleteCmapTable (dpy)
  Display *dpy;

{
  register WindowCmapPtr entry;
  register int i;

  if (MUXWindowCmapTable[ConnectionNumber (dpy)] == NullWindowCmapTable)
    return;

  for (i = 0; i < W_HASHSIZE; i++)
    if ((entry = MUXWindowCmapTable[ConnectionNumber (dpy)][i]))
      while (entry) {
	register WindowCmapPtr pnext = entry->next;
	Xfree ((char *) entry);
	entry = pnext;
      }
  MUXWindowCmapTable[ConnectionNumber (dpy)] = NullWindowCmapTable;
}

void
XmuXSetWindowCmap (dpy, window, cmap, parent)
  Display *dpy;
  XID window, cmap, parent;	/* in case we have cmap == CopyFromParent */
{
  register WindowCmapPtr newentry, *bucket;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXSetWindowCmap");
#endif


  /* no hash table for the default display */
  if (IsClientDisplay (dpy))
    return;

  /*
   * if cmap == CopyFromParent we must find out the cmap ID that is used by
   * the parent
   */
  if (cmap == CopyFromParent)
    cmap = FindWindowCmap (dpy, parent);

  /*
   * Multiplex servers must use a hash table
   */
  if (MUXWindowCmapTable[ConnectionNumber (dpy)] == NullWindowCmapTable)
    /* create it */
    InstallWindowCmapTable (dpy);

  /* find the right place */
  bucket = MUXWindowCmapTable[ConnectionNumber (dpy)] + HashW (window);

  for (newentry = *bucket; newentry != NullWindowCmapPtr;
       newentry = newentry->next)
    /* entry exists, override old value */
    if (newentry->window == window)
      break;

  if (newentry == NullWindowCmapPtr) {
    if ((newentry = (WindowCmapPtr)
	 Xmalloc (sizeof (WindowCmapEntry))) == NullWindowCmapPtr)
      XmuXFatalError ("XmuXSetWindowCmap: Out of memory (conn %d)!\n",
		      ConnectionNumber (dpy));
    newentry->window = window;
    newentry->cmap = cmap;
    newentry->next = *bucket;
    *bucket = newentry;
  }
  else
    newentry->cmap = cmap;
}

static
 PixelMapPtr
AllocPixelMap (dpy, cmap)
  Display *dpy;
  Colormap cmap;

{
  register PixelMapPtr new_map, ptr;
  register int i;

#ifdef DEBUG
  CheckDisplay (dpy, "AllocPixelMap");
#endif

  /* alloc a new entry */
  new_map = (PixelMapPtr) Xmalloc (sizeof (PixelMap));

  if (new_map == NullPixelMapPtr)
    XmuXFatalError ("AllocPixelMap: Out of memory (conn %d)!\n",
		    ConnectionNumber (dpy));
  /* fill in the values */
  new_map->cmap = cmap;
  new_map->sorted = False;
  new_map->next = NullPixelMapPtr;

  new_map->pr_calls[ACCCalls] =
    new_map->pr_calls[ACPCalls] = 0;
  new_map->index[ACCPixels] =
    new_map->index[ACCPlanes] =
    new_map->index[ACPPixels] = NULL;
  for (i = SharedPixels; i <= DirectPlanes; i++) {
    new_map->length[i] =
      new_map->allocated[i] = 0;
    new_map->maps[i] = NULL;
  }
  /* append it to the list */
  if ((ptr = MUXPixelMaps[ConnectionNumber (dpy)]) == NullPixelMapPtr)
    MUXPixelMaps[ConnectionNumber (dpy)] = new_map;
  else {
    while (ptr->next != NullPixelMapPtr)
      ptr = ptr->next;
    ptr->next = new_map;
  }
  return (new_map);
}

static
void
ReallocPixelValues (dpy, pmap_ptr, n, which)
  Display *dpy;
  PixelMapPtr pmap_ptr;
  int n, which;

{
  register int size;

#ifdef DEBUG
  CheckDisplay (dpy, "ReallocPixelMap");
#endif

  if (which == DirectPlanes)
    /* always three plane masks */
    size = pmap_ptr->length[which] + PLANES_RGB;
  else
    size = ((pmap_ptr->length[which] + n) / ALLOC_AT_ONCE + 1) *
      ALLOC_AT_ONCE;

  if (pmap_ptr->maps[which] == NullPixelMapValues)
    pmap_ptr->maps[which] = (PixelMapValues *)
      Xmalloc (size * sizeof (PixelMapValues));
  else
    pmap_ptr->maps[which] = (PixelMapValues *)
      Xrealloc ((char *) pmap_ptr->maps[which],
		size * sizeof (PixelMapValues));

  if (pmap_ptr->maps[which] == NullPixelMapValues)
    XmuXFatalError ("ReallocPixelMap: Out of memory (conn %d)!\n",
		    ConnectionNumber (dpy));
  pmap_ptr->length[which] = size;
}

static
void
DeletePixelMaps (dpy)
  Display *dpy;

{
  register PixelMapPtr ptr;

  ptr = MUXPixelMaps[ConnectionNumber (dpy)];

  while (ptr != NullPixelMapPtr) {
    register PixelMapPtr pnext = ptr->next;
    register int i;

    for (i = SharedPixels; i <= DirectPlanes; i++)
      if (ptr->length[i])
	Xfree ((char *) ptr->maps[i]);
    for (i = ACCPixels; i <= ACPPixels; i++)
      if (ptr->index[i])
	Xfree ((char *) ptr->index[i]);
    Xfree ((char *) ptr);
    ptr = pnext;
  }
  MUXPixelMaps[ConnectionNumber (dpy)] = NullPixelMapPtr;
}

static
void
SetIndex (pmap_ptr, index, which, call)
  PixelMapPtr pmap_ptr;
  int index, which, call;

{
  pmap_ptr->index[which] = (int *)
    Xrealloc ((char *) pmap_ptr->index[which],
	      pmap_ptr->pr_calls[call] * sizeof (int));
  if (!pmap_ptr->index[which])
    XmuXFatalError ("SetIndex: Can't realloc!\n");
  pmap_ptr->index[which][pmap_ptr->pr_calls[call] - 1] = index;
}

static
int
SharedCell (dpy, window, pixel)
  Display *dpy;
  Window window;
  unsigned long pixel;

{
  register int i;
  register PixelMapPtr ptr;

  /* don't do any mapping on the primary connection */
  if (IsClientDisplay (dpy))
    return (False);

  /*
   * find the right pixel map
   */
  if ((ptr = FindPixelMapPtr (dpy, window)) == NullPixelMapPtr)
    XmuXFatalError ("SharedCell: no map (win 0x%lx, conn %d)!\n ",
		    window, ConnectionNumber (dpy));

  for (i = 0; i < ptr->allocated[SharedPixels]; i++)
    if (pixel == CLIENT_VALUE (ptr, i, SharedPixels))
      return (True);

  return (False);
}
static
int
GetServerMask (ptr, old_mask, new_mask)
  PixelMapPtr ptr;
  unsigned long old_mask, *new_mask;

{
  register int i, k, bits_set, matches;


  if (!ptr->pr_calls[ACCCalls] && !ptr->pr_calls[ACPCalls]) {
    *new_mask = 0L;
    return (False);
  }
  else {
    register int start_planes, end_planes;
    bits_set = 0;
    for (i = 0; i < sizeof (unsigned long); i++)
      if ((1 << i) & old_mask)
	bits_set++;

    /*
     * Is it a private cell allocated by XAllocColorCells ?
     */
    /* generate plane values for every call */
    for (k = 0; k < ptr->pr_calls[ACCCalls]; k++) {
      *new_mask = 0L;
      matches = 0;
      start_planes = INDEX (ptr, k, ACCPlanes);
      if (k < ptr->pr_calls[ACCCalls] - 1)
	end_planes = INDEX (ptr, k + 1, ACCPlanes);
      else
	end_planes = ALLOCATED (ptr, PseudoPlanes);
      for (i = start_planes; i < end_planes; i++)
	if (CLIENT_VALUE (ptr, i, PseudoPlanes) & old_mask) {
	  matches++;
	  *new_mask |= SERVER_VALUE (ptr, i, PseudoPlanes);
	}
      if (matches == bits_set)
	return (True);
    }

    /*
     * last chance: Is it a private cell allocated by XAllocColorPlanes ?
     */
    for (k = 0; k < ptr->pr_calls[ACPCalls]; k++) {
      register unsigned long cl_base, cl_bits, cl_mask, sv_base, sv_bits,
       sv_mask;


      cl_bits =
	sv_bits = 0L;
      cl_mask = CLIENT_RGBS (ptr, k);
      sv_mask = SERVER_RGBS (ptr, k);
      cl_base = lowbit (cl_mask);
      sv_base = lowbit (sv_mask);

      *new_mask = 0L;
      matches = 0;
      /* try all bit combinations */
      while (1) {
	if (old_mask & cl_bits) {
	  matches++;
	  *new_mask |= sv_bits;
	}
	GetNextBitsOrBreak (sv_bits, sv_mask, sv_base);
	GetNextBitsOrBreak (cl_bits, cl_mask, cl_base);
      }
      if (matches == bits_set)
	return (True);
    }
    /* we fall through all loops, so we didn't find a match */
    *new_mask = 0L;
    return (False);
  }
}

/*
 * GetServerPixel: Tries to map a pixel passed by the client to the
 * corresponding pixel value on the (multiplex'ed) server side.
 * 
 * Since we're allowing private color cell allocation, we have to use a
 * try-and-error algorithm (best case first, worst case last): First try to
 * map the pixel using the shared cells. On failure, try to map the pixel to
 * a private cell allocated via XAllocColorCells. If even that step fails,
 * look at the private cells allocated via XAllocColorPlanes. The second and
 * third case indicate, that we may have to map planes too. To make this
 * plane mapping faster, we return in index and call the place we have to
 * look for.
 */

static
unsigned long
GetServerPixel (ptr, pixel)
  PixelMapPtr ptr;
  unsigned long pixel;

{
  register int i;

  /*
   * Is it a shared color cell ?
   */
  if (ptr->sorted) {
    int l, m, r;

    /* use binary search */
    l = 0;
    r = ptr->allocated[SharedPixels] - 1;
    m = (l + r) >> 1;

    while (l <= r) {
      if (CLIENT_VALUE (ptr, m, SharedPixels) == pixel)
	return (SERVER_VALUE (ptr, m, SharedPixels));
      else if (CLIENT_VALUE (ptr, m, SharedPixels) < pixel)
	l = m + 1;
      else
	r = m - 1;
      m = (r + l) >> 1;
    }
  }
  else {
    /* use linear search */
    for (i = 0;
	 i < ptr->allocated[SharedPixels]; i++)
      if (CLIENT_VALUE (ptr, i, SharedPixels) == pixel)
	return (SERVER_VALUE (ptr, i, SharedPixels));
  }

  /*
   * nothing found ; if client hasn't allocated private cells return the
   * original value
   */
  if (!ptr->pr_calls[ACCCalls] && !ptr->pr_calls[ACPCalls])
    return (pixel);
  else {
    unsigned long used_planes, base_pixel;
    register int start_planes, end_planes, start_pixels, end_pixels, j,
     k, found;

    /*
     * This is how we map a private color cell (pixel value): 1. Strip off
     * the used plane masks to get the base pixel 2. Map the base pixel 3. OR
     * the mapped base pixel with the mapped plane masks PROBLEM: The only
     * thing that's unique is the base pixel value (you can't get the same
     * base pixel values from different calls to XAllocColorCells), whereas
     * one plane_mask may occur several times. So, we must use a
     * try-and-error tecnique here: Do the three steps above for every
     * "private call" until you find the recalculated base pixel value in
     * that part of the private pixel array.
     */

    /*
     * Is it a private cell allocated by XAllocColorCells ?
     */
    /* generate pixel values for every call */
    for (k = 0; k < ptr->pr_calls[ACCCalls]; k++) {

      start_pixels = INDEX (ptr, k, ACCPixels);
      start_planes = INDEX (ptr, k, ACCPlanes);

      if (k < ptr->pr_calls[ACCCalls] - 1) {
	end_planes = INDEX (ptr, k + 1, ACCPlanes);
	end_pixels = INDEX (ptr, k + 1, ACCPixels);
      }
      else {
	end_planes = ALLOCATED (ptr, PseudoPlanes);
	end_pixels = ALLOCATED (ptr, PseudoPixels);
      }
      used_planes = 0L;
      base_pixel = pixel;

      for (i = start_planes; i < end_planes; i++) {

	/*
	 * 1. Strip off the planes ( and implicitly "map" them )
	 */
	if (base_pixel & CLIENT_VALUE (ptr, i, PseudoPlanes)) {
	  base_pixel &= ~CLIENT_VALUE (ptr, i, PseudoPlanes);
	  used_planes |= SERVER_VALUE (ptr, i, PseudoPlanes);
	}
      }

      /*
       * Try to find the base pixel
       */
      for (j = start_pixels; j < end_pixels; j++)
	if (CLIENT_VALUE (ptr, j, PseudoPixels) == base_pixel) {
	  /* that's it! */
	  base_pixel = SERVER_VALUE (ptr, j, PseudoPixels);
	  return (base_pixel | used_planes);
	}
    }

    /*
     * last chance: Is it a private cell allocated by XAllocColorPlanes ?
     */
    for (k = 0; k < ptr->pr_calls[ACPCalls]; k++) {
      register unsigned long cl_bit, cl_planes, sv_bit, sv_planes;

      start_pixels = INDEX (ptr, k, ACCPixels);

      if (k < ptr->pr_calls[ACPCalls] - 1)
	end_pixels = INDEX (ptr, k + 1, ACPPixels);
      else
	end_pixels = ALLOCATED (ptr, DirectPixels);

      base_pixel = pixel;
      used_planes = 0L;

      /* always three masks (red,green,blue) */
      for (i = 0; i < PLANES_RGB; i++) {

	/*
	 * 1. Strip off the planes ( and implicitly "map" them ) here, plane
	 * is really a set of planes must try all "single-bit-set"
	 * combinations for red_mask, green_mask, blue_mask
	 */
	cl_bit =
	  sv_bit = 0L;
	cl_planes = CLIENT_VALUE (ptr, k * PLANES_RGB + i,
				  DirectPlanes);
	sv_planes = SERVER_VALUE (ptr, k * PLANES_RGB + i,
				  DirectPlanes);
	while (cl_planes) {
	  if (base_pixel & cl_bit) {
	    base_pixel &= ~cl_bit;
	    used_planes |= ~sv_bit;
	  }

	  /*
	   * NOTE: sv_planes and cl_planes have the same number of bis set!
	   */
	  cl_bit = lowbit (cl_planes);
	  cl_planes &= ~cl_bit;
	  sv_bit = lowbit (sv_planes);
	  sv_planes &= ~sv_bit;
	}
      }

      /*
       * all planes are stripped off; try to find the base pixel
       */

      for (j = start_pixels; j < end_pixels; j++)
	if (CLIENT_VALUE (ptr, j, DirectPixels) == base_pixel) {
	  /* that's it! */
	  base_pixel = SERVER_VALUE (ptr, j, DirectPixels);
	  return (base_pixel | used_planes);
	}
    }

    /* we're out of luck, return the unchanged value */
    return (pixel);
  }
}

static
MapPlanes (i, ptr, offset, cl_mask, sv_mask)
  int i;
  PixelMapPtr ptr;
  int offset;
  unsigned long *cl_mask, *sv_mask;

{
  register int j;

  *cl_mask =
    *sv_mask = 0L;

  for (j = 0; (1L << j) <= i; j++)
    if ((1L << j) & i) {
      *cl_mask |= CLIENT_VALUE (ptr, offset + j,
				PseudoPlanes);
      *sv_mask |= SERVER_VALUE (ptr, offset + j,
				PseudoPlanes);
    }
}

/*
 * pixel caches are restricted to 8 plane machines
 */
#define CACHE_SIZE 256
unsigned long _theCache[CACHE_SIZE];

static
unsigned long
*
PixelMapCache (ptr, length)
  PixelMapPtr ptr;
  int *length;

{
  register int i, k, start_pixels, end_pixels;

  unsigned long cl_pixel, sv_pixel, cl_mask, sv_mask;

  for (i = 0; i < CACHE_SIZE; i++)
    /* garbage in, garbage out */
    _theCache[i] = i;

  /* the shared cells */
  for (i = 0; i < ptr->allocated[SharedPixels]; i++)
    _theCache[CLIENT_VALUE (ptr, i, SharedPixels)] =
      SERVER_VALUE (ptr, i, SharedPixels);

  /*
   * the private cells from XAllocColorCells: generate pixel values for every
   * call
   */
  for (k = 0; k < ptr->pr_calls[ACCCalls]; k++) {
    register int num_planes;

    start_pixels = INDEX (ptr, k, ACCPixels);
    if (k < ptr->pr_calls[ACCCalls] - 1) {
      num_planes = (int) (1L << (INDEX (ptr, k + 1, ACCPlanes) -
				 INDEX (ptr, k, ACCPlanes)));
      end_pixels = INDEX (ptr, k + 1, ACCPixels);
    }
    else {
      num_planes = (int) (1L << (ALLOCATED (ptr, PseudoPlanes) -
				 INDEX (ptr, k, ACCPlanes)));
      end_pixels = ALLOCATED (ptr, PseudoPixels);
    }

    for (i = 0; i < num_planes; i++) {
      register int j;

      MapPlanes (i, ptr, INDEX (ptr, k, ACCPlanes),
		 &cl_mask, &sv_mask);

      for (j = start_pixels; j < end_pixels; j++) {
	cl_pixel = CLIENT_VALUE (ptr, j, PseudoPixels) | cl_mask;
	sv_pixel = SERVER_VALUE (ptr, j, PseudoPixels) | sv_mask;
	if (cl_pixel > CACHE_SIZE)
	  return ((unsigned long *) NULL);
	else
	  _theCache[cl_pixel] = sv_pixel;
      }
    }
  }

  /*
   * generate all pixels allocated by XAlocColorPlanes ; REMEMBER: the
   * red/green/blue plane masks  are sets of planes  Must generate all pixels *
   * 2^( red + blue + green ) combinations
   */
  for (k = 0; k < ptr->pr_calls[ACPCalls]; k++) {
    register unsigned long cl_bits, sv_bits, cl_base, sv_base;
    cl_bits =
      sv_bits = 0L;
    cl_mask = CLIENT_RGBS (ptr, k);
    sv_mask = SERVER_RGBS (ptr, k);
    cl_base = lowbit (cl_mask);
    sv_base = lowbit (sv_mask);

    start_pixels = INDEX (ptr, k, ACCPixels);
    if (k < ptr->pr_calls[ACPCalls] - 1)
      end_pixels = INDEX (ptr, k + 1, ACCPixels);
    else
      end_pixels = ALLOCATED (ptr, DirectPixels);

    while (1) {
      register int j;

      for (j = start_pixels; j < end_pixels; j++) {
	cl_pixel = CLIENT_VALUE (ptr, j, DirectPixels) | cl_bits;
	sv_bits = SERVER_VALUE (ptr, j, DirectPixels) | sv_bits;
	if (cl_pixel > CACHE_SIZE)
	  return ((unsigned long *) NULL);
	else
	  _theCache[cl_pixel] = sv_pixel;
      }
      /* macro from X server sources */
      GetNextBitsOrBreak (sv_bits, sv_mask, sv_base);
      GetNextBitsOrBreak (cl_bits, cl_mask, cl_base);
    }
  }

  *length = CACHE_SIZE;
  return (&_theCache[0]);
}

void
InitPixelMaps ()
{
  register int i;

  for (i = 0; i < MAXSOCKS; i++)
    MUXPixelMaps[i] = NullPixelMapPtr;
}

void
XmuXInsertSharedCell (dpy, cmap, client_pixel, server_pixel)
  Display *dpy;
  Colormap cmap;
  unsigned long client_pixel, server_pixel;

{
  register PixelMapPtr ptr;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXInsertSharedCell");
#endif

  ptr = MUXPixelMaps[ConnectionNumber (dpy)];
  while (ptr != NullPixelMapPtr && ptr->cmap != cmap)
    ptr = ptr->next;
  if (ptr == NullPixelMapPtr)
    ptr = AllocPixelMap (dpy, cmap);

  if (MUST_REALLOC (ptr, 1, SharedPixels))
    ReallocPixelValues (dpy, ptr, 1, SharedPixels);

  CLIENT_VALUE (ptr, ALLOCATED (ptr, SharedPixels), SharedPixels) =
    client_pixel;
  SERVER_VALUE (ptr, ALLOCATED (ptr, SharedPixels), SharedPixels) =
    server_pixel;
  ptr->allocated[SharedPixels]++;

}

void
XmuXInsertPrivateCells (dpy, cmap, client_pixels, server_pixels,
			n_pixels, client_planes, server_planes, n_planes,
			AllocColorCells)
  Display *dpy;
  Colormap cmap;
  unsigned long *client_pixels, *server_pixels, *client_planes, *server_planes;
  int n_pixels, n_planes, AllocColorCells;	/* cells from
						 * XAllocColorCells ? */

{
  register PixelMapPtr ptr;
  register int which_pixels, which_planes;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXInsertPrivateCells");
#endif

  ptr = MUXPixelMaps[ConnectionNumber (dpy)];
  while (ptr != NullPixelMapPtr && ptr->cmap != cmap)
    ptr = ptr->next;

  if (ptr == NullPixelMapPtr)
    ptr = AllocPixelMap (dpy, cmap);

  if (AllocColorCells) {
    if (MUST_REALLOC (ptr, n_pixels, PseudoPixels))
      ReallocPixelValues (dpy, ptr, n_pixels, PseudoPixels);
    if (MUST_REALLOC (ptr, n_planes, PseudoPlanes))
      ReallocPixelValues (dpy, ptr, n_planes, PseudoPlanes);

    ptr->pr_calls[ACCCalls]++;

    SetPseudoPixelIndex (ptr, ALLOCATED (ptr, PseudoPixels));
    SetPseudoPlaneIndex (ptr, ALLOCATED (ptr, PseudoPlanes));
    which_pixels = PseudoPixels;
    which_planes = PseudoPlanes;
  }
  else {
    if (MUST_REALLOC (ptr, n_pixels, DirectPixels))
      ReallocPixelValues (dpy, ptr, n_pixels, DirectPixels);
    if (MUST_REALLOC (ptr, n_planes, DirectPlanes))
      ReallocPixelValues (dpy, ptr, n_planes, DirectPlanes);

    ptr->pr_calls[ACPCalls]++;

    SetDirectPixelIndex (ptr, ALLOCATED (ptr, DirectPixels));
    which_pixels = DirectPixels;
    which_planes = DirectPlanes;
  }

  while (--n_pixels >= 0) {

    CLIENT_VALUE (ptr,
		  ALLOCATED (ptr, which_pixels), which_pixels) =
      client_pixels[n_pixels];
    SERVER_VALUE (ptr,
		  ALLOCATED (ptr, which_pixels), which_pixels) =
      server_pixels[n_pixels];
    ptr->allocated[which_pixels]++;
  }


  while (--n_planes >= 0) {

    CLIENT_VALUE (ptr,
		  ALLOCATED (ptr, which_planes), which_planes) =
      client_planes[n_planes];
    SERVER_VALUE (ptr,
		  ALLOCATED (ptr, which_planes), which_planes) =
      server_planes[n_planes];
    ptr->allocated[which_planes]++;
  }
}

unsigned long
XmuXMapPixel (dpy, window, pixel)
  Display *dpy;
  Window window;
  unsigned long pixel;

{
  register PixelMapPtr ptr;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXMapPixel");
#endif

  /* don't do any mapping on the primary connection */
  if (IsClientDisplay (dpy))
    return (pixel);

  /*
   * find the right pixel map
   */
  if ((ptr = FindPixelMapPtr (dpy, window)) == NullPixelMapPtr)
    XmuXFatalError ("XmuXMapPixel: no map (win 0x%lx, conn %d)!\n ",
		    window, ConnectionNumber (dpy));

  return (GetServerPixel (ptr, pixel));
}

void
XmuXMapGCValues (dpy, window, old_fg, old_bg, old_mask,
		 new_fg, new_bg, new_mask, function)
  Display *dpy;
  Window window;
  unsigned long old_fg, old_bg, old_mask, *new_fg, *new_bg, *new_mask,
   function;

{
  register PixelMapPtr ptr;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXMapPixelsAndPlane");
#endif

  /* don't do any mapping on the primary connection */
  if (IsClientDisplay (dpy))
    return;

  /*
   * find the right pixel map
   */
  if ((ptr = FindPixelMapPtr (dpy, window)) == NullPixelMapPtr)
    XmuXFatalError ("XmuXMapGCValues: no map (win 0x%lx, conn %d)!\n ",
		    window, ConnectionNumber (dpy));

  switch (function) {

  case GXxor:
    *new_fg = GetServerPixel (ptr, (old_fg ^ old_bg));
    *new_bg = GetServerPixel (ptr, old_bg);
    *new_fg ^= *new_bg;
    if (!GetServerMask (ptr, old_mask, new_mask))
      *new_mask = old_mask;
    break;

  case GXinvert:
    *new_bg = GetServerPixel (ptr, old_bg);
    *new_fg = GetServerPixel (ptr, old_fg);
    if (!GetServerMask (ptr, old_mask, new_mask))
      *new_mask = *new_fg ^ *new_bg;
    break;

  default:
    *new_bg = GetServerPixel (ptr, old_bg);
    *new_fg = GetServerPixel (ptr, old_fg);
    if (!GetServerMask (ptr, old_mask, new_mask)) {
      if (old_fg == old_mask)
	*new_fg = old_fg;
      *new_mask = old_mask;
    }
    break;
  }
}

int
XmuXMapPixelsAndPlanes (dpy, cmap, pixels, npixels, planes)
  Display *dpy;
  Colormap cmap;
  unsigned long *pixels, *planes;
  int npixels;

{
  int i;
  register PixelMapPtr ptr;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXMapPixelsAndPlane");
#endif

  /*
   * find the right pixel map
   */

  ptr = MUXPixelMaps[ConnectionNumber (dpy)];
  while (ptr != NullPixelMapPtr && cmap != ptr->cmap)
    ptr = ptr->next;

  if (ptr == NullPixelMapPtr)
    XmuXFatalError ("XmuXMapPixelsAndPlanes: no map!\n");

  for (i = 0; i < npixels; i++)
    pixels[i] = GetServerPixel (ptr, pixels[i]);

  if (*planes != 0L)
    if (!GetServerMask (ptr, *planes, planes))
      return (0);
  return (1);
}

int
CompareClPixel (a, b)
  PixelMapValues *a, *b;

{
  if (a->client_value < b->client_value)
    return (-1);
  else if (a->client_value > b->client_value)
    return (1);
  else
    return (0);
}

void
XmuXSortPixelMaps (dpy)
  Display *dpy;

{
  register PixelMapPtr ptr;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXSortPixelMap");
#endif

  for (ptr = MUXPixelMaps[ConnectionNumber (dpy)];
       ptr != NullPixelMapPtr;
       ptr = ptr->next)
    if (!ptr->sorted) {
      XmuXdebug (debug_mux, "Sort pixel map %d...",
		 ConnectionNumber (dpy));
      qsort (&(ptr->maps[SharedPixels][0]),
	     ptr->allocated[SharedPixels],
	     sizeof (PixelMapValues),
	     CompareClPixel);
      ptr->sorted = True;
      XmuXdebug (debug_mux, "done\n");
    }
}

unsigned long
*
CreatePixelMapCache (dpy, window, length)
  Display *dpy;
  Window window;
  int *length;

{
  register PixelMapPtr ptr;

  ptr = FindPixelMapPtr (dpy, window);
  *length = 0;
  if (ptr == NullPixelMapPtr)
    return ((unsigned long *) NULL);
  else
    return (PixelMapCache (ptr, length));
}

/* two macros from XImUtil.c: used in XmuXMapImage */

#define ZNORMALIZE(bp, nbytes, img) \
    if (img->byte_order == MSBFirst) \
	_normalizeimagebits((unsigned char *)(bp), (nbytes), MSBFirst, img->bits_per_pixel, \
	LSBFirst)

#define ZINDEX(x, y, img) ((y) * img->bytes_per_line) + \
    (((x) * img->bits_per_pixel) >> 3)

static unsigned char _reverse_byte[0x100] =
{
  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
  0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
  0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
  0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
  0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
  0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
  0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
  0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
  0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
  0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
  0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
  0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
  0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
  0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
  0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
  0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
  0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
  0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
  0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
  0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
  0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
  0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
  0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
  0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
  0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
  0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
  0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};				/* found in XPutImage */

/*
 * from XImUtil.c
 */
static
_normalizeimagebits (bpt, nb, byteorder, unitsize, bitorder)
  unsigned char *bpt;		/* beginning pointer to image bits */
  int nb;			/* number of bytes to normalize */
  int byteorder;		/* swap bytes if byteorder == MSBFirst */
  int unitsize;			/* size of the bitmap_unit or Zpixel */
  int bitorder;			/* swap bits if bitorder == MSBFirst */
{
  if ((byteorder == MSBFirst) && (byteorder != bitorder)) {
    register char c;
    register unsigned char *bp = bpt;
    register unsigned char *ep = bpt + nb;
    register unsigned char *sp;
    switch (unitsize) {

    case 4:
      do {			/* swap nibble */
	*bp = ((*bp >> 4) & 0xF) | ((*bp << 4) & ~0xF);
	bp++;
      }
      while (bp < ep);
      break;

    case 16:
      do {			/* swap short */
	c = *bp;
	*bp = *(bp + 1);
	bp++;
	*bp = c;
	bp++;
      }
      while (bp < ep);
      break;

    case 24:
      do {			/* swap three */
	c = *(bp + 2);
	*(bp + 2) = *bp;
	*bp = c;
	bp += 3;
      }
      while (bp < ep);
      break;

    case 32:
      do {			/* swap long */
	sp = bp + 3;
	c = *sp;
	*sp = *bp;
	*bp++ = c;
	sp = bp + 1;
	c = *sp;
	*sp = *bp;
	*bp++ = c;
	bp += 2;
      }
      while (bp < ep);
      break;
    }
  }
  if (bitorder == MSBFirst) {
    do {
      *bpt = _reverse_byte[*bpt];
      bpt++;
    }
    while (--nb > 0);
  }
}

#define MAPPIXEL( dpy, window, pcache, pixel, length ) \
    ( pcache != ( unsigned long * ) NULL ) ? \
       ( ( pixel <= length ) ? pcache[ pixel ] : pixel ) :\
    XmuXMapPixel( dpy, window, pixel )\

#define FREE_CACHE( cache )

void
XmuXMapImage (dpy, window, ximage)
  Display *dpy;
  Window window;
  XImage *ximage;

{
  unsigned long pixel;
  register unsigned char *dp, *src, *dst, *tmp;
  register int x;
  int y, length;
  long nbytes, i;
  register unsigned long *pcache;

  /*
   * What shall we do if application uses a private colormap and the
   * specified window in really a pixmap ? (e.g. this mapping results from a
   * call to XCreatePixmapFromBitmapData ) We hope our application is a
   * polite one that creates "real" pixmaps (depth > 1) with the window where
   * it is used with; or to put it in a "politness rule": "Don't use the same
   * pixmap with (windows that use) different colormaps!" We hope our
   * application will follow this rule: if "window" is a pixmap, we retrieve
   * the drawable (and hope that it is a window!) the pixmap was created with
   * and use this as a reference to the "right" colormap!
   */
  if (XmuXUsesPrivateCmaps (dpy) && XmuXIsPixmap (dpy, window)) {
    register Display *dflt_dpy;

    /*
     * XmuXMapImage is called with a multiplexed display, but the information
     * we need here is stored in the default display's resource list
     */
    dflt_dpy = XmuXPrimaryDisplayFromDisplay (dpy);
    /* unmap the window ID */
    window = XmuXMapID (dpy, window, FromServer);
    window = XmuXGetPixmapDrawable (dpy, window);
    /* map the new ID */
    window = XmuXMapID (dpy, window, FromClient);
  }

  /*
   * We want to avoid calling XPutPixel and XGetPixel for every image pixel.
   * We must confess, that we are too lazy to develop a new algorithm here;
   * instead, we modify _XAddPixel and hope that we don't fall in the else
   * case ASSUMPTION: image depth is always greater than one! When called
   * from DuplicatePixmap image is always in ZPixmap format We accelerate
   * pixel mapping here by creating a pixel map cache, where pixel mapping
   * can be done by simple indexing instead of calling XmuXMapPixel (which
   * searches for the right pixel map every time )
   */
  length = 0;
  pcache = CreatePixelMapCache (dpy, window, &length);

  if (ximage->format == XYPixmap) {
    /* this is slow, may do better later */
    for (y = 0; y < ximage->height; y++) {
      for (x = 0; x < ximage->width; x++) {
	pixel = XGetPixel (ximage, x, y);
	pixel = MAPPIXEL (dpy, window, pcache, pixel, length);
	XPutPixel (ximage, x, y, pixel);
      }
    }
  }
  else

    /*
     * This has stupid little-endian assumptions; to fix it you would need to
     * make sure that it copies the right nbytes into and out of the variable
     * "pixel".  Later.
     */

    /*
     * If the bits_per_pixel makes the alignment occur on even byte
     * boundaries, perform the addition by stepping thru the data one pixel
     * at a time.  Otherwise, do it the slow way by calling get and put
     * pixel.
     */
  if ((ximage->bits_per_pixel & 7) == 0) {
    nbytes = (ximage->bits_per_pixel + 7) >> 3;
    for (y = 0; y < ximage->height; y++) {
      src = (unsigned char *) &ximage->data[ZINDEX (0, y, ximage)];
      dst = src;
      for (x = 0; x < ximage->width; x++) {
	pixel = 0;
	tmp = (unsigned char *) &pixel;
	for (i = 0; i < nbytes; i++)
	  *tmp++ = *src++;
	ZNORMALIZE (&pixel, nbytes, ximage);
	pixel = MAPPIXEL (dpy, window, pcache,
			  pixel, length);
	ZNORMALIZE (&pixel, nbytes, ximage);
	tmp = (unsigned char *) &pixel;
	for (i = 0; i < nbytes; i++)
	  *dst++ = *tmp++;
      }
    }
  }
  else {
    for (y = 0; y < ximage->height; y++) {
      for (x = 0; x < ximage->width; x++) {
	pixel = XGetPixel (ximage, x, y);
	pixel = XmuXMapPixel (dpy, window, pixel);
	XPutPixel (ximage, x, y, pixel);
      }
    }
  }

  FREE_CACHE (pcache);
}

#undef MAPPIXEL

int
XmuXMapDepth (dpy, depth, drawable, type)
  Display *dpy;
  unsigned int depth;
  Drawable drawable;
  int type;

{
  unsigned int i;
  long sup_depths;

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXMapDepth");
#endif

  /*
   * depth 0 is always supported ( depth of input-only windows )
   */
  if (depth == 0)
    return (0);

  if ((type == IsPixmap) ||
      ((type == DontKnow) && XmuXIsPixmap (dpy, drawable))) {
    sup_depths =
      ConnectionTranslation[ConnectionNumber (dpy)]->all_depths;

    /*
     * supported depths is a bit array; check if dpy supports depths greater
     * or equal than the requested
     */
    if (sup_depths >= (1L << (depth - 1))) {
      for (i = depth; i < sizeof (long) << 3; i++)
	if (sup_depths & (1L << (i - 1)))
	  return (i);
    }
    else
      for (i = depth - 1; i >= 0; i--)
	if (sup_depths & (1L << (i - 1)))
	  return (i);
  }

  /*
   * it's a window --> use the corresponding visual to find the right depth
   */
  else {
    if (XmuXDepthViaVisual (dpy, drawable, &i))
      return (i);
    else {

      /*
       * still possible, e.g call from FindBestVisual
       */
      sup_depths =
	ConnectionTranslation[ConnectionNumber (dpy)]->vis_depths;

      /*
       * supported depths is a bit array; check if dpy supports depths
       * greater or equal than the requested
       */
      if (sup_depths >= (1L << (depth - 1))) {
	for (i = depth; i < sizeof (long) << 3; i++)
	  if (sup_depths & (1L << (i - 1)))
	    return (i);
      }
      else
	for (i = depth - 1; i >= 0; i--)
	  if (sup_depths & (1L << (i - 1)))
	    return (i);
    }
  }
}

void
XmuXMapColors (dpy, cmap, colors, ncolors)
  Display *dpy;
  Colormap cmap;
  XColor *colors;
  int ncolors;

{
  PixelMapPtr ptr;
  int length;
  register unsigned long *pcache;

  ptr = MUXPixelMaps[ConnectionNumber (dpy)];
  while (ptr != NullPixelMapPtr && cmap != ptr->cmap)
    ptr = ptr->next;

  if (ptr == NullPixelMapPtr)
    XmuXFatalError ("XmuXMapColors: no map!\n");

  length = 0;
  pcache = PixelMapCache (ptr, &length);
  if (!length) {
    while (--ncolors >= 0)
      colors[ncolors].pixel =
	GetServerPixel (ptr, colors[ncolors].pixel);

  }
  else {
    while (--ncolors >= 0)
      colors[ncolors].pixel = pcache[colors[ncolors].pixel];
    FREE_CACHE (pcache);
  }
}

XmuXFreeMaps (dpy)
  Display *dpy;

{

#ifdef DEBUG
  CheckDisplay (dpy, "XmuXFreeMaps");
#endif

  DeleteIDMap (dpy);
  DeleteCmapTable (dpy);
  DeletePixelMaps (dpy);

}
