/* $Id: resources.c,v 1.2 90/11/29 10:05:01 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.
 */
#define NEED_RESOURCES		/* we need the resource type defs here */

#include "XmuXlibint.h"

#include "map.h"

#define ALLOC_AT_ONCE 5

#ifndef XmuXMask
#define XmuXCreate 1		/* -- same as in resources.h -- */
#define XmuXUpdate 2

#define XmuXMask ( PropertyChangeMask )
#endif

#define XMUXGCFLAGS ( GCTile | GCStipple | GCClipMask |\
		      GCFont | GCForeground | GCBackground )

#define RIGHTTYPE( ptr ) \
	   ( ptr->type &  ( RT_WINDOW ) )

static
MUXResourceInfoPtr _FreeInfos = NullResourceInfoPtr;

static void
  PrintWindowInfo (),
  PrintGCInfo (),
  PrintPixmapInfo (),
  PrintCursorInfo (),
  PrintFontInfo ();


static long
ResourceType (dpy, rID)
  Display *dpy;
  XID rID;
{
  register MUXResourceInfoPtr entry;

  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    if (entry->id == rID)
      return (entry->type);
  return (RT_INVALID);
}

/*
 * CompareDepth:  compare resource info's depth with the depth passed
 *		  as second argument
 * 
 * Need this if drawable that was used to create a GC has gone and we have
 * to find another drawable with the same depth
 */
static int
CompareDepth (info, depth)
  MUXResourceInfoPtr info;
  int depth;
{
  /* code assumes that info->type RT_WINDOW or RT_PIXMAP */

  if (info->type & RT_WINDOW)
    return (info->u.win_info->depth == depth);
  else
    return (info->u.pmap_info->depth == depth);
}

/*
 * GetDepth : find depth of resource rID
 * 
 * Need this in InsertWindowInfo/InsertGCInfo: must retrieve depth from
 * parent/drawable resource info
 */
static int
GetDepth (dpy, rID, depth)
  Display *dpy;
  XID rID;
  int *depth;
{
  MUXResourceInfoPtr entry;

  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    if (entry->id == rID) {
      if (!entry->used)
	return (False);
      if (entry->type & RT_WINDOW) {
	*depth = entry->u.win_info->depth;
	return (True);
      }
      else if (entry->type & RT_PIXMAP) {
	*depth = entry->u.pmap_info->depth;
	return (True);
      }
      else {
	/*-- wrong type -- */
	XmuXErrorF ("GetDepth: Wrong type %d!\n",
		    entry->type);
	return (False);
      }
    }

  return (False);
}

/*
 * GetVisualID: determine the visual ID that was used to create this
 *		resource (window)
 * 
 * Need this in InsertWindowInfo if window was created the simple way
 * (visual == CopyFromParent)
 */
int
GetVisualID (dpy, rID, vid)
  Display *dpy;
  XID rID, *vid;
{
  MUXResourceInfoPtr entry;

  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    if (entry->id == rID) {
      if (!entry->used)
	return (False);
      if (entry->type & RT_WINDOW) {
	*vid = entry->u.win_info->visualid;
	return (True);
      }
      else {
	/*-- wrong type -- */
	XmuXErrorF ("GetVisualID: Wrong type %d!\n",
		    entry->type);
	return (False);
      }
    }

  return (False);
}

/*
 * RemoveDrawableReference: Reset drawable attribute in GC/Colormap/Pixmap
 * infos whose value is equal to drawable (second argument)
 */
static
RemoveDrawableReference (dpy, drawable)
  Display *dpy;
  Drawable drawable;
{
  MUXResourceInfoPtr entry;

#ifdef DEBUG
  CheckDisplay (dpy, "GetClientResource");
#endif
  XmuXdebug (debug_resources, "\nreference 0x%lx in ", drawable);

  /*
   * remove all references (i.e. info->drawabale == drawable) from the
   * list in gc_infos, cmap_infos, pmap_infos
   */
  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    switch (entry->type) {

    case RT_GC:
      if (entry->u.gc_info->drawable == drawable) {
	XmuXdebug (debug_resources, " GC 0x%lx",
		   entry->id);
	entry->u.gc_info->drawable = None;
      }
      break;

    case RT_PIXMAP:
      if (entry->u.pmap_info->drawable == drawable) {
	XmuXdebug (debug_resources, " Pixmap 0x%lx",
		   entry->id);
	entry->u.pmap_info->drawable = None;
      }
      break;

    case RT_COLORMAP:
      if (entry->u.cmap_info->window == drawable) {
	XmuXdebug (debug_resources, " Colormap 0x%lx",
		   entry->id);
	entry->u.cmap_info->window = None;
      }
      break;

    default:
      break;
    }
  XmuXdebug (debug_resources, "removed\n");
}

/*
 * NewResource:  local resource allocation routine
 */
static MUXResourceInfoPtr
NewResource ()
{
  register MUXResourceInfoPtr new;

  if (_FreeInfos == NullResourceInfoPtr) {
    if ((new = (MUXResourceInfoPtr)
	 Xmalloc (sizeof (MUXResourceInfo))) == NullResourceInfoPtr)
      XmuXFatalError ("NewResource: Out of memory!\n");
  }
  else {
    new = _FreeInfos;
    _FreeInfos = _FreeInfos->next;
    new->next = NullResourceInfoPtr;
  }
  return (new);
}

/*
 * FreeResourceInfo: local deallocation routine
 */
static void
FreeResourceInfo (info)
  MUXResourceInfoPtr info;
{
  XmuXdebug (debug_resources, "resource 0x%lx (type RT_", info->id);
  switch (info->type) {
  case RT_WINDOW:
    XmuXdebug (debug_resources, "WINDOW) removed\n");
    Xfree ((char *) info->u.win_info);
    break;

  case RT_GC:
    XmuXdebug (debug_resources, "GC) removed\n");

    /* free the gc here ( is hasn't been freed in XFreeGC !!) */
    Xfree ((char *) info->u.gc_info->gc);
    Xfree ((char *) info->u.gc_info);
    break;

  case RT_PIXMAP:
    XmuXdebug (debug_resources, "PIXMAP) removed\n");
    Xfree ((char *) info->u.pmap_info);
    break;

  case RT_CURSOR:
    XmuXdebug (debug_resources, "CURSOR) removed\n");
    Xfree ((char *) info->u.curs_info);
    break;

  case RT_FONT:
    XmuXdebug (debug_resources, "FONT) removed\n");
    Xfree ((char *) info->u.font_info->name);
    Xfree ((char *) info->u.font_info);
    break;

  case RT_COLORMAP:
    XmuXdebug (debug_resources, "COLORMAP) removed\n");
    Xfree ((char *) info->u.cmap_info->sh_cells);
    while (info->u.cmap_info->pr_cells) {
      register MUXPrivateColorCells *pnext =
      info->u.cmap_info->pr_cells->next;
      Xfree ((char *) info->u.cmap_info->pr_cells->pixels);
      if (info->u.cmap_info->pr_cells->planes)
	Xfree ((char *) info->u.cmap_info->pr_cells->planes);
      Xfree ((char *) info->u.cmap_info->pr_cells);
      info->u.cmap_info->pr_cells = pnext;
    }
    Xfree ((char *) info->u.cmap_info);
    break;
  case RT_GCPIXELS:
    XmuXdebug (debug_resources, "GCPIXELS) removed\n");
    Xfree ((char *) info->u.gcpix_info);
    break;
  }

  info->used = False;
  info->next = _FreeInfos;
  _FreeInfos = info;
}

/*
 * A kind of "generic" allocation routine: it allocates a new
 * MUXResourceInfo structure and the type specific data (the resource type
 * is passed as an argument). Initializes the new resource info with valid
 * values
 */
