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