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