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