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__)
27 /* Move this include up otherwise tclPort.h tried to redefine signals */
28 #include <sys/signal.h>
35 #define MASK_SIZE howmany(FD_SETSIZE, NFDBITS)
38 #define SELECT_MASK fd_set
41 /* Prototype (too lazy to create new .h) */
42 int Tcl_PollSelectEvent(void);
45 * This structure is used to keep track of the notifier info for a
49 typedef struct FileHandler
{
51 int mask
; /* Mask of desired events: TCL_READABLE,
53 int readyMask
; /* Mask of events that have been seen since the
54 * last time file handlers were invoked for
56 Tcl_FileProc
*proc
; /* Procedure to call, in the style of
57 * Tcl_CreateFileHandler. */
58 ClientData clientData
; /* Argument to pass to proc. */
59 struct FileHandler
*nextPtr
;/* Next in list of all files we care about. */
63 * The following structure is what is added to the Tcl event queue when
64 * file handlers are ready to fire.
67 typedef struct FileHandlerEvent
{
68 Tcl_Event header
; /* Information that is standard for
70 int fd
; /* File descriptor that is ready. Used
71 * to find the FileHandler structure for
72 * the file (can't point directly to the
73 * FileHandler structure because it could
74 * go away while the event is queued). */
78 * The following static structure contains the state information for the
79 * select based implementation of the Tcl notifier.
83 FileHandler
*firstFileHandlerPtr
;
84 /* Pointer to head of file handler list. */
85 fd_mask checkMasks
[3*MASK_SIZE
];
86 /* This array is used to build up the masks
87 * to be used in the next call to select.
88 * Bits are set in response to calls to
89 * Tcl_CreateFileHandler. */
90 fd_mask readyMasks
[3*MASK_SIZE
];
91 /* This array reflects the readable/writable
92 * conditions that were found to exist by the
93 * last call to select. */
94 int numFdBits
; /* Number of valid bits in checkMasks
95 * (one more than highest fd for which
96 * Tcl_WatchFile has been called). */
100 * The following static indicates whether this module has been initialized.
103 static int initialized
= 0;
106 * Static routines defined in this file.
109 static void InitNotifier
_ANSI_ARGS_((void));
110 static void NotifierExitHandler
_ANSI_ARGS_((
111 ClientData clientData
));
112 static int FileHandlerEventProc
_ANSI_ARGS_((Tcl_Event
*evPtr
,
116 *----------------------------------------------------------------------
120 * Initializes the notifier state.
126 * Creates a new exit handler.
128 *----------------------------------------------------------------------
135 memset(¬ifier
, 0, sizeof(notifier
));
136 Tcl_CreateExitHandler(NotifierExitHandler
, NULL
);
140 *----------------------------------------------------------------------
142 * NotifierExitHandler --
144 * This function is called to cleanup the notifier state before
151 * Destroys the notifier window.
153 *----------------------------------------------------------------------
157 NotifierExitHandler(clientData
)
158 ClientData clientData
; /* Not used. */
164 *----------------------------------------------------------------------
166 * Tcl_CreateFileHandler --
168 * This procedure registers a file handler with the Xt notifier.
174 * Creates a new file handler structure and registers one or more
175 * input procedures with Xt.
177 *----------------------------------------------------------------------
181 Tcl_CreateFileHandler(fd
, mask
, proc
, clientData
)
182 int fd
; /* Handle of stream to watch. */
183 int mask
; /* OR'ed combination of TCL_READABLE,
184 * TCL_WRITABLE, and TCL_EXCEPTION:
185 * indicates conditions under which
186 * proc should be called. */
187 Tcl_FileProc
*proc
; /* Procedure to call for each
189 ClientData clientData
; /* Arbitrary data to pass to proc. */
191 FileHandler
*filePtr
;
198 for (filePtr
= notifier
.firstFileHandlerPtr
; filePtr
!= NULL
;
199 filePtr
= filePtr
->nextPtr
) {
200 if (filePtr
->fd
== fd
) {
204 if (filePtr
== NULL
) {
205 filePtr
= (FileHandler
*) ckalloc(sizeof(FileHandler
)); /* MLK */
207 filePtr
->readyMask
= 0;
208 filePtr
->nextPtr
= notifier
.firstFileHandlerPtr
;
209 notifier
.firstFileHandlerPtr
= filePtr
;
211 filePtr
->proc
= proc
;
212 filePtr
->clientData
= clientData
;
213 filePtr
->mask
= mask
;
216 * Update the check masks for this file.
219 index
= fd
/(NBBY
*sizeof(fd_mask
));
220 bit
= 1 << (fd
%(NBBY
*sizeof(fd_mask
)));
221 if (mask
& TCL_READABLE
) {
222 notifier
.checkMasks
[index
] |= bit
;
224 notifier
.checkMasks
[index
] &= ~bit
;
226 if (mask
& TCL_WRITABLE
) {
227 (notifier
.checkMasks
+MASK_SIZE
)[index
] |= bit
;
229 (notifier
.checkMasks
+MASK_SIZE
)[index
] &= ~bit
;
231 if (mask
& TCL_EXCEPTION
) {
232 (notifier
.checkMasks
+2*(MASK_SIZE
))[index
] |= bit
;
234 (notifier
.checkMasks
+2*(MASK_SIZE
))[index
] &= ~bit
;
236 if (notifier
.numFdBits
<= fd
) {
237 notifier
.numFdBits
= fd
+1;
242 *----------------------------------------------------------------------
244 * Tcl_DeleteFileHandler --
246 * Cancel a previously-arranged callback arrangement for
253 * If a callback was previously registered on file, remove it.
255 *----------------------------------------------------------------------
259 Tcl_DeleteFileHandler(fd
)
260 int fd
; /* Stream id for which to remove callback procedure. */
262 FileHandler
*filePtr
, *prevPtr
;
263 int index
, bit
, mask
, i
;
270 * Find the entry for the given file (and return if there
274 for (prevPtr
= NULL
, filePtr
= notifier
.firstFileHandlerPtr
; ;
275 prevPtr
= filePtr
, filePtr
= filePtr
->nextPtr
) {
276 if (filePtr
== NULL
) {
279 if (filePtr
->fd
== fd
) {
285 * Update the check masks for this file.
288 index
= fd
/(NBBY
*sizeof(fd_mask
));
289 bit
= 1 << (fd
%(NBBY
*sizeof(fd_mask
)));
291 if (filePtr
->mask
& TCL_READABLE
) {
292 notifier
.checkMasks
[index
] &= ~bit
;
294 if (filePtr
->mask
& TCL_WRITABLE
) {
295 (notifier
.checkMasks
+MASK_SIZE
)[index
] &= ~bit
;
297 if (filePtr
->mask
& TCL_EXCEPTION
) {
298 (notifier
.checkMasks
+2*(MASK_SIZE
))[index
] &= ~bit
;
302 * Find current max fd.
305 if (fd
+1 == notifier
.numFdBits
) {
306 for (notifier
.numFdBits
= 0; index
>= 0; index
--) {
307 mask
= notifier
.checkMasks
[index
]
308 | (notifier
.checkMasks
+MASK_SIZE
)[index
]
309 | (notifier
.checkMasks
+2*(MASK_SIZE
))[index
];
311 for (i
= (NBBY
*sizeof(fd_mask
)); i
> 0; i
--) {
312 if (mask
& (1 << (i
-1))) {
316 notifier
.numFdBits
= index
* (NBBY
*sizeof(fd_mask
)) + i
;
323 * Clean up information in the callback record.
326 if (prevPtr
== NULL
) {
327 notifier
.firstFileHandlerPtr
= filePtr
->nextPtr
;
329 prevPtr
->nextPtr
= filePtr
->nextPtr
;
331 ckfree((char *) filePtr
);
335 *----------------------------------------------------------------------
337 * FileHandlerEventProc --
339 * This procedure is called by Tcl_ServiceEvent when a file event
340 * reaches the front of the event queue. This procedure is
341 * responsible for actually handling the event by invoking the
342 * callback for the file handler.
345 * Returns 1 if the event was handled, meaning it should be removed
346 * from the queue. Returns 0 if the event was not handled, meaning
347 * it should stay on the queue. The only time the event isn't
348 * handled is if the TCL_FILE_EVENTS flag bit isn't set.
351 * Whatever the file handler's callback procedure does.
353 *----------------------------------------------------------------------
357 FileHandlerEventProc(evPtr
, flags
)
358 Tcl_Event
*evPtr
; /* Event to service. */
359 int flags
; /* Flags that indicate what events to
360 * handle, such as TCL_FILE_EVENTS. */
362 FileHandler
*filePtr
;
363 FileHandlerEvent
*fileEvPtr
= (FileHandlerEvent
*) evPtr
;
366 if (!(flags
& TCL_FILE_EVENTS
)) {
371 * Search through the file handlers to find the one whose handle matches
372 * the event. We do this rather than keeping a pointer to the file
373 * handler directly in the event, so that the handler can be deleted
374 * while the event is queued without leaving a dangling pointer.
377 for (filePtr
= notifier
.firstFileHandlerPtr
; filePtr
!= NULL
;
378 filePtr
= filePtr
->nextPtr
) {
379 if (filePtr
->fd
!= fileEvPtr
->fd
) {
384 * The code is tricky for two reasons:
385 * 1. The file handler's desired events could have changed
386 * since the time when the event was queued, so AND the
387 * ready mask with the desired mask.
388 * 2. The file could have been closed and re-opened since
389 * the time when the event was queued. This is why the
390 * ready mask is stored in the file handler rather than
391 * the queued event: it will be zeroed when a new
392 * file handler is created for the newly opened file.
395 mask
= filePtr
->readyMask
& filePtr
->mask
;
396 filePtr
->readyMask
= 0;
398 (*filePtr
->proc
)(filePtr
->clientData
, mask
);
406 *----------------------------------------------------------------------
408 * Tcl_PollSelectEvent --
410 * This function is called by Tcl_WaitForEvent to wait for new
411 * events on the message queue. If the block time is 0, then
412 * Tcl_WaitForEvent just polls without blocking.
415 * Returns 1 if any event handled, 0 otherwise.
418 * Queues file events that are detected by the select.
420 *----------------------------------------------------------------------
424 Tcl_PollSelectEvent(void)
426 FileHandler
*filePtr
;
427 FileHandlerEvent
*fileEvPtr
;
428 struct timeval timeout
, *timeoutPtr
;
429 int bit
, index
, mask
, numFound
;
436 * Set up the timeout structure.
441 timeoutPtr
= &timeout
;
443 memcpy((VOID
*) notifier
.readyMasks
, (VOID
*) notifier
.checkMasks
,
444 3*MASK_SIZE
*sizeof(fd_mask
));
445 numFound
= select(notifier
.numFdBits
,
446 (SELECT_MASK
*) ¬ifier
.readyMasks
[0],
447 (SELECT_MASK
*) ¬ifier
.readyMasks
[MASK_SIZE
],
448 (SELECT_MASK
*) ¬ifier
.readyMasks
[2*MASK_SIZE
], timeoutPtr
);
451 * Some systems don't clear the masks after an error, so
452 * we have to do it here.
455 if (numFound
== -1) {
456 memset((VOID
*) notifier
.readyMasks
, 0, 3*MASK_SIZE
*sizeof(fd_mask
));
460 * Return if nothing to do.
466 * Queue all detected file events before returning.
469 for (filePtr
= notifier
.firstFileHandlerPtr
;
470 (filePtr
!= NULL
) && (numFound
> 0);
471 filePtr
= filePtr
->nextPtr
) {
472 index
= filePtr
->fd
/ (NBBY
*sizeof(fd_mask
));
473 bit
= 1 << (filePtr
->fd
% (NBBY
*sizeof(fd_mask
)));
476 if (notifier
.readyMasks
[index
] & bit
) {
477 mask
|= TCL_READABLE
;
479 if ((notifier
.readyMasks
+MASK_SIZE
)[index
] & bit
) {
480 mask
|= TCL_WRITABLE
;
482 if ((notifier
.readyMasks
+2*(MASK_SIZE
))[index
] & bit
) {
483 mask
|= TCL_EXCEPTION
;
493 * Don't bother to queue an event if the mask was previously
494 * non-zero since an event must still be on the queue.
497 if (filePtr
->readyMask
== 0) {
498 fileEvPtr
= (FileHandlerEvent
*) ckalloc(
499 sizeof(FileHandlerEvent
));
500 fileEvPtr
->header
.proc
= FileHandlerEventProc
;
501 fileEvPtr
->fd
= filePtr
->fd
;
502 Tcl_QueueEvent((Tcl_Event
*) fileEvPtr
, TCL_QUEUE_TAIL
);
504 filePtr
->readyMask
= mask
;