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-2000 Ajuba Solutions.
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: tkEvent.c,v 1.17.2.8 2006/01/20 18:42:04 jenglish Exp $
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
33typedef 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/*
43 * For each call to Tk_CreateGenericHandler, an instance of the following
44 * structure will be created.  All of the active handlers are linked into a
45 * list.
46 */
47
48typedef struct GenericHandler {
49    Tk_GenericProc *proc;	/* Procedure to dispatch on all X events. */
50    ClientData clientData;	/* Client data to pass to procedure. */
51    int deleteFlag;		/* Flag to set when this handler is deleted. */
52    struct GenericHandler *nextPtr;
53				/* Next handler in list of all generic
54				 * handlers, or NULL for end of list. */
55} GenericHandler;
56
57/*
58 * There's a potential problem if Tk_HandleEvent is entered recursively.
59 * A handler cannot be deleted physically until we have returned from
60 * calling it.  Otherwise, we're looking at unallocated memory in advancing to
61 * its `next' entry.  We deal with the problem by using the `delete flag' and
62 * deleting handlers only when it's known that there's no handler active.
63 *
64 */
65
66/*
67 * The following structure is used for queueing X-style events on the
68 * Tcl event queue.
69 */
70
71typedef struct TkWindowEvent {
72    Tcl_Event header;		/* Standard information for all events. */
73    XEvent event;		/* The X event. */
74} TkWindowEvent;
75
76/*
77 * Array of event masks corresponding to each X event:
78 */
79
80static unsigned long eventMasks[TK_LASTEVENT] = {
81    0,
82    0,
83    KeyPressMask,			/* KeyPress */
84    KeyReleaseMask,			/* KeyRelease */
85    ButtonPressMask,			/* ButtonPress */
86    ButtonReleaseMask,			/* ButtonRelease */
87    PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
88	    |Button1MotionMask|Button2MotionMask|Button3MotionMask
89	    |Button4MotionMask|Button5MotionMask,
90					/* MotionNotify */
91    EnterWindowMask,			/* EnterNotify */
92    LeaveWindowMask,			/* LeaveNotify */
93    FocusChangeMask,			/* FocusIn */
94    FocusChangeMask,			/* FocusOut */
95    KeymapStateMask,			/* KeymapNotify */
96    ExposureMask,			/* Expose */
97    ExposureMask,			/* GraphicsExpose */
98    ExposureMask,			/* NoExpose */
99    VisibilityChangeMask,		/* VisibilityNotify */
100    SubstructureNotifyMask,		/* CreateNotify */
101    StructureNotifyMask,		/* DestroyNotify */
102    StructureNotifyMask,		/* UnmapNotify */
103    StructureNotifyMask,		/* MapNotify */
104    SubstructureRedirectMask,		/* MapRequest */
105    StructureNotifyMask,		/* ReparentNotify */
106    StructureNotifyMask,		/* ConfigureNotify */
107    SubstructureRedirectMask,		/* ConfigureRequest */
108    StructureNotifyMask,		/* GravityNotify */
109    ResizeRedirectMask,			/* ResizeRequest */
110    StructureNotifyMask,		/* CirculateNotify */
111    SubstructureRedirectMask,		/* CirculateRequest */
112    PropertyChangeMask,			/* PropertyNotify */
113    0,					/* SelectionClear */
114    0,					/* SelectionRequest */
115    0,					/* SelectionNotify */
116    ColormapChangeMask,			/* ColormapNotify */
117    0,					/* ClientMessage */
118    0,					/* Mapping Notify */
119    VirtualEventMask,			/* VirtualEvents */
120    ActivateMask,			/* ActivateNotify */
121    ActivateMask,			/* DeactivateNotify */
122    MouseWheelMask			/* MouseWheelEvent */
123};
124
125
126/*
127 * The structure below is used to store Data for the Event module that
128 * must be kept thread-local.  The "dataKey" is used to fetch the
129 * thread-specific storage for the current thread.
130 */
131
132typedef struct ThreadSpecificData {
133    int handlersActive;		/* The following variable has a non-zero
134				 * value when a handler is active. */
135    InProgress *pendingPtr;	/* Topmost search in progress, or
136				 * NULL if none. */
137
138    GenericHandler *genericList; /* First handler in the list, or NULL. */
139    GenericHandler *lastGenericPtr;	/* Last handler in list. */
140
141    GenericHandler *cmList; /* First handler in the list, or NULL. */
142    GenericHandler *lastCmPtr;	/* Last handler in list. */
143
144    /*
145     * If someone has called Tk_RestrictEvents, the information below
146     * keeps track of it.
147     */
148
149    Tk_RestrictProc *restrictProc;
150				/* Procedure to call.  NULL means no
151				 * restrictProc is currently in effect. */
152    ClientData restrictArg;	/* Argument to pass to restrictProc. */
153} ThreadSpecificData;
154static Tcl_ThreadDataKey dataKey;
155
156/*
157 * Prototypes for procedures that are only referenced locally within
158 * this file.
159 */
160
161static void		DelayedMotionProc _ANSI_ARGS_((ClientData clientData));
162static int		WindowEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
163			    int flags));
164static int		TkXErrorHandler _ANSI_ARGS_((ClientData clientData,
165			    XErrorEvent *errEventPtr));
166
167
168/*
169 *--------------------------------------------------------------
170 *
171 * Tk_CreateEventHandler --
172 *
173 *	Arrange for a given procedure to be invoked whenever
174 *	events from a given class occur in a given window.
175 *
176 * Results:
177 *	None.
178 *
179 * Side effects:
180 *	From now on, whenever an event of the type given by
181 *	mask occurs for token and is processed by Tk_HandleEvent,
182 *	proc will be called.  See the manual entry for details
183 *	of the calling sequence and return value for proc.
184 *
185 *--------------------------------------------------------------
186 */
187
188void
189Tk_CreateEventHandler(token, mask, proc, clientData)
190    Tk_Window token;		/* Token for window in which to
191				 * create handler. */
192    unsigned long mask;		/* Events for which proc should
193				 * be called. */
194    Tk_EventProc *proc;		/* Procedure to call for each
195				 * selected event */
196    ClientData clientData;	/* Arbitrary data to pass to proc. */
197{
198    register TkEventHandler *handlerPtr;
199    register TkWindow *winPtr = (TkWindow *) token;
200    int found;
201
202    /*
203     * Skim through the list of existing handlers to (a) compute the
204     * overall event mask for the window (so we can pass this new
205     * value to the X system) and (b) see if there's already a handler
206     * declared with the same callback and clientData (if so, just
207     * change the mask).  If no existing handler matches, then create
208     * a new handler.
209     */
210
211    found = 0;
212    if (winPtr->handlerList == NULL) {
213	handlerPtr = (TkEventHandler *) ckalloc(
214		(unsigned) sizeof(TkEventHandler));
215	winPtr->handlerList = handlerPtr;
216	goto initHandler;
217    } else {
218	for (handlerPtr = winPtr->handlerList; ;
219		handlerPtr = handlerPtr->nextPtr) {
220	    if ((handlerPtr->proc == proc)
221		    && (handlerPtr->clientData == clientData)) {
222		handlerPtr->mask = mask;
223		found = 1;
224	    }
225	    if (handlerPtr->nextPtr == NULL) {
226		break;
227	    }
228	}
229    }
230
231    /*
232     * Create a new handler if no matching old handler was found.
233     */
234
235    if (!found) {
236	handlerPtr->nextPtr = (TkEventHandler *)
237		ckalloc(sizeof(TkEventHandler));
238	handlerPtr = handlerPtr->nextPtr;
239	initHandler:
240	handlerPtr->mask = mask;
241	handlerPtr->proc = proc;
242	handlerPtr->clientData = clientData;
243	handlerPtr->nextPtr = NULL;
244    }
245
246    /*
247     * No need to call XSelectInput:  Tk always selects on all events
248     * for all windows (needed to support bindings on classes and "all").
249     */
250}
251
252/*
253 *--------------------------------------------------------------
254 *
255 * Tk_DeleteEventHandler --
256 *
257 *	Delete a previously-created handler.
258 *
259 * Results:
260 *	None.
261 *
262 * Side effects:
263 *	If there existed a handler as described by the
264 *	parameters, the handler is deleted so that proc
265 *	will not be invoked again.
266 *
267 *--------------------------------------------------------------
268 */
269
270void
271Tk_DeleteEventHandler(token, mask, proc, clientData)
272    Tk_Window token;		/* Same as corresponding arguments passed */
273    unsigned long mask;		/* previously to Tk_CreateEventHandler. */
274    Tk_EventProc *proc;
275    ClientData clientData;
276{
277    register TkEventHandler *handlerPtr;
278    register InProgress *ipPtr;
279    TkEventHandler *prevPtr;
280    register TkWindow *winPtr = (TkWindow *) token;
281    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
282            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
283
284    /*
285     * Find the event handler to be deleted, or return
286     * immediately if it doesn't exist.
287     */
288
289    for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ;
290	    prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) {
291	if (handlerPtr == NULL) {
292	    return;
293	}
294	if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc)
295		&& (handlerPtr->clientData == clientData)) {
296	    break;
297	}
298    }
299
300    /*
301     * If Tk_HandleEvent is about to process this handler, tell it to
302     * process the next one instead.
303     */
304
305    for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
306	if (ipPtr->nextHandler == handlerPtr) {
307	    ipPtr->nextHandler = handlerPtr->nextPtr;
308	}
309    }
310
311    /*
312     * Free resources associated with the handler.
313     */
314
315    if (prevPtr == NULL) {
316	winPtr->handlerList = handlerPtr->nextPtr;
317    } else {
318	prevPtr->nextPtr = handlerPtr->nextPtr;
319    }
320    ckfree((char *) handlerPtr);
321
322
323    /*
324     * No need to call XSelectInput:  Tk always selects on all events
325     * for all windows (needed to support bindings on classes and "all").
326     */
327}
328
329/*--------------------------------------------------------------
330 *
331 * Tk_CreateGenericHandler --
332 *
333 *	Register a procedure to be called on each X event, regardless
334 *	of display or window.  Generic handlers are useful for capturing
335 *	events that aren't associated with windows, or events for windows
336 *	not managed by Tk.
337 *
338 * Results:
339 *	None.
340 *
341 * Side Effects:
342 *	From now on, whenever an X event is given to Tk_HandleEvent,
343 *	invoke proc, giving it clientData and the event as arguments.
344 *
345 *--------------------------------------------------------------
346 */
347
348void
349Tk_CreateGenericHandler(proc, clientData)
350     Tk_GenericProc *proc;	/* Procedure to call on every event. */
351     ClientData clientData;	/* One-word value to pass to proc. */
352{
353    GenericHandler *handlerPtr;
354    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
355            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
356
357    handlerPtr = (GenericHandler *) ckalloc (sizeof (GenericHandler));
358
359    handlerPtr->proc		= proc;
360    handlerPtr->clientData	= clientData;
361    handlerPtr->deleteFlag	= 0;
362    handlerPtr->nextPtr		= NULL;
363    if (tsdPtr->genericList == NULL) {
364	tsdPtr->genericList	= handlerPtr;
365    } else {
366	tsdPtr->lastGenericPtr->nextPtr = handlerPtr;
367    }
368    tsdPtr->lastGenericPtr	= handlerPtr;
369}
370
371/*
372 *--------------------------------------------------------------
373 *
374 * Tk_DeleteGenericHandler --
375 *
376 *	Delete a previously-created generic handler.
377 *
378 * Results:
379 *	None.
380 *
381 * Side Effects:
382 *	If there existed a handler as described by the parameters,
383 *	that handler is logically deleted so that proc will not be
384 *	invoked again.  The physical deletion happens in the event
385 *	loop in Tk_HandleEvent.
386 *
387 *--------------------------------------------------------------
388 */
389
390void
391Tk_DeleteGenericHandler(proc, clientData)
392     Tk_GenericProc *proc;
393     ClientData clientData;
394{
395    GenericHandler * handler;
396    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
397	Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
398
399    for (handler = tsdPtr->genericList; handler; handler = handler->nextPtr) {
400	if ((handler->proc == proc) && (handler->clientData == clientData)) {
401	    handler->deleteFlag = 1;
402	}
403    }
404}
405
406/*--------------------------------------------------------------
407 *
408 * Tk_CreateClientMessageHandler --
409 *
410 *	Register a procedure to be called on each ClientMessage event.
411 *	ClientMessage handlers are useful for Drag&Drop extensions.
412 *
413 * Results:
414 *	None.
415 *
416 * Side Effects:
417 *	From now on, whenever a ClientMessage event is received that isn't
418 *	a WM_PROTOCOL event or SelectionEvent, invoke proc, giving it
419 *	tkwin and the event as arguments.
420 *
421 *--------------------------------------------------------------
422 */
423
424void
425Tk_CreateClientMessageHandler(proc)
426     Tk_ClientMessageProc *proc;	/* Procedure to call on event. */
427{
428    GenericHandler *handlerPtr;
429    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
430	Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
431
432    /*
433     * We use a GenericHandler struct, because it's basically the same,
434     * except with an extra clientData field we'll never use.
435     */
436    handlerPtr = (GenericHandler *)
437	ckalloc (sizeof (GenericHandler));
438
439    handlerPtr->proc		= (Tk_GenericProc *) proc;
440    handlerPtr->clientData	= NULL;	/* never used */
441    handlerPtr->deleteFlag	= 0;
442    handlerPtr->nextPtr		= NULL;
443    if (tsdPtr->cmList == NULL) {
444	tsdPtr->cmList		= handlerPtr;
445    } else {
446	tsdPtr->lastCmPtr->nextPtr = handlerPtr;
447    }
448    tsdPtr->lastCmPtr		= handlerPtr;
449}
450
451/*
452 *--------------------------------------------------------------
453 *
454 * Tk_DeleteClientMessageHandler --
455 *
456 *	Delete a previously-created ClientMessage handler.
457 *
458 * Results:
459 *	None.
460 *
461 * Side Effects:
462 *	If there existed a handler as described by the parameters,
463 *	that handler is logically deleted so that proc will not be
464 *	invoked again.  The physical deletion happens in the event
465 *	loop in TkClientMessageEventProc.
466 *
467 *--------------------------------------------------------------
468 */
469
470void
471Tk_DeleteClientMessageHandler(proc)
472     Tk_ClientMessageProc *proc;
473{
474    GenericHandler * handler;
475    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
476	Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
477
478    for (handler = tsdPtr->cmList; handler != NULL;
479	 handler = handler->nextPtr) {
480	if (handler->proc == (Tk_GenericProc *) proc) {
481	    handler->deleteFlag = 1;
482	}
483    }
484}
485
486/*
487 *--------------------------------------------------------------
488 *
489 * TkEventInit --
490 *
491 *	This procedures initializes all the event module
492 *      structures used by the current thread.  It must be
493 *      called before any other procedure in this file is
494 *      called.
495 *
496 * Results:
497 *	None.
498 *
499 * Side Effects:
500 *	None.
501 *
502 *--------------------------------------------------------------
503 */
504
505void
506TkEventInit _ANSI_ARGS_((void))
507{
508    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
509	Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
510
511    tsdPtr->handlersActive	= 0;
512    tsdPtr->pendingPtr		= NULL;
513    tsdPtr->genericList		= NULL;
514    tsdPtr->lastGenericPtr	= NULL;
515    tsdPtr->cmList		= NULL;
516    tsdPtr->lastCmPtr		= NULL;
517    tsdPtr->restrictProc	= NULL;
518    tsdPtr->restrictArg		= NULL;
519}
520
521/*
522 *--------------------------------------------------------------
523 *
524 * TkXErrorHandler --
525 *
526 *	TkXErrorHandler is an error handler, to be installed
527 *	via Tk_CreateErrorHandler, that will set a flag if an
528 *	X error occurred.
529 *
530 * Results:
531 *	Always returns 0, indicating that the X error was
532 *	handled.
533 *
534 * Side effects:
535 *	None.
536 *
537 *--------------------------------------------------------------
538 */
539
540static int
541TkXErrorHandler (clientData, errEventPtr)
542    ClientData clientData;      /* Pointer to flag we set       */
543    XErrorEvent *errEventPtr;   /* X error info                 */
544{
545    int *error;
546
547    error = (int *) clientData;
548    *error = 1;
549    return 0;
550}
551
552/*
553 *--------------------------------------------------------------
554 *
555 * ParentXId --
556 *
557 *	Returns the parent of the given window, or "None"
558 *	if the window doesn't exist.
559 *
560 * Results:
561 *	Returns an X window ID.
562 *
563 * Side effects:
564 *	None.
565 *
566 *--------------------------------------------------------------
567 */
568
569static Window
570ParentXId(display, w)
571    Display *display;
572    Window w;
573{
574    Tk_ErrorHandler handler;
575    int gotXError;
576    Status status;
577    Window parent;
578    Window root;
579    Window *childList;
580    unsigned int nChildren;
581
582    /* Handle errors ourselves. */
583
584    gotXError = 0;
585    handler = Tk_CreateErrorHandler(display, -1, -1, -1,
586			TkXErrorHandler, (ClientData) (&gotXError));
587
588    /* Get the parent window. */
589
590    status = XQueryTree(display, w, &root, &parent, &childList, &nChildren);
591
592    /* Do some cleanup; gotta return "None" if we got an error. */
593
594    Tk_DeleteErrorHandler(handler);
595    XSync(display, False);
596    if (status != 0 && childList != NULL) {
597	XFree(childList);
598    }
599    if (status == 0) {
600        parent = None;
601    }
602
603    return parent;
604}
605
606/*
607 *--------------------------------------------------------------
608 *
609 * Tk_HandleEvent --
610 *
611 *	Given an event, invoke all the handlers that have
612 *	been registered for the event.
613 *
614 * Results:
615 *	None.
616 *
617 * Side effects:
618 *	Depends on the handlers.
619 *
620 *--------------------------------------------------------------
621 */
622
623void
624Tk_HandleEvent(eventPtr)
625    XEvent *eventPtr;		/* Event to dispatch. */
626{
627    register TkEventHandler *handlerPtr;
628    register GenericHandler *genericPtr;
629    register GenericHandler *genPrevPtr;
630    TkWindow *winPtr;
631    unsigned long mask;
632    InProgress ip;
633    Window handlerWindow;
634    Window parentXId;
635    TkDisplay *dispPtr;
636    Tcl_Interp *interp = (Tcl_Interp *) NULL;
637    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
638	Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
639
640    /*
641     * Hack for simulated X-events: Correct the state field
642     * of the event record to match with the ButtonPress
643     * and ButtonRelease events.
644     */
645
646    if (eventPtr->type==ButtonPress) {
647	dispPtr = TkGetDisplay(eventPtr->xbutton.display);
648	dispPtr->mouseButtonWindow = eventPtr->xbutton.window;
649	eventPtr->xbutton.state |= dispPtr->mouseButtonState;
650	switch (eventPtr->xbutton.button) {
651	    case 1: dispPtr->mouseButtonState |= Button1Mask; break;
652	    case 2: dispPtr->mouseButtonState |= Button2Mask; break;
653	    case 3: dispPtr->mouseButtonState |= Button3Mask; break;
654	}
655    } else if (eventPtr->type==ButtonRelease) {
656	dispPtr = TkGetDisplay(eventPtr->xbutton.display);
657	dispPtr->mouseButtonWindow = 0;
658	switch (eventPtr->xbutton.button) {
659	    case 1: dispPtr->mouseButtonState &= ~Button1Mask; break;
660	    case 2: dispPtr->mouseButtonState &= ~Button2Mask; break;
661	    case 3: dispPtr->mouseButtonState &= ~Button3Mask; break;
662	}
663	eventPtr->xbutton.state |= dispPtr->mouseButtonState;
664    } else if (eventPtr->type==MotionNotify) {
665	dispPtr = TkGetDisplay(eventPtr->xmotion.display);
666	if (dispPtr->mouseButtonState & (Button1Mask|Button2Mask|Button3Mask)) {
667	    if (eventPtr->xbutton.window != dispPtr->mouseButtonWindow) {
668	        /*
669	         * This motion event should not be interpreted as a button
670	         * press + motion event since this is not the same window
671	         * the button was pressed down in.
672	         */
673	        dispPtr->mouseButtonState &=
674	                ~(Button1Mask|Button2Mask|Button3Mask);
675	        dispPtr->mouseButtonWindow = 0;
676	    } else {
677	        eventPtr->xmotion.state |= dispPtr->mouseButtonState;
678	    }
679	}
680    }
681
682    /*
683     * Next, invoke all the generic event handlers (those that are
684     * invoked for all events).  If a generic event handler reports that
685     * an event is fully processed, go no further.
686     */
687
688    for (genPrevPtr = NULL, genericPtr = tsdPtr->genericList;
689            genericPtr != NULL; ) {
690	if (genericPtr->deleteFlag) {
691	    if (!tsdPtr->handlersActive) {
692		GenericHandler *tmpPtr;
693
694		/*
695		 * This handler needs to be deleted and there are no
696		 * calls pending through the handler, so now is a safe
697		 * time to delete it.
698		 */
699
700		tmpPtr = genericPtr->nextPtr;
701		if (genPrevPtr == NULL) {
702		    tsdPtr->genericList = tmpPtr;
703		} else {
704		    genPrevPtr->nextPtr = tmpPtr;
705		}
706		if (tmpPtr == NULL) {
707		    tsdPtr->lastGenericPtr = genPrevPtr;
708		}
709		(void) ckfree((char *) genericPtr);
710		genericPtr = tmpPtr;
711		continue;
712	    }
713	} else {
714	    int done;
715
716	    tsdPtr->handlersActive++;
717	    done = (*genericPtr->proc)(genericPtr->clientData, eventPtr);
718	    tsdPtr->handlersActive--;
719	    if (done) {
720		return;
721	    }
722	}
723	genPrevPtr = genericPtr;
724	genericPtr = genPrevPtr->nextPtr;
725    }
726
727    /*
728     * If the event is a MappingNotify event, find its display and
729     * refresh the keyboard mapping information for the display.
730     * After that there's nothing else to do with the event, so just
731     * quit.
732     */
733
734    if (eventPtr->type == MappingNotify) {
735	dispPtr = TkGetDisplay(eventPtr->xmapping.display);
736	if (dispPtr != NULL) {
737	    XRefreshKeyboardMapping(&eventPtr->xmapping);
738	    dispPtr->bindInfoStale = 1;
739	}
740	return;
741    }
742
743    /*
744     * Events selected by StructureNotify require special handling.
745     * They look the same as those selected by SubstructureNotify.
746     * The only difference is whether the "event" and "window" fields
747     * are the same.  Compare the two fields and convert StructureNotify
748     * to SubstructureNotify if necessary.
749     */
750
751    handlerWindow = eventPtr->xany.window;
752    mask = eventMasks[eventPtr->xany.type];
753    if (mask == StructureNotifyMask) {
754	if (eventPtr->xmap.event != eventPtr->xmap.window) {
755	    mask = SubstructureNotifyMask;
756	    handlerWindow = eventPtr->xmap.event;
757	}
758    }
759    winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow);
760    if (winPtr == NULL) {
761	/*
762	 * There isn't a TkWindow structure for this window.
763	 * However, if the event is a PropertyNotify event then call
764	 * the selection manager (it deals beneath-the-table with
765	 * certain properties). Also, if the window's parent is a
766	 * Tk window that has the TK_PROP_PROPCHANGE flag set, then
767	 * we must propagate the PropertyNotify event up to the parent.
768	 */
769
770	if (eventPtr->type != PropertyNotify) {
771	    return;
772	}
773
774	TkSelPropProc(eventPtr);
775
776	/* Get handlerWindow's parent. */
777
778	parentXId = ParentXId(eventPtr->xany.display, handlerWindow);
779	if (parentXId == None) {
780	    return;
781	}
782
783	winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, parentXId);
784	if (winPtr == NULL) {
785	    return;
786	}
787
788	if (!(winPtr->flags & TK_PROP_PROPCHANGE)) {
789	    return;
790	}
791
792	handlerWindow = parentXId;
793    }
794
795    /*
796     * Once a window has started getting deleted, don't process any more
797     * events for it except for the DestroyNotify event.  This check is
798     * needed because a DestroyNotify handler could re-invoke the event
799     * loop, causing other pending events to be handled for the window
800     * (the window doesn't get totally expunged from our tables until
801     * after the DestroyNotify event has been completely handled).
802     */
803
804    if ((winPtr->flags & TK_ALREADY_DEAD)
805	    && (eventPtr->type != DestroyNotify)) {
806	return;
807    }
808
809    if (winPtr->mainPtr != NULL) {
810
811        /*
812         * Protect interpreter for this window from possible deletion
813         * while we are dealing with the event for this window. Thus,
814         * widget writers do not have to worry about protecting the
815         * interpreter in their own code.
816         */
817
818        interp = winPtr->mainPtr->interp;
819        Tcl_Preserve((ClientData) interp);
820
821	/*
822	 * Call focus-related code to look at FocusIn, FocusOut, Enter,
823	 * and Leave events;  depending on its return value, ignore the
824	 * event.
825	 */
826
827	if ((mask & (FocusChangeMask|EnterWindowMask|LeaveWindowMask))
828		&& !TkFocusFilterEvent(winPtr, eventPtr)) {
829            Tcl_Release((ClientData) interp);
830	    return;
831	}
832
833	/*
834	 * Redirect KeyPress and KeyRelease events to the focus window,
835	 * or ignore them entirely if there is no focus window.  We also
836	 * route the MouseWheel event to the focus window.  The MouseWheel
837	 * event is an extension to the X event set.  Currently, it is only
838	 * available on the Windows version of Tk.
839	 */
840
841#ifdef MAC_OSX_TK
842        /* MouseWheel events are not focus specific on Mac OS X */
843	if (mask & (KeyPressMask|KeyReleaseMask)) {
844#else
845	if (mask & (KeyPressMask|KeyReleaseMask|MouseWheelMask)) {
846#endif
847	    winPtr->dispPtr->lastEventTime = eventPtr->xkey.time;
848	    winPtr = TkFocusKeyEvent(winPtr, eventPtr);
849	    if (winPtr == NULL) {
850                Tcl_Release((ClientData) interp);
851		return;
852	    }
853	}
854
855	/*
856	 * Call a grab-related procedure to do special processing on
857	 * pointer events.
858	 */
859
860	if (mask & (ButtonPressMask|ButtonReleaseMask|PointerMotionMask
861		|EnterWindowMask|LeaveWindowMask)) {
862	    if (mask & (ButtonPressMask|ButtonReleaseMask)) {
863		winPtr->dispPtr->lastEventTime = eventPtr->xbutton.time;
864	    } else if (mask & PointerMotionMask) {
865		winPtr->dispPtr->lastEventTime = eventPtr->xmotion.time;
866	    } else {
867		winPtr->dispPtr->lastEventTime = eventPtr->xcrossing.time;
868	    }
869	    if (TkPointerEvent(eventPtr, winPtr) == 0) {
870                goto done;
871	    }
872	}
873    }
874
875#ifdef TK_USE_INPUT_METHODS
876    /*
877     * Pass the event to the input method(s), if there are any, and
878     * discard the event if the input method(s) insist.  Create the
879     * input context for the window if it hasn't already been done
880     * (XFilterEvent needs this context).  XIM is only ever enabled on
881     * Unix, but this hasn't been factored out of the generic code yet.
882     */
883    dispPtr = winPtr->dispPtr;
884    if ((dispPtr->flags & TK_DISPLAY_USE_IM)) {
885	long im_event_mask = 0L;
886	if (!(winPtr->flags & (TK_CHECKED_IC|TK_ALREADY_DEAD))) {
887	    winPtr->flags |= TK_CHECKED_IC;
888	    if (dispPtr->inputMethod != NULL) {
889#if TK_XIM_SPOT
890		if (dispPtr->flags & TK_DISPLAY_XIM_SPOT) {
891		    XVaNestedList preedit_attr;
892		    XPoint spot = {0, 0};
893
894		    if (dispPtr->inputXfs == NULL) {
895			/*
896			 * We only need to create one XFontSet
897			 */
898			char      **missing_list;
899			int       missing_count;
900			char      *def_string;
901
902			dispPtr->inputXfs = XCreateFontSet(dispPtr->display,
903				"-*-*-*-R-Normal--14-130-75-75-*-*",
904				&missing_list, &missing_count, &def_string);
905			if (missing_count > 0) {
906			    XFreeStringList(missing_list);
907			}
908		    }
909
910		    preedit_attr = XVaCreateNestedList(0, XNSpotLocation,
911			    &spot, XNFontSet, dispPtr->inputXfs, NULL);
912		    if (winPtr->inputContext != NULL)
913		        panic("inputContext not NULL");
914		    winPtr->inputContext = XCreateIC(dispPtr->inputMethod,
915			    XNInputStyle, XIMPreeditPosition|XIMStatusNothing,
916			    XNClientWindow, winPtr->window,
917			    XNFocusWindow, winPtr->window,
918			    XNPreeditAttributes, preedit_attr,
919			    NULL);
920		    XFree(preedit_attr);
921		} else {
922		    if (winPtr->inputContext != NULL)
923		        panic("inputContext not NULL");
924		    winPtr->inputContext = XCreateIC(dispPtr->inputMethod,
925			    XNInputStyle, XIMPreeditNothing|XIMStatusNothing,
926			    XNClientWindow, winPtr->window,
927			    XNFocusWindow, winPtr->window,
928			    NULL);
929		}
930#else
931		if (winPtr->inputContext != NULL)
932		    panic("inputContext not NULL");
933		winPtr->inputContext = XCreateIC(dispPtr->inputMethod,
934			XNInputStyle, XIMPreeditNothing|XIMStatusNothing,
935			XNClientWindow, winPtr->window,
936			XNFocusWindow, winPtr->window,
937			NULL);
938#endif
939	    }
940	}
941	if (winPtr->inputContext != NULL &&
942	    (eventPtr->xany.type == FocusIn)) {
943	    XGetICValues(winPtr->inputContext,
944			 XNFilterEvents, &im_event_mask, NULL);
945	    if (im_event_mask != 0L) {
946		XSelectInput(winPtr->display, winPtr->window,
947			     winPtr->atts.event_mask | im_event_mask);
948		XSetICFocus(winPtr->inputContext);
949	    }
950	}
951	if (eventPtr->type == KeyPress || eventPtr->type == KeyRelease) {
952	    if (XFilterEvent(eventPtr, None)) {
953		goto done;
954	    }
955	}
956    }
957#endif /* TK_USE_INPUT_METHODS */
958
959    /*
960     * For events where it hasn't already been done, update the current
961     * time in the display.
962     */
963
964    if (eventPtr->type == PropertyNotify) {
965	winPtr->dispPtr->lastEventTime = eventPtr->xproperty.time;
966    }
967
968    /*
969     * There's a potential interaction here with Tk_DeleteEventHandler.
970     * Read the documentation for pendingPtr.
971     */
972
973    ip.eventPtr = eventPtr;
974    ip.winPtr = winPtr;
975    ip.nextHandler = NULL;
976    ip.nextPtr = tsdPtr->pendingPtr;
977    tsdPtr->pendingPtr = &ip;
978    if (mask == 0) {
979	if ((eventPtr->type == SelectionClear)
980		|| (eventPtr->type == SelectionRequest)
981		|| (eventPtr->type == SelectionNotify)) {
982	    TkSelEventProc((Tk_Window) winPtr, eventPtr);
983	} else if (eventPtr->type == ClientMessage) {
984	    if (eventPtr->xclient.message_type ==
985		    Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS")) {
986		TkWmProtocolEventProc(winPtr, eventPtr);
987	    } else {
988		/*
989		 * Finally, invoke any ClientMessage event handlers.
990		 */
991
992		for (genPrevPtr = NULL, genericPtr = tsdPtr->cmList;
993		     genericPtr != NULL; ) {
994		    if (genericPtr->deleteFlag) {
995			if (!tsdPtr->handlersActive) {
996			    GenericHandler *tmpPtr;
997
998			    /*
999			     * This handler needs to be deleted and there are
1000			     * no calls pending through any handlers, so now
1001			     * is a safe time to delete it.
1002			     */
1003
1004			    tmpPtr = genericPtr->nextPtr;
1005			    if (genPrevPtr == NULL) {
1006				tsdPtr->cmList = tmpPtr;
1007			    } else {
1008				genPrevPtr->nextPtr = tmpPtr;
1009			    }
1010			    if (tmpPtr == NULL) {
1011				tsdPtr->lastCmPtr = genPrevPtr;
1012			    }
1013			    (void) ckfree((char *) genericPtr);
1014			    genericPtr = tmpPtr;
1015			    continue;
1016			}
1017		    } else {
1018			int done;
1019
1020			tsdPtr->handlersActive++;
1021			done = (*(Tk_ClientMessageProc *)genericPtr->proc)
1022			    ((Tk_Window) winPtr, eventPtr);
1023			tsdPtr->handlersActive--;
1024			if (done) {
1025			    break;
1026			}
1027		    }
1028		    genPrevPtr	= genericPtr;
1029		    genericPtr	= genPrevPtr->nextPtr;
1030		}
1031	    }
1032	}
1033    } else {
1034	for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {
1035	    if ((handlerPtr->mask & mask) != 0) {
1036		ip.nextHandler = handlerPtr->nextPtr;
1037		(*(handlerPtr->proc))(handlerPtr->clientData, eventPtr);
1038		handlerPtr = ip.nextHandler;
1039	    } else {
1040		handlerPtr = handlerPtr->nextPtr;
1041	    }
1042	}
1043
1044	/*
1045	 * Pass the event to the "bind" command mechanism.  But, don't
1046	 * do this for SubstructureNotify events.  The "bind" command
1047	 * doesn't support them anyway, and it's easier to filter out
1048	 * these events here than in the lower-level procedures.
1049	 */
1050
1051	/*
1052	 * ...well, except when we use the tkwm patches, in which case
1053	 * we DO handle CreateNotify events, so we gotta pass 'em through.
1054	 */
1055
1056	if ((ip.winPtr != None)
1057		&& ((mask != SubstructureNotifyMask)
1058				|| (eventPtr->type == CreateNotify))) {
1059	    TkBindEventProc(winPtr, eventPtr);
1060	}
1061    }
1062    tsdPtr->pendingPtr = ip.nextPtr;
1063done:
1064
1065    /*
1066     * Release the interpreter for this window so that it can be potentially
1067     * deleted if requested.
1068     */
1069
1070    if (interp != (Tcl_Interp *) NULL) {
1071        Tcl_Release((ClientData) interp);
1072    }
1073}
1074
1075/*
1076 *--------------------------------------------------------------
1077 *
1078 * TkEventDeadWindow --
1079 *
1080 *	This procedure is invoked when it is determined that
1081 *	a window is dead.  It cleans up event-related information
1082 *	about the window.
1083 *
1084 * Results:
1085 *	None.
1086 *
1087 * Side effects:
1088 *	Various things get cleaned up and recycled.
1089 *
1090 *--------------------------------------------------------------
1091 */
1092
1093void
1094TkEventDeadWindow(winPtr)
1095    TkWindow *winPtr;		/* Information about the window
1096				 * that is being deleted. */
1097{
1098    register TkEventHandler *handlerPtr;
1099    register InProgress *ipPtr;
1100    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1101            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1102
1103    /*
1104     * While deleting all the handlers, be careful to check for
1105     * Tk_HandleEvent being about to process one of the deleted
1106     * handlers.  If it is, tell it to quit (all of the handlers
1107     * are being deleted).
1108     */
1109
1110    while (winPtr->handlerList != NULL) {
1111	handlerPtr = winPtr->handlerList;
1112	winPtr->handlerList = handlerPtr->nextPtr;
1113	for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
1114                ipPtr = ipPtr->nextPtr) {
1115	    if (ipPtr->nextHandler == handlerPtr) {
1116		ipPtr->nextHandler = NULL;
1117	    }
1118	    if (ipPtr->winPtr == winPtr) {
1119		ipPtr->winPtr = None;
1120	    }
1121	}
1122	ckfree((char *) handlerPtr);
1123    }
1124}
1125
1126/*
1127 *----------------------------------------------------------------------
1128 *
1129 * TkCurrentTime --
1130 *
1131 *	Try to deduce the current time.  "Current time" means the time
1132 *	of the event that led to the current code being executed, which
1133 *	means the time in the most recently-nested invocation of
1134 *	Tk_HandleEvent.
1135 *
1136 * Results:
1137 *	The return value is the time from the current event, or
1138 *	CurrentTime if there is no current event or if the current
1139 *	event contains no time.
1140 *
1141 * Side effects:
1142 *	None.
1143 *
1144 *----------------------------------------------------------------------
1145 */
1146
1147Time
1148TkCurrentTime(dispPtr)
1149    TkDisplay *dispPtr;		/* Display for which the time is desired. */
1150{
1151    register XEvent *eventPtr;
1152    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1153            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1154
1155    if (tsdPtr->pendingPtr == NULL) {
1156	return dispPtr->lastEventTime;
1157    }
1158    eventPtr = tsdPtr->pendingPtr->eventPtr;
1159    switch (eventPtr->type) {
1160	case ButtonPress:
1161	case ButtonRelease:
1162	    return eventPtr->xbutton.time;
1163	case KeyPress:
1164	case KeyRelease:
1165	    return eventPtr->xkey.time;
1166	case MotionNotify:
1167	    return eventPtr->xmotion.time;
1168	case EnterNotify:
1169	case LeaveNotify:
1170	    return eventPtr->xcrossing.time;
1171	case PropertyNotify:
1172	    return eventPtr->xproperty.time;
1173    }
1174    return dispPtr->lastEventTime;
1175}
1176
1177/*
1178 *----------------------------------------------------------------------
1179 *
1180 * Tk_RestrictEvents --
1181 *
1182 *	This procedure is used to globally restrict the set of events
1183 *	that will be dispatched.  The restriction is done by filtering
1184 *	all incoming X events through a procedure that determines
1185 *	whether they are to be processed immediately, deferred, or
1186 *	discarded.
1187 *
1188 * Results:
1189 *	The return value is the previous restriction procedure in effect,
1190 *	if there was one, or NULL if there wasn't.
1191 *
1192 * Side effects:
1193 *	From now on, proc will be called to determine whether to process,
1194 *	defer or discard each incoming X event.
1195 *
1196 *----------------------------------------------------------------------
1197 */
1198
1199Tk_RestrictProc *
1200Tk_RestrictEvents(proc, arg, prevArgPtr)
1201    Tk_RestrictProc *proc;	/* Procedure to call for each incoming
1202				 * event. */
1203    ClientData arg;		/* Arbitrary argument to pass to proc. */
1204    ClientData *prevArgPtr;	/* Place to store information about previous
1205				 * argument. */
1206{
1207    Tk_RestrictProc *prev;
1208    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1209            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1210
1211    prev = tsdPtr->restrictProc;
1212    *prevArgPtr = tsdPtr->restrictArg;
1213    tsdPtr->restrictProc = proc;
1214    tsdPtr->restrictArg = arg;
1215    return prev;
1216}
1217
1218/*
1219 *----------------------------------------------------------------------
1220 *
1221 * Tk_CollapseMotionEvents --
1222 *
1223 *	This procedure controls whether we collapse motion events in a
1224 *	particular display or not.
1225 *
1226 * Results:
1227 *	The return value is the previous collapse value in effect.
1228 *
1229 * Side effects:
1230 *	Filtering of motion events may be changed after calling this.
1231 *
1232 *----------------------------------------------------------------------
1233 */
1234
1235int
1236Tk_CollapseMotionEvents(display, collapse)
1237    Display *display;		/* Display handling these events. */
1238    int collapse;		/* boolean value that specifies whether
1239				 * motion events should be collapsed. */
1240{
1241    TkDisplay *dispPtr = (TkDisplay *) display;
1242    int prev = (dispPtr->flags & TK_DISPLAY_COLLAPSE_MOTION_EVENTS);
1243
1244    if (collapse) {
1245	dispPtr->flags |= TK_DISPLAY_COLLAPSE_MOTION_EVENTS;
1246    } else {
1247	dispPtr->flags &= ~TK_DISPLAY_COLLAPSE_MOTION_EVENTS;
1248    }
1249    return prev;
1250}
1251
1252/*
1253 *----------------------------------------------------------------------
1254 *
1255 * Tk_QueueWindowEvent --
1256 *
1257 *	Given an X-style window event, this procedure adds it to the
1258 *	Tcl event queue at the given position.  This procedure also
1259 *	performs mouse motion event collapsing if possible.
1260 *
1261 * Results:
1262 *	None.
1263 *
1264 * Side effects:
1265 *	Adds stuff to the event queue, which will eventually be
1266 *	processed.
1267 *
1268 *----------------------------------------------------------------------
1269 */
1270
1271void
1272Tk_QueueWindowEvent(eventPtr, position)
1273    XEvent *eventPtr;			/* Event to add to queue.  This
1274					 * procedures copies it before adding
1275					 * it to the queue. */
1276    Tcl_QueuePosition position;		/* Where to put it on the queue:
1277					 * TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
1278					 * or TCL_QUEUE_MARK. */
1279{
1280    TkWindowEvent *wevPtr;
1281    TkDisplay *dispPtr;
1282
1283    /*
1284     * Find our display structure for the event's display.
1285     */
1286
1287    for (dispPtr = TkGetDisplayList(); ; dispPtr = dispPtr->nextPtr) {
1288	if (dispPtr == NULL) {
1289	    return;
1290	}
1291	if (dispPtr->display == eventPtr->xany.display) {
1292	    break;
1293	}
1294    }
1295
1296    /*
1297     * Don't filter motion events if the user
1298     * defaulting to true (1), which could be set to false (0) when the
1299     * user wishes to receive all the motion data)
1300     */
1301    if (!(dispPtr->flags & TK_DISPLAY_COLLAPSE_MOTION_EVENTS)) {
1302	wevPtr = (TkWindowEvent *) ckalloc(sizeof(TkWindowEvent));
1303	wevPtr->header.proc = WindowEventProc;
1304	wevPtr->event = *eventPtr;
1305	Tcl_QueueEvent(&wevPtr->header, position);
1306	return;
1307    }
1308
1309    if ((dispPtr->delayedMotionPtr != NULL) && (position == TCL_QUEUE_TAIL)) {
1310	if ((eventPtr->type == MotionNotify) && (eventPtr->xmotion.window
1311		== dispPtr->delayedMotionPtr->event.xmotion.window)) {
1312	    /*
1313	     * The new event is a motion event in the same window as the
1314	     * saved motion event.  Just replace the saved event with the
1315	     * new one.
1316	     */
1317
1318	    dispPtr->delayedMotionPtr->event = *eventPtr;
1319	    return;
1320	} else if ((eventPtr->type != GraphicsExpose)
1321		&& (eventPtr->type != NoExpose)
1322		&& (eventPtr->type != Expose)) {
1323	    /*
1324	     * The new event may conflict with the saved motion event.  Queue
1325	     * the saved motion event now so that it will be processed before
1326	     * the new event.
1327	     */
1328
1329	    Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, position);
1330	    dispPtr->delayedMotionPtr = NULL;
1331	    Tcl_CancelIdleCall(DelayedMotionProc, (ClientData) dispPtr);
1332	}
1333    }
1334
1335    wevPtr = (TkWindowEvent *) ckalloc(sizeof(TkWindowEvent));
1336    wevPtr->header.proc = WindowEventProc;
1337    wevPtr->event = *eventPtr;
1338    if ((eventPtr->type == MotionNotify) && (position == TCL_QUEUE_TAIL)) {
1339	/*
1340	 * The new event is a motion event so don't queue it immediately;
1341	 * save it around in case another motion event arrives that it can
1342	 * be collapsed with.
1343	 */
1344
1345	if (dispPtr->delayedMotionPtr != NULL) {
1346	    panic("Tk_QueueWindowEvent found unexpected delayed motion event");
1347	}
1348	dispPtr->delayedMotionPtr = wevPtr;
1349	Tcl_DoWhenIdle(DelayedMotionProc, (ClientData) dispPtr);
1350    } else {
1351	Tcl_QueueEvent(&wevPtr->header, position);
1352    }
1353}
1354
1355/*
1356 *---------------------------------------------------------------------------
1357 *
1358 * TkQueueEventForAllChildren --
1359 *
1360 *	Given an XEvent, recursively queue the event for this window and
1361 *	all non-toplevel children of the given window.
1362 *
1363 * Results:
1364 *	None.
1365 *
1366 * Side effects:
1367 *	Events queued.
1368 *
1369 *---------------------------------------------------------------------------
1370 */
1371
1372void
1373TkQueueEventForAllChildren(winPtr, eventPtr)
1374    TkWindow *winPtr;	    /* Window to which event is sent. */
1375    XEvent *eventPtr;	    /* The event to be sent. */
1376{
1377    TkWindow *childPtr;
1378
1379    if (!Tk_IsMapped(winPtr)) {
1380        return;
1381    }
1382
1383    eventPtr->xany.window = winPtr->window;
1384    Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);
1385
1386    childPtr = winPtr->childList;
1387    while (childPtr != NULL) {
1388	if (!Tk_TopWinHierarchy(childPtr)) {
1389	    TkQueueEventForAllChildren(childPtr, eventPtr);
1390	}
1391	childPtr = childPtr->nextPtr;
1392    }
1393}
1394
1395/*
1396 *----------------------------------------------------------------------
1397 *
1398 * WindowEventProc --
1399 *
1400 *	This procedure is called by Tcl_DoOneEvent when a window event
1401 *	reaches the front of the event queue.  This procedure is responsible
1402 *	for actually handling the event.
1403 *
1404 * Results:
1405 *	Returns 1 if the event was handled, meaning it should be removed
1406 *	from the queue.  Returns 0 if the event was not handled, meaning
1407 *	it should stay on the queue.  The event isn't handled if the
1408 *	TCL_WINDOW_EVENTS bit isn't set in flags, if a restrict proc
1409 *	prevents the event from being handled.
1410 *
1411 * Side effects:
1412 *	Whatever the event handlers for the event do.
1413 *
1414 *----------------------------------------------------------------------
1415 */
1416
1417static int
1418WindowEventProc(evPtr, flags)
1419    Tcl_Event *evPtr;		/* Event to service. */
1420    int flags;			/* Flags that indicate what events to
1421				 * handle, such as TCL_WINDOW_EVENTS. */
1422{
1423    TkWindowEvent *wevPtr = (TkWindowEvent *) evPtr;
1424    Tk_RestrictAction result;
1425    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1426            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1427
1428    if (!(flags & TCL_WINDOW_EVENTS)) {
1429	return 0;
1430    }
1431    if (tsdPtr->restrictProc != NULL) {
1432	result = (*tsdPtr->restrictProc)(tsdPtr->restrictArg, &wevPtr->event);
1433	if (result != TK_PROCESS_EVENT) {
1434	    if (result == TK_DEFER_EVENT) {
1435		return 0;
1436	    } else {
1437		/*
1438		 * TK_DELETE_EVENT: return and say we processed the event,
1439		 * even though we didn't do anything at all.
1440		 */
1441		return 1;
1442	    }
1443	}
1444    }
1445    Tk_HandleEvent(&wevPtr->event);
1446    return 1;
1447}
1448
1449/*
1450 *----------------------------------------------------------------------
1451 *
1452 * DelayedMotionProc --
1453 *
1454 *	This procedure is invoked as an idle handler when a mouse motion
1455 *	event has been delayed.  It queues the delayed event so that it
1456 *	will finally be serviced.
1457 *
1458 * Results:
1459 *	None.
1460 *
1461 * Side effects:
1462 *	The delayed mouse motion event gets added to the Tcl event
1463 *	queue for servicing.
1464 *
1465 *----------------------------------------------------------------------
1466 */
1467
1468static void
1469DelayedMotionProc(clientData)
1470    ClientData clientData;	/* Pointer to display containing a delayed
1471				 * motion event to be serviced. */
1472{
1473    TkDisplay *dispPtr = (TkDisplay *) clientData;
1474
1475    if (dispPtr->delayedMotionPtr == NULL) {
1476	panic("DelayedMotionProc found no delayed mouse motion event");
1477    }
1478    Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, TCL_QUEUE_TAIL);
1479    dispPtr->delayedMotionPtr = NULL;
1480}
1481
1482/*
1483 *--------------------------------------------------------------
1484 *
1485 * Tk_MainLoop --
1486 *
1487 *	Call Tcl_DoOneEvent over and over again in an infinite
1488 *	loop as long as there exist any main windows.
1489 *
1490 * Results:
1491 *	None.
1492 *
1493 * Side effects:
1494 *	Arbitrary;  depends on handlers for events.
1495 *
1496 *--------------------------------------------------------------
1497 */
1498
1499void
1500Tk_MainLoop()
1501{
1502    while (Tk_GetNumMainWindows() > 0) {
1503	Tcl_DoOneEvent(0);
1504    }
1505}
1506