tkUnixSelect.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:43k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkUnixSelect.c --
  3.  *
  4.  * This file contains X specific routines for manipulating 
  5.  * selections.
  6.  *
  7.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * RCS: @(#) $Id: tkUnixSelect.c,v 1.11.2.1 2005/11/22 11:32:37 dkf Exp $
  13.  */
  14. #include "tkInt.h"
  15. #include "tkSelect.h"
  16. typedef struct ConvertInfo {
  17.     int offset; /* The starting byte offset into the selection
  18.  * for the next chunk; -1 means all data has
  19.  * been transferred for this conversion. -2
  20.  * means only the final zero-length transfer
  21.  * still has to be done.  Otherwise it is the
  22.  * offset of the next chunk of data to
  23.  * transfer. */
  24.     Tcl_EncodingState state; /* The encoding state needed across chunks. */
  25.     char buffer[TCL_UTF_MAX]; /* A buffer to hold part of a UTF character
  26.  * that is split across chunks.*/
  27. } ConvertInfo;
  28. /*
  29.  * When handling INCR-style selection retrievals, the selection owner
  30.  * uses the following data structure to communicate between the
  31.  * ConvertSelection procedure and TkSelPropProc.
  32.  */
  33. typedef struct IncrInfo {
  34.     TkWindow *winPtr; /* Window that owns selection. */
  35.     Atom selection; /* Selection that is being retrieved. */
  36.     Atom *multAtoms; /* Information about conversions to
  37.  * perform:  one or more pairs of
  38.  * (target, property).  This either
  39.  * points to a retrieved  property (for
  40.  * MULTIPLE retrievals) or to a static
  41.  * array. */
  42.     unsigned long numConversions;
  43. /* Number of entries in converts (same as
  44.  * # of pairs in multAtoms). */
  45.     ConvertInfo *converts; /* One entry for each pair in multAtoms.
  46.  * This array is malloc-ed. */
  47.     char **tempBufs; /* One pointer for each pair in multAtoms;
  48.  * each pointer is either NULL, or it points
  49.  * to a small bit of character data that was
  50.  * left over from the previous chunk. */
  51.     Tcl_EncodingState *state; /* One state info per pair in multAtoms:
  52.  * State info for encoding conversions
  53.  * that span multiple buffers. */
  54.     int *flags; /* One state flag per pair in multAtoms:
  55.  * Encoding flags, set to TCL_ENCODING_START
  56.  * at the beginning of an INCR transfer. */
  57.     int numIncrs; /* Number of entries in converts that
  58.  * aren't -1 (i.e. # of INCR-mode transfers
  59.  * not yet completed). */
  60.     Tcl_TimerToken timeout; /* Token for timer procedure. */
  61.     int idleTime; /* Number of seconds since we heard
  62.  * anything from the selection
  63.  * requestor. */
  64.     Window reqWindow; /* Requestor's window id. */
  65.     Time time; /* Timestamp corresponding to
  66.  * selection at beginning of request;
  67.  * used to abort transfer if selection
  68.  * changes. */
  69.     struct IncrInfo *nextPtr; /* Next in list of all INCR-style
  70.  * retrievals currently pending. */
  71. } IncrInfo;
  72. typedef struct ThreadSpecificData {
  73.     IncrInfo *pendingIncrs;     /* List of all incr structures
  74.  * currently active. */
  75. } ThreadSpecificData;
  76. static Tcl_ThreadDataKey dataKey;
  77. /*
  78.  * Largest property that we'll accept when sending or receiving the
  79.  * selection:
  80.  */
  81. #define MAX_PROP_WORDS 100000
  82. static TkSelRetrievalInfo *pendingRetrievals = NULL;
  83. /* List of all retrievals currently
  84.  * being waited for. */
  85. /*
  86.  * Forward declarations for procedures defined in this file:
  87.  */
  88. static void ConvertSelection _ANSI_ARGS_((TkWindow *winPtr,
  89.     XSelectionRequestEvent *eventPtr));
  90. static void IncrTimeoutProc _ANSI_ARGS_((ClientData clientData));
  91. static void SelCvtFromX _ANSI_ARGS_((long *propPtr, int numValues,
  92.     Atom type, Tk_Window tkwin, Tcl_DString *dsPtr));
  93. static long * SelCvtToX _ANSI_ARGS_((char *string, Atom type,
  94.     Tk_Window tkwin, int *numLongsPtr));
  95. static int SelectionSize _ANSI_ARGS_((TkSelHandler *selPtr));
  96. static void SelRcvIncrProc _ANSI_ARGS_((ClientData clientData,
  97.     XEvent *eventPtr));
  98. static void SelTimeoutProc _ANSI_ARGS_((ClientData clientData));
  99. /*
  100.  *----------------------------------------------------------------------
  101.  *
  102.  * TkSelGetSelection --
  103.  *
  104.  * Retrieve the specified selection from another process.
  105.  *
  106.  * Results:
  107.  * The return value is a standard Tcl return value.
  108.  * If an error occurs (such as no selection exists)
  109.  * then an error message is left in the interp's result.
  110.  *
  111.  * Side effects:
  112.  * None.
  113.  *
  114.  *----------------------------------------------------------------------
  115.  */
  116. int
  117. TkSelGetSelection(interp, tkwin, selection, target, proc, clientData)
  118.     Tcl_Interp *interp; /* Interpreter to use for reporting
  119.  * errors. */
  120.     Tk_Window tkwin; /* Window on whose behalf to retrieve
  121.  * the selection (determines display
  122.  * from which to retrieve). */
  123.     Atom selection; /* Selection to retrieve. */
  124.     Atom target; /* Desired form in which selection
  125.  * is to be returned. */
  126.     Tk_GetSelProc *proc; /* Procedure to call to process the
  127.  * selection, once it has been retrieved. */
  128.     ClientData clientData; /* Arbitrary value to pass to proc. */
  129. {
  130.     TkSelRetrievalInfo retr;
  131.     TkWindow *winPtr = (TkWindow *) tkwin;
  132.     TkDisplay *dispPtr = winPtr->dispPtr;
  133.     /*
  134.      * The selection is owned by some other process.  To
  135.      * retrieve it, first record information about the retrieval
  136.      * in progress.  Use an internal window as the requestor.
  137.      */
  138.     retr.interp = interp;
  139.     if (dispPtr->clipWindow == NULL) {
  140. int result;
  141. result = TkClipInit(interp, dispPtr);
  142. if (result != TCL_OK) {
  143.     return result;
  144. }
  145.     }
  146.     retr.winPtr = (TkWindow *) dispPtr->clipWindow;
  147.     retr.selection = selection;
  148.     retr.property = selection;
  149.     retr.target = target;
  150.     retr.proc = proc;
  151.     retr.clientData = clientData;
  152.     retr.result = -1;
  153.     retr.idleTime = 0;
  154.     retr.encFlags = TCL_ENCODING_START;
  155.     retr.nextPtr = pendingRetrievals;
  156.     Tcl_DStringInit(&retr.buf);
  157.     pendingRetrievals = &retr;
  158.     /*
  159.      * Initiate the request for the selection.  Note:  can't use
  160.      * TkCurrentTime for the time.  If we do, and this application hasn't
  161.      * received any X events in a long time, the current time will be way
  162.      * in the past and could even predate the time when the selection was
  163.      * made;  if this happens, the request will be rejected.
  164.      */
  165.     XConvertSelection(winPtr->display, retr.selection, retr.target,
  166.     retr.property, retr.winPtr->window, CurrentTime);
  167.     /*
  168.      * Enter a loop processing X events until the selection
  169.      * has been retrieved and processed.  If no response is
  170.      * received within a few seconds, then timeout.
  171.      */
  172.     retr.timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc,
  173.     (ClientData) &retr);
  174.     while (retr.result == -1) {
  175. Tcl_DoOneEvent(0);
  176.     }
  177.     Tcl_DeleteTimerHandler(retr.timeout);
  178.     /*
  179.      * Unregister the information about the selection retrieval
  180.      * in progress.
  181.      */
  182.     if (pendingRetrievals == &retr) {
  183. pendingRetrievals = retr.nextPtr;
  184.     } else {
  185. TkSelRetrievalInfo *retrPtr;
  186. for (retrPtr = pendingRetrievals; retrPtr != NULL;
  187. retrPtr = retrPtr->nextPtr) {
  188.     if (retrPtr->nextPtr == &retr) {
  189. retrPtr->nextPtr = retr.nextPtr;
  190. break;
  191.     }
  192. }
  193.     }
  194.     Tcl_DStringFree(&retr.buf);
  195.     return retr.result;
  196. }
  197. /*
  198.  *----------------------------------------------------------------------
  199.  *
  200.  * TkSelPropProc --
  201.  *
  202.  * This procedure is invoked when property-change events
  203.  * occur on windows not known to the toolkit.  Its function
  204.  * is to implement the sending side of the INCR selection
  205.  * retrieval protocol when the selection requestor deletes
  206.  * the property containing a part of the selection.
  207.  *
  208.  * Results:
  209.  * None.
  210.  *
  211.  * Side effects:
  212.  * If the property that is receiving the selection was just
  213.  * deleted, then a new piece of the selection is fetched and
  214.  * placed in the property, until eventually there's no more
  215.  * selection to fetch.
  216.  *
  217.  *----------------------------------------------------------------------
  218.  */
  219. void
  220. TkSelPropProc(eventPtr)
  221.     register XEvent *eventPtr; /* X PropertyChange event. */
  222. {
  223.     register IncrInfo *incrPtr;
  224.     register TkSelHandler *selPtr;
  225.     int i, length, numItems;
  226.     Atom target, formatType;
  227.     long buffer[TK_SEL_WORDS_AT_ONCE];
  228.     TkDisplay *dispPtr = TkGetDisplay(eventPtr->xany.display);
  229.     Tk_ErrorHandler errorHandler;
  230.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  231.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  232.     /*
  233.      * See if this event announces the deletion of a property being
  234.      * used for an INCR transfer.  If so, then add the next chunk of
  235.      * data to the property.
  236.      */
  237.     if (eventPtr->xproperty.state != PropertyDelete) {
  238. return;
  239.     }
  240.     for (incrPtr = tsdPtr->pendingIncrs; incrPtr != NULL;
  241.     incrPtr = incrPtr->nextPtr) {
  242. if (incrPtr->reqWindow != eventPtr->xproperty.window) {
  243.     continue;
  244. }
  245. /*
  246.  * For each conversion that has been requested, handle any
  247.  * chunks that haven't been transmitted yet.
  248.  */   
  249. for (i = 0; i < incrPtr->numConversions; i++) {
  250.     if ((eventPtr->xproperty.atom != incrPtr->multAtoms[2*i + 1])
  251.     || (incrPtr->converts[i].offset == -1)) {
  252. continue;
  253.     }
  254.     target = incrPtr->multAtoms[2*i];
  255.     incrPtr->idleTime = 0;
  256.     /*
  257.      * Look for a matching selection handler.
  258.      */
  259.     for (selPtr = incrPtr->winPtr->selHandlerList; ;
  260.     selPtr = selPtr->nextPtr) {
  261. if (selPtr == NULL) {
  262.     /*
  263.      * No handlers match, so mark the conversion as done.
  264.      */
  265.     incrPtr->multAtoms[2*i + 1] = None;
  266.     incrPtr->converts[i].offset = -1;
  267.     incrPtr->numIncrs --;
  268.     return;
  269. }
  270. if ((selPtr->target == target)
  271. && (selPtr->selection == incrPtr->selection)) {
  272.     break;
  273. }
  274.     }
  275.     /*
  276.      * We found a handler, so get the next chunk from it.
  277.      */
  278.     formatType = selPtr->format;
  279.     if (incrPtr->converts[i].offset == -2) {
  280. /*
  281.  * We already got the last chunk, so send a null chunk
  282.  * to indicate that we are finished.
  283.  */
  284. numItems = 0;
  285. length = 0;
  286.     } else {
  287. TkSelInProgress ip;
  288. ip.selPtr = selPtr;
  289. ip.nextPtr = TkSelGetInProgress();
  290. TkSelSetInProgress(&ip);
  291. /*
  292.  * Copy any bytes left over from a partial character at the end
  293.  * of the previous chunk into the beginning of the buffer.
  294.  * Pass the rest of the buffer space into the selection
  295.  * handler.
  296.  */
  297. length = strlen(incrPtr->converts[i].buffer);
  298. strcpy((char *)buffer, incrPtr->converts[i].buffer);
  299.     
  300. numItems = (*selPtr->proc)(selPtr->clientData,
  301. incrPtr->converts[i].offset,
  302. ((char *) buffer) + length,
  303. TK_SEL_BYTES_AT_ONCE - length);
  304. TkSelSetInProgress(ip.nextPtr);
  305. if (ip.selPtr == NULL) {
  306.     /*
  307.      * The selection handler deleted itself.
  308.      */
  309.     return;
  310. }
  311. if (numItems < 0) {
  312.     numItems = 0;
  313. }
  314. numItems += length;
  315. if (numItems > TK_SEL_BYTES_AT_ONCE) {
  316.     panic("selection handler returned too many bytes");
  317. }
  318.     }
  319.     ((char *) buffer)[numItems] = 0;
  320.     errorHandler = Tk_CreateErrorHandler(eventPtr->xproperty.display,
  321.     -1, -1, -1, (int (*)()) NULL, (ClientData) NULL);
  322.     /*
  323.      * Encode the data using the proper format for each type.
  324.      */
  325.     if ((formatType == XA_STRING)
  326.     || (dispPtr && formatType==dispPtr->utf8Atom)
  327.     || (dispPtr && formatType==dispPtr->compoundTextAtom)) {
  328. Tcl_DString ds;
  329. int encodingCvtFlags;
  330. int srcLen, dstLen, result, srcRead, dstWrote, soFar;
  331. char *src, *dst;
  332. Tcl_Encoding encoding;
  333. /*
  334.  * Set up the encoding state based on the format and whether
  335.  * this is the first and/or last chunk.
  336.  */
  337. encodingCvtFlags = 0;
  338. if (incrPtr->converts[i].offset == 0) {
  339.     encodingCvtFlags |= TCL_ENCODING_START;
  340. }
  341. if (numItems < TK_SEL_BYTES_AT_ONCE) {
  342.     encodingCvtFlags |= TCL_ENCODING_END;
  343. }
  344. if (formatType == XA_STRING) {
  345.     encoding = Tcl_GetEncoding(NULL, "iso8859-1");
  346. } else if (dispPtr && formatType==dispPtr->utf8Atom) {
  347.     encoding = Tcl_GetEncoding(NULL, "utf-8");
  348. } else {
  349.     encoding = Tcl_GetEncoding(NULL, "iso2022");
  350. }
  351. /*
  352.  * Now convert the data.
  353.  */
  354. src = (char *)buffer;
  355. srcLen = numItems;
  356. Tcl_DStringInit(&ds);
  357. dst = Tcl_DStringValue(&ds);
  358. dstLen = ds.spaceAvl - 1;
  359. /*
  360.  * Now convert the data, growing the destination buffer
  361.  * as needed. 
  362.  */
  363. while (1) {
  364.     result = Tcl_UtfToExternal(NULL, encoding,
  365.     src, srcLen, encodingCvtFlags,
  366.     &incrPtr->converts[i].state,
  367.     dst, dstLen, &srcRead, &dstWrote, NULL);
  368.     soFar = dst + dstWrote - Tcl_DStringValue(&ds);
  369.     encodingCvtFlags &= ~TCL_ENCODING_START;
  370.     src += srcRead;
  371.     srcLen -= srcRead;
  372.     if (result != TCL_CONVERT_NOSPACE) {
  373. Tcl_DStringSetLength(&ds, soFar);
  374. break;
  375.     }
  376.     if (Tcl_DStringLength(&ds) == 0) {
  377. Tcl_DStringSetLength(&ds, dstLen);
  378.     }
  379.     Tcl_DStringSetLength(&ds, 2 * Tcl_DStringLength(&ds) + 1);
  380.     dst = Tcl_DStringValue(&ds) + soFar;
  381.     dstLen = Tcl_DStringLength(&ds) - soFar - 1;
  382. }
  383. Tcl_DStringSetLength(&ds, soFar);
  384. if (encoding) {
  385.     Tcl_FreeEncoding(encoding);
  386. }
  387. /*
  388.  * Set the property to the encoded string value.
  389.  */
  390. XChangeProperty(eventPtr->xproperty.display,
  391. eventPtr->xproperty.window, eventPtr->xproperty.atom,
  392. formatType, 8, PropModeReplace,
  393. (unsigned char *) Tcl_DStringValue(&ds),
  394. Tcl_DStringLength(&ds));
  395. /*
  396.  * Preserve any left-over bytes.
  397.  */
  398. if (srcLen > TCL_UTF_MAX) {
  399.     panic("selection conversion left too many bytes unconverted");
  400. }
  401. memcpy(incrPtr->converts[i].buffer, src, (size_t) srcLen+1);
  402. Tcl_DStringFree(&ds);
  403.     } else {
  404. /*
  405.  * Set the property to the encoded string value.
  406.  */
  407. char *propPtr = (char *) SelCvtToX((char *) buffer,
  408. formatType, (Tk_Window) incrPtr->winPtr,
  409. &numItems);
  410. if (propPtr == NULL) {
  411.     numItems = 0;
  412. }
  413. XChangeProperty(eventPtr->xproperty.display,
  414. eventPtr->xproperty.window, eventPtr->xproperty.atom,
  415. formatType, 32, PropModeReplace,
  416. (unsigned char *) propPtr, numItems);
  417. if (propPtr != NULL) {
  418.     ckfree(propPtr);
  419. }
  420.     }
  421.     Tk_DeleteErrorHandler(errorHandler);
  422.     /*
  423.      * Compute the next offset value.  If this was the last chunk,
  424.      * then set the offset to -2.  If this was an empty chunk,
  425.      * then set the offset to -1 to indicate we are done.
  426.      */
  427.     if (numItems < TK_SEL_BYTES_AT_ONCE) {
  428. if (numItems <= 0) {
  429.     incrPtr->converts[i].offset = -1;
  430.     incrPtr->numIncrs--;
  431. } else {
  432.     incrPtr->converts[i].offset = -2;
  433. }
  434.     } else {
  435. /*
  436.  * Advance over the selection data that was consumed
  437.  * this time.
  438.  */
  439.  
  440. incrPtr->converts[i].offset += numItems - length;
  441.     }
  442.     return;
  443. }
  444.     }
  445. }
  446. /*
  447.  *--------------------------------------------------------------
  448.  *
  449.  * TkSelEventProc --
  450.  *
  451.  * This procedure is invoked whenever a selection-related
  452.  * event occurs.  It does the lion's share of the work
  453.  * in implementing the selection protocol.
  454.  *
  455.  * Results:
  456.  * None.
  457.  *
  458.  * Side effects:
  459.  * Lots:  depends on the type of event.
  460.  *
  461.  *--------------------------------------------------------------
  462.  */
  463. void
  464. TkSelEventProc(tkwin, eventPtr)
  465.     Tk_Window tkwin; /* Window for which event was
  466.  * targeted. */
  467.     register XEvent *eventPtr; /* X event:  either SelectionClear,
  468.  * SelectionRequest, or
  469.  * SelectionNotify. */
  470. {
  471.     register TkWindow *winPtr = (TkWindow *) tkwin;
  472.     TkDisplay *dispPtr = winPtr->dispPtr;
  473.     Tcl_Interp *interp;
  474.     /*
  475.      * Case #1: SelectionClear events.
  476.      */
  477.     if (eventPtr->type == SelectionClear) {
  478. TkSelClearSelection(tkwin, eventPtr);
  479.     }
  480.     /*
  481.      * Case #2: SelectionNotify events.  Call the relevant procedure
  482.      * to handle the incoming selection.
  483.      */
  484.     if (eventPtr->type == SelectionNotify) {
  485. register TkSelRetrievalInfo *retrPtr;
  486. char *propInfo;
  487. Atom type;
  488. int format, result;
  489. unsigned long numItems, bytesAfter;
  490. Tcl_DString ds;
  491. for (retrPtr = pendingRetrievals; ; retrPtr = retrPtr->nextPtr) {
  492.     if (retrPtr == NULL) {
  493. return;
  494.     }
  495.     if ((retrPtr->winPtr == winPtr)
  496.     && (retrPtr->selection == eventPtr->xselection.selection)
  497.     && (retrPtr->target == eventPtr->xselection.target)
  498.     && (retrPtr->result == -1)) {
  499. if (retrPtr->property == eventPtr->xselection.property) {
  500.     break;
  501. }
  502. if (eventPtr->xselection.property == None) {
  503.     Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
  504.     Tcl_AppendResult(retrPtr->interp,
  505.     Tk_GetAtomName(tkwin, retrPtr->selection),
  506.     " selection doesn't exist or form "",
  507.     Tk_GetAtomName(tkwin, retrPtr->target),
  508.     "" not defined", (char *) NULL);
  509.     retrPtr->result = TCL_ERROR;
  510.     return;
  511. }
  512.     }
  513. }
  514. propInfo = NULL;
  515. result = XGetWindowProperty(eventPtr->xselection.display,
  516. eventPtr->xselection.requestor, retrPtr->property,
  517. 0, MAX_PROP_WORDS, False, (Atom) AnyPropertyType,
  518. &type, &format, &numItems, &bytesAfter,
  519. (unsigned char **) &propInfo);
  520. if ((result != Success) || (type == None)) {
  521.     return;
  522. }
  523. if (bytesAfter != 0) {
  524.     Tcl_SetResult(retrPtr->interp, "selection property too large",
  525. TCL_STATIC);
  526.     retrPtr->result = TCL_ERROR;
  527.     XFree(propInfo);
  528.     return;
  529. }
  530. if ((type == XA_STRING) || (type == dispPtr->textAtom)
  531. || (type == dispPtr->compoundTextAtom)) {
  532.     Tcl_Encoding encoding;
  533.     if (format != 8) {
  534. char buf[64 + TCL_INTEGER_SPACE];
  535. sprintf(buf, 
  536. "bad format for string selection: wanted "8", got "%d"",
  537. format);
  538. Tcl_SetResult(retrPtr->interp, buf, TCL_VOLATILE);
  539. retrPtr->result = TCL_ERROR;
  540. return;
  541.     }
  542.             interp = retrPtr->interp;
  543.             Tcl_Preserve((ClientData) interp);
  544.     /*
  545.      * Convert the X selection data into UTF before passing it
  546.      * to the selection callback.  Note that the COMPOUND_TEXT
  547.      * uses a modified iso2022 encoding, not the current system
  548.      * encoding.  For now we'll just blindly apply the iso2022
  549.      * encoding.  This is probably wrong, but it's a placeholder
  550.      * until we figure out what we're really supposed to do.  For
  551.      * STRING, we need to use Latin-1 instead.  Again, it's not
  552.      * really the full iso8859-1 space, but this is close enough.
  553.      */
  554.     if (type == dispPtr->compoundTextAtom) {
  555. encoding = Tcl_GetEncoding(NULL, "iso2022");
  556.     } else {
  557. encoding = Tcl_GetEncoding(NULL, "iso8859-1");
  558.     }
  559.     Tcl_ExternalToUtfDString(encoding, propInfo, (int)numItems, &ds);
  560.     if (encoding) {
  561. Tcl_FreeEncoding(encoding);
  562.     }
  563.     retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
  564.     interp, Tcl_DStringValue(&ds));
  565.     Tcl_DStringFree(&ds);
  566.     Tcl_Release((ClientData) interp);
  567. } else if (type == dispPtr->utf8Atom) {
  568.     /*
  569.      * The X selection data is in UTF-8 format already.
  570.      * We can't guarantee that propInfo is NULL-terminated,
  571.      * so we might have to copy the string.
  572.      */
  573.     char *propData = propInfo;
  574.     if (format != 8) {
  575. char buf[64 + TCL_INTEGER_SPACE];
  576. sprintf(buf, 
  577. "bad format for string selection: wanted "8", got "%d"",
  578. format);
  579. Tcl_SetResult(retrPtr->interp, buf, TCL_VOLATILE);
  580. retrPtr->result = TCL_ERROR;
  581. return;
  582.     }
  583.     if (propInfo[numItems] != '') {
  584. propData = ckalloc((size_t) numItems + 1);
  585. strcpy(propData, propInfo);
  586. propData[numItems] = '';
  587.     }
  588.     retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
  589.     retrPtr->interp, propData);
  590.     if (propData != propInfo) {
  591. ckfree((char *) propData);
  592.     }
  593. } else if (type == dispPtr->incrAtom) {
  594.     /*
  595.      * It's a !?#@!?!! INCR-style reception.  Arrange to receive
  596.      * the selection in pieces, using the ICCCM protocol, then
  597.      * hang around until either the selection is all here or a
  598.      * timeout occurs.
  599.      */
  600.     retrPtr->idleTime = 0;
  601.     Tk_CreateEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
  602.     (ClientData) retrPtr);
  603.     XDeleteProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
  604.     retrPtr->property);
  605.     while (retrPtr->result == -1) {
  606. Tcl_DoOneEvent(0);
  607.     }
  608.     Tk_DeleteEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
  609.     (ClientData) retrPtr);
  610. } else {
  611.     Tcl_DString ds;
  612.     if (format != 32) {
  613. char buf[64 + TCL_INTEGER_SPACE];
  614. sprintf(buf, 
  615. "bad format for selection: wanted "32", got "%d"",
  616. format);
  617. Tcl_SetResult(retrPtr->interp, buf, TCL_VOLATILE);
  618. retrPtr->result = TCL_ERROR;
  619. return;
  620.     }
  621.     Tcl_DStringInit(&ds);
  622.     SelCvtFromX((long *) propInfo, (int) numItems, type,
  623.     (Tk_Window) winPtr, &ds);
  624.             interp = retrPtr->interp;
  625.             Tcl_Preserve((ClientData) interp);
  626.     retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
  627.     interp, Tcl_DStringValue(&ds));
  628.             Tcl_Release((ClientData) interp);
  629.     Tcl_DStringFree(&ds);
  630. }
  631. XFree(propInfo);
  632. return;
  633.     }
  634.     /*
  635.      * Case #3: SelectionRequest events.  Call ConvertSelection to
  636.      * do the dirty work.
  637.      */
  638.     if (eventPtr->type == SelectionRequest) {
  639. ConvertSelection(winPtr, &eventPtr->xselectionrequest);
  640. return;
  641.     }
  642. }
  643. /*
  644.  *----------------------------------------------------------------------
  645.  *
  646.  * SelTimeoutProc --
  647.  *
  648.  * This procedure is invoked once every second while waiting for
  649.  * the selection to be returned.  After a while it gives up and
  650.  * aborts the selection retrieval.
  651.  *
  652.  * Results:
  653.  * None.
  654.  *
  655.  * Side effects:
  656.  * A new timer callback is created to call us again in another
  657.  * second, unless time has expired, in which case an error is
  658.  * recorded for the retrieval.
  659.  *
  660.  *----------------------------------------------------------------------
  661.  */
  662. static void
  663. SelTimeoutProc(clientData)
  664.     ClientData clientData; /* Information about retrieval
  665.  * in progress. */
  666. {
  667.     register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
  668.     /*
  669.      * Make sure that the retrieval is still in progress.  Then
  670.      * see how long it's been since any sort of response was received
  671.      * from the other side.
  672.      */
  673.     if (retrPtr->result != -1) {
  674. return;
  675.     }
  676.     retrPtr->idleTime++;
  677.     if (retrPtr->idleTime >= 5) {
  678. /*
  679.  * Use a careful procedure to store the error message, because
  680.  * the result could already be partially filled in with a partial
  681.  * selection return.
  682.  */
  683. Tcl_SetResult(retrPtr->interp, "selection owner didn't respond",
  684. TCL_STATIC);
  685. retrPtr->result = TCL_ERROR;
  686.     } else {
  687. retrPtr->timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc,
  688.     (ClientData) retrPtr);
  689.     }
  690. }
  691. /*
  692.  *----------------------------------------------------------------------
  693.  *
  694.  * ConvertSelection --
  695.  *
  696.  * This procedure is invoked to handle SelectionRequest events.
  697.  * It responds to the requests, obeying the ICCCM protocols.
  698.  *
  699.  * Results:
  700.  * None.
  701.  *
  702.  * Side effects:
  703.  * Properties are created for the selection requestor, and a
  704.  * SelectionNotify event is generated for the selection
  705.  * requestor.  In the event of long selections, this procedure
  706.  * implements INCR-mode transfers, using the ICCCM protocol.
  707.  *
  708.  *----------------------------------------------------------------------
  709.  */
  710. static void
  711. ConvertSelection(winPtr, eventPtr)
  712.     TkWindow *winPtr; /* Window that received the
  713.  * conversion request;  may not be
  714.  * selection's current owner, be we
  715.  * set it to the current owner. */
  716.     register XSelectionRequestEvent *eventPtr;
  717. /* Event describing request. */
  718. {
  719.     XSelectionEvent reply; /* Used to notify requestor that
  720.  * selection info is ready. */
  721.     int multiple; /* Non-zero means a MULTIPLE request
  722.  * is being handled. */
  723.     IncrInfo incr; /* State of selection conversion. */
  724.     Atom singleInfo[2]; /* incr.multAtoms points here except
  725.  * for multiple conversions. */
  726.     int i;
  727.     Tk_ErrorHandler errorHandler;
  728.     TkSelectionInfo *infoPtr;
  729.     TkSelInProgress ip;
  730.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  731.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  732.     errorHandler = Tk_CreateErrorHandler(eventPtr->display, -1, -1,-1,
  733.     (int (*)()) NULL, (ClientData) NULL);
  734.     /*
  735.      * Initialize the reply event.
  736.      */
  737.     reply.type = SelectionNotify;
  738.     reply.serial = 0;
  739.     reply.send_event = True;
  740.     reply.display = eventPtr->display;
  741.     reply.requestor = eventPtr->requestor;
  742.     reply.selection = eventPtr->selection;
  743.     reply.target = eventPtr->target;
  744.     reply.property = eventPtr->property;
  745.     if (reply.property == None) {
  746. reply.property = reply.target;
  747.     }
  748.     reply.time = eventPtr->time;
  749.     for (infoPtr = winPtr->dispPtr->selectionInfoPtr; infoPtr != NULL;
  750.     infoPtr = infoPtr->nextPtr) {
  751. if (infoPtr->selection == eventPtr->selection)
  752.     break;
  753.     }
  754.     if (infoPtr == NULL) {
  755. goto refuse;
  756.     }
  757.     winPtr = (TkWindow *) infoPtr->owner;
  758.     /*
  759.      * Figure out which kind(s) of conversion to perform.  If handling
  760.      * a MULTIPLE conversion, then read the property describing which
  761.      * conversions to perform.
  762.      */
  763.     incr.winPtr = winPtr;
  764.     incr.selection = eventPtr->selection;
  765.     if (eventPtr->target != winPtr->dispPtr->multipleAtom) {
  766. multiple = 0;
  767. singleInfo[0] = reply.target;
  768. singleInfo[1] = reply.property;
  769. incr.multAtoms = singleInfo;
  770. incr.numConversions = 1;
  771.     } else {
  772. Atom type;
  773. int format, result;
  774. unsigned long bytesAfter;
  775. multiple = 1;
  776. incr.multAtoms = NULL;
  777. if (eventPtr->property == None) {
  778.     goto refuse;
  779. }
  780. result = XGetWindowProperty(eventPtr->display,
  781. eventPtr->requestor, eventPtr->property,
  782. 0, MAX_PROP_WORDS, False, XA_ATOM,
  783. &type, &format, &incr.numConversions, &bytesAfter,
  784. (unsigned char **) &incr.multAtoms);
  785. if ((result != Success) || (bytesAfter != 0) || (format != 32)
  786. || (type == None)) {
  787.     if (incr.multAtoms != NULL) {
  788. XFree((char *) incr.multAtoms);
  789.     }
  790.     goto refuse;
  791. }
  792. incr.numConversions /= 2; /* Two atoms per conversion. */
  793.     }
  794.     /*
  795.      * Loop through all of the requested conversions, and either return
  796.      * the entire converted selection, if it can be returned in a single
  797.      * bunch, or return INCR information only (the actual selection will
  798.      * be returned below).
  799.      */
  800.     incr.converts = (ConvertInfo *) ckalloc((unsigned)
  801.     (incr.numConversions*sizeof(ConvertInfo)));
  802.     incr.numIncrs = 0;
  803.     for (i = 0; i < incr.numConversions; i++) {
  804. Atom target, property, type;
  805. long buffer[TK_SEL_WORDS_AT_ONCE];
  806. register TkSelHandler *selPtr;
  807. int numItems, format;
  808. char *propPtr;
  809. target = incr.multAtoms[2*i];
  810. property = incr.multAtoms[2*i + 1];
  811. incr.converts[i].offset = -1;
  812. incr.converts[i].buffer[0] = '';
  813. for (selPtr = winPtr->selHandlerList; selPtr != NULL;
  814. selPtr = selPtr->nextPtr) {
  815.     if ((selPtr->target == target)
  816.     && (selPtr->selection == eventPtr->selection)) {
  817. break;
  818.     }
  819. }
  820. if (selPtr == NULL) {
  821.     /*
  822.      * Nobody seems to know about this kind of request.  If
  823.      * it's of a sort that we can handle without any help, do
  824.      * it.  Otherwise mark the request as an errror.
  825.      */
  826.     numItems = TkSelDefaultSelection(infoPtr, target, (char *) buffer,
  827.     TK_SEL_BYTES_AT_ONCE, &type);
  828.     if (numItems < 0) {
  829. incr.multAtoms[2*i + 1] = None;
  830. continue;
  831.     }
  832. } else {
  833.     ip.selPtr = selPtr;
  834.     ip.nextPtr = TkSelGetInProgress();
  835.     TkSelSetInProgress(&ip);
  836.     type = selPtr->format;
  837.     numItems = (*selPtr->proc)(selPtr->clientData, 0,
  838.     (char *) buffer, TK_SEL_BYTES_AT_ONCE);
  839.     TkSelSetInProgress(ip.nextPtr);
  840.     if ((ip.selPtr == NULL) || (numItems < 0)) {
  841. incr.multAtoms[2*i + 1] = None;
  842. continue;
  843.     }
  844.     if (numItems > TK_SEL_BYTES_AT_ONCE) {
  845. panic("selection handler returned too many bytes");
  846.     }
  847.     ((char *) buffer)[numItems] = '';
  848. }
  849. /*
  850.  * Got the selection;  store it back on the requestor's property.
  851.  */
  852. if (numItems == TK_SEL_BYTES_AT_ONCE) {
  853.     /*
  854.      * Selection is too big to send at once;  start an
  855.      * INCR-mode transfer.
  856.      */
  857.     incr.numIncrs++;
  858.     type = winPtr->dispPtr->incrAtom;
  859.     buffer[0] = SelectionSize(selPtr);
  860.     if (buffer[0] == 0) {
  861. incr.multAtoms[2*i + 1] = None;
  862. continue;
  863.     }
  864.     numItems = 1;
  865.     propPtr = (char *) buffer;
  866.     format = 32;
  867.     incr.converts[i].offset = 0;
  868.     XChangeProperty(reply.display, reply.requestor,
  869.     property, type, format, PropModeReplace,
  870.     (unsigned char *) propPtr, numItems);
  871. } else if (type == winPtr->dispPtr->utf8Atom) {
  872.     /*
  873.      * This matches selection requests of type UTF8_STRING,
  874.      * which allows us to pass our utf-8 information untouched.
  875.      */
  876.     XChangeProperty(reply.display, reply.requestor,
  877.     property, type, 8, PropModeReplace,
  878.     (unsigned char *) buffer, numItems);
  879. } else if ((type == XA_STRING)
  880. || (type == winPtr->dispPtr->compoundTextAtom)) {
  881.     Tcl_DString ds;
  882.     Tcl_Encoding encoding;
  883.     /*
  884.      * STRING is Latin-1, COMPOUND_TEXT is an iso2022 variant.
  885.      * We need to convert the selection text into these external
  886.      * forms before modifying the property.
  887.      */
  888.     if (type == XA_STRING) {
  889. encoding = Tcl_GetEncoding(NULL, "iso8859-1");
  890.     } else {
  891. encoding = Tcl_GetEncoding(NULL, "iso2022");
  892.     } 
  893.     Tcl_UtfToExternalDString(encoding, (char*)buffer, -1, &ds);
  894.     XChangeProperty(reply.display, reply.requestor,
  895.     property, type, 8, PropModeReplace,
  896.     (unsigned char *) Tcl_DStringValue(&ds),
  897.     Tcl_DStringLength(&ds));
  898.     if (encoding) {
  899. Tcl_FreeEncoding(encoding);
  900.     }
  901.     Tcl_DStringFree(&ds);
  902. } else {
  903.     propPtr = (char *) SelCvtToX((char *) buffer,
  904.     type, (Tk_Window) winPtr, &numItems);
  905.     if (propPtr == NULL) {
  906. goto refuse;
  907.     }
  908.     format = 32;
  909.     XChangeProperty(reply.display, reply.requestor,
  910.     property, type, format, PropModeReplace,
  911.     (unsigned char *) propPtr, numItems);
  912.     ckfree(propPtr);
  913. }
  914.     }
  915.     /*
  916.      * Send an event back to the requestor to indicate that the
  917.      * first stage of conversion is complete (everything is done
  918.      * except for long conversions that have to be done in INCR
  919.      * mode).
  920.      */
  921.     if (incr.numIncrs > 0) {
  922. XSelectInput(reply.display, reply.requestor, PropertyChangeMask);
  923. incr.timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
  924.     (ClientData) &incr);
  925. incr.idleTime = 0;
  926. incr.reqWindow = reply.requestor;
  927. incr.time = infoPtr->time;
  928. incr.nextPtr = tsdPtr->pendingIncrs;
  929. tsdPtr->pendingIncrs = &incr;
  930.     }
  931.     if (multiple) {
  932. XChangeProperty(reply.display, reply.requestor, reply.property,
  933. XA_ATOM, 32, PropModeReplace,
  934. (unsigned char *) incr.multAtoms,
  935. (int) incr.numConversions*2);
  936.     } else {
  937. /*
  938.  * Not a MULTIPLE request.  The first property in "multAtoms"
  939.  * got set to None if there was an error in conversion.
  940.  */
  941. reply.property = incr.multAtoms[1];
  942.     }
  943.     XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
  944.     Tk_DeleteErrorHandler(errorHandler);
  945.     /*
  946.      * Handle any remaining INCR-mode transfers.  This all happens
  947.      * in callbacks to TkSelPropProc, so just wait until the number
  948.      * of uncompleted INCR transfers drops to zero.
  949.      */
  950.     if (incr.numIncrs > 0) {
  951. IncrInfo *incrPtr2;
  952. while (incr.numIncrs > 0) {
  953.     Tcl_DoOneEvent(0);
  954. }
  955. Tcl_DeleteTimerHandler(incr.timeout);
  956. errorHandler = Tk_CreateErrorHandler(winPtr->display,
  957. -1, -1,-1, (int (*)()) NULL, (ClientData) NULL);
  958. XSelectInput(reply.display, reply.requestor, 0L);
  959. Tk_DeleteErrorHandler(errorHandler);
  960. if (tsdPtr->pendingIncrs == &incr) {
  961.     tsdPtr->pendingIncrs = incr.nextPtr;
  962. } else {
  963.     for (incrPtr2 = tsdPtr->pendingIncrs; incrPtr2 != NULL;
  964.     incrPtr2 = incrPtr2->nextPtr) {
  965. if (incrPtr2->nextPtr == &incr) {
  966.     incrPtr2->nextPtr = incr.nextPtr;
  967.     break;
  968. }
  969.     }
  970. }
  971.     }
  972.     /*
  973.      * All done.  Cleanup and return.
  974.      */
  975.     ckfree((char *) incr.converts);
  976.     if (multiple) {
  977. XFree((char *) incr.multAtoms);
  978.     }
  979.     return;
  980.     /*
  981.      * An error occurred.  Send back a refusal message.
  982.      */
  983.     refuse:
  984.     reply.property = None;
  985.     XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
  986.     Tk_DeleteErrorHandler(errorHandler);
  987.     return;
  988. }
  989. /*
  990.  *----------------------------------------------------------------------
  991.  *
  992.  * SelRcvIncrProc --
  993.  *
  994.  * This procedure handles the INCR protocol on the receiving
  995.  * side.  It is invoked in response to property changes on
  996.  * the requestor's window (which hopefully are because a new
  997.  * chunk of the selection arrived).
  998.  *
  999.  * Results:
  1000.  * None.
  1001.  *
  1002.  * Side effects:
  1003.  * If a new piece of selection has arrived, a procedure is
  1004.  * invoked to deal with that piece.  When the whole selection
  1005.  * is here, a flag is left for the higher-level procedure that
  1006.  * initiated the selection retrieval.
  1007.  *
  1008.  *----------------------------------------------------------------------
  1009.  */
  1010. static void
  1011. SelRcvIncrProc(clientData, eventPtr)
  1012.     ClientData clientData; /* Information about retrieval. */
  1013.     register XEvent *eventPtr; /* X PropertyChange event. */
  1014. {
  1015.     register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
  1016.     char *propInfo;
  1017.     Atom type;
  1018.     int format, result;
  1019.     unsigned long numItems, bytesAfter;
  1020.     Tcl_Interp *interp;
  1021.     if ((eventPtr->xproperty.atom != retrPtr->property)
  1022.     || (eventPtr->xproperty.state != PropertyNewValue)
  1023.     || (retrPtr->result != -1)) {
  1024. return;
  1025.     }
  1026.     propInfo = NULL;
  1027.     result = XGetWindowProperty(eventPtr->xproperty.display,
  1028.     eventPtr->xproperty.window, retrPtr->property, 0, MAX_PROP_WORDS,
  1029.     True, (Atom) AnyPropertyType, &type, &format, &numItems,
  1030.     &bytesAfter, (unsigned char **) &propInfo);
  1031.     if ((result != Success) || (type == None)) {
  1032. return;
  1033.     }
  1034.     if (bytesAfter != 0) {
  1035. Tcl_SetResult(retrPtr->interp, "selection property too large",
  1036. TCL_STATIC);
  1037. retrPtr->result = TCL_ERROR;
  1038. goto done;
  1039.     }
  1040.     if ((type == XA_STRING)
  1041.     || (type == retrPtr->winPtr->dispPtr->textAtom)
  1042.     || (type == retrPtr->winPtr->dispPtr->utf8Atom)
  1043.     || (type == retrPtr->winPtr->dispPtr->compoundTextAtom)) {
  1044. char *dst, *src;
  1045. int srcLen, dstLen, srcRead, dstWrote, soFar;
  1046. Tcl_Encoding encoding;
  1047. Tcl_DString *dstPtr, temp;
  1048. if (format != 8) {
  1049.     char buf[64 + TCL_INTEGER_SPACE];
  1050.     
  1051.     sprintf(buf, 
  1052.     "bad format for string selection: wanted "8", got "%d"",
  1053.     format);
  1054.     Tcl_SetResult(retrPtr->interp, buf, TCL_VOLATILE);
  1055.     retrPtr->result = TCL_ERROR;
  1056.     goto done;
  1057. }
  1058.         interp = retrPtr->interp;
  1059.         Tcl_Preserve((ClientData) interp);
  1060. if (type == retrPtr->winPtr->dispPtr->compoundTextAtom) {
  1061.     encoding = Tcl_GetEncoding(NULL, "iso2022");
  1062. } else if (type == retrPtr->winPtr->dispPtr->utf8Atom) {
  1063.     encoding = Tcl_GetEncoding(NULL, "utf-8");
  1064. } else {
  1065.     encoding = Tcl_GetEncoding(NULL, "iso8859-1");
  1066. }
  1067. /*
  1068.  * Check to see if there is any data left over from the previous
  1069.  * chunk.  If there is, copy the old data and the new data into
  1070.  * a new buffer.
  1071.  */
  1072. Tcl_DStringInit(&temp);
  1073. if (Tcl_DStringLength(&retrPtr->buf) > 0) {
  1074.     Tcl_DStringAppend(&temp, Tcl_DStringValue(&retrPtr->buf),
  1075.     Tcl_DStringLength(&retrPtr->buf));
  1076.     if (numItems > 0) {
  1077. Tcl_DStringAppend(&temp, propInfo, (int)numItems);
  1078.     }
  1079.     src = Tcl_DStringValue(&temp);
  1080.     srcLen = Tcl_DStringLength(&temp);
  1081. } else if (numItems == 0) {
  1082.     /*
  1083.      * There is no new data, so we're done.
  1084.      */
  1085.     retrPtr->result = TCL_OK;
  1086.     Tcl_Release((ClientData) interp);
  1087.     goto done;
  1088. } else {
  1089.     src = propInfo;
  1090.     srcLen = numItems;
  1091. }
  1092. /*
  1093.  * Set up the destination buffer so we can use as much space as
  1094.  * is available.
  1095.  */
  1096. dstPtr = &retrPtr->buf;
  1097. dst = Tcl_DStringValue(dstPtr);
  1098. dstLen = dstPtr->spaceAvl - 1;
  1099. /*
  1100.  * Now convert the data, growing the destination buffer as needed.
  1101.  */
  1102. while (1) {
  1103.     result = Tcl_ExternalToUtf(NULL, encoding, src, srcLen,
  1104.     retrPtr->encFlags, &retrPtr->encState,
  1105.     dst, dstLen, &srcRead, &dstWrote, NULL);
  1106.     soFar = dst + dstWrote - Tcl_DStringValue(dstPtr);
  1107.     retrPtr->encFlags &= ~TCL_ENCODING_START;
  1108.     src += srcRead;
  1109.     srcLen -= srcRead;
  1110.     if (result != TCL_CONVERT_NOSPACE) {
  1111. Tcl_DStringSetLength(dstPtr, soFar);
  1112. break;
  1113.     }
  1114.     if (Tcl_DStringLength(dstPtr) == 0) {
  1115. Tcl_DStringSetLength(dstPtr, dstLen);
  1116.     }
  1117.     Tcl_DStringSetLength(dstPtr, 2 * Tcl_DStringLength(dstPtr) + 1);
  1118.     dst = Tcl_DStringValue(dstPtr) + soFar;
  1119.     dstLen = Tcl_DStringLength(dstPtr) - soFar - 1;
  1120. }
  1121. Tcl_DStringSetLength(dstPtr, soFar);
  1122. result = (*retrPtr->proc)(retrPtr->clientData, interp,
  1123. Tcl_DStringValue(dstPtr));
  1124.         Tcl_Release((ClientData) interp);
  1125. /*
  1126.  * Copy any unused data into the destination buffer so we can
  1127.  * pick it up next time around.
  1128.  */
  1129. Tcl_DStringSetLength(dstPtr, 0);
  1130. Tcl_DStringAppend(dstPtr, src, srcLen);
  1131. Tcl_DStringFree(&temp);
  1132. if (encoding) {
  1133.     Tcl_FreeEncoding(encoding);
  1134. }
  1135. if (result != TCL_OK) {
  1136.     retrPtr->result = result;
  1137. }
  1138.     } else if (numItems == 0) {
  1139. retrPtr->result = TCL_OK;
  1140.     } else {
  1141. Tcl_DString ds;
  1142. if (format != 32) {
  1143.     char buf[64 + TCL_INTEGER_SPACE];
  1144.     sprintf(buf,
  1145.     "bad format for selection: wanted "32", got "%d"",
  1146.     format);
  1147.     Tcl_SetResult(retrPtr->interp, buf, TCL_VOLATILE);
  1148.     retrPtr->result = TCL_ERROR;
  1149.     goto done;
  1150. }
  1151. Tcl_DStringInit(&ds);
  1152. SelCvtFromX((long *) propInfo, (int) numItems, type,
  1153. (Tk_Window) retrPtr->winPtr, &ds);
  1154.         interp = retrPtr->interp;
  1155.         Tcl_Preserve((ClientData) interp);
  1156. result = (*retrPtr->proc)(retrPtr->clientData, interp,
  1157. Tcl_DStringValue(&ds));
  1158.         Tcl_Release((ClientData) interp);
  1159. Tcl_DStringFree(&ds);
  1160. if (result != TCL_OK) {
  1161.     retrPtr->result = result;
  1162. }
  1163.     }
  1164.   done:
  1165.     XFree(propInfo);
  1166.     retrPtr->idleTime = 0;
  1167. }
  1168. /*
  1169.  *----------------------------------------------------------------------
  1170.  *
  1171.  * SelectionSize --
  1172.  *
  1173.  * This procedure is called when the selection is too large to
  1174.  * send in a single buffer;  it computes the total length of
  1175.  * the selection in bytes.
  1176.  *
  1177.  * Results:
  1178.  * The return value is the number of bytes in the selection
  1179.  * given by selPtr.
  1180.  *
  1181.  * Side effects:
  1182.  * The selection is retrieved from its current owner (this is
  1183.  * the only way to compute its size).
  1184.  *
  1185.  *----------------------------------------------------------------------
  1186.  */
  1187. static int
  1188. SelectionSize(selPtr)
  1189.     TkSelHandler *selPtr; /* Information about how to retrieve
  1190.  * the selection whose size is wanted. */
  1191. {
  1192.     char buffer[TK_SEL_BYTES_AT_ONCE+1];
  1193.     int size, chunkSize;
  1194.     TkSelInProgress ip;
  1195.     size = TK_SEL_BYTES_AT_ONCE;
  1196.     ip.selPtr = selPtr;
  1197.     ip.nextPtr = TkSelGetInProgress();
  1198.     TkSelSetInProgress(&ip);
  1199.     do {
  1200. chunkSize = (*selPtr->proc)(selPtr->clientData, size,
  1201. (char *) buffer, TK_SEL_BYTES_AT_ONCE);
  1202. if (ip.selPtr == NULL) {
  1203.     size = 0;
  1204.     break;
  1205. }
  1206. size += chunkSize;
  1207.     } while (chunkSize == TK_SEL_BYTES_AT_ONCE);
  1208.     TkSelSetInProgress(ip.nextPtr);
  1209.     return size;
  1210. }
  1211. /*
  1212.  *----------------------------------------------------------------------
  1213.  *
  1214.  * IncrTimeoutProc --
  1215.  *
  1216.  * This procedure is invoked once a second while sending the
  1217.  * selection to a requestor in INCR mode.  After a while it
  1218.  * gives up and aborts the selection operation.
  1219.  *
  1220.  * Results:
  1221.  * None.
  1222.  *
  1223.  * Side effects:
  1224.  * A new timeout gets registered so that this procedure gets
  1225.  * called again in another second, unless too many seconds
  1226.  * have elapsed, in which case incrPtr is marked as "all done".
  1227.  *
  1228.  *----------------------------------------------------------------------
  1229.  */
  1230. static void
  1231. IncrTimeoutProc(clientData)
  1232.     ClientData clientData; /* Information about INCR-mode
  1233.  * selection retrieval for which
  1234.  * we are selection owner. */
  1235. {
  1236.     register IncrInfo *incrPtr = (IncrInfo *) clientData;
  1237.     incrPtr->idleTime++;
  1238.     if (incrPtr->idleTime >= 5) {
  1239. incrPtr->numIncrs = 0;
  1240.     } else {
  1241. incrPtr->timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
  1242. (ClientData) incrPtr);
  1243.     }
  1244. }
  1245. /*
  1246.  *----------------------------------------------------------------------
  1247.  *
  1248.  * SelCvtToX --
  1249.  *
  1250.  * Given a selection represented as a string (the normal Tcl form),
  1251.  * convert it to the ICCCM-mandated format for X, depending on
  1252.  * the type argument.  This procedure and SelCvtFromX are inverses.
  1253.  *
  1254.  * Results:
  1255.  * The return value is a malloc'ed buffer holding a value
  1256.  * equivalent to "string", but formatted as for "type".  It is
  1257.  * the caller's responsibility to free the string when done with
  1258.  * it.  The word at *numLongsPtr is filled in with the number of
  1259.  * 32-bit words returned in the result.
  1260.  *
  1261.  * Side effects:
  1262.  * None.
  1263.  *
  1264.  *----------------------------------------------------------------------
  1265.  */
  1266. static long *
  1267. SelCvtToX(string, type, tkwin, numLongsPtr)
  1268.     char *string; /* String representation of selection. */
  1269.     Atom type; /* Atom specifying the X format that is
  1270.  * desired for the selection.  Should not
  1271.  * be XA_STRING (if so, don't bother calling
  1272.  * this procedure at all). */
  1273.     Tk_Window tkwin; /* Window that governs atom conversion. */
  1274.     int *numLongsPtr; /* Number of 32-bit words contained in the
  1275.  * result. */
  1276. {
  1277.     const char **field;
  1278.     int numFields, i;
  1279.     long *propPtr;
  1280.     /*
  1281.      * The string is assumed to consist of fields separated by spaces. The
  1282.      * property gets generated by converting each field to an integer number,
  1283.      * in one of two ways:
  1284.      * 1. If type is XA_ATOM, convert each field to its corresponding atom.
  1285.      * 2. If type is anything else, convert each field from an ASCII number to
  1286.      *    a 32-bit binary number.
  1287.      */
  1288.     if (Tcl_SplitList(NULL, string, &numFields, &field) != TCL_OK) {
  1289. return NULL;
  1290.     }
  1291.     propPtr = (long *) ckalloc((unsigned) numFields*sizeof(long));
  1292.     /*
  1293.      * Convert the fields one-by-one.
  1294.      */
  1295.     for (i=0 ; i<numFields ; i++) {
  1296. if (type == XA_ATOM) {
  1297.     propPtr[i] = (long) Tk_InternAtom(tkwin, field[i]);
  1298. } else {
  1299.     char *dummy;
  1300.     /*
  1301.      * If this fails to parse a number, we just plunge on regardless
  1302.      * anyway.
  1303.      */
  1304.     propPtr[i] = strtol(field[i], &dummy, 0);
  1305. }
  1306.     }
  1307.     /*
  1308.      * Release the parsed list.
  1309.      */
  1310.     ckfree((char *) field);
  1311.     *numLongsPtr = i;
  1312.     return propPtr;
  1313. }
  1314. /*
  1315.  *----------------------------------------------------------------------
  1316.  *
  1317.  * SelCvtFromX --
  1318.  *
  1319.  * Given an X property value, formatted as a collection of 32-bit
  1320.  * values according to "type" and the ICCCM conventions, convert
  1321.  * the value to a string suitable for manipulation by Tcl.  This
  1322.  * procedure is the inverse of SelCvtToX.
  1323.  *
  1324.  * Results:
  1325.  * The return value (stored in a Tcl_DString) is the string equivalent of
  1326.  * "property". It is up to the caller to initialize and free the DString.
  1327.  *
  1328.  * Side effects:
  1329.  * None.
  1330.  *
  1331.  *----------------------------------------------------------------------
  1332.  */
  1333. static void
  1334. SelCvtFromX(propPtr, numValues, type, tkwin, dsPtr)
  1335.     register long *propPtr; /* Property value from X. */
  1336.     int numValues; /* Number of 32-bit values in property. */
  1337.     Atom type; /* Type of property  Should not be
  1338.  * XA_STRING (if so, don't bother calling
  1339.  * this procedure at all). */
  1340.     Tk_Window tkwin; /* Window to use for atom conversion. */
  1341.     Tcl_DString *dsPtr; /* Where to store the converted string. */
  1342. {
  1343.     /*
  1344.      * Convert each long in the property to a string value, which is either
  1345.      * the name of an atom (if type is XA_ATOM) or a hexadecimal string. We
  1346.      * build the list in a Tcl_DString because this is easier than trying to
  1347.      * get the quoting correct ourselves; this is tricky because atoms can
  1348.      * contain spaces in their names (encountered when the atoms are really
  1349.      * MIME types). [Bug 1353414]
  1350.      */
  1351.     for ( ; numValues > 0; propPtr++, numValues--) {
  1352. if (type == XA_ATOM) {
  1353.     Tcl_DStringAppendElement(dsPtr,
  1354.     Tk_GetAtomName(tkwin, (Atom) *propPtr));
  1355. } else {
  1356.     char buf[12];
  1357.     sprintf(buf, "0x%x", (unsigned int) *propPtr);
  1358.     Tcl_DStringAppendElement(dsPtr, buf);
  1359. }
  1360.     }
  1361. }