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