summaryrefslogtreecommitdiffstats
path: root/unix/tkUnixSelect.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tkUnixSelect.c')
-rw-r--r--unix/tkUnixSelect.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/unix/tkUnixSelect.c b/unix/tkUnixSelect.c
new file mode 100644
index 0000000..5d08d4e
--- /dev/null
+++ b/unix/tkUnixSelect.c
@@ -0,0 +1,380 @@
+/*
+ * 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 "tk.h"
+#include "tkInt.h"
+#include "tkSelect.h"
+
+static TkSelRetrievalInfo *pendingRetrievals = NULL;
+ /* List of all retrievals currently being
+ * waited for. */
+
+/*
+ * Forward declarations for functions defined in this file:
+ */
+
+static void TkDND_SelTimeoutProc(ClientData clientData);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkDNDSelGetSelection --
+ *
+ * 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
+TkDNDSelGetSelection(
+ 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. */
+ Time time,
+ 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) {
+printf("1\n");
+ 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;
+
+ /*
+ * 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, time);
+
+ /*
+ * 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, TkDND_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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkDND_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
+TkDND_SelTimeoutProc(
+ ClientData clientData) /* Information about retrieval in progress. */
+{
+ register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) 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_SetResult(retrPtr->interp, "selection owner didn't respond",
+ TCL_STATIC);
+ retrPtr->result = TCL_ERROR;
+ } else {
+ retrPtr->timeout = Tcl_CreateTimerHandler(1000, TkDND_SelTimeoutProc,
+ (ClientData) retrPtr);
+ }
+}
+
+/*
+ * The structure below is used to keep each thread's pending list separate.
+ */
+
+typedef struct ThreadSpecificData {
+ TkSelInProgress *pendingPtr;
+ /* Topmost search in progress, or NULL if
+ * none. */
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+
+int
+TkSelDefaultSelection(
+ TkSelectionInfo *infoPtr, /* Info about selection being retrieved. */
+ Atom target, /* Desired form of selection. */
+ char *buffer, /* Place to put selection characters. */
+ int maxBytes, /* Maximum # of bytes to store at buffer. */
+ Atom *typePtr) /* Store here the type of the selection, for
+ * use in converting to proper X format. */
+{
+ register TkWindow *winPtr = (TkWindow *) infoPtr->owner;
+ TkDisplay *dispPtr = winPtr->dispPtr;
+
+ if (target == dispPtr->timestampAtom) {
+ if (maxBytes < 20) {
+ return -1;
+ }
+ sprintf(buffer, "0x%x", (unsigned int) infoPtr->time);
+ *typePtr = XA_INTEGER;
+ return strlen(buffer);
+ }
+
+ if (target == dispPtr->targetsAtom) {
+ register TkSelHandler *selPtr;
+ int length;
+ Tcl_DString ds;
+
+ if (maxBytes < 50) {
+ return -1;
+ }
+ Tcl_DStringInit(&ds);
+ Tcl_DStringAppend(&ds,
+ "MULTIPLE TARGETS TIMESTAMP TK_APPLICATION TK_WINDOW", -1);
+ for (selPtr = winPtr->selHandlerList; selPtr != NULL;
+ selPtr = selPtr->nextPtr) {
+ if ((selPtr->selection == infoPtr->selection)
+ && (selPtr->target != dispPtr->applicationAtom)
+ && (selPtr->target != dispPtr->windowAtom)) {
+ const char *atomString = Tk_GetAtomName((Tk_Window) winPtr,
+ selPtr->target);
+ Tcl_DStringAppendElement(&ds, atomString);
+ }
+ }
+ length = Tcl_DStringLength(&ds);
+ if (length >= maxBytes) {
+ Tcl_DStringFree(&ds);
+ return -1;
+ }
+ memcpy(buffer, Tcl_DStringValue(&ds), (unsigned) (1+length));
+ Tcl_DStringFree(&ds);
+ *typePtr = XA_ATOM;
+ return length;
+ }
+
+ if (target == dispPtr->applicationAtom) {
+ int length;
+ Tk_Uid name = winPtr->mainPtr->winPtr->nameUid;
+
+ length = strlen(name);
+ if (maxBytes <= length) {
+ return -1;
+ }
+ strcpy(buffer, name);
+ *typePtr = XA_STRING;
+ return length;
+ }
+
+ if (target == dispPtr->windowAtom) {
+ int length;
+ char *name = winPtr->pathName;
+
+ length = strlen(name);
+ if (maxBytes <= length) {
+ return -1;
+ }
+ strcpy(buffer, name);
+ *typePtr = XA_STRING;
+ return length;
+ }
+
+ return -1;
+}
+
+int TkDND_GetSelection(Tcl_Interp *interp, Tk_Window tkwin, Atom selection,
+ Atom target, Time time,
+ Tk_GetSelProc *proc, ClientData clientData) {
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ TkDisplay *dispPtr = winPtr->dispPtr;
+ TkSelectionInfo *infoPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ if (dispPtr->multipleAtom == None) {
+ TkSelInit(tkwin);
+ }
+
+ /*
+ * If the selection is owned by a window managed by this process, then
+ * call the retrieval function directly, rather than going through the X
+ * server (it's dangerous to go through the X server in this case because
+ * it could result in deadlock if an INCR-style selection results).
+ */
+
+ for (infoPtr = dispPtr->selectionInfoPtr; infoPtr != NULL;
+ infoPtr = infoPtr->nextPtr) {
+ if (infoPtr->selection == selection) {
+ break;
+ }
+ }
+ if (infoPtr != NULL) {
+ register TkSelHandler *selPtr;
+ int offset, result, count;
+ char buffer[TK_SEL_BYTES_AT_ONCE+1];
+ TkSelInProgress ip;
+
+ for (selPtr = ((TkWindow *) infoPtr->owner)->selHandlerList;
+ selPtr != NULL; selPtr = selPtr->nextPtr) {
+ if (selPtr->target==target && selPtr->selection==selection) {
+ break;
+ }
+ }
+ if (selPtr == NULL) {
+ Atom type;
+
+ count = TkSelDefaultSelection(infoPtr, target, buffer,
+ TK_SEL_BYTES_AT_ONCE, &type);
+ if (count > TK_SEL_BYTES_AT_ONCE) {
+ Tcl_Panic("selection handler returned too many bytes");
+ }
+ if (count < 0) {
+ goto cantget;
+ }
+ buffer[count] = 0;
+ result = proc(clientData, interp, buffer);
+ } else {
+ offset = 0;
+ result = TCL_OK;
+ ip.selPtr = selPtr;
+ ip.nextPtr = tsdPtr->pendingPtr;
+ tsdPtr->pendingPtr = &ip;
+ while (1) {
+ count = selPtr->proc(selPtr->clientData, offset, buffer,
+ TK_SEL_BYTES_AT_ONCE);
+ if ((count < 0) || (ip.selPtr == NULL)) {
+ tsdPtr->pendingPtr = ip.nextPtr;
+ goto cantget;
+ }
+ if (count > TK_SEL_BYTES_AT_ONCE) {
+ Tcl_Panic("selection handler returned too many bytes");
+ }
+ buffer[count] = '\0';
+ result = proc(clientData, interp, buffer);
+ if ((result != TCL_OK) || (count < TK_SEL_BYTES_AT_ONCE)
+ || (ip.selPtr == NULL)) {
+ break;
+ }
+ offset += count;
+ }
+ tsdPtr->pendingPtr = ip.nextPtr;
+ }
+ return result;
+ }
+
+ /*
+ * The selection is owned by some other process.
+ */
+
+ return TkDNDSelGetSelection(interp, tkwin, selection, target, time,
+ proc, clientData);
+
+ cantget:
+ Tcl_AppendResult(interp, Tk_GetAtomName(tkwin, selection),
+ " selection doesn't exist or form \"",
+ Tk_GetAtomName(tkwin, target), "\" not defined", NULL);
+ return TCL_ERROR;
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */