Quick update to the README file. For intros and books we now point to
[python/dscho.git] / Mac / tclmods / tclSelectNotify.c
blobea35fc62365f71e62a638e093056a003d7d62e2e
1 /*
2 * tclSelectNotify.c --
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__)
17 #pragma import on
18 #endif
20 #include <unistd.h>
22 #if defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__)
23 #pragma import reset
24 #endif
26 #include "tclInt.h"
27 #include "tclPort.h"
28 #include <signal.h>
30 #ifndef MASK_SIZE
31 #define MASK_SIZE howmany(FD_SETSIZE, NFDBITS)
32 #endif
33 #ifndef SELECT_MASK
34 #define SELECT_MASK fd_set
35 #endif
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
42 * a registered file.
45 typedef struct FileHandler {
46 int fd;
47 int mask; /* Mask of desired events: TCL_READABLE,
48 * etc. */
49 int readyMask; /* Mask of events that have been seen since the
50 * last time file handlers were invoked for
51 * this file. */
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. */
56 } FileHandler;
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
65 * all events. */
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). */
71 } FileHandlerEvent;
74 * The following static structure contains the state information for the
75 * select based implementation of the Tcl notifier.
78 static struct {
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). */
93 } notifier;
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,
109 int flags));
112 *----------------------------------------------------------------------
114 * InitNotifier --
116 * Initializes the notifier state.
118 * Results:
119 * None.
121 * Side effects:
122 * Creates a new exit handler.
124 *----------------------------------------------------------------------
127 static void
128 InitNotifier()
130 initialized = 1;
131 memset(&notifier, 0, sizeof(notifier));
132 Tcl_CreateExitHandler(NotifierExitHandler, NULL);
136 *----------------------------------------------------------------------
138 * NotifierExitHandler --
140 * This function is called to cleanup the notifier state before
141 * Tcl is unloaded.
143 * Results:
144 * None.
146 * Side effects:
147 * Destroys the notifier window.
149 *----------------------------------------------------------------------
152 static void
153 NotifierExitHandler(clientData)
154 ClientData clientData; /* Not used. */
156 initialized = 0;
160 *----------------------------------------------------------------------
162 * Tcl_CreateFileHandler --
164 * This procedure registers a file handler with the Xt notifier.
166 * Results:
167 * None.
169 * Side effects:
170 * Creates a new file handler structure and registers one or more
171 * input procedures with Xt.
173 *----------------------------------------------------------------------
176 void
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
184 * selected event. */
185 ClientData clientData; /* Arbitrary data to pass to proc. */
187 FileHandler *filePtr;
188 int index, bit;
190 if (!initialized) {
191 InitNotifier();
194 for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
195 filePtr = filePtr->nextPtr) {
196 if (filePtr->fd == fd) {
197 break;
200 if (filePtr == NULL) {
201 filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); /* MLK */
202 filePtr->fd = fd;
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;
219 } else {
220 notifier.checkMasks[index] &= ~bit;
222 if (mask & TCL_WRITABLE) {
223 (notifier.checkMasks+MASK_SIZE)[index] |= bit;
224 } else {
225 (notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
227 if (mask & TCL_EXCEPTION) {
228 (notifier.checkMasks+2*(MASK_SIZE))[index] |= bit;
229 } else {
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
243 * a file.
245 * Results:
246 * None.
248 * Side effects:
249 * If a callback was previously registered on file, remove it.
251 *----------------------------------------------------------------------
254 void
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;
261 if (!initialized) {
262 InitNotifier();
266 * Find the entry for the given file (and return if there
267 * isn't one).
270 for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
271 prevPtr = filePtr, filePtr = filePtr->nextPtr) {
272 if (filePtr == NULL) {
273 return;
275 if (filePtr->fd == fd) {
276 break;
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];
306 if (mask) {
307 for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) {
308 if (mask & (1 << (i-1))) {
309 break;
312 notifier.numFdBits = index * (NBBY*sizeof(fd_mask)) + i;
313 break;
319 * Clean up information in the callback record.
322 if (prevPtr == NULL) {
323 notifier.firstFileHandlerPtr = filePtr->nextPtr;
324 } else {
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.
340 * Results:
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.
346 * Side effects:
347 * Whatever the file handler's callback procedure does.
349 *----------------------------------------------------------------------
352 static int
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;
360 int mask;
362 if (!(flags & TCL_FILE_EVENTS)) {
363 return 0;
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) {
376 continue;
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;
393 if (mask != 0) {
394 (*filePtr->proc)(filePtr->clientData, mask);
396 break;
398 return 1;
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.
410 * Results:
411 * Returns 1 if any event handled, 0 otherwise.
413 * Side effects:
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;
427 if (!initialized) {
428 InitNotifier();
432 * Set up the timeout structure.
435 timeout.tv_sec = 0;
436 timeout.tv_usec = 0;
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 *) &notifier.readyMasks[0],
443 (SELECT_MASK *) &notifier.readyMasks[MASK_SIZE],
444 (SELECT_MASK *) &notifier.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.
458 if ( numFound == 0 )
459 return 0;
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)));
470 mask = 0;
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;
482 if (!mask) {
483 continue;
484 } else {
485 numFound--;
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;
502 return 1;