/* $Id: conmgr.c,v 1.3 90/12/05 13:46:07 swick 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:	conmgr.c,v $
 * Revision 1.3  90/12/05  13:46:07  swick
 * comment out calls to clock() needed only for debugging to try
 * to get this to compile on non-sysV systems.
 * 
 * Revision 1.2  90/11/29  10:05:05  altenhof
 * checked in with -k by swick at 90.12.05.13.45.21.
 * 
 * Revision 1.2  90/11/29  10:05:05  altenhof
 * BUG FIX in XmuXRemoveToplevelInfo (ApplCtx.c): ntops-- would leave a
 * None toplevel if you removed the last toplevel info
 * Cleanups in XRegion.c: Substitute FatalError with fprintf(stderr,...);exit(1)
 * Changes in other sources implement a new feature: 'Selective window
 * sharing' (you can hide windows!)
 * 
 * Revision 1.1.1.1  90/11/28  17:01:52  altenhof
 * First public release
 * 
 * Revision 1.1  90/11/28  17:01:49  altenhof
 * Initial revision
 * 
 * Revision 1.1.1.1  90/11/28  16:25:09  spanachi
 * First public version of shX
 * 
 * Revision 1.1  90/11/28  16:25:07  spanachi
 * Initial revision
 * 
 *
 * Revision 1.2  90/07/26  08:31:47  altenhof
 * Changes implement the new feature (share applications from start) without
 * ringing the bell
 * 
 * Revision 1.1.1.1  90/04/23  10:11:40  spanachi First version of 'shared X'
 * library (X11R3).
 * 
 * Revision 1.1  90/04/23  10:11:35  spanachi Initial revision
 * 
 * Revision 1.1  90/04/03  15:13:02  altenhof Initial revision
 * 
 * Revision 1.1  89/11/03  14:45:16  neideck Original send to X consortium
 * 
 * Revision 2.3  89/09/27  19:37:57  michael XXmuXMultiplex has been renamed to
 * XmuXReplicate Code has been added to fix the "bug" from
 * XmuXInsertWindowInfo (see comments there)
 * 
 * Revision 2.2  89/09/07  19:20:03  michael Changes in XmuXFlushGC due to
 * changes in XmuXMapPixelsAndPlane. Code to manage private cells from
 * XAllocColorPlanes added ( but not tested ! ). Register window colormaps
 * even if they are already duplicated.
 * 
 * Revision 2.1  89/09/05  17:24:54  michael FIXED BUG : Plane generation was
 * wrong .
 * 
 * Revision 2.0  89/09/01  12:37:38  michael This is the first version that
 * supports clients allocating private color cells Restriction : Does not
 * duplicate XAllocColorPlanes ( will be done later !? )
 * 
 * Revision 1.7  89/08/23  11:49:25  michael *** empty log message ***
 * 
 * Revision 1.6  89/08/15  15:37:47  michael We have a new file ( ApplCtx.c )
 * that deals with "application specific" data. We define "application" as
 * one client connection to the server, i.e. an "X" applications may be
 * departed into several "XmuX" applications. Due to this, some routine
 * interfaces/ calling sequences have changed.
 * 
 * Revision 1.5  89/08/15  10:48:49  michael Old version of DuplicateWindow was
 * buggy (unnecessarily added/changed toplevel infos). Multiplexing may now
 * be triggered from all displays.
 * 
 * Revision 1.4  89/08/10  18:48:30  michael Changes: multiplex now allows
 * broadcasting ! XmuXFlushGC implements the "how friendly applications use
 * "real" pixmaps" rule (you heard about it ?)
 * 
 * Revision 1.3  89/07/20  20:16:16  michael It would have been better to make
 * this revision version 2.0. The resource list is scaned three times: First
 * we allocate all windows to get a "drawable save set". We may have to use
 * "matching" windows (if original drawables have been destroyed) to create
 * GC's/pixmaps/colormaps and these must be mapped before we can use them.
 * 
 * Revision 1.2  89/07/07  08:45:01  michael *** empty log message ***
 * 
 * Revision 1.1  89/06/09  13:34:54  michael Initial revision
 * 
 */

#define NEED_RESOURCES
#define NEED_EVENTS

#include "XmuX.h"
#include "Xlibint.h"
#include "XmuXlibint.h"
#include "Xutil.h"
#include <X11/Xatom.h>
#include "resources.h"
#include "ApplCtx.h"
#include "build.h"
#include "map.h"

#include "macros.h"

int do_put_get, do_sort, do_map_pmap, dont_map_b_and_w;

#define MAX_PER_CALL 256	/* -- query 8 plane colormap cells in one
				 * call */
#define HUGE_PIXMAP  1024	/* 32 * 32 */

#define DEFAULT_LENGTH 8192L

static void
FreeDpyNames (dpy_list, i)
  char **dpy_list;
  int i;
{
  while (--i >= 0)
    Xfree (dpy_list[i]);

  Xfree ((char *) dpy_list);
}

static char *
ExtractDisplayName (buffer)
  char *buffer;
{
  register char *display_ptr;	/* Display string buffer pointer */
  register char *name_ptr;	/* Server number buffer pointer */

  /*
   * Find the ',' seperator assume that between buffer start and separator
   * buffer contains a valid display name (keep things simple:  ConnDis will
   * tell us if we were wrong)
   */
  if ((display_ptr = SearchString (buffer, ',')) == NULL)
    /*-- may be we're at the end of the list -- */
    if ((display_ptr = SearchString (buffer, '\0')) == NULL)
      return (NULL);
  /* -- now buffer points to the start and display_ptr to the end (+1) -- */
  name_ptr = (char *) Xmalloc (display_ptr - buffer + 1);
  if (name_ptr == NULL)
    return (NULL);
  /* -- copy -- */
  bcopy (buffer, name_ptr, display_ptr - buffer);
  name_ptr[display_ptr - buffer] = '\0';
  return (name_ptr);
}

#define SEPARATOR ','

char **
CreateDpyNameList (display_names, n)
  char *display_names;
  int *n;
{
  register int i;
  register char **dpy_list;
  register char *displaybuf;

  if (display_names == NULL) {
    *n = 0;
    return ((char **) NULL);
  }

  /*
   * we assume display_names is a list of valid display names 
   * separated by colons (like sm's host list), terminated by \0 with no
   * whitespaces allowed !
   */
  i = 1;
  /* scan the list one time to determine the number of display names */
  for (displaybuf = display_names; *displaybuf != '\0'; *displaybuf++)
    if (*displaybuf == SEPARATOR)
      i++;

  dpy_list = (char **) Xmalloc ((sizeof (char *) * i));
  *n = i;
  /* second scan: put each valid display name in one array pointer */
  displaybuf = display_names;
  i = 0;
  while (*displaybuf) {
    dpy_list[i] = NULL;
    dpy_list[i] = ExtractDisplayName (displaybuf);
    /* something went wrong */
    if (dpy_list[i] == NULL) {
      FreeDpyNames (dpy_list, i);
      *n = 0;
      return (NULL);
    }
    displaybuf += strlen (dpy_list[i]);
    if (*displaybuf)
      /* next character MUST be the separating colon ! */
      if (*displaybuf != SEPARATOR) {
	FreeDpyNames (dpy_list, i);
	*n = 0;
	return (NULL);
      }
      else
	displaybuf++;
    i++;
  }
  return (dpy_list);
}

#define NUM_VISUAL_CLASSES 6

static int class_weight[NUM_VISUAL_CLASSES] = {
  0,				/* StaticGray */
  1,				/* GrayScale */
  2,				/* StaticColor */
  4,				/* PseudoColor *//* is better than TrueColor,
				 * since we can alloc private cells */
  3,				/* TrueColor */
  5				/* DirectColor */
};

static XID
FindBestVisual (dpy, v_info, vis_list, n)
  Display *dpy;
  XVisualInfo *v_info, *vis_list;
  int n;
{
  register int i, class;
  register unsigned int mapped_depth;

  mapped_depth = XmuXMapDepth (dpy, v_info->depth, None, None);

  i = 0;
  while (vis_list[i].depth < mapped_depth)
    i++;

  if (mapped_depth >= v_info->depth) {
    class = v_info->class;

    while (i < n && (vis_list[i].depth == mapped_depth &&
		     class_weight[class] > class_weight[vis_list[i].class]))
      i++;

    /*
     * if we don't find an exact match, return the biggest one we have
     * with this depth
     */
    if (vis_list[i].depth > mapped_depth)
      return (vis_list[i > 0 ? i - 1 : 0].visualid);
    else
      return (vis_list[i].visualid);
  }
  else {

    /*
     * try the biggest one we have
     */
    while (i < n && vis_list[i].depth == mapped_depth)
      i++;
    return (vis_list[i > 0 ? i - 1 : 0].visualid);
  }
}

static void
GeneratePlanes (i, cl_planes, cl_mask, sv_planes, sv_mask)
  int i;
  unsigned long *cl_planes, *cl_mask, *sv_planes, *sv_mask;
{
  register int j;

  *cl_mask =
    *sv_mask = 0L;

  for (j = 0; (1L << j) <= i; j++)
    if ((1L << j) & i) {
      *cl_mask |= cl_planes[j];
      *sv_mask |= sv_planes[j];
    }
}

