XDPS.c   [plain text]


/*
 * XDPS.c -- implementation of low-level Xlib routines for XDPS extension
 *
 * (c) Copyright 1989-1994 Adobe Systems Incorporated.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, distribute, and sublicense this software
 * and its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notices appear in all copies and that
 * both those copyright notices and this permission notice appear in
 * supporting documentation and that the name of Adobe Systems Incorporated
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  No trademark license
 * to use the Adobe trademarks is hereby granted.  If the Adobe trademark
 * "Display PostScript"(tm) is used to describe this software, its
 * functionality or for any other purpose, such use shall be limited to a
 * statement that this software works in conjunction with the Display
 * PostScript system.  Proper trademark attribution to reflect Adobe's
 * ownership of the trademark shall be given whenever any such reference to
 * the Display PostScript system is made.
 * 
 * ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 * ADOBE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON- INFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO EVENT SHALL ADOBE BE LIABLE
 * TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  ADOBE WILL NOT
 * PROVIDE ANY TRAINING OR OTHER SUPPORT FOR THE SOFTWARE.
 * 
 * Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
 * Incorporated which may be registered in certain jurisdictions
 * 
 * Author:  Adobe Systems Incorporated
 */
/* $XFree86: xc/lib/dps/XDPS.c,v 1.5 2003/10/24 15:50:20 tsi Exp $ */

#define NEED_EVENTS
#define NEED_REPLIES

#include <stdio.h>
/* Include this first so that Xasync.h, included from Xlibint.h, can find
   the definition of NOFILE */
#include <stdlib.h>
#include <sys/param.h>
#include <X11/Xlibint.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>

#include "DPS/XDPS.h"
#include "DPS/XDPSproto.h"
#include "DPS/XDPSlib.h"
#include "DPS/dpsNXargs.h"

#include "cslibint.h"
#include "dpsassert.h"
#include "DPSCAPClient.h"

#include "publictypes.h"
#include "dpsXpriv.h"

/* === DEFINITIONS === */

#ifndef _NFILE
#define DPSMAXDISPLAYS 128
#else
#define DPSMAXDISPLAYS _NFILE
#endif /* _NFILE */

#define MajorOpCode(dpy)	(Codes[DPY_NUMBER(dpy)] ?		\
				 Codes[DPY_NUMBER(dpy)]->major_opcode : \
				 Punt())

#define DPSCAP_INITCTXTS	4  /* per display */

/* === TYPES === */

typedef Status (*PSCMProc)(Display *, XEvent *, xEvent *);

typedef struct {
    char passEvents;
    char wrapWaiting;
    char syncMask;      /* CSDPS only */
    char debugMask;     /* CSDPS only */
} DPSDisplayFlags;

/* For now DPSDisplayFlags is no larger than a pointer.  Revisit this if the
   structure grows. */

typedef int (*GenericProcPtrReturnsInt)(Display *);

typedef struct _t_DPSCAPPausedContextData {
    struct _t_DPSCAPPausedContextData *next;
    Bool paused;
    ContextXID cxid;
    unsigned int seqnum;
} DPSCAPPausedContextData;

typedef struct {
    char showSmallSizes;
    char pixMem;
} DPSCAPAgentArgs;

typedef struct {
    void (*Flush)(Display *);
    int (*Read)(Display*, char*, long);
    void (*ReadPad)(Display*, char*, long);
    Status (*Reply)(Display*, xReply*, int, Bool);
    void (*Send)(Display*, _Xconst char*, long);
} XDPSLIOProcs;

/* === GLOBALS === */

/* For debugging, allows client to force the library to use an agent */
int gForceCSDPS = 0;

/* Force all DPS NX protocol requests to flush if CSDPS */
int gAutoFlush = 1;

/* Force all NX XDPSLGiveInputs to flush independent of gAutoFlush */
int gForceFlush = 1;

/* Quick check for any paused contexts */
int gTotalPaused = 0;

/* === LOCALS === */

/* Common stuff */
static XExtCodes *Codes[DPSMAXDISPLAYS];
static int version[DPSMAXDISPLAYS];
static XDPSLEventHandler StatusProc[DPSMAXDISPLAYS];
static DPSDisplayFlags displayFlags[DPSMAXDISPLAYS];
static XDPSLEventHandler TextProc = NULL;
static XDPSLEventHandler ReadyProc[DPSMAXDISPLAYS]; /* L2-DPS/PROTO 9 */
static int NumberType[DPSMAXDISPLAYS];  /* Garbage okay after dpy closed */
static char *FloatingName[DPSMAXDISPLAYS];  /* Garbage okay after dpy closed */

/* CSDPS stuff */
static Display *ShuntMap[DPSMAXDISPLAYS];
static PSCMProc ClientMsgProc[DPSMAXDISPLAYS];
static GenericProcPtrReturnsInt AfterProcs[DPSMAXDISPLAYS];
static DPSCAPPausedContextData *PausedPerDisplay[DPSMAXDISPLAYS];
static DPSCAPAgentArgs AgentArgs[DPSMAXDISPLAYS];
static unsigned int LastXRequest[DPSMAXDISPLAYS];
static int GCFlushMode[DPSMAXDISPLAYS];

#ifdef VMS
static Display *dpys[DPSMAXDISPLAYS];
static nextDpy = 0;
#endif /* VMS */

static void DPSCAPInitGC(Display *dpy, Display *agent, GC gc);
static Status DPSCAPClientMessageProc(Display *dpy, XEvent *re, xEvent *event);
static int DPSCAPAfterProc(Display *xdpy);
static unsigned int DPSCAPSetPause(Display *xdpy, ContextXID cxid);
static Bool DPSCAPResumeContext(Display *xdpy, ContextXID cxid);
static Bool WaitForSyncProc(Display *xdpy, XEvent *event, char *arg);

static XDPSLIOProcs xlProcs = {  /* Use these for DPS/X extension */
    _XFlush,
    _XRead,
    _XReadPad,
    _XReply,
    _XSend
    };

static XDPSLIOProcs nxlProcs = { /* Use these for NX */
    N_XFlush,
    N_XRead,
    N_XReadPad,
    N_XReply,
    N_XSend
    };

/* === MACROS === */

#define IFNXSETCALL(a, x) call = ((a) != (x)) ? &nxlProcs : &xlProcs

/* === PRIVATE PROCS === */

static int Punt(void)
{
    DPSFatalProc(NULL, "Extension has not been initialized");
    exit(1);
}

#ifdef VMS
/* This is a terribly inefficient way to find a per-display index, but we
   need it till we get dpy->fd fixed in VMS%%%%%*/
static int FindDpyNum(Display *dpy)
{
int i;
for (i=0; dpys[i] != dpy ; i++)
    {
    if (i == nextDpy)
        {
        dpys[nextDpy++]=dpy;
        break;
        }
    }
return i;
}
#define DPY_NUMBER(dpy)         FindDpyNum(dpy)
#else /* VMS */
#define DPY_NUMBER(dpy)		((dpy)->fd)
#endif /* VMS */

/* === PROCEDURES === */

/* ARGSUSED */
void
XDPSLSetTextEventHandler(Display *dpy, XDPSLEventHandler proc)
{
    TextProc = proc;
}

/* ARGSUSED */
void
XDPSLCallOutputEventHandler(Display *dpy, XEvent *event)
{
    (*TextProc)(event);
}

void
XDPSLSetStatusEventHandler(Display *dpy, XDPSLEventHandler proc)
{
    StatusProc[DPY_NUMBER(dpy)] = proc;
}

void
XDPSLCallStatusEventHandler(Display *dpy, XEvent *event)
{
    (*(StatusProc[DPY_NUMBER(dpy)]))(event);
}

/* Added for L2-DPS/PROTO 9 */
void
XDPSLSetReadyEventHandler(Display *dpy, XDPSLEventHandler proc)
{
    ReadyProc[DPY_NUMBER(dpy)] = proc;
}

/* Added for L2-DPS/PROTO 9 */
void
XDPSLCallReadyEventHandler(Display *dpy, XEvent *event)
{
    (*(ReadyProc[DPY_NUMBER(dpy)]))(event);
}

/* Added for L2-DPS/PROTO 9 */
int
XDPSLGetVersion(Display *dpy)
{
    return(version[DPY_NUMBER(dpy)]);
}
/* See CSDPS additions for XDPSLSetVersion */

