4 * This file contains the notifier driver implementation for the Xt
7 * Copyright (c) 1997 by Sun Microsystems, Inc.
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 # define USE_TCL_STUBS
16 #include <X11/Intrinsic.h>
20 * This structure is used to keep track of the notifier info for a a
24 typedef struct FileHandler {
26 int mask; /* Mask of desired events: TCL_READABLE,
28 int readyMask; /* Events that have been seen since the last
29 * time FileHandlerEventProc was called for
31 XtInputId read; /* Xt read callback handle. */
32 XtInputId write; /* Xt write callback handle. */
33 XtInputId except; /* Xt exception callback handle. */
34 Tcl_FileProc *proc; /* Procedure to call, in the style of
35 * Tcl_CreateFileHandler. */
36 ClientData clientData; /* Argument to pass to proc. */
37 struct FileHandler *nextPtr;/* Next in list of all files we care about. */
41 * The following structure is what is added to the Tcl event queue when file
42 * handlers are ready to fire.
45 typedef struct FileHandlerEvent {
46 Tcl_Event header; /* Information that is standard for all
48 int fd; /* File descriptor that is ready. Used to find
49 * the FileHandler structure for the file
50 * (can't point directly to the FileHandler
51 * structure because it could go away while
52 * the event is queued). */
56 * The following static structure contains the state information for the Xt
57 * based implementation of the Tcl notifier.
60 static struct NotifierState {
61 XtAppContext appContext; /* The context used by the Xt notifier. Can be
62 * set with TclSetAppContext. */
63 int appContextCreated; /* Was it created by us? */
64 XtIntervalId currentTimeout;/* Handle of current timer. */
65 FileHandler *firstFileHandlerPtr;
66 /* Pointer to head of file handler list. */
70 * The following static indicates whether this module has been initialized.
73 static int initialized = 0;
76 * Static routines defined in this file.
79 static int FileHandlerEventProc(Tcl_Event *evPtr, int flags);
80 static void FileProc(XtPointer clientData, int *source,
82 static void NotifierExitHandler(ClientData clientData);
83 static void TimerProc(XtPointer clientData, XtIntervalId *id);
84 static void CreateFileHandler(int fd, int mask,
85 Tcl_FileProc *proc, ClientData clientData);
86 static void DeleteFileHandler(int fd);
87 static void SetTimer(const Tcl_Time * timePtr);
88 static int WaitForEvent(const Tcl_Time * timePtr);
91 * Functions defined in this file for use by users of the Xt Notifier:
94 MODULE_SCOPE void InitNotifier(void);
95 MODULE_SCOPE XtAppContext TclSetAppContext(XtAppContext ctx);
98 *----------------------------------------------------------------------
100 * TclSetAppContext --
102 * Set the notifier application context.
108 * Sets the application context used by the notifier. Panics if the
109 * context is already set when called.
111 *----------------------------------------------------------------------
116 XtAppContext appContext)
123 * If we already have a context we check whether we were asked to set a
124 * new context. If so, we panic because we try to prevent switching
125 * contexts by mistake. Otherwise, we return the one we have.
128 if (notifier.appContext != NULL) {
129 if (appContext != NULL) {
131 * We already have a context. We do not allow switching contexts
132 * after initialization, so we panic.
135 Tcl_Panic("TclSetAppContext: multiple application contexts");
139 * If we get here we have not yet gotten a context, so either create
140 * one or use the one supplied by our caller.
143 if (appContext == NULL) {
145 * We must create a new context and tell our caller what it is, so
146 * she can use it too.
149 notifier.appContext = XtCreateApplicationContext();
150 notifier.appContextCreated = 1;
153 * Otherwise we remember the context that our caller gave us and
157 notifier.appContextCreated = 0;
158 notifier.appContext = appContext;
162 return notifier.appContext;
166 *----------------------------------------------------------------------
170 * Initializes the notifier state.
176 * Creates a new exit handler.
178 *----------------------------------------------------------------------
184 Tcl_NotifierProcs np;
187 * Only reinitialize if we are not in exit handling. The notifier can get
188 * reinitialized after its own exit handler has run, because of exit
189 * handlers for the I/O and timer sub-systems (order dependency).
196 np.createFileHandlerProc = CreateFileHandler;
197 np.deleteFileHandlerProc = DeleteFileHandler;
198 np.setTimerProc = SetTimer;
199 np.waitForEventProc = WaitForEvent;
200 np.initNotifierProc = Tcl_InitNotifier;
201 np.finalizeNotifierProc = Tcl_FinalizeNotifier;
202 np.alertNotifierProc = Tcl_AlertNotifier;
203 np.serviceModeHookProc = Tcl_ServiceModeHook;
204 Tcl_SetNotifier(&np);
207 * DO NOT create the application context yet; doing so would prevent
208 * external applications from setting it for us to their own ones.
212 memset(&np, 0, sizeof(np));
213 Tcl_CreateExitHandler(NotifierExitHandler, NULL);
217 *----------------------------------------------------------------------
219 * NotifierExitHandler --
221 * This function is called to cleanup the notifier state before Tcl is
228 * Destroys the notifier window.
230 *----------------------------------------------------------------------
235 ClientData clientData) /* Not used. */
237 if (notifier.currentTimeout != 0) {
238 XtRemoveTimeOut(notifier.currentTimeout);
240 for (; notifier.firstFileHandlerPtr != NULL; ) {
241 Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd);
243 if (notifier.appContextCreated) {
244 XtDestroyApplicationContext(notifier.appContext);
245 notifier.appContextCreated = 0;
246 notifier.appContext = NULL;
252 *----------------------------------------------------------------------
256 * This procedure sets the current notifier timeout value.
262 * Replaces any previous timer.
264 *----------------------------------------------------------------------
269 const Tcl_Time *timePtr) /* Timeout value, may be NULL. */
277 TclSetAppContext(NULL);
278 if (notifier.currentTimeout != 0) {
279 XtRemoveTimeOut(notifier.currentTimeout);
282 timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
283 notifier.currentTimeout = XtAppAddTimeOut(notifier.appContext,
284 (unsigned long) timeout, TimerProc, NULL);
286 notifier.currentTimeout = 0;
291 *----------------------------------------------------------------------
295 * This procedure is the XtTimerCallbackProc used to handle timeouts.
301 * Processes all queued events.
303 *----------------------------------------------------------------------
308 XtPointer clientData, /* Not used. */
311 if (*id != notifier.currentTimeout) {
314 notifier.currentTimeout = 0;
320 *----------------------------------------------------------------------
322 * CreateFileHandler --
324 * This procedure registers a file handler with the Xt notifier.
330 * Creates a new file handler structure and registers one or more input
331 * procedures with Xt.
333 *----------------------------------------------------------------------
338 int fd, /* Handle of stream to watch. */
339 int mask, /* OR'ed combination of TCL_READABLE,
340 * TCL_WRITABLE, and TCL_EXCEPTION: indicates
341 * conditions under which proc should be
343 Tcl_FileProc *proc, /* Procedure to call for each selected
345 ClientData clientData) /* Arbitrary data to pass to proc. */
347 FileHandler *filePtr;
353 TclSetAppContext(NULL);
355 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
356 filePtr = filePtr->nextPtr) {
357 if (filePtr->fd == fd) {
361 if (filePtr == NULL) {
362 filePtr = ckalloc(sizeof(FileHandler));
367 filePtr->readyMask = 0;
369 filePtr->nextPtr = notifier.firstFileHandlerPtr;
370 notifier.firstFileHandlerPtr = filePtr;
372 filePtr->proc = proc;
373 filePtr->clientData = clientData;
376 * Register the file with the Xt notifier, if it hasn't been done yet.
379 if (mask & TCL_READABLE) {
380 if (!(filePtr->mask & TCL_READABLE)) {
381 filePtr->read = XtAppAddInput(notifier.appContext, fd,
382 INT2PTR(XtInputReadMask), FileProc, filePtr);
385 if (filePtr->mask & TCL_READABLE) {
386 XtRemoveInput(filePtr->read);
389 if (mask & TCL_WRITABLE) {
390 if (!(filePtr->mask & TCL_WRITABLE)) {
391 filePtr->write = XtAppAddInput(notifier.appContext, fd,
392 INT2PTR(XtInputWriteMask), FileProc, filePtr);
395 if (filePtr->mask & TCL_WRITABLE) {
396 XtRemoveInput(filePtr->write);
399 if (mask & TCL_EXCEPTION) {
400 if (!(filePtr->mask & TCL_EXCEPTION)) {
401 filePtr->except = XtAppAddInput(notifier.appContext, fd,
402 INT2PTR(XtInputExceptMask), FileProc, filePtr);
405 if (filePtr->mask & TCL_EXCEPTION) {
406 XtRemoveInput(filePtr->except);
409 filePtr->mask = mask;
413 *----------------------------------------------------------------------
415 * DeleteFileHandler --
417 * Cancel a previously-arranged callback arrangement for a file.
423 * If a callback was previously registered on file, remove it.
425 *----------------------------------------------------------------------
430 int fd) /* Stream id for which to remove callback
433 FileHandler *filePtr, *prevPtr;
439 TclSetAppContext(NULL);
442 * Find the entry for the given file (and return if there isn't one).
445 for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
446 prevPtr = filePtr, filePtr = filePtr->nextPtr) {
447 if (filePtr == NULL) {
450 if (filePtr->fd == fd) {
456 * Clean up information in the callback record.
459 if (prevPtr == NULL) {
460 notifier.firstFileHandlerPtr = filePtr->nextPtr;
462 prevPtr->nextPtr = filePtr->nextPtr;
464 if (filePtr->mask & TCL_READABLE) {
465 XtRemoveInput(filePtr->read);
467 if (filePtr->mask & TCL_WRITABLE) {
468 XtRemoveInput(filePtr->write);
470 if (filePtr->mask & TCL_EXCEPTION) {
471 XtRemoveInput(filePtr->except);
477 *----------------------------------------------------------------------
481 * These procedures are called by Xt when a file becomes readable,
482 * writable, or has an exception.
488 * Makes an entry on the Tcl event queue if the event is interesting.
490 *----------------------------------------------------------------------
495 XtPointer clientData,
499 FileHandler *filePtr = (FileHandler *)clientData;
500 FileHandlerEvent *fileEvPtr;
504 * Determine which event happened.
507 if (*id == filePtr->read) {
509 } else if (*id == filePtr->write) {
511 } else if (*id == filePtr->except) {
512 mask = TCL_EXCEPTION;
516 * Ignore unwanted or duplicate events.
519 if (!(filePtr->mask & mask) || (filePtr->readyMask & mask)) {
524 * This is an interesting event, so put it onto the event queue.
527 filePtr->readyMask |= mask;
528 fileEvPtr = ckalloc(sizeof(FileHandlerEvent));
529 fileEvPtr->header.proc = FileHandlerEventProc;
530 fileEvPtr->fd = filePtr->fd;
531 Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
534 * Process events on the Tcl event queue before returning to Xt.
541 *----------------------------------------------------------------------
543 * FileHandlerEventProc --
545 * This procedure is called by Tcl_ServiceEvent when a file event reaches
546 * the front of the event queue. This procedure is responsible for
547 * actually handling the event by invoking the callback for the file
551 * Returns 1 if the event was handled, meaning it should be removed from
552 * the queue. Returns 0 if the event was not handled, meaning it should
553 * stay on the queue. The only time the event isn't handled is if the
554 * TCL_FILE_EVENTS flag bit isn't set.
557 * Whatever the file handler's callback procedure does.
559 *----------------------------------------------------------------------
563 FileHandlerEventProc(
564 Tcl_Event *evPtr, /* Event to service. */
565 int flags) /* Flags that indicate what events to handle,
566 * such as TCL_FILE_EVENTS. */
568 FileHandler *filePtr;
569 FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
572 if (!(flags & TCL_FILE_EVENTS)) {
577 * Search through the file handlers to find the one whose handle matches
578 * the event. We do this rather than keeping a pointer to the file handler
579 * directly in the event, so that the handler can be deleted while the
580 * event is queued without leaving a dangling pointer.
583 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
584 filePtr = filePtr->nextPtr) {
585 if (filePtr->fd != fileEvPtr->fd) {
590 * The code is tricky for two reasons:
591 * 1. The file handler's desired events could have changed since the
592 * time when the event was queued, so AND the ready mask with the
594 * 2. The file could have been closed and re-opened since the time
595 * when the event was queued. This is why the ready mask is stored
596 * in the file handler rather than the queued event: it will be
597 * zeroed when a new file handler is created for the newly opened
601 mask = filePtr->readyMask & filePtr->mask;
602 filePtr->readyMask = 0;
604 filePtr->proc(filePtr->clientData, mask);
612 *----------------------------------------------------------------------
616 * This function is called by Tcl_DoOneEvent to wait for new events on
617 * the message queue. If the block time is 0, then Tcl_WaitForEvent just
618 * polls without blocking.
621 * Returns 1 if an event was found, else 0. This ensures that
622 * Tcl_DoOneEvent will return 1, even if the event is handled by non-Tcl
626 * Queues file events that are detected by the select.
628 *----------------------------------------------------------------------
633 const Tcl_Time *timePtr) /* Maximum block time, or NULL. */
641 TclSetAppContext(NULL);
644 timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
646 if (XtAppPending(notifier.appContext)) {
652 Tcl_SetTimer(timePtr);
657 XtAppProcessEvent(notifier.appContext, XtIMAll);