Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtdocbook / tcl / tclNotify.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: tclNotify.c /main/2 1996/08/08 14:45:43 cde-hp $ */
24 /* 
25  * tclNotify.c --
26  *
27  *      This file provides the parts of the Tcl event notifier that are
28  *      the same on all platforms, plus a few other parts that are used
29  *      on more than one platform but not all.
30  *
31  *      The notifier is the lowest-level part of the event system.  It
32  *      manages an event queue that holds Tcl_Event structures and a list
33  *      of event sources that can add events to the queue.  It also
34  *      contains the procedure Tcl_DoOneEvent that invokes the event
35  *      sources and blocks to wait for new events, but Tcl_DoOneEvent
36  *      is in the platform-specific part of the notifier (in files like
37  *      tclUnixNotify.c).
38  *
39  * Copyright (c) 1995 Sun Microsystems, Inc.
40  *
41  * See the file "license.terms" for information on usage and redistribution
42  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
43  *
44  * SCCS: @(#) tclNotify.c 1.6 96/02/29 09:20:10
45  */
46
47 #include "tclInt.h"
48 #include "tclPort.h"
49
50 /*
51  * The following variable records the address of the first event
52  * source in the list of all event sources for the application.
53  * This variable is accessed by the notifier to traverse the list
54  * and invoke each event source.
55  */
56
57 TclEventSource *tclFirstEventSourcePtr = NULL;
58
59 /*
60  * The following variables indicate how long to block in the event
61  * notifier the next time it blocks (default:  block forever).
62  */
63
64 static int blockTimeSet = 0;    /* 0 means there is no maximum block
65                                  * time:  block forever. */
66 static Tcl_Time blockTime;      /* If blockTimeSet is 1, gives the
67                                  * maximum elapsed time for the next block. */
68
69 /*
70  * The following variables keep track of the event queue.  In addition
71  * to the first (next to be serviced) and last events in the queue,
72  * we keep track of a "marker" event.  This provides a simple priority
73  * mechanism whereby events can be inserted at the front of the queue
74  * but behind all other high-priority events already in the queue (this
75  * is used for things like a sequence of Enter and Leave events generated
76  * during a grab in Tk).
77  */
78
79 static Tcl_Event *firstEventPtr = NULL;
80                                 /* First pending event, or NULL if none. */
81 static Tcl_Event *lastEventPtr = NULL;
82                                 /* Last pending event, or NULL if none. */
83 static Tcl_Event *markerEventPtr = NULL;
84                                 /* Last high-priority event in queue, or
85                                  * NULL if none. */
86
87 /*
88  * Prototypes for procedures used only in this file:
89  */
90
91 static int              ServiceEvent _ANSI_ARGS_((int flags));
92 \f
93 /*
94  *----------------------------------------------------------------------
95  *
96  * Tcl_CreateEventSource --
97  *
98  *      This procedure is invoked to create a new source of events.
99  *      The source is identified by a procedure that gets invoked
100  *      during Tcl_DoOneEvent to check for events on that source
101  *      and queue them.
102  *
103  *
104  * Results:
105  *      None.
106  *
107  * Side effects:
108  *      SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent
109  *      runs out of things to do.  SetupProc will be invoked before
110  *      Tcl_DoOneEvent calls select or whatever else it uses to wait
111  *      for events.  SetupProc typically calls functions like Tcl_WatchFile
112  *      or Tcl_SetMaxBlockTime to indicate what to wait for.
113  *
114  *      CheckProc is called after select or whatever operation was actually
115  *      used to wait.  It figures out whether anything interesting actually
116  *      happened (e.g. by calling Tcl_FileReady), and then calls
117  *      Tcl_QueueEvent to queue any events that are ready.
118  *
119  *      Each of these procedures is passed two arguments, e.g.
120  *              (*checkProc)(ClientData clientData, int flags));
121  *      ClientData is the same as the clientData argument here, and flags
122  *      is a combination of things like TCL_FILE_EVENTS that indicates
123  *      what events are of interest:  setupProc and checkProc use flags
124  *      to figure out whether their events are relevant or not.
125  *
126  *----------------------------------------------------------------------
127  */
128
129 void
130 Tcl_CreateEventSource(setupProc, checkProc, clientData)
131     Tcl_EventSetupProc *setupProc;      /* Procedure to invoke to figure out
132                                          * what to wait for. */
133     Tcl_EventCheckProc *checkProc;      /* Procedure to call after waiting
134                                          * to see what happened. */
135     ClientData clientData;              /* One-word argument to pass to
136                                          * setupProc and checkProc. */
137 {
138     TclEventSource *sourcePtr;
139
140     sourcePtr = (TclEventSource *) ckalloc(sizeof(TclEventSource));
141     sourcePtr->setupProc = setupProc;
142     sourcePtr->checkProc = checkProc;
143     sourcePtr->clientData = clientData;
144     sourcePtr->nextPtr = tclFirstEventSourcePtr;
145     tclFirstEventSourcePtr = sourcePtr;
146 }
147 \f
148 /*
149  *----------------------------------------------------------------------
150  *
151  * Tcl_DeleteEventSource --
152  *
153  *      This procedure is invoked to delete the source of events
154  *      given by proc and clientData.
155  *
156  * Results:
157  *      None.
158  *
159  * Side effects:
160  *      The given event source is cancelled, so its procedure will
161  *      never again be called.  If no such source exists, nothing
162  *      happens.
163  *
164  *----------------------------------------------------------------------
165  */
166
167 void
168 Tcl_DeleteEventSource(setupProc, checkProc, clientData)
169     Tcl_EventSetupProc *setupProc;      /* Procedure to invoke to figure out
170                                          * what to wait for. */
171     Tcl_EventCheckProc *checkProc;      /* Procedure to call after waiting
172                                          * to see what happened. */
173     ClientData clientData;              /* One-word argument to pass to
174                                          * setupProc and checkProc. */
175 {
176     TclEventSource *sourcePtr, *prevPtr;
177
178     for (sourcePtr = tclFirstEventSourcePtr, prevPtr = NULL;
179             sourcePtr != NULL;
180             prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {
181         if ((sourcePtr->setupProc != setupProc)
182                 || (sourcePtr->checkProc != checkProc)
183                 || (sourcePtr->clientData != clientData)) {
184             continue;
185         }
186         if (prevPtr == NULL) {
187             tclFirstEventSourcePtr = sourcePtr->nextPtr;
188         } else {
189             prevPtr->nextPtr = sourcePtr->nextPtr;
190         }
191         ckfree((char *) sourcePtr);
192         return;
193     }
194 }
195 \f
196 /*
197  *----------------------------------------------------------------------
198  *
199  * Tcl_QueueEvent --
200  *
201  *      Insert an event into the Tk event queue at one of three
202  *      positions: the head, the tail, or before a floating marker.
203  *      Events inserted before the marker will be processed in
204  *      first-in-first-out order, but before any events inserted at
205  *      the tail of the queue.  Events inserted at the head of the
206  *      queue will be processed in last-in-first-out order.
207  *
208  * Results:
209  *      None.
210  *
211  * Side effects:
212  *      None.
213  *
214  *----------------------------------------------------------------------
215  */
216
217 void
218 Tcl_QueueEvent(evPtr, position)
219     Tcl_Event* evPtr;           /* Event to add to queue.  The storage
220                                  * space must have been allocated the caller
221                                  * with malloc (ckalloc), and it becomes
222                                  * the property of the event queue.  It
223                                  * will be freed after the event has been
224                                  * handled. */
225     Tcl_QueuePosition position; /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
226                                  * TCL_QUEUE_MARK. */
227 {
228     if (position == TCL_QUEUE_TAIL) {
229         /*
230          * Append the event on the end of the queue.
231          */
232
233         evPtr->nextPtr = NULL;
234         if (firstEventPtr == NULL) {
235             firstEventPtr = evPtr;
236         } else {
237             lastEventPtr->nextPtr = evPtr;
238         }
239         lastEventPtr = evPtr;
240     } else if (position == TCL_QUEUE_HEAD) {
241         /*
242          * Push the event on the head of the queue.
243          */
244
245         evPtr->nextPtr = firstEventPtr;
246         if (firstEventPtr == NULL) {
247             lastEventPtr = evPtr;
248         }           
249         firstEventPtr = evPtr;
250     } else if (position == TCL_QUEUE_MARK) {
251         /*
252          * Insert the event after the current marker event and advance
253          * the marker to the new event.
254          */
255
256         if (markerEventPtr == NULL) {
257             evPtr->nextPtr = firstEventPtr;
258             firstEventPtr = evPtr;
259         } else {
260             evPtr->nextPtr = markerEventPtr->nextPtr;
261             markerEventPtr->nextPtr = evPtr;
262         }
263         markerEventPtr = evPtr;
264         if (evPtr->nextPtr == NULL) {
265             lastEventPtr = evPtr;
266         }
267     }
268 }
269 \f
270 /*
271  *----------------------------------------------------------------------
272  *
273  * Tcl_DeleteEvents --
274  *
275  *      Calls a procedure for each event in the queue and deletes those
276  *      for which the procedure returns 1. Events for which the
277  *      procedure returns 0 are left in the queue.
278  *
279  * Results:
280  *      None.
281  *
282  * Side effects:
283  *      Potentially removes one or more events from the event queue.
284  *
285  *----------------------------------------------------------------------
286  */
287
288 void
289 Tcl_DeleteEvents(proc, clientData)
290     Tcl_EventDeleteProc *proc;          /* The procedure to call. */
291     ClientData clientData;              /* type-specific data. */
292 {
293     Tcl_Event *evPtr, *prevPtr, *hold;
294
295     for (prevPtr = (Tcl_Event *) NULL, evPtr = firstEventPtr;
296              evPtr != (Tcl_Event *) NULL;
297              ) {
298         if ((*proc) (evPtr, clientData) == 1) {
299             if (firstEventPtr == evPtr) {
300                 firstEventPtr = evPtr->nextPtr;
301                 if (evPtr->nextPtr == (Tcl_Event *) NULL) {
302                     lastEventPtr = (Tcl_Event *) NULL;
303                 }
304             } else {
305                 prevPtr->nextPtr = evPtr->nextPtr;
306             }
307             hold = evPtr;
308             evPtr = evPtr->nextPtr;
309             ckfree((char *) hold);
310         } else {
311             prevPtr = evPtr;
312             evPtr = evPtr->nextPtr;
313         }
314     }
315 }
316 \f
317 /*
318  *----------------------------------------------------------------------
319  *
320  * ServiceEvent --
321  *
322  *      Process one event from the event queue.  This routine is called
323  *      by the notifier whenever it wants Tk to process an event.  
324  *
325  * Results:
326  *      The return value is 1 if the procedure actually found an event
327  *      to process.  If no processing occurred, then 0 is returned.
328  *
329  * Side effects:
330  *      Invokes all of the event handlers for the highest priority
331  *      event in the event queue.  May collapse some events into a
332  *      single event or discard stale events.
333  *
334  *----------------------------------------------------------------------
335  */
336
337 static int
338 ServiceEvent(flags)
339     int flags;                  /* Indicates what events should be processed.
340                                  * May be any combination of TCL_WINDOW_EVENTS
341                                  * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other
342                                  * flags defined elsewhere.  Events not
343                                  * matching this will be skipped for processing
344                                  * later. */
345 {
346     Tcl_Event *evPtr, *prevPtr;
347     Tcl_EventProc *proc;
348
349     /*
350      * No event flags is equivalent to TCL_ALL_EVENTS.
351      */
352     
353     if ((flags & TCL_ALL_EVENTS) == 0) {
354         flags |= TCL_ALL_EVENTS;
355     }
356
357     /*
358      * Loop through all the events in the queue until we find one
359      * that can actually be handled.
360      */
361
362     for (evPtr = firstEventPtr; evPtr != NULL; evPtr = evPtr->nextPtr) {
363         /*
364          * Call the handler for the event.  If it actually handles the
365          * event then free the storage for the event.  There are two
366          * tricky things here, but stemming from the fact that the event
367          * code may be re-entered while servicing the event:
368          *
369          * 1. Set the "proc" field to NULL.  This is a signal to ourselves
370          *    that we shouldn't reexecute the handler if the event loop
371          *    is re-entered.
372          * 2. When freeing the event, must search the queue again from the
373          *    front to find it.  This is because the event queue could
374          *    change almost arbitrarily while handling the event, so we
375          *    can't depend on pointers found now still being valid when
376          *    the handler returns.
377          */
378
379         proc = evPtr->proc;
380         evPtr->proc = NULL;
381         if ((proc != NULL) && (*proc)(evPtr, flags)) {
382             if (firstEventPtr == evPtr) {
383                 firstEventPtr = evPtr->nextPtr;
384                 if (evPtr->nextPtr == NULL) {
385                     lastEventPtr = NULL;
386                 }
387             } else {
388                 for (prevPtr = firstEventPtr; prevPtr->nextPtr != evPtr;
389                         prevPtr = prevPtr->nextPtr) {
390                     /* Empty loop body. */
391                 }
392                 prevPtr->nextPtr = evPtr->nextPtr;
393                 if (evPtr->nextPtr == NULL) {
394                     lastEventPtr = prevPtr;
395                 }
396             }
397             if (markerEventPtr == evPtr) {
398                 markerEventPtr = NULL;
399             }
400             ckfree((char *) evPtr);
401             return 1;
402         } else {
403             /*
404              * The event wasn't actually handled, so we have to restore
405              * the proc field to allow the event to be attempted again.
406              */
407
408             evPtr->proc = proc;
409         }
410
411         /*
412          * The handler for this event asked to defer it.  Just go on to
413          * the next event.
414          */
415
416         continue;
417     }
418     return 0;
419 }
420 \f
421 /*
422  *----------------------------------------------------------------------
423  *
424  * Tcl_SetMaxBlockTime --
425  *
426  *      This procedure is invoked by event sources to tell the notifier
427  *      how long it may block the next time it blocks.  The timePtr
428  *      argument gives a maximum time;  the actual time may be less if
429  *      some other event source requested a smaller time.
430  *
431  * Results:
432  *      None.
433  *
434  * Side effects:
435  *      May reduce the length of the next sleep in the notifier.
436  *
437  *----------------------------------------------------------------------
438  */
439
440 void
441 Tcl_SetMaxBlockTime(timePtr)
442     Tcl_Time *timePtr;          /* Specifies a maximum elapsed time for
443                                  * the next blocking operation in the
444                                  * event notifier. */
445 {
446     if (!blockTimeSet || (timePtr->sec < blockTime.sec)
447             || ((timePtr->sec == blockTime.sec)
448             && (timePtr->usec < blockTime.usec))) {
449         blockTime = *timePtr;
450         blockTimeSet = 1;
451     }
452 }
453 \f
454 /*
455  *----------------------------------------------------------------------
456  *
457  * Tcl_DoOneEvent --
458  *
459  *      Process a single event of some sort.  If there's no work to
460  *      do, wait for an event to occur, then process it.
461  *
462  * Results:
463  *      The return value is 1 if the procedure actually found an event
464  *      to process.  If no processing occurred, then 0 is returned (this
465  *      can happen if the TCL_DONT_WAIT flag is set or if there are no
466  *      event handlers to wait for in the set specified by flags).
467  *
468  * Side effects:
469  *      May delay execution of process while waiting for an event,
470  *      unless TCL_DONT_WAIT is set in the flags argument.  Event
471  *      sources are invoked to check for and queue events.  Event
472  *      handlers may produce arbitrary side effects.
473  *
474  *----------------------------------------------------------------------
475  */
476
477 int
478 Tcl_DoOneEvent(flags)
479     int flags;                  /* Miscellaneous flag values:  may be any
480                                  * combination of TCL_DONT_WAIT,
481                                  * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,
482                                  * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or
483                                  * others defined by event sources. */
484 {
485     TclEventSource *sourcePtr;
486     Tcl_Time *timePtr;
487
488     /*
489      * No event flags is equivalent to TCL_ALL_EVENTS.
490      */
491     
492     if ((flags & TCL_ALL_EVENTS) == 0) {
493         flags |= TCL_ALL_EVENTS;
494     }
495
496     /*
497      * The core of this procedure is an infinite loop, even though
498      * we only service one event.  The reason for this is that we
499      * might think we have an event ready (e.g. the connection to
500      * the server becomes readable), but then we might discover that
501      * there's nothing interesting on that connection, so no event
502      * was serviced.  Or, the select operation could return prematurely
503      * due to a signal.  The easiest thing in both these cases is
504      * just to loop back and try again.
505      */
506
507     while (1) {
508
509         /*
510          * The first thing we do is to service any asynchronous event
511          * handlers.
512          */
513     
514         if (Tcl_AsyncReady()) {
515             (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
516             return 1;
517         }
518
519         /*
520          * If idle events are the only things to service, skip the
521          * main part of the loop and go directly to handle idle
522          * events (i.e. don't wait even if TCL_DONT_WAIT isn't set.
523          */
524
525         if (flags == TCL_IDLE_EVENTS) {
526             flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
527             goto idleEvents;
528         }
529
530         /*
531          * Ask Tk to service a queued event, if there are any.
532          */
533
534         if (ServiceEvent(flags)) {
535             return 1;
536         }
537
538         /*
539          * There are no events already queued.  Invoke all of the
540          * event sources to give them a chance to setup for the wait.
541          */
542
543         blockTimeSet = 0;
544         for (sourcePtr = tclFirstEventSourcePtr; sourcePtr != NULL;
545                 sourcePtr = sourcePtr->nextPtr) {
546             (*sourcePtr->setupProc)(sourcePtr->clientData, flags);
547         }
548         if ((flags & TCL_DONT_WAIT) ||
549                 ((flags & TCL_IDLE_EVENTS) && TclIdlePending())) {
550             /*
551              * Don't block:  there are idle events waiting, or we don't
552              * care about idle events anyway, or the caller asked us not
553              * to block.
554              */
555
556             blockTime.sec = 0;
557             blockTime.usec = 0;
558             timePtr = &blockTime;
559         } else if (blockTimeSet) {
560             timePtr = &blockTime;
561         } else {
562             timePtr = NULL;
563         }
564
565         /*
566          * Wait until an event occurs or the timer expires.
567          */
568
569         if (Tcl_WaitForEvent(timePtr) == TCL_ERROR) {
570             return 0;
571         }
572
573         /*
574          * Give each of the event sources a chance to queue events,
575          * then call ServiceEvent and give it another chance to
576          * service events.
577          */
578
579         for (sourcePtr = tclFirstEventSourcePtr; sourcePtr != NULL;
580                 sourcePtr = sourcePtr->nextPtr) {
581             (*sourcePtr->checkProc)(sourcePtr->clientData, flags);
582         }
583         if (ServiceEvent(flags)) {
584             return 1;
585         }
586
587         /*
588          * We've tried everything at this point, but nobody had anything
589          * to do.  Check for idle events.  If none, either quit or go back
590          * to the top and try again.
591          */
592
593         idleEvents:
594         if ((flags & TCL_IDLE_EVENTS) && TclServiceIdle()) {
595             return 1;
596         }
597         if (flags & TCL_DONT_WAIT) {
598             return 0;
599         }
600     }
601 }