!<arch>
ApplCtx.c       679050123   301   0     100644  22080     `
/* $Id: ApplCtx.c,v 1.7 1991/06/26 07:02:00 altenhof Exp $ */
 
#define NEED_RESOURCES
#define NEED_EVENTS
 
#include "Xlibint.h"
#include "XmuXlibint.h"
#include "XmuX.h"
#include "Xatom.h"
 
#define MAX_IDX MAXSOCKS
 
#define TOPLEVELID( idx , i ) \
    ( _muxApplCtx[ idx ]->toplevels[ i ].window )
 
#define TOPLEVELMASK( idx , i ) \
    ( _muxApplCtx[ idx ]->toplevels[ i ].mux_mask )
 
#ifndef XmuXMask
#define XmuXMask ( PropertyChangeMask )
#endif
 
/*
 * The next two data structures describe what we call the "application
 * context". In the XmuXlib environment, application means one client
 * connection to the server (i.e. an "X application" may define more than one
 * "XmuX application"). What we duplicate (or multiplex) are such XmuX
 * applications. The "appplication context" describes some properties that
 * are "shared" among the associated (client and mux'ed) server connections:
 * Whether we are in ChalkpassMode or AnarchyMode, which connection has the
 * chalk... In the older version this information was stored in the client's
 * server info. These structures are private to this file !
 */
 
/*
 * This data structure is used to store the names of the associated hosts
 * Will be used to pass the chalk and to keep the MUX_HOSTS property uptodate
 */
 
typedef struct _MUXHostInfo {
 
  int fd;			/* the socket number for this host */
  char *hostname;		/* the host name */
  struct _MUXHostInfo *next;
 
} MUXHostInfo;
 
/*****************************************************************
 * The "application context" :
 *   Contents :
 *     - communication mode ( Anarchy / Chalkpassing )
 *     - chalk holder connection ( if we're in ChalkPassMode )
 *     - the client fd (socket) this context belongs to
 *     - queue length of the "must do" event queue
 *     - pointers to the head and the tail of the "must do" event queue
 *     - the number and
 *     - the array of top-level window infos
 *     - pointer to the host list
 *
 * The "must do" event queue holds those events that trigger some
 * XmuX-specific work,e.g. we "must multiplex" this application or we "must
 * pass the chalk". Top-level infos store the XmuX-specific event masks for
 * top-level windows: we "add" PropertyChangeMask to all top-level windows to
 * solicit notification about changes of the XMUX_MUX property.
 *****************************************************************/
 
typedef struct _MUXApplCtx {
 
  int commMode, ChalkHolder, client_fd, qlen;
  _XQEvent *head, *tail;
  int ntoplevels;
  MUXToplevelInfo *toplevels;
  MUXHostInfo *hosts;
 
} MUXApplCtx, *MUXApplCtxPtr;
 
/* private array storing pointers to application contexts */
static MUXApplCtxPtr *_muxApplCtx;
 
/* what's the max index of the _muxApplCtx array */
static int _lastApplIdx;
 
/* pointer to unused queue events */
static _XQEvent *_XmuXqfree;
 
/*
 * next two routines handle "applications indices",i.e. indices in the
 * application context array.
 */
 
static void
SetApplicationIndex (dpy, idx)
  Display *dpy;
  int idx;
{
 
#ifdef DEBUG
  CheckDisplay (dpy, "SetApplicationIndex");
#endif
 
  if (idx > _lastApplIdx)
    XmuXErrorF ("SetApplCtx: invalid index (%d > %d)!\n",
		idx, _lastApplIdx);
  else
    ConnectionTranslation[dpy->fd]->index = idx;
}
 
static int
GetApplicationIndex (dpy)
  Display *dpy;
{
  register int appl_idx;
 
#ifdef DEBUG
  CheckDisplay (dpy, "GetApplicationIndex");
#endif
 
  appl_idx = ConnectionTranslation[dpy->fd]->index;
 
#ifdef DEBUG
  if (appl_idx < 0 || appl_idx > _lastApplIdx)
    XmuXFatalError ("GetApplIdx: index out of range!\n");
#endif
 
  return (appl_idx);
}
 
/* application context initialisation routine */
void
XmuXInitApplCtx ()
{
  register int i;
 
  _XmuXqfree = NULL;
  _lastApplIdx = MAX_IDX;
  _muxApplCtx =
    (MUXApplCtxPtr *) Xmalloc (_lastApplIdx * sizeof (MUXApplCtxPtr));
  if (_muxApplCtx == (MUXApplCtxPtr *) NULL)
    XmuXFatalError ("InitApplCtx: Can't allocate!\n");
 
  for (i = 0; i < _lastApplIdx; i++)
    _muxApplCtx[i] = (MUXApplCtxPtr) NULL;
}
 
/*
 * XmuXCreateApplCtx - creates a new application context and fills it with
 *                     default values
 */
 
void
XmuXCreateApplCtx (server)
  MUXServerPtr server;
{
  register int curr_idx;
 
  /* find a free slot */
  curr_idx = 0;
  while (_muxApplCtx[curr_idx] != (MUXApplCtxPtr) NULL) {
    if (++curr_idx > _lastApplIdx) {
      /* out of bounds; realloc array */
      _muxApplCtx = (MUXApplCtxPtr *)
	Xrealloc ((char *) _muxApplCtx,
		  curr_idx * sizeof (MUXApplCtxPtr));
 
      if (_muxApplCtx == (MUXApplCtxPtr *) NULL)
	XmuXFatalError ("CreateApplCtx: Can't realloc!\n");
 
      _lastApplIdx = curr_idx;
      break;
    }
  }
 
  /* now, curr_idx points to a free slot */
  _muxApplCtx[curr_idx] =
    (MUXApplCtxPtr) Xmalloc (sizeof (MUXApplCtx));
  /* malloc failed ? */
  if (_muxApplCtx[curr_idx] == (MUXApplCtxPtr) NULL)
    XmuXFatalError ("CreateApplCtx: Can't alloc!\n");
 
  /* establish links it to the appropriate server info */
  server->index = curr_idx;
  _muxApplCtx[curr_idx]->ChalkHolder =
    _muxApplCtx[curr_idx]->client_fd = server->xdpy->fd;
 
  _muxApplCtx[curr_idx]->commMode = ChalkMode;
  _muxApplCtx[curr_idx]->qlen = 0;
  _muxApplCtx[curr_idx]->head =
    _muxApplCtx[curr_idx]->tail = NULL;
  _muxApplCtx[curr_idx]->ntoplevels = 0;
  _muxApplCtx[curr_idx]->toplevels = NULL;
  _muxApplCtx[curr_idx]->hosts = NULL;
 
}
 
/*
 * XmuXFreeApplCtx -- free the application context dpy points to
 * We create application ctx for mux'ed connections too (although we don't
 * need them), because we don't want two versions of XOpenDisplay, and
 * immediately free it by calling this routine
 */
 
void
XmuXFreeApplCtx (dpy)
  Display *dpy;
{
  register int appl_idx, i;
  void XmuXFreeQ (), XmuXFreeHosts ();
  register MUXApplCtxPtr appl_ctx;
 
  appl_idx = GetApplicationIndex (dpy);
  appl_ctx = _muxApplCtx[appl_idx];
 
  SetApplicationIndex (dpy, -1);
  /* free everything */
  XmuXFreeHosts (appl_ctx->hosts);
  XmuXFreeQ (appl_ctx->head);
  if (appl_ctx->ntoplevels > 0)
    Xfree ((char *) appl_ctx->toplevels);
 
  _muxApplCtx[appl_idx] = (MUXApplCtxPtr) NULL;
 
}
 
/*
 * XmuXSetApplCtx -- link dpy1 to dpy2's application context
 */
 
void
XmuXSetApplCtx (dpy1, dpy2)
  Display *dpy1, *dpy2;
{
  SetApplicationIndex (dpy1, GetApplicationIndex (dpy2));
}
 
/* routines dealing with communication mode / chalk passing */
 
/*
 * XmuXCommunicationMode -- Anarchy or ChalkPass ?
 */
int
XmuXCommunicationMode (dpy)
  Display *dpy;
{
 
  return (_muxApplCtx[GetApplicationIndex (dpy)]->commMode);
}
 
/*
 * XmuXChalkHolder -- which connection has the chalk?  If we're in
 * anarchy mode, we'll return the calling dpy's fd
 */
 
int
XmuXChalkHolder (dpy)
  Display *dpy;
{
  register int appl_idx;
 
  appl_idx = GetApplicationIndex (dpy);
 
  if (_muxApplCtx[appl_idx]->commMode == AnarchyMode)
    /* calling dpy is "chalk holder" */
    return (dpy->fd);
  else
    return (_muxApplCtx[appl_idx]->ChalkHolder);
}
 
/*
 * XmuXSetChalkHolder -- register calling dpy as the new chalk holder
 * Returns the connection number of the new chalk holder, or -1 if we
 * are in anarchy mode
 */
 
int
XmuXSetChalkHolder (dpy, index)
  Display *dpy;
  unsigned int index;
{
  register int i, idx;
  register MUXHostInfo *host;
 
#ifdef DEBUG
  CheckDisplay (dpy, "ApplSetChalkHolder");
#endif
 
  idx = GetApplicationIndex (dpy);
  if (_muxApplCtx[idx]->commMode == AnarchyMode)
    return (-1);
  else {
    for (i = 0, host = _muxApplCtx[idx]->hosts;
	 i < index && host != NULL;
	 i++, host = host->next)
      /* just advance */
      ;
    if (host != NULL) {
      _muxApplCtx[idx]->ChalkHolder = host->fd;
      /* change the toplevel property */
      if (!IsClientDisplay (dpy))
	dpy = XmuXPrimaryDisplayFromDisplay (dpy);
      for (i = 0; i < _muxApplCtx[idx]->ntoplevels; i++)
	XChangeProperty (dpy,
			 TOPLEVELID (idx, i),
			 XmuXServerAtom (dpy, _MUX_CHALKHOLDER),
			 XA_STRING, 8, PropModeReplace,
			 host->hostname,
			 strlen (host->hostname) + 1
	  );
      return (host->fd);
    }
    else
      return (-1);
  }
}
 
/* routines handling the "must do" event queue */
 
/*
 * XmuXQLength
 */
 
int
XmuXQLength (dpy)
  Display *dpy;
{
  return (_muxApplCtx[GetApplicationIndex (dpy)]->qlen);
}
 
/*
 * XmuXEnq -- enqueue an event in application's "must do" event queue
 * Really enqueues a duplicate of the event.
 */
 
void
XmuXEnq (dpy, event)
  Display *dpy;
  XEvent *event;
{
  register _XQEvent *qevent;
  register int idx;
 
  idx = GetApplicationIndex (dpy, False);
 
  /* code is stolen from _XEnq */
 
  /* NOSTRICT */
  if (qevent = _XmuXqfree) {
    /* if _XmuXqfree is non-NULL do this, else malloc a new one. */
    _XmuXqfree = qevent->next;
  }
  else if ((qevent =
	    (_XQEvent *) Xmalloc ((unsigned) sizeof (_XQEvent))) == NULL) {
    /* Malloc call failed! */
    errno = ENOMEM;
    (*_XIOErrorFunction) (dpy);
  }
  qevent->next = NULL;
 
  /* copy event values */
  bcopy ((char *) event, (char *) &qevent->event, sizeof (XEvent));
 
  /* enque it */
  if (_muxApplCtx[idx]->tail)
    _muxApplCtx[idx]->tail->next = qevent;
  else
    _muxApplCtx[idx]->head = qevent;
 
  _muxApplCtx[idx]->tail = qevent;
  _muxApplCtx[idx]->qlen++;
}
 
/*
 * XmuXDeq - reverse of XmuXEnq
 */
 
_XQEvent *
XmuXDeq (dpy)
  Display *dpy;
{
  register _XQEvent *qevent;
  register int idx;
 
  idx = GetApplicationIndex (dpy);
 
  if (_muxApplCtx[idx]->head == NULL)
    return (NULL);
  else {
    qevent = _muxApplCtx[idx]->head;
    _muxApplCtx[idx]->head =
      _muxApplCtx[idx]->head->next;
    _muxApplCtx[idx]->qlen--;
    if (_muxApplCtx[idx]->head == NULL)
      _muxApplCtx[idx]->tail = NULL;
    qevent->next = NULL;
    return (qevent);
  }
}
 
/*
 * XmuXFreeQEvent -- free an event by appending it to the list of
 * unused events.
 */
 
void
XmuXFreeQEvent (qevent)
  _XQEvent *qevent;
{
  qevent->next = _XmuXqfree;
  _XmuXqfree = qevent;
}
 
 
/*
 * XmuXFreeQ  - free application's queue of events
 */
 
void
XmuXFreeQ (queue)
  _XQEvent *queue;
{
  register _XQEvent *qelt = queue;
 
  while (qelt) {
    register _XQEvent *qnext = qelt->next;
    XmuXFreeQEvent (qelt);
    qelt = qnext;
  }
  queue = NULL;
  return;
}
 
/* routines dealing with top-level infos */
 
/*
 * ToplevelMask -- find and return window's mux_mask
 */
 
static Mask
ToplevelMask (dpy, window)
  Display *dpy;
  Window window;
{
  register int i, appl_idx;
  appl_idx = GetApplicationIndex (dpy);
 
  for (i = 0;
       i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    if (TOPLEVELID (appl_idx, i) == window)
      return (TOPLEVELMASK (appl_idx, i));
 
  return (0L);
}
 
/*
 * XmuXIsToplevel -- Is window an top-level window?
 */
 
Bool
XmuXIsToplevel (dpy, window)
  Display *dpy;
  Window window;
{
  register int i, appl_idx;
 
  appl_idx = GetApplicationIndex (dpy);
 
  /* if called with the wrong display */
  if (!IsClientDisplay (dpy))
    window = XmuXMapID (dpy, window, FromServer);
 
  for (i = 0;
       i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    if (TOPLEVELID (appl_idx, i) == window)
      break;
 
  return (i < _muxApplCtx[appl_idx]->ntoplevels);
}
 
/*
 * XmuXAddToplevelInfo -- Insert a new info in application's top-level
 * array
 */
 
void
XmuXAddToplevelInfo (dpy, window, mux_mask)
  Display *dpy;
  Window window;
  long mux_mask;
{
  register MUXToplevelInfo *toplevels;
  register int i, appl_idx, ntops;
  void XmuXAddHosts ();
 
  appl_idx = GetApplicationIndex (dpy);
 
  toplevels = _muxApplCtx[appl_idx]->toplevels;
  ntops = _muxApplCtx[appl_idx]->ntoplevels + 1;
 
  if (!toplevels)
    /* first time */
    toplevels =
      (MUXToplevelInfo *) Xmalloc (ntops * sizeof (MUXToplevelInfo));
  else
    toplevels = (MUXToplevelInfo *)
      Xrealloc ((char *) toplevels,
		ntops * sizeof (MUXToplevelInfo));
 
  if (!toplevels)
    XmuXFatalError ("XmuXAddToplevel: Out of memory (appl %d)!\n",
		    appl_idx);
 
  toplevels[ntops - 1].window = window;
  toplevels[ntops - 1].mux_mask = mux_mask;
 
  XmuXdebug (debug_resources, "Top level 0x%lx mask=%u added\n",
	     window, mux_mask);
  _muxApplCtx[appl_idx]->toplevels = toplevels;
  _muxApplCtx[appl_idx]->ntoplevels = ntops;
 
  /* force installation of XmuX-specific properties */
  XmuXAddHosts (dpy, (char **) NULL, (int *) NULL, 0);
  XmuXSetChalkHolder (dpy, XmuXChalkHolder (dpy));
}
 
void
XmuXRemoveToplevelInfo (dpy, window)
  Display *dpy;
  Window window;
{
  register MUXToplevelInfo *toplevels;
  register int i, j, appl_idx, ntops;
 
  appl_idx = GetApplicationIndex (dpy);
 
  toplevels = _muxApplCtx[appl_idx]->toplevels;
  ntops = _muxApplCtx[appl_idx]->ntoplevels;
 
  /* find the window and mark it */
  for (i = 0; i < ntops; i++) {
    if (toplevels[i].window == window)
      toplevels[i].window = None;
  }
 
  /*
   * array will shrink --> copy in place and realloc array
   */
  for (j = 0, i = 0; i < ntops; i++)
    if (toplevels[i].window != None)
      toplevels[j++].window = toplevels[i].window;
    else
      ntops--;
  toplevels = (MUXToplevelInfo *)
    Xrealloc ((char *) toplevels,
	      ntops * sizeof (MUXToplevelInfo));
 
  _muxApplCtx[appl_idx]->toplevels = toplevels;
  _muxApplCtx[appl_idx]->ntoplevels = ntops;
}
 
/*
 * XmuXChangeToplevelInfo -- change mask of window's top-level info
 */
 
void
XmuXChangeToplevelInfo (dpy, window, mux_mask)
  Display *dpy;
  Window window;
  long mux_mask;
{
  register int i, appl_idx;
 
  appl_idx = GetApplicationIndex (dpy);
 
  for (i = 0;
       i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    /* find the window */
    if (TOPLEVELID (appl_idx, i) == window)
      break;
 
  /* error check */
  if (i == _muxApplCtx[appl_idx]->ntoplevels)
    XmuXFatalError ("XmuXChToplev: Window 0x%lx not found (# %d)!\n",
		    window, appl_idx);
  _muxApplCtx[appl_idx]->toplevels[i].mux_mask = mux_mask;
}
 
/*
 * XmuXSpecificEvent -- Used to filter XmuX-specific events.  In fact,
 * returns whether window's mux_mask contains one of the masks in XmuXMask
 * (== PropertyChangeMask). So, it should only be called in the
 * appropriate cases in _XWireToEvent (it's "context free": you do not
 * pass an event mask)!
 */
Bool
XmuXSpecificEvent (dpy, window)
  Display *dpy;
  Window window;
{
  /* just to be sure */
  if (!IsClientDisplay (dpy)) {
    window = XmuXMapID (dpy, window, FromServer);
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
  }
  return (ToplevelMask (dpy, window) & XmuXMask);
}
void
XmuXUpdateStatus (dpy, window)
 
Display *dpy;
Window window;
 
{ unsigned char *data = NULL;
  int ret_type, ret_format, ret_length, 
      bytes_after, i, idx;
 
  idx = GetApplicationIndex (dpy);
  XGetWindowProperty (dpy, 
		      XmuXMapID(dpy, window, FromClient), 
		      XmuXServerAtom (dpy, _MUX_STATUS),
		      0,	/* offset */
		      8192L,	/* data length */
		      False,	/* delete ? */
		      XA_STRING, /* prop type */
		      &ret_type,	/* returned prop type */
		      &ret_format,	/* returned prop format */
		      &ret_length,	/* returned prop length (items) */
		      &bytes_after,	/* remaining bytes */
		      &data	/* returned data */
		      );
  if (!IsClientDisplay (dpy))
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
  for (i = 0; i < _muxApplCtx[idx]->ntoplevels; i++)
    XChangeProperty (dpy,
		     TOPLEVELID (idx, i),
		     XmuXServerAtom (dpy, _MUX_STATUS),
		     XA_STRING, 8, PropModeReplace,
		     data,
		     ret_length
		     );
}
 
/* routines dealing with the host list */
 
/*
 * XmuXAddHosts -- add new host names to the list                 AND
 * change the corresponding property
 */
 
void
XmuXAddHosts (dpy, hostnames, fds, n)
  Display *dpy;
  char **hostnames;
  int *fds, n;
{
  register int i, num_hosts, prop_length, appl_idx;
  register char *prop_data, *bufptr;
  register MUXHostInfo *new_host;
 
  appl_idx = GetApplicationIndex (dpy);
  prop_length = 0;
  num_hosts = n;
  XmuXIncServers (n);
 
  /*
   * we'll change the XMUX_HOSTS property here which contains the names
   * of all displays (!) separated by semicolons don't rely on old value,
   * construct the new from application's host list
   */
  for (new_host = _muxApplCtx[appl_idx]->hosts;
       new_host != NULL; new_host = new_host->next) {
    /* watch out for the separating semicolons ! */
    prop_length += strlen (new_host->hostname) + 1;
    num_hosts++;
  }
  for (i = 0; i < n; i++) {
    int str_length;
 
    new_host = (MUXHostInfo *) Xmalloc (sizeof (MUXHostInfo));
 
    if (new_host == NULL) {
      XmuXFatalError ("XmuXAddHosts: Out of mem (new host)!\n");
    }
 
   /*****************************************************************
    if ((bufptr = SearchString (hostnames[i], '\0')) == NULL) {
      XmuXErrorF ("XmuXAddHosts: non null-terminated string!\n");
      str_length = 6;
    }
    else {
      str_length = bufptr - hostnames[i];
    }
    *****************************************************************/
 
    str_length = strlen (hostnames[i]);
 
    prop_length += str_length + 1;
    new_host->hostname = Xmalloc (str_length + 1);
 
    if (new_host->hostname == NULL) {
      XmuXFatalError ("XmuXAddHosts: Out of mem (host name)!\n");
    }
 
    strncpy (new_host->hostname, hostnames[i], str_length);
    new_host->hostname[str_length] = '\0';
 
    new_host->fd = fds[i];
 
    new_host->next = _muxApplCtx[appl_idx]->hosts;
    _muxApplCtx[appl_idx]->hosts = new_host;
  }
 
  /*
   * Let's do a little arithmetic here:
   *
   * prop_length = sum (strlen(host names)) + number of hosts;
   *
   * We need number of hosts - 1 semicolons and one terminating '\0'
   */
  if ((prop_data = Xmalloc (prop_length)) == NULL) {
    XmuXErrorF ("XmuXAddHosts : Can't update prop (out of mem)!\n");
    return;
  }
  bufptr = prop_data;
  for (new_host = _muxApplCtx[appl_idx]->hosts;
       new_host != NULL; new_host = new_host->next) {
    strcpy (bufptr, new_host->hostname);
    bufptr += strlen (new_host->hostname);
    *bufptr++ = ',';
  }
  prop_data[prop_length - 1] = '\0';
 
  /*
   * we change the property on ALL toplevel windows
   */
  if (!IsClientDisplay (dpy))
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
 
  for (i = 0; i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    XChangeProperty (dpy,
		     TOPLEVELID (appl_idx, i),
		     XmuXServerAtom (dpy, _MUX_HOSTS),
		     XA_STRING, 8, PropModeReplace, prop_data,
		     prop_length);
 
  Xfree (prop_data);
}
 
void
XmuXRemoveHosts (dpy, hostnames, n)
  Display *dpy;
  char **hostnames;
  int n;
{
  register int i, num_hosts, prop_length, appl_idx, chalk_removed;
  register char *prop_data, *bufptr;
  register MUXHostInfo *pred, *curr;
 
  appl_idx = GetApplicationIndex (dpy);
  chalk_removed = False;
  XmuXDecServers (n);
  for (i = 0; i < n; i++) {
    curr = _muxApplCtx[appl_idx]->hosts;
 
    if (curr == NULL) {
      XmuXErrorF ("XmuXRemoveHost: list empty!\n");
      return;
    }
    if (!strcmp (curr->hostname, hostnames[i])) {
      /* don't remove the original connection */
      if (_muxApplCtx[appl_idx]->hosts->fd !=
	  _muxApplCtx[appl_idx]->client_fd) {
	if (curr->fd == _muxApplCtx[appl_idx]->ChalkHolder)
	  chalk_removed = True;
	_muxApplCtx[appl_idx]->hosts =
	  _muxApplCtx[appl_idx]->hosts->next;
 
	/*
	 * NOTE: We would get in trouble, if we freed the default
	 * connection (XCloseDisplay would free the application context);
	 * but that's not allowed here !
	 */
	XCloseDisplay (MuxDisplay (curr->fd));
	Xfree (curr->hostname);
	Xfree ((char *) curr);
      }
      continue;
    }
    for (pred = curr, curr = curr->next;
	 curr != NULL;
	 pred = curr, curr = curr->next)
      if (!strcmp (curr->hostname, hostnames[i])) {
	/* don't remove the original connection */
	if (curr->fd !=
	    _muxApplCtx[appl_idx]->client_fd) {
	  if (curr->fd == _muxApplCtx[appl_idx]->ChalkHolder)
	    chalk_removed = True;
	  pred->next = curr->next;
 
	  /*
	   * NOTE: We would get in trouble, if we freed the default
	   * connection (XCloseDisplay would free the application context);
	   * but that's not allowed here !
	   */
	  XCloseDisplay (MuxDisplay (curr->fd));
	  Xfree (curr->hostname);
	  Xfree ((char *) curr);
	  continue;
	}
      }
  }
 
  prop_length = 0;
  num_hosts = 0;
 
  /*
   * We'll change the XMUX_HOSTS property here which contains the names
   * of all hosts separated by semicolons don't rely on old value,
   * construct the new from application's host list
   */
  for (curr = _muxApplCtx[appl_idx]->hosts;
       curr != NULL; curr = curr->next) {
    /* watch out for the separating semicolons ! */
    prop_length += strlen (curr->hostname) + 1;
    num_hosts++;
  }
 
  /*
   * Let's do a little arithmetic here:
   *
   * prop_length = sum(strlen(host names)) + number of hosts;
   *
   * We need number of hosts - 1 semicolons and one terminating '\0'
   */
  if ((prop_data = Xmalloc (prop_length)) == NULL) {
    XmuXErrorF ("XmuXAddHosts : Can't update prop (out of mem)!\n");
    return;
  }
  bufptr = prop_data;
  for (curr = _muxApplCtx[appl_idx]->hosts;
       curr != NULL; curr = curr->next) {
    strcpy (bufptr, curr->hostname);
    bufptr += strlen (curr->hostname);
    *bufptr++ = ',';
  }
  prop_data[prop_length - 1] = '\0';
 
  /*
   * we change the property on ALL toplevel windows
   */
  if (!IsClientDisplay (dpy))
    dpy = XmuXPrimaryDisplayFromDisplay (dpy);
 
  for (i = 0; i < _muxApplCtx[appl_idx]->ntoplevels; i++)
    XChangeProperty (dpy,
		     TOPLEVELID (appl_idx, i),
		     XmuXServerAtom (dpy, _MUX_HOSTS),
		     XA_STRING, 8, PropModeReplace, prop_data,
		     prop_length);
 
  Xfree (prop_data);
  if (chalk_removed)
    /* switch to the default */
    XmuXSetChalkHolder (dpy, num_hosts - 1);
 
}
 
void
XmuXFreeHosts (hosts)
  MUXHostInfo *hosts;
{
  register MUXHostInfo *helt = hosts;
 
  while (helt) {
    register MUXHostInfo *hnext = helt->next;
 
    Xfree (helt->hostname);
    Xfree ((char *) helt);
    helt = hnext;
  }
  hosts = NULL;
  return;
}
 
Window
XmuXToplevel (dpy, i)
  Display *dpy;
  int i;
{
  register int appl_idx;
 
  appl_idx = GetApplicationIndex (dpy);
 
  if (i >= _muxApplCtx[appl_idx]->ntoplevels)
    return (None);
  else
    return (TOPLEVELID (appl_idx, i));
}
conmgr.c        679050485   301   0     100644  61954     `
/* $Id: conmgr.c,v 1.1.1.1 1990/11/28 17:01:52 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
#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]);
 
      before = clock ();
      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);
	    }
      }
      after = clock ();
      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) {
      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;
    } /* switch */
    break;
  
  case XMUXUpdateStatus:
  
    XmuXUpdateStatus (event->xany.display, event->xclient.window);
    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 ");
  }
}
XmuX.h          679050548   301   0     100644  3508      `
/* 
 * -- $Header: /cec/src/nestor/shX/shXlib/X11/XmuX.h,v 1.1.1.1 90/11/28
16:47:27 altenhof Exp $ 
 
/* 
 * -- Public definition header file 
 * -- used with Xlib Multiplex extension (XmuX)
 */
 
#ifndef _XMUX_H  
#define _XMUX_H
 
/* 
 * -- want we need are some definitions to allow communication with XmuX
 * -- via ClientMessage Events and Properties; this implies that we 
 * -- need to create unique atoms
 */
 
#define _MUX_CPORT       0
#define _MUX_MESSAGE     1
#define _MUX_HOSTS       2
#define _MUX_CHALKHOLDER 3
#define _MUX_STATUS      4
 
#define XMUX_MESSAGE_NAME   "XMUX_MESSAGE"  /* should be used in XInternAtom 
					       to create/get the unique 
					       XMUX_MESSAGE Atom */
#define XMUX_CPORT_NAME  "XMUX_CPORT"       /* should be used in XInternAtom
					       to create/get the unique
					       XMUX_CPORT Atom */
 
#define XMUX_HOSTS_NAME "XMUX_HOSTS"        /* should be used in XInternAtom
                                               to create/get the unique
                                               XMUX_HOSTS Atom */
 
#define XMUX_CHALKHOLDER_NAME "XMUX_CHALKHOLDER"
                                            /* should be used in XInternAtom
                                               to create/get the unique
                                               XMUX_CHALKHOLDER Atom */
 
 
#define XMUX_STATUS_NAME "XMUX_STATUS"        /* should be used in XInternAtom
                                               to create/get the unique
                                               XMUX_STATUS Atom */
 
#define XMUX_MESSAGE_FORMAT 32              /* we use long values 
					       to communicate with messages */
 
#define MuxAdd     '+'
#define MuxRemove  '-'
 
#define XMUXType     0
#define XMUXSubtype  1
#define XMUXDetail   2
/* 
 * -- the different possible meanings of this new message type are defined
 * -- as constants; clients are recommended to use these constants -- or
 * -- (even better) to use the convenience functions that hide these details)
 * -- when communicating with XmuX
 * -- LOGICAL STRUCTURE :
 * -- xmessage.data.l[ 0 ] --> message type: Action/Information/Error
 * -- xmessage.data.l[ 1 ] --> message subtype: e.g. Error-DuplicationFailed
 */
 
/* -- message types -- */
 
#define XMUXAction  (1L << 0)
#define XMUXInfo    (1L << 1)
#define XMUXError   (1L << 2)
 
/* -- message subtypes -- */
 
/* -- action subtypes -- */
 
#define XMUXPassChalk       0L
#define XMUXDebug           1L
#define XMUXGlobalPassChalk 2L
#define XMUXUpdateStatus    3L
 
#define DbgEvents    1L
#define DbgRequests  2L
#define DbgResources 3L
#define DbgMux       4L
#define DbgAll       5L
 
/* -- Information subtypes -- */
#define XMUXMuxSetup    0L           /* XmuX says: "I'm trying to multiplex an
					application" */
#define XMUXBroadSetup  1L           /* XmuX says: "I'm trying to broadcast an
					application" */
#define XMUXChalkPassed 2L           /* XmuX says: "I've passed the chalk" */
					
#define XMUXFinished    3L           /* XmuX says: "I've successfully finished
					multiplexing/broadcasting" */
/* -- error subtypes -- */
#define XMUXMuxFailed   0L           /* XmuX says: "I'm sorry, can't 
					multiplex" */
#define XMUXBroadFailed 1L           /* XmuX says: "I'm sorry, can't 
					braodcast */
 
#define XMUXProperty 0
#define XMUXMessage  1
 
typedef 
struct _XmuXHints {
    int type ;              /* message or property */
    union {
	char  *strings[ 2 ] ;
	long   mess[ 3 ] ;
    } u ;
} XmuXHints ;
 
#endif
    
 
 
chalkbox.c      679050591   301   0     100666  23034     `
#ifndef lint
static char rcsid[] = "";
#endif /* lint */
 
