home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / lib / Xt / Selection.c.orig < prev    next >
Encoding:
Text File  |  1993-07-21  |  49.1 KB  |  1,681 lines

  1. /* $XConsortium: Selection.c,v 1.78 93/05/13 11:09:15 converse Exp $ */
  2.  
  3. /***********************************************************
  4. Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
  5. and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
  6.  
  7.                         All Rights Reserved
  8.  
  9. Permission to use, copy, modify, and distribute this software and its 
  10. documentation for any purpose and without fee is hereby granted, 
  11. provided that the above copyright notice appear in all copies and that
  12. both that copyright notice and this permission notice appear in 
  13. supporting documentation, and that the names of Digital or MIT not be
  14. used in advertising or publicity pertaining to distribution of the
  15. software without specific, written prior permission.  
  16.  
  17. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  18. ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  19. DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  20. ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21. WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  22. ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  23. SOFTWARE.
  24.  
  25. ******************************************************************/
  26.  
  27. #include "IntrinsicI.h"
  28. #include "StringDefs.h"
  29. #include "SelectionI.h"
  30. #include <X11/Xatom.h>
  31.  
  32. void _XtSetDefaultSelectionTimeout(timeout)
  33.     unsigned long *timeout;
  34. {
  35.     *timeout = 5000; /* default to 5 seconds */
  36. }
  37.  
  38. void XtSetSelectionTimeout(timeout)
  39.     unsigned long timeout;
  40. {
  41.     XtAppSetSelectionTimeout(_XtDefaultAppContext(), timeout);
  42. }
  43.  
  44. void XtAppSetSelectionTimeout(app, timeout)
  45.     XtAppContext app;
  46.     unsigned long timeout;
  47. {
  48.     app->selectionTimeout = timeout;
  49. }
  50.  
  51. unsigned long XtGetSelectionTimeout()
  52. {
  53.     return XtAppGetSelectionTimeout(_XtDefaultAppContext());
  54. }
  55.  
  56. unsigned long XtAppGetSelectionTimeout(app)
  57.     XtAppContext app;
  58. {
  59.     return app->selectionTimeout;
  60. }
  61.  
  62.  
  63. /* General utilities */
  64.  
  65. static void HandleSelectionReplies();
  66. static void ReqTimedOut();
  67. static void HandlePropertyGone();
  68. static void HandleGetIncrement();
  69. static HandleIncremental();
  70.  
  71. static XContext selectPropertyContext = 0;
  72.  
  73. static int StorageSize[3] = {1, sizeof(short), sizeof(long)};
  74. #define BYTELENGTH(length, format) ((length) * StorageSize[(format)>>4])
  75. #define NUMELEM(bytelength, format) ((bytelength) / StorageSize[(format)>>4])
  76.  
  77. /* Xlib and Xt are permitted to have different memory allocators, and in the
  78.  * XtSelectionCallbackProc the client is instructed to free the selection
  79.  * value with XtFree, so the selection value received from XGetWindowProperty
  80.  * should be copied to memory allocated through Xt.  But copying is
  81.  * undesirable since the selection value may be large, and, under normal
  82.  * library configuration copying is unnecessary.
  83.  */
  84. #ifdef XTTRACEMEMORY
  85. #define XT_COPY_SELECTION    1
  86. #endif
  87.  
  88. /*ARGSUSED*/
  89. static void FreePropList(w, closure, callData)
  90.  Widget w;            /* unused */
  91.  XtPointer closure;
  92.  XtPointer callData;        /* unused */
  93. {
  94.     PropList sarray = (PropList)closure;
  95.     XDeleteContext(sarray->dpy, DefaultRootWindow(sarray->dpy),
  96.            selectPropertyContext);
  97.     XtFree((char*)sarray->list);
  98.     XtFree((char*)closure);
  99. }
  100.  
  101.  
  102. static PropList GetPropList(dpy)
  103.     Display *dpy;
  104. {
  105.     PropList sarray;
  106.     if (selectPropertyContext == 0)
  107.     selectPropertyContext = XUniqueContext();
  108.     if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
  109.              (XPointer *)&sarray)) {
  110.     XtPerDisplay pd = _XtGetPerDisplay(dpy);
  111.     sarray = (PropList) XtMalloc((unsigned) sizeof(PropListRec));
  112.     sarray->dpy = dpy;
  113.     sarray->incr_atom = XInternAtom(dpy, "INCR", FALSE);
  114.     sarray->indirect_atom = XInternAtom(dpy, "MULTIPLE", FALSE);
  115.     sarray->timestamp_atom = XInternAtom(dpy, "TIMESTAMP", FALSE);
  116. #ifdef DRAFT_ICCCM_COMPATIBILITY
  117. /*    Should have been INCREMENTAL all along, but Xt has always used
  118.  *    INCR to implement draft ICCCM incremental protocol.  So it stays 
  119.  *    as INCR, in violation of the ICCCM, for historical reasons.  
  120.  *    This code will be removed in a future release.
  121.  */
  122.      sarray->incremental_atom = XInternAtom(dpy, "INCR", FALSE);
  123. #endif
  124.     sarray->propCount = 1;
  125.     sarray->list = (SelectionProp)XtMalloc((unsigned) sizeof(SelectionPropRec));
  126.     sarray->list[0].prop = XInternAtom(dpy, "_XT_SELECTION_0", FALSE);
  127.     sarray->list[0].avail = TRUE;
  128.     (void) XSaveContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
  129.                 (char *) sarray);
  130.     _XtAddCallback( &pd->destroy_callbacks,
  131.             FreePropList, (XtPointer)sarray );
  132.     }
  133.     return sarray;
  134. }
  135.  
  136.  
  137. static Atom GetSelectionProperty(dpy)
  138. Display *dpy;
  139. {
  140.  SelectionProp p;
  141.  int propCount;
  142.  char propname[80];
  143.  PropList sarray = GetPropList(dpy);
  144.  
  145.  for (p = sarray->list, propCount=sarray->propCount;
  146.     propCount;
  147.     p++, propCount--) {
  148.    if (p->avail) {
  149.       p->avail = FALSE;
  150.       return(p->prop);
  151.    }
  152.  }
  153.  propCount = sarray->propCount++;
  154.  sarray->list = (SelectionProp) XtRealloc((XtPointer)sarray->list, 
  155.           (unsigned)(sarray->propCount*sizeof(SelectionPropRec)));
  156.  (void) sprintf(propname, "%s%d", "_XT_SELECTION_", propCount);
  157.  sarray->list[propCount].prop = XInternAtom(dpy, propname, FALSE);
  158.  sarray->list[propCount].avail = FALSE;
  159.  return(sarray->list[propCount].prop);
  160. }
  161.  
  162. static FreeSelectionProperty(dpy, prop)
  163. Display *dpy;
  164. Atom prop;
  165. {
  166.  SelectionProp p;
  167.  PropList sarray;
  168.  if (prop == None) return;
  169.  if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
  170.           (XPointer *)&sarray)) 
  171.     XtAppErrorMsg(XtDisplayToApplicationContext(dpy),
  172.         "noSelectionProperties", "freeSelectionProperty", XtCXtToolkitError,
  173.         "internal error: no selection property context for display",
  174.          (String *)NULL,  (Cardinal *)NULL );
  175.  for (p = sarray->list; p; p++) 
  176.    if (p->prop == prop) {
  177.       p->avail = TRUE;
  178.       return;
  179.       }
  180. }
  181.  
  182. static CallBackInfo MakeInfo(ctx, callback, closures, count, widget,
  183.                  time, incremental)
  184. Select ctx;
  185. XtSelectionCallbackProc callback;
  186. XtPointer *closures;
  187. int count;
  188. Widget widget;
  189. Time time;
  190. Boolean incremental;
  191. {
  192.         CallBackInfo info = XtNew(CallBackInfoRec);
  193.  
  194.     info->ctx = ctx;
  195.     info->callback = callback;
  196.     info->req_closure =
  197.         (XtPointer*)XtMalloc((unsigned) (count * sizeof(XtPointer)));
  198.     bcopy((char*)closures, (char*)info->req_closure, count * sizeof(XtPointer));
  199.         info->property = GetSelectionProperty(XtDisplay(widget));
  200.     info->proc = HandleSelectionReplies;
  201.     info->widget = widget;
  202.     info->time = time;
  203.     info->incremental = incremental;
  204.     return (info);
  205. }
  206.  
  207. static RequestSelectionValue(info, selection, target)
  208. CallBackInfo info;
  209. Atom selection;
  210. Atom target;
  211. {
  212. #ifndef DEBUG_WO_TIMERS
  213.     XtAppContext app = XtWidgetToApplicationContext(info->widget);
  214.     info->timeout = XtAppAddTimeOut(app,
  215.              app->selectionTimeout, ReqTimedOut, (XtPointer)info);
  216. #endif 
  217.     XtAddEventHandler(info->widget, (EventMask)0, TRUE,
  218.               HandleSelectionReplies, (XtPointer)info);
  219.     XConvertSelection(info->ctx->dpy, selection, target, 
  220.               info->property, XtWindow(info->widget), info->time);
  221. }
  222.  
  223.  
  224. static XContext selectContext = 0;
  225.  
  226. static Select NewContext(dpy, selection)
  227. Display *dpy;
  228. Atom selection;
  229. {
  230.     /* assert(selectContext != 0) */
  231.     Select ctx = XtNew(SelectRec);
  232.     ctx->dpy = dpy;
  233.     ctx->selection = selection;
  234.     ctx->widget = NULL;
  235.     ctx->prop_list = GetPropList(dpy);
  236.     ctx->ref_count = 0;
  237.     ctx->free_when_done = FALSE;
  238.     ctx->was_disowned = FALSE;
  239.     (void)XSaveContext(dpy, (Window)selection, selectContext, (char *)ctx);
  240.     return ctx;
  241. }
  242.  
  243. static Select FindCtx(dpy, selection)
  244. Display *dpy;
  245. Atom selection;
  246. {
  247.     Select ctx;
  248.  
  249.     if (selectContext == 0)
  250.     selectContext = XUniqueContext();
  251.     if (XFindContext(dpy, (Window)selection, selectContext, (XPointer *)&ctx))
  252.     ctx = NewContext(dpy, selection);
  253.  
  254.     return ctx;
  255. }
  256.  
  257. /*ARGSUSED*/
  258. static void WidgetDestroyed(widget, closure, data)
  259. Widget widget;
  260. XtPointer closure, data;
  261. {
  262.     Select ctx = (Select) closure;
  263.     if (ctx->widget == widget) {
  264.     if (ctx->free_when_done)
  265.         XtFree((char*)ctx);
  266.     else
  267.         ctx->widget = NULL;
  268.     }
  269. }
  270.  
  271. /* Selection Owner code */
  272.  
  273. static void HandleSelectionEvents();
  274.  
  275. static Boolean LoseSelection(ctx, widget, selection, time)
  276. Select ctx;
  277. Widget widget;
  278. Atom selection;
  279. Time time;
  280. {
  281.     if ((ctx->widget == widget) &&
  282.     (ctx->selection == selection) && /* paranoia */
  283.     !ctx->was_disowned &&
  284.     ((time == CurrentTime) || (time >= ctx->time)))
  285.     {
  286.     XtRemoveEventHandler(widget, (EventMask)0, TRUE,
  287.                  HandleSelectionEvents, (XtPointer)ctx); 
  288.     XtRemoveCallback(widget, XtNdestroyCallback, 
  289.              WidgetDestroyed, (XtPointer)ctx); 
  290.     ctx->was_disowned = TRUE; /* widget officially loses ownership */
  291.     /* now inform widget */
  292.     if (ctx->loses) { 
  293.         if (ctx->incremental)  
  294.            (*(XtLoseSelectionIncrProc)ctx->loses)
  295.            (widget, &ctx->selection, ctx->owner_closure);
  296.         else  (*ctx->loses)(widget, &ctx->selection);
  297.     }
  298.     return(TRUE);
  299.     }
  300.     else return(FALSE);
  301. }
  302.  
  303. static XContext selectWindowContext = 0;
  304.  
  305. /* %%% Xlib.h should make this public! */
  306. typedef int (*xErrorHandler)(
  307. #if NeedFunctionPrototypes
  308.                Display*, XErrorEvent*
  309. #endif
  310.                );
  311.  
  312. static xErrorHandler oldErrorHandler = NULL;
  313. static unsigned long firstProtectRequest;
  314. static Window errorWindow;
  315.  
  316. static int LocalErrorHandler (dpy, error)
  317. Display *dpy;
  318. XErrorEvent *error;
  319. {
  320.     /* If BadWindow error on selection requestor, nothing to do but let
  321.      * the transfer timeout.  Otherwise, invoke saved error handler. */
  322.  
  323.     if (error->error_code == BadWindow && error->resourceid == errorWindow &&
  324.     error->serial >= firstProtectRequest)
  325.     return 0;
  326.  
  327.     if (oldErrorHandler == NULL) return 0;  /* should never happen */
  328.  
  329.     return (*oldErrorHandler)(dpy, error);
  330. }
  331.  
  332. static void StartProtectedSection(dpy, window)
  333.     Display *dpy;
  334.     Window window;
  335. {
  336.     /* protect ourselves against request window being destroyed
  337.      * before completion of transfer */
  338.  
  339.     oldErrorHandler = XSetErrorHandler(LocalErrorHandler);
  340.     firstProtectRequest = NextRequest(dpy);
  341.     errorWindow = window;
  342. }
  343.  
  344. static void EndProtectedSection(dpy)
  345.     Display *dpy;
  346. {
  347.     /* flush any generated errors on requestor and
  348.      * restore original error handler */
  349.  
  350.     XSync(dpy, False);
  351.  
  352.     XSetErrorHandler(oldErrorHandler);
  353.     oldErrorHandler = NULL;
  354. }
  355.  
  356. static void AddHandler(req, mask, proc, closure)
  357. Request req;
  358. EventMask mask;
  359. XtEventHandler proc;
  360. XtPointer closure;
  361. {
  362.     Display *dpy = req->ctx->dpy;
  363.     Window window = req->requestor;
  364.     Widget widget = XtWindowToWidget(dpy, window);
  365.  
  366.     if (widget != NULL) req->widget = widget;
  367.     else widget = req->widget;
  368.  
  369.     if (XtWindow(widget) == window)
  370.     XtAddEventHandler(widget, mask, False, proc, closure);
  371.     else {
  372.     RequestWindowRec *requestWindowRec;
  373.     if (selectWindowContext == 0)
  374.         selectWindowContext = XUniqueContext();
  375.     if (XFindContext(dpy, window, selectWindowContext,
  376.              (XPointer *)&requestWindowRec)) {
  377.         requestWindowRec = XtNew(RequestWindowRec);
  378.         requestWindowRec->active_transfer_count = 0;
  379.         (void)XSaveContext(dpy, window, selectWindowContext,
  380.                    (char *)requestWindowRec);
  381.     }
  382.     if (requestWindowRec->active_transfer_count++ == 0) {
  383.         _XtRegisterWindow(window, widget);
  384.         XSelectInput(dpy, window, mask);
  385.     }
  386.     XtAddRawEventHandler(widget, mask, FALSE, proc, closure);
  387.     }
  388. }
  389.  
  390. static void RemoveHandler(req, mask, proc, closure)
  391. Request req;
  392. EventMask mask;
  393. XtEventHandler proc;
  394. XtPointer closure;
  395. {
  396.     Display *dpy = req->ctx->dpy;
  397.     Window window = req->requestor;
  398.     Widget widget = req->widget;
  399.  
  400.     if ((XtWindowToWidget(dpy, window) == widget) && 
  401.         (XtWindow(widget) != window)) {
  402.     /* we had to hang this window onto our widget; take it off */
  403.     RequestWindowRec* requestWindowRec;
  404.     XtRemoveRawEventHandler(widget, mask, TRUE, proc, closure);
  405.     (void)XFindContext(dpy, window, selectWindowContext,
  406.                (XPointer *)&requestWindowRec);
  407.     if (--requestWindowRec->active_transfer_count == 0) {
  408.         _XtUnregisterWindow(window, widget);
  409.         StartProtectedSection(dpy, window);
  410.         XSelectInput(dpy, window, 0L);
  411.         EndProtectedSection(dpy);
  412.  
  413.         (void)XDeleteContext(dpy, window, selectWindowContext);
  414.         XtFree((char*)requestWindowRec);
  415.     }
  416.     } else {
  417.         XtRemoveEventHandler(widget, mask, TRUE,  proc, closure); 
  418.     }
  419. }
  420.  
  421. /* ARGSUSED */
  422. static void OwnerTimedOut(closure, id)
  423. XtPointer closure;
  424. XtIntervalId   *id;
  425. {
  426.     Request req = (Request)closure;
  427.     Select ctx = req->ctx;
  428.  
  429.     if (ctx->incremental && (ctx->owner_cancel != NULL)) {
  430.     (*ctx->owner_cancel)(ctx->widget, &ctx->selection, 
  431.                  &req->target, (XtRequestId*)&req,
  432.                  ctx->owner_closure);
  433.     } else {
  434.     if (ctx->notify == NULL)
  435.         XtFree((char*)req->value);
  436.     else {
  437.         /* the requestor hasn't deleted the property, but
  438.          * the owner needs to free the value.
  439.          */
  440.         if (ctx->incremental)
  441.         (*(XtSelectionDoneIncrProc)ctx->notify)
  442.                   (ctx->widget, &ctx->selection, &req->target, 
  443.                    (XtRequestId*)&req, ctx->owner_closure);
  444.         else
  445.         (*ctx->notify)(ctx->widget, &ctx->selection, &req->target);
  446.     }
  447.     }
  448.  
  449.     RemoveHandler(req, (EventMask)PropertyChangeMask,
  450.           HandlePropertyGone, closure); 
  451.     XtFree((char*)req);
  452.     if (--ctx->ref_count == 0 && ctx->free_when_done)
  453.     XtFree((char*)ctx);
  454. }
  455.  
  456. static void SendIncrement(incr)
  457.     Request incr;
  458. {
  459.     Display *dpy = incr->ctx->dpy;
  460.  
  461.     int incrSize = MAX_SELECTION_INCR(dpy);
  462.     if (incrSize >  incr->bytelength - incr->offset)
  463.         incrSize = incr->bytelength - incr->offset;
  464.     StartProtectedSection(dpy, incr->requestor);
  465.     XChangeProperty(dpy, incr->requestor, incr->property, 
  466.             incr->type, incr->format, PropModeReplace, 
  467.         (unsigned char *)incr->value + incr->offset,
  468.         NUMELEM(incrSize, incr->format));
  469.     EndProtectedSection(dpy);
  470.     incr->offset += incrSize;
  471. }
  472.  
  473. static AllSent(req)
  474. Request req;
  475. {
  476.     Select ctx = req->ctx;
  477.     StartProtectedSection(ctx->dpy, req->requestor);
  478.     XChangeProperty(ctx->dpy, req->requestor, 
  479.             req->property, req->type,  req->format, 
  480.             PropModeReplace, (unsigned char *) NULL, 0);
  481.     EndProtectedSection(ctx->dpy);
  482.     req->allSent = TRUE;
  483.  
  484.     if (ctx->notify == NULL) XtFree((char*)req->value);
  485. }
  486.  
  487. /*ARGSUSED*/
  488. static void HandlePropertyGone(widget, closure, ev)
  489. Widget widget;
  490. XtPointer closure;
  491. XEvent *ev;
  492. {
  493.     XPropertyEvent *event = (XPropertyEvent *) ev;
  494.     Request req = (Request)closure;
  495.     Select ctx = req->ctx;
  496.  
  497.     if ((event->type != PropertyNotify) ||
  498.         (event->state != PropertyDelete) ||
  499.     (event->atom != req->property) ||
  500.     (event->window != req->requestor))
  501.       return;
  502. #ifndef DEBUG_WO_TIMERS
  503.     XtRemoveTimeOut(req->timeout);
  504. #endif 
  505.     if (req->allSent) { 
  506.     if (ctx->notify)  
  507.         if (ctx->incremental) {
  508.         (*(XtSelectionDoneIncrProc)ctx->notify)
  509.                   (ctx->widget, &ctx->selection, &req->target,
  510.                    (XtRequestId*)&req, ctx->owner_closure);
  511.         }
  512.         else (*ctx->notify)(ctx->widget, &ctx->selection, &req->target);
  513.     RemoveHandler(req, (EventMask)PropertyChangeMask,
  514.               HandlePropertyGone, closure); 
  515.     XtFree((char*)req);
  516.     if (--ctx->ref_count == 0 && ctx->free_when_done)
  517.         XtFree((char*)ctx);
  518.     } else  { /* is this part of an incremental transfer? */ 
  519.     if (ctx->incremental) {
  520.          if (req->bytelength == 0)
  521.         AllSent(req);
  522.          else {
  523.         unsigned long size = MAX_SELECTION_INCR(ctx->dpy);
  524.             SendIncrement(req);
  525.         (*(XtConvertSelectionIncrProc)ctx->convert)
  526.                (ctx->widget, &ctx->selection, &req->target, 
  527.                 &req->type, &req->value, 
  528.                 &req->bytelength, &req->format,
  529.                 &size, ctx->owner_closure, (XtPointer*)&req);
  530.         if (req->bytelength)
  531.             req->bytelength = BYTELENGTH(req->bytelength, req->format);
  532.         req->offset = 0;
  533.         }
  534.     } else {
  535.         if (req->offset < req->bytelength) 
  536.         SendIncrement(req);
  537.         else AllSent(req);
  538.     }
  539. #ifndef DEBUG_WO_TIMERS
  540.     {
  541.       XtAppContext app = XtWidgetToApplicationContext(req->widget);
  542.       req->timeout = XtAppAddTimeOut(app,
  543.              app->selectionTimeout, OwnerTimedOut, (XtPointer)req);
  544.     }
  545. #endif 
  546.     }
  547. }
  548.  
  549. static PrepareIncremental(req, widget, window, property, target, 
  550.      targetType, value, length, format)
  551. Request req;
  552. Widget widget;
  553. Window window;
  554. Atom target;
  555. Atom property;
  556. Atom targetType;
  557. XtPointer value;
  558. unsigned long length;
  559. int format;
  560. {
  561.     unsigned long size;
  562.  
  563.     req->requestor = window;
  564.     req->type = targetType;
  565.     req->property = property;
  566.     req->value = value;
  567.     req->bytelength = BYTELENGTH(length,format);
  568.     req->format = format;
  569.     req->offset = 0;
  570.     req->target = target;
  571.     req->widget = widget;
  572.     req->allSent = FALSE;
  573. #ifndef DEBUG_WO_TIMERS
  574.     {
  575.     XtAppContext app = XtWidgetToApplicationContext(widget);
  576.     req->timeout = XtAppAddTimeOut(app,
  577.              app->selectionTimeout, OwnerTimedOut, (XtPointer)req);
  578.     }
  579. #endif 
  580.     AddHandler(req, (EventMask)PropertyChangeMask, 
  581.            HandlePropertyGone, (XtPointer)req);
  582. /* now send client INCR property */
  583.     size = BYTELENGTH(length,format);
  584.     value = ((char*)&size) + sizeof(long) - 4;
  585.     XChangeProperty(req->ctx->dpy, window, req->property,
  586.             req->ctx->prop_list->incr_atom,
  587.             32, PropModeReplace, (unsigned char *)value, 1);
  588. }
  589.  
  590. static Boolean GetConversion(ctx, event, target, property, widget, incremental)
  591. Select ctx;            /* logical owner */
  592. XSelectionRequestEvent* event;
  593. Atom target;
  594. Atom property;            /* requestor's property */
  595. Widget widget;            /* physical owner (receives events) */
  596. Boolean *incremental;
  597. {
  598.     XtPointer value = NULL;
  599.     unsigned long length;
  600.     int format;
  601.     Atom targetType;
  602.     Request req = XtNew(RequestRec);
  603.     Boolean timestamp_target = (target == ctx->prop_list->timestamp_atom);
  604.  
  605.     req->ctx = ctx;
  606.     req->event = *event;
  607.  
  608.     if (timestamp_target) {
  609.     value = XtMalloc(4);
  610.     if (sizeof(long) == 4)
  611.         *(long*)value = ctx->time;
  612.     else {
  613.         /* NOTREACHED */ /* sizeof(long)!=4 */
  614.         long temp = ctx->time;
  615.         bcopy( ((char*)&temp)+sizeof(long)-4, value, 4);
  616.     }
  617.     targetType = XA_INTEGER;
  618.     length = 1;
  619.     format = 32;
  620.     }
  621.     else {
  622.     ctx->ref_count++;
  623.     if (ctx->incremental == TRUE) {
  624.          unsigned long size = MAX_SELECTION_INCR(ctx->dpy);
  625.          if ((*(XtConvertSelectionIncrProc)ctx->convert)
  626.                    (ctx->widget, &event->selection, &target,
  627.                 &targetType, &value, &length, &format,
  628.                 &size, ctx->owner_closure, (XtRequestId*)&req)
  629.              == FALSE) {
  630.          XtFree((char*)req);
  631.          ctx->ref_count--;
  632.          return(FALSE);
  633.          }
  634.          StartProtectedSection(ctx->dpy, event->requestor);
  635.          PrepareIncremental(req, widget, event->requestor, property,
  636.                 target, targetType, value, length, format);
  637.          *incremental = True;
  638.          return(TRUE);
  639.     }
  640.     ctx->req = req;
  641.     if ((*ctx->convert)(ctx->widget, &event->selection, &target,
  642.                 &targetType, &value, &length, &format) == FALSE) {
  643.         XtFree((char*)req);
  644.         ctx->req = NULL;
  645.         ctx->ref_count--;
  646.         return(FALSE);
  647.     }
  648.     ctx->req = NULL;
  649.     }
  650.     StartProtectedSection(ctx->dpy, event->requestor);
  651.     if (BYTELENGTH(length,format) <= MAX_SELECTION_INCR(ctx->dpy)) {
  652.     if (! timestamp_target) {
  653.         if (ctx->notify != NULL) {
  654.           req->target = target;
  655.           req->property = property;
  656.           req->widget = widget;
  657.           req->requestor = event->requestor;
  658.           req->allSent = TRUE;
  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.               AddHandler(req, (EventMask)PropertyChangeMask, 
  667.                  HandlePropertyGone, (XtPointer)req);
  668.           }
  669.           else ctx->ref_count--;
  670.         }
  671.     XChangeProperty(ctx->dpy, event->requestor, property, 
  672.             targetType, format, PropModeReplace,
  673.             (unsigned char *)value, (int)length);
  674.     /* free storage for client if no notify proc */
  675.     if (timestamp_target || ctx->notify == NULL) {
  676.         XtFree((char*)value);
  677.         XtFree((char*)req);
  678.     }
  679.     *incremental = FALSE;
  680.     } else {
  681.      PrepareIncremental(req, widget, event->requestor, property,
  682.                 target, targetType, value, length, format);
  683.      *incremental = True;
  684.     }
  685.     return(TRUE);
  686. }
  687.  
  688. /*ARGSUSED*/
  689. static void HandleSelectionEvents(widget, closure, event, cont)
  690. Widget widget;
  691. XtPointer closure;
  692. XEvent *event;
  693. Boolean *cont;
  694. {
  695.     Select ctx;
  696.     XSelectionEvent ev;
  697.     Boolean incremental;
  698.     Atom target;
  699.     int count;
  700.     Boolean writeback = FALSE;
  701.  
  702.     ctx = (Select) closure;
  703.     switch (event->type) {
  704.       case SelectionClear:
  705.     /* if this event is not for the selection we registered for,
  706.      * don't do anything */
  707.     if (ctx->selection != event->xselectionclear.selection ||
  708.         ctx->serial > event->xselectionclear.serial)
  709.         break;
  710.     (void) LoseSelection(ctx, widget, event->xselectionclear.selection,
  711.             event->xselectionclear.time);
  712.     break;
  713.       case SelectionRequest:
  714.     /* if this event is not for the selection we registered for,
  715.      * don't do anything */
  716.     if (ctx->selection != event->xselectionrequest.selection)
  717.         break;
  718.     ev.type = SelectionNotify;
  719.     ev.display = event->xselectionrequest.display;
  720.     ev.requestor = event->xselectionrequest.requestor;
  721.     ev.selection = event->xselectionrequest.selection;
  722.     ev.time = event->xselectionrequest.time;
  723.     ev.target = event->xselectionrequest.target;
  724.     if (event->xselectionrequest.property == None) /* obsolete requestor */
  725.        event->xselectionrequest.property = event->xselectionrequest.target;
  726.     if (ctx->widget != widget || ctx->was_disowned
  727.        || ((event->xselectionrequest.time != CurrentTime)
  728.             && (event->xselectionrequest.time < ctx->time)))
  729.         ev.property = None;
  730.          else {
  731.        if (ev.target == ctx->prop_list->indirect_atom) {
  732.           IndirectPair *p;
  733.           int format;
  734.           unsigned long bytesafter, length;
  735.           unsigned char *value;
  736.           ev.property = event->xselectionrequest.property;
  737.           StartProtectedSection(ev.display, ev.requestor);
  738.           (void) XGetWindowProperty(ev.display, ev.requestor,
  739.             event->xselectionrequest.property, 0L, 1000000,
  740.             False,(Atom)AnyPropertyType, &target, &format, &length,
  741.             &bytesafter, &value);
  742.           count = BYTELENGTH(length, format) / sizeof(IndirectPair);
  743.           for (p = (IndirectPair *)value; count; p++, count--) {
  744.           EndProtectedSection(ctx->dpy);
  745.           if (!GetConversion(ctx, (XSelectionRequestEvent*)event,
  746.                      p->target, p->property, widget,
  747.                      &incremental)) {
  748.  
  749.             p->property = None;
  750.             writeback = TRUE;
  751.             StartProtectedSection(ctx->dpy, ev.requestor);
  752.           }
  753.           }
  754.           if (writeback)
  755.         XChangeProperty(ev.display, ev.requestor, 
  756.             event->xselectionrequest.property, target,
  757.             format, PropModeReplace, value, (int)length);
  758.           XFree((char *)value);
  759.       } else /* not multiple */ {
  760.            if (GetConversion(ctx, (XSelectionRequestEvent*)event,
  761.                  event->xselectionrequest.target,
  762.                  event->xselectionrequest.property,
  763.                  widget, &incremental))
  764.            ev.property = event->xselectionrequest.property;
  765.            else {
  766.            ev.property = None;
  767.            StartProtectedSection(ctx->dpy, ev.requestor);
  768.            }
  769.        }
  770.       }
  771.       (void) XSendEvent(ctx->dpy, ev.requestor, False, (unsigned long)NULL,
  772.            (XEvent *) &ev);
  773.  
  774.       EndProtectedSection(ctx->dpy);
  775.  
  776.       break;
  777.     }
  778. }
  779.  
  780. static Boolean OwnSelection(widget, selection, time, convert, lose, notify,
  781.                 cancel, closure, incremental)
  782. Widget widget;
  783. Atom selection;
  784. Time time;
  785. XtConvertSelectionProc convert;
  786. XtLoseSelectionProc lose;
  787. XtSelectionDoneProc notify;
  788. XtCancelConvertSelectionProc cancel;
  789. XtPointer closure;
  790. Boolean incremental;
  791. {
  792.     Select ctx;
  793.     Select oldctx = NULL;
  794.  
  795.     if (!XtIsRealized(widget)) return False;
  796.  
  797.     ctx = FindCtx(XtDisplay(widget), selection);
  798.     if (ctx->widget != widget || ctx->time != time ||
  799.     ctx->ref_count || ctx->was_disowned)
  800.     {
  801.     Boolean replacement = FALSE;
  802.     Window window = XtWindow(widget);
  803.     unsigned long serial = XNextRequest(ctx->dpy);
  804.         XSetSelectionOwner(ctx->dpy, selection, window, time);
  805.         if (XGetSelectionOwner(ctx->dpy, selection) != window)
  806.         return FALSE;
  807.     if (ctx->ref_count) {    /* exchange is in-progress */
  808. #ifdef DEBUG_ACTIVE
  809.         printf( "Active exchange for widget \"%s\"; selection=0x%x, ref_count=%d\n",
  810.             XtName(widget), (long)selection, ctx->ref_count );
  811. #endif
  812.         if (ctx->widget != widget ||
  813.         ctx->convert != convert ||
  814.         ctx->loses != lose ||
  815.         ctx->notify != notify ||
  816.         ctx->owner_cancel != cancel ||
  817.         ctx->incremental != incremental ||
  818.         ctx->owner_closure != closure)
  819.         {
  820.         if (ctx->widget == widget) {
  821.             XtRemoveEventHandler(widget, (EventMask)0, TRUE,
  822.                     HandleSelectionEvents, (XtPointer)ctx);
  823.             XtRemoveCallback(widget, XtNdestroyCallback,
  824.                      WidgetDestroyed, (XtPointer)ctx);
  825.             replacement = TRUE;
  826.         }
  827.         else if (!ctx->was_disowned) {
  828.             oldctx = ctx;
  829.         }
  830.         ctx->free_when_done = TRUE;
  831.         ctx = NewContext(XtDisplay(widget), selection);
  832.         }
  833.         else if (!ctx->was_disowned) { /* current owner is new owner */
  834.         ctx->time = time;
  835.         return TRUE;
  836.         }
  837.     }
  838.         if (ctx->widget != widget || ctx->was_disowned || replacement) {
  839.         if (ctx->widget && !ctx->was_disowned && !replacement) {
  840.         oldctx = ctx;
  841.         oldctx->free_when_done = TRUE;
  842.         ctx = NewContext(XtDisplay(widget), selection);
  843.         }
  844.         XtAddEventHandler(widget, (EventMask)0, TRUE,
  845.                   HandleSelectionEvents, (XtPointer)ctx);
  846.         XtAddCallback(widget, XtNdestroyCallback,
  847.               WidgetDestroyed, (XtPointer)ctx);
  848.     }
  849.     ctx->widget = widget;    /* Selection offically changes hands. */
  850.     ctx->time = time;
  851.     ctx->serial = serial;
  852.     }
  853.     ctx->convert = convert;
  854.     ctx->loses = lose;
  855.     ctx->notify = notify;
  856.     ctx->owner_cancel = cancel;
  857.     ctx->incremental = incremental;
  858.     ctx->owner_closure = closure;
  859.     ctx->was_disowned = FALSE;
  860.  
  861.     /* Defer calling the previous selection owner's lose selection procedure
  862.      * until the new selection is established, to allow the previous 
  863.      * selection owner to ask for the new selection to be converted in 
  864.      * the lose selection procedure.  The context pointer is the closure
  865.      * of the event handler and the destroy callback, so the old context
  866.      * pointer and the record contents must be preserved for LoseSelection.
  867.      */
  868.     if (oldctx) {
  869.     (void) LoseSelection(oldctx, oldctx->widget, selection, oldctx->time);
  870.     if (!oldctx->ref_count && oldctx->free_when_done)
  871.         XtFree((char*)oldctx);
  872.     }
  873.     return TRUE;
  874. }
  875.  
  876.  
  877. Boolean XtOwnSelection(widget, selection, time, convert, lose, notify)
  878. Widget widget;
  879. Atom selection;
  880. Time time;
  881. XtConvertSelectionProc convert;
  882. XtLoseSelectionProc lose;
  883. XtSelectionDoneProc notify;
  884. {
  885.     return OwnSelection(widget, selection, time, convert, lose, notify,
  886.             (XtCancelConvertSelectionProc)NULL,
  887.             (XtPointer)NULL, FALSE);
  888. }
  889.  
  890.  
  891. Boolean XtOwnSelectionIncremental(widget, selection, time, convert, 
  892.                   lose, notify, cancel, closure)
  893. Widget widget;
  894. Atom selection;
  895. Time time;
  896. XtConvertSelectionIncrProc convert;
  897. XtLoseSelectionIncrProc lose;
  898. XtSelectionDoneIncrProc notify;
  899. XtCancelConvertSelectionProc cancel;
  900. XtPointer closure;
  901. {
  902.     return OwnSelection(widget, selection, time, 
  903.             (XtConvertSelectionProc)convert, 
  904.             (XtLoseSelectionProc)lose,
  905.             (XtSelectionDoneProc)notify,
  906.             cancel, closure, TRUE);
  907. }
  908.  
  909.  
  910. void XtDisownSelection(widget, selection, time)
  911. Widget widget;
  912. Atom selection;
  913. Time time;
  914. {
  915.     Select ctx;
  916.     ctx = FindCtx(XtDisplay(widget), selection);
  917.     if (LoseSelection(ctx, widget, selection, time))
  918.     XSetSelectionOwner(XtDisplay(widget), selection, None, time);
  919. }
  920.  
  921. /* Selection Requestor code */
  922.  
  923. static Boolean IsINCRtype(info, window, prop)
  924.     CallBackInfo info;
  925.     Window window;
  926.     Atom prop;
  927. {
  928.     unsigned long bytesafter;
  929.     unsigned long length;
  930.     int format;
  931.     Atom type;
  932.     unsigned char *value;
  933.  
  934.     if (prop == None) return False;
  935.  
  936.     (void)XGetWindowProperty(XtDisplay(info->widget), window, prop, 0L, 0L,
  937.                  False, info->ctx->prop_list->incr_atom,
  938.                  &type, &format, &length, &bytesafter, &value);
  939.  
  940.     return (type == info->ctx->prop_list->incr_atom);
  941. }
  942.  
  943. /*ARGSUSED*/
  944. static void ReqCleanup(widget, closure, ev, cont)
  945. Widget widget;
  946. XtPointer closure;
  947. XEvent *ev;
  948. Boolean *cont;
  949. {
  950.     CallBackInfo info = (CallBackInfo)closure;
  951.     unsigned long bytesafter, length;
  952.     char *value;
  953.     int format;
  954.     Atom target;
  955.  
  956.     if (ev->type == SelectionNotify) {
  957.     XSelectionEvent *event = (XSelectionEvent *) ev;
  958.     if (!MATCH_SELECT(event, info)) return; /* not really for us */
  959.          XtRemoveEventHandler(widget, (EventMask)0, TRUE,
  960.                ReqCleanup, (XtPointer) info );
  961.     if (IsINCRtype(info, XtWindow(widget), event->property)
  962. #ifdef DRAFT_ICCCM_COMPATIBILITY
  963.         || event->target == info->ctx->prop_list->incremental_atom
  964. #endif
  965.         ) {
  966.         info->proc = HandleGetIncrement;
  967.         XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask, 
  968.                   FALSE, ReqCleanup, (XtPointer) info);
  969.     } else {
  970.        if (event->property != None) 
  971.         XDeleteProperty(event->display, XtWindow(widget),
  972.                 event->property);
  973.            FreeSelectionProperty(XtDisplay(widget), info->property);
  974.        XtFree((char*)info->req_closure);
  975.        XtFree((char*)info->target);
  976.            XtFree((char*)info);
  977.     }
  978.     } else if ((ev->type == PropertyNotify) &&
  979.         (ev->xproperty.state == PropertyNewValue) &&
  980.             (ev->xproperty.atom == info->property)) {
  981.     XPropertyEvent *event = (XPropertyEvent *) ev;
  982.         (void) XGetWindowProperty(event->display, XtWindow(widget), 
  983.                event->atom, 0L, 1000000, True, AnyPropertyType,
  984.                &target, &format, &length, &bytesafter, 
  985.                (unsigned char **) &value);
  986.     XFree(value);
  987.     if (length == 0) {
  988.            XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
  989.                ReqCleanup, (XtPointer) info );
  990.            FreeSelectionProperty(XtDisplay(widget), info->property);
  991.        XtFree((char*)info->req_closure);
  992.        XtFree((char*)info->target);
  993.            XtFree((char*)info);
  994.     }
  995.     }
  996. }
  997.  
  998. /* ARGSUSED */
  999. static void ReqTimedOut(closure, id)
  1000. XtPointer closure;
  1001. XtIntervalId   *id;
  1002. {
  1003.     XtPointer value = NULL;
  1004.     unsigned long length = 0;
  1005.     int format = 8;
  1006.     Atom resulttype = XT_CONVERT_FAIL;
  1007.     CallBackInfo info = (CallBackInfo)closure;
  1008.     unsigned long bytesafter;
  1009.     unsigned long proplength;
  1010.     Atom type;
  1011.     IndirectPair *pairs;
  1012.     XtPointer *c;
  1013.  
  1014.     if (*info->target == info->ctx->prop_list->indirect_atom) {
  1015.         (void) XGetWindowProperty(XtDisplay(info->widget), 
  1016.                XtWindow(info->widget), info->property, 0L,
  1017.                10000000, True, AnyPropertyType, &type, &format,
  1018.                &proplength, &bytesafter, (unsigned char **) &pairs);
  1019.        XFree((char*)pairs);
  1020.        for (proplength = proplength / IndirectPairWordSize, c = info->req_closure;
  1021.                proplength; proplength--, c++) 
  1022.         (*info->callback)(info->widget, *c, 
  1023.             &info->ctx->selection, &resulttype, value, &length, &format);
  1024.     } else {
  1025.     (*info->callback)(info->widget, *info->req_closure, 
  1026.         &info->ctx->selection, &resulttype, value, &length, &format);
  1027.     }
  1028.  
  1029.     /* change event handlers for straggler events */
  1030.     if (info->proc == (XtEventHandler)HandleSelectionReplies) {
  1031.         XtRemoveEventHandler(info->widget, (EventMask)0, 
  1032.             TRUE, info->proc, (XtPointer) info);
  1033.     XtAddEventHandler(info->widget, (EventMask)0, TRUE,
  1034.         ReqCleanup, (XtPointer) info);
  1035.     } else {
  1036.         XtRemoveEventHandler(info->widget,(EventMask) PropertyChangeMask, 
  1037.             FALSE, info->proc, (XtPointer) info);
  1038.     XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask, 
  1039.         FALSE, ReqCleanup, (XtPointer) info);
  1040.     }
  1041.  
  1042. }
  1043.  
  1044. /*ARGSUSED*/
  1045. static void HandleGetIncrement(widget, closure, ev, cont)
  1046. Widget widget;
  1047. XtPointer closure;
  1048. XEvent *ev;
  1049. Boolean *cont;
  1050. {
  1051.     XPropertyEvent *event = (XPropertyEvent *) ev;
  1052.     CallBackInfo info = (CallBackInfo) closure;
  1053.     Select ctx = info->ctx; 
  1054.     char *value;
  1055.     unsigned long bytesafter;
  1056.     unsigned long length;
  1057.     int bad;
  1058.  
  1059.     if ((event->state != PropertyNewValue) || (event->atom != info->property))
  1060.      return;
  1061.  
  1062.     bad = XGetWindowProperty(event->display, XtWindow(widget),
  1063.                  event->atom, 0L,
  1064.                  10000000, True, AnyPropertyType, &info->type, 
  1065.                  &info->format, &length, &bytesafter, 
  1066.                  (unsigned char **) &value);
  1067.     if (bad) 
  1068.       return;
  1069. #ifndef DEBUG_WO_TIMERS
  1070.     XtRemoveTimeOut(info->timeout); 
  1071. #endif 
  1072.     if (length == 0) {
  1073.        unsigned long u_offset = info->offset;
  1074.        (*info->callback)(widget, *info->req_closure, &ctx->selection, 
  1075.               &info->type, 
  1076.               (info->offset == 0 ? value : info->value), 
  1077.               &u_offset, &info->format);
  1078.        if (info->offset = u_offset) XFree(value);
  1079.        XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask, FALSE, 
  1080.         HandleGetIncrement, (XtPointer) info);
  1081.        FreeSelectionProperty(event->display, info->property);
  1082.        XtFree((char*)info->req_closure);
  1083.        XtFree((char*)info->target);
  1084.        XtFree((char*)info);
  1085.     } else { /* add increment to collection */
  1086.       if (info->incremental) {
  1087. #ifdef XT_COPY_SELECTION
  1088.       int size = BYTELENGTH(length, info->format) + 1;
  1089.       char *tmp = XtMalloc((Cardinal) size);
  1090.       bcopy(value, tmp, size);
  1091.       XFree(value);
  1092.       value = tmp;
  1093. #endif
  1094.         (*info->callback)(widget, *info->req_closure, &ctx->selection, 
  1095.               &info->type, value, &length, &info->format);
  1096.       } else {
  1097.           if ((BYTELENGTH(length,info->format)+info->offset) 
  1098.             > info->bytelength) {
  1099.           unsigned int bytes;
  1100. #ifdef DRAFT_ICCCM_COMPATIBILITY 
  1101.           /* Handle Incremental can be called with a size of 0 */
  1102.           bytes = ((info->bytelength) 
  1103.                ? (info->bytelength *= 2)
  1104.                : (info->bytelength = 
  1105.               BYTELENGTH(length, info->format) + info->offset));
  1106. #else
  1107.           bytes = (info->bytelength *= 2);
  1108. #endif
  1109.           info->value = XtRealloc(info->value, bytes);
  1110.           }
  1111.           bcopy(value, &info->value[info->offset], 
  1112.         (int) BYTELENGTH(length, info->format));
  1113.           info->offset += BYTELENGTH(length, info->format);
  1114.          XFree(value);
  1115.      }
  1116.      /* reset timer */
  1117. #ifndef DEBUG_WO_TIMERS
  1118.      {
  1119.      XtAppContext app = XtWidgetToApplicationContext(info->widget);
  1120.      info->timeout = XtAppAddTimeOut(app,
  1121.              app->selectionTimeout, ReqTimedOut, (XtPointer) info);
  1122.      }
  1123. #endif 
  1124.    }
  1125. }
  1126.  
  1127.  
  1128. static HandleNone(widget, callback, closure, selection)
  1129. Widget widget;
  1130. XtSelectionCallbackProc callback;
  1131. XtPointer closure;
  1132. Atom selection;
  1133. {
  1134.     unsigned long length = 0;
  1135.     int format = 8;
  1136.     Atom type = None;
  1137.  
  1138.     (*callback)(widget, closure, &selection, 
  1139.         &type, NULL, &length, &format);
  1140. }
  1141.  
  1142.  
  1143. static long IncrPropSize(widget, value, format, length)
  1144.      Widget widget;
  1145.      unsigned char* value;
  1146.      int format;
  1147.      unsigned long length;
  1148. {
  1149.     unsigned long size;
  1150.     if (format == 32
  1151. #ifdef DRAFT_ICCCM_COMPATIBILITY
  1152.     /* old code was Endian-dependent; don't bother trying to fix it! */
  1153.     || (format == 8 && length == 4)
  1154. #endif
  1155.     ) {
  1156.     if (sizeof(long) == 4)
  1157.         size = ((long*)value)[length-1]; /* %%% what order for longs? */
  1158.     else {
  1159.         /* NOTREACHED */ /* sizeof(long)!=4 */
  1160.         size = 0;
  1161.         bcopy(value+4*(length-1), ((char*)&size)+sizeof(long)-4, 4);
  1162.     }
  1163.     return size;
  1164.     }
  1165.     else {
  1166.     XtAppWarningMsg( XtWidgetToApplicationContext(widget),
  1167.             "badFormat","xtGetSelectionValue",XtCXtToolkitError,
  1168.     "Selection owner returned type INCR property with format != 32",
  1169.             (String*)NULL, (Cardinal*)NULL );
  1170.     return 0;
  1171.     }
  1172. }
  1173.  
  1174.  
  1175. static
  1176. Boolean HandleNormal(dpy, widget, property, info, closure, selection)
  1177. Display *dpy;
  1178. Widget widget;
  1179. Atom property;
  1180. CallBackInfo info;
  1181. XtPointer closure;
  1182. Atom selection;
  1183. {
  1184.     unsigned long bytesafter;
  1185.     unsigned long length;
  1186.     int format;
  1187.     Atom type;
  1188.     unsigned char *value;
  1189.  
  1190.     (void) XGetWindowProperty(dpy, XtWindow(widget), property, 0L,
  1191.                   10000000, False, AnyPropertyType,
  1192.                   &type, &format, &length, &bytesafter, &value);
  1193.  
  1194.     if (type == info->ctx->prop_list->incr_atom) {
  1195.     unsigned long size = IncrPropSize(widget, value, format, length);
  1196.     XFree((char *)value);
  1197.     HandleIncremental(dpy, widget, property, info, size);
  1198.     return FALSE;
  1199.     }
  1200.  
  1201.     XDeleteProperty(dpy, XtWindow(widget), property);
  1202. #ifdef XT_COPY_SELECTION
  1203.     if (value) {   /* it could have been deleted after the SelectionNotify */
  1204.     int size = BYTELENGTH(length, info->format) + 1;
  1205.     char *tmp = XtMalloc((Cardinal) size);
  1206.     bcopy(value, tmp, size);
  1207.     XFree(value);
  1208.     value = (unsigned char *) tmp;
  1209.     }
  1210. #endif
  1211.     (*info->callback)(widget, closure, &selection, 
  1212.               &type, (XtPointer)value, &length, &format);
  1213.  
  1214.     if (info->incremental) {
  1215.     /* let requestor know the whole thing has been received */
  1216.     value = (unsigned char*)XtMalloc((unsigned)1);
  1217.     length = 0;
  1218.     (*info->callback)(widget, closure, &selection,
  1219.               &type, (XtPointer)value, &length, &format);
  1220.     }
  1221.     return TRUE;
  1222. }
  1223.  
  1224. static HandleIncremental(dpy, widget, property, info, size)
  1225. Display *dpy;
  1226. Widget widget;
  1227. Atom property;
  1228. CallBackInfo info;
  1229. unsigned long size;
  1230. {
  1231.     XtAddEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
  1232.           HandleGetIncrement, (XtPointer) info);
  1233.     
  1234.     /* now start the transfer */
  1235.     XDeleteProperty(dpy, XtWindow(widget), property);
  1236.     XFlush(dpy);
  1237.  
  1238.     info->bytelength = size;
  1239.     info->value = (char *) XtMalloc((unsigned) info->bytelength);
  1240.     info->offset = 0;
  1241.  
  1242.     /* reset the timer */
  1243.     info->proc = HandleGetIncrement;
  1244. #ifndef DEBUG_WO_TIMERS
  1245.     {
  1246.     XtAppContext app = XtWidgetToApplicationContext(info->widget);
  1247.     info->timeout = XtAppAddTimeOut(app,
  1248.              app->selectionTimeout, ReqTimedOut, (XtPointer) info);
  1249.     }
  1250. #endif 
  1251. }
  1252.  
  1253. #ifdef DRAFT_ICCCM_COMPATIBILITY
  1254. static unsigned long GetSizeOfIncr(widget, ctx, property)
  1255.      Widget widget;
  1256.      Select ctx;
  1257.      Atom property;
  1258. {
  1259.     /* assert( prop type is INCR ) */
  1260.     unsigned long bytesafter;
  1261.     unsigned long length;
  1262.     int format;
  1263.     Atom type;
  1264.     unsigned char *value;
  1265.     unsigned long size;
  1266.  
  1267.     (void)XGetWindowProperty( XtDisplay(widget), XtWindow(widget), property,
  1268.                   0L, 10000000, False, ctx->prop_list->incremental_atom,
  1269.                   &type, &format, &length, &bytesafter, &value);
  1270.  
  1271.     size = IncrPropSize(widget, value, format, length);
  1272.     XFree((char *)value);
  1273.     return size;
  1274. }
  1275. #endif
  1276.  
  1277.  
  1278. /*ARGSUSED*/
  1279. static void HandleSelectionReplies(widget, closure, ev, cont)
  1280. Widget widget;
  1281. XtPointer closure;
  1282. XEvent *ev;
  1283. Boolean *cont;
  1284. {
  1285.     XSelectionEvent *event = (XSelectionEvent *) ev;
  1286.     Display *dpy = event->display;
  1287.     CallBackInfo info = (CallBackInfo) closure;
  1288.     Select ctx = info->ctx;
  1289.     IndirectPair *pairs, *p;
  1290.     unsigned long bytesafter;
  1291.     unsigned long length;
  1292.     int format;
  1293.     Atom type;
  1294.     XtPointer *c;
  1295. #ifdef DRAFT_ICCCM_COMPATIBILITY
  1296.     Atom *t;
  1297. #endif
  1298.  
  1299.     if (event->type != SelectionNotify) return;
  1300.     if (!MATCH_SELECT(event, info)) return; /* not really for us */
  1301. #ifndef DEBUG_WO_TIMERS
  1302.     XtRemoveTimeOut(info->timeout); 
  1303. #endif 
  1304.     XtRemoveEventHandler(widget, (EventMask)0, TRUE,
  1305.         HandleSelectionReplies, (XtPointer) info );
  1306.     if (event->target == ctx->prop_list->indirect_atom) {
  1307.         (void) XGetWindowProperty(dpy, XtWindow(widget), info->property, 0L,
  1308.                10000000, True, AnyPropertyType, &type, &format,
  1309.                &length, &bytesafter, (unsigned char **) &pairs);
  1310. #ifdef DRAFT_ICCCM_COMPATIBILITY
  1311.        for (length = length / IndirectPairWordSize, p = pairs, 
  1312.            c = info->req_closure, t = info->target+1;
  1313.                length; length--, p++, c++, t++) {
  1314. #else
  1315.        for (length = length / IndirectPairWordSize, p = pairs,
  1316.            c = info->req_closure;
  1317.                length; length--, p++, c++) {
  1318. #endif
  1319.         if ((event->property == None) || (format != 32) || 
  1320.          (p->property == None)) {
  1321.         HandleNone(widget, info->callback, *c, event->selection);
  1322.         if (p->property != None)
  1323.                     FreeSelectionProperty(XtDisplay(widget), p->property);
  1324. #ifdef DRAFT_ICCCM_COMPATIBILITY
  1325.         } else if (p->target == ctx->prop_list->incremental_atom) {
  1326.         CallBackInfo newinfo = XtNew(CallBackInfoRec);
  1327.         newinfo->callback = info->callback;
  1328.         newinfo->req_closure = (XtPointer *)XtNew(XtPointer);
  1329.         *newinfo->req_closure = *c;
  1330.         newinfo->property = p->property;
  1331.         newinfo->widget = info->widget;
  1332.         newinfo->time = info->time;
  1333.         newinfo->target = (Atom *)XtNew(Atom);
  1334.         *newinfo->target = *t;
  1335.         newinfo->ctx = info->ctx;
  1336.         newinfo->incremental = info->incremental;
  1337.         HandleIncremental(dpy, widget, p->property, newinfo,
  1338.                   GetSizeOfIncr(widget, ctx, p->property)
  1339.                   );
  1340. #endif
  1341.         } else {
  1342.         if (HandleNormal(dpy, widget, p->property, info, *c, 
  1343.                  event->selection)) {
  1344.             FreeSelectionProperty(XtDisplay(widget), p->property);
  1345.         }
  1346.         }
  1347.        }
  1348.        XFree((char*)pairs);
  1349.        FreeSelectionProperty(dpy, info->property);
  1350.        XtFree((char*)info->req_closure); 
  1351.        XtFree((char*)info->target); 
  1352.        XtFree((char*)info);
  1353.     } else if (event->property == None) {
  1354.     HandleNone(widget, info->callback, *info->req_closure, event->selection);
  1355.         FreeSelectionProperty(XtDisplay(widget), info->property);
  1356.         XtFree((char*)info->req_closure);
  1357.         XtFree((char*)info->target); 
  1358.         XtFree((char*)info);
  1359. #ifdef DRAFT_ICCCM_COMPATIBILITY
  1360.     } else if (event->target == ctx->prop_list->incremental_atom) {
  1361.     HandleIncremental(dpy, widget, event->property, info, 0);
  1362. #endif
  1363.     } else {
  1364.     if (HandleNormal(dpy, widget, event->property, info, 
  1365.              *info->req_closure, event->selection)) {
  1366.         FreeSelectionProperty(XtDisplay(widget), info->property);
  1367.         XtFree((char*)info->req_closure);
  1368.         XtFree((char*)info->target); 
  1369.         XtFree((char*)info);
  1370.     }
  1371.     }
  1372. }
  1373.  
  1374. static DoLocalTransfer(req, selection, target, widget,
  1375.                callback, closure, incremental)
  1376. Request req;
  1377. Atom selection;
  1378. Atom target;
  1379. Widget widget;        /* The widget requesting the value. */
  1380. XtSelectionCallbackProc callback;
  1381. XtPointer closure;    /* the closure for the callback, not the conversion */
  1382. Boolean incremental;
  1383. {
  1384.     Select ctx = req->ctx;
  1385.     XtPointer value = NULL, temp, total = NULL;
  1386.     unsigned long length;
  1387.     int format;
  1388.     Atom resulttype;
  1389.     unsigned long totallength = 0;
  1390.  
  1391.     req->event.target = target;
  1392.  
  1393.     if (ctx->incremental) {
  1394.        unsigned long size = MAX_SELECTION_INCR(ctx->dpy);
  1395.        if (!(*(XtConvertSelectionIncrProc)ctx->convert)
  1396.                (ctx->widget, &selection, &target,
  1397.                 &resulttype, &value, &length, &format,
  1398.                 &size, ctx->owner_closure, (XtRequestId*)&req)) {
  1399.            HandleNone(widget, callback, closure, selection);
  1400.        }
  1401.        else {
  1402.         if (incremental) {
  1403.           Boolean allSent = FALSE;
  1404.               while (!allSent) {
  1405.                   if (ctx->notify && (value != NULL)) {
  1406.                           int bytelength = BYTELENGTH(length,format);
  1407.                     /* both sides think they own this storage */
  1408.                     temp = XtMalloc((unsigned)bytelength);
  1409.                     bcopy(value, temp, bytelength);
  1410.                     value = temp;
  1411.                   }
  1412.               /* use care; older clients were never warned that
  1413.                * they must return a value even if length==0
  1414.                */
  1415.              if (value == NULL) value = XtMalloc((unsigned)1);
  1416.              (*callback)(widget, closure, &selection, 
  1417.             &resulttype, value, &length, &format);
  1418.              if (length) {
  1419.              /* should owner be notified on end-of-piece?
  1420.               * Spec is unclear, but non-local transfers don't.
  1421.               */
  1422.              (*(XtConvertSelectionIncrProc)ctx->convert)
  1423.                     (ctx->widget, &selection, &target,
  1424.                      &resulttype, &value, &length, &format,
  1425.                      &size, ctx->owner_closure,
  1426.                      (XtRequestId*)&req);
  1427.              }
  1428.              else allSent = TRUE;
  1429.           }
  1430.             } else {
  1431.               while (length) {
  1432.             int bytelength = BYTELENGTH(length, format);
  1433.             total = XtRealloc(total, 
  1434.                 (unsigned) (totallength += bytelength));
  1435.             bcopy( value,
  1436.                (unsigned char*)total + totallength - bytelength,
  1437.                bytelength
  1438.               );
  1439.             (*(XtConvertSelectionIncrProc)ctx->convert)
  1440.                (ctx->widget, &selection, &target, 
  1441.                 &resulttype, &value, &length, &format,
  1442.                 &size, ctx->owner_closure, (XtRequestId*)&req);
  1443.           }
  1444.           if (total == NULL) total = XtMalloc(1);
  1445.           totallength = NUMELEM(totallength, format); 
  1446.           (*callback)(widget, closure, &selection, &resulttype, 
  1447.             total,  &totallength, &format);
  1448.           }
  1449.           if (ctx->notify) 
  1450.           (*(XtSelectionDoneIncrProc)ctx->notify)
  1451.                 (ctx->widget, &selection, &target, 
  1452.                  (XtRequestId*)&req, ctx->owner_closure);
  1453.           else XtFree((char*)value);
  1454.       }
  1455.     } else { /* not incremental owner */
  1456.       if (!(*ctx->convert)(ctx->widget, &selection, &target, 
  1457.                  &resulttype, &value, &length, &format)) {
  1458.         HandleNone(widget, callback, closure, selection);
  1459.       } else {
  1460.           if (ctx->notify && (value != NULL)) {
  1461.                 int bytelength = BYTELENGTH(length,format);
  1462.             /* both sides think they own this storage; better copy */
  1463.             temp = XtMalloc((unsigned)bytelength);
  1464.             bcopy(value, temp, bytelength);
  1465.             value = temp;
  1466.           }
  1467.           if (value == NULL) value = XtMalloc((unsigned)1);
  1468.           (*callback)(widget, closure, &selection, &resulttype, 
  1469.               value, &length, &format);
  1470.           if (ctx->notify)
  1471.              (*ctx->notify)(ctx->widget, &selection, &target);
  1472.       }
  1473.       }
  1474. }
  1475.  
  1476. static void GetSelectionValue(widget, selection, target, callback,
  1477.                   closure, time, incremental)
  1478. Widget widget;
  1479. Atom selection;
  1480. Atom target;
  1481. XtSelectionCallbackProc callback;
  1482. XtPointer closure;
  1483. Time time;
  1484. Boolean incremental;
  1485. {
  1486.     Select ctx;
  1487.     CallBackInfo info;
  1488.  
  1489.     ctx = FindCtx(XtDisplay(widget), selection);
  1490.     if (ctx->widget && !ctx->was_disowned) {
  1491.     RequestRec req;
  1492.     ctx->req = &req;
  1493.     req.ctx = ctx;
  1494.     req.event.type = 0;
  1495.     req.event.requestor = XtWindow(widget);
  1496.     req.event.time = time;
  1497.     ctx->ref_count++;
  1498.     DoLocalTransfer(&req, selection, target, widget,
  1499.             callback, closure, incremental);
  1500.     if (--ctx->ref_count == 0 && ctx->free_when_done)
  1501.         XtFree((char*)ctx);
  1502.     else
  1503.         ctx->req = NULL;
  1504.     }
  1505.     else {
  1506.     info = MakeInfo(ctx, callback, &closure, 1, widget,
  1507.             time, incremental);
  1508.     info->target = (Atom *)XtMalloc((unsigned) sizeof(Atom));
  1509.      *(info->target) = target;
  1510.     RequestSelectionValue(info, selection, target);
  1511.     }
  1512. }
  1513.  
  1514.  
  1515. void XtGetSelectionValue(widget, selection, target, callback, closure, time)
  1516. Widget widget;
  1517. Atom selection;
  1518. Atom target;
  1519. XtSelectionCallbackProc callback;
  1520. XtPointer closure;
  1521. Time time;
  1522. {
  1523.     GetSelectionValue(widget, selection, target, callback,
  1524.               closure, time, FALSE);
  1525. }
  1526.  
  1527.  
  1528. void XtGetSelectionValueIncremental(widget, selection, target, callback, 
  1529.                     closure, time)
  1530. Widget widget;
  1531. Atom selection;
  1532. Atom target;
  1533. XtSelectionCallbackProc callback;
  1534. XtPointer closure;
  1535. Time time;
  1536. {
  1537.     GetSelectionValue(widget, selection, target, callback, 
  1538.               closure, time, TRUE);
  1539. }
  1540.  
  1541.  
  1542. static void GetSelectionValues(widget, selection, targets, count, callback, 
  1543.                    closures, time, incremental)
  1544. Widget widget;
  1545. Atom selection;
  1546. Atom *targets;
  1547. int count;
  1548. XtSelectionCallbackProc callback;
  1549. XtPointer *closures;
  1550. Time time;
  1551. Boolean incremental;
  1552. {
  1553.     Select ctx;
  1554.     CallBackInfo info;
  1555.     IndirectPair *pairs, *p;
  1556.     Atom *t;
  1557.  
  1558.     if (count == 0) return;
  1559.     ctx = FindCtx(XtDisplay(widget), selection);
  1560.     if (ctx->widget && !ctx->was_disowned) {
  1561.     RequestRec req;
  1562.     ctx->req = &req;
  1563.     req.ctx = ctx;
  1564.     req.event.type = 0;
  1565.     req.event.requestor = XtWindow(widget);
  1566.     req.event.time = time;
  1567.     ctx->ref_count++;
  1568.     for (; count; count--, targets++, closures++ )
  1569.         DoLocalTransfer(&req, selection, *targets, widget,
  1570.                 callback, *closures, incremental);
  1571.     if (--ctx->ref_count == 0 && ctx->free_when_done)
  1572.         XtFree((char*)ctx);
  1573.     else
  1574.         ctx->req = NULL;
  1575.     } else {
  1576.     info = MakeInfo(ctx, callback, closures, count, widget,
  1577.             time, incremental);
  1578.     info->target = (Atom *)XtMalloc((unsigned) ((count+1) * sizeof(Atom)));
  1579.         (*info->target) = ctx->prop_list->indirect_atom;
  1580.     bcopy((char *) targets, (char *) info->target+sizeof(Atom),
  1581.           count * sizeof(Atom));
  1582.     pairs = (IndirectPair*)XtMalloc((unsigned)(count*sizeof(IndirectPair)));
  1583.     for (p = &pairs[count-1], t = &targets[count-1];
  1584.          p >= pairs;  p--, t-- ) {
  1585.        p->target = *t;
  1586.        p->property = GetSelectionProperty(XtDisplay(widget));
  1587.     }
  1588.     XChangeProperty(XtDisplay(widget), XtWindow(widget), 
  1589.             info->property, info->property,
  1590.             32, PropModeReplace, (unsigned char *) pairs, 
  1591.             count * IndirectPairWordSize);
  1592.     XtFree((char*)pairs);
  1593.     RequestSelectionValue(info, selection, ctx->prop_list->indirect_atom);
  1594.     }
  1595. }
  1596.  
  1597.  
  1598. void XtGetSelectionValues(widget, selection, targets, count, callback, 
  1599.     closures, time)
  1600. Widget widget;
  1601. Atom selection;
  1602. Atom *targets;
  1603. int count;
  1604. XtSelectionCallbackProc callback;
  1605. XtPointer *closures;
  1606. Time time;
  1607. {
  1608.     GetSelectionValues(widget, selection, targets, count, callback,
  1609.                closures, time, FALSE);
  1610. }
  1611.  
  1612.  
  1613. void XtGetSelectionValuesIncremental(widget, selection, targets, count, 
  1614.     callback, closures, time)
  1615. Widget widget;
  1616. Atom selection;
  1617. Atom *targets;
  1618. int count;
  1619. XtSelectionCallbackProc callback;
  1620. XtPointer *closures;
  1621. Time time;
  1622. {
  1623.     GetSelectionValues(widget, selection, targets, count, 
  1624.                callback, closures, time, TRUE);
  1625. }
  1626.  
  1627.  
  1628. XSelectionRequestEvent *XtGetSelectionRequest( widget, selection, id )
  1629.     Widget widget;
  1630.     Atom selection;
  1631.     XtRequestId id;
  1632.     Request req = (Request)id;
  1633.     Select ctx;
  1634.  
  1635.     if (   (req == NULL
  1636.         && ((ctx = FindCtx( XtDisplay(widget), selection )) == NULL
  1637.         || ctx->req == NULL
  1638.         || ctx->selection != selection
  1639.         || ctx->widget == NULL))
  1640.     || (req != NULL
  1641.         && (req->ctx == NULL
  1642.         || req->ctx->selection != selection
  1643.         || req->ctx->widget != widget)))
  1644.     {
  1645.     String params = XtName(widget);
  1646.     Cardinal num_params = 1;
  1647.     XtAppWarningMsg( XtWidgetToApplicationContext(widget),
  1648.              "notInConvertSelection", "xtGetSelectionRequest",
  1649.              XtCXtToolkitError,
  1650.              "XtGetSelectionRequest called for widget \"%s\" outside of ConvertSelection proc",
  1651.              ¶ms, &num_params
  1652.                );
  1653.     return NULL;
  1654.     }
  1655.  
  1656.     if (req == NULL) {
  1657.     /* non-incremental owner; only one request can be
  1658.      * outstanding at a time, so it's safe to keep ptr in ctx */
  1659. #ifdef lint
  1660.     ctx = NULL;
  1661. #endif
  1662.     req = ctx->req;
  1663.     }
  1664.  
  1665.     if (req->event.type == 0) {
  1666.     /* owner is local; construct the remainder of the event */
  1667.     req->event.type = SelectionRequest;
  1668.     req->event.serial = LastKnownRequestProcessed(XtDisplay(widget));
  1669.     req->event.send_event = True;
  1670.     req->event.display = XtDisplay(widget);
  1671.     req->event.owner = XtWindow(req->ctx->widget);
  1672.     /*  req->event.requestor = XtWindow(requesting_widget); */
  1673.     req->event.selection = selection;
  1674.     /*  req->event.target = requestors_target; */
  1675.     req->event.property = None; /* %%% what to do about side-effects? */
  1676.     /*  req->event.time = requestors_time; */
  1677.     }
  1678.     return &req->event;
  1679. }
  1680.