/* $Xorg: lbxfuncs.c,v 1.3 2000/08/17 19:53:55 cpqbld Exp $ */
/*
 * Copyright 1994 Network Computing Devices, Inc.
 * Copyright 1996 X Consortium, Inc.
 *
 * 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 Network Computing Devices, Inc. not be
 * used in advertising or publicity pertaining to distribution of this
 * software without specific, written prior permission.
 *
 * THIS SOFTWARE IS PROVIDED `AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
 * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
 * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
 * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
 * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
 * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */
/* $XFree86: xc/programs/lbxproxy/di/lbxfuncs.c,v 1.8 2004/04/03 22:38:53 tsi Exp $ */

/*
 * top level LBX request & reply handling
 */

/*
 * requests copy out interesting stuff and then swap so original data
 * is left alone as much as possible.  note that the length field
 * is *not* swapped
 *
 * replied data is copied yet again before swapping because the data
 * may be stored as a tag result and we don't want to change that.
 */


#include	<stdio.h>
#include	"assert.h"
#include	"lbx.h"
#include        "gfx.h"
#include	"atomcache.h"
#include	"util.h"
#include	"init.h"
#include	"tags.h"
#include	"resource.h"
#include	"wire.h"
#include	"swap.h"
#include	"reqtype.h"
#include	"lbxext.h"
#include        "lbxfuncs.h"
#include	"proxyopts.h"

static Bool intern_atom_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data);
static Bool get_atom_name_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data);
static Bool get_mod_map_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data);
static Bool get_key_map_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data);
static Bool sync_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data);
static Bool get_queryfont_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data);
static Bool GetWinAttrAndGeomReply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data);

#define reply_length(cp,rep) ((rep)->type==X_Reply ? \
        32 + (HostUnswapLong((cp),(rep)->length) << 2) \
	: 32)

char protocolMode = PROTOCOL_FULL;

/* ARGSUSED */
static void 
get_connection_info(
    ClientPtr   client,
    xConnSetup *cs,
    int		cs_len,
    int		change_type,
    CARD32     *changes,
    int		changes_len)
{
    xWindowRoot *root;
    xDepth     *depth;
    xVisualType *vis;
    char       *dp = (char *) cs;
    int         i,
                j,
                k;
    static int  pad[4] = {0, 3, 2, 1};

    if (!changes)
	return;

    cs->ridBase = changes[0];
    dp += sizeof(xConnSetup);
    /* skip vendor string & pixmap formats */
    dp += cs->nbytesVendor + pad[cs->nbytesVendor & 3];
    for (i = 0; i < cs->numFormats; i++) {
	if (((xPixmapFormat *)dp)->depth == 1)
	    client->ZbitsPerPixel1 = ((xPixmapFormat *)dp)->bitsPerPixel;
	dp += sizeof(xPixmapFormat);
    }
    /* process screens */
    root = (xWindowRoot *) dp;
    if (change_type == 2) {
	if (changes[2])
	    root->rootVisualID = (VisualID) changes[2];
	if (changes[3]) {
	    root->defaultColormap = (Colormap) changes[3];
	    root->whitePixel = changes[4];
	    root->blackPixel = changes[5];
	}
    }
    client->rootWindow = root->windowId;
    for (i = 0; i < cs->numRoots; i++) {
	if (change_type != 2) {
	    root->currentInputMask = changes[i + 1];
	} else {
	    root->currentInputMask = changes[i + 6];
	}
	dp += sizeof(xWindowRoot);
	for (j = 0; j < root->nDepths; j++) {
	    depth = (xDepth *) dp;
	    dp += sizeof(xDepth);
	    for (k = 0; k < depth->nVisuals; k++) {
		vis = (xVisualType *) dp;
		CreateVisual(depth->depth, vis);
		dp += (sizeof(xVisualType));
		vis = (xVisualType *) dp;
	    }
	}
	if (!LookupIDByType(client, root->defaultColormap, RT_COLORMAP))
	    CreateColormap(client,
			   root->defaultColormap, root->rootVisualID);
    }
}

static void
send_setup_reply(ClientPtr client, Bool success, int majorVer, int minorVer,
		 void *cs, int cs_len)
{
    xConnSetupPrefix reply;

    reply.success = success;
    if (!success) {
	reply.lengthReason = strlen((char *)cs);
	cs_len = reply.lengthReason + 3;
    }
    reply.majorVersion = majorVer;
    reply.minorVersion = minorVer;
    reply.length = cs_len >> 2;

    if (client->swapped)
	WriteSConnSetupPrefix(client, &reply);
    else
	(void)WriteToClient(client, sizeof(xConnSetupPrefix), (char *) &reply);
	
    if (success && client->swapped)
	WriteSConnectionInfo(client, (unsigned long)(reply.length << 2), cs);
    else
	(void)WriteToClient(client, (int)(reply.length << 2), cs);
}

static void
finish_setup_reply(
    ClientPtr   client,
    xConnSetup *cs,
    int         cs_len,
    int		change_type,
    CARD32*     changes,
    int		changes_len,
    int		majorVer,
    int         minorVer)
{
    get_connection_info(client, cs, cs_len, change_type, changes, changes_len);
    client->minKeyCode = cs->minKeyCode;
    client->maxKeyCode = cs->maxKeyCode;
    client->imageByteOrder = cs->imageByteOrder;
    client->bitmapBitOrder = cs->bitmapBitOrder;
    client->bitmapScanlineUnit = cs->bitmapScanlineUnit;
    client->bitmapScanlinePad = cs->bitmapScanlinePad;
    client->ridBase = cs->ridBase;
    client->ridMask = cs->ridMask;
    FinishInitClientResources (client, cs->ridBase, cs->ridMask);
    send_setup_reply(client, TRUE, majorVer, minorVer, cs, cs_len);
}

