OSDN Git Service

Initial revision
[pf3gnuchains/pf3gnuchains3x.git] / tk / generic / tkEvent.c
1 /* 
2  * tkEvent.c --
3  *
4  *      This file provides basic low-level facilities for managing
5  *      X events in Tk.
6  *
7  * Copyright (c) 1990-1994 The Regents of the University of California.
8  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9  * Copyright (c) 1998 by Scriptics Corporation.
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  *
14  * RCS: @(#) $Id$
15  */
16
17 #include "tkPort.h"
18 #include "tkInt.h"
19 #include <signal.h>
20
21 /*
22  * There's a potential problem if a handler is deleted while it's
23  * current (i.e. its procedure is executing), since Tk_HandleEvent
24  * will need to read the handler's "nextPtr" field when the procedure
25  * returns.  To handle this problem, structures of the type below
26  * indicate the next handler to be processed for any (recursively
27  * nested) dispatches in progress.  The nextHandler fields get
28  * updated if the handlers pointed to are deleted.  Tk_HandleEvent
29  * also needs to know if the entire window gets deleted;  the winPtr
30  * field is set to zero if that particular window gets deleted.
31  */
32
33 typedef struct InProgress {
34     XEvent *eventPtr;            /* Event currently being handled. */
35     TkWindow *winPtr;            /* Window for event.  Gets set to None if
36                                   * window is deleted while event is being
37                                   * handled. */
38     TkEventHandler *nextHandler; /* Next handler in search. */
39     struct InProgress *nextPtr;  /* Next higher nested search. */
40 } InProgress;
41
42 static InProgress *pendingPtr = NULL;
43                                 /* Topmost search in progress, or
44                                  * NULL if none. */
45
46 /*
47  * For each call to Tk_CreateGenericHandler, an instance of the following
48  * structure will be created.  All of the active handlers are linked into a
49  * list.
50  */
51
52 typedef struct GenericHandler {
53     Tk_GenericProc *proc;       /* Procedure to dispatch on all X events. */
54     ClientData clientData;      /* Client data to pass to procedure. */
55     int deleteFlag;             /* Flag to set when this handler is deleted. */
56     struct GenericHandler *nextPtr;
57                                 /* Next handler in list of all generic
58                                  * handlers, or NULL for end of list. */
59 } GenericHandler;
60
61 static GenericHandler *genericList = NULL;
62                                 /* First handler in the list, or NULL. */
63 static GenericHandler *lastGenericPtr = NULL;
64                                 /* Last handler in list. */
65
66 /*
67  * There's a potential problem if Tk_HandleEvent is entered recursively.
68  * A handler cannot be deleted physically until we have returned from
69  * calling it.  Otherwise, we're looking at unallocated memory in advancing to
70  * its `next' entry.  We deal with the problem by using the `delete flag' and
71  * deleting handlers only when it's known that there's no handler active.
72  *
73  * The following variable has a non-zero value when a handler is active.
74  */
75
76 static int genericHandlersActive = 0;
77
78 /*
79  * The following structure is used for queueing X-style events on the
80  * Tcl event queue.
81  */
82
83 typedef struct TkWindowEvent {
84     Tcl_Event header;           /* Standard information for all events. */
85     XEvent event;               /* The X event. */
86 } TkWindowEvent;
87
88 /*
89  * Array of event masks corresponding to each X event:
90  */
91
92 static unsigned long eventMasks[TK_LASTEVENT] = {
93     0,
94     0,
95     KeyPressMask,                       /* KeyPress */
96     KeyReleaseMask,                     /* KeyRelease */
97     ButtonPressMask,                    /* ButtonPress */
98     ButtonReleaseMask,                  /* ButtonRelease */
99     PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
100             |Button1MotionMask|Button2MotionMask|Button3MotionMask
101             |Button4MotionMask|Button5MotionMask,
102                                         /* MotionNotify */
103     EnterWindowMask,                    /* EnterNotify */
104     LeaveWindowMask,                    /* LeaveNotify */
105     FocusChangeMask,                    /* FocusIn */
106     FocusChangeMask,                    /* FocusOut */
107     KeymapStateMask,                    /* KeymapNotify */
108     ExposureMask,                       /* Expose */
109     ExposureMask,                       /* GraphicsExpose */
110     ExposureMask,                       /* NoExpose */
111     VisibilityChangeMask,               /* VisibilityNotify */
112     SubstructureNotifyMask,             /* CreateNotify */
113     StructureNotifyMask,                /* DestroyNotify */
114     StructureNotifyMask,                /* UnmapNotify */
115     StructureNotifyMask,                /* MapNotify */
116     SubstructureRedirectMask,           /* MapRequest */
117     StructureNotifyMask,                /* ReparentNotify */
118     StructureNotifyMask,                /* ConfigureNotify */
119     SubstructureRedirectMask,           /* ConfigureRequest */
120     StructureNotifyMask,                /* GravityNotify */
121     ResizeRedirectMask,                 /* ResizeRequest */
122     StructureNotifyMask,                /* CirculateNotify */
123     SubstructureRedirectMask,           /* CirculateRequest */
124     PropertyChangeMask,                 /* PropertyNotify */
125     0,                                  /* SelectionClear */
126     0,                                  /* SelectionRequest */
127     0,                                  /* SelectionNotify */
128     ColormapChangeMask,                 /* ColormapNotify */
129     0,                                  /* ClientMessage */
130     0,                                  /* Mapping Notify */
131     VirtualEventMask,                   /* VirtualEvents */
132     ActivateMask,                       /* ActivateNotify */
133     ActivateMask,                       /* DeactivateNotify */
134     MouseWheelMask                      /* MouseWheelEvent */
135 };
136
137 /*
138  * If someone has called Tk_RestrictEvents, the information below
139  * keeps track of it.
140  */
141
142 static Tk_RestrictProc *restrictProc;
143                                 /* Procedure to call.  NULL means no
144                                  * restrictProc is currently in effect. */
145 static ClientData restrictArg;  /* Argument to pass to restrictProc. */
146
147 /*
148  * Prototypes for procedures that are only referenced locally within
149  * this file.
150  */
151
152 static void             DelayedMotionProc _ANSI_ARGS_((ClientData clientData));
153 static int              WindowEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
154                             int flags));
155 \f
156 /*
157  *--------------------------------------------------------------
158  *
159  * Tk_CreateEventHandler --
160  *
161  *      Arrange for a given procedure to be invoked whenever
162  *      events from a given class occur in a given window.
163  *
164  * Results:
165  *      None.
166  *
167  * Side effects:
168  *      From now on, whenever an event of the type given by
169  *      mask occurs for token and is processed by Tk_HandleEvent,
170  *      proc will be called.  See the manual entry for details
171  *      of the calling sequence and return value for proc.
172  *
173  *--------------------------------------------------------------
174  */
175
176 void
177 Tk_CreateEventHandler(token, mask, proc, clientData)
178     Tk_Window token;            /* Token for window in which to
179                                  * create handler. */
180     unsigned long mask;         /* Events for which proc should
181                                  * be called. */
182     Tk_EventProc *proc;         /* Procedure to call for each
183                                  * selected event */
184     ClientData clientData;      /* Arbitrary data to pass to proc. */
185 {
186     register TkEventHandler *handlerPtr;
187     register TkWindow *winPtr = (TkWindow *) token;
188     int found;
189
190     /*
191      * Skim through the list of existing handlers to (a) compute the
192      * overall event mask for the window (so we can pass this new
193      * value to the X system) and (b) see if there's already a handler
194      * declared with the same callback and clientData (if so, just
195      * change the mask).  If no existing handler matches, then create
196      * a new handler.
197      */
198
199     found = 0;
200     if (winPtr->handlerList == NULL) {
201         handlerPtr = (TkEventHandler *) ckalloc(
202                 (unsigned) sizeof(TkEventHandler));
203         winPtr->handlerList = handlerPtr;
204         goto initHandler;
205     } else {
206         for (handlerPtr = winPtr->handlerList; ;
207                 handlerPtr = handlerPtr->nextPtr) {
208             if ((handlerPtr->proc == proc)
209                     && (handlerPtr->clientData == clientData)) {
210                 handlerPtr->mask = mask;
211                 found = 1;
212             }
213             if (handlerPtr->nextPtr == NULL) {
214                 break;
215             }
216         }
217     }
218
219     /*
220      * Create a new handler if no matching old handler was found.
221      */
222
223     if (!found) {
224         handlerPtr->nextPtr = (TkEventHandler *)
225                 ckalloc(sizeof(TkEventHandler));
226         handlerPtr = handlerPtr->nextPtr;
227         initHandler:
228         handlerPtr->mask = mask;
229         handlerPtr->proc = proc;
230         handlerPtr->clientData = clientData;
231         handlerPtr->nextPtr = NULL;
232     }
233
234     /*
235      * No need to call XSelectInput:  Tk always selects on all events
236      * for all windows (needed to support bindings on classes and "all").
237      */
238 }
239 \f
240 /*
241  *--------------------------------------------------------------
242  *
243  * Tk_DeleteEventHandler --
244  *
245  *      Delete a previously-created handler.
246  *
247  * Results:
248  *      None.
249  *
250  * Side effects:
251  *      If there existed a handler as described by the
252  *      parameters, the handler is deleted so that proc
253  *      will not be invoked again.
254  *
255  *--------------------------------------------------------------
256  */
257
258 void
259 Tk_DeleteEventHandler(token, mask, proc, clientData)
260     Tk_Window token;            /* Same as corresponding arguments passed */
261     unsigned long mask;         /* previously to Tk_CreateEventHandler. */
262     Tk_EventProc *proc;
263     ClientData clientData;
264 {
265     register TkEventHandler *handlerPtr;
266     register InProgress *ipPtr;
267     TkEventHandler *prevPtr;
268     register TkWindow *winPtr = (TkWindow *) token;
269
270     /*
271      * Find the event handler to be deleted, or return
272      * immediately if it doesn't exist.
273      */
274
275     for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ;
276             prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) {
277         if (handlerPtr == NULL) {
278             return;
279         }
280         if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc)
281                 && (handlerPtr->clientData == clientData)) {
282             break;
283         }
284     }
285
286     /*
287      * If Tk_HandleEvent is about to process this handler, tell it to
288      * process the next one instead.
289      */
290
291     for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
292         if (ipPtr->nextHandler == handlerPtr) {
293             ipPtr->nextHandler = handlerPtr->nextPtr;
294         }
295     }
296
297     /*
298      * Free resources associated with the handler.
299      */
300
301     if (prevPtr == NULL) {
302         winPtr->handlerList = handlerPtr->nextPtr;
303     } else {
304         prevPtr->nextPtr = handlerPtr->nextPtr;
305     }
306     ckfree((char *) handlerPtr);
307
308
309     /*
310      * No need to call XSelectInput:  Tk always selects on all events
311      * for all windows (needed to support bindings on classes and "all").
312      */
313 }
314 \f
315 /*--------------------------------------------------------------
316  *
317  * Tk_CreateGenericHandler --
318  *
319  *      Register a procedure to be called on each X event, regardless
320  *      of display or window.  Generic handlers are useful for capturing
321  *      events that aren't associated with windows, or events for windows
322  *      not managed by Tk.
323  *
324  * Results:
325  *      None.
326  *
327  * Side Effects:
328  *      From now on, whenever an X event is given to Tk_HandleEvent,
329  *      invoke proc, giving it clientData and the event as arguments.
330  *
331  *--------------------------------------------------------------
332  */
333
334 void
335 Tk_CreateGenericHandler(proc, clientData)
336      Tk_GenericProc *proc;      /* Procedure to call on every event. */
337      ClientData clientData;     /* One-word value to pass to proc. */
338 {
339     GenericHandler *handlerPtr;
340     
341     handlerPtr = (GenericHandler *) ckalloc (sizeof (GenericHandler));
342     
343     handlerPtr->proc = proc;
344     handlerPtr->clientData = clientData;
345     handlerPtr->deleteFlag = 0;
346     handlerPtr->nextPtr = NULL;
347     if (genericList == NULL) {
348         genericList = handlerPtr;
349     } else {
350         lastGenericPtr->nextPtr = handlerPtr;
351     }
352     lastGenericPtr = handlerPtr;
353 }
354 \f
355 /*
356  *--------------------------------------------------------------
357  *
358  * Tk_DeleteGenericHandler --
359  *
360  *      Delete a previously-created generic handler.
361  *
362  * Results:
363  *      None.
364  *
365  * Side Effects:
366  *      If there existed a handler as described by the parameters,
367  *      that handler is logically deleted so that proc will not be
368  *      invoked again.  The physical deletion happens in the event
369  *      loop in Tk_HandleEvent.
370  *
371  *--------------------------------------------------------------
372  */
373
374 void
375 Tk_DeleteGenericHandler(proc, clientData)
376      Tk_GenericProc *proc;
377      ClientData clientData;
378 {
379     GenericHandler * handler;
380     
381     for (handler = genericList; handler; handler = handler->nextPtr) {
382         if ((handler->proc == proc) && (handler->clientData == clientData)) {
383             handler->deleteFlag = 1;
384         }
385     }
386 }
387 \f
388 /*
389  *--------------------------------------------------------------
390  *
391  * Tk_HandleEvent --
392  *
393  *      Given an event, invoke all the handlers that have
394  *      been registered for the event.
395  *
396  * Results:
397  *      None.
398  *
399  * Side effects:
400  *      Depends on the handlers.
401  *
402  *--------------------------------------------------------------
403  */
404
405 void
406 Tk_HandleEvent(eventPtr)
407     XEvent *eventPtr;           /* Event to dispatch. */
408 {
409     register TkEventHandler *handlerPtr;
410     register GenericHandler *genericPtr;
411     register GenericHandler *genPrevPtr;
412     TkWindow *winPtr;
413     unsigned long mask;
414     InProgress ip;
415     Window handlerWindow;
416     TkDisplay *dispPtr;
417     Tcl_Interp *interp = (Tcl_Interp *) NULL;
418
419     /* 
420      * Next, invoke all the generic event handlers (those that are
421      * invoked for all events).  If a generic event handler reports that
422      * an event is fully processed, go no further.
423      */
424
425     for (genPrevPtr = NULL, genericPtr = genericList;  genericPtr != NULL; ) {
426         if (genericPtr->deleteFlag) {
427             if (!genericHandlersActive) {
428                 GenericHandler *tmpPtr;
429
430                 /*
431                  * This handler needs to be deleted and there are no
432                  * calls pending through the handler, so now is a safe
433                  * time to delete it.
434                  */
435
436                 tmpPtr = genericPtr->nextPtr;
437                 if (genPrevPtr == NULL) {
438                     genericList = tmpPtr;
439                 } else {
440                     genPrevPtr->nextPtr = tmpPtr;
441                 }
442                 if (tmpPtr == NULL) {
443                     lastGenericPtr = genPrevPtr;
444                 }
445                 (void) ckfree((char *) genericPtr);
446                 genericPtr = tmpPtr;
447                 continue;
448             }
449         } else {
450             int done;
451
452             genericHandlersActive++;
453             done = (*genericPtr->proc)(genericPtr->clientData, eventPtr);
454             genericHandlersActive--;
455             if (done) {
456                 return;
457             }
458         }
459         genPrevPtr = genericPtr;
460         genericPtr = genPrevPtr->nextPtr;
461     }
462
463     /*
464      * If the event is a MappingNotify event, find its display and
465      * refresh the keyboard mapping information for the display.
466      * After that there's nothing else to do with the event, so just
467      * quit.
468      */
469
470     if (eventPtr->type == MappingNotify) {
471         dispPtr = TkGetDisplay(eventPtr->xmapping.display);
472         if (dispPtr != NULL) {
473             XRefreshKeyboardMapping(&eventPtr->xmapping);
474             dispPtr->bindInfoStale = 1;
475         }
476         return;
477     }
478
479     /*
480      * Events selected by StructureNotify require special handling.
481      * They look the same as those selected by SubstructureNotify.
482      * The only difference is whether the "event" and "window" fields
483      * are the same.  Compare the two fields and convert StructureNotify
484      * to SubstructureNotify if necessary.
485      */
486
487     handlerWindow = eventPtr->xany.window;
488     mask = eventMasks[eventPtr->xany.type];
489     if (mask == StructureNotifyMask) {
490         if (eventPtr->xmap.event != eventPtr->xmap.window) {
491             mask = SubstructureNotifyMask;
492             handlerWindow = eventPtr->xmap.event;
493         }
494     }
495     winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow);
496     if (winPtr == NULL) {
497
498         /*
499          * There isn't a TkWindow structure for this window.
500          * However, if the event is a PropertyNotify event then call
501          * the selection manager (it deals beneath-the-table with
502          * certain properties).
503          */
504
505         if (eventPtr->type == PropertyNotify) {
506             TkSelPropProc(eventPtr);
507         }
508         return;
509     }
510
511     /*
512      * Once a window has started getting deleted, don't process any more
513      * events for it except for the DestroyNotify event.  This check is
514      * needed because a DestroyNotify handler could re-invoke the event
515      * loop, causing other pending events to be handled for the window
516      * (the window doesn't get totally expunged from our tables until
517      * after the DestroyNotify event has been completely handled).
518      */
519
520     if ((winPtr->flags & TK_ALREADY_DEAD)
521             && (eventPtr->type != DestroyNotify)) {
522         return;
523     }
524
525     if (winPtr->mainPtr != NULL) {
526
527         /*
528          * Protect interpreter for this window from possible deletion
529          * while we are dealing with the event for this window. Thus,
530          * widget writers do not have to worry about protecting the
531          * interpreter in their own code.
532          */
533         
534         interp = winPtr->mainPtr->interp;
535         Tcl_Preserve((ClientData) interp);
536         
537         /*
538          * Call focus-related code to look at FocusIn, FocusOut, Enter,
539          * and Leave events;  depending on its return value, ignore the
540          * event.
541          */
542     
543         if ((mask & (FocusChangeMask|EnterWindowMask|LeaveWindowMask))
544                 && !TkFocusFilterEvent(winPtr, eventPtr)) {
545             Tcl_Release((ClientData) interp);
546             return;
547         }
548     
549         /*
550          * Redirect KeyPress and KeyRelease events to the focus window,
551          * or ignore them entirely if there is no focus window.  We also
552          * route the MouseWheel event to the focus window.  The MouseWheel
553          * event is an extension to the X event set.  Currently, it is only
554          * available on the Windows version of Tk.
555          */
556     
557         if (mask & (KeyPressMask|KeyReleaseMask|MouseWheelMask)) {
558             winPtr->dispPtr->lastEventTime = eventPtr->xkey.time;
559             winPtr = TkFocusKeyEvent(winPtr, eventPtr);
560             if (winPtr == NULL) {
561                 Tcl_Release((ClientData) interp);
562                 return;
563             }
564         }
565     
566         /*
567          * Call a grab-related procedure to do special processing on
568          * pointer events.
569          */
570     
571         if (mask & (ButtonPressMask|ButtonReleaseMask|PointerMotionMask
572                 |EnterWindowMask|LeaveWindowMask)) {
573             if (mask & (ButtonPressMask|ButtonReleaseMask)) {
574                 winPtr->dispPtr->lastEventTime = eventPtr->xbutton.time;
575             } else if (mask & PointerMotionMask) {
576                 winPtr->dispPtr->lastEventTime = eventPtr->xmotion.time;
577             } else {
578                 winPtr->dispPtr->lastEventTime = eventPtr->xcrossing.time;
579             }
580             if (TkPointerEvent(eventPtr, winPtr) == 0) {
581                 goto done;
582             }
583         }
584     }
585
586 #ifdef TK_USE_INPUT_METHODS
587     /*
588      * Pass the event to the input method(s), if there are any, and
589      * discard the event if the input method(s) insist.  Create the
590      * input context for the window if it hasn't already been done
591      * (XFilterEvent needs this context).
592      */
593
594     if (!(winPtr->flags & TK_CHECKED_IC)) {
595         if (winPtr->dispPtr->inputMethod != NULL) {
596             winPtr->inputContext = XCreateIC(
597                     winPtr->dispPtr->inputMethod, XNInputStyle,
598                     XIMPreeditNothing|XIMStatusNothing,
599                     XNClientWindow, winPtr->window,
600                     XNFocusWindow, winPtr->window, NULL);
601         }
602         winPtr->flags |= TK_CHECKED_IC;
603     }
604     if (XFilterEvent(eventPtr, None)) {
605         goto done;
606     }
607 #endif /* TK_USE_INPUT_METHODS */
608
609     /*
610      * For events where it hasn't already been done, update the current
611      * time in the display.
612      */
613
614     if (eventPtr->type == PropertyNotify) {
615         winPtr->dispPtr->lastEventTime = eventPtr->xproperty.time;
616     }
617
618     /*
619      * There's a potential interaction here with Tk_DeleteEventHandler.
620      * Read the documentation for pendingPtr.
621      */
622
623     ip.eventPtr = eventPtr;
624     ip.winPtr = winPtr;
625     ip.nextHandler = NULL;
626     ip.nextPtr = pendingPtr;
627     pendingPtr = &ip;
628     if (mask == 0) {
629         if ((eventPtr->type == SelectionClear)
630                 || (eventPtr->type == SelectionRequest)
631                 || (eventPtr->type == SelectionNotify)) {
632             TkSelEventProc((Tk_Window) winPtr, eventPtr);
633         } else if ((eventPtr->type == ClientMessage)
634                 && (eventPtr->xclient.message_type ==
635                     Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"))) {
636             TkWmProtocolEventProc(winPtr, eventPtr);
637         }
638     } else {
639         for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {
640             if ((handlerPtr->mask & mask) != 0) {
641                 ip.nextHandler = handlerPtr->nextPtr;
642                 (*(handlerPtr->proc))(handlerPtr->clientData, eventPtr);
643                 handlerPtr = ip.nextHandler;
644             } else {
645                 handlerPtr = handlerPtr->nextPtr;
646             }
647         }
648
649         /*
650          * Pass the event to the "bind" command mechanism.  But, don't
651          * do this for SubstructureNotify events.  The "bind" command
652          * doesn't support them anyway, and it's easier to filter out
653          * these events here than in the lower-level procedures.
654          */
655
656         if ((ip.winPtr != None) && (mask != SubstructureNotifyMask)) {
657             TkBindEventProc(winPtr, eventPtr);
658         }
659     }
660     pendingPtr = ip.nextPtr;
661 done:
662
663     /*
664      * Release the interpreter for this window so that it can be potentially
665      * deleted if requested.
666      */
667     
668     if (interp != (Tcl_Interp *) NULL) {
669         Tcl_Release((ClientData) interp);
670     }
671 }
672 \f
673 /*
674  *--------------------------------------------------------------
675  *
676  * TkEventDeadWindow --
677  *
678  *      This procedure is invoked when it is determined that
679  *      a window is dead.  It cleans up event-related information
680  *      about the window.
681  *
682  * Results:
683  *      None.
684  *
685  * Side effects:
686  *      Various things get cleaned up and recycled.
687  *
688  *--------------------------------------------------------------
689  */
690
691 void
692 TkEventDeadWindow(winPtr)
693     TkWindow *winPtr;           /* Information about the window
694                                  * that is being deleted. */
695 {
696     register TkEventHandler *handlerPtr;
697     register InProgress *ipPtr;
698
699     /*
700      * While deleting all the handlers, be careful to check for
701      * Tk_HandleEvent being about to process one of the deleted
702      * handlers.  If it is, tell it to quit (all of the handlers
703      * are being deleted).
704      */
705
706     while (winPtr->handlerList != NULL) {
707         handlerPtr = winPtr->handlerList;
708         winPtr->handlerList = handlerPtr->nextPtr;
709         for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
710             if (ipPtr->nextHandler == handlerPtr) {
711                 ipPtr->nextHandler = NULL;
712             }
713             if (ipPtr->winPtr == winPtr) {
714                 ipPtr->winPtr = None;
715             }
716         }
717         ckfree((char *) handlerPtr);
718     }
719 }
720 \f
721 /*
722  *----------------------------------------------------------------------
723  *
724  * TkCurrentTime --
725  *
726  *      Try to deduce the current time.  "Current time" means the time
727  *      of the event that led to the current code being executed, which
728  *      means the time in the most recently-nested invocation of
729  *      Tk_HandleEvent.
730  *
731  * Results:
732  *      The return value is the time from the current event, or
733  *      CurrentTime if there is no current event or if the current
734  *      event contains no time.
735  *
736  * Side effects:
737  *      None.
738  *
739  *----------------------------------------------------------------------
740  */
741
742 Time
743 TkCurrentTime(dispPtr)
744     TkDisplay *dispPtr;         /* Display for which the time is desired. */
745 {
746     register XEvent *eventPtr;
747
748     if (pendingPtr == NULL) {
749         return dispPtr->lastEventTime;
750     }
751     eventPtr = pendingPtr->eventPtr;
752     switch (eventPtr->type) {
753         case ButtonPress:
754         case ButtonRelease:
755             return eventPtr->xbutton.time;
756         case KeyPress:
757         case KeyRelease:
758             return eventPtr->xkey.time;
759         case MotionNotify:
760             return eventPtr->xmotion.time;
761         case EnterNotify:
762         case LeaveNotify:
763             return eventPtr->xcrossing.time;
764         case PropertyNotify:
765             return eventPtr->xproperty.time;
766     }
767     return dispPtr->lastEventTime;
768 }
769 \f
770 /*
771  *----------------------------------------------------------------------
772  *
773  * Tk_RestrictEvents --
774  *
775  *      This procedure is used to globally restrict the set of events
776  *      that will be dispatched.  The restriction is done by filtering
777  *      all incoming X events through a procedure that determines
778  *      whether they are to be processed immediately, deferred, or
779  *      discarded.
780  *
781  * Results:
782  *      The return value is the previous restriction procedure in effect,
783  *      if there was one, or NULL if there wasn't.
784  *
785  * Side effects:
786  *      From now on, proc will be called to determine whether to process,
787  *      defer or discard each incoming X event.
788  *
789  *----------------------------------------------------------------------
790  */
791
792 Tk_RestrictProc *
793 Tk_RestrictEvents(proc, arg, prevArgPtr)
794     Tk_RestrictProc *proc;      /* Procedure to call for each incoming
795                                  * event. */
796     ClientData arg;             /* Arbitrary argument to pass to proc. */
797     ClientData *prevArgPtr;     /* Place to store information about previous
798                                  * argument. */
799 {
800     Tk_RestrictProc *prev;
801
802     prev = restrictProc;
803     *prevArgPtr = restrictArg;
804     restrictProc = proc;
805     restrictArg = arg;
806     return prev;
807 }
808 \f
809 /*
810  *----------------------------------------------------------------------
811  *
812  * Tk_QueueWindowEvent --
813  *
814  *      Given an X-style window event, this procedure adds it to the
815  *      Tcl event queue at the given position.  This procedure also
816  *      performs mouse motion event collapsing if possible.
817  *
818  * Results:
819  *      None.
820  *
821  * Side effects:
822  *      Adds stuff to the event queue, which will eventually be
823  *      processed.
824  *
825  *----------------------------------------------------------------------
826  */
827
828 void
829 Tk_QueueWindowEvent(eventPtr, position)
830     XEvent *eventPtr;                   /* Event to add to queue.  This
831                                          * procedures copies it before adding
832                                          * it to the queue. */
833     Tcl_QueuePosition position;         /* Where to put it on the queue:
834                                          * TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
835                                          * or TCL_QUEUE_MARK. */
836 {
837     TkWindowEvent *wevPtr;
838     TkDisplay *dispPtr;
839
840     /*
841      * Find our display structure for the event's display.
842      */
843
844     for (dispPtr = tkDisplayList; ; dispPtr = dispPtr->nextPtr) {
845         if (dispPtr == NULL) {
846             return;
847         }
848         if (dispPtr->display == eventPtr->xany.display) {
849             break;
850         }
851     }
852
853     if ((dispPtr->delayedMotionPtr != NULL) && (position == TCL_QUEUE_TAIL)) {
854         if ((eventPtr->type == MotionNotify) && (eventPtr->xmotion.window
855                 == dispPtr->delayedMotionPtr->event.xmotion.window)) {
856             /*
857              * The new event is a motion event in the same window as the
858              * saved motion event.  Just replace the saved event with the
859              * new one.
860              */
861
862             dispPtr->delayedMotionPtr->event = *eventPtr;
863             return;
864         } else if ((eventPtr->type != GraphicsExpose)
865                 && (eventPtr->type != NoExpose)
866                 && (eventPtr->type != Expose)) {
867             /*
868              * The new event may conflict with the saved motion event.  Queue
869              * the saved motion event now so that it will be processed before
870              * the new event.
871              */
872
873             Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, position);
874             dispPtr->delayedMotionPtr = NULL;
875             Tcl_CancelIdleCall(DelayedMotionProc, (ClientData) dispPtr);
876         }
877     }
878
879     wevPtr = (TkWindowEvent *) ckalloc(sizeof(TkWindowEvent));
880     wevPtr->header.proc = WindowEventProc;
881     wevPtr->event = *eventPtr;
882     if ((eventPtr->type == MotionNotify) && (position == TCL_QUEUE_TAIL)) {
883         /*
884          * The new event is a motion event so don't queue it immediately;
885          * save it around in case another motion event arrives that it can
886          * be collapsed with.
887          */
888
889         if (dispPtr->delayedMotionPtr != NULL) {
890             panic("Tk_QueueWindowEvent found unexpected delayed motion event");
891         }
892         dispPtr->delayedMotionPtr = wevPtr;
893         Tcl_DoWhenIdle(DelayedMotionProc, (ClientData) dispPtr);
894     } else {
895         Tcl_QueueEvent(&wevPtr->header, position);
896     }
897 }
898 \f
899 /*
900  *---------------------------------------------------------------------------
901  *
902  * TkQueueEventForAllChildren --
903  *
904  *      Given an XEvent, recursively queue the event for this window and
905  *      all non-toplevel children of the given window.  
906  *
907  * Results:
908  *      None.
909  *
910  * Side effects:
911  *      Events queued.
912  *
913  *---------------------------------------------------------------------------
914  */
915
916 void
917 TkQueueEventForAllChildren(winPtr, eventPtr)
918     TkWindow *winPtr;       /* Window to which event is sent. */
919     XEvent *eventPtr;       /* The event to be sent. */
920 {
921     TkWindow *childPtr;
922
923     eventPtr->xany.window = winPtr->window;
924     Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);
925     
926     childPtr = winPtr->childList;
927     while (childPtr != NULL) {
928         if (!Tk_IsTopLevel(childPtr)) {
929             TkQueueEventForAllChildren(childPtr, eventPtr);
930         }
931         childPtr = childPtr->nextPtr;
932     }
933 }
934 \f
935 /*
936  *----------------------------------------------------------------------
937  *
938  * WindowEventProc --
939  *
940  *      This procedure is called by Tcl_DoOneEvent when a window event
941  *      reaches the front of the event queue.  This procedure is responsible
942  *      for actually handling the event.
943  *
944  * Results:
945  *      Returns 1 if the event was handled, meaning it should be removed
946  *      from the queue.  Returns 0 if the event was not handled, meaning
947  *      it should stay on the queue.  The event isn't handled if the
948  *      TCL_WINDOW_EVENTS bit isn't set in flags, if a restrict proc
949  *      prevents the event from being handled.
950  *
951  * Side effects:
952  *      Whatever the event handlers for the event do.
953  *
954  *----------------------------------------------------------------------
955  */
956
957 static int
958 WindowEventProc(evPtr, flags)
959     Tcl_Event *evPtr;           /* Event to service. */
960     int flags;                  /* Flags that indicate what events to
961                                  * handle, such as TCL_WINDOW_EVENTS. */
962 {
963     TkWindowEvent *wevPtr = (TkWindowEvent *) evPtr;
964     Tk_RestrictAction result;
965
966     if (!(flags & TCL_WINDOW_EVENTS)) {
967         return 0;
968     }
969     if (restrictProc != NULL) {
970         result = (*restrictProc)(restrictArg, &wevPtr->event);
971         if (result != TK_PROCESS_EVENT) {
972             if (result == TK_DEFER_EVENT) {
973                 return 0;
974             } else {
975                 /*
976                  * TK_DELETE_EVENT: return and say we processed the event,
977                  * even though we didn't do anything at all.
978                  */
979                 return 1;
980             }
981         }
982     }
983     Tk_HandleEvent(&wevPtr->event);
984     return 1;
985 }
986 \f
987 /*
988  *----------------------------------------------------------------------
989  *
990  * DelayedMotionProc --
991  *
992  *      This procedure is invoked as an idle handler when a mouse motion
993  *      event has been delayed.  It queues the delayed event so that it
994  *      will finally be serviced.
995  *
996  * Results:
997  *      None.
998  *
999  * Side effects:
1000  *      The delayed mouse motion event gets added to the Tcl event
1001  *      queue for servicing.
1002  *
1003  *----------------------------------------------------------------------
1004  */
1005
1006 static void
1007 DelayedMotionProc(clientData)
1008     ClientData clientData;      /* Pointer to display containing a delayed
1009                                  * motion event to be serviced. */
1010 {
1011     TkDisplay *dispPtr = (TkDisplay *) clientData;
1012
1013     if (dispPtr->delayedMotionPtr == NULL) {
1014         panic("DelayedMotionProc found no delayed mouse motion event");
1015     }
1016     Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, TCL_QUEUE_TAIL);
1017     dispPtr->delayedMotionPtr = NULL;
1018 }
1019 \f
1020 /*
1021  *--------------------------------------------------------------
1022  *
1023  * Tk_MainLoop --
1024  *
1025  *      Call Tcl_DoOneEvent over and over again in an infinite
1026  *      loop as long as there exist any main windows.
1027  *
1028  * Results:
1029  *      None.
1030  *
1031  * Side effects:
1032  *      Arbitrary;  depends on handlers for events.
1033  *
1034  *--------------------------------------------------------------
1035  */
1036
1037 void
1038 Tk_MainLoop()
1039 {
1040     while (Tk_GetNumMainWindows() > 0) {
1041         Tcl_DoOneEvent(0);
1042     }
1043 }