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