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