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