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