static  MUXResourceInfoPtr
AllocAndInitResource (type, id)
  int type;			/* the resource type */
  XID id;
{
  MUXResourceInfoPtr new;

  if ((new = NewResource ()) == NullResourceInfoPtr)
    XmuXFatalError ("Out of memory! Can't allocate new resource!\n");

  new->next = NullResourceInfoPtr;
  new->used = True;
  new->id = id;

  /* allocate space for type specific data */
  switch (type) {
  case RT_WINDOW:
    if ((new->u.win_info = (MUXWindowInfoPtr)
	 Xmalloc (sizeof (MUXWindowInfo))) ==
	NullWindowInfoPtr)
      XmuXFatalError ("Out of memory! Can't allocate new resource!\n");
    new->type = RT_WINDOW;
    new->u.win_info->window = id;
    new->u.win_info->bg_pixel = 1L;
    new->u.win_info->border_pixel = 0L;
    new->u.win_info->bg_pmap = None;
    new->u.win_info->border_pmap = CopyFromParent;
    new->u.win_info->cursor = None;
    new->u.win_info->flags = 0L;
    new->u.win_info->grabs = 0L;
    new->u.win_info->button_grabs = NULL;
    new->u.win_info->key_grabs = NULL;
    new->u.win_info->hidden = False;
    break;

  case RT_GC:
    if ((new->u.gc_info = (MUXGCInfoPtr)
	 Xmalloc (sizeof (MUXGCInfo))) ==
	NullGCInfoPtr)
      XmuXFatalError ("Out of memory! Can't allocate new resource!\n");
    new->type = RT_GC;
    new->u.gc_info->gid = id;
    new->u.gc_info->flags = 0L;
    break;

  case RT_PIXMAP:
    if ((new->u.pmap_info = (MUXPixmapInfoPtr)
	 Xmalloc (sizeof (MUXPixmapInfo))) ==
	NullPixmapInfoPtr)
      XmuXFatalError ("Out of memory! Can't allocate new resource!\n");
    new->type = RT_PIXMAP;
    new->u.pmap_info->pixmap = id;
    break;

  case RT_CURSOR:
    if ((new->u.curs_info = (MUXCursorInfoPtr)
	 Xmalloc (sizeof (MUXCursorInfo))) ==
	NullCursorInfoPtr)
      XmuXFatalError ("Out of memory! Can't allocate new resource!\n");
    new->type = RT_CURSOR;
    new->u.curs_info->cursor = id;
    break;

  case RT_FONT:
    if ((new->u.font_info = (MUXFontInfoPtr)
	 Xmalloc (sizeof (MUXFontInfo))) ==
	NullFontInfoPtr)
      XmuXFatalError ("Out of memory! Can't allocate new resource!\n");
    new->type = RT_FONT;
    new->u.font_info->font = id;
    break;

  case RT_COLORMAP:
    if ((new->u.cmap_info = (MUXColormapInfoPtr)
	 Xmalloc (sizeof (MUXColormapInfo))) ==
	NullColormapInfoPtr)
      XmuXFatalError ("Out of memory! Can't allocate new resource!\n");
    new->type = RT_COLORMAP;
    new->u.cmap_info->colormap = id;
    new->u.cmap_info->sh_allocated = 0;
    new->u.cmap_info->sh_cells = NULL;
    new->u.cmap_info->pr_cells = NULL;
    break;

  case RT_GCPIXELS:
    if ((new->u.gcpix_info = (MUXGCPixelsInfoPtr)
	 Xmalloc (sizeof (MUXGCPixelsInfo))) ==
	NullGCPixelsInfoPtr)
      XmuXFatalError ("Out of memory! Can't allocate new resource!\n");
    new->type = RT_GCPIXELS;
    new->u.gcpix_info->gid = id;
    break;
  default:
    XmuXFatalError ("No resource allocation (wrong resource type)!\n");
    break;
  }
  return (new);
}

/*
 * We use three "generic" routines:
 *	InsertClientResource - to insert a new resource information
 *	GetClientResource    - to retrieve an existing resource information
 *	RemoveClientResource - to remove an existing resource information
 *
 * For every resource type, we use "specific" routines that call the
 * corresponding "generic" with the appropriate type argument
 */

static void
InsertClientResource (dpy, info_ptr)
  Display *dpy;
  MUXResourceInfoPtr info_ptr;
{

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

  if (MUXClientResources[ConnectionNumber (dpy)].tail ==
      NullResourceInfoPtr)
    MUXClientResources[ConnectionNumber (dpy)].head =
      MUXClientResources[ConnectionNumber (dpy)].tail = info_ptr;
  else {
    MUXClientResources[ConnectionNumber (dpy)].tail->next =
      info_ptr;
    MUXClientResources[ConnectionNumber (dpy)].tail =
      MUXClientResources[ConnectionNumber (dpy)].tail->next;
  }
}

static  MUXResourceInfoPtr
GetClientResource (dpy, rID, type)
  Display *dpy;
  XID rID;
  long type;
{
  MUXResourceInfoPtr entry;

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

  /*
   * use resource ID and resource type to find the right entry special
   * case: resource type == RT_SUBWINDOW --> use parent ID
   * to find the right entry --> only return entries that
   * are still used
   */
  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    if (type & RT_SUBWINDOW) {
      if ((entry->type & RT_WINDOW) &&
	  entry->u.win_info->parent == rID && entry->used)
	return (entry);
    }
    else if (entry->id == rID && (entry->type & type) && entry->used)
      /* found it */
      return (entry);

  /* we've failed */
  return (NullResourceInfoPtr);
}

static  MUXResourceInfoPtr
RemoveClientResource (dpy, rID, type)
  Display *dpy;
  XID rID;
  long type;
{
  MUXResourceInfoPtr curr, pred;

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

  curr =
    pred = MUXClientResources[ConnectionNumber (dpy)].head;

  /*
   * use resource ID and resource type to find the right entry special
   * case: resource type == RT_SUBWINDOW --> use parent ID 
   * to find the right entry
   */
  if (curr == NullResourceInfoPtr) {
    XmuXErrorF ("Resource 0x%lx not removed (list %d empty)!\n",
		rID, ConnectionNumber (dpy));
    return (NullResourceInfoPtr);
  }

  /* special case: remove the head of the list */
  if (((type & RT_SUBWINDOW) && (curr->type & RT_WINDOW) &&
       curr->u.win_info->parent == rID) ||
      (curr->id == rID && (curr->type & type))) {
    /* is there only one entry in the list? */
    if (MUXClientResources[ConnectionNumber (dpy)].head ==
	MUXClientResources[ConnectionNumber (dpy)].tail)
      MUXClientResources[ConnectionNumber (dpy)].head =
	MUXClientResources[ConnectionNumber (dpy)].tail = NullResourceInfoPtr;

    else
      MUXClientResources[ConnectionNumber (dpy)].head = curr->next;

    curr->next = NullResourceInfoPtr;
  }
  /* search through the list */
  else
    for (
	  curr = curr->next;
	  curr != NullResourceInfoPtr;
	  pred = curr, curr = curr->next
      )

      /*
       * We've found it?  Note special case for removing subwindows
       */
      if (((type & RT_SUBWINDOW) && (curr->type & RT_WINDOW) &&
	   curr->u.win_info->parent == rID) ||
	  (curr->id == rID && (curr->type & type))) {
	pred->next = curr->next;
	curr->next = NullResourceInfoPtr;
	/* do we remove the tail? */
	if (curr == MUXClientResources[ConnectionNumber (dpy)].tail)
	  MUXClientResources[ConnectionNumber (dpy)].tail = pred;
	/* exit the loop */
	break;
      }

  /* if we haven't found it, curr == NullResourceInfoPtr */
  return (curr);
}

#define FREEANDADVANCE( ptr )  \
            {   register MUXResourceInfoPtr tmp = ptr ;\
		ptr = ptr->next ;\
		tmp->next = NullResourceInfoPtr ;\
		FreeResourceInfo( tmp ) ;\
	    }

/*
 * XmuXCleanupResources: remove all unused infos from the resource list
 */
void
XmuXCleanupResources (dpy)
  Display *dpy;
{
  MUXResourceInfoPtr curr, pred;

  if (!MUXClientResources[ConnectionNumber (dpy)].update)
    /* no previously freed resource info in list */
    return;


  XmuXdebug (debug_resources, "Cleanup resource list %d\n",
	     ConnectionNumber (dpy));

  curr = MUXClientResources[ConnectionNumber (dpy)].head;

  if (curr == NullResourceInfoPtr)
    /* list is empty, nothing to do */
    return;
  /* imaginary list element BEFORE head of list */
  pred = NullResourceInfoPtr;

  while (curr != NullResourceInfoPtr) {
    /* next candidate? */
    if (!curr->used) {
      /* head of list? */
      if (pred == NullResourceInfoPtr) {
	/* only one list element? */
	if (MUXClientResources[ConnectionNumber (dpy)].head ==
	    MUXClientResources[ConnectionNumber (dpy)].tail)
	  MUXClientResources[ConnectionNumber (dpy)].head =
	    MUXClientResources[ConnectionNumber (dpy)].tail =
	    NullResourceInfoPtr;
	else
	  MUXClientResources[ConnectionNumber (dpy)].head =
	    curr->next;
	/* free the entry */
	FREEANDADVANCE (curr);
      }
      else {
	pred->next = curr->next;
	/* remove the tail? */
	if (curr == MUXClientResources[ConnectionNumber (dpy)].tail)
	  MUXClientResources[ConnectionNumber (dpy)].tail =
	    pred;
	FREEANDADVANCE (curr);
      }
    }
    else {
      /* advance in list */
      pred = curr;
      curr = curr->next;
    }
  }
  MUXClientResources[ConnectionNumber (dpy)].update = False;
}

#undef FREEANDADVANCE

/*
 * two additional routines used to handle top-level windows in 
 * XCreateWindow, XCreateSimpleWindows and XmuXReplicate
 */

/*
 * XmuXIsRootWindow: find out if window is a root window
 * 
 * If window is a root window, return the screen number, else return -1
 */