/* 
 * ConnectionSetup data can come from three places: 1) a fully qualified
 * connection-setup in the reply, 2) from the tag store, and 3) built 
 * from the Display* returned by the call the proxy made to XOpenDisplay.
 */

static void
get_setup_reply(
    ClientPtr   client,
    char       *data,
    int		len)
{
    register xLbxConnSetupPrefix *rep;
    TagData     td;
    xConnSetup *tag_data;
    Bool	free_td = FALSE;
    CARD32*     changes = NULL;
    int		changes_len = 0;

    rep = (xLbxConnSetupPrefix *) data;
    if (client->swapped) {
	data = (char *)ALLOCATE_LOCAL(len);
	memcpy(data, (char *)rep, len);
	rep = (xLbxConnSetupPrefix *) data;
	SwapLbxConnSetupPrefix(rep);
    }
    switch (rep->changeType) {
    default:
    case 0:	/* full ConnectionSetup, possibly with tag */
#ifdef LBX_STATS
	getsetup_full++;
#endif
	tag_data = (xConnSetup *) &rep[1];
	if (rep->tag != 0) {
	    if (!TagStoreData(client->server, client->server->global_cache, 
			      rep->tag, rep->length,
			      LbxTagTypeConnInfo, tag_data)) {
		/* tell server we lost it */
		SendInvalidateTag(client, rep->tag);
	    }
	}
	len = rep->length << 2;
	break;

    case 1:	/* LbxNormalDeltas */
    case 2:	/* LbxEmbeddedAppGroup */
	changes = (CARD32*) &rep[1];
	changes_len = rep->length - 1;
	if (client->swapped)
	    SwapLongs(changes, changes_len);

	if (rep->tag != 0) {
	    td = TagGetTag(client->server, client->server->global_cache, 
			   rep->tag);
	    if (!td) {
		fprintf(stderr, "no data for setup tag 0x%lx\n",
			(long)rep->tag);
		send_setup_reply(client, FALSE, 0, 0, "bad tag data from server", 0);
		if (client->swapped)
		    DEALLOCATE_LOCAL(data);
		return;
	    }
#ifdef LBX_STATS
	    getsetup_tag++;
	    tag_bytes_unsent += td->size - len;
#endif
	    tag_data = td->tdata;
	    len = td->size;
	} else {
	    DisplayGetConnSetup (client->server->dpy, &tag_data, &len, 
				 rep->changeType, changes, changes_len);
	    free_td = TRUE;
	}
	break;

    }
    finish_setup_reply(client, tag_data, len, 
		       rep->changeType, changes, changes_len,
		       (int) rep->majorVersion, (int) rep->minorVersion);
    if (free_td) xfree (tag_data);
    if (client->swapped)
	DEALLOCATE_LOCAL(data);
}

int
ProcLBXInternAtom(
    ClientPtr   client)
{
    REQUEST(xInternAtomReq);
    char       *s;
    Atom        atom,
                a = None;
    xInternAtomReply reply;
    ReplyStuffPtr nr;
    char        n;
    CARD16      nbytes;

    nbytes = stuff->nbytes;
    if (client->swapped) {
	swaps(&nbytes, n);
    }
    if (nbytes > MAX_ATOM_LENGTH)
	return ProcStandardRequest(client);

    s = (char *)stuff + sizeof(xInternAtomReq);

    atom = LbxMakeAtom(client->server, s, nbytes, a, FALSE);
    if (atom != None) {
	reply.type = X_Reply;
	reply.length = 0;
	reply.sequenceNumber = LBXSequenceNumber(client);
	reply.atom = atom;
	if (client->swapped)
	    SwapInternAtomReply(&reply);
	if (LBXCacheSafe(client)) {
	    FinishLBXRequest(client, REQ_YANK);
	    WriteToClient(client, sizeof(xInternAtomReply), (char*)&reply);
	} else {			/* store for later */
	    if (!LBXCanDelayReply(client))
		SendLbxSync(client);
	    FinishLBXRequest(client, REQ_YANKLATE);
	    SaveReplyData(client, (xReply *) & reply, 0, NULL);
	}

#ifdef LBX_STATS
	intern_good++;
#endif

	return Success;
    } else if (nbytes < MAX_ATOM_LENGTH) {
	nr = NewReply(client, X_InternAtom, 0, intern_atom_reply);
	if (!nr)
	    return ProcStandardRequest(client);
	strncpy(nr->request_info.xintern.str, s, nbytes);
	nr->request_info.xintern.str[nbytes] = '\0';
	nr->request_info.xintern.len = nbytes;

#ifdef LBX_STATS
	intern_miss++;
#endif

	return ProcStandardRequest(client);
    }

    return 0;		/* ?!?!? */
}

static Bool
intern_atom_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data)
{
    Atom        atom;
    char       *str;
    xInternAtomReply *reply;
    int         len;
    char        n;

    reply = (xInternAtomReply *) data;

    str = nr->request_info.xintern.str;
    len = nr->request_info.xintern.len;

    atom = reply->atom;
    if (client->swapped) {
	swapl(&atom, n);
    }
    if (atom != None)
	/* make sure it gets stuffed in the DB */
	(void) LbxMakeAtom(client->server, str, len, atom, TRUE);
    return TRUE;
}

