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