void
XDPSLInitDisplayFlags(Display *dpy)
{
    int d = DPY_NUMBER(dpy);
    displayFlags[d].wrapWaiting = False;

    /* Instead of explicitly setting the pass-event flag, rely upon the fact
       that it gets initialized to 0 by the compiler.  This means that you
       can set the event delivery mode on a display before creating any
       contexts, which is a Good Thing */
}

XExtCodes *XDPSLGetCodes(Display *dpy)
{
    return Codes[DPY_NUMBER(dpy)];
}

/* ARGSUSED */
static int
CloseDisplayProc(Display *dpy, XExtCodes *codes)
{
    /* This proc is for native DPS/X only, not CSDPS */
    Codes[DPY_NUMBER(dpy)] = NULL;
    /* Clear list */
    XDPSPrivZapDpy(dpy);
#ifdef VMS
    dpys[DPY_NUMBER(dpy)] = NULL;
 /*%%%%Temp till we fix dpy->fd*/
#endif /* VMS */
    return 0;	/* return-value is ignored */
}

Bool
XDPSLGetPassEventsFlag(Display *dpy)
{
    return displayFlags[DPY_NUMBER(dpy)].passEvents;
}

void
XDPSLSetPassEventsFlag(Display *dpy, Bool flag)
{
    displayFlags[DPY_NUMBER(dpy)].passEvents = flag;
}

Bool
XDPSLGetWrapWaitingFlag(Display *dpy)
{
    return displayFlags[DPY_NUMBER(dpy)].wrapWaiting;
}

void
XDPSLSetWrapWaitingFlag(Display *dpy, Bool flag)
{
    displayFlags[DPY_NUMBER(dpy)].wrapWaiting = flag;
}

static Status 
ConvertOutputEvent(Display *dpy, XEvent *ce, xEvent *we)
{
    register PSOutputEvent *wireevent = (PSOutputEvent *) we;
    register XDPSLOutputEvent *clientevent = (XDPSLOutputEvent *) ce;
    
    clientevent->type = wireevent->type & 0x7f;
    clientevent->serial = _XSetLastRequestRead(dpy,
					       (xGenericReply *)wireevent);
    clientevent->send_event = (wireevent->type & 0x80) != 0;
    clientevent->display = dpy;
    clientevent->cxid = wireevent->cxid;
    clientevent->length = wireevent->length;
    bcopy((char *) wireevent->data, clientevent->data, wireevent->length);
    if (TextProc && !XDPSLGetPassEventsFlag(dpy)) {
	(*TextProc)((XEvent *) clientevent);
	return False;
    }
    return True;
}

static Status 
ConvertStatusEvent(Display *dpy, XEvent *ce, xEvent *we)
{
    register PSStatusEvent *wireevent = (PSStatusEvent *) we;
    register XDPSLStatusEvent *clientevent = (XDPSLStatusEvent *) ce;
    
    clientevent->type = wireevent->type & 0x7f;
    clientevent->serial = _XSetLastRequestRead(dpy,
					       (xGenericReply *)wireevent);
    clientevent->send_event = (wireevent->type & 0x80) != 0;
    clientevent->display = dpy;
    clientevent->cxid = wireevent->cxid;
    clientevent->status = wireevent->status;
    if (StatusProc[DPY_NUMBER(dpy)] && !XDPSLGetPassEventsFlag(dpy)) {
	(*(StatusProc[DPY_NUMBER(dpy)]))((XEvent *) clientevent);
	return False;
    }
    return True;
}

/* Added for L2-DPS/PROTO 9 */
static Status
ConvertReadyEvent(Display *dpy, XEvent *ce, xEvent *we)
{
    register PSReadyEvent *wireevent = (PSReadyEvent *) we;
    register XDPSLReadyEvent *clientevent = (XDPSLReadyEvent *) ce;
    
    clientevent->type = wireevent->type & 0x7f;
    clientevent->serial = _XSetLastRequestRead(dpy,
					       (xGenericReply *)wireevent);
    clientevent->send_event = (wireevent->type & 0x80) != 0;
    clientevent->display = dpy;
    clientevent->cxid = wireevent->cxid;
    clientevent->val[0] = wireevent->val1;
    clientevent->val[1] = wireevent->val2;
    clientevent->val[2] = wireevent->val3;
    clientevent->val[3] = wireevent->val4;
    if (ReadyProc[DPY_NUMBER(dpy)] && !XDPSLGetPassEventsFlag(dpy)) {
	(*(ReadyProc[DPY_NUMBER(dpy)]))((XEvent *) clientevent);
	return False;
    }
    return True;
}

/* Added for L2-DPS/PROTO 9 */
/* ARGSUSED */

static int
CatchBadMatch(Display *dpy, xError *err, XExtCodes *codes, int *ret_code)
{
    if (err->errorCode == BadMatch)
        {
	*ret_code = 0;
	return 1;  /* suppress error */
	}
    else
        {
	*ret_code = 1;
	return 0;  /* pass error along */
	}
}


int
XDPSLInit(
    Display *dpy,
    int *numberType,		/* RETURN */
    char **floatingName)	/* RETURN: CALLER MUST NOT MODIFY OR FREE! */
{
    XExtCodes *codes = (XExtCodes *)NULL;
    register xPSInitReq *req;
    xPSInitReply rep;
    char *ptr;
    int first_event;
    
    {
        char *ddt;

        if ((ddt = getenv("DPSNXOVER")) != NULL) {
            gForceCSDPS = (*ddt == 't' || *ddt == 'T');
            if (gForceCSDPS)
                DPSWarnProc(NULL, "*** USING DPS NX ***");
        }
    }
    
    if ((codes = Codes[DPY_NUMBER(dpy)]) != NULL) {
	if (numberType)
	    *numberType = NumberType[DPY_NUMBER(dpy)];
	if (floatingName)
	    *floatingName = FloatingName[DPY_NUMBER(dpy)];
	return codes->first_event;
    } else {
        if (gForceCSDPS)
            goto try_dps_nx;
	codes = XInitExtension(dpy, DPSNAME);
	if (codes == NULL) {
	    /* try DEC UWS 2.2 server */
	    codes = XInitExtension(dpy, DECDPSNAME);
try_dps_nx:
            if (codes == NULL) {
                int myNumberType;
		char *myFloatingName;
		
	        first_event = CSDPSInit(dpy, &myNumberType, &myFloatingName);
		NumberType[DPY_NUMBER(dpy)] = myNumberType;
		FloatingName[DPY_NUMBER(dpy)] = myFloatingName;
		if (numberType)
		    *numberType = myNumberType;
		if (floatingName)
		    *floatingName = myFloatingName;
		return first_event;
	    }
	}
	Codes[DPY_NUMBER(dpy)] = codes;
	ShuntMap[DPY_NUMBER(dpy)] = dpy;
	/* set procs for native DPS/X */
	XESetCloseDisplay(dpy, codes->extension, CloseDisplayProc);
	XESetWireToEvent(dpy, codes->first_event + PSEVENTOUTPUT,
			 ConvertOutputEvent);
	XESetWireToEvent(dpy, codes->first_event + PSEVENTSTATUS,
			 ConvertStatusEvent);
	XESetWireToEvent(dpy, codes->first_event + PSEVENTREADY,
			 ConvertReadyEvent);
	first_event = codes->first_event;
    }

    /* We have to handle a BadMatch error, in the case where
       the client has a later (higher) version of
       the protocol than the server */
    {
    int (*oldErrorProc)(Display*, xError*, XExtCodes*, int*);
    int libVersion;
    Bool doneIt;
    
    XSync(dpy, False);
    LockDisplay(dpy);
    oldErrorProc = XESetError(dpy, codes->extension, CatchBadMatch);
    libVersion = DPSPROTOCOLVERSION;
    doneIt = False;
    while (libVersion >= DPSPROTO_OLDEST)
        {
	GetReq(PSInit, req);
	req->reqType = MajorOpCode(dpy);
	req->dpsReqType = X_PSInit;
	req->libraryversion = libVersion;
        if (_XReply(dpy, (xReply *) &rep, 0, xFalse))
	    {
	    doneIt = True;
	    break;
	    }
	/* otherwise, try previous version */
	--libVersion;
        }
    oldErrorProc = XESetError(dpy, codes->extension, oldErrorProc);
    if (!doneIt)
        {
	DPSFatalProc(NULL, "Incompatible protocol versions");
	exit(1);	
	}
	
    /* NOTE *************************************************
       We made a boo-boo in the 1007.2 and earlier versions of
       our X server glue code.  Instead of sending a
       BadMatch error if the client's version is newer (higher)
       than the server's, it just replies with success.  We
       could test for that situation here by looking at
       rep.serverversion, but it turns out that we don't need
       to do anything special.  Since rep.serverversion gets
       assigned to our version[] array, it is as if we handled
       the BadMatch correctly.  Just for safety's sake, we'll
       do some bulletproofing here instead. 
       Fixes 2ps_xdps BUG #6 */
       
    else if (rep.serverversion < DPSPROTO_OLDEST 
      || rep.serverversion > DPSPROTOCOLVERSION)
        {
	DPSFatalProc(NULL, "Server replied with bogus version");
	exit(1);	
	}
    }

    version[DPY_NUMBER(dpy)] = rep.serverversion;
    NumberType[DPY_NUMBER(dpy)] = rep.preferredNumberFormat;
    if (numberType)
	*numberType = rep.preferredNumberFormat;

    ptr = (char *) Xmalloc(rep.floatingNameLength + 1);
    _XReadPad(dpy, ptr, rep.floatingNameLength);
    ptr[rep.floatingNameLength] = 0;
    FloatingName[DPY_NUMBER(dpy)] = ptr;
    if (floatingName)
	*floatingName = ptr;

    UnlockDisplay(dpy);
    SyncHandle();
    return first_event;
}