int
XmuXIsRootWindow (dpy, window)
  Display *dpy;
  Window window;
{
  register int i;

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

  for (i = 0; i < ScreenCount (dpy); i++)
    if (window == RootWindow (dpy, i))
      /* return the screen number */
      return (i);

  return (-1);
}

/*
 * XmuXMustAddEvents: find out if we must add events for this window
 */
Bool
XmuXMustAddEvents (dpy, wid, mode)
  Display *dpy;
  Window wid;
  int mode;			/* if mode == XmuXCreate, wid is the
				 * parent ID else it's a top-level ID */
{

  if (mode == XmuXCreate)
    return (XmuXStoreResources (dpy) &&
	    (XmuXIsRootWindow (dpy, wid) != -1));
  else
    return (XmuXStoreResources (dpy) &&
	    XmuXIsToplevel (dpy, wid));
}

/*
 * the eight(!) "window routines" one additional routine to update
 * resource attributes four routines to maintain grab specific information
 */

void
XmuXInsertWindowInfo (dpy, window, parent, visualid, depth)
  Display *dpy;			/* index to the resource table */
  XID window, parent, visualid;
  int depth;
{
  MUXResourceInfoPtr new_entry;
  int screen, pdepth, XmuXIsRootWindow ();

  /* don't check for errors here, it's done by AllocAndInitResource */
  new_entry = AllocAndInitResource (RT_WINDOW, window);

  /* space has been allocated by AllocAndInitResource */
  new_entry->u.win_info->parent = parent;

  /*
   * must determine the depth and the visual ID
   */
  if ((screen = XmuXIsRootWindow (dpy, parent)) != -1) {
    /* a top level window  */
    new_entry->u.win_info->depth = DefaultDepth (dpy, screen);
    if (visualid == CopyFromParent)
      new_entry->u.win_info->visualid =
	DefaultVisual (dpy, screen)->visualid;
    else
      new_entry->u.win_info->visualid = visualid;
  }
  else {
    if (visualid == CopyFromParent) {
      XID vid;

      /*
       * determine parent's visual id
       */
      if (GetVisualID (dpy, parent, &vid))
	new_entry->u.win_info->visualid = vid;
      else
	XmuXFatalError (
		  "XmuXInsertWindowInfo: Can't find parent's 0x%lx info!\n",
			 parent);
    }
    else
      new_entry->u.win_info->visualid = visualid;

    /*
     * depth == 0 has two possible meanings: either it's from
     * XCreateSimpleWindow where depth is "CopyFromParent" or it's an
     * InputOnly window. An exact solution would need the class attribute
     * here, but it's CopyFromParent too in XCreateSimpleWindow. So, we
     * treat depth == 0 as "CopyFromParent". We fix this "bug" in
     * XmuXReplicate where we know the window's class; otherwise we may
     * use an InputOnly window in XmuXMatchDrawable
     */
    if (depth == 0)
      if (GetDepth (dpy, parent, &pdepth))
	new_entry->u.win_info->depth = pdepth;
      else
	XmuXFatalError (
		      "InsertWindowInfo: Can't find parent's 0x%lx info!\n",
			 parent);
    else
      new_entry->u.win_info->depth = depth;
  }
  /* insert it in client's list */
  InsertClientResource (dpy, new_entry);
  XmuXdebug (debug_resources,
	     "new resource 0x%lx info (type RT_WINDOW) inserted\n",
	     window);
  PrintWindowInfo (dpy, new_entry->u.win_info);
}

MUXWindowInfoPtr
GetWindowInfo (dpy, window)
  Display *dpy;			/* index to the resource table */
  XID window;
{
  MUXResourceInfoPtr resource_info;

  resource_info = GetClientResource (dpy, window, RT_WINDOW);

  return ((resource_info != NullResourceInfoPtr)
	  ? resource_info->u.win_info
	  : NullWindowInfoPtr);
}

void
XmuXHideWindow (dpy, window)
  Display *dpy;
  XID window;
{
  MUXWindowInfoPtr win_info;

  if ((win_info = GetWindowInfo (dpy, window)) !=
      NullWindowInfoPtr)
    win_info->hidden = True;
}

void
XmuXUnhideWindow (dpy, window)
  Display *dpy;
  XID window;
{
  MUXWindowInfoPtr win_info;

  if ((win_info = GetWindowInfo (dpy, window)) !=
      NullWindowInfoPtr)
    win_info->hidden = False;
}

int
XmuXWindowIsHidden (dpy, window)
  Display *dpy;
  XID window;
{
  MUXWindowInfoPtr win_info;

  if ((win_info = GetWindowInfo (dpy, window)) !=
      NullWindowInfoPtr)
    return (win_info->hidden);
}

void
XmuXUpdateWindowInfo (dpy, window, flags, attributes)
  Display *dpy;
  XID window;
  unsigned long flags;
  XSetWindowAttributes *attributes;
{
  MUXWindowInfoPtr win_info;

  if ((win_info = GetWindowInfo (dpy, window)) !=
      NullWindowInfoPtr) {
    XmuXdebug (debug_resources, "Window 0x%lx updated:\n", window);

    /*
     * What the docu says: "If you specify a background_pixel, it
     * overrrides either the default background_pixmap or any value you
     * may have set in the background_pixmap member...If you set the
     * background_pixmap, it overrides the default background_pixmap." 
     * The same applies to the border_pixel and border_pixmap values. 
     * That's why we use this order.
     */
    if (flags & CWBackPixmap) {
      win_info->flags |= CWBackPixmap;
      /* reset background pixel bit */
      win_info->flags &= ~CWBackPixel;
      win_info->bg_pmap = attributes->background_pixmap;
      XmuXdebug (debug_resources, "bg pixmap 0x%lx\n",
		 win_info->bg_pmap);
    }
    if (flags & CWBackPixel) {
      win_info->flags |= CWBackPixel;
      /* background pixel overrides background pixmap */
      win_info->flags &= ~CWBackPixmap;
      win_info->bg_pixel = attributes->background_pixel;
      XmuXdebug (debug_resources, "bg pixel 0x%lx\n",
		 win_info->bg_pixel);
    }
    if (flags & CWBorderPixmap) {
      win_info->flags |= CWBorderPixmap;
      /* reset border pixel bit */
      win_info->flags &= ~CWBorderPixel;
      win_info->border_pmap = attributes->border_pixmap;
      XmuXdebug (debug_resources, "border pixmap 0x%lx\n",
		 win_info->border_pmap);
    }
    if (flags & CWBorderPixel) {
      win_info->flags |= CWBorderPixel;
      /* background pixel overrides background pixmap */
      win_info->flags &= ~CWBorderPixmap;
      win_info->border_pixel = attributes->border_pixel;
      XmuXdebug (debug_resources, "border pixel 0x%lx\n",
		 win_info->border_pixel);
    }
    if (flags & CWCursor) {
      win_info->flags |= CWCursor;
      win_info->cursor = attributes->cursor;
      XmuXdebug (debug_resources, "cursor 0x%lx\n",
		 win_info->cursor);
    }
    if (flags & CWColormap) {
      win_info->flags |= CWColormap;
      win_info->colormap = attributes->colormap;
      XmuXdebug (debug_resources, "colormap 0x%lx\n",
		 win_info->colormap);
    }
  }
}

void
XmuXRemoveWindowInfo (dpy, window)
  Display *dpy;			/* index to the resource table */
  XID window;
{
  MUXResourceInfoPtr resource_info;
  void XmuXRemoveSubwindowInfos ();

  resource_info = GetClientResource (dpy, window, RT_WINDOW);

  if (resource_info != NullResourceInfoPtr) {
    MUXClientResources[ConnectionNumber (dpy)].update = True;
    /* if this is a toplevel window, remove the toplevel info */
    XmuXRemoveToplevelInfo (dpy, window);
    /* remove all subwindow infos */
    XmuXRemoveSubwindowInfos (dpy, window);
    resource_info->used = False;
    /* remove all references of this window */
    RemoveDrawableReference (dpy, resource_info->id);
  }
  else
    XmuXErrorF (debug_resources, "window info 0x%lx not found!\n",
		window);
}

void
XmuXRemoveSubwindowInfos (dpy, window)
  Display *dpy;			/* index to the resource table */
  XID window;
{
  MUXResourceInfoPtr resource_info;

  while ((resource_info =
	  GetClientResource (dpy, window, RT_SUBWINDOW)) !=
	 NullResourceInfoPtr) {

    /*
     * That's the only place where we can remove the ID's
     */
    XmuXRemoveWindowID (dpy, resource_info->id);
    /* We recursive descent down the window tree */
    XmuXRemoveSubwindowInfos (dpy,
			      resource_info->id);
    resource_info->used = False;
    /* Remove all references of this window */
    RemoveDrawableReference (dpy, resource_info->id);
  }
}

