/* $Id: ApplCtx.c,v 1.2 90/11/29 10:04:51 altenhof Exp $ */

/*
 * Copyright (C) 1990 by Digital Equipment Corporation.
 * 
 * Author: Michael P. Altenhofen, CEC Karlsruhe e-mail:
 * Altenhofen@kampus.enet.dec.com
 * 
 * This file ist part of Shared X
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation without fee is hereby granted, but only for non-profit  use
 * and distribution,  and provided  that the copyright notice and this notice
 * is preserved on all copies.
 * 
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */
/*
 * $Log:	ApplCtx.c,v $
 * Revision 1.2  90/11/29  10:04:51  altenhof
 * BUG FIX in XmuXRemoveToplevelInfo (ApplCtx.c): ntops-- would leave a
 * None toplevel if you removed the last toplevel info
 * Cleanups in XRegion.c: Substitute FatalError with fprintf(stderr,...);exit(1)
 * Changes in other sources implement a new feature: 'Selective window
 * sharing' (you can hide windows!)
 * 
 * Revision 1.1.1.1  90/11/28  16:46:09  altenhof
 * First public release
 * 
 * Revision 1.1  90/11/28  16:46:07  altenhof
 * Initial revision
 * 
 * Revision 1.1.1.1  90/11/28  16:13:07  spanachi
 * First public version of shX
 * 
 * Revision 1.1  90/11/28  16:13:06  spanachi
 * Initial revision
 * 
 * Revision 1.2  90/07/02 15:53:42  altenhof Set MAX_IDX to MAXSOCKS
 * (there's still a bug but this "fixes" it).
 * 
 * Revision 1.1  90/04/23  09:45:21  spanachi Initial revision
 * 
 * Revision 1.1  90/03/21  15:24:24  altenhof Initial revision
 * 
 * Revision 1.2  89/11/28  13:53:48  neideck Added counting code for servers to
 * speed up our select replacement.
 * 
 * Revision 1.1  89/11/03  14:46:03  neideck Original send to X consortium
 * 
 * Revision 1.3  89/09/01  12:41:36  michael Minor cleanups (pointer casting in
 * Xrealloc )
 * 
 * Revision 1.2  89/08/23  11:37:16  michael *** empty log message ***
 * 
 */

#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, zapped;

  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
   */
  zapped = 0;
  for (j = 0, i = 0; i < ntops; i++)
    if (toplevels[i].window != None)
      toplevels[j++].window = toplevels[i].window;
    else
      zapped++;
  ntops -= zapped;
  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);
}

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