1 /*
2 * tkSelect.c --
3 *
4 * This file manages the selection for the Tk toolkit, translating
5 * between the standard X ICCCM conventions and Tcl commands.
6 *
7 * Copyright © 1990-1993 The Regents of the University of California.
8 * Copyright © 1994-1997 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 */
13
14 #include "tkInt.h"
15 #include "tkSelect.h"
16
17 /*
18 * When a selection handler is set up by invoking "selection handle", one of
19 * the following data structures is set up to hold information about the
20 * command to invoke and its interpreter.
21 */
22
23 typedef struct {
24 Tcl_Interp *interp; /* Interpreter in which to invoke command. */
25 int cmdLength; /* # of non-NULL bytes in command. */
26 int charOffset; /* The offset of the next char to retrieve. */
27 int byteOffset; /* The expected byte offset of the next
28 * chunk. */
29 char buffer[4]; /* A buffer to hold part of a UTF character
30 * that is split across chunks. */
31 char command[TKFLEXARRAY]; /* Command to invoke. Actual space is
32 * allocated as large as necessary. This must
33 * be the last entry in the structure. */
34 } CommandInfo;
35
36 /*
37 * When selection ownership is claimed with the "selection own" Tcl command,
38 * one of the following structures is created to record the Tcl command to be
39 * executed when the selection is lost again.
40 */
41
42 typedef struct LostCommand {
43 Tcl_Interp *interp; /* Interpreter in which to invoke command. */
44 Tcl_Obj *cmdObj; /* Reference to command to invoke. */
45 } LostCommand;
46
47 /*
48 * The structure below is used to keep each thread's pending list separate.
49 */
50
51 typedef struct {
52 TkSelInProgress *pendingPtr;
53 /* Topmost search in progress, or NULL if
54 * none. */
55 } ThreadSpecificData;
56 static Tcl_ThreadDataKey dataKey;
57
58 /*
59 * Forward declarations for functions defined in this file:
60 */
61
62 static TkSizeT HandleTclCommand(ClientData clientData,
63 TkSizeT offset, char *buffer, TkSizeT maxBytes);
64 static void LostSelection(ClientData clientData);
65 static int SelGetProc(ClientData clientData,
66 Tcl_Interp *interp, const char *portion);
67
68 /*
69 *--------------------------------------------------------------
70 *
71 * Tk_CreateSelHandler --
72 *
73 * This function is called to register a function as the handler for
74 * selection requests of a particular target type on a particular window
75 * for a particular selection.
76 *
77 * Results:
78 * None.
79 *
80 * Side effects:
81
82 * In the future, whenever the selection is in tkwin's window and someone
83 * requests the selection in the form given by target, proc will be
84 * invoked to provide part or all of the selection in the given form. If
85 * there was already a handler declared for the given window, target and
86 * selection type, then it is replaced. Proc should have the following
87 * form:
88 *
89 * int
90 * proc(
91 * ClientData clientData,
92 * int offset,
93 * char *buffer,
94 * int maxBytes)
95 * {
96 * }
97 *
98 * The clientData argument to proc will be the same as the clientData
99 * argument to this function. The offset argument indicates which portion
100 * of the selection to return: skip the first offset bytes. Buffer is a
101 * pointer to an area in which to place the converted selection, and
102 * maxBytes gives the number of bytes available at buffer. Proc should
103 * place the selection in buffer as a string, and return a count of the
104 * number of bytes of selection actually placed in buffer (not including
105 * the terminating NULL character). If the return value equals maxBytes,
106 * this is a sign that there is probably still more selection information
107 * available.
108 *
109 *--------------------------------------------------------------
110 */
111
112 void
Tk_CreateSelHandler(Tk_Window tkwin,Atom selection,Atom target,Tk_SelectionProc * proc,ClientData clientData,Atom format)113 Tk_CreateSelHandler(
114 Tk_Window tkwin, /* Token for window. */
115 Atom selection, /* Selection to be handled. */
116 Atom target, /* The kind of selection conversions that can
117 * be handled by proc, e.g. TARGETS or
118 * STRING. */
119 Tk_SelectionProc *proc, /* Function to invoke to convert selection to
120 * type "target". */
121 ClientData clientData, /* Value to pass to proc. */
122 Atom format) /* Format in which the selection information
123 * should be returned to the requestor.
124 * XA_STRING is best by far, but anything
125 * listed in the ICCCM will be tolerated
126 * (blech). */
127 {
128 TkSelHandler *selPtr;
129 TkWindow *winPtr = (TkWindow *) tkwin;
130
131 if (winPtr->dispPtr->multipleAtom == None) {
132 TkSelInit(tkwin);
133 }
134
135 /*
136 * See if there's already a handler for this target and selection on this
137 * window. If so, re-use it. If not, create a new one.
138 */
139
140 for (selPtr = winPtr->selHandlerList; ; selPtr = selPtr->nextPtr) {
141 if (selPtr == NULL) {
142 selPtr = (TkSelHandler *)ckalloc(sizeof(TkSelHandler));
143 selPtr->nextPtr = winPtr->selHandlerList;
144 winPtr->selHandlerList = selPtr;
145 break;
146 }
147 if ((selPtr->selection == selection) && (selPtr->target == target)) {
148 /*
149 * Special case: when replacing handler created by "selection
150 * handle", free up memory. Should there be a callback to allow
151 * other clients to do this too?
152 */
153
154 if (selPtr->proc == HandleTclCommand) {
155 ckfree(selPtr->clientData);
156 }
157 break;
158 }
159 }
160 selPtr->selection = selection;
161 selPtr->target = target;
162 selPtr->format = format;
163 selPtr->proc = proc;
164 selPtr->clientData = clientData;
165 if (format == XA_STRING) {
166 selPtr->size = 8;
167 } else {
168 selPtr->size = 32;
169 }
170
171 if ((target == XA_STRING) && (winPtr->dispPtr->utf8Atom != (Atom) 0)) {
172 /*
173 * If the user asked for a STRING handler and we understand
174 * UTF8_STRING, we implicitly create a UTF8_STRING handler for them.
175 */
176
177 target = winPtr->dispPtr->utf8Atom;
178 for (selPtr = winPtr->selHandlerList; ; selPtr = selPtr->nextPtr) {
179 if (selPtr == NULL) {
180 selPtr = (TkSelHandler *)ckalloc(sizeof(TkSelHandler));
181 selPtr->nextPtr = winPtr->selHandlerList;
182 winPtr->selHandlerList = selPtr;
183 selPtr->selection = selection;
184 selPtr->target = target;
185 selPtr->format = target; /* We want UTF8_STRING format */
186 selPtr->proc = proc;
187 if (selPtr->proc == HandleTclCommand) {
188 /*
189 * The clientData is selection controlled memory, so we
190 * should make a copy for this selPtr.
191 */
192
193 size_t cmdInfoLen = offsetof(CommandInfo, command) + 1 +
194 ((CommandInfo *)clientData)->cmdLength;
195
196 selPtr->clientData = ckalloc(cmdInfoLen);
197 memcpy(selPtr->clientData, clientData, cmdInfoLen);
198 } else {
199 selPtr->clientData = clientData;
200 }
201 selPtr->size = 8;
202 break;
203 }
204 if (selPtr->selection==selection && selPtr->target==target) {
205 /*
206 * Looks like we had a utf-8 target already. Leave it alone.
207 */
208
209 break;
210 }
211 }
212 }
213 }
214
215 /*
216 *----------------------------------------------------------------------
217 *
218 * Tk_DeleteSelHandler --
219 *
220 * Remove the selection handler for a given window, target, and
221 * selection, if it exists.
222 *
223 * Results:
224 * None.
225 *
226 * Side effects:
227 * The selection handler for tkwin and target is removed. If there is no
228 * such handler then nothing happens.
229 *
230 *----------------------------------------------------------------------
231 */
232
233 void
Tk_DeleteSelHandler(Tk_Window tkwin,Atom selection,Atom target)234 Tk_DeleteSelHandler(
235 Tk_Window tkwin, /* Token for window. */
236 Atom selection, /* The selection whose handler is to be
237 * removed. */
238 Atom target) /* The target whose selection handler is to be
239 * removed. */
240 {
241 TkWindow *winPtr = (TkWindow *) tkwin;
242 TkSelHandler *selPtr, *prevPtr;
243 TkSelInProgress *ipPtr;
244 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
245 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
246
247 /*
248 * Find the selection handler to be deleted, or return if it doesn't
249 * exist.
250 */
251
252 for (selPtr = winPtr->selHandlerList, prevPtr = NULL; ;
253 prevPtr = selPtr, selPtr = selPtr->nextPtr) {
254 if (selPtr == NULL) {
255 return;
256 }
257 if ((selPtr->selection == selection) && (selPtr->target == target)) {
258 break;
259 }
260 }
261
262 /*
263 * If ConvertSelection is processing this handler, tell it that the
264 * handler is dead.
265 */
266
267 for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
268 ipPtr = ipPtr->nextPtr) {
269 if (ipPtr->selPtr == selPtr) {
270 ipPtr->selPtr = NULL;
271 }
272 }
273
274 /*
275 * Free resources associated with the handler.
276 */
277
278 if (prevPtr == NULL) {
279 winPtr->selHandlerList = selPtr->nextPtr;
280 } else {
281 prevPtr->nextPtr = selPtr->nextPtr;
282 }
283
284 if ((target == XA_STRING) && (winPtr->dispPtr->utf8Atom != (Atom) 0)) {
285 /*
286 * If the user asked for a STRING handler and we understand
287 * UTF8_STRING, we may have implicitly created a UTF8_STRING handler
288 * for them. Look for it and delete it as necessary.
289 */
290
291 TkSelHandler *utf8selPtr;
292
293 target = winPtr->dispPtr->utf8Atom;
294 for (utf8selPtr = winPtr->selHandlerList; utf8selPtr != NULL;
295 utf8selPtr = utf8selPtr->nextPtr) {
296 if ((utf8selPtr->selection == selection)
297 && (utf8selPtr->target == target)) {
298 break;
299 }
300 }
301 if (utf8selPtr != NULL) {
302 if ((utf8selPtr->format == target)
303 && (utf8selPtr->proc == selPtr->proc)
304 && (utf8selPtr->size == selPtr->size)) {
305 /*
306 * This recursive call is OK, because we've changed the value
307 * of 'target'.
308 */
309
310 Tk_DeleteSelHandler(tkwin, selection, target);
311 }
312 }
313 }
314
315 if (selPtr->proc == HandleTclCommand) {
316 /*
317 * Mark the CommandInfo as deleted and free it if we can.
318 */
319
320 ((CommandInfo *) selPtr->clientData)->interp = NULL;
321 Tcl_EventuallyFree(selPtr->clientData, TCL_DYNAMIC);
322 }
323 ckfree(selPtr);
324 }
325
326 /*
327 *--------------------------------------------------------------
328 *
329 * Tk_OwnSelection --
330 *
331 * Arrange for tkwin to become the owner of a selection.
332 *
333 * Results:
334 * None.
335 *
336 * Side effects:
337 * From now on, requests for the selection will be directed to functions
338 * associated with tkwin (they must have been declared with calls to
339 * Tk_CreateSelHandler). When the selection is lost by this window, proc
340 * will be invoked (see the manual entry for details). This function may
341 * invoke callbacks, including Tcl scripts, so any calling function
342 * should be reentrant at the point where Tk_OwnSelection is invoked.
343 *
344 *--------------------------------------------------------------
345 */
346
347 void
Tk_OwnSelection(Tk_Window tkwin,Atom selection,Tk_LostSelProc * proc,ClientData clientData)348 Tk_OwnSelection(
349 Tk_Window tkwin, /* Window to become new selection owner. */
350 Atom selection, /* Selection that window should own. */
351 Tk_LostSelProc *proc, /* Function to call when selection is taken
352 * away from tkwin. */
353 ClientData clientData) /* Arbitrary one-word argument to pass to
354 * proc. */
355 {
356 TkWindow *winPtr = (TkWindow *) tkwin;
357 TkDisplay *dispPtr = winPtr->dispPtr;
358 TkSelectionInfo *infoPtr;
359 Tk_LostSelProc *clearProc = NULL;
360 void *clearData = NULL;/* Initialization needed only to prevent
361 * compiler warning. */
362
363 if (dispPtr->multipleAtom == None) {
364 TkSelInit(tkwin);
365 }
366 Tk_MakeWindowExist(tkwin);
367
368 /*
369 * This code is somewhat tricky. First, we find the specified selection on
370 * the selection list. If the previous owner is in this process, and is a
371 * different window, then we need to invoke the clearProc. However, it's
372 * dangerous to call the clearProc right now, because it could invoke a
373 * Tcl script that wrecks the current state (e.g. it could delete the
374 * window). To be safe, defer the call until the end of the function when
375 * we no longer care about the state.
376 */
377
378 for (infoPtr = dispPtr->selectionInfoPtr; infoPtr != NULL;
379 infoPtr = infoPtr->nextPtr) {
380 if (infoPtr->selection == selection) {
381 break;
382 }
383 }
384 if (infoPtr == NULL) {
385 infoPtr = (TkSelectionInfo *)ckalloc(sizeof(TkSelectionInfo));
386 infoPtr->selection = selection;
387 infoPtr->nextPtr = dispPtr->selectionInfoPtr;
388 dispPtr->selectionInfoPtr = infoPtr;
389 } else if (infoPtr->clearProc != NULL) {
390 if (infoPtr->owner != tkwin) {
391 clearProc = infoPtr->clearProc;
392 clearData = infoPtr->clearData;
393 } else if (infoPtr->clearProc == LostSelection) {
394 /*
395 * If the selection handler is one created by "selection own", be
396 * sure to free the record for it; otherwise there will be a
397 * memory leak.
398 */
399
400 ckfree(infoPtr->clearData);
401 }
402 }
403
404 infoPtr->owner = tkwin;
405 infoPtr->serial = NextRequest(winPtr->display);
406 infoPtr->clearProc = proc;
407 infoPtr->clearData = clientData;
408
409 /*
410 * Note that we are using CurrentTime, even though ICCCM recommends
411 * against this practice (the problem is that we don't necessarily have a
412 * valid time to use). We will not be able to retrieve a useful timestamp
413 * for the TIMESTAMP target later.
414 */
415
416 infoPtr->time = CurrentTime;
417
418 /*
419 * Note that we are not checking to see if the selection claim succeeded.
420 * If the ownership does not change, then the clearProc may never be
421 * invoked, and we will return incorrect information when queried for the
422 * current selection owner.
423 */
424
425 XSetSelectionOwner(winPtr->display, infoPtr->selection, winPtr->window,
426 infoPtr->time);
427
428 /*
429 * Now that we are done, we can invoke clearProc without running into
430 * reentrancy problems.
431 */
432
433 if (clearProc != NULL) {
434 clearProc(clearData);
435 }
436 }
437
438 /*
439 *----------------------------------------------------------------------
440 *
441 * Tk_ClearSelection --
442 *
443 * Eliminate the specified selection on tkwin's display, if there is one.
444 *
445 * Results:
446 * None.
447 *
448 * Side effects:
449 * The specified selection is cleared, so that future requests to
450 * retrieve it will fail until some application owns it again. This
451 * function invokes callbacks, possibly including Tcl scripts, so any
452 * calling function should be reentrant at the point Tk_ClearSelection is
453 * invoked.
454 *
455 *----------------------------------------------------------------------
456 */
457
458 void
Tk_ClearSelection(Tk_Window tkwin,Atom selection)459 Tk_ClearSelection(
460 Tk_Window tkwin, /* Window that selects a display. */
461 Atom selection) /* Selection to be cancelled. */
462 {
463 TkWindow *winPtr = (TkWindow *) tkwin;
464 TkDisplay *dispPtr = winPtr->dispPtr;
465 TkSelectionInfo *infoPtr;
466 TkSelectionInfo *prevPtr;
467 TkSelectionInfo *nextPtr;
468 Tk_LostSelProc *clearProc = NULL;
469 void *clearData = NULL;/* Initialization needed only to prevent
470 * compiler warning. */
471
472 if (dispPtr->multipleAtom == None) {
473 TkSelInit(tkwin);
474 }
475
476 for (infoPtr = dispPtr->selectionInfoPtr, prevPtr = NULL;
477 infoPtr != NULL; infoPtr = nextPtr) {
478 nextPtr = infoPtr->nextPtr;
479 if (infoPtr->selection == selection) {
480 if (prevPtr == NULL) {
481 dispPtr->selectionInfoPtr = nextPtr;
482 } else {
483 prevPtr->nextPtr = nextPtr;
484 }
485 break;
486 }
487 prevPtr = infoPtr;
488 }
489
490 if (infoPtr != NULL) {
491 clearProc = infoPtr->clearProc;
492 clearData = infoPtr->clearData;
493 ckfree(infoPtr);
494 }
495 XSetSelectionOwner(winPtr->display, selection, None, CurrentTime);
496
497 if (clearProc != NULL) {
498 clearProc(clearData);
499 }
500 }
501
502 /*
503 *--------------------------------------------------------------
504 *
505 * Tk_GetSelection --
506 *
507 * Retrieve the value of a selection and pass it off (in pieces,
508 * possibly) to a given function.
509 *
510 * Results:
511 * The return value is a standard Tcl return value. If an error occurs
512 * (such as no selection exists) then an error message is left in the
513 * interp's result.
514 *
515 * Side effects:
516 * The standard X11 protocols are used to retrieve the selection. When it
517 * arrives, it is passed to proc. If the selection is very large, it will
518 * be passed to proc in several pieces. Proc should have the following
519 * structure:
520 *
521 * int
522 * proc(
523 * ClientData clientData,
524 * Tcl_Interp *interp,
525 * char *portion)
526 * {
527 * }
528 *
529 * The interp and clientData arguments to proc will be the same as the
530 * corresponding arguments to Tk_GetSelection. The portion argument
531 * points to a character string containing part of the selection, and
532 * numBytes indicates the length of the portion, not including the
533 * terminating NULL character. If the selection arrives in several
534 * pieces, the "portion" arguments in separate calls will contain
535 * successive parts of the selection. Proc should normally return TCL_OK.
536 * If it detects an error then it should return TCL_ERROR and leave an
537 * error message in the interp's result; the remainder of the selection
538 * retrieval will be aborted.
539 *
540 *--------------------------------------------------------------
541 */
542
543 int
Tk_GetSelection(Tcl_Interp * interp,Tk_Window tkwin,Atom selection,Atom target,Tk_GetSelProc * proc,ClientData clientData)544 Tk_GetSelection(
545 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
546 Tk_Window tkwin, /* Window on whose behalf to retrieve the
547 * selection (determines display from which to
548 * retrieve). */
549 Atom selection, /* Selection to retrieve. */
550 Atom target, /* Desired form in which selection is to be
551 * returned. */
552 Tk_GetSelProc *proc, /* Function to call to process the selection,
553 * once it has been retrieved. */
554 ClientData clientData) /* Arbitrary value to pass to proc. */
555 {
556 TkWindow *winPtr = (TkWindow *) tkwin;
557 TkDisplay *dispPtr = winPtr->dispPtr;
558 TkSelectionInfo *infoPtr;
559 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
560 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
561
562 if (dispPtr->multipleAtom == None) {
563 TkSelInit(tkwin);
564 }
565
566 /*
567 * If the selection is owned by a window managed by this process, then
568 * call the retrieval function directly, rather than going through the X
569 * server (it's dangerous to go through the X server in this case because
570 * it could result in deadlock if an INCR-style selection results).
571 */
572
573 for (infoPtr = dispPtr->selectionInfoPtr; infoPtr != NULL;
574 infoPtr = infoPtr->nextPtr) {
575 if (infoPtr->selection == selection) {
576 break;
577 }
578 }
579 if (infoPtr != NULL) {
580 TkSelHandler *selPtr;
581 int offset, result, count;
582 char buffer[TK_SEL_BYTES_AT_ONCE+1];
583 TkSelInProgress ip;
584
585 for (selPtr = ((TkWindow *) infoPtr->owner)->selHandlerList;
586 selPtr != NULL; selPtr = selPtr->nextPtr) {
587 if (selPtr->target==target && selPtr->selection==selection) {
588 break;
589 }
590 }
591 if (selPtr == NULL) {
592 Atom type;
593
594 count = TkSelDefaultSelection(infoPtr, target, buffer,
595 TK_SEL_BYTES_AT_ONCE, &type);
596 if (count > TK_SEL_BYTES_AT_ONCE) {
597 Tcl_Panic("selection handler returned too many bytes");
598 }
599 if (count < 0) {
600 goto cantget;
601 }
602 buffer[count] = 0;
603 result = proc(clientData, interp, buffer);
604 } else {
605 offset = 0;
606 result = TCL_OK;
607 ip.selPtr = selPtr;
608 ip.nextPtr = tsdPtr->pendingPtr;
609 tsdPtr->pendingPtr = &ip;
610 while (1) {
611 count = selPtr->proc(selPtr->clientData, offset, buffer,
612 TK_SEL_BYTES_AT_ONCE);
613 if ((count < 0) || (ip.selPtr == NULL)) {
614 tsdPtr->pendingPtr = ip.nextPtr;
615 goto cantget;
616 }
617 if (count > TK_SEL_BYTES_AT_ONCE) {
618 Tcl_Panic("selection handler returned too many bytes");
619 }
620 buffer[count] = '\0';
621 result = proc(clientData, interp, buffer);
622 if ((result != TCL_OK) || (count < TK_SEL_BYTES_AT_ONCE)
623 || (ip.selPtr == NULL)) {
624 break;
625 }
626 offset += count;
627 }
628 tsdPtr->pendingPtr = ip.nextPtr;
629 }
630 return result;
631 }
632
633 /*
634 * The selection is owned by some other process.
635 */
636
637 return TkSelGetSelection(interp, tkwin, selection, target, proc,
638 clientData);
639
640 cantget:
641 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
642 "%s selection doesn't exist or form \"%s\" not defined",
643 Tk_GetAtomName(tkwin, selection),
644 Tk_GetAtomName(tkwin, target)));
645 return TCL_ERROR;
646 }
647
648 /*
649 *--------------------------------------------------------------
650 *
651 * Tk_SelectionObjCmd --
652 *
653 * This function is invoked to process the "selection" Tcl command. See
654 * the user documentation for details on what it does.
655 *
656 * Results:
657 * A standard Tcl result.
658 *
659 * Side effects:
660 * See the user documentation.
661 *
662 *--------------------------------------------------------------
663 */
664
665 int
Tk_SelectionObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])666 Tk_SelectionObjCmd(
667 ClientData clientData, /* Main window associated with
668 * interpreter. */
669 Tcl_Interp *interp, /* Current interpreter. */
670 int objc, /* Number of arguments. */
671 Tcl_Obj *const objv[]) /* Argument objects. */
672 {
673 Tk_Window tkwin = (Tk_Window)clientData;
674 const char *path = NULL;
675 Atom selection;
676 const char *selName = NULL;
677 const char *string;
678 int count, index;
679 Tcl_Obj **objs;
680 static const char *const optionStrings[] = {
681 "clear", "get", "handle", "own", NULL
682 };
683 enum options {
684 SELECTION_CLEAR, SELECTION_GET, SELECTION_HANDLE, SELECTION_OWN
685 };
686
687 if (objc < 2) {
688 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
689 return TCL_ERROR;
690 }
691
692 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
693 &index) != TCL_OK) {
694 return TCL_ERROR;
695 }
696
697 switch ((enum options) index) {
698 case SELECTION_CLEAR: {
699 static const char *const clearOptionStrings[] = {
700 "-displayof", "-selection", NULL
701 };
702 enum clearOptions { CLEAR_DISPLAYOF, CLEAR_SELECTION };
703 int clearIndex;
704
705 for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
706 count-=2, objs+=2) {
707 string = Tcl_GetString(objs[0]);
708 if (string[0] != '-') {
709 break;
710 }
711 if (count < 2) {
712 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
713 "value for \"%s\" missing", string));
714 Tcl_SetErrorCode(interp, "TK", "SELECTION", "VALUE", NULL);
715 return TCL_ERROR;
716 }
717
718 if (Tcl_GetIndexFromObj(interp, objs[0], clearOptionStrings,
719 "option", 0, &clearIndex) != TCL_OK) {
720 return TCL_ERROR;
721 }
722 switch ((enum clearOptions) clearIndex) {
723 case CLEAR_DISPLAYOF:
724 path = Tcl_GetString(objs[1]);
725 break;
726 case CLEAR_SELECTION:
727 selName = Tcl_GetString(objs[1]);
728 break;
729 }
730 }
731
732 if (count == 1) {
733 path = Tcl_GetString(objs[0]);
734 } else if (count > 1) {
735 Tcl_WrongNumArgs(interp, 2, objv, "?-option value ...?");
736 return TCL_ERROR;
737 }
738 if (path != NULL) {
739 tkwin = Tk_NameToWindow(interp, path, tkwin);
740 }
741 if (tkwin == NULL) {
742 return TCL_ERROR;
743 }
744 if (selName != NULL) {
745 selection = Tk_InternAtom(tkwin, selName);
746 } else {
747 selection = XA_PRIMARY;
748 }
749
750 Tk_ClearSelection(tkwin, selection);
751 break;
752 }
753
754 case SELECTION_GET: {
755 Atom target;
756 const char *targetName = NULL;
757 Tcl_DString selBytes;
758 int result;
759 static const char *const getOptionStrings[] = {
760 "-displayof", "-selection", "-type", NULL
761 };
762 enum getOptions { GET_DISPLAYOF, GET_SELECTION, GET_TYPE };
763 int getIndex;
764
765 for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count>0;
766 count-=2, objs+=2) {
767 string = Tcl_GetString(objs[0]);
768 if (string[0] != '-') {
769 break;
770 }
771 if (count < 2) {
772 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
773 "value for \"%s\" missing", string));
774 Tcl_SetErrorCode(interp, "TK", "SELECTION", "VALUE", NULL);
775 return TCL_ERROR;
776 }
777
778 if (Tcl_GetIndexFromObj(interp, objs[0], getOptionStrings,
779 "option", 0, &getIndex) != TCL_OK) {
780 return TCL_ERROR;
781 }
782
783 switch ((enum getOptions) getIndex) {
784 case GET_DISPLAYOF:
785 path = Tcl_GetString(objs[1]);
786 break;
787 case GET_SELECTION:
788 selName = Tcl_GetString(objs[1]);
789 break;
790 case GET_TYPE:
791 targetName = Tcl_GetString(objs[1]);
792 break;
793 }
794 }
795
796 if (path != NULL) {
797 tkwin = Tk_NameToWindow(interp, path, tkwin);
798 }
799 if (tkwin == NULL) {
800 return TCL_ERROR;
801 }
802 if (selName != NULL) {
803 selection = Tk_InternAtom(tkwin, selName);
804 } else {
805 selection = XA_PRIMARY;
806 }
807 if (count > 1) {
808 Tcl_WrongNumArgs(interp, 2, objv, "?-option value ...?");
809 return TCL_ERROR;
810 } else if (count == 1) {
811 target = Tk_InternAtom(tkwin, Tcl_GetString(objs[0]));
812 } else if (targetName != NULL) {
813 target = Tk_InternAtom(tkwin, targetName);
814 } else {
815 target = XA_STRING;
816 }
817
818 Tcl_DStringInit(&selBytes);
819 result = Tk_GetSelection(interp, tkwin, selection, target,
820 SelGetProc, &selBytes);
821 if (result == TCL_OK) {
822 Tcl_DStringResult(interp, &selBytes);
823 } else {
824 Tcl_DStringFree(&selBytes);
825 }
826 return result;
827 }
828
829 case SELECTION_HANDLE: {
830 Atom target, format;
831 const char *targetName = NULL;
832 const char *formatName = NULL;
833 CommandInfo *cmdInfoPtr;
834 TkSizeT cmdLength;
835 static const char *const handleOptionStrings[] = {
836 "-format", "-selection", "-type", NULL
837 };
838 enum handleOptions {
839 HANDLE_FORMAT, HANDLE_SELECTION, HANDLE_TYPE
840 };
841 int handleIndex;
842
843 for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
844 count-=2, objs+=2) {
845 string = Tcl_GetString(objs[0]);
846 if (string[0] != '-') {
847 break;
848 }
849 if (count < 2) {
850 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
851 "value for \"%s\" missing", string));
852 Tcl_SetErrorCode(interp, "TK", "SELECTION", "VALUE", NULL);
853 return TCL_ERROR;
854 }
855
856 if (Tcl_GetIndexFromObj(interp, objs[0],handleOptionStrings,
857 "option", 0, &handleIndex) != TCL_OK) {
858 return TCL_ERROR;
859 }
860
861 switch ((enum handleOptions) handleIndex) {
862 case HANDLE_FORMAT:
863 formatName = Tcl_GetString(objs[1]);
864 break;
865 case HANDLE_SELECTION:
866 selName = Tcl_GetString(objs[1]);
867 break;
868 case HANDLE_TYPE:
869 targetName = Tcl_GetString(objs[1]);
870 break;
871 }
872 }
873
874 if ((count < 2) || (count > 4)) {
875 Tcl_WrongNumArgs(interp, 2, objv,
876 "?-option value ...? window command");
877 return TCL_ERROR;
878 }
879 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objs[0]), tkwin);
880 if (tkwin == NULL) {
881 return TCL_ERROR;
882 }
883 if (selName != NULL) {
884 selection = Tk_InternAtom(tkwin, selName);
885 } else {
886 selection = XA_PRIMARY;
887 }
888
889 if (count > 2) {
890 target = Tk_InternAtom(tkwin, Tcl_GetString(objs[2]));
891 } else if (targetName != NULL) {
892 target = Tk_InternAtom(tkwin, targetName);
893 } else {
894 target = XA_STRING;
895 }
896 if (count > 3) {
897 format = Tk_InternAtom(tkwin, Tcl_GetString(objs[3]));
898 } else if (formatName != NULL) {
899 format = Tk_InternAtom(tkwin, formatName);
900 } else {
901 format = XA_STRING;
902 }
903 string = Tcl_GetStringFromObj(objs[1], &cmdLength);
904 if (cmdLength == 0) {
905 Tk_DeleteSelHandler(tkwin, selection, target);
906 } else {
907 cmdInfoPtr = (CommandInfo *)ckalloc(offsetof(CommandInfo, command)
908 + 1 + cmdLength);
909 cmdInfoPtr->interp = interp;
910 cmdInfoPtr->charOffset = 0;
911 cmdInfoPtr->byteOffset = 0;
912 cmdInfoPtr->buffer[0] = '\0';
913 cmdInfoPtr->cmdLength = cmdLength;
914 memcpy(cmdInfoPtr->command, string, cmdLength + 1);
915 Tk_CreateSelHandler(tkwin, selection, target, HandleTclCommand,
916 cmdInfoPtr, format);
917 }
918 return TCL_OK;
919 }
920
921 case SELECTION_OWN: {
922 LostCommand *lostPtr;
923 Tcl_Obj *commandObj = NULL;
924 static const char *const ownOptionStrings[] = {
925 "-command", "-displayof", "-selection", NULL
926 };
927 enum ownOptions { OWN_COMMAND, OWN_DISPLAYOF, OWN_SELECTION };
928 int ownIndex;
929
930 for (count = objc-2, objs = ((Tcl_Obj **)objv)+2; count > 0;
931 count-=2, objs+=2) {
932 string = Tcl_GetString(objs[0]);
933 if (string[0] != '-') {
934 break;
935 }
936 if (count < 2) {
937 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
938 "value for \"%s\" missing", string));
939 Tcl_SetErrorCode(interp, "TK", "SELECTION", "VALUE", NULL);
940 return TCL_ERROR;
941 }
942
943 if (Tcl_GetIndexFromObj(interp, objs[0], ownOptionStrings,
944 "option", 0, &ownIndex) != TCL_OK) {
945 return TCL_ERROR;
946 }
947
948 switch ((enum ownOptions) ownIndex) {
949 case OWN_COMMAND:
950 commandObj = objs[1];
951 break;
952 case OWN_DISPLAYOF:
953 path = Tcl_GetString(objs[1]);
954 break;
955 case OWN_SELECTION:
956 selName = Tcl_GetString(objs[1]);
957 break;
958 }
959 }
960
961 if (count > 2) {
962 Tcl_WrongNumArgs(interp, 2, objv, "?-option value ...? ?window?");
963 return TCL_ERROR;
964 }
965 if (selName != NULL) {
966 selection = Tk_InternAtom(tkwin, selName);
967 } else {
968 selection = XA_PRIMARY;
969 }
970
971 if (count == 0) {
972 TkSelectionInfo *infoPtr;
973 TkWindow *winPtr;
974
975 if (path != NULL) {
976 tkwin = Tk_NameToWindow(interp, path, tkwin);
977 }
978 if (tkwin == NULL) {
979 return TCL_ERROR;
980 }
981 winPtr = (TkWindow *) tkwin;
982 for (infoPtr = winPtr->dispPtr->selectionInfoPtr;
983 infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
984 if (infoPtr->selection == selection) {
985 break;
986 }
987 }
988
989 /*
990 * Ignore the internal clipboard window.
991 */
992
993 if ((infoPtr != NULL)
994 && (infoPtr->owner != winPtr->dispPtr->clipWindow)) {
995 Tcl_SetObjResult(interp, Tk_NewWindowObj(infoPtr->owner));
996 }
997 return TCL_OK;
998 }
999
1000 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objs[0]), tkwin);
1001 if (tkwin == NULL) {
1002 return TCL_ERROR;
1003 }
1004 if (count == 2) {
1005 commandObj = objs[1];
1006 }
1007 if (commandObj == NULL) {
1008 Tk_OwnSelection(tkwin, selection, NULL, NULL);
1009 return TCL_OK;
1010 }
1011 lostPtr = (LostCommand *)ckalloc(sizeof(LostCommand));
1012 lostPtr->interp = interp;
1013 lostPtr->cmdObj = commandObj;
1014 Tcl_IncrRefCount(commandObj);
1015 Tk_OwnSelection(tkwin, selection, LostSelection, lostPtr);
1016 return TCL_OK;
1017 }
1018 }
1019 return TCL_OK;
1020 }
1021
1022 /*
1023 *----------------------------------------------------------------------
1024 *
1025 * TkSelGetInProgress --
1026 *
1027 * This function returns a pointer to the thread-local list of pending
1028 * searches.
1029 *
1030 * Results:
1031 * The return value is a pointer to the first search in progress, or NULL
1032 * if there are none.
1033 *
1034 * Side effects:
1035 * None.
1036 *
1037 *----------------------------------------------------------------------
1038 */
1039
1040 TkSelInProgress *
TkSelGetInProgress(void)1041 TkSelGetInProgress(void)
1042 {
1043 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1044 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1045
1046 return tsdPtr->pendingPtr;
1047 }
1048
1049 /*
1050 *----------------------------------------------------------------------
1051 *
1052 * TkSelSetInProgress --
1053 *
1054 * This function is used to set the thread-local list of pending
1055 * searches. It is required because the pending list is kept in thread
1056 * local storage.
1057 *
1058 * Results:
1059 * None.
1060 *
1061 * Side effects:
1062 * None.
1063 *
1064 *----------------------------------------------------------------------
1065 */
1066 void
TkSelSetInProgress(TkSelInProgress * pendingPtr)1067 TkSelSetInProgress(
1068 TkSelInProgress *pendingPtr)
1069 {
1070 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1071 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1072
1073 tsdPtr->pendingPtr = pendingPtr;
1074 }
1075
1076 /*
1077 *----------------------------------------------------------------------
1078 *
1079 * TkSelDeadWindow --
1080 *
1081 * This function is invoked just before a TkWindow is deleted. It
1082 * performs selection-related cleanup.
1083 *
1084 * Results:
1085 * None.
1086 *
1087 * Side effects:
1088 * Frees up memory associated with the selection.
1089 *
1090 *----------------------------------------------------------------------
1091 */
1092
1093 void
TkSelDeadWindow(TkWindow * winPtr)1094 TkSelDeadWindow(
1095 TkWindow *winPtr) /* Window that's being deleted. */
1096 {
1097 TkSelHandler *selPtr;
1098 TkSelInProgress *ipPtr;
1099 TkSelectionInfo *infoPtr, *prevPtr, *nextPtr;
1100 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1101 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1102
1103 /*
1104 * While deleting all the handlers, be careful to check whether
1105 * ConvertSelection or TkSelPropProc are about to process one of the
1106 * deleted handlers.
1107 */
1108
1109 while (winPtr->selHandlerList != NULL) {
1110 selPtr = winPtr->selHandlerList;
1111 winPtr->selHandlerList = selPtr->nextPtr;
1112 for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
1113 ipPtr = ipPtr->nextPtr) {
1114 if (ipPtr->selPtr == selPtr) {
1115 ipPtr->selPtr = NULL;
1116 }
1117 }
1118 if (selPtr->proc == HandleTclCommand) {
1119 /*
1120 * Mark the CommandInfo as deleted and free it when we can.
1121 */
1122
1123 ((CommandInfo *) selPtr->clientData)->interp = NULL;
1124 Tcl_EventuallyFree(selPtr->clientData, TCL_DYNAMIC);
1125 }
1126 ckfree(selPtr);
1127 }
1128
1129 /*
1130 * Remove selections owned by window being deleted.
1131 */
1132
1133 for (infoPtr = winPtr->dispPtr->selectionInfoPtr, prevPtr = NULL;
1134 infoPtr != NULL; infoPtr = nextPtr) {
1135 nextPtr = infoPtr->nextPtr;
1136 if (infoPtr->owner == (Tk_Window) winPtr) {
1137 if (infoPtr->clearProc == LostSelection) {
1138 ckfree(infoPtr->clearData);
1139 }
1140 ckfree(infoPtr);
1141 infoPtr = prevPtr;
1142 if (prevPtr == NULL) {
1143 winPtr->dispPtr->selectionInfoPtr = nextPtr;
1144 } else {
1145 prevPtr->nextPtr = nextPtr;
1146 }
1147 }
1148 prevPtr = infoPtr;
1149 }
1150 }
1151
1152 /*
1153 *----------------------------------------------------------------------
1154 *
1155 * TkSelInit --
1156 *
1157 * Initialize selection-related information for a display.
1158 *
1159 * Results:
1160 * None.
1161 *
1162 * Side effects:
1163 * Selection-related information is initialized.
1164 *
1165 *----------------------------------------------------------------------
1166 */
1167
1168 void
TkSelInit(Tk_Window tkwin)1169 TkSelInit(
1170 Tk_Window tkwin) /* Window token (used to find display to
1171 * initialize). */
1172 {
1173 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
1174
1175 /*
1176 * Fetch commonly-used atoms.
1177 */
1178
1179 dispPtr->multipleAtom = Tk_InternAtom(tkwin, "MULTIPLE");
1180 dispPtr->incrAtom = Tk_InternAtom(tkwin, "INCR");
1181 dispPtr->targetsAtom = Tk_InternAtom(tkwin, "TARGETS");
1182 dispPtr->timestampAtom = Tk_InternAtom(tkwin, "TIMESTAMP");
1183 dispPtr->textAtom = Tk_InternAtom(tkwin, "TEXT");
1184 dispPtr->compoundTextAtom = Tk_InternAtom(tkwin, "COMPOUND_TEXT");
1185 dispPtr->applicationAtom = Tk_InternAtom(tkwin, "TK_APPLICATION");
1186 dispPtr->windowAtom = Tk_InternAtom(tkwin, "TK_WINDOW");
1187 dispPtr->clipboardAtom = Tk_InternAtom(tkwin, "CLIPBOARD");
1188 dispPtr->atomPairAtom = Tk_InternAtom(tkwin, "ATOM_PAIR");
1189
1190 /*
1191 * Using UTF8_STRING instead of the XA_UTF8_STRING macro allows us to
1192 * support older X servers that didn't have UTF8_STRING yet. This is
1193 * necessary on Unix systems. For more information, see:
1194 * http://www.cl.cam.ac.uk/~mgk25/unicode.html#x11
1195 */
1196
1197 #if !defined(_WIN32)
1198 dispPtr->utf8Atom = Tk_InternAtom(tkwin, "UTF8_STRING");
1199 #else
1200 dispPtr->utf8Atom = (Atom) 0;
1201 #endif
1202 }
1203
1204 /*
1205 *----------------------------------------------------------------------
1206 *
1207 * TkSelClearSelection --
1208 *
1209 * This function is invoked to process a SelectionClear event.
1210 *
1211 * Results:
1212 * None.
1213 *
1214 * Side effects:
1215 * Invokes the clear function for the window which lost the
1216 * selection.
1217 *
1218 *----------------------------------------------------------------------
1219 */
1220
1221 void
TkSelClearSelection(Tk_Window tkwin,XEvent * eventPtr)1222 TkSelClearSelection(
1223 Tk_Window tkwin, /* Window for which event was targeted. */
1224 XEvent *eventPtr) /* X SelectionClear event. */
1225 {
1226 TkWindow *winPtr = (TkWindow *) tkwin;
1227 TkDisplay *dispPtr = winPtr->dispPtr;
1228 TkSelectionInfo *infoPtr;
1229 TkSelectionInfo *prevPtr;
1230
1231 /*
1232 * Invoke clear function for window that just lost the selection. This
1233 * code is a bit tricky, because any callbacks due to selection changes
1234 * between windows managed by the process have already been made. Thus,
1235 * ignore the event unless it refers to the window that's currently the
1236 * selection owner and the event was generated after the server saw the
1237 * SetSelectionOwner request.
1238 */
1239
1240 for (infoPtr = dispPtr->selectionInfoPtr, prevPtr = NULL;
1241 infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
1242 if (infoPtr->selection == eventPtr->xselectionclear.selection) {
1243 break;
1244 }
1245 prevPtr = infoPtr;
1246 }
1247
1248 if (infoPtr != NULL && (infoPtr->owner == tkwin) &&
1249 (eventPtr->xselectionclear.serial >= (unsigned long) infoPtr->serial)) {
1250 if (prevPtr == NULL) {
1251 dispPtr->selectionInfoPtr = infoPtr->nextPtr;
1252 } else {
1253 prevPtr->nextPtr = infoPtr->nextPtr;
1254 }
1255
1256 /*
1257 * Because of reentrancy problems, calling clearProc must be done
1258 * after the infoPtr has been removed from the selectionInfoPtr list
1259 * (clearProc could modify the list, e.g. by creating a new
1260 * selection).
1261 */
1262
1263 if (infoPtr->clearProc != NULL) {
1264 infoPtr->clearProc(infoPtr->clearData);
1265 }
1266 ckfree(infoPtr);
1267 }
1268 }
1269
1270 /*
1271 *--------------------------------------------------------------
1272 *
1273 * SelGetProc --
1274 *
1275 * This function is invoked to process pieces of the selection as they
1276 * arrive during "selection get" commands.
1277 *
1278 * Results:
1279 * Always returns TCL_OK.
1280 *
1281 * Side effects:
1282 * Bytes get appended to the dynamic string pointed to by the clientData
1283 * argument.
1284 *
1285 *--------------------------------------------------------------
1286 */
1287
1288 static int
SelGetProc(ClientData clientData,TCL_UNUSED (Tcl_Interp *),const char * portion)1289 SelGetProc(
1290 ClientData clientData, /* Dynamic string holding partially assembled
1291 * selection. */
1292 TCL_UNUSED(Tcl_Interp *), /* Interpreter used for error reporting (not
1293 * used). */
1294 const char *portion) /* New information to be appended. */
1295 {
1296 Tcl_DStringAppend((Tcl_DString *)clientData, portion, -1);
1297 return TCL_OK;
1298 }
1299
1300 /*
1301 *----------------------------------------------------------------------
1302 *
1303 * HandleTclCommand --
1304 *
1305 * This function acts as selection handler for handlers created by the
1306 * "selection handle" command. It invokes a Tcl command to retrieve the
1307 * selection.
1308 *
1309 * Results:
1310 * The return value is a count of the number of bytes actually stored at
1311 * buffer, or -1 if an error occurs while executing the Tcl command to
1312 * retrieve the selection.
1313 *
1314 * Side effects:
1315 * None except for things done by the Tcl command.
1316 *
1317 *----------------------------------------------------------------------
1318 */
1319
1320 static TkSizeT
HandleTclCommand(ClientData clientData,TkSizeT offset,char * buffer,TkSizeT maxBytes)1321 HandleTclCommand(
1322 ClientData clientData, /* Information about command to execute. */
1323 TkSizeT offset, /* Return selection bytes starting at this
1324 * offset. */
1325 char *buffer, /* Place to store converted selection. */
1326 TkSizeT maxBytes) /* Maximum # of bytes to store at buffer. */
1327 {
1328 CommandInfo *cmdInfoPtr = (CommandInfo *)clientData;
1329 int length;
1330 Tcl_Obj *command;
1331 const char *string;
1332 Tcl_Interp *interp = cmdInfoPtr->interp;
1333 Tcl_InterpState savedState;
1334 int extraBytes, charOffset, count, numChars, code;
1335 const char *p;
1336
1337 /*
1338 * We must also protect the interpreter and the command from being deleted
1339 * too soon.
1340 */
1341
1342 Tcl_Preserve(clientData);
1343 Tcl_Preserve(interp);
1344
1345 /*
1346 * Compute the proper byte offset in the case where the last chunk split a
1347 * character.
1348 */
1349
1350 if ((int)offset == cmdInfoPtr->byteOffset) {
1351 charOffset = cmdInfoPtr->charOffset;
1352 extraBytes = strlen(cmdInfoPtr->buffer);
1353 if (extraBytes > 0) {
1354 strcpy(buffer, cmdInfoPtr->buffer);
1355 maxBytes -= extraBytes;
1356 buffer += extraBytes;
1357 }
1358 } else {
1359 cmdInfoPtr->byteOffset = 0;
1360 cmdInfoPtr->charOffset = 0;
1361 extraBytes = 0;
1362 charOffset = 0;
1363 }
1364
1365 /*
1366 * First, generate a command by taking the command string and appending
1367 * the offset and maximum # of bytes.
1368 */
1369
1370 command = Tcl_ObjPrintf("%s %d %d",
1371 cmdInfoPtr->command, charOffset, (int)maxBytes);
1372 Tcl_IncrRefCount(command);
1373
1374 /*
1375 * Execute the command. Be sure to restore the state of the interpreter
1376 * after executing the command.
1377 */
1378
1379 savedState = Tcl_SaveInterpState(interp, TCL_OK);
1380 code = Tcl_EvalObjEx(interp, command, TCL_EVAL_GLOBAL);
1381 Tcl_DecrRefCount(command);
1382 if (code == TCL_OK) {
1383 /*
1384 * TODO: This assumes that bytes are characters; that's not true!
1385 */
1386
1387 string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);
1388 count = (length > (int)maxBytes) ? (int)maxBytes : length;
1389 memcpy(buffer, string, count);
1390 buffer[count] = '\0';
1391
1392 /*
1393 * Update the partial character information for the next retrieval if
1394 * the command has not been deleted.
1395 */
1396
1397 if (cmdInfoPtr->interp != NULL) {
1398 if (length <= (int)maxBytes) {
1399 cmdInfoPtr->charOffset += Tcl_NumUtfChars(string, -1);
1400 cmdInfoPtr->buffer[0] = '\0';
1401 } else {
1402 Tcl_UniChar ch = 0;
1403 p = string;
1404 string += count;
1405 numChars = 0;
1406 while (p < string) {
1407 p += Tcl_UtfToUniChar(p, &ch);
1408 numChars++;
1409 }
1410 cmdInfoPtr->charOffset += numChars;
1411 length = p - string;
1412 if (length > 0) {
1413 strncpy(cmdInfoPtr->buffer, string, length);
1414 }
1415 cmdInfoPtr->buffer[length] = '\0';
1416 }
1417 cmdInfoPtr->byteOffset += count + extraBytes;
1418 }
1419 count += extraBytes;
1420 } else {
1421 /*
1422 * Something went wrong. Log errors as background errors, and silently
1423 * drop everything else.
1424 */
1425
1426 if (code == TCL_ERROR) {
1427 Tcl_AddErrorInfo(interp, "\n (command handling selection)");
1428 Tcl_BackgroundException(interp, code);
1429 }
1430 count = -1;
1431 }
1432 (void) Tcl_RestoreInterpState(interp, savedState);
1433
1434 Tcl_Release(clientData);
1435 Tcl_Release(interp);
1436 return count;
1437 }
1438
1439 /*
1440 *----------------------------------------------------------------------
1441 *
1442 * TkSelDefaultSelection --
1443 *
1444 * This function is called to generate selection information for a few
1445 * standard targets such as TIMESTAMP and TARGETS. It is invoked only if
1446 * no handler has been declared by the application.
1447 *
1448 * Results:
1449 * If "target" is a standard target understood by this function, the
1450 * selection is converted to that form and stored as a character string
1451 * in buffer. The type of the selection (e.g. STRING or ATOM) is stored
1452 * in *typePtr, and the return value is a count of the # of non-NULL
1453 * bytes at buffer. If the target wasn't understood, or if there isn't
1454 * enough space at buffer to hold the entire selection (no INCR-mode
1455 * transfers for this stuff!), then -1 is returned.
1456 *
1457 * Side effects:
1458 * None.
1459 *
1460 *----------------------------------------------------------------------
1461 */
1462
1463 int
TkSelDefaultSelection(TkSelectionInfo * infoPtr,Atom target,char * buffer,int maxBytes,Atom * typePtr)1464 TkSelDefaultSelection(
1465 TkSelectionInfo *infoPtr, /* Info about selection being retrieved. */
1466 Atom target, /* Desired form of selection. */
1467 char *buffer, /* Place to put selection characters. */
1468 int maxBytes, /* Maximum # of bytes to store at buffer. */
1469 Atom *typePtr) /* Store here the type of the selection, for
1470 * use in converting to proper X format. */
1471 {
1472 TkWindow *winPtr = (TkWindow *) infoPtr->owner;
1473 TkDisplay *dispPtr = winPtr->dispPtr;
1474
1475 if (target == dispPtr->timestampAtom) {
1476 if (maxBytes < 20) {
1477 return -1;
1478 }
1479 sprintf(buffer, "0x%x", (unsigned int) infoPtr->time);
1480 *typePtr = XA_INTEGER;
1481 return strlen(buffer);
1482 }
1483
1484 if (target == dispPtr->targetsAtom) {
1485 TkSelHandler *selPtr;
1486 int length;
1487 Tcl_DString ds;
1488
1489 if (maxBytes < 50) {
1490 return -1;
1491 }
1492 Tcl_DStringInit(&ds);
1493 Tcl_DStringAppend(&ds,
1494 "MULTIPLE TARGETS TIMESTAMP TK_APPLICATION TK_WINDOW", -1);
1495 for (selPtr = winPtr->selHandlerList; selPtr != NULL;
1496 selPtr = selPtr->nextPtr) {
1497 if ((selPtr->selection == infoPtr->selection)
1498 && (selPtr->target != dispPtr->applicationAtom)
1499 && (selPtr->target != dispPtr->windowAtom)) {
1500 const char *atomString = Tk_GetAtomName((Tk_Window) winPtr,
1501 selPtr->target);
1502
1503 Tcl_DStringAppendElement(&ds, atomString);
1504 }
1505 }
1506 length = Tcl_DStringLength(&ds);
1507 if (length >= maxBytes) {
1508 Tcl_DStringFree(&ds);
1509 return -1;
1510 }
1511 memcpy(buffer, Tcl_DStringValue(&ds), length + 1);
1512 Tcl_DStringFree(&ds);
1513 *typePtr = XA_ATOM;
1514 return length;
1515 }
1516
1517 if (target == dispPtr->applicationAtom) {
1518 int length;
1519 Tk_Uid name = winPtr->mainPtr->winPtr->nameUid;
1520
1521 length = strlen(name);
1522 if (maxBytes <= length) {
1523 return -1;
1524 }
1525 strcpy(buffer, name);
1526 *typePtr = XA_STRING;
1527 return length;
1528 }
1529
1530 if (target == dispPtr->windowAtom) {
1531 int length;
1532 char *name = winPtr->pathName;
1533
1534 length = strlen(name);
1535 if (maxBytes <= length) {
1536 return -1;
1537 }
1538 strcpy(buffer, name);
1539 *typePtr = XA_STRING;
1540 return length;
1541 }
1542
1543 return -1;
1544 }
1545
1546 /*
1547 *----------------------------------------------------------------------
1548 *
1549 * LostSelection --
1550 *
1551 * This function is invoked when a window has lost ownership of the
1552 * selection and the ownership was claimed with the command "selection
1553 * own".
1554 *
1555 * Results:
1556 * None.
1557 *
1558 * Side effects:
1559 * A Tcl script is executed; it can do almost anything.
1560 *
1561 *----------------------------------------------------------------------
1562 */
1563
1564 static void
LostSelection(ClientData clientData)1565 LostSelection(
1566 ClientData clientData) /* Pointer to LostCommand structure. */
1567 {
1568 LostCommand *lostPtr = (LostCommand *)clientData;
1569 Tcl_Interp *interp = lostPtr->interp;
1570 Tcl_InterpState savedState;
1571 int code;
1572
1573 Tcl_Preserve(interp);
1574
1575 /*
1576 * Execute the command. Save the interpreter's result, if any, and restore
1577 * it after executing the command.
1578 */
1579
1580 savedState = Tcl_SaveInterpState(interp, TCL_OK);
1581 Tcl_ResetResult(interp);
1582 code = Tcl_EvalObjEx(interp, lostPtr->cmdObj, TCL_EVAL_GLOBAL);
1583 if (code != TCL_OK) {
1584 Tcl_BackgroundException(interp, code);
1585 }
1586 (void) Tcl_RestoreInterpState(interp, savedState);
1587
1588 /*
1589 * Free the storage for the command, since we're done with it now.
1590 */
1591
1592 Tcl_DecrRefCount(lostPtr->cmdObj);
1593 ckfree(lostPtr);
1594 Tcl_Release(interp);
1595 }
1596
1597 /*
1598 * Local Variables:
1599 * mode: c
1600 * c-basic-offset: 4
1601 * fill-column: 78
1602 * End:
1603 */
1604