XRecord.c revision 072eb593
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 = 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 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;
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 = 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 (unsigned int 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 (unsigned int 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    for (unsigned long i = 0; i < state->nclients; i++) {
514	if (state->client_info[i]->ranges) {
515	    if (state->client_info[i]->ranges[0])
516		Xfree(state->client_info[i]->ranges[0]);
517	    Xfree(state->client_info[i]->ranges);
518	}
519    }
520    if (state->client_info) {
521	if (state->client_info[0])
522	    Xfree(state->client_info[0]);
523	Xfree(state->client_info);
524    }
525    Xfree(state);
526}
527
528static struct reply_buffer *alloc_reply_buffer(
529    XExtDisplayInfo *info,
530    int nbytes)
531{
532    struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
533    struct reply_buffer *rbp;
534    struct reply_buffer *saved_rb = NULL;
535    /*
536     * First look for an allocated buffer that is not in use.
537     * If we have a big enough buffer, use that, otherwise
538     * realloc an existing one.
539     */
540    for (rbp = cache->reply_buffers; rbp; rbp = rbp->next) {
541	if (rbp->ref_count == 0) {
542	    if (rbp->nbytes >= nbytes)
543		return rbp;
544	    else
545		saved_rb = rbp;
546	}
547    }
548    if (saved_rb) {
549	saved_rb->buf = (unsigned char *)Xrealloc(saved_rb->buf, nbytes);
550	if (!saved_rb->buf) {
551	    saved_rb->nbytes = 0;
552	    return NULL;
553	}
554	saved_rb->nbytes = nbytes;
555	return saved_rb;
556    }
557
558    /*
559     * nothing available; malloc a new struct
560     */
561    rbp = Xmalloc(sizeof(struct reply_buffer));
562    if (!rbp)
563	return NULL;
564    rbp->buf = Xmalloc(nbytes);
565    if (!rbp->buf) {
566	Xfree(rbp);
567	return NULL;
568    }
569    rbp->nbytes = nbytes;
570    rbp->ref_count = 0;
571    rbp->next = cache->reply_buffers;
572    cache->reply_buffers = rbp;
573    return rbp;
574}
575
576static XRecordInterceptData *alloc_inter_data(XExtDisplayInfo *info)
577{
578    struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
579    struct intercept_queue *iq;
580
581    /* if there is one on the free list, pop it */
582    if (cache->inter_data) {
583	iq = cache->inter_data;
584	cache->inter_data = iq->next;
585	return &iq->data;
586    }
587    /* allocate a new one */
588    iq = Xmalloc(sizeof(struct intercept_queue));
589    if (!iq)
590	return NULL;
591    iq->cache = cache;
592    cache->inter_data_count++;
593    return &iq->data;
594}
595
596void
597XRecordFreeData(XRecordInterceptData *data)
598{
599    /* we can do this cast because that is what we really allocated */
600    struct intercept_queue *iq = (struct intercept_queue *)data;
601    struct reply_buffer *rbp = NULL;
602    struct mem_cache_str *cache = iq->cache;
603
604    /*
605     * figure out what reply_buffer this points at
606     * and decrement its ref_count.
607     */
608    if (data->data) {
609
610	for (rbp = cache->reply_buffers; rbp; rbp = rbp->next) {
611	    if (data->data >= rbp->buf
612		&& data->data < rbp->buf + rbp->nbytes)
613	    {
614		assert(rbp->ref_count > 0);
615		rbp->ref_count--;
616		break;
617	    }
618	}
619	/* it's an error if we didn't find something to free */
620	assert(rbp);
621    }
622    /*
623     * If the display is still open, put this back on the free queue.
624     *
625     * Otherwise the display is closed and we won't reuse this, so free it.
626     * See if we can free the reply buffer, too.
627     * If we can, see if this is the last reply buffer and if so
628     * free the list of reply buffers.
629     */
630    if (cache->display_closed == False) {
631	iq->next = cache->inter_data;
632	cache->inter_data = iq;
633    } else {
634	if (rbp && rbp->ref_count == 0) {
635	    struct reply_buffer *rbp2, **rbp_next_p;
636
637	    /* Have to search the list again to find the prev element.
638	       This is not the common case, so don't slow down the code
639	       above by doing it then. */
640	    for (rbp_next_p = &cache->reply_buffers; *rbp_next_p; ) {
641		rbp2 = *rbp_next_p;
642		if (rbp == rbp2) {
643		    *rbp_next_p = rbp2->next;
644		    break;
645		} else {
646		    rbp_next_p = &rbp2->next;
647		}
648	    }
649	    XFree(rbp->buf);
650	    XFree(rbp);
651	}
652
653	XFree(iq);
654	cache->inter_data_count--;
655
656	if (cache->reply_buffers == NULL  &&  cache->inter_data_count == 0) {
657	    XFree(cache); /* all finished */
658	}
659    }
660}
661
662/* the EXTRACT macros are adapted from ICElibint.h */
663
664#ifndef WORD64
665
666#define EXTRACT_CARD16(swap, src, dst) \
667{ \
668    (dst) = *((CARD16 *) (src)); \
669    if (swap) \
670        (dst) = lswaps (dst); \
671}
672
673#define EXTRACT_CARD32(swap, src, dst) \
674{ \
675    (dst) = *((CARD32 *) (src)); \
676    if (swap) \
677        (dst) = lswapl (dst); \
678}
679
680#else /* WORD64 */
681
682#define EXTRACT_CARD16(swap, src, dst) \
683{ \
684    (dst) = *((src) + 0); \
685    (dst) <<= 8; \
686    (dst) |= *((src) + 1); \
687    if (swap) \
688        (dst) = lswaps (dst); \
689}
690
691#define EXTRACT_CARD32(swap, src, dst) \
692{ \
693    (dst) = *((src) + 0); \
694    (dst) <<= 8; \
695    (dst) |= *((src) + 1); \
696    (dst) <<= 8; \
697    (dst) |= *((src) + 2); \
698    (dst) <<= 8; \
699    (dst) |= *((src) + 3); \
700    if (swap) \
701        (dst) = lswapl (dst); \
702}
703
704#endif /* WORD64 */
705
706/* byte swapping macros from xfs/include/misc.h */
707
708/* byte swap a long literal */
709#define lswapl(x) ((((x) & 0xff) << 24) |\
710		   (((x) & 0xff00) << 8) |\
711		   (((x) & 0xff0000) >> 8) |\
712		   (((x) >> 24) & 0xff))
713
714/* byte swap a short literal */
715#define lswaps(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
716
717enum parser_return { Continue, End, Error };
718
719static enum parser_return
720parse_reply_call_callback(
721    Display *dpy,
722    XExtDisplayInfo *info,
723    xRecordEnableContextReply *rep,
724    struct reply_buffer *reply,
725    XRecordInterceptProc callback,
726    XPointer		 closure)
727{
728    unsigned int current_index;
729    int datum_bytes = 0;
730
731    /* call the callback for each protocol element in the reply */
732    current_index = 0;
733    do {
734	XRecordInterceptData *data = alloc_inter_data(info);
735	if (!data)
736	    return Error;
737
738	data->id_base = rep->idBase;
739	data->category = rep->category;
740	data->client_swapped = rep->clientSwapped;
741	data->server_time = rep->serverTime;
742	data->client_seq = rep->recordedSequenceNumber;
743	/*
744	 * compute the size of this protocol element.
745	 */
746	switch (rep->category) {
747	case XRecordFromServer:
748	    if (rep->elementHeader&XRecordFromServerTime) {
749		if (current_index + 4 > rep->length << 2)
750		    return Error;
751		EXTRACT_CARD32(rep->clientSwapped,
752			       reply->buf+current_index,
753			       data->server_time);
754		current_index += 4;
755	    }
756	    if (current_index + 1 > rep->length << 2)
757		goto out;
758	    switch (reply->buf[current_index]) {
759	    case X_Reply: /* reply */
760		if (current_index + 8 > rep->length << 2)
761		    goto out;
762		EXTRACT_CARD32(rep->clientSwapped,
763			       reply->buf+current_index+4, datum_bytes);
764		if (datum_bytes < 0 || datum_bytes > ((INT_MAX >> 2) - 8))
765		    goto out;
766		datum_bytes = (datum_bytes+8) << 2;
767		break;
768	    default: /* error or event */
769		datum_bytes = 32;
770	    }
771	    break;
772	case XRecordFromClient:
773	    if (rep->elementHeader&XRecordFromClientTime) {
774		if (current_index + 4 > rep->length << 2)
775		    goto out;
776		EXTRACT_CARD32(rep->clientSwapped,
777			       reply->buf+current_index,
778			       data->server_time);
779		current_index += 4;
780	    }
781	    if (rep->elementHeader&XRecordFromClientSequence) {
782		if (current_index + 4 > rep->length << 2)
783		    goto out;
784		EXTRACT_CARD32(rep->clientSwapped,
785			       reply->buf+current_index,
786			       data->client_seq);
787		current_index += 4;
788	    }
789	    if (current_index + 4 > rep->length<<2)
790		goto out;
791	    if (reply->buf[current_index+2] == 0
792		&& reply->buf[current_index+3] == 0) /* needn't swap 0 */
793	    {	/* BIG-REQUESTS */
794		if (current_index + 8 > rep->length << 2)
795		    goto out;
796		EXTRACT_CARD32(rep->clientSwapped,
797			       reply->buf+current_index+4, datum_bytes);
798	    } else {
799		EXTRACT_CARD16(rep->clientSwapped,
800			       reply->buf+current_index+2, datum_bytes);
801	    }
802	    if (datum_bytes < 0 || datum_bytes > INT_MAX >> 2)
803		goto out;
804	    datum_bytes <<= 2;
805	    break;
806	case XRecordClientStarted:
807	    if (current_index + 8 > rep->length << 2)
808		goto out;
809	    EXTRACT_CARD16(rep->clientSwapped,
810			   reply->buf+current_index+6, datum_bytes);
811	    datum_bytes = (datum_bytes+2) << 2;
812	    break;
813	case XRecordClientDied:
814	    if (rep->elementHeader&XRecordFromClientSequence) {
815		if (current_index + 4 > rep->length << 2)
816		    goto out;
817		EXTRACT_CARD32(rep->clientSwapped,
818			       reply->buf+current_index,
819			       data->client_seq);
820		current_index += 4;
821	    } else if (current_index < rep->length << 2)
822		goto out;
823	    datum_bytes = 0;
824	    break;
825	case XRecordStartOfData:
826	case XRecordEndOfData:
827	    if (current_index < rep->length << 2)
828		goto out;
829	    datum_bytes = 0;
830	    break;
831	}
832
833	if (datum_bytes > 0) {
834	    if (INT_MAX - datum_bytes < (rep->length << 2) - current_index) {
835		fprintf(stderr,
836			"XRecord: %lu-byte reply claims %d-byte element (seq %lu)\n",
837			(unsigned long)rep->length << 2, current_index + datum_bytes,
838			dpy->last_request_read);
839		goto out;
840	    }
841	    /*
842	     * This assignment (and indeed the whole buffer sharing
843	     * scheme) assumes arbitrary 4-byte boundaries are
844	     * addressable.
845	     */
846	    data->data = reply->buf+current_index;
847	    reply->ref_count++;
848	} else {
849	    data->data = NULL;
850	}
851	data->data_len = datum_bytes >> 2;
852
853	(*callback)(closure, data);
854
855	current_index += datum_bytes;
856    } while (current_index<rep->length<<2);
857
858    if (rep->category == XRecordEndOfData)
859	return End;
860
861    return Continue;
862out:
863    Xfree(data);
864    return Error;
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
876    XRecordCheckExtension (dpy, info, 0);
877    LockDisplay(dpy);
878    GetReq(RecordEnableContext, req);
879
880    req->reqType = info->codes->major_opcode;
881    req->recordReqType = X_RecordEnableContext;
882    req->context = context;
883
884    while (1)
885    {
886	enum parser_return status;
887
888	/* This code should match that in XRecordEnableContextAsync */
889	if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
890	{
891	    UnlockDisplay(dpy);
892	    SyncHandle();
893	    return 0;
894	}
895
896	if (rep.length > INT_MAX >> 2) {
897	    UnlockDisplay(dpy);
898	    SyncHandle();
899	    return 0;
900	}
901
902	if (rep.length > 0) {
903	    reply = alloc_reply_buffer(info, rep.length<<2);
904	    if (!reply) {
905		UnlockDisplay(dpy);
906		SyncHandle();
907		return 0;
908	    }
909	    _XRead (dpy, (char *)reply->buf, rep.length<<2);
910	} else {
911	    reply = NULL;
912	}
913
914	status = parse_reply_call_callback(dpy, info, &rep, reply,
915					   callback, closure);
916	switch (status) {
917	case Continue:
918	    break;
919	case End:
920	    UnlockDisplay(dpy);
921	    SyncHandle();
922	    return 1;
923	case Error:
924	    UnlockDisplay(dpy);
925	    SyncHandle();
926	    return 0;
927	}
928    }
929}
930
931
932typedef struct _record_async_state
933{
934    unsigned long enable_seq;
935    _XAsyncHandler *async;
936    _XAsyncErrorState *error_state;
937    XExtDisplayInfo *info;
938    XRecordInterceptProc callback;
939    XPointer closure;
940} record_async_state;
941
942static Bool
943record_async_handler(
944    register Display *dpy,
945    register xReply *rep,
946    char *buf,
947    int len,
948    XPointer adata)
949{
950    register record_async_state *state = (record_async_state *)adata;
951    struct reply_buffer *reply;
952    enum parser_return status;
953
954    if (dpy->last_request_read != state->enable_seq)
955    {
956	if (dpy->last_request_read > state->enable_seq) {
957	    /* it is an error that we are still on the handler list */
958	    fprintf(stderr, "XRecord: handler for seq %lu never saw XRecordEndOfData.  (seq now %lu)\n",
959		    state->enable_seq, dpy->last_request_read);
960	    DeqAsyncHandler(dpy, state->async);
961	    Xfree(state->async);
962	}
963	return False;
964    }
965    if (rep->generic.type == X_Error)
966    {
967	DeqAsyncHandler(dpy, state->async);
968	Xfree(state->async);
969	return False;
970    }
971
972    if (rep->generic.length > 0) {
973	reply = alloc_reply_buffer(state->info, rep->generic.length<<2);
974
975	if (!reply) {
976	    DeqAsyncHandler(dpy, state->async);
977	    Xfree(state->async);
978	    return False;
979	}
980
981	_XGetAsyncData(dpy, (char *)reply->buf, buf, len,
982		       SIZEOF(xRecordEnableContextReply),
983		       rep->generic.length << 2, 0);
984    } else {
985	reply = NULL;
986    }
987
988    status = parse_reply_call_callback(dpy, state->info,
989				       (xRecordEnableContextReply*) rep,
990				       reply, state->callback, state->closure);
991
992    if (status != Continue)
993    {
994	DeqAsyncHandler(dpy, state->async);
995	Xfree(state->async);
996	if (status == Error)
997	    return False;
998    }
999
1000    return True;
1001}
1002
1003/*
1004 * reads the first reply, StartOfData, synchronously,
1005 * then returns allowing the app to call XRecordProcessReplies
1006 * to get the rest.
1007 */
1008Status
1009XRecordEnableContextAsync(Display *dpy, XRecordContext context,
1010			  XRecordInterceptProc callback, XPointer closure)
1011{
1012    XExtDisplayInfo *info = find_display (dpy);
1013    register xRecordEnableContextReq *req;
1014    xRecordEnableContextReply rep;
1015    struct reply_buffer *reply;
1016    enum parser_return status;
1017    _XAsyncHandler *async;
1018    record_async_state *async_state;
1019
1020    XRecordCheckExtension (dpy, info, 0);
1021    async = Xmalloc(sizeof(_XAsyncHandler) + 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