/* $XFree86: xc/programs/Xserver/lbx/lbxmain.c,v 1.14 2005/10/14 15:17:20 tsi Exp $ */
/*

Copyright 1996, 1998  The Open Group

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.

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.

*/
/*
 * Copyright 1992 Network Computing Devices
 *
 * 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 NCD. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  NCD. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * NCD. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NCD.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */
 
#include <sys/types.h>
#define NEED_REPLIES
#define NEED_EVENTS
#include <X11/X.h>
#include <X11/Xproto.h>
#include <X11/Xos.h>
#include "misc.h"
#include "os.h"
#include "dixstruct.h"
#include "resource.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "pixmapstr.h"
#include "gcstruct.h"
#include "extnsionst.h"
#include "servermd.h"
#define _XLBX_SERVER_
#include <X11/extensions/lbxstr.h>
#include <X11/extensions/lbxdeltastr.h>
#include "lbxserve.h"
#include <X11/extensions/lbximage.h>
#include "lbxsrvopts.h"
#include "lbxtags.h"
#include <X11/Xfuncproto.h>
#include <errno.h>
#ifndef Lynx
#include <sys/uio.h>
#else
#include <uio.h>
#endif
#include <stdio.h>

#ifndef X_NOT_POSIX
#include <unistd.h>
#endif

#define CloseLbxClient	0xff

#define MAXBYTESDIFF	8

int LbxWhoAmI = 1;		/*
				 * for lbx zlib library to know who we are
				 * server = 1
				 * proxy = 0
				 */


static void LbxResetProc ( ExtensionEntry *extEntry );
static void LbxFreeClient ( ClientPtr client );
static void LbxShutdownProxy ( LbxProxyPtr proxy );
static int DecodeLbxDelta ( ClientPtr client );

static LbxProxyPtr proxyList;
unsigned char LbxReqCode;
int 	LbxEventCode;
static int BadLbxClientCode;
static int	uid_seed;

static int	lbxCompressWorkProcCount;

LbxClientPtr	lbxClients[MAXCLIENTS];

extern xConnSetupPrefix connSetupPrefix;
extern char *ConnectionInfo;
extern int  (*LbxInitialVector[3])(ClientPtr);

#ifdef DEBUG
int lbxDebug = 0;
#endif


void
LbxExtensionInit(void)
{
    ExtensionEntry *extEntry;

    lbxCompressWorkProcCount = 0;
    proxyList = NULL;
    uid_seed = 0;
    if ((extEntry = AddExtension(LBXNAME, LbxNumberEvents, LbxNumberErrors,
				 ProcLbxDispatch, SProcLbxDispatch,
				 LbxResetProc, StandardMinorOpcode)))
    {
	LbxReqCode = (unsigned char)extEntry->base;
	LbxEventCode = extEntry->eventBase;
	BadLbxClientCode = extEntry->errorBase + BadLbxClient;
        LbxDixInit();

	LbxCmapInit ();
	DeclareExtensionSecurity(LBXNAME, TRUE); 
    }
}

/*ARGSUSED*/
static void
LbxResetProc (ExtensionEntry	*extEntry)
{
   LbxResetTags();
   uid_seed = 0;
}

void
LbxCloseClient (ClientPtr	client)
{
    xLbxCloseEvent  closeEvent;
    ClientPtr	    master;
    LbxProxyPtr	    proxy;
    LbxClientPtr    lbxClient = LbxClient(client);
    CARD32	    id;

    if (!lbxClient)
	return;
    id = lbxClient->id;
    proxy = lbxClient->proxy;

    DBG (DBG_CLIENT, (stderr, "Close client %d\n", client->index));
    LbxFreeClient (client);
    if (!id)
    {
	isItTimeToYield = TRUE;
	CloseDownFileDescriptor (client);
	LbxShutdownProxy (proxy);
    } 
    else
    {
	master = NULL;
	if (proxy->lbxClients[0])
	    master = LbxProxyClient(proxy);
	if (master && !master->clientGone)
	{
	    closeEvent.type = LbxEventCode;
	    closeEvent.lbxType = LbxCloseEvent;
	    closeEvent.client = id;
	    closeEvent.sequenceNumber = master->sequence;
	    closeEvent.pad1 = closeEvent.pad2 = closeEvent.pad3 =
		closeEvent.pad4 = closeEvent.pad5 = closeEvent.pad6 = 0;
	    if (master->swapped) {
		int	    n;

		swaps(&closeEvent.sequenceNumber, n);
		swapl(&closeEvent.client, n);
	    }
	    WriteToClient(master, sizeof (closeEvent), (char *)&closeEvent);
	    LbxForceOutput(proxy);
	}
    }
}