static void
PrintWindowInfo (dpy, win_info)
  Display *dpy;			/* index to the resource table */
  MUXWindowInfoPtr win_info;
{
  XmuXdebug (debug_resources, "resource 0x%lx type RT_WINDOW values:\n",
	     win_info->window);
  XmuXdebug (debug_resources, "parent = 0x%lx\n", win_info->parent);
}

void
XmuXSetWindowButtonGrab (dpy, grab_window, button, modifiers,
			 owner_events, event_mask, pointer_mode,
			 keyboard_mode, confine_to, curs)
  Display *dpy;
  XID grab_window;
  unsigned int modifiers;	/* CARD16 */
  unsigned int button;		/* CARD8 */
  Bool owner_events;
  unsigned int event_mask;	/* CARD16 */
  int pointer_mode, keyboard_mode;
  Window confine_to;
  Cursor curs;
{
  register MUXWindowInfoPtr win_info;

  if ((win_info = GetWindowInfo (dpy, grab_window)) !=
      NullWindowInfoPtr) {
    register GrabButtonParams *bgrab;

    if ((bgrab =
	 (GrabButtonParams *) Xmalloc (sizeof (GrabButtonParams))) ==
	NULL)
      XmuXFatalError ("XmuXSetWindowButtonGrab: Out of memory!\n");

    bgrab->modifiers = modifiers;
    bgrab->button = button;
    bgrab->owner_events = owner_events;
    bgrab->event_mask = event_mask;
    bgrab->pointer_mode = pointer_mode;
    bgrab->keyboard_mode = keyboard_mode;
    bgrab->confine_to = confine_to;
    bgrab->cursor = curs;

    /* insert it in the button grab list */
    bgrab->next = win_info->button_grabs;
    win_info->button_grabs = bgrab;
    win_info->grabs |= ButtonGrabbed;
    XmuXdebug (debug_resources, "Add button grab %d for window 0x%lx\n",
	       button, grab_window);

  }
  else
    XmuXErrorF ("XmuXSetWindowButtonGrab: window 0x%lx not found!\n",
		grab_window);
}

void
XmuXRemoveWindowButtonGrab (dpy, grab_window, button, modifiers)
  Display *dpy;
  Window grab_window;
  unsigned int button;
  unsigned int modifiers;
{
  register MUXWindowInfoPtr win_info;

  if ((win_info = GetWindowInfo (dpy, grab_window)) !=
      NullWindowInfoPtr) {
    register GrabButtonParams *pred, *curr;

    pred = curr = win_info->button_grabs;
    if (curr == NULL) {
      XmuXErrorF ("XmuXRemoveWindowButtonGrab: list 0x%lx empty!\n",
		  grab_window);
      return;
    }
    /* remove the head? */
    if (curr->button == button && curr->modifiers == modifiers) {
      win_info->button_grabs = win_info->button_grabs->next;
      if (win_info->button_grabs == NULL)
	/* no more button grabs */
	win_info->grabs &= ~ButtonGrabbed;
      Xfree ((char *) curr);
      return;
    }
    /* search through the list */
    for (curr = curr->next; curr != NULL;
	 pred = curr, curr = curr->next)
      if (curr->button == button && curr->modifiers == modifiers) {
	pred->next = curr->next;
	Xfree ((char *) curr);
	return;
      }
    XmuXErrorF ("XmuXRemoveWindowButtonGrab: list 0x%lx entry not found!\n",
		grab_window);
  }
  else
    XmuXErrorF ("XmuXRemoveWindowButtonGrab: window 0x%lx not found!\n",
		grab_window);
}

void
XmuXSetWindowKeyGrab (dpy, grab_window, key, modifiers,
		      owner_events, pointer_mode,
		      keyboard_mode)
  Display *dpy;
  Window grab_window;
  int key;
  unsigned int modifiers;
  Bool owner_events;
  int pointer_mode, keyboard_mode;
{
  register MUXWindowInfoPtr win_info;

  if ((win_info = GetWindowInfo (dpy, grab_window)) !=
      NullWindowInfoPtr) {
    register GrabKeyParams *kgrab;

    if ((kgrab =
	 (GrabKeyParams *) Xmalloc (sizeof (GrabKeyParams))) ==
	NULL)
      XmuXFatalError ("XmuXSetWindowKeyGrab: Out of memory!\n");

    kgrab->modifiers = modifiers;
    kgrab->key = key;
    kgrab->owner_events = owner_events;
    kgrab->pointer_mode = pointer_mode;
    kgrab->keyboard_mode = keyboard_mode;

    /* insert it in the key grab list */
    kgrab->next = win_info->key_grabs;
    win_info->key_grabs = kgrab;
    win_info->grabs |= KeyGrabbed;
    XmuXdebug (debug_resources, "Add key grab %d for window 0x%lx\n",
	       key, grab_window);
  }
  else
    XmuXErrorF ("XmuXSetWindowKeyGrab: window 0x%lx not found!\n",
		grab_window);
}

void
XmuXRemoveWindowKeyGrab (dpy, grab_window, key, modifiers)
  Display *dpy;
  Window grab_window;
  int key;
  unsigned int modifiers;
{
  register MUXWindowInfoPtr win_info;

  if ((win_info = GetWindowInfo (dpy, grab_window)) !=
      NullWindowInfoPtr) {
    register GrabKeyParams *pred, *curr;

    pred = curr = win_info->key_grabs;
    if (curr == NULL) {
      XmuXErrorF ("XmuXRemoveWindowKeyGrab: list 0x%lx empty!\n",
		  grab_window);
      return;
    }
    /* remove the head? */
    if (curr->key == key && curr->modifiers == modifiers) {
      win_info->key_grabs = win_info->key_grabs->next;
      if (win_info->key_grabs == NULL)
	/* no more button grabs */
	win_info->grabs &= ~KeyGrabbed;
      Xfree ((char *) curr);
      return;
    }
    /* search through the list */
    for (curr = curr->next; curr != NULL;
	 pred = curr, curr = curr->next)
      if (curr->key == key && curr->modifiers == modifiers) {
	pred->next = curr->next;
	Xfree ((char *) curr);
	return;
      }
    XmuXErrorF ("XmuXRemoveWindowKeyGrab: list 0x%lx entry not found!\n",
		grab_window);
  }
  else
    XmuXErrorF ("XmuXRemoveWindowKeyGrab: window 0x%lx not found!\n",
		grab_window);
}

/*
 * our four "gc routines" -- one additional routine to update the flags
 */

void
XmuXInsertGCInfo (dpy, gc, drawable)
  Display *dpy;
  GC gc;
  XID drawable;
{
  MUXResourceInfoPtr new_entry;
  int screen, depth;

  /* don't check for errors here, it's done by AllocAndInitResource */
  new_entry = AllocAndInitResource (RT_GC, gc->gid);

  new_entry->u.gc_info->gc = gc;
  new_entry->u.gc_info->drawable = drawable;

  if ((screen = XmuXIsRootWindow (dpy, drawable)) != -1)
    new_entry->u.gc_info->depth = DefaultDepth (dpy, screen);
  else if (GetDepth (dpy, drawable, &depth))
    new_entry->u.gc_info->depth = depth;
  else
    XmuXFatalError ("XmuXInsertGCInfo: Can't find drawable's 0x%lx info!\n",
		     drawable);

  /* insert it in client's list */
  InsertClientResource (dpy, new_entry);
  XmuXdebug (debug_resources,
	     "new resource 0x%lx info (type RT_GC) inserted\n",
	     gc->gid);
  PrintGCInfo (new_entry->u.gc_info);
}

MUXGCInfoPtr
GetGCInfo (dpy, gid)
  Display *dpy;
  XID gid;
{
  MUXResourceInfoPtr resource_info;

  resource_info = GetClientResource (dpy, gid, RT_GC);

  return ((resource_info != NullResourceInfoPtr)
	  ? resource_info->u.gc_info
	  : NullGCInfoPtr);
}

void
XmuXUpdateGCFlags (dpy, gid, flags)
  Display *dpy;
  XID gid;
  long flags;
{
  MUXGCInfoPtr gc_info;

  if ((gc_info = GetGCInfo (dpy, gid)) != NullGCInfoPtr) {
    gc_info->flags |= (flags & XMUXGCFLAGS);
    XmuXdebug (debug_resources, "GC 0x%lx updated\n", gid);
  }
}

/*
 * This is the only remove routine that returns a result !! this result
 * is needed in XFreeGC (to find out if we can free the GC struct there)!!!
 */
int
XmuXRemoveGCInfo (dpy, gid)
  Display *dpy;
  XID gid;
{
  MUXResourceInfoPtr resource_info;

  resource_info = GetClientResource (dpy, gid, RT_GC);

  if (resource_info != NullResourceInfoPtr) {
    MUXClientResources[ConnectionNumber (dpy)].update = True;
    resource_info->used = False;
    return (1);
  }
  else
    return (0);
}