#include        <stdio.h>
#include        <X11/Xatom.h>
#include        <X11/IntrinsicP.h>
#include        <X11/Intrinsic.h>
#include        <X11/StringDefs.h>
#include        <X11/Shell.h>
#include        <X11/CoreP.h>
 
#include        <X11/Xaw/Cardinals.h>
#include        <X11/Xaw/List.h>
#include        <X11/Xaw/Paned.h>
#include        <X11/Xaw/Command.h>
#include        <X11/Xaw/Box.h>
#include        <X11/Xaw/AsciiText.h>
#include        <X11/Xaw/TextP.h>
#include        <X11/Xaw/TextSinkP.h>
#include        <X11/Xaw/Label.h>
 
#include        "Person.h"
#include        "XmuX.h"
 
#define		INITVAL		1
#define		HUNDRED		100
#define		STOCKMESSAGE	"Click on user to give chalk to"
#define		MAX(a,b)	((a) > (b) ? (a) : (b))
 
#define		GIVE		0
#define		DELETE		1
 
#define		ANARCHY		0
#define		POLITE		1
 
typedef struct {
	char	*displayname;
	char	*fullname;
	char	*login;
	int	status;
	Boolean	chair;
} DisplayEntry, *DisplayEntryPtr;
 
DisplayEntryPtr	displays;
 
