4 Copyright 1995, 1998 The Open Group
6 Permission to use, copy, modify, distribute, and sell this software and its
7 documentation for any purpose is hereby granted without fee, provided that
8 the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
23 Except as contained in this notice, the name of The Open Group shall
24 not be used in advertising or otherwise to promote the sale, use or
25 other dealings in this Software without prior written authorization
28 Author: David P. Wiggins, The Open Group
30 This work benefited from earlier work done by Martha Zimet of NCD
31 and Jim Haggerty of Metheus.
36 #ifdef HAVE_DIX_CONFIG_H
37 #include <dix-config.h>
40 #include "dixstruct.h"
41 #include "extnsionst.h"
42 #define _XRECORD_SERVER_
43 #include <X11/extensions/recordstr.h>
52 #include "panoramiX.h"
53 #include "panoramiXsrv.h"
57 static RESTYPE RTContext
; /* internal resource type for Record contexts */
58 static int RecordErrorBase
; /* first Record error number */
60 /* How many bytes of protocol data to buffer in a context. Don't set to less
63 #define REPLY_BUF_SIZE 1024
65 /* Record Context structure */
68 XID id
; /* resource id of context */
69 ClientPtr pRecordingClient
; /* client that has context enabled */
70 struct _RecordClientsAndProtocolRec
*pListOfRCAP
; /* all registered info */
71 ClientPtr pBufClient
; /* client whose protocol is in replyBuffer*/
72 unsigned int continuedReply
:1; /* recording a reply that is split up? */
73 char elemHeaders
; /* element header flags (time/seq no.) */
74 char bufCategory
; /* category of protocol in replyBuffer */
75 int numBufBytes
; /* number of bytes in replyBuffer */
76 char replyBuffer
[REPLY_BUF_SIZE
]; /* buffered recorded protocol */
77 } RecordContextRec
, *RecordContextPtr
;
79 /* RecordMinorOpRec - to hold minor opcode selections for extension requests
84 int count
; /* first element of array: how many "major" structs to follow */
85 struct { /* rest of array elements are this */
86 short first
; /* first major opcode */
87 short last
; /* last major opcode */
88 RecordSetPtr pMinOpSet
; /* minor opcode set for above major range */
90 } RecordMinorOpRec
, *RecordMinorOpPtr
;
93 /* RecordClientsAndProtocolRec, nicknamed RCAP - holds all the client and
94 * protocol selections passed in a single CreateContext or RegisterClients.
95 * Generally, a context will have one of these from the create and an
96 * additional one for each RegisterClients. RCAPs are freed when all their
97 * clients are unregistered.
100 typedef struct _RecordClientsAndProtocolRec
{
101 RecordContextPtr pContext
; /* context that owns this RCAP */
102 struct _RecordClientsAndProtocolRec
*pNextRCAP
; /* next RCAP on context */
103 RecordSetPtr pRequestMajorOpSet
; /* requests to record */
104 RecordMinorOpPtr pRequestMinOpInfo
; /* extension requests to record */
105 RecordSetPtr pReplyMajorOpSet
; /* replies to record */
106 RecordMinorOpPtr pReplyMinOpInfo
; /* extension replies to record */
107 RecordSetPtr pDeviceEventSet
; /* device events to record */
108 RecordSetPtr pDeliveredEventSet
; /* delivered events to record */
109 RecordSetPtr pErrorSet
; /* errors to record */
110 XID
* pClientIDs
; /* array of clients to record */
111 short numClients
; /* number of clients in pClientIDs */
112 short sizeClients
; /* size of pClientIDs array */
113 unsigned int clientStarted
:1; /* record new client connections? */
114 unsigned int clientDied
:1; /* record client disconnections? */
115 unsigned int clientIDsSeparatelyAllocated
:1; /* pClientIDs malloced? */
116 } RecordClientsAndProtocolRec
, *RecordClientsAndProtocolPtr
;
118 /* how much bigger to make pRCAP->pClientIDs when reallocing */
119 #define CLIENT_ARRAY_GROWTH_INCREMENT 4
121 /* counts the total number of RCAPs belonging to enabled contexts. */
122 static int numEnabledRCAPs
;
124 /* void VERIFY_CONTEXT(RecordContextPtr, XID, ClientPtr)
125 * In the spirit of the VERIFY_* macros in dix.h, this macro fills in
126 * the context pointer if the given ID is a valid Record Context, else it
129 #define VERIFY_CONTEXT(_pContext, _contextid, _client) { \
130 (_pContext) = (RecordContextPtr)LookupIDByType((_contextid), RTContext); \
131 if (!(_pContext)) { \
132 (_client)->errorValue = (_contextid); \
133 return RecordErrorBase + XRecordBadContext; \
137 static int RecordDeleteContext(
143 /***************************************************************************/
145 /* client private stuff */
147 /* To make declarations less obfuscated, have a typedef for a pointer to a
150 typedef int (*ProcFunctionPtr
)(
151 ClientPtr
/*pClient*/
154 /* Record client private. Generally a client only has one of these if
155 * any of its requests are being recorded.
158 /* ptr to client's proc vector before Record stuck its nose in */
159 ProcFunctionPtr
*originalVector
;
161 /* proc vector with pointers for recorded requests redirected to the
162 * function RecordARequest
164 ProcFunctionPtr recordVector
[256];
165 } RecordClientPrivateRec
, *RecordClientPrivatePtr
;
167 static int RecordClientPrivateIndex
;
169 /* RecordClientPrivatePtr RecordClientPrivate(ClientPtr)
170 * gets the client private of the given client. Syntactic sugar.
172 #define RecordClientPrivate(_pClient) (RecordClientPrivatePtr) \
173 ((_pClient)->devPrivates[RecordClientPrivateIndex].ptr)
176 /***************************************************************************/
178 /* global list of all contexts */
180 static RecordContextPtr
*ppAllContexts
;
182 static int numContexts
;/* number of contexts in ppAllContexts */
184 /* number of currently enabled contexts. All enabled contexts are bunched
185 * up at the front of the ppAllContexts array, from ppAllContexts[0] to
186 * ppAllContexts[numEnabledContexts-1], to eliminate time spent skipping
187 * past disabled contexts.
189 static int numEnabledContexts
;
191 /* RecordFindContextOnAllContexts
194 * pContext is the context to search for.
197 * The index into the array ppAllContexts at which pContext is stored.
198 * If pContext is not found in ppAllContexts, returns -1.
200 * Side Effects: none.
203 RecordFindContextOnAllContexts(RecordContextPtr pContext
)
207 assert(numContexts
>= numEnabledContexts
);
208 for (i
= 0; i
< numContexts
; i
++)
210 if (ppAllContexts
[i
] == pContext
)
214 } /* RecordFindContextOnAllContexts */
217 /***************************************************************************/
219 /* RecordFlushReplyBuffer
222 * pContext is the context to flush.
223 * data1 is a pointer to additional data, and len1 is its length in bytes.
224 * data2 is a pointer to additional data, and len2 is its length in bytes.
229 * If the context is enabled, any buffered (recorded) protocol is written
230 * to the recording client, and the number of buffered bytes is set to
231 * zero. If len1 is not zero, data1/len1 are then written to the
232 * recording client, and similarly for data2/len2 (written after
236 RecordFlushReplyBuffer(
237 RecordContextPtr pContext
,
244 if (!pContext
->pRecordingClient
|| pContext
->pRecordingClient
->clientGone
)
246 if (pContext
->numBufBytes
)
247 WriteToClient(pContext
->pRecordingClient
, pContext
->numBufBytes
,
248 (char *)pContext
->replyBuffer
);
249 pContext
->numBufBytes
= 0;
251 WriteToClient(pContext
->pRecordingClient
, len1
, (char *)data1
);
253 WriteToClient(pContext
->pRecordingClient
, len2
, (char *)data2
);
254 } /* RecordFlushReplyBuffer */
257 /* RecordAProtocolElement
260 * pContext is the context that is recording a protocol element.
261 * pClient is the client whose protocol is being recorded. For
262 * device events and EndOfData, pClient is NULL.
263 * category is the category of the protocol element, as defined
264 * by the RECORD spec.
265 * data is a pointer to the protocol data, and datalen is its length
267 * futurelen is the number of bytes that will be sent in subsequent
268 * calls to this function to complete this protocol element.
269 * In those subsequent calls, futurelen will be -1 to indicate
270 * that the current data is a continuation of the same protocol
276 * The context may be flushed. The new protocol element will be
277 * added to the context's protocol buffer with appropriate element
278 * headers prepended (sequence number and timestamp). If the data
279 * is continuation data (futurelen == -1), element headers won't
280 * be added. If the protocol element and headers won't fit in
281 * the context's buffer, it is sent directly to the recording
282 * client (after any buffered data).
285 RecordAProtocolElement(RecordContextPtr pContext
, ClientPtr pClient
,
286 int category
, pointer data
, int datalen
, int futurelen
)
288 CARD32 elemHeaderData
[2];
289 int numElemHeaders
= 0;
290 Bool recordingClientSwapped
= pContext
->pRecordingClient
->swapped
;
292 CARD32 serverTime
= 0;
293 Bool gotServerTime
= FALSE
;
297 { /* start of new protocol element */
298 xRecordEnableContextReply
*pRep
= (xRecordEnableContextReply
*)
299 pContext
->replyBuffer
;
300 if (pContext
->pBufClient
!= pClient
||
301 pContext
->bufCategory
!= category
)
303 RecordFlushReplyBuffer(pContext
, NULL
, 0, NULL
, 0);
304 pContext
->pBufClient
= pClient
;
305 pContext
->bufCategory
= category
;
308 if (!pContext
->numBufBytes
)
310 serverTime
= GetTimeInMillis();
311 gotServerTime
= TRUE
;
312 pRep
->type
= X_Reply
;
313 pRep
->category
= category
;
314 pRep
->sequenceNumber
= pContext
->pRecordingClient
->sequence
;
316 pRep
->elementHeader
= pContext
->elemHeaders
;
317 pRep
->serverTime
= serverTime
;
320 pRep
->clientSwapped
=
321 (pClient
->swapped
!= recordingClientSwapped
);
322 pRep
->idBase
= pClient
->clientAsMask
;
323 pRep
->recordedSequenceNumber
= pClient
->sequence
;
325 else /* it's a device event, StartOfData, or EndOfData */
327 pRep
->clientSwapped
= (category
!= XRecordFromServer
) &&
328 recordingClientSwapped
;
330 pRep
->recordedSequenceNumber
= 0;
333 if (recordingClientSwapped
)
335 swaps(&pRep
->sequenceNumber
, n
);
336 swapl(&pRep
->length
, n
);
337 swapl(&pRep
->idBase
, n
);
338 swapl(&pRep
->serverTime
, n
);
339 swapl(&pRep
->recordedSequenceNumber
, n
);
341 pContext
->numBufBytes
= SIZEOF(xRecordEnableContextReply
);
344 /* generate element headers if needed */
346 if ( ( (pContext
->elemHeaders
& XRecordFromClientTime
)
347 && category
== XRecordFromClient
)
349 ( (pContext
->elemHeaders
& XRecordFromServerTime
)
350 && category
== XRecordFromServer
))
353 elemHeaderData
[numElemHeaders
] = serverTime
;
355 elemHeaderData
[numElemHeaders
] = GetTimeInMillis();
356 if (recordingClientSwapped
)
357 swapl(&elemHeaderData
[numElemHeaders
], n
);
361 if ( (pContext
->elemHeaders
& XRecordFromClientSequence
)
363 (category
== XRecordFromClient
|| category
== XRecordClientDied
))
365 elemHeaderData
[numElemHeaders
] = pClient
->sequence
;
366 if (recordingClientSwapped
)
367 swapl(&elemHeaderData
[numElemHeaders
], n
);
371 /* adjust reply length */
373 replylen
= pRep
->length
;
374 if (recordingClientSwapped
) swapl(&replylen
, n
);
375 replylen
+= numElemHeaders
+ (datalen
>> 2) + (futurelen
>> 2);
376 if (recordingClientSwapped
) swapl(&replylen
, n
);
377 pRep
->length
= replylen
;
378 } /* end if not continued reply */
382 /* if space available >= space needed, buffer the data */
384 if (REPLY_BUF_SIZE
- pContext
->numBufBytes
>= datalen
+ numElemHeaders
)
388 memcpy(pContext
->replyBuffer
+ pContext
->numBufBytes
,
389 elemHeaderData
, numElemHeaders
);
390 pContext
->numBufBytes
+= numElemHeaders
;
394 memcpy(pContext
->replyBuffer
+ pContext
->numBufBytes
,
396 pContext
->numBufBytes
+= datalen
;
400 RecordFlushReplyBuffer(pContext
, (pointer
)elemHeaderData
,
401 numElemHeaders
, (pointer
)data
, datalen
);
403 } /* RecordAProtocolElement */
406 /* RecordFindClientOnContext
409 * pContext is the context to search.
410 * clientspec is the resource ID mask identifying the client to search
411 * for, or XRecordFutureClients.
412 * pposition is a pointer to an int, or NULL. See Returns.
415 * The RCAP on which clientspec was found, or NULL if not found on
416 * any RCAP on the given context.
417 * If pposition was not NULL and the returned RCAP is not NULL,
418 * *pposition will be set to the index into the returned the RCAP's
419 * pClientIDs array that holds clientspec.
421 * Side Effects: none.
423 static RecordClientsAndProtocolPtr
424 RecordFindClientOnContext(
425 RecordContextPtr pContext
,
430 RecordClientsAndProtocolPtr pRCAP
;
432 for (pRCAP
= pContext
->pListOfRCAP
; pRCAP
; pRCAP
= pRCAP
->pNextRCAP
)
435 for (i
= 0; i
< pRCAP
->numClients
; i
++)
437 if (pRCAP
->pClientIDs
[i
] == clientspec
)
446 } /* RecordFindClientOnContext */
452 * pContext is the recording context.
453 * client is the client being recorded.
454 * stuff is a pointer to the big request of client (see the Big Requests
455 * extension for details.)
460 * The big request is recorded with the correct length field re-inserted.
462 * Note: this function exists mainly to make RecordARequest smaller.
465 RecordABigRequest(RecordContextPtr pContext
, ClientPtr client
, xReq
*stuff
)
471 /* note: client->req_len has been frobbed by ReadRequestFromClient
472 * (os/io.c) to discount the extra 4 bytes taken by the extended length
473 * field in a big request. The actual request length to record is
474 * client->req_len + 1 (measured in CARD32s).
477 /* record the request header */
478 bytesLeft
= client
->req_len
<< 2;
479 RecordAProtocolElement(pContext
, client
, XRecordFromClient
,
480 (pointer
)stuff
, SIZEOF(xReq
), bytesLeft
);
482 /* reinsert the extended length field that was squished out */
483 bigLength
= client
->req_len
+ (sizeof(bigLength
) >> 2);
485 swapl(&bigLength
, n
);
486 RecordAProtocolElement(pContext
, client
, XRecordFromClient
,
487 (pointer
)&bigLength
, sizeof(bigLength
), /* continuation */ -1);
488 bytesLeft
-= sizeof(bigLength
);
490 /* record the rest of the request after the length */
491 RecordAProtocolElement(pContext
, client
, XRecordFromClient
,
492 (pointer
)(stuff
+ 1), bytesLeft
, /* continuation */ -1);
493 } /* RecordABigRequest */
499 * client is a client that the server has dispatched a request to by
500 * calling client->requestVector[request opcode] .
501 * The request is in client->requestBuffer.
504 * Whatever is returned by the "real" Proc function for this request.
505 * The "real" Proc function is the function that was in
506 * client->requestVector[request opcode] before it was replaced by
507 * RecordARequest. (See the function RecordInstallHooks.)
510 * The request is recorded by all contexts that have registered this
511 * request for this client. The real Proc function is called.
514 RecordARequest(ClientPtr client
)
516 RecordContextPtr pContext
;
517 RecordClientsAndProtocolPtr pRCAP
;
519 RecordClientPrivatePtr pClientPriv
;
523 majorop
= stuff
->reqType
;
524 for (i
= 0; i
< numEnabledContexts
; i
++)
526 pContext
= ppAllContexts
[i
];
527 pRCAP
= RecordFindClientOnContext(pContext
, client
->clientAsMask
,
529 if (pRCAP
&& pRCAP
->pRequestMajorOpSet
&&
530 RecordIsMemberOfSet(pRCAP
->pRequestMajorOpSet
, majorop
))
535 if (stuff
->length
== 0)
536 RecordABigRequest(pContext
, client
, stuff
);
538 RecordAProtocolElement(pContext
, client
, XRecordFromClient
,
539 (pointer
)stuff
, client
->req_len
<< 2, 0);
541 else /* extension, check minor opcode */
543 int minorop
= MinorOpcodeOfRequest(client
);
545 RecordMinorOpPtr pMinorOpInfo
= pRCAP
->pRequestMinOpInfo
;
547 assert (pMinorOpInfo
);
548 numMinOpInfo
= pMinorOpInfo
->count
;
550 assert (numMinOpInfo
);
551 for ( ; numMinOpInfo
; numMinOpInfo
--, pMinorOpInfo
++)
553 if (majorop
>= pMinorOpInfo
->major
.first
&&
554 majorop
<= pMinorOpInfo
->major
.last
&&
555 RecordIsMemberOfSet(pMinorOpInfo
->major
.pMinOpSet
,
558 if (stuff
->length
== 0)
559 RecordABigRequest(pContext
, client
, stuff
);
561 RecordAProtocolElement(pContext
, client
,
562 XRecordFromClient
, (pointer
)stuff
,
563 client
->req_len
<< 2, 0);
566 } /* end for each minor op info */
567 } /* end extension request */
568 } /* end this RCAP wants this major opcode */
569 } /* end for each context */
570 pClientPriv
= RecordClientPrivate(client
);
572 return (* pClientPriv
->originalVector
[majorop
])(client
);
573 } /* RecordARequest */
576 /* RecordASkippedRequest
579 * pcbl is &SkippedRequestCallback.
581 * calldata is a pointer to a SkippedRequestInfoRec (include/os.h)
582 * which provides information about requests that the server is
583 * skipping. The client's proc vector won't be called for skipped
584 * requests, so that's why we have to catch them here.
589 * The skipped requests are recorded by all contexts that have
590 * registered those requests for this client.
592 * Note: most servers don't skip requests, so calls to this will probably
593 * be rare. For more information on skipped requests, search for
594 * the word skip in ddx.tbl.ms (the porting layer document).
597 RecordASkippedRequest(CallbackListPtr
*pcbl
, pointer nulldata
, pointer calldata
)
599 SkippedRequestInfoRec
*psi
= (SkippedRequestInfoRec
*)calldata
;
600 RecordContextPtr pContext
;
601 RecordClientsAndProtocolPtr pRCAP
;
602 xReqPtr stuff
= psi
->req
;
603 ClientPtr client
= psi
->client
;
604 int numSkippedRequests
= psi
->numskipped
;
609 while (numSkippedRequests
--)
611 majorop
= stuff
->reqType
;
612 reqlen
= ReqLen(stuff
, client
);
613 /* handle big request */
614 if (stuff
->length
== 0)
616 for (i
= 0; i
< numEnabledContexts
; i
++)
618 pContext
= ppAllContexts
[i
];
619 pRCAP
= RecordFindClientOnContext(pContext
, client
->clientAsMask
,
621 if (pRCAP
&& pRCAP
->pRequestMajorOpSet
&&
622 RecordIsMemberOfSet(pRCAP
->pRequestMajorOpSet
, majorop
))
627 RecordAProtocolElement(pContext
, client
, XRecordFromClient
,
628 (pointer
)stuff
, reqlen
, 0);
630 else /* extension, check minor opcode */
632 int minorop
= MinorOpcodeOfRequest(client
);
634 RecordMinorOpPtr pMinorOpInfo
= pRCAP
->pRequestMinOpInfo
;
636 assert (pMinorOpInfo
);
637 numMinOpInfo
= pMinorOpInfo
->count
;
639 assert (numMinOpInfo
);
640 for ( ; numMinOpInfo
; numMinOpInfo
--, pMinorOpInfo
++)
642 if (majorop
>= pMinorOpInfo
->major
.first
&&
643 majorop
<= pMinorOpInfo
->major
.last
&&
644 RecordIsMemberOfSet(pMinorOpInfo
->major
.pMinOpSet
,
647 RecordAProtocolElement(pContext
, client
,
648 XRecordFromClient
, (pointer
)stuff
,
652 } /* end for each minor op info */
653 } /* end extension request */
654 } /* end this RCAP wants this major opcode */
655 } /* end for each context */
657 /* go to next request */
658 stuff
= (xReqPtr
)( ((char *)stuff
) + reqlen
);
660 } /* end for each skipped request */
661 } /* RecordASkippedRequest */
667 * pcbl is &ReplyCallback.
669 * calldata is a pointer to a ReplyInfoRec (include/os.h)
670 * which provides information about replies that are being sent
676 * The reply is recorded by all contexts that have registered this
677 * reply type for this client. If more data belonging to the same
678 * reply is expected, and if the reply is being recorded by any
679 * context, pContext->continuedReply is set to 1.
680 * If pContext->continuedReply was already 1 and this is the last
681 * chunk of data belonging to this reply, it is set to 0.
684 RecordAReply(CallbackListPtr
*pcbl
, pointer nulldata
, pointer calldata
)
686 RecordContextPtr pContext
;
687 RecordClientsAndProtocolPtr pRCAP
;
690 ReplyInfoRec
*pri
= (ReplyInfoRec
*)calldata
;
691 ClientPtr client
= pri
->client
;
694 majorop
= stuff
->reqType
;
695 for (eci
= 0; eci
< numEnabledContexts
; eci
++)
697 pContext
= ppAllContexts
[eci
];
698 pRCAP
= RecordFindClientOnContext(pContext
, client
->clientAsMask
,
702 if (pContext
->continuedReply
)
704 RecordAProtocolElement(pContext
, client
, XRecordFromServer
,
705 pri
->replyData
, pri
->dataLenBytes
, /* continuation */ -1);
706 if (!pri
->bytesRemaining
)
707 pContext
->continuedReply
= 0;
709 else if (pri
->startOfReply
&& pRCAP
->pReplyMajorOpSet
&&
710 RecordIsMemberOfSet(pRCAP
->pReplyMajorOpSet
, majorop
))
714 RecordAProtocolElement(pContext
, client
, XRecordFromServer
,
715 pri
->replyData
, pri
->dataLenBytes
, pri
->bytesRemaining
);
716 if (pri
->bytesRemaining
)
717 pContext
->continuedReply
= 1;
719 else /* extension, check minor opcode */
721 int minorop
= MinorOpcodeOfRequest(client
);
723 RecordMinorOpPtr pMinorOpInfo
= pRCAP
->pReplyMinOpInfo
;
724 assert (pMinorOpInfo
);
725 numMinOpInfo
= pMinorOpInfo
->count
;
727 assert (numMinOpInfo
);
728 for ( ; numMinOpInfo
; numMinOpInfo
--, pMinorOpInfo
++)
730 if (majorop
>= pMinorOpInfo
->major
.first
&&
731 majorop
<= pMinorOpInfo
->major
.last
&&
732 RecordIsMemberOfSet(pMinorOpInfo
->major
.pMinOpSet
,
735 RecordAProtocolElement(pContext
, client
,
736 XRecordFromServer
, pri
->replyData
,
737 pri
->dataLenBytes
, pri
->bytesRemaining
);
738 if (pri
->bytesRemaining
)
739 pContext
->continuedReply
= 1;
742 } /* end for each minor op info */
743 } /* end extension reply */
744 } /* end continued reply vs. start of reply */
745 } /* end client is registered on this context */
746 } /* end for each context */
750 /* RecordADeliveredEventOrError
753 * pcbl is &EventCallback.
755 * calldata is a pointer to a EventInfoRec (include/dix.h)
756 * which provides information about events that are being sent
762 * The event or error is recorded by all contexts that have registered
763 * it for this client.
766 RecordADeliveredEventOrError(CallbackListPtr
*pcbl
, pointer nulldata
, pointer calldata
)
768 EventInfoRec
*pei
= (EventInfoRec
*)calldata
;
769 RecordContextPtr pContext
;
770 RecordClientsAndProtocolPtr pRCAP
;
771 int eci
; /* enabled context index */
772 ClientPtr pClient
= pei
->client
;
774 for (eci
= 0; eci
< numEnabledContexts
; eci
++)
776 pContext
= ppAllContexts
[eci
];
777 pRCAP
= RecordFindClientOnContext(pContext
, pClient
->clientAsMask
,
779 if (pRCAP
&& (pRCAP
->pDeliveredEventSet
|| pRCAP
->pErrorSet
))
781 int ev
; /* event index */
782 xEvent
*pev
= pei
->events
;
783 for (ev
= 0; ev
< pei
->count
; ev
++, pev
++)
786 if (pRCAP
->pErrorSet
)
788 recordit
= RecordIsMemberOfSet(pRCAP
->pErrorSet
,
789 ((xError
*)(pev
))->errorCode
);
791 else if (pRCAP
->pDeliveredEventSet
)
793 recordit
= RecordIsMemberOfSet(pRCAP
->pDeliveredEventSet
,
794 pev
->u
.u
.type
& 0177);
799 xEvent
*pEvToRecord
= pev
;
801 if (pClient
->swapped
)
803 (*EventSwapVector
[pev
->u
.u
.type
& 0177])
804 (pev
, &swappedEvent
);
805 pEvToRecord
= &swappedEvent
;
808 RecordAProtocolElement(pContext
, pClient
,
809 XRecordFromServer
, pEvToRecord
, SIZEOF(xEvent
), 0);
811 } /* end for each event */
812 } /* end this client is on this context */
813 } /* end for each enabled context */
814 } /* RecordADeliveredEventOrError */
817 /* RecordADeviceEvent
820 * pcbl is &DeviceEventCallback.
822 * calldata is a pointer to a DeviceEventInfoRec (include/dix.h)
823 * which provides information about device events that occur.
828 * The device event is recorded by all contexts that have registered
829 * it for this client.
832 RecordADeviceEvent(CallbackListPtr
*pcbl
, pointer nulldata
, pointer calldata
)
834 DeviceEventInfoRec
*pei
= (DeviceEventInfoRec
*)calldata
;
835 RecordContextPtr pContext
;
836 RecordClientsAndProtocolPtr pRCAP
;
837 int eci
; /* enabled context index */
839 for (eci
= 0; eci
< numEnabledContexts
; eci
++)
841 pContext
= ppAllContexts
[eci
];
842 for (pRCAP
= pContext
->pListOfRCAP
; pRCAP
; pRCAP
= pRCAP
->pNextRCAP
)
844 if (pRCAP
->pDeviceEventSet
)
846 int ev
; /* event index */
847 xEvent
*pev
= pei
->events
;
848 for (ev
= 0; ev
< pei
->count
; ev
++, pev
++)
850 if (RecordIsMemberOfSet(pRCAP
->pDeviceEventSet
,
851 pev
->u
.u
.type
& 0177))
854 xEvent
*pEvToRecord
= pev
;
858 if (!noPanoramiXExtension
&&
859 (pev
->u
.u
.type
== MotionNotify
||
860 pev
->u
.u
.type
== ButtonPress
||
861 pev
->u
.u
.type
== ButtonRelease
||
862 pev
->u
.u
.type
== KeyPress
||
863 pev
->u
.u
.type
== KeyRelease
)) {
864 int scr
= XineramaGetCursorScreen();
865 memcpy(&shiftedEvent
, pev
, sizeof(xEvent
));
866 shiftedEvent
.u
.keyButtonPointer
.rootX
+=
867 panoramiXdataPtr
[scr
].x
-
868 panoramiXdataPtr
[0].x
;
869 shiftedEvent
.u
.keyButtonPointer
.rootY
+=
870 panoramiXdataPtr
[scr
].y
-
871 panoramiXdataPtr
[0].y
;
872 pEvToRecord
= &shiftedEvent
;
874 #endif /* PANORAMIX */
876 if (pContext
->pRecordingClient
->swapped
)
878 (*EventSwapVector
[pEvToRecord
->u
.u
.type
& 0177])
879 (pEvToRecord
, &swappedEvent
);
880 pEvToRecord
= &swappedEvent
;
883 RecordAProtocolElement(pContext
, NULL
,
884 XRecordFromServer
, pEvToRecord
, SIZEOF(xEvent
), 0);
885 /* make sure device events get flushed in the absence
886 * of other client activity
888 SetCriticalOutputPending();
890 } /* end for each event */
891 } /* end this RCAP selects device events */
892 } /* end for each RCAP on this context */
893 } /* end for each enabled context */
894 } /* RecordADeviceEvent */
897 /* RecordFlushAllContexts
900 * pcbl is &FlushCallback.
901 * nulldata and calldata are NULL.
906 * All buffered reply data of all enabled contexts is written to
907 * the recording clients.
910 RecordFlushAllContexts(
911 CallbackListPtr
*pcbl
,
916 int eci
; /* enabled context index */
917 RecordContextPtr pContext
;
919 for (eci
= 0; eci
< numEnabledContexts
; eci
++)
921 pContext
= ppAllContexts
[eci
];
923 /* In most cases we leave it to RecordFlushReplyBuffer to make
924 * this check, but this function could be called very often, so we
925 * check before calling hoping to save the function call cost
928 if (pContext
->numBufBytes
)
929 RecordFlushReplyBuffer(ppAllContexts
[eci
], NULL
, 0, NULL
, 0);
931 } /* RecordFlushAllContexts */
934 /* RecordInstallHooks
937 * pRCAP is an RCAP on an enabled or being-enabled context.
938 * oneclient can be zero or the resource ID mask identifying a client.
940 * Returns: BadAlloc if a memory allocation error occurred, else Success.
943 * Recording hooks needed by RCAP are installed.
944 * If oneclient is zero, recording hooks needed for all clients and
945 * protocol on the RCAP are installed. If oneclient is non-zero,
946 * only those hooks needed for the specified client are installed.
948 * Client requestVectors may be altered. numEnabledRCAPs will be
949 * incremented if oneclient == 0. Callbacks may be added to
950 * various callback lists.
953 RecordInstallHooks(RecordClientsAndProtocolPtr pRCAP
, XID oneclient
)
961 client
= pRCAP
->numClients
? pRCAP
->pClientIDs
[i
++] : 0;
965 if (client
!= XRecordFutureClients
)
967 if (pRCAP
->pRequestMajorOpSet
)
969 RecordSetIteratePtr pIter
= NULL
;
970 RecordSetInterval interval
;
971 ClientPtr pClient
= clients
[CLIENT_ID(client
)];
973 if (pClient
&& !RecordClientPrivate(pClient
))
975 RecordClientPrivatePtr pClientPriv
;
976 /* no Record proc vector; allocate one */
977 pClientPriv
= (RecordClientPrivatePtr
)
978 xalloc(sizeof(RecordClientPrivateRec
));
981 /* copy old proc vector to new */
982 memcpy(pClientPriv
->recordVector
, pClient
->requestVector
,
983 sizeof (pClientPriv
->recordVector
));
984 pClientPriv
->originalVector
= pClient
->requestVector
;
985 pClient
->devPrivates
[RecordClientPrivateIndex
].ptr
=
986 (pointer
)pClientPriv
;
987 pClient
->requestVector
= pClientPriv
->recordVector
;
989 while ((pIter
= RecordIterateSet(pRCAP
->pRequestMajorOpSet
,
993 for (j
= interval
.first
; j
<= interval
.last
; j
++)
994 pClient
->requestVector
[j
] = RecordARequest
;
1001 client
= (i
< pRCAP
->numClients
) ? pRCAP
->pClientIDs
[i
++] : 0;
1004 assert(numEnabledRCAPs
>= 0);
1005 if (!oneclient
&& ++numEnabledRCAPs
== 1)
1006 { /* we're enabling the first context */
1007 if (!AddCallback(&EventCallback
, RecordADeliveredEventOrError
, NULL
))
1009 if (!AddCallback(&DeviceEventCallback
, RecordADeviceEvent
, NULL
))
1011 if (!AddCallback(&ReplyCallback
, RecordAReply
, NULL
))
1013 if (!AddCallback(&SkippedRequestsCallback
, RecordASkippedRequest
,
1016 if (!AddCallback(&FlushCallback
, RecordFlushAllContexts
, NULL
))
1018 /* Alternate context flushing scheme: delete the line above
1019 * and call RegisterBlockAndWakeupHandlers here passing
1020 * RecordFlushAllContexts. Is this any better?
1024 } /* RecordInstallHooks */
1027 /* RecordUninstallHooks
1030 * pRCAP is an RCAP on an enabled or being-disabled context.
1031 * oneclient can be zero or the resource ID mask identifying a client.
1036 * Recording hooks needed by RCAP may be uninstalled.
1037 * If oneclient is zero, recording hooks needed for all clients and
1038 * protocol on the RCAP may be uninstalled. If oneclient is non-zero,
1039 * only those hooks needed for the specified client may be uninstalled.
1041 * Client requestVectors may be altered. numEnabledRCAPs will be
1042 * decremented if oneclient == 0. Callbacks may be deleted from
1043 * various callback lists.
1046 RecordUninstallHooks(RecordClientsAndProtocolPtr pRCAP
, XID oneclient
)
1054 client
= pRCAP
->numClients
? pRCAP
->pClientIDs
[i
++] : 0;
1058 if (client
!= XRecordFutureClients
)
1060 if (pRCAP
->pRequestMajorOpSet
)
1062 ClientPtr pClient
= clients
[CLIENT_ID(client
)];
1064 Bool otherRCAPwantsProcVector
= FALSE
;
1065 RecordClientPrivatePtr pClientPriv
=
1066 RecordClientPrivate(pClient
);
1068 assert (pClient
&& RecordClientPrivate(pClient
));
1069 memcpy(pClientPriv
->recordVector
, pClientPriv
->originalVector
,
1070 sizeof (pClientPriv
->recordVector
));
1072 for (c
= 0; c
< numEnabledContexts
; c
++)
1074 RecordClientsAndProtocolPtr pOtherRCAP
;
1075 RecordContextPtr pContext
= ppAllContexts
[c
];
1077 if (pContext
== pRCAP
->pContext
) continue;
1078 pOtherRCAP
= RecordFindClientOnContext(pContext
, client
,
1080 if (pOtherRCAP
&& pOtherRCAP
->pRequestMajorOpSet
)
1082 RecordSetIteratePtr pIter
= NULL
;
1083 RecordSetInterval interval
;
1085 otherRCAPwantsProcVector
= TRUE
;
1086 while ((pIter
= RecordIterateSet(
1087 pOtherRCAP
->pRequestMajorOpSet
,
1091 for (j
= interval
.first
; j
<= interval
.last
; j
++)
1092 pClient
->requestVector
[j
] = RecordARequest
;
1096 if (!otherRCAPwantsProcVector
)
1097 { /* nobody needs it, so free it */
1098 pClient
->requestVector
= pClientPriv
->originalVector
;
1099 pClient
->devPrivates
[RecordClientPrivateIndex
].ptr
= NULL
;
1102 } /* end if this RCAP specifies any requests */
1103 } /* end if not future clients */
1107 client
= (i
< pRCAP
->numClients
) ? pRCAP
->pClientIDs
[i
++] : 0;
1110 assert(numEnabledRCAPs
>= 1);
1111 if (!oneclient
&& --numEnabledRCAPs
== 0)
1112 { /* we're disabling the last context */
1113 DeleteCallback(&EventCallback
, RecordADeliveredEventOrError
, NULL
);
1114 DeleteCallback(&DeviceEventCallback
, RecordADeviceEvent
, NULL
);
1115 DeleteCallback(&ReplyCallback
, RecordAReply
, NULL
);
1116 DeleteCallback(&SkippedRequestsCallback
, RecordASkippedRequest
, NULL
);
1117 DeleteCallback(&FlushCallback
, RecordFlushAllContexts
, NULL
);
1118 /* Alternate context flushing scheme: delete the line above
1119 * and call RemoveBlockAndWakeupHandlers here passing
1120 * RecordFlushAllContexts. Is this any better?
1122 /* Having deleted the callback, call it one last time. -gildea */
1123 RecordFlushAllContexts(&FlushCallback
, NULL
, NULL
);
1125 } /* RecordUninstallHooks */
1128 /* RecordDeleteClientFromRCAP
1131 * pRCAP is an RCAP to delete the client from.
1132 * position is the index into the array pRCAP->pClientIDs of the
1138 * Recording hooks needed by client will be uninstalled if the context
1139 * is enabled. The designated client will be removed from the
1140 * pRCAP->pClientIDs array. If it was the only client on the RCAP,
1141 * the RCAP is removed from the context and freed. (Invariant: RCAPs
1142 * have at least one client.)
1145 RecordDeleteClientFromRCAP(RecordClientsAndProtocolPtr pRCAP
, int position
)
1147 if (pRCAP
->pContext
->pRecordingClient
)
1148 RecordUninstallHooks(pRCAP
, pRCAP
->pClientIDs
[position
]);
1149 if (position
!= pRCAP
->numClients
- 1)
1150 pRCAP
->pClientIDs
[position
] = pRCAP
->pClientIDs
[pRCAP
->numClients
- 1];
1151 if (--pRCAP
->numClients
== 0)
1152 { /* no more clients; remove RCAP from context's list */
1153 RecordContextPtr pContext
= pRCAP
->pContext
;
1154 if (pContext
->pRecordingClient
)
1155 RecordUninstallHooks(pRCAP
, 0);
1156 if (pContext
->pListOfRCAP
== pRCAP
)
1157 pContext
->pListOfRCAP
= pRCAP
->pNextRCAP
;
1160 RecordClientsAndProtocolPtr prevRCAP
;
1161 for (prevRCAP
= pContext
->pListOfRCAP
;
1162 prevRCAP
->pNextRCAP
!= pRCAP
;
1163 prevRCAP
= prevRCAP
->pNextRCAP
)
1165 prevRCAP
->pNextRCAP
= pRCAP
->pNextRCAP
;
1168 if (pRCAP
->clientIDsSeparatelyAllocated
)
1169 xfree(pRCAP
->pClientIDs
);
1172 } /* RecordDeleteClientFromRCAP */
1175 /* RecordAddClientToRCAP
1178 * pRCAP is an RCAP to add the client to.
1179 * clientspec is the resource ID mask identifying a client, or
1180 * XRecordFutureClients.
1185 * Recording hooks needed by client will be installed if the context
1186 * is enabled. The designated client will be added to the
1187 * pRCAP->pClientIDs array, which may be realloced.
1188 * pRCAP->clientIDsSeparatelyAllocated may be set to 1 if there
1189 * is no more room to hold clients internal to the RCAP.
1192 RecordAddClientToRCAP(RecordClientsAndProtocolPtr pRCAP
, XID clientspec
)
1194 if (pRCAP
->numClients
== pRCAP
->sizeClients
)
1196 if (pRCAP
->clientIDsSeparatelyAllocated
)
1198 XID
*pNewIDs
= (XID
*)xrealloc(pRCAP
->pClientIDs
,
1199 (pRCAP
->sizeClients
+ CLIENT_ARRAY_GROWTH_INCREMENT
) *
1203 pRCAP
->pClientIDs
= pNewIDs
;
1204 pRCAP
->sizeClients
+= CLIENT_ARRAY_GROWTH_INCREMENT
;
1208 XID
*pNewIDs
= (XID
*)xalloc((pRCAP
->sizeClients
+
1209 CLIENT_ARRAY_GROWTH_INCREMENT
) * sizeof(XID
));
1212 memcpy(pNewIDs
, pRCAP
->pClientIDs
, pRCAP
->numClients
*sizeof(XID
));
1213 pRCAP
->pClientIDs
= pNewIDs
;
1214 pRCAP
->sizeClients
+= CLIENT_ARRAY_GROWTH_INCREMENT
;
1215 pRCAP
->clientIDsSeparatelyAllocated
= 1;
1218 pRCAP
->pClientIDs
[pRCAP
->numClients
++] = clientspec
;
1219 if (pRCAP
->pContext
->pRecordingClient
)
1220 RecordInstallHooks(pRCAP
, clientspec
);
1221 } /* RecordDeleteClientFromRCAP */
1224 /* RecordDeleteClientFromContext
1227 * pContext is the context to delete from.
1228 * clientspec is the resource ID mask identifying a client, or
1229 * XRecordFutureClients.
1234 * If clientspec is on any RCAP of the context, it is deleted from that
1235 * RCAP. (A given clientspec can only be on one RCAP of a context.)
1238 RecordDeleteClientFromContext(RecordContextPtr pContext
, XID clientspec
)
1240 RecordClientsAndProtocolPtr pRCAP
;
1243 if ((pRCAP
= RecordFindClientOnContext(pContext
, clientspec
, &position
)))
1244 RecordDeleteClientFromRCAP(pRCAP
, position
);
1245 } /* RecordDeleteClientFromContext */
1248 /* RecordSanityCheckClientSpecifiers
1251 * clientspecs is an array of alleged CLIENTSPECs passed by the client.
1252 * nspecs is the number of elements in clientspecs.
1253 * errorspec, if non-zero, is the resource id base of a client that
1254 * must not appear in clienspecs.
1256 * Returns: BadMatch if any of the clientspecs are invalid, else Success.
1258 * Side Effects: none.
1261 RecordSanityCheckClientSpecifiers(XID
*clientspecs
, int nspecs
, XID errorspec
)
1266 for (i
= 0; i
< nspecs
; i
++)
1268 if (clientspecs
[i
] == XRecordCurrentClients
||
1269 clientspecs
[i
] == XRecordFutureClients
||
1270 clientspecs
[i
] == XRecordAllClients
)
1272 if (errorspec
&& (CLIENT_BITS(clientspecs
[i
]) == errorspec
) )
1274 clientIndex
= CLIENT_ID(clientspecs
[i
]);
1275 if (clientIndex
&& clients
[clientIndex
] &&
1276 clients
[clientIndex
]->clientState
== ClientStateRunning
)
1278 if (clientspecs
[i
] == clients
[clientIndex
]->clientAsMask
)
1280 if (!LookupIDByClass(clientspecs
[i
], RC_ANY
))
1287 } /* RecordSanityCheckClientSpecifiers */
1290 /* RecordCanonicalizeClientSpecifiers
1293 * pClientspecs is an array of CLIENTSPECs that have been sanity
1295 * pNumClientspecs is a pointer to the number of elements in pClientspecs.
1296 * excludespec, if non-zero, is the resource id base of a client that
1297 * should not be included in the expansion of XRecordAllClients or
1298 * XRecordCurrentClients.
1301 * A pointer to an array of CLIENTSPECs that is the same as the
1302 * passed array with the following modifications:
1303 * - all but the client id bits of resource IDs are stripped off.
1304 * - duplicates removed.
1305 * - XRecordAllClients expanded to a list of all currently connected
1306 * clients + XRecordFutureClients - excludespec (if non-zero)
1307 * - XRecordCurrentClients expanded to a list of all currently
1308 * connected clients - excludespec (if non-zero)
1309 * The returned array may be the passed array modified in place, or
1310 * it may be an Xalloc'ed array. The caller should keep a pointer to the
1311 * original array and free the returned array if it is different.
1313 * *pNumClientspecs is set to the number of elements in the returned
1317 * pClientspecs may be modified in place.
1320 RecordCanonicalizeClientSpecifiers(XID
*pClientspecs
, int *pNumClientspecs
, XID excludespec
)
1323 int numClients
= *pNumClientspecs
;
1325 /* first pass strips off the resource index bits, leaving just the
1326 * client id bits. This makes searching for a particular client simpler
1329 for (i
= 0; i
< numClients
; i
++)
1331 XID cs
= pClientspecs
[i
];
1332 if (cs
> XRecordAllClients
)
1333 pClientspecs
[i
] = CLIENT_BITS(cs
);
1336 for (i
= 0; i
< numClients
; i
++)
1338 if (pClientspecs
[i
] == XRecordAllClients
||
1339 pClientspecs
[i
] == XRecordCurrentClients
)
1340 { /* expand All/Current */
1342 XID
*pCanon
= (XID
*)xalloc(sizeof(XID
) * (currentMaxClients
+ 1));
1343 if (!pCanon
) return NULL
;
1344 for (nc
= 0, j
= 1; j
< currentMaxClients
; j
++)
1346 ClientPtr client
= clients
[j
];
1347 if (client
!= NullClient
&&
1348 client
->clientState
== ClientStateRunning
&&
1349 client
->clientAsMask
!= excludespec
)
1351 pCanon
[nc
++] = client
->clientAsMask
;
1354 if (pClientspecs
[i
] == XRecordAllClients
)
1355 pCanon
[nc
++] = XRecordFutureClients
;
1356 *pNumClientspecs
= nc
;
1359 else /* not All or Current */
1362 for (j
= i
+ 1; j
< numClients
; )
1364 if (pClientspecs
[i
] == pClientspecs
[j
])
1366 pClientspecs
[j
] = pClientspecs
[--numClients
];
1372 } /* end for each clientspec */
1373 *pNumClientspecs
= numClients
;
1374 return pClientspecs
;
1375 } /* RecordCanonicalizeClientSpecifiers */
1378 /****************************************************************************/
1380 /* stuff for RegisterClients */
1385 * size is the number of bytes taken by an object.
1386 * align is a byte boundary (e.g. 4, 8)
1389 * the number of pad bytes to add at the end of an object of the
1390 * given size so that an object placed immediately behind it will
1391 * begin on an <align>-byte boundary.
1393 * Side Effects: none.
1396 RecordPadAlign(int size
, int align
)
1398 return (align
- (size
& (align
- 1))) & (align
- 1);
1399 } /* RecordPadAlign */
1402 /* RecordSanityCheckRegisterClients
1405 * pContext is the context being registered on.
1406 * client is the client that issued a RecordCreateContext or
1407 * RecordRegisterClients request.
1408 * stuff is a pointer to the request.
1411 * Any one of several possible error values if any of the request
1412 * arguments are invalid. Success if everything is OK.
1414 * Side Effects: none.
1417 RecordSanityCheckRegisterClients(RecordContextPtr pContext
, ClientPtr client
, xRecordRegisterClientsReq
*stuff
)
1420 xRecordRange
*pRange
;
1422 XID recordingClient
;
1424 if (((client
->req_len
<< 2) - SIZEOF(xRecordRegisterClientsReq
)) !=
1425 4 * stuff
->nClients
+ SIZEOF(xRecordRange
) * stuff
->nRanges
)
1428 if (stuff
->elementHeader
&
1429 ~(XRecordFromClientSequence
|XRecordFromClientTime
|XRecordFromServerTime
))
1431 client
->errorValue
= stuff
->elementHeader
;
1435 recordingClient
= pContext
->pRecordingClient
?
1436 pContext
->pRecordingClient
->clientAsMask
: 0;
1437 err
= RecordSanityCheckClientSpecifiers((XID
*)&stuff
[1], stuff
->nClients
,
1439 if (err
!= Success
) return err
;
1441 pRange
= (xRecordRange
*)(((XID
*)&stuff
[1]) + stuff
->nClients
);
1442 for (i
= 0; i
< stuff
->nRanges
; i
++, pRange
++)
1444 if (pRange
->coreRequestsFirst
> pRange
->coreRequestsLast
)
1446 client
->errorValue
= pRange
->coreRequestsFirst
;
1449 if (pRange
->coreRepliesFirst
> pRange
->coreRepliesLast
)
1451 client
->errorValue
= pRange
->coreRepliesFirst
;
1454 if ((pRange
->extRequestsMajorFirst
|| pRange
->extRequestsMajorLast
) &&
1455 (pRange
->extRequestsMajorFirst
< 128 ||
1456 pRange
->extRequestsMajorLast
< 128 ||
1457 pRange
->extRequestsMajorFirst
> pRange
->extRequestsMajorLast
))
1459 client
->errorValue
= pRange
->extRequestsMajorFirst
;
1462 if (pRange
->extRequestsMinorFirst
> pRange
->extRequestsMinorLast
)
1464 client
->errorValue
= pRange
->extRequestsMinorFirst
;
1467 if ((pRange
->extRepliesMajorFirst
|| pRange
->extRepliesMajorLast
) &&
1468 (pRange
->extRepliesMajorFirst
< 128 ||
1469 pRange
->extRepliesMajorLast
< 128 ||
1470 pRange
->extRepliesMajorFirst
> pRange
->extRepliesMajorLast
))
1472 client
->errorValue
= pRange
->extRepliesMajorFirst
;
1475 if (pRange
->extRepliesMinorFirst
> pRange
->extRepliesMinorLast
)
1477 client
->errorValue
= pRange
->extRepliesMinorFirst
;
1480 if ((pRange
->deliveredEventsFirst
|| pRange
->deliveredEventsLast
) &&
1481 (pRange
->deliveredEventsFirst
< 2 ||
1482 pRange
->deliveredEventsLast
< 2 ||
1483 pRange
->deliveredEventsFirst
> pRange
->deliveredEventsLast
))
1485 client
->errorValue
= pRange
->deliveredEventsFirst
;
1488 if ((pRange
->deviceEventsFirst
|| pRange
->deviceEventsLast
) &&
1489 (pRange
->deviceEventsFirst
< 2 ||
1490 pRange
->deviceEventsLast
< 2 ||
1491 pRange
->deviceEventsFirst
> pRange
->deviceEventsLast
))
1493 client
->errorValue
= pRange
->deviceEventsFirst
;
1496 if (pRange
->errorsFirst
> pRange
->errorsLast
)
1498 client
->errorValue
= pRange
->errorsFirst
;
1501 if (pRange
->clientStarted
!= xFalse
&& pRange
->clientStarted
!= xTrue
)
1503 client
->errorValue
= pRange
->clientStarted
;
1506 if (pRange
->clientDied
!= xFalse
&& pRange
->clientDied
!= xTrue
)
1508 client
->errorValue
= pRange
->clientDied
;
1511 } /* end for each range */
1513 } /* end RecordSanityCheckRegisterClients */
1515 /* This is a tactical structure used to gather information about all the sets
1516 * (RecordSetPtr) that need to be created for an RCAP in the process of
1517 * digesting a list of RECORDRANGEs (converting it to the internal
1522 int nintervals
; /* number of intervals in following array */
1523 RecordSetInterval
*intervals
; /* array of intervals for this set */
1524 int size
; /* size of intevals array; >= nintervals */
1525 int align
; /* alignment restriction for set */
1526 int offset
; /* where to store set pointer rel. to start of RCAP */
1527 short first
, last
; /* if for extension, major opcode interval */
1528 } SetInfoRec
, *SetInfoPtr
;
1530 /* These constant are used to index into an array of SetInfoRec. */
1531 enum {REQ
, /* set info for requests */
1532 REP
, /* set info for replies */
1533 ERR
, /* set info for errors */
1534 DEV
, /* set info for device events */
1535 DLEV
, /* set info for delivered events */
1536 PREDEFSETS
}; /* number of predefined array entries */
1539 /* RecordAllocIntervals
1542 * psi is a pointer to a SetInfoRec whose intervals pointer is NULL.
1543 * nIntervals is the desired size of the intervals array.
1545 * Returns: BadAlloc if a memory allocation error occurred, else Success.
1548 * If Success is returned, psi->intervals is a pointer to size
1549 * RecordSetIntervals, all zeroed, and psi->size is set to size.
1552 RecordAllocIntervals(SetInfoPtr psi
, int nIntervals
)
1554 assert(!psi
->intervals
);
1555 psi
->intervals
= (RecordSetInterval
*)
1556 xalloc(nIntervals
* sizeof(RecordSetInterval
));
1557 if (!psi
->intervals
)
1559 bzero(psi
->intervals
, nIntervals
* sizeof(RecordSetInterval
));
1560 psi
->size
= nIntervals
;
1562 } /* end RecordAllocIntervals */
1565 /* RecordConvertRangesToIntervals
1568 * psi is a pointer to the SetInfoRec we are building.
1569 * pRanges is an array of xRecordRanges.
1570 * nRanges is the number of elements in pRanges.
1571 * byteoffset is the offset from the start of an xRecordRange of the
1572 * two bytes (1 for first, 1 for last) we are interested in.
1573 * pExtSetInfo, if non-NULL, indicates that the two bytes mentioned
1574 * above are followed by four bytes (2 for first, 2 for last)
1575 * representing a minor opcode range, and this information should be
1576 * stored in one of the SetInfoRecs starting at pExtSetInfo.
1577 * pnExtSetInfo is the number of elements in the pExtSetInfo array.
1579 * Returns: BadAlloc if a memory allocation error occurred, else Success.
1582 * The slice of pRanges indicated by byteoffset is stored in psi.
1583 * If pExtSetInfo is non-NULL, minor opcode intervals are stored
1584 * in an existing SetInfoRec if the major opcode interval matches, else
1585 * they are stored in a new SetInfoRec, and *pnExtSetInfo is
1586 * increased accordingly.
1589 RecordConvertRangesToIntervals(
1591 xRecordRange
*pRanges
,
1594 SetInfoPtr pExtSetInfo
,
1603 for (i
= 0; i
< nRanges
; i
++, pRanges
++)
1605 pCARD8
= ((CARD8
*)pRanges
) + byteoffset
;
1610 if (!psi
->intervals
)
1612 err
= RecordAllocIntervals(psi
, 2 * (nRanges
- i
));
1616 psi
->intervals
[psi
->nintervals
].first
= first
;
1617 psi
->intervals
[psi
->nintervals
].last
= last
;
1619 assert(psi
->nintervals
<= psi
->size
);
1622 SetInfoPtr pesi
= pExtSetInfo
;
1623 CARD16
*pCARD16
= (CARD16
*)(pCARD8
+ 2);
1626 for (j
= 0; j
< *pnExtSetInfo
; j
++, pesi
++)
1628 if ( (first
== pesi
->first
) && (last
== pesi
->last
) )
1631 if (j
== *pnExtSetInfo
)
1633 err
= RecordAllocIntervals(pesi
, 2 * (nRanges
- i
));
1636 pesi
->first
= first
;
1640 pesi
->intervals
[pesi
->nintervals
].first
= pCARD16
[0];
1641 pesi
->intervals
[pesi
->nintervals
].last
= pCARD16
[1];
1643 assert(pesi
->nintervals
<= pesi
->size
);
1648 } /* end RecordConvertRangesToIntervals */
1650 #define offset_of(_structure, _field) \
1651 ((char *)(& (_structure . _field)) - (char *)(&_structure))
1653 /* RecordRegisterClients
1656 * pContext is the context on which to register the clients.
1657 * client is the client that issued the RecordCreateContext or
1658 * RecordRegisterClients request.
1659 * stuff is a pointer to the request.
1662 * Any one of several possible error values defined by the protocol.
1663 * Success if everything is OK.
1666 * If different element headers are specified, the context is flushed.
1667 * If any of the specified clients are already registered on the
1668 * context, they are first unregistered. A new RCAP is created to
1669 * hold the specified protocol and clients, and it is linked onto the
1670 * context. If the context is enabled, appropriate hooks are installed
1671 * to record the new clients and protocol.
1674 RecordRegisterClients(RecordContextPtr pContext
, ClientPtr client
, xRecordRegisterClientsReq
*stuff
)
1680 int nExtReqSets
= 0;
1681 int nExtRepSets
= 0;
1682 int extReqSetsOffset
= 0;
1683 int extRepSetsOffset
= 0;
1684 SetInfoPtr pExtReqSets
, pExtRepSets
;
1685 int clientListOffset
;
1687 int clientStarted
= 0, clientDied
= 0;
1688 xRecordRange
*pRanges
, rr
;
1692 RecordClientsAndProtocolPtr pRCAP
;
1694 XID recordingClient
;
1696 /* do all sanity checking up front */
1698 err
= RecordSanityCheckRegisterClients(pContext
, client
, stuff
);
1702 /* if element headers changed, flush buffer */
1704 if (pContext
->elemHeaders
!= stuff
->elementHeader
)
1706 RecordFlushReplyBuffer(pContext
, NULL
, 0, NULL
, 0);
1707 pContext
->elemHeaders
= stuff
->elementHeader
;
1710 nClients
= stuff
->nClients
;
1712 /* if empty clients list, we're done. */
1715 recordingClient
= pContext
->pRecordingClient
?
1716 pContext
->pRecordingClient
->clientAsMask
: 0;
1717 pCanonClients
= RecordCanonicalizeClientSpecifiers((XID
*)&stuff
[1],
1718 &nClients
, recordingClient
);
1722 /* We may have to create as many as one set for each "predefined"
1723 * protocol types, plus one per range for extension reuests, plus one per
1724 * range for extension replies.
1726 maxSets
= PREDEFSETS
+ 2 * stuff
->nRanges
;
1727 si
= (SetInfoPtr
)ALLOCATE_LOCAL(sizeof(SetInfoRec
) * maxSets
);
1733 bzero(si
, sizeof(SetInfoRec
) * maxSets
);
1735 /* theoretically you must do this because NULL may not be all-bits-zero */
1736 for (i
= 0; i
< maxSets
; i
++)
1737 si
[i
].intervals
= NULL
;
1739 pExtReqSets
= si
+ PREDEFSETS
;
1740 pExtRepSets
= pExtReqSets
+ stuff
->nRanges
;
1742 pRanges
= (xRecordRange
*)(((XID
*)&stuff
[1]) + stuff
->nClients
);
1744 err
= RecordConvertRangesToIntervals(&si
[REQ
], pRanges
, stuff
->nRanges
,
1745 offset_of(rr
, coreRequestsFirst
), NULL
, NULL
);
1746 if (err
!= Success
) goto bailout
;
1748 err
= RecordConvertRangesToIntervals(&si
[REQ
], pRanges
, stuff
->nRanges
,
1749 offset_of(rr
, extRequestsMajorFirst
), pExtReqSets
, &nExtReqSets
);
1750 if (err
!= Success
) goto bailout
;
1752 err
= RecordConvertRangesToIntervals(&si
[REP
], pRanges
, stuff
->nRanges
,
1753 offset_of(rr
, coreRepliesFirst
), NULL
, NULL
);
1754 if (err
!= Success
) goto bailout
;
1756 err
= RecordConvertRangesToIntervals(&si
[REP
], pRanges
, stuff
->nRanges
,
1757 offset_of(rr
, extRepliesMajorFirst
), pExtRepSets
, &nExtRepSets
);
1758 if (err
!= Success
) goto bailout
;
1760 err
= RecordConvertRangesToIntervals(&si
[ERR
], pRanges
, stuff
->nRanges
,
1761 offset_of(rr
, errorsFirst
), NULL
, NULL
);
1762 if (err
!= Success
) goto bailout
;
1764 err
= RecordConvertRangesToIntervals(&si
[DLEV
], pRanges
, stuff
->nRanges
,
1765 offset_of(rr
, deliveredEventsFirst
), NULL
, NULL
);
1766 if (err
!= Success
) goto bailout
;
1768 err
= RecordConvertRangesToIntervals(&si
[DEV
], pRanges
, stuff
->nRanges
,
1769 offset_of(rr
, deviceEventsFirst
), NULL
, NULL
);
1770 if (err
!= Success
) goto bailout
;
1772 /* collect client-started and client-died */
1774 for (i
= 0; i
< stuff
->nRanges
; i
++)
1776 if (pRanges
[i
].clientStarted
) clientStarted
= TRUE
;
1777 if (pRanges
[i
].clientDied
) clientDied
= TRUE
;
1780 /* We now have all the information collected to create all the sets,
1781 * and we can compute the total memory required for the RCAP.
1784 totRCAPsize
= sizeof(RecordClientsAndProtocolRec
);
1786 /* leave a little room to grow before forcing a separate allocation */
1787 sizeClients
= nClients
+ CLIENT_ARRAY_GROWTH_INCREMENT
;
1788 pad
= RecordPadAlign(totRCAPsize
, sizeof(XID
));
1789 clientListOffset
= totRCAPsize
+ pad
;
1790 totRCAPsize
+= pad
+ sizeClients
* sizeof(XID
);
1794 pad
= RecordPadAlign(totRCAPsize
, sizeof(RecordSetPtr
));
1795 extReqSetsOffset
= totRCAPsize
+ pad
;
1796 totRCAPsize
+= pad
+ (nExtReqSets
+ 1) * sizeof(RecordMinorOpRec
);
1800 pad
= RecordPadAlign(totRCAPsize
, sizeof(RecordSetPtr
));
1801 extRepSetsOffset
= totRCAPsize
+ pad
;
1802 totRCAPsize
+= pad
+ (nExtRepSets
+ 1) * sizeof(RecordMinorOpRec
);
1805 for (i
= 0; i
< maxSets
; i
++)
1807 if (si
[i
].nintervals
)
1809 si
[i
].size
= RecordSetMemoryRequirements(
1810 si
[i
].intervals
, si
[i
].nintervals
, &si
[i
].align
);
1811 pad
= RecordPadAlign(totRCAPsize
, si
[i
].align
);
1812 si
[i
].offset
= pad
+ totRCAPsize
;
1813 totRCAPsize
+= pad
+ si
[i
].size
;
1817 /* allocate memory for the whole RCAP */
1819 pRCAP
= (RecordClientsAndProtocolPtr
)xalloc(totRCAPsize
);
1826 /* fill in the RCAP */
1828 pRCAP
->pContext
= pContext
;
1829 pRCAP
->pClientIDs
= (XID
*)((char *)pRCAP
+ clientListOffset
);
1830 pRCAP
->numClients
= nClients
;
1831 pRCAP
->sizeClients
= sizeClients
;
1832 pRCAP
->clientIDsSeparatelyAllocated
= 0;
1833 for (i
= 0; i
< nClients
; i
++)
1835 RecordDeleteClientFromContext(pContext
, pCanonClients
[i
]);
1836 pRCAP
->pClientIDs
[i
] = pCanonClients
[i
];
1839 /* create all the sets */
1841 if (si
[REQ
].intervals
)
1843 pRCAP
->pRequestMajorOpSet
=
1844 RecordCreateSet(si
[REQ
].intervals
, si
[REQ
].nintervals
,
1845 (RecordSetPtr
)((char *)pRCAP
+ si
[REQ
].offset
), si
[REQ
].size
);
1847 else pRCAP
->pRequestMajorOpSet
= NULL
;
1849 if (si
[REP
].intervals
)
1851 pRCAP
->pReplyMajorOpSet
=
1852 RecordCreateSet(si
[REP
].intervals
, si
[REP
].nintervals
,
1853 (RecordSetPtr
)((char *)pRCAP
+ si
[REP
].offset
), si
[REP
].size
);
1855 else pRCAP
->pReplyMajorOpSet
= NULL
;
1857 if (si
[ERR
].intervals
)
1860 RecordCreateSet(si
[ERR
].intervals
, si
[ERR
].nintervals
,
1861 (RecordSetPtr
)((char *)pRCAP
+ si
[ERR
].offset
), si
[ERR
].size
);
1863 else pRCAP
->pErrorSet
= NULL
;
1865 if (si
[DEV
].intervals
)
1867 pRCAP
->pDeviceEventSet
=
1868 RecordCreateSet(si
[DEV
].intervals
, si
[DEV
].nintervals
,
1869 (RecordSetPtr
)((char *)pRCAP
+ si
[DEV
].offset
), si
[DEV
].size
);
1871 else pRCAP
->pDeviceEventSet
= NULL
;
1873 if (si
[DLEV
].intervals
)
1875 pRCAP
->pDeliveredEventSet
=
1876 RecordCreateSet(si
[DLEV
].intervals
, si
[DLEV
].nintervals
,
1877 (RecordSetPtr
)((char *)pRCAP
+ si
[DLEV
].offset
), si
[DLEV
].size
);
1879 else pRCAP
->pDeliveredEventSet
= NULL
;
1883 pRCAP
->pRequestMinOpInfo
= (RecordMinorOpPtr
)
1884 ((char *)pRCAP
+ extReqSetsOffset
);
1885 pRCAP
->pRequestMinOpInfo
[0].count
= nExtReqSets
;
1886 for (i
= 0; i
< nExtReqSets
; i
++, pExtReqSets
++)
1888 pRCAP
->pRequestMinOpInfo
[i
+1].major
.first
= pExtReqSets
->first
;
1889 pRCAP
->pRequestMinOpInfo
[i
+1].major
.last
= pExtReqSets
->last
;
1890 pRCAP
->pRequestMinOpInfo
[i
+1].major
.pMinOpSet
=
1891 RecordCreateSet(pExtReqSets
->intervals
,
1892 pExtReqSets
->nintervals
,
1893 (RecordSetPtr
)((char *)pRCAP
+ pExtReqSets
->offset
),
1897 else pRCAP
->pRequestMinOpInfo
= NULL
;
1901 pRCAP
->pReplyMinOpInfo
= (RecordMinorOpPtr
)
1902 ((char *)pRCAP
+ extRepSetsOffset
);
1903 pRCAP
->pReplyMinOpInfo
[0].count
= nExtRepSets
;
1904 for (i
= 0; i
< nExtRepSets
; i
++, pExtRepSets
++)
1906 pRCAP
->pReplyMinOpInfo
[i
+1].major
.first
= pExtRepSets
->first
;
1907 pRCAP
->pReplyMinOpInfo
[i
+1].major
.last
= pExtRepSets
->last
;
1908 pRCAP
->pReplyMinOpInfo
[i
+1].major
.pMinOpSet
=
1909 RecordCreateSet(pExtRepSets
->intervals
,
1910 pExtRepSets
->nintervals
,
1911 (RecordSetPtr
)((char *)pRCAP
+ pExtRepSets
->offset
),
1915 else pRCAP
->pReplyMinOpInfo
= NULL
;
1917 pRCAP
->clientStarted
= clientStarted
;
1918 pRCAP
->clientDied
= clientDied
;
1920 /* link the RCAP onto the context */
1922 pRCAP
->pNextRCAP
= pContext
->pListOfRCAP
;
1923 pContext
->pListOfRCAP
= pRCAP
;
1925 if (pContext
->pRecordingClient
) /* context enabled */
1926 RecordInstallHooks(pRCAP
, 0);
1931 for (i
= 0; i
< maxSets
; i
++)
1932 if (si
[i
].intervals
)
1933 xfree(si
[i
].intervals
);
1934 DEALLOCATE_LOCAL(si
);
1936 if (pCanonClients
&& pCanonClients
!= (XID
*)&stuff
[1])
1937 xfree(pCanonClients
);
1939 } /* RecordRegisterClients */
1942 /* Proc functions all take a client argument, execute the request in
1943 * client->requestBuffer, and return a protocol error status.
1947 ProcRecordQueryVersion(ClientPtr client
)
1949 /* REQUEST(xRecordQueryVersionReq); */
1950 xRecordQueryVersionReply rep
;
1953 REQUEST_SIZE_MATCH(xRecordQueryVersionReq
);
1955 rep
.sequenceNumber
= client
->sequence
;
1957 rep
.majorVersion
= RECORD_MAJOR_VERSION
;
1958 rep
.minorVersion
= RECORD_MINOR_VERSION
;
1961 swaps(&rep
.sequenceNumber
, n
);
1962 swaps(&rep
.majorVersion
, n
);
1963 swaps(&rep
.minorVersion
, n
);
1965 (void)WriteToClient(client
, sizeof(xRecordQueryVersionReply
),
1967 return (client
->noClientException
);
1968 } /* ProcRecordQueryVersion */
1972 ProcRecordCreateContext(ClientPtr client
)
1974 REQUEST(xRecordCreateContextReq
);
1975 RecordContextPtr pContext
;
1976 RecordContextPtr
*ppNewAllContexts
= NULL
;
1979 REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq
);
1980 LEGAL_NEW_RESOURCE(stuff
->context
, client
);
1982 pContext
= (RecordContextPtr
)xalloc(sizeof(RecordContextRec
));
1986 /* make sure there is room in ppAllContexts to store the new context */
1988 ppNewAllContexts
= (RecordContextPtr
*)
1989 xrealloc(ppAllContexts
, sizeof(RecordContextPtr
) * (numContexts
+ 1));
1990 if (!ppNewAllContexts
)
1992 ppAllContexts
= ppNewAllContexts
;
1994 pContext
->id
= stuff
->context
;
1995 pContext
->pRecordingClient
= NULL
;
1996 pContext
->pListOfRCAP
= NULL
;
1997 pContext
->elemHeaders
= 0;
1998 pContext
->bufCategory
= 0;
1999 pContext
->numBufBytes
= 0;
2000 pContext
->pBufClient
= NULL
;
2001 pContext
->continuedReply
= 0;
2003 err
= RecordRegisterClients(pContext
, client
,
2004 (xRecordRegisterClientsReq
*)stuff
);
2008 if (AddResource(pContext
->id
, RTContext
, pContext
))
2010 ppAllContexts
[numContexts
++] = pContext
;
2015 RecordDeleteContext((pointer
)pContext
, pContext
->id
);
2022 } /* ProcRecordCreateContext */
2026 ProcRecordRegisterClients(ClientPtr client
)
2028 RecordContextPtr pContext
;
2029 REQUEST(xRecordRegisterClientsReq
);
2031 REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq
);
2032 VERIFY_CONTEXT(pContext
, stuff
->context
, client
);
2034 return RecordRegisterClients(pContext
, client
, stuff
);
2035 } /* ProcRecordRegisterClients */
2039 ProcRecordUnregisterClients(ClientPtr client
)
2041 RecordContextPtr pContext
;
2043 REQUEST(xRecordUnregisterClientsReq
);
2048 REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq
);
2049 if ((client
->req_len
<< 2) - SIZEOF(xRecordUnregisterClientsReq
) !=
2050 4 * stuff
->nClients
)
2052 VERIFY_CONTEXT(pContext
, stuff
->context
, client
);
2053 err
= RecordSanityCheckClientSpecifiers((XID
*)&stuff
[1],
2054 stuff
->nClients
, 0);
2058 nClients
= stuff
->nClients
;
2059 pCanonClients
= RecordCanonicalizeClientSpecifiers((XID
*)&stuff
[1],
2064 for (i
= 0; i
< nClients
; i
++)
2066 RecordDeleteClientFromContext(pContext
, pCanonClients
[i
]);
2068 if (pCanonClients
!= (XID
*)&stuff
[1])
2069 xfree(pCanonClients
);
2071 } /* ProcRecordUnregisterClients */
2074 /****************************************************************************/
2076 /* stuff for GetContext */
2078 /* This is a tactical structure used to hold the xRecordRanges as they are
2079 * being reconstituted from the sets in the RCAPs.
2083 xRecordRange
*pRanges
; /* array of xRecordRanges for one RCAP */
2084 int size
; /* number of elements in pRanges, >= nRanges */
2085 int nRanges
; /* number of occupied element of pRanges */
2086 } GetContextRangeInfoRec
, *GetContextRangeInfoPtr
;
2089 /* RecordAllocRanges
2092 * pri is a pointer to a GetContextRangeInfoRec to allocate for.
2093 * nRanges is the number of xRecordRanges desired for pri.
2095 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2098 * If Success is returned, pri->pRanges points to at least nRanges
2099 * ranges. pri->nRanges is set to nRanges. pri->size is the actual
2100 * number of ranges. Newly allocated ranges are zeroed.
2103 RecordAllocRanges(GetContextRangeInfoPtr pri
, int nRanges
)
2106 xRecordRange
*pNewRange
;
2109 newsize
= max(pri
->size
+ SZINCR
, nRanges
);
2110 pNewRange
= (xRecordRange
*)xrealloc(pri
->pRanges
,
2111 newsize
* sizeof(xRecordRange
));
2115 pri
->pRanges
= pNewRange
;
2116 pri
->size
= newsize
;
2117 bzero(&pri
->pRanges
[pri
->size
- SZINCR
], SZINCR
* sizeof(xRecordRange
));
2118 if (pri
->nRanges
< nRanges
)
2119 pri
->nRanges
= nRanges
;
2121 } /* RecordAllocRanges */
2124 /* RecordConvertSetToRanges
2127 * pSet is the set to be converted.
2128 * pri is where the result should be stored.
2129 * byteoffset is the offset from the start of an xRecordRange of the
2130 * two vales (first, last) we are interested in.
2131 * card8 is TRUE if the vales are one byte each and FALSE if two bytes
2133 * imax is the largest set value to store in pri->pRanges.
2134 * pStartIndex, if non-NULL, is the index of the first range in
2135 * pri->pRanges that should be stored to. If NULL,
2138 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2141 * If Success is returned, the slice of pri->pRanges indicated by
2142 * byteoffset and card8 is filled in with the intervals from pSet.
2143 * if pStartIndex was non-NULL, *pStartIndex is filled in with one
2144 * more than the index of the last xRecordRange that was touched.
2147 RecordConvertSetToRanges(
2149 GetContextRangeInfoPtr pri
,
2157 RecordSetIteratePtr pIter
= NULL
;
2158 RecordSetInterval interval
;
2166 nRanges
= pStartIndex
? *pStartIndex
: 0;
2167 while ((pIter
= RecordIterateSet(pSet
, pIter
, &interval
)))
2169 if (interval
.first
> imax
) break;
2170 if (interval
.last
> imax
) interval
.last
= imax
;
2172 if (nRanges
> pri
->size
)
2174 err
= RecordAllocRanges(pri
, nRanges
);
2179 pri
->nRanges
= max(pri
->nRanges
, nRanges
);
2182 pCARD8
= ((CARD8
*)&pri
->pRanges
[nRanges
-1]) + byteoffset
;
2183 *pCARD8
++ = interval
.first
;
2184 *pCARD8
= interval
.last
;
2188 pCARD16
= (CARD16
*)
2189 (((char *)&pri
->pRanges
[nRanges
-1]) + byteoffset
);
2190 *pCARD16
++ = interval
.first
;
2191 *pCARD16
= interval
.last
;
2195 *pStartIndex
= nRanges
;
2197 } /* RecordConvertSetToRanges */
2200 /* RecordConvertMinorOpInfoToRanges
2203 * pMinOpInfo is the minor opcode info to convert to xRecordRanges.
2204 * pri is where the result should be stored.
2205 * byteoffset is the offset from the start of an xRecordRange of the
2206 * four vales (CARD8 major_first, CARD8 major_last,
2207 * CARD16 minor_first, CARD16 minor_last) we are going to store.
2209 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2212 * If Success is returned, the slice of pri->pRanges indicated by
2213 * byteoffset is filled in with the information from pMinOpInfo.
2216 RecordConvertMinorOpInfoToRanges(
2217 RecordMinorOpPtr pMinOpInfo
,
2218 GetContextRangeInfoPtr pri
,
2230 nsets
= pMinOpInfo
->count
;
2233 for (i
= 0; i
< nsets
; i
++)
2237 err
= RecordConvertSetToRanges(pMinOpInfo
[i
].major
.pMinOpSet
, pri
,
2238 byteoffset
+ 2, FALSE
, 65535, &start
);
2239 if (err
!= Success
) return err
;
2240 for (j
= s
; j
< start
; j
++)
2242 CARD8
*pCARD8
= ((CARD8
*)&pri
->pRanges
[j
]) + byteoffset
;
2243 *pCARD8
++ = pMinOpInfo
[i
].major
.first
;
2244 *pCARD8
= pMinOpInfo
[i
].major
.last
;
2248 } /* RecordConvertMinorOpInfoToRanges */
2254 * pRanges is an array of xRecordRanges.
2255 * nRanges is the number of elements in pRanges.
2260 * The 16 bit fields of each xRecordRange are byte swapped.
2263 RecordSwapRanges(xRecordRange
*pRanges
, int nRanges
)
2267 for (i
= 0; i
< nRanges
; i
++, pRanges
++)
2269 swaps(&pRanges
->extRequestsMinorFirst
, n
);
2270 swaps(&pRanges
->extRequestsMinorLast
, n
);
2271 swaps(&pRanges
->extRepliesMinorFirst
, n
);
2272 swaps(&pRanges
->extRepliesMinorLast
, n
);
2274 } /* RecordSwapRanges */
2278 ProcRecordGetContext(ClientPtr client
)
2280 RecordContextPtr pContext
;
2281 REQUEST(xRecordGetContextReq
);
2282 xRecordGetContextReply rep
;
2284 RecordClientsAndProtocolPtr pRCAP
;
2286 GetContextRangeInfoPtr pRangeInfo
;
2287 GetContextRangeInfoPtr pri
;
2291 REQUEST_SIZE_MATCH(xRecordGetContextReq
);
2292 VERIFY_CONTEXT(pContext
, stuff
->context
, client
);
2294 /* how many RCAPs are there on this context? */
2296 for (pRCAP
= pContext
->pListOfRCAP
; pRCAP
; pRCAP
= pRCAP
->pNextRCAP
)
2299 /* allocate and initialize space for record range info */
2301 pRangeInfo
= (GetContextRangeInfoPtr
)ALLOCATE_LOCAL(
2302 nRCAPs
* sizeof(GetContextRangeInfoRec
));
2303 if (!pRangeInfo
&& nRCAPs
> 0)
2305 for (i
= 0; i
< nRCAPs
; i
++)
2307 pRangeInfo
[i
].pRanges
= NULL
;
2308 pRangeInfo
[i
].size
= 0;
2309 pRangeInfo
[i
].nRanges
= 0;
2312 /* convert the RCAP (internal) representation of the recorded protocol
2313 * to the wire protocol (external) representation, storing the information
2314 * for the ith RCAP in pri[i]
2317 for (pRCAP
= pContext
->pListOfRCAP
, pri
= pRangeInfo
;
2319 pRCAP
= pRCAP
->pNextRCAP
, pri
++)
2323 err
= RecordConvertSetToRanges(pRCAP
->pRequestMajorOpSet
, pri
,
2324 offset_of(rr
, coreRequestsFirst
), TRUE
, 127, NULL
);
2325 if (err
!= Success
) goto bailout
;
2327 err
= RecordConvertSetToRanges(pRCAP
->pReplyMajorOpSet
, pri
,
2328 offset_of(rr
, coreRepliesFirst
), TRUE
, 127, NULL
);
2329 if (err
!= Success
) goto bailout
;
2331 err
= RecordConvertSetToRanges(pRCAP
->pDeliveredEventSet
, pri
,
2332 offset_of(rr
, deliveredEventsFirst
), TRUE
, 255, NULL
);
2333 if (err
!= Success
) goto bailout
;
2335 err
= RecordConvertSetToRanges(pRCAP
->pDeviceEventSet
, pri
,
2336 offset_of(rr
, deviceEventsFirst
), TRUE
, 255, NULL
);
2337 if (err
!= Success
) goto bailout
;
2339 err
= RecordConvertSetToRanges(pRCAP
->pErrorSet
, pri
,
2340 offset_of(rr
, errorsFirst
), TRUE
, 255, NULL
);
2341 if (err
!= Success
) goto bailout
;
2343 err
= RecordConvertMinorOpInfoToRanges(pRCAP
->pRequestMinOpInfo
,
2344 pri
, offset_of(rr
, extRequestsMajorFirst
));
2345 if (err
!= Success
) goto bailout
;
2347 err
= RecordConvertMinorOpInfoToRanges(pRCAP
->pReplyMinOpInfo
,
2348 pri
, offset_of(rr
, extRepliesMajorFirst
));
2349 if (err
!= Success
) goto bailout
;
2351 if (pRCAP
->clientStarted
|| pRCAP
->clientDied
)
2353 if (pri
->nRanges
== 0)
2354 RecordAllocRanges(pri
, 1);
2355 pri
->pRanges
[0].clientStarted
= pRCAP
->clientStarted
;
2356 pri
->pRanges
[0].clientDied
= pRCAP
->clientDied
;
2360 /* calculate number of clients and reply length */
2364 for (pRCAP
= pContext
->pListOfRCAP
, pri
= pRangeInfo
;
2366 pRCAP
= pRCAP
->pNextRCAP
, pri
++)
2368 rep
.nClients
+= pRCAP
->numClients
;
2369 rep
.length
+= pRCAP
->numClients
*
2370 ( (sizeof(xRecordClientInfo
) >> 2) +
2371 pri
->nRanges
* (sizeof(xRecordRange
) >> 2));
2374 /* write the reply header */
2377 rep
.sequenceNumber
= client
->sequence
;
2378 rep
.enabled
= pContext
->pRecordingClient
!= NULL
;
2379 rep
.elementHeader
= pContext
->elemHeaders
;
2382 swaps(&rep
.sequenceNumber
, n
);
2383 swapl(&rep
.length
, n
);
2384 swapl(&rep
.nClients
, n
);
2386 (void)WriteToClient(client
, sizeof(xRecordGetContextReply
),
2389 /* write all the CLIENT_INFOs */
2391 for (pRCAP
= pContext
->pListOfRCAP
, pri
= pRangeInfo
;
2393 pRCAP
= pRCAP
->pNextRCAP
, pri
++)
2395 xRecordClientInfo rci
;
2396 rci
.nRanges
= pri
->nRanges
;
2397 if (client
->swapped
)
2399 swapl(&rci
.nRanges
, n
);
2400 RecordSwapRanges(pri
->pRanges
, pri
->nRanges
);
2402 for (i
= 0; i
< pRCAP
->numClients
; i
++)
2404 rci
.clientResource
= pRCAP
->pClientIDs
[i
];
2405 if (client
->swapped
) swapl(&rci
.clientResource
, n
);
2406 WriteToClient(client
, sizeof(xRecordClientInfo
), (char *)&rci
);
2407 WriteToClient(client
, sizeof(xRecordRange
) * pri
->nRanges
,
2408 (char *)pri
->pRanges
);
2411 err
= client
->noClientException
;
2414 for (i
= 0; i
< nRCAPs
; i
++)
2416 if (pRangeInfo
[i
].pRanges
) xfree(pRangeInfo
[i
].pRanges
);
2418 DEALLOCATE_LOCAL(pRangeInfo
);
2420 } /* ProcRecordGetContext */
2424 ProcRecordEnableContext(ClientPtr client
)
2426 RecordContextPtr pContext
;
2427 REQUEST(xRecordEnableContextReq
);
2429 RecordClientsAndProtocolPtr pRCAP
;
2431 REQUEST_SIZE_MATCH(xRecordGetContextReq
);
2432 VERIFY_CONTEXT(pContext
, stuff
->context
, client
);
2433 if (pContext
->pRecordingClient
)
2434 return BadMatch
; /* already enabled */
2436 /* install record hooks for each RCAP */
2438 for (pRCAP
= pContext
->pListOfRCAP
; pRCAP
; pRCAP
= pRCAP
->pNextRCAP
)
2440 int err
= RecordInstallHooks(pRCAP
, 0);
2442 { /* undo the previous installs */
2443 RecordClientsAndProtocolPtr pUninstallRCAP
;
2444 for (pUninstallRCAP
= pContext
->pListOfRCAP
;
2445 pUninstallRCAP
!= pRCAP
;
2446 pUninstallRCAP
= pUninstallRCAP
->pNextRCAP
)
2448 RecordUninstallHooks(pUninstallRCAP
, 0);
2454 /* Disallow further request processing on this connection until
2455 * the context is disabled.
2457 IgnoreClient(client
);
2458 pContext
->pRecordingClient
= client
;
2460 /* Don't allow the data connection to record itself; unregister it. */
2461 RecordDeleteClientFromContext(pContext
,
2462 pContext
->pRecordingClient
->clientAsMask
);
2464 /* move the newly enabled context to the front part of ppAllContexts,
2465 * where all the enabled contexts are
2467 i
= RecordFindContextOnAllContexts(pContext
);
2468 assert(i
>= numEnabledContexts
);
2469 if (i
!= numEnabledContexts
)
2471 ppAllContexts
[i
] = ppAllContexts
[numEnabledContexts
];
2472 ppAllContexts
[numEnabledContexts
] = pContext
;
2475 ++numEnabledContexts
;
2476 assert(numEnabledContexts
> 0);
2478 /* send StartOfData */
2479 RecordAProtocolElement(pContext
, NULL
, XRecordStartOfData
, NULL
, 0, 0);
2480 RecordFlushReplyBuffer(pContext
, NULL
, 0, NULL
, 0);
2482 } /* ProcRecordEnableContext */
2485 /* RecordDisableContext
2488 * pContext is the context to disable.
2489 * nRanges is the number of elements in pRanges.
2494 * If the context was enabled, it is disabled. An EndOfData
2495 * message is sent to the recording client. Recording hooks for
2496 * this context are uninstalled. The context is moved to the
2497 * rear part of the ppAllContexts array. numEnabledContexts is
2498 * decremented. Request processing for the formerly recording client
2502 RecordDisableContext(RecordContextPtr pContext
)
2504 RecordClientsAndProtocolPtr pRCAP
;
2507 if (!pContext
->pRecordingClient
) return;
2508 if (!pContext
->pRecordingClient
->clientGone
)
2510 RecordAProtocolElement(pContext
, NULL
, XRecordEndOfData
, NULL
, 0, 0);
2511 RecordFlushReplyBuffer(pContext
, NULL
, 0, NULL
, 0);
2512 /* Re-enable request processing on this connection. */
2513 AttendClient(pContext
->pRecordingClient
);
2516 for (pRCAP
= pContext
->pListOfRCAP
; pRCAP
; pRCAP
= pRCAP
->pNextRCAP
)
2518 RecordUninstallHooks(pRCAP
, 0);
2521 pContext
->pRecordingClient
= NULL
;
2523 /* move the newly disabled context to the rear part of ppAllContexts,
2524 * where all the disabled contexts are
2526 i
= RecordFindContextOnAllContexts(pContext
);
2527 assert( (i
!= -1) && (i
< numEnabledContexts
) );
2528 if (i
!= (numEnabledContexts
- 1) )
2530 ppAllContexts
[i
] = ppAllContexts
[numEnabledContexts
-1];
2531 ppAllContexts
[numEnabledContexts
-1] = pContext
;
2533 --numEnabledContexts
;
2534 assert(numEnabledContexts
>= 0);
2535 } /* RecordDisableContext */
2539 ProcRecordDisableContext(ClientPtr client
)
2541 RecordContextPtr pContext
;
2542 REQUEST(xRecordDisableContextReq
);
2544 REQUEST_SIZE_MATCH(xRecordDisableContextReq
);
2545 VERIFY_CONTEXT(pContext
, stuff
->context
, client
);
2546 RecordDisableContext(pContext
);
2548 } /* ProcRecordDisableContext */
2551 /* RecordDeleteContext
2554 * value is the context to delete.
2555 * id is its resource ID.
2560 * Disables the context, frees all associated memory, and removes
2561 * it from the ppAllContexts array.
2564 RecordDeleteContext(pointer value
, XID id
)
2567 RecordContextPtr pContext
= (RecordContextPtr
)value
;
2568 RecordClientsAndProtocolPtr pRCAP
;
2570 RecordDisableContext(pContext
);
2572 /* Remove all the clients from all the RCAPs.
2573 * As a result, the RCAPs will be freed.
2576 while ((pRCAP
= pContext
->pListOfRCAP
))
2578 int numClients
= pRCAP
->numClients
;
2579 /* when the last client is deleted, the RCAP will go away. */
2582 RecordDeleteClientFromRCAP(pRCAP
, numClients
);
2588 /* remove context from AllContexts list */
2590 if (-1 != (i
= RecordFindContextOnAllContexts(pContext
)))
2592 ppAllContexts
[i
] = ppAllContexts
[numContexts
- 1];
2593 if (--numContexts
== 0)
2595 xfree(ppAllContexts
);
2596 ppAllContexts
= NULL
;
2600 } /* RecordDeleteContext */
2604 ProcRecordFreeContext(ClientPtr client
)
2606 RecordContextPtr pContext
;
2607 REQUEST(xRecordFreeContextReq
);
2609 REQUEST_SIZE_MATCH(xRecordFreeContextReq
);
2610 VERIFY_CONTEXT(pContext
, stuff
->context
, client
);
2611 FreeResource(stuff
->context
, RT_NONE
);
2613 } /* ProcRecordFreeContext */
2617 ProcRecordDispatch(ClientPtr client
)
2621 switch (stuff
->data
)
2623 case X_RecordQueryVersion
:
2624 return ProcRecordQueryVersion(client
);
2625 case X_RecordCreateContext
:
2626 return ProcRecordCreateContext(client
);
2627 case X_RecordRegisterClients
:
2628 return ProcRecordRegisterClients(client
);
2629 case X_RecordUnregisterClients
:
2630 return ProcRecordUnregisterClients(client
);
2631 case X_RecordGetContext
:
2632 return ProcRecordGetContext(client
);
2633 case X_RecordEnableContext
:
2634 return ProcRecordEnableContext(client
);
2635 case X_RecordDisableContext
:
2636 return ProcRecordDisableContext(client
);
2637 case X_RecordFreeContext
:
2638 return ProcRecordFreeContext(client
);
2642 } /* ProcRecordDispatch */
2646 SProcRecordQueryVersion(ClientPtr client
)
2648 REQUEST(xRecordQueryVersionReq
);
2651 swaps(&stuff
->length
, n
);
2652 REQUEST_SIZE_MATCH(xRecordQueryVersionReq
);
2653 swaps(&stuff
->majorVersion
, n
);
2654 swaps(&stuff
->minorVersion
,n
);
2655 return ProcRecordQueryVersion(client
);
2656 } /* SProcRecordQueryVersion */
2660 SwapCreateRegister(xRecordRegisterClientsReq
*stuff
)
2666 swapl(&stuff
->context
, n
);
2667 swapl(&stuff
->nClients
, n
);
2668 swapl(&stuff
->nRanges
, n
);
2669 pClientID
= (XID
*)&stuff
[1];
2670 for (i
= 0; i
< stuff
->nClients
; i
++, pClientID
++)
2672 swapl(pClientID
, n
);
2674 RecordSwapRanges((xRecordRange
*)pClientID
, stuff
->nRanges
);
2675 } /* SwapCreateRegister */
2679 SProcRecordCreateContext(ClientPtr client
)
2681 REQUEST(xRecordCreateContextReq
);
2684 swaps(&stuff
->length
, n
);
2685 REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq
);
2686 SwapCreateRegister((pointer
)stuff
);
2687 return ProcRecordCreateContext(client
);
2688 } /* SProcRecordCreateContext */
2692 SProcRecordRegisterClients(ClientPtr client
)
2694 REQUEST(xRecordRegisterClientsReq
);
2697 swaps(&stuff
->length
, n
);
2698 REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq
);
2699 SwapCreateRegister((pointer
)stuff
);
2700 return ProcRecordRegisterClients(client
);
2701 } /* SProcRecordRegisterClients */
2705 SProcRecordUnregisterClients(ClientPtr client
)
2707 REQUEST(xRecordUnregisterClientsReq
);
2710 swaps(&stuff
->length
, n
);
2711 REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq
);
2712 swapl(&stuff
->context
, n
);
2713 swapl(&stuff
->nClients
, n
);
2715 return ProcRecordUnregisterClients(client
);
2716 } /* SProcRecordUnregisterClients */
2720 SProcRecordGetContext(ClientPtr client
)
2722 REQUEST(xRecordGetContextReq
);
2725 swaps(&stuff
->length
, n
);
2726 REQUEST_SIZE_MATCH(xRecordGetContextReq
);
2727 swapl(&stuff
->context
, n
);
2728 return ProcRecordGetContext(client
);
2729 } /* SProcRecordGetContext */
2732 SProcRecordEnableContext(ClientPtr client
)
2734 REQUEST(xRecordEnableContextReq
);
2737 swaps(&stuff
->length
, n
);
2738 REQUEST_SIZE_MATCH(xRecordEnableContextReq
);
2739 swapl(&stuff
->context
, n
);
2740 return ProcRecordEnableContext(client
);
2741 } /* SProcRecordEnableContext */
2745 SProcRecordDisableContext(ClientPtr client
)
2747 REQUEST(xRecordDisableContextReq
);
2750 swaps(&stuff
->length
, n
);
2751 REQUEST_SIZE_MATCH(xRecordDisableContextReq
);
2752 swapl(&stuff
->context
, n
);
2753 return ProcRecordDisableContext(client
);
2754 } /* SProcRecordDisableContext */
2758 SProcRecordFreeContext(ClientPtr client
)
2760 REQUEST(xRecordFreeContextReq
);
2763 swaps(&stuff
->length
, n
);
2764 REQUEST_SIZE_MATCH(xRecordFreeContextReq
);
2765 swapl(&stuff
->context
, n
);
2766 return ProcRecordFreeContext(client
);
2767 } /* SProcRecordFreeContext */
2771 SProcRecordDispatch(ClientPtr client
)
2775 switch (stuff
->data
)
2777 case X_RecordQueryVersion
:
2778 return SProcRecordQueryVersion(client
);
2779 case X_RecordCreateContext
:
2780 return SProcRecordCreateContext(client
);
2781 case X_RecordRegisterClients
:
2782 return SProcRecordRegisterClients(client
);
2783 case X_RecordUnregisterClients
:
2784 return SProcRecordUnregisterClients(client
);
2785 case X_RecordGetContext
:
2786 return SProcRecordGetContext(client
);
2787 case X_RecordEnableContext
:
2788 return SProcRecordEnableContext(client
);
2789 case X_RecordDisableContext
:
2790 return SProcRecordDisableContext(client
);
2791 case X_RecordFreeContext
:
2792 return SProcRecordFreeContext(client
);
2796 } /* SProcRecordDispatch */
2798 /* RecordConnectionSetupInfo
2801 * pContext is an enabled context that specifies recording of
2802 * connection setup info.
2803 * pci holds the connection setup info.
2808 * The connection setup info is sent to the recording client.
2811 RecordConnectionSetupInfo(RecordContextPtr pContext
, NewClientInfoRec
*pci
)
2813 int prefixsize
= SIZEOF(xConnSetupPrefix
);
2814 int restsize
= pci
->prefix
->length
* 4;
2816 if (pci
->client
->swapped
)
2818 char *pConnSetup
= (char *)ALLOCATE_LOCAL(prefixsize
+ restsize
);
2821 SwapConnSetupPrefix(pci
->prefix
, pConnSetup
);
2822 SwapConnSetupInfo(pci
->setup
, pConnSetup
+ prefixsize
);
2823 RecordAProtocolElement(pContext
, pci
->client
, XRecordClientStarted
,
2824 (pointer
)pConnSetup
, prefixsize
+ restsize
, 0);
2825 DEALLOCATE_LOCAL(pConnSetup
);
2829 /* don't alloc and copy as in the swapped case; just send the
2830 * data in two pieces
2832 RecordAProtocolElement(pContext
, pci
->client
, XRecordClientStarted
,
2833 (pointer
)pci
->prefix
, prefixsize
, restsize
);
2834 RecordAProtocolElement(pContext
, pci
->client
, XRecordClientStarted
,
2835 (pointer
)pci
->setup
, restsize
, /* continuation */ -1);
2837 } /* RecordConnectionSetupInfo */
2840 /* RecordDeleteContext
2843 * pcbl is &ClientStateCallback.
2845 * calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
2846 * which contains information about client state changes.
2851 * If a new client has connected and any contexts have specified
2852 * XRecordFutureClients, the new client is registered on those contexts.
2853 * If any of those contexts specify recording of the connection setup
2854 * info, it is recorded.
2856 * If an existing client has disconnected, it is deleted from any
2857 * contexts that it was registered on. If any of those contexts
2858 * specified XRecordClientDied, they record a ClientDied protocol element.
2859 * If the disconnectiong client happened to be the data connection of an
2860 * enabled context, the context is disabled.
2864 RecordAClientStateChange(CallbackListPtr
*pcbl
, pointer nulldata
, pointer calldata
)
2866 NewClientInfoRec
*pci
= (NewClientInfoRec
*)calldata
;
2868 ClientPtr pClient
= pci
->client
;
2870 switch (pClient
->clientState
)
2872 case ClientStateRunning
: /* new client */
2873 for (i
= 0; i
< numContexts
; i
++)
2875 RecordClientsAndProtocolPtr pRCAP
;
2876 RecordContextPtr pContext
= ppAllContexts
[i
];
2878 if ((pRCAP
= RecordFindClientOnContext(pContext
,
2879 XRecordFutureClients
, NULL
)))
2881 RecordAddClientToRCAP(pRCAP
, pClient
->clientAsMask
);
2882 if (pContext
->pRecordingClient
&& pRCAP
->clientStarted
)
2883 RecordConnectionSetupInfo(pContext
, pci
);
2888 case ClientStateGone
:
2889 case ClientStateRetained
: /* client disconnected */
2890 for (i
= 0; i
< numContexts
; i
++)
2892 RecordClientsAndProtocolPtr pRCAP
;
2893 RecordContextPtr pContext
= ppAllContexts
[i
];
2896 if (pContext
->pRecordingClient
== pClient
)
2897 RecordDisableContext(pContext
);
2898 if ((pRCAP
= RecordFindClientOnContext(pContext
,
2899 pClient
->clientAsMask
, &pos
)))
2901 if (pContext
->pRecordingClient
&& pRCAP
->clientDied
)
2902 RecordAProtocolElement(pContext
, pClient
,
2903 XRecordClientDied
, NULL
, 0, 0);
2904 RecordDeleteClientFromRCAP(pRCAP
, pos
);
2911 } /* end switch on client state */
2912 } /* RecordAClientStateChange */
2918 * extEntry is the extension information for RECORD.
2923 * Performs any cleanup needed by RECORD at server shutdown time.
2927 RecordCloseDown(ExtensionEntry
*extEntry
)
2929 DeleteCallback(&ClientStateCallback
, RecordAClientStateChange
, NULL
);
2930 } /* RecordCloseDown */
2933 /* RecordExtensionInit
2940 * Enables the RECORD extension if possible.
2943 RecordExtensionInit(void)
2945 ExtensionEntry
*extentry
;
2947 RTContext
= CreateNewResourceType(RecordDeleteContext
);
2951 RecordClientPrivateIndex
= AllocateClientPrivateIndex();
2952 if (!AllocateClientPrivate(RecordClientPrivateIndex
, 0))
2955 ppAllContexts
= NULL
;
2956 numContexts
= numEnabledContexts
= numEnabledRCAPs
= 0;
2958 if (!AddCallback(&ClientStateCallback
, RecordAClientStateChange
, NULL
))
2961 extentry
= AddExtension(RECORD_NAME
, RecordNumEvents
, RecordNumErrors
,
2962 ProcRecordDispatch
, SProcRecordDispatch
,
2963 RecordCloseDown
, StandardMinorOpcode
);
2966 DeleteCallback(&ClientStateCallback
, RecordAClientStateChange
, NULL
);
2969 RecordErrorBase
= extentry
->errorBase
;
2971 } /* RecordExtensionInit */