/*
 * local routine to print current GC values
 */

static void
PrintGCInfo (gc_info)
  MUXGCInfoPtr gc_info;
{
  XmuXdebug (debug_resources, "resource 0x%lx type: RT_GC values:\n",
	     gc_info->gid);
  XmuXdebug (debug_resources, "drawable = 0x%lx\n", gc_info->drawable);
  XmuXdebug (debug_resources, "GC function   = %d\n",
	     gc_info->gc->values.function);
  XmuXdebug (debug_resources, "GC foreground = %x\n",
	     gc_info->gc->values.foreground);
  XmuXdebug (debug_resources, "GC background = %x\n",
	     gc_info->gc->values.background);
  /* ... */
}

/*
 * the four "pixmap routines"
 */

void
XmuXInsertPixmapInfo (dpy, pixmap, drawable, depth)
  Display *dpy;
  XID pixmap, drawable;
  int depth;
{
  MUXResourceInfoPtr new_entry;
  int screen, junk_depth;

  /* don't check for errors here, it's done by AllocAndInitResource */
  new_entry = AllocAndInitResource (RT_PIXMAP, pixmap);
  /* space has been allocated by AllocAndInitResource */
  new_entry->u.pmap_info->drawable = drawable;
  new_entry->u.pmap_info->depth = depth;

  /* Insert it in client's list */
  InsertClientResource (dpy, new_entry);
  XmuXdebug (debug_resources,
	     "new resource 0x%lx info (type RT_PIXMAP) inserted\n",
	     pixmap);
}

MUXPixmapInfoPtr
GetPixmapInfo (dpy, pixmap)
  Display *dpy;
  XID pixmap;
{
  MUXResourceInfoPtr resource_info;

  resource_info = GetClientResource (dpy, pixmap, RT_PIXMAP);

  return ((resource_info != NullResourceInfoPtr)
	  ? resource_info->u.pmap_info
	  : NullPixmapInfoPtr);
}

XID
XmuXGetPixmapDrawable (dpy, pixmap)
  Display *dpy;
  XID pixmap;
{
  MUXResourceInfoPtr resource_info;

  resource_info = GetClientResource (dpy, pixmap, RT_PIXMAP);

  return ((resource_info != NullResourceInfoPtr)
	  ? resource_info->u.pmap_info->drawable
	  : None);
}

void
XmuXRemovePixmapInfo (dpy, pixmap)
  Display *dpy;
  XID pixmap;
{
  MUXResourceInfoPtr resource_info;
  MUXPixmapInfoPtr pmap_info;

  resource_info = GetClientResource (dpy, pixmap, RT_PIXMAP);

  if (resource_info != NullResourceInfoPtr) {
    MUXClientResources[ConnectionNumber (dpy)].update = True;
    resource_info->used = False;
    RemoveDrawableReference (dpy, pixmap);
  }
  else
    XmuXErrorF (debug_resources, "pixmap info 0x%lx not found!\n",
		pixmap);
}

/*
 * local routine to print parts of pixmap values
 */
static void
PrintPixmapInfo (dpy, pmap_info)
  Display *dpy;
  MUXPixmapInfoPtr pmap_info;
{
  XmuXdebug (debug_resources, "resource 0x%lx type = RT_PIXMAP values:\n",
	     pmap_info->pixmap);
  XmuXdebug (debug_resources, "drawable: 0x%lx\n", pmap_info->drawable);
}

/*
 * the five(!) "cursor routines" the two different cursor types are
 * handled in two different routines one additional routine to handle
 * XRecolorCursor
 */
void
XmuXInsertGlyphCursorInfo (dpy, cursor, fg_color, bg_color,
			   src_font, mask_font, src_char, mask_char)
  Display *dpy;
  XID cursor;
  XColor *fg_color, *bg_color;
  XID src_font, mask_font;
  unsigned int src_char, mask_char;
{
  MUXResourceInfoPtr new_entry;

  /* don't check for errors here, it's done by AllocAndInitResource */
  new_entry = AllocAndInitResource (RT_CURSOR, cursor);

  /* space has been allocated by AllocAndInitResource */
  new_entry->u.curs_info->cursor_type = GLYPH_CURSOR;

  new_entry->u.curs_info->fg.red = fg_color->red;
  new_entry->u.curs_info->fg.green = fg_color->green;
  new_entry->u.curs_info->fg.blue = fg_color->blue;

  new_entry->u.curs_info->bg.red = bg_color->red;
  new_entry->u.curs_info->bg.green = bg_color->green;
  new_entry->u.curs_info->bg.blue = bg_color->blue;

  new_entry->u.curs_info->u.glyph_cursor.source_font = src_font;
  new_entry->u.curs_info->u.glyph_cursor.mask_font = mask_font;
  new_entry->u.curs_info->u.glyph_cursor.source_char = src_char;
  new_entry->u.curs_info->u.glyph_cursor.mask_char = mask_char;

  /* insert it in client's list */
  InsertClientResource (dpy, new_entry);
  XmuXdebug (debug_resources,
	     "new resource 0x%lx info (type RT_CURSOR) inserted\n",
	     cursor);
  PrintCursorInfo (new_entry->u.curs_info);
}

void
XmuXInsertPixmapCursorInfo (dpy, cursor, fg_color, bg_color,
			    src_pmap, mask_pmap, hot_x, hot_y)
  Display *dpy;
  XID cursor;
  XColor *fg_color, *bg_color;
  XID src_pmap, mask_pmap;
  int hot_x, hot_y;
{
  MUXResourceInfoPtr new_entry;

  /* don't check for errors here, it's done by AllocAndInitResource */
  new_entry = AllocAndInitResource (RT_CURSOR, cursor);

  /* space has been allocated by AllocAndInitResource */
  new_entry->u.curs_info->cursor_type = PIXMAP_CURSOR;

  new_entry->u.curs_info->fg.red = fg_color->red;
  new_entry->u.curs_info->fg.green = fg_color->green;
  new_entry->u.curs_info->fg.blue = fg_color->blue;

  new_entry->u.curs_info->bg.red = bg_color->red;
  new_entry->u.curs_info->bg.green = bg_color->green;
  new_entry->u.curs_info->bg.blue = bg_color->blue;

  new_entry->u.curs_info->u.pixmap_cursor.source_pmap = src_pmap;
  new_entry->u.curs_info->u.pixmap_cursor.mask_pmap = mask_pmap;
  new_entry->u.curs_info->u.pixmap_cursor.hot_x = hot_x;
  new_entry->u.curs_info->u.pixmap_cursor.hot_y = hot_y;

  /* insert it in client's list */
  InsertClientResource (dpy, new_entry);
  XmuXdebug (debug_resources,
	     "new resource 0x%lx info (type RT_CURSOR) inserted\n",
	     cursor);
  PrintCursorInfo (new_entry->u.curs_info);
}

MUXCursorInfoPtr
GetCursorInfo (dpy, cursor)
  Display *dpy;
  XID cursor;
{
  MUXResourceInfoPtr resource_info;

  resource_info = GetClientResource (dpy, cursor, RT_CURSOR);

  return ((resource_info != NullResourceInfoPtr)
	  ? resource_info->u.curs_info
	  : NullCursorInfoPtr);
}

void
XmuXChangeCursorColors (dpy, cursor, fg_color, bg_color)
  Display *dpy;
  XID cursor;
  XColor *fg_color, *bg_color;
{
  MUXCursorInfoPtr curs_info;

  if ((curs_info = GetCursorInfo (dpy, cursor)) ==
      NullCursorInfoPtr) {
    XmuXErrorF ("Can't update cursor colors (cursor 0x%lx not found)!\n",
		cursor);
    return;
  }
  curs_info->fg.red = fg_color->red;
  curs_info->fg.green = fg_color->green;
  curs_info->fg.blue = fg_color->blue;

  curs_info->bg.red = bg_color->red;
  curs_info->bg.green = bg_color->green;
  curs_info->bg.blue = bg_color->blue;
  XmuXdebug (debug_resources, "cursor 0x%lx colors changed\n", cursor);
}

void
XmuXRemoveCursorInfo (dpy, cursor)
  Display *dpy;
  XID cursor;
{
  MUXResourceInfoPtr resource_info;
  MUXCursorInfoPtr curs_info;

  resource_info = GetClientResource (dpy, cursor, RT_CURSOR);

  if (resource_info != NullResourceInfoPtr) {
    MUXClientResources[ConnectionNumber (dpy)].update = True;
    resource_info->used = False;
  }
  else
    XmuXErrorF (debug_resources, "cursor info 0x%lx not found!\n",
		cursor);

}

/*
 * local routine to print current cursor info
 */
