/*
 * -- $Header: /cec/src/nestor/servers/R3/shXlib/ApplCtx.c,v 1.2 90/07/02 15:53:42 altenhof Exp $
 * -- 
 * -- $Log:	ApplCtx.c,v $
 * 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 )
	/* -- caling 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 ) ;
}

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