int
ProcLBXGetAtomName(
    ClientPtr   client)
{
    REQUEST(xResourceReq);
    char       *str;
    xGetAtomNameReply reply;
    int         len;
    ReplyStuffPtr nr;
    char        n;
    Atom        id;

    id = stuff->id;
    if (client->swapped) {
	swapl(&id, n);
    }
    str = NameForAtom(client->server, id);

    if (str) {	/* found the value */
	len = strlen(str);
	reply.type = X_Reply;
	reply.length = (len + 3) >> 2;
	reply.sequenceNumber = LBXSequenceNumber(client);
	reply.nameLength = len;
	if (client->swapped)
	    SwapGetAtomNameReply(&reply);
	if (LBXCacheSafe(client)) {
	    FinishLBXRequest(client, REQ_YANK);
	    WriteToClient(client, sizeof(xGetAtomNameReply), (char*)&reply);
	    WriteToClient(client, len, str);
	} else {
	    if (!LBXCanDelayReply(client))
		SendLbxSync(client);
	    FinishLBXRequest(client, REQ_YANKLATE);
	    SaveReplyData(client, (xReply *) & reply, len, str);
	}

#ifdef LBX_STATS
	getatom_good++;
#endif

	return Success;
    } else {
	nr = NewReply(client, X_GetAtomName, 0, get_atom_name_reply);
	if (!nr)
	    return ProcStandardRequest(client);
	nr->request_info.xgetatom.atom = id;

#ifdef LBX_STATS
	getatom_miss++;
#endif

	return ProcStandardRequest(client);
    }
}

static Bool
get_atom_name_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data)
{
    Atom        atom;
    char       *s;
    xGetAtomNameReply *reply;
    CARD16	len;
    char	n;

    reply = (xGetAtomNameReply *) data;

    len = reply->nameLength;

    if (client->swapped) {
	swaps(&len, n);
    }

    if (len > MAX_ATOM_LENGTH)
	return FALSE;

    s = data + sizeof(xGetAtomNameReply);

    atom = nr->request_info.xgetatom.atom;
    /* make sure it gets stuffed in the DB */
    if (atom)
	(void) LbxMakeAtom(client->server, s, (unsigned) len, atom, TRUE);
    return TRUE;
}

/*
 * Send an LbxSync request to the server.  After the SyncReply comes
 * back, the proxy will flush any replies it saved for the client.
 */

void
SendLbxSync (
    ClientPtr client)
{
    xLbxSyncReq req;
    ReplyStuffPtr nr;

#ifdef SEQ_DEBUG
    fprintf(stderr, "sending LbxSync, seq 0x%x\n", LBXSequenceNumber(client) - 1);
#endif

    ForceSequenceUpdate(client);
    nr = NewReply(client, client->server->lbxReq, X_LbxSync, sync_reply);
    --nr->sequenceNumber; /* executed BEFORE the real request */
    req.reqType = client->server->lbxReq;
    req.lbxReqType = X_LbxSync;
    req.length = sz_xLbxSyncReq >> 2;

    WriteReqToServer (client, sz_xLbxSyncReq, (char *) &req, TRUE);
}

/* ARGSUSED */
static Bool
sync_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data)
{
    /* do nothing; just here to catch and discard the reply */
    return TRUE;
}

void
WriteError(
    ClientPtr client,
    unsigned int majorCode,
    unsigned int minorCode,
    XID resId,
    int errorCode)
{
    xError      rep;
    int		n;

    rep.type = X_Error;
    rep.sequenceNumber = LBXSequenceNumber(client);
    rep.errorCode = errorCode;
    rep.majorCode = majorCode;
    rep.minorCode = minorCode;
    rep.resourceID = resId;

    if (client->swapped) {
	swaps(&rep.sequenceNumber, n);
	swaps(&rep.minorCode, n);
	swaps(&rep.resourceID, n);
    }

    WriteToClient(client, sizeof(rep), (char *)&rep);
}

int
ProcLBXGetModifierMapping(
    ClientPtr   client)
{
    ReplyStuffPtr nr;

    nr = NewReply(client, client->server->lbxReq, X_LbxGetModifierMapping,
		  get_mod_map_reply);
    if (!nr)
	return ProcStandardRequest(client);

    FinishLBXRequest(client, REQ_PASSTHROUGH);
    SendGetModifierMapping(client);
    return Success;
}

/*ARGSUSED*/
static Bool
get_mod_map_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data)
{
    xLbxGetModifierMappingReply *rep;
    int         len;
    pointer     tag_data;
    CARD32      tag;
    char        n;
    xGetModifierMappingReply reply;

    rep = (xLbxGetModifierMappingReply *) data;

    tag = rep->tag;
    if (client->swapped) {
	swapl(&tag, n);
    }
    len = rep->keyspermod << 3;
    if (rep->tag) {
	if (rep->length) {	/* first time, set tag */

#ifdef LBX_STATS
	    getmodmap_full++;
#endif

	    tag_data = (pointer) &rep[1];
	    if (!TagStoreData(client->server, client->server->global_cache, 
			      tag, len,
			      LbxTagTypeModmap, tag_data)) {
		/* tell server we lost it */
		SendInvalidateTag(client, tag);
	    }
	} else {
	    tag_data = TagGetData(client->server, client->server->global_cache, 
				  tag);
	    if (!tag_data) {
		fprintf(stderr, "no data for mod map tag 0x%lx\n", (long)tag);
		WriteError(client, X_GetModifierMapping, 0, 0, BadAlloc);
		return TRUE;
	    }

#ifdef LBX_STATS
	    getmodmap_tag++;
	    tag_bytes_unsent += (rep->keyspermod << 3);
#endif
	}
    } else {

#ifdef LBX_STATS
	getmodmap_full++;
#endif

	/* server didn't send us a tag for some reason -- just pass on data */
	tag_data = (pointer) &rep[1];
    }

    reply.type = X_Reply;
    reply.numKeyPerModifier = rep->keyspermod;
    reply.sequenceNumber = rep->sequenceNumber;
    reply.length = len >> 2;
    if (client->swapped)
	SwapModmapReply(&reply);
    WriteToClient(client, sizeof(xGetModifierMappingReply), (char*)&reply);
    WriteToClient(client, len, tag_data);

    return TRUE;
}