static char *status_name[] = { "sitting", "requesting" , "speaking" };
 
static	char *adduserbuttons[] = {
		"add user",
		"abort",
		NULL};
 
static	char *top_button_list[] = {
		"add user",
		"delete user",
		"choose app",
		"quit",
		NULL};
 
static void	MyMainLoop();
static void	SelectCB();
static void	NumCB();
static void	TopCB(), BotCB();
void		GetInput();
 
extern Window	Select_Window();
extern XtAppContext	_XtDefaultAppContext();
extern char     *getenv();
 
Display		*dpy;
Window		window;
Widget		top_buttons[10];
Widget		topW, listW, statusLabelW, aclPaneW;
int		char_width = 8;
Widget		inputPopupW = 0, inputTextW;
Widget		aclPopupW = 0;
void		TopSelect();
void		TriggerInput();
Widget		addLabelW;
TextWidget	addTextW;
static void	DoDelete();
static void     UpdateStatus();
void		(*input_function)() = NULL;
char		*saved_line = NULL;
int		saved_index = -1;
int		mode = GIVE;
int		paradigm = POLITE;
Widget		paneW, box1W, box2W, box3W;
Widget		people[10];
Widget		labels[10];
Atom		host_atom, chalk_atom, status_atom;
Boolean		ihavethechalk = True;
char		*mydisplayname;
char		changed_host[50];
char		my_new_chalkholder[50];
Boolean		need_to_redisplay_hosts = True;
static char	oldhostlist[200];
 