static int
DuplicateProperties (dflt_dpy, new_dpys, n, old_win)
  Display *dflt_dpy, **new_dpys;
  int n;
  Window old_win;
{
  register Atom *properties, new_atom, new_type;
  register char *atom_name, *type_name;
  int nprops, k;

  nprops = 0;
  properties = NULL;
  /* get all defined properties */
  properties = XListProperties (dflt_dpy, old_win, &nprops);

  if (!nprops) {

    /*
     * nothing to do, but "free" the pointer You ask why ? Well,
     * without freeing we may get a bus error in malloc (believe it or
     * not)!
     */
    Xfree ((char *) properties);
    return (XmuXSuccess);

  }
  XmuXdebug (debug_mux, "window 0x%lx duplicate %d properties :",
	     old_win, nprops);

  /*****************************************************************
   * two possible cases  :
   *  a) next property is a predefined one :
   *     -->retrieve value on the old server via XGetWindowProperty 
   *     -->set value on the new server via XChangeProperty
   *  b) next property is not predefined :
   *     -->retrieve value on the old server
   *     -->retrieve atom name for this property via XGetAtomName
   *     -->try to create Atom on the new server via XInternAtom
   *     -->if the two Atom values differ, store them in the ID map 
   *     -->set value on the new server 
   *****************************************************************/

  for (; --nprops >= 0;) {
    Atom actual_type;
    int actual_format, actual_length, bytes_after;
    char *actual_data;

    /*
     * We only know the property atom; property type and format will be
     * returned by XGetWindowProperty (but only because we ask for a
     * property that exists for the specified window !)
     */
    actual_data = NULL;
    actual_format =
      actual_length =
      bytes_after = 0;
    actual_type = None;

    XGetWindowProperty (dflt_dpy, old_win, properties[nprops],
			0,	/* offset */
			DEFAULT_LENGTH,	/* data length */
			False,	/* delete ? */
			AnyPropertyType, /* prop type */
			&actual_type,	/* returned prop type */
			&actual_format,	/* returned prop format */
			&actual_length,	/* returned prop length (items) */
			&bytes_after,	/* remaining bytes */
			&actual_data	/* returned data */
      );

    new_atom = properties[nprops];
    new_type = actual_type;

#ifdef ADDSIZEHINTS
    /* don't alter WM_NORMAL_HINTS property again */
    if (properties[nprops] == XA_WM_NORMAL_HINTS)
      continue;
#endif
    for (k = 0; k < n; k++) {
      if (properties[nprops] > XA_LAST_PREDEFINED) {
	if (k == 0)
	  atom_name = XGetAtomName (dflt_dpy,
				    properties[nprops]);
	new_atom = XInternAtom (new_dpys[k], atom_name, False);
	if (!new_atom) {
	  unsigned char error_request;

	  if (XmuXCheckLastError (new_dpys[k], BadAtom,
				  &error_request))
	    XmuXErrorF ("Can't duplicate properties!\n");
	  /* other errors were reported via _XError */
	  Xfree ((char *) properties);
	  Xfree (actual_data);
	  return (XmuXFailure);
	}

	if (properties[nprops] != new_atom) {
	  XmuXInsertID (new_dpys[k], properties[nprops],
			new_atom, FromClient);
	  XmuXInsertID (new_dpys[k], new_atom,
			properties[nprops], FromServer);

	  /*
	   * assume property atom == property type
	   */
	  new_type = new_atom;
	}
	if ((actual_type > XA_LAST_PREDEFINED) &&
	    (actual_type != properties[nprops])) {
	  if (k == 0)
	    type_name = XGetAtomName (dflt_dpy, actual_type);
	  new_type = XInternAtom (new_dpys[k], type_name,
				  False);
	  if (!new_type) {
	    unsigned char error_request;

	    if (XmuXCheckLastError (new_dpys[k], BadAtom,
				    &error_request))
	      XmuXErrorF ("Can't duplicate properties!\n");
	    /* other errors were reported via _XError */
	    Xfree ((char *) properties);
	    Xfree (actual_data);
	    return (XmuXFailure);
	  }
	  if (actual_type != new_type) {
	    XmuXInsertID (new_dpys[k], actual_type, new_type,
			  FromClient);
	    XmuXInsertID (new_dpys[k], new_type, actual_type,
			  FromServer);
	    actual_type = new_type;
	  }
	}
      }

      XmuXdebug (debug_mux, "property %d (format %d , %d items)\n",
		 properties[nprops], actual_format, actual_length);


      if (actual_format == 32)
	/* long properties may contain IDs !! */
	CheckProperty (new_dpys[k], new_atom, actual_data,
		       actual_length, FromClient);
      XChangeProperty (new_dpys[k],
		       XmuXMapID (new_dpys[k], old_win,
				  FromClient),
		       new_atom,
		       new_type, actual_format,
		       PropModeReplace, actual_data,
		       actual_length);
      Xfree (actual_data);
      while (bytes_after) {

	/*
	 * DEFAULT_LENGTH was too short (a giant property!) retrieve
	 * the rest
	 */
	if (k == 0)
	  XGetWindowProperty (dflt_dpy, old_win,
			      properties[nprops],
			      0, bytes_after, False,
			      AnyPropertyType, &actual_type,
			      &actual_format, &actual_length,
			      &bytes_after, &actual_data);

	if (actual_format == 32)
	  CheckProperty (new_dpys[k], new_atom, actual_data,
			 actual_length, FromClient);
	XChangeProperty (new_dpys[k],
			 XmuXMapID (new_dpys[k], old_win,
				    FromClient),
			 new_atom,
			 new_type, actual_format,
			 PropModeAppend, actual_data,
			 actual_length);
	Xfree (actual_data);
      }
    }
  }
  XmuXdebug (debug_mux, "done\n");
  Xfree ((char *) properties);
  return (XmuXSuccess);
}

#define VisualIDFromVisual( visual )  (visual->visualid )

static void
CompareDisplays (dpy1, dpy2)
  Display *dpy1, *dpy2;
{
  register int i;
  int n1, n2;
  register XVisualInfo *vis_infos[2];
  XVisualInfo vis_tmp;

  /*****************************************************************
   * Here we try to find out the differences between two displays: 
   * We're mainly interested in
   *   --> the depths that are supported (maybe we'll must "match" depths)
   *   --> the visuals that are available 
   * NOTE : dpy1, the default display, may use more than one screen, whereas
   * dpy2 only uses the default screen. We use the utility function
   * XGetVisualInfo here, which returns the visuals sorted by depth, and each
   * depth set sorted by class. We map the DefaultVisual(s) to the
   * DefaultVisual to avoid complications with the default colormaps.
   *****************************************************************/
  vis_tmp.screen = DefaultScreen (dpy2);
  vis_infos[1] = XGetVisualInfo (dpy2, VisualScreenMask,
				 &vis_tmp, &n2);

  for (i = 0; i < ScreenCount (dpy1); i++) {
    XID vid;
    int j;

    vis_tmp.screen = i;
    vis_infos[0] =
      XGetVisualInfo (dpy1, VisualScreenMask, &vis_tmp, &n1);

    for (j = 0; j < n1; j++) {
      if (vis_infos[0][j].visual == DefaultVisual (dpy1, i))
	vid = VisualIDFromVisual (
				   DefaultVisual (dpy2,
						  DefaultScreen (dpy2)));
      else
	vid = FindBestVisual (dpy2, &(vis_infos[0][j]),
			      vis_infos[1], n2);

      XmuXInsertID (dpy2, vid, vis_infos[0][j].visualid,
		    FromServer);
      XmuXInsertID (dpy2, vis_infos[0][j].visualid, vid,
		    FromClient);
    }
  }
}

#define GCIDFromGC( gc )              ( gc->gid )

static void
MapDefaultIDs (prim_server, new_server)
  MUXServerPtr prim_server, new_server;
{
  register XID client_id, server_id;
  register Display *dflt_dpy = prim_server->xdpy, *new_dpy = new_server->xdpy;
  register int i;

  /* map the default roots */

  XmuXInsertID (new_dpy, DefaultRootWindow (dflt_dpy),
		DefaultRootWindow (new_dpy), FromClient);
  XmuXInsertID (new_dpy, DefaultRootWindow (new_dpy),
		DefaultRootWindow (dflt_dpy), FromServer);

  /* map the default GC */
  client_id = GCIDFromGC (DefaultGC (dflt_dpy, DefaultScreen (dflt_dpy)));
  server_id = GCIDFromGC (DefaultGC (new_dpy, DefaultScreen (new_dpy)));

  XmuXInsertID (new_dpy, client_id, server_id, FromClient);
  XmuXInsertID (new_dpy, server_id, client_id, FromServer);

  /* map the XmuX-specific atoms (XMUX_MULTIPLEX, XMUX_MESSAGE,...) */
  XmuXdebug (debug_mux, "map XmuX atoms :\n");
  for (i = 0; i < MAXATOM; i++) {
    XmuXdebug (debug_mux, "%u <--> %u \n",
	       prim_server->atoms[i], new_server->atoms[i]);
    XmuXInsertID (new_dpy, prim_server->atoms[i],
		  new_server->atoms[i], FromClient);
    XmuXInsertID (new_dpy, new_server->atoms[i],
		  prim_server->atoms[i], FromServer);
  }
}