int
ProcLBXGetKeyboardMapping(
    ClientPtr   client)
{
    REQUEST(xGetKeyboardMappingReq);
    ReplyStuffPtr nr;

    nr = NewReply(client, client->server->lbxReq, X_LbxGetKeyboardMapping,
		  get_key_map_reply);
    if (!nr)
	return ProcStandardRequest(client);
    nr->request_info.lbxgetkeymap.count = stuff->count;
    nr->request_info.lbxgetkeymap.first = stuff->firstKeyCode;


    FinishLBXRequest(client, REQ_PASSTHROUGH);
    SendGetKeyboardMapping(client);
    return Success;
}

/*
 * always ask for the whole map from server, and send requested subset to
 * client
 */

static Bool
get_key_map_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data)
{
    xLbxGetKeyboardMappingReply *rep;
    int         len;
    pointer     tag_data;
    CARD32      tag;
    char        n;
    xGetKeyboardMappingReply reply;
    char	*sdata;

    rep = (xLbxGetKeyboardMappingReply *) data;

    tag = rep->tag;
    if (client->swapped) {
	swapl(&tag, n);
    }
    if (tag) {
	if (rep->length) {	/* first time, set tag */

#ifdef LBX_STATS
	    getkeymap_full++;
#endif

	    tag_data = (pointer) &rep[1];
	    len = rep->keysperkeycode *
		(LBXMaxKeyCode(client) - LBXMinKeyCode(client) + 1) * 4;
	    /* data always swapped, because reswapped when written */
	    if (client->swapped)
		SwapLongs((CARD32 *) tag_data, len / 4);
	    if (!TagStoreData(client->server, client->server->global_cache, 
			      tag, len,
			      LbxTagTypeKeymap, tag_data)) {
		/* tell server we lost it */
		SendInvalidateTag(client, tag);
	    }
	} else {
	    tag_data = TagGetData(client->server, 
				  client->server->global_cache, tag);
	    if (!tag_data) {
		fprintf(stderr, "no data for key map tag 0x%lx\n", (long)tag);
		WriteError(client, X_GetKeyboardMapping, 0, 0, BadAlloc);
		return TRUE;
	    }

#ifdef LBX_STATS
	    getkeymap_tag++;
	    tag_bytes_unsent += (rep->keysperkeycode *
		    (LBXMaxKeyCode(client) - LBXMinKeyCode(client) + 1) * 4);
#endif
	}
    } else {

#ifdef LBX_STATS
	getkeymap_full++;
#endif

	/* server didn't send us a tag for some reason -- just pass on data */
	tag_data = (pointer) &rep[1];
	/* data always swapped, because reswapped when written */
	if (client->swapped) {
	    len = rep->keysperkeycode *
		(LBXMaxKeyCode(client) - LBXMinKeyCode(client) + 1) * 4;
	    SwapLongs((CARD32 *) tag_data, len / 4);
	}
    }

    len = (rep->keysperkeycode * nr->request_info.lbxgetkeymap.count) << 2;

    reply.type = X_Reply;
    reply.keySymsPerKeyCode = rep->keysperkeycode;
    reply.sequenceNumber = rep->sequenceNumber;
    reply.length = len >> 2;

    tag_data = (char *)tag_data + (rep->keysperkeycode *
				   (nr->request_info.lbxgetkeymap.first -
				    LBXMinKeyCode(client)));
    if (client->swapped) {
	SwapKeymapReply(&reply);
	/* have to copy data because we could be handed the tag storage */
	sdata = (char *) ALLOCATE_LOCAL(len);
	if (sdata) {
	    memcpy(sdata, tag_data, len);
	    SwapLongs((CARD32 *) sdata, len / 4);
	}
    } else
	sdata = tag_data;
    WriteToClient(client, sizeof(xGetKeyboardMappingReply), (char*)&reply);
    WriteToClient(client, len, sdata);
    if (sdata != tag_data)
	DEALLOCATE_LOCAL(sdata);

    return TRUE;
}

int
ProcLBXQueryFont(
    ClientPtr   client)
{
    REQUEST(xResourceReq);
    ReplyStuffPtr nr;
    Font        fid;
    char        n;

    fid = stuff->id;
    if (client->swapped) {
	swapl(&fid, n);
    }
    nr = NewReply(client, client->server->lbxReq, X_LbxQueryFont,
		  get_queryfont_reply);
    if (!nr)
	return ProcStandardRequest(client);

    FinishLBXRequest(client, REQ_PASSTHROUGH);
    SendQueryFont(client, fid);
    return Success;
}

static INT16
unpack_val(
    CARD32      val,
    CARD32      mask,
    int         sft,
    int         bts)
{
    CARD16      utmp;
    INT16       sval;

    /* get the proper value */
    utmp = (val & mask) >> sft;
    /* push the sign bit to the right spot */
    utmp <<= (16 - bts);
    /* cast it so sign bit takes effect */
    sval = (INT16) utmp;
    /* shift back down */
    sval >>= (16 - bts);

    return sval;
}