static int
LbxReencodeEvent(ClientPtr	client,
		 LbxProxyPtr	proxy,
		 char 		*buf)
{
    xEvent *ev = (xEvent *)buf;
    int n;
    lbxMotionCache *motionCache = &proxy->motionCache;
    int motionDelta = 0;
    Bool swapCache;
    xEvent tev, *sev;

    if (ev->u.u.type != MotionNotify) {
	if (proxy->dosquishing)
	    return LbxSquishEvent(buf);
	return 0;
    }

    /*
     * Check if we can generate a motion delta event.
     *
     * The motion cache contains the last motion event the server sent.
     *
     * The following are always stored in the cache in the server's
     * byte order:
     *     sequenceNumber, time, rootX, rootY, eventX, eventY
     * This is because when determining if we can do a delta, all
     * arithmetic must be done using the server's byte order.
     *
     * The following are stored in the byte order of the latest client
     * receiving a motion event (indicated by motionCache->swapped):
     *     root, event, child, state
     * These fields do not need to be stored in the server's byte order
     * because we only use the '==' operator on them.
     */

    if (!proxy->motion_allowed_events) {
	DBG(DBG_CLIENT, (stderr, "throttling motion event for client %d\n", client->index));
	return sz_xEvent;
    }
    proxy->motion_allowed_events--;

    motionCache = &proxy->motionCache;

    if (!client->swapped)
    {
	swapCache = motionCache->swapped;
	sev = ev;
    }
    else
    {
	swapCache = !motionCache->swapped;
	sev = &tev;
	cpswaps (ev->u.keyButtonPointer.rootX,
		 sev->u.keyButtonPointer.rootX);
	cpswaps (ev->u.keyButtonPointer.rootY,
		 sev->u.keyButtonPointer.rootY);
	cpswaps (ev->u.keyButtonPointer.eventX,
		 sev->u.keyButtonPointer.eventX);
	cpswaps (ev->u.keyButtonPointer.eventY,
		 sev->u.keyButtonPointer.eventY);
	cpswaps (ev->u.u.sequenceNumber,
		 sev->u.u.sequenceNumber);
	cpswapl (ev->u.keyButtonPointer.time,
		 sev->u.keyButtonPointer.time);
    }

    if (swapCache)
    {
	swapl (&motionCache->root, n);
	swapl (&motionCache->event, n);
	swapl (&motionCache->child, n);
	swaps (&motionCache->state, n);

	motionCache->swapped = !motionCache->swapped;
    }

    motionDelta = 0;

    if (ev->u.u.detail == motionCache->detail &&
	ev->u.keyButtonPointer.root == motionCache->root &&
	ev->u.keyButtonPointer.event == motionCache->event &&
	ev->u.keyButtonPointer.child == motionCache->child &&
	ev->u.keyButtonPointer.state == motionCache->state &&
	ev->u.keyButtonPointer.sameScreen == motionCache->sameScreen) {

	int root_delta_x =
	    sev->u.keyButtonPointer.rootX - motionCache->rootX;
	int root_delta_y =
	    sev->u.keyButtonPointer.rootY - motionCache->rootY;
	int event_delta_x =
	    sev->u.keyButtonPointer.eventX - motionCache->eventX;
	int event_delta_y =
	    sev->u.keyButtonPointer.eventY - motionCache->eventY;
	unsigned long sequence_delta =
	    sev->u.u.sequenceNumber - motionCache->sequenceNumber;
	unsigned long time_delta =
	    sev->u.keyButtonPointer.time - motionCache->time;

	if (root_delta_x == event_delta_x &&
	    event_delta_x >= -128 && event_delta_x < 128 &&
	    root_delta_y == event_delta_y &&
	    event_delta_y >= -128 && event_delta_y < 128) {

	    if (sequence_delta == 0 && time_delta < 256) {

		lbxQuickMotionDeltaEvent *mev =
		    (lbxQuickMotionDeltaEvent *)(buf + sz_xEvent -
						 sz_lbxQuickMotionDeltaEvent);

		mev->type = LbxEventCode + LbxQuickMotionDeltaEvent;
		mev->deltaTime = time_delta;
		mev->deltaX = event_delta_x;
		mev->deltaY = event_delta_y;

		motionDelta = sz_xEvent - sz_lbxQuickMotionDeltaEvent;

	    } else if (sequence_delta < 65536 && time_delta < 65536) {

		lbxMotionDeltaEvent *mev =
		    (lbxMotionDeltaEvent *)(buf + sz_xEvent -
					    sz_lbxMotionDeltaEvent);

		mev->type = LbxEventCode;
		mev->lbxType = LbxMotionDeltaEvent;
		mev->deltaTime = time_delta;
		mev->deltaSequence = sequence_delta;
		mev->deltaX = event_delta_x;
		mev->deltaY = event_delta_y;

		if (LbxProxyClient(proxy)->swapped)
		{
		    swaps (&mev->deltaTime, n);
		    swaps (&mev->deltaSequence, n);
		}

		motionDelta = sz_xEvent - sz_lbxMotionDeltaEvent;
	    }
	}
    }

    motionCache->sequenceNumber = sev->u.u.sequenceNumber;
    motionCache->time = sev->u.keyButtonPointer.time;
    motionCache->rootX = sev->u.keyButtonPointer.rootX;
    motionCache->rootY = sev->u.keyButtonPointer.rootY;
    motionCache->eventX = sev->u.keyButtonPointer.eventX;
    motionCache->eventY = sev->u.keyButtonPointer.eventY;

    if (motionDelta)
	return motionDelta;

    ev->u.keyButtonPointer.pad1 = 0;
    motionCache->detail = ev->u.u.detail;
    motionCache->root = ev->u.keyButtonPointer.root;
    motionCache->event = ev->u.keyButtonPointer.event;
    motionCache->child = ev->u.keyButtonPointer.child;
    motionCache->state = ev->u.keyButtonPointer.state;
    motionCache->sameScreen = ev->u.keyButtonPointer.sameScreen;
    return 0;
}

static int
LbxComposeDelta(LbxProxyPtr	 proxy,
		char	 	*reply,
		int		 len,
		char	 	*buf)
{
    int		 diffs;
    int		 cindex;
    int		 n;
    xLbxDeltaReq *p = (xLbxDeltaReq *)buf;

    diffs = LBXDeltaMinDiffs(&proxy->outdeltas, (unsigned char *)reply, len,
			     min(MAXBYTESDIFF, (len - sz_xLbxDeltaReq) >> 1),
			     &cindex);
    if (diffs < 0) {
	LBXAddDeltaOut(&proxy->outdeltas, (unsigned char *)reply, len);
	return 0;
    }
    LBXEncodeDelta(&proxy->outdeltas, (unsigned char *)reply, diffs, cindex,
		   (unsigned char *)(&buf[sz_xLbxDeltaReq]));
    LBXAddDeltaOut(&proxy->outdeltas, (unsigned char *)reply, len);
    p->reqType = LbxEventCode;
    p->lbxReqType = LbxDeltaEvent;
    p->diffs = diffs;
    p->cindex = cindex;
    len = (sz_xLbxDeltaReq + sz_xLbxDiffItem * diffs + 3) & ~3;
    p->length = len >> 2;
    if (LbxProxyClient(proxy)->swapped) {
	swaps(&p->length, n);
    }
    return len;
}

void
LbxReencodeOutput(ClientPtr 	 client,
		  char 		*pbuf,
		  int 		*pcount,
		  char 		*cbuf,
		  int 		*ccount)
{
    LbxClientPtr lbxClient = LbxClient(client);
    LbxProxyPtr proxy = lbxClient->proxy;
    CARD32 len;
    int n;
    int count = *ccount;
    char *obuf = cbuf;

    if (client->clientState != ClientStateRunning) {
	if (DELTA_CACHEABLE(&proxy->outdeltas, count) &&
	    (n = LbxComposeDelta(proxy, cbuf, count, proxy->oDeltaBuf))) {
	    memcpy(obuf, proxy->oDeltaBuf, n);
	    *ccount -= (count - n);
	}
	return;
    }
    if (lbxClient->bytes_remaining) {
	if (count < lbxClient->bytes_remaining) {
	    lbxClient->bytes_remaining -= count;
	    return;
	}
	if (DELTA_CACHEABLE(&proxy->outdeltas, lbxClient->bytes_in_reply)) {
	    len = lbxClient->bytes_in_reply - lbxClient->bytes_remaining;
	    pbuf += (*pcount - len);
	    memcpy(proxy->replyBuf, pbuf, len);
	    memcpy(proxy->replyBuf + len, cbuf, lbxClient->bytes_remaining);
	    n = LbxComposeDelta(proxy, proxy->replyBuf,
				lbxClient->bytes_in_reply, proxy->oDeltaBuf);
	    if (!n)
		obuf += lbxClient->bytes_remaining;
	    else if (n <= len) {
		memcpy(pbuf, proxy->oDeltaBuf, n);
		*pcount -= (len - n);
		*ccount -= lbxClient->bytes_remaining;
	    } else {
		memcpy(pbuf, proxy->oDeltaBuf, len);
		memcpy(obuf, proxy->oDeltaBuf + len, n - len);
		*ccount -= lbxClient->bytes_remaining - (n - len);
		obuf += n - len;
	    }
	} else
	    obuf += lbxClient->bytes_remaining;
	cbuf += lbxClient->bytes_remaining;
	count -= lbxClient->bytes_remaining;
	lbxClient->bytes_remaining = 0;
    }
    while (count) {
	lbxClient->bytes_in_reply = sz_xEvent;
	if (((xGenericReply *)cbuf)->type == X_Reply) {
	    len = ((xGenericReply *)cbuf)->length;
	    if (client->swapped) {
		swapl(&len, n);
            }
	    lbxClient->bytes_in_reply += (len << 2);
	    if (LbxProxyClient(proxy)->swapped != client->swapped) {
		swapl(&((xGenericReply *)cbuf)->length, n);
	    }
	    if (count < lbxClient->bytes_in_reply) {
		lbxClient->bytes_remaining = lbxClient->bytes_in_reply - count;
		if (obuf != cbuf)
		    memmove(obuf, cbuf, count);
		return;
	    }
	} else if (((xGenericReply *)cbuf)->type > X_Reply &&
		   ((xGenericReply *)cbuf)->type < LASTEvent &&
		   (n = LbxReencodeEvent(client, proxy, cbuf))) {
	    cbuf += n;
	    *ccount -= n;
	    count -= n;
	    if (n == sz_xEvent)
		continue;
	    lbxClient->bytes_in_reply -= n;
	}
	if (DELTA_CACHEABLE(&proxy->outdeltas, lbxClient->bytes_in_reply) &&
	    (n = LbxComposeDelta(proxy, cbuf, lbxClient->bytes_in_reply,
				 proxy->oDeltaBuf))) {
	    memcpy(obuf, proxy->oDeltaBuf, n);
	    obuf += n;
	    *ccount -= (lbxClient->bytes_in_reply - n);
	} else {
	    if (obuf != cbuf)
		memmove(obuf, cbuf, lbxClient->bytes_in_reply);
	    obuf += lbxClient->bytes_in_reply;
	}
	cbuf += lbxClient->bytes_in_reply;
	count -= lbxClient->bytes_in_reply;
    }
}