static void CopyColorMapsIntoCreateContextReq(
    xPSCreateContextReq *req,
    XStandardColormap *colorcube,
    XStandardColormap *grayramp)
{
    req->cmap = 0;
    if (colorcube != NULL) {
        req->cmap = colorcube->colormap;
	req->redmax = colorcube->red_max;
	req->redmult = colorcube->red_mult;
	req->greenmax = colorcube->green_max;
	req->greenmult = colorcube->green_mult;
	req->bluemax = colorcube->blue_max;
	req->bluemult = colorcube->blue_mult;
	req->colorbase = colorcube->base_pixel;
    }
    else {
	req->redmult = 0;
	/* The rest of this shouldn't be necessary, but there are some
	   servers out there that erroneously check the other fields
	   even when redmult is 0 */
        req->redmax = 0;
        req->greenmult = 0;
        req->greenmax = 0;
        req->bluemult = 0;
        req->bluemax = 0;
        req->colorbase = 0;
    }

    if (grayramp != NULL) {
	req->cmap = grayramp->colormap;
	req->graymax = grayramp->red_max;
	req->graymult = grayramp->red_mult;
	req->graybase = grayramp->base_pixel;
    }
    else req->graymult = 0;
}






/* ARGSUSED */
ContextXID
XDPSLCreateContextAndSpace(
    register Display *xdpy,
    Drawable draw,
    GC gc,
    int x, int y,
    unsigned int eventMask,
    XStandardColormap *grayRamp,
    XStandardColormap *colorCube,
    unsigned int actual,
    ContextPSID *cpsid,		/* RETURN */
    SpaceXID *sxid,		/* RETURN */
    Bool secure)                /* Added for L2-DPS/PROTO 9 */
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    ContextXID cxid;
    register xPSCreateContextReq *req;  /* Same struct for CreateSecureContext */
    xPSCreateContextReply rep;
    XStandardColormap defColorcube, defGrayramp;
    XStandardColormap *requestCube, *requestRamp;
    int index;
    XDPSLIOProcs *call;
    
    if (grayRamp == NULL && colorCube == NULL) return(None);

    if (secure && version[dpyix] < DPSPROTO_V09)
        return(None);                 /* No secure contexts before PROTO 9 */

    /* Index gets encoded as follows:
     *
     *  0  grayRamp = Default,	   colorCube = Default
     *  1  grayRamp = non-Default, colorcube = Default
     *  2  grayRamp = Default,	   colorcube = non-Default
     *  3  grayRamp = non-Default, colorcube = non-Default
     *
     */
    index = ((grayRamp == DefaultStdCMap || grayRamp == NULL) ? 0 : 1) +
	(colorCube == DefaultStdCMap ? 0 : 2);

    switch (index)
	{
	default:
	case 0: /* Both are default */
	    XDPSGetDefaultColorMaps(xdpy, (Screen *) NULL, draw,
				    &defColorcube, &defGrayramp);
	    requestCube = &defColorcube;
	    requestRamp = &defGrayramp;
	    break;

	case 1: /* gray specified, Color default */
	    XDPSGetDefaultColorMaps(xdpy, (Screen *) NULL, draw,
				    &defColorcube, (XStandardColormap *) NULL);
	    requestCube = &defColorcube;
	    requestRamp = grayRamp;
	    break;

	case 2: /* gray default, Color specified */
	    XDPSGetDefaultColorMaps(xdpy, (Screen *) NULL, draw,
				    (XStandardColormap *) NULL, &defGrayramp);
	    requestCube = colorCube;
	    requestRamp = &defGrayramp;
	    break;

	case 3: /* Both specified */
	    requestCube = colorCube;
	    requestRamp = grayRamp;
	    break;
	}

    if (gc != NULL)
        XDPSLFlushGC(xdpy, gc);
    if (dpy != xdpy)
        {
        int syncMask = displayFlags[dpyix].syncMask;
        
	/* Don't worry about pauses here, since we are just
	   now creating the context! */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSCreateContext, req);
    CopyColorMapsIntoCreateContextReq(req, requestCube, requestRamp);

    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = (secure) ? X_PSCreateSecureContext : X_PSCreateContext;
    req->x = x;
    req->y = y;
    req->drawable = draw;
    req->gc = (gc != NULL) ? XGContextFromGC(gc) : None;
    cxid = req->cxid = XAllocID(xdpy);
    req->sxid = XAllocID(xdpy);	  
    if (sxid)
	*sxid = req->sxid;
    req->eventmask = 0;		/* %%% */
    req->actual = actual;
    IFNXSETCALL(dpy, xdpy);
    (void) (*call->Reply) (dpy, (xReply *)&rep, 0, xTrue);

    if (cpsid)
	*cpsid = (int)rep.cpsid;

    UnlockDisplay(dpy);
    /* If the context creation succeeded and we are CSDPS, send GC values */    
    if (rep.cpsid && xdpy != dpy && gc != NULL)
        {
        DPSCAPInitGC(xdpy, dpy, gc);
        }
    SyncHandle();
    
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
    return (cxid);
}


/* ARGSUSED */
ContextXID
XDPSLCreateContext(
    register Display *xdpy,
    SpaceXID sxid,
    Drawable draw,
    GC gc,
    int x, int y,
    unsigned int eventMask,
    XStandardColormap *grayRamp,
    XStandardColormap *colorCube,
    unsigned int actual,
    ContextPSID *cpsid,		/* RETURN */
    Bool secure)                /* L2-DPS/PROTO 9 addition */
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSCreateContextReq *req;
    xPSCreateContextReply rep;
    ContextXID cxid;			/* RETURN */
    XStandardColormap defColorcube, defGrayramp;
    XStandardColormap *requestCube, *requestRamp;
    int index;
    XDPSLIOProcs *call;

    if (secure && version[dpyix] < DPSPROTO_V09)
        return(None);                /* No secure contexts before PROTO 9 */

    /* Index gets encoded as follows:
     *
     *  0  grayRamp = Default,	   colorCube = Default
     *  1  grayRamp = non-Default, colorcube = Default
     *  2  grayRamp = Default,	   colorcube = non-Default
     *  3  grayRamp = non-Default, colorcube = non-Default
     *
     *  Note that only the first or last case should ever happen.
     */
    index = ((grayRamp == DefaultStdCMap) ? 0 : 1) +
	((colorCube == DefaultStdCMap) ? 0 : 2);

    switch (index)
	{
	default:
	case 0: /* Both are default */
	    XDPSGetDefaultColorMaps(xdpy, (Screen *) NULL, draw,
				    &defColorcube, &defGrayramp);
	    requestCube = &defColorcube;
	    requestRamp = &defGrayramp;
	    break;

	case 1: /* gray specified, Color default */
	    XDPSGetDefaultColorMaps(xdpy, (Screen *) NULL, draw,
				    &defColorcube, (XStandardColormap *) NULL);
	    requestCube = &defColorcube;
	    requestRamp = grayRamp;
	    break;

	case 2: /* gray default, Color specified */
	    XDPSGetDefaultColorMaps(xdpy, (Screen *) NULL, draw,
				    (XStandardColormap *) NULL, &defGrayramp);
	    requestCube = colorCube;
	    requestRamp = &defGrayramp;
	    break;

	case 3: /* Both specified */
	    requestCube = colorCube;
	    requestRamp = grayRamp;
	    break;
	}


    if (gc != NULL)
        XDPSLFlushGC(xdpy, gc);
    if (dpy != xdpy)
        {
        int syncMask = displayFlags[dpyix].syncMask;
        
	/* Don't worry about pauses here, since we are
	   just now creating this context! */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSCreateContext, req);
    CopyColorMapsIntoCreateContextReq(req, requestCube, requestRamp);

    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = (secure) ? X_PSCreateSecureContext : X_PSCreateContext;
    req->x = x;
    req->y = y;
    req->drawable = draw;
    req->gc = (gc != NULL) ? XGContextFromGC(gc) : None;
    cxid = req->cxid = XAllocID(xdpy);
    req->sxid = sxid;
    req->actual = actual;

    IFNXSETCALL(dpy, xdpy);
    (void) (*call->Reply) (dpy, (xReply *)&rep, 0, xTrue);
    if (cpsid)
	*cpsid = (int)rep.cpsid;

    UnlockDisplay(dpy);
    /* If the context creation succeeded and we are CSDPS, send GC values */
    if (rep.cpsid && xdpy != dpy && gc != NULL)
        {
        DPSCAPInitGC(xdpy, dpy, gc);
        }

    SyncHandle();
    
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
    return cxid;
}