main (argc, argv)
int argc;
char *argv[];
{
	Arg		args[5];
	unsigned int	n, i;
	char		**toplist = top_button_list;
	char		myname[32];
	static XtActionsRec actions[] = {
		{"TriggerInput",	TriggerInput}};
 
	if (argc > 1) {
		if (argv[1][0] == 'a') {
			paradigm = ANARCHY;
		}
	}
 
	switch (paradigm) {
	case ANARCHY:
		fprintf (stderr, "Working in anarchy mode\n");
		break;
	case POLITE:
		fprintf (stderr, "Working in polite mode\n");
		break;
	}
 
	strcpy (changed_host, "\0");
	strcpy (my_new_chalkholder, "gunk");
 
	topW = XtInitialize("topwidget", "Chalkbox", NULL, 0, &argc, argv);
 
	dpy = XtDisplay(topW);
	mydisplayname = getenv ("DISPLAY");
 
	fprintf (stderr, "I'm user %s\n" , 
		 mydisplayname ? mydisplayname : "NULL");
        XtAppAddActions (       XtWidgetToApplicationContext(topW),
					actions, XtNumber(actions));
 
	n = 0;
	paneW = XtCreateManagedWidget(
			"pane",
			panedWidgetClass,
			topW,
			args,
			n);
 
	n = 0;
	box1W = XtCreateManagedWidget(
			"box1",
			boxWidgetClass,
			paneW,
			args,
			n);
	n = 0;
	box2W = XtCreateManagedWidget(
			"box2",
			boxWidgetClass,
			paneW,
			args,
			n);
 
	gethostname (myname, 32);
 
	n = 0;
	XtSetArg(args[n], XtNlabel, "No application selected");	n++;
	statusLabelW = XtCreateManagedWidget(
			"statuslabel",
			labelWidgetClass,
			paneW,
			args,
			n);
 
	n = 0;
	box3W = XtCreateManagedWidget(
			"box3",
			boxWidgetClass,
			paneW,
			args,
			n);
 
	for (i = 0; *toplist; toplist++, i++) {
		n = 0;
		XtSetArg(args[n], XtNlabel, *toplist);	n++;
 
		top_buttons[i] = XtCreateManagedWidget(
				"topbutton",
				commandWidgetClass,
				box3W,
				args,
				n);
 
		XtAddCallback (top_buttons[i], XtNcallback, TopCB, i);
	}
 
	XtRealizeWidget(topW);
 
	host_atom = XInternAtom (dpy, XMUX_HOSTS_NAME, False);
	if (!host_atom) {
		printf ("Atom %s not valid!\n", XMUX_HOSTS_NAME);
		return (BadAtom);
	}
 
	chalk_atom = XInternAtom (dpy, XMUX_CHALKHOLDER_NAME, False);
	if (!chalk_atom) {
		printf ("Atom %s not valid!\n", XMUX_CHALKHOLDER_NAME);
		return (BadAtom);
	}
 
	status_atom = XInternAtom (dpy, XMUX_STATUS_NAME, False);
	if (!status_atom) {
		printf ("Atom %s not valid!\n", XMUX_STATUS_NAME);
		return (BadAtom);
	}
 
	MyMainLoop();
}
 
