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