/*ARGSUSED*/
static void
LbxReplyCallback(CallbackListPtr *pcbl,
		 pointer 	  nulldata,
		 pointer 	  calldata)
{
    ReplyInfoRec *pri = (ReplyInfoRec *)calldata;
    ClientPtr client = pri->client;
    LbxClientPtr lbxClient;
    REQUEST(xReq);

    if (!pri->startOfReply || stuff->reqType > 127)
	return;
    lbxClient = LbxClient(client);
    if (lbxClient)
	ZeroReplyPadBytes(pri->replyData, stuff->reqType);
}

/*
 * XXX If you think this is moronic, you're in good company,
 * but things definitely hang if we don't have this.
 */
/* ARGSUSED */
static Bool
LbxCheckCompressInput (ClientPtr dummy1,
		       pointer   dummy2)
{
    LbxProxyPtr	    proxy;

    if (!lbxCompressWorkProcCount)
	return TRUE;

    for (proxy = proxyList; proxy; proxy = proxy->next) {
	if (proxy->compHandle &&
	    proxy->streamOpts.streamCompInputAvail(proxy->fd))
	    AvailableClientInput (LbxProxyClient(proxy));
    }
    return FALSE;
}

static Bool
LbxIsClientBlocked (LbxClientPtr lbxClient)
{
    LbxProxyPtr		proxy = lbxClient->proxy;
    
    return (lbxClient->ignored ||
	    (GrabInProgress && lbxClient->client->index != GrabInProgress &&
	     lbxClient != proxy->lbxClients[0]));
}

static void
LbxSwitchRecv (LbxProxyPtr  proxy,
	       LbxClientPtr lbxClient)
{
    ClientPtr	client;
    
    proxy->curRecv = lbxClient;
    if (!lbxClient || lbxClient->client->clientGone)
    {
	DBG(DBG_CLIENT, (stderr, "switching to dispose input\n"));
	lbxClient = proxy->lbxClients[0];
        if (!lbxClient)
            return;
    }
    client = lbxClient->client;
    DBG (DBG_SWITCH, (stderr, "switching input to client %d\n", client->index));

    SwitchClientInput (client, FALSE);
    proxy->curDix = lbxClient;
}

/* ARGSUSED */
static Bool
LbxWaitForUnblocked (ClientPtr	client,
		     pointer	closure)
{
    LbxClientPtr    lbxClient;
    LbxProxyPtr	    proxy;

    if (client->clientGone)
	return TRUE;
    lbxClient = LbxClient(client);
    if (!lbxClient)
	return TRUE;
    proxy = lbxClient->proxy;
    if (LbxIsClientBlocked (lbxClient) ||
	((lbxClient != proxy->curDix) && proxy->curDix->reqs_pending &&
	 !LbxIsClientBlocked(proxy->curDix)))
	return FALSE;
    lbxClient->input_blocked = FALSE;
    DBG (DBG_BLOCK, (stderr, "client %d no longer blocked, switching\n",
		     client->index));
    SwitchClientInput (client, TRUE);
    proxy->curDix = lbxClient;
    return TRUE;
}

void
LbxSetForBlock(LbxClientPtr lbxClient)
{
    lbxClient->reqs_pending++;
    if (!lbxClient->input_blocked)
    {
	lbxClient->input_blocked = TRUE;
	QueueWorkProc(LbxWaitForUnblocked, lbxClient->client, NULL);
    }
}

/* ARGSUSED */
static int
LbxWaitForUngrab (ClientPtr	client,
		  pointer	closure)
{
    LbxClientPtr lbxClient = LbxClient(client);
    LbxProxyPtr  proxy;
    xLbxListenToAllEvent ungrabEvent;

    if (client->clientGone || !lbxClient)
	return TRUE;
    if (GrabInProgress)
	return FALSE;
    proxy = lbxClient->proxy;
    proxy->grabClient = 0;
    ungrabEvent.type = LbxEventCode;
    ungrabEvent.lbxType = LbxListenToAll;
    ungrabEvent.pad1 = ungrabEvent.pad2 = ungrabEvent.pad3 =
	ungrabEvent.pad4 = ungrabEvent.pad5 = ungrabEvent.pad6 =
	ungrabEvent.pad7 = 0;
    WriteToClient (client,
		   sizeof(xLbxListenToAllEvent), (char *)&ungrabEvent);
    LbxForceOutput(proxy);
    return TRUE;
}

static void
LbxServerGrab(LbxProxyPtr proxy)
{
    LbxClientPtr	grabbingLbxClient;
    xLbxListenToOneEvent grabEvent;

    /*
     * If the current grabbing client has changed, then we need
     * to send a message to update the proxy.
     */

    grabEvent.type = LbxEventCode;
    grabEvent.lbxType = LbxListenToOne;
    if (!(grabbingLbxClient = lbxClients[GrabInProgress]) ||
	grabbingLbxClient->proxy != proxy)
	grabEvent.client = 0xffffffff; /* client other than a proxy client */
    else
	grabEvent.client = grabbingLbxClient->id;
    grabEvent.pad1 = grabEvent.pad2 = grabEvent.pad3 =
	grabEvent.pad4 = grabEvent.pad5 = grabEvent.pad6 = 0;
    if (LbxProxyClient(proxy)->swapped) {
	int n;
	swapl(&grabEvent.client, n);
    }
    WriteToClient(LbxProxyClient(proxy),
		  sizeof(xLbxListenToOneEvent), (char *)&grabEvent);
    LbxForceOutput(proxy);
    if (!proxy->grabClient)
	QueueWorkProc(LbxWaitForUngrab, LbxProxyClient(proxy), NULL);
    proxy->grabClient = GrabInProgress;
}

#define MAJOROP(client) ((xReq *)client->requestBuffer)->reqType
#define MINOROP(client) ((xReq *)client->requestBuffer)->data

static Bool lbxCacheable[] = {
	FALSE,	/* LbxQueryVersion	  0 */
	FALSE,	/* LbxStartProxy	  1 */
	TRUE,	/* LbxStopProxy		  2 */
	FALSE,	/* LbxSwitch		  3 */
	FALSE,	/* LbxNewClient		  4 */
	TRUE,	/* LbxCloseClient	  5 */
	TRUE,	/* LbxModifySequence	  6 */
	FALSE,	/* LbxAllowMotion	  7 */
	TRUE,	/* LbxIncrementPixel	  8 */
	FALSE,	/* LbxDelta		  9 */
	TRUE,	/* LbxGetModifierMapping 10 */
	FALSE,	/* nothing		 11 */
	TRUE,	/* LbxInvalidateTag	 12 */
	TRUE,	/* LbxPolyPoint		 13 */
	TRUE,	/* LbxPolyLine		 14 */
	TRUE,	/* LbxPolySegment	 15 */
	TRUE,	/* LbxPolyRectangle	 16 */
	TRUE,	/* LbxPolyArc		 17 */
	TRUE,	/* LbxFillPoly		 18 */
	TRUE,	/* LbxPolyFillRectangle	 19 */
	TRUE,	/* LbxPolyFillArc	 20 */
	TRUE,	/* LbxGetKeyboardMapping 21 */
	TRUE,	/* LbxQueryFont		 22 */
	TRUE,	/* LbxChangeProperty	 23 */
	TRUE,	/* LbxGetProperty	 24 */
	TRUE,	/* LbxTagData		 25 */
	TRUE,	/* LbxCopyArea		 26 */
	TRUE,	/* LbxCopyPlane		 27 */
	TRUE,	/* LbxPolyText8		 28 */
	TRUE,	/* LbxPolyText16	 29 */
	TRUE,	/* LbxImageText8	 30 */
	TRUE,	/* LbxImageText16	 31 */
	FALSE,	/* LbxQueryExtension	 32 */
	TRUE,	/* LbxPutImage		 33 */
	TRUE,	/* LbxGetImage		 34 */
	FALSE,	/* LbxBeginLargeRequest	 35 */
	FALSE,	/* LbxLargeRequestData	 36 */
	FALSE,	/* LbxEndLargeRequest	 37 */
	FALSE,	/* LbxInternAtoms	 38 */
	TRUE,	/* LbxGetWinAttrAndGeom  39 */
	TRUE,	/* LbxGrabCmap		 40 */
	TRUE,	/* LbxReleaseCmap	 41 */
	TRUE,	/* LbxAllocColor	 42 */
	TRUE,	/* LbxSync		 43 */
};

#define NUM(a)	(sizeof (a) / sizeof (a[0]))

static int
LbxReadRequestFromClient (ClientPtr	client)
{
    int		    ret;
    LbxClientPtr    lbxClient = LbxClient(client);
    LbxProxyPtr	    proxy = lbxClient->proxy;
    ClientPtr	    masterClient = LbxProxyClient(proxy);
    Bool	    isblocked;
    Bool	    cacheable;

    DBG (DBG_READ_REQ, (stderr, "Reading request from client %d\n", client->index));

    if (GrabInProgress && (proxy->grabClient != GrabInProgress))
	LbxServerGrab(proxy);
    isblocked = LbxIsClientBlocked(lbxClient);

    if (lbxClient->reqs_pending && !isblocked) {
	ret = StandardReadRequestFromClient(client);
	if (ret > 0 && (MAJOROP(client) == LbxReqCode) &&
	    (MINOROP(client) == X_LbxEndLargeRequest))
	    ret = PrepareLargeReqBuffer(client);
	if (!--lbxClient->reqs_pending && (lbxClient != proxy->curRecv))
	    LbxSwitchRecv (proxy, proxy->curRecv);
	return ret;
    }
    while (1) {
	ret = StandardReadRequestFromClient(masterClient);
	if (ret <= 0)
	    return ret;
	client->requestBuffer = masterClient->requestBuffer;
	client->req_len = masterClient->req_len;
	cacheable = client->clientState == ClientStateRunning;
	if (cacheable && (MAJOROP(client) == LbxReqCode)) {
	    /* Check to see if this request is delta cached */
	    if (MINOROP(client) < NUM(lbxCacheable))
		cacheable = lbxCacheable[MINOROP(client)];
	    switch (MINOROP(client)) {
	    case X_LbxSwitch:
		/* Switch is sent by proxy */
		if (masterClient->swapped)
		    SProcLbxSwitch (client);
		else
		    ProcLbxSwitch (client);
		return 0;
	    case X_LbxDelta:
		ret = DecodeLbxDelta (client);
		DBG(DBG_DELTA,
		    (stderr,"delta decompressed msg %d, len = %d\n",
		     (unsigned)((unsigned char *)client->requestBuffer)[0],
		     ret));
		break;
	    case X_LbxEndLargeRequest:
		if (!isblocked)
		    ret = PrepareLargeReqBuffer(client);
		break;
	    }
	}
	if (cacheable && DELTA_CACHEABLE(&proxy->indeltas, ret)) {
	    DBG(DBG_DELTA,
		(stderr, "caching msg %d, len = %d, index = %d\n",
		 (unsigned)((unsigned char *)client->requestBuffer)[0],
		 ret, proxy->indeltas.nextDelta));
	    LBXAddDeltaIn(&proxy->indeltas, client->requestBuffer, ret);
	}
	if (client->swapped != masterClient->swapped) {
	    char        n;
	    /* put length in client order */
	    swaps(&((xReq *)client->requestBuffer)->length, n);
	}
	if (!isblocked)
	    return ret;
	DBG (DBG_BLOCK, (stderr, "Stashing %d bytes for %d\n", 
			 ret, client->index));
	AppendFakeRequest (client, client->requestBuffer, ret);
	LbxSetForBlock(lbxClient);
    }
}

static LbxClientPtr
LbxInitClient (LbxProxyPtr	proxy,
	       ClientPtr	client,
	       CARD32		id)
{
    LbxClientPtr lbxClient;
    int i;
    
    lbxClient = (LbxClientPtr) xalloc (sizeof (LbxClientRec));
    if (!lbxClient)
	return NULL;
    lbxClient->id = id;
    lbxClient->client = client;
    lbxClient->proxy = proxy;
    lbxClient->ignored = FALSE;
    lbxClient->input_blocked = FALSE;
    lbxClient->reqs_pending = 0;
    lbxClient->bytes_in_reply = 0;
    lbxClient->bytes_remaining = 0;
    client->readRequest = LbxReadRequestFromClient;
    bzero (lbxClient->drawableCache, sizeof (lbxClient->drawableCache));
    bzero (lbxClient->gcontextCache, sizeof (lbxClient->gcontextCache));
    lbxClients[client->index] = lbxClient;
    for (i = 0; proxy->lbxClients[i]; i++)
	;
    if (i > proxy->maxIndex)
	proxy->maxIndex = i;
    proxy->lbxClients[i] = lbxClient;
    proxy->numClients++;
    lbxClient->gfx_buffer = (pointer) NULL;
    lbxClient->gb_size = 0;
    return lbxClient;
}

static void
LbxFreeClient (ClientPtr client)
{
    LbxClientPtr    lbxClient = LbxClient(client);
    LbxProxyPtr	    proxy = lbxClient->proxy;
    int		    i;

    if (lbxClient != proxy->lbxClients[0]) {
	if (lbxClient == proxy->curRecv)
	    LbxSwitchRecv(proxy, NULL);
	else if (lbxClient == proxy->curDix)
	    LbxSwitchRecv(proxy, proxy->curRecv);
    }
	
    --proxy->numClients;
    lbxClients[client->index] = NULL;
    for (i = 0; i <= proxy->maxIndex; i++) {
	if (proxy->lbxClients[i] == lbxClient) {
	    proxy->lbxClients[i] = NULL;
	    break;
	}
    }
    while (proxy->maxIndex >= 0 && !proxy->lbxClients[proxy->maxIndex])
	--proxy->maxIndex;
    xfree(lbxClient->gfx_buffer);
    client->readRequest = StandardReadRequestFromClient;
    xfree (lbxClient);
}

static void
LbxFreeProxy (LbxProxyPtr proxy)
{
    LbxProxyPtr *p;

    LBXFreeDeltaCache(&proxy->indeltas);
    LBXFreeDeltaCache(&proxy->outdeltas);
    LbxFreeOsBuffers(proxy);
    if (proxy->iDeltaBuf)
	xfree(proxy->iDeltaBuf);
    if (proxy->replyBuf)
	xfree(proxy->replyBuf);
    if (proxy->oDeltaBuf)
	xfree(proxy->oDeltaBuf);
    if (proxy->compHandle)
	proxy->streamOpts.streamCompFreeHandle(proxy->compHandle);
    if (proxy->bitmapCompMethods)
	xfree (proxy->bitmapCompMethods);
    if (proxy->pixmapCompMethods)
	xfree (proxy->pixmapCompMethods);
    if (proxy->pixmapCompDepths)
    {
	int i;
	for (i = 0; i < proxy->numPixmapCompMethods; i++)
	    xfree (proxy->pixmapCompDepths[i]);
	xfree (proxy->pixmapCompDepths);
    }

    for (p = &proxyList; *p; p = &(*p)->next) {
	if (*p == proxy) {
	    *p = proxy->next;
	    break;
	}
    }
    if (!proxyList)
	DeleteCallback(&ReplyCallback, LbxReplyCallback, NULL);

    xfree (proxy);
}

LbxProxyPtr
LbxPidToProxy(int pid)
{
    LbxProxyPtr proxy;

    for (proxy = proxyList; proxy; proxy = proxy->next) {
	if (proxy->pid == pid)
	    return proxy;
    }
    return NULL;
}

static void
LbxShutdownProxy (LbxProxyPtr proxy)
{
    int		    i;
    ClientPtr	    client;

    if (proxy->compHandle)
	--lbxCompressWorkProcCount;
    while (proxy->grabbedCmaps)
	LbxReleaseCmap(proxy->grabbedCmaps, FALSE);
    for (i = 0; i <= proxy->maxIndex; i++)
    {
	if (proxy->lbxClients[i])
	{
	    client = proxy->lbxClients[i]->client;
	    if (!client->clientGone)
		CloseDownClient (client);
	}
    }
    LbxFlushTags(proxy);
    LbxFreeProxy(proxy);
}


int
ProcLbxQueryVersion (ClientPtr client)
{
    /* REQUEST(xLbxQueryVersionReq); */
    xLbxQueryVersionReply rep;
    register int n;

    REQUEST_SIZE_MATCH(xLbxQueryVersionReq);
    rep.type = X_Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    rep.majorVersion = LBX_MAJOR_VERSION;
    rep.minorVersion = LBX_MINOR_VERSION;
    rep.pad0 = rep.pad1 = rep.pad2 = rep.pad3 = rep.pad4 = 0;

    if (client->swapped) {
    	swaps(&rep.sequenceNumber, n);
    	swapl(&rep.length, n);
	swaps(&rep.majorVersion, n);
	swaps(&rep.minorVersion, n);
    }
    WriteToClient(client, sizeof(xLbxQueryVersionReply), (char *)&rep);
    return (client->noClientException);
}

static int
NextProxyID (void)
{
    LbxProxyPtr proxy;
    int         id;

    for (id = 1; id < MAX_NUM_PROXIES; id++) {
	for (proxy = proxyList; proxy && proxy->pid != id; proxy = proxy->next)
	    ;
	if (!proxy)
	    return id;
    }
    return -1;
}

int
ProcLbxStartProxy (ClientPtr	client)
{
    REQUEST(xLbxStartProxyReq);
    LbxProxyPtr	    proxy;
    LbxClientPtr    lbxClient;
    int		    reqlen;
    int		    replylen;
    xLbxStartReply  *replybuf;
    LbxNegOptsRec   negopt;
    register int    n;
    pointer	    compHandle = NULL;

    REQUEST_AT_LEAST_SIZE(xLbxStartProxyReq);
    if (lbxClients[client->index])
	return BadLbxClientCode;
    proxy = (LbxProxyPtr) xalloc (sizeof (LbxProxyRec));
    if (!proxy)
	return BadAlloc;
    bzero(proxy, sizeof (LbxProxyRec));
    proxy->pid = NextProxyID();
    if (proxy->pid < 0) {	/* too many proxies */
	xfree(proxy);
	return BadAlloc;
    }
    proxy->uid = ++uid_seed;
    if (!proxyList)
	AddCallback(&ReplyCallback, LbxReplyCallback, NULL);

    if(!proxyList)
	proxyList = proxy;
    else{
	proxy->next = proxyList;
	proxyList = proxy;
    }

    /*
     * Don't know exactly how big the reply will be, but it won't be
     * bigger than the request
     */
    reqlen = client->req_len << 2;
    replybuf = (xLbxStartReply *) xalloc(max(reqlen, sz_xLbxStartReply));
    if (!replybuf) {
	LbxFreeProxy(proxy);
	return BadAlloc;
    }

    LbxOptionInit(&negopt);

    replylen = LbxOptionParse(&negopt,
			      (unsigned char *)&stuff[1],
			      reqlen - sz_xLbxStartProxyReq,
			      (unsigned char *)&replybuf->optDataStart);
    if (replylen < 0) {
	/*
	 * Didn't understand option format, so we'll just end up
	 * using the defaults.  Set nopts so that the proxy will
	 * be informed that we rejected the options because of
	 * decoding problems.
	 */
	LbxOptionInit(&negopt);
	negopt.nopts = 0xff;
	replylen = 0;
    }

    if (LBXInitDeltaCache(&proxy->indeltas, negopt.proxyDeltaN,
			  negopt.proxyDeltaMaxLen) < 0
			||
	LBXInitDeltaCache(&proxy->outdeltas, negopt.serverDeltaN,
			  negopt.serverDeltaMaxLen) < 0) {
	LbxFreeProxy(proxy);
	xfree(replybuf);
	return BadAlloc;
    }

    n = 0;
    if (negopt.proxyDeltaN)
	n = negopt.proxyDeltaMaxLen;
    if (negopt.serverDeltaN && negopt.serverDeltaMaxLen > n)
	n = negopt.serverDeltaMaxLen;
    if (n &&
	(!(proxy->iDeltaBuf = (char *)xalloc (n)) ||
	 !(proxy->replyBuf = (char *)xalloc (n)) ||
	 !(proxy->oDeltaBuf = (char *)xalloc (n)))) {
	LbxFreeProxy(proxy);
	xfree(replybuf);
	return BadAlloc;
    }

    MakeClientGrabImpervious(client);	/* proxy needs to be grab-proof */
    proxy->fd = ClientConnectionNumber(client);
    if (negopt.streamOpts.streamCompInit) {
	compHandle =
	    (*negopt.streamOpts.streamCompInit)(proxy->fd, negopt.streamOpts.streamCompArg);
	if (!compHandle) {
	    LbxFreeProxy(proxy);
	    xfree(replybuf);
	    return BadAlloc;
	}
    }
    proxy->ofirst = NULL;
    proxy->olast = NULL;
    if (!LbxInitClient (proxy, client, 0))
    {
	LbxFreeProxy(proxy);
	xfree(replybuf);
	return BadAlloc;
    }
    proxy->dosquishing = negopt.squish;
    proxy->numBitmapCompMethods = negopt.numBitmapCompMethods;
    proxy->bitmapCompMethods = negopt.bitmapCompMethods;
    proxy->numPixmapCompMethods = negopt.numPixmapCompMethods;
    proxy->pixmapCompMethods = negopt.pixmapCompMethods;
    proxy->pixmapCompDepths = negopt.pixmapCompDepths;

    proxy->streamOpts = negopt.streamOpts;
    proxy->useTags = negopt.useTags;

    proxy->grabbedCmaps = NULL;

    /* send reply */
    replybuf->type = X_Reply;
    replybuf->nOpts = negopt.nopts;
    replybuf->sequenceNumber = client->sequence;

    replylen += sz_xLbxStartReplyHdr;
    if (replylen < sz_xLbxStartReply)
	replylen = sz_xLbxStartReply;
    replybuf->length = (replylen - sz_xLbxStartReply + 3) >> 2;
    if (client->swapped) {
	swaps(&replybuf->sequenceNumber, n);
	swapl(&replybuf->length, n);
    }
    lbxClient = LbxClient(client);
    WriteToClient(client, replylen, (char *)replybuf);

    LbxProxyConnection(client, proxy);
    lbxClient = proxy->lbxClients[0];
    proxy->curDix = lbxClient;
    proxy->curRecv = lbxClient;
    proxy->compHandle = compHandle;

    if (proxy->compHandle && !lbxCompressWorkProcCount++)
	QueueWorkProc(LbxCheckCompressInput, NULL, NULL);

    xfree(replybuf);
    return Success;
}