static void
MyMainLoop()
{
	XEvent		the_event;
	XtAppContext	app = _XtDefaultAppContext();
 
	while (1) {
		XtAppNextEvent(app, &the_event);
		if (the_event.type == PropertyNotify) {
			ServicePropertyNotify(&the_event);
		}
		if (the_event.type == ClientMessage) {
			ServiceClientMessage(&the_event);
		}
		if (the_event.type == DestroyNotify) {
		        ServiceDestroyNotify(&the_event);
		}
		XtDispatchEvent(&the_event);
	}
}
 
ServiceDestroyNotify(destroy_event)
 
XDestroyWindowEvent *destroy_event;
 
{ int		n;
  Arg		args[1];
 
  if (destroy_event->window != window) {
           return;
  }  
 
  DeleteAllUsers();
 
  /* reset the status label */
  n = 0;
  XtSetArg(args[n], XtNlabel, "Application removed! select a new one");	n++;
  XtSetValues (statusLabelW, args, n);
  
}
ServiceClientMessage(client_event)
XClientMessageEvent	*client_event;
{
}
 
ServicePropertyNotify(prop_event)
XPropertyEvent	*prop_event;
{
 
	if (prop_event->window != window) {
		return;
	}
	if (prop_event->atom == chalk_atom) {
		HandleChalkChange(prop_event);
	}
	if (prop_event->atom == host_atom) {
		HandleHostChange(prop_event);
	}
	if (prop_event->atom == status_atom) {
		DisplayUsers();
	}
}
 
