1/*
2 * tkSelect.c --
3 *
4 *	This file manages the selection for the Tk toolkit,
5 *	translating between the standard X ICCCM conventions
6 *	and Tcl commands.
7 *
8 * Copyright (c) 1990-1993 The Regents of the University of California.
9 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: tkSelect.c,v 1.13.2.1 2005/11/22 11:32:37 dkf Exp $
15 */
16
17#include "tkInt.h"
18#include "tkSelect.h"
19
20/*
21 * When a selection handler is set up by invoking "selection handle",
22 * one of the following data structures is set up to hold information
23 * about the command to invoke and its interpreter.
24 */
25
26typedef struct {
27    Tcl_Interp *interp;		/* Interpreter in which to invoke command. */
28    int cmdLength;		/* # of non-NULL bytes in command. */
29    int charOffset;		/* The offset of the next char to retrieve. */
30    int byteOffset;		/* The expected byte offset of the next
31				 * chunk. */
32    char buffer[TCL_UTF_MAX];	/* A buffer to hold part of a UTF character
33				 * that is split across chunks.*/
34    char command[4];		/* Command to invoke.  Actual space is
35				 * allocated as large as necessary.  This
36				 * must be the last entry in the structure. */
37} CommandInfo;
38
39/*
40 * When selection ownership is claimed with the "selection own" Tcl command,
41 * one of the following structures is created to record the Tcl command
42 * to be executed when the selection is lost again.
43 */
44
45typedef struct LostCommand {
46    Tcl_Interp *interp;		/* Interpreter in which to invoke command. */
47    char command[4];		/* Command to invoke.  Actual space is
48				 * allocated as large as necessary.  This
49				 * must be the last entry in the structure. */
50} LostCommand;
51
52/*
53 * The structure below is used to keep each thread's pending list
54 * separate.
55 */
56
57typedef struct ThreadSpecificData {
58    TkSelInProgress *pendingPtr;
59				/* Topmost search in progress, or
60				 * NULL if none. */
61} ThreadSpecificData;
62static Tcl_ThreadDataKey dataKey;
63
64/*
65 * Forward declarations for procedures defined in this file:
66 */
67
68static int		HandleTclCommand _ANSI_ARGS_((ClientData clientData,
69			    int offset, char *buffer, int maxBytes));
70static void		LostSelection _ANSI_ARGS_((ClientData clientData));
71static int		SelGetProc _ANSI_ARGS_((ClientData clientData,
72			    Tcl_Interp *interp, char *portion));
73
74/*
75 *--------------------------------------------------------------
76 *
77 * Tk_CreateSelHandler --
78 *
79 *	This procedure is called to register a procedure
80 *	as the handler for selection requests of a particular
81 *	target type on a particular window for a particular
82 *	selection.
83 *
84 * Results:
85 *	None.
86 *
87 * Side effects:
88 *	In the future, whenever the selection is in tkwin's
89 *	window and someone requests the selection in the
90 *	form given by target, proc will be invoked to provide
91 *	part or all of the selection in the given form.  If
92 *	there was already a handler declared for the given
93 *	window, target and selection type, then it is replaced.
94 *	Proc should have the following form:
95 *
96 *	int
97 *	proc(clientData, offset, buffer, maxBytes)
98 *	    ClientData clientData;
99 *	    int offset;
100 *	    char *buffer;
101 *	    int maxBytes;
102 *	{
103 *	}
104 *
105 *	The clientData argument to proc will be the same as
106 *	the clientData argument to this procedure.  The offset
107 *	argument indicates which portion of the selection to
108 *	return:  skip the first offset bytes.  Buffer is a
109 *	pointer to an area in which to place the converted
110 *	selection, and maxBytes gives the number of bytes
111 *	available at buffer.  Proc should place the selection
112 *	in buffer as a string, and return a count of the number
113 *	of bytes of selection actually placed in buffer (not
114 *	including the terminating NULL character).  If the
115 *	return value equals maxBytes, this is a sign that there
116 *	is probably still more selection information available.
117 *
118 *--------------------------------------------------------------
119 */
120
121void
122Tk_CreateSelHandler(tkwin, selection, target, proc, clientData, format)
123    Tk_Window tkwin;		/* Token for window. */
124    Atom selection;		/* Selection to be handled. */
125    Atom target;		/* The kind of selection conversions
126				 * that can be handled by proc,
127				 * e.g. TARGETS or STRING. */
128    Tk_SelectionProc *proc;	/* Procedure to invoke to convert
129				 * selection to type "target". */
130    ClientData clientData;	/* Value to pass to proc. */
131    Atom format;		/* Format in which the selection
132				 * information should be returned to
133				 * the requestor. XA_STRING is best by
134				 * far, but anything listed in the ICCCM
135				 * will be tolerated (blech). */
136{
137    register TkSelHandler *selPtr;
138    TkWindow *winPtr = (TkWindow *) tkwin;
139
140    if (winPtr->dispPtr->multipleAtom == None) {
141	TkSelInit(tkwin);
142    }
143
144    /*
145     * See if there's already a handler for this target and selection on
146     * this window.  If so, re-use it.  If not, create a new one.
147     */
148
149    for (selPtr = winPtr->selHandlerList; ; selPtr = selPtr->nextPtr) {
150	if (selPtr == NULL) {
151	    selPtr = (TkSelHandler *) ckalloc(sizeof(TkSelHandler));
152	    selPtr->nextPtr = winPtr->selHandlerList;
153	    winPtr->selHandlerList = selPtr;
154	    break;
155	}
156	if ((selPtr->selection == selection) && (selPtr->target == target)) {
157
158	    /*
159	     * Special case:  when replacing handler created by
160	     * "selection handle", free up memory.  Should there be a
161	     * callback to allow other clients to do this too?
162	     */
163
164	    if (selPtr->proc == HandleTclCommand) {
165		ckfree((char *) selPtr->clientData);
166	    }
167	    break;
168	}
169    }
170    selPtr->selection = selection;
171    selPtr->target = target;
172    selPtr->format = format;
173    selPtr->proc = proc;
174    selPtr->clientData = clientData;
175    if (format == XA_STRING) {
176	selPtr->size = 8;
177    } else {
178	selPtr->size = 32;
179    }
180
181    if ((target == XA_STRING) && (winPtr->dispPtr->utf8Atom != (Atom) NULL)) {
182	/*
183	 * If the user asked for a STRING handler and we understand
184	 * UTF8_STRING, we implicitly create a UTF8_STRING handler for them.
185	 */
186
187	target = winPtr->dispPtr->utf8Atom;
188	for (selPtr = winPtr->selHandlerList; ;
189	     selPtr = selPtr->nextPtr) {
190	    if (selPtr == NULL) {
191		selPtr = (TkSelHandler *) ckalloc(sizeof(TkSelHandler));
192		selPtr->nextPtr = winPtr->selHandlerList;
193		winPtr->selHandlerList = selPtr;
194		selPtr->selection = selection;
195		selPtr->target = target;
196		selPtr->format = target; /* We want UTF8_STRING format */
197		selPtr->proc = proc;
198		if (selPtr->proc == HandleTclCommand) {
199		    /*
200		     * The clientData is selection controlled memory, so
201		     * we should make a copy for this selPtr.
202		     */
203		    unsigned cmdInfoLen = sizeof(CommandInfo) +
204			    ((CommandInfo*)clientData)->cmdLength - 3;
205		    selPtr->clientData = (ClientData)ckalloc(cmdInfoLen);
206		    memcpy(selPtr->clientData, clientData, cmdInfoLen);
207		} else {
208		    selPtr->clientData = clientData;
209		}
210		selPtr->size = 8;
211		break;
212	    }
213	    if ((selPtr->selection == selection)
214		    && (selPtr->target == target)) {
215		/*
216		 * Looks like we had a utf-8 target already.  Leave it alone.
217		 */
218
219		break;
220	    }
221	}
222    }
223}
224
225/*
226 *----------------------------------------------------------------------
227 *
228 * Tk_DeleteSelHandler --
229 *
230 *	Remove the selection handler for a given window, target, and
231 *	selection, if it exists.
232 *
233 * Results:
234 *	None.
235 *
236 * Side effects:
237 *	The selection handler for tkwin and target is removed.  If there
238 *	is no such handler then nothing happens.
239 *
240 *----------------------------------------------------------------------
241 */
242
243void
244Tk_DeleteSelHandler(tkwin, selection, target)
245    Tk_Window tkwin;			/* Token for window. */
246    Atom selection;			/* The selection whose handler
247					 * is to be removed. */
248    Atom target;			/* The target whose selection
249					 * handler is to be removed. */
250{
251    TkWindow *winPtr = (TkWindow *) tkwin;
252    register TkSelHandler *selPtr, *prevPtr;
253    register TkSelInProgress *ipPtr;
254    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
255            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
256
257    /*
258     * Find the selection handler to be deleted, or return if it doesn't
259     * exist.
260     */
261
262    for (selPtr = winPtr->selHandlerList, prevPtr = NULL; ;
263	    prevPtr = selPtr, selPtr = selPtr->nextPtr) {
264	if (selPtr == NULL) {
265	    return;
266	}
267	if ((selPtr->selection == selection) && (selPtr->target == target)) {
268	    break;
269	}
270    }
271
272    /*
273     * If ConvertSelection is processing this handler, tell it that the
274     * handler is dead.
275     */
276
277    for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
278            ipPtr = ipPtr->nextPtr) {
279	if (ipPtr->selPtr == selPtr) {
280	    ipPtr->selPtr = NULL;
281	}
282    }
283
284    /*
285     * Free resources associated with the handler.
286     */
287
288    if (prevPtr == NULL) {
289	winPtr->selHandlerList = selPtr->nextPtr;
290    } else {
291	prevPtr->nextPtr = selPtr->nextPtr;
292    }
293
294    if ((target == XA_STRING) && (winPtr->dispPtr->utf8Atom != (Atom) NULL)) {
295	/*
296	 * If the user asked for a STRING handler and we understand
297	 * UTF8_STRING, we may have implicitly created a UTF8_STRING handler
298	 * for them.  Look for it and delete it as necessary.
299	 */
300	TkSelHandler *utf8selPtr;
301
302	target = winPtr->dispPtr->utf8Atom;
303	for (utf8selPtr = winPtr->selHandlerList; utf8selPtr != NULL;
304	     utf8selPtr = utf8selPtr->nextPtr) {
305	    if ((utf8selPtr->selection == selection)
306		    && (utf8selPtr->target == target)) {
307		break;
308	    }
309	}
310	if (utf8selPtr != NULL) {
311	    if ((utf8selPtr->format == target)
312		    && (utf8selPtr->proc == selPtr->proc)
313		    && (utf8selPtr->size == selPtr->size)) {
314		/*
315		 * This recursive call is OK, because we've
316		 * changed the value of 'target'
317		 */
318		Tk_DeleteSelHandler(tkwin, selection, target);
319	    }
320	}
321    }
322
323    if (selPtr->proc == HandleTclCommand) {
324	/*
325	 * Mark the CommandInfo as deleted and free it if we can.
326	 */
327
328	((CommandInfo*)selPtr->clientData)->interp = NULL;
329	Tcl_EventuallyFree(selPtr->clientData, TCL_DYNAMIC);
330    }
331    ckfree((char *) selPtr);
332}
333
334/*
335 *--------------------------------------------------------------
336 *
337 * Tk_OwnSelection --
338 *
339 *	Arrange for tkwin to become the owner of a selection.
340 *
341 * Results:
342 *	None.
343 *
344 * Side effects:
345 *	From now on, requests for the selection will be directed
346 *	to procedures associated with tkwin (they must have been
347 *	declared with calls to Tk_CreateSelHandler).  When the
348 *	selection is lost by this window, proc will be invoked
349 *	(see the manual entry for details).  This procedure may
350 *	invoke callbacks, including Tcl scripts, so any calling
351 *	function should be reentrant at the point where
352 *	Tk_OwnSelection is invoked.
353 *
354 *--------------------------------------------------------------
355 */
356
357void
358Tk_OwnSelection(tkwin, selection, proc, clientData)
359    Tk_Window tkwin;		/* Window to become new selection
360				 * owner. */
361    Atom selection;		/* Selection that window should own. */
362    Tk_LostSelProc *proc;	/* Procedure to call when selection
363				 * is taken away from tkwin. */
364    ClientData clientData;	/* Arbitrary one-word argument to
365				 * pass to proc. */
366{
367    register TkWindow *winPtr = (TkWindow *) tkwin;
368    TkDisplay *dispPtr = winPtr->dispPtr;
369    TkSelectionInfo *infoPtr;
370    Tk_LostSelProc *clearProc = NULL;
371    ClientData clearData = NULL;	/* Initialization needed only to
372					 * prevent compiler warning. */
373
374
375    if (dispPtr->multipleAtom == None) {
376	TkSelInit(tkwin);
377    }
378    Tk_MakeWindowExist(tkwin);
379
380    /*
381     * This code is somewhat tricky.  First, we find the specified selection
382     * on the selection list.  If the previous owner is in this process, and
383     * is a different window, then we need to invoke the clearProc.  However,
384     * it's dangerous to call the clearProc right now, because it could
385     * invoke a Tcl script that wrecks the current state (e.g. it could
386     * delete the window).  To be safe, defer the call until the end of the
387     * procedure when we no longer care about the state.
388     */
389
390    for (infoPtr = dispPtr->selectionInfoPtr; infoPtr != NULL;
391	    infoPtr = infoPtr->nextPtr) {
392	if (infoPtr->selection == selection) {
393	    break;
394	}
395    }
396    if (infoPtr == NULL) {
397	infoPtr = (TkSelectionInfo*) ckalloc(sizeof(TkSelectionInfo));
398	infoPtr->selection = selection;
399	infoPtr->nextPtr = dispPtr->selectionInfoPtr;
400	dispPtr->selectionInfoPtr = infoPtr;
401    } else if (infoPtr->clearProc != NULL) {
402	if (infoPtr->owner != tkwin) {
403	    clearProc = infoPtr->clearProc;
404	    clearData = infoPtr->clearData;
405	} else if (infoPtr->clearProc == LostSelection) {
406	    /*
407	     * If the selection handler is one created by "selection own",
408	     * be sure to free the record for it;  otherwise there will be
409	     * a memory leak.
410	     */
411
412	    ckfree((char *) infoPtr->clearData);
413	}
414    }
415
416    infoPtr->owner = tkwin;
417    infoPtr->serial = NextRequest(winPtr->display);
418    infoPtr->clearProc = proc;
419    infoPtr->clearData = clientData;
420
421    /*
422     * Note that we are using CurrentTime, even though ICCCM recommends against
423     * this practice (the problem is that we don't necessarily have a valid
424     * time to use).  We will not be able to retrieve a useful timestamp for
425     * the TIMESTAMP target later.
426     */
427
428    infoPtr->time = CurrentTime;
429
430    /*
431     * Note that we are not checking to see if the selection claim succeeded.
432     * If the ownership does not change, then the clearProc may never be
433     * invoked, and we will return incorrect information when queried for the
434     * current selection owner.
435     */
436
437    XSetSelectionOwner(winPtr->display, infoPtr->selection, winPtr->window,
438	    infoPtr->time);
439
440    /*
441     * Now that we are done, we can invoke clearProc without running into
442     * reentrancy problems.
443     */
444
445    if (clearProc != NULL) {
446	(*clearProc)(clearData);
447    }
448}
449
450/*
451 *----------------------------------------------------------------------
452 *
453 * Tk_ClearSelection --
454 *
455 *	Eliminate the specified selection on tkwin's display, if there is one.
456 *
457 * Results:
458 *	None.
459 *
460 * Side effects:
461 *	The specified selection is cleared, so that future requests to retrieve
462 *	it will fail until some application owns it again.  This procedure
463 *	invokes callbacks, possibly including Tcl scripts, so any calling
464 *	function should be reentrant at the point Tk_ClearSelection is invoked.
465 *
466 *----------------------------------------------------------------------
467 */
468
469void
470Tk_ClearSelection(tkwin, selection)
471    Tk_Window tkwin;		/* Window that selects a display. */
472    Atom selection;		/* Selection to be cancelled. */
473{
474    register TkWindow *winPtr = (TkWindow *) tkwin;
475    TkDisplay *dispPtr = winPtr->dispPtr;
476    TkSelectionInfo *infoPtr;
477    TkSelectionInfo *prevPtr;
478    TkSelectionInfo *nextPtr;
479    Tk_LostSelProc *clearProc = NULL;
480    ClientData clearData = NULL;	/* Initialization needed only to
481					 * prevent compiler warning. */
482
483    if (dispPtr->multipleAtom == None) {
484	TkSelInit(tkwin);
485    }
486
487    for (infoPtr = dispPtr->selectionInfoPtr, prevPtr = NULL;
488	     infoPtr != NULL; infoPtr = nextPtr) {
489	nextPtr = infoPtr->nextPtr;
490	if (infoPtr->selection == selection) {
491	    if (prevPtr == NULL) {
492		dispPtr->selectionInfoPtr = nextPtr;
493	    } else {
494		prevPtr->nextPtr = nextPtr;
495	    }
496	    break;
497	}
498	prevPtr = infoPtr;
499    }
500
501    if (infoPtr != NULL) {
502	clearProc = infoPtr->clearProc;
503	clearData = infoPtr->clearData;
504	ckfree((char *) infoPtr);
505    }
506    XSetSelectionOwner(winPtr->display, selection, None, CurrentTime);
507
508    if (clearProc != NULL) {
509	(*clearProc)(clearData);
510    }
511}
512
513/*
514 *--------------------------------------------------------------
515 *
516 * Tk_GetSelection --
517 *
518 *	Retrieve the value of a selection and pass it off (in
519 *	pieces, possibly) to a given procedure.
520 *
521 * Results:
522 *	The return value is a standard Tcl return value.
523 *	If an error occurs (such as no selection exists)
524 *	then an error message is left in the interp's result.
525 *
526 * Side effects:
527 *	The standard X11 protocols are used to retrieve the
528 *	selection.  When it arrives, it is passed to proc.  If
529 *	the selection is very large, it will be passed to proc
530 *	in several pieces.  Proc should have the following
531 *	structure:
532 *
533 *	int
534 *	proc(clientData, interp, portion)
535 *	    ClientData clientData;
536 *	    Tcl_Interp *interp;
537 *	    char *portion;
538 *	{
539 *	}
540 *
541 *	The interp and clientData arguments to proc will be the
542 *	same as the corresponding arguments to Tk_GetSelection.
543 *	The portion argument points to a character string
544 *	containing part of the selection, and numBytes indicates
545 *	the length of the portion, not including the terminating
546 *	NULL character.  If the selection arrives in several pieces,
547 *	the "portion" arguments in separate calls will contain
548 *	successive parts of the selection.  Proc should normally
549 *	return TCL_OK.  If it detects an error then it should return
550 *	TCL_ERROR and leave an error message in the interp's result; the
551 *	remainder of the selection retrieval will be aborted.
552 *
553 *--------------------------------------------------------------
554 */
555
556int
557Tk_GetSelection(interp, tkwin, selection, target, proc, clientData)
558    Tcl_Interp *interp;		/* Interpreter to use for reporting
559				 * errors. */
560    Tk_Window tkwin;		/* Window on whose behalf to retrieve
561				 * the selection (determines display
562				 * from which to retrieve). */
563    Atom selection;		/* Selection to retrieve. */
564    Atom target;		/* Desired form in which selection
565				 * is to be returned. */
566    Tk_GetSelProc *proc;	/* Procedure to call to process the
567				 * selection, once it has been retrieved. */
568    ClientData clientData;	/* Arbitrary value to pass to proc. */
569{
570    TkWindow *winPtr = (TkWindow *) tkwin;
571    TkDisplay *dispPtr = winPtr->dispPtr;
572    TkSelectionInfo *infoPtr;
573    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
574            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
575
576    if (dispPtr->multipleAtom == None) {
577	TkSelInit(tkwin);
578    }
579
580    /*
581     * If the selection is owned by a window managed by this
582     * process, then call the retrieval procedure directly,
583     * rather than going through the X server (it's dangerous
584     * to go through the X server in this case because it could
585     * result in deadlock if an INCR-style selection results).
586     */
587
588    for (infoPtr = dispPtr->selectionInfoPtr; infoPtr != NULL;
589	    infoPtr = infoPtr->nextPtr) {
590	if (infoPtr->selection == selection)
591	    break;
592    }
593    if (infoPtr != NULL) {
594	register TkSelHandler *selPtr;
595	int offset, result, count;
596	char buffer[TK_SEL_BYTES_AT_ONCE+1];
597	TkSelInProgress ip;
598
599	for (selPtr = ((TkWindow *) infoPtr->owner)->selHandlerList;
600	     selPtr != NULL; selPtr = selPtr->nextPtr) {
601	    if  ((selPtr->target == target)
602		    && (selPtr->selection == selection)) {
603		break;
604	    }
605	}
606	if (selPtr == NULL) {
607	    Atom type;
608
609	    count = TkSelDefaultSelection(infoPtr, target, buffer,
610		    TK_SEL_BYTES_AT_ONCE, &type);
611	    if (count > TK_SEL_BYTES_AT_ONCE) {
612		panic("selection handler returned too many bytes");
613	    }
614	    if (count < 0) {
615		goto cantget;
616	    }
617	    buffer[count] = 0;
618	    result = (*proc)(clientData, interp, buffer);
619	} else {
620	    offset = 0;
621	    result = TCL_OK;
622	    ip.selPtr = selPtr;
623	    ip.nextPtr = tsdPtr->pendingPtr;
624	    tsdPtr->pendingPtr = &ip;
625	    while (1) {
626		count = (selPtr->proc)(selPtr->clientData, offset, buffer,
627			TK_SEL_BYTES_AT_ONCE);
628		if ((count < 0) || (ip.selPtr == NULL)) {
629		    tsdPtr->pendingPtr = ip.nextPtr;
630		    goto cantget;
631		}
632		if (count > TK_SEL_BYTES_AT_ONCE) {
633		    panic("selection handler returned too many bytes");
634		}
635		buffer[count] = '\0';
636		result = (*proc)(clientData, interp, buffer);
637		if ((result != TCL_OK) || (count < TK_SEL_BYTES_AT_ONCE)
638			|| (ip.selPtr == NULL)) {
639		    break;
640		}
641		offset += count;
642	    }
643	    tsdPtr->pendingPtr = ip.nextPtr;
644	}
645	return result;
646    }
647
648    /*
649     * The selection is owned by some other process.
650     */
651
652    return TkSelGetSelection(interp, tkwin, selection, target, proc,
653	    clientData);
654
655    cantget:
656    Tcl_AppendResult(interp, Tk_GetAtomName(tkwin, selection),
657	" selection doesn't exist or form \"", Tk_GetAtomName(tkwin, target),
658	"\" not defined", (char *) NULL);
659    return TCL_ERROR;
660}
661
662/*
663 *--------------------------------------------------------------
664 *
665 * Tk_SelectionObjCmd --
666 *
667 *	This procedure is invoked to process the "selection" Tcl
668 *	command.  See the user documentation for details on what
669 *	it does.
670 *
671 * Results:
672 *	A standard Tcl result.
673 *
674 * Side effects:
675 *	See the user documentation.
676 *
677 *--------------------------------------------------------------
678 */
679
680int
681Tk_SelectionObjCmd(clientData, interp, objc, objv)
682    ClientData clientData;	/* Main window associated with
683				 * interpreter. */
684    Tcl_Interp *interp;		/* Current interpreter. */
685    int objc;			/* Number of arguments. */
686    Tcl_Obj *CONST objv[];	/* Argument objects. */
687{
688    Tk_Window tkwin = (Tk_Window) clientData;
689    char *path = NULL;
690    Atom selection;
691    char *selName = NULL, *string;
692    int count, index;
693    Tcl_Obj **objs;
694    static CONST char *optionStrings[] = {
695	"clear", "get", "handle", "own", (char *) NULL
696    };
697    enum options { SELECTION_CLEAR, SELECTION_GET, SELECTION_HANDLE,
698		       SELECTION_OWN };
699
700    if (objc < 2) {
701	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
702	return TCL_ERROR;
703    }
704
705    if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
706	    &index) != TCL_OK) {
707	return TCL_ERROR;
708    }
709
710    switch ((enum options) index) {
711	case SELECTION_CLEAR: {
712	    static CONST char *clearOptionStrings[] = {
713		"-displayof", "-selection", (char *) NULL
714	    };
715	    enum clearOptions { CLEAR_DISPLAYOF, CLEAR_SELECTION };
716	    int clearIndex;
717
718	    for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
719		 count-=2, objs+=2) {
720		string = Tcl_GetString(objs[0]);
721		if (string[0] != '-') {
722		    break;
723		}
724		if (count < 2) {
725		    Tcl_AppendResult(interp, "value for \"", string,
726			    "\" missing", (char *) NULL);
727		    return TCL_ERROR;
728		}
729
730		if (Tcl_GetIndexFromObj(interp, objs[0], clearOptionStrings,
731			"option", 0, &clearIndex) != TCL_OK) {
732		    return TCL_ERROR;
733		}
734		switch ((enum clearOptions) clearIndex) {
735		    case CLEAR_DISPLAYOF:
736			path = Tcl_GetString(objs[1]);
737			break;
738		    case CLEAR_SELECTION:
739			selName = Tcl_GetString(objs[1]);
740			break;
741		}
742	    }
743	    if (count == 1) {
744		path = Tcl_GetString(objs[0]);
745	    } else if (count > 1) {
746		Tcl_WrongNumArgs(interp, 2, objv, "?options?");
747		return TCL_ERROR;
748	    }
749	    if (path != NULL) {
750		tkwin = Tk_NameToWindow(interp, path, tkwin);
751	    }
752	    if (tkwin == NULL) {
753		return TCL_ERROR;
754	    }
755	    if (selName != NULL) {
756		selection = Tk_InternAtom(tkwin, selName);
757	    } else {
758		selection = XA_PRIMARY;
759	    }
760
761	    Tk_ClearSelection(tkwin, selection);
762	    break;
763	}
764
765	case SELECTION_GET: {
766	    Atom target;
767	    char *targetName = NULL;
768	    Tcl_DString selBytes;
769	    int result;
770	    static CONST char *getOptionStrings[] = {
771		"-displayof", "-selection", "-type", (char *) NULL
772	    };
773	    enum getOptions { GET_DISPLAYOF, GET_SELECTION, GET_TYPE };
774	    int getIndex;
775
776	    for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count>0;
777		 count-=2, objs+=2) {
778		string = Tcl_GetString(objs[0]);
779		if (string[0] != '-') {
780		    break;
781		}
782		if (count < 2) {
783		    Tcl_AppendResult(interp, "value for \"", string,
784			    "\" missing", (char *) NULL);
785		    return TCL_ERROR;
786		}
787
788		if (Tcl_GetIndexFromObj(interp, objs[0], getOptionStrings,
789			"option", 0, &getIndex) != TCL_OK) {
790		    return TCL_ERROR;
791		}
792
793		switch ((enum getOptions) getIndex) {
794		    case GET_DISPLAYOF:
795			path = Tcl_GetString(objs[1]);
796			break;
797		    case GET_SELECTION:
798			selName = Tcl_GetString(objs[1]);
799			break;
800		    case GET_TYPE:
801			targetName = Tcl_GetString(objs[1]);
802			break;
803		}
804	    }
805	    if (path != NULL) {
806		tkwin = Tk_NameToWindow(interp, path, tkwin);
807	    }
808	    if (tkwin == NULL) {
809		return TCL_ERROR;
810	    }
811	    if (selName != NULL) {
812		selection = Tk_InternAtom(tkwin, selName);
813	    } else {
814		selection = XA_PRIMARY;
815	    }
816	    if (count > 1) {
817		Tcl_WrongNumArgs(interp, 2, objv, "?options?");
818		return TCL_ERROR;
819	    } else if (count == 1) {
820		target = Tk_InternAtom(tkwin, Tcl_GetString(objs[0]));
821	    } else if (targetName != NULL) {
822		target = Tk_InternAtom(tkwin, targetName);
823	    } else {
824		target = XA_STRING;
825	    }
826
827	    Tcl_DStringInit(&selBytes);
828	    result = Tk_GetSelection(interp, tkwin, selection, target,
829		    SelGetProc,	(ClientData) &selBytes);
830	    if (result == TCL_OK) {
831		Tcl_DStringResult(interp, &selBytes);
832	    } else {
833		Tcl_DStringFree(&selBytes);
834	    }
835	    return result;
836	}
837
838	case SELECTION_HANDLE: {
839	    Atom target, format;
840	    char *targetName = NULL;
841	    char *formatName = NULL;
842	    register CommandInfo *cmdInfoPtr;
843	    int cmdLength;
844	    static CONST char *handleOptionStrings[] = {
845		"-format", "-selection", "-type", (char *) NULL
846	    };
847	    enum handleOptions { HANDLE_FORMAT, HANDLE_SELECTION,
848				     HANDLE_TYPE };
849	    int handleIndex;
850
851	    for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
852		 count-=2, objs+=2) {
853		string = Tcl_GetString(objs[0]);
854		if (string[0] != '-') {
855		    break;
856		}
857		if (count < 2) {
858		    Tcl_AppendResult(interp, "value for \"", string,
859			    "\" missing", (char *) NULL);
860		    return TCL_ERROR;
861		}
862
863		if (Tcl_GetIndexFromObj(interp, objs[0],handleOptionStrings,
864			"option", 0, &handleIndex) != TCL_OK) {
865		    return TCL_ERROR;
866		}
867
868		switch ((enum handleOptions) handleIndex) {
869		    case HANDLE_FORMAT:
870			formatName = Tcl_GetString(objs[1]);
871			break;
872		    case HANDLE_SELECTION:
873			selName = Tcl_GetString(objs[1]);
874			break;
875		    case HANDLE_TYPE:
876			targetName = Tcl_GetString(objs[1]);
877			break;
878		}
879	    }
880
881	    if ((count < 2) || (count > 4)) {
882		Tcl_WrongNumArgs(interp, 2, objv, "?options? window command");
883		return TCL_ERROR;
884	    }
885	    tkwin = Tk_NameToWindow(interp, Tcl_GetString(objs[0]), tkwin);
886	    if (tkwin == NULL) {
887		return TCL_ERROR;
888	    }
889	    if (selName != NULL) {
890		selection = Tk_InternAtom(tkwin, selName);
891	    } else {
892		selection = XA_PRIMARY;
893	    }
894
895	    if (count > 2) {
896		target = Tk_InternAtom(tkwin, Tcl_GetString(objs[2]));
897	    } else if (targetName != NULL) {
898		target = Tk_InternAtom(tkwin, targetName);
899	    } else {
900		target = XA_STRING;
901	    }
902	    if (count > 3) {
903		format = Tk_InternAtom(tkwin, Tcl_GetString(objs[3]));
904	    } else if (formatName != NULL) {
905		format = Tk_InternAtom(tkwin, formatName);
906	    } else {
907		format = XA_STRING;
908	    }
909	    string = Tcl_GetStringFromObj(objs[1], &cmdLength);
910	    if (cmdLength == 0) {
911		Tk_DeleteSelHandler(tkwin, selection, target);
912	    } else {
913		cmdInfoPtr = (CommandInfo *) ckalloc((unsigned) (
914		    sizeof(CommandInfo) - 3 + cmdLength));
915		cmdInfoPtr->interp = interp;
916		cmdInfoPtr->charOffset = 0;
917		cmdInfoPtr->byteOffset = 0;
918		cmdInfoPtr->buffer[0] = '\0';
919		cmdInfoPtr->cmdLength = cmdLength;
920		strcpy(cmdInfoPtr->command, string);
921		Tk_CreateSelHandler(tkwin, selection, target, HandleTclCommand,
922			(ClientData) cmdInfoPtr, format);
923	    }
924	    return TCL_OK;
925	}
926
927	case SELECTION_OWN: {
928	    register LostCommand *lostPtr;
929	    char *script = NULL;
930	    int cmdLength;
931	    static CONST char *ownOptionStrings[] = {
932		"-command", "-displayof", "-selection", (char *) NULL
933	    };
934	    enum ownOptions { OWN_COMMAND, OWN_DISPLAYOF, OWN_SELECTION };
935	    int ownIndex;
936
937	    for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
938		 count-=2, objs+=2) {
939		string = Tcl_GetString(objs[0]);
940		if (string[0] != '-') {
941		    break;
942		}
943		if (count < 2) {
944		    Tcl_AppendResult(interp, "value for \"", string,
945			    "\" missing", (char *) NULL);
946		    return TCL_ERROR;
947		}
948
949		if (Tcl_GetIndexFromObj(interp, objs[0], ownOptionStrings,
950			"option", 0, &ownIndex) != TCL_OK) {
951		    return TCL_ERROR;
952		}
953
954		switch ((enum ownOptions) ownIndex) {
955		    case OWN_COMMAND:
956			script = Tcl_GetString(objs[1]);
957			break;
958		    case OWN_DISPLAYOF:
959			path = Tcl_GetString(objs[1]);
960			break;
961		    case OWN_SELECTION:
962			selName = Tcl_GetString(objs[1]);
963			break;
964		}
965	    }
966
967	    if (count > 2) {
968		Tcl_WrongNumArgs(interp, 2, objv, "?options? ?window?");
969		return TCL_ERROR;
970	    }
971	    if (selName != NULL) {
972		selection = Tk_InternAtom(tkwin, selName);
973	    } else {
974		selection = XA_PRIMARY;
975	    }
976	    if (count == 0) {
977		TkSelectionInfo *infoPtr;
978		TkWindow *winPtr;
979		if (path != NULL) {
980		    tkwin = Tk_NameToWindow(interp, path, tkwin);
981		}
982		if (tkwin == NULL) {
983		    return TCL_ERROR;
984		}
985		winPtr = (TkWindow *)tkwin;
986		for (infoPtr = winPtr->dispPtr->selectionInfoPtr;
987		     infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
988		    if (infoPtr->selection == selection)
989			break;
990		}
991
992		/*
993		 * Ignore the internal clipboard window.
994		 */
995
996		if ((infoPtr != NULL)
997			&& (infoPtr->owner != winPtr->dispPtr->clipWindow)) {
998		    Tcl_SetResult(interp, Tk_PathName(infoPtr->owner),
999			    TCL_STATIC);
1000		}
1001		return TCL_OK;
1002	    }
1003	    tkwin = Tk_NameToWindow(interp, Tcl_GetString(objs[0]), tkwin);
1004	    if (tkwin == NULL) {
1005		return TCL_ERROR;
1006	    }
1007	    if (count == 2) {
1008		script = Tcl_GetString(objs[1]);
1009	    }
1010	    if (script == NULL) {
1011		Tk_OwnSelection(tkwin, selection, (Tk_LostSelProc *) NULL,
1012			(ClientData) NULL);
1013		return TCL_OK;
1014	    }
1015	    cmdLength = strlen(script);
1016	    lostPtr = (LostCommand *) ckalloc((unsigned) (sizeof(LostCommand)
1017		    -3 + cmdLength));
1018	    lostPtr->interp = interp;
1019	    strcpy(lostPtr->command, script);
1020	    Tk_OwnSelection(tkwin, selection, LostSelection,
1021		    (ClientData) lostPtr);
1022	    return TCL_OK;
1023	}
1024    }
1025    return TCL_OK;
1026}
1027
1028/*
1029 *----------------------------------------------------------------------
1030 *
1031 * TkSelGetInProgress --
1032 *
1033 *	This procedure returns a pointer to the thread-local
1034 *      list of pending searches.
1035 *
1036 * Results:
1037 *	The return value is a pointer to the first search in progress,
1038 *      or NULL if there are none.
1039 *
1040 * Side effects:
1041 *	None.
1042 *
1043 *----------------------------------------------------------------------
1044 */
1045
1046TkSelInProgress *
1047TkSelGetInProgress _ANSI_ARGS_((void))
1048{
1049    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1050            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1051
1052    return tsdPtr->pendingPtr;
1053}
1054
1055/*
1056 *----------------------------------------------------------------------
1057 *
1058 * TkSelSetInProgress --
1059 *
1060 *	This procedure is used to set the thread-local list of pending
1061 *      searches.  It is required because the pending list is kept
1062 *      in thread local storage.
1063 *
1064 * Results:
1065 *	None.
1066 *
1067 * Side effects:
1068 *	None.
1069 *
1070 *----------------------------------------------------------------------
1071 */
1072void
1073TkSelSetInProgress(pendingPtr)
1074    TkSelInProgress *pendingPtr;
1075{
1076    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1077            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1078
1079   tsdPtr->pendingPtr = pendingPtr;
1080}
1081
1082/*
1083 *----------------------------------------------------------------------
1084 *
1085 * TkSelDeadWindow --
1086 *
1087 *	This procedure is invoked just before a TkWindow is deleted.
1088 *	It performs selection-related cleanup.
1089 *
1090 * Results:
1091 *	None.
1092 *
1093 * Side effects:
1094 *	Frees up memory associated with the selection.
1095 *
1096 *----------------------------------------------------------------------
1097 */
1098
1099void
1100TkSelDeadWindow(winPtr)
1101    register TkWindow *winPtr;	/* Window that's being deleted. */
1102{
1103    register TkSelHandler *selPtr;
1104    register TkSelInProgress *ipPtr;
1105    TkSelectionInfo *infoPtr, *prevPtr, *nextPtr;
1106    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1107            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1108
1109    /*
1110     * While deleting all the handlers, be careful to check whether
1111     * ConvertSelection or TkSelPropProc are about to process one of the
1112     * deleted handlers.
1113     */
1114
1115    while (winPtr->selHandlerList != NULL) {
1116	selPtr = winPtr->selHandlerList;
1117	winPtr->selHandlerList = selPtr->nextPtr;
1118	for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
1119                ipPtr = ipPtr->nextPtr) {
1120	    if (ipPtr->selPtr == selPtr) {
1121		ipPtr->selPtr = NULL;
1122	    }
1123	}
1124	if (selPtr->proc == HandleTclCommand) {
1125	    /*
1126	     * Mark the CommandInfo as deleted and free it when we can.
1127	     */
1128
1129	    ((CommandInfo*)selPtr->clientData)->interp = NULL;
1130	    Tcl_EventuallyFree(selPtr->clientData, TCL_DYNAMIC);
1131	}
1132	ckfree((char *) selPtr);
1133    }
1134
1135    /*
1136     * Remove selections owned by window being deleted.
1137     */
1138
1139    for (infoPtr = winPtr->dispPtr->selectionInfoPtr, prevPtr = NULL;
1140	     infoPtr != NULL; infoPtr = nextPtr) {
1141	nextPtr = infoPtr->nextPtr;
1142	if (infoPtr->owner == (Tk_Window) winPtr) {
1143	    if (infoPtr->clearProc == LostSelection) {
1144		ckfree((char *) infoPtr->clearData);
1145	    }
1146	    ckfree((char *) infoPtr);
1147	    infoPtr = prevPtr;
1148	    if (prevPtr == NULL) {
1149		winPtr->dispPtr->selectionInfoPtr = nextPtr;
1150	    } else {
1151		prevPtr->nextPtr = nextPtr;
1152	    }
1153	}
1154	prevPtr = infoPtr;
1155    }
1156}
1157
1158/*
1159 *----------------------------------------------------------------------
1160 *
1161 * TkSelInit --
1162 *
1163 *	Initialize selection-related information for a display.
1164 *
1165 * Results:
1166 *	None.
1167 *
1168 * Side effects:
1169 *	Selection-related information is initialized.
1170 *
1171 *----------------------------------------------------------------------
1172 */
1173
1174void
1175TkSelInit(tkwin)
1176    Tk_Window tkwin;		/* Window token (used to find
1177				 * display to initialize). */
1178{
1179    register TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1180
1181    /*
1182     * Fetch commonly-used atoms.
1183     */
1184
1185    dispPtr->multipleAtom	= Tk_InternAtom(tkwin, "MULTIPLE");
1186    dispPtr->incrAtom		= Tk_InternAtom(tkwin, "INCR");
1187    dispPtr->targetsAtom	= Tk_InternAtom(tkwin, "TARGETS");
1188    dispPtr->timestampAtom	= Tk_InternAtom(tkwin, "TIMESTAMP");
1189    dispPtr->textAtom		= Tk_InternAtom(tkwin, "TEXT");
1190    dispPtr->compoundTextAtom	= Tk_InternAtom(tkwin, "COMPOUND_TEXT");
1191    dispPtr->applicationAtom	= Tk_InternAtom(tkwin, "TK_APPLICATION");
1192    dispPtr->windowAtom		= Tk_InternAtom(tkwin, "TK_WINDOW");
1193    dispPtr->clipboardAtom	= Tk_InternAtom(tkwin, "CLIPBOARD");
1194
1195    /*
1196     * Using UTF8_STRING instead of the XA_UTF8_STRING macro allows us
1197     * to support older X servers that didn't have UTF8_STRING yet.
1198     * This is necessary on Unix systems.
1199     * For more information, see:
1200     *    http://www.cl.cam.ac.uk/~mgk25/unicode.html#x11
1201     */
1202
1203#if !(defined(__WIN32__) || defined(MAC_TCL) || defined(MAC_OSX_TK))
1204    dispPtr->utf8Atom		= Tk_InternAtom(tkwin, "UTF8_STRING");
1205#else
1206    dispPtr->utf8Atom		= (Atom) NULL;
1207#endif
1208}
1209
1210/*
1211 *----------------------------------------------------------------------
1212 *
1213 * TkSelClearSelection --
1214 *
1215 *	This procedure is invoked to process a SelectionClear event.
1216 *
1217 * Results:
1218 *	None.
1219 *
1220 * Side effects:
1221 *	Invokes the clear procedure for the window which lost the
1222 *	selection.
1223 *
1224 *----------------------------------------------------------------------
1225 */
1226
1227void
1228TkSelClearSelection(tkwin, eventPtr)
1229    Tk_Window tkwin;		/* Window for which event was targeted. */
1230    register XEvent *eventPtr;	/* X SelectionClear event. */
1231{
1232    register TkWindow *winPtr = (TkWindow *) tkwin;
1233    TkDisplay *dispPtr = winPtr->dispPtr;
1234    TkSelectionInfo *infoPtr;
1235    TkSelectionInfo *prevPtr;
1236
1237    /*
1238     * Invoke clear procedure for window that just lost the selection.  This
1239     * code is a bit tricky, because any callbacks due to selection changes
1240     * between windows managed by the process have already been made.  Thus,
1241     * ignore the event unless it refers to the window that's currently the
1242     * selection owner and the event was generated after the server saw the
1243     * SetSelectionOwner request.
1244     */
1245
1246    for (infoPtr = dispPtr->selectionInfoPtr, prevPtr = NULL;
1247	 infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
1248	if (infoPtr->selection == eventPtr->xselectionclear.selection) {
1249	    break;
1250	}
1251	prevPtr = infoPtr;
1252    }
1253
1254    if (infoPtr != NULL && (infoPtr->owner == tkwin)
1255	    && (eventPtr->xselectionclear.serial >= (unsigned) infoPtr->serial)) {
1256	if (prevPtr == NULL) {
1257	    dispPtr->selectionInfoPtr = infoPtr->nextPtr;
1258	} else {
1259	    prevPtr->nextPtr = infoPtr->nextPtr;
1260	}
1261
1262	/*
1263	 * Because of reentrancy problems, calling clearProc must be done
1264	 * after the infoPtr has been removed from the selectionInfoPtr
1265	 * list (clearProc could modify the list, e.g. by creating
1266	 * a new selection).
1267	 */
1268
1269	if (infoPtr->clearProc != NULL) {
1270	    (*infoPtr->clearProc)(infoPtr->clearData);
1271	}
1272	ckfree((char *) infoPtr);
1273    }
1274}
1275
1276/*
1277 *--------------------------------------------------------------
1278 *
1279 * SelGetProc --
1280 *
1281 *	This procedure is invoked to process pieces of the selection
1282 *	as they arrive during "selection get" commands.
1283 *
1284 * Results:
1285 *	Always returns TCL_OK.
1286 *
1287 * Side effects:
1288 *	Bytes get appended to the dynamic string pointed to by the
1289 *	clientData argument.
1290 *
1291 *--------------------------------------------------------------
1292 */
1293
1294	/* ARGSUSED */
1295static int
1296SelGetProc(clientData, interp, portion)
1297    ClientData clientData;	/* Dynamic string holding partially
1298				 * assembled selection. */
1299    Tcl_Interp *interp;		/* Interpreter used for error
1300				 * reporting (not used). */
1301    char *portion;		/* New information to be appended. */
1302{
1303    Tcl_DStringAppend((Tcl_DString *) clientData, portion, -1);
1304    return TCL_OK;
1305}
1306
1307/*
1308 *----------------------------------------------------------------------
1309 *
1310 * HandleTclCommand --
1311 *
1312 *	This procedure acts as selection handler for handlers created
1313 *	by the "selection handle" command.  It invokes a Tcl command to
1314 *	retrieve the selection.
1315 *
1316 * Results:
1317 *	The return value is a count of the number of bytes actually
1318 *	stored at buffer, or -1 if an error occurs while executing
1319 *	the Tcl command to retrieve the selection.
1320 *
1321 * Side effects:
1322 *	None except for things done by the Tcl command.
1323 *
1324 *----------------------------------------------------------------------
1325 */
1326
1327static int
1328HandleTclCommand(clientData, offset, buffer, maxBytes)
1329    ClientData clientData;	/* Information about command to execute. */
1330    int offset;			/* Return selection bytes starting at this
1331				 * offset. */
1332    char *buffer;		/* Place to store converted selection. */
1333    int maxBytes;		/* Maximum # of bytes to store at buffer. */
1334{
1335    CommandInfo *cmdInfoPtr = (CommandInfo *) clientData;
1336    int spaceNeeded, length;
1337#define MAX_STATIC_SIZE 100
1338    char staticSpace[MAX_STATIC_SIZE];
1339    char *command, *string;
1340    Tcl_Interp *interp = cmdInfoPtr->interp;
1341    Tcl_DString oldResult;
1342    Tcl_Obj *objPtr;
1343    int extraBytes, charOffset, count, numChars;
1344    CONST char *p;
1345
1346    /*
1347     * We must also protect the interpreter and the command from being
1348     * deleted too soon.
1349     */
1350
1351    Tcl_Preserve(clientData);
1352    Tcl_Preserve((ClientData) interp);
1353
1354    /*
1355     * Compute the proper byte offset in the case where the last chunk
1356     * split a character.
1357     */
1358
1359    if (offset == cmdInfoPtr->byteOffset) {
1360	charOffset = cmdInfoPtr->charOffset;
1361	extraBytes = strlen(cmdInfoPtr->buffer);
1362	if (extraBytes > 0) {
1363	    strcpy(buffer, cmdInfoPtr->buffer);
1364	    maxBytes -= extraBytes;
1365	    buffer += extraBytes;
1366	}
1367    } else {
1368	cmdInfoPtr->byteOffset = 0;
1369	cmdInfoPtr->charOffset = 0;
1370	extraBytes = 0;
1371	charOffset = 0;
1372    }
1373
1374    /*
1375     * First, generate a command by taking the command string
1376     * and appending the offset and maximum # of bytes.
1377     */
1378
1379    spaceNeeded = cmdInfoPtr->cmdLength + 30;
1380    if (spaceNeeded < MAX_STATIC_SIZE) {
1381	command = staticSpace;
1382    } else {
1383	command = (char *) ckalloc((unsigned) spaceNeeded);
1384    }
1385    sprintf(command, "%s %d %d", cmdInfoPtr->command, charOffset, maxBytes);
1386
1387    /*
1388     * Execute the command.  Be sure to restore the state of the
1389     * interpreter after executing the command.
1390     */
1391
1392    Tcl_DStringInit(&oldResult);
1393    Tcl_DStringGetResult(interp, &oldResult);
1394    if (TkCopyAndGlobalEval(interp, command) == TCL_OK) {
1395	objPtr = Tcl_GetObjResult(interp);
1396	string = Tcl_GetStringFromObj(objPtr, &length);
1397	count = (length > maxBytes) ? maxBytes : length;
1398	memcpy((VOID *) buffer, (VOID *) string, (size_t) count);
1399	buffer[count] = '\0';
1400
1401	/*
1402	 * Update the partial character information for the next
1403	 * retrieval if the command has not been deleted.
1404	 */
1405
1406	if (cmdInfoPtr->interp != NULL) {
1407	    if (length <= maxBytes) {
1408		cmdInfoPtr->charOffset += Tcl_NumUtfChars(string, -1);
1409		cmdInfoPtr->buffer[0] = '\0';
1410	    } else {
1411		p = string;
1412		string += count;
1413		numChars = 0;
1414		while (p < string) {
1415		    p = Tcl_UtfNext(p);
1416		    numChars++;
1417		}
1418		cmdInfoPtr->charOffset += numChars;
1419		length = p - string;
1420		if (length > 0) {
1421		    strncpy(cmdInfoPtr->buffer, string, (size_t) length);
1422		}
1423		cmdInfoPtr->buffer[length] = '\0';
1424	    }
1425	    cmdInfoPtr->byteOffset += count + extraBytes;
1426	}
1427	count += extraBytes;
1428    } else {
1429	count = -1;
1430    }
1431    Tcl_DStringResult(interp, &oldResult);
1432
1433    if (command != staticSpace) {
1434	ckfree(command);
1435    }
1436
1437
1438    Tcl_Release(clientData);
1439    Tcl_Release((ClientData) interp);
1440    return count;
1441}
1442
1443/*
1444 *----------------------------------------------------------------------
1445 *
1446 * TkSelDefaultSelection --
1447 *
1448 *	This procedure is called to generate selection information
1449 *	for a few standard targets such as TIMESTAMP and TARGETS.
1450 *	It is invoked only if no handler has been declared by the
1451 *	application.
1452 *
1453 * Results:
1454 *	If "target" is a standard target understood by this procedure,
1455 *	the selection is converted to that form and stored as a
1456 *	character string in buffer.  The type of the selection (e.g.
1457 *	STRING or ATOM) is stored in *typePtr, and the return value is
1458 *	a count of the # of non-NULL bytes at buffer.  If the target
1459 *	wasn't understood, or if there isn't enough space at buffer
1460 *	to hold the entire selection (no INCR-mode transfers for this
1461 *	stuff!), then -1 is returned.
1462 *
1463 * Side effects:
1464 *	None.
1465 *
1466 *----------------------------------------------------------------------
1467 */
1468
1469int
1470TkSelDefaultSelection(infoPtr, target, buffer, maxBytes, typePtr)
1471    TkSelectionInfo *infoPtr;	/* Info about selection being retrieved. */
1472    Atom target;		/* Desired form of selection. */
1473    char *buffer;		/* Place to put selection characters. */
1474    int maxBytes;		/* Maximum # of bytes to store at buffer. */
1475    Atom *typePtr;		/* Store here the type of the selection,
1476				 * for use in converting to proper X format. */
1477{
1478    register TkWindow *winPtr = (TkWindow *) infoPtr->owner;
1479    TkDisplay *dispPtr = winPtr->dispPtr;
1480
1481    if (target == dispPtr->timestampAtom) {
1482	if (maxBytes < 20) {
1483	    return -1;
1484	}
1485	sprintf(buffer, "0x%x", (unsigned int) infoPtr->time);
1486	*typePtr = XA_INTEGER;
1487	return strlen(buffer);
1488    }
1489
1490    if (target == dispPtr->targetsAtom) {
1491	register TkSelHandler *selPtr;
1492	int length;
1493	Tcl_DString ds;
1494
1495	if (maxBytes < 50) {
1496	    return -1;
1497	}
1498	Tcl_DStringInit(&ds);
1499	Tcl_DStringAppend(&ds,
1500		"MULTIPLE TARGETS TIMESTAMP TK_APPLICATION TK_WINDOW", -1);
1501	for (selPtr = winPtr->selHandlerList; selPtr != NULL;
1502		selPtr = selPtr->nextPtr) {
1503	    if ((selPtr->selection == infoPtr->selection)
1504		    && (selPtr->target != dispPtr->applicationAtom)
1505		    && (selPtr->target != dispPtr->windowAtom)) {
1506		CONST char *atomString = Tk_GetAtomName((Tk_Window) winPtr,
1507			selPtr->target);
1508		Tcl_DStringAppendElement(&ds, atomString);
1509	    }
1510	}
1511	length = Tcl_DStringLength(&ds);
1512	if (length >= maxBytes) {
1513	    Tcl_DStringFree(&ds);
1514	    return -1;
1515	}
1516	memcpy(buffer, Tcl_DStringValue(&ds), (unsigned) (1+length));
1517	Tcl_DStringFree(&ds);
1518	*typePtr = XA_ATOM;
1519	return length;
1520    }
1521
1522    if (target == dispPtr->applicationAtom) {
1523	int length;
1524	Tk_Uid name = winPtr->mainPtr->winPtr->nameUid;
1525
1526	length = strlen(name);
1527	if (maxBytes <= length) {
1528	    return -1;
1529	}
1530	strcpy(buffer, name);
1531	*typePtr = XA_STRING;
1532	return length;
1533    }
1534
1535    if (target == dispPtr->windowAtom) {
1536	int length;
1537	char *name = winPtr->pathName;
1538
1539	length = strlen(name);
1540	if (maxBytes <= length) {
1541	    return -1;
1542	}
1543	strcpy(buffer, name);
1544	*typePtr = XA_STRING;
1545	return length;
1546    }
1547
1548    return -1;
1549}
1550
1551/*
1552 *----------------------------------------------------------------------
1553 *
1554 * LostSelection --
1555 *
1556 *	This procedure is invoked when a window has lost ownership of
1557 *	the selection and the ownership was claimed with the command
1558 *	"selection own".
1559 *
1560 * Results:
1561 *	None.
1562 *
1563 * Side effects:
1564 *	A Tcl script is executed;  it can do almost anything.
1565 *
1566 *----------------------------------------------------------------------
1567 */
1568
1569static void
1570LostSelection(clientData)
1571    ClientData clientData;		/* Pointer to LostCommand structure. */
1572{
1573    LostCommand *lostPtr = (LostCommand *) clientData;
1574    Tcl_Obj *objPtr;
1575    Tcl_Interp *interp;
1576
1577    interp = lostPtr->interp;
1578    Tcl_Preserve((ClientData) interp);
1579
1580    /*
1581     * Execute the command.  Save the interpreter's result, if any, and
1582     * restore it after executing the command.
1583     */
1584
1585    objPtr = Tcl_GetObjResult(interp);
1586    Tcl_IncrRefCount(objPtr);
1587    Tcl_ResetResult(interp);
1588
1589    if (TkCopyAndGlobalEval(interp, lostPtr->command) != TCL_OK) {
1590	Tcl_BackgroundError(interp);
1591    }
1592
1593    Tcl_SetObjResult(interp, objPtr);
1594    Tcl_DecrRefCount(objPtr);
1595
1596    Tcl_Release((ClientData) interp);
1597
1598    /*
1599     * Free the storage for the command, since we're done with it now.
1600     */
1601
1602    ckfree((char *) lostPtr);
1603}
1604