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