/*
**  If I'm just sent off a request to add or delete a user, this is my
**  confirmation that it worked.  Check that this is so, and if so,
**  add or delete the status entry for the user.
**
*/
 
HandleHostChange(prop_event)
XPropertyEvent	*prop_event;
{
	Atom		atomback;
	int		formatback;
	unsigned long	itemsback, bytesback;
	char		*hostlist, *status;
	int		n;
	Arg		args[5];
	int		i;
	char		newstatus[200];
	char		localentry[80];
	char		*hoststart, *oldhoststart, *statusstart;
	char		*hostend, *oldhostend, *statusend;
 
	if (! *changed_host) {
		need_to_redisplay_hosts = True;
		return;
	}
	fprintf (stderr, "Handling host change\n");
 
	XGetWindowProperty(	dpy, window, host_atom, 0L, 200L,
				False, AnyPropertyType, &atomback,
				&formatback, &itemsback, &bytesback,
				&hostlist);
 
	fprintf (stderr, "hostlist = '%s'\n",hostlist);
	fprintf (stderr, "oldhostlist = '%s'\n",oldhostlist);
	if (!hostlist) return;
 
	XGetWindowProperty(	dpy, window, status_atom, 0L, 200L,
				False, AnyPropertyType, &atomback,
				&formatback, &itemsback, &bytesback,
				&status);
	fprintf (stderr, "status = '%s'\n",status);
 
/*
** Bootstrapping or adding a new host.
*/
	if (	(!oldhostlist[0]) || 
		(strlen (oldhostlist) < strlen (hostlist))) {
		fprintf (stderr, "Adding a host/bootstrapping\n");
 
		if (!status)
			sprintf (newstatus, "S=%d|C=%d", PERSON_SITTING, True);
		else
			sprintf (newstatus, "S=%d|C=%d,%s",
PERSON_SITTING, True, status);
	}
 
/*
** Say what?  No change, do nothing.
*/
	else if (!strcmp (oldhostlist, hostlist)) {
		fprintf (stderr, "No change, returning\n");
		return;
	}
 
/*
** Deleting an existing host...need to find its status entry and delete it.
** Walk through old and new host lists until you find an entry that is
** different, then delete the corresponding status entry.
*/
	else {
		fprintf (stderr, "Deleting a host\n");
		
		hoststart = hostlist;
		oldhoststart = oldhostlist;
		statusstart = status;
 
		hostend = strchr(hostlist, ',');
		oldhostend = strchr(oldhostlist, ',');
		statusend = strchr(status, ',');
		*newstatus = '\0';
 
		while (hostend && oldhostend && statusend) {
			*hostend = '\0'; 
			*oldhostend = '\0';
			*statusend = '\0';
 
			if (strcmp (hoststart, oldhoststart))
				break;
 
			sprintf (	newstatus,"%s%s%s",
					newstatus,
					(*newstatus ? "," : ""),
					statusstart);
			*hostend = ',';
			*oldhostend = ',';
			*statusend = ',';
 
			hoststart = hostend + 1;
			oldhoststart = oldhostend + 1;
			statusstart = statusend + 1;
 
			hostend = strchr(hoststart, ',');
			oldhostend = strchr(oldhoststart, ',');
			statusend = strchr(statusstart, ',');
		}
/*
** Save the leftovers, if there are any
*/
		if (statusend)
			sprintf (	newstatus,"%s%s%s",
					newstatus,
					(*newstatus ? "," : ""),
					statusend + 1);
	}
 
	fprintf (stderr, "newstatus = '%s'\n",newstatus);
 
	XChangeProperty (	dpy, window, status_atom, 
				XA_STRING, 8,
				PropModeReplace, newstatus,
				strlen (newstatus) + 1);
	UpdateStatus (dpy, window);
	strcpy (changed_host, "\0");
	strcpy (oldhostlist, hostlist);
	need_to_redisplay_hosts = True;
}
 
HandleChalkChange(prop_event)
XPropertyEvent	*prop_event;
{
	Atom		atomback;
	int		formatback;
	unsigned long	itemsback, bytesback;
	char		*chalkdata, *hostlist, *statusdata;
	char		*hostend, *statusend;
	int		n;
	Arg		args[5];
	int		i;
	char		buffer[80];
	XmuXHints	mux_hints;
 
	XGetWindowProperty(	dpy, window, chalk_atom, 0L, 200L,
				False, AnyPropertyType, &atomback,
				&formatback, &itemsback, &bytesback,
				&chalkdata);
 
/*
** Not a change I asked for.  Don't do anything now...I'll notice when
** the status property changes.
*/
	if (strcmp (chalkdata, my_new_chalkholder)) {
		return;
	}
 
/*
** Sit the current speaker down and make the new guy stand up.
*/
	for (i = 0; displays[i].displayname; i++) {
		if (displays[i].status == PERSON_SPEAKER)
			displays[i].status = PERSON_SITTING;
		if (!strcmp (chalkdata, displays[i].displayname))
			displays[i].status = PERSON_SPEAKER;
	}
 
	SendStatus();
	strcpy (my_new_chalkholder, "gunk");
}
 
static void
AddCB(w, client_data, call_data)
Widget	w;
XtPointer	client_data;
XtPointer	call_data;
{
	char		*tempstring, *temp;
	Arg		args[5];
	unsigned int	n, i;
	Widget		labelW;
	XmuXHints	mux_hints;
	char		statusline[80];
 
	if (!inputPopupW)
		return;
 
	if (client_data != 0)
		goto ABORT;
 
	n = 0;
	XtSetArg(args[n], XtNstring, &tempstring);		n++;
	XtGetValues (inputTextW, args, n);
 
	temp = (char *) malloc (80);
 
	sprintf (temp, "+%s",tempstring);
	mux_hints.type = XMUXProperty;
	mux_hints.u.strings[0] = XMUX_CPORT_NAME;
	mux_hints.u.strings[1] = temp;
	XmuXSetHints (dpy, window, &mux_hints);
 
	strcpy (changed_host, tempstring);
 
	n = 0;
	XtSetArg(args[n], XtNlabel, "waiting for new user");		n++;
	XtSetValues (statusLabelW, args, n);
 
ABORT:
	n = 0;
	XtSetArg(args[n], XtNlabel, STOCKMESSAGE);	n++;
	XtSetValues (statusLabelW, args, n);
 
	XtDestroyWidget(inputPopupW);
	XtSetKeyboardFocus(topW, topW);
	inputPopupW = 0;
}
 