int
ProcLbxStopProxy(ClientPtr client)
{
    /* REQUEST(xLbxStopProxyReq); */
    LbxProxyPtr	    proxy;
    LbxClientPtr    lbxClient = LbxClient(client);

    REQUEST_SIZE_MATCH(xLbxStopProxyReq);

    if (!lbxClient)
	return BadLbxClientCode;
    if (lbxClient->id)
	return BadLbxClientCode;
    
    proxy = lbxClient->proxy;
    LbxFreeClient (client);
    LbxShutdownProxy (proxy);
    return Success;
}
    
int
ProcLbxSwitch(ClientPtr	client)
{
    REQUEST(xLbxSwitchReq);
    LbxProxyPtr	proxy = LbxMaybeProxy(client);
    LbxClientPtr lbxClient;
    int i;

    REQUEST_SIZE_MATCH(xLbxSwitchReq);
    if (!proxy)
	return BadLbxClientCode;
    for (i = 0; i <= proxy->maxIndex; i++) {
	lbxClient = proxy->lbxClients[i];
	if (lbxClient && lbxClient->id == stuff->client) {
	    LbxSwitchRecv (proxy, lbxClient);
	    return Success;
	}
    }
    LbxSwitchRecv (proxy, NULL);
    return BadLbxClientCode;
}

int
ProcLbxBeginLargeRequest(ClientPtr client)
{
    REQUEST(xLbxBeginLargeRequestReq);

    client->sequence--;
    REQUEST_SIZE_MATCH(xLbxBeginLargeRequestReq);
    if (!AllocateLargeReqBuffer(client, stuff->largeReqLength << 2))
	return BadAlloc;
    return Success;
}


int
ProcLbxLargeRequestData(ClientPtr client)
{
    REQUEST(xLbxLargeRequestDataReq);

    client->sequence--;
    REQUEST_AT_LEAST_SIZE(xLbxLargeRequestDataReq);
    if (!AddToLargeReqBuffer(client, (char *) (stuff + 1),
			     (client->req_len - 1) << 2))
	return BadAlloc;
    return Success;
}


int
ProcLbxEndLargeRequest(ClientPtr client)
{
    /* REQUEST(xReq); */

    client->sequence--;
    REQUEST_SIZE_MATCH(xReq);
    return BadAlloc;
}


int
ProcLbxInternAtoms(ClientPtr client)
{
    REQUEST(xLbxInternAtomsReq);
    LbxClientPtr lbxClient = LbxClient(client);
    xLbxInternAtomsReply *replyRet;
    char *ptr = (char *) stuff + sz_xLbxInternAtomsReq;
    Atom *atomsRet;
    int replyLen, i;
    char lenbuf[2];
    CARD16 len;
    char n;

    REQUEST_AT_LEAST_SIZE(xLbxInternAtomsReq);

    if (!lbxClient)
	return BadLbxClientCode;
    if (lbxClient->id)
	return BadLbxClientCode;

    replyLen = sz_xLbxInternAtomsReplyHdr + stuff->num * sizeof (Atom);
    if (replyLen < sz_xLbxInternAtomsReply)
	replyLen = sz_xLbxInternAtomsReply;

    if (!(replyRet = (xLbxInternAtomsReply *) xalloc (replyLen)))
	return BadAlloc;

    atomsRet = (Atom *) ((char *) replyRet + sz_xLbxInternAtomsReplyHdr);

    for (i = 0; i < stuff->num; i++)
    {
	lenbuf[0] = ptr[0];
	lenbuf[1] = ptr[1];
	len = *((CARD16 *) lenbuf);
	ptr += 2;

	if ((atomsRet[i] = MakeAtom (ptr, len, TRUE)) == BAD_RESOURCE)
	{
	    xfree (replyRet);
	    return BadAlloc;
	}	    

	ptr += len;
    }

    if (client->swapped)
	for (i = 0; i < stuff->num; i++)
	    swapl (&atomsRet[i], n);

    replyRet->type = X_Reply;
    replyRet->sequenceNumber = client->sequence;
    replyRet->length = (replyLen - sz_xLbxInternAtomsReply + 3) >> 2;

    if (client->swapped) {
	swaps(&replyRet->sequenceNumber, n);
	swapl(&replyRet->length, n);
    }

    WriteToClient (client, replyLen, (char *) replyRet);

    xfree (replyRet);

    return Success;
}


