XRecord.c revision de79cf69
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    XRecordInterceptData *data;
729    unsigned int current_index;
730    int datum_bytes = 0;
731
732    /* call the callback for each protocol element in the reply */
733    current_index = 0;
734    do {
735	data = alloc_inter_data(info);
736	if (!data)
737	    return Error;
738
739	data->id_base = rep->idBase;
740	data->category = rep->category;
741	data->client_swapped = rep->clientSwapped;
742	data->server_time = rep->serverTime;
743	data->client_seq = rep->recordedSequenceNumber;
744	/*
745	 * compute the size of this protocol element.
746	 */
747	switch (rep->category) {
748	case XRecordFromServer:
749	    if (rep->elementHeader&XRecordFromServerTime) {
750		if (current_index + 4 > rep->length << 2)
751		    return Error;
752		EXTRACT_CARD32(rep->clientSwapped,
753			       reply->buf+current_index,
754			       data->server_time);
755		current_index += 4;
756	    }
757	    if (current_index + 1 > rep->length << 2)
758		goto out;
759	    switch (reply->buf[current_index]) {
760	    case X_Reply: /* reply */
761		if (current_index + 8 > rep->length << 2)
762		    goto out;
763		EXTRACT_CARD32(rep->clientSwapped,
764			       reply->buf+current_index+4, datum_bytes);
765		if (datum_bytes < 0 || datum_bytes > ((INT_MAX >> 2) - 8))
766		    goto out;
767		datum_bytes = (datum_bytes+8) << 2;
768		break;
769	    default: /* error or event */
770		datum_bytes = 32;
771	    }
772	    break;
773	case XRecordFromClient:
774	    if (rep->elementHeader&XRecordFromClientTime) {
775		if (current_index + 4 > rep->length << 2)
776		    goto out;
777		EXTRACT_CARD32(rep->clientSwapped,
778			       reply->buf+current_index,
779			       data->server_time);
780		current_index += 4;
781	    }
782	    if (rep->elementHeader&XRecordFromClientSequence) {
783		if (current_index + 4 > rep->length << 2)
784		    goto out;
785		EXTRACT_CARD32(rep->clientSwapped,
786			       reply->buf+current_index,
787			       data->client_seq);
788		current_index += 4;
789	    }
790	    if (current_index + 4 > rep->length<<2)
791		goto out;
792	    if (reply->buf[current_index+2] == 0
793		&& reply->buf[current_index+3] == 0) /* needn't swap 0 */
794	    {	/* BIG-REQUESTS */
795		if (current_index + 8 > rep->length << 2)
796		    goto out;
797		EXTRACT_CARD32(rep->clientSwapped,
798			       reply->buf+current_index+4, datum_bytes);
799	    } else {
800		EXTRACT_CARD16(rep->clientSwapped,
801			       reply->buf+current_index+2, datum_bytes);
802	    }
803	    if (datum_bytes < 0 || datum_bytes > INT_MAX >> 2)
804		goto out;
805	    datum_bytes <<= 2;
806	    break;
807	case XRecordClientStarted:
808	    if (current_index + 8 > rep->length << 2)
809		goto out;
810	    EXTRACT_CARD16(rep->clientSwapped,
811			   reply->buf+current_index+6, datum_bytes);
812	    datum_bytes = (datum_bytes+2) << 2;
813	    break;
814	case XRecordClientDied:
815	    if (rep->elementHeader&XRecordFromClientSequence) {
816		if (current_index + 4 > rep->length << 2)
817		    goto out;
818		EXTRACT_CARD32(rep->clientSwapped,
819			       reply->buf+current_index,
820			       data->client_seq);
821		current_index += 4;
822	    } else if (current_index < rep->length << 2)
823		goto out;
824	    datum_bytes = 0;
825	    break;
826	case XRecordStartOfData:
827	case XRecordEndOfData:
828	    if (current_index < rep->length << 2)
829		goto out;
830	    datum_bytes = 0;
831	    break;
832	}
833
834	if (datum_bytes > 0) {
835	    if (INT_MAX - datum_bytes < (rep->length << 2) - current_index) {
836		fprintf(stderr,
837			"XRecord: %lu-byte reply claims %d-byte element (seq %lu)\n",
838			(unsigned long)rep->length << 2, current_index + datum_bytes,
839			dpy->last_request_read);
840		goto out;
841	    }
842	    /*
843	     * This assignment (and indeed the whole buffer sharing
844	     * scheme) assumes arbitrary 4-byte boundaries are
845	     * addressable.
846	     */
847	    data->data = reply->buf+current_index;
848	    reply->ref_count++;
849	} else {
850	    data->data = NULL;
851	}
852	data->data_len = datum_bytes >> 2;
853
854	(*callback)(closure, data);
855
856	current_index += datum_bytes;
857    } while (current_index<rep->length<<2);
858
859    if (rep->category == XRecordEndOfData)
860	return End;
861
862    return Continue;
863out:
864    Xfree(data);
865    return Error;
866}
867
868Status
869XRecordEnableContext(Display *dpy, XRecordContext context,
870		     XRecordInterceptProc callback, XPointer closure)
871{
872    XExtDisplayInfo *info = find_display (dpy);
873    register xRecordEnableContextReq   	*req;
874    xRecordEnableContextReply 	rep;
875    struct reply_buffer *reply;
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	enum parser_return status;
888
889	/* This code should match that in XRecordEnableContextAsync */
890	if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
891	{
892	    UnlockDisplay(dpy);
893	    SyncHandle();
894	    return 0;
895	}
896
897	if (rep.length > INT_MAX >> 2) {
898	    UnlockDisplay(dpy);
899	    SyncHandle();
900	    return 0;
901	}
902
903	if (rep.length > 0) {
904	    reply = alloc_reply_buffer(info, rep.length<<2);
905	    if (!reply) {
906		UnlockDisplay(dpy);
907		SyncHandle();
908		return 0;
909	    }
910	    _XRead (dpy, (char *)reply->buf, rep.length<<2);
911	} else {
912	    reply = NULL;
913	}
914
915	status = parse_reply_call_callback(dpy, info, &rep, reply,
916					   callback, closure);
917	switch (status) {
918	case Continue:
919	    break;
920	case End:
921	    UnlockDisplay(dpy);
922	    SyncHandle();
923	    return 1;
924	case Error:
925	    UnlockDisplay(dpy);
926	    SyncHandle();
927	    return 0;
928	}
929    }
930}
931
932
933typedef struct _record_async_state
934{
935    unsigned long enable_seq;
936    _XAsyncHandler *async;
937    _XAsyncErrorState *error_state;
938    XExtDisplayInfo *info;
939    XRecordInterceptProc callback;
940    XPointer closure;
941} record_async_state;
942
943static Bool
944record_async_handler(
945    register Display *dpy,
946    register xReply *rep,
947    char *buf,
948    int len,
949    XPointer adata)
950{
951    register record_async_state *state = (record_async_state *)adata;
952    struct reply_buffer *reply;
953    enum parser_return status;
954
955    if (dpy->last_request_read != state->enable_seq)
956    {
957	if (dpy->last_request_read > state->enable_seq) {
958	    /* it is an error that we are still on the handler list */
959	    fprintf(stderr, "XRecord: handler for seq %lu never saw XRecordEndOfData.  (seq now %lu)\n",
960		    state->enable_seq, dpy->last_request_read);
961	    DeqAsyncHandler(dpy, state->async);
962	    Xfree(state->async);
963	}
964	return False;
965    }
966    if (rep->generic.type == X_Error)
967    {
968	DeqAsyncHandler(dpy, state->async);
969	Xfree(state->async);
970	return False;
971    }
972
973    if (rep->generic.length > 0) {
974	reply = alloc_reply_buffer(state->info, rep->generic.length<<2);
975
976	if (!reply) {
977	    DeqAsyncHandler(dpy, state->async);
978	    Xfree(state->async);
979	    return False;
980	}
981
982	_XGetAsyncData(dpy, (char *)reply->buf, buf, len,
983		       SIZEOF(xRecordEnableContextReply),
984		       rep->generic.length << 2, 0);
985    } else {
986	reply = NULL;
987    }
988
989    status = parse_reply_call_callback(dpy, state->info,
990				       (xRecordEnableContextReply*) rep,
991				       reply, state->callback, state->closure);
992
993    if (status != Continue)
994    {
995	DeqAsyncHandler(dpy, state->async);
996	Xfree(state->async);
997	if (status == Error)
998	    return False;
999    }
1000
1001    return True;
1002}
1003
1004/*
1005 * reads the first reply, StartOfData, synchronously,
1006 * then returns allowing the app to call XRecordProcessReplies
1007 * to get the rest.
1008 */
1009Status
1010XRecordEnableContextAsync(Display *dpy, XRecordContext context,
1011			  XRecordInterceptProc callback, XPointer closure)
1012{
1013    XExtDisplayInfo *info = find_display (dpy);
1014    register xRecordEnableContextReq *req;
1015    xRecordEnableContextReply rep;
1016    struct reply_buffer *reply;
1017    enum parser_return status;
1018    _XAsyncHandler *async;
1019    record_async_state *async_state;
1020
1021    XRecordCheckExtension (dpy, info, 0);
1022    async = Xmalloc(sizeof(_XAsyncHandler) + sizeof(record_async_state));
1023    if (!async)
1024	return 0;
1025    async_state = (record_async_state *)(async + 1);
1026
1027    LockDisplay(dpy);
1028    GetReq(RecordEnableContext, req);
1029
1030    req->reqType = info->codes->major_opcode;
1031    req->recordReqType = X_RecordEnableContext;
1032    req->context = context;
1033
1034    /* Get the StartOfData reply. */
1035    /* This code should match that in XRecordEnableContext */
1036    if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
1037    {
1038	UnlockDisplay(dpy);
1039	SyncHandle();
1040	Xfree(async);
1041	return 0;
1042    }
1043
1044    /* this had better be a StartOfData, which has no extra data. */
1045    if (rep.length != 0) {
1046	fprintf(stderr, "XRecord: malformed StartOfData for sequence %lu\n",
1047		dpy->last_request_read);
1048    }
1049    reply = NULL;
1050
1051    status = parse_reply_call_callback(dpy, info, &rep, reply,
1052				       callback, closure);
1053    if (status != Continue)
1054    {
1055	UnlockDisplay(dpy);
1056	Xfree(async);
1057	return 0;
1058    }
1059
1060    /* hook in the async handler for the rest of the replies */
1061    async_state->enable_seq = dpy->request;
1062    async_state->async = async;
1063    async_state->info = info;
1064    async_state->callback = callback;
1065    async_state->closure = closure;
1066
1067    async->next = dpy->async_handlers;
1068    async->handler = record_async_handler;
1069    async->data = (XPointer)async_state;
1070    dpy->async_handlers = async;
1071
1072    UnlockDisplay(dpy);
1073    /* Don't invoke SyncHandle here, since this is an async
1074       function.  Does this break XSetAfterFunction() ? */
1075    return 1;
1076}
1077
1078void
1079XRecordProcessReplies(Display *dpy)
1080{
1081    (void) XPending(dpy);
1082}
1083
1084Status
1085XRecordDisableContext(Display *dpy, XRecordContext context)
1086{
1087    XExtDisplayInfo *info = find_display (dpy);
1088    register xRecordDisableContextReq 	*req;
1089
1090    XRecordCheckExtension (dpy, info, 0);
1091    LockDisplay(dpy);
1092    GetReq(RecordDisableContext, req);
1093    req->reqType = info->codes->major_opcode;
1094    req->recordReqType = X_RecordDisableContext;
1095    req->context =  context;
1096
1097    UnlockDisplay(dpy);
1098    SyncHandle();
1099    return 1;
1100}
1101
1102Status
1103XRecordFreeContext(Display *dpy, XRecordContext context)
1104{
1105    XExtDisplayInfo *info = find_display (dpy);
1106    register xRecordFreeContextReq 	*req;
1107
1108    XRecordCheckExtension (dpy, info, 0);
1109
1110    LockDisplay(dpy);
1111    GetReq(RecordFreeContext, req);
1112    req->reqType = info->codes->major_opcode;
1113    req->recordReqType = X_RecordFreeContext;
1114    req->context = context;
1115
1116    UnlockDisplay(dpy);
1117    SyncHandle();
1118    return 1;
1119}
1120