AddUserToList(name, status)
char	*name;
char	*status;
{
	Arg		args[5];
	unsigned int	n, i;
	char		*newguy;
	Dimension	newwidth;
	Atom		atomback;
	int		formatback;
	unsigned long	itemsback, bytesback;
	char		*statusdata, *end, *start;
	XmuXHints	mux_hints;
 
	if (displays) {
		for (i = 0; displays[i].displayname; i++)
			;
		displays = (DisplayEntryPtr) 
			realloc (displays, (i + 2) * sizeof (DisplayEntry));
	}
	else {
		displays = (DisplayEntryPtr) 
			malloc (2 * sizeof (DisplayEntry));
		i = 0;
	}
 
	displays[i].fullname = (char *) NULL;
	displays[i].login = (char *) NULL;
	displays[i].status = (int) NULL;
	displays[i].chair = (Boolean) NULL;
 
	newguy = (char *) malloc (80);
	sprintf (newguy, "%s", name);
	displays[i].displayname = newguy;
	displays[i + 1].displayname = NULL;
 
	FillDisplayEntry(&(displays[i]), status);
 
	newwidth = MAX (((i + 1) * HUNDRED + 20), 300);
 
	n = 0;
	XtSetArg(args[n], XtNwidth, newwidth);		n++;
	XtSetValues (paneW, args, n);
 
	n = 0;
	if (displays[i].fullname) {
		XtSetArg(args[n], XtNlabel, displays[i].fullname);	n++;
	}
	else {
		XtSetArg(args[n], XtNlabel, displays[i].displayname);	n++;
	}
 
	labels[i] = XtCreateManagedWidget(	"namelabel",
					commandWidgetClass,
					box2W,
					args,
					n);
	XtAddCallback (labels[i], XtNcallback, SelectCB, i);
 
	n = 0;
	XtSetArg(args[n], XtNshapeStyle, XawShapeOval); n++;
	people[i] = XtCreateManagedWidget(	"personlabel",
					personWidgetClass,
					box1W,
					args,
					n);
	XtAddCallback (people[i], XtNcallback, SelectCB, i);
}
 
Fill(dest, source)
char	**dest, *source;
{
	if (!source) {
		fprintf (stderr, "WARNING!  Null source passed to Fill\n");
		return;
	}
	*dest = (char *) malloc (strlen(source));
	strcpy (dest, source);
}
 
/*
** parse the status entry and fill the data structure appropriately.
*/
 
FillDisplayEntry(pde, status)
DisplayEntryPtr	pde;
char		*status;
{
	char	*start, *end;
 
	for (start = status;;) {
		end = strchr(start, '|');
		if (end)
			*end = '\0';
 
		switch (*start) {
		case 'N':
			Fill(&(pde->fullname), start);
			break;
		case 'L':
			Fill(&(pde->login), start);
			break;
		case 'S':
			pde->status = atoi(start + 2);
			break;
		case 'C':
			pde->chair = atoi(start + 2);
			break;
		}
 
		if (end) {
			*end = '|';
			start = end + 1;
		}
		else
			break;
	}
}
 
DeleteAllUsers()
{
	int	i;
	
	if (!displays)
		return;
 
	for (i = 0; displays[i].displayname; i++) {
		free(displays[i].displayname);
		if (displays[i].fullname)
			free(displays[i].fullname);
		if (displays[i].login)
			free(displays[i].login);
		displays[i].displayname = 0;
		XtDestroyWidget(labels[i]);
		labels[i] = 0;
		XtDestroyWidget(people[i]);
		people[i] = 0;
	}
	free(displays);
	displays = 0;
}
 
static void
TopCB(w, client_data, call_data)
Widget	w;
XtPointer	client_data;
XtPointer	call_data;
{
	XawListReturnStruct	*which;
	int		n;
	Arg		args[5];
 
	switch ( (int) client_data) {
		case 0:
			SetUpForAdd();
			break;
		case 1:
			n = 0;
			XtSetArg(args[n], XtNlabel, "Click on user to delete");	n++;
			XtSetValues (statusLabelW, args, n);
			mode = DELETE;
			break;
		case 2:
			GetNewApp();
			break;
		case 3:
			exit(0);
	}
 
}
 
GetNewApp()
{
	window = Select_Window(dpy);
	XSelectInput(dpy, window, PropertyChangeMask | StructureNotifyMask);
	DeleteAllUsers();
	DisplayUsers();
}
 