int
ProcLbxGetWinAttrAndGeom(ClientPtr client)
{
    REQUEST(xLbxGetWinAttrAndGeomReq);
    xGetWindowAttributesReply wa;
    xGetGeometryReply wg;
    xLbxGetWinAttrAndGeomReply reply;
    WindowPtr pWin;
    int status;

    REQUEST_SIZE_MATCH(xLbxGetWinAttrAndGeomReq);
    pWin = (WindowPtr)SecurityLookupWindow(stuff->id, client,
					   SecurityReadAccess);
    if (!pWin)
        return(BadWindow);
    GetWindowAttributes(pWin, client, &wa);

    if ((status = GetGeometry(client, &wg)) != Success)
	return status;

    reply.type = X_Reply;
    reply.length = (sz_xLbxGetWinAttrAndGeomReply - 32) >> 2;
    reply.sequenceNumber = client->sequence;

    reply.backingStore = wa.backingStore;
    reply.visualID = wa.visualID;
#if defined(__cplusplus) || defined(c_plusplus)
    reply.c_class = wa.c_class;
#else
    reply.class = wa.class;
#endif
    reply.bitGravity = wa.bitGravity;
    reply.winGravity = wa.winGravity;
    reply.backingBitPlanes = wa.backingBitPlanes;
    reply.backingPixel = wa.backingPixel;
    reply.saveUnder = wa.saveUnder;
    reply.mapInstalled = wa.mapInstalled;
    reply.mapState = wa.mapState;
    reply.override = wa.override;
    reply.colormap = wa.colormap;
    reply.allEventMasks = wa.allEventMasks;
    reply.yourEventMask = wa.yourEventMask;
    reply.doNotPropagateMask = wa.doNotPropagateMask;
    reply.pad1 = 0;
    reply.root = wg.root;
    reply.x = wg.x;
    reply.y = wg.y;
    reply.width = wg.width;
    reply.height = wg.height;
    reply.borderWidth = wg.borderWidth;
    reply.depth = wg.depth;
    reply.pad2 = 0;

    if (client->swapped)
    {
	register char n;

	swaps(&reply.sequenceNumber, n);
	swapl(&reply.length, n);
	swapl(&reply.visualID, n);
	swaps(&reply.class, n);
	swapl(&reply.backingBitPlanes, n);
	swapl(&reply.backingPixel, n);
	swapl(&reply.colormap, n);
	swapl(&reply.allEventMasks, n);
	swapl(&reply.yourEventMask, n);
	swaps(&reply.doNotPropagateMask, n);
	swapl(&reply.root, n);
	swaps(&reply.x, n);
	swaps(&reply.y, n);
	swaps(&reply.width, n);
	swaps(&reply.height, n);
	swaps(&reply.borderWidth, n);
    }

    WriteToClient(client, sizeof(xLbxGetWinAttrAndGeomReply), (char *)&reply);
    return(client->noClientException);
}

int
ProcLbxNewClient(ClientPtr client)
{
    REQUEST(xLbxNewClientReq);
    ClientPtr	    newClient;
    LbxProxyPtr	    proxy = LbxMaybeProxy(client);
    CARD32	    id;
    int		    len, i;
    char	    *setupbuf;
    LbxClientPtr    lbxClient;

    REQUEST_AT_LEAST_SIZE(xLbxNewClientReq);

    /* save info before our request disappears */
    id = stuff->client;
    if (!proxy || !id)
	return BadLbxClientCode;
    if (proxy->numClients == MAX_LBX_CLIENTS)
	return BadAlloc;
    for (i = 1; i <= proxy->maxIndex; i++) {
	if (proxy->lbxClients[i] && proxy->lbxClients[i]->id == id)
	    return BadLbxClientCode;
    }
    len = (client->req_len << 2) - sizeof(xLbxNewClientReq);
    setupbuf = (char *)xalloc (len);
    if (!setupbuf)
	return BadAlloc;
    memcpy (setupbuf, (char *)&stuff[1], len);

    newClient = AllocLbxClientConnection (client, proxy);
    if (!newClient)
	return BadAlloc;
    newClient->requestVector = LbxInitialVector;
    lbxClient = LbxInitClient (proxy, newClient, id);
    if (!lbxClient)
    {
	CloseDownClient (newClient);
	return BadAlloc;
    }
    
    AppendFakeRequest (newClient, setupbuf, len);
    xfree (setupbuf);
    LbxSetForBlock(lbxClient);

    DBG (DBG_CLIENT, (stderr, "lbxNewClient X %d\n", newClient->index));
    return Success;
}

int
ProcLbxEstablishConnection(ClientPtr client)
{
    char *reason = NULL;
    char *auth_proto, *auth_string;
    register xConnClientPrefix *prefix;
    REQUEST(xReq);

    prefix = (xConnClientPrefix *)((char *)stuff + sz_xReq);
    auth_proto = (char *)prefix + sz_xConnClientPrefix;
    auth_string = auth_proto + ((prefix->nbytesAuthProto + 3) & ~3);
    if ((prefix->majorVersion != X_PROTOCOL) ||
	(prefix->minorVersion != X_PROTOCOL_REVISION))
	reason = "Protocol version mismatch";
    else
	reason = ClientAuthorized(client,
				  prefix->nbytesAuthProto,
				  auth_proto,
				  prefix->nbytesAuthString,
				  auth_string);

    if (client->clientState == ClientStateCheckingSecurity ||
	client->clientState == ClientStateAuthenticating)
	return (client->noClientException = -1); /* XXX some day */
    return(LbxSendConnSetup(client, reason));
}

int
ProcLbxCloseClient (ClientPtr client)
{
    REQUEST(xLbxCloseClientReq);
    LbxClientPtr lbxClient = LbxClient(client);

    REQUEST_SIZE_MATCH(xLbxCloseClientReq);
    if (!lbxClient || lbxClient->id != stuff->client)
	return BadLbxClientCode;

    /* this will cause the client to be closed down back in Dispatch() */
    return(client->noClientException = CloseLbxClient);
}

int
ProcLbxModifySequence (ClientPtr client)
{
    REQUEST(xLbxModifySequenceReq);

    REQUEST_SIZE_MATCH(xLbxModifySequenceReq);
    client->sequence += (stuff->adjust - 1);	/* Dispatch() adds 1 */
    return Success;
}

int
ProcLbxAllowMotion (ClientPtr client)
{
    REQUEST(xLbxAllowMotionReq);

    client->sequence--;
    REQUEST_SIZE_MATCH(xLbxAllowMotionReq);
    LbxAllowMotion(client, stuff->num);
    return Success;
}


static int
DecodeLbxDelta (ClientPtr client)
{
    REQUEST(xLbxDeltaReq);
    LbxClientPtr    lbxClient = LbxClient(client);
    LbxProxyPtr	    proxy = lbxClient->proxy;
    int		    len;
    unsigned char  *buf;

    /* Note that LBXDecodeDelta decodes and adds current msg to the cache */
    len = LBXDecodeDelta(&proxy->indeltas, 
			 (xLbxDiffItem *)(((char *)stuff) + sz_xLbxDeltaReq),
			 stuff->diffs, stuff->cindex, &buf);
    /*
     * Some requests, such as FillPoly, result in the protocol input
     * buffer being modified.  So we need to copy the request
     * into a temporary buffer where a write would be harmless.
     * Maybe some day do this copying on a case by case basis,
     * since not all requests are guilty of this.
     */
    memcpy(proxy->iDeltaBuf, buf, len);

    client->requestBuffer = proxy->iDeltaBuf;
    client->req_len = len >> 2;
    return len;
}

int
ProcLbxGetModifierMapping(ClientPtr client)
{
    /* REQUEST(xLbxGetModifierMappingReq); */

    REQUEST_SIZE_MATCH(xLbxGetModifierMappingReq);
    return LbxGetModifierMapping(client);
}

int
ProcLbxGetKeyboardMapping(ClientPtr client)
{
    /* REQUEST(xLbxGetKeyboardMappingReq); */

    REQUEST_SIZE_MATCH(xLbxGetKeyboardMappingReq);
    return LbxGetKeyboardMapping(client);
}

int
ProcLbxQueryFont(ClientPtr client)
{
    /* REQUEST(xLbxQueryFontReq); */

    REQUEST_SIZE_MATCH(xLbxQueryFontReq);
    return LbxQueryFont(client);
}

int
ProcLbxChangeProperty(ClientPtr	client)
{
    /* REQUEST(xLbxChangePropertyReq); */

    REQUEST_SIZE_MATCH(xLbxChangePropertyReq);
    return LbxChangeProperty(client);
}