/*ARGSUSED*/
static int
UnsquishFontInfo(int compression, xLbxFontInfo *fdata, int dlen, pointer *qfr)
{
    int         len,
                hlen,
                junklen = sizeof(BYTE) * 2 + sizeof(CARD16) + sizeof(CARD32);
    char *t;
    int         nchars;
    int         i;
    xCharInfo  *maxb;
    xQueryFontReply *new;
    xLbxCharInfo *lci;
    xCharInfo  *ci;
    CARD16       attrs;

    maxb = &fdata->maxBounds;

    nchars = fdata->nCharInfos;
    hlen = sizeof(xQueryFontReply) + fdata->nFontProps * sizeof(xFontProp);
    len = hlen + nchars * sizeof(xCharInfo);

    new = (xQueryFontReply *) xalloc(len);
    *qfr = (pointer) new;

    if (!new)			/* XXX bad stuff... */
	return 0;

    /* copy the header & props parts */
    t = (char *) new;
    t += junklen;
    if (compression) {
	memcpy((char *) t, (char *) fdata, hlen - junklen);
    } else {
	memcpy((char *) t, (char *) fdata, len - junklen);
	return len;
    }

    attrs = maxb->attributes;

    t = (char *) fdata;
    t += hlen - junklen;
    lci = (xLbxCharInfo *) t;

    t = (char *) new;
    t += hlen;
    ci = (xCharInfo *) t;

    /* now expand the chars */
    for (i = 0; i < nchars; i++, lci++, ci++) {
	if (lci->metrics == 0) {
	    /* empty char */
	    ci->characterWidth = ci->leftSideBearing = ci->rightSideBearing =
		ci->ascent = ci->descent = ci->attributes = 0;
	} else {
	    ci->characterWidth =
	        unpack_val(lci->metrics, LBX_WIDTH_MASK,
		       LBX_WIDTH_SHIFT, LBX_WIDTH_BITS);
	    ci->leftSideBearing = unpack_val(lci->metrics, LBX_LEFT_MASK,
					 LBX_LEFT_SHIFT, LBX_LEFT_BITS);
	    ci->rightSideBearing = unpack_val(lci->metrics, LBX_RIGHT_MASK,
					  LBX_RIGHT_SHIFT, LBX_RIGHT_BITS);
	    ci->ascent = unpack_val(lci->metrics, LBX_ASCENT_MASK,
				LBX_ASCENT_SHIFT, LBX_ASCENT_BITS);
	    ci->descent = unpack_val(lci->metrics, LBX_DESCENT_MASK,
				 LBX_DESCENT_SHIFT, LBX_DESCENT_BITS);
	    ci->attributes = attrs;
	}
    }

    return len;
}

/*ARGSUSED*/
static Bool
get_queryfont_reply(
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data)
{
    xLbxQueryFontReply *rep;
    int         len,
                sqlen;
    pointer     tag_data,
                sqtag_data;
    TagData     td;
    CARD32      tag;
    char        n;
    xQueryFontReply *reply;

    rep = (xLbxQueryFontReply *) data;

    tag = rep->tag;
    if (client->swapped) {
	swapl(&tag, n);
    }
    if (tag) {
	if (rep->length) {	/* first time, set tag */

#ifdef LBX_STATS
	    queryfont_full++;
#endif

	    sqtag_data = (pointer) &rep[1];
	    sqlen = rep->length << 2;
	    if (client->swapped)
		LbxSwapFontInfo(sqtag_data, rep->compression);

	    /*
	     * store squished version of data
	     */
	    if (!TagStoreData(client->server, client->server->global_cache, 
				      tag, sqlen,
				      LbxTagTypeFont, sqtag_data)) {
		/* tell server we lost it */
		SendInvalidateTag(client, tag);
	    }
	    len = UnsquishFontInfo(rep->compression, sqtag_data, sqlen,
				   &tag_data);
	} else {
	    td = TagGetTag(client->server, client->server->global_cache, tag);
	    if (!td) {
		fprintf(stderr, "no data for font tag 0x%lx\n", (long)tag);
		WriteError(client, X_QueryFont, 0, 0, BadAlloc);
		return TRUE;
	    }
	    sqlen = td->size;
	    sqtag_data = td->tdata;
	    len = UnsquishFontInfo(rep->compression, sqtag_data, sqlen,
						&tag_data);

#ifdef LBX_STATS
	    queryfont_tag++;
	    tag_bytes_unsent += len;
#endif
	}
    } else {

#ifdef LBX_STATS
	queryfont_full++;
#endif

	/* server didn't send us a tag for some reason -- just pass on data */
	sqtag_data = (pointer) &rep[1];
	if (client->swapped)
	    LbxSwapFontInfo(sqtag_data, FALSE);
	sqlen = rep->length << 2;
	len = UnsquishFontInfo(rep->compression, sqtag_data, sqlen, &tag_data);
    }

    reply = (xQueryFontReply *) ALLOCATE_LOCAL(len);
    if (reply) {
	memcpy((char *) reply, (char *) tag_data, len);
	/* patch up certain fields */
	reply->type = X_Reply;
	reply->sequenceNumber = rep->sequenceNumber;
	reply->length = (len - sizeof(xGenericReply)) >> 2;

	len -= sizeof(xQueryFontReply);
	if (client->swapped)
	    SwapFont(reply, TRUE);
	WriteToClient(client, sizeof(xQueryFontReply), (char*)reply);
	WriteToClient(client, len, (char*)(reply + 1));
	DEALLOCATE_LOCAL(reply);
    }

    xfree(tag_data);		/* free unsquished version */

    return TRUE;
}