static int
AllocColorCells (dflt_dpy, old_cmap, new_dpys, new_cmaps, n, pr_cells)
  Display *dflt_dpy, **new_dpys;
  Colormap old_cmap, *new_cmaps;
  int n;
  MUXPrivateColorCells *pr_cells;
{
  register int k;
  unsigned long *server_planes = NULL, *server_pixels = NULL, s_plane, c_plane, num_planes;
  XColor *colors;


  server_pixels = (unsigned long *)
    Xmalloc (pr_cells->n_pixels * sizeof (unsigned long));
  server_planes = (unsigned long *)
    Xmalloc (pr_cells->n_planes * sizeof (unsigned long));

  XmuXdebug (debug_mux, " AllocCells : %d pixels %d planes ",
	     pr_cells->n_pixels,
	     pr_cells->n_planes);
  if (!server_planes && !server_pixels) {
    XmuXErrorF ("AllocCells: Out of mem!(alloc private cells)!\n");
    return (XmuXFailure);
  }

  if ((colors = (XColor *)
       Xmalloc (pr_cells->n_pixels * sizeof (XColor))) == NULL) {
    XmuXErrorF ("AllocCells: Out of mem!(alloc color array)!\n");
    Xfree ((char *) server_planes);
    Xfree ((char *) server_pixels);
    return (XmuXFailure);
  }

  for (k = 0; k < n; k++) {
    register int i, j;

    if (!XAllocColorCells (new_dpys[k], new_cmaps[k], False,
			   server_planes, pr_cells->n_planes,
			   server_pixels, pr_cells->n_pixels)) {
      unsigned char error_request;

      if (XmuXCheckLastError (new_dpys[k], BadAlloc,
			      &error_request)) {
	XmuXErrorF ("AllocCells: can't Alloc private cells !\n");
	Xfree ((char *) server_planes);
	Xfree ((char *) server_pixels);
	Xfree ((char *) colors);
	Xfree ((char *) new_cmaps);
	return (XmuXFailure);
      }
    }

    XmuXInsertPrivateCells (new_dpys[k], new_cmaps[k],
			    pr_cells->pixels, server_pixels,
			    pr_cells->n_pixels,
			    pr_cells->planes, server_planes,
			    pr_cells->n_planes, True);

    num_planes = (int) (1L << pr_cells->n_planes);

    for (i = 0; i < num_planes; i++) {
      GeneratePlanes (i, pr_cells->planes, &c_plane,
		      server_planes, &s_plane);
      for (j = 0; j < pr_cells->n_pixels; j++)
	colors[j].pixel = pr_cells->pixels[j] | c_plane;

      XQueryColors (dflt_dpy, old_cmap, colors,
		    pr_cells->n_pixels);

      for (j = 0; j < pr_cells->n_pixels; j++)
	colors[j].pixel = server_pixels[j] | s_plane;

      XStoreColors (new_dpys[k], new_cmaps[k], colors,
		    pr_cells->n_pixels);
    }
  }
  Xfree ((char *) server_planes);
  Xfree ((char *) server_pixels);
  Xfree ((char *) colors);
  return (XmuXSuccess);
}

#define RED   0
#define GREEN 1
#define BLUE  2

static int
AllocColorPlanes (dflt_dpy, old_cmap, new_dpys, new_cmaps, n, pr_cells)
  Display *dflt_dpy, **new_dpys;
  Colormap old_cmap, *new_cmaps;
  int n;
  MUXPrivateColorCells *pr_cells;
{
  register int k;
  unsigned long *server_pixels = NULL, client_planes[3], server_planes[3], s_plane, c_plane;
  unsigned int nplanes[3];
  XColor *colors;

  server_pixels = (unsigned long *)
    Xmalloc (pr_cells->n_pixels * sizeof (unsigned long));
  if (!server_pixels) {
    XmuXErrorF ("AllocPlanes: Out of mem (alloc pixels)!\n");
    return (XmuXFailure);
  }

  /*
   * assume: red_mask is pr_cells->planes[0] , green_mask is
   * pr_cells->planes[1], blue_mask is pr_cells->planes[2]
   */
  client_planes[RED] = pr_cells->planes[RED];
  client_planes[GREEN] = pr_cells->planes[GREEN];
  client_planes[BLUE] = pr_cells->planes[BLUE];
  nplanes[RED] =
    nplanes[GREEN] =
    nplanes[BLUE] = 0;

  for (k = 0; k < sizeof (unsigned long); k++) {
    if ((1L << k) & client_planes[RED])
      nplanes[RED]++;
    if ((1L << k) & client_planes[GREEN])
      nplanes[GREEN]++;
    if ((1L << k) & client_planes[BLUE])
      nplanes[BLUE]++;
  }
  XmuXdebug (debug_mux,
	     " AllocPlanes : %d pixels %d red %d green %d blue ",
	     pr_cells->n_pixels,
	     nplanes[RED], nplanes[GREEN], nplanes[BLUE]);

  colors = (XColor *)
    Xmalloc (pr_cells->n_pixels * sizeof (XColor));

  if (!colors) {
    XmuXErrorF ("AllocPlanes: Out of mem!(alloc color array)!\n");
    Xfree ((char *) server_pixels);
    return (XmuXFailure);
  }

  for (k = 0; k < n; k++) {
    register int i, j;

    if (!XAllocColorPlanes (new_dpys[k], new_cmaps[k], False,
			    server_pixels, pr_cells->n_pixels,
			    nplanes[RED],
			    nplanes[GREEN],
			    nplanes[BLUE],
			    &server_planes[RED],
			    &server_planes[GREEN],
			    &server_planes[BLUE])) {
      unsigned char error_request;

      if (XmuXCheckLastError (new_dpys[k], BadAlloc,
			      &error_request)) {
	XmuXErrorF ("AllocPlanes: can't Alloc private cells !\n");
	Xfree ((char *) new_cmaps);
	Xfree ((char *) server_pixels);
	return (XmuXFailure);
      }
    }

    XmuXInsertPrivateCells (new_dpys[k], new_cmaps[k],
			    pr_cells->pixels, server_pixels,
			    pr_cells->n_pixels,
			    pr_cells->planes, server_planes,
			    3, False);

    for (i = RED; i <= BLUE; i++) {
      unsigned long s_base, c_base;

      s_base = lowbit (server_planes[i]);
      c_base = lowbit (client_planes[i]);
      s_plane =
	c_plane = 0L;

      while (1) {
	register int j;

	for (j = 0; j < pr_cells->n_pixels; j++)
	  colors[j].pixel = pr_cells->pixels[j] | c_plane;

	XQueryColors (dflt_dpy, old_cmap, colors,
		      pr_cells->n_pixels);

	for (j = 0; j < pr_cells->n_pixels; j++)
	  colors[j].pixel = server_pixels[j] | s_plane;

	XStoreColors (new_dpys[k], new_cmaps[k], colors,
		      pr_cells->n_pixels);
	/* macro from X server sources */
	GetNextBitsOrBreak (s_plane, server_planes[i], s_base);
	GetNextBitsOrBreak (c_plane, client_planes[i], c_base);
      }
    }
  }
  Xfree ((char *) colors);
  Xfree ((char *) server_pixels);
  return (XmuXSuccess);
}

