]> www.wagner.pp.ru Git - oss/ck.git/blob - tkEvent.c
Ck console graphics toolkit
[oss/ck.git] / tkEvent.c
1 /* 
2  * tkEvent.c --
3  *
4  *      This file provides basic event-managing facilities, whereby
5  *      procedure callbacks may be attached to certain events.  It
6  *      also contains the command procedures for the commands "after"
7  *      and "fileevent", plus abridged versions of "tkwait" and
8  *      "update", for use with Tk_EventInit.
9  *
10  * Copyright (c) 1990-1994 The Regents of the University of California.
11  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
12  *
13  * See the file "license.terms" for information on usage and redistribution
14  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15  */
16
17 #include "ckPort.h"
18 #include "ck.h"
19
20 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
21
22 /*
23  * For each timer callback that's pending, there is one record
24  * of the following type, chained together in a list sorted by
25  * time (earliest event first).
26  */
27
28 typedef struct TimerEvent {
29     struct timeval time;        /* When timer is to fire. */
30     void (*proc)  _ANSI_ARGS_((ClientData clientData));
31                                 /* Procedure to call. */
32     ClientData clientData;      /* Argument to pass to proc. */
33     Tk_TimerToken token;        /* Identifies event so it can be
34                                  * deleted. */
35     struct TimerEvent *nextPtr; /* Next event in queue, or NULL for
36                                  * end of queue. */
37 } TimerEvent;
38
39 static TimerEvent *firstTimerHandlerPtr;
40                                 /* First event in queue. */
41
42 /*
43  * The information below is used to provide read, write, and
44  * exception masks to select during calls to Tk_DoOneEvent.
45  */
46
47 static fd_mask ready[3*MASK_SIZE];
48                                 /* Masks passed to select and modified
49                                  * by kernel to indicate which files are
50                                  * actually ready. */
51 static fd_mask check[3*MASK_SIZE];
52                                 /* Temporary set of masks, built up during
53                                  * Tk_DoOneEvent, that reflects what files
54                                  * we should wait for in the next select
55                                  * (doesn't include things that we've been
56                                  * asked to ignore in this call). */
57 static int numFds = 0;          /* Number of valid bits in mask
58                                  * arrays (this value is passed
59                                  * to select). */
60
61 /*
62  * For each file registered in a call to Tk_CreateFileHandler,
63  * and for each display that's currently active, there is one
64  * record of the following type.  All of these records are
65  * chained together into a single list.
66  */
67
68 typedef struct FileHandler {
69     int fd;                     /* POSIX file descriptor for file. */
70     fd_mask *readPtr;           /* Pointer to word in ready array
71                                  * for this file's read mask bit. */
72     fd_mask *writePtr;          /* Same for write mask bit. */
73     fd_mask *exceptPtr;         /* Same for except mask bit. */
74     fd_mask *checkReadPtr;      /* Pointer to word in check array for
75                                  * this file's read mask bit. */
76     fd_mask *checkWritePtr;     /* Same for write mask bit. */
77     fd_mask *checkExceptPtr;    /* Same for except mask bit. */
78     fd_mask bitSelect;          /* Value to AND with *readPtr etc. to
79                                  * select just this file's bit. */
80     int mask;                   /* Mask of desired events: TK_READABLE, etc. */
81     Tk_FileProc *proc;          /* Procedure to call, in the style of
82                                  * Tk_CreateFileHandler.  This is NULL
83                                  * if the handler was created by
84                                  * Tk_CreateFileHandler2. */
85     Tk_FileProc2 *proc2;        /* Procedure to call, in the style of
86                                  * Tk_CreateFileHandler2.  NULL means that
87                                  * the handler was created by
88                                  * Tk_CreateFileHandler. */
89     ClientData clientData;      /* Argument to pass to proc. */
90     struct FileHandler *nextPtr;/* Next in list of all files we
91                                  * care about (NULL for end of
92                                  * list). */
93 } FileHandler;
94
95 static FileHandler *firstFileHandlerPtr;
96                                 /* List of all file events. */
97
98 /*
99  * There is one of the following structures for each of the
100  * handlers declared in a call to Tk_DoWhenIdle.  All of the
101  * currently-active handlers are linked together into a list.
102  */
103
104 typedef struct IdleHandler {
105     void (*proc)  _ANSI_ARGS_((ClientData clientData));
106                                 /* Procedure to call. */
107     ClientData clientData;      /* Value to pass to proc. */
108     int generation;             /* Used to distinguish older handlers from
109                                  * recently-created ones. */
110     struct IdleHandler *nextPtr;/* Next in list of active handlers. */
111 } IdleHandler;
112
113 static IdleHandler *idleList = NULL;
114                                 /* First in list of all idle handlers. */
115 static IdleHandler *lastIdlePtr = NULL;
116                                 /* Last in list (or NULL for empty list). */
117 static int idleGeneration = 0;  /* Used to fill in the "generation" fields
118                                  * of IdleHandler structures.  Increments
119                                  * each time Tk_DoOneEvent starts calling
120                                  * idle handlers, so that all old handlers
121                                  * can be called without calling any of the
122                                  * new ones created by old ones. */
123 static int oldGeneration = 0;   /* "generation" currently being handled. */
124
125 /*
126  * The following procedure provides a secret hook for tkXEvent.c so that
127  * it can handle delayed mouse motion events at the right time.
128  */
129
130 void (*tkDelayedEventProc) _ANSI_ARGS_((void)) = NULL;
131
132 /*
133  * One of the following structures exists for each file with a handler
134  * created by the "fileevent" command.  Several of the fields are
135  * two-element arrays, in which the first element is used for read
136  * events and the second for write events.
137  */
138
139 typedef struct FileEvent {
140     FILE *f;                            /* Stdio handle for file. */
141     Tcl_Interp *interps[2];             /* Interpreters in which to execute
142                                          * scripts.  NULL means no handler
143                                          * for event. */
144     char *scripts[2];                   /* Scripts to evaluate in response to
145                                          * events (malloc'ed).  NULL means no
146                                          * handler for event. */
147     struct FileEvent *nextPtr;          /* Next in list of all file events
148                                          * currently defined. */
149 } FileEvent;
150
151 static FileEvent *firstFileEventPtr = NULL;
152                                         /* First in list of all existing
153                                          * file events. */
154
155 /*
156  * The data structure below is used by the "after" command to remember
157  * the command to be executed later.
158  */
159
160 typedef struct AfterInfo {
161     Tcl_Interp *interp;         /* Interpreter in which to execute command. */
162     char *command;              /* Command to execute.  Malloc'ed, so must
163                                  * be freed when structure is deallocated. */
164     int id;                     /* Integer identifier for command;  used to
165                                  * cancel it. */
166     Tk_TimerToken token;        /* Used to cancel the "after" command.  NULL
167                                  * means that the command is run as an
168                                  * idle handler rather than as a timer
169                                  * handler. */
170     struct AfterInfo *nextPtr;  /* Next in list of all "after" commands for
171                                  * the application. */
172 } AfterInfo;
173
174 static AfterInfo *firstAfterPtr = NULL;
175                                 /* First in list of all pending "after"
176                                  * commands. */
177
178 /*
179  * The data structure below is used to report background errors.  One
180  * such structure is allocated for each error;  it holds information
181  * about the interpreter and the error until tkerror can be invoked
182  * later as an idle handler.
183  */
184
185 typedef struct BgError {
186     Tcl_Interp *interp;         /* Interpreter in which error occurred.  NULL
187                                  * means this error report has been cancelled
188                                  * (a previous report generated a break). */
189     char *errorMsg;             /* The error message (interp->result when
190                                  * the error occurred).  Malloc-ed. */
191     char *errorInfo;            /* Value of the errorInfo variable
192                                  * (malloc-ed). */
193     char *errorCode;            /* Value of the errorCode variable
194                                  * (malloc-ed). */
195     struct BgError *nextPtr;    /* Next in list of all pending error
196                                  * reports. */
197 } BgError;
198
199 static BgError *firstBgPtr = NULL;
200                                 /* First in list of all background errors
201                                  * waiting to be processed (NULL if none). */
202 static BgError *lastBgPtr = NULL;
203                                 /* First in list of all background errors
204                                  * waiting to be processed (NULL if none). */
205
206 /*
207  * Prototypes for procedures referenced only in this file:
208  */
209
210 static void             AfterProc _ANSI_ARGS_((ClientData clientData));
211 static void             DeleteFileEvent _ANSI_ARGS_((FILE *f));
212 static int              FileEventProc _ANSI_ARGS_((ClientData clientData,
213                             int mask, int flags));
214 static void             FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
215 static void             HandleBgErrors _ANSI_ARGS_((ClientData clientData));
216 static int              TkwaitCmd2 _ANSI_ARGS_((ClientData clientData,
217                             Tcl_Interp *interp, int argc, char **argv));
218 static int              UpdateCmd2 _ANSI_ARGS_((ClientData clientData,
219                             Tcl_Interp *interp, int argc, char **argv));
220 static char *           WaitVariableProc2 _ANSI_ARGS_((ClientData clientData,
221                             Tcl_Interp *interp, char *name1, char *name2,
222                             int flags));
223 \f
224 /*
225  *--------------------------------------------------------------
226  *
227  * Tk_CreateFileHandler --
228  *
229  *      Arrange for a given procedure to be invoked whenever
230  *      a given file becomes readable or writable.
231  *
232  * Results:
233  *      None.
234  *
235  * Side effects:
236  *      From now on, whenever the I/O channel given by fd becomes
237  *      ready in the way indicated by mask, proc will be invoked.
238  *      See the manual entry for details on the calling sequence
239  *      to proc.  If fd is already registered then the old mask
240  *      and proc and clientData values will be replaced with
241  *      new ones.
242  *
243  *--------------------------------------------------------------
244  */
245
246 void
247 Tk_CreateFileHandler(fd, mask, proc, clientData)
248     int fd;                     /* Integer identifier for stream. */
249     int mask;                   /* OR'ed combination of TK_READABLE,
250                                  * TK_WRITABLE, and TK_EXCEPTION:
251                                  * indicates conditions under which
252                                  * proc should be called.  TK_IS_DISPLAY
253                                  * indicates that this is a display and that
254                                  * clientData is the (Display *) for it,
255                                  * and that events should be handled
256                                  * automatically.*/
257     Tk_FileProc *proc;          /* Procedure to call for each
258                                  * selected event. */
259     ClientData clientData;      /* Arbitrary data to pass to proc. */
260 {
261     register FileHandler *filePtr;
262     int index;
263
264     if (fd >= FD_SETSIZE) {
265         panic("Tk_CreatefileHandler can't handle file id %d", fd);
266     }
267
268     /*
269      * Make sure the file isn't already registered.  Create a
270      * new record in the normal case where there's no existing
271      * record.
272      */
273
274     for (filePtr = firstFileHandlerPtr; filePtr != NULL;
275             filePtr = filePtr->nextPtr) {
276         if (filePtr->fd == fd) {
277             break;
278         }
279     }
280     index = fd/(NBBY*sizeof(fd_mask));
281     if (filePtr == NULL) {
282         filePtr = (FileHandler *) ckalloc(sizeof(FileHandler));
283         filePtr->fd = fd;
284         filePtr->readPtr = &ready[index];
285         filePtr->writePtr = &ready[index+MASK_SIZE];
286         filePtr->exceptPtr = &ready[index+2*MASK_SIZE];
287         filePtr->checkReadPtr = &check[index];
288         filePtr->checkWritePtr = &check[index+MASK_SIZE];
289         filePtr->checkExceptPtr = &check[index+2*MASK_SIZE];
290         filePtr->bitSelect = 1 << (fd%(NBBY*sizeof(fd_mask)));
291         filePtr->nextPtr = firstFileHandlerPtr;
292         firstFileHandlerPtr = filePtr;
293     }
294
295     /*
296      * The remainder of the initialization below is done
297      * regardless of whether or not this is a new record
298      * or a modification of an old one.
299      */
300
301     filePtr->mask = mask;
302     filePtr->proc = proc;
303     filePtr->proc2 = NULL;
304     filePtr->clientData = clientData;
305
306     if (numFds <= fd) {
307         numFds = fd+1;
308     }
309 }
310 \f
311 /*
312  *--------------------------------------------------------------
313  *
314  * Tk_CreateFileHandler2 --
315  *
316  *      Arrange for a given procedure to be invoked during the
317  *      event loop to handle a particular file.
318  *
319  * Results:
320  *      None.
321  *
322  * Side effects:
323  *      In each pass through Tk_DoOneEvent, proc will be invoked to
324  *      decide whether fd is "ready" and take appropriate action if
325  *      it is.  See the manual entry for details on the calling
326  *      sequence to proc.  If a handler for fd has already been
327  *      registered then it is superseded by the new one.
328  *
329  *--------------------------------------------------------------
330  */
331
332 void
333 Tk_CreateFileHandler2(fd, proc, clientData)
334     int fd;                     /* Integer identifier for stream. */
335     Tk_FileProc2 *proc;         /* Procedure to call from the event
336                                  * dispatcher. */
337     ClientData clientData;      /* Arbitrary data to pass to proc. */
338 {
339     register FileHandler *filePtr;
340
341     /*
342      * Let Tk_CreateFileHandler do all of the work of setting up
343      * the handler, then just modify things a bit after it returns.
344      */
345
346     Tk_CreateFileHandler(fd, 0, (Tk_FileProc *) NULL, clientData);
347     for (filePtr = firstFileHandlerPtr; filePtr->fd != fd;
348             filePtr = filePtr->nextPtr) {
349         /* Empty loop body. */
350     }
351     filePtr->proc = NULL;
352     filePtr->proc2 = proc;
353 }
354 \f
355 /*
356  *--------------------------------------------------------------
357  *
358  * Tk_DeleteFileHandler --
359  *
360  *      Cancel a previously-arranged callback arrangement for
361  *      a file.
362  *
363  * Results:
364  *      None.
365  *
366  * Side effects:
367  *      If a callback was previously registered on fd, remove it.
368  *
369  *--------------------------------------------------------------
370  */
371
372 void
373 Tk_DeleteFileHandler(fd)
374     int fd;                     /* Stream id for which to remove
375                                  * callback procedure. */
376 {
377     register FileHandler *filePtr;
378     FileHandler *prevPtr;
379
380     /*
381      * Find the entry for the given file (and return if there
382      * isn't one).
383      */
384
385     for (prevPtr = NULL, filePtr = firstFileHandlerPtr; ;
386             prevPtr = filePtr, filePtr = filePtr->nextPtr) {
387         if (filePtr == NULL) {
388             return;
389         }
390         if (filePtr->fd == fd) {
391             break;
392         }
393     }
394
395     /*
396      * Clean up information in the callback record.
397      */
398
399     if (prevPtr == NULL) {
400         firstFileHandlerPtr = filePtr->nextPtr;
401     } else {
402         prevPtr->nextPtr = filePtr->nextPtr;
403     }
404     ckfree((char *) filePtr);
405
406     /*
407      * Recompute numFds.
408      */
409
410     numFds = 0;
411     for (filePtr = firstFileHandlerPtr; filePtr != NULL;
412             filePtr = filePtr->nextPtr) {
413         if (numFds <= filePtr->fd) {
414             numFds = filePtr->fd+1;
415         }
416     }
417 }
418 \f
419 /*
420  *--------------------------------------------------------------
421  *
422  * Tk_CreateTimerHandler --
423  *
424  *      Arrange for a given procedure to be invoked at a particular
425  *      time in the future.
426  *
427  * Results:
428  *      The return value is a token for the timer event, which
429  *      may be used to delete the event before it fires.
430  *
431  * Side effects:
432  *      When milliseconds have elapsed, proc will be invoked
433  *      exactly once.
434  *
435  *--------------------------------------------------------------
436  */
437
438 Tk_TimerToken
439 Tk_CreateTimerHandler(milliseconds, proc, clientData)
440     int milliseconds;           /* How many milliseconds to wait
441                                  * before invoking proc. */
442     Tk_TimerProc *proc;         /* Procedure to invoke. */
443     ClientData clientData;      /* Arbitrary data to pass to proc. */
444 {
445     register TimerEvent *timerPtr, *tPtr2, *prevPtr;
446     static int id = 0;
447
448     timerPtr = (TimerEvent *) ckalloc(sizeof(TimerEvent));
449
450     /*
451      * Compute when the event should fire.
452      */
453
454     (void) gettimeofday(&timerPtr->time, (struct timezone *) NULL);
455     timerPtr->time.tv_sec += milliseconds/1000;
456     timerPtr->time.tv_usec += (milliseconds%1000)*1000;
457     if (timerPtr->time.tv_usec >= 1000000) {
458         timerPtr->time.tv_usec -= 1000000;
459         timerPtr->time.tv_sec += 1;
460     }
461
462     /*
463      * Fill in other fields for the event.
464      */
465
466     timerPtr->proc = proc;
467     timerPtr->clientData = clientData;
468     id++;
469     timerPtr->token = (Tk_TimerToken) id;
470
471     /*
472      * Add the event to the queue in the correct position
473      * (ordered by event firing time).
474      */
475
476     for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
477             prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
478         if ((tPtr2->time.tv_sec > timerPtr->time.tv_sec)
479                 || ((tPtr2->time.tv_sec == timerPtr->time.tv_sec)
480                 && (tPtr2->time.tv_usec > timerPtr->time.tv_usec))) {
481             break;
482         }
483     }
484     if (prevPtr == NULL) {
485         timerPtr->nextPtr = firstTimerHandlerPtr;
486         firstTimerHandlerPtr = timerPtr;
487     } else {
488         timerPtr->nextPtr = prevPtr->nextPtr;
489         prevPtr->nextPtr = timerPtr;
490     }
491     return timerPtr->token;
492 }
493 \f
494 /*
495  *--------------------------------------------------------------
496  *
497  * Tk_DeleteTimerHandler --
498  *
499  *      Delete a previously-registered timer handler.
500  *
501  * Results:
502  *      None.
503  *
504  * Side effects:
505  *      Destroy the timer callback identified by TimerToken,
506  *      so that its associated procedure will not be called.
507  *      If the callback has already fired, or if the given
508  *      token doesn't exist, then nothing happens.
509  *
510  *--------------------------------------------------------------
511  */
512
513 void
514 Tk_DeleteTimerHandler(token)
515     Tk_TimerToken token;        /* Result previously returned by
516                                  * Tk_DeleteTimerHandler. */
517 {
518     register TimerEvent *timerPtr, *prevPtr;
519
520     for (timerPtr = firstTimerHandlerPtr, prevPtr = NULL; timerPtr != NULL;
521             prevPtr = timerPtr, timerPtr = timerPtr->nextPtr) {
522         if (timerPtr->token != token) {
523             continue;
524         }
525         if (prevPtr == NULL) {
526             firstTimerHandlerPtr = timerPtr->nextPtr;
527         } else {
528             prevPtr->nextPtr = timerPtr->nextPtr;
529         }
530         ckfree((char *) timerPtr);
531         return;
532     }
533 }
534 \f
535 /*
536  *--------------------------------------------------------------
537  *
538  * Tk_DoWhenIdle --
539  *
540  *      Arrange for proc to be invoked the next time the
541  *      system is idle (i.e., just before the next time
542  *      that Tk_DoOneEvent would have to wait for something
543  *      to happen).
544  *
545  * Results:
546  *      None.
547  *
548  * Side effects:
549  *      Proc will eventually be called, with clientData
550  *      as argument.  See the manual entry for details.
551  *
552  *--------------------------------------------------------------
553  */
554
555 void
556 Tk_DoWhenIdle(proc, clientData)
557     Tk_IdleProc *proc;          /* Procedure to invoke. */
558     ClientData clientData;      /* Arbitrary value to pass to proc. */
559 {
560     register IdleHandler *idlePtr;
561
562     idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
563     idlePtr->proc = proc;
564     idlePtr->clientData = clientData;
565     idlePtr->generation = idleGeneration;
566     idlePtr->nextPtr = NULL;
567     if (lastIdlePtr == NULL) {
568         idleList = idlePtr;
569     } else {
570         lastIdlePtr->nextPtr = idlePtr;
571     }
572     lastIdlePtr = idlePtr;
573 }
574 \f
575 /*
576  *--------------------------------------------------------------
577  *
578  * Tk_DoWhenIdle2 --
579  *
580  *      Arrange for proc to be invoked when the system is idle
581  *      (i.e., if currently idle or just before the next time
582  *      that Tk_DoOneEvent would have to wait for something
583  *      to happen).
584  *
585  * Results:
586  *      None.
587  *
588  * Side effects:
589  *      Proc will eventually be called, with clientData
590  *      as argument.  See the manual entry for details.
591  *
592  *--------------------------------------------------------------
593  */
594
595 void
596 Tk_DoWhenIdle2(proc, clientData)
597     Tk_IdleProc *proc;          /* Procedure to invoke. */
598     ClientData clientData;      /* Arbitrary value to pass to proc. */
599 {
600     register IdleHandler *idlePtr;
601
602     idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
603     idlePtr->proc = proc;
604     idlePtr->clientData = clientData;
605     idlePtr->generation = idleList == NULL ? oldGeneration :
606         idleList->generation;
607     idlePtr->nextPtr = NULL;
608     if (lastIdlePtr == NULL) {
609         idleList = idlePtr;
610     } else {
611         lastIdlePtr->nextPtr = idlePtr;
612     }
613     lastIdlePtr = idlePtr;
614 }
615 \f
616 /*
617  *----------------------------------------------------------------------
618  *
619  * Tk_CancelIdleCall --
620  *
621  *      If there are any when-idle calls requested to a given procedure
622  *      with given clientData, cancel all of them.
623  *
624  * Results:
625  *      None.
626  *
627  * Side effects:
628  *      If the proc/clientData combination were on the when-idle list,
629  *      they are removed so that they will never be called.
630  *
631  *----------------------------------------------------------------------
632  */
633
634 void
635 Tk_CancelIdleCall(proc, clientData)
636     Tk_IdleProc *proc;          /* Procedure that was previously registered. */
637     ClientData clientData;      /* Arbitrary value to pass to proc. */
638 {
639     register IdleHandler *idlePtr, *prevPtr;
640     IdleHandler *nextPtr;
641
642     for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
643             prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
644         while ((idlePtr->proc == proc)
645                 && (idlePtr->clientData == clientData)) {
646             nextPtr = idlePtr->nextPtr;
647             ckfree((char *) idlePtr);
648             idlePtr = nextPtr;
649             if (prevPtr == NULL) {
650                 idleList = idlePtr;
651             } else {
652                 prevPtr->nextPtr = idlePtr;
653             }
654             if (idlePtr == NULL) {
655                 lastIdlePtr = prevPtr;
656                 return;
657             }
658         }
659     }
660 }
661 \f
662 /*
663  *--------------------------------------------------------------
664  *
665  * Tk_DoOneEvent --
666  *
667  *      Process a single event of some sort.  If there's no
668  *      work to do, wait for an event to occur, then process
669  *      it.
670  *
671  * Results:
672  *      The return value is 1 if the procedure actually found
673  *      an event to process.  If no event was found then 0 is
674  *      returned.
675  *
676  * Side effects:
677  *      May delay execution of process while waiting for an
678  *      X event, X error, file-ready event, or timer event.
679  *      The handling of the event could cause additional
680  *      side effects.  Collapses sequences of mouse-motion
681  *      events for the same window into a single event by
682  *      delaying motion event processing.
683  *
684  *--------------------------------------------------------------
685  */
686
687 int
688 Tk_DoOneEvent(flags)
689     int flags;                  /* Miscellaneous flag values:  may be any
690                                  * combination of TK_DONT_WAIT, TK_X_EVENTS,
691                                  * TK_FILE_EVENTS, TK_TIMER_EVENTS, and
692                                  * TK_IDLE_EVENTS. */
693 {
694     register FileHandler *filePtr;
695     struct timeval curTime, timeoutVal, *timeoutPtr;
696     int numFound, mask, anyFilesToWaitFor;
697
698     if ((flags & TK_ALL_EVENTS) == 0) {
699         flags |= TK_ALL_EVENTS;
700     }
701
702     /*
703      * Phase One: see if there's a ready file that was left over
704      * from before (i.e don't do a select, just check the bits from
705      * the last select).
706      */
707
708     checkFiles:
709     if (tcl_AsyncReady) {
710         (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
711         return 1;
712     }
713     memset((VOID *) check, 0, 3*MASK_SIZE*sizeof(fd_mask));
714     anyFilesToWaitFor = 0;
715     for (filePtr = firstFileHandlerPtr; filePtr != NULL;
716             filePtr = filePtr->nextPtr) {
717         mask = 0;
718         if (*filePtr->readPtr & filePtr->bitSelect) {
719             mask |= TK_READABLE;
720             *filePtr->readPtr &= ~filePtr->bitSelect;
721         }
722         if (*filePtr->writePtr & filePtr->bitSelect) {
723             mask |= TK_WRITABLE;
724             *filePtr->writePtr &= ~filePtr->bitSelect;
725         }
726         if (*filePtr->exceptPtr & filePtr->bitSelect) {
727             mask |= TK_EXCEPTION;
728             *filePtr->exceptPtr &= ~filePtr->bitSelect;
729         }
730         if (filePtr->proc2 != NULL) {
731             /*
732              * Handler created by Tk_CreateFileHandler2.
733              */
734
735             mask = (*filePtr->proc2)(filePtr->clientData, mask, flags);
736             if (mask == TK_FILE_HANDLED) {
737                 return 1;
738             }
739         } else {
740             /*
741              * Handler created by Tk_CreateFileHandler.
742              */
743
744             if (!(flags & TK_FILE_EVENTS)) {
745                 continue;
746             }
747             if (mask != 0) {
748                 (*filePtr->proc)(filePtr->clientData, mask);
749                 return 1;
750             }
751             mask = filePtr->mask;
752         }
753         if (mask != 0) {
754             anyFilesToWaitFor = 1;
755             if (mask & TK_READABLE) {
756                 *filePtr->checkReadPtr |= filePtr->bitSelect;
757             }
758             if (mask & TK_WRITABLE) {
759                 *filePtr->checkWritePtr |= filePtr->bitSelect;
760             }
761             if (mask & TK_EXCEPTION) {
762                 *filePtr->checkExceptPtr |= filePtr->bitSelect;
763             }
764         }
765     }
766
767     /*
768      * Phase Two: get the current time and see if any timer
769      * events are ready to fire.  If so, fire one and return.
770      */
771
772     checkTime:
773     if ((firstTimerHandlerPtr != NULL) && (flags & TK_TIMER_EVENTS)) {
774         register TimerEvent *timerPtr = firstTimerHandlerPtr;
775
776         (void) gettimeofday(&curTime, (struct timezone *) NULL);
777         if ((timerPtr->time.tv_sec < curTime.tv_sec)
778                 || ((timerPtr->time.tv_sec == curTime.tv_sec)
779                 &&  (timerPtr->time.tv_usec < curTime.tv_usec))) {
780             firstTimerHandlerPtr = timerPtr->nextPtr;
781             (*timerPtr->proc)(timerPtr->clientData);
782             ckfree((char *) timerPtr);
783             return 1;
784         }
785     }
786
787     /*
788      * Phase Three: if there are DoWhenIdle requests pending (or
789      * if we're not allowed to block), then do a select with an
790      * instantaneous timeout.  If a ready file is found, then go
791      * back to process it.
792      */
793
794     if (((idleList != NULL) && (flags & TK_IDLE_EVENTS))
795             || (flags & TK_DONT_WAIT)) {
796         if (flags & (TK_X_EVENTS|TK_FILE_EVENTS)) {
797             memcpy((VOID *) ready, (VOID *) check,
798                     3*MASK_SIZE*sizeof(fd_mask));
799             timeoutVal.tv_sec = timeoutVal.tv_usec = 0;
800             numFound = select(numFds, (SELECT_MASK *) &ready[0],
801                     (SELECT_MASK *) &ready[MASK_SIZE],
802                     (SELECT_MASK *) &ready[2*MASK_SIZE], &timeoutVal);
803             if (numFound <= 0) {
804                 /*
805                  * Some systems don't clear the masks after an error, so
806                  * we have to do it here.
807                  */
808
809                 memset((VOID *) ready, 0, 3*MASK_SIZE*sizeof(fd_mask));
810             }
811             if ((numFound > 0) || ((numFound == -1) && (errno == EINTR))) {
812                 goto checkFiles;
813             }
814         }
815     }
816
817     /*
818      * Phase Four: if there is a delayed motion event then call a procedure
819      * to handle it.  Do it now, before calling any DoWhenIdle handlers,
820      * since the goal of idle handlers is to delay until after all pending
821      * events have been processed.
822      *
823      * The particular implementation of this (a procedure variable shared
824      * with tkXEvent.c) is a bit kludgy, but it allows this file to be used
825      * separately without any of the rest of Tk.
826      */
827
828     if ((tkDelayedEventProc != NULL) && (flags & TK_X_EVENTS)) {
829         (*tkDelayedEventProc)();
830         return 1;
831     }
832
833     /*
834      * Phase Five:  process all pending DoWhenIdle requests.
835      */
836
837     if ((idleList != NULL) && (flags & TK_IDLE_EVENTS)) {
838         register IdleHandler *idlePtr;
839         int myGeneration;
840
841         oldGeneration = myGeneration = idleList->generation;
842         idleGeneration++;
843
844         /*
845          * The code below is trickier than it may look, for the following
846          * reasons:
847          *
848          * 1. New handlers can get added to the list while the current
849          *    one is being processed.  If new ones get added, we don't
850          *    want to process them during this pass through the list (want
851          *    to check for other work to do first).  This is implemented
852          *    using the generation number in the handler:  new handlers
853          *    will have a different generation than any of the ones currently
854          *    on the list.
855          * 2. The handler can call Tk_DoOneEvent, so we have to remove
856          *    the hander from the list before calling it. Otherwise an
857          *    infinite loop could result.
858          * 3. Tk_CancelIdleCall can be called to remove an element from
859          *    the list while a handler is executing, so the list could
860          *    change structure during the call.
861          */
862
863         for (idlePtr = idleList;
864                 ((idlePtr != NULL) && (idlePtr->generation == myGeneration));
865                 idlePtr = idleList) {
866             idleList = idlePtr->nextPtr;
867             if (idleList == NULL) {
868                 lastIdlePtr = NULL;
869             }
870             (*idlePtr->proc)(idlePtr->clientData);
871             ckfree((char *) idlePtr);
872         }
873         return 1;
874     }
875
876     /*
877      * Phase Six: do a select to wait for either one of the
878      * files to become ready or for the first timer event to
879      * fire.  Then go back to process the event.
880      */
881
882     if ((flags & TK_DONT_WAIT)
883             || !(flags & (TK_TIMER_EVENTS|TK_FILE_EVENTS|TK_X_EVENTS))) {
884         return 0;
885     }
886     if ((firstTimerHandlerPtr == NULL) || !(flags & TK_TIMER_EVENTS)) {
887         timeoutPtr = NULL;
888     } else {
889         timeoutPtr = &timeoutVal;
890         timeoutVal.tv_sec = firstTimerHandlerPtr->time.tv_sec
891             - curTime.tv_sec;
892         timeoutVal.tv_usec = firstTimerHandlerPtr->time.tv_usec
893             - curTime.tv_usec;
894         if (timeoutVal.tv_usec < 0) {
895             timeoutVal.tv_sec -= 1;
896             timeoutVal.tv_usec += 1000000;
897         }
898     }
899     if ((timeoutPtr == NULL) && !anyFilesToWaitFor) {
900         return 0;
901     }
902     memcpy((VOID *) ready, (VOID *) check, 3*MASK_SIZE*sizeof(fd_mask));
903     numFound = select(numFds, (SELECT_MASK *) &ready[0],
904             (SELECT_MASK *) &ready[MASK_SIZE],
905             (SELECT_MASK *) &ready[2*MASK_SIZE], timeoutPtr);
906     if (numFound == -1) {
907         /*
908          * Some systems don't clear the masks after an error, so
909          * we have to do it here.
910          */
911
912         memset((VOID *) ready, 0, 3*MASK_SIZE*sizeof(fd_mask));
913     }
914     if (numFound == 0) {
915         goto checkTime;
916     }
917     goto checkFiles;
918 }
919 \f
920 /*
921  *----------------------------------------------------------------------
922  *
923  * Tk_Sleep --
924  *
925  *      Delay execution for the specified number of milliseconds.
926  *
927  * Results:
928  *      None.
929  *
930  * Side effects:
931  *      Time passes.
932  *
933  *----------------------------------------------------------------------
934  */
935
936 void
937 Tk_Sleep(ms)
938     int ms;                     /* Number of milliseconds to sleep. */
939 {
940     static struct timeval delay;
941
942     delay.tv_sec = ms/1000;
943     delay.tv_usec = (ms%1000)*1000;
944     (void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
945             (SELECT_MASK *) 0, &delay);
946 }
947 \f
948 /*
949  *----------------------------------------------------------------------
950  *
951  * Tk_BackgroundError --
952  *
953  *      This procedure is invoked to handle errors that occur in Tcl
954  *      commands that are invoked in "background" (e.g. from event or
955  *      timer bindings).
956  *
957  * Results:
958  *      None.
959  *
960  * Side effects:
961  *      The command "tkerror" is invoked later as an idle handler to
962  *      process the error, passing it the error message.  If that fails,
963  *      then an error message is output on stderr.
964  *
965  *----------------------------------------------------------------------
966  */
967
968 void
969 Tk_BackgroundError(interp)
970     Tcl_Interp *interp;         /* Interpreter in which an error has
971                                  * occurred. */
972 {
973     BgError *errPtr;
974     char *varValue;
975
976     /*
977      * The Tcl_AddErrorInfo call below (with an empty string) ensures that
978      * errorInfo gets properly set.  It's needed in cases where the error
979      * came from a utility procedure like Tcl_GetVar instead of Tcl_Eval;
980      * in these cases errorInfo still won't have been set when this
981      * procedure is called.
982      */
983
984     Tcl_AddErrorInfo(interp, "");
985     errPtr = (BgError *) ckalloc(sizeof(BgError));
986     errPtr->interp = interp;
987     errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(interp->result)
988             + 1));
989     strcpy(errPtr->errorMsg, interp->result);
990     varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
991     if (varValue == NULL) {
992         varValue = errPtr->errorMsg;
993     }
994     errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
995     strcpy(errPtr->errorInfo, varValue);
996     varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
997     if (varValue == NULL) {
998         varValue = "";
999     }
1000     errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
1001     strcpy(errPtr->errorCode, varValue);
1002     errPtr->nextPtr = NULL;
1003     if (firstBgPtr == NULL) {
1004         firstBgPtr = errPtr;
1005         Tk_DoWhenIdle(HandleBgErrors, (ClientData) NULL);
1006     } else {
1007         lastBgPtr->nextPtr = errPtr;
1008     }
1009     lastBgPtr = errPtr;
1010     Tcl_ResetResult(interp);
1011 }
1012 \f
1013 /*
1014  *----------------------------------------------------------------------
1015  *
1016  * HandleBgErrors --
1017  *
1018  *      This procedure is invoked as an idle handler to process all of
1019  *      the accumulated background errors.
1020  *
1021  * Results:
1022  *      None.
1023  *
1024  * Side effects:
1025  *      Depends on what actions "tkerror" takes for the errors.
1026  *
1027  *----------------------------------------------------------------------
1028  */
1029
1030 static void
1031 HandleBgErrors(clientData)
1032     ClientData clientData;              /* Not used. */
1033 {
1034     Tcl_Interp *interp;
1035     char *command;
1036     char *argv[2];
1037     int code;
1038     BgError *errPtr;
1039
1040     while (firstBgPtr != NULL) {
1041         interp = firstBgPtr->interp;
1042         if (interp == NULL) {
1043             goto doneWithReport;
1044         }
1045
1046         /*
1047          * Restore important state variables to what they were at
1048          * the time the error occurred.
1049          */
1050
1051         Tcl_SetVar(interp, "errorInfo", firstBgPtr->errorInfo,
1052                 TCL_GLOBAL_ONLY);
1053         Tcl_SetVar(interp, "errorCode", firstBgPtr->errorCode,
1054                 TCL_GLOBAL_ONLY);
1055
1056         /*
1057          * Create and invoke the tkerror command.
1058          */
1059
1060         argv[0] = "tkerror";
1061         argv[1] = firstBgPtr->errorMsg;
1062         command = Tcl_Merge(2, argv);
1063         Tcl_AllowExceptions(interp);
1064         code = Tcl_GlobalEval(interp, command);
1065         ckfree(command);
1066         if (code == TCL_ERROR) {
1067             if (strcmp(interp->result, "\"tkerror\" is an invalid command name or ambiguous abbreviation") == 0) {
1068                 fprintf(stderr, "%s\n", firstBgPtr->errorInfo);
1069             } else {
1070                 fprintf(stderr, "tkerror failed to handle background error.\n");
1071                 fprintf(stderr, "    Original error: %s\n",
1072                         firstBgPtr->errorMsg);
1073                 fprintf(stderr, "    Error in tkerror: %s\n", interp->result);
1074             }
1075         } else if (code == TCL_BREAK) {
1076             /*
1077              * Break means cancel any remaining error reports for this
1078              * interpreter.
1079              */
1080
1081             for (errPtr = firstBgPtr; errPtr != NULL;
1082                     errPtr = errPtr->nextPtr) {
1083                 if (errPtr->interp == interp) {
1084                     errPtr->interp = NULL;
1085                 }
1086             }
1087         }
1088
1089         /*
1090          * Discard the command and the information about the error report.
1091          */
1092
1093         doneWithReport:
1094         ckfree(firstBgPtr->errorMsg);
1095         ckfree(firstBgPtr->errorInfo);
1096         ckfree(firstBgPtr->errorCode);
1097         errPtr = firstBgPtr->nextPtr;
1098         ckfree((char *) firstBgPtr);
1099         firstBgPtr = errPtr;
1100     }
1101     lastBgPtr = NULL;
1102 }
1103 \f
1104 /*
1105  *----------------------------------------------------------------------
1106  *
1107  * Tk_AfterCmd --
1108  *
1109  *      This procedure is invoked to process the "after" Tcl command.
1110  *      See the user documentation for details on what it does.
1111  *
1112  * Results:
1113  *      A standard Tcl result.
1114  *
1115  * Side effects:
1116  *      See the user documentation.
1117  *
1118  *----------------------------------------------------------------------
1119  */
1120
1121         /* ARGSUSED */
1122 int
1123 Tk_AfterCmd(clientData, interp, argc, argv)
1124     ClientData clientData;      /* Main window associated with
1125                                  * interpreter.  Not used.*/
1126     Tcl_Interp *interp;         /* Current interpreter. */
1127     int argc;                   /* Number of arguments. */
1128     char **argv;                /* Argument strings. */
1129 {
1130     /*
1131      * The variable below is used to generate unique identifiers for
1132      * after commands.  This id can wrap around, which can potentially
1133      * cause problems.  However, there are not likely to be problems
1134      * in practice, because after commands can only be requested to
1135      * about a month in the future, and wrap-around is unlikely to
1136      * occur in less than about 1-10 years.  Thus it's unlikely that
1137      * any old ids will still be around when wrap-around occurs.
1138      */
1139
1140     static int nextId = 1;
1141     int ms, id;
1142     AfterInfo *afterPtr;
1143
1144     if (argc < 2) {
1145         Tcl_AppendResult(interp, "wrong # args: should be \"",
1146                 argv[0], " milliseconds ?command? ?arg arg ...?\" or \"",
1147                 argv[0], " cancel id|command\"", (char *) NULL);
1148         return TCL_ERROR;
1149     }
1150
1151     if (isdigit((unsigned char) argv[1][0])) {
1152         if (Tcl_GetInt(interp, argv[1], &ms) != TCL_OK) {
1153             return TCL_ERROR;
1154         }
1155         if (ms < 0) {
1156             ms = 0;
1157         }
1158         if (argc == 2) {
1159             Tk_Sleep(ms);
1160             return TCL_OK;
1161         }
1162         afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
1163         afterPtr->interp = interp;
1164         if (argc == 3) {
1165             afterPtr->command = (char *) ckalloc((unsigned)
1166                     (strlen(argv[2]) + 1));
1167             strcpy(afterPtr->command, argv[2]);
1168         } else {
1169             afterPtr->command = Tcl_Concat(argc-2, argv+2);
1170         }
1171         afterPtr->id = nextId;
1172         nextId += 1;
1173         afterPtr->token = Tk_CreateTimerHandler(ms, AfterProc,
1174                 (ClientData) afterPtr);
1175         afterPtr->nextPtr = firstAfterPtr;
1176         firstAfterPtr = afterPtr;
1177         sprintf(interp->result, "after#%d", afterPtr->id);
1178     } else if (strncmp(argv[1], "cancel", strlen(argv[1])) == 0) {
1179         char *arg;
1180
1181         if (argc < 3) {
1182             Tcl_AppendResult(interp, "wrong # args: should be \"",
1183                     argv[0], " cancel id|command\"", (char *) NULL);
1184             return TCL_ERROR;
1185         }
1186         if (argc == 3) {
1187             arg = argv[2];
1188         } else {
1189             arg = Tcl_Concat(argc-2, argv+2);
1190         }
1191         if (strncmp(arg, "after#", 6) == 0) {
1192             if (Tcl_GetInt(interp, arg+6, &id) != TCL_OK) {
1193                 return TCL_ERROR;
1194             }
1195             for (afterPtr = firstAfterPtr; afterPtr != NULL;
1196                     afterPtr = afterPtr->nextPtr) {
1197                 if (afterPtr->id == id) {
1198                     break;
1199                 }
1200             }
1201         } else {
1202             for (afterPtr = firstAfterPtr; afterPtr != NULL;
1203                     afterPtr = afterPtr->nextPtr) {
1204                 if (strcmp(afterPtr->command, arg) == 0) {
1205                     break;
1206                 }
1207             }
1208         }
1209         if (arg != argv[2]) {
1210             ckfree(arg);
1211         }
1212         if (afterPtr != NULL) {
1213             if (afterPtr->token != NULL) {
1214                 Tk_DeleteTimerHandler(afterPtr->token);
1215             } else {
1216                 Tk_CancelIdleCall(AfterProc, (ClientData) afterPtr);
1217             }
1218             FreeAfterPtr(afterPtr);
1219         }
1220     } else if (strncmp(argv[1], "idle", strlen(argv[1])) == 0) {
1221         if (argc < 3) {
1222             Tcl_AppendResult(interp, "wrong # args: should be \"",
1223                     argv[0], " idle script script ...\"", (char *) NULL);
1224             return TCL_ERROR;
1225         }
1226         afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
1227         afterPtr->interp = interp;
1228         if (argc == 3) {
1229             afterPtr->command = (char *) ckalloc((unsigned)
1230                     (strlen(argv[2]) + 1));
1231             strcpy(afterPtr->command, argv[2]);
1232         } else {
1233             afterPtr->command = Tcl_Concat(argc-2, argv+2);
1234         }
1235         afterPtr->id = nextId;
1236         nextId += 1;
1237         afterPtr->token = NULL;
1238         afterPtr->nextPtr = firstAfterPtr;
1239         firstAfterPtr = afterPtr;
1240         Tk_DoWhenIdle(AfterProc, (ClientData) afterPtr);
1241         sprintf(interp->result, "after#%d", afterPtr->id);
1242     } else {
1243         Tcl_AppendResult(interp, "bad argument \"", argv[1],
1244                 "\": must be cancel, idle, or a number", (char *) NULL);
1245         return TCL_ERROR;
1246     }
1247     return TCL_OK;
1248 }
1249 \f
1250 /*
1251  *----------------------------------------------------------------------
1252  *
1253  * AfterProc --
1254  *
1255  *      Timer callback to execute commands registered with the
1256  *      "after" command.
1257  *
1258  * Results:
1259  *      None.
1260  *
1261  * Side effects:
1262  *      Executes whatever command was specified.  If the command
1263  *      returns an error, then the command "tkerror" is invoked
1264  *      to process the error;  if tkerror fails then information
1265  *      about the error is output on stderr.
1266  *
1267  *----------------------------------------------------------------------
1268  */
1269
1270 static void
1271 AfterProc(clientData)
1272     ClientData clientData;      /* Describes command to execute. */
1273 {
1274     AfterInfo *afterPtr = (AfterInfo *) clientData;
1275     AfterInfo *prevPtr;
1276     int result;
1277
1278     /*
1279      * First remove the callback from our list of callbacks;  otherwise
1280      * someone could delete the callback while it's being executed, which
1281      * could cause a core dump.
1282      */
1283
1284     if (firstAfterPtr == afterPtr) {
1285         firstAfterPtr = afterPtr->nextPtr;
1286     } else {
1287         for (prevPtr = firstAfterPtr; prevPtr->nextPtr != afterPtr;
1288                 prevPtr = prevPtr->nextPtr) {
1289             /* Empty loop body. */
1290         }
1291         prevPtr->nextPtr = afterPtr->nextPtr;
1292     }
1293
1294     /*
1295      * Execute the callback.
1296      */
1297
1298     result = Tcl_GlobalEval(afterPtr->interp, afterPtr->command);
1299     if (result != TCL_OK) {
1300         Tcl_AddErrorInfo(afterPtr->interp, "\n    (\"after\" script)");
1301         Tk_BackgroundError(afterPtr->interp);
1302     }
1303
1304     /*
1305      * Free the memory for the callback.
1306      */
1307
1308     ckfree(afterPtr->command);
1309     ckfree((char *) afterPtr);
1310 }
1311 \f
1312 /*
1313  *----------------------------------------------------------------------
1314  *
1315  * FreeAfterPtr --
1316  *
1317  *      This procedure removes an "after" command from the list of
1318  *      those that are pending and frees its resources.  This procedure
1319  *      does *not* cancel the timer handler;  if that's needed, the
1320  *      caller must do it.
1321  *
1322  * Results:
1323  *      None.
1324  *
1325  * Side effects:
1326  *      The memory associated with afterPtr is released.
1327  *
1328  *----------------------------------------------------------------------
1329  */
1330
1331 static void
1332 FreeAfterPtr(afterPtr)
1333     AfterInfo *afterPtr;                /* Command to be deleted. */
1334 {
1335     AfterInfo *prevPtr;
1336     if (firstAfterPtr == afterPtr) {
1337         firstAfterPtr = afterPtr->nextPtr;
1338     } else {
1339         for (prevPtr = firstAfterPtr; prevPtr->nextPtr != afterPtr;
1340                 prevPtr = prevPtr->nextPtr) {
1341             /* Empty loop body. */
1342         }
1343         prevPtr->nextPtr = afterPtr->nextPtr;
1344     }
1345     ckfree(afterPtr->command);
1346     ckfree((char *) afterPtr);
1347 }
1348 \f
1349 /*
1350  *----------------------------------------------------------------------
1351  *
1352  * Tk_FileeventCmd --
1353  *
1354  *      This procedure is invoked to process the "fileevent" Tcl
1355  *      command. See the user documentation for details on what it does.
1356  *      This command is based on Mark Diekhans' "addinput" command.
1357  *
1358  * Results:
1359  *      A standard Tcl result.
1360  *
1361  * Side effects:
1362  *      See the user documentation.
1363  *
1364  *----------------------------------------------------------------------
1365  */
1366
1367         /* ARGSUSED */
1368 int
1369 Tk_FileeventCmd(clientData, interp, argc, argv)
1370     ClientData clientData;      /* Main window associated with interpreter.
1371                                  * Not used.*/
1372     Tcl_Interp *interp;         /* Current interpreter. */
1373     int argc;                   /* Number of arguments. */
1374     char **argv;                /* Argument strings. */
1375 {
1376     FILE *f;
1377     int index, fd, c;
1378     size_t length;
1379     FileEvent *fevPtr, *prevPtr;
1380
1381     /*
1382      * Parse arguments.
1383      */
1384
1385     if ((argc != 3) && (argc != 4)) {
1386         Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
1387                 " fileId event ?script?", (char *) NULL);
1388         return TCL_ERROR;
1389     }
1390     c = argv[2][0];
1391     length = strlen(argv[2]);
1392     if ((c == 'r') && (strncmp(argv[2], "readable", length) == 0)) {
1393         index = 0;
1394     } else if ((c == 'w') && (strncmp(argv[2], "writable", length) == 0)) {
1395         index = 1;
1396     } else {
1397         Tcl_AppendResult(interp, "bad event name \"", argv[2],
1398                 "\": must be readable or writable", (char *) NULL);
1399         return TCL_ERROR;
1400     }
1401     if (Tcl_GetOpenFile(interp, argv[1], index, 1, &f) != TCL_OK) {
1402         return TCL_ERROR;
1403     }
1404     fd = fileno(f);
1405
1406     /*
1407      * Locate an existing file handler for this file, if one exists,
1408      * and make a new one if none currently exists.
1409      */
1410
1411     for (fevPtr = firstFileEventPtr; ; fevPtr = fevPtr->nextPtr) {
1412         if (fevPtr == NULL) {
1413             if ((argc == 3) || (argv[3][0] == 0)) {
1414                 return TCL_OK;
1415             }
1416             fevPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
1417             fevPtr->f = f;
1418             fevPtr->interps[0] = NULL;
1419             fevPtr->interps[1] = NULL;
1420             fevPtr->scripts[0] = NULL;
1421             fevPtr->scripts[1] = NULL;
1422             fevPtr->nextPtr = firstFileEventPtr;
1423             firstFileEventPtr = fevPtr;
1424             Tk_CreateFileHandler2(fileno(f), FileEventProc,
1425                     (ClientData) fevPtr);
1426             tcl_FileCloseProc = DeleteFileEvent;
1427             break;
1428         }
1429         if (fevPtr->f == f) {
1430             break;
1431         }
1432     }
1433
1434     /*
1435      * If we're just supposed to return the current script, do so.
1436      */
1437
1438     if (argc == 3) {
1439         if (fevPtr->scripts[index] != NULL) {
1440             interp->result = fevPtr->scripts[index];
1441         }
1442         return TCL_OK;
1443     }
1444
1445     /*
1446      * If we're supposed to delete the event handler, do so.
1447      */
1448
1449     if (argv[3][0] == 0) {
1450         if (fevPtr->scripts[index] != NULL) {
1451             fevPtr->interps[index] = NULL;
1452             ckfree(fevPtr->scripts[index]);
1453             fevPtr->scripts[index] = NULL;
1454         }
1455         if ((fevPtr->scripts[0] == NULL) && (fevPtr->scripts[1] == NULL)) {
1456             if (firstFileEventPtr == fevPtr) {
1457                 firstFileEventPtr = fevPtr->nextPtr;
1458             } else {
1459                 for (prevPtr = firstFileEventPtr; prevPtr->nextPtr != fevPtr;
1460                         prevPtr = prevPtr->nextPtr) {
1461                     /* Empty loop body. */
1462                 }
1463                 prevPtr->nextPtr = fevPtr->nextPtr;
1464             }
1465             Tk_DeleteFileHandler(fileno(fevPtr->f));
1466             ckfree((char *) fevPtr);
1467         }
1468         return TCL_OK;
1469     }
1470
1471     /*
1472      * This is a new handler being created.  Save its script.
1473      */
1474
1475     fevPtr->interps[index] = interp;
1476     if (fevPtr->scripts[index] != NULL) {
1477         ckfree(fevPtr->scripts[index]);
1478     }
1479     fevPtr->scripts[index] = ckalloc((unsigned) (strlen(argv[3]) + 1));
1480     strcpy(fevPtr->scripts[index], argv[3]);
1481     return TCL_OK;
1482 }
1483 \f
1484 /*
1485  *----------------------------------------------------------------------
1486  *
1487  * FileEventProc --
1488  *
1489  *      This procedure is invoked by Tk's event loop to deal with file
1490  *      event bindings created by the "fileevent" command.
1491  *
1492  * Results:
1493  *      The return value is TK_FILE_HANDLED if the file was ready and
1494  *      a script was invoked to handle it.  Otherwise an OR-ed combination
1495  *      of TK_READABLE and TK_WRITABLE is returned, indicating the events
1496  *      that should be checked in future calls to select.
1497  *
1498  * Side effects:
1499  *      Whatever the event script does.
1500  *
1501  *----------------------------------------------------------------------
1502  */
1503
1504 static int
1505 FileEventProc(clientData, mask, flags)
1506     ClientData clientData;      /* Pointer to FileEvent structure for file. */
1507     int mask;                   /* OR-ed combination of the bits TK_READABLE,
1508                                  * TK_WRITABLE, and TK_EXCEPTION, indicating
1509                                  * current state of file. */
1510     int flags;                  /* Flag bits passed to Tk_DoOneEvent;
1511                                  * contains bits such as TK_DONT_WAIT,
1512                                  * TK_X_EVENTS, Tk_FILE_EVENTS, etc. */
1513 {
1514     FileEvent *fevPtr = (FileEvent *) clientData;
1515     Tcl_DString script;
1516     Tcl_Interp *interp;
1517     FILE *f;
1518     int code, checkMask;
1519
1520     if (!(flags & TK_FILE_EVENTS)) {
1521         return 0;
1522     }
1523
1524     /*
1525      * The code here is a little tricky, because the script for an
1526      * event could delete the event handler.  Thus, after we call
1527      * Tcl_GlobalEval we can't use fevPtr anymore.  We also have to
1528      * copy the script to make sure that it doesn't get freed while
1529      * being evaluated.
1530      */
1531
1532     checkMask = 0;
1533     f = fevPtr->f;
1534     if (fevPtr->scripts[1] != NULL) {
1535         if (mask & TK_WRITABLE) {
1536             Tcl_DStringInit(&script);
1537             Tcl_DStringAppend(&script, fevPtr->scripts[1], -1);
1538             interp = fevPtr->interps[1];
1539             code = Tcl_GlobalEval(interp, Tcl_DStringValue(&script));
1540             Tcl_DStringFree(&script);
1541             if (code != TCL_OK) {
1542                 goto error;
1543             }
1544             return TK_FILE_HANDLED;
1545         } else {
1546             checkMask |= TK_WRITABLE;
1547         }
1548     }
1549     if (fevPtr->scripts[0] != NULL) {
1550         if ((mask & TK_READABLE) || TK_READ_DATA_PENDING(f)) {
1551             Tcl_DStringInit(&script);
1552             Tcl_DStringAppend(&script, fevPtr->scripts[0], -1);
1553             interp = fevPtr->interps[0];
1554             code = Tcl_GlobalEval(interp, Tcl_DStringValue(&script));
1555             Tcl_DStringFree(&script);
1556             if (code != TCL_OK) {
1557                 goto error;
1558             }
1559             return TK_FILE_HANDLED;
1560         } else {
1561             checkMask |= TK_READABLE;
1562         }
1563     }
1564     return checkMask;
1565
1566     /*
1567      * An error occurred in the script, so we have to call
1568      * Tk_BackgroundError.  However, it's possible that the file ready
1569      * condition didn't get cleared for the file, so we could end
1570      * up in an infinite loop if we're not careful.  To be safe,
1571      * delete the event handler.
1572      */
1573
1574     error:
1575     DeleteFileEvent(f);
1576     Tcl_AddErrorInfo(interp,
1577             "\n    (script bound to file event - binding deleted)");
1578     Tk_BackgroundError(interp);
1579     return TK_FILE_HANDLED;
1580 }
1581 \f
1582 /*
1583  *----------------------------------------------------------------------
1584  *
1585  * DeleteFileEvent --
1586  *
1587  *      This procedure is invoked to delete all file event handlers
1588  *      for a file.  For example, this is necessary if a file is closed,
1589  *      or if an error occurs in a handler for a file.
1590  *
1591  * Results:
1592  *      None.
1593  *
1594  * Side effects:
1595  *      The file event handler is removed, so it will never be invoked
1596  *      again.
1597  *
1598  *----------------------------------------------------------------------
1599  */
1600
1601 static void
1602 DeleteFileEvent(f)
1603     FILE *f;                    /* Stdio structure describing open file. */
1604 {
1605     register FileEvent *fevPtr;
1606     FileEvent *prevPtr;
1607
1608     /*
1609      * See if there exists a file handler for the given file.
1610      */
1611
1612     for (prevPtr = NULL, fevPtr = firstFileEventPtr; ;
1613             prevPtr = fevPtr, fevPtr = fevPtr->nextPtr) {
1614         if (fevPtr == NULL) {
1615             return;
1616         }
1617         if (fevPtr->f == f) {
1618             break;
1619         }
1620     }
1621
1622     /*
1623      * Unlink it from the list, then free it.
1624      */
1625
1626     if (prevPtr == NULL) {
1627         firstFileEventPtr = fevPtr->nextPtr;
1628     } else {
1629         prevPtr->nextPtr = fevPtr->nextPtr;
1630     }
1631     Tk_DeleteFileHandler(fileno(fevPtr->f));
1632     if (fevPtr->scripts[0] != NULL) {
1633         ckfree(fevPtr->scripts[0]);
1634     }
1635     if (fevPtr->scripts[1] != NULL) {
1636         ckfree(fevPtr->scripts[1]);
1637     }
1638     ckfree((char *) fevPtr);
1639 }
1640 \f
1641 /*
1642  *----------------------------------------------------------------------
1643  *
1644  * TkEventCleanupProc --
1645  *
1646  *      This procedure is invoked whenever an interpreter is deleted.
1647  *      It deletes any file events and after commands that refer to
1648  *      that interpreter.
1649  *
1650  * Results:
1651  *      None.
1652  *
1653  * Side effects:
1654  *      File event handlers and after commands are removed.
1655  *
1656  *----------------------------------------------------------------------
1657  */
1658
1659         /* ARGSUSED */
1660 void
1661 TkEventCleanupProc(clientData, interp)
1662     ClientData clientData;      /* Not used. */
1663     Tcl_Interp *interp;         /* Interpreter that is being deleted. */
1664 {
1665     FileEvent *fevPtr, *prevPtr, *nextPtr;
1666     AfterInfo *afterPtr, *prevAfterPtr, *nextAfterPtr;
1667     int i;
1668
1669     prevPtr = NULL;
1670     fevPtr = firstFileEventPtr;
1671     while (fevPtr != NULL) {
1672         for (i = 0; i < 2; i++) {
1673             if (fevPtr->interps[i] == interp) {
1674                 fevPtr->interps[i] = NULL;
1675                 ckfree((char *) fevPtr->scripts[i]);
1676                 fevPtr->scripts[i] = NULL;
1677             }
1678         }
1679         if ((fevPtr->scripts[0] != NULL) || (fevPtr->scripts[1] != NULL)) {
1680             prevPtr = fevPtr;
1681             fevPtr = fevPtr->nextPtr;
1682             continue;
1683         }
1684         nextPtr = fevPtr->nextPtr;
1685         if (prevPtr == NULL) {
1686             firstFileEventPtr = nextPtr;
1687         } else {
1688             prevPtr->nextPtr = nextPtr;
1689         }
1690         Tk_DeleteFileHandler(fileno(fevPtr->f));
1691         ckfree((char *) fevPtr);
1692         fevPtr = nextPtr;
1693     }
1694
1695     prevAfterPtr = NULL;
1696     afterPtr = firstAfterPtr;
1697     while (afterPtr != NULL) {
1698         if (afterPtr->interp != interp) {
1699             prevAfterPtr = afterPtr;
1700             afterPtr = afterPtr->nextPtr;
1701             continue;
1702         }
1703         nextAfterPtr = afterPtr->nextPtr;
1704         if (prevAfterPtr == NULL) {
1705             firstAfterPtr = nextAfterPtr;
1706         } else {
1707             prevAfterPtr->nextPtr = nextAfterPtr;
1708         }
1709         if (afterPtr->token != NULL) {
1710             Tk_DeleteTimerHandler(afterPtr->token);
1711         } else {
1712             Tk_CancelIdleCall(AfterProc, (ClientData) afterPtr);
1713         }
1714         ckfree(afterPtr->command);
1715         ckfree((char *) afterPtr);
1716         afterPtr = nextAfterPtr;
1717     }
1718 }
1719 \f
1720 /*
1721  *----------------------------------------------------------------------
1722  *
1723  * TkwaitCmd2 --
1724  *
1725  *      This procedure is invoked to process the "tkwait" Tcl command.
1726  *      See the user documentation for details on what it does.  This
1727  *      is a modified version of tkwait with only the "variable"
1728  *      option, suitable for use in stand-alone mode without the rest
1729  *      of Tk.  It's only used when Tk_EventInit has been called.
1730  *
1731  * Results:
1732  *      A standard Tcl result.
1733  *
1734  * Side effects:
1735  *      See the user documentation.
1736  *
1737  *----------------------------------------------------------------------
1738  */
1739
1740         /* ARGSUSED */
1741 static int
1742 TkwaitCmd2(clientData, interp, argc, argv)
1743     ClientData clientData;      /* Not used. */
1744     Tcl_Interp *interp;         /* Current interpreter. */
1745     int argc;                   /* Number of arguments. */
1746     char **argv;                /* Argument strings. */
1747 {
1748     int c, done;
1749     size_t length;
1750
1751     if (argc != 3) {
1752         Tcl_AppendResult(interp, "wrong # args: should be \"",
1753                 argv[0], " variable name\"", (char *) NULL);
1754         return TCL_ERROR;
1755     }
1756     c = argv[1][0];
1757     length = strlen(argv[1]);
1758     if ((c == 'v') && (strncmp(argv[1], "variable", length) == 0)
1759             && (length >= 2)) {
1760         Tcl_TraceVar(interp, argv[2],
1761                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1762                 WaitVariableProc2, (ClientData) &done);
1763         done = 0;
1764         while (!done) {
1765             Tk_DoOneEvent(0);
1766         }
1767         Tcl_UntraceVar(interp, argv[2],
1768                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1769                 WaitVariableProc2, (ClientData) &done);
1770     } else {
1771         Tcl_AppendResult(interp, "bad option \"", argv[1],
1772                 "\": must be variable", (char *) NULL);
1773         return TCL_ERROR;
1774     }
1775
1776     /*
1777      * Clear out the interpreter's result, since it may have been set
1778      * by event handlers.
1779      */
1780
1781     Tcl_ResetResult(interp);
1782     return TCL_OK;
1783 }
1784
1785         /* ARGSUSED */
1786 static char *
1787 WaitVariableProc2(clientData, interp, name1, name2, flags)
1788     ClientData clientData;      /* Pointer to integer to set to 1. */
1789     Tcl_Interp *interp;         /* Interpreter containing variable. */
1790     char *name1;                /* Name of variable. */
1791     char *name2;                /* Second part of variable name. */
1792     int flags;                  /* Information about what happened. */
1793 {
1794     int *donePtr = (int *) clientData;
1795
1796     *donePtr = 1;
1797     return (char *) NULL;
1798 }
1799 \f
1800 /*
1801  *----------------------------------------------------------------------
1802  *
1803  * UpdateCmd2 --
1804  *
1805  *      This procedure is invoked to process the "update" Tcl command.
1806  *      See the user documentation for details on what it does.  This
1807  *      is a modified version of the command that doesn't deal with
1808  *      windows, suitable for use in stand-alone mode without the rest
1809  *      of Tk.  It's only used when Tk_EventInit has been called.
1810  *
1811  * Results:
1812  *      A standard Tcl result.
1813  *
1814  * Side effects:
1815  *      See the user documentation.
1816  *
1817  *----------------------------------------------------------------------
1818  */
1819
1820         /* ARGSUSED */
1821 static int
1822 UpdateCmd2(clientData, interp, argc, argv)
1823     ClientData clientData;      /* Not used. */
1824     Tcl_Interp *interp;         /* Current interpreter. */
1825     int argc;                   /* Number of arguments. */
1826     char **argv;                /* Argument strings. */
1827 {
1828     int flags;
1829
1830     if (argc == 1) {
1831         flags = TK_DONT_WAIT|TK_FILE_EVENTS|TK_TIMER_EVENTS|TK_IDLE_EVENTS;
1832     } else if (argc == 2) {
1833         if (strncmp(argv[1], "idletasks", strlen(argv[1])) != 0) {
1834             Tcl_AppendResult(interp, "bad argument \"", argv[1],
1835                     "\": must be idletasks", (char *) NULL);
1836             return TCL_ERROR;
1837         }
1838         flags = TK_IDLE_EVENTS;
1839     } else {
1840         Tcl_AppendResult(interp, "wrong # args: should be \"",
1841                 argv[0], " ?idletasks?\"", (char *) NULL);
1842         return TCL_ERROR;
1843     }
1844
1845     /*
1846      * Handle all pending events.
1847      */
1848
1849     while (Tk_DoOneEvent(flags) != 0) {
1850         /* Empty loop body */
1851     }
1852
1853     /*
1854      * Must clear the interpreter's result because event handlers could
1855      * have executed commands.
1856      */
1857
1858     Tcl_ResetResult(interp);
1859     return TCL_OK;
1860 }
1861 \f
1862 /*
1863  *----------------------------------------------------------------------
1864  *
1865  * Tk_EventInit --
1866  *
1867  *      This procedure is invoked from Tcl_AppInit if the Tk event stuff
1868  *      is being used by itself (without the rest of Tk) in an application.
1869  *      It creates the "after" and "fileevent" commands.
1870  *
1871  * Results:
1872  *      Always returns TCL_OK.
1873  *
1874  * Side effects:
1875  *      New commands get added to interp.
1876  *
1877  *----------------------------------------------------------------------
1878  */
1879
1880 int
1881 Tk_EventInit(interp)
1882     Tcl_Interp *interp;         /* Interpreter in which to set up
1883                                  * event-handling. */
1884 {
1885     Tcl_CreateCommand(interp, "after", Tk_AfterCmd, (ClientData) NULL,
1886             (void (*)()) NULL);
1887     Tcl_CreateCommand(interp, "fileevent", Tk_FileeventCmd, (ClientData) NULL,
1888             (void (*)()) NULL);
1889     Tcl_CreateCommand(interp, "tkwait", TkwaitCmd2, (ClientData) NULL,
1890             (void (*)()) NULL);
1891     Tcl_CreateCommand(interp, "update", UpdateCmd2, (ClientData) NULL,
1892             (void (*)()) NULL);
1893     Tcl_CallWhenDeleted(interp, TkEventCleanupProc, (ClientData) NULL);
1894     return TCL_OK;
1895 }
1896
1897 #endif /* TCL_MINOR_VERSION == 7 && TCL_MINOR_VERSION <= 4 */