OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tk/] [unix/] [tkUnixSelect.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
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
 * RCS: @(#) $Id: tkUnixSelect.c,v 1.1.1.1 2002-01-16 10:26:01 markom Exp $
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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.