static int
DuplicateColormap (dflt_dpy, new_dpys, n, old_cmap, new_windows)
  Display *dflt_dpy, **new_dpys;
  int n;
  Colormap old_cmap;		/* UNMAPPED ID !! */
  Window *new_windows;		/* MAPPED IDs !! */
{
  register int total, n_colors, i, j, k, allocated_here, dflt_cmap;
  register unsigned long client_pixel;
  register XColor *colors;
  register Colormap *new_cmaps;
  MUXColormapInfoPtr cmap_info;
  MUXPrivateColorCells *pr_cells;

  /*
   * don't create a colormap twice ! Check on any new connection BUT :
   * register it as this window's current colormap !
   */
  if (XmuXIsRegistered (new_dpys[0], old_cmap, FromClient)) {
    if (new_windows)
      for (k = 0; k < n; k++)
	XmuXSetWindowCmap (new_dpys[k], new_windows[k],
			   XmuXMapID (new_dpys[k], old_cmap,
				      FromClient), None);
    return (XmuXSuccess);
  }

  /* allocate space for new colormap IDs */
  new_cmaps = (Colormap *) Xmalloc (n * sizeof (Colormap));
  if (new_cmaps == NULL) {
    XmuXErrorF ("DuplicateColormap: Out of mem (new_cmaps)!\n");
    return (XmuXFailure);
  }

  cmap_info = XmuXGetColormapInfo (dflt_dpy, old_cmap);
  allocated_here = False;
  /* Call from the loop (not from DuplicateWindow ) ? */
  if (new_windows == NULL) {
    allocated_here = True;
    new_windows = (Window *) Xmalloc (n * sizeof (Window));
    if (new_windows == NULL) {
      XmuXErrorF ("DuplicateColormap: Out of mem (new_windows)!\n");
      return (XmuXFailure);
    }
    for (k = 0; k < n; k++)
      new_windows[k] = XmuXMapID (new_dpys[k],
				  cmap_info->window, FromClient);
  }
  dflt_cmap = True;
  /* default is mapped to default */
  if (old_cmap == DefaultColormap (dflt_dpy, DefaultScreen (dflt_dpy)))
    for (k = 0; k < n; k++) {
      new_cmaps[k] =
	DefaultColormap (new_dpys[k],
			 DefaultScreen (new_dpys[k]));

      /*
       * map the IDs
       */
      XmuXInsertID (new_dpys[k], old_cmap, new_cmaps[k],
		    FromClient);
      XmuXInsertID (new_dpys[k], new_cmaps[k], old_cmap,
		    FromServer);
    }
  else {
    Visual *new_vis;
    register Window cmap_win;

    dflt_cmap = False;
    if (!cmap_info->window)

      /*
       * find a "matching" window (i.e. on the same screen) screen's
       * root window is good
       */
      cmap_win = DefaultRootWindow (dflt_dpy);
    else
      cmap_win = cmap_info->window;

    for (k = 0; k < n; k++) {
      /* XXX Must be changed !?! */
      new_vis = _XVIDtoVisual (new_dpys[k],
			       XmuXMapID (new_dpys[k],
					  cmap_info->visualid,
					  FromClient));

      new_cmaps[k] = XCreateColormap (new_dpys[k],
				      XmuXMapID (new_dpys[k],
						 cmap_win,
						 FromClient),
				      new_vis,
				      AllocNone);

      /*
       * map the IDs
       */
      XmuXInsertID (new_dpys[k], old_cmap, new_cmaps[k],
		    FromClient);
      XmuXInsertID (new_dpys[k], new_cmaps[k], old_cmap,
		    FromServer);
    }
  }

  total = cmap_info->sh_allocated;
  XmuXdebug (debug_mux, "Duplicate %d shared colors...", total);
  if (total > MAX_PER_CALL) {
    colors = (XColor *) Xmalloc (MAX_PER_CALL * sizeof (XColor));
    n_colors = MAX_PER_CALL;
  }
  else {
    colors = (XColor *) Xmalloc (total * sizeof (XColor));
    n_colors = total;
  }
  if (colors == NULL) {
    XmuXErrorF ("DuplicateColors: Out of mem (colors) !\n");
    /* free the new cmaps array */
    Xfree ((char *) new_cmaps);
    return (XmuXFailure);
  }

  for (j = 0; total > 0; j++) {
    for (i = 0; i < n_colors; i++)
      colors[i].pixel = cmap_info->sh_cells[j * MAX_PER_CALL + i];

    /* do the round-trip ONE TIME ! */
    XQueryColors (dflt_dpy, old_cmap, colors, n_colors);

    for (i = 0; i < n_colors; i++) {
      client_pixel = colors[i].pixel;
      /* this round-trip N TIMES ! */
      for (k = 0; k < n; k++) {
	if (!XAllocColor (new_dpys[k],
			  new_cmaps[k],
			  &colors[i])) {
	  unsigned char error_request;
	  if (XmuXCheckLastError (new_dpys[k], Success,
				  &error_request))
	    XmuXErrorF ("Can't alloc new color %d!\n", i);

	  /* free the new cmap array */
	  Xfree ((char *) new_cmaps);
	  Xfree ((char *) colors);
	  return (XmuXFailure);
	}
	XmuXInsertSharedCell (new_dpys[k], new_cmaps[k],
			      client_pixel,
			      colors[i].pixel);
      }
    }
    total -= n_colors;
    if (total < n_colors)
      n_colors = total;
  }
  for (k = 0; k < n; k++) {
    if (do_sort)
      XmuXSortPixelMaps (new_dpys[k]);

    /*
     * register the new colormap as new_window's colormap Don't need
     * parent id in this call (since new_cmap != CopyFromParent )
     */
    XmuXSetWindowCmap (new_dpys[k], new_windows[k],
		       new_cmaps[k], None);
  }
  if (allocated_here)
    Xfree ((char *) new_windows);

  Xfree ((char *) colors);
  XmuXdebug (debug_mux, "done\n");

  /*
   * duplicate private color cells
   */
  for (pr_cells = cmap_info->pr_cells; pr_cells != NULL;
       pr_cells = pr_cells->next) {
    int status;

    XmuXdebug (debug_mux, "Duplicate %d private colors...",
	       pr_cells->n_pixels * (1L << pr_cells->n_planes));
    if (pr_cells->alloc_cells)
      status = AllocColorCells (dflt_dpy, old_cmap, new_dpys,
				new_cmaps, n, pr_cells);
    else
      status = AllocColorPlanes (dflt_dpy, old_cmap, new_dpys,
				 new_cmaps, n, pr_cells);
    if (status != XmuXSuccess)
      return (status);
    XmuXdebug (debug_mux, "done\n");
  }
  Xfree ((char *) new_cmaps);
  return (XmuXSuccess);
}

static int
DuplicateWindow (dflt_dpy, new_dpys, n, win_info)
  Display *dflt_dpy, **new_dpys;
  int n;
  MUXWindowInfoPtr win_info;
{
  XWindowAttributes win_attr;
  XSetWindowAttributes set_win_attr;
  register Window *new_windows;
  int x, y, k, top_level;
  int status;
  long mux_specific_mask;
  XSizeHints size_hints;

  /* allocate space for new windows array */
  new_windows = (Window *) Xmalloc (n * sizeof (Window));
  if (new_windows == NULL) {
    XmuXErrorF ("DupWindow: Out of mem (new windows)!\n");
    return (XmuXFailure);
  }

  /* Retrieve the unknown attributes from the original server */
  if (!XGetWindowAttributes (dflt_dpy, win_info->window, &win_attr)) {
    XmuXErrorF ("Can't get window 0x%lx's attributes!\n",
		win_info->window);
    Xfree ((char *) new_windows);
    return (XmuXFailure);
  }

  set_win_attr.bit_gravity = win_attr.bit_gravity;
  set_win_attr.win_gravity = win_attr.win_gravity;
  set_win_attr.event_mask = win_attr.your_event_mask;
  set_win_attr.do_not_propagate_mask = win_attr.do_not_propagate_mask;
  set_win_attr.override_redirect = win_attr.override_redirect;

  set_win_attr.backing_store = NotUseful;
  set_win_attr.backing_planes = AllPlanes;
  set_win_attr.backing_pixel = 0L;
  set_win_attr.save_under = False;

  /*
   * Hard stuff ahead: The window attributes background_pixmap,
   * border_pixmap, cursor and colormaps reference other resources which
   * may have been created later on the original connection and then 
   * set by Xlib convenience routines. To avoid server complaining we fill
   * in the "default values" (by "unmasking" them) and override them after
   * all resources have been created. BUT: Window's colormap may be
   * used in DuplicatePixmap to map pixel values; so we create the
   * colormap here (if it does not already exist) to guarantee that
   * XmuXMapPixel in XmuXMapImage will use the right one.
   */
#define InputOutputWindowAttributes\
        ( CWBitGravity | CWWinGravity | \
	  CWBackingStore | CWBackingPlanes | CWBackingPixel | \
	  CWOverrideRedirect | CWSaveUnder | CWEventMask | \
	  CWDontPropagate )
#define InputOnlyWindowAttributes\
        ( CWWinGravity | CWOverrideRedirect | CWEventMask | CWDontPropagate)

  /*
   * If this window is a toplevel window, add it to the top-level
   * table and change the event mask
   */
  if (XmuXIsRootWindow (dflt_dpy, win_info->parent) != -1) {
    Window junk_child;

    top_level = True;

    /*
     * Top-levels may have been reparented by a window manager in this
     * case we get a strange window position recalculate it via
     * XTranslateCoords
     */
    XTranslateCoordinates (dflt_dpy,
			   win_info->window,	/* source */
			   win_info->parent,	/* dest */
			   win_attr.x, win_attr.y,
			   &x, &y,
			   &junk_child);	/* not needed */

#ifdef ADDSIZEHINTS
    /* get old hints */
    if (XGetNormalHints (dflt_dpy, win_info->window, &size_hints))
      /* change the appropriate fields */
      size_hints.flags |= USPosition | USSize;
    else
      size_hints.flags = USPosition | USSize;
    size_hints.x = x;
    size_hints.y = y;
    size_hints.width = win_attr.width;
    size_hints.height = win_attr.height;
#endif
  }
  else {
    top_level = False;
    x = win_attr.x;
    y = win_attr.y;
  }

  /*
   * Store the map_state of the window, so that we can decide in
   * UpdateResourceWindowAttributes if we must map this window
   */
  win_info->map_state = win_attr.map_state;

  /*
   * Fix the "bug" concering window depth in XmuXInsertWindowInfo
   */
  if (win_attr.class == InputOnly)
    win_info->depth = 0;

  /* create the window on the new connections */
  XmuXdebug (debug_mux, "Duplicate window 0x%lx :\n",
	     win_info->window);

  for (k = 0; k < n; k++) {
    int depth;
    Visual *vis;

    vis = _XVIDtoVisual (new_dpys[k],
			 XmuXMapID (new_dpys[k],
				    win_info->visualid,
				    FromClient));
    if (!XmuXDepthFromVisualID (new_dpys[k],
				VisualIDFromVisual (vis), &depth))
      depth = win_info->depth;

    new_windows[k] =
      XCreateWindow (new_dpys[k],
		     XmuXMapID (new_dpys[k],
				win_info->parent, FromClient),
		     x,
		     y,
		     win_attr.width,
		     win_attr.height,
		     win_attr.border_width,
		     depth,
		     win_attr.class,
		     vis,
		     (win_attr.class == InputOutput) ?
		     InputOutputWindowAttributes :
		     InputOnlyWindowAttributes,
		     &set_win_attr);
    XmuXdebug (debug_mux, "-->0x%lx\n", new_windows[k]);

    XmuXInsertID (new_dpys[k], new_windows[k],
		  win_info->window, FromServer);
    XmuXInsertID (new_dpys[k], win_info->window,
		  new_windows[k], FromClient);
#ifdef ADDSIZEHINTS
    if (top_level)
      /* set new hints */
      XSetNormalHints (new_dpys[k], new_windows[k], &size_hints);
#endif
    XmuXdebug (debug_mux, "done\n");

  }
  status = XmuXSuccess;

  /*
   * Duplicate window's colormap (see comments above) loop is in
   * DuplicateColormap
   *
   * NOTE (forget it until now) : InputOnly windows have no colormap !
   */
  if (win_attr.class == InputOutput)
    status = DuplicateColormap (dflt_dpy, new_dpys, n,
				win_attr.colormap, new_windows);

  Xfree ((char *) new_windows);
  return (status);
#undef InputOutputWindowAttributes
#undef InputOnlyWindowAttributes
}

