1190 lines
33 KiB
C
1190 lines
33 KiB
C
|
/*
|
|||
|
* tkUnixSelect.c --
|
|||
|
*
|
|||
|
* This file contains X specific routines for manipulating
|
|||
|
* selections.
|
|||
|
*
|
|||
|
* Copyright (c) 1995 Sun Microsystems, Inc.
|
|||
|
*
|
|||
|
* See the file "license.terms" for information on usage and redistribution
|
|||
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|||
|
*
|
|||
|
* SCCS: @(#) tkUnixSelect.c 1.5 96/03/29 14:14:31
|
|||
|
*/
|
|||
|
|
|||
|
#include "tkInt.h"
|
|||
|
#include "tkSelect.h"
|
|||
|
|
|||
|
/*
|
|||
|
* When handling INCR-style selection retrievals, the selection owner
|
|||
|
* uses the following data structure to communicate between the
|
|||
|
* ConvertSelection procedure 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 offsets (same as
|
|||
|
* # of pairs in multAtoms). */
|
|||
|
int *offsets; /* One entry for each pair in
|
|||
|
* multAtoms; -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. This array is malloc-ed. */
|
|||
|
int numIncrs; /* Number of entries in offsets that
|
|||
|
* aren't -1 (i.e. # of INCR-mode transfers
|
|||
|
* not yet completed). */
|
|||
|
Tcl_TimerToken timeout; /* Token for timer procedure. */
|
|||
|
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;
|
|||
|
|
|||
|
static IncrInfo *pendingIncrs = NULL;
|
|||
|
/* List of all incr structures
|
|||
|
* currently active. */
|
|||
|
|
|||
|
/*
|
|||
|
* 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 procedures defined in this file:
|
|||
|
*/
|
|||
|
|
|||
|
static void ConvertSelection _ANSI_ARGS_((TkWindow *winPtr,
|
|||
|
XSelectionRequestEvent *eventPtr));
|
|||
|
static void IncrTimeoutProc _ANSI_ARGS_((ClientData clientData));
|
|||
|
static char * SelCvtFromX _ANSI_ARGS_((long *propPtr, int numValues,
|
|||
|
Atom type, Tk_Window tkwin));
|
|||
|
static long * SelCvtToX _ANSI_ARGS_((char *string, Atom type,
|
|||
|
Tk_Window tkwin, int *numLongsPtr));
|
|||
|
static int SelectionSize _ANSI_ARGS_((TkSelHandler *selPtr));
|
|||
|
static void SelRcvIncrProc _ANSI_ARGS_((ClientData clientData,
|
|||
|
XEvent *eventPtr));
|
|||
|
static void SelTimeoutProc _ANSI_ARGS_((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 interp->result.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
TkSelGetSelection(interp, tkwin, selection, target, proc, clientData)
|
|||
|
Tcl_Interp *interp; /* Interpreter to use for reporting
|
|||
|
* errors. */
|
|||
|
Tk_Window tkwin; /* Window on whose behalf to retrieve
|
|||
|
* the selection (determines display
|
|||
|
* from which to retrieve). */
|
|||
|
Atom selection; /* Selection to retrieve. */
|
|||
|
Atom target; /* Desired form in which selection
|
|||
|
* is to be returned. */
|
|||
|
Tk_GetSelProc *proc; /* Procedure to call to process the
|
|||
|
* selection, once it has been retrieved. */
|
|||
|
ClientData clientData; /* Arbitrary value to pass to proc. */
|
|||
|
{
|
|||
|
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.nextPtr = pendingRetrievals;
|
|||
|
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, 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,
|
|||
|
(ClientData) &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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return retr.result;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkSelPropProc --
|
|||
|
*
|
|||
|
* This procedure 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(eventPtr)
|
|||
|
register XEvent *eventPtr; /* X PropertyChange event. */
|
|||
|
{
|
|||
|
register IncrInfo *incrPtr;
|
|||
|
int i, format;
|
|||
|
Atom target, formatType;
|
|||
|
register TkSelHandler *selPtr;
|
|||
|
long buffer[TK_SEL_WORDS_AT_ONCE];
|
|||
|
int numItems;
|
|||
|
char *propPtr;
|
|||
|
Tk_ErrorHandler errorHandler;
|
|||
|
|
|||
|
/*
|
|||
|
* 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 = pendingIncrs; incrPtr != NULL;
|
|||
|
incrPtr = incrPtr->nextPtr) {
|
|||
|
if (incrPtr->reqWindow != eventPtr->xproperty.window) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
for (i = 0; i < incrPtr->numConversions; i++) {
|
|||
|
if ((eventPtr->xproperty.atom != incrPtr->multAtoms[2*i + 1])
|
|||
|
|| (incrPtr->offsets[i] == -1)){
|
|||
|
continue;
|
|||
|
}
|
|||
|
target = incrPtr->multAtoms[2*i];
|
|||
|
incrPtr->idleTime = 0;
|
|||
|
for (selPtr = incrPtr->winPtr->selHandlerList; ;
|
|||
|
selPtr = selPtr->nextPtr) {
|
|||
|
if (selPtr == NULL) {
|
|||
|
incrPtr->multAtoms[2*i + 1] = None;
|
|||
|
incrPtr->offsets[i] = -1;
|
|||
|
incrPtr->numIncrs --;
|
|||
|
return;
|
|||
|
}
|
|||
|
if ((selPtr->target == target)
|
|||
|
&& (selPtr->selection == incrPtr->selection)) {
|
|||
|
formatType = selPtr->format;
|
|||
|
if (incrPtr->offsets[i] == -2) {
|
|||
|
numItems = 0;
|
|||
|
((char *) buffer)[0] = 0;
|
|||
|
} else {
|
|||
|
TkSelInProgress ip;
|
|||
|
ip.selPtr = selPtr;
|
|||
|
ip.nextPtr = pendingPtr;
|
|||
|
pendingPtr = &ip;
|
|||
|
numItems = (*selPtr->proc)(selPtr->clientData,
|
|||
|
incrPtr->offsets[i], (char *) buffer,
|
|||
|
TK_SEL_BYTES_AT_ONCE);
|
|||
|
pendingPtr = ip.nextPtr;
|
|||
|
if (ip.selPtr == NULL) {
|
|||
|
/*
|
|||
|
* The selection handler deleted itself.
|
|||
|
*/
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
if (numItems > TK_SEL_BYTES_AT_ONCE) {
|
|||
|
panic("selection handler returned too many bytes");
|
|||
|
} else {
|
|||
|
if (numItems < 0) {
|
|||
|
numItems = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
((char *) buffer)[numItems] = '\0';
|
|||
|
}
|
|||
|
if (numItems < TK_SEL_BYTES_AT_ONCE) {
|
|||
|
if (numItems <= 0) {
|
|||
|
incrPtr->offsets[i] = -1;
|
|||
|
incrPtr->numIncrs--;
|
|||
|
} else {
|
|||
|
incrPtr->offsets[i] = -2;
|
|||
|
}
|
|||
|
} else {
|
|||
|
incrPtr->offsets[i] += numItems;
|
|||
|
}
|
|||
|
if (formatType == XA_STRING) {
|
|||
|
propPtr = (char *) buffer;
|
|||
|
format = 8;
|
|||
|
} else {
|
|||
|
propPtr = (char *) SelCvtToX((char *) buffer,
|
|||
|
formatType, (Tk_Window) incrPtr->winPtr,
|
|||
|
&numItems);
|
|||
|
format = 32;
|
|||
|
}
|
|||
|
errorHandler = Tk_CreateErrorHandler(
|
|||
|
eventPtr->xproperty.display, -1, -1, -1,
|
|||
|
(int (*)()) NULL, (ClientData) NULL);
|
|||
|
XChangeProperty(eventPtr->xproperty.display,
|
|||
|
eventPtr->xproperty.window,
|
|||
|
eventPtr->xproperty.atom, formatType,
|
|||
|
format, PropModeReplace,
|
|||
|
(unsigned char *) propPtr, numItems);
|
|||
|
Tk_DeleteErrorHandler(errorHandler);
|
|||
|
if (propPtr != (char *) buffer) {
|
|||
|
ckfree(propPtr);
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkSelEventProc --
|
|||
|
*
|
|||
|
* This procedure 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(tkwin, eventPtr)
|
|||
|
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 procedure
|
|||
|
* to handle the incoming selection.
|
|||
|
*/
|
|||
|
|
|||
|
if (eventPtr->type == SelectionNotify) {
|
|||
|
register TkSelRetrievalInfo *retrPtr;
|
|||
|
char *propInfo;
|
|||
|
Atom type;
|
|||
|
int format, result;
|
|||
|
unsigned long numItems, bytesAfter;
|
|||
|
|
|||
|
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_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
|
|||
|
Tcl_AppendResult(retrPtr->interp,
|
|||
|
Tk_GetAtomName(tkwin, retrPtr->selection),
|
|||
|
" selection doesn't exist or form \"",
|
|||
|
Tk_GetAtomName(tkwin, retrPtr->target),
|
|||
|
"\" not defined", (char *) 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 **) &propInfo);
|
|||
|
if ((result != Success) || (type == None)) {
|
|||
|
return;
|
|||
|
}
|
|||
|
if (bytesAfter != 0) {
|
|||
|
Tcl_SetResult(retrPtr->interp, "selection property too large",
|
|||
|
TCL_STATIC);
|
|||
|
retrPtr->result = TCL_ERROR;
|
|||
|
XFree(propInfo);
|
|||
|
return;
|
|||
|
}
|
|||
|
if ((type == XA_STRING) || (type == dispPtr->textAtom)
|
|||
|
|| (type == dispPtr->compoundTextAtom)) {
|
|||
|
if (format != 8) {
|
|||
|
sprintf(retrPtr->interp->result,
|
|||
|
"bad format for string selection: wanted \"8\", got \"%d\"",
|
|||
|
format);
|
|||
|
retrPtr->result = TCL_ERROR;
|
|||
|
return;
|
|||
|
}
|
|||
|
interp = retrPtr->interp;
|
|||
|
Tcl_Preserve((ClientData) interp);
|
|||
|
retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
|
|||
|
interp, propInfo);
|
|||
|
Tcl_Release((ClientData) interp);
|
|||
|
} 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,
|
|||
|
(ClientData) retrPtr);
|
|||
|
XDeleteProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
|
|||
|
retrPtr->property);
|
|||
|
while (retrPtr->result == -1) {
|
|||
|
Tcl_DoOneEvent(0);
|
|||
|
}
|
|||
|
Tk_DeleteEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
|
|||
|
(ClientData) retrPtr);
|
|||
|
} else {
|
|||
|
char *string;
|
|||
|
|
|||
|
if (format != 32) {
|
|||
|
sprintf(retrPtr->interp->result,
|
|||
|
"bad format for selection: wanted \"32\", got \"%d\"",
|
|||
|
format);
|
|||
|
retrPtr->result = TCL_ERROR;
|
|||
|
return;
|
|||
|
}
|
|||
|
string = SelCvtFromX((long *) propInfo, (int) numItems, type,
|
|||
|
(Tk_Window) winPtr);
|
|||
|
interp = retrPtr->interp;
|
|||
|
Tcl_Preserve((ClientData) interp);
|
|||
|
retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
|
|||
|
interp, string);
|
|||
|
Tcl_Release((ClientData) interp);
|
|||
|
ckfree(string);
|
|||
|
}
|
|||
|
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 procedure 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 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 procedure 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, SelTimeoutProc,
|
|||
|
(ClientData) retrPtr);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* ConvertSelection --
|
|||
|
*
|
|||
|
* This procedure 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 procedure
|
|||
|
* implements INCR-mode transfers, using the ICCCM protocol.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
ConvertSelection(winPtr, eventPtr)
|
|||
|
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. */
|
|||
|
{
|
|||
|
XSelectionEvent 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. */
|
|||
|
int i;
|
|||
|
Tk_ErrorHandler errorHandler;
|
|||
|
TkSelectionInfo *infoPtr;
|
|||
|
TkSelInProgress ip;
|
|||
|
|
|||
|
errorHandler = Tk_CreateErrorHandler(eventPtr->display, -1, -1,-1,
|
|||
|
(int (*)()) NULL, (ClientData) NULL);
|
|||
|
|
|||
|
/*
|
|||
|
* Initialize the reply event.
|
|||
|
*/
|
|||
|
|
|||
|
reply.type = SelectionNotify;
|
|||
|
reply.serial = 0;
|
|||
|
reply.send_event = True;
|
|||
|
reply.display = eventPtr->display;
|
|||
|
reply.requestor = eventPtr->requestor;
|
|||
|
reply.selection = eventPtr->selection;
|
|||
|
reply.target = eventPtr->target;
|
|||
|
reply.property = eventPtr->property;
|
|||
|
if (reply.property == None) {
|
|||
|
reply.property = reply.target;
|
|||
|
}
|
|||
|
reply.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.target;
|
|||
|
singleInfo[1] = reply.property;
|
|||
|
incr.multAtoms = singleInfo;
|
|||
|
incr.numConversions = 1;
|
|||
|
} else {
|
|||
|
Atom type;
|
|||
|
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, XA_ATOM,
|
|||
|
&type, &format, &incr.numConversions, &bytesAfter,
|
|||
|
(unsigned char **) &incr.multAtoms);
|
|||
|
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.offsets = (int *) ckalloc((unsigned)
|
|||
|
(incr.numConversions*sizeof(int)));
|
|||
|
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.offsets[i] = -1;
|
|||
|
|
|||
|
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 = pendingPtr;
|
|||
|
pendingPtr = &ip;
|
|||
|
type = selPtr->format;
|
|||
|
numItems = (*selPtr->proc)(selPtr->clientData, 0,
|
|||
|
(char *) buffer, TK_SEL_BYTES_AT_ONCE);
|
|||
|
pendingPtr = ip.nextPtr;
|
|||
|
if ((ip.selPtr == NULL) || (numItems < 0)) {
|
|||
|
incr.multAtoms[2*i + 1] = None;
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (numItems > TK_SEL_BYTES_AT_ONCE) {
|
|||
|
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.offsets[i] = 0;
|
|||
|
} else if (type == XA_STRING) {
|
|||
|
propPtr = (char *) buffer;
|
|||
|
format = 8;
|
|||
|
} else {
|
|||
|
propPtr = (char *) SelCvtToX((char *) buffer,
|
|||
|
type, (Tk_Window) winPtr, &numItems);
|
|||
|
format = 32;
|
|||
|
}
|
|||
|
XChangeProperty(reply.display, reply.requestor,
|
|||
|
property, type, format, PropModeReplace,
|
|||
|
(unsigned char *) propPtr, numItems);
|
|||
|
if (propPtr != (char *) buffer) {
|
|||
|
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.display, reply.requestor, PropertyChangeMask);
|
|||
|
incr.timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
|
|||
|
(ClientData) &incr);
|
|||
|
incr.idleTime = 0;
|
|||
|
incr.reqWindow = reply.requestor;
|
|||
|
incr.time = infoPtr->time;
|
|||
|
incr.nextPtr = pendingIncrs;
|
|||
|
pendingIncrs = &incr;
|
|||
|
}
|
|||
|
if (multiple) {
|
|||
|
XChangeProperty(reply.display, reply.requestor, reply.property,
|
|||
|
XA_ATOM, 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.property = incr.multAtoms[1];
|
|||
|
}
|
|||
|
XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
|
|||
|
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, (ClientData) NULL);
|
|||
|
XSelectInput(reply.display, reply.requestor, 0L);
|
|||
|
Tk_DeleteErrorHandler(errorHandler);
|
|||
|
if (pendingIncrs == &incr) {
|
|||
|
pendingIncrs = incr.nextPtr;
|
|||
|
} else {
|
|||
|
for (incrPtr2 = pendingIncrs; incrPtr2 != NULL;
|
|||
|
incrPtr2 = incrPtr2->nextPtr) {
|
|||
|
if (incrPtr2->nextPtr == &incr) {
|
|||
|
incrPtr2->nextPtr = incr.nextPtr;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* All done. Cleanup and return.
|
|||
|
*/
|
|||
|
|
|||
|
ckfree((char *) incr.offsets);
|
|||
|
if (multiple) {
|
|||
|
XFree((char *) incr.multAtoms);
|
|||
|
}
|
|||
|
return;
|
|||
|
|
|||
|
/*
|
|||
|
* An error occurred. Send back a refusal message.
|
|||
|
*/
|
|||
|
|
|||
|
refuse:
|
|||
|
reply.property = None;
|
|||
|
XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
|
|||
|
Tk_DeleteErrorHandler(errorHandler);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* SelRcvIncrProc --
|
|||
|
*
|
|||
|
* This procedure 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 procedure is
|
|||
|
* invoked to deal with that piece. When the whole selection
|
|||
|
* is here, a flag is left for the higher-level procedure that
|
|||
|
* initiated the selection retrieval.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
SelRcvIncrProc(clientData, eventPtr)
|
|||
|
ClientData clientData; /* Information about retrieval. */
|
|||
|
register XEvent *eventPtr; /* X PropertyChange event. */
|
|||
|
{
|
|||
|
register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
|
|||
|
char *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 **) &propInfo);
|
|||
|
if ((result != Success) || (type == None)) {
|
|||
|
return;
|
|||
|
}
|
|||
|
if (bytesAfter != 0) {
|
|||
|
Tcl_SetResult(retrPtr->interp, "selection property too large",
|
|||
|
TCL_STATIC);
|
|||
|
retrPtr->result = TCL_ERROR;
|
|||
|
goto done;
|
|||
|
}
|
|||
|
if (numItems == 0) {
|
|||
|
retrPtr->result = TCL_OK;
|
|||
|
} else if ((type == XA_STRING)
|
|||
|
|| (type == retrPtr->winPtr->dispPtr->textAtom)
|
|||
|
|| (type == retrPtr->winPtr->dispPtr->compoundTextAtom)) {
|
|||
|
if (format != 8) {
|
|||
|
Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
|
|||
|
sprintf(retrPtr->interp->result,
|
|||
|
"bad format for string selection: wanted \"8\", got \"%d\"",
|
|||
|
format);
|
|||
|
retrPtr->result = TCL_ERROR;
|
|||
|
goto done;
|
|||
|
}
|
|||
|
interp = retrPtr->interp;
|
|||
|
Tcl_Preserve((ClientData) interp);
|
|||
|
result = (*retrPtr->proc)(retrPtr->clientData, interp, propInfo);
|
|||
|
Tcl_Release((ClientData) interp);
|
|||
|
if (result != TCL_OK) {
|
|||
|
retrPtr->result = result;
|
|||
|
}
|
|||
|
} else {
|
|||
|
char *string;
|
|||
|
|
|||
|
if (format != 32) {
|
|||
|
Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
|
|||
|
sprintf(retrPtr->interp->result,
|
|||
|
"bad format for selection: wanted \"32\", got \"%d\"",
|
|||
|
format);
|
|||
|
retrPtr->result = TCL_ERROR;
|
|||
|
goto done;
|
|||
|
}
|
|||
|
string = SelCvtFromX((long *) propInfo, (int) numItems, type,
|
|||
|
(Tk_Window) retrPtr->winPtr);
|
|||
|
interp = retrPtr->interp;
|
|||
|
Tcl_Preserve((ClientData) interp);
|
|||
|
result = (*retrPtr->proc)(retrPtr->clientData, interp, string);
|
|||
|
Tcl_Release((ClientData) interp);
|
|||
|
if (result != TCL_OK) {
|
|||
|
retrPtr->result = result;
|
|||
|
}
|
|||
|
ckfree(string);
|
|||
|
}
|
|||
|
|
|||
|
done:
|
|||
|
XFree(propInfo);
|
|||
|
retrPtr->idleTime = 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* SelectionSize --
|
|||
|
*
|
|||
|
* This procedure 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(selPtr)
|
|||
|
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 = pendingPtr;
|
|||
|
pendingPtr = &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);
|
|||
|
pendingPtr = ip.nextPtr;
|
|||
|
return size;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* IncrTimeoutProc --
|
|||
|
*
|
|||
|
* This procedure 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 procedure 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 clientData; /* Information about INCR-mode
|
|||
|
* selection retrieval for which
|
|||
|
* we are selection owner. */
|
|||
|
{
|
|||
|
register IncrInfo *incrPtr = (IncrInfo *) clientData;
|
|||
|
|
|||
|
incrPtr->idleTime++;
|
|||
|
if (incrPtr->idleTime >= 5) {
|
|||
|
incrPtr->numIncrs = 0;
|
|||
|
} else {
|
|||
|
incrPtr->timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
|
|||
|
(ClientData) 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 procedure 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.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static long *
|
|||
|
SelCvtToX(string, type, tkwin, numLongsPtr)
|
|||
|
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 procedure at all). */
|
|||
|
Tk_Window tkwin; /* Window that governs atom conversion. */
|
|||
|
int *numLongsPtr; /* Number of 32-bit words contained in the
|
|||
|
* result. */
|
|||
|
{
|
|||
|
register char *p;
|
|||
|
char *field;
|
|||
|
int numFields;
|
|||
|
long *propPtr, *longPtr;
|
|||
|
#define MAX_ATOM_NAME_LENGTH 100
|
|||
|
char atomName[MAX_ATOM_NAME_LENGTH+1];
|
|||
|
|
|||
|
/*
|
|||
|
* 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.
|
|||
|
*/
|
|||
|
|
|||
|
numFields = 1;
|
|||
|
for (p = string; *p != 0; p++) {
|
|||
|
if (isspace(UCHAR(*p))) {
|
|||
|
numFields++;
|
|||
|
}
|
|||
|
}
|
|||
|
propPtr = (long *) ckalloc((unsigned) numFields*sizeof(long));
|
|||
|
|
|||
|
/*
|
|||
|
* Convert the fields one-by-one.
|
|||
|
*/
|
|||
|
|
|||
|
for (longPtr = propPtr, *numLongsPtr = 0, p = string;
|
|||
|
; longPtr++, (*numLongsPtr)++) {
|
|||
|
while (isspace(UCHAR(*p))) {
|
|||
|
p++;
|
|||
|
}
|
|||
|
if (*p == 0) {
|
|||
|
break;
|
|||
|
}
|
|||
|
field = p;
|
|||
|
while ((*p != 0) && !isspace(UCHAR(*p))) {
|
|||
|
p++;
|
|||
|
}
|
|||
|
if (type == XA_ATOM) {
|
|||
|
int length;
|
|||
|
|
|||
|
length = p - field;
|
|||
|
if (length > MAX_ATOM_NAME_LENGTH) {
|
|||
|
length = MAX_ATOM_NAME_LENGTH;
|
|||
|
}
|
|||
|
strncpy(atomName, field, (unsigned) length);
|
|||
|
atomName[length] = 0;
|
|||
|
*longPtr = (long) Tk_InternAtom(tkwin, atomName);
|
|||
|
} else {
|
|||
|
char *dummy;
|
|||
|
|
|||
|
*longPtr = strtol(field, &dummy, 0);
|
|||
|
}
|
|||
|
}
|
|||
|
return propPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* SelCvtFromX --
|
|||
|
*
|
|||
|
* Given an X property value, formatted as a collection of 32-bit
|
|||
|
* values according to "type" and the ICCCM conventions, convert
|
|||
|
* the value to a string suitable for manipulation by Tcl. This
|
|||
|
* procedure is the inverse of SelCvtToX.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* The return value is the string equivalent of "property". It is
|
|||
|
* malloc-ed and should be freed by the caller when no longer
|
|||
|
* needed.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static char *
|
|||
|
SelCvtFromX(propPtr, numValues, type, tkwin)
|
|||
|
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 procedure at all). */
|
|||
|
Tk_Window tkwin; /* Window to use for atom conversion. */
|
|||
|
{
|
|||
|
char *result;
|
|||
|
int resultSpace, curSize, fieldSize;
|
|||
|
char *atomName;
|
|||
|
|
|||
|
/*
|
|||
|
* 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. Make an initial guess about the size of the result, but
|
|||
|
* be prepared to enlarge the result if necessary.
|
|||
|
*/
|
|||
|
|
|||
|
resultSpace = 12*numValues+1;
|
|||
|
curSize = 0;
|
|||
|
atomName = ""; /* Not needed, but eliminates compiler warning. */
|
|||
|
result = (char *) ckalloc((unsigned) resultSpace);
|
|||
|
*result = '\0';
|
|||
|
for ( ; numValues > 0; propPtr++, numValues--) {
|
|||
|
if (type == XA_ATOM) {
|
|||
|
atomName = Tk_GetAtomName(tkwin, (Atom) *propPtr);
|
|||
|
fieldSize = strlen(atomName) + 1;
|
|||
|
} else {
|
|||
|
fieldSize = 12;
|
|||
|
}
|
|||
|
if (curSize+fieldSize >= resultSpace) {
|
|||
|
char *newResult;
|
|||
|
|
|||
|
resultSpace *= 2;
|
|||
|
if (curSize+fieldSize >= resultSpace) {
|
|||
|
resultSpace = curSize + fieldSize + 1;
|
|||
|
}
|
|||
|
newResult = (char *) ckalloc((unsigned) resultSpace);
|
|||
|
strncpy(newResult, result, (unsigned) curSize);
|
|||
|
ckfree(result);
|
|||
|
result = newResult;
|
|||
|
}
|
|||
|
if (curSize != 0) {
|
|||
|
result[curSize] = ' ';
|
|||
|
curSize++;
|
|||
|
}
|
|||
|
if (type == XA_ATOM) {
|
|||
|
strcpy(result+curSize, atomName);
|
|||
|
} else {
|
|||
|
sprintf(result+curSize, "0x%x", (unsigned int) *propPtr);
|
|||
|
}
|
|||
|
curSize += strlen(result+curSize);
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|