int
ProcLBXGetWindowAttributes(
    ClientPtr   client)
{
    REQUEST(xResourceReq);
    xLbxGetWinAttrAndGeomReq newreq;
    XServerPtr server = client->server;
    ReplyStuffPtr nr;

    /*
     * GetWindowAttributes is always followed by GetGeometry.
     * At this point, if lbxproxy requests the window attributes
     * *AND* the geometry in one request, we are guaranteed to be
     * able to short circuit the GetGeometry that follows.
     */

    nr = NewReply(client, client->server->lbxReq, X_LbxGetWinAttrAndGeom,
		  GetWinAttrAndGeomReply);

    if (!nr)
	return ProcStandardRequest(client);

    /*
     * R6 Xlib will not wait for the GetWindowAttributes reply before
     * sending the GetGeometry.  As a result, when lbxproxy gets the
     * GetGeometry we must defer short circuting until lbxproxy receives
     * the LbxGetWinAttrAndGeomReply.
     */

    nr->request_info.lbxWinGeom.got_geom_request = FALSE;
    nr->request_info.lbxWinGeom.got_geom_reply = FALSE;


    FinishLBXRequest(client, REQ_PASSTHROUGH);

    newreq.reqType = server->lbxReq;
    newreq.lbxReqType = X_LbxGetWinAttrAndGeom;
    newreq.length = sz_xLbxGetWinAttrAndGeomReq >> 2;
    newreq.id = stuff->id;

    WriteReqToServer (client, sz_xLbxGetWinAttrAndGeomReq, (char *) &newreq, TRUE);
    return Success;
}


static Bool
GetWinAttrAndGeomReply (
    ClientPtr   client,
    ReplyStuffPtr nr,
    char       *data)
{
    xLbxGetWinAttrAndGeomReply *lbxrep;
    xGetWindowAttributesReply reply;
    char n;

    /*
     * We got the window attributes and geometry.  Write the
     * WindowAttributes reply now.
     */

    lbxrep = (xLbxGetWinAttrAndGeomReply *) data;

    reply.type = X_Reply;
    reply.sequenceNumber = lbxrep->sequenceNumber;
    reply.length = (sizeof(xGetWindowAttributesReply) -
	sizeof(xGenericReply)) >> 2;
    reply.backingStore = lbxrep->backingStore;
    reply.visualID = lbxrep->visualID;
#if defined(__cplusplus) || defined(c_plusplus)
    reply.c_class = lbxrep->c_class;
#else
    reply.class = lbxrep->class;
#endif
    reply.bitGravity = lbxrep->bitGravity;
    reply.winGravity = lbxrep->winGravity;
    reply.backingBitPlanes = lbxrep->backingBitPlanes;
    reply.backingPixel = lbxrep->backingPixel;
    reply.saveUnder = lbxrep->saveUnder;
    reply.mapInstalled = lbxrep->mapInstalled;
    reply.mapState = lbxrep->mapState;
    reply.override = lbxrep->override;
    reply.colormap = lbxrep->colormap;
    reply.allEventMasks = lbxrep->allEventMasks;
    reply.yourEventMask = lbxrep->yourEventMask;
    reply.doNotPropagateMask = lbxrep->doNotPropagateMask;

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

    WriteToClient(client, sizeof(xGetWindowAttributesReply), (char*)&reply);

    if (nr->request_info.lbxWinGeom.got_geom_request)
    {
	/*
	 * We can write the GetGeometry reply now too.
	 */

	xGetGeometryReply geomReply;

	geomReply.type = X_Reply;
	geomReply.sequenceNumber = lbxrep->sequenceNumber + 1;
	geomReply.length = 0;
	geomReply.depth = lbxrep->depth;
	geomReply.root = lbxrep->root;
	geomReply.x = lbxrep->x;
	geomReply.y = lbxrep->y;
	geomReply.width = lbxrep->width;
	geomReply.height = lbxrep->height;
	geomReply.borderWidth = lbxrep->borderWidth;

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

	WriteToClient(client, sizeof(xGetGeometryReply), (char*)&geomReply);

	LBXLastResponse(client) = lbxrep->sequenceNumber + 1;
 
	return TRUE; /* remove matching reply record */
    }
    else
    {
	/*
	 * We didn't get the GetGeometry request yet, so we
	 * stash away the geometry.
	 */

	nr->request_info.lbxWinGeom.got_geom_reply = TRUE;
	nr->request_info.lbxWinGeom.depth = lbxrep->depth;
	nr->request_info.lbxWinGeom.root = lbxrep->root;
	nr->request_info.lbxWinGeom.x = lbxrep->x;
	nr->request_info.lbxWinGeom.y = lbxrep->y;
	nr->request_info.lbxWinGeom.width = lbxrep->width;
	nr->request_info.lbxWinGeom.height = lbxrep->height;
	nr->request_info.lbxWinGeom.borderWidth = lbxrep->borderWidth;

	return FALSE; /* don't remove matching reply record */
    }
}