static int
DuplicateGC (dflt_dpy, new_dpys, n, gc_info)
  Display *dflt_dpy, **new_dpys;
  int n;
  MUXGCInfoPtr gc_info;
{
  register GC new_gc = NULL;
  register int k;
  register unsigned long gcmask;
  register Drawable gc_drawable, new_drawable, dummy = None;
  register XGCValues newgc_val, oldgc_val;

  /*
   * Hard stuff ahead: The GC values tile, stipple, font  and 
   * clip_mask reference other resources which may have been created later
   * on the original connection and then set by Xlib convenience routines.
   * To avoid server complaining we fill in the "default values" (by
   * "unmasking" them) and override them after all resources have been
   * created.
   */

#define AllGCValuesExceptResources \
        ( GCFunction | GCPlaneMask | GCForeground | GCBackground | \
	  GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle | \
	  GCFillStyle | GCFillRule | GCTileStipXOrigin | GCTileStipYOrigin | \
	  GCSubwindowMode | GCGraphicsExposures | GCClipXOrigin | \
	  GCClipYOrigin | GCDashOffset | GCDashList | GCArcMode )
  XmuXdebug (debug_mux, "Duplicate GC 0x%lx :\n",
	     gc_info->gc->gid);

  oldgc_val = gc_info->gc->values;

  if (!gc_info->drawable)
    /* find a "matching drawable" (same depth, same screen) */
    gc_drawable = XmuXMatchDrawable (dflt_dpy, gc_info->depth);
  else
    gc_drawable = gc_info->drawable;

  /*
   * set up values
   */
  newgc_val.function = oldgc_val.function;
  newgc_val.plane_mask = oldgc_val.plane_mask;
  newgc_val.line_width = oldgc_val.line_width;
  newgc_val.line_style = oldgc_val.line_style;
  newgc_val.cap_style = oldgc_val.cap_style;
  newgc_val.join_style = oldgc_val.join_style;
  newgc_val.fill_style = oldgc_val.fill_style;
  newgc_val.fill_rule = oldgc_val.fill_rule;
  newgc_val.arc_mode = oldgc_val.arc_mode;
  newgc_val.ts_x_origin = oldgc_val.ts_x_origin;
  newgc_val.ts_y_origin = oldgc_val.ts_y_origin;
  newgc_val.subwindow_mode = oldgc_val.subwindow_mode;
  newgc_val.graphics_exposures = oldgc_val.graphics_exposures;
  newgc_val.clip_x_origin = oldgc_val.clip_x_origin;
  newgc_val.clip_y_origin = oldgc_val.clip_y_origin;
  newgc_val.dash_offset = oldgc_val.dash_offset;
  newgc_val.dashes = oldgc_val.dashes;

  for (k = 0; k < n; k++) {
    new_drawable = XmuXMapID (new_dpys[k], gc_drawable, FromClient);

    if (!new_drawable)

      /*
       * no matching drawable found, create a dummy (a pixmap) withthe
       * appropriate depth
       */
      dummy =
	new_drawable =
	XCreatePixmap (new_dpys[k],
		       DefaultRootWindow (new_dpys[k]),
		       1, 1,
		       XmuXMapDepth (new_dpys[k],
				     gc_info->depth,
				     IsPixmap));

    /*
     * must map pixels
     */
    newgc_val.foreground = XmuXMapPixel (new_dpys[k],
					 new_drawable,
					 oldgc_val.foreground);
    newgc_val.background = XmuXMapPixel (new_dpys[k],
					 new_drawable,
					 oldgc_val.background);
    new_gc = XCreateGC (new_dpys[k],
			new_drawable,
			AllGCValuesExceptResources,
			&newgc_val);
    XmuXdebug (debug_mux, "-->0x%lx\n", new_gc->gid);

    if (new_gc == NULL) {
      XmuXErrorF ("DupGC: Duplication failed!\n");
      return (XmuXFailure);
    }

    /*
     * Until now, we're using a TEST VERSION, which calls XmuXFlushGC
     * before every graphic request (in fact, we want to test if
     * XmuXFlushGC works correctly). If we didn't store these values here,
     * XmuXFlushGC would report a fatal bug : "No GCPIXELS Info found !".
     * (This GC could be used without changing fg/bg !) REMEMBER : use
     * UNMAPPED pixel values
     */
    gcmask = (GCForeground | GCBackground);
    if (oldgc_val.function != GXcopy)
      gcmask |= GCFunction;
    if (oldgc_val.plane_mask != AllPlanes)
      gcmask |= GCPlaneMask;

    XmuXUpdateGCPixels (new_dpys[k], new_gc->gid,
			gcmask, &oldgc_val);

    XmuXInsertID (new_dpys[k], new_gc->gid,
		  gc_info->gid, FromServer);
    XmuXInsertID (new_dpys[k], gc_info->gid,
		  new_gc->gid, FromClient);

    /*
     * WE don't need it anymore (because we refer to this gc via the 
     * client's GC cache), but the multiplex'ed server does! That's why we
     * can't use XFreeGC here !
     */
    XFree ((char *) new_gc);

    /* if we have created a dummy pixmap, free it */
    if (dummy)
      XFreePixmap (new_dpys[k], dummy);
  }
  XmuXdebug (debug_mux, "done\n");

  return (XmuXSuccess);

#undef AllGCValuesExceptResources
}

static int
DuplicatePixmap (dflt_dpy, new_dpys, n, pmap_info)
  Display *dflt_dpy, **new_dpys;
  int n;
  MUXPixmapInfoPtr pmap_info;
{
  XImage *image;
  char *im_data;
  register GC put_gc;
  register Drawable pmap_drawable, new_drawable;
  register Pixmap new_pixmap;
  Window ret_root;
  int k, ret_x, ret_y, format, data_bytes;
  unsigned int ret_width, ret_height, ret_border_width, ret_depth;

  XmuXdebug (debug_mux, "Duplicate pixmap 0x%lx :\n",
	     pmap_info->pixmap);
  if (!XGetGeometry (dflt_dpy, pmap_info->pixmap, &ret_root,
		     &ret_x, &ret_y, &ret_width, &ret_height,
		     &ret_border_width, &ret_depth)) {
    XmuXErrorF ("Can't get pixmap 0x%lx geometry!\n",
		pmap_info->pixmap);
    return (XmuXFailure);
  }
  image = XGetImage (dflt_dpy,
		     pmap_info->pixmap,
		     0,
		     0,
		     ret_width,
		     ret_height,
		     AllPlanes,
		     ZPixmap);

  if (image == (XImage *) NULL) {
    XmuXErrorF ("Can't get pixmap 0x%lx's \"image\"!\n",
		pmap_info->pixmap);
    return (XmuXFailure);
  }
  if (!pmap_info->drawable)

    /*
     * find "matching" drawable, screen's root window fits
     */
    pmap_drawable = DefaultRootWindow (dflt_dpy);
  else
    pmap_drawable = pmap_info->drawable;
  if (n > 1) {
    data_bytes = image->height * image->bytes_per_line;
    if ((im_data = Xmalloc (data_bytes)) == NULL) {
      XmuXErrorF ("Can't buffer image data!\n");
      return (XmuXFailure);
    }
    bcopy (image->data, im_data, data_bytes);
  }
  for (k = 0; k < n; k++) {
    new_drawable = XmuXMapID (new_dpys[k], pmap_drawable,
			      FromClient);
    new_pixmap = XCreatePixmap (new_dpys[k],
				new_drawable,
				ret_width,
				ret_height,
				XmuXMapDepth (new_dpys[k],
					      ret_depth,
					      None, IsPixmap));

    XmuXdebug (debug_mux, "-->0x%lx\n", new_pixmap);
    put_gc = XCreateGC (new_dpys[k],
			new_pixmap, 0, (XGCValues *) NULL);

    if (put_gc == NULL) {
      XmuXErrorF ("Can't create new gc on connection %d!\n",
		  ConnectionNumber (new_dpys[k]));
      return (XmuXFailure);
    }
    /* only map "real" pixmaps ( depth > 1 ) ! */
    if (ret_depth != 1 && do_map_pmap) {
      int row, col;
      long before, after;
      if (n > 1)
	bcopy (im_data, image->data, data_bytes);

      /*
       * must do a lot of XmuXMapPixel --> sort the pixel map (i.e. use
       * binary search instead of linear search in XmuXMapPixel)
       */
      if ((ret_width * ret_height > HUGE_PIXMAP) && do_sort)
	XmuXSortPixelMaps (new_dpys[k]);

#ifdef DEBUG
      before = clock ();
#endif
      if (do_put_get) {
	for (col = 0; col < ret_width; col++)
	  for (row = 0; row < ret_height; row++)
	    if (!XPutPixel (image, row, col,
			    XmuXMapPixel (new_dpys[k],
					  new_drawable,
					  XGetPixel (image,
						     row, col)))) {
	      XmuXErrorF ("DupPixmap: XPutPixel failed!\n");
	      return (XmuXFailure);
	    }
      }
#ifdef DEBUG
      after = clock ();
#endif
      XmuXdebug (debug_mux, "Translate %d pixels...(%lf cpu sec)",
		 ret_width * ret_height,
		 (after - before) / 1000000.0);

    }

    XPutImage (new_dpys[k],
	       new_pixmap,
	       put_gc,
	       image,
	       0, 0,
	       0, 0,
	       ret_width,
	       ret_height);
    XFreeGC (new_dpys[k], put_gc);

    XmuXInsertID (new_dpys[k], new_pixmap,
		  pmap_info->pixmap, FromServer);
    XmuXInsertID (new_dpys[k], pmap_info->pixmap,
		  new_pixmap, FromClient);
  }
  if (n > 1)
    Xfree (im_data);
  XDestroyImage (image);
  XmuXdebug (debug_mux, "done\n");
  return (XmuXSuccess);
}

