XRecord.c revision a253d6ae
1/*
2$Xorg: XRecord.c,v 1.4 2001/02/09 02:04:00 xorgcvs Exp $
3
4XRecord.c - client-side library for RECORD extension
5
6Copyright 1995, 1998  The Open Group
7
8Permission to use, copy, modify, distribute, and sell this software and its
9documentation for any purpose is hereby granted without fee, provided that
10the above copyright notice appear in all copies and that both that
11copyright notice and this permission notice appear in supporting
12documentation.
13
14The above copyright notice and this permission notice shall be
15included in all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
21OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of The Open Group shall
26not be used in advertising or otherwise to promote the sale, use or
27other dealings in this Software without prior written authorization
28from The Open Group.
29
30*/
31/***************************************************************************
32 * Copyright 1995 Network Computing Devices
33 *
34 * Permission to use, copy, modify, distribute, and sell this software and
35 * its documentation for any purpose is hereby granted without fee, provided
36 * that the above copyright notice appear in all copies and that both that
37 * copyright notice and this permission notice appear in supporting
38 * documentation, and that the name of Network Computing Devices
39 * not be used in advertising or publicity pertaining to distribution
40 * of the software without specific, written prior permission.
41 *
42 * NETWORK COMPUTING DEVICES DISCLAIMs ALL WARRANTIES WITH REGARD TO
43 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
44 * AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE
45 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
46 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
47 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
48 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49 **************************************************************************/
50/*
51 * By Stephen Gildea, X Consortium, and Martha Zimet, NCD.
52 */
53/* $XFree86: xc/lib/Xtst/XRecord.c,v 1.6 2002/10/16 00:37:33 dawes Exp $ */
54
55#include <stdio.h>
56#include <assert.h>
57#define NEED_EVENTS
58#define NEED_REPLIES
59#include <X11/Xlibint.h>
60#include <X11/extensions/Xext.h>
61#include <X11/extensions/extutil.h>
62#include <X11/extensions/recordstr.h>
63
64static XExtensionInfo _xrecord_info_data;
65static XExtensionInfo *xrecord_info = &_xrecord_info_data;
66static /* const */ char *xrecord_extension_name = RECORD_NAME;
67
68#define XRecordCheckExtension(dpy,i,val) \
69    XextCheckExtension(dpy, i, xrecord_extension_name, val)
70
71/**************************************************************************
72 *                                                                        *
73 *			   private utility routines                       *
74 *                                                                        *
75 **************************************************************************/
76
77static XExtDisplayInfo *find_display(Display *dpy);
78
79/*
80 * A reply buffer holds a reply from RecordEnableContext.
81 * Pieces of that buffer are passed to the XRecordEnableContext callback.
82 * ref_count is incremented each time we do that.
83 * ref_count is decremented each time XRecordFreeData is called on
84 * the buffer.  When ref_count is 0, we can free or reuse the buffer.
85 */
86struct reply_buffer
87{
88    struct reply_buffer *next;	/* next in list or NULL */
89    unsigned char *buf;		/* pointer to malloc'd buffer */
90    int nbytes;			/* size of buf */
91    int ref_count;		/* callback uses pending */
92};
93
94
95/*
96 * There's some extra information the implementation finds useful
97 * to attach to an XRecordInterceptData packet to handle memory
98 * management.  So we really allocate one of these.
99 */
100struct intercept_queue
101{
102    /* this struct gets passed to the user as an XRecordInterceptData,
103       so the data field must come first so we can cast the address
104       back and forth */
105    XRecordInterceptData data;
106    struct intercept_queue *next; /* next in free list or NULL */
107    struct mem_cache_str *cache; /* contains head of free list */
108};
109
110/*
111 * per-display pointers to cache of malloc'd but unused memory
112 */
113struct mem_cache_str
114{
115    struct intercept_queue *inter_data;	/* free structs only */
116    struct reply_buffer *reply_buffers;	/* all reply buffers */
117    int inter_data_count;	/* total allocated, free and in use */
118    Bool display_closed;	/* so we know when to free ourself */
119};
120
121static int close_display(
122    Display *dpy,
123    XExtCodes *codes)		/* not used */
124{
125    XExtDisplayInfo *info = find_display (dpy);
126
127    LockDisplay(dpy);
128    if (info && info->data) {
129	struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
130	struct intercept_queue *iq, *iq_next;
131	struct reply_buffer *rbp, **rbp_next_p;
132
133	for (iq=cache->inter_data; iq; iq=iq_next) {
134	    iq_next = iq->next;
135	    XFree(iq);
136	    cache->inter_data_count--;
137	}
138
139	/* this is a little trickier, because some of these
140	   might still be in use */
141	for (rbp_next_p = &cache->reply_buffers; *rbp_next_p; ) {
142	    rbp = *rbp_next_p;
143	    if (rbp->ref_count == 0) {
144		*rbp_next_p = rbp->next;
145		XFree(rbp->buf);
146		XFree(rbp);
147	    } else {
148		rbp_next_p = &rbp->next;
149	    }
150	}
151
152	if (cache->reply_buffers == NULL  &&  cache->inter_data_count == 0) {
153	    /* every thing has been freed, can free ourselves, too */
154	    XFree(cache);
155	} else {
156	    cache->display_closed = True;
157	    cache->inter_data = NULL; /* neatness only; won't be used */
158	}
159    }
160    UnlockDisplay(dpy);
161    return XextRemoveDisplay(xrecord_info, dpy);
162}
163
164static XPointer alloc_mem_cache(void)
165{
166    struct mem_cache_str *cache;
167
168    /* note that an error will go unnoticed */
169    cache = (struct mem_cache_str *) Xmalloc(sizeof(struct mem_cache_str));
170    if (cache) {
171	cache->display_closed = False;
172	cache->inter_data = NULL;
173	cache->inter_data_count = 0;
174	cache->reply_buffers = NULL;
175    }
176    return (XPointer) cache;
177}
178
179static const char *xrecord_error_list[] = {
180    "XRecordBadContext (Not a defined RECORD context)",
181};
182
183static XEXT_GENERATE_ERROR_STRING (error_string, xrecord_extension_name,
184                                   RecordNumErrors, xrecord_error_list)
185
186static XExtensionHooks xrecord_extension_hooks = {
187    NULL,                               /* create_gc */
188    NULL,                               /* copy_gc */
189    NULL,                               /* flush_gc */
190    NULL,                               /* free_gc */
191    NULL,                               /* create_font */
192    NULL,                               /* free_font */
193    close_display,                      /* close_display */
194    NULL,                      		/* wire_to_event */
195    NULL,                      		/* event_to_wire */
196    NULL,                               /* error */
197    error_string                        /* error_string */
198};
199
200static XEXT_GENERATE_FIND_DISPLAY (find_display, xrecord_info,
201	xrecord_extension_name, &xrecord_extension_hooks, RecordNumEvents,
202	alloc_mem_cache())
203
204/**************************************************************************
205 *                                                                        *
206 *			   private library routines                       *
207 *                                                                        *
208 **************************************************************************/
209
210static void
211SendRange(
212    Display 	*dpy,
213    XRecordRange **range_item,
214    int   	nranges)
215{
216    int 		rlen = SIZEOF(xRecordRange);
217    while(nranges--)
218    {
219       xRecordRange xrange;
220
221       xrange.coreRequestsFirst = (*range_item)->core_requests.first;
222       xrange.coreRequestsLast = (*range_item)->core_requests.last;
223       xrange.coreRepliesFirst = (*range_item)->core_replies.first;
224       xrange.coreRepliesLast = (*range_item)->core_replies.last;
225       xrange.extRequestsMajorFirst = (*range_item)->ext_requests.ext_major.first;
226       xrange.extRequestsMajorLast = (*range_item)->ext_requests.ext_major.last;
227       xrange.extRequestsMinorFirst = (*range_item)->ext_requests.ext_minor.first;
228       xrange.extRequestsMinorLast = (*range_item)->ext_requests.ext_minor.last;
229       xrange.extRepliesMajorFirst = (*range_item)->ext_replies.ext_major.first;
230       xrange.extRepliesMajorLast = (*range_item)->ext_replies.ext_major.last;
231       xrange.extRepliesMinorFirst = (*range_item)->ext_replies.ext_minor.first;
232       xrange.extRepliesMinorLast = (*range_item)->ext_replies.ext_minor.last;
233       xrange.deliveredEventsFirst = (*range_item)->delivered_events.first;
234       xrange.deliveredEventsLast = (*range_item)->delivered_events.last;
235       xrange.deviceEventsFirst = (*range_item)->device_events.first;
236       xrange.deviceEventsLast = (*range_item)->device_events.last;
237       xrange.errorsFirst = (*range_item)->errors.first;
238       xrange.errorsLast = (*range_item)->errors.last;
239       xrange.clientStarted = (*range_item)->client_started;
240       xrange.clientDied = (*range_item)->client_died;
241
242       Data(dpy, (char *)&xrange, rlen);
243       range_item++;
244    }
245}
246
247/**************************************************************************
248 *                                                                        *
249 *		    public routines               			  *
250 *                                                                        *
251 **************************************************************************/
252
253XID
254XRecordIdBaseMask(dpy)
255    Display *dpy;
256{
257    return 0x1fffffff & ~dpy->resource_mask;
258}
259
260Status
261XRecordQueryVersion (dpy, cmajor_return, cminor_return)
262    Display 	*dpy;
263    int 	*cmajor_return, *cminor_return;
264{
265    XExtDisplayInfo *info = find_display (dpy);
266    register xRecordQueryVersionReq   	*req;
267    xRecordQueryVersionReply 		rep;
268
269    XRecordCheckExtension (dpy, info, False);
270
271    LockDisplay(dpy);
272    GetReq(RecordQueryVersion, req);
273    req->reqType = info->codes->major_opcode;
274    req->recordReqType = X_RecordQueryVersion;
275    req->majorVersion = RECORD_MAJOR_VERSION;
276    req->minorVersion = RECORD_MINOR_VERSION;
277    if (!_XReply(dpy,(xReply *)&rep, 0, True)) {
278	UnlockDisplay(dpy);
279	SyncHandle();
280	return False;
281    }
282    UnlockDisplay(dpy);
283    SyncHandle();
284    *cmajor_return = rep.majorVersion;
285    *cminor_return = rep.minorVersion;
286    return ((rep.majorVersion == RECORD_MAJOR_VERSION) &&
287	    (rep.minorVersion >= RECORD_LOWEST_MINOR_VERSION));
288}
289
290XRecordContext
291XRecordCreateContext(dpy, datum_flags, clients, nclients, ranges, nranges)
292    Display 		*dpy;
293    int			datum_flags;
294    XRecordClientSpec   *clients;
295    int 	        nclients;
296    XRecordRange  	**ranges;
297    int			nranges;
298{
299    XExtDisplayInfo 	*info = find_display (dpy);
300    register xRecordCreateContextReq 	*req;
301    int			clen = 4 * nclients;
302
303    XRecordCheckExtension (dpy, info, 0);
304    LockDisplay(dpy);
305    GetReq(RecordCreateContext, req);
306
307    req->reqType = info->codes->major_opcode;
308    req->recordReqType = X_RecordCreateContext;
309    req->context = XAllocID(dpy);
310    req->length += (nclients * 4 +
311		    nranges * SIZEOF(xRecordRange)) >> 2;
312    req->elementHeader = datum_flags;
313    req->nClients = nclients;
314    req->nRanges = nranges;
315
316    Data32(dpy, (long *)clients, clen);
317    SendRange(dpy, ranges, nranges);
318
319    UnlockDisplay(dpy);
320    SyncHandle();
321    return req->context;
322}
323
324XRecordRange *
325XRecordAllocRange()
326{
327    return (XRecordRange*)Xcalloc(1, sizeof(XRecordRange));
328}
329
330Status
331XRecordRegisterClients(dpy, context, datum_flags, clients, nclients, ranges, nranges)
332    Display 		*dpy;
333    XRecordContext	context;
334    int			datum_flags;
335    XRecordClientSpec   *clients;
336    int 	        nclients;
337    XRecordRange  	**ranges;
338    int			nranges;
339{
340    XExtDisplayInfo *info = find_display (dpy);
341    register xRecordRegisterClientsReq 	*req;
342    int			clen = 4 * nclients;
343
344    XRecordCheckExtension (dpy, info, 0);
345    LockDisplay(dpy);
346    GetReq(RecordRegisterClients, req);
347
348    req->reqType = info->codes->major_opcode;
349    req->recordReqType = X_RecordRegisterClients;
350    req->context =  context;
351    req->length += (nclients * 4 +
352		    nranges * SIZEOF(xRecordRange)) >> 2;
353    req->elementHeader = datum_flags;
354    req->nClients = nclients;
355    req->nRanges = nranges;
356
357    Data32(dpy, (long *)clients, clen);
358    SendRange(dpy, ranges, nranges);
359
360    UnlockDisplay(dpy);
361    SyncHandle();
362    return 1;
363}
364
365Status
366XRecordUnregisterClients(dpy, context, clients, nclients)
367    Display 		*dpy;
368    XRecordContext	context;
369    XRecordClientSpec   *clients;
370    int			nclients;
371{
372    XExtDisplayInfo *info = find_display (dpy);
373    register xRecordUnregisterClientsReq 	*req;
374    int			clen = 4 * nclients;
375
376    XRecordCheckExtension (dpy, info, 0);
377    LockDisplay(dpy);
378    GetReq(RecordUnregisterClients, req);
379
380    req->reqType = info->codes->major_opcode;
381    req->recordReqType = X_RecordUnregisterClients;
382    req->context = context;
383    req->length += nclients;
384    req->nClients = nclients;
385
386    Data32(dpy, (long *)clients, clen);
387
388    UnlockDisplay(dpy);
389    SyncHandle();
390    return 1;
391}
392
393static void
394WireToLibRange(
395    xRecordRange *wire_range,
396    XRecordRange *lib_range)
397{
398    lib_range->core_requests.first = wire_range->coreRequestsFirst;
399    lib_range->core_requests.last = wire_range->coreRequestsLast;
400    lib_range->core_replies.first = wire_range->coreRepliesFirst;
401    lib_range->core_replies.last = wire_range->coreRepliesLast;
402    lib_range->ext_requests.ext_major.first = wire_range->extRequestsMajorFirst;
403    lib_range->ext_requests.ext_major.last = wire_range->extRequestsMajorLast;
404    lib_range->ext_requests.ext_minor.first = wire_range->extRequestsMinorFirst;
405    lib_range->ext_requests.ext_minor.last = wire_range->extRequestsMinorLast;
406    lib_range->ext_replies.ext_major.first = wire_range->extRepliesMajorFirst;
407    lib_range->ext_replies.ext_major.last = wire_range->extRepliesMajorLast;
408    lib_range->ext_replies.ext_minor.first = wire_range->extRepliesMinorFirst;
409    lib_range->ext_replies.ext_minor.last = wire_range->extRepliesMinorLast;
410    lib_range->delivered_events.first = wire_range->deliveredEventsFirst;
411    lib_range->delivered_events.last = wire_range->deliveredEventsLast;
412    lib_range->device_events.first = wire_range->deviceEventsFirst;
413    lib_range->device_events.last = wire_range->deviceEventsLast;
414    lib_range->errors.first = wire_range->errorsFirst;
415    lib_range->errors.last = wire_range->errorsLast;
416    lib_range->client_started = wire_range->clientStarted;
417    lib_range->client_died = wire_range->clientDied;
418}
419
420Status
421XRecordGetContext(dpy, context, state_return)
422    Display 		*dpy;
423    XRecordContext	context;
424    XRecordState 	**state_return;
425{
426    XExtDisplayInfo 	*info = find_display (dpy);
427    register 		xRecordGetContextReq   	*req;
428    xRecordGetContextReply 	rep;
429    int			count, i, rn;
430    xRecordRange   	xrange;
431    XRecordRange	*ranges;
432    xRecordClientInfo   xclient_inf;
433    XRecordClientInfo	**client_inf, *client_inf_str;
434    XRecordState	*ret;
435
436    XRecordCheckExtension (dpy, info, 0);
437    LockDisplay(dpy);
438    GetReq(RecordGetContext, req);
439    req->reqType = info->codes->major_opcode;
440    req->recordReqType = X_RecordGetContext;
441    req->context = context;
442    if (!_XReply(dpy,(xReply *)&rep, 0, False)) {
443	UnlockDisplay(dpy);
444	SyncHandle();
445	return 0;
446    }
447    count = rep.nClients;
448
449    ret = (XRecordState*)Xmalloc(sizeof(XRecordState));
450    if (!ret) {
451	/* XXX - eat data */
452	UnlockDisplay(dpy);
453	SyncHandle();
454	return 0;
455    }
456
457    ret->enabled = rep.enabled;
458    ret->datum_flags = rep.elementHeader;
459    ret->nclients = count;
460
461    if (count)
462    {
463     	client_inf = (XRecordClientInfo **) Xcalloc(count, sizeof(XRecordClientInfo*));
464	ret->client_info = client_inf;
465	if (client_inf != NULL) {
466	    client_inf_str = (XRecordClientInfo *) Xmalloc(count*sizeof(XRecordClientInfo));
467	}
468        if (!client_inf || !client_inf_str)
469        {
470           for(i = 0; i < count; i++)
471           {
472	        _XEatData (dpy, sizeof(xRecordClientInfo));
473                _XEatData (dpy, SIZEOF(xRecordRange)); /* XXX - don't know how many */
474           }
475	   UnlockDisplay(dpy);
476	   XRecordFreeState(ret);
477	   SyncHandle();
478	   return 0;
479        }
480        for(i = 0; i < count; i++)
481        {
482	    client_inf[i] = &(client_inf_str[i]);
483            _XRead(dpy, (char *)&xclient_inf, (long)sizeof(xRecordClientInfo));
484            client_inf_str[i].client = xclient_inf.clientResource;
485            client_inf_str[i].nranges = xclient_inf.nRanges;
486
487	    if (xclient_inf.nRanges)
488	    {
489		client_inf_str[i].ranges = (XRecordRange**) Xcalloc(xclient_inf.nRanges, sizeof(XRecordRange*));
490		if (client_inf_str[i].ranges != NULL) {
491		    ranges = (XRecordRange*)
492			Xmalloc(xclient_inf.nRanges * sizeof(XRecordRange));
493		}
494		if (!client_inf_str[i].ranges || !ranges) {
495		    /* XXX eat data */
496		    UnlockDisplay(dpy);
497		    XRecordFreeState(ret);
498		    SyncHandle();
499		    return 0;
500		}
501		for (rn=0; rn<xclient_inf.nRanges; rn++) {
502		    client_inf_str[i].ranges[rn] = &(ranges[rn]);
503		    _XRead(dpy, (char *)&xrange, (long)sizeof(xRecordRange));
504		    WireToLibRange(&xrange, &(ranges[rn]));
505		}
506	    } else {
507		client_inf_str[i].ranges = NULL;
508	    }
509        }
510    } else {
511	ret->client_info = NULL;
512    }
513
514    *state_return = ret;
515
516    UnlockDisplay(dpy);
517    SyncHandle();
518    return 1;
519}
520
521void
522XRecordFreeState(state)
523    XRecordState *state;
524{
525    int i;
526
527    for(i=0; i<state->nclients; i++) {
528	if (state->client_info[i]->ranges) {
529	    if (state->client_info[i]->ranges[0])
530		Xfree(state->client_info[i]->ranges[0]);
531	    Xfree(state->client_info[i]->ranges);
532	}
533    }
534    if (state->client_info) {
535	if (state->client_info[0])
536	    Xfree(state->client_info[0]);
537	Xfree(state->client_info);
538    }
539    Xfree(state);
540}
541
542static struct reply_buffer *alloc_reply_buffer(
543    XExtDisplayInfo *info,
544    int nbytes)
545{
546    struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
547    struct reply_buffer *rbp;
548    struct reply_buffer *saved_rb = NULL;
549    /*
550     * First look for an allocated buffer that is not in use.
551     * If we have a big enough buffer, use that, otherwise
552     * realloc an existing one.
553     */
554    for (rbp = cache->reply_buffers; rbp; rbp = rbp->next) {
555	if (rbp->ref_count == 0) {
556	    if (rbp->nbytes >= nbytes)
557		return rbp;
558	    else
559		saved_rb = rbp;
560	}
561    }
562    if (saved_rb) {
563	saved_rb->buf = (unsigned char *)Xrealloc(saved_rb->buf, nbytes);
564	if (!saved_rb->buf) {
565	    saved_rb->nbytes = 0;
566	    return NULL;
567	}
568	saved_rb->nbytes = nbytes;
569	return saved_rb;
570    }
571
572    /*
573     * nothing available; malloc a new struct
574     */
575    rbp = (struct reply_buffer *)Xmalloc(sizeof(struct reply_buffer));
576    if (!rbp)
577	return NULL;
578    rbp->buf = (unsigned char *)Xmalloc(nbytes);
579    if (!rbp->buf) {
580	Xfree(rbp);
581	return NULL;
582    }
583    rbp->nbytes = nbytes;
584    rbp->ref_count = 0;
585    rbp->next = cache->reply_buffers;
586    cache->reply_buffers = rbp;
587    return rbp;
588}
589
590static XRecordInterceptData *alloc_inter_data(XExtDisplayInfo *info)
591{
592    struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
593    struct intercept_queue *iq;
594
595    /* if there is one on the free list, pop it */
596    if (cache->inter_data) {
597	iq = cache->inter_data;
598	cache->inter_data = iq->next;
599	return &iq->data;
600    }
601    /* allocate a new one */
602    iq = (struct intercept_queue *)Xmalloc(sizeof(struct intercept_queue));
603    if (!iq)
604	return NULL;
605    iq->cache = cache;
606    cache->inter_data_count++;
607    return &iq->data;
608}
609
610void
611XRecordFreeData(data)
612    XRecordInterceptData *data;
613{
614    /* we can do this cast because that is what we really allocated */
615    struct intercept_queue *iq = (struct intercept_queue *)data;
616    struct reply_buffer *rbp = NULL;
617    struct mem_cache_str *cache = iq->cache;
618
619    /*
620     * figure out what reply_buffer this points at
621     * and decrement its ref_count.
622     */
623    if (data->data) {
624
625	for (rbp = cache->reply_buffers; rbp; rbp = rbp->next) {
626	    if (data->data >= rbp->buf
627		&& data->data < rbp->buf + rbp->nbytes)
628	    {
629		assert(rbp->ref_count > 0);
630		rbp->ref_count--;
631		break;
632	    }
633	}
634	/* it's an error if we didn't find something to free */
635	assert(rbp);
636    }
637    /*
638     * If the display is still open, put this back on the free queue.
639     *
640     * Otherwise the display is closed and we won't reuse this, so free it.
641     * See if we can free the reply buffer, too.
642     * If we can, see if this is the last reply buffer and if so
643     * free the list of reply buffers.
644     */
645    if (cache->display_closed == False) {
646	iq->next = cache->inter_data;
647	cache->inter_data = iq;
648    } else {
649	if (rbp && rbp->ref_count == 0) {
650	    struct reply_buffer *rbp2, **rbp_next_p;
651
652	    /* Have to search the list again to find the prev element.
653	       This is not the common case, so don't slow down the code
654	       above by doing it then. */
655	    for (rbp_next_p = &cache->reply_buffers; *rbp_next_p; ) {
656		rbp2 = *rbp_next_p;
657		if (rbp == rbp2) {
658		    *rbp_next_p = rbp2->next;
659		    break;
660		} else {
661		    rbp_next_p = &rbp2->next;
662		}
663	    }
664	    XFree(rbp->buf);
665	    XFree(rbp);
666	}
667
668	XFree(iq);
669	cache->inter_data_count--;
670
671	if (cache->reply_buffers == NULL  &&  cache->inter_data_count == 0) {
672	    XFree(cache); /* all finished */
673	}
674    }
675}
676
677/* the EXTRACT macros are adapted from ICElibint.h */
678
679#ifndef WORD64
680
681#define EXTRACT_CARD16(swap, src, dst) \
682{ \
683    (dst) = *((CARD16 *) (src)); \
684    if (swap) \
685        (dst) = lswaps (dst); \
686}
687
688#define EXTRACT_CARD32(swap, src, dst) \
689{ \
690    (dst) = *((CARD32 *) (src)); \
691    if (swap) \
692        (dst) = lswapl (dst); \
693}
694
695#else /* WORD64 */
696
697#define EXTRACT_CARD16(swap, src, dst) \
698{ \
699    (dst) = *((src) + 0); \
700    (dst) <<= 8; \
701    (dst) |= *((src) + 1); \
702    if (swap) \
703        (dst) = lswaps (dst); \
704}
705
706#define EXTRACT_CARD32(swap, src, dst) \
707{ \
708    (dst) = *((src) + 0); \
709    (dst) <<= 8; \
710    (dst) |= *((src) + 1); \
711    (dst) <<= 8; \
712    (dst) |= *((src) + 2); \
713    (dst) <<= 8; \
714    (dst) |= *((src) + 3); \
715    if (swap) \
716        (dst) = lswapl (dst); \
717}
718
719#endif /* WORD64 */
720
721/* byte swapping macros from xfs/include/misc.h */
722
723/* byte swap a long literal */
724#define lswapl(x) ((((x) & 0xff) << 24) |\
725		   (((x) & 0xff00) << 8) |\
726		   (((x) & 0xff0000) >> 8) |\
727		   (((x) >> 24) & 0xff))
728
729/* byte swap a short literal */
730#define lswaps(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
731
732enum parser_return { Continue, End, Error };
733
734static enum parser_return
735parse_reply_call_callback(
736    Display *dpy,
737    XExtDisplayInfo *info,
738    xRecordEnableContextReply *rep,
739    struct reply_buffer *reply,
740    XRecordInterceptProc callback,
741    XPointer		 closure)
742{
743    int current_index;
744    int datum_bytes = 0;
745    XRecordInterceptData *data;
746
747    /* call the callback for each protocol element in the reply */
748    current_index = 0;
749    do {
750	data = alloc_inter_data(info);
751	if (!data)
752	    return Error;
753
754	data->id_base = rep->idBase;
755	data->category = rep->category;
756	data->client_swapped = rep->clientSwapped;
757	data->server_time = rep->serverTime;
758	data->client_seq = rep->recordedSequenceNumber;
759	/*
760	 * compute the size of this protocol element.
761	 */
762	switch (rep->category) {
763	case XRecordFromServer:
764	    if (rep->elementHeader&XRecordFromServerTime) {
765		EXTRACT_CARD32(rep->clientSwapped,
766			       reply->buf+current_index,
767			       data->server_time);
768		current_index += 4;
769	    }
770	    switch (reply->buf[current_index]) {
771	    case X_Reply: /* reply */
772		EXTRACT_CARD32(rep->clientSwapped,
773			       reply->buf+current_index+4, datum_bytes);
774		datum_bytes = (datum_bytes+8) << 2;
775		break;
776	    default: /* error or event */
777		datum_bytes = 32;
778	    }
779	    break;
780	case XRecordFromClient:
781	    if (rep->elementHeader&XRecordFromClientTime) {
782		EXTRACT_CARD32(rep->clientSwapped,
783			       reply->buf+current_index,
784			       data->server_time);
785		current_index += 4;
786	    }
787	    if (rep->elementHeader&XRecordFromClientSequence) {
788		EXTRACT_CARD32(rep->clientSwapped,
789			       reply->buf+current_index,
790			       data->client_seq);
791		current_index += 4;
792	    }
793	    if (reply->buf[current_index+2] == 0
794		&& reply->buf[current_index+3] == 0) /* needn't swap 0 */
795	    {	/* BIG-REQUESTS */
796		EXTRACT_CARD32(rep->clientSwapped,
797			       reply->buf+current_index+4, datum_bytes);
798	    } else {
799		EXTRACT_CARD16(rep->clientSwapped,
800			       reply->buf+current_index+2, datum_bytes);
801	    }
802	    datum_bytes <<= 2;
803	    break;
804	case XRecordClientStarted:
805	    EXTRACT_CARD16(rep->clientSwapped,
806			   reply->buf+current_index+6, datum_bytes);
807	    datum_bytes = (datum_bytes+2) << 2;
808	    break;
809	case XRecordClientDied:
810	    if (rep->elementHeader&XRecordFromClientSequence) {
811		EXTRACT_CARD32(rep->clientSwapped,
812			       reply->buf+current_index,
813			       data->client_seq);
814		current_index += 4;
815	    }
816	    /* fall through */
817	case XRecordStartOfData:
818	case XRecordEndOfData:
819	    datum_bytes = 0;
820	}
821
822	if (datum_bytes > 0) {
823	    if (current_index + datum_bytes > rep->length << 2)
824		fprintf(stderr,
825			"XRecord: %lu-byte reply claims %d-byte element (seq %lu)\n",
826			(long)rep->length << 2, current_index + datum_bytes,
827			dpy->last_request_read);
828	    /*
829	     * This assignment (and indeed the whole buffer sharing
830	     * scheme) assumes arbitrary 4-byte boundaries are
831	     * addressable.
832	     */
833	    data->data = reply->buf+current_index;
834	    reply->ref_count++;
835	} else {
836	    data->data = NULL;
837	}
838	data->data_len = datum_bytes >> 2;
839
840	(*callback)(closure, data);
841
842	current_index += datum_bytes;
843    } while (current_index<rep->length<<2);
844
845    if (rep->category == XRecordEndOfData)
846	return End;
847
848    return Continue;
849}
850
851Status
852XRecordEnableContext(dpy, context, callback, closure)
853    Display 		*dpy;
854    XRecordContext 	 context;
855    XRecordInterceptProc callback;
856    XPointer		 closure;
857{
858    XExtDisplayInfo *info = find_display (dpy);
859    register xRecordEnableContextReq   	*req;
860    xRecordEnableContextReply 	rep;
861    struct reply_buffer *reply;
862    enum parser_return status;
863
864    XRecordCheckExtension (dpy, info, 0);
865    LockDisplay(dpy);
866    GetReq(RecordEnableContext, req);
867
868    req->reqType = info->codes->major_opcode;
869    req->recordReqType = X_RecordEnableContext;
870    req->context = context;
871
872    while (1)
873    {
874	/* This code should match that in XRecordEnableContextAsync */
875	if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
876	{
877	    UnlockDisplay(dpy);
878	    SyncHandle();
879	    return 0;
880	}
881
882	if (rep.length > 0) {
883	    reply = alloc_reply_buffer(info, rep.length<<2);
884	    if (!reply) {
885		UnlockDisplay(dpy);
886		SyncHandle();
887		return 0;
888	    }
889	    _XRead (dpy, (char *)reply->buf, rep.length<<2);
890	} else {
891	    reply = NULL;
892	}
893
894	status = parse_reply_call_callback(dpy, info, &rep, reply,
895					   callback, closure);
896	switch (status) {
897	case Continue:
898	    break;
899	case End:
900	    UnlockDisplay(dpy);
901	    SyncHandle();
902	    return 1;
903	case Error:
904	    UnlockDisplay(dpy);
905	    SyncHandle();
906	    return 0;
907	}
908    }
909}
910
911
912typedef struct _record_async_state
913{
914    unsigned long enable_seq;
915    _XAsyncHandler *async;
916    _XAsyncErrorState *error_state;
917    XExtDisplayInfo *info;
918    XRecordInterceptProc callback;
919    XPointer closure;
920} record_async_state;
921
922static Bool
923record_async_handler(
924    register Display *dpy,
925    register xReply *rep,
926    char *buf,
927    int len,
928    XPointer adata)
929{
930    register record_async_state *state = (record_async_state *)adata;
931    struct reply_buffer *reply;
932    enum parser_return status;
933
934    if (dpy->last_request_read != state->enable_seq)
935    {
936	if (dpy->last_request_read > state->enable_seq) {
937	    /* it is an error that we are still on the handler list */
938	    fprintf(stderr, "XRecord: handler for seq %lu never saw XRecordEndOfData.  (seq now %lu)\n",
939		    state->enable_seq, dpy->last_request_read);
940	    DeqAsyncHandler(dpy, state->async);
941	    Xfree(state->async);
942	}
943	return False;
944    }
945    if (rep->generic.type == X_Error)
946    {
947	DeqAsyncHandler(dpy, state->async);
948	Xfree(state->async);
949	return False;
950    }
951
952    if (rep->generic.length > 0) {
953	reply = alloc_reply_buffer(state->info, rep->generic.length<<2);
954
955	if (!reply) {
956	    DeqAsyncHandler(dpy, state->async);
957	    Xfree(state->async);
958	    return False;
959	}
960
961	_XGetAsyncData(dpy, (char *)reply->buf, buf, len,
962		       SIZEOF(xRecordEnableContextReply),
963		       rep->generic.length << 2, 0);
964    } else {
965	reply = NULL;
966    }
967
968    status = parse_reply_call_callback(dpy, state->info,
969				       (xRecordEnableContextReply*) rep,
970				       reply, state->callback, state->closure);
971
972    if (status != Continue)
973    {
974	DeqAsyncHandler(dpy, state->async);
975	Xfree(state->async);
976	if (status == Error)
977	    return False;
978    }
979
980    return True;
981}
982
983/*
984 * reads the first reply, StartOfData, synchronously,
985 * then returns allowing the app to call XRecordProcessReplies
986 * to get the rest.
987 */
988Status
989XRecordEnableContextAsync(dpy, context, callback, closure)
990    Display 		*dpy;
991    XRecordContext 	 context;
992    XRecordInterceptProc callback;
993    XPointer		 closure;
994{
995    XExtDisplayInfo *info = find_display (dpy);
996    register xRecordEnableContextReq *req;
997    xRecordEnableContextReply rep;
998    struct reply_buffer *reply;
999    enum parser_return status;
1000    _XAsyncHandler *async;
1001    record_async_state *async_state;
1002
1003    XRecordCheckExtension (dpy, info, 0);
1004    async = (_XAsyncHandler *)Xmalloc(sizeof(_XAsyncHandler) +
1005	sizeof(record_async_state));
1006    if (!async)
1007	return 0;
1008    async_state = (record_async_state *)(async + 1);
1009
1010    LockDisplay(dpy);
1011    GetReq(RecordEnableContext, req);
1012
1013    req->reqType = info->codes->major_opcode;
1014    req->recordReqType = X_RecordEnableContext;
1015    req->context = context;
1016
1017    /* Get the StartOfData reply. */
1018    /* This code should match that in XRecordEnableContext */
1019    if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
1020    {
1021	UnlockDisplay(dpy);
1022	SyncHandle();
1023	Xfree(async);
1024	return 0;
1025    }
1026
1027    /* this had better be a StartOfData, which has no extra data. */
1028    if (rep.length != 0) {
1029	fprintf(stderr, "XRecord: malformed StartOfData for sequence %lu\n",
1030		dpy->last_request_read);
1031    }
1032    reply = NULL;
1033
1034    status = parse_reply_call_callback(dpy, info, &rep, reply,
1035				       callback, closure);
1036    if (status != Continue)
1037    {
1038	UnlockDisplay(dpy);
1039	Xfree(async);
1040	return 0;
1041    }
1042
1043    /* hook in the async handler for the rest of the replies */
1044    async_state->enable_seq = dpy->request;
1045    async_state->async = async;
1046    async_state->info = info;
1047    async_state->callback = callback;
1048    async_state->closure = closure;
1049
1050    async->next = dpy->async_handlers;
1051    async->handler = record_async_handler;
1052    async->data = (XPointer)async_state;
1053    dpy->async_handlers = async;
1054
1055    UnlockDisplay(dpy);
1056    /* Don't invoke SyncHandle here, since this is an async
1057       function.  Does this break XSetAfterFunction() ? */
1058    return 1;
1059}
1060
1061void
1062XRecordProcessReplies(dpy)
1063    Display   *dpy;
1064{
1065    (void) XPending(dpy);
1066}
1067
1068Status
1069XRecordDisableContext(dpy, context)
1070    Display 		*dpy;
1071    XRecordContext 	context;
1072{
1073    XExtDisplayInfo *info = find_display (dpy);
1074    register xRecordDisableContextReq 	*req;
1075
1076    XRecordCheckExtension (dpy, info, 0);
1077    LockDisplay(dpy);
1078    GetReq(RecordDisableContext, req);
1079    req->reqType = info->codes->major_opcode;
1080    req->recordReqType = X_RecordDisableContext;
1081    req->context =  context;
1082
1083    UnlockDisplay(dpy);
1084    SyncHandle();
1085    return 1;
1086}
1087
1088Status
1089XRecordFreeContext(dpy, context)
1090    Display 		*dpy;
1091    XRecordContext	context;
1092{
1093    XExtDisplayInfo *info = find_display (dpy);
1094    register xRecordFreeContextReq 	*req;
1095
1096    XRecordCheckExtension (dpy, info, 0);
1097
1098    LockDisplay(dpy);
1099    GetReq(RecordFreeContext, req);
1100    req->reqType = info->codes->major_opcode;
1101    req->recordReqType = X_RecordFreeContext;
1102    req->context = context;
1103
1104    UnlockDisplay(dpy);
1105    SyncHandle();
1106    return 1;
1107}
1108