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