static int
DuplicateFont (new_dpys, n, font_info)
  Display **new_dpys;
  int n;
  MUXFontInfoPtr font_info;
{
  register XFontStruct *font_str;
  register int k;

  XmuXdebug (debug_mux, "Duplicate font %s :\n",
	     font_info->name);

  for (k = 0; k < n; k++) {
    if ((font_str =
	 XLoadQueryFont (new_dpys[k], font_info->name)) == NULL) {
      unsigned char error_request;
      if (XmuXCheckLastError (new_dpys[k], BadFont,
			      &error_request))
	XmuXErrorF ("Duplicate font: Can't load font %s\n",
		    font_info->name);
#ifdef DONT_DIE_ON_FONTS
      XmuXErrorF ("failed rying \"fixed\"... ");
      if ((font_str = XLoadQueryFont (new_dpys[k], "fixed")) ==
	  NULL) {
	if (XmuXCheckLastError (new_dpys[k], BadFont,
				&error_request))
	  XmuXErrorF ("Duplicate font: Can't load font %s\n",
		      font_info->name);
      }
      else {
	XmuXdebug (debug_mux, "-->0x%lx\n", font_str->fid);
	XmuXInsertID (new_dpys[k], font_str->fid,
		      font_info->font, FromServer);
	XmuXInsertID (new_dpys[k], font_info->font,
		      font_str->fid,
		      FromClient);
      }
#else
      XFreeFontInfo ((char **) NULL, font_str, 1);
      return (XmuXFailure);
#endif
    }
    XmuXdebug (debug_mux, "-->0x%lx\n", font_str->fid);
    XmuXInsertID (new_dpys[k], font_str->fid, font_info->font,
		  FromServer);
    XmuXInsertID (new_dpys[k], font_info->font, font_str->fid,
		  FromClient);

    XFreeFontInfo ((char **) NULL, font_str, 1);
  }
  XmuXdebug (debug_mux, "done\n");
  return (XmuXSuccess);
}

static int
DuplicateCursor (new_dpys, n, curs_info)
  Display **new_dpys;
  int n;
  MUXCursorInfoPtr curs_info;
{
  register Cursor new_cursor;
  register int k;

  XmuXdebug (debug_mux, "Duplicate cursor 0x%lx :\n",
	     curs_info->cursor);
  if (curs_info->cursor_type == PIXMAP_CURSOR) {
    register Pixmap src = curs_info->u.pixmap_cursor.source_pmap, mask = curs_info->u.pixmap_cursor.mask_pmap;

    for (k = 0; k < n; k++) {
      new_cursor =
	XCreatePixmapCursor (new_dpys[k],
			     XmuXMapID (new_dpys[k], src,
					FromClient),
			     XmuXMapID (new_dpys[k], mask,
					FromClient),
			     &curs_info->fg,
			     &curs_info->bg,
			     curs_info->u.pixmap_cursor.hot_x,
			     curs_info->u.pixmap_cursor.hot_y);
      XmuXdebug (debug_mux, "-->0x%lx\n", new_cursor);
      XmuXInsertID (new_dpys[k], new_cursor,
		    curs_info->cursor, FromServer);
      XmuXInsertID (new_dpys[k], curs_info->cursor,
		    new_cursor, FromClient);
    }
  }
  else {
    register Font src = curs_info->u.glyph_cursor.source_font,
    mask = curs_info->u.glyph_cursor.mask_font;
    for (k = 0; k < n; k++) {
      new_cursor =
	XCreateGlyphCursor (new_dpys[k],
			    XmuXMapID (new_dpys[k], src,
				       FromClient),
			    XmuXMapID (new_dpys[k], mask,
				       FromClient),
			    curs_info->u.glyph_cursor.source_char,
			    curs_info->u.glyph_cursor.mask_char,
			    &curs_info->fg,
			    &curs_info->bg);

      XmuXdebug (debug_mux, "-->0x%lx\n", new_cursor);
      XmuXInsertID (new_dpys[k], new_cursor,
		    curs_info->cursor, FromServer);
      XmuXInsertID (new_dpys[k], curs_info->cursor,
		    new_cursor, FromClient);
    }
  }
  XmuXdebug (debug_mux, "done\n");
  return (XmuXSuccess);
}

void
DuplicatePassiveGrabs (new_dpy, new_win, win_info)
  Display *new_dpy;
  Window new_win;
  MUXWindowInfoPtr win_info;
{
  register GrabButtonParams *bgrabs;
  register GrabKeyParams *kgrabs;

  XmuXdebug (debug_mux, "Duplicate window 0x%lx's passive grabs :\n");

  /* button grabs */
  for (bgrabs = win_info->button_grabs; bgrabs != NULL;
       bgrabs = bgrabs->next) {
    XmuXdebug (debug_mux, "button %u mod %u\n",
	       bgrabs->button, bgrabs->modifiers);

    XGrabButton (new_dpy,
		 bgrabs->button,
		 bgrabs->modifiers,
		 new_win,
		 bgrabs->owner_events,
		 bgrabs->event_mask,
		 bgrabs->pointer_mode,
		 bgrabs->keyboard_mode,
    /* next two params are ID's --> map them */
		 XmuXMapID (new_dpy, bgrabs->confine_to, FromClient),
		 XmuXMapID (new_dpy, bgrabs->cursor, FromClient)
      );
  }
  /* key grabs */
  for (kgrabs = win_info->key_grabs; kgrabs != NULL;
       kgrabs = kgrabs->next) {
    XmuXdebug (debug_mux, "key %d modifiers %u\n",
	       kgrabs->key, kgrabs->modifiers);
    XGrabKey (new_dpy,
	      kgrabs->key,
	      kgrabs->modifiers,
	      new_win,
	      kgrabs->owner_events,
	      kgrabs->pointer_mode,
	      kgrabs->keyboard_mode
      );
  }
}

static int
UpdateWindowResourceAttributes (dflt_dpy, new_dpys, n, win_info)
  Display *dflt_dpy, **new_dpys;
  int n;
  MUXWindowInfoPtr win_info;
{
  XSetWindowAttributes set_win_attr;
  register Window new_window;
  register int k;
  register Mask flags;

  XmuXdebug (debug_mux, "Update window 0x%lx: ", win_info->window);
  for (k = 0; k < n; k++) {

    /*
     * we need the new window id
     */
    new_window = XmuXMapID (new_dpys[k], win_info->window,
			    FromClient);

    /*
     * look up the flags field to update the attributes that point to
     * other resources
     */
    flags = 0L;
    if (win_info->flags & CWBorderPixel) {
      flags |= CWBorderPixel;
      set_win_attr.border_pixel =
	XmuXMapPixel (new_dpys[k], new_window,
		      win_info->border_pixel);
      XmuXdebug (debug_mux, " border %u",
		 set_win_attr.border_pixel);
    }
    if (win_info->flags & CWBackPixel) {
      flags |= CWBackPixel;
      set_win_attr.background_pixel =
	XmuXMapPixel (new_dpys[k], new_window,
		      win_info->bg_pixel);
      XmuXdebug (debug_mux, " backgr %u ",
		 set_win_attr.background_pixel);
    }
    if (win_info->flags & CWBackPixmap) {
      flags |= CWBackPixmap;
      set_win_attr.background_pixmap =
	XmuXMapID (new_dpys[k], win_info->bg_pmap, FromClient);
      XmuXdebug (debug_mux, " bg pm 0x%lx ",
		 set_win_attr.background_pixmap);
    }
    if (win_info->flags & CWBorderPixmap) {
      flags |= CWBorderPixmap;
      set_win_attr.border_pixmap =
	XmuXMapID (new_dpys[k], win_info->border_pmap,
		   FromClient);
      XmuXdebug (debug_mux, " bd pm 0x%lx ",
		 set_win_attr.border_pixmap);
    }
    if (win_info->flags & CWCursor) {
      flags |= CWCursor;
      set_win_attr.cursor =
	XmuXMapID (new_dpys[k], win_info->cursor, FromClient);
      XmuXdebug (debug_mux, " curs 0x%lx ",
		 set_win_attr.cursor);
    }
    if (win_info->flags & CWColormap) {
      flags |= CWColormap;
      set_win_attr.colormap =
	XmuXMapID (new_dpys[k], win_info->colormap, FromClient);
      XmuXdebug (debug_mux, " cmap 0x%lx ",
		 set_win_attr.colormap);
    }
    XChangeWindowAttributes (new_dpys[k], new_window, flags,
			     &set_win_attr);

    /*
     * Are there passive grabs associated with this window ?
     */
    if (win_info->grabs)
      DuplicatePassiveGrabs (new_dpys[k], new_window, win_info);

    /*
     * If this is not an unmapped window, map it !
     */
    if ((win_info->map_state != IsUnmapped) && !win_info->hidden) {
      XMapWindow (new_dpys[k], new_window);
      XmuXdebug (debug_mux, " mapped done\n");
    }
    else
      XmuXdebug (debug_mux, " not mapped done\n");

    /*
     * Duplicate the properties
     */
  }
  return (DuplicateProperties (dflt_dpy, new_dpys, n,
			       win_info->window));
}