static void
PrintCursorInfo (curs_info)
  MUXCursorInfoPtr curs_info;
{

  XmuXdebug (debug_resources, "resource 0x%lx type = RT_CURSOR ",
	     curs_info->cursor);
  if (curs_info->cursor_type == PIXMAP_CURSOR) {
    XmuXdebug (debug_resources, "(type PIXMAP_CURSOR) values:\n");
    XmuXdebug (debug_resources, "foreground color (rgb) = %u %u %u\n",
	       curs_info->fg.red, curs_info->fg.green, curs_info->fg.blue);
    XmuXdebug (debug_resources, "background color (rgb) = %u %u %u\n",
	       curs_info->bg.red, curs_info->bg.green, curs_info->bg.blue);
    XmuXdebug (debug_resources, "pixmaps: src = 0x%lx mask = 0x%lx\n",
	       curs_info->u.pixmap_cursor.source_pmap,
	       curs_info->u.pixmap_cursor.mask_pmap);
    XmuXdebug (debug_resources, "hot spot: x = %d y = %d\n",
	       curs_info->u.pixmap_cursor.hot_x,
	       curs_info->u.pixmap_cursor.hot_y);
  }
  else {
    XmuXdebug (debug_resources, "(type = GLYPH_CURSOR) values:\n");
    XmuXdebug (debug_resources, "foreground color (rgb) = %u %u %u\n",
	       curs_info->fg.red, curs_info->fg.green, curs_info->fg.blue);
    XmuXdebug (debug_resources, "background color (rgb) = %u %u %u\n",
	       curs_info->bg.red, curs_info->bg.green, curs_info->bg.blue);
    XmuXdebug (debug_resources, "fonts: src = 0x%lx mask = 0x%lx\n",
	       curs_info->u.glyph_cursor.source_font,
	       curs_info->u.glyph_cursor.mask_font);
    XmuXdebug (debug_resources, "glyphs: src = %u mask = %u\n",
	       curs_info->u.glyph_cursor.source_char,
	       curs_info->u.glyph_cursor.mask_char);
  }
}

/*
 * the three "font routines"
 */

void
XmuXInsertFontInfo (dpy, font, name)
  Display *dpy;
  XID font;
  char *name;
{
  MUXResourceInfoPtr new_entry;
  char *font_name;
  int str_length;

  /* don't check for errors here, it's done by AllocAndInitResource */
  new_entry = AllocAndInitResource (RT_FONT, font);

  /* space has been allocated by AllocAndInitResource... */
  /* exept for this */
  str_length = strlen (name);
  if ((font_name = (char *) Xmalloc (str_length + 1)) == NULL)
    XmuXFatalError ("Out of memory (can't store font name)!\n");
  strcpy (font_name, name);
  font_name[str_length] = '\0';
  new_entry->u.font_info->name = font_name;

  /* insert it in client's list */
  InsertClientResource (dpy, new_entry);
  XmuXdebug (debug_resources,
	     "new resource 0x%lx info (type RT_FONT) inserted\n", font);
  PrintFontInfo (new_entry->u.font_info);
}

MUXFontInfoPtr
GetFontInfo (dpy, font)
  Display *dpy;
  XID font;
{
  MUXResourceInfoPtr resource_info;

  resource_info = GetClientResource (dpy, font, RT_FONT);

  return ((resource_info != NullResourceInfoPtr)
	  ? resource_info->u.font_info
	  : NullFontInfoPtr);
}

void
XmuXRemoveFontInfo (dpy, font)
  Display *dpy;
  XID font;
{
  MUXResourceInfoPtr resource_info;


  resource_info = GetClientResource (dpy, font, RT_FONT);

  if (resource_info != NullResourceInfoPtr) {
    MUXClientResources[ConnectionNumber (dpy)].update = True;
    resource_info->used = False;
  }
  else
    XmuXErrorF (debug_resources, "font info 0x%lx not found!\n", font);

}

/*
 * - local routine to print font info
 */
static void
PrintFontInfo (font_info)
  MUXFontInfoPtr font_info;
{

  XmuXdebug (debug_resources, "resource 0x%lx type = RT_FONT values: \n",
	     font_info->font);
  XmuXdebug (debug_resources, "name = %s\n", font_info->name);
}

/*
 * the six "colormap routines" One additional routine to handle
 * XCopyColormapAndFree One additional routine to update the colormap info
 * One additional routine to handle XFreeColors
 */

void
XmuXCreateColormapInfo (dpy, colormap, visual, window, private, alloc)
  Display *dpy;
  XID colormap;
  Visual *visual;
  Window window;
  int private,			/* is this a private colormap */
   alloc;			/* did the client pass AllocAll to
				 * XCreateColormap */
{
  register unsigned long *map;
  register int size;
  register MUXResourceInfoPtr new_entry;

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

  /* don't check for errors here, it's done by AllocAndInitResource */
  new_entry = AllocAndInitResource (RT_COLORMAP, colormap);

  /* space has been allocated by AllocAndInitResource */
  new_entry->u.cmap_info->visualid = visual->visualid;
  new_entry->u.cmap_info->window = window;

  size = ALLOC_AT_ONCE;
  new_entry->u.cmap_info->sh_length = size;
  map = (unsigned long *) Xmalloc (size * sizeof (unsigned long));
  if (map == (unsigned long *) NULL)
    XmuXFatalError ("XmuXCreateColormapInfo: Out of memory (conn %d)!\n",
		    ConnectionNumber (dpy));
  new_entry->u.cmap_info->sh_cells = map;
  new_entry->u.cmap_info->sh_allocated = 0;

  /* insert it in client's list */
  InsertClientResource (dpy, new_entry);
  if (private) {
    MUXClientResources[ConnectionNumber (dpy)].privcmaps++;
    new_entry->u.cmap_info->is_private = True;
  }
  else
    new_entry->u.cmap_info->is_private = False;

  if (alloc == AllocAll) {
    Visual *vis;
    XID vid;
    MUXColormapInfoPtr cmap_info = new_entry->u.cmap_info;

    /*
     * client has passed AllocAll to XCreateColormap i.e., Client
     * doesn't need to call XAllocColorCells to allocate cells in this new
     * colormap and we won't get informed about those allocation of private
     * cells if we don't register it here !
     */
    if (visual == NULL)

      /*
       * we need the visual structure to determine the size of the map
       */
      if (!GetVisualID (dpy, window, &vid))
	XmuXFatalError ("XmuXCreateCmap: Can't find window's 0x%lx visual!",
			window);
      else
	vis = (Visual *) (_XVIDtoVisual (dpy, vid)); /* typecast added (fs) */
    else
      vis = visual;

    if ((cmap_info->pr_cells = (MUXPrivateColorCells *)
	 Xmalloc (sizeof (MUXPrivateColorCells))) == NULL)
      XmuXFatalError ("XmuXCreateCmap: Out of mem (pr_cells)!\n");
    if (visual->class == PseudoColor) {
      if ((cmap_info->pr_cells->pixels = (unsigned long *)
	   Xmalloc (visual->map_entries * sizeof (long))) == NULL)
	XmuXFatalError (
			 "XmuXCreateCmap: Out of mem (priv pixels)!\n");
      for (size = 0; size < visual->map_entries; size++)
	cmap_info->pr_cells->pixels[size] = size;
      cmap_info->pr_cells->n_pixels = visual->map_entries;
      cmap_info->pr_cells->n_planes = 0;
      cmap_info->pr_cells->planes = NULL;
      cmap_info->pr_cells->alloc_cells = True;
    }
    else {			/* DirectColor */
      if ((cmap_info->pr_cells->pixels = (unsigned long *)
	   Xmalloc (sizeof (long))) == NULL)
	XmuXFatalError (
			 "XmuXCreateCmap: Out of mem (priv pixels)!\n");
      cmap_info->pr_cells->pixels[0] = 0;
      cmap_info->pr_cells->n_pixels = 1;
      if ((cmap_info->pr_cells->planes = (unsigned long *)
	   Xmalloc (3 * sizeof (long))) == NULL)
	XmuXFatalError (
			 "XmuXCreateCmap: Out of mem (priv planes)!\n");
      cmap_info->pr_cells->n_planes = 3;
      cmap_info->pr_cells->planes[0] = visual->red_mask;
      cmap_info->pr_cells->planes[1] = visual->green_mask;
      cmap_info->pr_cells->planes[2] = visual->blue_mask;
      cmap_info->pr_cells->alloc_cells = False;
    }
  }
  XmuXdebug (debug_resources,
	     "new resource 0x%lx info (type RT_COLORMAP) inserted\n",
	     colormap);
}

MUXColormapInfoPtr
XmuXGetColormapInfo (dpy, colormap)
  Display *dpy;
  XID colormap;
{
  MUXResourceInfoPtr resource_info;

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

  resource_info = GetClientResource (dpy, colormap, RT_COLORMAP);

  return ((resource_info != NullResourceInfoPtr)
	  ? resource_info->u.cmap_info
	  : NullColormapInfoPtr);
}

