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