diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2019-09-28 14:41:16 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2019-09-28 14:41:16 (GMT) |
commit | 0f2071c7e4cfc72944fe2d0659ad2b31aaaaa4c7 (patch) | |
tree | da5d943c62e2a58276c3a9e503220d74cfd3cc2d /tk8.6/unix/tkUnixSelect.c | |
parent | b1b7f616cd9f9fdf6a8c0af88e4226d0a23daac9 (diff) | |
download | blt-0f2071c7e4cfc72944fe2d0659ad2b31aaaaa4c7.zip blt-0f2071c7e4cfc72944fe2d0659ad2b31aaaaa4c7.tar.gz blt-0f2071c7e4cfc72944fe2d0659ad2b31aaaaa4c7.tar.bz2 |
upgrade tcl/tk
Diffstat (limited to 'tk8.6/unix/tkUnixSelect.c')
-rw-r--r-- | tk8.6/unix/tkUnixSelect.c | 1552 |
1 files changed, 0 insertions, 1552 deletions
diff --git a/tk8.6/unix/tkUnixSelect.c b/tk8.6/unix/tkUnixSelect.c deleted file mode 100644 index 4819183..0000000 --- a/tk8.6/unix/tkUnixSelect.c +++ /dev/null @@ -1,1552 +0,0 @@ -/* - * tkUnixSelect.c -- - * - * This file contains X specific routines for manipulating selections. - * - * Copyright (c) 1995-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. - */ - -#include "tkInt.h" -#include "tkSelect.h" - -typedef struct ConvertInfo { - int offset; /* The starting byte offset into the selection - * for the next chunk; -1 means all data has - * been transferred for this conversion. -2 - * means only the final zero-length transfer - * still has to be done. Otherwise it is the - * offset of the next chunk of data to - * transfer. */ - Tcl_EncodingState state; /* The encoding state needed across chunks. */ - char buffer[4]; /* A buffer to hold part of a UTF character - * that is split across chunks.*/ -} ConvertInfo; - -/* - * When handling INCR-style selection retrievals, the selection owner uses the - * following data structure to communicate between the ConvertSelection - * function and TkSelPropProc. - */ - -typedef struct IncrInfo { - TkWindow *winPtr; /* Window that owns selection. */ - Atom selection; /* Selection that is being retrieved. */ - Atom *multAtoms; /* Information about conversions to perform: - * one or more pairs of (target, property). - * This either points to a retrieved property - * (for MULTIPLE retrievals) or to a static - * array. */ - unsigned long numConversions; - /* Number of entries in converts (same as # of - * pairs in multAtoms). */ - ConvertInfo *converts; /* One entry for each pair in multAtoms. This - * array is malloc-ed. */ - char **tempBufs; /* One pointer for each pair in multAtoms; - * each pointer is either NULL, or it points - * to a small bit of character data that was - * left over from the previous chunk. */ - Tcl_EncodingState *state; /* One state info per pair in multAtoms: State - * info for encoding conversions that span - * multiple buffers. */ - int *flags; /* One state flag per pair in multAtoms: - * Encoding flags, set to TCL_ENCODING_START - * at the beginning of an INCR transfer. */ - int numIncrs; /* Number of entries in converts that aren't - * -1 (i.e. # of INCR-mode transfers not yet - * completed). */ - Tcl_TimerToken timeout; /* Token for timer function. */ - int idleTime; /* Number of seconds since we heard anything - * from the selection requestor. */ - Window reqWindow; /* Requestor's window id. */ - Time time; /* Timestamp corresponding to selection at - * beginning of request; used to abort - * transfer if selection changes. */ - struct IncrInfo *nextPtr; /* Next in list of all INCR-style retrievals - * currently pending. */ -} IncrInfo; - -typedef struct { - IncrInfo *pendingIncrs; /* List of all incr structures currently - * active. */ -} ThreadSpecificData; -static Tcl_ThreadDataKey dataKey; - -/* - * Largest property that we'll accept when sending or receiving the selection: - */ - -#define MAX_PROP_WORDS 100000 - -static TkSelRetrievalInfo *pendingRetrievals = NULL; - /* List of all retrievals currently being - * waited for. */ - -/* - * Forward declarations for functions defined in this file: - */ - -static void ConvertSelection(TkWindow *winPtr, - XSelectionRequestEvent *eventPtr); -static void IncrTimeoutProc(ClientData clientData); -static void SelCvtFromX32(long *propPtr, int numValues, Atom type, - Tk_Window tkwin, Tcl_DString *dsPtr); -static void SelCvtFromX8(char *propPtr, int numValues, Atom type, - Tk_Window tkwin, Tcl_DString *dsPtr); -static long * SelCvtToX(char *string, Atom type, Tk_Window tkwin, - int *numLongsPtr); -static int SelectionSize(TkSelHandler *selPtr); -static void SelRcvIncrProc(ClientData clientData, - XEvent *eventPtr); -static void SelTimeoutProc(ClientData clientData); - -/* - *---------------------------------------------------------------------- - * - * TkSelGetSelection -- - * - * Retrieve the specified selection from another process. - * - * 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: - * None. - * - *---------------------------------------------------------------------- - */ - -int -TkSelGetSelection( - 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, /* Function to call to process the selection, - * once it has been retrieved. */ - ClientData clientData) /* Arbitrary value to pass to proc. */ -{ - TkSelRetrievalInfo retr; - TkWindow *winPtr = (TkWindow *) tkwin; - TkDisplay *dispPtr = winPtr->dispPtr; - - /* - * The selection is owned by some other process. To retrieve it, first - * record information about the retrieval in progress. Use an internal - * window as the requestor. - */ - - retr.interp = interp; - if (dispPtr->clipWindow == NULL) { - int result; - - result = TkClipInit(interp, dispPtr); - if (result != TCL_OK) { - return result; - } - } - retr.winPtr = (TkWindow *) dispPtr->clipWindow; - retr.selection = selection; - retr.property = selection; - retr.target = target; - retr.proc = proc; - retr.clientData = clientData; - retr.result = -1; - retr.idleTime = 0; - retr.encFlags = TCL_ENCODING_START; - retr.nextPtr = pendingRetrievals; - Tcl_DStringInit(&retr.buf); - pendingRetrievals = &retr; - - /* - * Delete the property to indicate that no parameters are supplied for - * the conversion request. - */ - - XDeleteProperty(winPtr->display, retr.winPtr->window, retr.property); - - /* - * Initiate the request for the selection. Note: can't use TkCurrentTime - * for the time. If we do, and this application hasn't received any X - * events in a long time, the current time will be way in the past and - * could even predate the time when the selection was made; if this - * happens, the request will be rejected. - */ - - XConvertSelection(winPtr->display, retr.selection, retr.target, - retr.property, retr.winPtr->window, CurrentTime); - - /* - * Enter a loop processing X events until the selection has been retrieved - * and processed. If no response is received within a few seconds, then - * timeout. - */ - - retr.timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc, - &retr); - while (retr.result == -1) { - Tcl_DoOneEvent(0); - } - Tcl_DeleteTimerHandler(retr.timeout); - - /* - * Unregister the information about the selection retrieval in progress. - */ - - if (pendingRetrievals == &retr) { - pendingRetrievals = retr.nextPtr; - } else { - TkSelRetrievalInfo *retrPtr; - - for (retrPtr = pendingRetrievals; retrPtr != NULL; - retrPtr = retrPtr->nextPtr) { - if (retrPtr->nextPtr == &retr) { - retrPtr->nextPtr = retr.nextPtr; - break; - } - } - } - Tcl_DStringFree(&retr.buf); - return retr.result; -} - -/* - *---------------------------------------------------------------------- - * - * TkSelPropProc -- - * - * This function is invoked when property-change events occur on windows - * not known to the toolkit. Its function is to implement the sending - * side of the INCR selection retrieval protocol when the selection - * requestor deletes the property containing a part of the selection. - * - * Results: - * None. - * - * Side effects: - * If the property that is receiving the selection was just deleted, then - * a new piece of the selection is fetched and placed in the property, - * until eventually there's no more selection to fetch. - * - *---------------------------------------------------------------------- - */ - -void -TkSelPropProc( - register XEvent *eventPtr) /* X PropertyChange event. */ -{ - register IncrInfo *incrPtr; - register TkSelHandler *selPtr; - int length, numItems; - unsigned long i; - Atom target, formatType; - long buffer[TK_SEL_WORDS_AT_ONCE]; - TkDisplay *dispPtr = TkGetDisplay(eventPtr->xany.display); - Tk_ErrorHandler errorHandler; - ThreadSpecificData *tsdPtr = - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - - /* - * See if this event announces the deletion of a property being used for - * an INCR transfer. If so, then add the next chunk of data to the - * property. - */ - - if (eventPtr->xproperty.state != PropertyDelete) { - return; - } - for (incrPtr = tsdPtr->pendingIncrs; incrPtr != NULL; - incrPtr = incrPtr->nextPtr) { - if (incrPtr->reqWindow != eventPtr->xproperty.window) { - continue; - } - - /* - * For each conversion that has been requested, handle any chunks that - * haven't been transmitted yet. - */ - - for (i = 0; i < incrPtr->numConversions; i++) { - if ((eventPtr->xproperty.atom != incrPtr->multAtoms[2*i + 1]) - || (incrPtr->converts[i].offset == -1)) { - continue; - } - target = incrPtr->multAtoms[2*i]; - incrPtr->idleTime = 0; - - /* - * Look for a matching selection handler. - */ - - for (selPtr = incrPtr->winPtr->selHandlerList; ; - selPtr = selPtr->nextPtr) { - if (selPtr == NULL) { - /* - * No handlers match, so mark the conversion as done. - */ - - incrPtr->multAtoms[2*i + 1] = None; - incrPtr->converts[i].offset = -1; - incrPtr->numIncrs --; - return; - } - if ((selPtr->target == target) - && (selPtr->selection == incrPtr->selection)) { - break; - } - } - - /* - * We found a handler, so get the next chunk from it. - */ - - formatType = selPtr->format; - if (incrPtr->converts[i].offset == -2) { - /* - * We already got the last chunk, so send a null chunk to - * indicate that we are finished. - */ - - numItems = 0; - length = 0; - } else { - TkSelInProgress ip; - - ip.selPtr = selPtr; - ip.nextPtr = TkSelGetInProgress(); - TkSelSetInProgress(&ip); - - /* - * Copy any bytes left over from a partial character at the - * end of the previous chunk into the beginning of the buffer. - * Pass the rest of the buffer space into the selection - * handler. - */ - - length = strlen(incrPtr->converts[i].buffer); - strcpy((char *)buffer, incrPtr->converts[i].buffer); - - numItems = selPtr->proc(selPtr->clientData, - incrPtr->converts[i].offset, - ((char *) buffer) + length, - TK_SEL_BYTES_AT_ONCE - length); - TkSelSetInProgress(ip.nextPtr); - if (ip.selPtr == NULL) { - /* - * The selection handler deleted itself. - */ - - return; - } - if (numItems < 0) { - numItems = 0; - } - numItems += length; - if (numItems > TK_SEL_BYTES_AT_ONCE) { - Tcl_Panic("selection handler returned too many bytes"); - } - } - ((char *) buffer)[numItems] = 0; - - errorHandler = Tk_CreateErrorHandler(eventPtr->xproperty.display, - -1, -1, -1, (int (*)()) NULL, NULL); - - /* - * Encode the data using the proper format for each type. - */ - - if ((formatType == XA_STRING) - || (dispPtr && formatType==dispPtr->utf8Atom) - || (dispPtr && formatType==dispPtr->compoundTextAtom)) { - Tcl_DString ds; - int encodingCvtFlags; - int srcLen, dstLen, result, srcRead, dstWrote, soFar; - char *src, *dst; - Tcl_Encoding encoding; - - /* - * Set up the encoding state based on the format and whether - * this is the first and/or last chunk. - */ - - encodingCvtFlags = 0; - if (incrPtr->converts[i].offset == 0) { - encodingCvtFlags |= TCL_ENCODING_START; - } - if (numItems < TK_SEL_BYTES_AT_ONCE) { - encodingCvtFlags |= TCL_ENCODING_END; - } - if (formatType == XA_STRING) { - encoding = Tcl_GetEncoding(NULL, "iso8859-1"); - } else if (dispPtr && formatType==dispPtr->utf8Atom) { - encoding = Tcl_GetEncoding(NULL, "utf-8"); - } else { - encoding = Tcl_GetEncoding(NULL, "iso2022"); - } - - /* - * Now convert the data. - */ - - src = (char *)buffer; - srcLen = numItems; - Tcl_DStringInit(&ds); - dst = Tcl_DStringValue(&ds); - dstLen = ds.spaceAvl - 1; - - - /* - * Now convert the data, growing the destination buffer as - * needed. - */ - - while (1) { - result = Tcl_UtfToExternal(NULL, encoding, src, srcLen, - encodingCvtFlags, &incrPtr->converts[i].state, - dst, dstLen, &srcRead, &dstWrote, NULL); - soFar = dst + dstWrote - Tcl_DStringValue(&ds); - encodingCvtFlags &= ~TCL_ENCODING_START; - src += srcRead; - srcLen -= srcRead; - if (result != TCL_CONVERT_NOSPACE) { - Tcl_DStringSetLength(&ds, soFar); - break; - } - if (Tcl_DStringLength(&ds) == 0) { - Tcl_DStringSetLength(&ds, dstLen); - } - Tcl_DStringSetLength(&ds, 2 * Tcl_DStringLength(&ds) + 1); - dst = Tcl_DStringValue(&ds) + soFar; - dstLen = Tcl_DStringLength(&ds) - soFar - 1; - } - Tcl_DStringSetLength(&ds, soFar); - - if (encoding) { - Tcl_FreeEncoding(encoding); - } - - /* - * Set the property to the encoded string value. - */ - - XChangeProperty(eventPtr->xproperty.display, - eventPtr->xproperty.window, eventPtr->xproperty.atom, - formatType, 8, PropModeReplace, - (unsigned char *) Tcl_DStringValue(&ds), - Tcl_DStringLength(&ds)); - - /* - * Preserve any left-over bytes. - */ - - if (srcLen > 3) { - Tcl_Panic("selection conversion left too many bytes unconverted"); - } - memcpy(incrPtr->converts[i].buffer, src, (size_t) srcLen+1); - Tcl_DStringFree(&ds); - } else { - /* - * Set the property to the encoded string value. - */ - - char *propPtr = (char *) SelCvtToX((char *) buffer, - formatType, (Tk_Window) incrPtr->winPtr, &numItems); - - if (propPtr == NULL) { - numItems = 0; - } - XChangeProperty(eventPtr->xproperty.display, - eventPtr->xproperty.window, eventPtr->xproperty.atom, - formatType, 32, PropModeReplace, - (unsigned char *) propPtr, numItems); - if (propPtr != NULL) { - ckfree(propPtr); - } - } - Tk_DeleteErrorHandler(errorHandler); - - /* - * Compute the next offset value. If this was the last chunk, then - * set the offset to -2. If this was an empty chunk, then set the - * offset to -1 to indicate we are done. - */ - - if (numItems < TK_SEL_BYTES_AT_ONCE) { - if (numItems <= 0) { - incrPtr->converts[i].offset = -1; - incrPtr->numIncrs--; - } else { - incrPtr->converts[i].offset = -2; - } - } else { - /* - * Advance over the selection data that was consumed this - * time. - */ - - incrPtr->converts[i].offset += numItems - length; - } - return; - } - } -} - -/* - *-------------------------------------------------------------- - * - * TkSelEventProc -- - * - * This function is invoked whenever a selection-related event occurs. - * It does the lion's share of the work in implementing the selection - * protocol. - * - * Results: - * None. - * - * Side effects: - * Lots: depends on the type of event. - * - *-------------------------------------------------------------- - */ - -void -TkSelEventProc( - Tk_Window tkwin, /* Window for which event was targeted. */ - register XEvent *eventPtr) /* X event: either SelectionClear, - * SelectionRequest, or SelectionNotify. */ -{ - register TkWindow *winPtr = (TkWindow *) tkwin; - TkDisplay *dispPtr = winPtr->dispPtr; - Tcl_Interp *interp; - - /* - * Case #1: SelectionClear events. - */ - - if (eventPtr->type == SelectionClear) { - TkSelClearSelection(tkwin, eventPtr); - } - - /* - * Case #2: SelectionNotify events. Call the relevant function to handle - * the incoming selection. - */ - - if (eventPtr->type == SelectionNotify) { - register TkSelRetrievalInfo *retrPtr; - char *propInfo, **propInfoPtr = &propInfo; - Atom type; - int format, result; - unsigned long numItems, bytesAfter; - Tcl_DString ds; - - for (retrPtr = pendingRetrievals; ; retrPtr = retrPtr->nextPtr) { - if (retrPtr == NULL) { - return; - } - if ((retrPtr->winPtr == winPtr) - && (retrPtr->selection == eventPtr->xselection.selection) - && (retrPtr->target == eventPtr->xselection.target) - && (retrPtr->result == -1)) { - if (retrPtr->property == eventPtr->xselection.property) { - break; - } - if (eventPtr->xselection.property == None) { - Tcl_SetObjResult(retrPtr->interp, Tcl_ObjPrintf( - "%s selection doesn't exist or form \"%s\" not defined", - Tk_GetAtomName(tkwin, retrPtr->selection), - Tk_GetAtomName(tkwin, retrPtr->target))); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", - "NONE", NULL); - retrPtr->result = TCL_ERROR; - return; - } - } - } - - propInfo = NULL; - result = XGetWindowProperty(eventPtr->xselection.display, - eventPtr->xselection.requestor, retrPtr->property, - 0, MAX_PROP_WORDS, False, (Atom) AnyPropertyType, - &type, &format, &numItems, &bytesAfter, - (unsigned char **) propInfoPtr); - if ((result != Success) || (type == None)) { - return; - } - if (bytesAfter != 0) { - Tcl_SetObjResult(retrPtr->interp, Tcl_NewStringObj( - "selection property too large", -1)); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", "SIZE",NULL); - retrPtr->result = TCL_ERROR; - XFree(propInfo); - return; - } - if ((type == XA_STRING) || (type == dispPtr->textAtom) - || (type == dispPtr->compoundTextAtom)) { - Tcl_Encoding encoding; - - if (format != 8) { - Tcl_SetObjResult(retrPtr->interp, Tcl_ObjPrintf( - "bad format for string selection: wanted \"8\", got \"%d\"", - format)); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", "FORMAT", - NULL); - retrPtr->result = TCL_ERROR; - return; - } - interp = retrPtr->interp; - Tcl_Preserve(interp); - - /* - * Convert the X selection data into UTF before passing it to the - * selection callback. Note that the COMPOUND_TEXT uses a modified - * iso2022 encoding, not the current system encoding. For now - * we'll just blindly apply the iso2022 encoding. This is probably - * wrong, but it's a placeholder until we figure out what we're - * really supposed to do. For STRING, we need to use Latin-1 - * instead. Again, it's not really the full iso8859-1 space, but - * this is close enough. - */ - - if (type == dispPtr->compoundTextAtom) { - encoding = Tcl_GetEncoding(NULL, "iso2022"); - } else { - encoding = Tcl_GetEncoding(NULL, "iso8859-1"); - } - Tcl_ExternalToUtfDString(encoding, propInfo, (int)numItems, &ds); - if (encoding) { - Tcl_FreeEncoding(encoding); - } - - retrPtr->result = retrPtr->proc(retrPtr->clientData, interp, - Tcl_DStringValue(&ds)); - Tcl_DStringFree(&ds); - Tcl_Release(interp); - } else if (type == dispPtr->utf8Atom) { - /* - * The X selection data is in UTF-8 format already. We can't - * guarantee that propInfo is NULL-terminated, so we might have to - * copy the string. - */ - - char *propData = propInfo; - - if (format != 8) { - Tcl_SetObjResult(retrPtr->interp, Tcl_ObjPrintf( - "bad format for string selection: wanted \"8\", got \"%d\"", - format)); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", "FORMAT", - NULL); - retrPtr->result = TCL_ERROR; - return; - } - - if (propInfo[numItems] != '\0') { - propData = ckalloc(numItems + 1); - strcpy(propData, propInfo); - propData[numItems] = '\0'; - } - retrPtr->result = retrPtr->proc(retrPtr->clientData, - retrPtr->interp, propData); - if (propData != propInfo) { - ckfree(propData); - } - - } else if (type == dispPtr->incrAtom) { - /* - * It's a !?#@!?!! INCR-style reception. Arrange to receive the - * selection in pieces, using the ICCCM protocol, then hang around - * until either the selection is all here or a timeout occurs. - */ - - retrPtr->idleTime = 0; - Tk_CreateEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc, - retrPtr); - XDeleteProperty(Tk_Display(tkwin), Tk_WindowId(tkwin), - retrPtr->property); - while (retrPtr->result == -1) { - Tcl_DoOneEvent(0); - } - Tk_DeleteEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc, - retrPtr); - } else { - Tcl_DString ds; - - if (format != 32 && format != 8) { - Tcl_SetObjResult(retrPtr->interp, Tcl_ObjPrintf( - "bad format for selection: wanted \"32\" or " - "\"8\", got \"%d\"", format)); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", "FORMAT", - NULL); - retrPtr->result = TCL_ERROR; - return; - } - Tcl_DStringInit(&ds); - if (format == 32) { - SelCvtFromX32((long *) propInfo, (int) numItems, type, - (Tk_Window) winPtr, &ds); - } else { - SelCvtFromX8((char *) propInfo, (int) numItems, type, - (Tk_Window) winPtr, &ds); - } - interp = retrPtr->interp; - Tcl_Preserve(interp); - retrPtr->result = retrPtr->proc(retrPtr->clientData, - interp, Tcl_DStringValue(&ds)); - Tcl_Release(interp); - Tcl_DStringFree(&ds); - } - XFree(propInfo); - return; - } - - /* - * Case #3: SelectionRequest events. Call ConvertSelection to do the dirty - * work. - */ - - if (eventPtr->type == SelectionRequest) { - ConvertSelection(winPtr, &eventPtr->xselectionrequest); - return; - } -} - -/* - *---------------------------------------------------------------------- - * - * SelTimeoutProc -- - * - * This function is invoked once every second while waiting for the - * selection to be returned. After a while it gives up and aborts the - * selection retrieval. - * - * Results: - * None. - * - * Side effects: - * A new timer callback is created to call us again in another second, - * unless time has expired, in which case an error is recorded for the - * retrieval. - * - *---------------------------------------------------------------------- - */ - -static void -SelTimeoutProc( - ClientData clientData) /* Information about retrieval in progress. */ -{ - register TkSelRetrievalInfo *retrPtr = clientData; - - /* - * Make sure that the retrieval is still in progress. Then see how long - * it's been since any sort of response was received from the other side. - */ - - if (retrPtr->result != -1) { - return; - } - retrPtr->idleTime++; - if (retrPtr->idleTime >= 5) { - /* - * Use a careful function to store the error message, because the - * result could already be partially filled in with a partial - * selection return. - */ - - Tcl_SetObjResult(retrPtr->interp, Tcl_NewStringObj( - "selection owner didn't respond", -1)); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", "IGNORED", NULL); - retrPtr->result = TCL_ERROR; - } else { - retrPtr->timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc, - (ClientData) retrPtr); - } -} - -/* - *---------------------------------------------------------------------- - * - * ConvertSelection -- - * - * This function is invoked to handle SelectionRequest events. It - * responds to the requests, obeying the ICCCM protocols. - * - * Results: - * None. - * - * Side effects: - * Properties are created for the selection requestor, and a - * SelectionNotify event is generated for the selection requestor. In the - * event of long selections, this function implements INCR-mode - * transfers, using the ICCCM protocol. - * - *---------------------------------------------------------------------- - */ - -static void -ConvertSelection( - TkWindow *winPtr, /* Window that received the conversion - * request; may not be selection's current - * owner, be we set it to the current - * owner. */ - register XSelectionRequestEvent *eventPtr) - /* Event describing request. */ -{ - union { - XSelectionEvent xsel; - XEvent ev; - } reply; /* Used to notify requestor that selection - * info is ready. */ - int multiple; /* Non-zero means a MULTIPLE request is being - * handled. */ - IncrInfo incr; /* State of selection conversion. */ - Atom singleInfo[2]; /* incr.multAtoms points here except for - * multiple conversions. */ - unsigned long i; - Tk_ErrorHandler errorHandler; - TkSelectionInfo *infoPtr; - TkSelInProgress ip; - ThreadSpecificData *tsdPtr = - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - - errorHandler = Tk_CreateErrorHandler(eventPtr->display, -1, -1,-1, - (int (*)()) NULL, NULL); - - /* - * Initialize the reply event. - */ - - reply.xsel.type = SelectionNotify; - reply.xsel.serial = 0; - reply.xsel.send_event = True; - reply.xsel.display = eventPtr->display; - reply.xsel.requestor = eventPtr->requestor; - reply.xsel.selection = eventPtr->selection; - reply.xsel.target = eventPtr->target; - reply.xsel.property = eventPtr->property; - if (reply.xsel.property == None) { - reply.xsel.property = reply.xsel.target; - } - reply.xsel.time = eventPtr->time; - - for (infoPtr = winPtr->dispPtr->selectionInfoPtr; infoPtr != NULL; - infoPtr = infoPtr->nextPtr) { - if (infoPtr->selection == eventPtr->selection) { - break; - } - } - if (infoPtr == NULL) { - goto refuse; - } - winPtr = (TkWindow *) infoPtr->owner; - - /* - * Figure out which kind(s) of conversion to perform. If handling a - * MULTIPLE conversion, then read the property describing which - * conversions to perform. - */ - - incr.winPtr = winPtr; - incr.selection = eventPtr->selection; - if (eventPtr->target != winPtr->dispPtr->multipleAtom) { - multiple = 0; - singleInfo[0] = reply.xsel.target; - singleInfo[1] = reply.xsel.property; - incr.multAtoms = singleInfo; - incr.numConversions = 1; - } else { - Atom type, **multAtomsPtr = &incr.multAtoms; - int format, result; - unsigned long bytesAfter; - - multiple = 1; - incr.multAtoms = NULL; - if (eventPtr->property == None) { - goto refuse; - } - result = XGetWindowProperty(eventPtr->display, eventPtr->requestor, - eventPtr->property, 0, MAX_PROP_WORDS, False, - winPtr->dispPtr->atomPairAtom, &type, &format, - &incr.numConversions, &bytesAfter, - (unsigned char **) multAtomsPtr); - if ((result != Success) || (bytesAfter != 0) || (format != 32) - || (type == None)) { - if (incr.multAtoms != NULL) { - XFree((char *) incr.multAtoms); - } - goto refuse; - } - incr.numConversions /= 2; /* Two atoms per conversion. */ - } - - /* - * Loop through all of the requested conversions, and either return the - * entire converted selection, if it can be returned in a single bunch, or - * return INCR information only (the actual selection will be returned - * below). - */ - - incr.converts = ckalloc(incr.numConversions * sizeof(ConvertInfo)); - incr.numIncrs = 0; - for (i = 0; i < incr.numConversions; i++) { - Atom target, property, type; - long buffer[TK_SEL_WORDS_AT_ONCE]; - register TkSelHandler *selPtr; - int numItems, format; - char *propPtr; - - target = incr.multAtoms[2*i]; - property = incr.multAtoms[2*i + 1]; - incr.converts[i].offset = -1; - incr.converts[i].buffer[0] = '\0'; - - for (selPtr = winPtr->selHandlerList; selPtr != NULL; - selPtr = selPtr->nextPtr) { - if ((selPtr->target == target) - && (selPtr->selection == eventPtr->selection)) { - break; - } - } - - if (selPtr == NULL) { - /* - * Nobody seems to know about this kind of request. If it's of a - * sort that we can handle without any help, do it. Otherwise mark - * the request as an errror. - */ - - numItems = TkSelDefaultSelection(infoPtr, target, (char *) buffer, - TK_SEL_BYTES_AT_ONCE, &type); - if (numItems < 0) { - incr.multAtoms[2*i + 1] = None; - continue; - } - } else { - ip.selPtr = selPtr; - ip.nextPtr = TkSelGetInProgress(); - TkSelSetInProgress(&ip); - type = selPtr->format; - numItems = selPtr->proc(selPtr->clientData, 0, (char *) buffer, - TK_SEL_BYTES_AT_ONCE); - TkSelSetInProgress(ip.nextPtr); - if ((ip.selPtr == NULL) || (numItems < 0)) { - incr.multAtoms[2*i + 1] = None; - continue; - } - if (numItems > TK_SEL_BYTES_AT_ONCE) { - Tcl_Panic("selection handler returned too many bytes"); - } - ((char *) buffer)[numItems] = '\0'; - } - - /* - * Got the selection; store it back on the requestor's property. - */ - - if (numItems == TK_SEL_BYTES_AT_ONCE) { - /* - * Selection is too big to send at once; start an INCR-mode - * transfer. - */ - - incr.numIncrs++; - type = winPtr->dispPtr->incrAtom; - buffer[0] = SelectionSize(selPtr); - if (buffer[0] == 0) { - incr.multAtoms[2*i + 1] = None; - continue; - } - numItems = 1; - propPtr = (char *) buffer; - format = 32; - incr.converts[i].offset = 0; - XChangeProperty(reply.xsel.display, reply.xsel.requestor, - property, type, format, PropModeReplace, - (unsigned char *) propPtr, numItems); - } else if (type == winPtr->dispPtr->utf8Atom) { - /* - * This matches selection requests of type UTF8_STRING, which - * allows us to pass our utf-8 information untouched. - */ - - XChangeProperty(reply.xsel.display, reply.xsel.requestor, - property, type, 8, PropModeReplace, - (unsigned char *) buffer, numItems); - } else if ((type == XA_STRING) - || (type == winPtr->dispPtr->compoundTextAtom)) { - Tcl_DString ds; - Tcl_Encoding encoding; - - /* - * STRING is Latin-1, COMPOUND_TEXT is an iso2022 variant. We need - * to convert the selection text into these external forms before - * modifying the property. - */ - - if (type == XA_STRING) { - encoding = Tcl_GetEncoding(NULL, "iso8859-1"); - } else { - encoding = Tcl_GetEncoding(NULL, "iso2022"); - } - Tcl_UtfToExternalDString(encoding, (char *) buffer, -1, &ds); - XChangeProperty(reply.xsel.display, reply.xsel.requestor, - property, type, 8, PropModeReplace, - (unsigned char *) Tcl_DStringValue(&ds), - Tcl_DStringLength(&ds)); - if (encoding) { - Tcl_FreeEncoding(encoding); - } - Tcl_DStringFree(&ds); - } else { - propPtr = (char *) SelCvtToX((char *) buffer, - type, (Tk_Window) winPtr, &numItems); - if (propPtr == NULL) { - goto refuse; - } - format = 32; - XChangeProperty(reply.xsel.display, reply.xsel.requestor, - property, type, format, PropModeReplace, - (unsigned char *) propPtr, numItems); - ckfree(propPtr); - } - } - - /* - * Send an event back to the requestor to indicate that the first stage of - * conversion is complete (everything is done except for long conversions - * that have to be done in INCR mode). - */ - - if (incr.numIncrs > 0) { - XSelectInput(reply.xsel.display, reply.xsel.requestor, - PropertyChangeMask); - incr.timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc, &incr); - incr.idleTime = 0; - incr.reqWindow = reply.xsel.requestor; - incr.time = infoPtr->time; - incr.nextPtr = tsdPtr->pendingIncrs; - tsdPtr->pendingIncrs = &incr; - } - if (multiple) { - XChangeProperty(reply.xsel.display, reply.xsel.requestor, - reply.xsel.property, winPtr->dispPtr->atomPairAtom, - 32, PropModeReplace, (unsigned char *) incr.multAtoms, - (int) incr.numConversions*2); - } else { - /* - * Not a MULTIPLE request. The first property in "multAtoms" got set - * to None if there was an error in conversion. - */ - - reply.xsel.property = incr.multAtoms[1]; - } - XSendEvent(reply.xsel.display, reply.xsel.requestor, False, 0, &reply.ev); - Tk_DeleteErrorHandler(errorHandler); - - /* - * Handle any remaining INCR-mode transfers. This all happens in callbacks - * to TkSelPropProc, so just wait until the number of uncompleted INCR - * transfers drops to zero. - */ - - if (incr.numIncrs > 0) { - IncrInfo *incrPtr2; - - while (incr.numIncrs > 0) { - Tcl_DoOneEvent(0); - } - Tcl_DeleteTimerHandler(incr.timeout); - errorHandler = Tk_CreateErrorHandler(winPtr->display, - -1, -1, -1, (int (*)()) NULL, NULL); - XSelectInput(reply.xsel.display, reply.xsel.requestor, 0L); - Tk_DeleteErrorHandler(errorHandler); - if (tsdPtr->pendingIncrs == &incr) { - tsdPtr->pendingIncrs = incr.nextPtr; - } else { - for (incrPtr2 = tsdPtr->pendingIncrs; incrPtr2 != NULL; - incrPtr2 = incrPtr2->nextPtr) { - if (incrPtr2->nextPtr == &incr) { - incrPtr2->nextPtr = incr.nextPtr; - break; - } - } - } - } - - /* - * All done. Cleanup and return. - */ - - ckfree(incr.converts); - if (multiple) { - XFree((char *) incr.multAtoms); - } - return; - - /* - * An error occurred. Send back a refusal message. - */ - - refuse: - reply.xsel.property = None; - XSendEvent(reply.xsel.display, reply.xsel.requestor, False, 0, &reply.ev); - Tk_DeleteErrorHandler(errorHandler); - return; -} - -/* - *---------------------------------------------------------------------- - * - * SelRcvIncrProc -- - * - * This function handles the INCR protocol on the receiving side. It is - * invoked in response to property changes on the requestor's window - * (which hopefully are because a new chunk of the selection arrived). - * - * Results: - * None. - * - * Side effects: - * If a new piece of selection has arrived, a function is invoked to deal - * with that piece. When the whole selection is here, a flag is left for - * the higher-level function that initiated the selection retrieval. - * - *---------------------------------------------------------------------- - */ - -static void -SelRcvIncrProc( - ClientData clientData, /* Information about retrieval. */ - register XEvent *eventPtr) /* X PropertyChange event. */ -{ - register TkSelRetrievalInfo *retrPtr = clientData; - char *propInfo, **propInfoPtr = &propInfo; - Atom type; - int format, result; - unsigned long numItems, bytesAfter; - Tcl_Interp *interp; - - if ((eventPtr->xproperty.atom != retrPtr->property) - || (eventPtr->xproperty.state != PropertyNewValue) - || (retrPtr->result != -1)) { - return; - } - propInfo = NULL; - result = XGetWindowProperty(eventPtr->xproperty.display, - eventPtr->xproperty.window, retrPtr->property, 0, MAX_PROP_WORDS, - True, (Atom) AnyPropertyType, &type, &format, &numItems, - &bytesAfter, (unsigned char **) propInfoPtr); - if ((result != Success) || (type == None)) { - return; - } - if (bytesAfter != 0) { - Tcl_SetObjResult(retrPtr->interp, Tcl_NewStringObj( - "selection property too large", -1)); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", "SIZE", NULL); - retrPtr->result = TCL_ERROR; - goto done; - } - if ((type == XA_STRING) - || (type == retrPtr->winPtr->dispPtr->textAtom) - || (type == retrPtr->winPtr->dispPtr->utf8Atom) - || (type == retrPtr->winPtr->dispPtr->compoundTextAtom)) { - char *dst, *src; - int srcLen, dstLen, srcRead, dstWrote, soFar; - Tcl_Encoding encoding; - Tcl_DString *dstPtr, temp; - - if (format != 8) { - Tcl_SetObjResult(retrPtr->interp, Tcl_ObjPrintf( - "bad format for string selection: wanted \"8\", got \"%d\"", - format)); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", "FORMAT", - NULL); - retrPtr->result = TCL_ERROR; - goto done; - } - interp = retrPtr->interp; - Tcl_Preserve(interp); - - if (type == retrPtr->winPtr->dispPtr->compoundTextAtom) { - encoding = Tcl_GetEncoding(NULL, "iso2022"); - } else if (type == retrPtr->winPtr->dispPtr->utf8Atom) { - encoding = Tcl_GetEncoding(NULL, "utf-8"); - } else { - encoding = Tcl_GetEncoding(NULL, "iso8859-1"); - } - - /* - * Check to see if there is any data left over from the previous - * chunk. If there is, copy the old data and the new data into a new - * buffer. - */ - - Tcl_DStringInit(&temp); - if (Tcl_DStringLength(&retrPtr->buf) > 0) { - Tcl_DStringAppend(&temp, Tcl_DStringValue(&retrPtr->buf), - Tcl_DStringLength(&retrPtr->buf)); - if (numItems > 0) { - Tcl_DStringAppend(&temp, propInfo, (int)numItems); - } - src = Tcl_DStringValue(&temp); - srcLen = Tcl_DStringLength(&temp); - } else if (numItems == 0) { - /* - * There is no new data, so we're done. - */ - - retrPtr->result = TCL_OK; - Tcl_Release(interp); - goto done; - } else { - src = propInfo; - srcLen = numItems; - } - - /* - * Set up the destination buffer so we can use as much space as is - * available. - */ - - dstPtr = &retrPtr->buf; - dst = Tcl_DStringValue(dstPtr); - dstLen = dstPtr->spaceAvl - 1; - - /* - * Now convert the data, growing the destination buffer as needed. - */ - - while (1) { - result = Tcl_ExternalToUtf(NULL, encoding, src, srcLen, - retrPtr->encFlags, &retrPtr->encState, - dst, dstLen, &srcRead, &dstWrote, NULL); - soFar = dst + dstWrote - Tcl_DStringValue(dstPtr); - retrPtr->encFlags &= ~TCL_ENCODING_START; - src += srcRead; - srcLen -= srcRead; - if (result != TCL_CONVERT_NOSPACE) { - Tcl_DStringSetLength(dstPtr, soFar); - break; - } - if (Tcl_DStringLength(dstPtr) == 0) { - Tcl_DStringSetLength(dstPtr, dstLen); - } - Tcl_DStringSetLength(dstPtr, 2 * Tcl_DStringLength(dstPtr) + 1); - dst = Tcl_DStringValue(dstPtr) + soFar; - dstLen = Tcl_DStringLength(dstPtr) - soFar - 1; - } - Tcl_DStringSetLength(dstPtr, soFar); - - result = retrPtr->proc(retrPtr->clientData, interp, - Tcl_DStringValue(dstPtr)); - Tcl_Release(interp); - - /* - * Copy any unused data into the destination buffer so we can pick it - * up next time around. - */ - - Tcl_DStringSetLength(dstPtr, 0); - Tcl_DStringAppend(dstPtr, src, srcLen); - - Tcl_DStringFree(&temp); - if (encoding) { - Tcl_FreeEncoding(encoding); - } - if (result != TCL_OK) { - retrPtr->result = result; - } - } else if (numItems == 0) { - retrPtr->result = TCL_OK; - } else { - Tcl_DString ds; - - if (format != 32 && format != 8) { - Tcl_SetObjResult(retrPtr->interp, Tcl_ObjPrintf( - "bad format for selection: wanted \"32\" or " - "\"8\", got \"%d\"", format)); - Tcl_SetErrorCode(retrPtr->interp, "TK", "SELECTION", "FORMAT", - NULL); - retrPtr->result = TCL_ERROR; - goto done; - } - Tcl_DStringInit(&ds); - if (format == 32) { - SelCvtFromX32((long *) propInfo, (int) numItems, type, - (Tk_Window) retrPtr->winPtr, &ds); - } else { - SelCvtFromX8((char *) propInfo, (int) numItems, type, - (Tk_Window) retrPtr->winPtr, &ds); - } - interp = retrPtr->interp; - Tcl_Preserve(interp); - result = retrPtr->proc(retrPtr->clientData, interp, - Tcl_DStringValue(&ds)); - Tcl_Release(interp); - Tcl_DStringFree(&ds); - if (result != TCL_OK) { - retrPtr->result = result; - } - } - - done: - XFree(propInfo); - retrPtr->idleTime = 0; -} - -/* - *---------------------------------------------------------------------- - * - * SelectionSize -- - * - * This function is called when the selection is too large to send in a - * single buffer; it computes the total length of the selection in bytes. - * - * Results: - * The return value is the number of bytes in the selection given by - * selPtr. - * - * Side effects: - * The selection is retrieved from its current owner (this is the only - * way to compute its size). - * - *---------------------------------------------------------------------- - */ - -static int -SelectionSize( - TkSelHandler *selPtr) /* Information about how to retrieve the - * selection whose size is wanted. */ -{ - char buffer[TK_SEL_BYTES_AT_ONCE+1]; - int size, chunkSize; - TkSelInProgress ip; - - size = TK_SEL_BYTES_AT_ONCE; - ip.selPtr = selPtr; - ip.nextPtr = TkSelGetInProgress(); - TkSelSetInProgress(&ip); - - do { - chunkSize = selPtr->proc(selPtr->clientData, size, (char *) buffer, - TK_SEL_BYTES_AT_ONCE); - if (ip.selPtr == NULL) { - size = 0; - break; - } - size += chunkSize; - } while (chunkSize == TK_SEL_BYTES_AT_ONCE); - - TkSelSetInProgress(ip.nextPtr); - return size; -} - -/* - *---------------------------------------------------------------------- - * - * IncrTimeoutProc -- - * - * This function is invoked once a second while sending the selection to - * a requestor in INCR mode. After a while it gives up and aborts the - * selection operation. - * - * Results: - * None. - * - * Side effects: - * A new timeout gets registered so that this function gets called again - * in another second, unless too many seconds have elapsed, in which case - * incrPtr is marked as "all done". - * - *---------------------------------------------------------------------- - */ - -static void -IncrTimeoutProc( - ClientData clientData) /* Information about INCR-mode selection - * retrieval for which we are selection - * owner. */ -{ - register IncrInfo *incrPtr = clientData; - - incrPtr->idleTime++; - if (incrPtr->idleTime >= 5) { - incrPtr->numIncrs = 0; - } else { - incrPtr->timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc, - incrPtr); - } -} - -/* - *---------------------------------------------------------------------- - * - * SelCvtToX -- - * - * Given a selection represented as a string (the normal Tcl form), - * convert it to the ICCCM-mandated format for X, depending on the type - * argument. This function and SelCvtFromX are inverses. - * - * Results: - * The return value is a malloc'ed buffer holding a value equivalent to - * "string", but formatted as for "type". It is the caller's - * responsibility to free the string when done with it. The word at - * *numLongsPtr is filled in with the number of 32-bit words returned in - * the result. If NULL is returned, the input list was not actually a - * list. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static long * -SelCvtToX( - char *string, /* String representation of selection. */ - Atom type, /* Atom specifying the X format that is - * desired for the selection. Should not be - * XA_STRING (if so, don't bother calling this - * function at all). */ - Tk_Window tkwin, /* Window that governs atom conversion. */ - int *numLongsPtr) /* Number of 32-bit words contained in the - * result. */ -{ - const char **field; - int numFields, i; - long *propPtr; - - /* - * The string is assumed to consist of fields separated by spaces. The - * property gets generated by converting each field to an integer number, - * in one of two ways: - * 1. If type is XA_ATOM, convert each field to its corresponding atom. - * 2. If type is anything else, convert each field from an ASCII number to - * a 32-bit binary number. - */ - - if (Tcl_SplitList(NULL, string, &numFields, &field) != TCL_OK) { - return NULL; - } - propPtr = ckalloc(numFields * sizeof(long)); - - /* - * Convert the fields one-by-one. - */ - - for (i=0 ; i<numFields ; i++) { - if (type == XA_ATOM) { - propPtr[i] = (long) Tk_InternAtom(tkwin, field[i]); - } else { - char *dummy; - - /* - * If this fails to parse a number, we just plunge on regardless - * anyway. - */ - - propPtr[i] = strtol(field[i], &dummy, 0); - } - } - - /* - * Release the parsed list. - */ - - ckfree(field); - *numLongsPtr = i; - return propPtr; -} - -/* - *---------------------------------------------------------------------- - * - * SelCvtFromX32, SelCvtFromX8 -- - * - * Given an X property value, formatted as a collection of 32-bit or - * 8-bit values according to "type" and the ICCCM conventions, convert - * the value to a string suitable for manipulation by Tcl. These - * functions are the inverse of SelCvtToX. - * - * Results: - * The return value (stored in a Tcl_DString) is the string equivalent of - * "property". It is up to the caller to initialize and free the DString. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static void -SelCvtFromX32( - register long *propPtr, /* Property value from X. */ - int numValues, /* Number of 32-bit values in property. */ - Atom type, /* Type of property Should not be XA_STRING - * (if so, don't bother calling this function - * at all). */ - Tk_Window tkwin, /* Window to use for atom conversion. */ - Tcl_DString *dsPtr) /* Where to store the converted string. */ -{ - /* - * Convert each long in the property to a string value, which is either - * the name of an atom (if type is XA_ATOM) or a hexadecimal string. We - * build the list in a Tcl_DString because this is easier than trying to - * get the quoting correct ourselves; this is tricky because atoms can - * contain spaces in their names (encountered when the atoms are really - * MIME types). [Bug 1353414] - */ - - for ( ; numValues > 0; propPtr++, numValues--) { - if (type == XA_ATOM) { - Tcl_DStringAppendElement(dsPtr, - Tk_GetAtomName(tkwin, (Atom) *propPtr)); - } else { - char buf[12]; - - sprintf(buf, "0x%x", (unsigned int) *propPtr); - Tcl_DStringAppendElement(dsPtr, buf); - } - } - Tcl_DStringAppend(dsPtr, " ", 1); -} - -static void -SelCvtFromX8( - register char *propPtr, /* Property value from X. */ - int numValues, /* Number of 8-bit values in property. */ - Atom type, /* Type of property Should not be XA_STRING - * (if so, don't bother calling this function - * at all). */ - Tk_Window tkwin, /* Window to use for atom conversion. */ - Tcl_DString *dsPtr) /* Where to store the converted string. */ -{ - /* - * Convert each long in the property to a string value, which is a - * hexadecimal string. We build the list in a Tcl_DString because this is - * easier than trying to get the quoting correct ourselves. - */ - - for ( ; numValues > 0; propPtr++, numValues--) { - char buf[12]; - - sprintf(buf, "0x%x", (unsigned char) *propPtr); - Tcl_DStringAppendElement(dsPtr, buf); - } - Tcl_DStringAppend(dsPtr, " ", 1); -} - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ |