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