Home | History | Annotate | Line # | Download | only in src
      1 /*
      2 XRecord.c - client-side library for RECORD extension
      3 
      4 Copyright 1995, 1998  The Open Group
      5 
      6 Permission to use, copy, modify, distribute, and sell this software and its
      7 documentation for any purpose is hereby granted without fee, provided that
      8 the above copyright notice appear in all copies and that both that
      9 copyright notice and this permission notice appear in supporting
     10 documentation.
     11 
     12 The above copyright notice and this permission notice shall be
     13 included in all copies or substantial portions of the Software.
     14 
     15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     18 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
     19 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     21 OTHER DEALINGS IN THE SOFTWARE.
     22 
     23 Except as contained in this notice, the name of The Open Group shall
     24 not be used in advertising or otherwise to promote the sale, use or
     25 other dealings in this Software without prior written authorization
     26 from 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 
     64 static XExtensionInfo _xrecord_info_data;
     65 static XExtensionInfo *xrecord_info = &_xrecord_info_data;
     66 static 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 
     77 static 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  */
     86 struct 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  */
    100 struct 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  */
    113 struct 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 
    121 static 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 
    164 static 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 
    179 static const char *xrecord_error_list[] = {
    180     "XRecordBadContext (Not a defined RECORD context)",
    181 };
    182 
    183 static XEXT_GENERATE_ERROR_STRING (error_string, xrecord_extension_name,
    184                                    RecordNumErrors, xrecord_error_list)
    185 
    186 static 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 
    200 static 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 
    210 static void
    211 SendRange(
    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 
    253 XID
    254 XRecordIdBaseMask(Display *dpy)
    255 {
    256     return 0x1fffffff & ~dpy->resource_mask;
    257 }
    258 
    259 Status
    260 XRecordQueryVersion(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 
    287 XRecordContext
    288 XRecordCreateContext(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 
    317 XRecordRange *
    318 XRecordAllocRange(void)
    319 {
    320     return Xcalloc(1, sizeof(XRecordRange));
    321 }
    322 
    323 Status
    324 XRecordRegisterClients(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 
    353 Status
    354 XRecordUnregisterClients(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 
    378 static void
    379 WireToLibRange(
    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 
    405 Status
    406 XRecordGetContext(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 	   _XEatDataWords (dpy, rep.length);
    456 	   UnlockDisplay(dpy);
    457 	   XRecordFreeState(ret); /* frees ret->client_info, aka client_inf */
    458 	   SyncHandle();
    459 	   return 0;
    460         }
    461         for (unsigned int i = 0; i < count; i++)
    462         {
    463 	    client_inf[i] = &(client_inf_str[i]);
    464             _XRead(dpy, (char *)&xclient_inf, (long)sizeof(xRecordClientInfo));
    465             client_inf_str[i].client = xclient_inf.clientResource;
    466             client_inf_str[i].nranges = xclient_inf.nRanges;
    467 
    468 	    if (xclient_inf.nRanges)
    469 	    {
    470 		XRecordRange	*ranges = NULL;
    471 
    472 		if (xclient_inf.nRanges < (INT_MAX / sizeof(XRecordRange))) {
    473 		    client_inf_str[i].ranges =
    474 			Xcalloc(xclient_inf.nRanges, sizeof(XRecordRange *));
    475 		    if (client_inf_str[i].ranges != NULL)
    476 			ranges =
    477 			    Xmalloc(xclient_inf.nRanges * sizeof(XRecordRange));
    478 		}
    479 		else
    480 		    client_inf_str[i].ranges = NULL;
    481 
    482 		if (!client_inf_str[i].ranges || !ranges) {
    483 		    /* XXX eat data */
    484 		    UnlockDisplay(dpy);
    485 		    XRecordFreeState(ret);
    486 		    SyncHandle();
    487 		    return 0;
    488 		}
    489 		for (unsigned int rn = 0; rn < xclient_inf.nRanges; rn++) {
    490 		    client_inf_str[i].ranges[rn] = &(ranges[rn]);
    491 		    _XRead(dpy, (char *)&xrange, (long)sizeof(xRecordRange));
    492 		    WireToLibRange(&xrange, &(ranges[rn]));
    493 		}
    494 	    } else {
    495 		client_inf_str[i].ranges = NULL;
    496 	    }
    497         }
    498     } else {
    499 	ret->client_info = NULL;
    500     }
    501 
    502     *state_return = ret;
    503 
    504     UnlockDisplay(dpy);
    505     SyncHandle();
    506     return 1;
    507 }
    508 
    509 void
    510 XRecordFreeState(XRecordState *state)
    511 {
    512     if (state->client_info) {
    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[0])
    521 	    Xfree(state->client_info[0]);
    522 	Xfree(state->client_info);
    523     }
    524     Xfree(state);
    525 }
    526 
    527 static struct reply_buffer *alloc_reply_buffer(
    528     XExtDisplayInfo *info,
    529     int nbytes)
    530 {
    531     struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
    532     struct reply_buffer *rbp;
    533     struct reply_buffer *saved_rb = NULL;
    534     /*
    535      * First look for an allocated buffer that is not in use.
    536      * If we have a big enough buffer, use that, otherwise
    537      * realloc an existing one.
    538      */
    539     for (rbp = cache->reply_buffers; rbp; rbp = rbp->next) {
    540 	if (rbp->ref_count == 0) {
    541 	    if (rbp->nbytes >= nbytes)
    542 		return rbp;
    543 	    else
    544 		saved_rb = rbp;
    545 	}
    546     }
    547     if (saved_rb) {
    548 	saved_rb->buf = (unsigned char *)Xrealloc(saved_rb->buf, nbytes);
    549 	if (!saved_rb->buf) {
    550 	    saved_rb->nbytes = 0;
    551 	    return NULL;
    552 	}
    553 	saved_rb->nbytes = nbytes;
    554 	return saved_rb;
    555     }
    556 
    557     /*
    558      * nothing available; malloc a new struct
    559      */
    560     rbp = Xmalloc(sizeof(struct reply_buffer));
    561     if (!rbp)
    562 	return NULL;
    563     rbp->buf = Xmalloc(nbytes);
    564     if (!rbp->buf) {
    565 	Xfree(rbp);
    566 	return NULL;
    567     }
    568     rbp->nbytes = nbytes;
    569     rbp->ref_count = 0;
    570     rbp->next = cache->reply_buffers;
    571     cache->reply_buffers = rbp;
    572     return rbp;
    573 }
    574 
    575 static XRecordInterceptData *alloc_inter_data(XExtDisplayInfo *info)
    576 {
    577     struct mem_cache_str *cache = (struct mem_cache_str *)info->data;
    578     struct intercept_queue *iq;
    579 
    580     /* if there is one on the free list, pop it */
    581     if (cache->inter_data) {
    582 	iq = cache->inter_data;
    583 	cache->inter_data = iq->next;
    584 	return &iq->data;
    585     }
    586     /* allocate a new one */
    587     iq = Xmalloc(sizeof(struct intercept_queue));
    588     if (!iq)
    589 	return NULL;
    590     iq->cache = cache;
    591     cache->inter_data_count++;
    592     return &iq->data;
    593 }
    594 
    595 void
    596 XRecordFreeData(XRecordInterceptData *data)
    597 {
    598     /* we can do this cast because that is what we really allocated */
    599     struct intercept_queue *iq = (struct intercept_queue *)data;
    600     struct reply_buffer *rbp = NULL;
    601     struct mem_cache_str *cache = iq->cache;
    602 
    603     /*
    604      * figure out what reply_buffer this points at
    605      * and decrement its ref_count.
    606      */
    607     if (data->data) {
    608 
    609 	for (rbp = cache->reply_buffers; rbp; rbp = rbp->next) {
    610 	    if (data->data >= rbp->buf
    611 		&& data->data < rbp->buf + rbp->nbytes)
    612 	    {
    613 		assert(rbp->ref_count > 0);
    614 		rbp->ref_count--;
    615 		break;
    616 	    }
    617 	}
    618 	/* it's an error if we didn't find something to free */
    619 	assert(rbp);
    620     }
    621     /*
    622      * If the display is still open, put this back on the free queue.
    623      *
    624      * Otherwise the display is closed and we won't reuse this, so free it.
    625      * See if we can free the reply buffer, too.
    626      * If we can, see if this is the last reply buffer and if so
    627      * free the list of reply buffers.
    628      */
    629     if (cache->display_closed == False) {
    630 	iq->next = cache->inter_data;
    631 	cache->inter_data = iq;
    632     } else {
    633 	if (rbp && rbp->ref_count == 0) {
    634 	    struct reply_buffer *rbp2, **rbp_next_p;
    635 
    636 	    /* Have to search the list again to find the prev element.
    637 	       This is not the common case, so don't slow down the code
    638 	       above by doing it then. */
    639 	    for (rbp_next_p = &cache->reply_buffers; *rbp_next_p; ) {
    640 		rbp2 = *rbp_next_p;
    641 		if (rbp == rbp2) {
    642 		    *rbp_next_p = rbp2->next;
    643 		    break;
    644 		} else {
    645 		    rbp_next_p = &rbp2->next;
    646 		}
    647 	    }
    648 	    XFree(rbp->buf);
    649 	    XFree(rbp);
    650 	}
    651 
    652 	XFree(iq);
    653 	cache->inter_data_count--;
    654 
    655 	if (cache->reply_buffers == NULL  &&  cache->inter_data_count == 0) {
    656 	    XFree(cache); /* all finished */
    657 	}
    658     }
    659 }
    660 
    661 /* the EXTRACT macros are adapted from ICElibint.h */
    662 
    663 #ifndef WORD64
    664 
    665 #define EXTRACT_CARD16(swap, src, dst) \
    666 { \
    667     (dst) = *((CARD16 *) (src)); \
    668     if (swap) \
    669         (dst) = lswaps (dst); \
    670 }
    671 
    672 #define EXTRACT_CARD32(swap, src, dst) \
    673 { \
    674     (dst) = *((CARD32 *) (src)); \
    675     if (swap) \
    676         (dst) = lswapl (dst); \
    677 }
    678 
    679 #else /* WORD64 */
    680 
    681 #define EXTRACT_CARD16(swap, src, dst) \
    682 { \
    683     (dst) = *((src) + 0); \
    684     (dst) <<= 8; \
    685     (dst) |= *((src) + 1); \
    686     if (swap) \
    687         (dst) = lswaps (dst); \
    688 }
    689 
    690 #define EXTRACT_CARD32(swap, src, dst) \
    691 { \
    692     (dst) = *((src) + 0); \
    693     (dst) <<= 8; \
    694     (dst) |= *((src) + 1); \
    695     (dst) <<= 8; \
    696     (dst) |= *((src) + 2); \
    697     (dst) <<= 8; \
    698     (dst) |= *((src) + 3); \
    699     if (swap) \
    700         (dst) = lswapl (dst); \
    701 }
    702 
    703 #endif /* WORD64 */
    704 
    705 /* byte swapping macros from xfs/include/misc.h */
    706 
    707 /* byte swap a long literal */
    708 #define lswapl(x) ((((x) & 0xff) << 24) |\
    709 		   (((x) & 0xff00) << 8) |\
    710 		   (((x) & 0xff0000) >> 8) |\
    711 		   (((x) >> 24) & 0xff))
    712 
    713 /* byte swap a short literal */
    714 #define lswaps(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
    715 
    716 enum parser_return { Continue, End, Error };
    717 
    718 static enum parser_return
    719 parse_reply_call_callback(
    720     Display *dpy,
    721     XExtDisplayInfo *info,
    722     xRecordEnableContextReply *rep,
    723     struct reply_buffer *reply,
    724     XRecordInterceptProc callback,
    725     XPointer		 closure)
    726 {
    727     XRecordInterceptData *data;
    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 	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 (reply == NULL)
    749 		goto out;
    750 	    if (rep->elementHeader&XRecordFromServerTime) {
    751 		if (current_index + 4 > rep->length << 2)
    752 		    return Error;
    753 		EXTRACT_CARD32(rep->clientSwapped,
    754 			       reply->buf+current_index,
    755 			       data->server_time);
    756 		current_index += 4;
    757 	    }
    758 	    if (current_index + 1 > rep->length << 2)
    759 		goto out;
    760 	    switch (reply->buf[current_index]) {
    761 	    case X_Reply: /* reply */
    762 		if (current_index + 8 > rep->length << 2)
    763 		    goto out;
    764 		EXTRACT_CARD32(rep->clientSwapped,
    765 			       reply->buf+current_index+4, datum_bytes);
    766 		if (datum_bytes < 0 || datum_bytes > ((INT_MAX >> 2) - 8))
    767 		    goto out;
    768 		datum_bytes = (datum_bytes+8) << 2;
    769 		break;
    770 	    default: /* error or event */
    771 		datum_bytes = 32;
    772 	    }
    773 	    break;
    774 	case XRecordFromClient:
    775 	    if (reply == NULL)
    776 		goto out;
    777 	    if (rep->elementHeader&XRecordFromClientTime) {
    778 		if (current_index + 4 > rep->length << 2)
    779 		    goto out;
    780 		EXTRACT_CARD32(rep->clientSwapped,
    781 			       reply->buf+current_index,
    782 			       data->server_time);
    783 		current_index += 4;
    784 	    }
    785 	    if (rep->elementHeader&XRecordFromClientSequence) {
    786 		if (current_index + 4 > rep->length << 2)
    787 		    goto out;
    788 		EXTRACT_CARD32(rep->clientSwapped,
    789 			       reply->buf+current_index,
    790 			       data->client_seq);
    791 		current_index += 4;
    792 	    }
    793 	    if (current_index + 4 > rep->length<<2)
    794 		goto out;
    795 	    if (reply->buf[current_index+2] == 0
    796 		&& reply->buf[current_index+3] == 0) /* needn't swap 0 */
    797 	    {	/* BIG-REQUESTS */
    798 		if (current_index + 8 > rep->length << 2)
    799 		    goto out;
    800 		EXTRACT_CARD32(rep->clientSwapped,
    801 			       reply->buf+current_index+4, datum_bytes);
    802 	    } else {
    803 		EXTRACT_CARD16(rep->clientSwapped,
    804 			       reply->buf+current_index+2, datum_bytes);
    805 	    }
    806 	    if (datum_bytes < 0 || datum_bytes > INT_MAX >> 2)
    807 		goto out;
    808 	    datum_bytes <<= 2;
    809 	    break;
    810 	case XRecordClientStarted:
    811 	    if (reply == NULL)
    812 		goto out;
    813 	    if (current_index + 8 > rep->length << 2)
    814 		goto out;
    815 	    EXTRACT_CARD16(rep->clientSwapped,
    816 			   reply->buf+current_index+6, datum_bytes);
    817 	    datum_bytes = (datum_bytes+2) << 2;
    818 	    break;
    819 	case XRecordClientDied:
    820 	    if (rep->elementHeader&XRecordFromClientSequence) {
    821 		if (reply == NULL)
    822 		    goto out;
    823 		if (current_index + 4 > rep->length << 2)
    824 		    goto out;
    825 		EXTRACT_CARD32(rep->clientSwapped,
    826 			       reply->buf+current_index,
    827 			       data->client_seq);
    828 		current_index += 4;
    829 	    } else if (current_index < rep->length << 2)
    830 		goto out;
    831 	    datum_bytes = 0;
    832 	    break;
    833 	case XRecordStartOfData:
    834 	case XRecordEndOfData:
    835 	    if (current_index < rep->length << 2)
    836 		goto out;
    837 	    datum_bytes = 0;
    838 	    break;
    839 	}
    840 
    841 	if (datum_bytes > 0) {
    842 	    if (INT_MAX - datum_bytes < (rep->length << 2) - current_index) {
    843 		fprintf(stderr,
    844 			"XRecord: %lu-byte reply claims %d-byte element (seq %lu)\n",
    845 			(unsigned long)rep->length << 2, current_index + datum_bytes,
    846 			dpy->last_request_read);
    847 		goto out;
    848 	    }
    849 	    /*
    850 	     * This assignment (and indeed the whole buffer sharing
    851 	     * scheme) assumes arbitrary 4-byte boundaries are
    852 	     * addressable.
    853 	     */
    854 	    data->data = reply->buf+current_index;
    855 	    reply->ref_count++;
    856 	} else {
    857 	    data->data = NULL;
    858 	}
    859 	data->data_len = datum_bytes >> 2;
    860 
    861 	(*callback)(closure, data);
    862 
    863 	current_index += datum_bytes;
    864     } while (current_index<rep->length<<2);
    865 
    866     if (rep->category == XRecordEndOfData)
    867 	return End;
    868 
    869     return Continue;
    870 out:
    871     Xfree(data);
    872     return Error;
    873 }
    874 
    875 Status
    876 XRecordEnableContext(Display *dpy, XRecordContext context,
    877 		     XRecordInterceptProc callback, XPointer closure)
    878 {
    879     XExtDisplayInfo *info = find_display (dpy);
    880     register xRecordEnableContextReq   	*req;
    881     xRecordEnableContextReply 	rep;
    882     struct reply_buffer *reply;
    883 
    884     XRecordCheckExtension (dpy, info, 0);
    885     LockDisplay(dpy);
    886     GetReq(RecordEnableContext, req);
    887 
    888     req->reqType = info->codes->major_opcode;
    889     req->recordReqType = X_RecordEnableContext;
    890     req->context = context;
    891 
    892     while (1)
    893     {
    894 	enum parser_return status;
    895 
    896 	/* This code should match that in XRecordEnableContextAsync */
    897 	if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
    898 	{
    899 	    UnlockDisplay(dpy);
    900 	    SyncHandle();
    901 	    return 0;
    902 	}
    903 
    904 	if (rep.length > INT_MAX >> 2) {
    905 	    UnlockDisplay(dpy);
    906 	    SyncHandle();
    907 	    return 0;
    908 	}
    909 
    910 	if (rep.length > 0) {
    911 	    reply = alloc_reply_buffer(info, rep.length<<2);
    912 	    if (!reply) {
    913 		UnlockDisplay(dpy);
    914 		SyncHandle();
    915 		return 0;
    916 	    }
    917 	    _XRead (dpy, (char *)reply->buf, rep.length<<2);
    918 	} else {
    919 	    reply = NULL;
    920 	}
    921 
    922 	status = parse_reply_call_callback(dpy, info, &rep, reply,
    923 					   callback, closure);
    924 	switch (status) {
    925 	case Continue:
    926 	    break;
    927 	case End:
    928 	    UnlockDisplay(dpy);
    929 	    SyncHandle();
    930 	    return 1;
    931 	case Error:
    932 	    UnlockDisplay(dpy);
    933 	    SyncHandle();
    934 	    return 0;
    935 	}
    936     }
    937 }
    938 
    939 
    940 typedef struct _record_async_state
    941 {
    942     unsigned long enable_seq;
    943     _XAsyncHandler *async;
    944     _XAsyncErrorState *error_state;
    945     XExtDisplayInfo *info;
    946     XRecordInterceptProc callback;
    947     XPointer closure;
    948 } record_async_state;
    949 
    950 static Bool
    951 record_async_handler(
    952     register Display *dpy,
    953     register xReply *rep,
    954     char *buf,
    955     int len,
    956     XPointer adata)
    957 {
    958     register record_async_state *state = (record_async_state *)adata;
    959     struct reply_buffer *reply;
    960     enum parser_return status;
    961 
    962     if (dpy->last_request_read != state->enable_seq)
    963     {
    964 	if (dpy->last_request_read > state->enable_seq) {
    965 	    /* it is an error that we are still on the handler list */
    966 	    fprintf(stderr, "XRecord: handler for seq %lu never saw XRecordEndOfData.  (seq now %lu)\n",
    967 		    state->enable_seq, dpy->last_request_read);
    968 	    DeqAsyncHandler(dpy, state->async);
    969 	    Xfree(state->async);
    970 	}
    971 	return False;
    972     }
    973     if (rep->generic.type == X_Error)
    974     {
    975 	DeqAsyncHandler(dpy, state->async);
    976 	Xfree(state->async);
    977 	return False;
    978     }
    979 
    980     if (rep->generic.length > 0) {
    981 	reply = alloc_reply_buffer(state->info, rep->generic.length<<2);
    982 
    983 	if (!reply) {
    984 	    DeqAsyncHandler(dpy, state->async);
    985 	    Xfree(state->async);
    986 	    return False;
    987 	}
    988 
    989 	_XGetAsyncData(dpy, (char *)reply->buf, buf, len,
    990 		       SIZEOF(xRecordEnableContextReply),
    991 		       rep->generic.length << 2, 0);
    992     } else {
    993 	reply = NULL;
    994     }
    995 
    996     status = parse_reply_call_callback(dpy, state->info,
    997 				       (xRecordEnableContextReply*) rep,
    998 				       reply, state->callback, state->closure);
    999 
   1000     if (status != Continue)
   1001     {
   1002 	DeqAsyncHandler(dpy, state->async);
   1003 	Xfree(state->async);
   1004 	if (status == Error)
   1005 	    return False;
   1006     }
   1007 
   1008     return True;
   1009 }
   1010 
   1011 /*
   1012  * reads the first reply, StartOfData, synchronously,
   1013  * then returns allowing the app to call XRecordProcessReplies
   1014  * to get the rest.
   1015  */
   1016 Status
   1017 XRecordEnableContextAsync(Display *dpy, XRecordContext context,
   1018 			  XRecordInterceptProc callback, XPointer closure)
   1019 {
   1020     XExtDisplayInfo *info = find_display (dpy);
   1021     register xRecordEnableContextReq *req;
   1022     xRecordEnableContextReply rep;
   1023     struct reply_buffer *reply;
   1024     enum parser_return status;
   1025     _XAsyncHandler *async;
   1026     record_async_state *async_state;
   1027 
   1028     XRecordCheckExtension (dpy, info, 0);
   1029     async = Xmalloc(sizeof(_XAsyncHandler) + sizeof(record_async_state));
   1030     if (!async)
   1031 	return 0;
   1032     async_state = (record_async_state *)(async + 1);
   1033 
   1034     LockDisplay(dpy);
   1035     GetReq(RecordEnableContext, req);
   1036 
   1037     req->reqType = info->codes->major_opcode;
   1038     req->recordReqType = X_RecordEnableContext;
   1039     req->context = context;
   1040 
   1041     /* Get the StartOfData reply. */
   1042     /* This code should match that in XRecordEnableContext */
   1043     if (!_XReply (dpy, (xReply *)&rep, 0, xFalse))
   1044     {
   1045 	UnlockDisplay(dpy);
   1046 	SyncHandle();
   1047 	Xfree(async);
   1048 	return 0;
   1049     }
   1050 
   1051     /* this had better be a StartOfData, which has no extra data. */
   1052     if (rep.length != 0) {
   1053 	fprintf(stderr, "XRecord: malformed StartOfData for sequence %lu\n",
   1054 		dpy->last_request_read);
   1055     }
   1056     reply = NULL;
   1057 
   1058     status = parse_reply_call_callback(dpy, info, &rep, reply,
   1059 				       callback, closure);
   1060     if (status != Continue)
   1061     {
   1062 	UnlockDisplay(dpy);
   1063 	Xfree(async);
   1064 	return 0;
   1065     }
   1066 
   1067     /* hook in the async handler for the rest of the replies */
   1068     async_state->enable_seq = dpy->request;
   1069     async_state->async = async;
   1070     async_state->info = info;
   1071     async_state->callback = callback;
   1072     async_state->closure = closure;
   1073 
   1074     async->next = dpy->async_handlers;
   1075     async->handler = record_async_handler;
   1076     async->data = (XPointer)async_state;
   1077     dpy->async_handlers = async;
   1078 
   1079     UnlockDisplay(dpy);
   1080     /* Don't invoke SyncHandle here, since this is an async
   1081        function.  Does this break XSetAfterFunction() ? */
   1082     return 1;
   1083 }
   1084 
   1085 void
   1086 XRecordProcessReplies(Display *dpy)
   1087 {
   1088     (void) XPending(dpy);
   1089 }
   1090 
   1091 Status
   1092 XRecordDisableContext(Display *dpy, XRecordContext context)
   1093 {
   1094     XExtDisplayInfo *info = find_display (dpy);
   1095     register xRecordDisableContextReq 	*req;
   1096 
   1097     XRecordCheckExtension (dpy, info, 0);
   1098     LockDisplay(dpy);
   1099     GetReq(RecordDisableContext, req);
   1100     req->reqType = info->codes->major_opcode;
   1101     req->recordReqType = X_RecordDisableContext;
   1102     req->context =  context;
   1103 
   1104     UnlockDisplay(dpy);
   1105     SyncHandle();
   1106     return 1;
   1107 }
   1108 
   1109 Status
   1110 XRecordFreeContext(Display *dpy, XRecordContext context)
   1111 {
   1112     XExtDisplayInfo *info = find_display (dpy);
   1113     register xRecordFreeContextReq 	*req;
   1114 
   1115     XRecordCheckExtension (dpy, info, 0);
   1116 
   1117     LockDisplay(dpy);
   1118     GetReq(RecordFreeContext, req);
   1119     req->reqType = info->codes->major_opcode;
   1120     req->recordReqType = X_RecordFreeContext;
   1121     req->context = context;
   1122 
   1123     UnlockDisplay(dpy);
   1124     SyncHandle();
   1125     return 1;
   1126 }
   1127