SpaceXID
XDPSLCreateSpace(Display *xdpy)
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSCreateSpaceReq *req;
    SpaceXID sxid;

    LockDisplay(dpy);

    NXMacroGetReq(PSCreateSpace, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSCreateSpace;
    sxid = req->sxid = XAllocID(xdpy);

    UnlockDisplay(dpy);
    SyncHandle();
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
    return sxid;
}



/*
 * I'm not sure how portable my coalescing code is, so I've provided the
 * below define.  If it turns out this code just breaks somewhere, you
 * can simply undefine COALESCEGIVEINPUT, and then everything will work
 * (but slower).  6/16/89 (tw)
 */

#define COALESCEGIVEINPUT

void
XDPSLGiveInput(Display *xdpy, ContextXID cxid, char *data, int length)
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSGiveInputReq *req;
    Bool didFlush = False;

    if (dpy != xdpy)
        {
        register int syncMask = displayFlags[dpyix].syncMask;
	
	if (syncMask & DPSCAP_SYNCMASK_RECONCILE)
	    {
	    XDPSLReconcileRequests(xdpy, cxid);
	    didFlush = True;
	    }
	
	/* If this context got paused, no matter how, ignore
	   mode and resume it */
	if (gTotalPaused && DPSCAPResumeContext(xdpy, cxid))
	    {
	    /* xdpy was flushed by DPSCAPResumeContext */
	    if (!didFlush)
	        {
	        N_XFlush(dpy);
		didFlush = True;
		}
	    }
	else if (syncMask & DPSCAP_SYNCMASK_SYNC)
	    {
	    didFlush = True;
            XSync(xdpy, False);
	    }
        }
    LockDisplay(dpy);
    
#ifdef COALESCEGIVEINPUT
    req = (xPSGiveInputReq *) dpy->last_req;
    if (req->reqType == MajorOpCode(xdpy)
	  && req->dpsReqType == X_PSGiveInput
	  && req->cxid == cxid
	  && dpy->bufptr + length + 3 < dpy->bufmax) {
	bcopy(data, ((char *) req) + sizeof(xPSGiveInputReq) + req->nunits,
	      length);
	req->nunits += length;
	req->length = (sizeof(xPSGiveInputReq) + req->nunits + 3) >> 2;
	dpy->bufptr = dpy->last_req + sizeof(xPSGiveInputReq) +
	    ((req->nunits + 3) & ~3);
    } else
#endif /* COALESCEGIVEINPUT */
    {
        int flushOnce = 1;
	int maxedOutLen = xdpy->max_request_size - sizeof(xPSGiveInputReq) - 4;
	int nunits;
	
	/* We have the rare opportunity to chop up a buffer that is larger
	   than the max request size into separate requests, unlike
	   most other X requests (such as DrawText).  The -4 is to
	   force these maxed out requests to be less than the maximum
	   padding that would ever be needed, and to minimize padding
	   in the case where the input buffer is several times
	   larger than max request length. */
	   
        nunits = maxedOutLen;
	do {
	    if (length < maxedOutLen)
	        nunits = length;  /* Normal size block */
	    NXMacroGetReq(PSGiveInput, req);
	    req->reqType = MajorOpCode(xdpy);
	    req->dpsReqType = X_PSGiveInput;
	    req->cxid = cxid;
	    req->nunits = nunits;
	    req->length += ((nunits + 3) >> 2);
	    if (dpy != xdpy) {
	        if (flushOnce && !didFlush) {
		    LockDisplay(xdpy);
		    _XFlush(xdpy);
		    UnlockDisplay(xdpy);
		    flushOnce = 0;
		}
	        NXProcData(dpy, (char *) data, nunits);
	    } else
	        {Data(dpy, (char *) data, nunits);}
	    data += nunits;
	    length -= nunits;
	} while (length);
    }

    /* In the NX case (didFlush is always False for the non-NX case),
       the xdpy may have been flushed, but there is stuff left
       buffered in dpy (NX connection).  We can't leave the stuff
       there, since we may never call a DPS routine again.  Until
       we can be notified about xdpy being flushed, we have to
       clear out the dpy buffer after we cleared out the xdpy
       buffer (didFlush == True). */

    if (dpy != xdpy
      && dpy->bufptr != dpy->buffer
      && (gForceFlush || didFlush))
	N_XFlush(dpy);

    UnlockDisplay(dpy);
    SyncHandle();
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
}


