/*
 * -- $Header: /cec/src/nestor/servers/R3/shXlib/conmgr.c,v 1.2 90/07/26 08:31:47 altenhof Exp $
 * 
 * -- $Log:	conmgr.c,v $
 * Revision 1.2  90/07/26  08:31:47  altenhof
 * Changes implement the new feature (share applications from start)
 * without ringing the bell
 * 
 * Revision 1.1.1.1  90/04/23  10:11:40  spanachi
 * First version of 'shared X' library (X11R3). 
 * 
 * Revision 1.1  90/04/23  10:11:35  spanachi
 * Initial revision
 * 
 * Revision 1.1  90/04/03  15:13:02  altenhof
 * Initial revision
 * 
 * Revision 1.1  89/11/03  14:45:16  neideck
 * Original send to X consortium
 * 
 * Revision 2.3  89/09/27  19:37:57  michael
 * XXmuXMultiplex has been renamed to XmuXReplicate
 * Code has been added to fix the "bug" from XmuXInsertWindowInfo (see
 * comments there)
 * 
 * Revision 2.2  89/09/07  19:20:03  michael
 * Changes in XmuXFlushGC due to changes in XmuXMapPixelsAndPlane.
 *  Code to manage private cells from XAllocColorPlanes added ( but not tested !
 * ).
 * Register window colormaps even if they are already duplicated.
 * 
 * Revision 2.1  89/09/05  17:24:54  michael
 * FIXED BUG : Plane generation was wrong .
 * 
 * Revision 2.0  89/09/01  12:37:38  michael
 * This is the first version that supports clients allocating private color
 * cells
 * Restriction : Does not duplicate XAllocColorPlanes ( will be done later !? )
 * 
 * Revision 1.7  89/08/23  11:49:25  michael
 * *** empty log message ***
 * 
 * Revision 1.6  89/08/15  15:37:47  michael
 * We have a new file ( ApplCtx.c ) that deals with "application specific"
 * data. We define "application" as one client connection to the server, i.e.
 * an "X" applications may be departed into several "XmuX" applications.
 * Due to this, some routine interfaces/ calling sequences have changed.
 * 
 * Revision 1.5  89/08/15  10:48:49  michael
 * Old version of DuplicateWindow was buggy (unnecessarily added/changed
 * toplevel infos).
 * Multiplexing may now be triggered from all displays.
 * 
 * Revision 1.4  89/08/10  18:48:30  michael
 * Changes: multiplex now allows broadcasting !
 * XmuXFlushGC implements the "how friendly applications use "real" pixmaps"
 * rule (you heard about it ?)
 * 
 * Revision 1.3  89/07/20  20:16:16  michael
 * It would have been better to make this revision version 2.0.
 * The resource list is scaned three times: First we allocate all windows
 * to get a "drawable save set". We may have to use "matching" windows (if
 * original drawables have been destroyed) to create GC's/pixmaps/colormaps and
 * these must be mapped before we can use them.
 * 
 * Revision 1.2  89/07/07  08:45:01  michael
 * *** empty log message ***
 * 
 * Revision 1.1  89/06/09  13:34:54  michael
 * Initial revision
 * 
 */

#define NEED_RESOURCES
#define NEED_EVENTS

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

#include "macros.h"

int do_put_get ,
    do_sort ,
    do_map_pmap ,
    dont_map_b_and_w ;
   
#define MAX_PER_CALL 256    /* -- query 8 plane colormap cells in one call */
#define HUGE_PIXMAP  1024   /* 32 * 32 */

#define DEFAULT_LENGTH 8192L

static
void
FreeDpyNames( dpy_list , i )

char **dpy_list ;
int    i ;

{
    while( --i >= 0 )
	 Xfree( dpy_list[ i ] ) ;
     
    Xfree( ( char * ) dpy_list ) ;
}

static
char 
*ExtractDisplayName( buffer )

char *buffer ;