int
ProcLBXGetGeometry(
    ClientPtr   client)
{
    /* REQUEST(xResourceReq); */
    xGetGeometryReply reply;
    ReplyStuffPtr nr;
    char n;

    /*
     * If the previous request was GetWindowAttributes, then lbxproxy
     * requested the window attributes *AND* the geometry in one request.
     * That means we can short circuit this GetGeometry!
     */

    nr = GetMatchingReply (client, LBXSequenceNumber(client) - 1, FALSE);

    if (!nr || nr->major != client->server->lbxReq ||
	nr->minor != X_LbxGetWinAttrAndGeom)
    {
	/*
	 * This GetGeometry was not preceded by a GetWindowAttributes,
	 * so we can't short circuit.  Send the normal GetGeometry request.
	 */

	return ProcStandardRequest(client);
    }

    if (nr->request_info.lbxWinGeom.got_geom_reply)
    {
	/*
	 * We already have the geom information, so we
	 * can write the reply now.
	 */

	reply.type = X_Reply;
	reply.sequenceNumber = LBXSequenceNumber(client);
	reply.length = 0;
	reply.depth = nr->request_info.lbxWinGeom.depth;
	reply.root = nr->request_info.lbxWinGeom.root;
	reply.x = nr->request_info.lbxWinGeom.x;
	reply.y = nr->request_info.lbxWinGeom.y;
	reply.width = nr->request_info.lbxWinGeom.width;
	reply.height = nr->request_info.lbxWinGeom.height;
	reply.borderWidth = nr->request_info.lbxWinGeom.borderWidth;

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

	FinishLBXRequest(client, REQ_YANK);
	WriteToClient(client, sizeof(xGetGeometryReply), (char*)&reply);
	RemoveReply (client, nr);
    }
    else
    {
	/*
	 * We must wait for the geom information before
	 * writing the GetGeometry reply.
	 */
      
	nr->request_info.lbxWinGeom.got_geom_request = TRUE;

	FinishLBXRequest(client, REQ_YANKLATE);
    }
    return Success;
}

/* We're just looking for signs of the window manager here */
int
ProcLBXChangeWindowAttributes(
    ClientPtr client)
{
    REQUEST(xChangeWindowAttributesReq);
    Window win;
    CARD32 mask;
    char n;

    win = stuff->window;
    mask = stuff->valueMask;
    if (client->swapped) {
	swapl(&win, n);
	swapl(&mask, n);
    }
    if (mask == CWEventMask && win == LBXRootWindow(client)) {
	mask = *(CARD32 *)&stuff[1];
	if (client->swapped) {
	    swapl(&mask, n);
	}
	if (mask & SubstructureRedirectMask)
	    client->server->wm_running = TRUE;
    }
    return ProcStandardRequest(client);
}

void
FinishLBXRequest(
    ClientPtr   client,
    int         yank)
{
    REQUEST(xReq);
    char        n;

    if (yank != REQ_PASSTHROUGH) {
	LBXLastForReply(client) = LBXSequenceNumber(client);
	LBXLastForResponse(client) = LBXSequenceNumber(client);
	if (yank == REQ_YANK || yank == REQ_REPLACE)
	    LBXLastResponse(client) = LBXSequenceNumber(client);
    } else {
	switch (GeneratesReplies(client, stuff)) {
	case REQ_TYPE_YES:
	    LBXLastForReply(client) = LBXSequenceNumber(client);
	    LBXLastForResponse(client) = LBXSequenceNumber(client);
	    break;
	case REQ_TYPE_MAYBE:
	    LBXLastForResponse(client) = LBXSequenceNumber(client);
	    break;
	case REQ_TYPE_NO:
	    if (protocolMode != PROTOCOL_POOR &&
		GeneratesEvents(client, stuff))
		LBXLastForResponse(client) = LBXSequenceNumber(client);
	    break;
	}
    }
    switch (protocolMode) {
    case PROTOCOL_FULL:
	LBXCacheSafe(client) = (LBXSequenceNumber(client) ==
				LBXLastResponse(client));
	LBXCanDelayReply(client) = (LBXSequenceNumber(client) ==
				    LBXLastForReply(client));
	break;
    default:
	LBXCacheSafe(client) = (LBXLastForResponse(client) <=
				LBXLastResponse(client));
	LBXCanDelayReply(client) = (LBXLastForResponse(client) <=
				    LBXLastForReply(client));
	break;
    }
#ifdef SEQ_DEBUG
    fprintf(stderr,
	    "finished req %d, seq 0x%x, yank %d cache %d delay %d reply 0x%x response 0x%x\n",
	    stuff->reqType, LBXSequenceNumber(client), yank,
	    LBXCacheSafe(client), LBXCanDelayReply(client),
	    LBXLastForReply(client), LBXLastForResponse(client));
#endif

    if (yank == REQ_YANK || yank == REQ_YANKLATE) {
	LBXSequenceLost(client)++;
	LBXYanked(client)++;
	DBG(DBG_CLIENT, (stderr, "short-circuited client %d req %d\n",
			 client->index, stuff->reqType));
    }
    /* make sure server's sequence number is accurate */
    if (yank != REQ_YANK && yank != REQ_YANKLATE && LBXSequenceLost(client))
	BumpSequence(client);

    /* put request length in proxy byte order */
    if (client->swapped) {
	swaps(&stuff->length, n);
    }
}

/*
 * need to rewrite error codes for requests we've replaced.
 *
 * QueryFont regularly hits this in normal operation
 */
