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