Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/unix/tkUnixSelect.c
1811 views
1
/*
2
* tkUnixSelect.c --
3
*
4
* This file contains X specific routines for manipulating
5
* selections.
6
*
7
* Copyright (c) 1995 Sun Microsystems, Inc.
8
*
9
* See the file "license.terms" for information on usage and redistribution
10
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
*
12
* SCCS: @(#) tkUnixSelect.c 1.5 96/03/29 14:14:31
13
*/
14
15
#include "tkInt.h"
16
#include "tkSelect.h"
17
18
/*
19
* When handling INCR-style selection retrievals, the selection owner
20
* uses the following data structure to communicate between the
21
* ConvertSelection procedure and TkSelPropProc.
22
*/
23
24
typedef struct IncrInfo {
25
TkWindow *winPtr; /* Window that owns selection. */
26
Atom selection; /* Selection that is being retrieved. */
27
Atom *multAtoms; /* Information about conversions to
28
* perform: one or more pairs of
29
* (target, property). This either
30
* points to a retrieved property (for
31
* MULTIPLE retrievals) or to a static
32
* array. */
33
unsigned long numConversions;
34
/* Number of entries in offsets (same as
35
* # of pairs in multAtoms). */
36
int *offsets; /* One entry for each pair in
37
* multAtoms; -1 means all data has
38
* been transferred for this
39
* conversion. -2 means only the
40
* final zero-length transfer still
41
* has to be done. Otherwise it is the
42
* offset of the next chunk of data
43
* to transfer. This array is malloc-ed. */
44
int numIncrs; /* Number of entries in offsets that
45
* aren't -1 (i.e. # of INCR-mode transfers
46
* not yet completed). */
47
Tcl_TimerToken timeout; /* Token for timer procedure. */
48
int idleTime; /* Number of seconds since we heard
49
* anything from the selection
50
* requestor. */
51
Window reqWindow; /* Requestor's window id. */
52
Time time; /* Timestamp corresponding to
53
* selection at beginning of request;
54
* used to abort transfer if selection
55
* changes. */
56
struct IncrInfo *nextPtr; /* Next in list of all INCR-style
57
* retrievals currently pending. */
58
} IncrInfo;
59
60
static IncrInfo *pendingIncrs = NULL;
61
/* List of all incr structures
62
* currently active. */
63
64
/*
65
* Largest property that we'll accept when sending or receiving the
66
* selection:
67
*/
68
69
#define MAX_PROP_WORDS 100000
70
71
static TkSelRetrievalInfo *pendingRetrievals = NULL;
72
/* List of all retrievals currently
73
* being waited for. */
74
75
/*
76
* Forward declarations for procedures defined in this file:
77
*/
78
79
static void ConvertSelection _ANSI_ARGS_((TkWindow *winPtr,
80
XSelectionRequestEvent *eventPtr));
81
static void IncrTimeoutProc _ANSI_ARGS_((ClientData clientData));
82
static char * SelCvtFromX _ANSI_ARGS_((long *propPtr, int numValues,
83
Atom type, Tk_Window tkwin));
84
static long * SelCvtToX _ANSI_ARGS_((char *string, Atom type,
85
Tk_Window tkwin, int *numLongsPtr));
86
static int SelectionSize _ANSI_ARGS_((TkSelHandler *selPtr));
87
static void SelRcvIncrProc _ANSI_ARGS_((ClientData clientData,
88
XEvent *eventPtr));
89
static void SelTimeoutProc _ANSI_ARGS_((ClientData clientData));
90
91
/*
92
*----------------------------------------------------------------------
93
*
94
* TkSelGetSelection --
95
*
96
* Retrieve the specified selection from another process.
97
*
98
* Results:
99
* The return value is a standard Tcl return value.
100
* If an error occurs (such as no selection exists)
101
* then an error message is left in interp->result.
102
*
103
* Side effects:
104
* None.
105
*
106
*----------------------------------------------------------------------
107
*/
108
109
int
110
TkSelGetSelection(interp, tkwin, selection, target, proc, clientData)
111
Tcl_Interp *interp; /* Interpreter to use for reporting
112
* errors. */
113
Tk_Window tkwin; /* Window on whose behalf to retrieve
114
* the selection (determines display
115
* from which to retrieve). */
116
Atom selection; /* Selection to retrieve. */
117
Atom target; /* Desired form in which selection
118
* is to be returned. */
119
Tk_GetSelProc *proc; /* Procedure to call to process the
120
* selection, once it has been retrieved. */
121
ClientData clientData; /* Arbitrary value to pass to proc. */
122
{
123
TkSelRetrievalInfo retr;
124
TkWindow *winPtr = (TkWindow *) tkwin;
125
TkDisplay *dispPtr = winPtr->dispPtr;
126
127
/*
128
* The selection is owned by some other process. To
129
* retrieve it, first record information about the retrieval
130
* in progress. Use an internal window as the requestor.
131
*/
132
133
retr.interp = interp;
134
if (dispPtr->clipWindow == NULL) {
135
int result;
136
137
result = TkClipInit(interp, dispPtr);
138
if (result != TCL_OK) {
139
return result;
140
}
141
}
142
retr.winPtr = (TkWindow *) dispPtr->clipWindow;
143
retr.selection = selection;
144
retr.property = selection;
145
retr.target = target;
146
retr.proc = proc;
147
retr.clientData = clientData;
148
retr.result = -1;
149
retr.idleTime = 0;
150
retr.nextPtr = pendingRetrievals;
151
pendingRetrievals = &retr;
152
153
/*
154
* Initiate the request for the selection. Note: can't use
155
* TkCurrentTime for the time. If we do, and this application hasn't
156
* received any X events in a long time, the current time will be way
157
* in the past and could even predate the time when the selection was
158
* made; if this happens, the request will be rejected.
159
*/
160
161
XConvertSelection(winPtr->display, retr.selection, retr.target,
162
retr.property, retr.winPtr->window, CurrentTime);
163
164
/*
165
* Enter a loop processing X events until the selection
166
* has been retrieved and processed. If no response is
167
* received within a few seconds, then timeout.
168
*/
169
170
retr.timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc,
171
(ClientData) &retr);
172
while (retr.result == -1) {
173
Tcl_DoOneEvent(0);
174
}
175
Tcl_DeleteTimerHandler(retr.timeout);
176
177
/*
178
* Unregister the information about the selection retrieval
179
* in progress.
180
*/
181
182
if (pendingRetrievals == &retr) {
183
pendingRetrievals = retr.nextPtr;
184
} else {
185
TkSelRetrievalInfo *retrPtr;
186
187
for (retrPtr = pendingRetrievals; retrPtr != NULL;
188
retrPtr = retrPtr->nextPtr) {
189
if (retrPtr->nextPtr == &retr) {
190
retrPtr->nextPtr = retr.nextPtr;
191
break;
192
}
193
}
194
}
195
return retr.result;
196
}
197
198
/*
199
*----------------------------------------------------------------------
200
*
201
* TkSelPropProc --
202
*
203
* This procedure is invoked when property-change events
204
* occur on windows not known to the toolkit. Its function
205
* is to implement the sending side of the INCR selection
206
* retrieval protocol when the selection requestor deletes
207
* the property containing a part of the selection.
208
*
209
* Results:
210
* None.
211
*
212
* Side effects:
213
* If the property that is receiving the selection was just
214
* deleted, then a new piece of the selection is fetched and
215
* placed in the property, until eventually there's no more
216
* selection to fetch.
217
*
218
*----------------------------------------------------------------------
219
*/
220
221
void
222
TkSelPropProc(eventPtr)
223
register XEvent *eventPtr; /* X PropertyChange event. */
224
{
225
register IncrInfo *incrPtr;
226
int i, format;
227
Atom target, formatType;
228
register TkSelHandler *selPtr;
229
long buffer[TK_SEL_WORDS_AT_ONCE];
230
int numItems;
231
char *propPtr;
232
Tk_ErrorHandler errorHandler;
233
234
/*
235
* See if this event announces the deletion of a property being
236
* used for an INCR transfer. If so, then add the next chunk of
237
* data to the property.
238
*/
239
240
if (eventPtr->xproperty.state != PropertyDelete) {
241
return;
242
}
243
for (incrPtr = pendingIncrs; incrPtr != NULL;
244
incrPtr = incrPtr->nextPtr) {
245
if (incrPtr->reqWindow != eventPtr->xproperty.window) {
246
continue;
247
}
248
for (i = 0; i < incrPtr->numConversions; i++) {
249
if ((eventPtr->xproperty.atom != incrPtr->multAtoms[2*i + 1])
250
|| (incrPtr->offsets[i] == -1)){
251
continue;
252
}
253
target = incrPtr->multAtoms[2*i];
254
incrPtr->idleTime = 0;
255
for (selPtr = incrPtr->winPtr->selHandlerList; ;
256
selPtr = selPtr->nextPtr) {
257
if (selPtr == NULL) {
258
incrPtr->multAtoms[2*i + 1] = None;
259
incrPtr->offsets[i] = -1;
260
incrPtr->numIncrs --;
261
return;
262
}
263
if ((selPtr->target == target)
264
&& (selPtr->selection == incrPtr->selection)) {
265
formatType = selPtr->format;
266
if (incrPtr->offsets[i] == -2) {
267
numItems = 0;
268
((char *) buffer)[0] = 0;
269
} else {
270
TkSelInProgress ip;
271
ip.selPtr = selPtr;
272
ip.nextPtr = pendingPtr;
273
pendingPtr = &ip;
274
numItems = (*selPtr->proc)(selPtr->clientData,
275
incrPtr->offsets[i], (char *) buffer,
276
TK_SEL_BYTES_AT_ONCE);
277
pendingPtr = ip.nextPtr;
278
if (ip.selPtr == NULL) {
279
/*
280
* The selection handler deleted itself.
281
*/
282
283
return;
284
}
285
if (numItems > TK_SEL_BYTES_AT_ONCE) {
286
panic("selection handler returned too many bytes");
287
} else {
288
if (numItems < 0) {
289
numItems = 0;
290
}
291
}
292
((char *) buffer)[numItems] = '\0';
293
}
294
if (numItems < TK_SEL_BYTES_AT_ONCE) {
295
if (numItems <= 0) {
296
incrPtr->offsets[i] = -1;
297
incrPtr->numIncrs--;
298
} else {
299
incrPtr->offsets[i] = -2;
300
}
301
} else {
302
incrPtr->offsets[i] += numItems;
303
}
304
if (formatType == XA_STRING) {
305
propPtr = (char *) buffer;
306
format = 8;
307
} else {
308
propPtr = (char *) SelCvtToX((char *) buffer,
309
formatType, (Tk_Window) incrPtr->winPtr,
310
&numItems);
311
format = 32;
312
}
313
errorHandler = Tk_CreateErrorHandler(
314
eventPtr->xproperty.display, -1, -1, -1,
315
(int (*)()) NULL, (ClientData) NULL);
316
XChangeProperty(eventPtr->xproperty.display,
317
eventPtr->xproperty.window,
318
eventPtr->xproperty.atom, formatType,
319
format, PropModeReplace,
320
(unsigned char *) propPtr, numItems);
321
Tk_DeleteErrorHandler(errorHandler);
322
if (propPtr != (char *) buffer) {
323
ckfree(propPtr);
324
}
325
return;
326
}
327
}
328
}
329
}
330
}
331
332
/*
333
*--------------------------------------------------------------
334
*
335
* TkSelEventProc --
336
*
337
* This procedure is invoked whenever a selection-related
338
* event occurs. It does the lion's share of the work
339
* in implementing the selection protocol.
340
*
341
* Results:
342
* None.
343
*
344
* Side effects:
345
* Lots: depends on the type of event.
346
*
347
*--------------------------------------------------------------
348
*/
349
350
void
351
TkSelEventProc(tkwin, eventPtr)
352
Tk_Window tkwin; /* Window for which event was
353
* targeted. */
354
register XEvent *eventPtr; /* X event: either SelectionClear,
355
* SelectionRequest, or
356
* SelectionNotify. */
357
{
358
register TkWindow *winPtr = (TkWindow *) tkwin;
359
TkDisplay *dispPtr = winPtr->dispPtr;
360
Tcl_Interp *interp;
361
362
/*
363
* Case #1: SelectionClear events.
364
*/
365
366
if (eventPtr->type == SelectionClear) {
367
TkSelClearSelection(tkwin, eventPtr);
368
}
369
370
/*
371
* Case #2: SelectionNotify events. Call the relevant procedure
372
* to handle the incoming selection.
373
*/
374
375
if (eventPtr->type == SelectionNotify) {
376
register TkSelRetrievalInfo *retrPtr;
377
char *propInfo;
378
Atom type;
379
int format, result;
380
unsigned long numItems, bytesAfter;
381
382
for (retrPtr = pendingRetrievals; ; retrPtr = retrPtr->nextPtr) {
383
if (retrPtr == NULL) {
384
return;
385
}
386
if ((retrPtr->winPtr == winPtr)
387
&& (retrPtr->selection == eventPtr->xselection.selection)
388
&& (retrPtr->target == eventPtr->xselection.target)
389
&& (retrPtr->result == -1)) {
390
if (retrPtr->property == eventPtr->xselection.property) {
391
break;
392
}
393
if (eventPtr->xselection.property == None) {
394
Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
395
Tcl_AppendResult(retrPtr->interp,
396
Tk_GetAtomName(tkwin, retrPtr->selection),
397
" selection doesn't exist or form \"",
398
Tk_GetAtomName(tkwin, retrPtr->target),
399
"\" not defined", (char *) NULL);
400
retrPtr->result = TCL_ERROR;
401
return;
402
}
403
}
404
}
405
406
propInfo = NULL;
407
result = XGetWindowProperty(eventPtr->xselection.display,
408
eventPtr->xselection.requestor, retrPtr->property,
409
0, MAX_PROP_WORDS, False, (Atom) AnyPropertyType,
410
&type, &format, &numItems, &bytesAfter,
411
(unsigned char **) &propInfo);
412
if ((result != Success) || (type == None)) {
413
return;
414
}
415
if (bytesAfter != 0) {
416
Tcl_SetResult(retrPtr->interp, "selection property too large",
417
TCL_STATIC);
418
retrPtr->result = TCL_ERROR;
419
XFree(propInfo);
420
return;
421
}
422
if ((type == XA_STRING) || (type == dispPtr->textAtom)
423
|| (type == dispPtr->compoundTextAtom)) {
424
if (format != 8) {
425
sprintf(retrPtr->interp->result,
426
"bad format for string selection: wanted \"8\", got \"%d\"",
427
format);
428
retrPtr->result = TCL_ERROR;
429
return;
430
}
431
interp = retrPtr->interp;
432
Tcl_Preserve((ClientData) interp);
433
retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
434
interp, propInfo);
435
Tcl_Release((ClientData) interp);
436
} else if (type == dispPtr->incrAtom) {
437
438
/*
439
* It's a !?#@!?!! INCR-style reception. Arrange to receive
440
* the selection in pieces, using the ICCCM protocol, then
441
* hang around until either the selection is all here or a
442
* timeout occurs.
443
*/
444
445
retrPtr->idleTime = 0;
446
Tk_CreateEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
447
(ClientData) retrPtr);
448
XDeleteProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
449
retrPtr->property);
450
while (retrPtr->result == -1) {
451
Tcl_DoOneEvent(0);
452
}
453
Tk_DeleteEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
454
(ClientData) retrPtr);
455
} else {
456
char *string;
457
458
if (format != 32) {
459
sprintf(retrPtr->interp->result,
460
"bad format for selection: wanted \"32\", got \"%d\"",
461
format);
462
retrPtr->result = TCL_ERROR;
463
return;
464
}
465
string = SelCvtFromX((long *) propInfo, (int) numItems, type,
466
(Tk_Window) winPtr);
467
interp = retrPtr->interp;
468
Tcl_Preserve((ClientData) interp);
469
retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
470
interp, string);
471
Tcl_Release((ClientData) interp);
472
ckfree(string);
473
}
474
XFree(propInfo);
475
return;
476
}
477
478
/*
479
* Case #3: SelectionRequest events. Call ConvertSelection to
480
* do the dirty work.
481
*/
482
483
if (eventPtr->type == SelectionRequest) {
484
ConvertSelection(winPtr, &eventPtr->xselectionrequest);
485
return;
486
}
487
}
488
489
/*
490
*----------------------------------------------------------------------
491
*
492
* SelTimeoutProc --
493
*
494
* This procedure is invoked once every second while waiting for
495
* the selection to be returned. After a while it gives up and
496
* aborts the selection retrieval.
497
*
498
* Results:
499
* None.
500
*
501
* Side effects:
502
* A new timer callback is created to call us again in another
503
* second, unless time has expired, in which case an error is
504
* recorded for the retrieval.
505
*
506
*----------------------------------------------------------------------
507
*/
508
509
static void
510
SelTimeoutProc(clientData)
511
ClientData clientData; /* Information about retrieval
512
* in progress. */
513
{
514
register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
515
516
/*
517
* Make sure that the retrieval is still in progress. Then
518
* see how long it's been since any sort of response was received
519
* from the other side.
520
*/
521
522
if (retrPtr->result != -1) {
523
return;
524
}
525
retrPtr->idleTime++;
526
if (retrPtr->idleTime >= 5) {
527
528
/*
529
* Use a careful procedure to store the error message, because
530
* the result could already be partially filled in with a partial
531
* selection return.
532
*/
533
534
Tcl_SetResult(retrPtr->interp, "selection owner didn't respond",
535
TCL_STATIC);
536
retrPtr->result = TCL_ERROR;
537
} else {
538
retrPtr->timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc,
539
(ClientData) retrPtr);
540
}
541
}
542
543
/*
544
*----------------------------------------------------------------------
545
*
546
* ConvertSelection --
547
*
548
* This procedure is invoked to handle SelectionRequest events.
549
* It responds to the requests, obeying the ICCCM protocols.
550
*
551
* Results:
552
* None.
553
*
554
* Side effects:
555
* Properties are created for the selection requestor, and a
556
* SelectionNotify event is generated for the selection
557
* requestor. In the event of long selections, this procedure
558
* implements INCR-mode transfers, using the ICCCM protocol.
559
*
560
*----------------------------------------------------------------------
561
*/
562
563
static void
564
ConvertSelection(winPtr, eventPtr)
565
TkWindow *winPtr; /* Window that received the
566
* conversion request; may not be
567
* selection's current owner, be we
568
* set it to the current owner. */
569
register XSelectionRequestEvent *eventPtr;
570
/* Event describing request. */
571
{
572
XSelectionEvent reply; /* Used to notify requestor that
573
* selection info is ready. */
574
int multiple; /* Non-zero means a MULTIPLE request
575
* is being handled. */
576
IncrInfo incr; /* State of selection conversion. */
577
Atom singleInfo[2]; /* incr.multAtoms points here except
578
* for multiple conversions. */
579
int i;
580
Tk_ErrorHandler errorHandler;
581
TkSelectionInfo *infoPtr;
582
TkSelInProgress ip;
583
584
errorHandler = Tk_CreateErrorHandler(eventPtr->display, -1, -1,-1,
585
(int (*)()) NULL, (ClientData) NULL);
586
587
/*
588
* Initialize the reply event.
589
*/
590
591
reply.type = SelectionNotify;
592
reply.serial = 0;
593
reply.send_event = True;
594
reply.display = eventPtr->display;
595
reply.requestor = eventPtr->requestor;
596
reply.selection = eventPtr->selection;
597
reply.target = eventPtr->target;
598
reply.property = eventPtr->property;
599
if (reply.property == None) {
600
reply.property = reply.target;
601
}
602
reply.time = eventPtr->time;
603
604
for (infoPtr = winPtr->dispPtr->selectionInfoPtr; infoPtr != NULL;
605
infoPtr = infoPtr->nextPtr) {
606
if (infoPtr->selection == eventPtr->selection)
607
break;
608
}
609
if (infoPtr == NULL) {
610
goto refuse;
611
}
612
winPtr = (TkWindow *) infoPtr->owner;
613
614
/*
615
* Figure out which kind(s) of conversion to perform. If handling
616
* a MULTIPLE conversion, then read the property describing which
617
* conversions to perform.
618
*/
619
620
incr.winPtr = winPtr;
621
incr.selection = eventPtr->selection;
622
if (eventPtr->target != winPtr->dispPtr->multipleAtom) {
623
multiple = 0;
624
singleInfo[0] = reply.target;
625
singleInfo[1] = reply.property;
626
incr.multAtoms = singleInfo;
627
incr.numConversions = 1;
628
} else {
629
Atom type;
630
int format, result;
631
unsigned long bytesAfter;
632
633
multiple = 1;
634
incr.multAtoms = NULL;
635
if (eventPtr->property == None) {
636
goto refuse;
637
}
638
result = XGetWindowProperty(eventPtr->display,
639
eventPtr->requestor, eventPtr->property,
640
0, MAX_PROP_WORDS, False, XA_ATOM,
641
&type, &format, &incr.numConversions, &bytesAfter,
642
(unsigned char **) &incr.multAtoms);
643
if ((result != Success) || (bytesAfter != 0) || (format != 32)
644
|| (type == None)) {
645
if (incr.multAtoms != NULL) {
646
XFree((char *) incr.multAtoms);
647
}
648
goto refuse;
649
}
650
incr.numConversions /= 2; /* Two atoms per conversion. */
651
}
652
653
/*
654
* Loop through all of the requested conversions, and either return
655
* the entire converted selection, if it can be returned in a single
656
* bunch, or return INCR information only (the actual selection will
657
* be returned below).
658
*/
659
660
incr.offsets = (int *) ckalloc((unsigned)
661
(incr.numConversions*sizeof(int)));
662
incr.numIncrs = 0;
663
for (i = 0; i < incr.numConversions; i++) {
664
Atom target, property, type;
665
long buffer[TK_SEL_WORDS_AT_ONCE];
666
register TkSelHandler *selPtr;
667
int numItems, format;
668
char *propPtr;
669
670
target = incr.multAtoms[2*i];
671
property = incr.multAtoms[2*i + 1];
672
incr.offsets[i] = -1;
673
674
for (selPtr = winPtr->selHandlerList; selPtr != NULL;
675
selPtr = selPtr->nextPtr) {
676
if ((selPtr->target == target)
677
&& (selPtr->selection == eventPtr->selection)) {
678
break;
679
}
680
}
681
682
if (selPtr == NULL) {
683
/*
684
* Nobody seems to know about this kind of request. If
685
* it's of a sort that we can handle without any help, do
686
* it. Otherwise mark the request as an errror.
687
*/
688
689
numItems = TkSelDefaultSelection(infoPtr, target, (char *) buffer,
690
TK_SEL_BYTES_AT_ONCE, &type);
691
if (numItems < 0) {
692
incr.multAtoms[2*i + 1] = None;
693
continue;
694
}
695
} else {
696
ip.selPtr = selPtr;
697
ip.nextPtr = pendingPtr;
698
pendingPtr = &ip;
699
type = selPtr->format;
700
numItems = (*selPtr->proc)(selPtr->clientData, 0,
701
(char *) buffer, TK_SEL_BYTES_AT_ONCE);
702
pendingPtr = ip.nextPtr;
703
if ((ip.selPtr == NULL) || (numItems < 0)) {
704
incr.multAtoms[2*i + 1] = None;
705
continue;
706
}
707
if (numItems > TK_SEL_BYTES_AT_ONCE) {
708
panic("selection handler returned too many bytes");
709
}
710
((char *) buffer)[numItems] = '\0';
711
}
712
713
/*
714
* Got the selection; store it back on the requestor's property.
715
*/
716
717
if (numItems == TK_SEL_BYTES_AT_ONCE) {
718
/*
719
* Selection is too big to send at once; start an
720
* INCR-mode transfer.
721
*/
722
723
incr.numIncrs++;
724
type = winPtr->dispPtr->incrAtom;
725
buffer[0] = SelectionSize(selPtr);
726
if (buffer[0] == 0) {
727
incr.multAtoms[2*i + 1] = None;
728
continue;
729
}
730
numItems = 1;
731
propPtr = (char *) buffer;
732
format = 32;
733
incr.offsets[i] = 0;
734
} else if (type == XA_STRING) {
735
propPtr = (char *) buffer;
736
format = 8;
737
} else {
738
propPtr = (char *) SelCvtToX((char *) buffer,
739
type, (Tk_Window) winPtr, &numItems);
740
format = 32;
741
}
742
XChangeProperty(reply.display, reply.requestor,
743
property, type, format, PropModeReplace,
744
(unsigned char *) propPtr, numItems);
745
if (propPtr != (char *) buffer) {
746
ckfree(propPtr);
747
}
748
}
749
750
/*
751
* Send an event back to the requestor to indicate that the
752
* first stage of conversion is complete (everything is done
753
* except for long conversions that have to be done in INCR
754
* mode).
755
*/
756
757
if (incr.numIncrs > 0) {
758
XSelectInput(reply.display, reply.requestor, PropertyChangeMask);
759
incr.timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
760
(ClientData) &incr);
761
incr.idleTime = 0;
762
incr.reqWindow = reply.requestor;
763
incr.time = infoPtr->time;
764
incr.nextPtr = pendingIncrs;
765
pendingIncrs = &incr;
766
}
767
if (multiple) {
768
XChangeProperty(reply.display, reply.requestor, reply.property,
769
XA_ATOM, 32, PropModeReplace,
770
(unsigned char *) incr.multAtoms,
771
(int) incr.numConversions*2);
772
} else {
773
774
/*
775
* Not a MULTIPLE request. The first property in "multAtoms"
776
* got set to None if there was an error in conversion.
777
*/
778
779
reply.property = incr.multAtoms[1];
780
}
781
XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
782
Tk_DeleteErrorHandler(errorHandler);
783
784
/*
785
* Handle any remaining INCR-mode transfers. This all happens
786
* in callbacks to TkSelPropProc, so just wait until the number
787
* of uncompleted INCR transfers drops to zero.
788
*/
789
790
if (incr.numIncrs > 0) {
791
IncrInfo *incrPtr2;
792
793
while (incr.numIncrs > 0) {
794
Tcl_DoOneEvent(0);
795
}
796
Tcl_DeleteTimerHandler(incr.timeout);
797
errorHandler = Tk_CreateErrorHandler(winPtr->display,
798
-1, -1,-1, (int (*)()) NULL, (ClientData) NULL);
799
XSelectInput(reply.display, reply.requestor, 0L);
800
Tk_DeleteErrorHandler(errorHandler);
801
if (pendingIncrs == &incr) {
802
pendingIncrs = incr.nextPtr;
803
} else {
804
for (incrPtr2 = pendingIncrs; incrPtr2 != NULL;
805
incrPtr2 = incrPtr2->nextPtr) {
806
if (incrPtr2->nextPtr == &incr) {
807
incrPtr2->nextPtr = incr.nextPtr;
808
break;
809
}
810
}
811
}
812
}
813
814
/*
815
* All done. Cleanup and return.
816
*/
817
818
ckfree((char *) incr.offsets);
819
if (multiple) {
820
XFree((char *) incr.multAtoms);
821
}
822
return;
823
824
/*
825
* An error occurred. Send back a refusal message.
826
*/
827
828
refuse:
829
reply.property = None;
830
XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
831
Tk_DeleteErrorHandler(errorHandler);
832
return;
833
}
834
835
/*
836
*----------------------------------------------------------------------
837
*
838
* SelRcvIncrProc --
839
*
840
* This procedure handles the INCR protocol on the receiving
841
* side. It is invoked in response to property changes on
842
* the requestor's window (which hopefully are because a new
843
* chunk of the selection arrived).
844
*
845
* Results:
846
* None.
847
*
848
* Side effects:
849
* If a new piece of selection has arrived, a procedure is
850
* invoked to deal with that piece. When the whole selection
851
* is here, a flag is left for the higher-level procedure that
852
* initiated the selection retrieval.
853
*
854
*----------------------------------------------------------------------
855
*/
856
857
static void
858
SelRcvIncrProc(clientData, eventPtr)
859
ClientData clientData; /* Information about retrieval. */
860
register XEvent *eventPtr; /* X PropertyChange event. */
861
{
862
register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
863
char *propInfo;
864
Atom type;
865
int format, result;
866
unsigned long numItems, bytesAfter;
867
Tcl_Interp *interp;
868
869
if ((eventPtr->xproperty.atom != retrPtr->property)
870
|| (eventPtr->xproperty.state != PropertyNewValue)
871
|| (retrPtr->result != -1)) {
872
return;
873
}
874
propInfo = NULL;
875
result = XGetWindowProperty(eventPtr->xproperty.display,
876
eventPtr->xproperty.window, retrPtr->property, 0, MAX_PROP_WORDS,
877
True, (Atom) AnyPropertyType, &type, &format, &numItems,
878
&bytesAfter, (unsigned char **) &propInfo);
879
if ((result != Success) || (type == None)) {
880
return;
881
}
882
if (bytesAfter != 0) {
883
Tcl_SetResult(retrPtr->interp, "selection property too large",
884
TCL_STATIC);
885
retrPtr->result = TCL_ERROR;
886
goto done;
887
}
888
if (numItems == 0) {
889
retrPtr->result = TCL_OK;
890
} else if ((type == XA_STRING)
891
|| (type == retrPtr->winPtr->dispPtr->textAtom)
892
|| (type == retrPtr->winPtr->dispPtr->compoundTextAtom)) {
893
if (format != 8) {
894
Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
895
sprintf(retrPtr->interp->result,
896
"bad format for string selection: wanted \"8\", got \"%d\"",
897
format);
898
retrPtr->result = TCL_ERROR;
899
goto done;
900
}
901
interp = retrPtr->interp;
902
Tcl_Preserve((ClientData) interp);
903
result = (*retrPtr->proc)(retrPtr->clientData, interp, propInfo);
904
Tcl_Release((ClientData) interp);
905
if (result != TCL_OK) {
906
retrPtr->result = result;
907
}
908
} else {
909
char *string;
910
911
if (format != 32) {
912
Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
913
sprintf(retrPtr->interp->result,
914
"bad format for selection: wanted \"32\", got \"%d\"",
915
format);
916
retrPtr->result = TCL_ERROR;
917
goto done;
918
}
919
string = SelCvtFromX((long *) propInfo, (int) numItems, type,
920
(Tk_Window) retrPtr->winPtr);
921
interp = retrPtr->interp;
922
Tcl_Preserve((ClientData) interp);
923
result = (*retrPtr->proc)(retrPtr->clientData, interp, string);
924
Tcl_Release((ClientData) interp);
925
if (result != TCL_OK) {
926
retrPtr->result = result;
927
}
928
ckfree(string);
929
}
930
931
done:
932
XFree(propInfo);
933
retrPtr->idleTime = 0;
934
}
935
936
/*
937
*----------------------------------------------------------------------
938
*
939
* SelectionSize --
940
*
941
* This procedure is called when the selection is too large to
942
* send in a single buffer; it computes the total length of
943
* the selection in bytes.
944
*
945
* Results:
946
* The return value is the number of bytes in the selection
947
* given by selPtr.
948
*
949
* Side effects:
950
* The selection is retrieved from its current owner (this is
951
* the only way to compute its size).
952
*
953
*----------------------------------------------------------------------
954
*/
955
956
static int
957
SelectionSize(selPtr)
958
TkSelHandler *selPtr; /* Information about how to retrieve
959
* the selection whose size is wanted. */
960
{
961
char buffer[TK_SEL_BYTES_AT_ONCE+1];
962
int size, chunkSize;
963
TkSelInProgress ip;
964
965
size = TK_SEL_BYTES_AT_ONCE;
966
ip.selPtr = selPtr;
967
ip.nextPtr = pendingPtr;
968
pendingPtr = &ip;
969
do {
970
chunkSize = (*selPtr->proc)(selPtr->clientData, size,
971
(char *) buffer, TK_SEL_BYTES_AT_ONCE);
972
if (ip.selPtr == NULL) {
973
size = 0;
974
break;
975
}
976
size += chunkSize;
977
} while (chunkSize == TK_SEL_BYTES_AT_ONCE);
978
pendingPtr = ip.nextPtr;
979
return size;
980
}
981
982
/*
983
*----------------------------------------------------------------------
984
*
985
* IncrTimeoutProc --
986
*
987
* This procedure is invoked once a second while sending the
988
* selection to a requestor in INCR mode. After a while it
989
* gives up and aborts the selection operation.
990
*
991
* Results:
992
* None.
993
*
994
* Side effects:
995
* A new timeout gets registered so that this procedure gets
996
* called again in another second, unless too many seconds
997
* have elapsed, in which case incrPtr is marked as "all done".
998
*
999
*----------------------------------------------------------------------
1000
*/
1001
1002
static void
1003
IncrTimeoutProc(clientData)
1004
ClientData clientData; /* Information about INCR-mode
1005
* selection retrieval for which
1006
* we are selection owner. */
1007
{
1008
register IncrInfo *incrPtr = (IncrInfo *) clientData;
1009
1010
incrPtr->idleTime++;
1011
if (incrPtr->idleTime >= 5) {
1012
incrPtr->numIncrs = 0;
1013
} else {
1014
incrPtr->timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
1015
(ClientData) incrPtr);
1016
}
1017
}
1018
1019
/*
1020
*----------------------------------------------------------------------
1021
*
1022
* SelCvtToX --
1023
*
1024
* Given a selection represented as a string (the normal Tcl form),
1025
* convert it to the ICCCM-mandated format for X, depending on
1026
* the type argument. This procedure and SelCvtFromX are inverses.
1027
*
1028
* Results:
1029
* The return value is a malloc'ed buffer holding a value
1030
* equivalent to "string", but formatted as for "type". It is
1031
* the caller's responsibility to free the string when done with
1032
* it. The word at *numLongsPtr is filled in with the number of
1033
* 32-bit words returned in the result.
1034
*
1035
* Side effects:
1036
* None.
1037
*
1038
*----------------------------------------------------------------------
1039
*/
1040
1041
static long *
1042
SelCvtToX(string, type, tkwin, numLongsPtr)
1043
char *string; /* String representation of selection. */
1044
Atom type; /* Atom specifying the X format that is
1045
* desired for the selection. Should not
1046
* be XA_STRING (if so, don't bother calling
1047
* this procedure at all). */
1048
Tk_Window tkwin; /* Window that governs atom conversion. */
1049
int *numLongsPtr; /* Number of 32-bit words contained in the
1050
* result. */
1051
{
1052
register char *p;
1053
char *field;
1054
int numFields;
1055
long *propPtr, *longPtr;
1056
#define MAX_ATOM_NAME_LENGTH 100
1057
char atomName[MAX_ATOM_NAME_LENGTH+1];
1058
1059
/*
1060
* The string is assumed to consist of fields separated by spaces.
1061
* The property gets generated by converting each field to an
1062
* integer number, in one of two ways:
1063
* 1. If type is XA_ATOM, convert each field to its corresponding
1064
* atom.
1065
* 2. If type is anything else, convert each field from an ASCII number
1066
* to a 32-bit binary number.
1067
*/
1068
1069
numFields = 1;
1070
for (p = string; *p != 0; p++) {
1071
if (isspace(UCHAR(*p))) {
1072
numFields++;
1073
}
1074
}
1075
propPtr = (long *) ckalloc((unsigned) numFields*sizeof(long));
1076
1077
/*
1078
* Convert the fields one-by-one.
1079
*/
1080
1081
for (longPtr = propPtr, *numLongsPtr = 0, p = string;
1082
; longPtr++, (*numLongsPtr)++) {
1083
while (isspace(UCHAR(*p))) {
1084
p++;
1085
}
1086
if (*p == 0) {
1087
break;
1088
}
1089
field = p;
1090
while ((*p != 0) && !isspace(UCHAR(*p))) {
1091
p++;
1092
}
1093
if (type == XA_ATOM) {
1094
int length;
1095
1096
length = p - field;
1097
if (length > MAX_ATOM_NAME_LENGTH) {
1098
length = MAX_ATOM_NAME_LENGTH;
1099
}
1100
strncpy(atomName, field, (unsigned) length);
1101
atomName[length] = 0;
1102
*longPtr = (long) Tk_InternAtom(tkwin, atomName);
1103
} else {
1104
char *dummy;
1105
1106
*longPtr = strtol(field, &dummy, 0);
1107
}
1108
}
1109
return propPtr;
1110
}
1111
1112
/*
1113
*----------------------------------------------------------------------
1114
*
1115
* SelCvtFromX --
1116
*
1117
* Given an X property value, formatted as a collection of 32-bit
1118
* values according to "type" and the ICCCM conventions, convert
1119
* the value to a string suitable for manipulation by Tcl. This
1120
* procedure is the inverse of SelCvtToX.
1121
*
1122
* Results:
1123
* The return value is the string equivalent of "property". It is
1124
* malloc-ed and should be freed by the caller when no longer
1125
* needed.
1126
*
1127
* Side effects:
1128
* None.
1129
*
1130
*----------------------------------------------------------------------
1131
*/
1132
1133
static char *
1134
SelCvtFromX(propPtr, numValues, type, tkwin)
1135
register long *propPtr; /* Property value from X. */
1136
int numValues; /* Number of 32-bit values in property. */
1137
Atom type; /* Type of property Should not be
1138
* XA_STRING (if so, don't bother calling
1139
* this procedure at all). */
1140
Tk_Window tkwin; /* Window to use for atom conversion. */
1141
{
1142
char *result;
1143
int resultSpace, curSize, fieldSize;
1144
char *atomName;
1145
1146
/*
1147
* Convert each long in the property to a string value, which is
1148
* either the name of an atom (if type is XA_ATOM) or a hexadecimal
1149
* string. Make an initial guess about the size of the result, but
1150
* be prepared to enlarge the result if necessary.
1151
*/
1152
1153
resultSpace = 12*numValues+1;
1154
curSize = 0;
1155
atomName = ""; /* Not needed, but eliminates compiler warning. */
1156
result = (char *) ckalloc((unsigned) resultSpace);
1157
*result = '\0';
1158
for ( ; numValues > 0; propPtr++, numValues--) {
1159
if (type == XA_ATOM) {
1160
atomName = Tk_GetAtomName(tkwin, (Atom) *propPtr);
1161
fieldSize = strlen(atomName) + 1;
1162
} else {
1163
fieldSize = 12;
1164
}
1165
if (curSize+fieldSize >= resultSpace) {
1166
char *newResult;
1167
1168
resultSpace *= 2;
1169
if (curSize+fieldSize >= resultSpace) {
1170
resultSpace = curSize + fieldSize + 1;
1171
}
1172
newResult = (char *) ckalloc((unsigned) resultSpace);
1173
strncpy(newResult, result, (unsigned) curSize);
1174
ckfree(result);
1175
result = newResult;
1176
}
1177
if (curSize != 0) {
1178
result[curSize] = ' ';
1179
curSize++;
1180
}
1181
if (type == XA_ATOM) {
1182
strcpy(result+curSize, atomName);
1183
} else {
1184
sprintf(result+curSize, "0x%x", (unsigned int) *propPtr);
1185
}
1186
curSize += strlen(result+curSize);
1187
}
1188
return result;
1189
}
1190
1191