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