int
XDPSLGetStatus(Display *xdpy, ContextXID cxid)
{
    int dpyix;
    Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSGetStatusReq *req;
    xPSGetStatusReply rep;
    XDPSLIOProcs *call;
    
    if (dpy != xdpy)
        {
        register int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSGetStatus, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSGetStatus;
    req->cxid = cxid;
    req->notifyIfChange = 0;

    IFNXSETCALL(dpy, xdpy);
    if (! (*call->Reply)(dpy, (xReply *)&rep, 0, xTrue))
	rep.status = PSSTATUSERROR;
    UnlockDisplay(dpy);
    SyncHandle();
    /* For CSDPS, guarantee that status events arrive just like DPS/X */
    if (dpy != xdpy)
        {
	XDPSLSync(xdpy);
	LastXRequest[dpyix] = XNextRequest(xdpy)-1;
	}
    return (int) rep.status;
}

void
XDPSLDestroySpace(Display *xdpy, SpaceXID sxid)
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSDestroySpaceReq *req;

    if (dpy != xdpy)
        {
        int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSDestroySpace, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSDestroySpace;
    req->sxid = sxid;

    if (gAutoFlush && dpy != xdpy)
        {
        N_XFlush(dpy);
        }
    UnlockDisplay(dpy);
    SyncHandle();
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
}


void
XDPSLReset(Display *xdpy, ContextXID cxid) 
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSResetReq *req;

    if (dpy != xdpy)
        {
        register int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSReset, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSReset;
    req->cxid = cxid;

    if (gAutoFlush && dpy != xdpy)
        {
        N_XFlush(dpy);
        }
    UnlockDisplay(dpy);
    SyncHandle();
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
}

void
XDPSLNotifyContext(
    Display *xdpy, 
    ContextXID cxid, 
    int ntype)		  /* should this be an enum?? %%% */
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSNotifyContextReq *req;

    if (dpy != xdpy)
        {
        register int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSNotifyContext, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSNotifyContext;
    req->cxid = cxid;
    req->notifyType = ntype;
    
    if (dpy != xdpy)
        {
        N_XFlush(dpy);  /* THIS IS CRITICAL TO AVOID HANGING! */
        }

    UnlockDisplay(dpy);
    SyncHandle();

    if (dpy != xdpy)
        {
        if (ntype == PSKILL)
            XDPSLCleanContext(xdpy, cxid);
	LastXRequest[dpyix] = XNextRequest(xdpy)-1;
	}
}


ContextXID
XDPSLCreateContextFromID(
    Display *xdpy, 
    ContextPSID cpsid, 
    SpaceXID *sxid)		/* RETURN */
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSCreateContextFromIDReq *req;
    xPSCreateContextFromIDReply rep;
    ContextXID cxid;
    XDPSLIOProcs *call;

    if (dpy != xdpy)
        {
        int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSCreateContextFromID, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSCreateContextFromID;
    req->cpsid = cpsid;
    cxid = req->cxid = XAllocID(xdpy);
    
    IFNXSETCALL(dpy, xdpy);
    (void) (*call->Reply) (dpy, (xReply *)&rep, 0, xTrue);
    if (sxid)
	*sxid = (int)rep.sxid;

    UnlockDisplay(dpy);
    SyncHandle();
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
    return(cxid);
}


/* Returns 1 on success, 0 on failure (cpsid not a valid context). */

Status
XDPSLIDFromContext(
    Display *xdpy,
    ContextPSID cpsid, 
    ContextXID *cxid,			/* RETURN */
    SpaceXID *sxid)			/* RETURN */
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSXIDFromContextReq *req;
    xPSXIDFromContextReply rep;
    XDPSLIOProcs *call;

    if (dpy != xdpy)
        {
        int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSXIDFromContext, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSXIDFromContext;
    req->cpsid = cpsid;
    
    IFNXSETCALL(dpy, xdpy);
    (void) (*call->Reply) (dpy, (xReply *)&rep, 0, xTrue);
    *sxid = (int)rep.sxid;
    *cxid = (int)rep.cxid;

    UnlockDisplay(dpy);
    SyncHandle();

    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
    return((Status)(*sxid != None && *cxid != None));
}


ContextPSID
XDPSLContextFromXID(Display *xdpy, ContextXID cxid)
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSContextFromXIDReq *req;
    xPSContextFromXIDReply rep;
    XDPSLIOProcs *call;

    if (dpy != xdpy)
        {
        int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSContextFromXID, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSContextFromXID;
    req->cxid = cxid;
    
    IFNXSETCALL(dpy, xdpy);
    (void) (*call->Reply) (dpy, (xReply *)&rep, 0, xTrue);

    UnlockDisplay(dpy);
    SyncHandle();

    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
    return (int)rep.cpsid;
}


void
XDPSLSetStatusMask(
    Display *xdpy,
    ContextXID cxid,
    unsigned int enableMask,
    unsigned int disableMask,
    unsigned int nextMask)
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSSetStatusMaskReq *req;

    if (dpy != xdpy)
        {
        register int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSSetStatusMask, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSSetStatusMask;
    req->cxid = cxid;
    req->enableMask = enableMask;
    req->disableMask = disableMask;
    req->nextMask = nextMask;

    if (gAutoFlush && dpy != xdpy)
        {
        N_XFlush(dpy);
        }
    UnlockDisplay(dpy);
    SyncHandle();
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
}


#ifdef VMS
/*
 * _XReadPad - Read bytes from the socket taking into account incomplete
 * reads.  If the number of bytes is not 0 mod 32, read additional pad
 * bytes. This routine may have to be reworked if int < long.
 */
 
/* This is really in xlib, but is not in the sharable image transfer vector
 * so I am copying it here for now.  BF

The following notice applies only to the functions
_XReadPad and XFlush

Copyright 1985, 1986, 1987, 1988, 1989 by the
Massachusetts Institute of Technology

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of M.I.T. not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission.  M.I.T. makes no representations about the
suitability of this software for any purpose.  It is provided "as is"
without express or implied warranty.

*/  
void
_XReadPad (Display *dpy, char *data, long size)
{
	static int padlength[4] = {0,3,2,1};
	register long bytes_read;
	char pad[3];
 
	CheckLock(dpy);
	if (size == 0) return;
	_XRead( dpy, data, size );
	if ( padlength[size & 3] ) {
	    _XRead( dpy, pad, padlength[size & 3] );
	}

}
#endif /* VMS */

/* _____________ LEVEL 2 DPS/PROTOCOL 9 ADDITIONS _____________ */

void
XDPSLNotifyWhenReady(
    Display *xdpy,
    ContextXID cxid,
    int val[4])
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xPSNotifyWhenReadyReq *req;

    if (version[dpyix] < DPSPROTO_V09)
        {
	DPSWarnProc(NULL, "Attempted use of XDPSLNotifyWhenReady with incompatible server ignored");
        return;                                   /* PROTO 9 or later only */
	}
	
    if (dpy != xdpy)
        {
        register int syncMask = displayFlags[dpyix].syncMask;
        
	if (syncMask & DPSCAP_SYNCMASK_RECONCILE)
	    XDPSLReconcileRequests(xdpy, cxid);
	
	/* If this context got paused, no matter how, ignore
	   mode and resume it */
	if (gTotalPaused && DPSCAPResumeContext(xdpy, cxid))
	    {
	    /* xdpy was flushed by DPSCAPResumeContext */
	    if (gAutoFlush)
	        N_XFlush(dpy);
	    }
	else if (syncMask & DPSCAP_SYNCMASK_SYNC)
            XSync(xdpy, False);
        }
    LockDisplay(dpy);

    NXMacroGetReq(PSNotifyWhenReady, req);
    req->reqType = MajorOpCode(xdpy);
    req->dpsReqType = X_PSNotifyWhenReady;
    req->cxid = cxid;
    req->val1 = val[0];
    req->val2 = val[1];
    req->val3 = val[2];
    req->val4 = val[3];

    if (gAutoFlush && dpy != xdpy)
        {
        N_XFlush(dpy);
        }
    UnlockDisplay(dpy);
    SyncHandle();
    if (dpy != xdpy)
        LastXRequest[dpyix] = XNextRequest(xdpy)-1;
}

XDPSLPSErrors
XDPSLTestErrorCode(Display *dpy, int ecode)
{
    XExtCodes *c = XDPSLGetCodes(dpy);

    if (c == NULL)
        return not_pserror;

    switch (ecode - c->first_error)
        {
	case PSERRORBADCONTEXT: return(pserror_badcontext);
	case PSERRORBADSPACE: return(pserror_badspace);
	case PSERRORABORT: 
	    if (version[DPY_NUMBER(dpy)] < DPSPROTO_V09)
	        return(not_pserror);
	    else
	        return(pserror_abort);
	default: return(not_pserror);
        }
}

/* _____________ CLIENT SIDE DPS ADDITIONS _____________ */

/* === NEW HOOKS INTO XDPS === */

void
XDPSLSetVersion(Display *dpy, unsigned ver)
{
    version[DPY_NUMBER(dpy)] = ver;
}

void
XDPSLSetCodes(Display *dpy, XExtCodes *codes)
{
    Codes[DPY_NUMBER(dpy)] = codes;
}

Display *
XDPSLGetShunt(Display *dpy_in)
{
    return(ShuntMap[DPY_NUMBER(dpy_in)]);
}

void
XDPSLSetShunt(Display *dpy_in, Display *dpy_out)
{
    ShuntMap[DPY_NUMBER(dpy_in)] = dpy_out;
}

int
XDPSLGetSyncMask(Display *dpy)
{
    return (int)displayFlags[DPY_NUMBER(dpy)].syncMask;
}

void
XDPSLSetSyncMask(Display *dpy, int mask)
{
    displayFlags[DPY_NUMBER(dpy)].syncMask = (char)mask;
    gForceFlush = (mask & DPSCAP_SYNCMASK_RECONCILE);
}

void
XDPSLFlush(Display *xdpy)
{
    register Display *dpy = ShuntMap[DPY_NUMBER(xdpy)];

    _XFlush(xdpy);
    if (dpy != xdpy)
        N_XFlush(dpy);
}

void
XDPSLSyncGCClip(Display *xdpy, GC gc)
{
    register unsigned long oldDirty;
    register int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];

    /* We DON'T want to notice all gc changes, just the clip */
    oldDirty = gc->dirty;
    gc->dirty = (GCClipXOrigin|GCClipYOrigin);
    XDPSLFlushGC(xdpy, gc);
    gc->dirty = oldDirty;
    if (dpy == xdpy || gNXSyncGCMode != 1) /* 1 means sync always */
        {
	/* For DPS NX and SLOW mode, flushing the gc cache has
	the side-effect of synching agent and server connections.
	So, to have consistent behavior, we sync for the DPS/X
	or FAST cases too. */
	
	if (GCFlushMode[dpyix] == XDPSNX_GC_UPDATES_FAST
	  || dpy == xdpy)
	    XDPSLSync(xdpy);
	}
}