static int
patchup_error(
    ClientPtr   client,
    xError     *err,
    ReplyStuffPtr nr)
{
    int         retval = 1;
    CARD16      minor_code;
    char        n;

    minor_code = err->minorCode;
    if (client->swapped) {
	swaps(&minor_code, n);
    }
    switch (minor_code) {
    case X_LbxGetModifierMapping:
	minor_code = X_GetModifierMapping;
	break;
    case X_LbxGetKeyboardMapping:
	minor_code = X_GetKeyboardMapping;
	break;
    case X_LbxGetProperty:
	minor_code = X_GetProperty;
	break;
    case X_LbxQueryFont:
	minor_code = X_QueryFont;
	break;
    case X_LbxGetWinAttrAndGeom:
	minor_code = X_GetWindowAttributes;
	if (nr->request_info.lbxWinGeom.got_geom_request) {
	    err->majorCode = minor_code;
	    err->minorCode = 0;
	    if (client->swapped) {
		swaps(&err->sequenceNumber, n);
	    }
	    WriteToClient (client, sizeof(xError), (char *)err);
	    if (client->swapped) {
		swaps(&err->sequenceNumber, n);
	    }
	    err->sequenceNumber++;
	    LBXLastResponse(client) = err->sequenceNumber;
	    minor_code = X_GetGeometry;
	}
	break;
    default:
	retval = 0;		/* error caused by some LBX req that shouldn't
				 * have an error, so eat it */
	break;
    }
    err->majorCode = minor_code;    /* err->majorCode is CARD8, don't swap */
    err->minorCode = 0;
    return retval;
}

static Bool
error_matches(
    ClientPtr   client,
    ReplyStuffPtr nr,
    xError     *err)
{
    CARD16      mc;
    char        n;

    mc = err->minorCode;
    if (client->swapped) {
	swaps(&mc, n);
    }
    return (err->majorCode == nr->major && mc == nr->minor);
}

/*
 * returns TRUE if data (possibly modified) is to be sent on to client,
 * FALSE if data is replaced
 */
static Bool
HandleReply(
    ClientPtr   client,
    char       *data,
    int         len)
{
    xGenericReply *reply;
    xError     *err;
    ReplyStuffPtr nr;
    Bool        ret = TRUE;
    char        n;

    reply = (xGenericReply *) data;

    if (client->awaitingSetup) {
	xConnSetupPrefix *prefix = (xConnSetupPrefix *) reply;

	AttendClient(client);
	if (prefix->success) {
	    get_setup_reply(client, (char *) reply, len);
	    return FALSE;
	}
	return TRUE;
    }
    if (client->swapped && reply->type != KeymapNotify) {
	swaps(&reply->sequenceNumber, n);
    }

    if (reply->type != KeymapNotify &&
	reply->sequenceNumber < LBXLastResponse(client)) {
	/* spontaneous events, and events in PROTOCOL_POOR mode,
	 * may have sequence numbers that are earlier
	 * than short-circuited replies and errors */
	if (reply->type > X_Reply)
	    reply->sequenceNumber = LBXLastResponse(client);
    }

    if (reply->type != KeymapNotify &&
	reply->sequenceNumber > LBXSequenceNumber(client))
    {
#ifdef SEQ_DEBUG
	fprintf(stderr, "lbxproxy: reply seq #0x%x > internal seq 0x%x\n",
		reply->sequenceNumber, LBXSequenceNumber(client));
#endif
	reply->sequenceNumber = LBXSequenceNumber(client);
    }

    if (reply->type != X_Reply) {	/* event or error */
	/* clear out pending replies that resulted in errors */
	if (reply->type == X_Error) {
	    LBXLastResponse(client) = reply->sequenceNumber;
	    err = (xError *) reply;
	    nr = GetMatchingReply(client, reply->sequenceNumber, TRUE);
	    if (nr && error_matches(client, nr, err)) {
		if (err->majorCode == client->server->lbxReq) {
		    int         eret;

		    if ((eret = patchup_error(client, err, nr)) < 0) {
			CloseDownClient(client);
			return FALSE;
		    } else if (eret == 0) {
			/* error for proxy -- eat it */
			ret = FALSE;
		    }
		} else {
		    /* error in core X or other extension */
		    HandleExtensionError(client, err, nr);
		}
	    }
	    if (nr)
		RemoveReply(client, nr);
	} else if (reply->type >= 64)
	    HandleExtensionEvent(client, (xEvent *)reply);
	if (client->swapped && reply->type != KeymapNotify) {
	    swaps(&reply->sequenceNumber, n);
	}
	return ret;
    }

    LBXLastResponse(client) = reply->sequenceNumber;

    nr = GetMatchingReply(client, reply->sequenceNumber, TRUE);
    if (nr) {
	if (nr->major == client->server->lbxReq)
	    ret = FALSE;
	if ((*nr->reply_func)(client, nr, (char *)reply))
	    RemoveReply(client, nr);
    }

    if (client->swapped) {	/* put seq & length back */
	swaps(&reply->sequenceNumber, n);
	swapl(&reply->length, n);
    }
    return ret;
}

void
DoLBXReply(client, data, len)
    ClientPtr   client;
    char       *data;
    int         len;
{
    if (HandleReply(client, data, len))
	WriteToClient (client, len, data);

    FlushDelayedReplies (client);

    switch (protocolMode) {
    case PROTOCOL_FULL:
	LBXCacheSafe(client) = (LBXSequenceNumber(client) ==
				LBXLastResponse(client));
	break;
    default:
	LBXCacheSafe(client) = (LBXLastForResponse(client) <=
				LBXLastResponse(client));
	break;
    }
#ifdef SEQ_DEBUG
    fprintf(stderr, "finished reply 0x%x, cache %d\n",
	    ((xGenericReply *)data)->sequenceNumber, LBXCacheSafe(client));
#endif
}
