/*1* tkEvent.c --2*3* This file provides basic low-level facilities for managing4* X events in Tk.5*6* Copyright (c) 1990-1994 The Regents of the University of California.7* Copyright (c) 1994-1995 Sun Microsystems, Inc.8*9* See the file "license.terms" for information on usage and redistribution10* of this file, and for a DISCLAIMER OF ALL WARRANTIES.11*12* SCCS: @(#) tkEvent.c 1.18 96/09/12 09:25:2213*/1415#include "tkInt.h"16#include "tkPort.h"17#include <signal.h>1819/*20* There's a potential problem if a handler is deleted while it's21* current (i.e. its procedure is executing), since Tk_HandleEvent22* will need to read the handler's "nextPtr" field when the procedure23* returns. To handle this problem, structures of the type below24* indicate the next handler to be processed for any (recursively25* nested) dispatches in progress. The nextHandler fields get26* updated if the handlers pointed to are deleted. Tk_HandleEvent27* also needs to know if the entire window gets deleted; the winPtr28* field is set to zero if that particular window gets deleted.29*/3031typedef struct InProgress {32XEvent *eventPtr; /* Event currently being handled. */33TkWindow *winPtr; /* Window for event. Gets set to None if34* window is deleted while event is being35* handled. */36TkEventHandler *nextHandler; /* Next handler in search. */37struct InProgress *nextPtr; /* Next higher nested search. */38} InProgress;3940static InProgress *pendingPtr = NULL;41/* Topmost search in progress, or42* NULL if none. */4344/*45* For each call to Tk_CreateGenericHandler, an instance of the following46* structure will be created. All of the active handlers are linked into a47* list.48*/4950typedef struct GenericHandler {51Tk_GenericProc *proc; /* Procedure to dispatch on all X events. */52ClientData clientData; /* Client data to pass to procedure. */53int deleteFlag; /* Flag to set when this handler is deleted. */54struct GenericHandler *nextPtr;55/* Next handler in list of all generic56* handlers, or NULL for end of list. */57} GenericHandler;5859static GenericHandler *genericList = NULL;60/* First handler in the list, or NULL. */61static GenericHandler *lastGenericPtr = NULL;62/* Last handler in list. */6364/*65* There's a potential problem if Tk_HandleEvent is entered recursively.66* A handler cannot be deleted physically until we have returned from67* calling it. Otherwise, we're looking at unallocated memory in advancing to68* its `next' entry. We deal with the problem by using the `delete flag' and69* deleting handlers only when it's known that there's no handler active.70*71* The following variable has a non-zero value when a handler is active.72*/7374static int genericHandlersActive = 0;7576/*77* The following structure is used for queueing X-style events on the78* Tcl event queue.79*/8081typedef struct TkWindowEvent {82Tcl_Event header; /* Standard information for all events. */83XEvent event; /* The X event. */84} TkWindowEvent;8586/*87* Array of event masks corresponding to each X event:88*/8990static unsigned long eventMasks[TK_LASTEVENT] = {910,920,93KeyPressMask, /* KeyPress */94KeyReleaseMask, /* KeyRelease */95ButtonPressMask, /* ButtonPress */96ButtonReleaseMask, /* ButtonRelease */97PointerMotionMask|PointerMotionHintMask|ButtonMotionMask98|Button1MotionMask|Button2MotionMask|Button3MotionMask99|Button4MotionMask|Button5MotionMask,100/* MotionNotify */101EnterWindowMask, /* EnterNotify */102LeaveWindowMask, /* LeaveNotify */103FocusChangeMask, /* FocusIn */104FocusChangeMask, /* FocusOut */105KeymapStateMask, /* KeymapNotify */106ExposureMask, /* Expose */107ExposureMask, /* GraphicsExpose */108ExposureMask, /* NoExpose */109VisibilityChangeMask, /* VisibilityNotify */110SubstructureNotifyMask, /* CreateNotify */111StructureNotifyMask, /* DestroyNotify */112StructureNotifyMask, /* UnmapNotify */113StructureNotifyMask, /* MapNotify */114SubstructureRedirectMask, /* MapRequest */115StructureNotifyMask, /* ReparentNotify */116StructureNotifyMask, /* ConfigureNotify */117SubstructureRedirectMask, /* ConfigureRequest */118StructureNotifyMask, /* GravityNotify */119ResizeRedirectMask, /* ResizeRequest */120StructureNotifyMask, /* CirculateNotify */121SubstructureRedirectMask, /* CirculateRequest */122PropertyChangeMask, /* PropertyNotify */1230, /* SelectionClear */1240, /* SelectionRequest */1250, /* SelectionNotify */126ColormapChangeMask, /* ColormapNotify */1270, /* ClientMessage */1280, /* Mapping Notify */129VirtualEventMask, /* VirtualEvents */130ActivateMask, /* ActivateNotify */131ActivateMask /* DeactivateNotify */132};133134/*135* If someone has called Tk_RestrictEvents, the information below136* keeps track of it.137*/138139static Tk_RestrictProc *restrictProc;140/* Procedure to call. NULL means no141* restrictProc is currently in effect. */142static ClientData restrictArg; /* Argument to pass to restrictProc. */143144/*145* Prototypes for procedures that are only referenced locally within146* this file.147*/148149static void DelayedMotionProc _ANSI_ARGS_((ClientData clientData));150static int WindowEventProc _ANSI_ARGS_((Tcl_Event *evPtr,151int flags));152153/*154*--------------------------------------------------------------155*156* Tk_CreateEventHandler --157*158* Arrange for a given procedure to be invoked whenever159* events from a given class occur in a given window.160*161* Results:162* None.163*164* Side effects:165* From now on, whenever an event of the type given by166* mask occurs for token and is processed by Tk_HandleEvent,167* proc will be called. See the manual entry for details168* of the calling sequence and return value for proc.169*170*--------------------------------------------------------------171*/172173void174Tk_CreateEventHandler(token, mask, proc, clientData)175Tk_Window token; /* Token for window in which to176* create handler. */177unsigned long mask; /* Events for which proc should178* be called. */179Tk_EventProc *proc; /* Procedure to call for each180* selected event */181ClientData clientData; /* Arbitrary data to pass to proc. */182{183register TkEventHandler *handlerPtr;184register TkWindow *winPtr = (TkWindow *) token;185int found;186187/*188* Skim through the list of existing handlers to (a) compute the189* overall event mask for the window (so we can pass this new190* value to the X system) and (b) see if there's already a handler191* declared with the same callback and clientData (if so, just192* change the mask). If no existing handler matches, then create193* a new handler.194*/195196found = 0;197if (winPtr->handlerList == NULL) {198handlerPtr = (TkEventHandler *) ckalloc(199(unsigned) sizeof(TkEventHandler));200winPtr->handlerList = handlerPtr;201goto initHandler;202} else {203for (handlerPtr = winPtr->handlerList; ;204handlerPtr = handlerPtr->nextPtr) {205if ((handlerPtr->proc == proc)206&& (handlerPtr->clientData == clientData)) {207handlerPtr->mask = mask;208found = 1;209}210if (handlerPtr->nextPtr == NULL) {211break;212}213}214}215216/*217* Create a new handler if no matching old handler was found.218*/219220if (!found) {221handlerPtr->nextPtr = (TkEventHandler *)222ckalloc(sizeof(TkEventHandler));223handlerPtr = handlerPtr->nextPtr;224initHandler:225handlerPtr->mask = mask;226handlerPtr->proc = proc;227handlerPtr->clientData = clientData;228handlerPtr->nextPtr = NULL;229}230231/*232* No need to call XSelectInput: Tk always selects on all events233* for all windows (needed to support bindings on classes and "all").234*/235}236237/*238*--------------------------------------------------------------239*240* Tk_DeleteEventHandler --241*242* Delete a previously-created handler.243*244* Results:245* None.246*247* Side effects:248* If there existed a handler as described by the249* parameters, the handler is deleted so that proc250* will not be invoked again.251*252*--------------------------------------------------------------253*/254255void256Tk_DeleteEventHandler(token, mask, proc, clientData)257Tk_Window token; /* Same as corresponding arguments passed */258unsigned long mask; /* previously to Tk_CreateEventHandler. */259Tk_EventProc *proc;260ClientData clientData;261{262register TkEventHandler *handlerPtr;263register InProgress *ipPtr;264TkEventHandler *prevPtr;265register TkWindow *winPtr = (TkWindow *) token;266267/*268* Find the event handler to be deleted, or return269* immediately if it doesn't exist.270*/271272for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ;273prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) {274if (handlerPtr == NULL) {275return;276}277if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc)278&& (handlerPtr->clientData == clientData)) {279break;280}281}282283/*284* If Tk_HandleEvent is about to process this handler, tell it to285* process the next one instead.286*/287288for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {289if (ipPtr->nextHandler == handlerPtr) {290ipPtr->nextHandler = handlerPtr->nextPtr;291}292}293294/*295* Free resources associated with the handler.296*/297298if (prevPtr == NULL) {299winPtr->handlerList = handlerPtr->nextPtr;300} else {301prevPtr->nextPtr = handlerPtr->nextPtr;302}303ckfree((char *) handlerPtr);304305306/*307* No need to call XSelectInput: Tk always selects on all events308* for all windows (needed to support bindings on classes and "all").309*/310}311312/*--------------------------------------------------------------313*314* Tk_CreateGenericHandler --315*316* Register a procedure to be called on each X event, regardless317* of display or window. Generic handlers are useful for capturing318* events that aren't associated with windows, or events for windows319* not managed by Tk.320*321* Results:322* None.323*324* Side Effects:325* From now on, whenever an X event is given to Tk_HandleEvent,326* invoke proc, giving it clientData and the event as arguments.327*328*--------------------------------------------------------------329*/330331void332Tk_CreateGenericHandler(proc, clientData)333Tk_GenericProc *proc; /* Procedure to call on every event. */334ClientData clientData; /* One-word value to pass to proc. */335{336GenericHandler *handlerPtr;337338handlerPtr = (GenericHandler *) ckalloc (sizeof (GenericHandler));339340handlerPtr->proc = proc;341handlerPtr->clientData = clientData;342handlerPtr->deleteFlag = 0;343handlerPtr->nextPtr = NULL;344if (genericList == NULL) {345genericList = handlerPtr;346} else {347lastGenericPtr->nextPtr = handlerPtr;348}349lastGenericPtr = handlerPtr;350}351352/*353*--------------------------------------------------------------354*355* Tk_DeleteGenericHandler --356*357* Delete a previously-created generic handler.358*359* Results:360* None.361*362* Side Effects:363* If there existed a handler as described by the parameters,364* that handler is logically deleted so that proc will not be365* invoked again. The physical deletion happens in the event366* loop in Tk_HandleEvent.367*368*--------------------------------------------------------------369*/370371void372Tk_DeleteGenericHandler(proc, clientData)373Tk_GenericProc *proc;374ClientData clientData;375{376GenericHandler * handler;377378for (handler = genericList; handler; handler = handler->nextPtr) {379if ((handler->proc == proc) && (handler->clientData == clientData)) {380handler->deleteFlag = 1;381}382}383}384385/*386*--------------------------------------------------------------387*388* Tk_HandleEvent --389*390* Given an event, invoke all the handlers that have391* been registered for the event.392*393* Results:394* None.395*396* Side effects:397* Depends on the handlers.398*399*--------------------------------------------------------------400*/401402void403Tk_HandleEvent(eventPtr)404XEvent *eventPtr; /* Event to dispatch. */405{406register TkEventHandler *handlerPtr;407register GenericHandler *genericPtr;408register GenericHandler *genPrevPtr;409TkWindow *winPtr;410unsigned long mask;411InProgress ip;412Window handlerWindow;413TkDisplay *dispPtr;414Tcl_Interp *interp = (Tcl_Interp *) NULL;415416/*417* Next, invoke all the generic event handlers (those that are418* invoked for all events). If a generic event handler reports that419* an event is fully processed, go no further.420*/421422for (genPrevPtr = NULL, genericPtr = genericList; genericPtr != NULL; ) {423if (genericPtr->deleteFlag) {424if (!genericHandlersActive) {425GenericHandler *tmpPtr;426427/*428* This handler needs to be deleted and there are no429* calls pending through the handler, so now is a safe430* time to delete it.431*/432433tmpPtr = genericPtr->nextPtr;434if (genPrevPtr == NULL) {435genericList = tmpPtr;436} else {437genPrevPtr->nextPtr = tmpPtr;438}439if (tmpPtr == NULL) {440lastGenericPtr = genPrevPtr;441}442(void) ckfree((char *) genericPtr);443genericPtr = tmpPtr;444continue;445}446} else {447int done;448449genericHandlersActive++;450done = (*genericPtr->proc)(genericPtr->clientData, eventPtr);451genericHandlersActive--;452if (done) {453return;454}455}456genPrevPtr = genericPtr;457genericPtr = genPrevPtr->nextPtr;458}459460/*461* If the event is a MappingNotify event, find its display and462* refresh the keyboard mapping information for the display.463* After that there's nothing else to do with the event, so just464* quit.465*/466467if (eventPtr->type == MappingNotify) {468dispPtr = TkGetDisplay(eventPtr->xmapping.display);469if (dispPtr != NULL) {470XRefreshKeyboardMapping(&eventPtr->xmapping);471dispPtr->bindInfoStale = 1;472}473return;474}475476/*477* Events selected by StructureNotify require special handling.478* They look the same as those selected by SubstructureNotify.479* The only difference is whether the "event" and "window" fields480* are the same. Compare the two fields and convert StructureNotify481* to SubstructureNotify if necessary.482*/483484handlerWindow = eventPtr->xany.window;485mask = eventMasks[eventPtr->xany.type];486if (mask == StructureNotifyMask) {487if (eventPtr->xmap.event != eventPtr->xmap.window) {488mask = SubstructureNotifyMask;489handlerWindow = eventPtr->xmap.event;490}491}492winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow);493if (winPtr == NULL) {494495/*496* There isn't a TkWindow structure for this window.497* However, if the event is a PropertyNotify event then call498* the selection manager (it deals beneath-the-table with499* certain properties).500*/501502if (eventPtr->type == PropertyNotify) {503TkSelPropProc(eventPtr);504}505return;506}507508/*509* Once a window has started getting deleted, don't process any more510* events for it except for the DestroyNotify event. This check is511* needed because a DestroyNotify handler could re-invoke the event512* loop, causing other pending events to be handled for the window513* (the window doesn't get totally expunged from our tables until514* after the DestroyNotify event has been completely handled).515*/516517if ((winPtr->flags & TK_ALREADY_DEAD)518&& (eventPtr->type != DestroyNotify)) {519return;520}521522if (winPtr->mainPtr != NULL) {523524/*525* Protect interpreter for this window from possible deletion526* while we are dealing with the event for this window. Thus,527* widget writers do not have to worry about protecting the528* interpreter in their own code.529*/530531interp = winPtr->mainPtr->interp;532Tcl_Preserve((ClientData) interp);533534/*535* Call focus-related code to look at FocusIn, FocusOut, Enter,536* and Leave events; depending on its return value, ignore the537* event.538*/539540if ((mask & (FocusChangeMask|EnterWindowMask|LeaveWindowMask))541&& !TkFocusFilterEvent(winPtr, eventPtr)) {542Tcl_Release((ClientData) interp);543return;544}545546/*547* Redirect KeyPress and KeyRelease events to the focus window,548* or ignore them entirely if there is no focus window. Map the549* x and y coordinates to make sense in the context of the focus550* window, if possible (make both -1 if the map-from and map-to551* windows don't share the same screen).552*/553554if (mask & (KeyPressMask|KeyReleaseMask)) {555TkWindow *focusPtr;556int winX, winY, focusX, focusY;557558winPtr->dispPtr->lastEventTime = eventPtr->xkey.time;559focusPtr = TkGetFocus(winPtr);560if (focusPtr == NULL) {561Tcl_Release((ClientData) interp);562return;563}564if ((focusPtr->display != winPtr->display)565|| (focusPtr->screenNum != winPtr->screenNum)) {566eventPtr->xkey.x = -1;567eventPtr->xkey.y = -1;568} else {569Tk_GetRootCoords((Tk_Window) winPtr, &winX, &winY);570Tk_GetRootCoords((Tk_Window) focusPtr, &focusX, &focusY);571eventPtr->xkey.x -= focusX - winX;572eventPtr->xkey.y -= focusY - winY;573}574eventPtr->xkey.window = focusPtr->window;575winPtr = focusPtr;576}577578/*579* Call a grab-related procedure to do special processing on580* pointer events.581*/582583if (mask & (ButtonPressMask|ButtonReleaseMask|PointerMotionMask584|EnterWindowMask|LeaveWindowMask)) {585if (mask & (ButtonPressMask|ButtonReleaseMask)) {586winPtr->dispPtr->lastEventTime = eventPtr->xbutton.time;587} else if (mask & PointerMotionMask) {588winPtr->dispPtr->lastEventTime = eventPtr->xmotion.time;589} else {590winPtr->dispPtr->lastEventTime = eventPtr->xcrossing.time;591}592if (TkPointerEvent(eventPtr, winPtr) == 0) {593goto done;594}595}596}597598#ifdef TK_USE_INPUT_METHODS599/*600* Pass the event to the input method(s), if there are any, and601* discard the event if the input method(s) insist. Create the602* input context for the window if it hasn't already been done603* (XFilterEvent needs this context).604*/605606if (!(winPtr->flags & TK_CHECKED_IC)) {607if (winPtr->dispPtr->inputMethod != NULL) {608winPtr->inputContext = XCreateIC(609winPtr->dispPtr->inputMethod, XNInputStyle,610XIMPreeditNothing|XIMStatusNothing,611XNClientWindow, winPtr->window,612XNFocusWindow, winPtr->window, NULL);613}614winPtr->flags |= TK_CHECKED_IC;615}616if (XFilterEvent(eventPtr, None)) {617goto done;618}619#endif /* TK_USE_INPUT_METHODS */620621/*622* For events where it hasn't already been done, update the current623* time in the display.624*/625626if (eventPtr->type == PropertyNotify) {627winPtr->dispPtr->lastEventTime = eventPtr->xproperty.time;628}629630/*631* There's a potential interaction here with Tk_DeleteEventHandler.632* Read the documentation for pendingPtr.633*/634635ip.eventPtr = eventPtr;636ip.winPtr = winPtr;637ip.nextHandler = NULL;638ip.nextPtr = pendingPtr;639pendingPtr = &ip;640if (mask == 0) {641if ((eventPtr->type == SelectionClear)642|| (eventPtr->type == SelectionRequest)643|| (eventPtr->type == SelectionNotify)) {644TkSelEventProc((Tk_Window) winPtr, eventPtr);645} else if ((eventPtr->type == ClientMessage)646&& (eventPtr->xclient.message_type ==647Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"))) {648TkWmProtocolEventProc(winPtr, eventPtr);649}650} else {651for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {652if ((handlerPtr->mask & mask) != 0) {653ip.nextHandler = handlerPtr->nextPtr;654(*(handlerPtr->proc))(handlerPtr->clientData, eventPtr);655handlerPtr = ip.nextHandler;656} else {657handlerPtr = handlerPtr->nextPtr;658}659}660661/*662* Pass the event to the "bind" command mechanism. But, don't663* do this for SubstructureNotify events. The "bind" command664* doesn't support them anyway, and it's easier to filter out665* these events here than in the lower-level procedures.666*/667668if ((ip.winPtr != None) && (mask != SubstructureNotifyMask)) {669TkBindEventProc(winPtr, eventPtr);670}671}672pendingPtr = ip.nextPtr;673done:674675/*676* Release the interpreter for this window so that it can be potentially677* deleted if requested.678*/679680if (interp != (Tcl_Interp *) NULL) {681Tcl_Release((ClientData) interp);682}683}684685/*686*--------------------------------------------------------------687*688* TkEventDeadWindow --689*690* This procedure is invoked when it is determined that691* a window is dead. It cleans up event-related information692* about the window.693*694* Results:695* None.696*697* Side effects:698* Various things get cleaned up and recycled.699*700*--------------------------------------------------------------701*/702703void704TkEventDeadWindow(winPtr)705TkWindow *winPtr; /* Information about the window706* that is being deleted. */707{708register TkEventHandler *handlerPtr;709register InProgress *ipPtr;710711/*712* While deleting all the handlers, be careful to check for713* Tk_HandleEvent being about to process one of the deleted714* handlers. If it is, tell it to quit (all of the handlers715* are being deleted).716*/717718while (winPtr->handlerList != NULL) {719handlerPtr = winPtr->handlerList;720winPtr->handlerList = handlerPtr->nextPtr;721for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {722if (ipPtr->nextHandler == handlerPtr) {723ipPtr->nextHandler = NULL;724}725if (ipPtr->winPtr == winPtr) {726ipPtr->winPtr = None;727}728}729ckfree((char *) handlerPtr);730}731}732733/*734*----------------------------------------------------------------------735*736* TkCurrentTime --737*738* Try to deduce the current time. "Current time" means the time739* of the event that led to the current code being executed, which740* means the time in the most recently-nested invocation of741* Tk_HandleEvent.742*743* Results:744* The return value is the time from the current event, or745* CurrentTime if there is no current event or if the current746* event contains no time.747*748* Side effects:749* None.750*751*----------------------------------------------------------------------752*/753754Time755TkCurrentTime(dispPtr)756TkDisplay *dispPtr; /* Display for which the time is desired. */757{758register XEvent *eventPtr;759760if (pendingPtr == NULL) {761return dispPtr->lastEventTime;762}763eventPtr = pendingPtr->eventPtr;764switch (eventPtr->type) {765case ButtonPress:766case ButtonRelease:767return eventPtr->xbutton.time;768case KeyPress:769case KeyRelease:770return eventPtr->xkey.time;771case MotionNotify:772return eventPtr->xmotion.time;773case EnterNotify:774case LeaveNotify:775return eventPtr->xcrossing.time;776case PropertyNotify:777return eventPtr->xproperty.time;778}779return dispPtr->lastEventTime;780}781782/*783*----------------------------------------------------------------------784*785* Tk_RestrictEvents --786*787* This procedure is used to globally restrict the set of events788* that will be dispatched. The restriction is done by filtering789* all incoming X events through a procedure that determines790* whether they are to be processed immediately, deferred, or791* discarded.792*793* Results:794* The return value is the previous restriction procedure in effect,795* if there was one, or NULL if there wasn't.796*797* Side effects:798* From now on, proc will be called to determine whether to process,799* defer or discard each incoming X event.800*801*----------------------------------------------------------------------802*/803804Tk_RestrictProc *805Tk_RestrictEvents(proc, arg, prevArgPtr)806Tk_RestrictProc *proc; /* Procedure to call for each incoming807* event. */808ClientData arg; /* Arbitrary argument to pass to proc. */809ClientData *prevArgPtr; /* Place to store information about previous810* argument. */811{812Tk_RestrictProc *prev;813814prev = restrictProc;815*prevArgPtr = restrictArg;816restrictProc = proc;817restrictArg = arg;818return prev;819}820821/*822*----------------------------------------------------------------------823*824* Tk_QueueWindowEvent --825*826* Given an X-style window event, this procedure adds it to the827* Tcl event queue at the given position. This procedure also828* performs mouse motion event collapsing if possible.829*830* Results:831* None.832*833* Side effects:834* Adds stuff to the event queue, which will eventually be835* processed.836*837*----------------------------------------------------------------------838*/839840void841Tk_QueueWindowEvent(eventPtr, position)842XEvent *eventPtr; /* Event to add to queue. This843* procedures copies it before adding844* it to the queue. */845Tcl_QueuePosition position; /* Where to put it on the queue:846* TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,847* or TCL_QUEUE_MARK. */848{849TkWindowEvent *wevPtr;850TkDisplay *dispPtr;851852/*853* Find our display structure for the event's display.854*/855856for (dispPtr = tkDisplayList; ; dispPtr = dispPtr->nextPtr) {857if (dispPtr == NULL) {858return;859}860if (dispPtr->display == eventPtr->xany.display) {861break;862}863}864865if ((dispPtr->delayedMotionPtr != NULL) && (position == TCL_QUEUE_TAIL)) {866if ((eventPtr->type == MotionNotify) && (eventPtr->xmotion.window867== dispPtr->delayedMotionPtr->event.xmotion.window)) {868/*869* The new event is a motion event in the same window as the870* saved motion event. Just replace the saved event with the871* new one.872*/873874dispPtr->delayedMotionPtr->event = *eventPtr;875return;876} else if ((eventPtr->type != GraphicsExpose)877&& (eventPtr->type != NoExpose)878&& (eventPtr->type != Expose)) {879/*880* The new event may conflict with the saved motion event. Queue881* the saved motion event now so that it will be processed before882* the new event.883*/884885Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, position);886dispPtr->delayedMotionPtr = NULL;887Tcl_CancelIdleCall(DelayedMotionProc, (ClientData) dispPtr);888}889}890891wevPtr = (TkWindowEvent *) ckalloc(sizeof(TkWindowEvent));892wevPtr->header.proc = WindowEventProc;893wevPtr->event = *eventPtr;894if ((eventPtr->type == MotionNotify) && (position == TCL_QUEUE_TAIL)) {895/*896* The new event is a motion event so don't queue it immediately;897* save it around in case another motion event arrives that it can898* be collapsed with.899*/900901if (dispPtr->delayedMotionPtr != NULL) {902panic("Tk_QueueWindowEvent found unexpected delayed motion event");903}904dispPtr->delayedMotionPtr = wevPtr;905Tcl_DoWhenIdle(DelayedMotionProc, (ClientData) dispPtr);906} else {907Tcl_QueueEvent(&wevPtr->header, position);908}909}910911/*912*---------------------------------------------------------------------------913*914* TkQueueEventForAllChildren --915*916* Given an XEvent, recursively queue the event for this window and917* all non-toplevel children of the given window.918*919* Results:920* None.921*922* Side effects:923* Events queued.924*925*---------------------------------------------------------------------------926*/927928void929TkQueueEventForAllChildren(tkwin, eventPtr)930Tk_Window tkwin; /* Window to which event is sent. */931XEvent *eventPtr; /* The event to be sent. */932{933TkWindow *winPtr, *childPtr;934935winPtr = (TkWindow *) tkwin;936eventPtr->xany.window = winPtr->window;937Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);938939childPtr = winPtr->childList;940while (childPtr != NULL) {941if (!Tk_IsTopLevel(childPtr)) {942TkQueueEventForAllChildren((Tk_Window) childPtr, eventPtr);943}944childPtr = childPtr->nextPtr;945}946}947948/*949*----------------------------------------------------------------------950*951* WindowEventProc --952*953* This procedure is called by Tcl_DoOneEvent when a window event954* reaches the front of the event queue. This procedure is responsible955* for actually handling the event.956*957* Results:958* Returns 1 if the event was handled, meaning it should be removed959* from the queue. Returns 0 if the event was not handled, meaning960* it should stay on the queue. The event isn't handled if the961* TCL_WINDOW_EVENTS bit isn't set in flags, if a restrict proc962* prevents the event from being handled.963*964* Side effects:965* Whatever the event handlers for the event do.966*967*----------------------------------------------------------------------968*/969970static int971WindowEventProc(evPtr, flags)972Tcl_Event *evPtr; /* Event to service. */973int flags; /* Flags that indicate what events to974* handle, such as TCL_WINDOW_EVENTS. */975{976TkWindowEvent *wevPtr = (TkWindowEvent *) evPtr;977Tk_RestrictAction result;978979if (!(flags & TCL_WINDOW_EVENTS)) {980return 0;981}982if (restrictProc != NULL) {983result = (*restrictProc)(restrictArg, &wevPtr->event);984if (result != TK_PROCESS_EVENT) {985if (result == TK_DEFER_EVENT) {986return 0;987} else {988/*989* TK_DELETE_EVENT: return and say we processed the event,990* even though we didn't do anything at all.991*/992return 1;993}994}995}996Tk_HandleEvent(&wevPtr->event);997return 1;998}9991000/*1001*----------------------------------------------------------------------1002*1003* DelayedMotionProc --1004*1005* This procedure is invoked as an idle handler when a mouse motion1006* event has been delayed. It queues the delayed event so that it1007* will finally be serviced.1008*1009* Results:1010* None.1011*1012* Side effects:1013* The delayed mouse motion event gets added to the Tcl event1014* queue for servicing.1015*1016*----------------------------------------------------------------------1017*/10181019static void1020DelayedMotionProc(clientData)1021ClientData clientData; /* Pointer to display containing a delayed1022* motion event to be serviced. */1023{1024TkDisplay *dispPtr = (TkDisplay *) clientData;10251026if (dispPtr->delayedMotionPtr == NULL) {1027panic("DelayedMotionProc found no delayed mouse motion event");1028}1029Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, TCL_QUEUE_TAIL);1030dispPtr->delayedMotionPtr = NULL;1031}10321033/*1034*--------------------------------------------------------------1035*1036* Tk_MainLoop --1037*1038* Call Tcl_DoOneEvent over and over again in an infinite1039* loop as long as there exist any main windows.1040*1041* Results:1042* None.1043*1044* Side effects:1045* Arbitrary; depends on handlers for events.1046*1047*--------------------------------------------------------------1048*/10491050void1051Tk_MainLoop()1052{1053while (Tk_GetNumMainWindows() > 0) {1054Tcl_DoOneEvent(0);1055}1056}105710581059