int
ProcLbxGetProperty(ClientPtr client)
{
    /* REQUEST(xLbxGetPropertyReq); */

    REQUEST_SIZE_MATCH(xLbxGetPropertyReq);
    return LbxGetProperty(client);
}

int
ProcLbxTagData(ClientPtr client)
{
    REQUEST(xLbxTagDataReq);

    client->sequence--;		/* not a counted request */
    REQUEST_AT_LEAST_SIZE(xLbxTagDataReq);

    return LbxTagData(client, stuff->tag, stuff->real_length,
    		 (pointer)&stuff[1]);	/* better not give any errors */
}

int
ProcLbxInvalidateTag(ClientPtr client)
{
    REQUEST(xLbxInvalidateTagReq);

    client->sequence--;
    REQUEST_SIZE_MATCH(xLbxInvalidateTagReq);
    return LbxInvalidateTag(client, stuff->tag);
}

int
ProcLbxPolyPoint(ClientPtr client)
{
    return LbxDecodePoly(client, X_PolyPoint, LbxDecodePoints);
}

int
ProcLbxPolyLine(ClientPtr client)
{
    return LbxDecodePoly(client, X_PolyLine, LbxDecodePoints);
}

int
ProcLbxPolySegment(ClientPtr client)
{
    return LbxDecodePoly(client, X_PolySegment, LbxDecodeSegment);
}

int
ProcLbxPolyRectangle(ClientPtr client)
{
    return LbxDecodePoly(client, X_PolyRectangle, LbxDecodeRectangle);
}

int
ProcLbxPolyArc(ClientPtr client)
{
    return LbxDecodePoly(client, X_PolyArc, LbxDecodeArc);
}

int
ProcLbxFillPoly(ClientPtr client)
{
    return LbxDecodeFillPoly(client);
}

int
ProcLbxPolyFillRectangle(ClientPtr client)
{
    return LbxDecodePoly(client, X_PolyFillRectangle, LbxDecodeRectangle);
}

int
ProcLbxPolyFillArc(ClientPtr client)
{
    return LbxDecodePoly(client, X_PolyFillArc, LbxDecodeArc);
}

int
ProcLbxCopyArea(ClientPtr client)
{
    return LbxDecodeCopyArea(client);
}

int
ProcLbxCopyPlane(ClientPtr client)
{
    return LbxDecodeCopyPlane(client);
}


int
ProcLbxPolyText(ClientPtr client)
{
    return LbxDecodePolyText(client);
}

int
ProcLbxImageText(ClientPtr client)
{
    return LbxDecodeImageText(client);
}

int
ProcLbxQueryExtension(ClientPtr	client)
{
    REQUEST(xLbxQueryExtensionReq);
    char	*ename;

    REQUEST_AT_LEAST_SIZE(xLbxQueryExtensionReq);
    ename = (char *) &stuff[1];
    return LbxQueryExtension(client, ename, stuff->nbytes);
}

int
ProcLbxPutImage(ClientPtr client)
{
    return LbxDecodePutImage(client);
}

int
ProcLbxGetImage(ClientPtr client)
{
    return LbxDecodeGetImage(client);
}


int
ProcLbxSync(ClientPtr client)
{
    xLbxSyncReply reply;

    client->sequence--;		/* not a counted request */

#ifdef COLOR_DEBUG
    fprintf (stderr, "Got LBX sync, seq = 0x%x\n", client->sequence);
#endif

    reply.type = X_Reply;
    reply.length = 0;
    reply.sequenceNumber = client->sequence;
    reply.pad0 = reply.pad1 = reply.pad2 = reply.pad3 = reply.pad4 = 
        reply.pad5 = reply.pad6 = 0;

    if (client->swapped)
    {
	register char n;
	swaps (&reply.sequenceNumber, n);
    }

    WriteToClient (client, sz_xLbxSyncReply, (char *)&reply);

    return (client->noClientException);
}


int
ProcLbxDispatch(ClientPtr client)
{
    REQUEST(xReq);
    switch (stuff->data)
    {
    case X_LbxQueryVersion:
	return ProcLbxQueryVersion(client);
    case X_LbxStartProxy:
	return ProcLbxStartProxy(client);
    case X_LbxStopProxy:
	return ProcLbxStopProxy(client);
    case X_LbxNewClient:
	return ProcLbxNewClient(client);
    case X_LbxCloseClient:
	return ProcLbxCloseClient(client);
    case X_LbxModifySequence:
	return ProcLbxModifySequence(client);
    case X_LbxAllowMotion:
	return ProcLbxAllowMotion(client);
    case X_LbxIncrementPixel:
	return ProcLbxIncrementPixel(client);
    case X_LbxGrabCmap:
	return ProcLbxGrabCmap(client);
    case X_LbxReleaseCmap:
	return ProcLbxReleaseCmap(client);
    case X_LbxAllocColor:
	return ProcLbxAllocColor(client);
    case X_LbxGetModifierMapping:
	return ProcLbxGetModifierMapping(client);
    case X_LbxGetKeyboardMapping:
	return ProcLbxGetKeyboardMapping(client);
    case X_LbxInvalidateTag:
	return ProcLbxInvalidateTag(client);
    case X_LbxPolyPoint:
	return ProcLbxPolyPoint (client);
    case X_LbxPolyLine:
	return ProcLbxPolyLine (client);
    case X_LbxPolySegment:
	return ProcLbxPolySegment (client);
    case X_LbxPolyRectangle:
	return ProcLbxPolyRectangle (client);
    case X_LbxPolyArc:
	return ProcLbxPolyArc (client);
    case X_LbxFillPoly:
	return ProcLbxFillPoly (client);
    case X_LbxPolyFillRectangle:
	return ProcLbxPolyFillRectangle (client);
    case X_LbxPolyFillArc:
	return ProcLbxPolyFillArc (client);
    case X_LbxQueryFont:
	return ProcLbxQueryFont (client);
    case X_LbxChangeProperty:
	return ProcLbxChangeProperty (client);
    case X_LbxGetProperty:
	return ProcLbxGetProperty (client);
    case X_LbxTagData:
	return ProcLbxTagData (client);
    case X_LbxCopyArea:
	return ProcLbxCopyArea (client);
    case X_LbxCopyPlane:
	return ProcLbxCopyPlane (client);
    case X_LbxPolyText8:
    case X_LbxPolyText16:
	return ProcLbxPolyText (client);
    case X_LbxImageText8:
    case X_LbxImageText16:
	return ProcLbxImageText (client);
    case X_LbxQueryExtension:
	return ProcLbxQueryExtension (client);
    case X_LbxPutImage:
	return ProcLbxPutImage (client);
    case X_LbxGetImage:
	return ProcLbxGetImage (client);
    case X_LbxInternAtoms:
	return ProcLbxInternAtoms(client);
    case X_LbxGetWinAttrAndGeom:
	return ProcLbxGetWinAttrAndGeom(client);
    case X_LbxSync:
	return ProcLbxSync(client);
    case X_LbxBeginLargeRequest:
	return ProcLbxBeginLargeRequest(client);
    case X_LbxLargeRequestData:
	return ProcLbxLargeRequestData(client);
    case X_LbxEndLargeRequest:
	return ProcLbxLargeRequestData(client);
    default:
	return BadRequest;
    }
}
