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