/[dtapublic]/projs/ets/trunk/src/c_tk_base_7_5_w_mods/tkselect.c
ViewVC logotype

Diff of /projs/ets/trunk/src/c_tk_base_7_5_w_mods/tkselect.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.42  
changed lines
  Added in v.98

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25