void
XmuXCopyColormap (dpy, src_cmap, dst_cmap)
  Display *dpy;
  Colormap src_cmap, dst_cmap;
{
  MUXResourceInfoPtr resource_info;

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

  if ((resource_info = GetClientResource (dpy, src_cmap, RT_COLORMAP))
      != NullResourceInfoPtr)

    /*
     * just change the resource ID's
     */
    resource_info->id =
      resource_info->u.cmap_info->colormap = dst_cmap;
}

void
XmuXUpdateColormapInfo (dpy, colormap, pixels, n_pixels, planes,
			n_planes, shared, AllocCells)
  Display *dpy;
  XID colormap;
  unsigned long *pixels, *planes;
  int n_pixels, n_planes, shared, AllocCells;
{
  MUXResourceInfoPtr entry;
  MUXColormapInfoPtr cmap_info;
  int allocate;

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

  if ((entry = GetClientResource (dpy, colormap, RT_COLORMAP)) !=
      NullResourceInfoPtr) {
    cmap_info = entry->u.cmap_info;
    if (shared) {
      register int i;

      /* client allocated shared color cells */

      /*
       * see if this pixel has been allocated before
       */
      for (i = 0; i < cmap_info->sh_allocated; i++)
	if (pixels[0] == cmap_info->sh_cells[i])
	  return;

      if (cmap_info->sh_allocated == cmap_info->sh_length) {
	/* must realloc array */
	allocate =
	  ((cmap_info->sh_allocated + 1) / ALLOC_AT_ONCE + 1) *
	  ALLOC_AT_ONCE;
	cmap_info->sh_length = allocate;
	cmap_info->sh_cells = (unsigned long *)
	  Xrealloc ((char *) cmap_info->sh_cells,
		    allocate * sizeof (unsigned long));
	if (cmap_info->sh_cells == NULL)
	  XmuXFatalError ("XmuXUpdateColormapInfo: Realloc failed \
                                    (conn %d)!\n", ConnectionNumber (dpy));
      }
      cmap_info->sh_cells[cmap_info->sh_allocated] = pixels[0];
      cmap_info->sh_allocated++;
      XmuXdebug (debug_resources,
		 "Colormap 0x%lx  pixel %u inserted\n",
		 colormap,
	entry->u.cmap_info->sh_cells[entry->u.cmap_info->sh_allocated - 1]);
    }
    else {
      MUXPrivateColorCells *pr_cells;

      if ((pr_cells = (MUXPrivateColorCells *)
	   Xmalloc (sizeof (MUXPrivateColorCells))) == NULL)
	XmuXFatalError ("XmuXUpdCmap: Out of mem (private info)!\n");
      if (n_pixels) {
	if ((pr_cells->pixels = (unsigned long *)
	     Xmalloc (n_pixels * sizeof (unsigned long))) == NULL)
	  XmuXFatalError ("XmuXUpdCmap: Out of mem (private cells)!\n");
	bcopy ((char *) pixels, (char *) pr_cells->pixels,
	       n_pixels * sizeof (unsigned long));
      }
      else
	pr_cells->pixels = NULL;

      if (n_planes) {
	if ((pr_cells->planes = (unsigned long *)
	     Xmalloc (n_planes * sizeof (unsigned long))) == NULL)
	  XmuXFatalError ("XmuXUpdCmap: Out of mem (private cells)!\n");
	bcopy ((char *) planes, (char *) pr_cells->planes,
	       n_planes * sizeof (unsigned long));
      }
      else
	pr_cells->planes = NULL;

      pr_cells->n_pixels = n_pixels;
      pr_cells->n_planes = n_planes;
      pr_cells->alloc_cells = AllocCells;
      pr_cells->next = cmap_info->pr_cells;
      cmap_info->pr_cells = pr_cells;
      XmuXdebug (debug_resources,
		 "cmap 0x%lx  %d private pixels (%d planes)inserted\n",
		 colormap, n_pixels, n_planes);
    }
  }
}

void
XmuXRemoveColormapInfo (dpy, colormap)
  Display *dpy;
  XID colormap;
{
  MUXResourceInfoPtr resource_info;

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

  resource_info = GetClientResource (dpy, colormap,
				     RT_COLORMAP);

  if (resource_info != NullResourceInfoPtr) {
    MUXClientResources[ConnectionNumber (dpy)].update = True;
    resource_info->used = False;
  }
  else
    XmuXErrorF (debug_resources, "colormap 0x%lx not found!\n",
		colormap);
}

#define FREEANDADVANCE( ptr )  \
            {   register MUXPrivateColorCells *tmp = ptr ;\
		ptr = ptr->next ;\
		tmp->next = NULL ;\
		Xfree( ( char * ) tmp->pixels ) ;\
		if( tmp->planes ) Xfree( ( char * ) tmp->planes ) ;\
		Xfree( ( char * ) tmp ) ;\
	    }

void
XmuXFreeColors (dpy, cmap, pixels, npixels, planes)
  Display *dpy;
  Colormap cmap;
  unsigned long *pixels;
  int npixels;
  unsigned long planes;
{
  MUXColormapInfoPtr cmap_info;

  if ((cmap_info = XmuXGetColormapInfo (dpy, cmap)) != NULL) {
    register int i, j, zapped, matched;
    register MUXPrivateColorCells *curr, *prev;

    if (planes == 0L) {
      /* must check shared color cells */
      zapped = 0;
      for (i = 0; i < npixels; i++)
	for (j = cmap_info->sh_allocated - 1; j >= 0; j--)
	  if (cmap_info->sh_cells[j] == pixels[i]) {
	    cmap_info->sh_cells[j] = ~(0L);
	    zapped++;
	    break;
	  }
      if (zapped) {
	cmap_info->sh_allocated -= zapped;
	if (cmap_info->sh_allocated) {

	  /*
	   * array will shrink --> copy in place and realloc array
	   */
	  for (j = 0, i = 0;
	       i < cmap_info->sh_allocated + zapped; i++)
	    if (cmap_info->sh_cells[i] != ~(0L))
	      cmap_info->sh_cells[j++] =
		cmap_info->sh_cells[i];
	  cmap_info->sh_length =
	    (cmap_info->sh_allocated / ALLOC_AT_ONCE + 1) *
	    ALLOC_AT_ONCE;
	  cmap_info->sh_cells = (unsigned long *)
	    Xrealloc ((char *) cmap_info->sh_cells,
		      cmap_info->sh_length * sizeof (long));
	}
	if (zapped == npixels)
	  return;
      }
    }

    /*
     * private cells will only be freed if we find an exact match !
     */
    for (curr = cmap_info->pr_cells; curr != NULL; curr = curr->next) {
      matched = 0;
      for (i = 0; i < npixels; i++)
	for (j = curr->n_pixels - 1; j >= 0; j--)
	  if (pixels[i] == curr->pixels[j])
	    matched++;
      if (matched == curr->n_pixels) {
	matched = 0;
	for (j = curr->n_planes - 1; j >= 0; j--)
	  if (planes & curr->planes[j])
	    matched++;
	if (matched == curr->n_planes)
	  curr->n_pixels = -1;
      }
    }
    curr = cmap_info->pr_cells;
    prev = NULL;

    while (curr != NULL) {
      /* a new candidate? */
      if (curr->n_pixels == -1) {
	/* head of list? */
	if (prev == NULL) {
	  /* only one list element? */
	  if (curr->next == NULL)
	    cmap_info->pr_cells = NULL;
	  else
	    cmap_info->pr_cells = curr->next;

	  /* free the entry */
	  FREEANDADVANCE (curr);
	}
	else {
	  prev->next = curr->next;
	  FREEANDADVANCE (curr);
	}
      }
      else {
	prev = curr;
	curr = curr->next;
      }
    }
  }
}

#undef FREEANDADVANCE

/*
 * local routine to print colormap info
 */
void
PrintColormapInfo (cmap_info)
  MUXColormapInfoPtr cmap_info;
{
  register int i;

  XmuXdebug (debug_resources, "resource 0x%lx type = RT_COLORMAP values:\n",
	     cmap_info->colormap);
  for (i = 0; i < cmap_info->sh_allocated; i++) {
    XmuXdebug (debug_resources, "%dth allocated pixel: %lx\n",
	       i, cmap_info->sh_cells[i]);
  }
}

/* GCPixelInfo routines */

