Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/unix/tclXtNotify.c @ 25

Last change on this file since 25 was 25, checked in by landauf, 16 years ago

added tcl to libs

File size: 16.0 KB
Line 
1/*
2 * tclXtNotify.c --
3 *
4 *      This file contains the notifier driver implementation for the Xt
5 *      intrinsics.
6 *
7 * Copyright (c) 1997 by Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tclXtNotify.c,v 1.9 2007/04/16 13:36:36 dkf Exp $
13 */
14
15#include <X11/Intrinsic.h>
16#include "tclInt.h"
17
18/*
19 * This structure is used to keep track of the notifier info for a a
20 * registered file.
21 */
22
23typedef struct FileHandler {
24    int fd;
25    int mask;                   /* Mask of desired events: TCL_READABLE,
26                                 * etc. */
27    int readyMask;              /* Events that have been seen since the last
28                                 * time FileHandlerEventProc was called for
29                                 * this file. */
30    XtInputId read;             /* Xt read callback handle. */
31    XtInputId write;            /* Xt write callback handle. */
32    XtInputId except;           /* Xt exception callback handle. */
33    Tcl_FileProc *proc;         /* Procedure to call, in the style of
34                                 * Tcl_CreateFileHandler. */
35    ClientData clientData;      /* Argument to pass to proc. */
36    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
37} FileHandler;
38
39/*
40 * The following structure is what is added to the Tcl event queue when file
41 * handlers are ready to fire.
42 */
43
44typedef struct FileHandlerEvent {
45    Tcl_Event header;           /* Information that is standard for all
46                                 * events. */
47    int fd;                     /* File descriptor that is ready. Used to find
48                                 * the FileHandler structure for the file
49                                 * (can't point directly to the FileHandler
50                                 * structure because it could go away while
51                                 * the event is queued). */
52} FileHandlerEvent;
53
54/*
55 * The following static structure contains the state information for the Xt
56 * based implementation of the Tcl notifier.
57 */
58
59static struct NotifierState {
60    XtAppContext appContext;    /* The context used by the Xt notifier. Can be
61                                 * set with TclSetAppContext. */
62    int appContextCreated;      /* Was it created by us? */
63    XtIntervalId currentTimeout;/* Handle of current timer. */
64    FileHandler *firstFileHandlerPtr;
65                                /* Pointer to head of file handler list. */
66} notifier;
67
68/*
69 * The following static indicates whether this module has been initialized.
70 */
71
72static int initialized = 0;
73
74/*
75 * Static routines defined in this file.
76 */
77
78static int              FileHandlerEventProc(Tcl_Event *evPtr, int flags);
79static void             FileProc(caddr_t clientData, int *source,
80                            XtInputId *id);
81void                    InitNotifier(void);
82static void             NotifierExitHandler(ClientData clientData);
83static void             TimerProc(caddr_t clientData, XtIntervalId *id);
84static void             CreateFileHandler(int fd, int mask,
85                                Tcl_FileProc * proc, ClientData clientData);
86static void             DeleteFileHandler(int fd);
87static void             SetTimer(Tcl_Time * timePtr);
88static int              WaitForEvent(Tcl_Time * timePtr);
89
90/*
91 * Functions defined in this file for use by users of the Xt Notifier:
92 */
93
94EXTERN XtAppContext     TclSetAppContext(XtAppContext ctx);
95
96/*
97 *----------------------------------------------------------------------
98 *
99 * TclSetAppContext --
100 *
101 *      Set the notifier application context.
102 *
103 * Results:
104 *      None.
105 *
106 * Side effects:
107 *      Sets the application context used by the notifier. Panics if the
108 *      context is already set when called.
109 *
110 *----------------------------------------------------------------------
111 */
112
113XtAppContext
114TclSetAppContext(
115    XtAppContext appContext)
116{
117    if (!initialized) {
118        InitNotifier();
119    }
120
121    /*
122     * If we already have a context we check whether we were asked to set a
123     * new context. If so, we panic because we try to prevent switching
124     * contexts by mistake. Otherwise, we return the one we have.
125     */
126
127    if (notifier.appContext != NULL) {
128        if (appContext != NULL) {
129            /*
130             * We already have a context. We do not allow switching contexts
131             * after initialization, so we panic.
132             */
133
134            Tcl_Panic("TclSetAppContext:  multiple application contexts");
135        }
136    } else {
137        /*
138         * If we get here we have not yet gotten a context, so either create
139         * one or use the one supplied by our caller.
140         */
141
142        if (appContext == NULL) {
143            /*
144             * We must create a new context and tell our caller what it is, so
145             * she can use it too.
146             */
147
148            notifier.appContext = XtCreateApplicationContext();
149            notifier.appContextCreated = 1;
150        } else {
151            /*
152             * Otherwise we remember the context that our caller gave us and
153             * use it.
154             */
155
156            notifier.appContextCreated = 0;
157            notifier.appContext = appContext;
158        }
159    }
160
161    return notifier.appContext;
162}
163
164/*
165 *----------------------------------------------------------------------
166 *
167 * InitNotifier --
168 *
169 *      Initializes the notifier state.
170 *
171 * Results:
172 *      None.
173 *
174 * Side effects:
175 *      Creates a new exit handler.
176 *
177 *----------------------------------------------------------------------
178 */
179
180void
181InitNotifier(void)
182{
183    Tcl_NotifierProcs notifier;
184
185    /*
186     * Only reinitialize if we are not in exit handling. The notifier can get
187     * reinitialized after its own exit handler has run, because of exit
188     * handlers for the I/O and timer sub-systems (order dependency).
189     */
190
191    if (TclInExit()) {
192        return;
193    }
194
195    notifier.createFileHandlerProc = CreateFileHandler;
196    notifier.deleteFileHandlerProc = DeleteFileHandler;
197    notifier.setTimerProc = SetTimer;
198    notifier.waitForEventProc = WaitForEvent;
199    Tcl_SetNotifier(&notifier);
200
201    /*
202     * DO NOT create the application context yet; doing so would prevent
203     * external applications from setting it for us to their own ones.
204     */
205
206    initialized = 1;
207    memset(&notifier, 0, sizeof(notifier));
208    Tcl_CreateExitHandler(NotifierExitHandler, NULL);
209}
210
211/*
212 *----------------------------------------------------------------------
213 *
214 * NotifierExitHandler --
215 *
216 *      This function is called to cleanup the notifier state before Tcl is
217 *      unloaded.
218 *
219 * Results:
220 *      None.
221 *
222 * Side effects:
223 *      Destroys the notifier window.
224 *
225 *----------------------------------------------------------------------
226 */
227
228static void
229NotifierExitHandler(
230    ClientData clientData)      /* Not used. */
231{
232    if (notifier.currentTimeout != 0) {
233        XtRemoveTimeOut(notifier.currentTimeout);
234    }
235    for (; notifier.firstFileHandlerPtr != NULL; ) {
236        Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd);
237    }
238    if (notifier.appContextCreated) {
239        XtDestroyApplicationContext(notifier.appContext);
240        notifier.appContextCreated = 0;
241        notifier.appContext = NULL;
242    }
243    initialized = 0;
244}
245
246/*
247 *----------------------------------------------------------------------
248 *
249 * SetTimer --
250 *
251 *      This procedure sets the current notifier timeout value.
252 *
253 * Results:
254 *      None.
255 *
256 * Side effects:
257 *      Replaces any previous timer.
258 *
259 *----------------------------------------------------------------------
260 */
261
262static void
263SetTimer(
264    Tcl_Time *timePtr)          /* Timeout value, may be NULL. */
265{
266    long timeout;
267
268    if (!initialized) {
269        InitNotifier();
270    }
271
272    TclSetAppContext(NULL);
273    if (notifier.currentTimeout != 0) {
274        XtRemoveTimeOut(notifier.currentTimeout);
275    }
276    if (timePtr) {
277        timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
278        notifier.currentTimeout = XtAppAddTimeOut(notifier.appContext,
279                (unsigned long) timeout, TimerProc, NULL);
280    } else {
281        notifier.currentTimeout = 0;
282    }
283}
284
285/*
286 *----------------------------------------------------------------------
287 *
288 * TimerProc --
289 *
290 *      This procedure is the XtTimerCallbackProc used to handle timeouts.
291 *
292 * Results:
293 *      None.
294 *
295 * Side effects:
296 *      Processes all queued events.
297 *
298 *----------------------------------------------------------------------
299 */
300
301static void
302TimerProc(
303    caddr_t data,               /* Not used. */
304    XtIntervalId *id)
305{
306    if (*id != notifier.currentTimeout) {
307        return;
308    }
309    notifier.currentTimeout = 0;
310
311    Tcl_ServiceAll();
312}
313
314/*
315 *----------------------------------------------------------------------
316 *
317 * CreateFileHandler --
318 *
319 *      This procedure registers a file handler with the Xt notifier.
320 *
321 * Results:
322 *      None.
323 *
324 * Side effects:
325 *      Creates a new file handler structure and registers one or more input
326 *      procedures with Xt.
327 *
328 *----------------------------------------------------------------------
329 */
330
331static void
332CreateFileHandler(
333    int fd,                     /* Handle of stream to watch. */
334    int mask,                   /* OR'ed combination of TCL_READABLE,
335                                 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
336                                 * conditions under which proc should be
337                                 * called. */
338    Tcl_FileProc *proc,         /* Procedure to call for each selected
339                                 * event. */
340    ClientData clientData)      /* Arbitrary data to pass to proc. */
341{
342    FileHandler *filePtr;
343
344    if (!initialized) {
345        InitNotifier();
346    }
347
348    TclSetAppContext(NULL);
349
350    for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
351            filePtr = filePtr->nextPtr) {
352        if (filePtr->fd == fd) {
353            break;
354        }
355    }
356    if (filePtr == NULL) {
357        filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
358        filePtr->fd = fd;
359        filePtr->read = 0;
360        filePtr->write = 0;
361        filePtr->except = 0;
362        filePtr->readyMask = 0;
363        filePtr->mask = 0;
364        filePtr->nextPtr = notifier.firstFileHandlerPtr;
365        notifier.firstFileHandlerPtr = filePtr;
366    }
367    filePtr->proc = proc;
368    filePtr->clientData = clientData;
369
370    /*
371     * Register the file with the Xt notifier, if it hasn't been done yet.
372     */
373
374    if (mask & TCL_READABLE) {
375        if (!(filePtr->mask & TCL_READABLE)) {
376            filePtr->read = XtAppAddInput(notifier.appContext, fd,
377                    XtInputReadMask, FileProc, filePtr);
378        }
379    } else {
380        if (filePtr->mask & TCL_READABLE) {
381            XtRemoveInput(filePtr->read);
382        }
383    }
384    if (mask & TCL_WRITABLE) {
385        if (!(filePtr->mask & TCL_WRITABLE)) {
386            filePtr->write = XtAppAddInput(notifier.appContext, fd,
387                    XtInputWriteMask, FileProc, filePtr);
388        }
389    } else {
390        if (filePtr->mask & TCL_WRITABLE) {
391            XtRemoveInput(filePtr->write);
392        }
393    }
394    if (mask & TCL_EXCEPTION) {
395        if (!(filePtr->mask & TCL_EXCEPTION)) {
396            filePtr->except = XtAppAddInput(notifier.appContext, fd,
397                    XtInputExceptMask, FileProc, filePtr);
398        }
399    } else {
400        if (filePtr->mask & TCL_EXCEPTION) {
401            XtRemoveInput(filePtr->except);
402        }
403    }
404    filePtr->mask = mask;
405}
406
407/*
408 *----------------------------------------------------------------------
409 *
410 * DeleteFileHandler --
411 *
412 *      Cancel a previously-arranged callback arrangement for a file.
413 *
414 * Results:
415 *      None.
416 *
417 * Side effects:
418 *      If a callback was previously registered on file, remove it.
419 *
420 *----------------------------------------------------------------------
421 */
422
423static void
424DeleteFileHandler(
425    int fd)                     /* Stream id for which to remove callback
426                                 * procedure. */
427{
428    FileHandler *filePtr, *prevPtr;
429
430    if (!initialized) {
431        InitNotifier();
432    }
433
434    TclSetAppContext(NULL);
435
436    /*
437     * Find the entry for the given file (and return if there isn't one).
438     */
439
440    for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
441            prevPtr = filePtr, filePtr = filePtr->nextPtr) {
442        if (filePtr == NULL) {
443            return;
444        }
445        if (filePtr->fd == fd) {
446            break;
447        }
448    }
449
450    /*
451     * Clean up information in the callback record.
452     */
453
454    if (prevPtr == NULL) {
455        notifier.firstFileHandlerPtr = filePtr->nextPtr;
456    } else {
457        prevPtr->nextPtr = filePtr->nextPtr;
458    }
459    if (filePtr->mask & TCL_READABLE) {
460        XtRemoveInput(filePtr->read);
461    }
462    if (filePtr->mask & TCL_WRITABLE) {
463        XtRemoveInput(filePtr->write);
464    }
465    if (filePtr->mask & TCL_EXCEPTION) {
466        XtRemoveInput(filePtr->except);
467    }
468    ckfree((char *) filePtr);
469}
470
471/*
472 *----------------------------------------------------------------------
473 *
474 * FileProc --
475 *
476 *      These procedures are called by Xt when a file becomes readable,
477 *      writable, or has an exception.
478 *
479 * Results:
480 *      None.
481 *
482 * Side effects:
483 *      Makes an entry on the Tcl event queue if the event is interesting.
484 *
485 *----------------------------------------------------------------------
486 */
487
488static void
489FileProc(
490    caddr_t clientData,
491    int *fd,
492    XtInputId *id)
493{
494    FileHandler *filePtr = (FileHandler *)clientData;
495    FileHandlerEvent *fileEvPtr;
496    int mask = 0;
497
498    /*
499     * Determine which event happened.
500     */
501
502    if (*id == filePtr->read) {
503        mask = TCL_READABLE;
504    } else if (*id == filePtr->write) {
505        mask = TCL_WRITABLE;
506    } else if (*id == filePtr->except) {
507        mask = TCL_EXCEPTION;
508    }
509
510    /*
511     * Ignore unwanted or duplicate events.
512     */
513
514    if (!(filePtr->mask & mask) || (filePtr->readyMask & mask)) {
515        return;
516    }
517
518    /*
519     * This is an interesting event, so put it onto the event queue.
520     */
521
522    filePtr->readyMask |= mask;
523    fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
524    fileEvPtr->header.proc = FileHandlerEventProc;
525    fileEvPtr->fd = filePtr->fd;
526    Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
527
528    /*
529     * Process events on the Tcl event queue before returning to Xt.
530     */
531
532    Tcl_ServiceAll();
533}
534
535/*
536 *----------------------------------------------------------------------
537 *
538 * FileHandlerEventProc --
539 *
540 *      This procedure is called by Tcl_ServiceEvent when a file event reaches
541 *      the front of the event queue. This procedure is responsible for
542 *      actually handling the event by invoking the callback for the file
543 *      handler.
544 *
545 * Results:
546 *      Returns 1 if the event was handled, meaning it should be removed from
547 *      the queue. Returns 0 if the event was not handled, meaning it should
548 *      stay on the queue. The only time the event isn't handled is if the
549 *      TCL_FILE_EVENTS flag bit isn't set.
550 *
551 * Side effects:
552 *      Whatever the file handler's callback procedure does.
553 *
554 *----------------------------------------------------------------------
555 */
556
557static int
558FileHandlerEventProc(
559    Tcl_Event *evPtr,           /* Event to service. */
560    int flags)                  /* Flags that indicate what events to handle,
561                                 * such as TCL_FILE_EVENTS. */
562{
563    FileHandler *filePtr;
564    FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
565    int mask;
566
567    if (!(flags & TCL_FILE_EVENTS)) {
568        return 0;
569    }
570
571    /*
572     * Search through the file handlers to find the one whose handle matches
573     * the event. We do this rather than keeping a pointer to the file handler
574     * directly in the event, so that the handler can be deleted while the
575     * event is queued without leaving a dangling pointer.
576     */
577
578    for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
579            filePtr = filePtr->nextPtr) {
580        if (filePtr->fd != fileEvPtr->fd) {
581            continue;
582        }
583
584        /*
585         * The code is tricky for two reasons:
586         * 1. The file handler's desired events could have changed since the
587         *    time when the event was queued, so AND the ready mask with the
588         *    desired mask.
589         * 2. The file could have been closed and re-opened since the time
590         *    when the event was queued. This is why the ready mask is stored
591         *    in the file handler rather than the queued event: it will be
592         *    zeroed when a new file handler is created for the newly opened
593         *    file.
594         */
595
596        mask = filePtr->readyMask & filePtr->mask;
597        filePtr->readyMask = 0;
598        if (mask != 0) {
599            (*filePtr->proc)(filePtr->clientData, mask);
600        }
601        break;
602    }
603    return 1;
604}
605
606/*
607 *----------------------------------------------------------------------
608 *
609 * WaitForEvent --
610 *
611 *      This function is called by Tcl_DoOneEvent to wait for new events on
612 *      the message queue. If the block time is 0, then Tcl_WaitForEvent just
613 *      polls without blocking.
614 *
615 * Results:
616 *      Returns 1 if an event was found, else 0. This ensures that
617 *      Tcl_DoOneEvent will return 1, even if the event is handled by non-Tcl
618 *      code.
619 *
620 * Side effects:
621 *      Queues file events that are detected by the select.
622 *
623 *----------------------------------------------------------------------
624 */
625
626static int
627WaitForEvent(
628    Tcl_Time *timePtr)          /* Maximum block time, or NULL. */
629{
630    int timeout;
631
632    if (!initialized) {
633        InitNotifier();
634    }
635
636    TclSetAppContext(NULL);
637
638    if (timePtr) {
639        timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
640        if (timeout == 0) {
641            if (XtAppPending(notifier.appContext)) {
642                goto process;
643            } else {
644                return 0;
645            }
646        } else {
647            Tcl_SetTimer(timePtr);
648        }
649    }
650
651  process:
652    XtAppProcessEvent(notifier.appContext, XtIMAll);
653    return 1;
654}
655
656/*
657 * Local Variables:
658 * mode: c
659 * c-basic-offset: 4
660 * fill-column: 78
661 * End:
662 */
Note: See TracBrowser for help on using the repository browser.