|
|
1.1 root 1: /*
2: * tkSelect.c --
3: *
4: * This file manages the selection for the Tk toolkit,
5: * translating between the standard X ICCCM conventions
6: * and Tcl commands.
7: *
8: * Copyright 1990 Regents of the University of California.
9: * Permission to use, copy, modify, and distribute this
10: * software and its documentation for any purpose and without
11: * fee is hereby granted, provided that the above copyright
12: * notice appear in all copies. The University of California
13: * makes no representations about the suitability of this
14: * software for any purpose. It is provided "as is" without
15: * express or implied warranty.
16: */
17:
18: #ifndef lint
19: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkSelect.c,v 1.27 92/08/10 15:03:03 ouster Exp $ SPRITE (Berkeley)";
20: #endif
21:
22: #include "tkconfig.h"
23: #include "tkint.h"
24:
25: /*
26: * When the selection is being retrieved, one of the following
27: * structures is present on a list of pending selection retrievals.
28: * The structure is used to communicate between the background
29: * procedure that requests the selection and the foreground
30: * event handler that processes the events in which the selection
31: * is returned. There is a list of such structures so that there
32: * can be multiple simultaneous selection retrievals (e.g. on
33: * different displays).
34: */
35:
36: typedef struct RetrievalInfo {
37: Tcl_Interp *interp; /* Interpreter for error reporting. */
38: TkWindow *winPtr; /* Window used as requestor for
39: * selection. */
40: Atom property; /* Property where selection will appear. */
41: Atom target; /* Desired form for selection. */
42: int (*proc) _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
43: char *portion)); /* Procedure to call to handle pieces
44: * of selection. */
45: ClientData clientData; /* Argument for proc. */
46: int result; /* Initially -1. Set to a Tcl
47: * return value once the selection
48: * has been retrieved. */
49: Tk_TimerToken timeout; /* Token for current timeout procedure. */
50: int idleTime; /* Number of seconds that have gone by
51: * without hearing anything from the
52: * selection owner. */
53: struct RetrievalInfo *nextPtr;
54: /* Next in list of all pending
55: * selection retrievals. NULL means
56: * end of list. */
57: } RetrievalInfo;
58:
59: static RetrievalInfo *pendingRetrievals = NULL;
60: /* List of all retrievals currently
61: * being waited for. */
62:
63: /*
64: * When "selection get" is being used to retrieve the selection,
65: * the following data structure is used for communication between
66: * Tk_SelectionCmd and SelGetProc. Its purpose is to keep track
67: * of the selection contents, which are gradually assembled in a
68: * string.
69: */
70:
71: typedef struct {
72: char *string; /* Contents of selection are
73: * here. This space is malloc-ed. */
74: int bytesAvl; /* Total number of bytes available
75: * at string. */
76: int bytesUsed; /* Bytes currently in use in string,
77: * not including the terminating
78: * NULL. */
79: } GetInfo;
80:
81: /*
82: * When handling INCR-style selection retrievals, the selection owner
83: * uses the following data structure to communicate between the
84: * ConvertSelection procedure and TkSelPropProc.
85: */
86:
87: typedef struct IncrInfo {
88: TkWindow *winPtr; /* Window that owns selection. */
89: Atom *multAtoms; /* Information about conversions to
90: * perform: one or more pairs of
91: * (target, property). This either
92: * points to a retrieved property (for
93: * MULTIPLE retrievals) or to a static
94: * array. */
95: unsigned long numConversions;
96: /* Number of entries in offsets (same as
97: * # of pairs in multAtoms). */
98: int *offsets; /* One entry for each pair in
99: * multAtoms; -1 means all data has
100: * been transferred for this
101: * conversion. -2 means only the
102: * final zero-length transfer still
103: * has to be done. Otherwise it is the
104: * offset of the next chunk of data
105: * to transfer. This array is malloc-ed. */
106: int numIncrs; /* Number of entries in offsets that
107: * aren't -1 (i.e. # of INCR-mode transfers
108: * not yet completed). */
109: Tk_TimerToken timeout; /* Token for timer procedure. */
110: int idleTime; /* Number of seconds since we heard
111: * anything from the selection
112: * requestor. */
113: Window reqWindow; /* Requestor's window id. */
114: Time time; /* Timestamp corresponding to
115: * selection at beginning of request;
116: * used to abort transfer if selection
117: * changes. */
118: struct IncrInfo *nextPtr; /* Next in list of all INCR-style
119: * retrievals currently pending. */
120: } IncrInfo;
121:
122: static IncrInfo *pendingIncrs = NULL;
123: /* List of all IncrInfo structures
124: * currently active. */
125:
126: /*
127: * When a selection handler is set up by invoking "selection handle",
128: * one of the following data structures is set up to hold information
129: * about the command to invoke and its interpreter.
130: */
131:
132: typedef struct {
133: Tcl_Interp *interp; /* Interpreter in which to invoke command. */
134: int cmdLength; /* # of non-NULL bytes in command. */
135: char command[4]; /* Command to invoke. Actual space is
136: * allocated as large as necessary. This
137: * must be the last entry in the structure. */
138: } CommandInfo;
139:
140: /*
141: * Chunk size for retrieving selection. It's defined both in
142: * words and in bytes; the word size is used to allocate
143: * buffer space that's guaranteed to be word-aligned and that
144: * has an extra character for the terminating NULL.
145: */
146:
147: #define TK_SEL_BYTES_AT_ONCE 4000
148: #define TK_SEL_WORDS_AT_ONCE 1001
149:
150: /*
151: * Largest property that we'll accept when sending or receiving the
152: * selection:
153: */
154:
155: #define MAX_PROP_WORDS 100000
156:
157: /*
158: * Forward declarations for procedures defined in this file:
159: */
160:
161: static void ConvertSelection _ANSI_ARGS_((TkWindow *winPtr,
162: XSelectionRequestEvent *eventPtr));
163: static int DefaultSelection _ANSI_ARGS_((TkWindow *winPtr,
164: Atom target, char *buffer, int maxBytes,
165: Atom *typePtr));
166: static int HandleTclCommand _ANSI_ARGS_((ClientData clientData,
167: int offset, char *buffer, int maxBytes));
168: static void IncrTimeoutProc _ANSI_ARGS_((ClientData clientData));
169: static char * SelCvtFromX _ANSI_ARGS_((long *propPtr, int numValues,
170: Atom type, Tk_Window tkwin));
171: static long * SelCvtToX _ANSI_ARGS_((char *string, Atom type,
172: Tk_Window tkwin, int *numLongsPtr));
173: static int SelGetProc _ANSI_ARGS_((ClientData clientData,
174: Tcl_Interp *interp, char *portion));
175: static void SelInit _ANSI_ARGS_((Tk_Window tkwin));
176: static void SelRcvIncrProc _ANSI_ARGS_((ClientData clientData,
177: XEvent *eventPtr));
178: static void SelTimeoutProc _ANSI_ARGS_((ClientData clientData));
179:
180: /*
181: *--------------------------------------------------------------
182: *
183: * Tk_CreateSelHandler --
184: *
185: * This procedure is called to register a procedure
186: * as the handler for selection requests of a particular
187: * target type on a particular window.
188: *
189: * Results:
190: * None.
191: *
192: * Side effects:
193: * In the future, whenever the selection is in tkwin's
194: * window and someone requests the selection in the
195: * form given by target, proc will be invoked to provide
196: * part or all of the selection in the given form. If
197: * there was already a handler declared for the given
198: * window and target type, then it is replaced. Proc
199: * should have the following form:
200: *
201: * int
202: * proc(clientData, offset, buffer, maxBytes)
203: * ClientData clientData;
204: * int offset;
205: * char *buffer;
206: * int maxBytes;
207: * {
208: * }
209: *
210: * The clientData argument to proc will be the same as
211: * the clientData argument to this procedure. The offset
212: * argument indicates which portion of the selection to
213: * return: skip the first offset bytes. Buffer is a
214: * pointer to an area in which to place the converted
215: * selection, and maxBytes gives the number of bytes
216: * available at buffer. Proc should place the selection
217: * in buffer as a string, and return a count of the number
218: * of bytes of selection actually placed in buffer (not
219: * including the terminating NULL character). If the
220: * return value equals maxBytes, this is a sign that there
221: * is probably still more selection information available.
222: *
223: *--------------------------------------------------------------
224: */
225:
226: void
227: Tk_CreateSelHandler(tkwin, target, proc, clientData, format)
228: Tk_Window tkwin; /* Token for window. */
229: Atom target; /* The kind of selection conversions
230: * that can be handled by proc,
231: * e.g. TARGETS or XA_STRING. */
232: Tk_SelectionProc *proc; /* Procedure to invoke to convert
233: * selection to type "target". */
234: ClientData clientData; /* Value to pass to proc. */
235: Atom format; /* Format in which the selection
236: * information should be returned to
237: * the requestor. XA_STRING is best by
238: * far, but anything listed in the ICCCM
239: * will be tolerated (blech). */
240: {
241: register TkSelHandler *selPtr;
242: TkWindow *winPtr = (TkWindow *) tkwin;
243:
244: if (winPtr->dispPtr->multipleAtom == None) {
245: SelInit(tkwin);
246: }
247:
248: /*
249: * See if there's already a handler for this target on
250: * this window. If so, re-use it. If not, create a new one.
251: */
252:
253: for (selPtr = winPtr->selHandlerList; ; selPtr = selPtr->nextPtr) {
254: if (selPtr == NULL) {
255: selPtr = (TkSelHandler *) ckalloc(sizeof(TkSelHandler));
256: selPtr->nextPtr = winPtr->selHandlerList;
257: winPtr->selHandlerList = selPtr;
258: break;
259: }
260: if (selPtr->target == target) {
261:
262: /*
263: * Special case: when replacing handler created by
264: * "selection handle" free up memory. Should there be a
265: * callback to allow other clients to do this too?
266: */
267:
268: if (selPtr->proc == HandleTclCommand) {
269: ckfree((char *) selPtr->clientData);
270: }
271: break;
272: }
273: }
274: selPtr->target = target;
275: selPtr->format = format;
276: selPtr->proc = proc;
277: selPtr->clientData = clientData;
278: if (format == XA_STRING) {
279: selPtr->size = 8;
280: } else {
281: selPtr->size = 32;
282: }
283: }
284:
285: /*
286: *--------------------------------------------------------------
287: *
288: * Tk_OwnSelection --
289: *
290: * Arrange for tkwin to become the selection owner.
291: *
292: * Results:
293: * None.
294: *
295: * Side effects:
296: * From now on, requests for the selection will be
297: * directed to procedures associated with tkwin (they
298: * must have been declared with calls to Tk_CreateSelHandler).
299: * When the selection is lost by this window, proc will
300: * be invoked (see the manual entry for details).
301: *
302: *--------------------------------------------------------------
303: */
304:
305: void
306: Tk_OwnSelection(tkwin, proc, clientData)
307: Tk_Window tkwin; /* Window to become new selection
308: * owner. */
309: Tk_LostSelProc *proc; /* Procedure to call when selection
310: * is taken away from tkwin. */
311: ClientData clientData; /* Arbitrary one-word argument to
312: * pass to proc. */
313: {
314: register TkWindow *winPtr = (TkWindow *) tkwin;
315: TkDisplay *dispPtr = winPtr->dispPtr;
316:
317: if (dispPtr->multipleAtom == None) {
318: SelInit(tkwin);
319: }
320:
321: winPtr->selClearProc = proc;
322: winPtr->selClearData = clientData;
323: if (dispPtr->selectionOwner != tkwin) {
324: TkWindow *ownerPtr = (TkWindow *) dispPtr->selectionOwner;
325:
326: if ((ownerPtr != NULL)
327: && (ownerPtr->selClearProc != NULL)) {
328: (*ownerPtr->selClearProc)(ownerPtr->selClearData);
329: ownerPtr->selClearProc = NULL;
330: }
331: }
332: dispPtr->selectionOwner = tkwin;
333: dispPtr->selectionSerial = NextRequest(winPtr->display);
334: dispPtr->selectionTime = TkCurrentTime(dispPtr);
335: XSetSelectionOwner(winPtr->display, XA_PRIMARY, winPtr->window,
336: dispPtr->selectionTime);
337: }
338:
339: /*
340: *--------------------------------------------------------------
341: *
342: * Tk_GetSelection --
343: *
344: * Retrieve the selection and pass it off (in pieces,
345: * possibly) to a given procedure.
346: *
347: * Results:
348: * The return value is a standard Tcl return value.
349: * If an error occurs (such as no selection exists)
350: * then an error message is left in interp->result.
351: *
352: * Side effects:
353: * The standard X11 protocols are used to retrieve the
354: * selection. When it arrives, it is passed to proc. If
355: * the selection is very large, it will be passed to proc
356: * in several pieces. Proc should have the following
357: * structure:
358: *
359: * int
360: * proc(clientData, interp, portion)
361: * ClientData clientData;
362: * Tcl_Interp *interp;
363: * char *portion;
364: * {
365: * }
366: *
367: * The interp and clientData arguments to proc will be the
368: * same as the corresponding arguments to Tk_GetSelection.
369: * The portion argument points to a character string
370: * containing part of the selection, and numBytes indicates
371: * the length of the portion, not including the terminating
372: * NULL character. If the selection arrives in several pieces,
373: * the "portion" arguments in separate calls will contain
374: * successive parts of the selection. Proc should normally
375: * return TCL_OK. If it detects an error then it should return
376: * TCL_ERROR and leave an error message in interp->result; the
377: * remainder of the selection retrieval will be aborted.
378: *
379: *--------------------------------------------------------------
380: */
381:
382: int
383: Tk_GetSelection(interp, tkwin, target, proc, clientData)
384: Tcl_Interp *interp; /* Interpreter to use for reporting
385: * errors. */
386: Tk_Window tkwin; /* Window on whose behalf to retrieve
387: * the selection (determines display
388: * from which to retrieve). */
389: Atom target; /* Desired form in which selection
390: * is to be returned. */
391: Tk_GetSelProc *proc; /* Procedure to call to process the
392: * selection, once it has been retrieved. */
393: ClientData clientData; /* Arbitrary value to pass to proc. */
394: {
395: RetrievalInfo retr;
396: TkWindow *winPtr = (TkWindow *) tkwin;
397: TkDisplay *dispPtr = winPtr->dispPtr;
398:
399: if (dispPtr->multipleAtom == None) {
400: SelInit(tkwin);
401: }
402:
403: /*
404: * If the selection is owned by a window managed by this
405: * process, then call the retrieval procedure directly,
406: * rather than going through the X server (it's dangerous
407: * to go through the X server in this case because it could
408: * result in deadlock if an INCR-style selection results).
409: */
410:
411: if (dispPtr->selectionOwner != NULL) {
412: register TkSelHandler *selPtr;
413: int offset, result, count;
414: char buffer[TK_SEL_BYTES_AT_ONCE+1];
415: Time time;
416:
417: /*
418: * Make sure that the selection predates the request
419: * time.
420: */
421:
422: time = TkCurrentTime(dispPtr);
423: if ((time < dispPtr->selectionTime)
424: && (time != CurrentTime)
425: && (dispPtr->selectionTime != CurrentTime)) {
426: interp->result = "selection changed before it could be retrieved";
427: return TCL_ERROR;
428: }
429:
430: for (selPtr = ((TkWindow *) dispPtr->selectionOwner)->selHandlerList;
431: ; selPtr = selPtr->nextPtr) {
432: if (selPtr == NULL) {
433: Atom type;
434:
435: count = DefaultSelection((TkWindow *) dispPtr->selectionOwner,
436: target, buffer, TK_SEL_BYTES_AT_ONCE, &type);
437: if (count > TK_SEL_BYTES_AT_ONCE) {
438: panic("selection handler returned too many bytes");
439: }
440: if (count < 0) {
441: cantget:
442: Tcl_AppendResult(interp, "selection doesn't exist",
443: " or form \"", Tk_GetAtomName(tkwin, target),
444: "\" not defined", (char *) NULL);
445: return TCL_ERROR;
446: }
447: buffer[count] = 0;
448: return (*proc)(clientData, interp, buffer);
449: }
450: if (selPtr->target == target) {
451: break;
452: }
453: }
454: offset = 0;
455: while (1) {
456: count = (*selPtr->proc)(selPtr->clientData, offset,
457: buffer, TK_SEL_BYTES_AT_ONCE);
458: if (count < 0) {
459: goto cantget;
460: }
461: if (count > TK_SEL_BYTES_AT_ONCE) {
462: panic("selection handler returned too many bytes");
463: }
464: buffer[count] = '\0';
465: result = (*proc)(clientData, interp, buffer);
466: if (result != TCL_OK) {
467: return result;
468: }
469: if (count < TK_SEL_BYTES_AT_ONCE) {
470: return TCL_OK;
471: }
472: offset += count;
473: }
474: }
475:
476: /*
477: * The selection is owned by some other process. To
478: * retrieve it, first record information about the retrieval
479: * in progress. Also, try to use a non-top-level window
480: * as the requestor (property changes on this window may
481: * be monitored by a window manager, which will waste time).
482: */
483:
484: retr.interp = interp;
485: if ((winPtr->flags & TK_TOP_LEVEL)
486: && (winPtr->childList != NULL)) {
487: winPtr = winPtr->childList;
488: }
489: retr.winPtr = winPtr;
490: retr.property = XA_PRIMARY;
491: retr.target = target;
492: retr.proc = proc;
493: retr.clientData = clientData;
494: retr.result = -1;
495: retr.idleTime = 0;
496: retr.nextPtr = pendingRetrievals;
497: pendingRetrievals = &retr;
498:
499: /*
500: * Initiate the request for the selection.
501: */
502:
503: XConvertSelection(winPtr->display, XA_PRIMARY, target,
504: retr.property, winPtr->window, TkCurrentTime(dispPtr));
505:
506: /*
507: * Enter a loop processing X events until the selection
508: * has been retrieved and processed. If no response is
509: * received within a few seconds, then timeout.
510: */
511:
512: retr.timeout = Tk_CreateTimerHandler(1000, SelTimeoutProc,
513: (ClientData) &retr);
514: while (retr.result == -1) {
515: Tk_DoOneEvent(0);
516: }
517: Tk_DeleteTimerHandler(retr.timeout);
518:
519: /*
520: * Unregister the information about the selection retrieval
521: * in progress.
522: */
523:
524: if (pendingRetrievals == &retr) {
525: pendingRetrievals = retr.nextPtr;
526: } else {
527: RetrievalInfo *retrPtr;
528:
529: for (retrPtr = pendingRetrievals; retrPtr != NULL;
530: retrPtr = retrPtr->nextPtr) {
531: if (retrPtr->nextPtr == &retr) {
532: retrPtr->nextPtr = retr.nextPtr;
533: break;
534: }
535: }
536: }
537: return retr.result;
538: }
539:
540: /*
541: *--------------------------------------------------------------
542: *
543: * Tk_SelectionCmd --
544: *
545: * This procedure is invoked to process the "selection" Tcl
546: * command. See the user documentation for details on what
547: * it does.
548: *
549: * Results:
550: * A standard Tcl result.
551: *
552: * Side effects:
553: * See the user documentation.
554: *
555: *--------------------------------------------------------------
556: */
557:
558: int
559: Tk_SelectionCmd(clientData, interp, argc, argv)
560: ClientData clientData; /* Main window associated with
561: * interpreter. */
562: Tcl_Interp *interp; /* Current interpreter. */
563: int argc; /* Number of arguments. */
564: char **argv; /* Argument strings. */
565: {
566: Tk_Window tkwin = (Tk_Window) clientData;
567: int length;
568: char *cmd = argv[0];
569: char c;
570:
571: if (argc < 2) {
572: sprintf(interp->result,
573: "wrong # args: should be \"%.50s [-window win] option ?arg arg ...?\"",
574: cmd);
575: return TCL_ERROR;
576: }
577:
578: argc--; argv++;
579: c = argv[0][0];
580: length = strlen(argv[0]);
581:
582: if ((c == '-') && (strncmp(argv[0], "-window", length) == 0)) {
583: if ((argc < 2) ||
584: ((tkwin = Tk_NameToWindow(interp, argv[1], tkwin)) == NULL)) {
585: sprintf(interp->result, "bad arg to %s -window", cmd);
586: return TCL_ERROR;
587: }
588: argc -= 2; argv += 2;
589:
590: if (argc == 0) {
591: sprintf(interp->result, "not enough args to %s", cmd);
592: return TCL_ERROR;
593: }
594:
595: c = argv[0][0];
596: length = strlen(argv[0]);
597: }
598:
599: if ((c == 'g') && (strncmp(argv[0], "get", length) == 0)) {
600: Atom target;
601: GetInfo getInfo;
602: int result;
603:
604: argc--; argv++;
605:
606: if (argc > 1) {
607: sprintf(interp->result,
608: "too may args: should be \"%.50s get ?type?\"",
609: cmd);
610: return TCL_ERROR;
611: }
612: if (argc == 1) {
613: target = Tk_InternAtom(tkwin, argv[0]);
614: } else {
615: target = XA_STRING;
616: }
617: getInfo.string = (char *) ckalloc(100);
618: getInfo.bytesAvl = 100;
619: getInfo.bytesUsed = 0;
620: result = Tk_GetSelection(interp, tkwin, target, SelGetProc,
621: (ClientData) &getInfo);
622: if (result == TCL_OK) {
623: Tcl_SetResult(interp, getInfo.string, TCL_DYNAMIC);
624: } else {
625: ckfree(getInfo.string);
626: }
627: return result;
628: } else if ((c == 'h') && (strncmp(argv[0], "handle", length) == 0)) {
629: Tk_Window window;
630: Atom target, format;
631: register CommandInfo *cmdInfoPtr;
632: int cmdLength;
633:
634: argc--; argv++;
635:
636: if ((argc < 2) || (argc > 4)) {
637: Tcl_AppendResult(interp, "wrong # args: should be \"", cmd,
638: " handle window command ?type? ?format?\"", (char *) NULL);
639: return TCL_ERROR;
640: }
641: window = Tk_NameToWindow(interp, argv[0], tkwin);
642: if (window == NULL) {
643: return TCL_ERROR;
644: }
645: if (argc > 2) {
646: target = Tk_InternAtom(window, argv[2]);
647: } else {
648: target = XA_STRING;
649: }
650: if (argc > 3) {
651: format = Tk_InternAtom(window, argv[3]);
652: } else {
653: format = XA_STRING;
654: }
655: cmdLength = strlen(argv[1]);
656: cmdInfoPtr = (CommandInfo *) ckalloc((unsigned) (sizeof(CommandInfo)
657: + cmdLength));
658: cmdInfoPtr->interp = interp;
659: cmdInfoPtr->cmdLength = cmdLength;
660: strcpy(cmdInfoPtr->command, argv[1]);
661: Tk_CreateSelHandler(window, target, HandleTclCommand,
662: (ClientData) cmdInfoPtr, format);
663: return TCL_OK;
664: } else {
665: sprintf(interp->result,
666: "bad option to \"%.50s\": must be get or handle",
667: cmd);
668: return TCL_ERROR;
669: }
670: }
671:
672: /*
673: *----------------------------------------------------------------------
674: *
675: * TkSelDeadWindow --
676: *
677: * This procedure is invoked just before a TkWindow is deleted.
678: * It performs selection-related cleanup.
679: *
680: * Results:
681: * None.
682: *
683: * Side effects:
684: * Frees up memory associated with the selection.
685: *
686: *----------------------------------------------------------------------
687: */
688:
689: void
690: TkSelDeadWindow(winPtr)
691: register TkWindow *winPtr; /* Window that's being deleted. */
692: {
693: register TkSelHandler *selPtr;
694:
695: while (1) {
696: selPtr = winPtr->selHandlerList;
697: if (selPtr == NULL) {
698: break;
699: }
700: winPtr->selHandlerList = selPtr->nextPtr;
701: ckfree((char *) selPtr);
702: }
703: winPtr->selClearProc = NULL;
704:
705: if (winPtr->dispPtr->selectionOwner == (Tk_Window) winPtr) {
706: winPtr->dispPtr->selectionOwner = NULL;
707: }
708: }
709:
710: /*
711: *----------------------------------------------------------------------
712: *
713: * SelInit --
714: *
715: * Initialize selection-related information for a display.
716: *
717: * Results:
718: * None.
719: *
720: * Side effects:
721: * .
722: *
723: *----------------------------------------------------------------------
724: */
725:
726: static void
727: SelInit(tkwin)
728: Tk_Window tkwin; /* Window token (used to find
729: * display to initialize). */
730: {
731: register TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
732:
733: /*
734: * Fetch commonly-used atoms.
735: */
736:
737: dispPtr->multipleAtom = Tk_InternAtom(tkwin, "MULTIPLE");
738: dispPtr->incrAtom = Tk_InternAtom(tkwin, "INCR");
739: dispPtr->targetsAtom = Tk_InternAtom(tkwin, "TARGETS");
740: dispPtr->timestampAtom = Tk_InternAtom(tkwin, "TIMESTAMP");
741: dispPtr->textAtom = Tk_InternAtom(tkwin, "TEXT");
742: dispPtr->compoundTextAtom = Tk_InternAtom(tkwin, "COMPOUND_TEXT");
743: }
744:
745: /*
746: *--------------------------------------------------------------
747: *
748: * TkSelEventProc --
749: *
750: * This procedure is invoked whenever a selection-related
751: * event occurs. It does the lion's share of the work
752: * in implementing the selection protocol.
753: *
754: * Results:
755: * None.
756: *
757: * Side effects:
758: * Lots: depends on the type of event.
759: *
760: *--------------------------------------------------------------
761: */
762:
763: void
764: TkSelEventProc(tkwin, eventPtr)
765: Tk_Window tkwin; /* Window for which event was
766: * targeted. */
767: register XEvent *eventPtr; /* X event: either SelectionClear,
768: * SelectionRequest, or
769: * SelectionNotify. */
770: {
771: register TkWindow *winPtr = (TkWindow *) tkwin;
772:
773: /*
774: * Case #1: SelectionClear events. Invoke clear procedure
775: * for window that just lost the selection. This code is a
776: * bit tricky, because any callbacks to due selection changes
777: * between windows managed by the process have already been
778: * made. Thus, ignore the event unless it refers to the
779: * window that's currently the selection owner and the event
780: * was generated after the server saw the SetSelectionOwner
781: * request.
782: */
783:
784: if (eventPtr->type == SelectionClear) {
785: if ((eventPtr->xselectionclear.selection == XA_PRIMARY)
786: && (winPtr->dispPtr->selectionOwner == tkwin)
787: && (eventPtr->xselectionclear.serial
788: >= winPtr->dispPtr->selectionSerial)
789: && (winPtr->selClearProc != NULL)) {
790: (*winPtr->selClearProc)(winPtr->selClearData);
791: winPtr->selClearProc = NULL;
792: winPtr->dispPtr->selectionOwner = NULL;
793: }
794: return;
795: }
796:
797: /*
798: * Case #2: SelectionNotify events. Call the relevant procedure
799: * to handle the incoming selection.
800: */
801:
802: if (eventPtr->type == SelectionNotify) {
803: register RetrievalInfo *retrPtr;
804: char *propInfo;
805: Atom type;
806: int format, result;
807: unsigned long numItems, bytesAfter;
808:
809: for (retrPtr = pendingRetrievals; ; retrPtr = retrPtr->nextPtr) {
810: if (retrPtr == NULL) {
811: return;
812: }
813: if ((retrPtr->winPtr == winPtr)
814: && (eventPtr->xselection.selection == XA_PRIMARY)
815: && (retrPtr->target == eventPtr->xselection.target)
816: && (retrPtr->result == -1)) {
817: if (retrPtr->property == eventPtr->xselection.property) {
818: break;
819: }
820: if (eventPtr->xselection.property == None) {
821: Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
822: Tcl_AppendResult(retrPtr->interp,
823: "selection doesn't exist or form \"",
824: Tk_GetAtomName(tkwin, retrPtr->target),
825: "\" not defined", (char *) NULL);
826: retrPtr->result = TCL_ERROR;
827: return;
828: }
829: }
830: }
831:
832: propInfo = NULL;
833: result = XGetWindowProperty(eventPtr->xselection.display,
834: eventPtr->xselection.requestor, retrPtr->property,
835: 0, MAX_PROP_WORDS, False, (Atom) AnyPropertyType,
836: &type, &format, &numItems, &bytesAfter,
837: (unsigned char **) &propInfo);
838: if ((result != Success) || (type == None)) {
839: return;
840: }
841: if (bytesAfter != 0) {
842: Tcl_SetResult(retrPtr->interp, "selection property too large",
843: TCL_STATIC);
844: retrPtr->result = TCL_ERROR;
845: XFree(propInfo);
846: return;
847: }
848: if ((type == XA_STRING) || (type == winPtr->dispPtr->textAtom)
849: || (type == winPtr->dispPtr->compoundTextAtom)) {
850: if (format != 8) {
851: sprintf(retrPtr->interp->result,
852: "bad format for string selection: wanted \"8\", got \"%d\"",
853: format);
854: retrPtr->result = TCL_ERROR;
855: return;
856: }
857: retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
858: retrPtr->interp, propInfo);
859: } else if (type == winPtr->dispPtr->incrAtom) {
860:
861: /*
862: * It's a !?#@!?!! INCR-style reception. Arrange to receive
863: * the selection in pieces, using the ICCCM protocol, then
864: * hang around until either the selection is all here or a
865: * timeout occurs.
866: */
867:
868: retrPtr->idleTime = 0;
869: Tk_CreateEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
870: (ClientData) retrPtr);
871: XDeleteProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
872: retrPtr->property);
873: while (retrPtr->result == -1) {
874: Tk_DoOneEvent(0);
875: }
876: Tk_DeleteEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
877: (ClientData) retrPtr);
878: } else {
879: char *string;
880:
881: if (format != 32) {
882: sprintf(retrPtr->interp->result,
883: "bad format for selection: wanted \"32\", got \"%d\"",
884: format);
885: retrPtr->result = TCL_ERROR;
886: return;
887: }
888: string = SelCvtFromX((long *) propInfo, (int) numItems, type,
889: (Tk_Window) winPtr);
890: retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
891: retrPtr->interp, string);
892: ckfree(string);
893: }
894: XFree(propInfo);
895: return;
896: }
897:
898: /*
899: * Case #3: SelectionRequest events. Call ConvertSelection to
900: * do the dirty work.
901: */
902:
903: if ((eventPtr->type == SelectionRequest)
904: && (eventPtr->xselectionrequest.selection == XA_PRIMARY)) {
905: ConvertSelection(winPtr, &eventPtr->xselectionrequest);
906: return;
907: }
908: }
909:
910: /*
911: *--------------------------------------------------------------
912: *
913: * SelGetProc --
914: *
915: * This procedure is invoked to process pieces of the
916: * selection as they arrive during "selection get"
917: * commands.
918: *
919: * Results:
920: * Always returns TCL_OK.
921: *
922: * Side effects:
923: * Bytes get appended to the result currently stored
924: * in interp->result, and its memory area gets
925: * expanded if necessary.
926: *
927: *--------------------------------------------------------------
928: */
929:
930: /* ARGSUSED */
931: static int
932: SelGetProc(clientData, interp, portion)
933: ClientData clientData; /* Information about partially-
934: * assembled result. */
935: Tcl_Interp *interp; /* Interpreter used for error
936: * reporting (not used). */
937: char *portion; /* New information to be appended. */
938: {
939: register GetInfo *getInfoPtr = (GetInfo *) clientData;
940: int newLength;
941:
942: newLength = strlen(portion) + getInfoPtr->bytesUsed;
943:
944: /*
945: * Grow the result area if we've run out of space.
946: */
947:
948: if (newLength >= getInfoPtr->bytesAvl) {
949: char *newString;
950:
951: getInfoPtr->bytesAvl *= 2;
952: if (getInfoPtr->bytesAvl <= newLength) {
953: getInfoPtr->bytesAvl = newLength + 1;
954: }
955: newString = (char *) ckalloc((unsigned) getInfoPtr->bytesAvl);
956: memcpy((VOID *) newString, (VOID *) getInfoPtr->string,
957: getInfoPtr->bytesUsed);
958: ckfree(getInfoPtr->string);
959: getInfoPtr->string = newString;
960: }
961:
962: /*
963: * Append the new data to what was already there.
964: */
965:
966: strcpy(getInfoPtr->string + getInfoPtr->bytesUsed, portion);
967: getInfoPtr->bytesUsed = newLength;
968: return TCL_OK;
969: }
970:
971: /*
972: *----------------------------------------------------------------------
973: *
974: * SelCvtToX --
975: *
976: * Given a selection represented as a string (the normal Tcl form),
977: * convert it to the ICCCM-mandated format for X, depending on
978: * the type argument. This procedure and SelCvtFromX are inverses.
979: *
980: * Results:
981: * The return value is a malloc'ed buffer holding a value
982: * equivalent to "string", but formatted as for "type". It is
983: * the caller's responsibility to free the string when done with
984: * it. The word at *numLongsPtr is filled in with the number of
985: * 32-bit words returned in the result.
986: *
987: * Side effects:
988: * None.
989: *
990: *----------------------------------------------------------------------
991: */
992:
993: static long *
994: SelCvtToX(string, type, tkwin, numLongsPtr)
995: char *string; /* String representation of selection. */
996: Atom type; /* Atom specifying the X format that is
997: * desired for the selection. Should not
998: * be XA_STRING (if so, don't bother calling
999: * this procedure at all). */
1000: Tk_Window tkwin; /* Window that governs atom conversion. */
1001: int *numLongsPtr; /* Number of 32-bit words contained in the
1002: * result. */
1003: {
1004: register char *p;
1005: char *field;
1006: int numFields;
1007: long *propPtr, *longPtr;
1008: #define MAX_ATOM_NAME_LENGTH 100
1009: char atomName[MAX_ATOM_NAME_LENGTH+1];
1010:
1011: /*
1012: * The string is assumed to consist of fields separated by spaces.
1013: * The property gets generated by converting each field to an
1014: * integer number, in one of two ways:
1015: * 1. If type is XA_ATOM, convert each field to its corresponding
1016: * atom.
1017: * 2. If type is anything else, convert each field from an ASCII number
1018: * to a 32-bit binary number.
1019: */
1020:
1021: numFields = 1;
1022: for (p = string; *p != 0; p++) {
1023: if (isspace(*p)) {
1024: numFields++;
1025: }
1026: }
1027: propPtr = (long *) ckalloc((unsigned) numFields*sizeof(long));
1028:
1029: /*
1030: * Convert the fields one-by-one.
1031: */
1032:
1033: for (longPtr = propPtr, *numLongsPtr = 0, p = string;
1034: ; longPtr++, (*numLongsPtr)++) {
1035: while (isspace(*p)) {
1036: p++;
1037: }
1038: if (*p == 0) {
1039: break;
1040: }
1041: field = p;
1042: while ((*p != 0) && !isspace(*p)) {
1043: p++;
1044: }
1045: if (type == XA_ATOM) {
1046: int length;
1047:
1048: length = p - field;
1049: if (length > MAX_ATOM_NAME_LENGTH) {
1050: length = MAX_ATOM_NAME_LENGTH;
1051: }
1052: strncpy(atomName, field, length);
1053: atomName[length] = 0;
1054: *longPtr = (long) Tk_InternAtom(tkwin, atomName);
1055: } else {
1056: char *dummy;
1057:
1058: *longPtr = strtol(field, &dummy, 0);
1059: }
1060: }
1061: return propPtr;
1062: }
1063:
1064: /*
1065: *----------------------------------------------------------------------
1066: *
1067: * SelCvtFromX --
1068: *
1069: * Given an X property value, formatted as a collection of 32-bit
1070: * values according to "type" and the ICCCM conventions, convert
1071: * the value to a string suitable for manipulation by Tcl. This
1072: * procedure is the inverse of SelCvtToX.
1073: *
1074: * Results:
1075: * The return value is the string equivalent of "property". It is
1076: * malloc-ed and should be freed by the caller when no longer
1077: * needed.
1078: *
1079: * Side effects:
1080: * None.
1081: *
1082: *----------------------------------------------------------------------
1083: */
1084:
1085: static char *
1086: SelCvtFromX(propPtr, numValues, type, tkwin)
1087: register long *propPtr; /* Property value from X. */
1088: int numValues; /* Number of 32-bit values in property. */
1089: Atom type; /* Type of property Should not be
1090: * XA_STRING (if so, don't bother calling
1091: * this procedure at all). */
1092: Tk_Window tkwin; /* Window to use for atom conversion. */
1093: {
1094: char *result;
1095: int resultSpace, curSize, fieldSize;
1096: char *atomName;
1097:
1098: /*
1099: * Convert each long in the property to a string value, which is
1100: * either the name of an atom (if type is XA_ATOM) or a hexadecimal
1101: * string. Make an initial guess about the size of the result, but
1102: * be prepared to enlarge the result if necessary.
1103: */
1104:
1105: resultSpace = 12*numValues;
1106: curSize = 0;
1107: atomName = ""; /* Not needed, but eliminates compiler warning. */
1108: result = (char *) ckalloc((unsigned) resultSpace);
1109: for ( ; numValues > 0; propPtr++, numValues--) {
1110: if (type == XA_ATOM) {
1111: atomName = Tk_GetAtomName(tkwin, (Atom) *propPtr);
1112: fieldSize = strlen(atomName) + 1;
1113: } else {
1114: fieldSize = 12;
1115: }
1116: if (curSize+fieldSize >= resultSpace) {
1117: char *newResult;
1118:
1119: resultSpace *= 2;
1120: if (curSize+fieldSize >= resultSpace) {
1121: resultSpace = curSize + fieldSize + 1;
1122: }
1123: newResult = (char *) ckalloc((unsigned) resultSpace);
1124: strcpy(newResult, result);
1125: ckfree(result);
1126: result = newResult;
1127: }
1128: if (curSize != 0) {
1129: result[curSize] = ' ';
1130: curSize++;
1131: }
1132: if (type == XA_ATOM) {
1133: strcpy(result+curSize, atomName);
1134: } else {
1135: sprintf(result+curSize, "%#x", *propPtr);
1136: }
1137: curSize += strlen(result+curSize);
1138: }
1139: return result;
1140: }
1141:
1142: /*
1143: *----------------------------------------------------------------------
1144: *
1145: * ConvertSelection --
1146: *
1147: * This procedure is invoked to handle SelectionRequest events.
1148: * It responds to the requests, obeying the ICCCM protocols.
1149: *
1150: * Results:
1151: * None.
1152: *
1153: * Side effects:
1154: * Properties are created for the selection requestor, and a
1155: * SelectionNotify event is generated for the selection
1156: * requestor. In the event of long selections, this procedure
1157: * implements INCR-mode transfers, using the ICCCM protocol.
1158: *
1159: *----------------------------------------------------------------------
1160: */
1161:
1162: static void
1163: ConvertSelection(winPtr, eventPtr)
1164: TkWindow *winPtr; /* Window that owns selection. */
1165: register XSelectionRequestEvent *eventPtr;
1166: /* Event describing request. */
1167: {
1168: XSelectionEvent reply; /* Used to notify requestor that
1169: * selection info is ready. */
1170: int multiple; /* Non-zero means a MULTIPLE request
1171: * is being handled. */
1172: IncrInfo info; /* State of selection conversion. */
1173: Atom singleInfo[2]; /* info.multAtoms points here except
1174: * for multiple conversions. */
1175: int i;
1176: Tk_ErrorHandler errorHandler;
1177:
1178: errorHandler = Tk_CreateErrorHandler(eventPtr->display, -1, -1,-1,
1179: (int (*)()) NULL, (ClientData) NULL);
1180:
1181: /*
1182: * Initialize the reply event.
1183: */
1184:
1185: reply.type = SelectionNotify;
1186: reply.serial = 0;
1187: reply.send_event = True;
1188: reply.display = eventPtr->display;
1189: reply.requestor = eventPtr->requestor;
1190: reply.selection = XA_PRIMARY;
1191: reply.target = eventPtr->target;
1192: reply.property = eventPtr->property;
1193: if (reply.property == None) {
1194: reply.property = reply.target;
1195: }
1196: reply.time = eventPtr->time;
1197:
1198: /*
1199: * Watch out for races between conversion requests and
1200: * selection ownership changes: reject the conversion
1201: * request if it's for the wrong window or the wrong
1202: * time.
1203: */
1204:
1205: if ((winPtr->dispPtr->selectionOwner != (Tk_Window) winPtr)
1206: || ((eventPtr->time < winPtr->dispPtr->selectionTime)
1207: && (eventPtr->time != CurrentTime)
1208: && (winPtr->dispPtr->selectionTime != CurrentTime))) {
1209: goto refuse;
1210: }
1211:
1212: /*
1213: * Figure out which kind(s) of conversion to perform. If handling
1214: * a MULTIPLE conversion, then read the property describing which
1215: * conversions to perform.
1216: */
1217:
1218: info.winPtr = winPtr;
1219: if (eventPtr->target != winPtr->dispPtr->multipleAtom) {
1220: multiple = 0;
1221: singleInfo[0] = reply.target;
1222: singleInfo[1] = reply.property;
1223: info.multAtoms = singleInfo;
1224: info.numConversions = 1;
1225: } else {
1226: Atom type;
1227: int format, result;
1228: unsigned long bytesAfter;
1229:
1230: multiple = 1;
1231: info.multAtoms = NULL;
1232: if (eventPtr->property == None) {
1233: goto refuse;
1234: }
1235: result = XGetWindowProperty(eventPtr->display,
1236: eventPtr->requestor, eventPtr->property,
1237: 0, MAX_PROP_WORDS, False, XA_ATOM,
1238: &type, &format, &info.numConversions, &bytesAfter,
1239: (unsigned char **) &info.multAtoms);
1240: if ((result != Success) || (bytesAfter != 0) || (format != 32)
1241: || (type == None)) {
1242: if (info.multAtoms != NULL) {
1243: XFree((char *) info.multAtoms);
1244: }
1245: goto refuse;
1246: }
1247: info.numConversions /= 2; /* Two atoms per conversion. */
1248: }
1249:
1250: /*
1251: * Loop through all of the requested conversions, and either return
1252: * the entire converted selection, if it can be returned in a single
1253: * bunch, or return INCR information only (the actual selection will
1254: * be returned below).
1255: */
1256:
1257: info.offsets = (int *) ckalloc((unsigned) (info.numConversions*sizeof(int)));
1258: info.numIncrs = 0;
1259: for (i = 0; i < info.numConversions; i++) {
1260: Atom target, property;
1261: long buffer[TK_SEL_WORDS_AT_ONCE];
1262: register TkSelHandler *selPtr;
1263:
1264: target = info.multAtoms[2*i];
1265: property = info.multAtoms[2*i + 1];
1266: info.offsets[i] = -1;
1267:
1268: for (selPtr = winPtr->selHandlerList; ; selPtr = selPtr->nextPtr) {
1269: int numItems, format;
1270: char *propPtr;
1271: Atom type;
1272:
1273: if (selPtr == NULL) {
1274:
1275: /*
1276: * Nobody seems to know about this kind of request. If
1277: * it's of a sort that we can handle without any help, do
1278: * it. Otherwise mark the request as an errror.
1279: */
1280:
1281: numItems = DefaultSelection(winPtr, target, (char *) buffer,
1282: TK_SEL_BYTES_AT_ONCE, &type);
1283: if (numItems != 0) {
1284: goto gotStuff;
1285: }
1286: info.multAtoms[2*i + 1] = None;
1287: break;
1288: } else if (selPtr->target == target) {
1289: numItems = (*selPtr->proc)(selPtr->clientData, 0,
1290: (char *) buffer, TK_SEL_BYTES_AT_ONCE);
1291: if (numItems < 0) {
1292: info.multAtoms[2*i + 1] = None;
1293: break;
1294: }
1295: if (numItems > TK_SEL_BYTES_AT_ONCE) {
1296: panic("selection handler returned too many bytes");
1297: }
1298: ((char *) buffer)[numItems] = '\0';
1299: type = selPtr->format;
1300: } else {
1301: continue;
1302: }
1303:
1304: gotStuff:
1305: if (numItems == TK_SEL_BYTES_AT_ONCE) {
1306: info.numIncrs++;
1307: type = winPtr->dispPtr->incrAtom;
1308: buffer[0] = 10; /* Guess at # items avl. */
1309: numItems = 1;
1310: propPtr = (char *) buffer;
1311: format = 32;
1312: info.offsets[i] = 0;
1313: } else if (type == XA_STRING) {
1314: propPtr = (char *) buffer;
1315: format = 8;
1316: } else {
1317: propPtr = (char *) SelCvtToX((char *) buffer,
1318: type, (Tk_Window) winPtr, &numItems);
1319: format = 32;
1320: }
1321: XChangeProperty(reply.display, reply.requestor,
1322: property, type, format, PropModeReplace,
1323: (unsigned char *) propPtr, numItems);
1324: if (propPtr != (char *) buffer) {
1325: ckfree(propPtr);
1326: }
1327: break;
1328: }
1329: }
1330:
1331: /*
1332: * Send an event back to the requestor to indicate that the
1333: * first stage of conversion is complete (everything is done
1334: * except for long conversions that have to be done in INCR
1335: * mode).
1336: */
1337:
1338: if (info.numIncrs > 0) {
1339: XSelectInput(reply.display, reply.requestor, PropertyChangeMask);
1340: info.timeout = Tk_CreateTimerHandler(1000, IncrTimeoutProc,
1341: (ClientData) &info);
1342: info.idleTime = 0;
1343: info.reqWindow = reply.requestor;
1344: info.time = winPtr->dispPtr->selectionTime;
1345: info.nextPtr = pendingIncrs;
1346: pendingIncrs = &info;
1347: }
1348: if (multiple) {
1349: XChangeProperty(reply.display, reply.requestor, reply.property,
1350: XA_ATOM, 32, PropModeReplace,
1351: (unsigned char *) info.multAtoms,
1352: (int) info.numConversions*2);
1353: } else {
1354:
1355: /*
1356: * Not a MULTIPLE request. The first property in "multAtoms"
1357: * got set to None if there was an error in conversion.
1358: */
1359:
1360: reply.property = info.multAtoms[1];
1361: }
1362: XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
1363: Tk_DeleteErrorHandler(errorHandler);
1364:
1365: /*
1366: * Handle any remaining INCR-mode transfers. This all happens
1367: * in callbacks to TkSelPropProc, so just wait until the number
1368: * of uncompleted INCR transfers drops to zero.
1369: */
1370:
1371: if (info.numIncrs > 0) {
1372: IncrInfo *infoPtr2;
1373:
1374: while (info.numIncrs > 0) {
1375: Tk_DoOneEvent(0);
1376: }
1377: Tk_DeleteTimerHandler(info.timeout);
1378: errorHandler = Tk_CreateErrorHandler(winPtr->display,
1379: -1, -1,-1, (int (*)()) NULL, (ClientData) NULL);
1380: XSelectInput(reply.display, reply.requestor, 0L);
1381: Tk_DeleteErrorHandler(errorHandler);
1382: if (pendingIncrs == &info) {
1383: pendingIncrs = info.nextPtr;
1384: } else {
1385: for (infoPtr2 = pendingIncrs; infoPtr2 != NULL;
1386: infoPtr2 = infoPtr2->nextPtr) {
1387: if (infoPtr2->nextPtr == &info) {
1388: infoPtr2->nextPtr = info.nextPtr;
1389: break;
1390: }
1391: }
1392: }
1393: }
1394:
1395: /*
1396: * All done. Cleanup and return.
1397: */
1398:
1399: ckfree((char *) info.offsets);
1400: if (multiple) {
1401: XFree((char *) info.multAtoms);
1402: }
1403: return;
1404:
1405: /*
1406: * An error occurred. Send back a refusal message.
1407: */
1408:
1409: refuse:
1410: reply.property = None;
1411: XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
1412: Tk_DeleteErrorHandler(errorHandler);
1413: return;
1414: }
1415:
1416: /*
1417: *----------------------------------------------------------------------
1418: *
1419: * SelRcvIncrProc --
1420: *
1421: * This procedure handles the INCR protocol on the receiving
1422: * side. It is invoked in response to property changes on
1423: * the requestor's window (which hopefully are because a new
1424: * chunk of the selection arrived).
1425: *
1426: * Results:
1427: * None.
1428: *
1429: * Side effects:
1430: * If a new piece of selection has arrived, a procedure is
1431: * invoked to deal with that piece. When the whole selection
1432: * is here, a flag is left for the higher-level procedure that
1433: * initiated the selection retrieval.
1434: *
1435: *----------------------------------------------------------------------
1436: */
1437:
1438: static void
1439: SelRcvIncrProc(clientData, eventPtr)
1440: ClientData clientData; /* Information about retrieval. */
1441: register XEvent *eventPtr; /* X PropertyChange event. */
1442: {
1443: register RetrievalInfo *retrPtr = (RetrievalInfo *) clientData;
1444: char *propInfo;
1445: Atom type;
1446: int format, result;
1447: unsigned long numItems, bytesAfter;
1448:
1449: if ((eventPtr->xproperty.atom != retrPtr->property)
1450: || (eventPtr->xproperty.state != PropertyNewValue)
1451: || (retrPtr->result != -1)) {
1452: return;
1453: }
1454: propInfo = NULL;
1455: result = XGetWindowProperty(eventPtr->xproperty.display,
1456: eventPtr->xproperty.window, retrPtr->property, 0, MAX_PROP_WORDS,
1457: True, (Atom) AnyPropertyType, &type, &format, &numItems,
1458: &bytesAfter, (unsigned char **) &propInfo);
1459: if ((result != Success) || (type == None)) {
1460: return;
1461: }
1462: if (bytesAfter != 0) {
1463: Tcl_SetResult(retrPtr->interp, "selection property too large",
1464: TCL_STATIC);
1465: retrPtr->result = TCL_ERROR;
1466: goto done;
1467: }
1468: if (numItems == 0) {
1469: retrPtr->result = TCL_OK;
1470: } else if ((type == XA_STRING)
1471: || (type == retrPtr->winPtr->dispPtr->textAtom)
1472: || (type == retrPtr->winPtr->dispPtr->compoundTextAtom)) {
1473: if (format != 8) {
1474: Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
1475: sprintf(retrPtr->interp->result,
1476: "bad format for string selection: wanted \"8\", got \"%d\"",
1477: format);
1478: retrPtr->result = TCL_ERROR;
1479: goto done;
1480: }
1481: result = (*retrPtr->proc)(retrPtr->clientData, retrPtr->interp,
1482: propInfo);
1483: if (result != TCL_OK) {
1484: retrPtr->result = result;
1485: }
1486: } else {
1487: char *string;
1488:
1489: if (format != 32) {
1490: Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
1491: sprintf(retrPtr->interp->result,
1492: "bad format for selection: wanted \"32\", got \"%d\"",
1493: format);
1494: retrPtr->result = TCL_ERROR;
1495: goto done;
1496: }
1497: string = SelCvtFromX((long *) propInfo, (int) numItems, type,
1498: (Tk_Window) retrPtr->winPtr);
1499: result = (*retrPtr->proc)(retrPtr->clientData, retrPtr->interp,
1500: string);
1501: if (result != TCL_OK) {
1502: retrPtr->result = result;
1503: }
1504: ckfree(string);
1505: }
1506:
1507: done:
1508: XFree(propInfo);
1509: retrPtr->idleTime = 0;
1510: }
1511:
1512: /*
1513: *----------------------------------------------------------------------
1514: *
1515: * TkSelPropProc --
1516: *
1517: * This procedure is invoked when property-change events
1518: * occur on windows not known to the toolkit. Its function
1519: * is to implement the sending side of the INCR selection
1520: * retrieval protocol when the selection requestor deletes
1521: * the property containing a part of the selection.
1522: *
1523: * Results:
1524: * None.
1525: *
1526: * Side effects:
1527: * If the property that is receiving the selection was just
1528: * deleted, then a new piece of the selection is fetched and
1529: * placed in the property, until eventually there's no more
1530: * selection to fetch.
1531: *
1532: *----------------------------------------------------------------------
1533: */
1534:
1535: void
1536: TkSelPropProc(eventPtr)
1537: register XEvent *eventPtr; /* X PropertyChange event. */
1538: {
1539: register IncrInfo *infoPtr;
1540: int i, format;
1541: Atom target;
1542: register TkSelHandler *selPtr;
1543: long buffer[TK_SEL_WORDS_AT_ONCE];
1544: int numItems;
1545: char *propPtr;
1546: Tk_ErrorHandler errorHandler;
1547:
1548: /*
1549: * See if this event announces the deletion of a property being
1550: * used for an INCR transfer. If so, then add the next chunk of
1551: * data to the property.
1552: */
1553:
1554: if (eventPtr->xproperty.state != PropertyDelete) {
1555: return;
1556: }
1557: for (infoPtr = pendingIncrs; infoPtr != NULL;
1558: infoPtr = infoPtr->nextPtr) {
1559:
1560: /*
1561: * To avoid races between selection conversions and
1562: * changes in selection ownership, make sure the window
1563: * and timestamp for the current selection match those
1564: * in the INCR request.
1565: */
1566:
1567: if ((infoPtr->reqWindow != eventPtr->xproperty.window)
1568: || (infoPtr->winPtr->dispPtr->selectionOwner
1569: != (Tk_Window) infoPtr->winPtr)
1570: || (infoPtr->winPtr->dispPtr->selectionTime
1571: != infoPtr->time)) {
1572: continue;
1573: }
1574: for (i = 0; i < infoPtr->numConversions; i++) {
1575: if ((eventPtr->xproperty.atom != infoPtr->multAtoms[2*i + 1])
1576: || (infoPtr->offsets[i] == -1)){
1577: continue;
1578: }
1579: target = infoPtr->multAtoms[2*i];
1580: infoPtr->idleTime = 0;
1581: for (selPtr = infoPtr->winPtr->selHandlerList; ;
1582: selPtr = selPtr->nextPtr) {
1583: if (selPtr == NULL) {
1584: infoPtr->multAtoms[2*i + 1] = None;
1585: infoPtr->offsets[i] = -1;
1586: infoPtr->numIncrs --;
1587: return;
1588: }
1589: if (selPtr->target == target) {
1590: if (infoPtr->offsets[i] == -2) {
1591: numItems = 0;
1592: ((char *) buffer)[0] = 0;
1593: } else {
1594: numItems = (*selPtr->proc)(selPtr->clientData,
1595: infoPtr->offsets[i], (char *) buffer,
1596: TK_SEL_BYTES_AT_ONCE);
1597: if (numItems > TK_SEL_BYTES_AT_ONCE) {
1598: panic("selection handler returned too many bytes");
1599: } else {
1600: if (numItems < 0) {
1601: numItems = 0;
1602: }
1603: }
1604: ((char *) buffer)[numItems] = '\0';
1605: }
1606: if (numItems < TK_SEL_BYTES_AT_ONCE) {
1607: if (numItems <= 0) {
1608: infoPtr->offsets[i] = -1;
1609: infoPtr->numIncrs--;
1610: } else {
1611: infoPtr->offsets[i] = -2;
1612: }
1613: } else {
1614: infoPtr->offsets[i] += numItems;
1615: }
1616: if (selPtr->format == XA_STRING) {
1617: propPtr = (char *) buffer;
1618: format = 8;
1619: } else {
1620: propPtr = (char *) SelCvtToX((char *) buffer,
1621: selPtr->format,
1622: (Tk_Window) infoPtr->winPtr,
1623: &numItems);
1624: format = 32;
1625: }
1626: errorHandler = Tk_CreateErrorHandler(
1627: eventPtr->xproperty.display, -1, -1, -1,
1628: (int (*)()) NULL, (ClientData) NULL);
1629: XChangeProperty(eventPtr->xproperty.display,
1630: eventPtr->xproperty.window,
1631: eventPtr->xproperty.atom, selPtr->format,
1632: format, PropModeReplace,
1633: (unsigned char *) propPtr, numItems);
1634: Tk_DeleteErrorHandler(errorHandler);
1635: if (propPtr != (char *) buffer) {
1636: ckfree(propPtr);
1637: }
1638: return;
1639: }
1640: }
1641: }
1642: }
1643: }
1644:
1645: /*
1646: *----------------------------------------------------------------------
1647: *
1648: * HandleTclCommand --
1649: *
1650: * This procedure acts as selection handler for handlers created
1651: * by the "selection handle" command. It invokes a Tcl command to
1652: * retrieve the selection.
1653: *
1654: * Results:
1655: * The return value is a count of the number of bytes actually
1656: * stored at buffer.
1657: *
1658: * Side effects:
1659: * None except for things done by the Tcl command.
1660: *
1661: *----------------------------------------------------------------------
1662: */
1663:
1664: static int
1665: HandleTclCommand(clientData, offset, buffer, maxBytes)
1666: ClientData clientData; /* Information about command to execute. */
1667: int offset; /* Return selection bytes starting at this
1668: * offset. */
1669: char *buffer; /* Place to store converted selection. */
1670: int maxBytes; /* Maximum # of bytes to store at buffer. */
1671: {
1672: register CommandInfo *cmdInfoPtr = (CommandInfo *) clientData;
1673: char *oldResultString;
1674: Tcl_FreeProc *oldFreeProc;
1675: int spaceNeeded, length;
1676: #define MAX_STATIC_SIZE 100
1677: char staticSpace[MAX_STATIC_SIZE];
1678: char *command;
1679:
1680: /*
1681: * First, generate a command by taking the command string
1682: * and appending the offset and maximum # of bytes.
1683: */
1684:
1685: spaceNeeded = cmdInfoPtr->cmdLength + 30;
1686: if (spaceNeeded < MAX_STATIC_SIZE) {
1687: command = staticSpace;
1688: } else {
1689: command = (char *) ckalloc((unsigned) spaceNeeded);
1690: }
1691: sprintf(command, "%s %d %d", cmdInfoPtr->command, offset, maxBytes);
1692:
1693: /*
1694: * Execute the command. Be sure to restore the state of the
1695: * interpreter after executing the command.
1696: */
1697:
1698: oldFreeProc = cmdInfoPtr->interp->freeProc;
1699: if (oldFreeProc != 0) {
1700: oldResultString = cmdInfoPtr->interp->result;
1701: } else {
1702: oldResultString = (char *) ckalloc((unsigned)
1703: (strlen(cmdInfoPtr->interp->result) + 1));
1704: strcpy(oldResultString, cmdInfoPtr->interp->result);
1705: oldFreeProc = TCL_DYNAMIC;
1706: }
1707: cmdInfoPtr->interp->freeProc = 0;
1708: if (Tcl_GlobalEval(cmdInfoPtr->interp, command) == TCL_OK) {
1709: length = strlen(cmdInfoPtr->interp->result);
1710: } else {
1711: length = 0;
1712: }
1713: if (length > maxBytes) {
1714: length = maxBytes;
1715: }
1716: memcpy((VOID *) buffer, (VOID *) cmdInfoPtr->interp->result, length);
1717: buffer[length] = '\0';
1718: Tcl_FreeResult(cmdInfoPtr->interp);
1719: cmdInfoPtr->interp->result = oldResultString;
1720: cmdInfoPtr->interp->freeProc = oldFreeProc;
1721:
1722: if (command != staticSpace) {
1723: ckfree(command);
1724: }
1725:
1726: return length;
1727: }
1728:
1729: /*
1730: *----------------------------------------------------------------------
1731: *
1732: * SelTimeoutProc --
1733: *
1734: * This procedure is invoked once every second while waiting for
1735: * the selection to be returned. After a while it gives up and
1736: * aborts the selection retrieval.
1737: *
1738: * Results:
1739: * None.
1740: *
1741: * Side effects:
1742: * A new timer callback is created to call us again in another
1743: * second, unless time has expired, in which case an error is
1744: * recorded for the retrieval.
1745: *
1746: *----------------------------------------------------------------------
1747: */
1748:
1749: static void
1750: SelTimeoutProc(clientData)
1751: ClientData clientData; /* Information about retrieval
1752: * in progress. */
1753: {
1754: register RetrievalInfo *retrPtr = (RetrievalInfo *) clientData;
1755:
1756: /*
1757: * Make sure that the retrieval is still in progress. Then
1758: * see how long it's been since any sort of response was received
1759: * from the other side.
1760: */
1761:
1762: if (retrPtr->result != -1) {
1763: return;
1764: }
1765: retrPtr->idleTime++;
1766: if (retrPtr->idleTime >= 5) {
1767:
1768: /*
1769: * Use a careful procedure to store the error message, because
1770: * the result could already be partially filled in with a partial
1771: * selection return.
1772: */
1773:
1774: Tcl_SetResult(retrPtr->interp, "selection owner didn't respond",
1775: TCL_STATIC);
1776: retrPtr->result = TCL_ERROR;
1777: } else {
1778: retrPtr->timeout = Tk_CreateTimerHandler(1000, SelTimeoutProc,
1779: (ClientData) retrPtr);
1780: }
1781: }
1782:
1783: /*
1784: *----------------------------------------------------------------------
1785: *
1786: * IncrTimeoutProc --
1787: *
1788: * This procedure is invoked once a second while sending the
1789: * selection to a requestor in INCR mode. After a while it
1790: * gives up and aborts the selection operation.
1791: *
1792: * Results:
1793: * None.
1794: *
1795: * Side effects:
1796: * A new timeout gets registered so that this procedure gets
1797: * called again in another second, unless too many seconds
1798: * have elapsed, in which case infoPtr is marked as "all done".
1799: *
1800: *----------------------------------------------------------------------
1801: */
1802:
1803: static void
1804: IncrTimeoutProc(clientData)
1805: ClientData clientData; /* Information about INCR-mode
1806: * selection retrieval for which
1807: * we are selection owner. */
1808: {
1809: register IncrInfo *infoPtr = (IncrInfo *) clientData;
1810:
1811: infoPtr->idleTime++;
1812: if (infoPtr->idleTime >= 5) {
1813: infoPtr->numIncrs = 0;
1814: } else {
1815: infoPtr->timeout = Tk_CreateTimerHandler(1000, IncrTimeoutProc,
1816: (ClientData) infoPtr);
1817: }
1818: }
1819:
1820: /*
1821: *----------------------------------------------------------------------
1822: *
1823: * DefaultSelection --
1824: *
1825: * This procedure is called to generate selection information
1826: * for a few standard targets such as TIMESTAMP and TARGETS.
1827: * It is invoked only if no handler has been declared by the
1828: * application.
1829: *
1830: * Results:
1831: * If "target" is a standard target understood by this procedure,
1832: * the selection is converted to that form and stored as a
1833: * character string in buffer. The type of the selection (e.g.
1834: * STRING or ATOM) is stored in *typePtr, and the return value is
1835: * a count of the # of non-NULL bytes at buffer. If the target
1836: * wasn't understood, or if there isn't enough space at buffer
1837: * to hold the entire selection (no INCR-mode transfers for this
1838: * stuff!), then -1 is returned.
1839: *
1840: * Side effects:
1841: * None.
1842: *
1843: *----------------------------------------------------------------------
1844: */
1845:
1846: static int
1847: DefaultSelection(winPtr, target, buffer, maxBytes, typePtr)
1848: TkWindow *winPtr; /* Window that owns selection. */
1849: Atom target; /* Desired form of selection. */
1850: char *buffer; /* Place to put selection characters. */
1851: int maxBytes; /* Maximum # of bytes to store at buffer. */
1852: Atom *typePtr; /* Store here the type of the selection,
1853: * for use in converting to proper X format. */
1854: {
1855: if (target == winPtr->dispPtr->timestampAtom) {
1856: if (maxBytes < 20) {
1857: return -1;
1858: }
1859: sprintf(buffer, "%#x", winPtr->dispPtr->selectionTime);
1860: *typePtr = XA_INTEGER;
1861: return strlen(buffer);
1862: }
1863:
1864: if (target == winPtr->dispPtr->targetsAtom) {
1865: register TkSelHandler *selPtr;
1866: char *atomString;
1867: int length, atomLength;
1868:
1869: if (maxBytes < 50) {
1870: return -1;
1871: }
1872: strcpy(buffer, "TARGETS MULTIPLE TIMESTAMP");
1873: length = strlen(buffer);
1874: for (selPtr = winPtr->selHandlerList; selPtr != NULL;
1875: selPtr = selPtr->nextPtr) {
1876: atomString = Tk_GetAtomName((Tk_Window) winPtr, selPtr->target);
1877: atomLength = strlen(atomString) + 1;
1878: if ((length + atomLength) >= maxBytes) {
1879: return -1;
1880: }
1881: sprintf(buffer+length, " %s", atomString);
1882: length += atomLength;
1883: }
1884: *typePtr = XA_ATOM;
1885: return length;
1886: }
1887:
1888: return -1;
1889: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.