#ifdef VMS
void
XDPSLSetDisplay(Display *dpy)
{
    dpys[DPY_NUMBER(dpy)] = dpy;
}
#endif /* VMS */

char *
XDPSLSetAgentName(Display *dpy, char *name, int deflt)
{
    char *old;
    
    if (gCSDPS == NULL)
        DPSCAPStartUp();
    if (deflt)
        {
        old = gCSDPS->defaultAgentName;
        gCSDPS->defaultAgentName = name;
        }
    else
        {
        old = gCSDPS->map[DPY_NUMBER(dpy)];
        gCSDPS->map[DPY_NUMBER(dpy)] = name;
        }
    return(old);
} 


void
XDPSLSetClientMessageHandler(Display *dpy)
{
    if (dpy == NULL) return;
    ClientMsgProc[DPY_NUMBER(dpy)] = XESetWireToEvent(
      dpy,
      ClientMessage,
      DPSCAPClientMessageProc);
}

void
XDPSLSetAfterProc(Display *xdpy)
{
    if (xdpy == NULL) return;
    AfterProcs[DPY_NUMBER(xdpy)] = (GenericProcPtrReturnsInt)
      XSetAfterFunction(xdpy, DPSCAPAfterProc);
    /* +++ Consider using agent->synchandler to store old proc */
}


CSDPSFakeEventTypes
XDPSLGetCSDPSFakeEventType(Display *dpy, XEvent *event)
{
    XExtCodes *codes = Codes[DPY_NUMBER(dpy)];
    XExtData *extData;
    DPSCAPData my;
    
    if (event->type != ClientMessage || codes == NULL)
        return(csdps_not);
    extData = XFindOnExtensionList(
      CSDPSHeadOfDpyExt(dpy),
      codes->extension);
    if (!extData)
        return(csdps_not);
    my = (DPSCAPData) extData->private_data;
    
    if (event->xclient.message_type == my->typePSOutput)
        return(csdps_output);
    if (event->xclient.message_type == my->typePSOutputWithLen)
        return(csdps_output_with_len);
    if (event->xclient.message_type == my->typePSStatus)
        return(csdps_status);
    if (event->xclient.message_type == my->typeNoop)
        return(csdps_noop);
    if (event->xclient.message_type == my->typePSReady)
        return(csdps_ready);
    return(csdps_not);
}

Bool
XDPSLDispatchCSDPSFakeEvent(
    Display *dpy,
    XEvent *event,
    CSDPSFakeEventTypes t)
{
    register XDPSLOutputEvent *oce;
    register DPSCAPOutputEvent *oev;
    XDPSLOutputEvent fakeOutput;
    XExtCodes *codes = Codes[DPY_NUMBER(dpy)];

    if (codes == NULL)
        return(False);

    /* Fake up an event in the client's format.  Bypasses
       extension wire-to-event conversion */
    switch (t)
        {
        case csdps_output:
            oce = &fakeOutput;
	    oev = (DPSCAPOutputEvent *)event->xclient.data.b;
	    oce->length = DPSCAP_BYTESPEROUTPUTEVENT;
	    goto samo_samo;
	case csdps_output_with_len:
	    oce = &fakeOutput;
	    oev = (DPSCAPOutputEvent *)event->xclient.data.b;
	    oce->length = oev->data[DPSCAP_DATA_LEN];
samo_samo:
	    oce->type = codes->first_event + PSEVENTOUTPUT;
	    oce->serial = event->xclient.serial;
	    oce->send_event = True;  /* ??? */
	    oce->display = dpy;
	    oce->cxid = oev->cxid;
	    bcopy((char *) oev->data, oce->data, oce->length);
	    XDPSLCallOutputEventHandler(dpy, (XEvent *) oce);
	    break;
	case csdps_status:
	    {
	    register XDPSLStatusEvent *sce;
	    register DPSCAPStatusEvent *sev;
	    XDPSLStatusEvent fakeStatus;

	    sev = (DPSCAPStatusEvent *)event->xclient.data.b;
	    sce = &fakeStatus;
	    sce->type = codes->first_event + PSEVENTSTATUS;
	    sce->serial = event->xclient.serial;
	    sce->send_event = True;  /* ??? */
	    sce->display = dpy;
	    sce->status = sev->status;
	    sce->cxid = sev->cxid;
	    XDPSLCallStatusEventHandler(dpy, (XEvent *) sce);
	    break;
	    }
	case csdps_ready:                      /* L2-DPS/PROTO 9 addition */
	    {
	    register XDPSLReadyEvent *rce;
	    XDPSLReadyEvent fakeReady;
	    
	    rce = &fakeReady;
	    rce->type = codes->first_event + PSEVENTREADY;
	    rce->serial = event->xclient.serial;
	    rce->send_event = True;
	    rce->display = dpy;
	    rce->cxid = event->xclient.data.l[0];
	    rce->val[0] = event->xclient.data.l[1];
	    rce->val[1] = event->xclient.data.l[2];
	    rce->val[2] = event->xclient.data.l[3];
	    rce->val[3] = event->xclient.data.l[4];
	    XDPSLCallReadyEventHandler(dpy, (XEvent *) rce);
	    break;
	    }
	default:
	    return(False);
        }
    return(True);
}

void
XDPSLGetCSDPSStatus(
    Display *xdpy,
    XEvent *event,
    void **ret_ctxt,
    int *ret_status)
{
    register DPSCAPStatusEvent *sev;

    /* Assert: event is ClientMessage with typePSStatus */
    sev = (DPSCAPStatusEvent *)event->xclient.data.b;
   
    if (ret_ctxt != NULL) 
        *ret_ctxt = XDPSContextFromXID(xdpy, sev->cxid);
    if (ret_status != NULL)
        *ret_status = sev->status;
}

void
XDPSLGetCSDPSReady(
    Display *xdpy,
    XEvent *event,
    void **ret_ctxt,
    int *ret_val)
{
    /* Assert: event is ClientMessage with typePSReady */
   
    if (ret_ctxt != NULL) 
        *ret_ctxt = 
	  XDPSContextFromXID(xdpy, (ContextXID)event->xclient.data.l[0]);
    if (ret_val != NULL)
        {
	ret_val[0] = event->xclient.data.l[1];
	ret_val[1] = event->xclient.data.l[2];
	ret_val[2] = event->xclient.data.l[3];
	ret_val[3] = event->xclient.data.l[4];
	}
}

void
XDPSLCAPNotify(
    Display *xdpy, 
    ContextXID cxid, 
    unsigned int ntype,		  /* should this be an enum?? %%% */
    unsigned int data,
    unsigned int extra)
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    register xCAPNotifyReq *req;
    
    if (dpy == xdpy) return;
    
    /* We _have_ to sync client and server here in order to guarantee
       correct execution sequencing.  We call this procedure alot
       to keep track of GC's going away, so this is a major 
       performance hit. */
    if (ntype == DPSCAPNOTE_FREEGC)   
        XSync(xdpy, False);

    LockDisplay(dpy);

    NXMacroGetReq(CAPNotify, req);
    req->reqType = DPSCAPOPCODEBASE;
    req->type = X_CAPNotify;
    req->cxid = cxid;
    req->notification = ntype;
    req->data = data;
    req->extra = extra;
    
    if (gAutoFlush)
        N_XFlush(dpy);

    UnlockDisplay(dpy);
    SyncHandle();
    LastXRequest[dpyix] = XNextRequest(xdpy)-1;
}

void
XDPSLSync(Display *xdpy)
{
    register Display *dpy = ShuntMap[DPY_NUMBER(xdpy)];
    
    if (dpy == xdpy)
        {
        /* DPS/X */
        XSync(dpy, False);
        }
    else
        {
        /* CSDPS */
        XEvent event;
        DPSCAPData my;
        XExtData *extData;
        XExtCodes *codes = Codes[DPY_NUMBER(xdpy)];

	if (codes == NULL)
	    return;
	/* Get private data */
	extData = XFindOnExtensionList(
	  CSDPSHeadOfDpyExt(xdpy),
	  codes->extension);
	if (!extData)
	    return;
        my = (DPSCAPData) extData->private_data;
        my->saveseq = XNextRequest(dpy)-1;
	/* first send notification to agent */
	XDPSLCAPNotify(xdpy, 0, DPSCAPNOTE_SYNC, my->saveseq, 0);
#ifdef XXX
fprintf(stderr, "\nXDPSLSync(DPSCAPNOTE_SYNC) sending ... ");
#endif
        _XFlush(xdpy);
	N_XFlush(dpy);
#ifdef XXX
fprintf(stderr, "sent.\nWaiting for reply ... ");
#endif
	/* agent should send a ClientMessage, so wait for it */
	XIfEvent(xdpy, &event, WaitForSyncProc, (char *) my);

#ifdef XXX
fprintf(stderr, "received.\n");
#endif
	/* now client, agent, and server are synchronized */
        }
}

