#define NEED_EVENTS
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include "dixstruct.h"
#include "extnsionst.h"
#define _XRECORD_SERVER_
#include <X11/extensions/recordstr.h>
#include "set.h"
#include "swaprep.h"
#include "inputstr.h"
#include <stdio.h>
#include <assert.h>
#ifdef PANORAMIX
#include "globals.h"
#include "panoramiX.h"
#include "panoramiXsrv.h"
#include "cursor.h"
#endif
static RESTYPE RTContext;
static int RecordErrorBase;
#define REPLY_BUF_SIZE 1024
typedef struct {
XID id;
ClientPtr pRecordingClient;
struct _RecordClientsAndProtocolRec *pListOfRCAP;
ClientPtr pBufClient;
unsigned int continuedReply:1;
char elemHeaders;
char bufCategory;
int numBufBytes;
char replyBuffer[REPLY_BUF_SIZE];
} RecordContextRec, *RecordContextPtr;
typedef union {
int count;
struct {
short first;
short last;
RecordSetPtr pMinOpSet;
} major;
} RecordMinorOpRec, *RecordMinorOpPtr;
typedef struct _RecordClientsAndProtocolRec {
RecordContextPtr pContext;
struct _RecordClientsAndProtocolRec *pNextRCAP;
RecordSetPtr pRequestMajorOpSet;
RecordMinorOpPtr pRequestMinOpInfo;
RecordSetPtr pReplyMajorOpSet;
RecordMinorOpPtr pReplyMinOpInfo;
RecordSetPtr pDeviceEventSet;
RecordSetPtr pDeliveredEventSet;
RecordSetPtr pErrorSet;
XID * pClientIDs;
short numClients;
short sizeClients;
unsigned int clientStarted:1;
unsigned int clientDied:1;
unsigned int clientIDsSeparatelyAllocated:1;
} RecordClientsAndProtocolRec, *RecordClientsAndProtocolPtr;
#define CLIENT_ARRAY_GROWTH_INCREMENT 4
static int numEnabledRCAPs;
#define VERIFY_CONTEXT(_pContext, _contextid, _client) { \
(_pContext) = (RecordContextPtr)LookupIDByType((_contextid), RTContext); \
if (!(_pContext)) { \
(_client)->errorValue = (_contextid); \
return RecordErrorBase + XRecordBadContext; \
} \
}
static int RecordDeleteContext(
pointer ,
XID
);
typedef int (*ProcFunctionPtr)(
ClientPtr
);
typedef struct {
ProcFunctionPtr *originalVector;
ProcFunctionPtr recordVector[256];
} RecordClientPrivateRec, *RecordClientPrivatePtr;
static int RecordClientPrivateKeyIndex;
static DevPrivateKey RecordClientPrivateKey = &RecordClientPrivateKeyIndex;
#define RecordClientPrivate(_pClient) (RecordClientPrivatePtr) \
dixLookupPrivate(&(_pClient)->devPrivates, RecordClientPrivateKey)
static RecordContextPtr *ppAllContexts;
static int numContexts;
static int numEnabledContexts;
static int
RecordFindContextOnAllContexts(RecordContextPtr pContext)
{
int i;
assert(numContexts >= numEnabledContexts);
for (i = 0; i < numContexts; i++)
{
if (ppAllContexts[i] == pContext)
return i;
}
return -1;
}
static void
RecordFlushReplyBuffer(
RecordContextPtr pContext,
pointer data1,
int len1,
pointer data2,
int len2
)
{
if (!pContext->pRecordingClient || pContext->pRecordingClient->clientGone)
return;
if (pContext->numBufBytes)
WriteToClient(pContext->pRecordingClient, pContext->numBufBytes,
(char *)pContext->replyBuffer);
pContext->numBufBytes = 0;
if (len1)
WriteToClient(pContext->pRecordingClient, len1, (char *)data1);
if (len2)
WriteToClient(pContext->pRecordingClient, len2, (char *)data2);
}
static void
RecordAProtocolElement(RecordContextPtr pContext, ClientPtr pClient,
int category, pointer data, int datalen, int futurelen)
{
CARD32 elemHeaderData[2];
int numElemHeaders = 0;
Bool recordingClientSwapped = pContext->pRecordingClient->swapped;
int n;
CARD32 serverTime = 0;
Bool gotServerTime = FALSE;
int replylen;
if (futurelen >= 0)
{
xRecordEnableContextReply *pRep = (xRecordEnableContextReply *)
pContext->replyBuffer;
if (pContext->pBufClient != pClient ||
pContext->bufCategory != category)
{
RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
pContext->pBufClient = pClient;
pContext->bufCategory = category;
}
if (!pContext->numBufBytes)
{
serverTime = GetTimeInMillis();
gotServerTime = TRUE;
pRep->type = X_Reply;
pRep->category = category;
pRep->sequenceNumber = pContext->pRecordingClient->sequence;
pRep->length = 0;
pRep->elementHeader = pContext->elemHeaders;
pRep->serverTime = serverTime;
if (pClient)
{
pRep->clientSwapped =
(pClient->swapped != recordingClientSwapped);
pRep->idBase = pClient->clientAsMask;
pRep->recordedSequenceNumber = pClient->sequence;
}
else
{
pRep->clientSwapped = (category != XRecordFromServer) &&
recordingClientSwapped;
pRep->idBase = 0;
pRep->recordedSequenceNumber = 0;
}
if (recordingClientSwapped)
{
swaps(&pRep->sequenceNumber, n);
swapl(&pRep->length, n);
swapl(&pRep->idBase, n);
swapl(&pRep->serverTime, n);
swapl(&pRep->recordedSequenceNumber, n);
}
pContext->numBufBytes = SIZEOF(xRecordEnableContextReply);
}
if ( ( (pContext->elemHeaders & XRecordFromClientTime)
&& category == XRecordFromClient)
||
( (pContext->elemHeaders & XRecordFromServerTime)
&& category == XRecordFromServer))
{
if (gotServerTime)
elemHeaderData[numElemHeaders] = serverTime;
else
elemHeaderData[numElemHeaders] = GetTimeInMillis();
if (recordingClientSwapped)
swapl(&elemHeaderData[numElemHeaders], n);
numElemHeaders++;
}
if ( (pContext->elemHeaders & XRecordFromClientSequence)
&&
(category == XRecordFromClient || category == XRecordClientDied))
{
elemHeaderData[numElemHeaders] = pClient->sequence;
if (recordingClientSwapped)
swapl(&elemHeaderData[numElemHeaders], n);
numElemHeaders++;
}
replylen = pRep->length;
if (recordingClientSwapped) swapl(&replylen, n);
replylen += numElemHeaders + (datalen >> 2) + (futurelen >> 2);
if (recordingClientSwapped) swapl(&replylen, n);
pRep->length = replylen;
}
numElemHeaders *= 4;
if (REPLY_BUF_SIZE - pContext->numBufBytes >= datalen + numElemHeaders)
{
if (numElemHeaders)
{
memcpy(pContext->replyBuffer + pContext->numBufBytes,
elemHeaderData, numElemHeaders);
pContext->numBufBytes += numElemHeaders;
}
if (datalen)
{
memcpy(pContext->replyBuffer + pContext->numBufBytes,
data, datalen);
pContext->numBufBytes += datalen;
}
}
else
RecordFlushReplyBuffer(pContext, (pointer)elemHeaderData,
numElemHeaders, (pointer)data, datalen);
}
static RecordClientsAndProtocolPtr
RecordFindClientOnContext(
RecordContextPtr pContext,
XID clientspec,
int *pposition
)
{
RecordClientsAndProtocolPtr pRCAP;
for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
{
int i;
for (i = 0; i < pRCAP->numClients; i++)
{
if (pRCAP->pClientIDs[i] == clientspec)
{
if (pposition)
*pposition = i;
return pRCAP;
}
}
}
return NULL;
}
static void
RecordABigRequest(RecordContextPtr pContext, ClientPtr client, xReq *stuff)
{
CARD32 bigLength;
char n;
int bytesLeft;
bytesLeft = client->req_len << 2;
RecordAProtocolElement(pContext, client, XRecordFromClient,
(pointer)stuff, SIZEOF(xReq), bytesLeft);
bigLength = client->req_len + (sizeof(bigLength) >> 2);
if (client->swapped)
swapl(&bigLength, n);
RecordAProtocolElement(pContext, client, XRecordFromClient,
(pointer)&bigLength, sizeof(bigLength), -1);
bytesLeft -= sizeof(bigLength);
RecordAProtocolElement(pContext, client, XRecordFromClient,
(pointer)(stuff + 1), bytesLeft, -1);
}
static int
RecordARequest(ClientPtr client)
{
RecordContextPtr pContext;
RecordClientsAndProtocolPtr pRCAP;
int i;
RecordClientPrivatePtr pClientPriv;
REQUEST(xReq);
int majorop;
majorop = stuff->reqType;
for (i = 0; i < numEnabledContexts; i++)
{
pContext = ppAllContexts[i];
pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask,
NULL);
if (pRCAP && pRCAP->pRequestMajorOpSet &&
RecordIsMemberOfSet(pRCAP->pRequestMajorOpSet, majorop))
{
if (majorop <= 127)
{
if (stuff->length == 0)
RecordABigRequest(pContext, client, stuff);
else
RecordAProtocolElement(pContext, client, XRecordFromClient,
(pointer)stuff, client->req_len << 2, 0);
}
else
{
int minorop = MinorOpcodeOfRequest(client);
int numMinOpInfo;
RecordMinorOpPtr pMinorOpInfo = pRCAP->pRequestMinOpInfo;
assert (pMinorOpInfo);
numMinOpInfo = pMinorOpInfo->count;
pMinorOpInfo++;
assert (numMinOpInfo);
for ( ; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++)
{
if (majorop >= pMinorOpInfo->major.first &&
majorop <= pMinorOpInfo->major.last &&
RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
minorop))
{
if (stuff->length == 0)
RecordABigRequest(pContext, client, stuff);
else
RecordAProtocolElement(pContext, client,
XRecordFromClient, (pointer)stuff,
client->req_len << 2, 0);
break;
}
}
}
}
}
pClientPriv = RecordClientPrivate(client);
assert(pClientPriv);
return (* pClientPriv->originalVector[majorop])(client);
}
static void
RecordAReply(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
{
RecordContextPtr pContext;
RecordClientsAndProtocolPtr pRCAP;
int eci;
int majorop;
ReplyInfoRec *pri = (ReplyInfoRec *)calldata;
ClientPtr client = pri->client;
REQUEST(xReq);
majorop = stuff->reqType;
for (eci = 0; eci < numEnabledContexts; eci++)
{
pContext = ppAllContexts[eci];
pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask,
NULL);
if (pRCAP)
{
if (pContext->continuedReply)
{
RecordAProtocolElement(pContext, client, XRecordFromServer,
pri->replyData, pri->dataLenBytes, -1);
if (!pri->bytesRemaining)
pContext->continuedReply = 0;
}
else if (pri->startOfReply && pRCAP->pReplyMajorOpSet &&
RecordIsMemberOfSet(pRCAP->pReplyMajorOpSet, majorop))
{
if (majorop <= 127)
{
RecordAProtocolElement(pContext, client, XRecordFromServer,
pri->replyData, pri->dataLenBytes, pri->bytesRemaining);
if (pri->bytesRemaining)
pContext->continuedReply = 1;
}
else
{
int minorop = MinorOpcodeOfRequest(client);
int numMinOpInfo;
RecordMinorOpPtr pMinorOpInfo = pRCAP->pReplyMinOpInfo;
assert (pMinorOpInfo);
numMinOpInfo = pMinorOpInfo->count;
pMinorOpInfo++;
assert (numMinOpInfo);
for ( ; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++)
{
if (majorop >= pMinorOpInfo->major.first &&
majorop <= pMinorOpInfo->major.last &&
RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
minorop))
{
RecordAProtocolElement(pContext, client,
XRecordFromServer, pri->replyData,
pri->dataLenBytes, pri->bytesRemaining);
if (pri->bytesRemaining)
pContext->continuedReply = 1;
break;
}
}
}
}
}
}
}
static void
RecordADeliveredEventOrError(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
{
EventInfoRec *pei = (EventInfoRec *)calldata;
RecordContextPtr pContext;
RecordClientsAndProtocolPtr pRCAP;
int eci;
ClientPtr pClient = pei->client;
for (eci = 0; eci < numEnabledContexts; eci++)
{
pContext = ppAllContexts[eci];
pRCAP = RecordFindClientOnContext(pContext, pClient->clientAsMask,
NULL);
if (pRCAP && (pRCAP->pDeliveredEventSet || pRCAP->pErrorSet))
{
int ev;
xEvent *pev = pei->events;
for (ev = 0; ev < pei->count; ev++, pev++)
{
int recordit = 0;
if (pRCAP->pErrorSet)
{
recordit = RecordIsMemberOfSet(pRCAP->pErrorSet,
((xError *)(pev))->errorCode);
}
else if (pRCAP->pDeliveredEventSet)
{
recordit = RecordIsMemberOfSet(pRCAP->pDeliveredEventSet,
pev->u.u.type & 0177);
}
if (recordit)
{
xEvent swappedEvent;
xEvent *pEvToRecord = pev;
if (pClient->swapped)
{
(*EventSwapVector[pev->u.u.type & 0177])
(pev, &swappedEvent);
pEvToRecord = &swappedEvent;
}
RecordAProtocolElement(pContext, pClient,
XRecordFromServer, pEvToRecord, SIZEOF(xEvent), 0);
}
}
}
}
}
static void
RecordADeviceEvent(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
{
DeviceEventInfoRec *pei = (DeviceEventInfoRec *)calldata;
RecordContextPtr pContext;
RecordClientsAndProtocolPtr pRCAP;
int eci;
for (eci = 0; eci < numEnabledContexts; eci++)
{
pContext = ppAllContexts[eci];
for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
{
if (pRCAP->pDeviceEventSet)
{
int ev;
xEvent *pev = pei->events;
for (ev = 0; ev < pei->count; ev++, pev++)
{
if (RecordIsMemberOfSet(pRCAP->pDeviceEventSet,
pev->u.u.type & 0177))
{
xEvent swappedEvent;
xEvent *pEvToRecord = pev;
#ifdef PANORAMIX
xEvent shiftedEvent;
if (!noPanoramiXExtension &&
(pev->u.u.type == MotionNotify ||
pev->u.u.type == ButtonPress ||
pev->u.u.type == ButtonRelease ||
pev->u.u.type == KeyPress ||
pev->u.u.type == KeyRelease)) {
int scr = XineramaGetCursorScreen(inputInfo.pointer);
memcpy(&shiftedEvent, pev, sizeof(xEvent));
shiftedEvent.u.keyButtonPointer.rootX +=
panoramiXdataPtr[scr].x -
panoramiXdataPtr[0].x;
shiftedEvent.u.keyButtonPointer.rootY +=
panoramiXdataPtr[scr].y -
panoramiXdataPtr[0].y;
pEvToRecord = &shiftedEvent;
}
#endif
if (pContext->pRecordingClient->swapped)
{
(*EventSwapVector[pEvToRecord->u.u.type & 0177])
(pEvToRecord, &swappedEvent);
pEvToRecord = &swappedEvent;
}
RecordAProtocolElement(pContext, NULL,
XRecordFromServer, pEvToRecord, SIZEOF(xEvent), 0);
SetCriticalOutputPending();
}
}
}
}
}
}
static void
RecordFlushAllContexts(
CallbackListPtr *pcbl,
pointer nulldata,
pointer calldata
)
{
int eci;
RecordContextPtr pContext;
for (eci = 0; eci < numEnabledContexts; eci++)
{
pContext = ppAllContexts[eci];
if (pContext->numBufBytes)
RecordFlushReplyBuffer(ppAllContexts[eci], NULL, 0, NULL, 0);
}
}
static int
RecordInstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
{
int i = 0;
XID client;
if (oneclient)
client = oneclient;
else
client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
while (client)
{
if (client != XRecordFutureClients)
{
if (pRCAP->pRequestMajorOpSet)
{
RecordSetIteratePtr pIter = NULL;
RecordSetInterval interval;
ClientPtr pClient = clients[CLIENT_ID(client)];
if (pClient && !RecordClientPrivate(pClient))
{
RecordClientPrivatePtr pClientPriv;
pClientPriv = (RecordClientPrivatePtr)
xalloc(sizeof(RecordClientPrivateRec));
if (!pClientPriv)
return BadAlloc;
memcpy(pClientPriv->recordVector, pClient->requestVector,
sizeof (pClientPriv->recordVector));
pClientPriv->originalVector = pClient->requestVector;
dixSetPrivate(&pClient->devPrivates,
RecordClientPrivateKey, pClientPriv);
pClient->requestVector = pClientPriv->recordVector;
}
while ((pIter = RecordIterateSet(pRCAP->pRequestMajorOpSet,
pIter, &interval)))
{
unsigned int j;
for (j = interval.first; j <= interval.last; j++)
pClient->requestVector[j] = RecordARequest;
}
}
}
if (oneclient)
client = 0;
else
client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
}
assert(numEnabledRCAPs >= 0);
if (!oneclient && ++numEnabledRCAPs == 1)
{
if (!AddCallback(&EventCallback, RecordADeliveredEventOrError, NULL))
return BadAlloc;
if (!AddCallback(&DeviceEventCallback, RecordADeviceEvent, NULL))
return BadAlloc;
if (!AddCallback(&ReplyCallback, RecordAReply, NULL))
return BadAlloc;
if (!AddCallback(&FlushCallback, RecordFlushAllContexts, NULL))
return BadAlloc;
}
return Success;
}
static void
RecordUninstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
{
int i = 0;
XID client;
if (oneclient)
client = oneclient;
else
client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
while (client)
{
if (client != XRecordFutureClients)
{
if (pRCAP->pRequestMajorOpSet)
{
ClientPtr pClient = clients[CLIENT_ID(client)];
int c;
Bool otherRCAPwantsProcVector = FALSE;
RecordClientPrivatePtr pClientPriv =
RecordClientPrivate(pClient);
assert (pClient && RecordClientPrivate(pClient));
memcpy(pClientPriv->recordVector, pClientPriv->originalVector,
sizeof (pClientPriv->recordVector));
for (c = 0; c < numEnabledContexts; c++)
{
RecordClientsAndProtocolPtr pOtherRCAP;
RecordContextPtr pContext = ppAllContexts[c];
if (pContext == pRCAP->pContext) continue;
pOtherRCAP = RecordFindClientOnContext(pContext, client,
NULL);
if (pOtherRCAP && pOtherRCAP->pRequestMajorOpSet)
{
RecordSetIteratePtr pIter = NULL;
RecordSetInterval interval;
otherRCAPwantsProcVector = TRUE;
while ((pIter = RecordIterateSet(
pOtherRCAP->pRequestMajorOpSet,
pIter, &interval)))
{
unsigned int j;
for (j = interval.first; j <= interval.last; j++)
pClient->requestVector[j] = RecordARequest;
}
}
}
if (!otherRCAPwantsProcVector)
{
pClient->requestVector = pClientPriv->originalVector;
dixSetPrivate(&pClient->devPrivates,
RecordClientPrivateKey, NULL);
xfree(pClientPriv);
}
}
}
if (oneclient)
client = 0;
else
client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
}
assert(numEnabledRCAPs >= 1);
if (!oneclient && --numEnabledRCAPs == 0)
{
DeleteCallback(&EventCallback, RecordADeliveredEventOrError, NULL);
DeleteCallback(&DeviceEventCallback, RecordADeviceEvent, NULL);
DeleteCallback(&ReplyCallback, RecordAReply, NULL);
DeleteCallback(&FlushCallback, RecordFlushAllContexts, NULL);
RecordFlushAllContexts(&FlushCallback, NULL, NULL);
}
}
static void
RecordDeleteClientFromRCAP(RecordClientsAndProtocolPtr pRCAP, int position)
{
if (pRCAP->pContext->pRecordingClient)
RecordUninstallHooks(pRCAP, pRCAP->pClientIDs[position]);
if (position != pRCAP->numClients - 1)
pRCAP->pClientIDs[position] = pRCAP->pClientIDs[pRCAP->numClients - 1];
if (--pRCAP->numClients == 0)
{
RecordContextPtr pContext = pRCAP->pContext;
if (pContext->pRecordingClient)
RecordUninstallHooks(pRCAP, 0);
if (pContext->pListOfRCAP == pRCAP)
pContext->pListOfRCAP = pRCAP->pNextRCAP;
else
{
RecordClientsAndProtocolPtr prevRCAP;
for (prevRCAP = pContext->pListOfRCAP;
prevRCAP->pNextRCAP != pRCAP;
prevRCAP = prevRCAP->pNextRCAP)
;
prevRCAP->pNextRCAP = pRCAP->pNextRCAP;
}
if (pRCAP->clientIDsSeparatelyAllocated)
xfree(pRCAP->pClientIDs);
xfree(pRCAP);
}
}
static void
RecordAddClientToRCAP(RecordClientsAndProtocolPtr pRCAP, XID clientspec)
{
if (pRCAP->numClients == pRCAP->sizeClients)
{
if (pRCAP->clientIDsSeparatelyAllocated)
{
XID *pNewIDs = (XID *)xrealloc(pRCAP->pClientIDs,
(pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT) *
sizeof(XID));
if (!pNewIDs)
return;
pRCAP->pClientIDs = pNewIDs;
pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
}
else
{
XID *pNewIDs = (XID *)xalloc((pRCAP->sizeClients +
CLIENT_ARRAY_GROWTH_INCREMENT) * sizeof(XID));
if (!pNewIDs)
return;
memcpy(pNewIDs, pRCAP->pClientIDs, pRCAP->numClients *sizeof(XID));
pRCAP->pClientIDs = pNewIDs;
pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
pRCAP->clientIDsSeparatelyAllocated = 1;
}
}
pRCAP->pClientIDs[pRCAP->numClients++] = clientspec;
if (pRCAP->pContext->pRecordingClient)
RecordInstallHooks(pRCAP, clientspec);
}
static void
RecordDeleteClientFromContext(RecordContextPtr pContext, XID clientspec)
{
RecordClientsAndProtocolPtr pRCAP;
int position;
if ((pRCAP = RecordFindClientOnContext(pContext, clientspec, &position)))
RecordDeleteClientFromRCAP(pRCAP, position);
}
static int
RecordSanityCheckClientSpecifiers(XID *clientspecs, int nspecs, XID errorspec)
{
int i;
int clientIndex;
for (i = 0; i < nspecs; i++)
{
if (clientspecs[i] == XRecordCurrentClients ||
clientspecs[i] == XRecordFutureClients ||
clientspecs[i] == XRecordAllClients)
continue;
if (errorspec && (CLIENT_BITS(clientspecs[i]) == errorspec) )
return BadMatch;
clientIndex = CLIENT_ID(clientspecs[i]);
if (clientIndex && clients[clientIndex] &&
clients[clientIndex]->clientState == ClientStateRunning)
{
if (clientspecs[i] == clients[clientIndex]->clientAsMask)
continue;
if (!LookupIDByClass(clientspecs[i], RC_ANY))
return BadMatch;
}
else
return BadMatch;
}
return Success;
}
static XID *
RecordCanonicalizeClientSpecifiers(XID *pClientspecs, int *pNumClientspecs, XID excludespec)
{
int i;
int numClients = *pNumClientspecs;
for (i = 0; i < numClients; i++)
{
XID cs = pClientspecs[i];
if (cs > XRecordAllClients)
pClientspecs[i] = CLIENT_BITS(cs);
}
for (i = 0; i < numClients; i++)
{
if (pClientspecs[i] == XRecordAllClients ||
pClientspecs[i] == XRecordCurrentClients)
{
int j, nc;
XID *pCanon = (XID *)xalloc(sizeof(XID) * (currentMaxClients + 1));
if (!pCanon) return NULL;
for (nc = 0, j = 1; j < currentMaxClients; j++)
{
ClientPtr client = clients[j];
if (client != NullClient &&
client->clientState == ClientStateRunning &&
client->clientAsMask != excludespec)
{
pCanon[nc++] = client->clientAsMask;
}
}
if (pClientspecs[i] == XRecordAllClients)
pCanon[nc++] = XRecordFutureClients;
*pNumClientspecs = nc;
return pCanon;
}
else
{
int j;
for (j = i + 1; j < numClients; )
{
if (pClientspecs[i] == pClientspecs[j])
{
pClientspecs[j] = pClientspecs[--numClients];
}
else
j++;
}
}
}
*pNumClientspecs = numClients;
return pClientspecs;
}
static int
RecordPadAlign(int size, int align)
{
return (align - (size & (align - 1))) & (align - 1);
}
static int
RecordSanityCheckRegisterClients(RecordContextPtr pContext, ClientPtr client, xRecordRegisterClientsReq *stuff)
{
int err;
xRecordRange *pRange;
int i;
XID recordingClient;
if (((client->req_len << 2) - SIZEOF(xRecordRegisterClientsReq)) !=
4 * stuff->nClients + SIZEOF(xRecordRange) * stuff->nRanges)
return BadLength;
if (stuff->elementHeader &
~(XRecordFromClientSequence|XRecordFromClientTime|XRecordFromServerTime))
{
client->errorValue = stuff->elementHeader;
return BadValue;
}
recordingClient = pContext->pRecordingClient ?
pContext->pRecordingClient->clientAsMask : 0;
err = RecordSanityCheckClientSpecifiers((XID *)&stuff[1], stuff->nClients,
recordingClient);
if (err != Success) return err;
pRange = (xRecordRange *)(((XID *)&stuff[1]) + stuff->nClients);
for (i = 0; i < stuff->nRanges; i++, pRange++)
{
if (pRange->coreRequestsFirst > pRange->coreRequestsLast)
{
client->errorValue = pRange->coreRequestsFirst;
return BadValue;
}
if (pRange->coreRepliesFirst > pRange->coreRepliesLast)
{
client->errorValue = pRange->coreRepliesFirst;
return BadValue;
}
if ((pRange->extRequestsMajorFirst || pRange->extRequestsMajorLast) &&
(pRange->extRequestsMajorFirst < 128 ||
pRange->extRequestsMajorLast < 128 ||
pRange->extRequestsMajorFirst > pRange->extRequestsMajorLast))
{
client->errorValue = pRange->extRequestsMajorFirst;
return BadValue;
}
if (pRange->extRequestsMinorFirst > pRange->extRequestsMinorLast)
{
client->errorValue = pRange->extRequestsMinorFirst;
return BadValue;
}
if ((pRange->extRepliesMajorFirst || pRange->extRepliesMajorLast) &&
(pRange->extRepliesMajorFirst < 128 ||
pRange->extRepliesMajorLast < 128 ||
pRange->extRepliesMajorFirst > pRange->extRepliesMajorLast))
{
client->errorValue = pRange->extRepliesMajorFirst;
return BadValue;
}
if (pRange->extRepliesMinorFirst > pRange->extRepliesMinorLast)
{
client->errorValue = pRange->extRepliesMinorFirst;
return BadValue;
}
if ((pRange->deliveredEventsFirst || pRange->deliveredEventsLast) &&
(pRange->deliveredEventsFirst < 2 ||
pRange->deliveredEventsLast < 2 ||
pRange->deliveredEventsFirst > pRange->deliveredEventsLast))
{
client->errorValue = pRange->deliveredEventsFirst;
return BadValue;
}
if ((pRange->deviceEventsFirst || pRange->deviceEventsLast) &&
(pRange->deviceEventsFirst < 2 ||
pRange->deviceEventsLast < 2 ||
pRange->deviceEventsFirst > pRange->deviceEventsLast))
{
client->errorValue = pRange->deviceEventsFirst;
return BadValue;
}
if (pRange->errorsFirst > pRange->errorsLast)
{
client->errorValue = pRange->errorsFirst;
return BadValue;
}
if (pRange->clientStarted != xFalse && pRange->clientStarted != xTrue)
{
client->errorValue = pRange->clientStarted;
return BadValue;
}
if (pRange->clientDied != xFalse && pRange->clientDied != xTrue)
{
client->errorValue = pRange->clientDied;
return BadValue;
}
}
return Success;
}
typedef struct
{
int nintervals;
RecordSetInterval *intervals;
int size;
int align;
int offset;
short first, last;
} SetInfoRec, *SetInfoPtr;
enum {REQ,
REP,
ERR,
DEV,
DLEV,
PREDEFSETS};
static int
RecordAllocIntervals(SetInfoPtr psi, int nIntervals)
{
assert(!psi->intervals);
psi->intervals = (RecordSetInterval *)
xalloc(nIntervals * sizeof(RecordSetInterval));
if (!psi->intervals)
return BadAlloc;
bzero(psi->intervals, nIntervals * sizeof(RecordSetInterval));
psi->size = nIntervals;
return Success;
}
static int
RecordConvertRangesToIntervals(
SetInfoPtr psi,
xRecordRange *pRanges,
int nRanges,
int byteoffset,
SetInfoPtr pExtSetInfo,
int *pnExtSetInfo
)
{
int i;
CARD8 *pCARD8;
int first, last;
int err;
for (i = 0; i < nRanges; i++, pRanges++)
{
pCARD8 = ((CARD8 *)pRanges) + byteoffset;
first = pCARD8[0];
last = pCARD8[1];
if (first || last)
{
if (!psi->intervals)
{
err = RecordAllocIntervals(psi, 2 * (nRanges - i));
if (err != Success)
return err;
}
psi->intervals[psi->nintervals].first = first;
psi->intervals[psi->nintervals].last = last;
psi->nintervals++;
assert(psi->nintervals <= psi->size);
if (pExtSetInfo)
{
SetInfoPtr pesi = pExtSetInfo;
CARD16 *pCARD16 = (CARD16 *)(pCARD8 + 2);
int j;
for (j = 0; j < *pnExtSetInfo; j++, pesi++)
{
if ( (first == pesi->first) && (last == pesi->last) )
break;
}
if (j == *pnExtSetInfo)
{
err = RecordAllocIntervals(pesi, 2 * (nRanges - i));
if (err != Success)
return err;
pesi->first = first;
pesi->last = last;
(*pnExtSetInfo)++;
}
pesi->intervals[pesi->nintervals].first = pCARD16[0];
pesi->intervals[pesi->nintervals].last = pCARD16[1];
pesi->nintervals++;
assert(pesi->nintervals <= pesi->size);
}
}
}
return Success;
}
#define offset_of(_structure, _field) \
((char *)(& (_structure . _field)) - (char *)(&_structure))
static int
RecordRegisterClients(RecordContextPtr pContext, ClientPtr client, xRecordRegisterClientsReq *stuff)
{
int err;
int i;
SetInfoPtr si;
int maxSets;
int nExtReqSets = 0;
int nExtRepSets = 0;
int extReqSetsOffset = 0;
int extRepSetsOffset = 0;
SetInfoPtr pExtReqSets, pExtRepSets;
int clientListOffset;
XID *pCanonClients;
int clientStarted = 0, clientDied = 0;
xRecordRange *pRanges, rr;
int nClients;
int sizeClients;
int totRCAPsize;
RecordClientsAndProtocolPtr pRCAP;
int pad;
XID recordingClient;
err = RecordSanityCheckRegisterClients(pContext, client, stuff);
if (err != Success)
return err;
if (pContext->elemHeaders != stuff->elementHeader)
{
RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
pContext->elemHeaders = stuff->elementHeader;
}
nClients = stuff->nClients;
if (!nClients)
return Success;
recordingClient = pContext->pRecordingClient ?
pContext->pRecordingClient->clientAsMask : 0;
pCanonClients = RecordCanonicalizeClientSpecifiers((XID *)&stuff[1],
&nClients, recordingClient);
if (!pCanonClients)
return BadAlloc;
maxSets = PREDEFSETS + 2 * stuff->nRanges;
si = (SetInfoPtr)xalloc(sizeof(SetInfoRec) * maxSets);
if (!si)
{
err = BadAlloc;
goto bailout;
}
bzero(si, sizeof(SetInfoRec) * maxSets);
for (i = 0; i < maxSets; i++)
si[i].intervals = NULL;
pExtReqSets = si + PREDEFSETS;
pExtRepSets = pExtReqSets + stuff->nRanges;
pRanges = (xRecordRange *)(((XID *)&stuff[1]) + stuff->nClients);
err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
offset_of(rr, coreRequestsFirst), NULL, NULL);
if (err != Success) goto bailout;
err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
offset_of(rr, extRequestsMajorFirst), pExtReqSets, &nExtReqSets);
if (err != Success) goto bailout;
err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
offset_of(rr, coreRepliesFirst), NULL, NULL);
if (err != Success) goto bailout;
err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
offset_of(rr, extRepliesMajorFirst), pExtRepSets, &nExtRepSets);
if (err != Success) goto bailout;
err = RecordConvertRangesToIntervals(&si[ERR], pRanges, stuff->nRanges,
offset_of(rr, errorsFirst), NULL, NULL);
if (err != Success) goto bailout;
err = RecordConvertRangesToIntervals(&si[DLEV], pRanges, stuff->nRanges,
offset_of(rr, deliveredEventsFirst), NULL, NULL);
if (err != Success) goto bailout;
err = RecordConvertRangesToIntervals(&si[DEV], pRanges, stuff->nRanges,
offset_of(rr, deviceEventsFirst), NULL, NULL);
if (err != Success) goto bailout;
for (i = 0; i < stuff->nRanges; i++)
{
if (pRanges[i].clientStarted) clientStarted = TRUE;
if (pRanges[i].clientDied) clientDied = TRUE;
}
totRCAPsize = sizeof(RecordClientsAndProtocolRec);
sizeClients = nClients + CLIENT_ARRAY_GROWTH_INCREMENT;
pad = RecordPadAlign(totRCAPsize, sizeof(XID));
clientListOffset = totRCAPsize + pad;
totRCAPsize += pad + sizeClients * sizeof(XID);
if (nExtReqSets)
{
pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
extReqSetsOffset = totRCAPsize + pad;
totRCAPsize += pad + (nExtReqSets + 1) * sizeof(RecordMinorOpRec);
}
if (nExtRepSets)
{
pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
extRepSetsOffset = totRCAPsize + pad;
totRCAPsize += pad + (nExtRepSets + 1) * sizeof(RecordMinorOpRec);
}
for (i = 0; i < maxSets; i++)
{
if (si[i].nintervals)
{
si[i].size = RecordSetMemoryRequirements(
si[i].intervals, si[i].nintervals, &si[i].align);
pad = RecordPadAlign(totRCAPsize, si[i].align);
si[i].offset = pad + totRCAPsize;
totRCAPsize += pad + si[i].size;
}
}
pRCAP = (RecordClientsAndProtocolPtr)xalloc(totRCAPsize);
if (!pRCAP)
{
err = BadAlloc;
goto bailout;
}
pRCAP->pContext = pContext;
pRCAP->pClientIDs = (XID *)((char *)pRCAP + clientListOffset);
pRCAP->numClients = nClients;
pRCAP->sizeClients = sizeClients;
pRCAP->clientIDsSeparatelyAllocated = 0;
for (i = 0; i < nClients; i++)
{
RecordDeleteClientFromContext(pContext, pCanonClients[i]);
pRCAP->pClientIDs[i] = pCanonClients[i];
}
if (si[REQ].intervals)
{
pRCAP->pRequestMajorOpSet =
RecordCreateSet(si[REQ].intervals, si[REQ].nintervals,
(RecordSetPtr)((char *)pRCAP + si[REQ].offset), si[REQ].size);
}
else pRCAP->pRequestMajorOpSet = NULL;
if (si[REP].intervals)
{
pRCAP->pReplyMajorOpSet =
RecordCreateSet(si[REP].intervals, si[REP].nintervals,
(RecordSetPtr)((char *)pRCAP + si[REP].offset), si[REP].size);
}
else pRCAP->pReplyMajorOpSet = NULL;
if (si[ERR].intervals)
{
pRCAP->pErrorSet =
RecordCreateSet(si[ERR].intervals, si[ERR].nintervals,
(RecordSetPtr)((char *)pRCAP + si[ERR].offset), si[ERR].size);
}
else pRCAP->pErrorSet = NULL;
if (si[DEV].intervals)
{
pRCAP->pDeviceEventSet =
RecordCreateSet(si[DEV].intervals, si[DEV].nintervals,
(RecordSetPtr)((char *)pRCAP + si[DEV].offset), si[DEV].size);
}
else pRCAP->pDeviceEventSet = NULL;
if (si[DLEV].intervals)
{
pRCAP->pDeliveredEventSet =
RecordCreateSet(si[DLEV].intervals, si[DLEV].nintervals,
(RecordSetPtr)((char *)pRCAP + si[DLEV].offset), si[DLEV].size);
}
else pRCAP->pDeliveredEventSet = NULL;
if (nExtReqSets)
{
pRCAP->pRequestMinOpInfo = (RecordMinorOpPtr)
((char *)pRCAP + extReqSetsOffset);
pRCAP->pRequestMinOpInfo[0].count = nExtReqSets;
for (i = 0; i < nExtReqSets; i++, pExtReqSets++)
{
pRCAP->pRequestMinOpInfo[i+1].major.first = pExtReqSets->first;
pRCAP->pRequestMinOpInfo[i+1].major.last = pExtReqSets->last;
pRCAP->pRequestMinOpInfo[i+1].major.pMinOpSet =
RecordCreateSet(pExtReqSets->intervals,
pExtReqSets->nintervals,
(RecordSetPtr)((char *)pRCAP + pExtReqSets->offset),
pExtReqSets->size);
}
}
else pRCAP->pRequestMinOpInfo = NULL;
if (nExtRepSets)
{
pRCAP->pReplyMinOpInfo = (RecordMinorOpPtr)
((char *)pRCAP + extRepSetsOffset);
pRCAP->pReplyMinOpInfo[0].count = nExtRepSets;
for (i = 0; i < nExtRepSets; i++, pExtRepSets++)
{
pRCAP->pReplyMinOpInfo[i+1].major.first = pExtRepSets->first;
pRCAP->pReplyMinOpInfo[i+1].major.last = pExtRepSets->last;
pRCAP->pReplyMinOpInfo[i+1].major.pMinOpSet =
RecordCreateSet(pExtRepSets->intervals,
pExtRepSets->nintervals,
(RecordSetPtr)((char *)pRCAP + pExtRepSets->offset),
pExtRepSets->size);
}
}
else pRCAP->pReplyMinOpInfo = NULL;
pRCAP->clientStarted = clientStarted;
pRCAP->clientDied = clientDied;
pRCAP->pNextRCAP = pContext->pListOfRCAP;
pContext->pListOfRCAP = pRCAP;
if (pContext->pRecordingClient)
RecordInstallHooks(pRCAP, 0);
bailout:
if (si)
{
for (i = 0; i < maxSets; i++)
if (si[i].intervals)
xfree(si[i].intervals);
xfree(si);
}
if (pCanonClients && pCanonClients != (XID *)&stuff[1])
xfree(pCanonClients);
return err;
}
static int
ProcRecordQueryVersion(ClientPtr client)
{
xRecordQueryVersionReply rep;
int n;
REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
rep.type = X_Reply;
rep.sequenceNumber = client->sequence;
rep.length = 0;
rep.majorVersion = RECORD_MAJOR_VERSION;
rep.minorVersion = RECORD_MINOR_VERSION;
if(client->swapped)
{
swaps(&rep.sequenceNumber, n);
swaps(&rep.majorVersion, n);
swaps(&rep.minorVersion, n);
}
(void)WriteToClient(client, sizeof(xRecordQueryVersionReply),
(char *)&rep);
return (client->noClientException);
}
static int
ProcRecordCreateContext(ClientPtr client)
{
REQUEST(xRecordCreateContextReq);
RecordContextPtr pContext;
RecordContextPtr *ppNewAllContexts = NULL;
int err = BadAlloc;
REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
LEGAL_NEW_RESOURCE(stuff->context, client);
pContext = (RecordContextPtr)xalloc(sizeof(RecordContextRec));
if (!pContext)
goto bailout;
ppNewAllContexts = (RecordContextPtr *)
xrealloc(ppAllContexts, sizeof(RecordContextPtr) * (numContexts + 1));
if (!ppNewAllContexts)
goto bailout;
ppAllContexts = ppNewAllContexts;
pContext->id = stuff->context;
pContext->pRecordingClient = NULL;
pContext->pListOfRCAP = NULL;
pContext->elemHeaders = 0;
pContext->bufCategory = 0;
pContext->numBufBytes = 0;
pContext->pBufClient = NULL;
pContext->continuedReply = 0;
err = RecordRegisterClients(pContext, client,
(xRecordRegisterClientsReq *)stuff);
if (err != Success)
goto bailout;
if (AddResource(pContext->id, RTContext, pContext))
{
ppAllContexts[numContexts++] = pContext;
return Success;
}
else
{
RecordDeleteContext((pointer)pContext, pContext->id);
err = BadAlloc;
}
bailout:
if (pContext)
xfree(pContext);
return err;
}
static int
ProcRecordRegisterClients(ClientPtr client)
{
RecordContextPtr pContext;
REQUEST(xRecordRegisterClientsReq);
REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
VERIFY_CONTEXT(pContext, stuff->context, client);
return RecordRegisterClients(pContext, client, stuff);
}
static int
ProcRecordUnregisterClients(ClientPtr client)
{
RecordContextPtr pContext;
int err;
REQUEST(xRecordUnregisterClientsReq);
XID *pCanonClients;
int nClients;
int i;
REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
if ((client->req_len << 2) - SIZEOF(xRecordUnregisterClientsReq) !=
4 * stuff->nClients)
return BadLength;
VERIFY_CONTEXT(pContext, stuff->context, client);
err = RecordSanityCheckClientSpecifiers((XID *)&stuff[1],
stuff->nClients, 0);
if (err != Success)
return err;
nClients = stuff->nClients;
pCanonClients = RecordCanonicalizeClientSpecifiers((XID *)&stuff[1],
&nClients, 0);
if (!pCanonClients)
return BadAlloc;
for (i = 0; i < nClients; i++)
{
RecordDeleteClientFromContext(pContext, pCanonClients[i]);
}
if (pCanonClients != (XID *)&stuff[1])
xfree(pCanonClients);
return Success;
}
typedef struct {
xRecordRange *pRanges;
int size;
int nRanges;
} GetContextRangeInfoRec, *GetContextRangeInfoPtr;
static int
RecordAllocRanges(GetContextRangeInfoPtr pri, int nRanges)
{
int newsize;
xRecordRange *pNewRange;
#define SZINCR 8
newsize = max(pri->size + SZINCR, nRanges);
pNewRange = (xRecordRange *)xrealloc(pri->pRanges,
newsize * sizeof(xRecordRange));
if (!pNewRange)
return BadAlloc;
pri->pRanges = pNewRange;
pri->size = newsize;
bzero(&pri->pRanges[pri->size - SZINCR], SZINCR * sizeof(xRecordRange));
if (pri->nRanges < nRanges)
pri->nRanges = nRanges;
return Success;
}
static int
RecordConvertSetToRanges(
RecordSetPtr pSet,
GetContextRangeInfoPtr pri,
int byteoffset,
Bool card8,
unsigned int imax,
int *pStartIndex
)
{
int nRanges;
RecordSetIteratePtr pIter = NULL;
RecordSetInterval interval;
CARD8 *pCARD8;
CARD16 *pCARD16;
int err;
if (!pSet)
return Success;
nRanges = pStartIndex ? *pStartIndex : 0;
while ((pIter = RecordIterateSet(pSet, pIter, &interval)))
{
if (interval.first > imax) break;
if (interval.last > imax) interval.last = imax;
nRanges++;
if (nRanges > pri->size)
{
err = RecordAllocRanges(pri, nRanges);
if (err != Success)
return err;
}
else
pri->nRanges = max(pri->nRanges, nRanges);
if (card8)
{
pCARD8 = ((CARD8 *)&pri->pRanges[nRanges-1]) + byteoffset;
*pCARD8++ = interval.first;
*pCARD8 = interval.last;
}
else
{
pCARD16 = (CARD16 *)
(((char *)&pri->pRanges[nRanges-1]) + byteoffset);
*pCARD16++ = interval.first;
*pCARD16 = interval.last;
}
}
if (pStartIndex)
*pStartIndex = nRanges;
return Success;
}
static int
RecordConvertMinorOpInfoToRanges(
RecordMinorOpPtr pMinOpInfo,
GetContextRangeInfoPtr pri,
int byteoffset
)
{
int nsets;
int start;
int i;
int err;
if (!pMinOpInfo)
return Success;
nsets = pMinOpInfo->count;
pMinOpInfo++;
start = 0;
for (i = 0; i < nsets; i++)
{
int j, s;
s = start;
err = RecordConvertSetToRanges(pMinOpInfo[i].major.pMinOpSet, pri,
byteoffset + 2, FALSE, 65535, &start);
if (err != Success) return err;
for (j = s; j < start; j++)
{
CARD8 *pCARD8 = ((CARD8 *)&pri->pRanges[j]) + byteoffset;
*pCARD8++ = pMinOpInfo[i].major.first;
*pCARD8 = pMinOpInfo[i].major.last;
}
}
return Success;
}
static void
RecordSwapRanges(xRecordRange *pRanges, int nRanges)
{
int i;
register char n;
for (i = 0; i < nRanges; i++, pRanges++)
{
swaps(&pRanges->extRequestsMinorFirst, n);
swaps(&pRanges->extRequestsMinorLast, n);
swaps(&pRanges->extRepliesMinorFirst, n);
swaps(&pRanges->extRepliesMinorLast, n);
}
}
static int
ProcRecordGetContext(ClientPtr client)
{
RecordContextPtr pContext;
REQUEST(xRecordGetContextReq);
xRecordGetContextReply rep;
int n;
RecordClientsAndProtocolPtr pRCAP;
int nRCAPs = 0;
GetContextRangeInfoPtr pRangeInfo;
GetContextRangeInfoPtr pri;
int i;
int err;
REQUEST_SIZE_MATCH(xRecordGetContextReq);
VERIFY_CONTEXT(pContext, stuff->context, client);
for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
nRCAPs++;
pRangeInfo = (GetContextRangeInfoPtr)xalloc(
nRCAPs * sizeof(GetContextRangeInfoRec));
if (!pRangeInfo && nRCAPs > 0)
return BadAlloc;
for (i = 0; i < nRCAPs; i++)
{
pRangeInfo[i].pRanges = NULL;
pRangeInfo[i].size = 0;
pRangeInfo[i].nRanges = 0;
}
for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
pRCAP;
pRCAP = pRCAP->pNextRCAP, pri++)
{
xRecordRange rr;
err = RecordConvertSetToRanges(pRCAP->pRequestMajorOpSet, pri,
offset_of(rr, coreRequestsFirst), TRUE, 127, NULL);
if (err != Success) goto bailout;
err = RecordConvertSetToRanges(pRCAP->pReplyMajorOpSet, pri,
offset_of(rr, coreRepliesFirst), TRUE, 127, NULL);
if (err != Success) goto bailout;
err = RecordConvertSetToRanges(pRCAP->pDeliveredEventSet, pri,
offset_of(rr, deliveredEventsFirst), TRUE, 255, NULL);
if (err != Success) goto bailout;
err = RecordConvertSetToRanges(pRCAP->pDeviceEventSet, pri,
offset_of(rr, deviceEventsFirst), TRUE, 255, NULL);
if (err != Success) goto bailout;
err = RecordConvertSetToRanges(pRCAP->pErrorSet, pri,
offset_of(rr, errorsFirst), TRUE, 255, NULL);
if (err != Success) goto bailout;
err = RecordConvertMinorOpInfoToRanges(pRCAP->pRequestMinOpInfo,
pri, offset_of(rr, extRequestsMajorFirst));
if (err != Success) goto bailout;
err = RecordConvertMinorOpInfoToRanges(pRCAP->pReplyMinOpInfo,
pri, offset_of(rr, extRepliesMajorFirst));
if (err != Success) goto bailout;
if (pRCAP->clientStarted || pRCAP->clientDied)
{
if (pri->nRanges == 0)
RecordAllocRanges(pri, 1);
pri->pRanges[0].clientStarted = pRCAP->clientStarted;
pri->pRanges[0].clientDied = pRCAP->clientDied;
}
}
rep.nClients = 0;
rep.length = 0;
for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
pRCAP;
pRCAP = pRCAP->pNextRCAP, pri++)
{
rep.nClients += pRCAP->numClients;
rep.length += pRCAP->numClients *
( (sizeof(xRecordClientInfo) >> 2) +
pri->nRanges * (sizeof(xRecordRange) >> 2));
}
rep.type = X_Reply;
rep.sequenceNumber = client->sequence;
rep.enabled = pContext->pRecordingClient != NULL;
rep.elementHeader = pContext->elemHeaders;
if(client->swapped)
{
swaps(&rep.sequenceNumber, n);
swapl(&rep.length, n);
swapl(&rep.nClients, n);
}
(void)WriteToClient(client, sizeof(xRecordGetContextReply),
(char *)&rep);
for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
pRCAP;
pRCAP = pRCAP->pNextRCAP, pri++)
{
xRecordClientInfo rci;
rci.nRanges = pri->nRanges;
if (client->swapped)
{
swapl(&rci.nRanges, n);
RecordSwapRanges(pri->pRanges, pri->nRanges);
}
for (i = 0; i < pRCAP->numClients; i++)
{
rci.clientResource = pRCAP->pClientIDs[i];
if (client->swapped) swapl(&rci.clientResource, n);
WriteToClient(client, sizeof(xRecordClientInfo), (char *)&rci);
WriteToClient(client, sizeof(xRecordRange) * pri->nRanges,
(char *)pri->pRanges);
}
}
err = client->noClientException;
bailout:
for (i = 0; i < nRCAPs; i++)
{
if (pRangeInfo[i].pRanges) xfree(pRangeInfo[i].pRanges);
}
xfree(pRangeInfo);
return err;
}
static int
ProcRecordEnableContext(ClientPtr client)
{
RecordContextPtr pContext;
REQUEST(xRecordEnableContextReq);
int i;
RecordClientsAndProtocolPtr pRCAP;
REQUEST_SIZE_MATCH(xRecordGetContextReq);
VERIFY_CONTEXT(pContext, stuff->context, client);
if (pContext->pRecordingClient)
return BadMatch;
for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
{
int err = RecordInstallHooks(pRCAP, 0);
if (err != Success)
{
RecordClientsAndProtocolPtr pUninstallRCAP;
for (pUninstallRCAP = pContext->pListOfRCAP;
pUninstallRCAP != pRCAP;
pUninstallRCAP = pUninstallRCAP->pNextRCAP)
{
RecordUninstallHooks(pUninstallRCAP, 0);
}
return err;
}
}
IgnoreClient(client);
pContext->pRecordingClient = client;
RecordDeleteClientFromContext(pContext,
pContext->pRecordingClient->clientAsMask);
i = RecordFindContextOnAllContexts(pContext);
assert(i >= numEnabledContexts);
if (i != numEnabledContexts)
{
ppAllContexts[i] = ppAllContexts[numEnabledContexts];
ppAllContexts[numEnabledContexts] = pContext;
}
++numEnabledContexts;
assert(numEnabledContexts > 0);
RecordAProtocolElement(pContext, NULL, XRecordStartOfData, NULL, 0, 0);
RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
return Success;
}
static void
RecordDisableContext(RecordContextPtr pContext)
{
RecordClientsAndProtocolPtr pRCAP;
int i;
if (!pContext->pRecordingClient) return;
if (!pContext->pRecordingClient->clientGone)
{
RecordAProtocolElement(pContext, NULL, XRecordEndOfData, NULL, 0, 0);
RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
AttendClient(pContext->pRecordingClient);
}
for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
{
RecordUninstallHooks(pRCAP, 0);
}
pContext->pRecordingClient = NULL;
i = RecordFindContextOnAllContexts(pContext);
assert( (i != -1) && (i < numEnabledContexts) );
if (i != (numEnabledContexts - 1) )
{
ppAllContexts[i] = ppAllContexts[numEnabledContexts-1];
ppAllContexts[numEnabledContexts-1] = pContext;
}
--numEnabledContexts;
assert(numEnabledContexts >= 0);
}
static int
ProcRecordDisableContext(ClientPtr client)
{
RecordContextPtr pContext;
REQUEST(xRecordDisableContextReq);
REQUEST_SIZE_MATCH(xRecordDisableContextReq);
VERIFY_CONTEXT(pContext, stuff->context, client);
RecordDisableContext(pContext);
return Success;
}
static int
RecordDeleteContext(pointer value, XID id)
{
int i;
RecordContextPtr pContext = (RecordContextPtr)value;
RecordClientsAndProtocolPtr pRCAP;
RecordDisableContext(pContext);
while ((pRCAP = pContext->pListOfRCAP))
{
int numClients = pRCAP->numClients;
while(numClients--)
{
RecordDeleteClientFromRCAP(pRCAP, numClients);
}
}
xfree(pContext);
if (-1 != (i = RecordFindContextOnAllContexts(pContext)))
{
ppAllContexts[i] = ppAllContexts[numContexts - 1];
if (--numContexts == 0)
{
xfree(ppAllContexts);
ppAllContexts = NULL;
}
}
return Success;
}
static int
ProcRecordFreeContext(ClientPtr client)
{
RecordContextPtr pContext;
REQUEST(xRecordFreeContextReq);
REQUEST_SIZE_MATCH(xRecordFreeContextReq);
VERIFY_CONTEXT(pContext, stuff->context, client);
FreeResource(stuff->context, RT_NONE);
return Success;
}
static int
ProcRecordDispatch(ClientPtr client)
{
REQUEST(xReq);
switch (stuff->data)
{
case X_RecordQueryVersion:
return ProcRecordQueryVersion(client);
case X_RecordCreateContext:
return ProcRecordCreateContext(client);
case X_RecordRegisterClients:
return ProcRecordRegisterClients(client);
case X_RecordUnregisterClients:
return ProcRecordUnregisterClients(client);
case X_RecordGetContext:
return ProcRecordGetContext(client);
case X_RecordEnableContext:
return ProcRecordEnableContext(client);
case X_RecordDisableContext:
return ProcRecordDisableContext(client);
case X_RecordFreeContext:
return ProcRecordFreeContext(client);
default:
return BadRequest;
}
}
static int
SProcRecordQueryVersion(ClientPtr client)
{
REQUEST(xRecordQueryVersionReq);
register char n;
swaps(&stuff->length, n);
REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
swaps(&stuff->majorVersion, n);
swaps(&stuff->minorVersion,n);
return ProcRecordQueryVersion(client);
}
static int
SwapCreateRegister(xRecordRegisterClientsReq *stuff)
{
register char n;
int i;
XID *pClientID;
swapl(&stuff->context, n);
swapl(&stuff->nClients, n);
swapl(&stuff->nRanges, n);
pClientID = (XID *)&stuff[1];
if (stuff->nClients > stuff->length - (sz_xRecordRegisterClientsReq >> 2))
return BadLength;
for (i = 0; i < stuff->nClients; i++, pClientID++)
{
swapl(pClientID, n);
}
if (stuff->nRanges > stuff->length - (sz_xRecordRegisterClientsReq >> 2)
- stuff->nClients)
return BadLength;
RecordSwapRanges((xRecordRange *)pClientID, stuff->nRanges);
return Success;
}
static int
SProcRecordCreateContext(ClientPtr client)
{
REQUEST(xRecordCreateContextReq);
int status;
register char n;
swaps(&stuff->length, n);
REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
if ((status = SwapCreateRegister((pointer)stuff)) != Success)
return status;
return ProcRecordCreateContext(client);
}
static int
SProcRecordRegisterClients(ClientPtr client)
{
REQUEST(xRecordRegisterClientsReq);
int status;
register char n;
swaps(&stuff->length, n);
REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
if ((status = SwapCreateRegister((pointer)stuff)) != Success)
return status;
return ProcRecordRegisterClients(client);
}
static int
SProcRecordUnregisterClients(ClientPtr client)
{
REQUEST(xRecordUnregisterClientsReq);
register char n;
swaps(&stuff->length, n);
REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
swapl(&stuff->context, n);
swapl(&stuff->nClients, n);
SwapRestL(stuff);
return ProcRecordUnregisterClients(client);
}
static int
SProcRecordGetContext(ClientPtr client)
{
REQUEST(xRecordGetContextReq);
register char n;
swaps(&stuff->length, n);
REQUEST_SIZE_MATCH(xRecordGetContextReq);
swapl(&stuff->context, n);
return ProcRecordGetContext(client);
}
static int
SProcRecordEnableContext(ClientPtr client)
{
REQUEST(xRecordEnableContextReq);
register char n;
swaps(&stuff->length, n);
REQUEST_SIZE_MATCH(xRecordEnableContextReq);
swapl(&stuff->context, n);
return ProcRecordEnableContext(client);
}
static int
SProcRecordDisableContext(ClientPtr client)
{
REQUEST(xRecordDisableContextReq);
register char n;
swaps(&stuff->length, n);
REQUEST_SIZE_MATCH(xRecordDisableContextReq);
swapl(&stuff->context, n);
return ProcRecordDisableContext(client);
}
static int
SProcRecordFreeContext(ClientPtr client)
{
REQUEST(xRecordFreeContextReq);
register char n;
swaps(&stuff->length, n);
REQUEST_SIZE_MATCH(xRecordFreeContextReq);
swapl(&stuff->context, n);
return ProcRecordFreeContext(client);
}
static int
SProcRecordDispatch(ClientPtr client)
{
REQUEST(xReq);
switch (stuff->data)
{
case X_RecordQueryVersion:
return SProcRecordQueryVersion(client);
case X_RecordCreateContext:
return SProcRecordCreateContext(client);
case X_RecordRegisterClients:
return SProcRecordRegisterClients(client);
case X_RecordUnregisterClients:
return SProcRecordUnregisterClients(client);
case X_RecordGetContext:
return SProcRecordGetContext(client);
case X_RecordEnableContext:
return SProcRecordEnableContext(client);
case X_RecordDisableContext:
return SProcRecordDisableContext(client);
case X_RecordFreeContext:
return SProcRecordFreeContext(client);
default:
return BadRequest;
}
}
static void
RecordConnectionSetupInfo(RecordContextPtr pContext, NewClientInfoRec *pci)
{
int prefixsize = SIZEOF(xConnSetupPrefix);
int restsize = pci->prefix->length * 4;
if (pci->client->swapped)
{
char *pConnSetup = (char *)xalloc(prefixsize + restsize);
if (!pConnSetup)
return;
SwapConnSetupPrefix(pci->prefix, pConnSetup);
SwapConnSetupInfo(pci->setup, pConnSetup + prefixsize);
RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
(pointer)pConnSetup, prefixsize + restsize, 0);
xfree(pConnSetup);
}
else
{
RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
(pointer)pci->prefix, prefixsize, restsize);
RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
(pointer)pci->setup, restsize, -1);
}
}
static void
RecordAClientStateChange(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
{
NewClientInfoRec *pci = (NewClientInfoRec *)calldata;
int i;
ClientPtr pClient = pci->client;
switch (pClient->clientState)
{
case ClientStateRunning:
for (i = 0; i < numContexts; i++)
{
RecordClientsAndProtocolPtr pRCAP;
RecordContextPtr pContext = ppAllContexts[i];
if ((pRCAP = RecordFindClientOnContext(pContext,
XRecordFutureClients, NULL)))
{
RecordAddClientToRCAP(pRCAP, pClient->clientAsMask);
if (pContext->pRecordingClient && pRCAP->clientStarted)
RecordConnectionSetupInfo(pContext, pci);
}
}
break;
case ClientStateGone:
case ClientStateRetained:
for (i = 0; i < numContexts; i++)
{
RecordClientsAndProtocolPtr pRCAP;
RecordContextPtr pContext = ppAllContexts[i];
int pos;
if (pContext->pRecordingClient == pClient)
RecordDisableContext(pContext);
if ((pRCAP = RecordFindClientOnContext(pContext,
pClient->clientAsMask, &pos)))
{
if (pContext->pRecordingClient && pRCAP->clientDied)
RecordAProtocolElement(pContext, pClient,
XRecordClientDied, NULL, 0, 0);
RecordDeleteClientFromRCAP(pRCAP, pos);
}
}
break;
default:
break;
}
}
static void
RecordCloseDown(ExtensionEntry *extEntry)
{
DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
}
void
RecordExtensionInit(void)
{
ExtensionEntry *extentry;
RTContext = CreateNewResourceType(RecordDeleteContext);
if (!RTContext)
return;
ppAllContexts = NULL;
numContexts = numEnabledContexts = numEnabledRCAPs = 0;
if (!AddCallback(&ClientStateCallback, RecordAClientStateChange, NULL))
return;
extentry = AddExtension(RECORD_NAME, RecordNumEvents, RecordNumErrors,
ProcRecordDispatch, SProcRecordDispatch,
RecordCloseDown, StandardMinorOpcode);
if (!extentry)
{
DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
return;
}
RecordErrorBase = extentry->errorBase;
}