4 * Partial even handling, select only. This file is adapted from TclUnixNotify.c, and
5 * meant as an add-in for Mac (and possibly Windows) environments where select *is* available.
6 * TclMacNotify.c works together with this file.
8 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 * SCCS: @(#) tclUnixNotfy.c 1.42 97/07/02 20:55:44
16 #if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__)
22 #if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__)
31 #define MASK_SIZE howmany(FD_SETSIZE, NFDBITS)
34 #define SELECT_MASK fd_set
37 /* Prototype (too lazy to create new .h) */
38 int Tcl_PollSelectEvent(void);
41 * This structure is used to keep track of the notifier info for a
45 typedef struct FileHandler
{
47 int mask
; /* Mask of desired events: TCL_READABLE,
49 int readyMask
; /* Mask of events that have been seen since the
50 * last time file handlers were invoked for
52 Tcl_FileProc
*proc
; /* Procedure to call, in the style of
53 * Tcl_CreateFileHandler. */
54 ClientData clientData
; /* Argument to pass to proc. */
55 struct FileHandler
*nextPtr
;/* Next in list of all files we care about. */
59 * The following structure is what is added to the Tcl event queue when
60 * file handlers are ready to fire.
63 typedef struct FileHandlerEvent
{
64 Tcl_Event header
; /* Information that is standard for
66 int fd
; /* File descriptor that is ready. Used
67 * to find the FileHandler structure for
68 * the file (can't point directly to the
69 * FileHandler structure because it could
70 * go away while the event is queued). */
74 * The following static structure contains the state information for the
75 * select based implementation of the Tcl notifier.
79 FileHandler
*firstFileHandlerPtr
;
80 /* Pointer to head of file handler list. */
81 fd_mask checkMasks
[3*MASK_SIZE
];
82 /* This array is used to build up the masks
83 * to be used in the next call to select.
84 * Bits are set in response to calls to
85 * Tcl_CreateFileHandler. */
86 fd_mask readyMasks
[3*MASK_SIZE
];
87 /* This array reflects the readable/writable
88 * conditions that were found to exist by the
89 * last call to select. */
90 int numFdBits
; /* Number of valid bits in checkMasks
91 * (one more than highest fd for which
92 * Tcl_WatchFile has been called). */
96 * The following static indicates whether this module has been initialized.
99 static int initialized
= 0;
102 * Static routines defined in this file.
105 static void InitNotifier
_ANSI_ARGS_((void));
106 static void NotifierExitHandler
_ANSI_ARGS_((
107 ClientData clientData
));
108 static int FileHandlerEventProc
_ANSI_ARGS_((Tcl_Event
*evPtr
,
112 *----------------------------------------------------------------------
116 * Initializes the notifier state.
122 * Creates a new exit handler.
124 *----------------------------------------------------------------------
131 memset(¬ifier
, 0, sizeof(notifier
));
132 Tcl_CreateExitHandler(NotifierExitHandler
, NULL
);
136 *----------------------------------------------------------------------
138 * NotifierExitHandler --
140 * This function is called to cleanup the notifier state before
147 * Destroys the notifier window.
149 *----------------------------------------------------------------------
153 NotifierExitHandler(clientData
)
154 ClientData clientData
; /* Not used. */
160 *----------------------------------------------------------------------
162 * Tcl_CreateFileHandler --
164 * This procedure registers a file handler with the Xt notifier.
170 * Creates a new file handler structure and registers one or more
171 * input procedures with Xt.
173 *----------------------------------------------------------------------
177 Tcl_CreateFileHandler(fd
, mask
, proc
, clientData
)
178 int fd
; /* Handle of stream to watch. */
179 int mask
; /* OR'ed combination of TCL_READABLE,
180 * TCL_WRITABLE, and TCL_EXCEPTION:
181 * indicates conditions under which
182 * proc should be called. */
183 Tcl_FileProc
*proc
; /* Procedure to call for each
185 ClientData clientData
; /* Arbitrary data to pass to proc. */
187 FileHandler
*filePtr
;
194 for (filePtr
= notifier
.firstFileHandlerPtr
; filePtr
!= NULL
;
195 filePtr
= filePtr
->nextPtr
) {
196 if (filePtr
->fd
== fd
) {
200 if (filePtr
== NULL
) {
201 filePtr
= (FileHandler
*) ckalloc(sizeof(FileHandler
)); /* MLK */
203 filePtr
->readyMask
= 0;
204 filePtr
->nextPtr
= notifier
.firstFileHandlerPtr
;
205 notifier
.firstFileHandlerPtr
= filePtr
;
207 filePtr
->proc
= proc
;
208 filePtr
->clientData
= clientData
;
209 filePtr
->mask
= mask
;
212 * Update the check masks for this file.
215 index
= fd
/(NBBY
*sizeof(fd_mask
));
216 bit
= 1 << (fd
%(NBBY
*sizeof(fd_mask
)));
217 if (mask
& TCL_READABLE
) {
218 notifier
.checkMasks
[index
] |= bit
;
220 notifier
.checkMasks
[index
] &= ~bit
;
222 if (mask
& TCL_WRITABLE
) {
223 (notifier
.checkMasks
+MASK_SIZE
)[index
] |= bit
;
225 (notifier
.checkMasks
+MASK_SIZE
)[index
] &= ~bit
;
227 if (mask
& TCL_EXCEPTION
) {
228 (notifier
.checkMasks
+2*(MASK_SIZE
))[index
] |= bit
;
230 (notifier
.checkMasks
+2*(MASK_SIZE
))[index
] &= ~bit
;
232 if (notifier
.numFdBits
<= fd
) {
233 notifier
.numFdBits
= fd
+1;
238 *----------------------------------------------------------------------
240 * Tcl_DeleteFileHandler --
242 * Cancel a previously-arranged callback arrangement for
249 * If a callback was previously registered on file, remove it.
251 *----------------------------------------------------------------------
255 Tcl_DeleteFileHandler(fd
)
256 int fd
; /* Stream id for which to remove callback procedure. */
258 FileHandler
*filePtr
, *prevPtr
;
259 int index
, bit
, mask
, i
;
266 * Find the entry for the given file (and return if there
270 for (prevPtr
= NULL
, filePtr
= notifier
.firstFileHandlerPtr
; ;
271 prevPtr
= filePtr
, filePtr
= filePtr
->nextPtr
) {
272 if (filePtr
== NULL
) {
275 if (filePtr
->fd
== fd
) {
281 * Update the check masks for this file.
284 index
= fd
/(NBBY
*sizeof(fd_mask
));
285 bit
= 1 << (fd
%(NBBY
*sizeof(fd_mask
)));
287 if (filePtr
->mask
& TCL_READABLE
) {
288 notifier
.checkMasks
[index
] &= ~bit
;
290 if (filePtr
->mask
& TCL_WRITABLE
) {
291 (notifier
.checkMasks
+MASK_SIZE
)[index
] &= ~bit
;
293 if (filePtr
->mask
& TCL_EXCEPTION
) {
294 (notifier
.checkMasks
+2*(MASK_SIZE
))[index
] &= ~bit
;
298 * Find current max fd.
301 if (fd
+1 == notifier
.numFdBits
) {
302 for (notifier
.numFdBits
= 0; index
>= 0; index
--) {
303 mask
= notifier
.checkMasks
[index
]
304 | (notifier
.checkMasks
+MASK_SIZE
)[index
]
305 | (notifier
.checkMasks
+2*(MASK_SIZE
))[index
];
307 for (i
= (NBBY
*sizeof(fd_mask
)); i
> 0; i
--) {
308 if (mask
& (1 << (i
-1))) {
312 notifier
.numFdBits
= index
* (NBBY
*sizeof(fd_mask
)) + i
;
319 * Clean up information in the callback record.
322 if (prevPtr
== NULL
) {
323 notifier
.firstFileHandlerPtr
= filePtr
->nextPtr
;
325 prevPtr
->nextPtr
= filePtr
->nextPtr
;
327 ckfree((char *) filePtr
);
331 *----------------------------------------------------------------------
333 * FileHandlerEventProc --
335 * This procedure is called by Tcl_ServiceEvent when a file event
336 * reaches the front of the event queue. This procedure is
337 * responsible for actually handling the event by invoking the
338 * callback for the file handler.
341 * Returns 1 if the event was handled, meaning it should be removed
342 * from the queue. Returns 0 if the event was not handled, meaning
343 * it should stay on the queue. The only time the event isn't
344 * handled is if the TCL_FILE_EVENTS flag bit isn't set.
347 * Whatever the file handler's callback procedure does.
349 *----------------------------------------------------------------------
353 FileHandlerEventProc(evPtr
, flags
)
354 Tcl_Event
*evPtr
; /* Event to service. */
355 int flags
; /* Flags that indicate what events to
356 * handle, such as TCL_FILE_EVENTS. */
358 FileHandler
*filePtr
;
359 FileHandlerEvent
*fileEvPtr
= (FileHandlerEvent
*) evPtr
;
362 if (!(flags
& TCL_FILE_EVENTS
)) {
367 * Search through the file handlers to find the one whose handle matches
368 * the event. We do this rather than keeping a pointer to the file
369 * handler directly in the event, so that the handler can be deleted
370 * while the event is queued without leaving a dangling pointer.
373 for (filePtr
= notifier
.firstFileHandlerPtr
; filePtr
!= NULL
;
374 filePtr
= filePtr
->nextPtr
) {
375 if (filePtr
->fd
!= fileEvPtr
->fd
) {
380 * The code is tricky for two reasons:
381 * 1. The file handler's desired events could have changed
382 * since the time when the event was queued, so AND the
383 * ready mask with the desired mask.
384 * 2. The file could have been closed and re-opened since
385 * the time when the event was queued. This is why the
386 * ready mask is stored in the file handler rather than
387 * the queued event: it will be zeroed when a new
388 * file handler is created for the newly opened file.
391 mask
= filePtr
->readyMask
& filePtr
->mask
;
392 filePtr
->readyMask
= 0;
394 (*filePtr
->proc
)(filePtr
->clientData
, mask
);
402 *----------------------------------------------------------------------
404 * Tcl_PollSelectEvent --
406 * This function is called by Tcl_WaitForEvent to wait for new
407 * events on the message queue. If the block time is 0, then
408 * Tcl_WaitForEvent just polls without blocking.
411 * Returns 1 if any event handled, 0 otherwise.
414 * Queues file events that are detected by the select.
416 *----------------------------------------------------------------------
420 Tcl_PollSelectEvent(void)
422 FileHandler
*filePtr
;
423 FileHandlerEvent
*fileEvPtr
;
424 struct timeval timeout
, *timeoutPtr
;
425 int bit
, index
, mask
, numFound
;
432 * Set up the timeout structure.
437 timeoutPtr
= &timeout
;
439 memcpy((VOID
*) notifier
.readyMasks
, (VOID
*) notifier
.checkMasks
,
440 3*MASK_SIZE
*sizeof(fd_mask
));
441 numFound
= select(notifier
.numFdBits
,
442 (SELECT_MASK
*) ¬ifier
.readyMasks
[0],
443 (SELECT_MASK
*) ¬ifier
.readyMasks
[MASK_SIZE
],
444 (SELECT_MASK
*) ¬ifier
.readyMasks
[2*MASK_SIZE
], timeoutPtr
);
447 * Some systems don't clear the masks after an error, so
448 * we have to do it here.
451 if (numFound
== -1) {
452 memset((VOID
*) notifier
.readyMasks
, 0, 3*MASK_SIZE
*sizeof(fd_mask
));
456 * Return if nothing to do.
462 * Queue all detected file events before returning.
465 for (filePtr
= notifier
.firstFileHandlerPtr
;
466 (filePtr
!= NULL
) && (numFound
> 0);
467 filePtr
= filePtr
->nextPtr
) {
468 index
= filePtr
->fd
/ (NBBY
*sizeof(fd_mask
));
469 bit
= 1 << (filePtr
->fd
% (NBBY
*sizeof(fd_mask
)));
472 if (notifier
.readyMasks
[index
] & bit
) {
473 mask
|= TCL_READABLE
;
475 if ((notifier
.readyMasks
+MASK_SIZE
)[index
] & bit
) {
476 mask
|= TCL_WRITABLE
;
478 if ((notifier
.readyMasks
+2*(MASK_SIZE
))[index
] & bit
) {
479 mask
|= TCL_EXCEPTION
;
489 * Don't bother to queue an event if the mask was previously
490 * non-zero since an event must still be on the queue.
493 if (filePtr
->readyMask
== 0) {
494 fileEvPtr
= (FileHandlerEvent
*) ckalloc(
495 sizeof(FileHandlerEvent
));
496 fileEvPtr
->header
.proc
= FileHandlerEventProc
;
497 fileEvPtr
->fd
= filePtr
->fd
;
498 Tcl_QueueEvent((Tcl_Event
*) fileEvPtr
, TCL_QUEUE_TAIL
);
500 filePtr
->readyMask
= mask
;