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