1444c061aSmrg/***********************************************************
2fdf6a26fSmrgCopyright (c) 1993, Oracle and/or its affiliates.
31477040fSmrg
41477040fSmrgPermission is hereby granted, free of charge, to any person obtaining a
51477040fSmrgcopy of this software and associated documentation files (the "Software"),
61477040fSmrgto deal in the Software without restriction, including without limitation
71477040fSmrgthe rights to use, copy, modify, merge, publish, distribute, sublicense,
81477040fSmrgand/or sell copies of the Software, and to permit persons to whom the
91477040fSmrgSoftware is furnished to do so, subject to the following conditions:
101477040fSmrg
111477040fSmrgThe above copyright notice and this permission notice (including the next
121477040fSmrgparagraph) shall be included in all copies or substantial portions of the
131477040fSmrgSoftware.
141477040fSmrg
151477040fSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
161477040fSmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
171477040fSmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
181477040fSmrgTHE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
191477040fSmrgLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
201477040fSmrgFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
211477040fSmrgDEALINGS IN THE SOFTWARE.
221477040fSmrg
231477040fSmrgCopyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24444c061aSmrg
25444c061aSmrg                        All Rights Reserved
26444c061aSmrg
27444c061aSmrgPermission to use, copy, modify, and distribute this software and its
28444c061aSmrgdocumentation for any purpose and without fee is hereby granted,
29444c061aSmrgprovided that the above copyright notice appear in all copies and that
30444c061aSmrgboth that copyright notice and this permission notice appear in
311477040fSmrgsupporting documentation, and that the name of Digital not be
32444c061aSmrgused in advertising or publicity pertaining to distribution of the
33444c061aSmrgsoftware without specific, written prior permission.
34444c061aSmrg
35444c061aSmrgDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36444c061aSmrgALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37444c061aSmrgDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38444c061aSmrgANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39444c061aSmrgWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40444c061aSmrgARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41444c061aSmrgSOFTWARE.
42444c061aSmrg
43444c061aSmrg******************************************************************/
44444c061aSmrg
45444c061aSmrg/*
46444c061aSmrg
47444c061aSmrgCopyright 1987, 1988, 1994, 1998  The Open Group
48444c061aSmrg
49444c061aSmrgPermission to use, copy, modify, distribute, and sell this software and its
50444c061aSmrgdocumentation for any purpose is hereby granted without fee, provided that
51444c061aSmrgthe above copyright notice appear in all copies and that both that
52444c061aSmrgcopyright notice and this permission notice appear in supporting
53444c061aSmrgdocumentation.
54444c061aSmrg
55444c061aSmrgThe above copyright notice and this permission notice shall be included in
56444c061aSmrgall copies or substantial portions of the Software.
57444c061aSmrg
58444c061aSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59444c061aSmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60444c061aSmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
61444c061aSmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
62444c061aSmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
63444c061aSmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64444c061aSmrg
65444c061aSmrgExcept as contained in this notice, the name of The Open Group shall not be
66444c061aSmrgused in advertising or otherwise to promote the sale, use or other dealings
67444c061aSmrgin this Software without prior written authorization from The Open Group.
68444c061aSmrg
69444c061aSmrg*/
70444c061aSmrg
71444c061aSmrg#ifdef HAVE_CONFIG_H
72444c061aSmrg#include <config.h>
73444c061aSmrg#endif
74444c061aSmrg#include "IntrinsicI.h"
75444c061aSmrg#include "StringDefs.h"
76444c061aSmrg#include "SelectionI.h"
77444c061aSmrg#include <X11/Xatom.h>
78444c061aSmrg#include <stdio.h>
79444c061aSmrg
80a3bd7f05Smrgvoid
81a3bd7f05Smrg_XtSetDefaultSelectionTimeout(unsigned long *timeout)
82444c061aSmrg{
83a3bd7f05Smrg    *timeout = 5000;            /* default to 5 seconds */
84444c061aSmrg}
85444c061aSmrg
86a3bd7f05Smrgvoid
87a3bd7f05SmrgXtSetSelectionTimeout(unsigned long timeout)
88444c061aSmrg{
89a3bd7f05Smrg    XtAppSetSelectionTimeout(_XtDefaultAppContext(), timeout);
90444c061aSmrg}
91444c061aSmrg
92a3bd7f05Smrgvoid
93a3bd7f05SmrgXtAppSetSelectionTimeout(XtAppContext app, unsigned long timeout)
94444c061aSmrg{
95a3bd7f05Smrg    LOCK_APP(app);
96a3bd7f05Smrg    app->selectionTimeout = timeout;
97a3bd7f05Smrg    UNLOCK_APP(app);
98444c061aSmrg}
99444c061aSmrg
100a3bd7f05Smrgunsigned long
101a3bd7f05SmrgXtGetSelectionTimeout(void)
102444c061aSmrg{
103a3bd7f05Smrg    return XtAppGetSelectionTimeout(_XtDefaultAppContext());
104444c061aSmrg}
105444c061aSmrg
106a3bd7f05Smrgunsigned long
107a3bd7f05SmrgXtAppGetSelectionTimeout(XtAppContext app)
108444c061aSmrg{
109a3bd7f05Smrg    unsigned long retval;
110444c061aSmrg
111a3bd7f05Smrg    LOCK_APP(app);
112a3bd7f05Smrg    retval = app->selectionTimeout;
113a3bd7f05Smrg    UNLOCK_APP(app);
114a3bd7f05Smrg    return retval;
115444c061aSmrg}
116444c061aSmrg
117444c061aSmrg/* General utilities */
118444c061aSmrg
119444c061aSmrgstatic void HandleSelectionReplies(Widget, XtPointer, XEvent *, Boolean *);
120444c061aSmrgstatic void ReqTimedOut(XtPointer, XtIntervalId *);
121444c061aSmrgstatic void HandlePropertyGone(Widget, XtPointer, XEvent *, Boolean *);
122444c061aSmrgstatic void HandleGetIncrement(Widget, XtPointer, XEvent *, Boolean *);
123a3bd7f05Smrgstatic void HandleIncremental(Display *, Widget, Atom, CallBackInfo,
124a3bd7f05Smrg                              unsigned long);
125444c061aSmrg
126444c061aSmrgstatic XContext selectPropertyContext = 0;
127444c061aSmrgstatic XContext paramPropertyContext = 0;
128444c061aSmrgstatic XContext multipleContext = 0;
129444c061aSmrg
130444c061aSmrg/* Multiple utilities */
131a3bd7f05Smrgstatic void AddSelectionRequests(Widget, Atom, int, Atom *,
132a3bd7f05Smrg                                 XtSelectionCallbackProc *, int, XtPointer *,
133a3bd7f05Smrg                                 Boolean *, Atom *);
134444c061aSmrgstatic Boolean IsGatheringRequest(Widget, Atom);
135444c061aSmrg
136444c061aSmrg#define PREALLOCED 32
137444c061aSmrg
138444c061aSmrg/* Parameter utilities */
139444c061aSmrgstatic void AddParamInfo(Widget, Atom, Atom);
140444c061aSmrgstatic void RemoveParamInfo(Widget, Atom);
141444c061aSmrgstatic Atom GetParamInfo(Widget, Atom);
142444c061aSmrg
143a3bd7f05Smrgstatic int StorageSize[3] = { 1, sizeof(short), sizeof(long) };
144a3bd7f05Smrg
1450568f49bSmrg#define BYTELENGTH(length, format) ((length) * (size_t)StorageSize[(format)>>4])
146444c061aSmrg#define NUMELEM(bytelength, format) ((bytelength) / StorageSize[(format)>>4])
1470568f49bSmrg#define NUMELEM2(bytelength, format) ((unsigned long)(bytelength) / (unsigned long) StorageSize[(format)>>4])
148444c061aSmrg
149444c061aSmrg/* Xlib and Xt are permitted to have different memory allocators, and in the
150444c061aSmrg * XtSelectionCallbackProc the client is instructed to free the selection
151444c061aSmrg * value with XtFree, so the selection value received from XGetWindowProperty
152444c061aSmrg * should be copied to memory allocated through Xt.  But copying is
153444c061aSmrg * undesirable since the selection value may be large, and, under normal
154444c061aSmrg * library configuration copying is unnecessary.
155444c061aSmrg */
156444c061aSmrg#ifdef XTTRACEMEMORY
157a3bd7f05Smrg#define XT_COPY_SELECTION       1
158444c061aSmrg#endif
159444c061aSmrg
160a3bd7f05Smrgstatic void
161a3bd7f05SmrgFreePropList(Widget w _X_UNUSED,
162a3bd7f05Smrg             XtPointer closure,
163a3bd7f05Smrg             XtPointer callData _X_UNUSED)
164444c061aSmrg{
165a3bd7f05Smrg    PropList sarray = (PropList) closure;
166a3bd7f05Smrg
167444c061aSmrg    LOCK_PROCESS;
168444c061aSmrg    XDeleteContext(sarray->dpy, DefaultRootWindow(sarray->dpy),
169a3bd7f05Smrg                   selectPropertyContext);
170444c061aSmrg    UNLOCK_PROCESS;
171a3bd7f05Smrg    XtFree((char *) sarray->list);
172a3bd7f05Smrg    XtFree((char *) closure);
173444c061aSmrg}
174444c061aSmrg
175a3bd7f05Smrgstatic PropList
176a3bd7f05SmrgGetPropList(Display *dpy)
177444c061aSmrg{
178444c061aSmrg    PropList sarray;
179444c061aSmrg
180444c061aSmrg    LOCK_PROCESS;
181444c061aSmrg    if (selectPropertyContext == 0)
182a3bd7f05Smrg        selectPropertyContext = XUniqueContext();
183444c061aSmrg    if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
184a3bd7f05Smrg                     (XPointer *) &sarray)) {
185a3bd7f05Smrg        Atom atoms[4];
186a3bd7f05Smrg
187a3bd7f05Smrg        static char *names[] = {
188a3bd7f05Smrg            "INCR",
189a3bd7f05Smrg            "MULTIPLE",
190a3bd7f05Smrg            "TIMESTAMP",
191a3bd7f05Smrg            "_XT_SELECTION_0"
192a3bd7f05Smrg        };
193a3bd7f05Smrg
194a3bd7f05Smrg        XtPerDisplay pd = _XtGetPerDisplay(dpy);
195a3bd7f05Smrg
196a3bd7f05Smrg        sarray = (PropList) __XtMalloc((unsigned) sizeof(PropListRec));
197a3bd7f05Smrg        sarray->dpy = dpy;
198a3bd7f05Smrg        XInternAtoms(dpy, names, 4, FALSE, atoms);
199a3bd7f05Smrg        sarray->incr_atom = atoms[0];
200a3bd7f05Smrg        sarray->indirect_atom = atoms[1];
201a3bd7f05Smrg        sarray->timestamp_atom = atoms[2];
202a3bd7f05Smrg        sarray->propCount = 1;
203a3bd7f05Smrg        sarray->list =
204a3bd7f05Smrg            (SelectionProp) __XtMalloc((unsigned) sizeof(SelectionPropRec));
205a3bd7f05Smrg        sarray->list[0].prop = atoms[3];
206a3bd7f05Smrg        sarray->list[0].avail = TRUE;
207a3bd7f05Smrg        (void) XSaveContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
208a3bd7f05Smrg                            (char *) sarray);
209a3bd7f05Smrg        _XtAddCallback(&pd->destroy_callbacks,
210a3bd7f05Smrg                       FreePropList, (XtPointer) sarray);
211444c061aSmrg    }
212444c061aSmrg    UNLOCK_PROCESS;
213444c061aSmrg    return sarray;
214444c061aSmrg}
215444c061aSmrg
216a3bd7f05Smrgstatic Atom
217a3bd7f05SmrgGetSelectionProperty(Display *dpy)
218444c061aSmrg{
219a3bd7f05Smrg    SelectionProp p;
220a3bd7f05Smrg    int propCount;
221a3bd7f05Smrg    char propname[80];
222a3bd7f05Smrg    PropList sarray = GetPropList(dpy);
223a3bd7f05Smrg
224a3bd7f05Smrg    for (p = sarray->list, propCount = sarray->propCount;
225a3bd7f05Smrg         propCount; p++, propCount--) {
226a3bd7f05Smrg        if (p->avail) {
227a3bd7f05Smrg            p->avail = FALSE;
228a3bd7f05Smrg            return (p->prop);
229a3bd7f05Smrg        }
230a3bd7f05Smrg    }
231a3bd7f05Smrg    propCount = sarray->propCount++;
232fdf6a26fSmrg    sarray->list = XtReallocArray(sarray->list, (Cardinal) sarray->propCount,
233fdf6a26fSmrg                                  (Cardinal) sizeof(SelectionPropRec));
234a3bd7f05Smrg    (void) snprintf(propname, sizeof(propname), "_XT_SELECTION_%d", propCount);
235a3bd7f05Smrg    sarray->list[propCount].prop = XInternAtom(dpy, propname, FALSE);
236a3bd7f05Smrg    sarray->list[propCount].avail = FALSE;
237a3bd7f05Smrg    return (sarray->list[propCount].prop);
238444c061aSmrg}
239444c061aSmrg
240a3bd7f05Smrgstatic void
241a3bd7f05SmrgFreeSelectionProperty(Display *dpy, Atom prop)
242444c061aSmrg{
243a3bd7f05Smrg    SelectionProp p;
244a3bd7f05Smrg    int propCount;
245a3bd7f05Smrg    PropList sarray;
246a3bd7f05Smrg
247a3bd7f05Smrg    if (prop == None)
248a3bd7f05Smrg        return;
249a3bd7f05Smrg    LOCK_PROCESS;
250a3bd7f05Smrg    if (XFindContext(dpy, DefaultRootWindow(dpy), selectPropertyContext,
251a3bd7f05Smrg                     (XPointer *) &sarray))
252a3bd7f05Smrg        XtAppErrorMsg(XtDisplayToApplicationContext(dpy),
253a3bd7f05Smrg                      "noSelectionProperties", "freeSelectionProperty",
254a3bd7f05Smrg                      XtCXtToolkitError,
255a3bd7f05Smrg                      "internal error: no selection property context for display",
256a3bd7f05Smrg                      NULL, NULL);
257a3bd7f05Smrg    UNLOCK_PROCESS;
258a3bd7f05Smrg    for (p = sarray->list, propCount = sarray->propCount;
259a3bd7f05Smrg         propCount; p++, propCount--)
260a3bd7f05Smrg        if (p->prop == prop) {
261a3bd7f05Smrg            p->avail = TRUE;
262a3bd7f05Smrg            return;
263a3bd7f05Smrg        }
264444c061aSmrg}
265444c061aSmrg
266a3bd7f05Smrgstatic void
267a3bd7f05SmrgFreeInfo(CallBackInfo info)
268444c061aSmrg{
269a3bd7f05Smrg    XtFree((char *) info->incremental);
270a3bd7f05Smrg    XtFree((char *) info->callbacks);
271a3bd7f05Smrg    XtFree((char *) info->req_closure);
272a3bd7f05Smrg    XtFree((char *) info->target);
273a3bd7f05Smrg    XtFree((char *) info);
274444c061aSmrg}
275444c061aSmrg
276a3bd7f05Smrgstatic CallBackInfo
277a3bd7f05SmrgMakeInfo(Select ctx,
278a3bd7f05Smrg         XtSelectionCallbackProc *callbacks,
279a3bd7f05Smrg         XtPointer *closures,
280a3bd7f05Smrg         int count,
281a3bd7f05Smrg         Widget widget,
282a3bd7f05Smrg         Time time,
283a3bd7f05Smrg         Boolean *incremental,
284a3bd7f05Smrg         Atom *properties)
285444c061aSmrg{
286a3bd7f05Smrg    CallBackInfo info = XtNew(CallBackInfoRec);
287a3bd7f05Smrg
288a3bd7f05Smrg    info->ctx = ctx;
289fdf6a26fSmrg    info->callbacks = XtMallocArray((Cardinal) count,
290fdf6a26fSmrg                                    (Cardinal) sizeof(XtSelectionCallbackProc));
291fdf6a26fSmrg    (void) memcpy(info->callbacks, callbacks,
292fdf6a26fSmrg                  (size_t) count * sizeof(XtSelectionCallbackProc));
293fdf6a26fSmrg    info->req_closure = XtMallocArray((Cardinal) count,
294fdf6a26fSmrg                                      (Cardinal) sizeof(XtPointer));
295fdf6a26fSmrg    (void) memcpy(info->req_closure, closures,
296fdf6a26fSmrg                  (size_t) count * sizeof(XtPointer));
297a3bd7f05Smrg    if (count == 1 && properties != NULL && properties[0] != None)
298a3bd7f05Smrg        info->property = properties[0];
299a3bd7f05Smrg    else {
300a3bd7f05Smrg        info->property = GetSelectionProperty(XtDisplay(widget));
301a3bd7f05Smrg        XDeleteProperty(XtDisplay(widget), XtWindow(widget), info->property);
302a3bd7f05Smrg    }
303a3bd7f05Smrg    info->proc = HandleSelectionReplies;
304a3bd7f05Smrg    info->widget = widget;
305a3bd7f05Smrg    info->time = time;
306fdf6a26fSmrg    info->incremental = XtMallocArray((Cardinal) count,
307fdf6a26fSmrg                                      (Cardinal) sizeof(Boolean));
308fdf6a26fSmrg    (void) memcpy(info->incremental, incremental,
309fdf6a26fSmrg                  (size_t) count * sizeof(Boolean));
310a3bd7f05Smrg    info->current = 0;
311a3bd7f05Smrg    info->value = NULL;
312a3bd7f05Smrg    return (info);
313444c061aSmrg}
314444c061aSmrg
315a3bd7f05Smrgstatic void
316a3bd7f05SmrgRequestSelectionValue(CallBackInfo info, Atom selection, Atom target)
317444c061aSmrg{
318444c061aSmrg#ifndef DEBUG_WO_TIMERS
319444c061aSmrg    XtAppContext app = XtWidgetToApplicationContext(info->widget);
320a3bd7f05Smrg
321a3bd7f05Smrg    info->timeout = XtAppAddTimeOut(app,
322a3bd7f05Smrg                                    app->selectionTimeout, ReqTimedOut,
323a3bd7f05Smrg                                    (XtPointer) info);
324444c061aSmrg#endif
325a3bd7f05Smrg    XtAddEventHandler(info->widget, (EventMask) 0, TRUE,
326a3bd7f05Smrg                      HandleSelectionReplies, (XtPointer) info);
327a3bd7f05Smrg    XConvertSelection(info->ctx->dpy, selection, target,
328a3bd7f05Smrg                      info->property, XtWindow(info->widget), info->time);
329444c061aSmrg}
330444c061aSmrg
331444c061aSmrgstatic XContext selectContext = 0;
332444c061aSmrg
333a3bd7f05Smrgstatic Select
334a3bd7f05SmrgNewContext(Display *dpy, Atom selection)
335444c061aSmrg{
336444c061aSmrg    /* assert(selectContext != 0) */
337444c061aSmrg    Select ctx = XtNew(SelectRec);
338a3bd7f05Smrg
339444c061aSmrg    ctx->dpy = dpy;
340444c061aSmrg    ctx->selection = selection;
341444c061aSmrg    ctx->widget = NULL;
342444c061aSmrg    ctx->prop_list = GetPropList(dpy);
343444c061aSmrg    ctx->ref_count = 0;
344444c061aSmrg    ctx->free_when_done = FALSE;
345444c061aSmrg    ctx->was_disowned = FALSE;
346444c061aSmrg    LOCK_PROCESS;
347a3bd7f05Smrg    (void) XSaveContext(dpy, (Window) selection, selectContext, (char *) ctx);
348444c061aSmrg    UNLOCK_PROCESS;
349444c061aSmrg    return ctx;
350444c061aSmrg}
351444c061aSmrg
352a3bd7f05Smrgstatic Select
353a3bd7f05SmrgFindCtx(Display *dpy, Atom selection)
354444c061aSmrg{
355444c061aSmrg    Select ctx;
356444c061aSmrg
357444c061aSmrg    LOCK_PROCESS;
358444c061aSmrg    if (selectContext == 0)
359a3bd7f05Smrg        selectContext = XUniqueContext();
360a3bd7f05Smrg    if (XFindContext(dpy, (Window) selection, selectContext, (XPointer *) &ctx))
361a3bd7f05Smrg        ctx = NewContext(dpy, selection);
362444c061aSmrg    UNLOCK_PROCESS;
363444c061aSmrg    return ctx;
364444c061aSmrg}
365444c061aSmrg
366a3bd7f05Smrgstatic void
367a3bd7f05SmrgWidgetDestroyed(Widget widget, XtPointer closure, XtPointer data _X_UNUSED)
368444c061aSmrg{
369444c061aSmrg    Select ctx = (Select) closure;
370a3bd7f05Smrg
371444c061aSmrg    if (ctx->widget == widget) {
372a3bd7f05Smrg        if (ctx->free_when_done)
373a3bd7f05Smrg            XtFree((char *) ctx);
374a3bd7f05Smrg        else
375a3bd7f05Smrg            ctx->widget = NULL;
376444c061aSmrg    }
377444c061aSmrg}
378444c061aSmrg
379444c061aSmrg/* Selection Owner code */
380444c061aSmrg
381444c061aSmrgstatic void HandleSelectionEvents(Widget, XtPointer, XEvent *, Boolean *);
382444c061aSmrg
383a3bd7f05Smrgstatic Boolean
384a3bd7f05SmrgLoseSelection(Select ctx, Widget widget, Atom selection, Time time)
385444c061aSmrg{
386a3bd7f05Smrg    if ((ctx->widget == widget) && (ctx->selection == selection) &&     /* paranoia */
387a3bd7f05Smrg        !ctx->was_disowned && ((time == CurrentTime) || (time >= ctx->time))) {
388a3bd7f05Smrg        XtRemoveEventHandler(widget, (EventMask) 0, TRUE,
389a3bd7f05Smrg                             HandleSelectionEvents, (XtPointer) ctx);
390a3bd7f05Smrg        XtRemoveCallback(widget, XtNdestroyCallback,
391a3bd7f05Smrg                         WidgetDestroyed, (XtPointer) ctx);
392a3bd7f05Smrg        ctx->was_disowned = TRUE;       /* widget officially loses ownership */
393a3bd7f05Smrg        /* now inform widget */
394a3bd7f05Smrg        if (ctx->loses) {
395a3bd7f05Smrg            if (ctx->incremental)
396a3bd7f05Smrg                (*(XtLoseSelectionIncrProc) ctx->loses)
397a3bd7f05Smrg                    (widget, &ctx->selection, ctx->owner_closure);
398a3bd7f05Smrg            else
399a3bd7f05Smrg                (*ctx->loses) (widget, &ctx->selection);
400a3bd7f05Smrg        }
401a3bd7f05Smrg        return (TRUE);
402a3bd7f05Smrg    }
403a3bd7f05Smrg    else
404a3bd7f05Smrg        return (FALSE);
405444c061aSmrg}
406444c061aSmrg
407444c061aSmrgstatic XContext selectWindowContext = 0;
408444c061aSmrg
409444c061aSmrg/* %%% Xlib.h should make this public! */
410a3bd7f05Smrgtypedef int (*xErrorHandler) (Display *, XErrorEvent *);
411444c061aSmrg
412444c061aSmrgstatic xErrorHandler oldErrorHandler = NULL;
413444c061aSmrgstatic unsigned long firstProtectRequest;
414444c061aSmrgstatic Window errorWindow;
415444c061aSmrg
416a3bd7f05Smrgstatic int
417a3bd7f05SmrgLocalErrorHandler(Display *dpy, XErrorEvent *error)
418444c061aSmrg{
419444c061aSmrg    int retval;
420444c061aSmrg
421444c061aSmrg    /* If BadWindow error on selection requestor, nothing to do but let
422444c061aSmrg     * the transfer timeout.  Otherwise, invoke saved error handler. */
423444c061aSmrg
424444c061aSmrg    LOCK_PROCESS;
425444c061aSmrg
426444c061aSmrg    if (error->error_code == BadWindow && error->resourceid == errorWindow &&
427a3bd7f05Smrg        error->serial >= firstProtectRequest) {
428a3bd7f05Smrg        UNLOCK_PROCESS;
429a3bd7f05Smrg        return 0;
430444c061aSmrg    }
431444c061aSmrg
432444c061aSmrg    if (oldErrorHandler == NULL) {
433a3bd7f05Smrg        UNLOCK_PROCESS;
434a3bd7f05Smrg        return 0;               /* should never happen */
435444c061aSmrg    }
436444c061aSmrg
437a3bd7f05Smrg    retval = (*oldErrorHandler) (dpy, error);
438444c061aSmrg    UNLOCK_PROCESS;
439444c061aSmrg    return retval;
440444c061aSmrg}
441444c061aSmrg
442a3bd7f05Smrgstatic void
443a3bd7f05SmrgStartProtectedSection(Display *dpy, Window window)
444444c061aSmrg{
445444c061aSmrg    /* protect ourselves against request window being destroyed
446444c061aSmrg     * before completion of transfer */
447444c061aSmrg
448444c061aSmrg    LOCK_PROCESS;
449444c061aSmrg    oldErrorHandler = XSetErrorHandler(LocalErrorHandler);
450444c061aSmrg    firstProtectRequest = NextRequest(dpy);
451444c061aSmrg    errorWindow = window;
452444c061aSmrg    UNLOCK_PROCESS;
453444c061aSmrg}
454444c061aSmrg
455a3bd7f05Smrgstatic void
456a3bd7f05SmrgEndProtectedSection(Display *dpy)
457444c061aSmrg{
458444c061aSmrg    /* flush any generated errors on requestor and
459444c061aSmrg     * restore original error handler */
460444c061aSmrg
461444c061aSmrg    XSync(dpy, False);
462444c061aSmrg
463444c061aSmrg    LOCK_PROCESS;
464444c061aSmrg    XSetErrorHandler(oldErrorHandler);
465444c061aSmrg    oldErrorHandler = NULL;
466444c061aSmrg    UNLOCK_PROCESS;
467444c061aSmrg}
468444c061aSmrg
469a3bd7f05Smrgstatic void
470a3bd7f05SmrgAddHandler(Request req, EventMask mask, XtEventHandler proc, XtPointer closure)
471444c061aSmrg{
472444c061aSmrg    Display *dpy = req->ctx->dpy;
473444c061aSmrg    Window window = req->requestor;
474444c061aSmrg    Widget widget = XtWindowToWidget(dpy, window);
475444c061aSmrg
476a3bd7f05Smrg    if (widget != NULL)
477a3bd7f05Smrg        req->widget = widget;
478a3bd7f05Smrg    else
479a3bd7f05Smrg        widget = req->widget;
480444c061aSmrg
481444c061aSmrg    if (XtWindow(widget) == window)
482a3bd7f05Smrg        XtAddEventHandler(widget, mask, False, proc, closure);
483444c061aSmrg    else {
484a3bd7f05Smrg        RequestWindowRec *requestWindowRec;
485a3bd7f05Smrg
486a3bd7f05Smrg        LOCK_PROCESS;
487a3bd7f05Smrg        if (selectWindowContext == 0)
488a3bd7f05Smrg            selectWindowContext = XUniqueContext();
489a3bd7f05Smrg        if (XFindContext(dpy, window, selectWindowContext,
490a3bd7f05Smrg                         (XPointer *) &requestWindowRec)) {
491a3bd7f05Smrg            requestWindowRec = XtNew(RequestWindowRec);
492a3bd7f05Smrg            requestWindowRec->active_transfer_count = 0;
493a3bd7f05Smrg            (void) XSaveContext(dpy, window, selectWindowContext,
494a3bd7f05Smrg                                (char *) requestWindowRec);
495a3bd7f05Smrg        }
496a3bd7f05Smrg        UNLOCK_PROCESS;
497a3bd7f05Smrg        if (requestWindowRec->active_transfer_count++ == 0) {
498a3bd7f05Smrg            XtRegisterDrawable(dpy, window, widget);
499a3bd7f05Smrg            XSelectInput(dpy, window, (long) mask);
500a3bd7f05Smrg        }
501a3bd7f05Smrg        XtAddRawEventHandler(widget, mask, FALSE, proc, closure);
502444c061aSmrg    }
503444c061aSmrg}
504444c061aSmrg
505a3bd7f05Smrgstatic void
506a3bd7f05SmrgRemoveHandler(Request req,
507a3bd7f05Smrg              EventMask mask,
508a3bd7f05Smrg              XtEventHandler proc,
509a3bd7f05Smrg              XtPointer closure)
510444c061aSmrg{
511444c061aSmrg    Display *dpy = req->ctx->dpy;
512444c061aSmrg    Window window = req->requestor;
513444c061aSmrg    Widget widget = req->widget;
514444c061aSmrg
515444c061aSmrg    if ((XtWindowToWidget(dpy, window) == widget) &&
516444c061aSmrg        (XtWindow(widget) != window)) {
517a3bd7f05Smrg        /* we had to hang this window onto our widget; take it off */
518a3bd7f05Smrg        RequestWindowRec *requestWindowRec;
519a3bd7f05Smrg
520a3bd7f05Smrg        XtRemoveRawEventHandler(widget, mask, TRUE, proc, closure);
521a3bd7f05Smrg        LOCK_PROCESS;
522a3bd7f05Smrg        (void) XFindContext(dpy, window, selectWindowContext,
523a3bd7f05Smrg                            (XPointer *) &requestWindowRec);
524a3bd7f05Smrg        UNLOCK_PROCESS;
525a3bd7f05Smrg        if (--requestWindowRec->active_transfer_count == 0) {
526a3bd7f05Smrg            XtUnregisterDrawable(dpy, window);
527a3bd7f05Smrg            StartProtectedSection(dpy, window);
528a3bd7f05Smrg            XSelectInput(dpy, window, 0L);
529a3bd7f05Smrg            EndProtectedSection(dpy);
530a3bd7f05Smrg            LOCK_PROCESS;
531a3bd7f05Smrg            (void) XDeleteContext(dpy, window, selectWindowContext);
532a3bd7f05Smrg            UNLOCK_PROCESS;
533a3bd7f05Smrg            XtFree((char *) requestWindowRec);
534a3bd7f05Smrg        }
535a3bd7f05Smrg    }
536a3bd7f05Smrg    else {
537a3bd7f05Smrg        XtRemoveEventHandler(widget, mask, TRUE, proc, closure);
538444c061aSmrg    }
539444c061aSmrg}
540444c061aSmrg
541a3bd7f05Smrgstatic void
542a3bd7f05SmrgOwnerTimedOut(XtPointer closure, XtIntervalId *id _X_UNUSED)
543444c061aSmrg{
544a3bd7f05Smrg    Request req = (Request) closure;
545444c061aSmrg    Select ctx = req->ctx;
546444c061aSmrg
547444c061aSmrg    if (ctx->incremental && (ctx->owner_cancel != NULL)) {
548a3bd7f05Smrg        (*ctx->owner_cancel) (ctx->widget, &ctx->selection,
549a3bd7f05Smrg                              &req->target, (XtRequestId *) &req,
550a3bd7f05Smrg                              ctx->owner_closure);
551a3bd7f05Smrg    }
552a3bd7f05Smrg    else {
553a3bd7f05Smrg        if (ctx->notify == NULL)
554a3bd7f05Smrg            XtFree((char *) req->value);
555a3bd7f05Smrg        else {
556a3bd7f05Smrg            /* the requestor hasn't deleted the property, but
557a3bd7f05Smrg             * the owner needs to free the value.
558a3bd7f05Smrg             */
559a3bd7f05Smrg            if (ctx->incremental)
560a3bd7f05Smrg                (*(XtSelectionDoneIncrProc) ctx->notify)
561a3bd7f05Smrg                    (ctx->widget, &ctx->selection, &req->target,
562a3bd7f05Smrg                     (XtRequestId *) &req, ctx->owner_closure);
563a3bd7f05Smrg            else
564a3bd7f05Smrg                (*ctx->notify) (ctx->widget, &ctx->selection, &req->target);
565a3bd7f05Smrg        }
566a3bd7f05Smrg    }
567a3bd7f05Smrg
568a3bd7f05Smrg    RemoveHandler(req, (EventMask) PropertyChangeMask,
569a3bd7f05Smrg                  HandlePropertyGone, closure);
570a3bd7f05Smrg    XtFree((char *) req);
571444c061aSmrg    if (--ctx->ref_count == 0 && ctx->free_when_done)
572a3bd7f05Smrg        XtFree((char *) ctx);
573444c061aSmrg}
574444c061aSmrg
575a3bd7f05Smrgstatic void
576a3bd7f05SmrgSendIncrement(Request incr)
577444c061aSmrg{
578444c061aSmrg    Display *dpy = incr->ctx->dpy;
579444c061aSmrg
5800568f49bSmrg    unsigned long incrSize = (unsigned long) MAX_SELECTION_INCR(dpy);
581a3bd7f05Smrg
582444c061aSmrg    if (incrSize > incr->bytelength - incr->offset)
583444c061aSmrg        incrSize = incr->bytelength - incr->offset;
584444c061aSmrg    StartProtectedSection(dpy, incr->requestor);
585444c061aSmrg    XChangeProperty(dpy, incr->requestor, incr->property,
586a3bd7f05Smrg                    incr->type, incr->format, PropModeReplace,
587a3bd7f05Smrg                    (unsigned char *) incr->value + incr->offset,
588a3bd7f05Smrg                    NUMELEM((int) incrSize, incr->format));
589444c061aSmrg    EndProtectedSection(dpy);
590444c061aSmrg    incr->offset += incrSize;
591444c061aSmrg}
592444c061aSmrg
593a3bd7f05Smrgstatic void
594a3bd7f05SmrgAllSent(Request req)
595444c061aSmrg{
596444c061aSmrg    Select ctx = req->ctx;
597a3bd7f05Smrg
598444c061aSmrg    StartProtectedSection(ctx->dpy, req->requestor);
599444c061aSmrg    XChangeProperty(ctx->dpy, req->requestor,
600a3bd7f05Smrg                    req->property, req->type, req->format,
601a3bd7f05Smrg                    PropModeReplace, (unsigned char *) NULL, 0);
602444c061aSmrg    EndProtectedSection(ctx->dpy);
603444c061aSmrg    req->allSent = TRUE;
604444c061aSmrg
605a3bd7f05Smrg    if (ctx->notify == NULL)
606a3bd7f05Smrg        XtFree((char *) req->value);
607444c061aSmrg}
608444c061aSmrg
609a3bd7f05Smrgstatic void
610a3bd7f05SmrgHandlePropertyGone(Widget widget _X_UNUSED,
611a3bd7f05Smrg                   XtPointer closure,
612a3bd7f05Smrg                   XEvent *ev,
613a3bd7f05Smrg                   Boolean *cont _X_UNUSED)
614444c061aSmrg{
615444c061aSmrg    XPropertyEvent *event = (XPropertyEvent *) ev;
616a3bd7f05Smrg    Request req = (Request) closure;
617444c061aSmrg    Select ctx = req->ctx;
618444c061aSmrg
619444c061aSmrg    if ((event->type != PropertyNotify) ||
620444c061aSmrg        (event->state != PropertyDelete) ||
621a3bd7f05Smrg        (event->atom != req->property) || (event->window != req->requestor))
622a3bd7f05Smrg        return;
623444c061aSmrg#ifndef DEBUG_WO_TIMERS
624444c061aSmrg    XtRemoveTimeOut(req->timeout);
625444c061aSmrg#endif
626444c061aSmrg    if (req->allSent) {
627a3bd7f05Smrg        if (ctx->notify) {
628a3bd7f05Smrg            if (ctx->incremental) {
629a3bd7f05Smrg                (*(XtSelectionDoneIncrProc) ctx->notify)
630a3bd7f05Smrg                    (ctx->widget, &ctx->selection, &req->target,
631a3bd7f05Smrg                     (XtRequestId *) &req, ctx->owner_closure);
632a3bd7f05Smrg            }
633a3bd7f05Smrg            else
634a3bd7f05Smrg                (*ctx->notify) (ctx->widget, &ctx->selection, &req->target);
635a3bd7f05Smrg        }
636a3bd7f05Smrg        RemoveHandler(req, (EventMask) PropertyChangeMask,
637a3bd7f05Smrg                      HandlePropertyGone, closure);
638a3bd7f05Smrg        XtFree((char *) req);
639a3bd7f05Smrg        if (--ctx->ref_count == 0 && ctx->free_when_done)
640a3bd7f05Smrg            XtFree((char *) ctx);
641a3bd7f05Smrg    }
642a3bd7f05Smrg    else {                      /* is this part of an incremental transfer? */
643a3bd7f05Smrg        if (ctx->incremental) {
644a3bd7f05Smrg            if (req->bytelength == 0)
645a3bd7f05Smrg                AllSent(req);
646a3bd7f05Smrg            else {
647a3bd7f05Smrg                unsigned long size =
648a3bd7f05Smrg                    (unsigned long) MAX_SELECTION_INCR(ctx->dpy);
649a3bd7f05Smrg                SendIncrement(req);
650a3bd7f05Smrg                (*(XtConvertSelectionIncrProc) ctx->convert)
651a3bd7f05Smrg                    (ctx->widget, &ctx->selection, &req->target,
652a3bd7f05Smrg                     &req->type, &req->value,
653a3bd7f05Smrg                     &req->bytelength, &req->format,
654a3bd7f05Smrg                     &size, ctx->owner_closure, (XtPointer *) &req);
655a3bd7f05Smrg                if (req->bytelength)
656a3bd7f05Smrg                    req->bytelength = BYTELENGTH(req->bytelength, req->format);
657a3bd7f05Smrg                req->offset = 0;
658a3bd7f05Smrg            }
659a3bd7f05Smrg        }
660a3bd7f05Smrg        else {
661a3bd7f05Smrg            if (req->offset < req->bytelength)
662a3bd7f05Smrg                SendIncrement(req);
663a3bd7f05Smrg            else
664a3bd7f05Smrg                AllSent(req);
665a3bd7f05Smrg        }
666444c061aSmrg#ifndef DEBUG_WO_TIMERS
667a3bd7f05Smrg        {
668a3bd7f05Smrg            XtAppContext app = XtWidgetToApplicationContext(req->widget);
669a3bd7f05Smrg
670a3bd7f05Smrg            req->timeout = XtAppAddTimeOut(app,
671a3bd7f05Smrg                                           app->selectionTimeout, OwnerTimedOut,
672a3bd7f05Smrg                                           (XtPointer) req);
673a3bd7f05Smrg        }
674444c061aSmrg#endif
675444c061aSmrg    }
676444c061aSmrg}
677444c061aSmrg
678a3bd7f05Smrgstatic void
679a3bd7f05SmrgPrepareIncremental(Request req,
680a3bd7f05Smrg                   Widget widget,
681a3bd7f05Smrg                   Window window,
682a3bd7f05Smrg                   Atom property _X_UNUSED,
683a3bd7f05Smrg                   Atom target,
684a3bd7f05Smrg                   Atom targetType,
685a3bd7f05Smrg                   XtPointer value,
686a3bd7f05Smrg                   unsigned long length,
687a3bd7f05Smrg                   int format)
688444c061aSmrg{
689a3bd7f05Smrg    req->type = targetType;
690a3bd7f05Smrg    req->value = value;
691a3bd7f05Smrg    req->bytelength = BYTELENGTH(length, format);
692a3bd7f05Smrg    req->format = format;
693a3bd7f05Smrg    req->offset = 0;
694a3bd7f05Smrg    req->target = target;
695a3bd7f05Smrg    req->widget = widget;
696a3bd7f05Smrg    req->allSent = FALSE;
697444c061aSmrg#ifndef DEBUG_WO_TIMERS
698a3bd7f05Smrg    {
699a3bd7f05Smrg        XtAppContext app = XtWidgetToApplicationContext(widget);
700a3bd7f05Smrg
701a3bd7f05Smrg        req->timeout = XtAppAddTimeOut(app,
702a3bd7f05Smrg                                       app->selectionTimeout, OwnerTimedOut,
703a3bd7f05Smrg                                       (XtPointer) req);
704a3bd7f05Smrg    }
705444c061aSmrg#endif
706a3bd7f05Smrg    AddHandler(req, (EventMask) PropertyChangeMask,
707a3bd7f05Smrg               HandlePropertyGone, (XtPointer) req);
708444c061aSmrg/* now send client INCR property */
709a3bd7f05Smrg    XChangeProperty(req->ctx->dpy, window, req->property,
710a3bd7f05Smrg                    req->ctx->prop_list->incr_atom,
711a3bd7f05Smrg                    32, PropModeReplace, (unsigned char *) &req->bytelength, 1);
712444c061aSmrg}
713444c061aSmrg
714a3bd7f05Smrgstatic Boolean
715a3bd7f05SmrgGetConversion(Select ctx,       /* logical owner */
716a3bd7f05Smrg              XSelectionRequestEvent *event,
717a3bd7f05Smrg              Atom target,
718a3bd7f05Smrg              Atom property,    /* requestor's property */
719a3bd7f05Smrg              Widget widget)    /* physical owner (receives events) */
720a3bd7f05Smrg{
721444c061aSmrg    XtPointer value = NULL;
722444c061aSmrg    unsigned long length;
723444c061aSmrg    int format;
724444c061aSmrg    Atom targetType;
725444c061aSmrg    Request req = XtNew(RequestRec);
726444c061aSmrg    Boolean timestamp_target = (target == ctx->prop_list->timestamp_atom);
727444c061aSmrg
728444c061aSmrg    req->ctx = ctx;
729444c061aSmrg    req->event = *event;
730444c061aSmrg    req->property = property;
731444c061aSmrg    req->requestor = event->requestor;
732444c061aSmrg
733444c061aSmrg    if (timestamp_target) {
734a3bd7f05Smrg        value = __XtMalloc(sizeof(long));
735a3bd7f05Smrg        *(long *) value = (long) ctx->time;
736a3bd7f05Smrg        targetType = XA_INTEGER;
737a3bd7f05Smrg        length = 1;
738a3bd7f05Smrg        format = 32;
739444c061aSmrg    }
740444c061aSmrg    else {
741a3bd7f05Smrg        ctx->ref_count++;
742a3bd7f05Smrg        if (ctx->incremental == TRUE) {
743a3bd7f05Smrg            unsigned long size = (unsigned long) MAX_SELECTION_INCR(ctx->dpy);
744a3bd7f05Smrg
745a3bd7f05Smrg            if ((*(XtConvertSelectionIncrProc) ctx->convert)
746a3bd7f05Smrg                (ctx->widget, &event->selection, &target,
747a3bd7f05Smrg                 &targetType, &value, &length, &format,
748a3bd7f05Smrg                 &size, ctx->owner_closure, (XtRequestId *) &req)
749a3bd7f05Smrg                == FALSE) {
750a3bd7f05Smrg                XtFree((char *) req);
751a3bd7f05Smrg                ctx->ref_count--;
752a3bd7f05Smrg                return (FALSE);
753a3bd7f05Smrg            }
754a3bd7f05Smrg            StartProtectedSection(ctx->dpy, event->requestor);
755a3bd7f05Smrg            PrepareIncremental(req, widget, event->requestor, property,
756a3bd7f05Smrg                               target, targetType, value, length, format);
757a3bd7f05Smrg            return (TRUE);
758a3bd7f05Smrg        }
759a3bd7f05Smrg        ctx->req = req;
760a3bd7f05Smrg        if ((*ctx->convert) (ctx->widget, &event->selection, &target,
761a3bd7f05Smrg                             &targetType, &value, &length, &format) == FALSE) {
762a3bd7f05Smrg            XtFree((char *) req);
763a3bd7f05Smrg            ctx->req = NULL;
764a3bd7f05Smrg            ctx->ref_count--;
765a3bd7f05Smrg            return (FALSE);
766a3bd7f05Smrg        }
767a3bd7f05Smrg        ctx->req = NULL;
768444c061aSmrg    }
769444c061aSmrg    StartProtectedSection(ctx->dpy, event->requestor);
770a3bd7f05Smrg    if (BYTELENGTH(length, format) <=
771a3bd7f05Smrg        (unsigned long) MAX_SELECTION_INCR(ctx->dpy)) {
772a3bd7f05Smrg        if (!timestamp_target) {
773a3bd7f05Smrg            if (ctx->notify != NULL) {
774a3bd7f05Smrg                req->target = target;
775a3bd7f05Smrg                req->widget = widget;
776a3bd7f05Smrg                req->allSent = TRUE;
777444c061aSmrg#ifndef DEBUG_WO_TIMERS
778a3bd7f05Smrg                {
779a3bd7f05Smrg                    XtAppContext app =
780a3bd7f05Smrg                        XtWidgetToApplicationContext(req->widget);
781a3bd7f05Smrg                    req->timeout =
782a3bd7f05Smrg                        XtAppAddTimeOut(app, app->selectionTimeout,
783a3bd7f05Smrg                                        OwnerTimedOut, (XtPointer) req);
784a3bd7f05Smrg                }
785444c061aSmrg#endif
786a3bd7f05Smrg                AddHandler(req, (EventMask) PropertyChangeMask,
787a3bd7f05Smrg                           HandlePropertyGone, (XtPointer) req);
788a3bd7f05Smrg            }
789a3bd7f05Smrg            else
790a3bd7f05Smrg                ctx->ref_count--;
791444c061aSmrg        }
792a3bd7f05Smrg        XChangeProperty(ctx->dpy, event->requestor, property,
793a3bd7f05Smrg                        targetType, format, PropModeReplace,
794a3bd7f05Smrg                        (unsigned char *) value, (int) length);
795a3bd7f05Smrg        /* free storage for client if no notify proc */
796a3bd7f05Smrg        if (timestamp_target || ctx->notify == NULL) {
797a3bd7f05Smrg            XtFree((char *) value);
798a3bd7f05Smrg            XtFree((char *) req);
799a3bd7f05Smrg        }
800a3bd7f05Smrg    }
801a3bd7f05Smrg    else {
802a3bd7f05Smrg        PrepareIncremental(req, widget, event->requestor, property,
803a3bd7f05Smrg                           target, targetType, value, length, format);
804a3bd7f05Smrg    }
805a3bd7f05Smrg    return (TRUE);
806444c061aSmrg}
807444c061aSmrg
808a3bd7f05Smrgstatic void
809a3bd7f05SmrgHandleSelectionEvents(Widget widget,
810a3bd7f05Smrg                      XtPointer closure,
811a3bd7f05Smrg                      XEvent *event,
812a3bd7f05Smrg                      Boolean *cont _X_UNUSED)
813444c061aSmrg{
814444c061aSmrg    Select ctx;
815444c061aSmrg    XSelectionEvent ev;
816444c061aSmrg    Atom target;
817444c061aSmrg
818444c061aSmrg    ctx = (Select) closure;
819444c061aSmrg    switch (event->type) {
820a3bd7f05Smrg    case SelectionClear:
821a3bd7f05Smrg        /* if this event is not for the selection we registered for,
822a3bd7f05Smrg         * don't do anything */
823a3bd7f05Smrg        if (ctx->selection != event->xselectionclear.selection ||
824a3bd7f05Smrg            ctx->serial > event->xselectionclear.serial)
825a3bd7f05Smrg            break;
826a3bd7f05Smrg        (void) LoseSelection(ctx, widget, event->xselectionclear.selection,
827a3bd7f05Smrg                             event->xselectionclear.time);
828a3bd7f05Smrg        break;
829a3bd7f05Smrg    case SelectionRequest:
830a3bd7f05Smrg        /* if this event is not for the selection we registered for,
831a3bd7f05Smrg         * don't do anything */
832a3bd7f05Smrg        if (ctx->selection != event->xselectionrequest.selection)
833a3bd7f05Smrg            break;
834a3bd7f05Smrg        ev.type = SelectionNotify;
835a3bd7f05Smrg        ev.display = event->xselectionrequest.display;
836a3bd7f05Smrg
837a3bd7f05Smrg        ev.requestor = event->xselectionrequest.requestor;
838a3bd7f05Smrg        ev.selection = event->xselectionrequest.selection;
839a3bd7f05Smrg        ev.time = event->xselectionrequest.time;
840a3bd7f05Smrg        ev.target = event->xselectionrequest.target;
841a3bd7f05Smrg        if (event->xselectionrequest.property == None)  /* obsolete requestor */
842a3bd7f05Smrg            event->xselectionrequest.property = event->xselectionrequest.target;
843a3bd7f05Smrg        if (ctx->widget != widget || ctx->was_disowned
844a3bd7f05Smrg            || ((event->xselectionrequest.time != CurrentTime)
845a3bd7f05Smrg                && (event->xselectionrequest.time < ctx->time))) {
846a3bd7f05Smrg            ev.property = None;
847a3bd7f05Smrg            StartProtectedSection(ev.display, ev.requestor);
848a3bd7f05Smrg        }
849a3bd7f05Smrg        else {
850a3bd7f05Smrg            if (ev.target == ctx->prop_list->indirect_atom) {
851a3bd7f05Smrg                IndirectPair *p;
852a3bd7f05Smrg                int format;
853a3bd7f05Smrg                unsigned long bytesafter, length;
854a3bd7f05Smrg                unsigned char *value = NULL;
855a3bd7f05Smrg                int count;
856a3bd7f05Smrg                Boolean writeback = FALSE;
857a3bd7f05Smrg
858a3bd7f05Smrg                ev.property = event->xselectionrequest.property;
859a3bd7f05Smrg                StartProtectedSection(ev.display, ev.requestor);
860a3bd7f05Smrg                if (XGetWindowProperty(ev.display, ev.requestor,
861a3bd7f05Smrg                                       event->xselectionrequest.property, 0L,
862a3bd7f05Smrg                                       1000000, False, (Atom) AnyPropertyType,
863a3bd7f05Smrg                                       &target, &format, &length, &bytesafter,
864a3bd7f05Smrg                                       &value) == Success)
865a3bd7f05Smrg                    count =
866a3bd7f05Smrg                        (int) (BYTELENGTH(length, format) /
867a3bd7f05Smrg                               sizeof(IndirectPair));
868a3bd7f05Smrg                else
869a3bd7f05Smrg                    count = 0;
870a3bd7f05Smrg                for (p = (IndirectPair *) value; count; p++, count--) {
871a3bd7f05Smrg                    EndProtectedSection(ctx->dpy);
872a3bd7f05Smrg                    if (!GetConversion(ctx, (XSelectionRequestEvent *) event,
873a3bd7f05Smrg                                       p->target, p->property, widget)) {
874a3bd7f05Smrg
875a3bd7f05Smrg                        p->target = None;
876a3bd7f05Smrg                        writeback = TRUE;
877a3bd7f05Smrg                        StartProtectedSection(ctx->dpy, ev.requestor);
878a3bd7f05Smrg                    }
879a3bd7f05Smrg                }
880a3bd7f05Smrg                if (writeback)
881a3bd7f05Smrg                    XChangeProperty(ev.display, ev.requestor,
882a3bd7f05Smrg                                    event->xselectionrequest.property, target,
883a3bd7f05Smrg                                    format, PropModeReplace, value,
884a3bd7f05Smrg                                    (int) length);
885a3bd7f05Smrg                XFree((char *) value);
886a3bd7f05Smrg            }
887a3bd7f05Smrg            else {              /* not multiple */
888a3bd7f05Smrg
889a3bd7f05Smrg                if (GetConversion(ctx, (XSelectionRequestEvent *) event,
890a3bd7f05Smrg                                  event->xselectionrequest.target,
891a3bd7f05Smrg                                  event->xselectionrequest.property, widget))
892a3bd7f05Smrg                    ev.property = event->xselectionrequest.property;
893a3bd7f05Smrg                else {
894a3bd7f05Smrg                    ev.property = None;
895a3bd7f05Smrg                    StartProtectedSection(ctx->dpy, ev.requestor);
896a3bd7f05Smrg                }
897a3bd7f05Smrg            }
898a3bd7f05Smrg        }
899a3bd7f05Smrg        (void) XSendEvent(ctx->dpy, ev.requestor, False, (unsigned long) NULL,
900a3bd7f05Smrg                          (XEvent *) &ev);
901a3bd7f05Smrg
902a3bd7f05Smrg        EndProtectedSection(ctx->dpy);
903a3bd7f05Smrg
904a3bd7f05Smrg        break;
905444c061aSmrg    }
906444c061aSmrg}
907444c061aSmrg
908a3bd7f05Smrgstatic Boolean
909a3bd7f05SmrgOwnSelection(Widget widget,
910a3bd7f05Smrg             Atom selection,
911a3bd7f05Smrg             Time time,
912a3bd7f05Smrg             XtConvertSelectionProc convert,
913a3bd7f05Smrg             XtLoseSelectionProc lose,
914a3bd7f05Smrg             XtSelectionDoneProc notify,
915a3bd7f05Smrg             XtCancelConvertSelectionProc cancel,
916a3bd7f05Smrg             XtPointer closure,
917a3bd7f05Smrg             Boolean incremental)
918444c061aSmrg{
919444c061aSmrg    Select ctx;
920444c061aSmrg    Select oldctx = NULL;
921444c061aSmrg
922a3bd7f05Smrg    if (!XtIsRealized(widget))
923a3bd7f05Smrg        return False;
924444c061aSmrg
925444c061aSmrg    ctx = FindCtx(XtDisplay(widget), selection);
926444c061aSmrg    if (ctx->widget != widget || ctx->time != time ||
927a3bd7f05Smrg        ctx->ref_count || ctx->was_disowned) {
928a3bd7f05Smrg        Boolean replacement = FALSE;
929a3bd7f05Smrg        Window window = XtWindow(widget);
930a3bd7f05Smrg        unsigned long serial = XNextRequest(ctx->dpy);
931a3bd7f05Smrg
932444c061aSmrg        XSetSelectionOwner(ctx->dpy, selection, window, time);
933444c061aSmrg        if (XGetSelectionOwner(ctx->dpy, selection) != window)
934a3bd7f05Smrg            return FALSE;
935a3bd7f05Smrg        if (ctx->ref_count) {   /* exchange is in-progress */
936444c061aSmrg#ifdef DEBUG_ACTIVE
937a3bd7f05Smrg            printf
938a3bd7f05Smrg                ("Active exchange for widget \"%s\"; selection=0x%lx, ref_count=%d\n",
939a3bd7f05Smrg                 XtName(widget), (long) selection, ctx->ref_count);
940444c061aSmrg#endif
941a3bd7f05Smrg            if (ctx->widget != widget ||
942a3bd7f05Smrg                ctx->convert != convert ||
943a3bd7f05Smrg                ctx->loses != lose ||
944a3bd7f05Smrg                ctx->notify != notify ||
945a3bd7f05Smrg                ctx->owner_cancel != cancel ||
946a3bd7f05Smrg                ctx->incremental != incremental ||
947a3bd7f05Smrg                ctx->owner_closure != closure) {
948a3bd7f05Smrg                if (ctx->widget == widget) {
949a3bd7f05Smrg                    XtRemoveEventHandler(widget, (EventMask) 0, TRUE,
950a3bd7f05Smrg                                         HandleSelectionEvents,
951a3bd7f05Smrg                                         (XtPointer) ctx);
952a3bd7f05Smrg                    XtRemoveCallback(widget, XtNdestroyCallback,
953a3bd7f05Smrg                                     WidgetDestroyed, (XtPointer) ctx);
954a3bd7f05Smrg                    replacement = TRUE;
955a3bd7f05Smrg                }
956a3bd7f05Smrg                else if (!ctx->was_disowned) {
957a3bd7f05Smrg                    oldctx = ctx;
958a3bd7f05Smrg                }
959a3bd7f05Smrg                ctx->free_when_done = TRUE;
960a3bd7f05Smrg                ctx = NewContext(XtDisplay(widget), selection);
961a3bd7f05Smrg            }
962a3bd7f05Smrg            else if (!ctx->was_disowned) {      /* current owner is new owner */
963a3bd7f05Smrg                ctx->time = time;
964a3bd7f05Smrg                return TRUE;
965a3bd7f05Smrg            }
966a3bd7f05Smrg        }
967a3bd7f05Smrg        if (ctx->widget != widget || ctx->was_disowned || replacement) {
968a3bd7f05Smrg            if (ctx->widget && !ctx->was_disowned && !replacement) {
969a3bd7f05Smrg                oldctx = ctx;
970a3bd7f05Smrg                oldctx->free_when_done = TRUE;
971a3bd7f05Smrg                ctx = NewContext(XtDisplay(widget), selection);
972a3bd7f05Smrg            }
973a3bd7f05Smrg            XtAddEventHandler(widget, (EventMask) 0, TRUE,
974a3bd7f05Smrg                              HandleSelectionEvents, (XtPointer) ctx);
975a3bd7f05Smrg            XtAddCallback(widget, XtNdestroyCallback,
976a3bd7f05Smrg                          WidgetDestroyed, (XtPointer) ctx);
977a3bd7f05Smrg        }
978fdf6a26fSmrg        ctx->widget = widget;   /* Selection officially changes hands. */
979a3bd7f05Smrg        ctx->time = time;
980a3bd7f05Smrg        ctx->serial = serial;
981444c061aSmrg    }
982444c061aSmrg    ctx->convert = convert;
983444c061aSmrg    ctx->loses = lose;
984444c061aSmrg    ctx->notify = notify;
985444c061aSmrg    ctx->owner_cancel = cancel;
9860568f49bSmrg    XtSetBit(ctx->incremental, incremental);
987444c061aSmrg    ctx->owner_closure = closure;
988444c061aSmrg    ctx->was_disowned = FALSE;
989444c061aSmrg
990444c061aSmrg    /* Defer calling the previous selection owner's lose selection procedure
991444c061aSmrg     * until the new selection is established, to allow the previous
992444c061aSmrg     * selection owner to ask for the new selection to be converted in
993444c061aSmrg     * the lose selection procedure.  The context pointer is the closure
994444c061aSmrg     * of the event handler and the destroy callback, so the old context
995444c061aSmrg     * pointer and the record contents must be preserved for LoseSelection.
996444c061aSmrg     */
997444c061aSmrg    if (oldctx) {
998a3bd7f05Smrg        (void) LoseSelection(oldctx, oldctx->widget, selection, oldctx->time);
999a3bd7f05Smrg        if (!oldctx->ref_count && oldctx->free_when_done)
1000a3bd7f05Smrg            XtFree((char *) oldctx);
1001444c061aSmrg    }
1002444c061aSmrg    return TRUE;
1003444c061aSmrg}
1004444c061aSmrg
1005a3bd7f05SmrgBoolean
1006a3bd7f05SmrgXtOwnSelection(Widget widget,
1007a3bd7f05Smrg               Atom selection,
1008a3bd7f05Smrg               Time time,
1009a3bd7f05Smrg               XtConvertSelectionProc convert,
1010a3bd7f05Smrg               XtLoseSelectionProc lose,
1011a3bd7f05Smrg               XtSelectionDoneProc notify)
1012444c061aSmrg{
1013444c061aSmrg    Boolean retval;
1014a3bd7f05Smrg
1015444c061aSmrg    WIDGET_TO_APPCON(widget);
1016444c061aSmrg
1017444c061aSmrg    LOCK_APP(app);
1018444c061aSmrg    retval = OwnSelection(widget, selection, time, convert, lose, notify,
1019a3bd7f05Smrg                          (XtCancelConvertSelectionProc) NULL,
1020a3bd7f05Smrg                          (XtPointer) NULL, FALSE);
1021444c061aSmrg    UNLOCK_APP(app);
1022444c061aSmrg    return retval;
1023444c061aSmrg}
1024444c061aSmrg
1025a3bd7f05SmrgBoolean
1026a3bd7f05SmrgXtOwnSelectionIncremental(Widget widget,
1027a3bd7f05Smrg                          Atom selection,
1028a3bd7f05Smrg                          Time time,
1029a3bd7f05Smrg                          XtConvertSelectionIncrProc convert,
1030a3bd7f05Smrg                          XtLoseSelectionIncrProc lose,
1031a3bd7f05Smrg                          XtSelectionDoneIncrProc notify,
1032a3bd7f05Smrg                          XtCancelConvertSelectionProc cancel,
1033a3bd7f05Smrg                          XtPointer closure)
1034444c061aSmrg{
1035444c061aSmrg    Boolean retval;
1036a3bd7f05Smrg
1037444c061aSmrg    WIDGET_TO_APPCON(widget);
1038444c061aSmrg
1039444c061aSmrg    LOCK_APP(app);
1040444c061aSmrg    retval = OwnSelection(widget, selection, time,
1041a3bd7f05Smrg                          (XtConvertSelectionProc) convert,
1042a3bd7f05Smrg                          (XtLoseSelectionProc) lose,
1043a3bd7f05Smrg                          (XtSelectionDoneProc) notify, cancel, closure, TRUE);
1044444c061aSmrg    UNLOCK_APP(app);
1045444c061aSmrg    return retval;
1046444c061aSmrg}
1047444c061aSmrg
1048a3bd7f05Smrgvoid
1049a3bd7f05SmrgXtDisownSelection(Widget widget, Atom selection, Time time)
1050444c061aSmrg{
1051444c061aSmrg    Select ctx;
1052a3bd7f05Smrg
1053444c061aSmrg    WIDGET_TO_APPCON(widget);
1054444c061aSmrg
1055444c061aSmrg    LOCK_APP(app);
1056444c061aSmrg    ctx = FindCtx(XtDisplay(widget), selection);
1057444c061aSmrg    if (LoseSelection(ctx, widget, selection, time))
1058a3bd7f05Smrg        XSetSelectionOwner(XtDisplay(widget), selection, None, time);
1059444c061aSmrg    UNLOCK_APP(app);
1060444c061aSmrg}
1061444c061aSmrg
1062444c061aSmrg/* Selection Requestor code */
1063444c061aSmrg
1064a3bd7f05Smrgstatic Boolean
1065a3bd7f05SmrgIsINCRtype(CallBackInfo info, Window window, Atom prop)
1066444c061aSmrg{
1067444c061aSmrg    unsigned long bytesafter;
1068444c061aSmrg    unsigned long length;
1069444c061aSmrg    int format;
1070444c061aSmrg    Atom type;
1071444c061aSmrg    unsigned char *value;
1072444c061aSmrg
1073a3bd7f05Smrg    if (prop == None)
1074a3bd7f05Smrg        return False;
1075444c061aSmrg
10769e7bcd65Smrg    if (XGetWindowProperty(XtDisplay(info->widget), window, prop, 0L, 0L,
1077a3bd7f05Smrg                           False, info->ctx->prop_list->incr_atom, &type,
1078a3bd7f05Smrg                           &format, &length, &bytesafter, &value) != Success)
1079a3bd7f05Smrg        return False;
1080444c061aSmrg
1081444c061aSmrg    return (type == info->ctx->prop_list->incr_atom);
1082444c061aSmrg}
1083444c061aSmrg
1084a3bd7f05Smrgstatic void
1085a3bd7f05SmrgReqCleanup(Widget widget,
1086a3bd7f05Smrg           XtPointer closure,
1087a3bd7f05Smrg           XEvent *ev,
1088a3bd7f05Smrg           Boolean *cont _X_UNUSED)
1089444c061aSmrg{
1090a3bd7f05Smrg    CallBackInfo info = (CallBackInfo) closure;
1091444c061aSmrg    unsigned long bytesafter, length;
1092444c061aSmrg    int format;
1093444c061aSmrg    Atom target;
1094444c061aSmrg
1095444c061aSmrg    if (ev->type == SelectionNotify) {
1096a3bd7f05Smrg        XSelectionEvent *event = (XSelectionEvent *) ev;
1097a3bd7f05Smrg
1098a3bd7f05Smrg        if (!MATCH_SELECT(event, info))
1099a3bd7f05Smrg            return;             /* not really for us */
1100a3bd7f05Smrg        XtRemoveEventHandler(widget, (EventMask) 0, TRUE,
1101a3bd7f05Smrg                             ReqCleanup, (XtPointer) info);
1102a3bd7f05Smrg        if (IsINCRtype(info, XtWindow(widget), event->property)) {
1103a3bd7f05Smrg            info->proc = HandleGetIncrement;
1104a3bd7f05Smrg            XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask,
1105a3bd7f05Smrg                              FALSE, ReqCleanup, (XtPointer) info);
1106a3bd7f05Smrg        }
1107a3bd7f05Smrg        else {
1108a3bd7f05Smrg            if (event->property != None)
1109a3bd7f05Smrg                XDeleteProperty(event->display, XtWindow(widget),
1110a3bd7f05Smrg                                event->property);
1111a3bd7f05Smrg            FreeSelectionProperty(XtDisplay(widget), info->property);
1112a3bd7f05Smrg            FreeInfo(info);
1113a3bd7f05Smrg        }
1114a3bd7f05Smrg    }
1115a3bd7f05Smrg    else if ((ev->type == PropertyNotify) &&
1116a3bd7f05Smrg             (ev->xproperty.state == PropertyNewValue) &&
1117a3bd7f05Smrg             (ev->xproperty.atom == info->property)) {
1118a3bd7f05Smrg        XPropertyEvent *event = (XPropertyEvent *) ev;
1119a3bd7f05Smrg        char *value = NULL;
1120a3bd7f05Smrg
1121a3bd7f05Smrg        if (XGetWindowProperty(event->display, XtWindow(widget),
1122a3bd7f05Smrg                               event->atom, 0L, 1000000, True, AnyPropertyType,
1123a3bd7f05Smrg                               &target, &format, &length, &bytesafter,
1124a3bd7f05Smrg                               (unsigned char **) &value) == Success) {
1125a3bd7f05Smrg            XFree(value);
1126a3bd7f05Smrg            if (length == 0) {
1127a3bd7f05Smrg                XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask,
1128a3bd7f05Smrg                                     FALSE, ReqCleanup, (XtPointer) info);
1129a3bd7f05Smrg                FreeSelectionProperty(XtDisplay(widget), info->property);
1130a3bd7f05Smrg                XtFree(info->value);    /* requestor never got this, so free now */
1131a3bd7f05Smrg                FreeInfo(info);
1132a3bd7f05Smrg            }
1133a3bd7f05Smrg        }
1134444c061aSmrg    }
1135444c061aSmrg}
1136444c061aSmrg
1137a3bd7f05Smrgstatic void
1138a3bd7f05SmrgReqTimedOut(XtPointer closure, XtIntervalId *id _X_UNUSED)
1139444c061aSmrg{
1140444c061aSmrg    XtPointer value = NULL;
1141444c061aSmrg    unsigned long length = 0;
1142444c061aSmrg    int format = 8;
1143444c061aSmrg    Atom resulttype = XT_CONVERT_FAIL;
1144a3bd7f05Smrg    CallBackInfo info = (CallBackInfo) closure;
1145444c061aSmrg    unsigned long bytesafter;
1146444c061aSmrg    unsigned long proplength;
1147444c061aSmrg    Atom type;
1148444c061aSmrg
1149444c061aSmrg    if (*info->target == info->ctx->prop_list->indirect_atom) {
1150a3bd7f05Smrg        IndirectPair *pairs = NULL;
1151a3bd7f05Smrg
1152a3bd7f05Smrg        if (XGetWindowProperty(XtDisplay(info->widget), XtWindow(info->widget),
1153a3bd7f05Smrg                               info->property, 0L, 10000000, True,
1154a3bd7f05Smrg                               AnyPropertyType, &type, &format, &proplength,
1155a3bd7f05Smrg                               &bytesafter, (unsigned char **) &pairs)
1156a3bd7f05Smrg            == Success) {
1157a3bd7f05Smrg            XtPointer *c;
1158a3bd7f05Smrg            int i;
1159a3bd7f05Smrg
1160a3bd7f05Smrg            XFree(pairs);
1161a3bd7f05Smrg            for (proplength = proplength / IndirectPairWordSize, i = 0,
1162a3bd7f05Smrg                 c = info->req_closure; proplength; proplength--, c++, i++)
1163a3bd7f05Smrg                (*info->callbacks[i]) (info->widget, *c, &info->ctx->selection,
1164a3bd7f05Smrg                                       &resulttype, value, &length, &format);
1165a3bd7f05Smrg        }
1166a3bd7f05Smrg    }
1167a3bd7f05Smrg    else {
1168a3bd7f05Smrg        (*info->callbacks[0]) (info->widget, *info->req_closure,
1169a3bd7f05Smrg                               &info->ctx->selection, &resulttype, value,
1170a3bd7f05Smrg                               &length, &format);
1171444c061aSmrg    }
1172444c061aSmrg
1173444c061aSmrg    /* change event handlers for straggler events */
117435525df4Smrg    if (info->proc == HandleSelectionReplies) {
1175a3bd7f05Smrg        XtRemoveEventHandler(info->widget, (EventMask) 0,
1176a3bd7f05Smrg                             TRUE, info->proc, (XtPointer) info);
1177a3bd7f05Smrg        XtAddEventHandler(info->widget, (EventMask) 0, TRUE,
1178a3bd7f05Smrg                          ReqCleanup, (XtPointer) info);
1179a3bd7f05Smrg    }
1180a3bd7f05Smrg    else {
1181a3bd7f05Smrg        XtRemoveEventHandler(info->widget, (EventMask) PropertyChangeMask,
1182a3bd7f05Smrg                             FALSE, info->proc, (XtPointer) info);
1183a3bd7f05Smrg        XtAddEventHandler(info->widget, (EventMask) PropertyChangeMask,
1184a3bd7f05Smrg                          FALSE, ReqCleanup, (XtPointer) info);
1185444c061aSmrg    }
1186444c061aSmrg
1187444c061aSmrg}
1188444c061aSmrg
1189a3bd7f05Smrgstatic void
1190a3bd7f05SmrgHandleGetIncrement(Widget widget,
1191a3bd7f05Smrg                   XtPointer closure,
1192a3bd7f05Smrg                   XEvent *ev,
1193a3bd7f05Smrg                   Boolean *cont _X_UNUSED)
1194444c061aSmrg{
1195444c061aSmrg    XPropertyEvent *event = (XPropertyEvent *) ev;
1196444c061aSmrg    CallBackInfo info = (CallBackInfo) closure;
1197444c061aSmrg    Select ctx = info->ctx;
1198444c061aSmrg    char *value;
1199444c061aSmrg    unsigned long bytesafter;
1200444c061aSmrg    unsigned long length;
1201444c061aSmrg    int bad;
1202444c061aSmrg    int n = info->current;
1203444c061aSmrg
1204444c061aSmrg    if ((event->state != PropertyNewValue) || (event->atom != info->property))
1205a3bd7f05Smrg        return;
1206444c061aSmrg
1207444c061aSmrg    bad = XGetWindowProperty(event->display, XtWindow(widget),
1208a3bd7f05Smrg                             event->atom, 0L,
1209a3bd7f05Smrg                             10000000, True, AnyPropertyType, &info->type,
1210a3bd7f05Smrg                             &info->format, &length, &bytesafter,
1211a3bd7f05Smrg                             (unsigned char **)&value);
1212444c061aSmrg    if (bad)
1213a3bd7f05Smrg        return;
1214444c061aSmrg#ifndef DEBUG_WO_TIMERS
1215444c061aSmrg    XtRemoveTimeOut(info->timeout);
1216444c061aSmrg#endif
1217444c061aSmrg    if (length == 0) {
1218a3bd7f05Smrg        unsigned long u_offset = NUMELEM2(info->offset, info->format);
1219a3bd7f05Smrg
1220a3bd7f05Smrg        (*info->callbacks[n]) (widget, *info->req_closure, &ctx->selection,
1221a3bd7f05Smrg                               &info->type,
1222a3bd7f05Smrg                               (info->offset == 0 ? value : info->value),
1223a3bd7f05Smrg                               &u_offset, &info->format);
1224a3bd7f05Smrg        /* assert ((info->offset != 0) == (info->incremental[n]) */
1225a3bd7f05Smrg        if (info->offset != 0)
1226a3bd7f05Smrg            XFree(value);
1227a3bd7f05Smrg        XtRemoveEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
1228a3bd7f05Smrg                             HandleGetIncrement, (XtPointer) info);
1229a3bd7f05Smrg        FreeSelectionProperty(event->display, info->property);
1230a3bd7f05Smrg
1231a3bd7f05Smrg        FreeInfo(info);
1232a3bd7f05Smrg    }
1233a3bd7f05Smrg    else {                      /* add increment to collection */
1234a3bd7f05Smrg        if (info->incremental[n]) {
1235444c061aSmrg#ifdef XT_COPY_SELECTION
1236a3bd7f05Smrg            int size = (int) BYTELENGTH(length, info->format) + 1;
1237a3bd7f05Smrg            char *tmp = __XtMalloc((Cardinal) size);
1238a3bd7f05Smrg
1239fdf6a26fSmrg            (void) memcpy(tmp, value, (size_t) size);
1240a3bd7f05Smrg            XFree(value);
1241a3bd7f05Smrg            value = tmp;
1242444c061aSmrg#endif
1243a3bd7f05Smrg            (*info->callbacks[n]) (widget, *info->req_closure, &ctx->selection,
1244a3bd7f05Smrg                                   &info->type, value, &length, &info->format);
1245a3bd7f05Smrg        }
1246a3bd7f05Smrg        else {
1247a3bd7f05Smrg            int size = (int) BYTELENGTH(length, info->format);
1248a3bd7f05Smrg
1249a3bd7f05Smrg            if (info->offset + size > info->bytelength) {
1250a3bd7f05Smrg                /* allocate enough for this and the next increment */
1251a3bd7f05Smrg                info->bytelength = info->offset + size * 2;
1252a3bd7f05Smrg                info->value = XtRealloc(info->value,
1253a3bd7f05Smrg                                        (Cardinal) info->bytelength);
1254a3bd7f05Smrg            }
1255fdf6a26fSmrg            (void) memcpy(&info->value[info->offset], value, (size_t) size);
1256a3bd7f05Smrg            info->offset += size;
1257a3bd7f05Smrg            XFree(value);
1258a3bd7f05Smrg        }
1259a3bd7f05Smrg        /* reset timer */
1260444c061aSmrg#ifndef DEBUG_WO_TIMERS
1261a3bd7f05Smrg        {
1262a3bd7f05Smrg            XtAppContext app = XtWidgetToApplicationContext(info->widget);
1263a3bd7f05Smrg
1264a3bd7f05Smrg            info->timeout = XtAppAddTimeOut(app,
1265a3bd7f05Smrg                                            app->selectionTimeout, ReqTimedOut,
1266a3bd7f05Smrg                                            (XtPointer) info);
1267a3bd7f05Smrg        }
1268444c061aSmrg#endif
1269a3bd7f05Smrg    }
1270444c061aSmrg}
1271444c061aSmrg
1272a3bd7f05Smrgstatic void
1273a3bd7f05SmrgHandleNone(Widget widget,
1274a3bd7f05Smrg           XtSelectionCallbackProc callback,
1275a3bd7f05Smrg           XtPointer closure,
1276a3bd7f05Smrg           Atom selection)
1277444c061aSmrg{
1278444c061aSmrg    unsigned long length = 0;
1279444c061aSmrg    int format = 8;
1280444c061aSmrg    Atom type = None;
1281444c061aSmrg
1282a3bd7f05Smrg    (*callback) (widget, closure, &selection, &type, NULL, &length, &format);
1283444c061aSmrg}
1284444c061aSmrg
1285a3bd7f05Smrgstatic unsigned long
1286a3bd7f05SmrgIncrPropSize(Widget widget,
1287a3bd7f05Smrg             unsigned char *value,
1288a3bd7f05Smrg             int format,
1289a3bd7f05Smrg             unsigned long length)
1290444c061aSmrg{
1291444c061aSmrg    if (format == 32) {
1292a3bd7f05Smrg        unsigned long size;
1293a3bd7f05Smrg
1294a3bd7f05Smrg        size = ((unsigned long *) value)[length - 1];   /* %%% what order for longs? */
1295a3bd7f05Smrg        return size;
1296444c061aSmrg    }
1297444c061aSmrg    else {
1298a3bd7f05Smrg        XtAppWarningMsg(XtWidgetToApplicationContext(widget),
1299a3bd7f05Smrg                        "badFormat", "xtGetSelectionValue", XtCXtToolkitError,
1300a3bd7f05Smrg                        "Selection owner returned type INCR property with format != 32",
1301a3bd7f05Smrg                        NULL, NULL);
1302a3bd7f05Smrg        return 0;
1303444c061aSmrg    }
1304444c061aSmrg}
1305444c061aSmrg
1306444c061aSmrgstatic
1307a3bd7f05Smrg    Boolean
1308a3bd7f05SmrgHandleNormal(Display *dpy,
1309a3bd7f05Smrg             Widget widget,
1310a3bd7f05Smrg             Atom property,
1311a3bd7f05Smrg             CallBackInfo info,
1312a3bd7f05Smrg             XtPointer closure,
1313a3bd7f05Smrg             Atom selection)
1314444c061aSmrg{
1315444c061aSmrg    unsigned long bytesafter;
1316444c061aSmrg    unsigned long length;
1317444c061aSmrg    int format;
1318444c061aSmrg    Atom type;
13199e7bcd65Smrg    unsigned char *value = NULL;
1320444c061aSmrg    int number = info->current;
1321444c061aSmrg
13229e7bcd65Smrg    if (XGetWindowProperty(dpy, XtWindow(widget), property, 0L, 10000000,
1323a3bd7f05Smrg                           False, AnyPropertyType, &type, &format, &length,
1324a3bd7f05Smrg                           &bytesafter, &value) != Success)
1325a3bd7f05Smrg        return FALSE;
1326444c061aSmrg
1327444c061aSmrg    if (type == info->ctx->prop_list->incr_atom) {
1328a3bd7f05Smrg        unsigned long size = IncrPropSize(widget, value, format, length);
1329a3bd7f05Smrg
1330a3bd7f05Smrg        XFree((char *) value);
1331a3bd7f05Smrg        if (info->property != property) {
1332a3bd7f05Smrg            /* within MULTIPLE */
1333a3bd7f05Smrg            CallBackInfo ninfo;
1334a3bd7f05Smrg
1335a3bd7f05Smrg            ninfo = MakeInfo(info->ctx, &info->callbacks[number],
1336a3bd7f05Smrg                             &info->req_closure[number], 1, widget,
1337a3bd7f05Smrg                             info->time, &info->incremental[number], &property);
1338a3bd7f05Smrg            ninfo->target = (Atom *) __XtMalloc((unsigned) sizeof(Atom));
1339a3bd7f05Smrg            *ninfo->target = info->target[number + 1];
1340a3bd7f05Smrg            info = ninfo;
1341a3bd7f05Smrg        }
1342a3bd7f05Smrg        HandleIncremental(dpy, widget, property, info, size);
1343a3bd7f05Smrg        return FALSE;
1344444c061aSmrg    }
1345444c061aSmrg
1346444c061aSmrg    XDeleteProperty(dpy, XtWindow(widget), property);
1347444c061aSmrg#ifdef XT_COPY_SELECTION
1348a3bd7f05Smrg    if (value) {                /* it could have been deleted after the SelectionNotify */
1349a3bd7f05Smrg        int size = (int) BYTELENGTH(length, info->format) + 1;
1350a3bd7f05Smrg        char *tmp = __XtMalloc((Cardinal) size);
1351a3bd7f05Smrg
1352fdf6a26fSmrg        (void) memcpy(tmp, value, (size_t) size);
1353a3bd7f05Smrg        XFree(value);
1354a3bd7f05Smrg        value = (unsigned char *) tmp;
1355444c061aSmrg    }
1356444c061aSmrg#endif
1357a3bd7f05Smrg    (*info->callbacks[number]) (widget, closure, &selection,
1358a3bd7f05Smrg                                &type, (XtPointer) value, &length, &format);
1359444c061aSmrg
1360444c061aSmrg    if (info->incremental[number]) {
1361a3bd7f05Smrg        /* let requestor know the whole thing has been received */
1362a3bd7f05Smrg        value = (unsigned char *) __XtMalloc((unsigned) 1);
1363a3bd7f05Smrg        length = 0;
1364a3bd7f05Smrg        (*info->callbacks[number]) (widget, closure, &selection,
1365a3bd7f05Smrg                                    &type, (XtPointer) value, &length, &format);
1366444c061aSmrg    }
1367444c061aSmrg    return TRUE;
1368444c061aSmrg}
1369444c061aSmrg
1370a3bd7f05Smrgstatic void
1371a3bd7f05SmrgHandleIncremental(Display *dpy,
1372a3bd7f05Smrg                  Widget widget,
1373a3bd7f05Smrg                  Atom property,
1374a3bd7f05Smrg                  CallBackInfo info,
1375a3bd7f05Smrg                  unsigned long size)
1376444c061aSmrg{
1377444c061aSmrg    XtAddEventHandler(widget, (EventMask) PropertyChangeMask, FALSE,
1378a3bd7f05Smrg                      HandleGetIncrement, (XtPointer) info);
1379444c061aSmrg
1380444c061aSmrg    /* now start the transfer */
1381444c061aSmrg    XDeleteProperty(dpy, XtWindow(widget), property);
1382444c061aSmrg    XFlush(dpy);
1383444c061aSmrg
13840568f49bSmrg    info->bytelength = (int) size;
1385a3bd7f05Smrg    if (info->incremental[info->current])       /* requestor wants incremental too */
1386a3bd7f05Smrg        info->value = NULL;     /* so no need for buffer to assemble value */
1387444c061aSmrg    else
1388a3bd7f05Smrg        info->value = (char *) __XtMalloc((unsigned) info->bytelength);
1389444c061aSmrg    info->offset = 0;
1390444c061aSmrg
1391444c061aSmrg    /* reset the timer */
1392444c061aSmrg    info->proc = HandleGetIncrement;
1393444c061aSmrg#ifndef DEBUG_WO_TIMERS
1394444c061aSmrg    {
1395a3bd7f05Smrg        XtAppContext app = XtWidgetToApplicationContext(info->widget);
1396a3bd7f05Smrg
1397a3bd7f05Smrg        info->timeout = XtAppAddTimeOut(app,
1398a3bd7f05Smrg                                        app->selectionTimeout, ReqTimedOut,
1399a3bd7f05Smrg                                        (XtPointer) info);
1400444c061aSmrg    }
1401444c061aSmrg#endif
1402444c061aSmrg}
1403444c061aSmrg
1404a3bd7f05Smrgstatic void
1405a3bd7f05SmrgHandleSelectionReplies(Widget widget,
1406a3bd7f05Smrg                       XtPointer closure,
1407a3bd7f05Smrg                       XEvent *ev,
1408a3bd7f05Smrg                       Boolean *cont _X_UNUSED)
1409444c061aSmrg{
1410444c061aSmrg    XSelectionEvent *event = (XSelectionEvent *) ev;
1411444c061aSmrg    Display *dpy = event->display;
1412444c061aSmrg    CallBackInfo info = (CallBackInfo) closure;
1413444c061aSmrg    Select ctx = info->ctx;
1414444c061aSmrg    unsigned long bytesafter;
1415444c061aSmrg    unsigned long length;
1416444c061aSmrg    int format;
1417444c061aSmrg    Atom type;
1418444c061aSmrg
1419a3bd7f05Smrg    if (event->type != SelectionNotify)
1420a3bd7f05Smrg        return;
1421a3bd7f05Smrg    if (!MATCH_SELECT(event, info))
1422a3bd7f05Smrg        return;                 /* not really for us */
1423444c061aSmrg#ifndef DEBUG_WO_TIMERS
1424444c061aSmrg    XtRemoveTimeOut(info->timeout);
1425444c061aSmrg#endif
1426a3bd7f05Smrg    XtRemoveEventHandler(widget, (EventMask) 0, TRUE,
1427a3bd7f05Smrg                         HandleSelectionReplies, (XtPointer) info);
1428444c061aSmrg    if (event->target == ctx->prop_list->indirect_atom) {
1429a3bd7f05Smrg        IndirectPair *pairs = NULL, *p;
1430a3bd7f05Smrg        XtPointer *c;
1431a3bd7f05Smrg
1432a3bd7f05Smrg        if (XGetWindowProperty(dpy, XtWindow(widget), info->property, 0L,
1433a3bd7f05Smrg                               10000000, True, AnyPropertyType, &type, &format,
1434a3bd7f05Smrg                               &length, &bytesafter, (unsigned char **) &pairs)
1435a3bd7f05Smrg            != Success)
1436a3bd7f05Smrg            length = 0;
1437a3bd7f05Smrg        for (length = length / IndirectPairWordSize, p = pairs,
1438a3bd7f05Smrg             c = info->req_closure;
1439a3bd7f05Smrg             length; length--, p++, c++, info->current++) {
1440a3bd7f05Smrg            if (event->property == None || format != 32 || p->target == None
1441a3bd7f05Smrg                || /* bug compatibility */ p->property == None) {
1442a3bd7f05Smrg                HandleNone(widget, info->callbacks[info->current],
1443a3bd7f05Smrg                           *c, event->selection);
1444a3bd7f05Smrg                if (p->property != None)
1445a3bd7f05Smrg                    FreeSelectionProperty(XtDisplay(widget), p->property);
1446a3bd7f05Smrg            }
1447a3bd7f05Smrg            else {
1448a3bd7f05Smrg                if (HandleNormal(dpy, widget, p->property, info, *c,
1449a3bd7f05Smrg                                 event->selection)) {
1450444c061aSmrg                    FreeSelectionProperty(XtDisplay(widget), p->property);
1451a3bd7f05Smrg                }
1452a3bd7f05Smrg            }
1453a3bd7f05Smrg        }
1454a3bd7f05Smrg        XFree((char *) pairs);
1455a3bd7f05Smrg        FreeSelectionProperty(dpy, info->property);
1456a3bd7f05Smrg        FreeInfo(info);
1457a3bd7f05Smrg    }
1458a3bd7f05Smrg    else if (event->property == None) {
1459a3bd7f05Smrg        HandleNone(widget, info->callbacks[0], *info->req_closure,
1460a3bd7f05Smrg                   event->selection);
1461444c061aSmrg        FreeSelectionProperty(XtDisplay(widget), info->property);
1462a3bd7f05Smrg        FreeInfo(info);
1463a3bd7f05Smrg    }
1464a3bd7f05Smrg    else {
1465a3bd7f05Smrg        if (HandleNormal(dpy, widget, event->property, info,
1466a3bd7f05Smrg                         *info->req_closure, event->selection)) {
1467a3bd7f05Smrg            FreeSelectionProperty(XtDisplay(widget), info->property);
1468a3bd7f05Smrg            FreeInfo(info);
1469a3bd7f05Smrg        }
1470444c061aSmrg    }
1471444c061aSmrg}
1472444c061aSmrg
1473a3bd7f05Smrgstatic void
1474a3bd7f05SmrgDoLocalTransfer(Request req,
1475a3bd7f05Smrg                Atom selection,
1476a3bd7f05Smrg                Atom target,
1477a3bd7f05Smrg                Widget widget, /* The widget requesting the value. */
1478a3bd7f05Smrg                XtSelectionCallbackProc callback,
1479a3bd7f05Smrg                XtPointer closure,    /* the closure for the callback, not the conversion */
1480a3bd7f05Smrg                Boolean incremental, Atom property)
1481444c061aSmrg{
1482444c061aSmrg    Select ctx = req->ctx;
1483444c061aSmrg    XtPointer value = NULL, temp, total = NULL;
1484444c061aSmrg    unsigned long length;
1485444c061aSmrg    int format;
1486444c061aSmrg    Atom resulttype;
1487444c061aSmrg    unsigned long totallength = 0;
1488444c061aSmrg
1489a3bd7f05Smrg    req->event.type = 0;
1490a3bd7f05Smrg    req->event.target = target;
1491a3bd7f05Smrg    req->event.property = req->property = property;
1492a3bd7f05Smrg    req->event.requestor = req->requestor = XtWindow(widget);
1493a3bd7f05Smrg
1494a3bd7f05Smrg    if (ctx->incremental) {
1495a3bd7f05Smrg        unsigned long size = (unsigned long) MAX_SELECTION_INCR(ctx->dpy);
1496a3bd7f05Smrg
1497a3bd7f05Smrg        if (!(*(XtConvertSelectionIncrProc) ctx->convert)
1498a3bd7f05Smrg            (ctx->widget, &selection, &target,
1499a3bd7f05Smrg             &resulttype, &value, &length, &format,
1500a3bd7f05Smrg             &size, ctx->owner_closure, (XtRequestId *) &req)) {
1501a3bd7f05Smrg            HandleNone(widget, callback, closure, selection);
1502a3bd7f05Smrg        }
1503a3bd7f05Smrg        else {
1504a3bd7f05Smrg            if (incremental) {
1505a3bd7f05Smrg                Boolean allSent = FALSE;
1506a3bd7f05Smrg
1507a3bd7f05Smrg                while (!allSent) {
1508a3bd7f05Smrg                    if (ctx->notify && (value != NULL)) {
1509a3bd7f05Smrg                        int bytelength = (int) BYTELENGTH(length, format);
1510a3bd7f05Smrg
1511a3bd7f05Smrg                        /* both sides think they own this storage */
1512a3bd7f05Smrg                        temp = __XtMalloc((unsigned) bytelength);
1513fdf6a26fSmrg                        (void) memcpy(temp, value, (size_t) bytelength);
1514a3bd7f05Smrg                        value = temp;
1515a3bd7f05Smrg                    }
1516a3bd7f05Smrg                    /* use care; older clients were never warned that
1517a3bd7f05Smrg                     * they must return a value even if length==0
1518a3bd7f05Smrg                     */
1519a3bd7f05Smrg                    if (value == NULL)
1520a3bd7f05Smrg                        value = __XtMalloc((unsigned) 1);
1521a3bd7f05Smrg                    (*callback) (widget, closure, &selection,
1522a3bd7f05Smrg                                 &resulttype, value, &length, &format);
1523a3bd7f05Smrg                    if (length) {
1524a3bd7f05Smrg                        /* should owner be notified on end-of-piece?
1525a3bd7f05Smrg                         * Spec is unclear, but non-local transfers don't.
1526a3bd7f05Smrg                         */
1527a3bd7f05Smrg                        (*(XtConvertSelectionIncrProc) ctx->convert)
1528a3bd7f05Smrg                            (ctx->widget, &selection, &target,
1529a3bd7f05Smrg                             &resulttype, &value, &length, &format,
1530a3bd7f05Smrg                             &size, ctx->owner_closure, (XtRequestId *) &req);
1531a3bd7f05Smrg                    }
1532a3bd7f05Smrg                    else
1533a3bd7f05Smrg                        allSent = TRUE;
1534a3bd7f05Smrg                }
1535a3bd7f05Smrg            }
1536a3bd7f05Smrg            else {
1537a3bd7f05Smrg                while (length) {
1538a3bd7f05Smrg                    int bytelength = (int) BYTELENGTH(length, format);
1539a3bd7f05Smrg
1540a3bd7f05Smrg                    total = XtRealloc(total,
1541a3bd7f05Smrg                                      (Cardinal) (totallength =
1542a3bd7f05Smrg                                                  totallength +
1543a3bd7f05Smrg                                                  (unsigned long) bytelength));
1544fdf6a26fSmrg                    (void) memcpy((char *) total + totallength - bytelength,
1545a3bd7f05Smrg                                   value, (size_t) bytelength);
1546a3bd7f05Smrg                    (*(XtConvertSelectionIncrProc) ctx->convert)
1547a3bd7f05Smrg                        (ctx->widget, &selection, &target,
1548a3bd7f05Smrg                         &resulttype, &value, &length, &format,
1549a3bd7f05Smrg                         &size, ctx->owner_closure, (XtRequestId *) &req);
1550a3bd7f05Smrg                }
1551a3bd7f05Smrg                if (total == NULL)
1552a3bd7f05Smrg                    total = __XtMalloc(1);
1553a3bd7f05Smrg                totallength = NUMELEM2(totallength, format);
1554a3bd7f05Smrg                (*callback) (widget, closure, &selection, &resulttype,
1555a3bd7f05Smrg                             total, &totallength, &format);
1556a3bd7f05Smrg            }
1557a3bd7f05Smrg            if (ctx->notify)
1558a3bd7f05Smrg                (*(XtSelectionDoneIncrProc) ctx->notify)
1559a3bd7f05Smrg                    (ctx->widget, &selection, &target,
1560a3bd7f05Smrg                     (XtRequestId *) &req, ctx->owner_closure);
1561a3bd7f05Smrg            else
1562a3bd7f05Smrg                XtFree((char *) value);
1563a3bd7f05Smrg        }
1564a3bd7f05Smrg    }
1565a3bd7f05Smrg    else {                      /* not incremental owner */
1566a3bd7f05Smrg        if (!(*ctx->convert) (ctx->widget, &selection, &target,
1567a3bd7f05Smrg                              &resulttype, &value, &length, &format)) {
1568a3bd7f05Smrg            HandleNone(widget, callback, closure, selection);
1569a3bd7f05Smrg        }
1570a3bd7f05Smrg        else {
1571a3bd7f05Smrg            if (ctx->notify && (value != NULL)) {
1572a3bd7f05Smrg                int bytelength = (int) BYTELENGTH(length, format);
1573a3bd7f05Smrg
1574a3bd7f05Smrg                /* both sides think they own this storage; better copy */
1575a3bd7f05Smrg                temp = __XtMalloc((unsigned) bytelength);
1576fdf6a26fSmrg                (void) memcpy(temp, value, (size_t) bytelength);
1577a3bd7f05Smrg                value = temp;
1578a3bd7f05Smrg            }
1579a3bd7f05Smrg            if (value == NULL)
1580a3bd7f05Smrg                value = __XtMalloc((unsigned) 1);
1581a3bd7f05Smrg            (*callback) (widget, closure, &selection, &resulttype,
1582a3bd7f05Smrg                         value, &length, &format);
1583a3bd7f05Smrg            if (ctx->notify)
1584a3bd7f05Smrg                (*ctx->notify) (ctx->widget, &selection, &target);
1585a3bd7f05Smrg        }
1586a3bd7f05Smrg    }
1587444c061aSmrg}
1588444c061aSmrg
1589a3bd7f05Smrgstatic void
1590a3bd7f05SmrgGetSelectionValue(Widget widget,
1591a3bd7f05Smrg                  Atom selection,
1592a3bd7f05Smrg                  Atom target,
1593a3bd7f05Smrg                  XtSelectionCallbackProc callback,
1594a3bd7f05Smrg                  XtPointer closure,
1595a3bd7f05Smrg                  Time time,
1596a3bd7f05Smrg                  Boolean incremental,
1597a3bd7f05Smrg                  Atom property)
1598444c061aSmrg{
1599444c061aSmrg    Select ctx;
1600444c061aSmrg    Atom properties[1];
1601444c061aSmrg
1602444c061aSmrg    properties[0] = property;
1603444c061aSmrg
1604444c061aSmrg    ctx = FindCtx(XtDisplay(widget), selection);
1605444c061aSmrg    if (ctx->widget && !ctx->was_disowned) {
1606a3bd7f05Smrg        RequestRec req;
1607a3bd7f05Smrg
1608a3bd7f05Smrg        ctx->req = &req;
1609fdf6a26fSmrg        memset(&req, 0, sizeof(req));
1610a3bd7f05Smrg        req.ctx = ctx;
1611a3bd7f05Smrg        req.event.time = time;
1612a3bd7f05Smrg        ctx->ref_count++;
1613a3bd7f05Smrg        DoLocalTransfer(&req, selection, target, widget,
1614a3bd7f05Smrg                        callback, closure, incremental, property);
1615a3bd7f05Smrg        if (--ctx->ref_count == 0 && ctx->free_when_done)
1616a3bd7f05Smrg            XtFree((char *) ctx);
1617a3bd7f05Smrg        else
1618a3bd7f05Smrg            ctx->req = NULL;
1619444c061aSmrg    }
1620444c061aSmrg    else {
1621a3bd7f05Smrg        CallBackInfo info;
1622a3bd7f05Smrg
1623a3bd7f05Smrg        info = MakeInfo(ctx, &callback, &closure, 1, widget,
1624a3bd7f05Smrg                        time, &incremental, properties);
1625a3bd7f05Smrg        info->target = (Atom *) __XtMalloc((unsigned) sizeof(Atom));
1626a3bd7f05Smrg        *(info->target) = target;
1627a3bd7f05Smrg        RequestSelectionValue(info, selection, target);
1628444c061aSmrg    }
1629444c061aSmrg}
1630444c061aSmrg
1631a3bd7f05Smrgvoid
1632a3bd7f05SmrgXtGetSelectionValue(Widget widget,
1633a3bd7f05Smrg                    Atom selection,
1634a3bd7f05Smrg                    Atom target,
1635a3bd7f05Smrg                    XtSelectionCallbackProc callback,
1636a3bd7f05Smrg                    XtPointer closure,
1637a3bd7f05Smrg                    Time time)
1638444c061aSmrg{
1639444c061aSmrg    Atom property;
1640444c061aSmrg    Boolean incr = False;
1641a3bd7f05Smrg
1642444c061aSmrg    WIDGET_TO_APPCON(widget);
1643444c061aSmrg
1644444c061aSmrg    LOCK_APP(app);
1645444c061aSmrg    property = GetParamInfo(widget, selection);
1646444c061aSmrg    RemoveParamInfo(widget, selection);
1647444c061aSmrg
1648444c061aSmrg    if (IsGatheringRequest(widget, selection)) {
1649a3bd7f05Smrg        AddSelectionRequests(widget, selection, 1, &target, &callback, 1,
1650a3bd7f05Smrg                             &closure, &incr, &property);
1651a3bd7f05Smrg    }
1652a3bd7f05Smrg    else {
1653a3bd7f05Smrg        GetSelectionValue(widget, selection, target, callback,
1654a3bd7f05Smrg                          closure, time, FALSE, property);
1655444c061aSmrg    }
1656444c061aSmrg    UNLOCK_APP(app);
1657444c061aSmrg}
1658444c061aSmrg
1659a3bd7f05Smrgvoid
1660a3bd7f05SmrgXtGetSelectionValueIncremental(Widget widget,
1661a3bd7f05Smrg                               Atom selection,
1662a3bd7f05Smrg                               Atom target,
1663a3bd7f05Smrg                               XtSelectionCallbackProc callback,
1664a3bd7f05Smrg                               XtPointer closure,
1665a3bd7f05Smrg                               Time time)
1666444c061aSmrg{
1667444c061aSmrg    Atom property;
1668444c061aSmrg    Boolean incr = TRUE;
1669a3bd7f05Smrg
1670444c061aSmrg    WIDGET_TO_APPCON(widget);
1671444c061aSmrg
1672444c061aSmrg    LOCK_APP(app);
1673444c061aSmrg    property = GetParamInfo(widget, selection);
1674444c061aSmrg    RemoveParamInfo(widget, selection);
1675444c061aSmrg
1676444c061aSmrg    if (IsGatheringRequest(widget, selection)) {
1677a3bd7f05Smrg        AddSelectionRequests(widget, selection, 1, &target, &callback, 1,
1678a3bd7f05Smrg                             &closure, &incr, &property);
1679a3bd7f05Smrg    }
1680a3bd7f05Smrg    else {
1681a3bd7f05Smrg        GetSelectionValue(widget, selection, target, callback,
1682a3bd7f05Smrg                          closure, time, TRUE, property);
1683444c061aSmrg    }
1684444c061aSmrg
1685444c061aSmrg    UNLOCK_APP(app);
1686444c061aSmrg}
1687444c061aSmrg
1688a3bd7f05Smrgstatic void
1689a3bd7f05SmrgGetSelectionValues(Widget widget,
1690a3bd7f05Smrg                   Atom selection,
1691a3bd7f05Smrg                   Atom *targets,
1692a3bd7f05Smrg                   int count,
1693a3bd7f05Smrg                   XtSelectionCallbackProc *callbacks,
1694a3bd7f05Smrg                   int num_callbacks,
1695a3bd7f05Smrg                   XtPointer *closures,
1696a3bd7f05Smrg                   Time time,
1697a3bd7f05Smrg                   Boolean *incremental,
1698a3bd7f05Smrg                   Atom *properties)
1699444c061aSmrg{
1700444c061aSmrg    Select ctx;
17010568f49bSmrg    IndirectPair *pairs;
1702444c061aSmrg
1703a3bd7f05Smrg    if (count == 0)
1704a3bd7f05Smrg        return;
1705444c061aSmrg    ctx = FindCtx(XtDisplay(widget), selection);
1706444c061aSmrg    if (ctx->widget && !ctx->was_disowned) {
1707444c061aSmrg        int j, i;
1708a3bd7f05Smrg        RequestRec req;
1709a3bd7f05Smrg
1710a3bd7f05Smrg        ctx->req = &req;
1711a3bd7f05Smrg        req.ctx = ctx;
1712a3bd7f05Smrg        req.event.time = time;
1713a3bd7f05Smrg        ctx->ref_count++;
1714fdf6a26fSmrg        for (i = 0, j = 0; count > 0; count--, i++, j++) {
1715a3bd7f05Smrg            if (j >= num_callbacks)
1716a3bd7f05Smrg                j = 0;
1717a3bd7f05Smrg
1718a3bd7f05Smrg            DoLocalTransfer(&req, selection, targets[i], widget,
1719a3bd7f05Smrg                            callbacks[j], closures[i], incremental[i],
1720a3bd7f05Smrg                            properties ? properties[i] : None);
1721a3bd7f05Smrg
1722a3bd7f05Smrg        }
1723a3bd7f05Smrg        if (--ctx->ref_count == 0 && ctx->free_when_done)
1724a3bd7f05Smrg            XtFree((char *) ctx);
1725a3bd7f05Smrg        else
1726a3bd7f05Smrg            ctx->req = NULL;
1727a3bd7f05Smrg    }
1728a3bd7f05Smrg    else {
1729444c061aSmrg        XtSelectionCallbackProc *passed_callbacks;
1730a3bd7f05Smrg        XtSelectionCallbackProc stack_cbs[32];
1731a3bd7f05Smrg        CallBackInfo info;
1732a3bd7f05Smrg        IndirectPair *p;
1733a3bd7f05Smrg        Atom *t;
1734444c061aSmrg        int i = 0, j = 0;
1735444c061aSmrg
1736a3bd7f05Smrg        passed_callbacks = (XtSelectionCallbackProc *)
1737a3bd7f05Smrg            XtStackAlloc(sizeof(XtSelectionCallbackProc) * (size_t) count,
1738a3bd7f05Smrg                         stack_cbs);
1739a3bd7f05Smrg
1740a3bd7f05Smrg        /* To deal with the old calls from XtGetSelectionValues* we
1741a3bd7f05Smrg           will repeat however many callbacks have been passed into
1742a3bd7f05Smrg           the array */
1743a3bd7f05Smrg        for (i = 0; i < count; i++) {
1744a3bd7f05Smrg            if (j >= num_callbacks)
1745a3bd7f05Smrg                j = 0;
1746a3bd7f05Smrg            passed_callbacks[i] = callbacks[j];
1747a3bd7f05Smrg            j++;
1748a3bd7f05Smrg        }
1749a3bd7f05Smrg        info = MakeInfo(ctx, passed_callbacks, closures, count, widget,
1750a3bd7f05Smrg                        time, incremental, properties);
1751a3bd7f05Smrg        XtStackFree((XtPointer) passed_callbacks, stack_cbs);
1752a3bd7f05Smrg
1753fdf6a26fSmrg        info->target = XtMallocArray ((Cardinal) count + 1,
1754fdf6a26fSmrg                                      (Cardinal) sizeof(Atom));
1755444c061aSmrg        (*info->target) = ctx->prop_list->indirect_atom;
1756fdf6a26fSmrg        (void) memcpy((char *) info->target + sizeof(Atom), targets,
1757a3bd7f05Smrg                       (size_t) count * sizeof(Atom));
1758fdf6a26fSmrg        pairs = XtMallocArray ((Cardinal) count + 1,
1759fdf6a26fSmrg                               (Cardinal) sizeof(IndirectPair));
1760a3bd7f05Smrg        for (p = &pairs[count - 1], t = &targets[count - 1], i = count - 1;
1761a3bd7f05Smrg             p >= pairs; p--, t--, i--) {
1762a3bd7f05Smrg            p->target = *t;
1763a3bd7f05Smrg            if (properties == NULL || properties[i] == None) {
1764a3bd7f05Smrg                p->property = GetSelectionProperty(XtDisplay(widget));
1765a3bd7f05Smrg                XDeleteProperty(XtDisplay(widget), XtWindow(widget),
1766a3bd7f05Smrg                                p->property);
1767a3bd7f05Smrg            }
1768a3bd7f05Smrg            else {
1769a3bd7f05Smrg                p->property = properties[i];
1770a3bd7f05Smrg            }
1771a3bd7f05Smrg        }
1772a3bd7f05Smrg        XChangeProperty(XtDisplay(widget), XtWindow(widget),
1773a3bd7f05Smrg                        info->property, info->property,
1774a3bd7f05Smrg                        32, PropModeReplace, (unsigned char *) pairs,
1775a3bd7f05Smrg                        count * IndirectPairWordSize);
1776a3bd7f05Smrg        XtFree((char *) pairs);
1777a3bd7f05Smrg        RequestSelectionValue(info, selection, ctx->prop_list->indirect_atom);
1778444c061aSmrg    }
1779444c061aSmrg}
1780444c061aSmrg
1781a3bd7f05Smrgvoid
1782a3bd7f05SmrgXtGetSelectionValues(Widget widget,
1783a3bd7f05Smrg                     Atom selection,
1784a3bd7f05Smrg                     Atom *targets,
1785a3bd7f05Smrg                     int count,
1786a3bd7f05Smrg                     XtSelectionCallbackProc callback,
1787a3bd7f05Smrg                     XtPointer *closures,
1788a3bd7f05Smrg                     Time time)
1789444c061aSmrg{
1790444c061aSmrg    Boolean incremental_values[32];
1791444c061aSmrg    Boolean *incremental;
1792444c061aSmrg    int i;
1793a3bd7f05Smrg
1794444c061aSmrg    WIDGET_TO_APPCON(widget);
1795444c061aSmrg
1796444c061aSmrg    LOCK_APP(app);
1797a3bd7f05Smrg    incremental =
1798a3bd7f05Smrg        XtStackAlloc((size_t) count * sizeof(Boolean), incremental_values);
1799a3bd7f05Smrg    for (i = 0; i < count; i++)
1800a3bd7f05Smrg        incremental[i] = FALSE;
1801444c061aSmrg    if (IsGatheringRequest(widget, selection)) {
1802a3bd7f05Smrg        AddSelectionRequests(widget, selection, count, targets, &callback,
1803a3bd7f05Smrg                             1, closures, incremental, NULL);
1804a3bd7f05Smrg    }
1805a3bd7f05Smrg    else {
1806a3bd7f05Smrg        GetSelectionValues(widget, selection, targets, count, &callback, 1,
1807a3bd7f05Smrg                           closures, time, incremental, NULL);
1808444c061aSmrg    }
1809444c061aSmrg    XtStackFree((XtPointer) incremental, incremental_values);
1810444c061aSmrg    UNLOCK_APP(app);
1811444c061aSmrg}
1812444c061aSmrg
1813a3bd7f05Smrgvoid
1814a3bd7f05SmrgXtGetSelectionValuesIncremental(Widget widget,
1815a3bd7f05Smrg                                Atom selection,
1816a3bd7f05Smrg                                Atom *targets,
1817a3bd7f05Smrg                                int count,
1818a3bd7f05Smrg                                XtSelectionCallbackProc callback,
1819a3bd7f05Smrg                                XtPointer *closures,
1820a3bd7f05Smrg                                Time time)
1821444c061aSmrg{
1822444c061aSmrg    Boolean incremental_values[32];
1823444c061aSmrg    Boolean *incremental;
1824444c061aSmrg    int i;
1825a3bd7f05Smrg
1826444c061aSmrg    WIDGET_TO_APPCON(widget);
1827444c061aSmrg
1828444c061aSmrg    LOCK_APP(app);
1829a3bd7f05Smrg    incremental =
1830a3bd7f05Smrg        XtStackAlloc((size_t) count * sizeof(Boolean), incremental_values);
1831a3bd7f05Smrg    for (i = 0; i < count; i++)
1832a3bd7f05Smrg        incremental[i] = TRUE;
1833444c061aSmrg    if (IsGatheringRequest(widget, selection)) {
1834a3bd7f05Smrg        AddSelectionRequests(widget, selection, count, targets, &callback,
1835a3bd7f05Smrg                             1, closures, incremental, NULL);
1836a3bd7f05Smrg    }
1837a3bd7f05Smrg    else {
1838a3bd7f05Smrg        GetSelectionValues(widget, selection, targets, count,
1839a3bd7f05Smrg                           &callback, 1, closures, time, incremental, NULL);
1840444c061aSmrg    }
1841444c061aSmrg    XtStackFree((XtPointer) incremental, incremental_values);
1842444c061aSmrg    UNLOCK_APP(app);
1843444c061aSmrg}
1844444c061aSmrg
1845a3bd7f05Smrgstatic Request
1846a3bd7f05SmrgGetRequestRecord(Widget widget, Atom selection, XtRequestId id)
1847444c061aSmrg{
1848a3bd7f05Smrg    Request req = (Request) id;
1849444c061aSmrg    Select ctx = NULL;
1850444c061aSmrg
1851a3bd7f05Smrg    if ((req == NULL
1852a3bd7f05Smrg         && ((ctx = FindCtx(XtDisplay(widget), selection)) == NULL
1853a3bd7f05Smrg             || ctx->req == NULL
1854a3bd7f05Smrg             || ctx->selection != selection || ctx->widget == NULL))
1855a3bd7f05Smrg        || (req != NULL
1856a3bd7f05Smrg            && (req->ctx == NULL
1857a3bd7f05Smrg                || req->ctx->selection != selection
1858a3bd7f05Smrg                || req->ctx->widget != widget))) {
1859a3bd7f05Smrg        String params = XtName(widget);
1860a3bd7f05Smrg        Cardinal num_params = 1;
1861a3bd7f05Smrg
1862a3bd7f05Smrg        XtAppWarningMsg(XtWidgetToApplicationContext(widget),
1863a3bd7f05Smrg                        "notInConvertSelection", "xtGetSelectionRequest",
1864a3bd7f05Smrg                        XtCXtToolkitError,
1865a3bd7f05Smrg                        "XtGetSelectionRequest or XtGetSelectionParameters called for widget \"%s\" outside of ConvertSelection proc",
1866a3bd7f05Smrg                        &params, &num_params);
1867a3bd7f05Smrg        return NULL;
1868444c061aSmrg    }
1869444c061aSmrg
1870444c061aSmrg    if (req == NULL) {
1871a3bd7f05Smrg        /* non-incremental owner; only one request can be
1872a3bd7f05Smrg         * outstanding at a time, so it's safe to keep ptr in ctx */
1873a3bd7f05Smrg        req = ctx->req;
1874444c061aSmrg    }
1875444c061aSmrg    return req;
1876444c061aSmrg}
1877444c061aSmrg
1878a3bd7f05SmrgXSelectionRequestEvent *
1879a3bd7f05SmrgXtGetSelectionRequest(Widget widget, Atom selection, XtRequestId id)
1880444c061aSmrg{
1881a3bd7f05Smrg    Request req = (Request) id;
1882a3bd7f05Smrg
1883444c061aSmrg    WIDGET_TO_APPCON(widget);
1884444c061aSmrg
1885444c061aSmrg    LOCK_APP(app);
1886444c061aSmrg
1887444c061aSmrg    req = GetRequestRecord(widget, selection, id);
1888444c061aSmrg
1889a3bd7f05Smrg    if (!req) {
1890a3bd7f05Smrg        UNLOCK_APP(app);
1891a3bd7f05Smrg        return (XSelectionRequestEvent *) NULL;
1892444c061aSmrg    }
1893444c061aSmrg
1894444c061aSmrg    if (req->event.type == 0) {
1895a3bd7f05Smrg        /* owner is local; construct the remainder of the event */
1896a3bd7f05Smrg        req->event.type = SelectionRequest;
1897a3bd7f05Smrg        req->event.serial = LastKnownRequestProcessed(XtDisplay(widget));
1898a3bd7f05Smrg        req->event.send_event = True;
1899a3bd7f05Smrg        req->event.display = XtDisplay(widget);
1900a3bd7f05Smrg
1901a3bd7f05Smrg        req->event.owner = XtWindow(req->ctx->widget);
1902a3bd7f05Smrg        req->event.selection = selection;
1903444c061aSmrg    }
1904444c061aSmrg    UNLOCK_APP(app);
1905444c061aSmrg    return &req->event;
1906444c061aSmrg}
1907444c061aSmrg
1908444c061aSmrg/* Property atom access */
1909a3bd7f05SmrgAtom
1910a3bd7f05SmrgXtReservePropertyAtom(Widget w)
1911444c061aSmrg{
1912a3bd7f05Smrg    return (GetSelectionProperty(XtDisplay(w)));
1913444c061aSmrg}
1914444c061aSmrg
1915a3bd7f05Smrgvoid
1916a3bd7f05SmrgXtReleasePropertyAtom(Widget w, Atom atom)
1917444c061aSmrg{
1918a3bd7f05Smrg    FreeSelectionProperty(XtDisplay(w), atom);
1919444c061aSmrg}
1920444c061aSmrg
1921444c061aSmrg/* Multiple utilities */
1922444c061aSmrg
1923444c061aSmrg/* All requests are put in a single list per widget.  It is
1924444c061aSmrg   very unlikely anyone will be gathering multiple MULTIPLE
1925444c061aSmrg   requests at the same time,  so the loss in efficiency for
1926444c061aSmrg   this case is acceptable */
1927444c061aSmrg
1928444c061aSmrg/* Queue one or more requests to the one we're gathering */
1929a3bd7f05Smrgstatic void
1930a3bd7f05SmrgAddSelectionRequests(Widget wid,
1931a3bd7f05Smrg                     Atom sel,
1932a3bd7f05Smrg                     int count,
1933a3bd7f05Smrg                     Atom *targets,
1934a3bd7f05Smrg                     XtSelectionCallbackProc *callbacks,
1935a3bd7f05Smrg                     int num_cb,
1936a3bd7f05Smrg                     XtPointer *closures,
1937a3bd7f05Smrg                     Boolean *incrementals,
1938a3bd7f05Smrg                     Atom *properties)
1939444c061aSmrg{
1940a3bd7f05Smrg    QueuedRequestInfo qi;
1941a3bd7f05Smrg    Window window = XtWindow(wid);
1942a3bd7f05Smrg    Display *dpy = XtDisplay(wid);
1943444c061aSmrg
1944a3bd7f05Smrg    LOCK_PROCESS;
1945a3bd7f05Smrg    if (multipleContext == 0)
1946a3bd7f05Smrg        multipleContext = XUniqueContext();
1947a3bd7f05Smrg
1948a3bd7f05Smrg    qi = NULL;
1949a3bd7f05Smrg    (void) XFindContext(dpy, window, multipleContext, (XPointer *) &qi);
1950a3bd7f05Smrg
1951a3bd7f05Smrg    if (qi != NULL) {
1952a3bd7f05Smrg        QueuedRequest *req = qi->requests;
1953a3bd7f05Smrg        int start = qi->count;
1954a3bd7f05Smrg        int i = 0;
1955a3bd7f05Smrg        int j = 0;
1956a3bd7f05Smrg
1957a3bd7f05Smrg        qi->count += count;
1958fdf6a26fSmrg        req = XtReallocArray(req, (Cardinal) (start + count),
1959fdf6a26fSmrg                             (Cardinal) sizeof(QueuedRequest));
1960a3bd7f05Smrg        while (i < count) {
1961a3bd7f05Smrg            QueuedRequest newreq = (QueuedRequest)
1962a3bd7f05Smrg                __XtMalloc(sizeof(QueuedRequestRec));
1963a3bd7f05Smrg
1964a3bd7f05Smrg            newreq->selection = sel;
1965a3bd7f05Smrg            newreq->target = targets[i];
1966a3bd7f05Smrg            if (properties != NULL)
1967a3bd7f05Smrg                newreq->param = properties[i];
1968a3bd7f05Smrg            else {
1969a3bd7f05Smrg                newreq->param = GetSelectionProperty(dpy);
1970a3bd7f05Smrg                XDeleteProperty(dpy, window, newreq->param);
1971a3bd7f05Smrg            }
1972a3bd7f05Smrg            newreq->callback = callbacks[j];
1973a3bd7f05Smrg            newreq->closure = closures[i];
1974a3bd7f05Smrg            newreq->incremental = incrementals[i];
1975a3bd7f05Smrg
1976a3bd7f05Smrg            req[start] = newreq;
1977a3bd7f05Smrg            start++;
1978a3bd7f05Smrg            i++;
1979a3bd7f05Smrg            j++;
1980a3bd7f05Smrg            if (j > num_cb)
1981a3bd7f05Smrg                j = 0;
1982a3bd7f05Smrg        }
1983444c061aSmrg
1984a3bd7f05Smrg        qi->requests = req;
1985a3bd7f05Smrg    }
1986a3bd7f05Smrg    else {
1987a3bd7f05Smrg        /* Impossible */
1988a3bd7f05Smrg    }
1989444c061aSmrg
1990a3bd7f05Smrg    UNLOCK_PROCESS;
1991a3bd7f05Smrg}
1992444c061aSmrg
1993a3bd7f05Smrg/* Only call IsGatheringRequest when we have a lock already */
1994444c061aSmrg
1995a3bd7f05Smrgstatic Boolean
1996a3bd7f05SmrgIsGatheringRequest(Widget wid, Atom sel)
1997a3bd7f05Smrg{
1998a3bd7f05Smrg    QueuedRequestInfo qi;
1999a3bd7f05Smrg    Window window = XtWindow(wid);
2000a3bd7f05Smrg    Display *dpy = XtDisplay(wid);
2001a3bd7f05Smrg    Boolean found = False;
2002a3bd7f05Smrg
2003a3bd7f05Smrg    if (multipleContext == 0)
2004a3bd7f05Smrg        multipleContext = XUniqueContext();
2005a3bd7f05Smrg
2006a3bd7f05Smrg    qi = NULL;
2007a3bd7f05Smrg    (void) XFindContext(dpy, window, multipleContext, (XPointer *) &qi);
2008a3bd7f05Smrg
2009a3bd7f05Smrg    if (qi != NULL) {
2010a3bd7f05Smrg        int i = 0;
2011a3bd7f05Smrg
2012a3bd7f05Smrg        while (qi->selections[i] != None) {
2013a3bd7f05Smrg            if (qi->selections[i] == sel) {
2014a3bd7f05Smrg                found = True;
2015a3bd7f05Smrg                break;
2016a3bd7f05Smrg            }
2017a3bd7f05Smrg            i++;
2018a3bd7f05Smrg        }
2019444c061aSmrg    }
2020444c061aSmrg
2021a3bd7f05Smrg    return (found);
2022444c061aSmrg}
2023444c061aSmrg
2024444c061aSmrg/* Cleanup request scans the request queue and releases any
2025444c061aSmrg   properties queued, and removes any requests queued */
2026a3bd7f05Smrgstatic void
2027a3bd7f05SmrgCleanupRequest(Display *dpy, QueuedRequestInfo qi, Atom sel)
2028444c061aSmrg{
2029a3bd7f05Smrg    int i, j, n;
2030a3bd7f05Smrg
2031fdf6a26fSmrg    if (qi == NULL)
2032fdf6a26fSmrg        return;
2033fdf6a26fSmrg
2034a3bd7f05Smrg    i = 0;
2035a3bd7f05Smrg
2036a3bd7f05Smrg    /* Remove this selection from the list */
2037a3bd7f05Smrg    n = 0;
2038a3bd7f05Smrg    while (qi->selections[n] != sel && qi->selections[n] != None)
2039a3bd7f05Smrg        n++;
2040a3bd7f05Smrg    if (qi->selections[n] == sel) {
2041a3bd7f05Smrg        while (qi->selections[n] != None) {
2042a3bd7f05Smrg            qi->selections[n] = qi->selections[n + 1];
2043a3bd7f05Smrg            n++;
2044a3bd7f05Smrg        }
2045444c061aSmrg    }
2046444c061aSmrg
2047a3bd7f05Smrg    while (i < qi->count) {
2048a3bd7f05Smrg        QueuedRequest req = qi->requests[i];
2049444c061aSmrg
2050a3bd7f05Smrg        if (req->selection == sel) {
2051a3bd7f05Smrg            /* Match */
2052a3bd7f05Smrg            if (req->param != None)
2053a3bd7f05Smrg                FreeSelectionProperty(dpy, req->param);
2054a3bd7f05Smrg            qi->count--;
2055444c061aSmrg
2056a3bd7f05Smrg            for (j = i; j < qi->count; j++)
2057a3bd7f05Smrg                qi->requests[j] = qi->requests[j + 1];
2058444c061aSmrg
2059a3bd7f05Smrg            XtFree((char *) req);
2060a3bd7f05Smrg        }
2061a3bd7f05Smrg        else {
2062a3bd7f05Smrg            i++;
2063a3bd7f05Smrg        }
2064444c061aSmrg    }
2065444c061aSmrg}
2066444c061aSmrg
2067a3bd7f05Smrgvoid
2068a3bd7f05SmrgXtCreateSelectionRequest(Widget widget, Atom selection)
2069444c061aSmrg{
2070a3bd7f05Smrg    QueuedRequestInfo queueInfo;
2071a3bd7f05Smrg    Window window = XtWindow(widget);
2072a3bd7f05Smrg    Display *dpy = XtDisplay(widget);
2073fdf6a26fSmrg    Cardinal n;
2074444c061aSmrg
2075a3bd7f05Smrg    LOCK_PROCESS;
2076a3bd7f05Smrg    if (multipleContext == 0)
2077a3bd7f05Smrg        multipleContext = XUniqueContext();
2078444c061aSmrg
2079a3bd7f05Smrg    queueInfo = NULL;
2080a3bd7f05Smrg    (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo);
2081444c061aSmrg
2082a3bd7f05Smrg    /* If there is one,  then cancel it */
2083a3bd7f05Smrg    if (queueInfo != NULL)
2084a3bd7f05Smrg        CleanupRequest(dpy, queueInfo, selection);
2085a3bd7f05Smrg    else {
2086a3bd7f05Smrg        /* Create it */
2087a3bd7f05Smrg        queueInfo =
2088a3bd7f05Smrg            (QueuedRequestInfo) __XtMalloc(sizeof(QueuedRequestInfoRec));
2089a3bd7f05Smrg        queueInfo->count = 0;
2090fdf6a26fSmrg        queueInfo->selections = XtMallocArray(2, (Cardinal) sizeof(Atom));
2091a3bd7f05Smrg        queueInfo->selections[0] = None;
2092a3bd7f05Smrg        queueInfo->requests = (QueuedRequest *)
2093a3bd7f05Smrg            __XtMalloc(sizeof(QueuedRequest));
2094a3bd7f05Smrg    }
2095a3bd7f05Smrg
2096a3bd7f05Smrg    /* Append this selection to list */
2097a3bd7f05Smrg    n = 0;
2098a3bd7f05Smrg    while (queueInfo->selections[n] != None)
2099a3bd7f05Smrg        n++;
2100fdf6a26fSmrg    queueInfo->selections = XtReallocArray(queueInfo->selections, (n + 2),
2101fdf6a26fSmrg                                           (Cardinal) sizeof(Atom));
2102a3bd7f05Smrg    queueInfo->selections[n] = selection;
2103a3bd7f05Smrg    queueInfo->selections[n + 1] = None;
2104a3bd7f05Smrg
2105a3bd7f05Smrg    (void) XSaveContext(dpy, window, multipleContext, (char *) queueInfo);
2106a3bd7f05Smrg    UNLOCK_PROCESS;
2107444c061aSmrg}
2108444c061aSmrg
2109a3bd7f05Smrgvoid
2110a3bd7f05SmrgXtSendSelectionRequest(Widget widget, Atom selection, Time time)
2111444c061aSmrg{
2112a3bd7f05Smrg    QueuedRequestInfo queueInfo;
2113a3bd7f05Smrg    Window window = XtWindow(widget);
2114a3bd7f05Smrg    Display *dpy = XtDisplay(widget);
2115444c061aSmrg
2116a3bd7f05Smrg    LOCK_PROCESS;
2117a3bd7f05Smrg    if (multipleContext == 0)
2118a3bd7f05Smrg        multipleContext = XUniqueContext();
2119a3bd7f05Smrg
2120a3bd7f05Smrg    queueInfo = NULL;
2121a3bd7f05Smrg    (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo);
2122a3bd7f05Smrg    if (queueInfo != NULL) {
2123a3bd7f05Smrg        int i;
2124a3bd7f05Smrg        int count = 0;
2125a3bd7f05Smrg        QueuedRequest *req = queueInfo->requests;
2126a3bd7f05Smrg
2127a3bd7f05Smrg        /* Construct the requests and send it using
2128a3bd7f05Smrg           GetSelectionValues */
2129a3bd7f05Smrg        for (i = 0; i < queueInfo->count; i++)
2130a3bd7f05Smrg            if (req[i]->selection == selection)
2131a3bd7f05Smrg                count++;
2132a3bd7f05Smrg
2133a3bd7f05Smrg        if (count > 0) {
2134a3bd7f05Smrg            if (count == 1) {
2135a3bd7f05Smrg                for (i = 0; i < queueInfo->count; i++)
2136a3bd7f05Smrg                    if (req[i]->selection == selection)
2137a3bd7f05Smrg                        break;
2138a3bd7f05Smrg
2139a3bd7f05Smrg                /* special case a multiple which isn't needed */
2140a3bd7f05Smrg                GetSelectionValue(widget, selection, req[i]->target,
2141a3bd7f05Smrg                                  req[i]->callback, req[i]->closure, time,
2142a3bd7f05Smrg                                  req[i]->incremental, req[i]->param);
2143a3bd7f05Smrg            }
2144a3bd7f05Smrg            else {
2145a3bd7f05Smrg                Atom *targets;
2146a3bd7f05Smrg                Atom t[PREALLOCED];
2147a3bd7f05Smrg                XtSelectionCallbackProc *cbs;
2148a3bd7f05Smrg                XtSelectionCallbackProc c[PREALLOCED];
2149a3bd7f05Smrg                XtPointer *closures;
2150a3bd7f05Smrg                XtPointer cs[PREALLOCED];
2151a3bd7f05Smrg                Boolean *incrs;
2152a3bd7f05Smrg                Boolean ins[PREALLOCED];
2153a3bd7f05Smrg                Atom *props;
2154a3bd7f05Smrg                Atom p[PREALLOCED];
2155a3bd7f05Smrg                int j = 0;
2156a3bd7f05Smrg
2157a3bd7f05Smrg                /* Allocate */
2158a3bd7f05Smrg                targets =
2159a3bd7f05Smrg                    (Atom *) XtStackAlloc((size_t) count * sizeof(Atom), t);
2160a3bd7f05Smrg                cbs = (XtSelectionCallbackProc *)
2161a3bd7f05Smrg                    XtStackAlloc((size_t) count *
2162a3bd7f05Smrg                                 sizeof(XtSelectionCallbackProc), c);
2163a3bd7f05Smrg                closures =
2164a3bd7f05Smrg                    (XtPointer *) XtStackAlloc((size_t) count *
2165a3bd7f05Smrg                                               sizeof(XtPointer), cs);
2166a3bd7f05Smrg                incrs =
2167a3bd7f05Smrg                    (Boolean *) XtStackAlloc((size_t) count * sizeof(Boolean),
2168a3bd7f05Smrg                                             ins);
2169a3bd7f05Smrg                props = (Atom *) XtStackAlloc((size_t) count * sizeof(Atom), p);
2170a3bd7f05Smrg
2171a3bd7f05Smrg                /* Copy */
2172a3bd7f05Smrg                for (i = 0; i < queueInfo->count; i++) {
2173a3bd7f05Smrg                    if (req[i]->selection == selection) {
2174a3bd7f05Smrg                        targets[j] = req[i]->target;
2175a3bd7f05Smrg                        cbs[j] = req[i]->callback;
2176a3bd7f05Smrg                        closures[j] = req[i]->closure;
2177a3bd7f05Smrg                        incrs[j] = req[i]->incremental;
2178a3bd7f05Smrg                        props[j] = req[i]->param;
2179a3bd7f05Smrg                        j++;
2180a3bd7f05Smrg                    }
2181a3bd7f05Smrg                }
2182a3bd7f05Smrg
2183a3bd7f05Smrg                /* Make the request */
2184a3bd7f05Smrg                GetSelectionValues(widget, selection, targets, count,
2185a3bd7f05Smrg                                   cbs, count, closures, time, incrs, props);
2186a3bd7f05Smrg
2187a3bd7f05Smrg                /* Free */
2188a3bd7f05Smrg                XtStackFree((XtPointer) targets, t);
2189a3bd7f05Smrg                XtStackFree((XtPointer) cbs, c);
2190a3bd7f05Smrg                XtStackFree((XtPointer) closures, cs);
2191a3bd7f05Smrg                XtStackFree((XtPointer) incrs, ins);
2192a3bd7f05Smrg                XtStackFree((XtPointer) props, p);
2193a3bd7f05Smrg            }
2194a3bd7f05Smrg        }
2195a3bd7f05Smrg    }
2196444c061aSmrg
2197a3bd7f05Smrg    CleanupRequest(dpy, queueInfo, selection);
2198a3bd7f05Smrg    UNLOCK_PROCESS;
2199444c061aSmrg}
2200444c061aSmrg
2201a3bd7f05Smrgvoid
2202a3bd7f05SmrgXtCancelSelectionRequest(Widget widget, Atom selection)
2203444c061aSmrg{
2204a3bd7f05Smrg    QueuedRequestInfo queueInfo;
2205a3bd7f05Smrg    Window window = XtWindow(widget);
2206a3bd7f05Smrg    Display *dpy = XtDisplay(widget);
2207444c061aSmrg
2208a3bd7f05Smrg    LOCK_PROCESS;
2209a3bd7f05Smrg    if (multipleContext == 0)
2210a3bd7f05Smrg        multipleContext = XUniqueContext();
2211a3bd7f05Smrg
2212a3bd7f05Smrg    queueInfo = NULL;
2213a3bd7f05Smrg    (void) XFindContext(dpy, window, multipleContext, (XPointer *) &queueInfo);
2214a3bd7f05Smrg    /* If there is one,  then cancel it */
2215a3bd7f05Smrg    if (queueInfo != NULL)
2216a3bd7f05Smrg        CleanupRequest(dpy, queueInfo, selection);
2217a3bd7f05Smrg    UNLOCK_PROCESS;
2218444c061aSmrg}
2219444c061aSmrg
2220444c061aSmrg/* Parameter utilities */
2221444c061aSmrg
2222444c061aSmrg/* Parameters on a selection request */
2223444c061aSmrg/* Places data on allocated parameter atom,  then records the
2224444c061aSmrg   parameter atom data for use in the next call to one of
2225444c061aSmrg   the XtGetSelectionValue functions. */
2226a3bd7f05Smrgvoid
2227a3bd7f05SmrgXtSetSelectionParameters(Widget requestor,
2228a3bd7f05Smrg                         Atom selection,
2229a3bd7f05Smrg                         Atom type,
2230a3bd7f05Smrg                         XtPointer value,
2231a3bd7f05Smrg                         unsigned long length,
2232a3bd7f05Smrg                         int format)
2233444c061aSmrg{
2234a3bd7f05Smrg    Display *dpy = XtDisplay(requestor);
2235a3bd7f05Smrg    Window window = XtWindow(requestor);
2236a3bd7f05Smrg    Atom property = GetParamInfo(requestor, selection);
2237a3bd7f05Smrg
2238a3bd7f05Smrg    if (property == None) {
2239a3bd7f05Smrg        property = GetSelectionProperty(dpy);
2240a3bd7f05Smrg        AddParamInfo(requestor, selection, property);
2241a3bd7f05Smrg    }
2242a3bd7f05Smrg
2243a3bd7f05Smrg    XChangeProperty(dpy, window, property,
2244a3bd7f05Smrg                    type, format, PropModeReplace,
2245a3bd7f05Smrg                    (unsigned char *) value, (int) length);
2246444c061aSmrg}
2247444c061aSmrg
2248444c061aSmrg/* Retrieves data passed in a parameter. Data for this is stored
2249444c061aSmrg   on the originator's window */
2250a3bd7f05Smrgvoid
2251a3bd7f05SmrgXtGetSelectionParameters(Widget owner,
2252a3bd7f05Smrg                         Atom selection,
2253a3bd7f05Smrg                         XtRequestId request_id,
2254a3bd7f05Smrg                         Atom *type_return,
2255a3bd7f05Smrg                         XtPointer *value_return,
2256a3bd7f05Smrg                         unsigned long *length_return,
2257a3bd7f05Smrg                         int *format_return)
2258444c061aSmrg{
2259444c061aSmrg    Request req;
2260444c061aSmrg    Display *dpy = XtDisplay(owner);
2261a3bd7f05Smrg
2262444c061aSmrg    WIDGET_TO_APPCON(owner);
2263444c061aSmrg
2264444c061aSmrg    *value_return = NULL;
22650568f49bSmrg    *length_return = (unsigned long) (*format_return = 0);
2266444c061aSmrg    *type_return = None;
2267444c061aSmrg
2268444c061aSmrg    LOCK_APP(app);
2269444c061aSmrg
2270444c061aSmrg    req = GetRequestRecord(owner, selection, request_id);
2271444c061aSmrg
2272444c061aSmrg    if (req && req->property) {
2273a3bd7f05Smrg        unsigned long bytes_after;      /* unused */
2274a3bd7f05Smrg
2275a3bd7f05Smrg        StartProtectedSection(dpy, req->requestor);
2276a3bd7f05Smrg        XGetWindowProperty(dpy, req->requestor, req->property, 0L, 10000000,
2277a3bd7f05Smrg                           False, AnyPropertyType, type_return, format_return,
2278a3bd7f05Smrg                           length_return, &bytes_after,
2279a3bd7f05Smrg                           (unsigned char **) value_return);
2280a3bd7f05Smrg        EndProtectedSection(dpy);
2281444c061aSmrg#ifdef XT_COPY_SELECTION
2282a3bd7f05Smrg        if (*value_return) {
2283a3bd7f05Smrg            int size = (int) BYTELENGTH(*length_return, *format_return) + 1;
2284a3bd7f05Smrg            char *tmp = __XtMalloc((Cardinal) size);
2285a3bd7f05Smrg
2286fdf6a26fSmrg            (void) memcpy(tmp, *value_return, (size_t) size);
2287a3bd7f05Smrg            XFree(*value_return);
2288a3bd7f05Smrg            *value_return = tmp;
2289a3bd7f05Smrg        }
2290444c061aSmrg#endif
2291444c061aSmrg    }
2292444c061aSmrg    UNLOCK_APP(app);
2293444c061aSmrg}
2294444c061aSmrg
2295444c061aSmrg/*  Parameters are temporarily stashed in an XContext.  A list is used because
2296444c061aSmrg *  there may be more than one selection request in progress.  The context
2297444c061aSmrg *  data is deleted when the list is empty.  In the future, the parameter
2298444c061aSmrg *  context could be merged with other contexts used during selections.
2299444c061aSmrg */
2300444c061aSmrg
2301a3bd7f05Smrgstatic void
2302a3bd7f05SmrgAddParamInfo(Widget w, Atom selection, Atom param_atom)
2303444c061aSmrg{
2304444c061aSmrg    Param p;
2305444c061aSmrg    ParamInfo pinfo;
2306444c061aSmrg
2307444c061aSmrg    LOCK_PROCESS;
2308444c061aSmrg    if (paramPropertyContext == 0)
2309a3bd7f05Smrg        paramPropertyContext = XUniqueContext();
2310444c061aSmrg
2311444c061aSmrg    if (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
2312a3bd7f05Smrg                     (XPointer *) &pinfo)) {
2313a3bd7f05Smrg        pinfo = (ParamInfo) __XtMalloc(sizeof(ParamInfoRec));
2314a3bd7f05Smrg        pinfo->count = 1;
2315a3bd7f05Smrg        pinfo->paramlist = XtNew(ParamRec);
2316a3bd7f05Smrg        p = pinfo->paramlist;
2317a3bd7f05Smrg        (void) XSaveContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
2318a3bd7f05Smrg                            (char *) pinfo);
2319444c061aSmrg    }
2320444c061aSmrg    else {
2321a3bd7f05Smrg        int n;
2322a3bd7f05Smrg
2323a3bd7f05Smrg        for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++) {
2324a3bd7f05Smrg            if (p->selection == None || p->selection == selection)
2325a3bd7f05Smrg                break;
2326a3bd7f05Smrg        }
2327a3bd7f05Smrg        if (n == 0) {
2328a3bd7f05Smrg            pinfo->count++;
2329fdf6a26fSmrg            pinfo->paramlist = XtReallocArray(pinfo->paramlist, pinfo->count,
2330fdf6a26fSmrg                                              (Cardinal) sizeof(ParamRec));
2331a3bd7f05Smrg            p = &pinfo->paramlist[pinfo->count - 1];
2332a3bd7f05Smrg            (void) XSaveContext(XtDisplay(w), XtWindow(w),
2333a3bd7f05Smrg                                paramPropertyContext, (char *) pinfo);
2334a3bd7f05Smrg        }
2335444c061aSmrg    }
2336444c061aSmrg    p->selection = selection;
2337444c061aSmrg    p->param = param_atom;
2338444c061aSmrg    UNLOCK_PROCESS;
2339444c061aSmrg}
2340444c061aSmrg
2341a3bd7f05Smrgstatic void
2342a3bd7f05SmrgRemoveParamInfo(Widget w, Atom selection)
2343444c061aSmrg{
2344444c061aSmrg    ParamInfo pinfo;
2345444c061aSmrg    Boolean retain = False;
2346444c061aSmrg
2347444c061aSmrg    LOCK_PROCESS;
2348444c061aSmrg    if (paramPropertyContext
2349a3bd7f05Smrg        && (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
2350a3bd7f05Smrg                         (XPointer *) &pinfo) == 0)) {
2351a3bd7f05Smrg        Param p;
2352a3bd7f05Smrg        int n;
2353a3bd7f05Smrg
2354a3bd7f05Smrg        /* Find and invalidate the parameter data. */
2355a3bd7f05Smrg        for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++) {
2356a3bd7f05Smrg            if (p->selection != None) {
2357a3bd7f05Smrg                if (p->selection == selection)
2358a3bd7f05Smrg                    p->selection = None;
2359a3bd7f05Smrg                else
2360a3bd7f05Smrg                    retain = True;
2361a3bd7f05Smrg            }
2362a3bd7f05Smrg        }
2363a3bd7f05Smrg        /* If there's no valid data remaining, release the context entry. */
2364a3bd7f05Smrg        if (!retain) {
2365a3bd7f05Smrg            XtFree((char *) pinfo->paramlist);
2366a3bd7f05Smrg            XtFree((char *) pinfo);
2367a3bd7f05Smrg            XDeleteContext(XtDisplay(w), XtWindow(w), paramPropertyContext);
2368a3bd7f05Smrg        }
2369444c061aSmrg    }
2370444c061aSmrg    UNLOCK_PROCESS;
2371444c061aSmrg}
2372444c061aSmrg
2373a3bd7f05Smrgstatic Atom
2374a3bd7f05SmrgGetParamInfo(Widget w, Atom selection)
2375444c061aSmrg{
2376444c061aSmrg    ParamInfo pinfo;
2377444c061aSmrg    Atom atom = None;
2378444c061aSmrg
2379444c061aSmrg    LOCK_PROCESS;
2380444c061aSmrg    if (paramPropertyContext
2381a3bd7f05Smrg        && (XFindContext(XtDisplay(w), XtWindow(w), paramPropertyContext,
2382a3bd7f05Smrg                         (XPointer *) &pinfo) == 0)) {
2383a3bd7f05Smrg        Param p;
2384a3bd7f05Smrg        int n;
2385a3bd7f05Smrg
2386a3bd7f05Smrg        for (n = (int) pinfo->count, p = pinfo->paramlist; n; n--, p++)
2387a3bd7f05Smrg            if (p->selection == selection) {
2388a3bd7f05Smrg                atom = p->param;
2389a3bd7f05Smrg                break;
2390a3bd7f05Smrg            }
2391444c061aSmrg    }
2392444c061aSmrg    UNLOCK_PROCESS;
2393444c061aSmrg    return atom;
2394444c061aSmrg}
2395