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