{   register char *display_ptr;	/* Display string buffer pointer */
    register char *name_ptr;	/* Server number buffer pointer */

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

char  
**CreateDpyNameList( display_names , n )

char *display_names ;
int  *n ;

{   register int i ;
    register char **dpy_list ;
    register char *displaybuf ;

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

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

#define NUM_VISUAL_CLASSES 6

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

static
XID
FindBestVisual( dpy , v_info , vis_list , n ) 

Display     *dpy ;
XVisualInfo *v_info ,
            *vis_list ;
int          n ;

{   register          int i ,
	                  class ;
    register unsigned int mapped_depth ;
    
    mapped_depth = XmuXMapDepth( dpy , v_info->depth , None , None ) ;
    
    i = 0 ;
    while( vis_list[ i ].depth < mapped_depth )
	i++ ;
    
    if( mapped_depth >= v_info->depth )
    {
	class = v_info->class ;

	while( i < n && ( vis_list[ i ].depth == mapped_depth && 
	       class_weight[ class ] > class_weight[ vis_list[ i ].class ] ) )
	    i++ ;
	/*
	 * -- if we don't find an exact match , 
	 * -- return the biggest one we have with this depth 
	 */
	if( vis_list[ i ].depth > mapped_depth  )
	    return( vis_list[ i > 0 ? i - 1 : 0 ].visualid ) ;
	else
	    return( vis_list[ i ].visualid ) ;
    }
    else
    {
	/*
	 * -- try the biggest one we have 
	 */
	while( i < n  && vis_list[ i ].depth == mapped_depth )
	    i++ ;
	return( vis_list[ i > 0 ? i - 1  : 0 ].visualid ) ;
    }
}
static
void
GeneratePlanes( i , cl_planes , cl_mask , sv_planes , sv_mask ) 

int            i ;
unsigned long *cl_planes ,
              *cl_mask ,
              *sv_planes ,
              *sv_mask ;

{   register int j ;

    *cl_mask = 
    *sv_mask = 0L ;

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

static
int
DuplicateProperties( dflt_dpy , new_dpys , n , old_win )

Display           *dflt_dpy ,
                 **new_dpys ;
int                n ;
Window             old_win ;

{   register Atom *properties ,  
	           new_atom , 
	           new_type ;
    register char *atom_name , 
                  *type_name ;
    int            nprops ,
                   k ;

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

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

    }
    XmuXdebug( debug_mux , "window 0x%lx duplicate %d properties :" ,
	       old_win , nprops ) ;
    /*
     * -- two possible cases  :
     * --  a) next property is a predefined one :
     * --     -->retrieve value on the old server via XGetWindowProperty 
     * --     -->set value on the new server via XChangeProperty
     * --  b) next property is not predefined :
     * --     -->retrieve value on the old server
     * --     -->retrieve atom name for this property via XGetAtomName
     * --     -->try to create Atom on the new server via XInternAtom
     * --     -->if the two Atom values differ, store them in the ID map 
     * --     -->set value on the new server 
     */
    
    for( ; --nprops >= 0 ; )
    {   Atom           actual_type ;
	int            actual_format , 
	               actual_length ,
	               bytes_after ;
        char *actual_data ;
	         
	/*
	 * -- We only know the property atom; property type and format 
	 * -- will be returned by XGetWindowProperty (but only because
	 * -- we ask for a property that exists for the specified window !)
	 */
	actual_data = NULL ;
	actual_format = 
	actual_length = 
	bytes_after   = 0 ;
	actual_type   = None ;

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

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

		    if( XmuXCheckLastError( new_dpys[ k ] , BadAtom ,
					     &error_request ) )
			XmuXErrorF( "Can't duplicate properties!\n" ) ;
		    /* -- other errors were reported via _XError -- */
		    Xfree( ( char * ) properties ) ;
		    Xfree( actual_data ) ;
		    return( XmuXFailure ) ;
		}
			
		if( properties[ nprops ] != new_atom )
		{
		    XmuXInsertID( new_dpys[ k ] , properties[ nprops ] , 
				  new_atom , FromClient ) ;
		    XmuXInsertID( new_dpys[ k ] , new_atom , 
				  properties[ nprops ] , FromServer ) ;
		    /*
		     * -- assume property atom == property type
		     */
		    new_type = new_atom ;
		}
		if( ( actual_type > XA_LAST_PREDEFINED ) &&
		    ( actual_type != properties[ nprops ] ) )
		{   
		    if( k == 0 )
			type_name = XGetAtomName( dflt_dpy , actual_type ) ;
		    new_type = XInternAtom( new_dpys[ k ] , type_name , 
					    False ) ;
		    if( !new_type )
		    {   unsigned char error_request ;

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

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


	    if( actual_format == 32 )
		/* -- long properties may contain IDs !! -- */
		CheckProperty( new_dpys[ k ] , new_atom , actual_data ,
			       actual_length , FromClient ) ;
	    XChangeProperty( new_dpys[ k ] , 
			     XmuXMapID( new_dpys[ k ] , old_win , 
				        FromClient ) ,
			     new_atom ,
			     new_type , actual_format ,
			     PropModeReplace , actual_data ,
			     actual_length ) ;
	    Xfree( actual_data ) ;
	    while( bytes_after )
	    {
		/* 
		 * -- DEFAULT_LENGTH was too short (a giant property!)
		 * -- retrieve the rest
		 */
		if( k == 0 )
		    XGetWindowProperty( dflt_dpy , old_win ,
				        properties[ nprops ] ,
				        0 , bytes_after , False , 
				        AnyPropertyType , &actual_type ,
				        &actual_format , &actual_length ,
				        &bytes_after , &actual_data ) ;

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

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

static 
void
CompareDisplays( dpy1 , dpy2 )

Display *dpy1 ,*dpy2 ;

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

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

	vis_tmp.screen = i ;
        vis_infos[ 0 ]  = 
	    XGetVisualInfo( dpy1 , VisualScreenMask , &vis_tmp , &n1 ) ;
	
	for( j = 0 ; j < n1 ; j++ )
	{
	    if( vis_infos[ 0 ][ j ].visual == DefaultVisual( dpy1 , i ) )
	       vid = VisualIDFromVisual( 
			   DefaultVisual( dpy2 , 
					  DefaultScreen( dpy2 ) ) ) ;
	    else
	       vid = FindBestVisual( dpy2 , &( vis_infos[ 0 ][ j ] ) , 
			      vis_infos[ 1 ] , n2 ) ;
			      
	    XmuXInsertID( dpy2 , vid , vis_infos[ 0 ][ j ].visualid , 
			  FromServer ) ;
	    XmuXInsertID( dpy2 , vis_infos[ 0 ][ j ].visualid , vid , 
		          FromClient ) ;
	}
    }
}

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

static
void
MapDefaultIDs( prim_server , new_server )

MUXServerPtr prim_server , 
             new_server ;

{   register XID      client_id , server_id ;
    register Display *dflt_dpy = prim_server->xdpy ,
                     *new_dpy  = new_server->xdpy ;
    register int i ;
 
    /* -- map the default roots -- */

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

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

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

static
int
AllocColorCells( dflt_dpy , old_cmap , new_dpys , new_cmaps , n , pr_cells )

Display               *dflt_dpy ,
                      **new_dpys ;
Colormap                old_cmap ,
                       *new_cmaps ;
int                     n ;
MUXPrivateColorCells   *pr_cells ;

{   register int   k ;
    unsigned long *server_planes = NULL ,
	          *server_pixels = NULL ,
	           s_plane , 
	           c_plane ,
	           num_planes ;
    XColor        *colors ;
    

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

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

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

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

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

	XmuXInsertPrivateCells( new_dpys[ k ] , new_cmaps[ k ] ,
			        pr_cells->pixels , server_pixels ,
			        pr_cells->n_pixels ,
			        pr_cells->planes , server_planes ,
			        pr_cells->n_planes , True ) ;
	
	num_planes = ( int )( 1L << pr_cells->n_planes ) ; 

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

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

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

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

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

static
int
AllocColorPlanes( dflt_dpy , old_cmap , new_dpys , new_cmaps , n , pr_cells )

Display               *dflt_dpy ,
                      **new_dpys ;
Colormap                old_cmap ,
                       *new_cmaps ;
int                     n ;
MUXPrivateColorCells   *pr_cells ;

{   register int   k ;
    unsigned long *server_pixels = NULL ,
                   client_planes[ 3 ] ,
                   server_planes[ 3 ] ,
	           s_plane , 
	           c_plane ;
    unsigned int   nplanes[ 3 ] ;
    XColor        *colors ;

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

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

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

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

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

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

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

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

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

	for( i = RED ; i <= BLUE ; i++ )
	{   unsigned long s_base ,
		          c_base ;
	 
	    s_base = lowbit( server_planes[ i ] ) ;
	    c_base = lowbit( client_planes[ i ] ) ;
	    s_plane = 
	    c_plane = 0L ;

	    while( 1 )
	    {   register int j ;
		
		for( j = 0 ; j < pr_cells->n_pixels ; j++ ) 
		    colors[ j ].pixel = pr_cells->pixels[ j ] | c_plane ;
		    
		XQueryColors( dflt_dpy, old_cmap , colors , 
			      pr_cells->n_pixels ) ;

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

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

static
int
DuplicateColormap( dflt_dpy , new_dpys , n , old_cmap , new_windows )

Display           *dflt_dpy ,
                 **new_dpys ;
int                n ;
Colormap           old_cmap ;    /* -- UNMAPPED ID !! -- */
Window            *new_windows ;  /* -- MAPPED IDs !! -- */

{   register int            total , 
	                    n_colors , 
	                    i ,
                            j ,
	                    k ,
                            allocated_here ,
                            dflt_cmap ;
    register unsigned long  client_pixel ;
    register XColor        *colors ;
    register Colormap      *new_cmaps ;
    MUXColormapInfoPtr      cmap_info ;
    MUXPrivateColorCells   *pr_cells ;

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

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

    cmap_info = XmuXGetColormapInfo( dflt_dpy , old_cmap ) ;
    allocated_here = False ;
    /* -- Call from the loop (not from DuplicateWindow ) ? -- */
    if( new_windows == NULL )
    {
	allocated_here = True ;
	new_windows = ( Window * ) Xmalloc( n * sizeof( Window ) ) ;
	if( new_windows == NULL )
	{
	    XmuXErrorF( "DuplicateColormap: Out of mem (new_windows)!\n" ) ;
	    return( XmuXFailure ) ;
	}
	for( k = 0 ; k < n ; k++ )
	    new_windows[ k ] = XmuXMapID( new_dpys[ k ] , 
					  cmap_info->window , FromClient ) ;
    }
    dflt_cmap = True ;
    /* -- default is mapped to default -- */
    if( old_cmap == DefaultColormap( dflt_dpy , DefaultScreen( dflt_dpy ) ) )
	for( k = 0 ; k < n ; k++ )
	{
	    new_cmaps[ k ] = 
		DefaultColormap( new_dpys[ k ] , 
				 DefaultScreen( new_dpys[ k ] ) ) ;
	    /* 
	     * -- map the IDs
	     */
	    XmuXInsertID( new_dpys[ k ] , old_cmap , new_cmaps[ k ] , 
			  FromClient ) ;
	    XmuXInsertID( new_dpys[ k ] , new_cmaps[ k ] , old_cmap ,
			  FromServer ) ;
	}
    else
    {   Visual *new_vis ;
	register Window cmap_win ;
	
	dflt_cmap = False ;
	if( !cmap_info->window )
	    /* 
	     * -- find a "matching" window (i.e. on the same screen)
	     * -- screen's root window is good
	     */
	    cmap_win = DefaultRootWindow( dflt_dpy ) ;
	else
	    cmap_win = cmap_info->window ;

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

	    new_cmaps[ k ] = XCreateColormap( new_dpys[ k ] , 
					      XmuXMapID( new_dpys[ k ] , 
							 cmap_win , 
							 FromClient ) ,
					      new_vis ,
				              AllocNone ) ;
	    /* 
	     * -- map the IDs
	     */
	    XmuXInsertID( new_dpys[ k ] , old_cmap , new_cmaps[ k ] , 
			  FromClient ) ;
	    XmuXInsertID( new_dpys[ k ] , new_cmaps[ k ] , old_cmap ,
			  FromServer ) ;
	}
    }

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

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

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

	for( i = 0 ; i < n_colors ; i++ )
	{
	    client_pixel = colors[ i ].pixel ;
	    /* -- this round-trip N TIMES ! -- */
	    for( k = 0 ; k < n ; k++ )
	    {
		if( !XAllocColor( new_dpys[ k ] , 
				  new_cmaps[ k ] ,
				  &colors[ i ] ) )
		{   unsigned char error_request ;
		    if( XmuXCheckLastError( new_dpys[ k ] , Success ,
					    &error_request ) )
			XmuXErrorF( "Can't alloc new color %d!\n", i ) ;
				         
		    /* -- free the new cmap array -- */
		    Xfree( ( char * ) new_cmaps ) ;
		    Xfree( ( char * ) colors ) ;
		    return( XmuXFailure ) ;
		}
		XmuXInsertSharedCell( new_dpys[ k ] , new_cmaps[ k ] ,
				      client_pixel ,
				      colors[ i ].pixel ) ;
	    }
	}
	total -= n_colors ;
	if( total < n_colors )
	    n_colors = total ;
    }
    for( k = 0 ; k < n ; k++ )
    {
	if( do_sort )
	    XmuXSortPixelMaps( new_dpys[ k ] ) ;
	/* 
	 * -- register the new colormap as new_window's colormap 
	 * -- Don't need parent id in this call 
	 * -- (since new_cmap != CopyFromParent )
	 */
	XmuXSetWindowCmap( new_dpys[ k ] , new_windows[ k ] , 
			   new_cmaps[ k ] , None ) ;
    }
    if( allocated_here )
	Xfree( ( char * ) new_windows ) ;

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

static 
int
DuplicateWindow( dflt_dpy , new_dpys , n , win_info )

Display         *dflt_dpy ,
                **new_dpys ;
int              n ;
MUXWindowInfoPtr win_info ;

{   XWindowAttributes    win_attr ;
    XSetWindowAttributes set_win_attr ;
    register Window      *new_windows ;
    int                  x , y , k , top_level ;
    int                  status ;
    long                 mux_specific_mask ;
    XSizeHints           size_hints ;

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

    /* --retrieve the unknown attributes from the original server -- */
    if( !XGetWindowAttributes( dflt_dpy , win_info->window , &win_attr ) )
    {
	XmuXErrorF( "Can't get window 0x%lx's attributes!\n" , 
		    win_info->window ) ;
	Xfree( ( char * ) new_windows ) ;
	return( XmuXFailure ) ;
    }
    
    set_win_attr.bit_gravity           = win_attr.bit_gravity ;
    set_win_attr.win_gravity           = win_attr.win_gravity ;
    set_win_attr.event_mask            = win_attr.your_event_mask ;
    set_win_attr.do_not_propagate_mask = win_attr.do_not_propagate_mask ;
    set_win_attr.override_redirect     = win_attr.override_redirect ;

    set_win_attr.backing_store         = NotUseful ;
    set_win_attr.backing_planes        = AllPlanes ;
    set_win_attr.backing_pixel         = 0L ;
    set_win_attr.save_under            = False ;
    /* 
     * -- Hard stuff ahead :
     * -- The window attributes background_pixmap , border_pixmap ,
     * -- cursor and colormaps reference other resources which
     * -- may have been created later on the original connection and then
     * -- set by Xlib convenience routines. To avoid server complaining
     * -- we fill in the "default values" (by "unmasking" them) and 
     * -- override them after all resources have been created. 
     * -- BUT : 
     * -- Window's colormap may be used in DuplicatePixmap to map 
     * -- pixel values; so we create the colormap here (if it does not already
     * -- exist) to guarantee that XmuXMapPixel in XmuXMapImage will use the
     * -- right one.
     */
#define InputOutputWindowAttributes\
        ( CWBitGravity | CWWinGravity | \
	  CWBackingStore | CWBackingPlanes | CWBackingPixel | \
	  CWOverrideRedirect | CWSaveUnder | CWEventMask | \
	  CWDontPropagate )
#define InputOnlyWindowAttributes\
        ( CWWinGravity | CWOverrideRedirect | CWEventMask | CWDontPropagate)
    /*
     * -- if this window is a toplevel window , add it to the
     * -- top-level table and change the event mask 
     */
    if( XmuXIsRootWindow( dflt_dpy , win_info->parent ) != -1 )
    {   Window junk_child ;

	top_level = True ;
	/*
	 * -- top-levels may have been reparented by a window manager 
	 * -- in this case we get a strange window position
	 * -- recalculate it via XTranslateCoords
	 */
	XTranslateCoordinates( dflt_dpy , 
			       win_info->window , /* source */
			       win_info->parent , /* dest */
			       win_attr.x , win_attr.y ,
			       &x , &y , 
			       &junk_child ) ;       /* not needed */
	
#ifdef ADDSIZEHINTS
	/* -- get old hints -- */
	if( XGetNormalHints( dflt_dpy , win_info->window , &size_hints ) )
	    /* -- change the appropriate fields -- */
	    size_hints.flags |= USPosition | USSize ;
	else
	    size_hints.flags = USPosition | USSize ;
	size_hints.x      = x ;
	size_hints.y      = y ;
	size_hints.width  = win_attr.width ;
	size_hints.height = win_attr.height ;
#endif
    }
    else
    {
	top_level = False ;
	x = win_attr.x ;
	y = win_attr.y ;
    }

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

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

    /* -- create the window on the new connections -- */
    XmuXdebug( debug_mux , "Duplicate window 0x%lx :\n" , 
	       win_info->window ) ;
	
    for( k = 0 ; k < n ; k++ )
    {   int    depth ;
	Visual *vis ;

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

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

	XmuXInsertID( new_dpys[ k ] , new_windows[ k ] , 
		      win_info->window , FromServer ) ;
	XmuXInsertID( new_dpys[ k ] , win_info->window , 
		      new_windows[ k ] , FromClient ) ;
#ifdef ADDSIZEHINTS
	if( top_level )
	/* -- set new hints -- */
	XSetNormalHints( new_dpys[ k ] , new_windows[ k ] , &size_hints ) ;
#endif
    XmuXdebug( debug_mux , "done\n" ) ;
    
    }
    status = XmuXSuccess ;
    /* 
     * -- duplicate window's colormap (see comments above) 
     * -- loop is in DuplicateColormap
     * -- NOTE (forget it until now) :
     * -- InputOnly windows have no colormap !
     */
    if( win_attr.class == InputOutput )
       status = DuplicateColormap( dflt_dpy , new_dpys , n ,
				   win_attr.colormap , new_windows ) ;
    
    Xfree( ( char * ) new_windows ) ;
    return( status ) ;
#undef InputOutputWindowAttributes
#undef InputOnlyWindowAttributes
}

static int
DuplicateGC( dflt_dpy , new_dpys , n , gc_info ) 

Display     *dflt_dpy , 
           **new_dpys ;
int          n ;
MUXGCInfoPtr gc_info ;

{   register GC            new_gc = NULL ;
    register int           k ;
    register unsigned long gcmask ;
    register Drawable      gc_drawable , 
                           new_drawable , 
                           dummy = None ;
    register XGCValues     newgc_val , 
                           oldgc_val ;

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

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

    oldgc_val = gc_info->gc->values ;

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

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

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

	if( !new_drawable )
	    /*
	     * -- no matching drawable found , create a dummy (a pixmap)
	     * -- withthe appropriate depth 
	     */
	    dummy = 
	    new_drawable = 
		XCreatePixmap( new_dpys[ k ] , 
			       DefaultRootWindow( new_dpys[ k ] ) ,
			       1 , 1 , 
			       XmuXMapDepth( new_dpys[ k ] ,
					     gc_info->depth , 
					     IsPixmap ) ) ;
	/*
	 * -- must map pixels
	 */
	newgc_val.foreground          = XmuXMapPixel( new_dpys[ k ] ,
						      new_drawable ,
						      oldgc_val.foreground ) ;
	newgc_val.background          = XmuXMapPixel( new_dpys[ k ] ,
						      new_drawable ,
						      oldgc_val.background ) ;
	new_gc = XCreateGC( new_dpys[ k ] , 
			    new_drawable ,
			    AllGCValuesExceptResources ,
			    &newgc_val ) ;
	XmuXdebug( debug_mux , "-->0x%lx\n" , new_gc->gid ) ;

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

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

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

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

    return( XmuXSuccess ) ;

#undef AllGCValuesExceptResources
}

static int
DuplicatePixmap( dflt_dpy , new_dpys , n , pmap_info )

Display          *dflt_dpy ,
                **new_dpys ;
int               n ;
MUXPixmapInfoPtr  pmap_info ;

{            XImage  *image ;
             char    *im_data ; 
    register GC       put_gc ;
    register Drawable pmap_drawable , 
                      new_drawable ;
    register Pixmap   new_pixmap ;
    Window            ret_root ;
    int               k ,
                      ret_x , 
                      ret_y ,
                      format ,
	              data_bytes ;
    unsigned int      ret_width ,
                      ret_height ,
                      ret_border_width ,
                      ret_depth ;

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

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

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

	if( put_gc == NULL )
	{
	    XmuXErrorF( "Can't create new gc on connection %d!\n" ,
		         ConnectionNumber( new_dpys[ k ] ) ) ;
	    return( XmuXFailure ) ;
	}
	/* -- only map "real" pixmaps ( depth > 1 ) ! -- */
	if( ret_depth != 1 && do_map_pmap )
	{ int row , col ; 
	  long before , after ;
	  if( n > 1 )
	      bcopy( im_data , image->data , data_bytes ) ;
	  /*
	   * -- must do a lot of XmuXMapPixel --> sort the pixel map 
	   * -- (i.e. use binary search instead of linear search 
	   * -- in XmuXMapPixel)
	   */
	  if( ( ret_width * ret_height > HUGE_PIXMAP ) && do_sort )
	      XmuXSortPixelMaps( new_dpys[ k ] ) ;

	  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 :
     * -- -->allocate all windows in a first scan (to find a matching
     * --    drawable if the referenced has gone).
     * -- -->using the default values on creation.
     * -- -->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 the connection(s))
     */
    
    dpy_list = CreateDpyNameList( data + 1 , &n ) ;

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

		if( XmuXReplicate( dpy , dpy_list , n , &fds , xTrue ) == XmuXSuccess )
		    XmuXAddHosts( dpy , dpy_list , fds , n ) ;
		Xfree( ( char * ) fds ) ;
		
	    }
	    break ;
        case MuxRemove :
	        XmuXRemoveHosts( dpy , dpy_list , n ) ;
	    break ;
	default :
	    break ;
        }
    }	
    FreeDpyNames( dpy_list , n ) ;
}

int
XmuXHandleMessage( dpy , event ) 

Display *dpy ;
XEvent  *event ;

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

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

    case XMUXDebug :

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

Bool
XmuXMustDoSomething( dpy )

Display *dpy ;

{

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

	return( XmuXQLength( dpy ) > 0 ) ;
}

void
XmuXDoSomething( dpy ) 

Display *dpy ;

{   _XQEvent *qevent ;
    register Display *dflt_dpy ;

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

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

XmuXFlushGC( dpy , gid , window )

Display *dpy ;
XID      gid ;     /* -- NOTE : ID's are NOT mapped !!! -- */
Window   window ;

{   register MUXGCPixelsInfoPtr gcpix_info ;
             MUXGCPixelsInfoPtr GetGCPixelsInfo( ) ;
    register int mapped = False ;

    /* 
     * -- Here's the point where we weaken our WYSIWIS ( What you see is
     * -- what I see ) to HWYSIWIS ( Hope WYSIWIS ). 
     * -- XmuXFlushGC is called before multiplexing graphic requests in 
     * -- order to map GC's pixel values using the colormap that is
     * -- referenced by the "window" . In fact, nearly all graphic requests
     * -- can operate on Drawables; so what shall we do if the "window" is
     * -- a pixmap ? 
     * -- We do it this way : 
     * -- We hope our application is a polite one that creates "real" pixmaps
     * -- (depth > 1) with the window where it is used with; or to put it
     * -- in a "politness rule" : "Don't use the same pixmap with (windows 
     * -- that use) different colormaps!"
     * -- We hope our application will follow this rule: if "window" is a
     * -- pixmap, we retrieve the drawable (and hope that it is a window!)
     * -- the pixmap was created with and use this as a reference to the 
     * -- "right" colormap !
     */
    if( XmuXUsesPrivateCmaps( dpy ) && XmuXIsPixmap( dpy , window ) )
    {   register Display *dflt_dpy ;
	/*
	 * -- XmuXFlushGC is called with a multiplexed display, but the
	 * -- information we need here is stored in the default display's
	 * -- resource list 
	 */
	dflt_dpy = XmuXPrimaryDisplayFromDisplay( dpy ) ;
	/* -- window ID is not MAPPED !-- */
	window = XmuXGetPixmapDrawable( dflt_dpy , window ) ;
    }
     
    gcpix_info = GetGCPixelsInfo( dpy ,
				 XmuXMapID( dpy , gid , FromClient ) ) ;

    if( gcpix_info == NullGCPixelsInfoPtr )
	XmuXFatalError( "XmuXFlushGC: GC 0x%lx not found (conn %d)!\n" ,
		         gid , ConnectionNumber( dpy ) ) ;
    
    if( gcpix_info->flags & 
        ( GCFunction | GCForeground | GCBackground | GCPlaneMask ) )
    {   register xChangeGCReq *req ;
	unsigned long fg , 
	              bg ,
		      mask ;
	unsigned long values[ 3 ] , *value ;
	int           nbytes ;

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