XRecord.c revision ec15139c
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		goto out;
761	    switch (reply->buf[current_index]) {
762	    case X_Reply: /* reply */
763		if (current_index + 8 > rep->length << 2)
764		    goto out;
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		    goto out;
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		    goto out;
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		    goto out;
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		goto out;
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		    goto out;
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		goto out;
807	    datum_bytes <<= 2;
808	    break;
809	case XRecordClientStarted:
810	    if (current_index + 8 > rep->length << 2)
811		goto out;
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		    goto out;
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		goto out;
826	    datum_bytes = 0;
827	    break;
828	case XRecordStartOfData:
829	case XRecordEndOfData:
830	    if (current_index < rep->length << 2)
831		goto out;
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		goto out;
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;
865out:
866    Xfree(data);
867    return Error;
868}
869
870Status
871XRecordEnableContext(Display *dpy, XRecordContext context,
872		     XRecordInterceptProc callback, XPointer closure)
873{
874    XExtDisplayInfo *info = find_display (dpy);
875    register xRecordEnableContextReq   	*req;
876    xRecordEnableContextReply 	rep;
877    struct reply_buffer *reply;
878    enum parser_return status;
879
880    XRecordCheckExtension (dpy, info, 0);
881    LockDisplay(dpy);
882    GetReq(RecordEnableContext, req);
883
884    req->reqType = info->codes->major_opcode;
885    req->recordReqType = X_RecordEnableContext;
886    req->context = context;
887
888    while (1)
889    {
890	/* This code should match that in XRecordEnableContextAsync */
891	if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
892	{
893	    UnlockDisplay(dpy);
894	    SyncHandle();
895	    return 0;
896	}
897
898	if (rep.length > INT_MAX >> 2) {
899	    UnlockDisplay(dpy);
900	    SyncHandle();
901	    return 0;
902	}
903
904	if (rep.length > 0) {
905	    reply = alloc_reply_buffer(info, rep.length<<2);
906	    if (!reply) {
907		UnlockDisplay(dpy);
908		SyncHandle();
909		return 0;
910	    }
911	    _XRead (dpy, (char *)reply->buf, rep.length<<2);
912	} else {
913	    reply = NULL;
914	}
915
916	status = parse_reply_call_callback(dpy, info, &rep, reply,
917					   callback, closure);
918	switch (status) {
919	case Continue:
920	    break;
921	case End:
922	    UnlockDisplay(dpy);
923	    SyncHandle();
924	    return 1;
925	case Error:
926	    UnlockDisplay(dpy);
927	    SyncHandle();
928	    return 0;
929	}
930    }
931}
932
933
934typedef struct _record_async_state
935{
936    unsigned long enable_seq;
937    _XAsyncHandler *async;
938    _XAsyncErrorState *error_state;
939    XExtDisplayInfo *info;
940    XRecordInterceptProc callback;
941    XPointer closure;
942} record_async_state;
943
944static Bool
945record_async_handler(
946    register Display *dpy,
947    register xReply *rep,
948    char *buf,
949    int len,
950    XPointer adata)
951{
952    register record_async_state *state = (record_async_state *)adata;
953    struct reply_buffer *reply;
954    enum parser_return status;
955
956    if (dpy->last_request_read != state->enable_seq)
957    {
958	if (dpy->last_request_read > state->enable_seq) {
959	    /* it is an error that we are still on the handler list */
960	    fprintf(stderr, "XRecord: handler for seq %lu never saw XRecordEndOfData.  (seq now %lu)\n",
961		    state->enable_seq, dpy->last_request_read);
962	    DeqAsyncHandler(dpy, state->async);
963	    Xfree(state->async);
964	}
965	return False;
966    }
967    if (rep->generic.type == X_Error)
968    {
969	DeqAsyncHandler(dpy, state->async);
970	Xfree(state->async);
971	return False;
972    }
973
974    if (rep->generic.length > 0) {
975	reply = alloc_reply_buffer(state->info, rep->generic.length<<2);
976
977	if (!reply) {
978	    DeqAsyncHandler(dpy, state->async);
979	    Xfree(state->async);
980	    return False;
981	}
982
983	_XGetAsyncData(dpy, (char *)reply->buf, buf, len,
984		       SIZEOF(xRecordEnableContextReply),
985		       rep->generic.length << 2, 0);
986    } else {
987	reply = NULL;
988    }
989
990    status = parse_reply_call_callback(dpy, state->info,
991				       (xRecordEnableContextReply*) rep,
992				       reply, state->callback, state->closure);
993
994    if (status != Continue)
995    {
996	DeqAsyncHandler(dpy, state->async);
997	Xfree(state->async);
998	if (status == Error)
999	    return False;
1000    }
1001
1002    return True;
1003}
1004
1005/*
1006 * reads the first reply, StartOfData, synchronously,
1007 * then returns allowing the app to call XRecordProcessReplies
1008 * to get the rest.
1009 */
1010Status
1011XRecordEnableContextAsync(Display *dpy, XRecordContext context,
1012			  XRecordInterceptProc callback, XPointer closure)
1013{
1014    XExtDisplayInfo *info = find_display (dpy);
1015    register xRecordEnableContextReq *req;
1016    xRecordEnableContextReply rep;
1017    struct reply_buffer *reply;
1018    enum parser_return status;
1019    _XAsyncHandler *async;
1020    record_async_state *async_state;
1021
1022    XRecordCheckExtension (dpy, info, 0);
1023    async = (_XAsyncHandler *)Xmalloc(sizeof(_XAsyncHandler) +
1024	sizeof(record_async_state));
1025    if (!async)
1026	return 0;
1027    async_state = (record_async_state *)(async + 1);
1028
1029    LockDisplay(dpy);
1030    GetReq(RecordEnableContext, req);
1031
1032    req->reqType = info->codes->major_opcode;
1033    req->recordReqType = X_RecordEnableContext;
1034    req->context = context;
1035
1036    /* Get the StartOfData reply. */
1037    /* This code should match that in XRecordEnableContext */
1038    if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
1039    {
1040	UnlockDisplay(dpy);
1041	SyncHandle();
1042	Xfree(async);
1043	return 0;
1044    }
1045
1046    /* this had better be a StartOfData, which has no extra data. */
1047    if (rep.length != 0) {
1048	fprintf(stderr, "XRecord: malformed StartOfData for sequence %lu\n",
1049		dpy->last_request_read);
1050    }
1051    reply = NULL;
1052
1053    status = parse_reply_call_callback(dpy, info, &rep, reply,
1054				       callback, closure);
1055    if (status != Continue)
1056    {
1057	UnlockDisplay(dpy);
1058	Xfree(async);
1059	return 0;
1060    }
1061
1062    /* hook in the async handler for the rest of the replies */
1063    async_state->enable_seq = dpy->request;
1064    async_state->async = async;
1065    async_state->info = info;
1066    async_state->callback = callback;
1067    async_state->closure = closure;
1068
1069    async->next = dpy->async_handlers;
1070    async->handler = record_async_handler;
1071    async->data = (XPointer)async_state;
1072    dpy->async_handlers = async;
1073
1074    UnlockDisplay(dpy);
1075    /* Don't invoke SyncHandle here, since this is an async
1076       function.  Does this break XSetAfterFunction() ? */
1077    return 1;
1078}
1079
1080void
1081XRecordProcessReplies(Display *dpy)
1082{
1083    (void) XPending(dpy);
1084}
1085
1086Status
1087XRecordDisableContext(Display *dpy, XRecordContext context)
1088{
1089    XExtDisplayInfo *info = find_display (dpy);
1090    register xRecordDisableContextReq 	*req;
1091
1092    XRecordCheckExtension (dpy, info, 0);
1093    LockDisplay(dpy);
1094    GetReq(RecordDisableContext, req);
1095    req->reqType = info->codes->major_opcode;
1096    req->recordReqType = X_RecordDisableContext;
1097    req->context =  context;
1098
1099    UnlockDisplay(dpy);
1100    SyncHandle();
1101    return 1;
1102}
1103
1104Status
1105XRecordFreeContext(Display *dpy, XRecordContext context)
1106{
1107    XExtDisplayInfo *info = find_display (dpy);
1108    register xRecordFreeContextReq 	*req;
1109
1110    XRecordCheckExtension (dpy, info, 0);
1111
1112    LockDisplay(dpy);
1113    GetReq(RecordFreeContext, req);
1114    req->reqType = info->codes->major_opcode;
1115    req->recordReqType = X_RecordFreeContext;
1116    req->context = context;
1117
1118    UnlockDisplay(dpy);
1119    SyncHandle();
1120    return 1;
1121}
1122