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