void
XDPSLReconcileRequests(Display *xdpy, ContextXID cxid)
{
    int dpyix;
    unsigned int seqnum;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];

    if (dpy == xdpy)
        return;  /* No-op for DPS/X */

    /* Get the sequence number and set the pause flag
       IFF we are sure that some X protocol has occurred
       since the last time we did a DPS request.  This
       minimizes pause/resume requests */
    
    if (LastXRequest[dpyix] == XNextRequest(xdpy)-1)
        {
	if (gAutoFlush)
	    N_XFlush(dpy);  /* This is what XDPSLCAPNotify would do */
        return;
	}
    else
        seqnum = DPSCAPSetPause(xdpy, cxid);
    
    /* Pause the context specified. */
    XDPSLCAPNotify(xdpy, cxid, DPSCAPNOTE_PAUSE, seqnum, 0);
    
    /* We don't even need to flush.  All we have to do is make
       sure that the notify request is queued before any
       DPS requests that follow. */
}

Status
XDPSLSetAgentArg(Display *xdpy, int arg, int val)
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    CARD32 capArg;
    register xCAPSetArgReq *req;
    
    if (dpy == xdpy)
        return(Success);  /* No-op for DPS/X */

    /* dpy will be NIL if we haven't opened a connection yet,
       but that's okay since we need to save the value anyway. */
    
    if (dpy)
        {
        int syncMask = displayFlags[dpyix].syncMask;
        
	/* ASSERT: There is no reason to pause the context for this
	   request, so just sync. */
        if (syncMask & (DPSCAP_SYNCMASK_SYNC|DPSCAP_SYNCMASK_RECONCILE))
            XSync(xdpy, False);
        }

    switch (arg)
        {
	case AGENT_ARG_SMALLFONTS:
	    AgentArgs[dpyix].showSmallSizes = val;
	    capArg = DPSCAP_ARG_SMALLFONTS;
	    break;
	case AGENT_ARG_PIXMEM:
	    AgentArgs[dpyix].pixMem = val;
	    capArg = DPSCAP_ARG_PIXMEM;
	    break;
	default:
	    return(!Success);
	}
    if (!dpy)
        return(Success);

    LockDisplay(dpy);

    NXMacroGetReq(CAPSetArg, req);
    req->reqType = DPSCAPOPCODEBASE;
    req->type = X_CAPSetArg;
    req->arg = capArg;
    req->val = val;
    
    if (gAutoFlush)
        N_XFlush(dpy);

    UnlockDisplay(dpy);
    SyncHandle();
    LastXRequest[dpyix] = XNextRequest(xdpy)-1;
    return(Success);
}


void
XDPSLCleanAll(xdpy)
    register Display *xdpy;
{
    /* Clean up all state associated with dpy */
    register DPSCAPPausedContextData *slot;
    int dpyix = DPY_NUMBER(xdpy);

    /* Clean up paused context list */
    for (slot = PausedPerDisplay[dpyix]; slot; slot = PausedPerDisplay[dpyix])
        {
	PausedPerDisplay[dpyix] = slot->next;
	Xfree(slot);
	}

    /* Clear agent args */
    AgentArgs[dpyix].showSmallSizes = 0;
    AgentArgs[dpyix].pixMem = 0;
}

void
XDPSLUpdateAgentArgs(xdpy)
    register Display *xdpy;
{
    int dpyix = DPY_NUMBER(xdpy);
    
    if (AgentArgs[dpyix].showSmallSizes)
        XDPSLSetAgentArg(xdpy, AGENT_ARG_SMALLFONTS, AgentArgs[dpyix].showSmallSizes);
    if (AgentArgs[dpyix].pixMem)
        XDPSLSetAgentArg(xdpy, AGENT_ARG_PIXMEM, AgentArgs[dpyix].pixMem);
}

void 
XDPSLCleanContext(xdpy, cxid)
    Display *xdpy;
    ContextXID cxid;
{
    /* Clean up all state associated with cxid on this dpy */
    register DPSCAPPausedContextData *slot, *prev;
    int dpyix = DPY_NUMBER(xdpy);
    
    /* If this is DPS/X, then slot will never have been initialized.
       See XDPSLNotifyContext */

    /* Clean up paused context list */
    prev = (DPSCAPPausedContextData *)NULL;
    for (slot = PausedPerDisplay[dpyix]; slot; prev = slot, slot = slot->next)
        {
	if (slot->cxid != cxid)
	    continue;
	if (!prev)
	    PausedPerDisplay[dpyix] = slot->next;
	else
	    prev->next = slot->next;
	Xfree(slot);
	break;
	}
}

/* DPS NX 2.0 */
void
XDPSLSetGCFlushMode(dpy, value)
    Display *dpy;
    int value;
{
    int dpyix;
    register Display *agent = ShuntMap[dpyix = DPY_NUMBER(dpy)];

    if (value != XDPSNX_GC_UPDATES_SLOW && value != XDPSNX_GC_UPDATES_FAST)
        {
	DPSWarnProc(NULL, "DPS NX: Bogus GC flush mode.\n");
        return;
	}
    /* 0 means no NX */
    GCFlushMode[dpyix] = (agent == dpy) ? 0 : value;
}

int
XDPSLGetGCFlushMode(dpy)
    Display *dpy;
{
    return(GCFlushMode[DPY_NUMBER(dpy)]);
}

void
XDPSLFlushGC(xdpy, gc)
    Display *xdpy;
    GC gc;
{
    int dpyix;
    register Display *dpy = ShuntMap[dpyix = DPY_NUMBER(xdpy)];
    
    if (!gc->dirty) return;

    if (GCFlushMode[dpyix] == XDPSNX_GC_UPDATES_FAST)
        {
	XGCValues vals;
	static unsigned long valuemask = DPSGCBITS & ~(GCClipMask);
	
	/* Okay to call Xlib, since dpy isn't locked */
	DPSAssertWarn(XGetGCValues(xdpy, gc, valuemask, &vals),
	      NULL, "DPS NX: XGetGCValues returned False\n");
	vals.clip_mask = gc->values.clip_mask;
	LockDisplay(dpy);
	DPSCAPChangeGC(dpy, gc, DPSGCBITS, &vals);
	UnlockDisplay(dpy);
	SyncHandle();
	}
     /* Fall thru.  Either the GCFlushMode is SLOW, which means
        we will DPSCAPChangeGC as a side-effect of FlushGC when
	the GC hook is called, or we just did it in the FAST case. */
     FlushGC(xdpy, gc);
     XDPSLFlush(xdpy);
}

/* === PRIVATE CSDPS PROCS === */

