1/*********************************************************** 2Copyright (c) 1993, Oracle and/or its affiliates. 3 4Permission is hereby granted, free of charge, to any person obtaining a 5copy of this software and associated documentation files (the "Software"), 6to deal in the Software without restriction, including without limitation 7the rights to use, copy, modify, merge, publish, distribute, sublicense, 8and/or sell copies of the Software, and to permit persons to whom the 9Software is furnished to do so, subject to the following conditions: 10 11The above copyright notice and this permission notice (including the next 12paragraph) shall be included in all copies or substantial portions of the 13Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21DEALINGS IN THE SOFTWARE. 22 23Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. 24 25 All Rights Reserved 26 27Permission to use, copy, modify, and distribute this software and its 28documentation for any purpose and without fee is hereby granted, 29provided that the above copyright notice appear in all copies and that 30both that copyright notice and this permission notice appear in 31supporting documentation, and that the name of Digital not be 32used in advertising or publicity pertaining to distribution of the 33software without specific, written prior permission. 34 35DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 36ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 37DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 38ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 39WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 40ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 41SOFTWARE. 42 43******************************************************************/ 44 45/* 46 47Copyright 1987, 1988, 1994, 1998 The Open Group 48 49Permission to use, copy, modify, distribute, and sell this software and its 50documentation for any purpose is hereby granted without fee, provided that 51the above copyright notice appear in all copies and that both that 52copyright notice and this permission notice appear in supporting 53documentation. 54 55The above copyright notice and this permission notice shall be included in 56all copies or substantial portions of the Software. 57 58THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 62AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 63CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 64 65Except as contained in this notice, the name of The Open Group shall not be 66used in advertising or otherwise to promote the sale, use or other dealings 67in this Software without prior written authorization from The Open Group. 68 69*/ 70 71#ifdef HAVE_CONFIG_H 72#include <config.h> 73#endif 74#include "IntrinsicI.h" 75#include "StringDefs.h" 76#include "SelectionI.h" 77#include <X11/Xatom.h> 78#include <stdio.h> 79 80void 81_XtSetDefaultSelectionTimeout(unsigned long *timeout) 82{ 83 *timeout = 5000; /* default to 5 seconds */ 84} 85 86void 87XtSetSelectionTimeout(unsigned long timeout) 88{ 89 XtAppSetSelectionTimeout(_XtDefaultAppContext(), timeout); 90} 91 92void 93XtAppSetSelectionTimeout(XtAppContext app, unsigned long timeout) 94{ 95 LOCK_APP(app); 96 app->selectionTimeout = timeout; 97 UNLOCK_APP(app); 98} 99 100unsigned long 101XtGetSelectionTimeout(void) 102{ 103 return XtAppGetSelectionTimeout(_XtDefaultAppContext()); 104} 105 106unsigned long 107XtAppGetSelectionTimeout(XtAppContext app) 108{ 109 unsigned long retval; 110 111 LOCK_APP(app); 112 retval = app->selectionTimeout; 113 UNLOCK_APP(app); 114 return retval; 115} 116 117/* General utilities */ 118 119static void HandleSelectionReplies(Widget, XtPointer, XEvent *, Boolean *); 120static void ReqTimedOut(XtPointer, XtIntervalId *); 121static void HandlePropertyGone(Widget, XtPointer, XEvent *, Boolean *); 122static void HandleGetIncrement(Widget, XtPointer, XEvent *, Boolean *); 123static void HandleIncremental(Display *, Widget, Atom, CallBackInfo, 124 unsigned long); 125 126static XContext selectPropertyContext = 0; 127static XContext paramPropertyContext = 0; 128static XContext multipleContext = 0; 129 130/* Multiple utilities */ 131static void AddSelectionRequests(Widget, Atom, int, Atom *, 132 XtSelectionCallbackProc *, int, XtPointer *, 133 Boolean *, Atom *); 134static Boolean IsGatheringRequest(Widget, Atom); 135 136#define PREALLOCED 32 137 138/* Parameter utilities */ 139static void AddParamInfo(Widget, Atom, Atom); 140static void RemoveParamInfo(Widget, Atom); 141static Atom GetParamInfo(Widget, Atom); 142 143static int StorageSize[3] = { 1, sizeof(short), sizeof(long) }; 144 145#define BYTELENGTH(length, format) ((length) * (size_t)StorageSize[(format)>>4]) 146#define NUMELEM(bytelength, format) ((bytelength) / StorageSize[(format)>>4]) 147#define NUMELEM2(bytelength, format) ((unsigned long)(bytelength) / (unsigned long) StorageSize[(format)>>4]) 148 149/* Xlib and Xt are permitted to have different memory allocators, and in the 150 * XtSelectionCallbackProc the client is instructed to free the selection 151 * value with XtFree, so the selection value received from XGetWindowProperty 152 * should be copied to memory allocated through Xt. But copying is 153 * undesirable since the selection value may be large, and, under normal 154 * library configuration copying is unnecessary. 155 */ 156#ifdef XTTRACEMEMORY 157#define XT_COPY_SELECTION 1 158#endif 159 160static void 161FreePropList(Widget w _X_UNUSED, 162 XtPointer closure, 163 XtPointer callData _X_UNUSED) 164{ 165 PropList sarray = (PropList) closure; 166 167 LOCK_PROCESS; 168 XDeleteContext(sarray->dpy, DefaultRootWindow(sarray->dpy), 169 selectPropertyContext); 170 UNLOCK_PROCESS; 171 XtFree((char *) sarray->list); 172 XtFree((char *) closure); 173} 174 175static PropList 176GetPropList(Display *dpy) 177{ 178 PropList sarray; 179 180 LOCK_PROCESS; 181 if (selectPropertyContext == 0) 182 selectPropertyContext = XUniqueContext(); 183 if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext, 184 (XPointer *) &sarray)) { 185 Atom atoms[4]; 186 187 static char *names[] = { 188 "INCR", 189 "MULTIPLE", 190 "TIMESTAMP", 191 "_XT_SELECTION_0" 192 }; 193 194 XtPerDisplay pd = _XtGetPerDisplay(dpy); 195 196 sarray = (PropList) __XtMalloc((unsigned) sizeof(PropListRec)); 197 sarray->dpy = dpy; 198 XInternAtoms(dpy, names, 4, FALSE, atoms); 199 sarray->incr_atom = atoms[0]; 200 sarray->indirect_atom = atoms[1]; 201 sarray->timestamp_atom = atoms[2]; 202 sarray->propCount = 1; 203 sarray->list = 204 (SelectionProp) __XtMalloc((unsigned) sizeof(SelectionPropRec)); 205 sarray->list[0].prop = atoms[3]; 206 sarray->list[0].avail = TRUE; 207 (void) XSaveContext(dpy, DefaultRootWindow(dpy), selectPropertyContext, 208 (char *) sarray); 209 _XtAddCallback(&pd->destroy_callbacks, 210 FreePropList, (XtPointer) sarray); 211 } 212 UNLOCK_PROCESS; 213 return sarray; 214} 215 216static Atom 217GetSelectionProperty(Display *dpy) 218{ 219 SelectionProp p; 220 int propCount; 221 char propname[80]; 222 PropList sarray = GetPropList(dpy); 223 224 for (p = sarray->list, propCount = sarray->propCount; 225 propCount; p++, propCount--) { 226 if (p->avail) { 227 p->avail = FALSE; 228 return (p->prop); 229 } 230 } 231 propCount = sarray->propCount++; 232 sarray->list = XtReallocArray(sarray->list, (Cardinal) sarray->propCount, 233 (Cardinal) sizeof(SelectionPropRec)); 234 (void) snprintf(propname, sizeof(propname), "_XT_SELECTION_%d", propCount); 235 sarray->list[propCount].prop = XInternAtom(dpy, propname, FALSE); 236 sarray->list[propCount].avail = FALSE; 237 return (sarray->list[propCount].prop); 238} 239 240static void 241FreeSelectionProperty(Display *dpy, Atom prop) 242{ 243 SelectionProp p; 244 int propCount; 245 PropList sarray; 246 247 if (prop == None) 248 return; 249 LOCK_PROCESS; 250 if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext, 251 (XPointer *) &sarray)) 252 XtAppErrorMsg(XtDisplayToApplicationContext(dpy), 253 "noSelectionProperties", "freeSelectionProperty", 254 XtCXtToolkitError, 255 "internal error: no selection property context for display", 256 NULL, NULL); 257 UNLOCK_PROCESS; 258 for (p = sarray->list, propCount = sarray->propCount; 259 propCount; p++, propCount--) 260 if (p->prop == prop) { 261 p->avail = TRUE; 262 return; 263 } 264} 265 266static void 267FreeInfo(CallBackInfo info) 268{ 269 XtFree((char *) info->incremental); 270 XtFree((char *) info->callbacks); 271 XtFree((char *) info->req_closure); 272 XtFree((char *) info->target); 273 XtFree((char *) info); 274} 275 276static CallBackInfo 277MakeInfo(Select ctx, 278 XtSelectionCallbackProc *callbacks, 279 XtPointer *closures, 280 int count, 281 Widget widget, 282 Time time, 283 Boolean *incremental, 284 Atom *properties) 285{ 286 CallBackInfo info = XtNew(CallBackInfoRec); 287 288 info->ctx = ctx; 289 info->callbacks = XtMallocArray((Cardinal) count, 290 (Cardinal) sizeof(XtSelectionCallbackProc)); 291 (void) memcpy(info->callbacks, callbacks, 292 (size_t) count * sizeof(XtSelectionCallbackProc)); 293 info->req_closure = XtMallocArray((Cardinal) count, 294 (Cardinal) sizeof(XtPointer)); 295 (void) memcpy(info->req_closure, closures, 296 (size_t) count * sizeof(XtPointer)); 297 if (count == 1 && properties != NULL && properties[0] != None) 298 info->property = properties[0]; 299 else { 300 info->property = GetSelectionProperty(XtDisplay(widget)); 301 XDeleteProperty(XtDisplay(widget), XtWindow(widget), info->property); 302 } 303 info->proc = HandleSelectionReplies; 304 info->widget = widget; 305 info->time = time; 306 info->incremental = XtMallocArray((Cardinal) count, 307 (Cardinal) sizeof(Boolean)); 308 (void) memcpy(info->incremental, incremental, 309 (size_t) count * sizeof(Boolean)); 310 info->current = 0; 311 info->value = NULL; 312 return (info); 313} 314 315static void 316RequestSelectionValue(CallBackInfo info, Atom selection, Atom target) 317{ 318#ifndef DEBUG_WO_TIMERS 319 XtAppContext app = XtWidgetToApplicationContext(info->widget); 320 321 info->timeout = XtAppAddTimeOut(app, 322 app->selectionTimeout, ReqTimedOut, 323 (XtPointer) info); 324#endif 325 XtAddEventHandler(info->widget, (EventMask) 0, TRUE, 326 HandleSelectionReplies, (XtPointer) info); 327 XConvertSelection(info->ctx->dpy, selection, target, 328 info->property, XtWindow(info->widget), info->time); 329} 330 331static XContext selectContext = 0; 332 333static Select 334NewContext(Display *dpy, Atom selection) 335{ 336 /* assert(selectContext != 0) */ 337 Select ctx = XtNew(SelectRec); 338 339 ctx->dpy = dpy; 340 ctx->selection = selection; 341 ctx->widget = NULL; 342 ctx->prop_list = GetPropList(dpy); 343 ctx->ref_count = 0; 344 ctx->free_when_done = FALSE; 345 ctx->was_disowned = FALSE; 346 LOCK_PROCESS; 347 (void) XSaveContext(dpy, (Window) selection, selectContext, (char *) ctx); 348 UNLOCK_PROCESS; 349 return ctx; 350} 351 352static Select 353FindCtx(Display *dpy, Atom selection) 354{ 355 Select ctx; 356 357 LOCK_PROCESS; 358 if (selectContext == 0) 359 selectContext = XUniqueContext(); 360 if (XFindContext(dpy, (Window) selection, selectContext, (XPointer *) &ctx)) 361 ctx = NewContext(dpy, selection); 362 UNLOCK_PROCESS; 363 return ctx; 364} 365 366static void 367WidgetDestroyed(Widget widget, XtPointer closure, XtPointer data _X_UNUSED) 368{ 369 Select ctx = (Select) closure; 370 371 if (ctx->widget == widget) { 372 if (ctx->free_when_done) 373 XtFree((char *) ctx); 374 else 375 ctx->widget = NULL; 376 } 377} 378 379/* Selection Owner code */ 380 381static void HandleSelectionEvents(Widget, XtPointer, XEvent *, Boolean *); 382 383static Boolean 384LoseSelection(Select ctx, Widget widget, Atom selection, Time time) 385{ 386 if ((ctx->widget == widget) && (ctx->selection == selection) && /* paranoia */ 387 !ctx->was_disowned && ((time == CurrentTime) || (time >= ctx->time))) { 388 XtRemoveEventHandler(widget, (EventMask) 0, TRUE, 389 HandleSelectionEvents, (XtPointer) ctx); 390 XtRemoveCallback(widget, XtNdestroyCallback, 391 WidgetDestroyed, (XtPointer) ctx); 392 ctx->was_disowned = TRUE; /* widget officially loses ownership */ 393 /* now inform widget */ 394 if (ctx->loses) { 395 if (ctx->incremental) 396 (*(XtLoseSelectionIncrProc) ctx->loses) 397 (widget, &ctx->selection, ctx->owner_closure); 398 else 399 (*ctx->loses) (widget, &ctx->selection); 400 } 401 return (TRUE); 402 } 403 else 404 return (FALSE); 405} 406 407static XContext selectWindowContext = 0; 408 409/* %%% Xlib.h should make this public! */ 410typedef int (*xErrorHandler) (Display *, XErrorEvent *); 411 412static xErrorHandler oldErrorHandler = NULL; 413static unsigned long firstProtectRequest; 414static Window errorWindow; 415 416static int 417LocalErrorHandler(Display *dpy, XErrorEvent *error) 418{ 419 int retval; 420 421 /* If BadWindow error on selection requestor, nothing to do but let 422 * the transfer timeout. Otherwise, invoke saved error handler. */ 423 424 LOCK_PROCESS; 425 426 if (error->error_code == BadWindow && error->resourceid == errorWindow && 427 error->serial >= firstProtectRequest) { 428 UNLOCK_PROCESS; 429 return 0; 430 } 431 432 if (oldErrorHandler == NULL) { 433 UNLOCK_PROCESS; 434 return 0; /* should never happen */ 435 } 436 437 retval = (*oldErrorHandler) (dpy, error); 438 UNLOCK_PROCESS; 439 return retval; 440} 441 442static void 443StartProtectedSection(Display *dpy, Window window) 444{ 445 /* protect ourselves against request window being destroyed 446 * before completion of transfer */ 447 448 LOCK_PROCESS; 449 oldErrorHandler = XSetErrorHandler(LocalErrorHandler); 450 firstProtectRequest = NextRequest(dpy); 451 errorWindow = window; 452 UNLOCK_PROCESS; 453} 454 455static void 456EndProtectedSection(Display *dpy) 457{ 458 /* flush any generated errors on requestor and 459 * restore original error handler */ 460 461 XSync(dpy, False); 462 463 LOCK_PROCESS; 464 XSetErrorHandler(oldErrorHandler); 465 oldErrorHandler = NULL; 466 UNLOCK_PROCESS; 467} 468 469static void 470AddHandler(Request req, EventMask mask, XtEventHandler proc, XtPointer closure) 471{ 472 Display *dpy = req->ctx->dpy; 473 Window window = req->requestor; 474 Widget widget = XtWindowToWidget(dpy, window); 475 476 if (widget != NULL) 477 req->widget = widget; 478 else 479 widget = req->widget; 480 481 if (XtWindow(widget) == window) 482 XtAddEventHandler(widget, mask, False, proc, closure); 483 else { 484 RequestWindowRec *requestWindowRec; 485 486 LOCK_PROCESS; 487 if (selectWindowContext == 0) 488 selectWindowContext = XUniqueContext(); 489 if (XFindContext(dpy, window, selectWindowContext, 490 (XPointer *) &requestWindowRec)) { 491 requestWindowRec = XtNew(RequestWindowRec); 492 requestWindowRec->active_transfer_count = 0; 493 (void) XSaveContext(dpy, window, selectWindowContext, 494 (char *) requestWindowRec); 495 } 496 UNLOCK_PROCESS; 497 if (requestWindowRec->active_transfer_count++ == 0) { 498 XtRegisterDrawable(dpy, window, widget); 499 XSelectInput(dpy, window, (long) mask); 500 } 501 XtAddRawEventHandler(widget, mask, FALSE, proc, closure); 502 } 503} 504 505static void 506RemoveHandler(Request req, 507 EventMask mask, 508 XtEventHandler proc, 509 XtPointer closure) 510{ 511 Display *dpy = req->ctx->dpy; 512 Window window = req->requestor; 513 Widget widget = req->widget; 514 515 if ((XtWindowToWidget(dpy, window) == widget) && 516 (XtWindow(widget) != window)) { 517 /* we had to hang this window onto our widget; take it off */ 518 RequestWindowRec *requestWindowRec; 519 520 XtRemoveRawEventHandler(widget, mask, TRUE, proc, closure); 521 LOCK_PROCESS; 522 (void) XFindContext(dpy, window, selectWindowContext, 523 (XPointer *) &requestWindowRec); 524 UNLOCK_PROCESS; 525 if (--requestWindowRec->active_transfer_count == 0) { 526 XtUnregisterDrawable(dpy, window); 527 StartProtectedSection(dpy, window); 528 XSelectInput(dpy, window, 0L); 529 EndProtectedSection(dpy); 530 LOCK_PROCESS; 531 (void) XDeleteContext(dpy, window, selectWindowContext); 532 UNLOCK_PROCESS; 533 XtFree((char *) requestWindowRec); 534 } 535 } 536 else { 537 XtRemoveEventHandler(widget, mask, TRUE, proc, closure); 538 } 539} 540 541static void 542OwnerTimedOut(XtPointer closure, XtIntervalId *id _X_UNUSED) 543{ 544 Request req = (Request) closure; 545 Select ctx = req->ctx; 546 547 if (ctx->incremental && (ctx->owner_cancel != NULL)) { 548 (*ctx->owner_cancel) (ctx->widget, &ctx->selection, 549 &req->target, (XtRequestId *) &req, 550 ctx->owner_closure); 551 } 552 else { 553 if (ctx->notify == NULL) 554 XtFree((char *) req->value); 555 else { 556 /* the requestor hasn't deleted the property, but 557 * the owner needs to free the value. 558 */ 559 if (ctx->incremental) 560 (*(XtSelectionDoneIncrProc) ctx->notify) 561 (ctx->widget, &ctx->selection, &req->target, 562 (XtRequestId *) &req, ctx->owner_closure); 563 else 564 (*ctx->notify) (ctx->widget, &ctx->selection, &req->target); 565 } 566 } 567 568 RemoveHandler(req, (EventMask) PropertyChangeMask, 569 HandlePropertyGone, closure); 570 XtFree((char *) req); 571 if (--ctx->ref_count == 0 && ctx->free_when_done) 572 XtFree((char *) ctx); 573} 574 575static void 576SendIncrement(Request incr) 577{ 578 Display *dpy = incr->ctx->dpy; 579 580 unsigned long incrSize = (unsigned long) MAX_SELECTION_INCR(dpy); 581 582 if (incrSize > incr->bytelength - incr->offset) 583 incrSize = incr->bytelength - incr->offset; 584 StartProtectedSection(dpy, incr->requestor); 585 XChangeProperty(dpy, incr->requestor, incr->property, 586 incr->type, incr->format, PropModeReplace, 587 (unsigned char *) incr->value + incr->offset, 588 NUMELEM((int) incrSize, incr->format)); 589 EndProtectedSection(dpy); 590 incr->offset += incrSize; 591} 592 593static void 594AllSent(Request req) 595{ 596 Select ctx = req->ctx; 597 598 StartProtectedSection(ctx->dpy, req->requestor); 599 XChangeProperty(ctx->dpy, req->requestor, 600 req->property, req->type, req->format, 601 PropModeReplace, (unsigned char *) NULL, 0); 602 EndProtectedSection(ctx->dpy); 603 req->allSent = TRUE; 604 605 if (ctx->notify == NULL) 606 XtFree((char *) req->value); 607} 608 609static void 610HandlePropertyGone(Widget widget _X_UNUSED, 611 XtPointer closure, 612 XEvent *ev, 613 Boolean *cont _X_UNUSED) 614{ 615 XPropertyEvent *event = (XPropertyEvent *) ev; 616 Request req = (Request) closure; 617 Select ctx = req->ctx; 618 619 if ((event->type != PropertyNotify) || 620 (event->state != PropertyDelete) || 621 (event->atom != req->property) || (event->window != req->requestor)) 622 return; 623#ifndef DEBUG_WO_TIMERS 624 XtRemoveTimeOut(req->timeout); 625#endif 626 if (req->allSent) { 627 if (ctx->notify) { 628 if (ctx->incremental) { 629 (*(XtSelectionDoneIncrProc) ctx->notify) 630 (ctx->widget, &ctx->selection, &req->target, 631 (XtRequestId *) &req, ctx->owner_closure); 632 } 633 else 634 (*ctx->notify) (ctx->widget, &ctx->selection, &req->target); 635 } 636 RemoveHandler(req, (EventMask) PropertyChangeMask, 637 HandlePropertyGone, closure); 638 XtFree((char *) req); 639 if (--ctx->ref_count == 0 && ctx->free_when_done) 640 XtFree((char *) ctx); 641 } 642 else { /* is this part of an incremental transfer? */ 643 if (ctx->incremental) { 644 if (req->bytelength == 0) 645 AllSent(req); 646 else { 647 unsigned long size = 648 (unsigned long) MAX_SELECTION_INCR(ctx->dpy); 649 SendIncrement(req); 650 (*(XtConvertSelectionIncrProc) ctx->convert) 651 (ctx->widget, &ctx->selection, &req->target, 652 &req->type, &req->value, 653 &req->bytelength, &req->format, 654 &size, ctx->owner_closure, (XtPointer *) &req); 655 if (req->bytelength) 656 req->bytelength = BYTELENGTH(req->bytelength, req->format); 657 req->offset = 0; 658 } 659 } 660 else { 661 if (req->offset < req->bytelength) 662 SendIncrement(req); 663 else 664 AllSent(req); 665 } 666#ifndef DEBUG_WO_TIMERS 667 { 668 XtAppContext app = XtWidgetToApplicationContext(req->widget); 669 670 req->timeout = XtAppAddTimeOut(app, 671 app->selectionTimeout, OwnerTimedOut, 672 (XtPointer) req); 673 } 674#endif 675 } 676} 677 678static void 679PrepareIncremental(Request req, 680 Widget widget, 681 Window window, 682 Atom property _X_UNUSED, 683 Atom target, 684 Atom targetType, 685 XtPointer value, 686 unsigned long length, 687 int format) 688{ 689 req->type = targetType; 690 req->value = value; 691 req->bytelength = BYTELENGTH(length, format); 692 req->format = format; 693 req->offset = 0; 694 req->target = target; 695 req->widget = widget; 696 req->allSent = FALSE; 697#ifndef DEBUG_WO_TIMERS 698 { 699 XtAppContext app = XtWidgetToApplicationContext(widget); 700 701 req->timeout = XtAppAddTimeOut(app, 702 app->selectionTimeout, OwnerTimedOut, 703 (XtPointer) req); 704 } 705#endif 706 AddHandler(req, (EventMask) PropertyChangeMask, 707 HandlePropertyGone, (XtPointer) req); 708/* now send client INCR property */ 709 XChangeProperty(req->ctx->dpy, window, req->property, 710 req->ctx->prop_list->incr_atom, 711 32, PropModeReplace, (unsigned char *) &req->bytelength, 1); 712} 713 714static Boolean 715GetConversion(Select ctx, /* logical owner */ 716 XSelectionRequestEvent *event, 717 Atom target, 718 Atom property, /* requestor's property */ 719 Widget widget) /* physical owner (receives events) */ 720{ 721 XtPointer value = NULL; 722 unsigned long length; 723 int format; 724 Atom targetType; 725 Request req = XtNew(RequestRec); 726 Boolean timestamp_target = (target == ctx->prop_list->timestamp_atom); 727 728 req->ctx = ctx; 729 req->event = *event; 730 req->property = property; 731 req->requestor = event->requestor; 732 733 if (timestamp_target) { 734 value = __XtMalloc(sizeof(long)); 735 *(long *) value = (long) ctx->time; 736 targetType = XA_INTEGER; 737 length = 1; 738 format = 32; 739 } 740 else { 741 ctx->ref_count++; 742 if (ctx->incremental == TRUE) { 743 unsigned long size = (unsigned long) MAX_SELECTION_INCR(ctx->dpy); 744 745 if ((*(XtConvertSelectionIncrProc) ctx->convert) 746 (ctx->widget, &event->selection, &target, 747 &targetType, &value, &length, &format, 748 &size, ctx->owner_closure, (XtRequestId *) &req) 749 == FALSE) { 750 XtFree((char *) req); 751 ctx->ref_count--; 752 return (FALSE); 753 } 754 StartProtectedSection(ctx->dpy, event->requestor); 755 PrepareIncremental(req, widget, event->requestor, property, 756 target, targetType, value, length, format); 757 return (TRUE); 758 } 759 ctx->req = req; 760 if ((*ctx->convert) (ctx->widget, &event->selection, &target, 761 &targetType, &value, &length, &format) == FALSE) { 762 XtFree((char *) req); 763 ctx->req = NULL; 764 ctx->ref_count--; 765 return (FALSE); 766 } 767 ctx->req = NULL; 768 } 769 StartProtectedSection(ctx->dpy, event->requestor); 770 if (BYTELENGTH(length, format) <= 771 (unsigned long) MAX_SELECTION_INCR(ctx->dpy)) { 772 if (!timestamp_target) { 773 if (ctx->notify != NULL) { 774 req->target = target; 775 req->widget = widget; 776 req->allSent = TRUE; 777#ifndef DEBUG_WO_TIMERS 778 { 779 XtAppContext app = 780 XtWidgetToApplicationContext(req->widget); 781 req->timeout = 782 XtAppAddTimeOut(app, app->selectionTimeout, 783 OwnerTimedOut, (XtPointer) req); 784 } 785#endif 786 AddHandler(req, (EventMask) PropertyChangeMask, 787 HandlePropertyGone, (XtPointer) req); 788 } 789 else 790 ctx->ref_count--; 791 } 792 XChangeProperty(ctx->dpy, event->requestor, property, 793 targetType, format, PropModeReplace, 794 (unsigned char *) value, (int) length); 795 /* free storage for client if no notify proc */ 796 if (timestamp_target || ctx->notify == NULL) { 797 XtFree((char *) value); 798 XtFree((char *) req); 799 } 800 } 801 else { 802 PrepareIncremental(req, widget, event->requestor, property, 803 target, targetType, value, length, format); 804 } 805 return (TRUE); 806} 807 808static void 809HandleSelectionEvents(Widget widget, 810 XtPointer closure, 811 XEvent *event, 812 Boolean *cont _X_UNUSED) 813{ 814 Select ctx; 815 XSelectionEvent ev; 816 Atom target; 817 818 ctx = (Select) closure; 819 switch (event->type) { 820 case SelectionClear: 821 /* if this event is not for the selection we registered for, 822 * don't do anything */ 823 if (ctx->selection != event->xselectionclear.selection || 824 ctx->serial > event->xselectionclear.serial) 825 break; 826 (void) LoseSelection(ctx, widget, event->xselectionclear.selection, 827 event->xselectionclear.time); 828 break; 829 case SelectionRequest: 830 /* if this event is not for the selection we registered for, 831 * don't do anything */ 832 if (ctx->selection != event->xselectionrequest.selection) 833 break; 834 ev.type = SelectionNotify; 835 ev.display = event->xselectionrequest.display; 836 837 ev.requestor = event->xselectionrequest.requestor; 838 ev.selection = event->xselectionrequest.selection; 839 ev.time = event->xselectionrequest.time; 840 ev.target = event->xselectionrequest.target; 841 if (event->xselectionrequest.property == None) /* obsolete requestor */ 842 event->xselectionrequest.property = event->xselectionrequest.target; 843 if (ctx->widget != widget || ctx->was_disowned 844 || ((event->xselectionrequest.time != CurrentTime) 845 && (event->xselectionrequest.time < ctx->time))) { 846 ev.property = None; 847 StartProtectedSection(ev.display, ev.requestor); 848 } 849 else { 850 if (ev.target == ctx->prop_list->indirect_atom) { 851 IndirectPair *p; 852 int format; 853 unsigned long bytesafter, length; 854 unsigned char *value = NULL; 855 int count; 856 Boolean writeback = FALSE; 857 858 ev.property = event->xselectionrequest.property; 859 StartProtectedSection(ev.display, ev.requestor); 860 if (XGetWindowProperty(ev.display, ev.requestor, 861 event->xselectionrequest.property, 0L, 862 1000000, False, (Atom) AnyPropertyType, 863 &target, &format, &length, &bytesafter, 864 &value) == Success) 865 count = 866 (int) (BYTELENGTH(length, format) / 867 sizeof(IndirectPair)); 868 else 869 count = 0; 870 for (p = (IndirectPair *) value; count; p++, count--) { 871 EndProtectedSection(ctx->dpy); 872 if (!GetConversion(ctx, (XSelectionRequestEvent *) event, 873 p->target, p->property, widget)) { 874 875 p->target = None; 876 writeback = TRUE; 877 StartProtectedSection(ctx->dpy, ev.requestor); 878 } 879 } 880 if (writeback) 881 XChangeProperty(ev.display, ev.requestor, 882 event->xselectionrequest.property, target, 883 format, PropModeReplace, value, 884 (int) length); 885 XFree((char *) value); 886 } 887 else { /* not multiple */ 888 889 if (GetConversion(ctx, (XSelectionRequestEvent *) event, 890 event->xselectionrequest.target, 891 event->xselectionrequest.property, widget)) 892 ev.property = event->xselectionrequest.property; 893 else { 894 ev.property = None; 895 StartProtectedSection(ctx->dpy, ev.requestor); 896 } 897 } 898 } 899 (void) XSendEvent(ctx->dpy, ev.requestor, False, (unsigned long) NULL, 900 (XEvent *) &ev); 901 902 EndProtectedSection(ctx->dpy); 903 904 break; 905 } 906} 907 908static Boolean 909OwnSelection(Widget widget, 910 Atom selection, 911 Time time, 912 XtConvertSelectionProc convert, 913 XtLoseSelectionProc lose, 914 XtSelectionDoneProc notify, 915 XtCancelConvertSelectionProc cancel, 916 XtPointer closure, 917 Boolean incremental) 918{ 919 Select ctx; 920 Select oldctx = NULL; 921 922 if (!XtIsRealized(widget)) 923 return False; 924 925 ctx = FindCtx(XtDisplay(widget), selection); 926 if (ctx->widget != widget || ctx->time != time || 927 ctx->ref_count || ctx->was_disowned) { 928 Boolean replacement = FALSE; 929 Window window = XtWindow(widget); 930 unsigned long serial = XNextRequest(ctx->dpy); 931 932 XSetSelectionOwner(ctx->dpy, selection, window, time); 933 if (XGetSelectionOwner(ctx->dpy, selection) != window) 934 return FALSE; 935 if (ctx->ref_count) { /* exchange is in-progress */ 936#ifdef DEBUG_ACTIVE 937 printf 938 ("Active exchange for widget \"%s\"; selection=0x%lx, ref_count=%d\n", 939 XtName(widget), (long) selection, ctx->ref_count); 940#endif 941 if (ctx->widget != widget || 942 ctx->convert != convert || 943 ctx->loses != lose || 944 ctx->notify != notify || 945 ctx->owner_cancel != cancel || 946 ctx->incremental != incremental || 947 ctx->owner_closure != closure) { 948 if (ctx->widget == widget) { 949 XtRemoveEventHandler(widget, (EventMask) 0, TRUE, 950 HandleSelectionEvents, 951 (XtPointer) ctx); 952 XtRemoveCallback(widget, XtNdestroyCallback, 953 WidgetDestroyed, (XtPointer) ctx); 954 replacement = TRUE; 955 } 956 else if (!ctx->was_disowned) { 957 oldctx = ctx; 958 } 959 ctx->free_when_done = TRUE; 960 ctx = NewContext(XtDisplay(widget), selection); 961 } 962 else if (!ctx->was_disowned) { /* current owner is new owner */ 963 ctx->time = time; 964 return TRUE; 965 } 966 } 967 if (ctx->widget != widget || ctx->was_disowned || replacement) { 968 if (ctx->widget && !ctx->was_disowned && !replacement) { 969 oldctx = ctx; 970 oldctx->free_when_done = TRUE; 971 ctx = NewContext(XtDisplay(widget), selection); 972 } 973 XtAddEventHandler(widget, (EventMask) 0, TRUE, 974 HandleSelectionEvents, (XtPointer) ctx); 975 XtAddCallback(widget, XtNdestroyCallback, 976 WidgetDestroyed, (XtPointer) ctx); 977 } 978 ctx->widget = widget; /* Selection officially changes hands. */ 979 ctx->time = time; 980 ctx->serial = serial; 981 } 982 ctx->convert = convert; 983 ctx->loses = lose; 984 ctx->notify = notify; 985 ctx->owner_cancel = cancel; 986 XtSetBit(ctx->incremental, incremental); 987 ctx->owner_closure = closure; 988 ctx->was_disowned = FALSE; 989 990 /* Defer calling the previous selection owner's lose selection procedure 991 * until the new selection is established, to allow the previous 992 * selection owner to ask for the new selection to be converted in 993 * the lose selection procedure. The context pointer is the closure 994 * of the event handler and the destroy callback, so the old context 995 * pointer and the record contents must be preserved for LoseSelection. 996 */ 997 if (oldctx) { 998 (void) LoseSelection(oldctx, oldctx->widget, selection, oldctx->time); 999 if (!oldctx->ref_count && oldctx->free_when_done) 1000 XtFree((char *) oldctx); 1001 } 1002 return TRUE; 1003} 1004 1005Boolean 1006XtOwnSelection(Widget widget, 1007 Atom selection, 1008 Time time, 1009 XtConvertSelectionProc convert, 1010 XtLoseSelectionProc lose, 1011 XtSelectionDoneProc notify) 1012{ 1013 Boolean retval; 1014 1015 WIDGET_TO_APPCON(widget); 1016 1017 LOCK_APP(app); 1018 retval = OwnSelection(widget, selection, time, convert, lose, notify, 1019 (XtCancelConvertSelectionProc) NULL, 1020 (XtPointer) NULL, FALSE); 1021 UNLOCK_APP(app); 1022 return retval; 1023} 1024 1025Boolean 1026XtOwnSelectionIncremental(Widget widget, 1027 Atom selection, 1028 Time time, 1029 XtConvertSelectionIncrProc convert, 1030 XtLoseSelectionIncrProc lose, 1031 XtSelectionDoneIncrProc notify, 1032 XtCancelConvertSelectionProc cancel, 1033 XtPointer closure) 1034{ 1035 Boolean retval; 1036 1037 WIDGET_TO_APPCON(widget); 1038 1039 LOCK_APP(app); 1040 retval = OwnSelection(widget, selection, time, 1041 (XtConvertSelectionProc) convert, 1042 (XtLoseSelectionProc) lose, 1043 (XtSelectionDoneProc) notify, cancel, closure, TRUE); 1044 UNLOCK_APP(app); 1045 return retval; 1046} 1047 1048void 1049XtDisownSelection(Widget widget, Atom selection, Time time) 1050{ 1051 Select ctx; 1052 1053 WIDGET_TO_APPCON(widget); 1054 1055 LOCK_APP(app); 1056 ctx = FindCtx(XtDisplay(widget), selection); 1057 if (LoseSelection(ctx, widget, selection, time)) 1058 XSetSelectionOwner(XtDisplay(widget), selection, None, time); 1059 UNLOCK_APP(app); 1060} 1061 1062/* Selection Requestor code */ 1063 1064static Boolean 1065IsINCRtype(CallBackInfo info, Window window, Atom prop) 1066{ 1067 unsigned long bytesafter; 1068 unsigned long length; 1069 int format; 1070 Atom type; 1071 unsigned char *value; 1072 1073 if (prop == None) 1074 return False; 1075 1076 if (XGetWindowProperty(XtDisplay(info->widget), window, prop, 0L, 0L, 1077 False, info->ctx->prop_list->incr_atom, &type, 1078 &format, &length, &bytesafter, &value) != Success) 1079 return False; 1080 1081 return (type == info->ctx->prop_list->incr_atom); 1082} 1083 1084static void 1085ReqCleanup(Widget widget, 1086 XtPointer closure, 1087 XEvent *ev, 1088 Boolean *cont _X_UNUSED) 1089{ 1090 CallBackInfo info = (CallBackInfo) closure; 1091 unsigned long bytesafter, length; 1092 int format; 1093 Atom target; 1094 1095 if (ev->type == SelectionNotify) { 1096 XSelectionEvent *event = (XSelectionEvent *) ev; 1097 1098 if (!MATCH_SELECT(event, info)) 1099 return; /* not really for us */ 1100 XtRemoveEventHandler(widget, (EventMask) 0, TRUE, 1101 ReqCleanup, (XtPointer) info); 1102 if (IsINCRtype(info, XtWindow(widget), event->property)) { 1103 info->proc = HandleGetIncrement; 1104 XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask, 1105 FALSE, ReqCleanup, (XtPointer) info); 1106 } 1107 else { 1108 if (event->property != None) 1109 XDeleteProperty(event->display, XtWindow(widget), 1110 event->property); 1111 FreeSelectionProperty(XtDisplay(widget), info->property); 1112 FreeInfo(info); 1113 } 1114 } 1115 else if ((ev->type == PropertyNotify) && 1116 (ev->xproperty.state == PropertyNewValue) && 1117 (ev->xproperty.atom == info->property)) { 1118 XPropertyEvent *event = (XPropertyEvent *) ev; 1119 char *value = NULL; 1120 1121 if (XGetWindowProperty(event->display, XtWindow(widget), 1122 event->atom, 0L, 1000000, True, AnyPropertyType, 1123 &target, &format, &length, &bytesafter, 1124 (unsigned char **) &value) == Success) { 1125 XFree(value); 1126 if (length == 0) { 1127 XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask, 1128 FALSE, ReqCleanup, (XtPointer) info); 1129 FreeSelectionProperty(XtDisplay(widget), info->property); 1130 XtFree(info->value); /* requestor never got this, so free now */ 1131 FreeInfo(info); 1132 } 1133 } 1134 } 1135} 1136 1137static void 1138ReqTimedOut(XtPointer closure, XtIntervalId *id _X_UNUSED) 1139{ 1140 XtPointer value = NULL; 1141 unsigned long length = 0; 1142 int format = 8; 1143 Atom resulttype = XT_CONVERT_FAIL; 1144 CallBackInfo info = (CallBackInfo) closure; 1145 unsigned long bytesafter; 1146 unsigned long proplength; 1147 Atom type; 1148 1149 if (*info->target == info->ctx->prop_list->indirect_atom) { 1150 IndirectPair *pairs = NULL; 1151 1152 if (XGetWindowProperty(XtDisplay(info->widget), XtWindow(info->widget), 1153 info->property, 0L, 10000000, True, 1154 AnyPropertyType, &type, &format, &proplength, 1155 &bytesafter, (unsigned char **) &pairs) 1156 == Success) { 1157 XtPointer *c; 1158 int i; 1159 1160 XFree(pairs); 1161 for (proplength = proplength / IndirectPairWordSize, i = 0, 1162 c = info->req_closure; proplength; proplength--, c++, i++) 1163 (*info->callbacks[i]) (info->widget, *c, &info->ctx->selection, 1164 &resulttype, value, &length, &format); 1165 } 1166 } 1167 else { 1168 (*info->callbacks[0]) (info->widget, *info->req_closure, 1169 &info->ctx->selection, &resulttype, value, 1170 &length, &format); 1171 } 1172 1173 /* change event handlers for straggler events */ 1174 if (info->proc == HandleSelectionReplies) { 1175 XtRemoveEventHandler(info->widget, (EventMask) 0, 1176 TRUE, info->proc, (XtPointer) info); 1177 XtAddEventHandler(info->widget, (EventMask) 0, TRUE, 1178 ReqCleanup, (XtPointer) info); 1179 } 1180 else { 1181 XtRemoveEventHandler(info->widget, (EventMask) PropertyChangeMask, 1182 FALSE, info->proc, (XtPointer) info); 1183 XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask, 1184 FALSE, ReqCleanup, (XtPointer) info); 1185 } 1186 1187} 1188 1189static void 1190HandleGetIncrement(Widget widget, 1191 XtPointer closure, 1192 XEvent *ev, 1193 Boolean *cont _X_UNUSED) 1194{ 1195 XPropertyEvent *event = (XPropertyEvent *) ev; 1196 CallBackInfo info = (CallBackInfo) closure; 1197 Select ctx = info->ctx; 1198 char *value; 1199 unsigned long bytesafter; 1200 unsigned long length; 1201 int bad; 1202 int n = info->current; 1203 1204 if ((event->state != PropertyNewValue) || (event->atom != info->property)) 1205 return; 1206 1207 bad = XGetWindowProperty(event->display, XtWindow(widget), 1208 event->atom, 0L, 1209 10000000, True, AnyPropertyType, &info->type, 1210 &info->format, &length, &bytesafter, 1211 (unsigned char **)&value); 1212 if (bad) 1213 return; 1214#ifndef DEBUG_WO_TIMERS 1215 XtRemoveTimeOut(info->timeout); 1216#endif 1217 if (length == 0) { 1218 unsigned long u_offset = NUMELEM2(info->offset, info->format); 1219 1220 (*info->callbacks[n]) (widget, *info->req_closure, &ctx->selection, 1221 &info->type, 1222 (info->offset == 0 ? value : info->value), 1223 &u_offset, &info->format); 1224 /* assert ((info->offset != 0) == (info->incremental[n]) */ 1225 if (info->offset != 0) 1226 XFree(value); 1227 XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask, FALSE, 1228 HandleGetIncrement, (XtPointer) info); 1229 FreeSelectionProperty(event->display, info->property); 1230 1231 FreeInfo(info); 1232 } 1233 else { /* add increment to collection */ 1234 if (info->incremental[n]) { 1235#ifdef XT_COPY_SELECTION 1236 int size = (int) BYTELENGTH(length, info->format) + 1; 1237 char *tmp = __XtMalloc((Cardinal) size); 1238 1239 (void) memcpy(tmp, value, (size_t) size); 1240 XFree(value); 1241 value = tmp; 1242#endif 1243 (*info->callbacks[n]) (widget, *info->req_closure, &ctx->selection, 1244 &info->type, value, &length, &info->format); 1245 } 1246 else { 1247 int size = (int) BYTELENGTH(length, info->format); 1248 1249 if (info->offset + size > info->bytelength) { 1250 /* allocate enough for this and the next increment */ 1251 info->bytelength = info->offset + size * 2; 1252 info->value = XtRealloc(info->value, 1253 (Cardinal) info->bytelength); 1254 } 1255 (void) memcpy(&info->value[info->offset], value, (size_t) size); 1256 info->offset += size; 1257 XFree(value); 1258 } 1259 /* reset timer */ 1260#ifndef DEBUG_WO_TIMERS 1261 { 1262 XtAppContext app = XtWidgetToApplicationContext(info->widget); 1263 1264 info->timeout = XtAppAddTimeOut(app, 1265 app->selectionTimeout, ReqTimedOut, 1266 (XtPointer) info); 1267 } 1268#endif 1269 } 1270} 1271 1272static void 1273HandleNone(Widget widget, 1274 XtSelectionCallbackProc callback, 1275 XtPointer closure, 1276 Atom selection) 1277{ 1278 unsigned long length = 0; 1279 int format = 8; 1280 Atom type = None; 1281 1282 (*callback) (widget, closure, &selection, &type, NULL, &length, &format); 1283} 1284 1285static unsigned long 1286IncrPropSize(Widget widget, 1287 unsigned char *value, 1288 int format, 1289 unsigned long length) 1290{ 1291 if (format == 32) { 1292 unsigned long size; 1293 1294 size = ((unsigned long *) value)[length - 1]; /* %%% what order for longs? */ 1295 return size; 1296 } 1297 else { 1298 XtAppWarningMsg(XtWidgetToApplicationContext(widget), 1299 "badFormat", "xtGetSelectionValue", XtCXtToolkitError, 1300 "Selection owner returned type INCR property with format != 32", 1301 NULL, NULL); 1302 return 0; 1303 } 1304} 1305 1306static 1307 Boolean 1308HandleNormal(Display *dpy, 1309 Widget widget, 1310 Atom property, 1311 CallBackInfo info, 1312 XtPointer closure, 1313 Atom selection) 1314{ 1315 unsigned long bytesafter; 1316 unsigned long length; 1317 int format; 1318 Atom type; 1319 unsigned char *value = NULL; 1320 int number = info->current; 1321 1322 if (XGetWindowProperty(dpy, XtWindow(widget), property, 0L, 10000000, 1323 False, AnyPropertyType, &type, &format, &length, 1324 &bytesafter, &value) != Success) 1325 return FALSE; 1326 1327 if (type == info->ctx->prop_list->incr_atom) { 1328 unsigned long size = IncrPropSize(widget, value, format, length); 1329 1330 XFree((char *) value); 1331 if (info->property != property) { 1332 /* within MULTIPLE */ 1333 CallBackInfo ninfo; 1334 1335 ninfo = MakeInfo(info->ctx, &info->callbacks[number], 1336 &info->req_closure[number], 1, widget, 1337 info->time, &info->incremental[number], &property); 1338 ninfo->target = (Atom *) __XtMalloc((unsigned) sizeof(Atom)); 1339 *ninfo->target = info->target[number + 1]; 1340 info = ninfo; 1341 } 1342 HandleIncremental(dpy, widget, property, info, size); 1343 return FALSE; 1344 } 1345 1346 XDeleteProperty(dpy, XtWindow(widget), property); 1347#ifdef XT_COPY_SELECTION 1348 if (value) { /* it could have been deleted after the SelectionNotify */ 1349 int size = (int) BYTELENGTH(length, info->format) + 1; 1350 char *tmp = __XtMalloc((Cardinal) size); 1351 1352 (void) memcpy(tmp, value, (size_t) size); 1353 XFree(value); 1354 value = (unsigned char *) tmp; 1355 } 1356#endif 1357 (*info->callbacks[number]) (widget, closure, &selection, 1358 &type, (XtPointer) value, &length, &format); 1359 1360 if (info->incremental[number]) { 1361 /* let requestor know the whole thing has been received */ 1362 value = (unsigned char *) __XtMalloc((unsigned) 1); 1363 length = 0; 1364 (*info->callbacks[number]) (widget, closure, &selection, 1365 &type, (XtPointer) value, &length, &format); 1366 } 1367 return TRUE; 1368} 1369 1370static void 1371HandleIncremental(Display *dpy, 1372 Widget widget, 1373 Atom property, 1374 CallBackInfo info, 1375 unsigned long size) 1376{ 1377 XtAddEventHandler(widget, (EventMask) PropertyChangeMask, FALSE, 1378 HandleGetIncrement, (XtPointer) info); 1379 1380 /* now start the transfer */ 1381 XDeleteProperty(dpy, XtWindow(widget), property); 1382 XFlush(dpy); 1383 1384 info->bytelength = (int) size; 1385 if (info->incremental[info->current]) /* requestor wants incremental too */ 1386 info->value = NULL; /* so no need for buffer to assemble value */ 1387 else 1388 info->value = (char *) __XtMalloc((unsigned) info->bytelength); 1389 info->offset = 0; 1390 1391 /* reset the timer */ 1392 info->proc = HandleGetIncrement; 1393#ifndef DEBUG_WO_TIMERS 1394 { 1395 XtAppContext app = XtWidgetToApplicationContext(info->widget); 1396 1397 info->timeout = XtAppAddTimeOut(app, 1398 app->selectionTimeout, ReqTimedOut, 1399 (XtPointer) info); 1400 } 1401#endif 1402} 1403 1404static void 1405HandleSelectionReplies(Widget widget, 1406 XtPointer closure, 1407 XEvent *ev, 1408 Boolean *cont _X_UNUSED) 1409{ 1410 XSelectionEvent *event = (XSelectionEvent *) ev; 1411 Display *dpy = event->display; 1412 CallBackInfo info = (CallBackInfo) closure; 1413 Select ctx = info->ctx; 1414 unsigned long bytesafter; 1415 unsigned long length; 1416 int format; 1417 Atom type; 1418 1419 if (event->type != SelectionNotify) 1420 return; 1421 if (!MATCH_SELECT(event, info)) 1422 return; /* not really for us */ 1423#ifndef DEBUG_WO_TIMERS 1424 XtRemoveTimeOut(info->timeout); 1425#endif 1426 XtRemoveEventHandler(widget, (EventMask) 0, TRUE, 1427 HandleSelectionReplies, (XtPointer) info); 1428 if (event->target == ctx->prop_list->indirect_atom) { 1429 IndirectPair *pairs = NULL, *p; 1430 XtPointer *c; 1431 1432 if (XGetWindowProperty(dpy, XtWindow(widget), info->property, 0L, 1433 10000000, True, AnyPropertyType, &type, &format, 1434 &length, &bytesafter, (unsigned char **) &pairs) 1435 != Success) 1436 length = 0; 1437 for (length = length / IndirectPairWordSize, p = pairs, 1438 c = info->req_closure; 1439 length; length--, p++, c++, info->current++) { 1440 if (event->property == None || format != 32 || p->target == None 1441 || /* bug compatibility */ p->property == None) { 1442 HandleNone(widget, info->callbacks[info->current], 1443 *c, event->selection); 1444 if (p->property != None) 1445 FreeSelectionProperty(XtDisplay(widget), p->property); 1446 } 1447 else { 1448 if (HandleNormal(dpy, widget, p->property, info, *c, 1449 event->selection)) { 1450 FreeSelectionProperty(XtDisplay(widget), p->property); 1451 } 1452 } 1453 } 1454 XFree((char *) pairs); 1455 FreeSelectionProperty(dpy, info->property); 1456 FreeInfo(info); 1457 } 1458 else if (event->property == None) { 1459 HandleNone(widget, info->callbacks[0], *info->req_closure, 1460 event->selection); 1461 FreeSelectionProperty(XtDisplay(widget), info->property); 1462 FreeInfo(info); 1463 } 1464 else { 1465 if (HandleNormal(dpy, widget, event->property, info, 1466 *info->req_closure, event->selection)) { 1467 FreeSelectionProperty(XtDisplay(widget), info->property); 1468 FreeInfo(info); 1469 } 1470 } 1471} 1472 1473static void 1474DoLocalTransfer(Request req, 1475 Atom selection, 1476 Atom target, 1477 Widget widget, /* The widget requesting the value. */ 1478 XtSelectionCallbackProc callback, 1479 XtPointer closure, /* the closure for the callback, not the conversion */ 1480 Boolean incremental, Atom property) 1481{ 1482 Select ctx = req->ctx; 1483 XtPointer value = NULL, temp, total = NULL; 1484 unsigned long length; 1485 int format; 1486 Atom resulttype; 1487 unsigned long totallength = 0; 1488 1489 req->event.type = 0; 1490 req->event.target = target; 1491 req->event.property = req->property = property; 1492 req->event.requestor = req->requestor = XtWindow(widget); 1493 1494 if (ctx->incremental) { 1495 unsigned long size = (unsigned long) MAX_SELECTION_INCR(ctx->dpy); 1496 1497 if (!(*(XtConvertSelectionIncrProc) ctx->convert) 1498 (ctx->widget, &selection, &target, 1499 &resulttype, &value, &length, &format, 1500 &size, ctx->owner_closure, (XtRequestId *) &req)) { 1501 HandleNone(widget, callback, closure, selection); 1502 } 1503 else { 1504 if (incremental) { 1505 Boolean allSent = FALSE; 1506 1507 while (!allSent) { 1508 if (ctx->notify && (value != NULL)) { 1509 int bytelength = (int) BYTELENGTH(length, format); 1510 1511 /* both sides think they own this storage */ 1512 temp = __XtMalloc((unsigned) bytelength); 1513 (void) memcpy(temp, value, (size_t) bytelength); 1514 value = temp; 1515 } 1516 /* use care; older clients were never warned that 1517 * they must return a value even if length==0 1518 */ 1519 if (value == NULL) 1520 value = __XtMalloc((unsigned) 1); 1521 (*callback) (widget, closure, &selection, 1522 &resulttype, value, &length, &format); 1523 if (length) { 1524 /* should owner be notified on end-of-piece? 1525 * Spec is unclear, but non-local transfers don't. 1526 */ 1527 (*(XtConvertSelectionIncrProc) ctx->convert) 1528 (ctx->widget, &selection, &target, 1529 &resulttype, &value, &length, &format, 1530 &size, ctx->owner_closure, (XtRequestId *) &req); 1531 } 1532 else 1533 allSent = TRUE; 1534 } 1535 } 1536 else { 1537 while (length) { 1538 int bytelength = (int) BYTELENGTH(length, format); 1539 1540 total = XtRealloc(total, 1541 (Cardinal) (totallength = 1542 totallength + 1543 (unsigned long) bytelength)); 1544 (void) memcpy((char *) total + totallength - bytelength, 1545 value, (size_t) bytelength); 1546 (*(XtConvertSelectionIncrProc) ctx->convert) 1547 (ctx->widget, &selection, &target, 1548 &resulttype, &value, &length, &format, 1549 &size, ctx->owner_closure, (XtRequestId *) &req); 1550 } 1551 if (total == NULL) 1552 total = __XtMalloc(1); 1553 totallength = NUMELEM2(totallength, format); 1554 (*callback) (widget, closure, &selection, &resulttype, 1555 total, &totallength, &format); 1556 } 1557 if (ctx->notify) 1558 (*(XtSelectionDoneIncrProc) ctx->notify) 1559 (ctx->widget, &selection, &target, 1560 (XtRequestId *) &req, ctx->owner_closure); 1561 else 1562 XtFree((char *) value); 1563 } 1564 } 1565 else { /* not incremental owner */ 1566 if (!(*ctx->convert) (ctx->widget, &selection, &target, 1567 &resulttype, &value, &length, &format)) { 1568 HandleNone(widget, callback, closure, selection); 1569 } 1570 else { 1571 if (ctx->notify && (value != NULL)) { 1572 int bytelength = (int) BYTELENGTH(length, format); 1573 1574 /* both sides think they own this storage; better copy */ 1575 temp = __XtMalloc((unsigned) bytelength); 1576 (void) memcpy(temp, value, (size_t) bytelength); 1577 value = temp; 1578 } 1579 if (value == NULL) 1580 value = __XtMalloc((unsigned) 1); 1581 (*callback) (widget, closure, &selection, &resulttype, 1582 value, &length, &format); 1583 if (ctx->notify) 1584 (*ctx->notify) (ctx->widget, &selection, &target); 1585 } 1586 } 1587} 1588 1589static void 1590GetSelectionValue(Widget widget, 1591 Atom selection, 1592 Atom target, 1593 XtSelectionCallbackProc callback, 1594 XtPointer closure, 1595 Time time, 1596 Boolean incremental, 1597 Atom property) 1598{ 1599 Select ctx; 1600 Atom properties[1]; 1601 1602 properties[0] = property; 1603 1604 ctx = FindCtx(XtDisplay(widget), selection); 1605 if (ctx->widget && !ctx->was_disowned) { 1606 RequestRec req; 1607 1608 ctx->req = &req; 1609 memset(&req, 0, sizeof(req)); 1610 req.ctx = ctx; 1611 req.event.time = time; 1612 ctx->ref_count++; 1613 DoLocalTransfer(&req, selection, target, widget, 1614 callback, closure, incremental, property); 1615 if (--ctx->ref_count == 0 && ctx->free_when_done) 1616 XtFree((char *) ctx); 1617 else 1618 ctx->req = NULL; 1619 } 1620 else { 1621 CallBackInfo info; 1622 1623 info = MakeInfo(ctx, &callback, &closure, 1, widget, 1624 time, &incremental, properties); 1625 info->target = (Atom *) __XtMalloc((unsigned) sizeof(Atom)); 1626 *(info->target) = target; 1627 RequestSelectionValue(info, selection, target); 1628 } 1629} 1630 1631void 1632XtGetSelectionValue(Widget widget, 1633 Atom selection, 1634 Atom target, 1635 XtSelectionCallbackProc callback, 1636 XtPointer closure, 1637 Time time) 1638{ 1639 Atom property; 1640 Boolean incr = False; 1641 1642 WIDGET_TO_APPCON(widget); 1643 1644 LOCK_APP(app); 1645 property = GetParamInfo(widget, selection); 1646 RemoveParamInfo(widget, selection); 1647 1648 if (IsGatheringRequest(widget, selection)) { 1649 AddSelectionRequests(widget, selection, 1, &target, &callback, 1, 1650 &closure, &incr, &property); 1651 } 1652 else { 1653 GetSelectionValue(widget, selection, target, callback, 1654 closure, time, FALSE, property); 1655 } 1656 UNLOCK_APP(app); 1657} 1658 1659void 1660XtGetSelectionValueIncremental(Widget widget, 1661 Atom selection, 1662 Atom target, 1663 XtSelectionCallbackProc callback, 1664 XtPointer closure, 1665 Time time) 1666{ 1667 Atom property; 1668 Boolean incr = TRUE; 1669 1670 WIDGET_TO_APPCON(widget); 1671 1672 LOCK_APP(app); 1673 property = GetParamInfo(widget, selection); 1674 RemoveParamInfo(widget, selection); 1675 1676 if (IsGatheringRequest(widget, selection)) { 1677 AddSelectionRequests(widget, selection, 1, &target, &callback, 1, 1678 &closure, &incr, &property); 1679 } 1680 else { 1681 GetSelectionValue(widget, selection, target, callback, 1682 closure, time, TRUE, property); 1683 } 1684 1685 UNLOCK_APP(app); 1686} 1687 1688static void 1689GetSelectionValues(Widget widget, 1690 Atom selection, 1691 Atom *targets, 1692 int count, 1693 XtSelectionCallbackProc *callbacks, 1694 int num_callbacks, 1695 XtPointer *closures, 1696 Time time, 1697 Boolean *incremental, 1698 Atom *properties) 1699{ 1700 Select ctx; 1701 IndirectPair *pairs; 1702 1703 if (count == 0) 1704 return; 1705 ctx = FindCtx(XtDisplay(widget), selection); 1706 if (ctx->widget && !ctx->was_disowned) { 1707 int j, i; 1708 RequestRec req; 1709 1710 ctx->req = &req; 1711 req.ctx = ctx; 1712 req.event.time = time; 1713 ctx->ref_count++; 1714 for (i = 0, j = 0; count > 0; count--, i++, j++) { 1715 if (j >= num_callbacks) 1716 j = 0; 1717 1718 DoLocalTransfer(&req, selection, targets[i], widget, 1719 callbacks[j], closures[i], incremental[i], 1720 properties ? properties[i] : None); 1721 1722 } 1723 if (--ctx->ref_count == 0 && ctx->free_when_done) 1724 XtFree((char *) ctx); 1725 else 1726 ctx->req = NULL; 1727 } 1728 else { 1729 XtSelectionCallbackProc *passed_callbacks; 1730 XtSelectionCallbackProc stack_cbs[32]; 1731 CallBackInfo info; 1732 IndirectPair *p; 1733 Atom *t; 1734 int i = 0, j = 0; 1735 1736 passed_callbacks = (XtSelectionCallbackProc *) 1737 XtStackAlloc(sizeof(XtSelectionCallbackProc) * (size_t) count, 1738 stack_cbs); 1739 1740 /* To deal with the old calls from XtGetSelectionValues* we 1741 will repeat however many callbacks have been passed into 1742 the array */ 1743 for (i = 0; i < count; i++) { 1744 if (j >= num_callbacks) 1745 j = 0; 1746 passed_callbacks[i] = callbacks[j]; 1747 j++; 1748 } 1749 info = MakeInfo(ctx, passed_callbacks, closures, count, widget, 1750 time, incremental, properties); 1751 XtStackFree((XtPointer) passed_callbacks, stack_cbs); 1752 1753 info->target = XtMallocArray ((Cardinal) count + 1, 1754 (Cardinal) sizeof(Atom)); 1755 (*info->target) = ctx->prop_list->indirect_atom; 1756 (void) memcpy((char *) info->target + sizeof(Atom), targets, 1757 (size_t) count * sizeof(Atom)); 1758 pairs = XtMallocArray ((Cardinal) count + 1, 1759 (Cardinal) sizeof(IndirectPair)); 1760 for (p = &pairs[count - 1], t = &targets[count - 1], i = count - 1; 1761 p >= pairs; p--, t--, i--) { 1762 p->target = *t; 1763 if (properties == NULL || properties[i] == None) { 1764 p->property = GetSelectionProperty(XtDisplay(widget)); 1765 XDeleteProperty(XtDisplay(widget), XtWindow(widget), 1766 p->property); 1767 } 1768 else { 1769 p->property = properties[i]; 1770 } 1771 } 1772 XChangeProperty(XtDisplay(widget), XtWindow(widget), 1773 info->property, info->property, 1774 32, PropModeReplace, (unsigned char *) pairs, 1775 count * IndirectPairWordSize); 1776 XtFree((char *) pairs); 1777 RequestSelectionValue(info, selection, ctx->prop_list->indirect_atom); 1778 } 1779} 1780 1781void 1782XtGetSelectionValues(Widget widget, 1783 Atom selection, 1784 Atom *targets, 1785 int count, 1786 XtSelectionCallbackProc callback, 1787 XtPointer *closures, 1788 Time time) 1789{ 1790 Boolean incremental_values[32]; 1791 Boolean *incremental; 1792 int i; 1793 1794 WIDGET_TO_APPCON(widget); 1795 1796 LOCK_APP(app); 1797 incremental = 1798 XtStackAlloc((size_t) count * sizeof(Boolean), incremental_values); 1799 for (i = 0; i < count; i++) 1800 incremental[i] = FALSE; 1801 if (IsGatheringRequest(widget, selection)) { 1802 AddSelectionRequests(widget, selection, count, targets, &callback, 1803 1, closures, incremental, NULL); 1804 } 1805 else { 1806 GetSelectionValues(widget, selection, targets, count, &callback, 1, 1807 closures, time, incremental, NULL); 1808 } 1809 XtStackFree((XtPointer) incremental, incremental_values); 1810 UNLOCK_APP(app); 1811} 1812 1813void 1814XtGetSelectionValuesIncremental(Widget widget, 1815 Atom selection, 1816 Atom *targets, 1817 int count, 1818 XtSelectionCallbackProc callback, 1819 XtPointer *closures, 1820 Time time) 1821{ 1822 Boolean incremental_values[32]; 1823 Boolean *incremental; 1824 int i; 1825 1826 WIDGET_TO_APPCON(widget); 1827 1828 LOCK_APP(app); 1829 incremental = 1830 XtStackAlloc((size_t) count * sizeof(Boolean), incremental_values); 1831 for (i = 0; i < count; i++) 1832 incremental[i] = TRUE; 1833 if (IsGatheringRequest(widget, selection)) { 1834 AddSelectionRequests(widget, selection, count, targets, &callback, 1835 1, closures, incremental, NULL); 1836 } 1837 else { 1838 GetSelectionValues(widget, selection, targets, count, 1839 &callback, 1, closures, time, incremental, NULL); 1840 } 1841 XtStackFree((XtPointer) incremental, incremental_values); 1842 UNLOCK_APP(app); 1843} 1844 1845static Request 1846GetRequestRecord(Widget widget, Atom selection, XtRequestId id) 1847{ 1848 Request req = (Request) id; 1849 Select ctx = NULL; 1850 1851 if ((req == NULL 1852 && ((ctx = FindCtx(XtDisplay(widget), selection)) == NULL 1853 || ctx->req == NULL 1854 || ctx->selection != selection || ctx->widget == NULL)) 1855 || (req != NULL 1856 && (req->ctx == NULL 1857 || req->ctx->selection != selection 1858 || req->ctx->widget != widget))) { 1859 String params = XtName(widget); 1860 Cardinal num_params = 1; 1861 1862 XtAppWarningMsg(XtWidgetToApplicationContext(widget), 1863 "notInConvertSelection", "xtGetSelectionRequest", 1864 XtCXtToolkitError, 1865 "XtGetSelectionRequest or XtGetSelectionParameters called for widget \"%s\" outside of ConvertSelection proc", 1866 ¶ms, &num_params); 1867 return NULL; 1868 } 1869 1870 if (req == NULL) { 1871 /* non-incremental owner; only one request can be 1872 * outstanding at a time, so it's safe to keep ptr in ctx */ 1873 req = ctx->req; 1874 } 1875 return req; 1876} 1877 1878XSelectionRequestEvent * 1879XtGetSelectionRequest(Widget widget, Atom selection, XtRequestId id) 1880{ 1881 Request req = (Request) id; 1882 1883 WIDGET_TO_APPCON(widget); 1884 1885 LOCK_APP(app); 1886 1887 req = GetRequestRecord(widget, selection, id); 1888 1889 if (!req) { 1890 UNLOCK_APP(app); 1891 return (XSelectionRequestEvent *) NULL; 1892 } 1893 1894 if (req->event.type == 0) { 1895 /* owner is local; construct the remainder of the event */ 1896 req->event.type = SelectionRequest; 1897 req->event.serial = LastKnownRequestProcessed(XtDisplay(widget)); 1898 req->event.send_event = True; 1899 req->event.display = XtDisplay(widget); 1900 1901 req->event.owner = XtWindow(req->ctx->widget); 1902 req->event.selection = selection; 1903 } 1904 UNLOCK_APP(app); 1905 return &req->event; 1906} 1907 1908/* Property atom access */ 1909Atom 1910XtReservePropertyAtom(Widget w) 1911{ 1912 return (GetSelectionProperty(XtDisplay(w))); 1913} 1914 1915void 1916XtReleasePropertyAtom(Widget w, Atom atom) 1917{ 1918 FreeSelectionProperty(XtDisplay(w), atom); 1919} 1920 1921/* Multiple utilities */ 1922 1923/* All requests are put in a single list per widget. It is 1924 very unlikely anyone will be gathering multiple MULTIPLE 1925 requests at the same time, so the loss in efficiency for 1926 this case is acceptable */ 1927 1928/* Queue one or more requests to the one we're gathering */ 1929static void 1930AddSelectionRequests(Widget wid, 1931 Atom sel, 1932 int count, 1933 Atom *targets, 1934 XtSelectionCallbackProc *callbacks, 1935 int num_cb, 1936 XtPointer *closures, 1937 Boolean *incrementals, 1938 Atom *properties) 1939{ 1940 QueuedRequestInfo qi; 1941 Window window = XtWindow(wid); 1942 Display *dpy = XtDisplay(wid); 1943 1944 LOCK_PROCESS; 1945 if (multipleContext == 0) 1946 multipleContext = XUniqueContext(); 1947 1948 qi = NULL; 1949 (void) XFindContext(dpy, window, multipleContext, (XPointer *) &qi); 1950 1951 if (qi != NULL) { 1952 QueuedRequest *req = qi->requests; 1953 int start = qi->count; 1954 int i = 0; 1955 int j = 0; 1956 1957 qi->count += count; 1958 req = XtReallocArray(req, (Cardinal) (start + count), 1959 (Cardinal) sizeof(QueuedRequest)); 1960 while (i < count) { 1961 QueuedRequest newreq = (QueuedRequest) 1962 __XtMalloc(sizeof(QueuedRequestRec)); 1963 1964 newreq->selection = sel; 1965 newreq->target = targets[i]; 1966 if (properties != NULL) 1967 newreq->param = properties[i]; 1968 else { 1969 newreq->param = GetSelectionProperty(dpy); 1970 XDeleteProperty(dpy, window, newreq->param); 1971 } 1972 newreq->callback = callbacks[j]; 1973 newreq->closure = closures[i]; 1974 newreq->incremental = incrementals[i]; 1975 1976 req[start] = newreq; 1977 start++; 1978 i++; 1979 j++; 1980 if (j > num_cb) 1981 j = 0; 1982 } 1983 1984 qi->requests = req; 1985 } 1986 else { 1987 /* Impossible */ 1988 } 1989 1990 UNLOCK_PROCESS; 1991} 1992 1993/* Only call IsGatheringRequest when we have a lock already */ 1994 1995static Boolean 1996IsGatheringRequest(Widget wid, Atom sel) 1997{ 1998 QueuedRequestInfo qi; 1999 Window window = XtWindow(wid); 2000 Display *dpy = XtDisplay(wid); 2001 Boolean found = False; 2002 2003 if (multipleContext == 0) 2004 multipleContext = XUniqueContext(); 2005 2006 qi = NULL; 2007 (void) XFindContext(dpy, window, multipleContext, (XPointer *) &qi); 2008 2009 if (qi != NULL) { 2010 int i = 0; 2011 2012 while (qi->selections[i] != None) { 2013 if (qi->selections[i] == sel) { 2014 found = True; 2015 break; 2016 } 2017 i++; 2018 } 2019 } 2020 2021 return (found); 2022} 2023 2024/* Cleanup request scans the request queue and releases any 2025 properties queued, and removes any requests queued */ 2026static void 2027CleanupRequest(Display *dpy, QueuedRequestInfo qi, Atom sel) 2028{ 2029 int i, j, n; 2030 2031 if (qi == NULL) 2032 return; 2033 2034 i = 0; 2035 2036 /* Remove this selection from the list */ 2037 n = 0; 2038 while (qi->selections[n] != sel && qi->selections[n] != None) 2039 n++; 2040 if (qi->selections[n] == sel) { 2041 while (qi->selections[n] != None) { 2042 qi->selections[n] = qi->selections[n + 1]; 2043 n++; 2044 } 2045 } 2046 2047 while (i < qi->count) { 2048 QueuedRequest req = qi->requests[i]; 2049 2050 if (req->selection == sel) { 2051 /* Match */ 2052 if (req->param != None) 2053 FreeSelectionProperty(dpy, req->param); 2054 qi->count--; 2055 2056 for (j = i; j < qi->count; j++) 2057 qi->requests[j] = qi->requests[j + 1]; 2058 2059 XtFree((char *) req); 2060 } 2061 else { 2062 i++; 2063 } 2064 } 2065} 2066 2067void 2068XtCreateSelectionRequest(Widget widget, Atom selection) 2069{ 2070 QueuedRequestInfo queueInfo; 2071 Window window = XtWindow(widget); 2072 Display *dpy = XtDisplay(widget); 2073 Cardinal n; 2074 2075 LOCK_PROCESS; 2076 if (multipleContext == 0) 2077 multipleContext = XUniqueContext(); 2078 2079 queueInfo = NULL; 2080 (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo); 2081 2082 /* If there is one, then cancel it */ 2083 if (queueInfo != NULL) 2084 CleanupRequest(dpy, queueInfo, selection); 2085 else { 2086 /* Create it */ 2087 queueInfo = 2088 (QueuedRequestInfo) __XtMalloc(sizeof(QueuedRequestInfoRec)); 2089 queueInfo->count = 0; 2090 queueInfo->selections = XtMallocArray(2, (Cardinal) sizeof(Atom)); 2091 queueInfo->selections[0] = None; 2092 queueInfo->requests = (QueuedRequest *) 2093 __XtMalloc(sizeof(QueuedRequest)); 2094 } 2095 2096 /* Append this selection to list */ 2097 n = 0; 2098 while (queueInfo->selections[n] != None) 2099 n++; 2100 queueInfo->selections = XtReallocArray(queueInfo->selections, (n + 2), 2101 (Cardinal) sizeof(Atom)); 2102 queueInfo->selections[n] = selection; 2103 queueInfo->selections[n + 1] = None; 2104 2105 (void) XSaveContext(dpy, window, multipleContext, (char *) queueInfo); 2106 UNLOCK_PROCESS; 2107} 2108 2109void 2110XtSendSelectionRequest(Widget widget, Atom selection, Time time) 2111{ 2112 QueuedRequestInfo queueInfo; 2113 Window window = XtWindow(widget); 2114 Display *dpy = XtDisplay(widget); 2115 2116 LOCK_PROCESS; 2117 if (multipleContext == 0) 2118 multipleContext = XUniqueContext(); 2119 2120 queueInfo = NULL; 2121 (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo); 2122 if (queueInfo != NULL) { 2123 int i; 2124 int count = 0; 2125 QueuedRequest *req = queueInfo->requests; 2126 2127 /* Construct the requests and send it using 2128 GetSelectionValues */ 2129 for (i = 0; i < queueInfo->count; i++) 2130 if (req[i]->selection == selection) 2131 count++; 2132 2133 if (count > 0) { 2134 if (count == 1) { 2135 for (i = 0; i < queueInfo->count; i++) 2136 if (req[i]->selection == selection) 2137 break; 2138 2139 /* special case a multiple which isn't needed */ 2140 GetSelectionValue(widget, selection, req[i]->target, 2141 req[i]->callback, req[i]->closure, time, 2142 req[i]->incremental, req[i]->param); 2143 } 2144 else { 2145 Atom *targets; 2146 Atom t[PREALLOCED]; 2147 XtSelectionCallbackProc *cbs; 2148 XtSelectionCallbackProc c[PREALLOCED]; 2149 XtPointer *closures; 2150 XtPointer cs[PREALLOCED]; 2151 Boolean *incrs; 2152 Boolean ins[PREALLOCED]; 2153 Atom *props; 2154 Atom p[PREALLOCED]; 2155 int j = 0; 2156 2157 /* Allocate */ 2158 targets = 2159 (Atom *) XtStackAlloc((size_t) count * sizeof(Atom), t); 2160 cbs = (XtSelectionCallbackProc *) 2161 XtStackAlloc((size_t) count * 2162 sizeof(XtSelectionCallbackProc), c); 2163 closures = 2164 (XtPointer *) XtStackAlloc((size_t) count * 2165 sizeof(XtPointer), cs); 2166 incrs = 2167 (Boolean *) XtStackAlloc((size_t) count * sizeof(Boolean), 2168 ins); 2169 props = (Atom *) XtStackAlloc((size_t) count * sizeof(Atom), p); 2170 2171 /* Copy */ 2172 for (i = 0; i < queueInfo->count; i++) { 2173 if (req[i]->selection == selection) { 2174 targets[j] = req[i]->target; 2175 cbs[j] = req[i]->callback; 2176 closures[j] = req[i]->closure; 2177 incrs[j] = req[i]->incremental; 2178 props[j] = req[i]->param; 2179 j++; 2180 } 2181 } 2182 2183 /* Make the request */ 2184 GetSelectionValues(widget, selection, targets, count, 2185 cbs, count, closures, time, incrs, props); 2186 2187 /* Free */ 2188 XtStackFree((XtPointer) targets, t); 2189 XtStackFree((XtPointer) cbs, c); 2190 XtStackFree((XtPointer) closures, cs); 2191 XtStackFree((XtPointer) incrs, ins); 2192 XtStackFree((XtPointer) props, p); 2193 } 2194 } 2195 } 2196 2197 CleanupRequest(dpy, queueInfo, selection); 2198 UNLOCK_PROCESS; 2199} 2200 2201void 2202XtCancelSelectionRequest(Widget widget, Atom selection) 2203{ 2204 QueuedRequestInfo queueInfo; 2205 Window window = XtWindow(widget); 2206 Display *dpy = XtDisplay(widget); 2207 2208 LOCK_PROCESS; 2209 if (multipleContext == 0) 2210 multipleContext = XUniqueContext(); 2211 2212 queueInfo = NULL; 2213 (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo); 2214 /* If there is one, then cancel it */ 2215 if (queueInfo != NULL) 2216 CleanupRequest(dpy, queueInfo, selection); 2217 UNLOCK_PROCESS; 2218} 2219 2220/* Parameter utilities */ 2221 2222/* Parameters on a selection request */ 2223/* Places data on allocated parameter atom, then records the 2224 parameter atom data for use in the next call to one of 2225 the XtGetSelectionValue functions. */ 2226void 2227XtSetSelectionParameters(Widget requestor, 2228 Atom selection, 2229 Atom type, 2230 XtPointer value, 2231 unsigned long length, 2232 int format) 2233{ 2234 Display *dpy = XtDisplay(requestor); 2235 Window window = XtWindow(requestor); 2236 Atom property = GetParamInfo(requestor, selection); 2237 2238 if (property == None) { 2239 property = GetSelectionProperty(dpy); 2240 AddParamInfo(requestor, selection, property); 2241 } 2242 2243 XChangeProperty(dpy, window, property, 2244 type, format, PropModeReplace, 2245 (unsigned char *) value, (int) length); 2246} 2247 2248/* Retrieves data passed in a parameter. Data for this is stored 2249 on the originator's window */ 2250void 2251XtGetSelectionParameters(Widget owner, 2252 Atom selection, 2253 XtRequestId request_id, 2254 Atom *type_return, 2255 XtPointer *value_return, 2256 unsigned long *length_return, 2257 int *format_return) 2258{ 2259 Request req; 2260 Display *dpy = XtDisplay(owner); 2261 2262 WIDGET_TO_APPCON(owner); 2263 2264 *value_return = NULL; 2265 *length_return = (unsigned long) (*format_return = 0); 2266 *type_return = None; 2267 2268 LOCK_APP(app); 2269 2270 req = GetRequestRecord(owner, selection, request_id); 2271 2272 if (req && req->property) { 2273 unsigned long bytes_after; /* unused */ 2274 2275 StartProtectedSection(dpy, req->requestor); 2276 XGetWindowProperty(dpy, req->requestor, req->property, 0L, 10000000, 2277 False, AnyPropertyType, type_return, format_return, 2278 length_return, &bytes_after, 2279 (unsigned char **) value_return); 2280 EndProtectedSection(dpy); 2281#ifdef XT_COPY_SELECTION 2282 if (*value_return) { 2283 int size = (int) BYTELENGTH(*length_return, *format_return) + 1; 2284 char *tmp = __XtMalloc((Cardinal) size); 2285 2286 (void) memcpy(tmp, *value_return, (size_t) size); 2287 XFree(*value_return); 2288 *value_return = tmp; 2289 } 2290#endif 2291 } 2292 UNLOCK_APP(app); 2293} 2294 2295/* Parameters are temporarily stashed in an XContext. A list is used because 2296 * there may be more than one selection request in progress. The context 2297 * data is deleted when the list is empty. In the future, the parameter 2298 * context could be merged with other contexts used during selections. 2299 */ 2300 2301static void 2302AddParamInfo(Widget w, Atom selection, Atom param_atom) 2303{ 2304 Param p; 2305 ParamInfo pinfo; 2306 2307 LOCK_PROCESS; 2308 if (paramPropertyContext == 0) 2309 paramPropertyContext = XUniqueContext(); 2310 2311 if (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext, 2312 (XPointer *) &pinfo)) { 2313 pinfo = (ParamInfo) __XtMalloc(sizeof(ParamInfoRec)); 2314 pinfo->count = 1; 2315 pinfo->paramlist = XtNew(ParamRec); 2316 p = pinfo->paramlist; 2317 (void) XSaveContext(XtDisplay(w), XtWindow(w), paramPropertyContext, 2318 (char *) pinfo); 2319 } 2320 else { 2321 int n; 2322 2323 for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++) { 2324 if (p->selection == None || p->selection == selection) 2325 break; 2326 } 2327 if (n == 0) { 2328 pinfo->count++; 2329 pinfo->paramlist = XtReallocArray(pinfo->paramlist, pinfo->count, 2330 (Cardinal) sizeof(ParamRec)); 2331 p = &pinfo->paramlist[pinfo->count - 1]; 2332 (void) XSaveContext(XtDisplay(w), XtWindow(w), 2333 paramPropertyContext, (char *) pinfo); 2334 } 2335 } 2336 p->selection = selection; 2337 p->param = param_atom; 2338 UNLOCK_PROCESS; 2339} 2340 2341static void 2342RemoveParamInfo(Widget w, Atom selection) 2343{ 2344 ParamInfo pinfo; 2345 Boolean retain = False; 2346 2347 LOCK_PROCESS; 2348 if (paramPropertyContext 2349 && (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext, 2350 (XPointer *) &pinfo) == 0)) { 2351 Param p; 2352 int n; 2353 2354 /* Find and invalidate the parameter data. */ 2355 for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++) { 2356 if (p->selection != None) { 2357 if (p->selection == selection) 2358 p->selection = None; 2359 else 2360 retain = True; 2361 } 2362 } 2363 /* If there's no valid data remaining, release the context entry. */ 2364 if (!retain) { 2365 XtFree((char *) pinfo->paramlist); 2366 XtFree((char *) pinfo); 2367 XDeleteContext(XtDisplay(w), XtWindow(w), paramPropertyContext); 2368 } 2369 } 2370 UNLOCK_PROCESS; 2371} 2372 2373static Atom 2374GetParamInfo(Widget w, Atom selection) 2375{ 2376 ParamInfo pinfo; 2377 Atom atom = None; 2378 2379 LOCK_PROCESS; 2380 if (paramPropertyContext 2381 && (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext, 2382 (XPointer *) &pinfo) == 0)) { 2383 Param p; 2384 int n; 2385 2386 for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++) 2387 if (p->selection == selection) { 2388 atom = p->param; 2389 break; 2390 } 2391 } 2392 UNLOCK_PROCESS; 2393 return atom; 2394} 2395