static void
UpdateGCResourceValues (dflt_dpy, new_dpys, n, gc_info)
  Display *dflt_dpy, **new_dpys;
  int n;
  MUXGCInfoPtr gc_info;
{
  register GC gc;
  register int k;

  gc = gc_info->gc;

  /*
   * GC may have to be flushed on the original display, since we use
   * the GC cache on all connections
   */
  FlushGC (dflt_dpy, gc);
  XmuXdebug (debug_mux, "Update gc 0x%lx ", gc->gid);

  /*
   * look up the flags field to update the attributes that point to
   * other resources.  It's a bit tricky here, since we fool the original
   * library by changing the dirty bits in the GC cache.  NOTE : 
   * FlushGC resets the dirty bits, so we must set them for every new 
   * display !
   */
  for (k = 0; k < n; k++) {
    if (gc_info->flags & GCTile) {
      gc->dirty |= GCTile;
      XmuXdebug (debug_mux, "tile pm 0x%lx ", gc->values.tile);
    }
    if (gc_info->flags & GCStipple) {
      gc->dirty |= GCStipple;
      XmuXdebug (debug_mux, "stipple pm 0x%lx ", gc->values.stipple);
    }
    if (gc_info->flags & GCFont) {
      gc->dirty |= GCFont;
      XmuXdebug (debug_mux, "font 0x%lx ", gc->values.font);
    }
    if (gc_info->flags & GCClipMask) {
      gc->dirty |= GCClipMask;
      XmuXdebug (debug_mux, "clip mask 0x%lx ",
		 gc->values.clip_mask);
    }
    FlushGC (new_dpys[k], gc);
  }
  XmuXdebug (debug_mux, "done\n");
}

void
RestackChildren (dflt_dpy, new_dpys, n, window)
  Display *dflt_dpy, **new_dpys;
  int n;
  Window window;
{
  Window root, parent, *children, *mapped_children;
  int num_children;

  if (!XQueryTree (dflt_dpy, window, &root, &parent, &children,
		   &num_children))
    /* do nothing */
    return;
  else if (num_children > 1) {
    register int i, j, k;

    mapped_children = (Window *) Xmalloc (num_children *
					  sizeof (Window));
    if (!mapped_children)
      return;
    for (k = 0; k < n; k++) {

      /*
       * must reorder the children array
       */
      for (i = 0, j = num_children - 1;
	   i < num_children; i++, j--)
	mapped_children[i] = XmuXMapID (new_dpys[k],
					children[j],
					FromClient);
      XRestackWindows (new_dpys[k], mapped_children,
		       num_children);
    }
    Xfree ((char *) mapped_children);

    /*
     * recursive call
     */
    for (i = 0; i < num_children; i++)
      RestackChildren (dflt_dpy, new_dpys, n,
		       children[i]);

    Xfree ((char *) children);
  }
}

void
RestackWindows (dflt_dpy, new_dpys, n)
  Display *dflt_dpy, **new_dpys;
  int n;
{
  register Window window;
  register int i;

  for (i = 0; (window = XmuXToplevel (dflt_dpy, i)) != None; i++)
    RestackChildren (dflt_dpy, new_dpys, n, window);

}

int
XmuXReplicate (dpy, dpy_names, n, fds, bell)
  Display *dpy;
  char **dpy_names;
  int n, **fds;			/* RETURN */
  int bell;
{
  register Display **new_dpys;
  register MUXServerPtr dflt_server, new_server;
  register MUXResourceInfoPtr res_info;
  register int k, status;

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


  XmuXdebug (debug_mux, "Start duplicating...\n");
#ifdef GRABSERVER
  /* avoid complications ?? */
  XGrabServer (dpy);
#endif

  /*
   * allocate space for the new display pointers and the returned array
   */
  new_dpys = (Display **) Xmalloc (n * sizeof (Display *));
  *fds = (int *) Xmalloc (n * sizeof (int));
  if (new_dpys == NULL || fds == NULL) {
    XmuXErrorF ("Multiplex: Out of mem (new dpys)!\n");
    return (XmuXFailure);
  }

  /*
   * must flush the primary display here; otherwise output queue may be 
   * messed up
   */
  XFlush (dpy);
  if (bell)
    XBell (dpy, 50);

  /*
   * clean up  the resource list (avoid duplication of removed resources)
   */
  XmuXCleanupResources (dpy);
  dflt_server = ConnectionTranslation[ConnectionNumber (dpy)];

  /*
   * turn multiplex mode off
   */
  dflt_server->mustMux = False;
  for (k = 0; k < n; k++) {

    /*
     * try to establish a connection to the new server(s) return on
     * failure
     */

    if ((new_dpys[k] = XOpenDisplay (dpy_names[k])) == NULL) {
      register int l;
      XmuXdebug (debug_mux, "Can't open display %s!\n",
		 dpy_names[k] ? dpy_names[k] : "0:0");
      for (l = 0; l < k; l++)
	XCloseDisplay (new_dpys[l]);
      Xfree ((char *) new_dpys);
      return (XmuXFailure);
    }

    /*
     * XOpenDisplay has created an entry in the ConnectionTranslation 
     * table filled with "default" values.  Override these values here.
     * Remove the "application context"
     */
    XmuXFreeApplCtx (new_dpys[k]);
    new_server =
      ConnectionTranslation[ConnectionNumber (new_dpys[k])];
    new_server->prim_server = ConnectionNumber (dpy);
    new_server->mustMux = False;
    XmuXSetApplCtx (new_dpys[k], dpy);

    /*
     * don't store "normal" resource info for multiplex'ed connections 
     * in the future (exception: GCPixels)
     */
    new_server->storeResources = False;

    CompareDisplays (dpy, new_dpys[k]);

    /*
     * Map the default ID's ( root window , default visual ...) AND
     * XmuX-specific atoms !!
     */
    MapDefaultIDs (dflt_server, new_server);
  }

  /*****************************************************************
   * Now try to "multiplex" the application by allocating the client
   * resources on the new connection. TWO PROBLEMS : Alas, we can't
   * just use the "creation history" (reflected in the sorting of the
   * resource list) since resources may be referenced in other resources
   * ( e.g. Windows may use cursors and pixmaps and GCs may use fonts )
   * that have been created earlier. Second, resources that are referenced
   * by others may have been gone (e.g. an application may destroy a
   * window that has been used to  create a GC) We ship around these
   * problems by:
   *
   * 1. allocate all windows in a first scan (to find a matching drawable
   *    if the referenced has gone).
   *
   * 2. using the default values on creation.
   *
   * 3. when we have allocated all resources we do a third scan over the
   *    resource list and update these values.
   *****************************************************************/

  status = XmuXSuccess;

  /* first scan : duplicate windows (and implicitly colormaps) */
  for (res_info = MUXClientResources[ConnectionNumber (dpy)].head;
       res_info != NullResourceInfoPtr && (status == XmuXSuccess);
       res_info = res_info->next)
    if (res_info->type & RT_WINDOW)
      status = DuplicateWindow (dpy, new_dpys, n,
				res_info->u.win_info);

  if (status != XmuXSuccess) {
    XmuXErrorF ("Oops! Something went wrong during duplication!\n");
    for (k = 0; k < n; k++)
      XCloseDisplay (new_dpys[k]);
#ifdef GRABSERVER
    XUngrabServer (dpy);
#endif

    Xfree ((char *) new_dpys);
    return (status);
  }

  /* second scan : duplicate all other resources */
  for (res_info = MUXClientResources[ConnectionNumber (dpy)].head;
       res_info != NullResourceInfoPtr && (status == XmuXSuccess);
       res_info = res_info->next)
    switch (res_info->type) {
    case RT_WINDOW:
      /* have been allocated during first scan */
      break;

    case RT_GC:
      status = DuplicateGC (dpy, new_dpys, n,
			    res_info->u.gc_info);
      break;

    case RT_PIXMAP:
      status = DuplicatePixmap (dpy, new_dpys, n,
				res_info->u.pmap_info);
      break;

    case RT_CURSOR:
      status = DuplicateCursor (new_dpys, n,
				res_info->u.curs_info);
      break;

    case RT_FONT:
      status = DuplicateFont (new_dpys, n,
			      res_info->u.font_info);
      break;

    case RT_COLORMAP:

      /*
       * this call should have no effect, since colormaps are 
       * implicitly duplicated in calls of DuplicateWindow.  BUT: We
       * duplicate the default colormap, no matter if it is used. Are
       * there any applications that never use the default? Of course!
       * Applications that only use their private one.
       */
      status = DuplicateColormap (dpy, new_dpys, n,
				  res_info->id, NULL);
      break;
    }

  if (status != XmuXSuccess) {
    XmuXErrorF ("Oops! Something went wrong during duplication!\n");
    for (k = 0; k < n; k++)
      XCloseDisplay (new_dpys[k]);

#ifdef GRABSERVER
    XUngrabServer (dpy);
#endif
    Xfree ((char *) new_dpys);

    return (status);
  }

  /* third scan to update Windows and GCs */

  for (res_info = MUXClientResources[ConnectionNumber (dpy)].head;
       res_info != NullResourceInfoPtr && (status == XmuXSuccess);
       res_info = res_info->next)
    switch (res_info->type) {
    case RT_WINDOW:
      status = UpdateWindowResourceAttributes (dpy, new_dpys, n,
					       res_info->u.win_info);
      break;

    case RT_GC:
      UpdateGCResourceValues (dpy, new_dpys, n,
			      res_info->u.gc_info);
      break;

    default:
      break;
    }

  if (status != XmuXSuccess) {
    XmuXErrorF ("Oops! Something went wrong during duplication!\n");
    for (k = 0; k < n; k++)
      XCloseDisplay (new_dpys[k]);

#ifdef GRABSERVER
    XUngrabServer (dpy);
#endif
    Xfree ((char *) new_dpys);

    return (status);
  }

  /*
   * restack the windows
   */
  RestackWindows (dpy, new_dpys, n);

  for (k = 0; k < n; k++) {
    XFlush (new_dpys[k]);

    /*
     * We're ready: store the new connection in client's other server
     * list and mark this connection as "must be multiplexed"
     */
    BITSET (dflt_server->other_servers,
	    ConnectionNumber (new_dpys[k]));
    fds[0][k] = ConnectionNumber (new_dpys[k]);
  }
  dflt_server->mustMux = True;

#ifdef GRABSERVER
  XUngrabServer (dpy);
#endif

  XmuXdebug (debug_mux, "done\n");
  Xfree ((char *) new_dpys);
  if (bell)
    XBell (dpy, 50);
  return (XmuXSuccess);
}