static Status
DPSCAPClientMessageProc(
    Display *dpy,
    XEvent *re,
    xEvent *event)
{
    register XDPSLOutputEvent *oce;
    register DPSCAPOutputEvent *oev;
    XDPSLOutputEvent fakeOutput;
    XExtCodes *codes = Codes[DPY_NUMBER(dpy)];
    PSCMProc oldProc = ClientMsgProc[DPY_NUMBER(dpy)];

    if (codes != NULL)
        {
        /* Get private data */
        XExtData *extData = XFindOnExtensionList(
	  CSDPSHeadOfDpyExt(dpy),
          codes->extension);
        DPSCAPData my;
        
        /* There's no extension, or there is an extension but we are
           passing events uninterpreted, so just pass it along 
           unless it is a DPSCAP error. */
           
        if (!extData)
            goto pass_the_buck;
        my = (DPSCAPData) extData->private_data;
        if (XDPSLGetPassEventsFlag(dpy) && 
            (event->u.clientMessage.u.l.type != my->typeXError))
            goto pass_the_buck;

	/* Fake up a DPS extension event and handle it transparently,
	   without going through the Xlib event queue */
	
	if (event->u.clientMessage.u.b.type == my->typePSOutput)
	    {
	    oce = &fakeOutput;
	    oce->length = DPSCAP_BYTESPEROUTPUTEVENT;
	    oev = (DPSCAPOutputEvent *)event->u.clientMessage.u.b.bytes;
	    goto common_stuff;
	    }
	else if (event->u.clientMessage.u.b.type == my->typePSOutputWithLen)
	    {
	    oce = &fakeOutput;
	    oev = (DPSCAPOutputEvent *)event->u.clientMessage.u.b.bytes;
	    oce->length = oev->data[DPSCAP_DATA_LEN];
common_stuff:
	    oce->type = codes->first_event + PSEVENTOUTPUT;
	    oce->serial = _XSetLastRequestRead(dpy, (xGenericReply *)event);
	    oce->send_event = True;  /* ??? */
	    oce->display = dpy;
	    oce->cxid = oev->cxid;
	    bcopy((char *) oev->data, oce->data, oce->length);
	    /* We've converted the event, give it to DPS */
	    if (TextProc)
		(*TextProc)((XEvent *) oce);
	    return(False);
	    }
	else if (event->u.clientMessage.u.b.type == my->typePSStatus)
	    {
	    register XDPSLStatusEvent *sce;
	    register DPSCAPStatusEvent *sev;
	    XDPSLStatusEvent fakeStatus;

	    sev = (DPSCAPStatusEvent *)event->u.clientMessage.u.b.bytes;
	    sce = &fakeStatus;
	    sce->type = codes->first_event + PSEVENTSTATUS;
	    sce->serial = _XSetLastRequestRead(dpy, (xGenericReply *)event);
	    sce->send_event = True; /* ??? */
	    sce->display = dpy;
	    sce->cxid = sev->cxid;
	    sce->status = sev->status;
	    /* We've converted the event, give it to DPS */
	    if (StatusProc[DPY_NUMBER(dpy)]) 
		(*(StatusProc[DPY_NUMBER(dpy)]))((XEvent *) sce);
	    return(False);
	    }
	else if (event->u.clientMessage.u.l.type == my->typeXError)
	    {
	    xError err;
	    register xError *e = &err;
	    
	    e->type = X_Error;
	    e->errorCode = event->u.clientMessage.u.l.longs0;
	    e->sequenceNumber =  event->u.u.sequenceNumber;
	    e->resourceID = event->u.clientMessage.u.l.longs3;
	    e->minorCode = event->u.clientMessage.u.l.longs2;
	    e->majorCode = event->u.clientMessage.u.l.longs1;
	    /* Smash the wire event here, before going off deep end */
	    event->u.clientMessage.u.l.type = my->typeNoop;
	    /* Jump! */
	    return(_XError(dpy, e));
	    }
	else if (event->u.clientMessage.u.l.type == my->typePSReady)
	    /* L2-DPS/PROTO 9 addition */
	    {
	    register XDPSLReadyEvent *rce;
	    XDPSLReadyEvent fakeReady;
	    
	    rce = &fakeReady;
	    rce->type = codes->first_event + PSEVENTREADY;
	    rce->serial = _XSetLastRequestRead(dpy, (xGenericReply *)event);
	    rce->send_event = True;
	    rce->display = dpy;
	    rce->cxid = event->u.clientMessage.u.l.longs0;
	    rce->val[0] = event->u.clientMessage.u.l.longs1;
	    rce->val[1] = event->u.clientMessage.u.l.longs2;
	    rce->val[2] = event->u.clientMessage.u.l.longs3;
	    rce->val[3] = event->u.clientMessage.u.l.longs4;
	    XDPSLCallReadyEventHandler(dpy, (XEvent *) rce);
	    return(False);
	    }
	}
    
    /* Put the event on the queue, so that Xlib is happy */
pass_the_buck:    
    return(oldProc(dpy, re, event));
}


static void
DPSCAPInitGC(Display *dpy, Display *agent, GC gc)
{
    XGCValues values;
    unsigned long valuemask = DPSGCBITS & ~(GCClipMask);
    
    /* Okay to call Xlib, since dpy isn't locked */
    DPSAssertWarn(XGetGCValues(dpy, gc, valuemask, &values),
         NULL, "DPS NX: XGetGCValues returned False\n");
    values.clip_mask = gc->values.clip_mask;
    DPSCAPChangeGC(agent, gc, DPSGCBITS, &values);
    SyncHandle();
    XDPSLSync(dpy);
}


/* ARGSUSED */

static Bool
WaitForSyncProc(Display *xdpy, XEvent *event, char *arg)
{
    DPSCAPData my = (DPSCAPData)arg;
    
    if ((event->type & 0x7F) == ClientMessage 
	&& event->xclient.message_type == my->typeSync
	&& event->xclient.data.l[0] == (long) my->saveseq) {
      return(True);
    } else {
      return(False);
    }
}


static int
DPSCAPAfterProc(Display *xdpy)
{
    register Display *dpy = ShuntMap[DPY_NUMBER(xdpy)];
    GenericProcPtrReturnsInt proc;
    
    if (dpy != (Display *)NULL && dpy != xdpy)
        {
        LockDisplay(dpy);
        N_XFlush(dpy);
        UnlockDisplay(dpy);
        LockDisplay(xdpy);
        _XFlush(xdpy);
        UnlockDisplay(xdpy);
        }
    if ((proc = AfterProcs[DPY_NUMBER(xdpy)]) != NULL)
        return((*proc)(xdpy));
    else
        return(0);
}


static unsigned int
DPSCAPSetPause(Display *xdpy, ContextXID cxid)
{
    register DPSCAPPausedContextData *slot;
    int dpyix;
    unsigned int ret;

    /* Find or create slot */
    
    slot = PausedPerDisplay[dpyix = DPY_NUMBER(xdpy)];
    if (!slot)
        {
	slot = (DPSCAPPausedContextData *)
	  Xcalloc(1, sizeof(DPSCAPPausedContextData));
	PausedPerDisplay[dpyix] = slot;
	goto common_code;
	/* IMPLICATION: it is okay to fall through common_code
	   and do test_ret. */
	}
    while (1)
        if (slot->cxid == cxid)
	    {
	    if (!slot->paused)
	        {
	        slot->paused = True;
		++gTotalPaused;
		}
	    /* Back-to-back pauses get different sequence numbers */
	    ret = ++slot->seqnum;
	    goto test_ret;
	    }
	else if (slot->next) slot = slot->next;
	else break;
    /* cxid wasn't found, so add it */
    /* ASSERT: slot points to last record of the list */
    slot->next = (DPSCAPPausedContextData *)
      Xcalloc(1, sizeof(DPSCAPPausedContextData));
    slot = slot->next;
common_code:
    slot->paused = True;
    ++gTotalPaused;
    slot->cxid = cxid;
    ret = ++slot->seqnum;
test_ret:
    if (!ret)
        {
	DPSWarnProc(NULL, "Pause sequence wrapped around!");
	}
    return(ret);
}

static Bool
DPSCAPResumeContext(Display *xdpy, ContextXID cxid)
{
    register DPSCAPPausedContextData *slot;
    int dpyix = DPY_NUMBER(xdpy);

    /* Try to match cxid to list of paused contexts */
    for (slot = PausedPerDisplay[dpyix]; slot; slot = slot->next)
        if (slot->cxid == cxid && slot->paused)
	    {
	    /* Send resume event */
	    register XClientMessageEvent *ee;
	    XEvent e;
	    XExtData *extData;
	    DPSCAPData my;
	    XExtCodes *codes = Codes[dpyix];
    
	    extData = XFindOnExtensionList(
		CSDPSHeadOfDpyExt(xdpy),
		codes->extension);
	    if (!extData)
		return(False);
	    my = (DPSCAPData) extData->private_data;

	    ee = &e.xclient;
	    ee->type = ClientMessage;
	    ee->display = xdpy;
	    ee->window = my->agentWindow;
	    ee->format = 32;
	    ee->message_type = my->typeResume;
	    ee->data.l[0] = cxid;
	    ee->data.l[1] = slot->seqnum;
	    (void) XSendEvent(
	      xdpy,
	      my->agentWindow,
	      False,
	      NoEventMask,
	      &e);
	    XFlush(xdpy);
	    /* Turn off flag */
	    slot->paused = False;
	    --gTotalPaused;
	    return(True);
	    }
    /* Fall thru */
    return(False);
}