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