int
XmuXHandleProperty (dpy, event)
  Display *dpy;
  XEvent *event;
{
  register char **dpy_list;
  int n;
  char *data = NULL;
  int ret_type, ret_length, ret_format, bytes_after;


  if (event->xproperty.state == PropertyDelete)
    /* don't react on deletion (normally, WE delete it !) */
    return;

  /*
   * Get window property XMUX_CPORT (stores the display name(s)) and
   * delete it.  NOTE: Display pointer in event structure has not been
   * altered
   */
  XGetWindowProperty (event->xany.display,
		      XmuXMapID (event->xany.display,
				 event->xproperty.window,
				 FromClient),
		      XmuXServerAtom (event->xany.display, _MUX_CPORT),
		      0, DEFAULT_LENGTH, True,
		      XA_STRING,
		      &ret_type, &ret_format, &ret_length,
		      &bytes_after, &data);
  if (ret_length == 0)
    return;

  /*
   * first byte says what to do:
   *
   * MuxAdd (+) says: add display(s) to the list (multiplex/broadcast)
   *
   * MuxRemove (-) says: remove display(s) from the list (close connection(s))
   */

  dpy_list = CreateDpyNameList (data + 1, &n);

  if (n != 0) {
    switch (data[0]) {
    case MuxAdd:
      {
	int *fds;

	if (XmuXReplicate (dpy, dpy_list, n, &fds, xTrue) == XmuXSuccess)
	  XmuXAddHosts (dpy, dpy_list, fds, n);
	Xfree ((char *) fds);

      }
      break;
    case MuxRemove:
      XmuXRemoveHosts (dpy, dpy_list, n);
      break;
    default:
      break;
    }
  }
  FreeDpyNames (dpy_list, n);
}

int
XmuXHandleMessage (dpy, event)
  Display *dpy;
  XEvent *event;
{
  if (event->xclient.data.l[XMUXType] != XMUXAction)
    /* nothing to do ( until now ) */
    return;

  switch (event->xclient.data.l[XMUXSubtype]) {
  case XMUXPassChalk:

    /*
     * NOTE: This is a demo version: Pass Chalk has changed to Get
     * Chalk to allow a broadcast simulation Display pointer in event
     * structure has not been altered.  It's used here to determine the new
     * chalk holder
     */
    XmuXSetChalkHolder (dpy, event->xclient.data.l[XMUXDetail]);
    break;

  case XMUXDebug:

    switch (event->xclient.data.l[XMUXDetail]) {
    case DbgAll:
      debug_events = debug_events ? False : True;
      debug_requests = debug_requests ? False : True;
      debug_resources = debug_resources ? False : True;
      debug_mux = debug_mux ? False : True;
      break;
    case DbgEvents:
      debug_events = debug_events ? False : True;
      break;
    case DbgRequests:
      debug_requests = debug_requests ? False : True;
      break;
    case DbgResources:
      debug_resources = debug_resources ? False : True;
      break;
    case DbgMux:
      debug_mux = debug_mux ? False : True;
      break;
    }
    break;
  }
  return (XmuXSuccess);
}

Bool
XmuXMustDoSomething (dpy)
  Display *dpy;
{

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

  return (XmuXQLength (dpy) > 0);
}

void
XmuXDoSomething (dpy)
  Display *dpy;
{
  _XQEvent *qevent;
  register Display *dflt_dpy;

  /* switch to the default display */
  dflt_dpy = XmuXPrimaryDisplayFromDisplay (dpy);

  while ((qevent = XmuXDeq (dflt_dpy)) != NULL) {
    switch (qevent->event.type) {
    case PropertyNotify:
      if (XmuXIsToplevel (dflt_dpy, qevent->event.xproperty.window))
	if (XmuXIsAtom (dflt_dpy,
			qevent->event.xproperty.atom,
			_MUX_CPORT))
	  (void) XmuXHandleProperty (dflt_dpy,
				     &(qevent->event));
      break;
    case ClientMessage:
      if (XmuXIsToplevel (dflt_dpy, qevent->event.xclient.window))
	if (XmuXIsAtom (dflt_dpy,
			qevent->event.xclient.message_type,
			_MUX_MESSAGE))
	  (void) XmuXHandleMessage (dflt_dpy,
				    &(qevent->event));
      break;
    }
    XmuXFreeQEvent (qevent);
  }
}

XmuXFlushGC (dpy, gid, window)
  Display *dpy;
  XID gid;			/* NOTE : ID's are NOT mapped !!! */
  Window window;
{
  register MUXGCPixelsInfoPtr gcpix_info;
  MUXGCPixelsInfoPtr GetGCPixelsInfo ();
  register int mapped = False;

  /*
   * Here's the point where we weaken our WYSIWIS (What you see is 
   * what I see) to HWYSIWIS (Hope WYSIWIS).  XmuXFlushGC is called
   * before multiplexing graphic requests in order to map GC's pixel
   * values using the colormap that is referenced by the "window". In
   * fact, nearly all graphic requests can operate on Drawables; so what
   * shall we do if the "window" is a pixmap?  We do it this way:
   * 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;

    /*
     * XmuXFlushGC 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);
    /* window ID is not MAPPED! */
    window = XmuXGetPixmapDrawable (dflt_dpy, window);
  }

  gcpix_info = GetGCPixelsInfo (dpy,
				XmuXMapID (dpy, gid, FromClient));

  if (gcpix_info == NullGCPixelsInfoPtr)
    XmuXFatalError ("XmuXFlushGC: GC 0x%lx not found (conn %d)!\n",
		    gid, ConnectionNumber (dpy));

  if (gcpix_info->flags &
      (GCFunction | GCForeground | GCBackground | GCPlaneMask)) {
    register xChangeGCReq *req;
    unsigned long fg, bg, mask;
    unsigned long values[3], *value;
    int nbytes;

    XmuXdebug (debug_requests, "XmuXFlushGC");
    GetReq (ChangeGC, req);
    req->gc = gcpix_info->gid;
    XmuXdebug (debug_requests, " 0x%lx", req->gc);
    req->mask = 0L;
    nbytes = 0;
    value = &values[0];
    window = XmuXMapID (dpy, window, FromClient);

    if (gcpix_info->flags & (GCFunction | GCPlaneMask)) {
      XmuXMapGCValues (dpy, window,
		       gcpix_info->fg, gcpix_info->bg,
		       gcpix_info->plane_mask,
		       &fg, &bg, &mask,
		       gcpix_info->function);
      gcpix_info->flags &= ~GCFunction;
      if (gcpix_info->flags & GCPlaneMask) {
	*value = mask;
	XmuXdebug (debug_requests, " plane_mask %u", *value);
	req->mask |= GCPlaneMask;
	req->length++;
	nbytes += 4;
	value++;
	gcpix_info->flags &= ~GCPlaneMask;
      }
      mapped = True;
    }
    if (gcpix_info->flags & GCForeground) {
      if (!mapped)
	*value = XmuXMapPixel (dpy, window, gcpix_info->fg);
      else
	*value = fg;
      XmuXdebug (debug_requests, " fg %u", *value);
      value++;
      req->mask |= GCForeground;
      req->length++;
      nbytes += 4;
      gcpix_info->flags &= ~GCForeground;
    }
    if (gcpix_info->flags & GCBackground) {
      if (!mapped)
	*value = XmuXMapPixel (dpy, window, gcpix_info->bg);
      else
	*value = bg;
      XmuXdebug (debug_requests, " bg %u", *value);
      value++;
      req->mask |= GCBackground;
      req->length++;
      nbytes += 4L;
      gcpix_info->flags &= ~GCBackground;
    }
    Data32 (dpy, (long *) &values[0], nbytes);
    XmuXdebug (debug_requests, " done ");
  }
}