DisplayUsers()
{
	Atom		atomback;
	int		formatback;
	unsigned long	itemsback, bytesback;
	char		*chalkdata, *hostlist, *statusdata;
	char		*hostend, *statusend;
	int		n;
	Arg		args[5];
	int		i;
	char		buffer[80];
	XmuXHints	mux_hints;
 
	XGetWindowProperty(	dpy, window, host_atom, 0L, 200L,
				False, AnyPropertyType, &atomback,
				&formatback, &itemsback, &bytesback,
				&hostlist);
	if (!hostlist) {
		n = 0;
		XtSetArg(args[n], XtNlabel, "Not a shared
application--Choose another");		n++;
		XtSetValues (statusLabelW, args, n);
		return;
	}
 
	strcpy (oldhostlist, hostlist);
 
	XGetWindowProperty(	dpy, window, chalk_atom, 0L, 200L,
				False, AnyPropertyType, &atomback,
				&formatback, &itemsback, &bytesback,
				&chalkdata);
 
	XGetWindowProperty(	dpy, window, status_atom, 0L, 200L,
				False, AnyPropertyType, &atomback,
				&formatback, &itemsback, &bytesback,
				&statusdata);
 
	if (!chalkdata || !statusdata) {
/*
** Bootstrapping...
*/
		sprintf (buffer, "S=%d|C=%d", PERSON_SPEAKER, True);
 
		XChangeProperty (	dpy, window, status_atom, 
					XA_STRING, 8,
					PropModeReplace, buffer,
					strlen (buffer) + 1);
		UpdateStatus (dpy, window);
		sprintf (buffer, "Assigning chalk to first user\n");
		n = 0;
		XtSetArg(args[n], XtNlabel, buffer);		n++;
		XtSetValues (statusLabelW, args, n);
 
		mux_hints.type = XMUXMessage;
		mux_hints.u.mess[XMUXType] = XMUXAction;
		mux_hints.u.mess[XMUXSubtype] = XMUXPassChalk;
		mux_hints.u.mess[XMUXDetail] = 0;
		XmuXSetHints (dpy, window, &mux_hints);
 
		XGetWindowProperty(	dpy, window, status_atom, 0L, 200L,
					False, AnyPropertyType, &atomback,
					&formatback, &itemsback, &bytesback,
					&statusdata);
 
		if (!statusdata) {
			fprintf (stderr, "Bootstrap failed!\n");
			return;
		}
 
	}
 
	if (	(!displays) || need_to_redisplay_hosts) {
		need_to_redisplay_hosts = False;
 
		DeleteAllUsers();
	
		hostend = strchr(hostlist, ',');
		statusend = strchr(statusdata, ',');
 
		while (hostend && statusend) {
			*hostend = '\0';
			*statusend = '\0';
			AddUserToList(hostlist, statusdata);
			*hostend = ',';
			*statusend = ',';
			statusdata = statusend + 1;
			hostlist = hostend + 1;
			statusend = strchr(statusdata, ',');
			hostend = strchr(hostlist, ',');
		}
 
		if (hostend || statusend) {
			n = 0;
			XtSetArg(args[n], XtNlabel, "Error parsing
host/status atoms");		n++;
			XtSetValues (statusLabelW, args, n);
			return;
		}
 
		AddUserToList(hostlist, statusdata);
	}
	else {
	}
 
	n = 0;
	XtSetArg(args[n], XtNlabel, STOCKMESSAGE);	n++;
	XtSetValues (statusLabelW, args, n);
 
	UpdateIcons();
}
 
SetUpForAdd()
{
	int		n;
	Arg		args[5];
	n = 0;
	XtSetArg(args[n], XtNlabel, "Adding a new user");	n++;
	XtSetValues (statusLabelW, args, n);
	GetInput("Name of new user:", adduserbuttons, AddCB);
 
}
 
void
GetInput(label, button_list, return_function)
char	*label;
char	**button_list;
void	(*return_function)();
{
	Arg		args[5];
	unsigned int	n, i;
	Widget		button, inputPaneW, inputBox1W;
	Position	parentx, parenty;
	Dimension	parentwidth, mywidth;
 
	if (inputPopupW)
		return;
 
	input_function = return_function;
 
	n = 0;
	XtSetArg(args[n], XtNwidth, &parentwidth);	n++;
	XtSetArg(args[n], XtNx, &parentx);		n++;
	XtSetArg(args[n], XtNy, &parenty);		n++;
	XtGetValues (topW, args, n);
 
	mywidth = 35 * char_width;
 
	n = 0;
	XtSetArg(args[n], XtNwidth, mywidth);		n++;
	XtSetArg(args[n], XtNtransient, True);		n++;
	XtSetArg(args[n], XtNtransientFor, topW);	n++;
	XtSetArg(args[n], XtNx, 
		parentx + ((parentwidth - mywidth) / 2));	n++;
	XtSetArg(args[n], XtNy, parenty + 100);		n++;
 
	inputPopupW = XtCreatePopupShell(	
			"inputpopup", 
			transientShellWidgetClass,
			topW,
			args,
			n);
 
	inputPaneW = XtCreateManagedWidget(
			"pane",
			panedWidgetClass,
			inputPopupW,
			NULL,
			0);
 
	n = 0;
	XtSetArg(args[n], XtNlabel, label);	n++;
	(void) XtCreateManagedWidget(
			"label",
			labelWidgetClass,
			inputPaneW,
			args,
			n);
 
	n = 0;
	XtSetArg(args[n], XtNborderWidth, 0);			n++;
	XtSetArg(args[n], XtNeditType, XawtextEdit);		n++;
 
	inputTextW = XtCreateManagedWidget(
			"text",
			asciiTextWidgetClass,
			inputPaneW,
			args,
			n);
 
	n = 0;
	XtSetArg(args[n], XtNborderWidth, 0);			n++;
	inputBox1W = XtCreateManagedWidget(
			"inputbox",
			boxWidgetClass,
			inputPaneW,
			args,
			n);
 
	for (i = 0; *button_list; button_list++, i++) {
		n = 0;
		XtSetArg(args[n], XtNlabel, *button_list);	n++;
 
		button = XtCreateManagedWidget(
				"anybutton",
				commandWidgetClass,
				inputBox1W,
				args,
				n);
 
		XtAddCallback (button, XtNcallback, return_function, i);
	}
 
	XtPopup(inputPopupW, XtGrabNone);
	XtSetKeyboardFocus(inputPaneW, inputTextW);
}
 
void
TriggerInput(w, event, params, num_params)
Widget  w;
XEvent  *event;
String  *params;
int     *num_params;
{
	if (*num_params < 1)
		return;
 
	if (!input_function)
		return;
 
/*
** Simulate pressing button zero, if passed "Go"
*/
	(*input_function) (w, (XtPointer) strcmp(params[0], "Go"), NULL);
 
}
 
static void
SelectCB(w, client_data, call_data)
Widget	w;
XtPointer	client_data;
XtPointer	call_data;
{
	switch (paradigm) {
	case ANARCHY:
		AnarchicClick((int) client_data);
		break;
	case POLITE:
		RequestClick((int) client_data);
		break;
	}
}
	
 
AnarchicClick(selected)
int selected;
{
	XmuXHints	mux_hints;
	Arg		args[5];
	unsigned int	n, i;
 
	switch (mode) {
	case GIVE:
		strcpy (my_new_chalkholder, displays[selected].displayname);
		mux_hints.type = XMUXMessage;
		mux_hints.u.mess[XMUXType] = XMUXAction;
		mux_hints.u.mess[XMUXSubtype] = XMUXPassChalk;
		mux_hints.u.mess[XMUXDetail] = selected;
		XmuXSetHints (dpy, window, &mux_hints);
		break;
 
	case DELETE:
		DoDelete(selected);
		mode = GIVE;
		n = 0;
		XtSetArg(args[n], XtNlabel, STOCKMESSAGE);	n++;
		XtSetValues (statusLabelW, args, n);
		break;
	}
}
 
RequestClick(selected)
int selected;
{
	XmuXHints	mux_hints;
	Arg		args[5];
	unsigned int	n, i;
	char		statusline[80];
 
/*
** I can pass the chalk if I have it.
*/
	if (ihavethechalk) {
		AnarchicClick(selected);
	}
/*
** Otherwise, I can make a request for it.
*/
	else {
		if (!strcmp (displays[selected].displayname, mydisplayname)) {
			displays[selected].status = PERSON_REQUEST;
			SendStatus();
		}
		else {
			sprintf (statusline, "You do not have the chalk");
			n = 0;
			XtSetArg(args[n], XtNlabel, statusline);	n++;
			XtSetValues (statusLabelW, args, n);
		}
	}
}
 
SendStatus()
{
	char	status[200];
	char	localentry[80];
	int i;
 
	if (!displays)
		return;
 
	*status = '\0';
 
	for (i = 0; displays[i].displayname; i++) {
		*localentry = '\0';
		if (displays[i].fullname)
			sprintf (	localentry, "%sN=%s|", 
					localentry, displays[i].fullname);
 
		if (displays[i].login)
			sprintf (	localentry, "%sL=%s|", 
					localentry, displays[i].login);
 
		sprintf (	localentry, "%sS=%d|", 
				localentry, displays[i].status);
 
		sprintf (	localentry, "%sC=%d", 
				localentry, displays[i].chair);
 
		if (*status)
			sprintf (status, "%s,%s", status, localentry);
		else
			sprintf (status, "%s", localentry);
	}
 
	XChangeProperty (	dpy, window, status_atom, 
				XA_STRING, 8,
				PropModeReplace, status,
				strlen (status) + 1);
	UpdateStatus (dpy, window);
}
 
static void UpdateStatus(dpy, window)
 
Display *dpy;
Window   window;
 
{ XmuXHints mux_hints;
 
  XFlush (dpy);
  mux_hints.type = XMUXMessage;
  mux_hints.u.mess[XMUXType] = XMUXAction;
  mux_hints.u.mess[XMUXSubtype] = XMUXUpdateStatus;
  mux_hints.u.mess[XMUXDetail] = 0;
  XmuXSetHints (dpy, window, &mux_hints);
 
  XmuXSetHints (dpy, window, &mux_hints);
}
 
static void
DoDelete(which)
int which;
{
	char		*tempstring, *temp;
	Arg		args[5];
	unsigned int	n, i;
	Widget		labelW;
	XmuXHints	mux_hints;
 
	temp = (char *) malloc (80);
	sprintf (temp, "-%s", displays[which].displayname);
 
	mux_hints.type = XMUXProperty;
	mux_hints.u.strings[0] = XMUX_CPORT_NAME;
	mux_hints.u.strings[1] = temp;
	XmuXSetHints (dpy, window, &mux_hints);
 
	strcpy (changed_host, displays[which].displayname);
 
	DisplayUsers();
 
	free(temp);
}
 
 
UpdateIcons()
{
 
	Arg		args[5];
	unsigned int	n, i;
	char		statusline[80];
	Atom		atomback;
	int		formatback;
	unsigned long	itemsback, bytesback;
	char		*statusdata, *end;
	int		status;
	XmuXHints	mux_hints;
 
	if (!displays)
		return;
 
 
	XGetWindowProperty(	dpy, window, status_atom, 0L, 200L,
				False, AnyPropertyType, &atomback,
				&formatback, &itemsback, &bytesback,
				&statusdata);
 
	if (!statusdata)
		return;
 
	i = 0;
	sprintf (statusline, "No one has the chalk");
	ihavethechalk = False;
 
	do {
		end = strchr(statusdata, ',');
		if (end)
			*end = '\0';
		FillDisplayEntry(&(displays[i]), statusdata);
		if (displays[i].status == PERSON_SPEAKER) {
			if (!strcmp (mydisplayname, displays[i].displayname)) {
				sprintf (statusline, "You have the chalk");
				ihavethechalk = True;
			}
			else {
				sprintf (statusline, "User %s has the
chalk", displays[i].displayname);
			}
		}
		n = 0;
		XtSetArg(args[n], XtNpose, displays[i].status);	n++;
		XtSetValues (people[i], args, n);
		statusdata = end + 1;
		i++;
	} while (end);
 
	n = 0;
	XtSetArg(args[n], XtNlabel, statusline);	n++;
	XtSetValues (statusLabelW, args, n);
}