void
XmuXUpdateGCPixels (dpy, gid, flags, values)
  Display *dpy;
  XID gid;
  unsigned long flags;
  XGCValues *values;
{
  register MUXResourceInfoPtr res_info;

  res_info = GetClientResource (dpy, gid, RT_GCPIXELS);

  XmuXdebug (debug_resources, "Update GC 0x%lx pixels", gid);
  if (res_info == NullResourceInfoPtr) {
    /* it's not an error here, must create a new entry */
    res_info = AllocAndInitResource (RT_GCPIXELS, gid);
    res_info->used = True;
    res_info->id = gid;
    res_info->type = RT_GCPIXELS;

    res_info->u.gcpix_info->gid = gid;
    res_info->u.gcpix_info->flags = flags;
    if (flags & GCFunction) {
      XmuXdebug (debug_resources, " function %u",
		 values->function);
      res_info->u.gcpix_info->function = values->function;
    }
    if (flags & GCForeground) {
      XmuXdebug (debug_resources, " fg %u", values->foreground);
      res_info->u.gcpix_info->fg = values->foreground;
    }
    if (flags & GCBackground) {
      XmuXdebug (debug_resources, " bg %u", values->foreground);
      res_info->u.gcpix_info->bg = values->background;
    }
    if (flags & GCPlaneMask) {
      XmuXdebug (debug_resources, " plane msk %u",
		 values->plane_mask);
      res_info->u.gcpix_info->plane_mask = values->plane_mask;
    }
    /* insert it */
    InsertClientResource (dpy, res_info);
  }
  else {
    res_info->u.gcpix_info->flags = flags;
    if (flags & GCFunction) {
      XmuXdebug (debug_resources, " function %u",
		 values->function);
      res_info->u.gcpix_info->function = values->function;
    }
    if (flags & GCForeground) {
      XmuXdebug (debug_resources, " fg %u", values->foreground);
      res_info->u.gcpix_info->fg = values->foreground;
    }
    if (flags & GCBackground) {
      XmuXdebug (debug_resources, " bg %u", values->foreground);
      res_info->u.gcpix_info->bg = values->background;
    }
    if (flags & GCPlaneMask) {
      XmuXdebug (debug_resources, " plane msk %u",
		 values->plane_mask);
      res_info->u.gcpix_info->plane_mask = values->plane_mask;
    }
  }
  XmuXdebug (debug_resources, " done\n");
}

void
PrintGCPixelsInfo (dpy, gcpix_info)
  Display *dpy;
  MUXGCPixelsInfoPtr gcpix_info;
{
}

MUXGCPixelsInfoPtr
GetGCPixelsInfo (dpy, gid)
  Display *dpy;
  XID gid;
{
  register MUXResourceInfoPtr resource_info;

  resource_info = GetClientResource (dpy, gid, RT_GCPIXELS);

  return ((resource_info != NullResourceInfoPtr)
	  ? resource_info->u.gcpix_info
	  : NullGCPixelsInfoPtr);
}

void
InitClientResources ()
{
  register int i;

  for (i = 0; i < MAXSOCKS; i++) {
    MUXClientResources[i].head =
      MUXClientResources[i].tail = NullResourceInfoPtr;
    MUXClientResources[i].privcmaps = 0;
    MUXClientResources[i].update = False;
  }
}

/*
 * That's what every "good" X application should do on normal termination:
 * free all allocated resources
 */

void
XmuXFreeClientResources (dpy)
  Display *dpy;			/* index in the resource info table */
{
  MUXResourceInfoPtr resource_info;
  void ListClientResources ();

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

  if (debug_resources)
    ListClientResources (dpy);

  /* do FIRST OUT */
  while (MUXClientResources[ConnectionNumber (dpy)].head !=
	 NullResourceInfoPtr) {
    resource_info = MUXClientResources[ConnectionNumber (dpy)].head;
    MUXClientResources[ConnectionNumber (dpy)].head =
      resource_info->next;
    resource_info->next = NullResourceInfoPtr;
    FreeResourceInfo (resource_info);
  }

  /* the list is empty ! */
  MUXClientResources[ConnectionNumber (dpy)].tail = NullResourceInfoPtr;

}

/*
 * debugging and information routine
 */
void
ListClientResources (dpy)
  Display *dpy;
{
  MUXResourceInfoPtr entry;

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

  XmuXdebug (debug_resources, "Client %d uses the following resources:\n",
	     ConnectionNumber (dpy));

  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next) {
    switch (entry->type) {

    case RT_WINDOW:
      PrintWindowInfo (dpy, entry->u.win_info);
      break;

    case RT_GC:
      PrintGCInfo (entry->u.gc_info);
      break;

    case RT_PIXMAP:
      PrintPixmapInfo (dpy, entry->u.pmap_info);
      break;

    case RT_CURSOR:
      PrintCursorInfo (entry->u.curs_info);
      break;

    case RT_FONT:
      PrintFontInfo (entry->u.font_info);
      break;

    case RT_COLORMAP:
      PrintColormapInfo (entry->u.cmap_info);
      break;
    case RT_GCPIXELS:
      PrintGCPixelsInfo (entry->u.gcpix_info);
      break;
    }
  }
}

Bool
XmuXStoreResources (dpy)
  Display *dpy;
{

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

  return (ConnectionTranslation[ConnectionNumber (dpy)]->storeResources);

}

XID
XmuXMatchDrawable (dpy, depth)
  Display *dpy;
  int depth;
{
  MUXResourceInfoPtr entry;
  int ret_screen, ret_depth;

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

  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    if ((entry->used) && RIGHTTYPE (entry))
      if (CompareDepth (entry, depth))
	return (entry->id);

  return (None);
}

int
XmuXBmapStillReferenced (dpy, bitmap)
  Display *dpy;
  Pixmap bitmap;
{
  register MUXResourceInfoPtr entry;

  /*
   * is this bitmap (or pixmap) still referenced in another resource info?
   */
  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    switch (entry->type) {

    case RT_CURSOR:
      if ((entry->u.curs_info->cursor_type == PIXMAP_CURSOR) &&
	  ((entry->u.curs_info->u.pixmap_cursor.source_pmap == bitmap) ||
	   (entry->u.curs_info->u.pixmap_cursor.mask_pmap == bitmap)))
	return (True);
      break;
    case RT_WINDOW:
      if ((entry->u.win_info->bg_pmap == bitmap) ||
	  (entry->u.win_info->border_pmap == bitmap))
	return (True);
      break;
    case RT_GC:
      if ((entry->u.gc_info->gc->values.tile == bitmap) ||
	  (entry->u.gc_info->gc->values.stipple == bitmap))
	return (True);
      break;
    default:
      break;
    }

  return (False);
}

int
XmuXFontStillReferenced (dpy, font)
  Display *dpy;
  Font font;
{
  register MUXResourceInfoPtr entry;

  /* is font still referenced in a glyph cursor info or gc info? */
  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    switch (entry->type) {

    case RT_CURSOR:
      if ((entry->u.curs_info->cursor_type == GLYPH_CURSOR) &&
	  ((entry->u.curs_info->u.glyph_cursor.source_font == font) ||
	   (entry->u.curs_info->u.glyph_cursor.mask_font == font)))
	return (True);
      break;
    case RT_GC:
      if (entry->u.gc_info->gc->values.font == font)
	return (True);
      break;
    default:
      break;
    }
  return (False);
}

int
XmuXCursorStillReferenced (dpy, cursor)
  Display *dpy;
  Cursor cursor;
{
  register MUXResourceInfoPtr entry;

  /* is this cursor still referenced in a window info? */
  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    if ((entry->type == RT_WINDOW) &&
	(entry->u.win_info->cursor == cursor))
      return (True);
  return (False);
}

int
XmuXColormapStillReferenced (dpy, colormap)
  Display *dpy;
  Colormap colormap;
{
  register MUXResourceInfoPtr entry;

  /* is colormap still referenced in a window info? */
  for (entry = MUXClientResources[ConnectionNumber (dpy)].head;
       entry != NullResourceInfoPtr;
       entry = entry->next)
    if ((entry->type == RT_WINDOW) &&
	(entry->u.win_info->colormap == colormap))
      return (True);
  return (False);
}

int
XmuXUsesPrivateCmaps (dpy)
  Display *dpy;
{
  register Display *dflt_dpy;

  dflt_dpy = XmuXPrimaryDisplayFromDisplay (dpy);
  return (MUXClientResources[ConnectionNumber (dflt_dpy)].privcmaps > 0);
}

Bool
XmuXIsPixmap (dpy, rID)
  Display *dpy;
  XID rID;
{
  if (!IsClientDisplay (dpy)) {
    rID = XmuXMapID (dpy, rID, FromServer);
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
  }
  return (ResourceType (dpy, rID) == RT_PIXMAP);
}

int
XmuXDepthViaVisual (dpy, window, depth)
  Display *dpy;
  Window window;
  int *depth;
{
  XID visualID;
  Display *default_dpy;

  /*
   * First of all, we need the default display pointer to retrieve the
   * visual ID
   */
  if (!IsClientDisplay (dpy)) {
    window = XmuXMapID (dpy, window, FromServer);
    default_dpy = XmuXPrimaryDisplayFromDisplay (dpy);
  }
  else
    default_dpy = dpy;

  if (!GetVisualID (default_dpy, window, &visualID))
    return (False);
  else {

    /*
     * NOTE: Visual ID must be mapped !!
     */
    if (!XmuXDepthFromVisualID (dpy,
				XmuXMapID (dpy, visualID, FromClient),
				depth))
      return (False);
    else
      return (True);
  }
}
