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