summaryrefslogtreecommitdiffstats
path: root/tk8.6/generic/tkEvent.c
diff options
context:
space:
mode:
Diffstat (limited to 'tk8.6/generic/tkEvent.c')
-rw-r--r--tk8.6/generic/tkEvent.c2147
1 files changed, 2147 insertions, 0 deletions
diff --git a/tk8.6/generic/tkEvent.c b/tk8.6/generic/tkEvent.c
new file mode 100644
index 0000000..95aeda1
--- /dev/null
+++ b/tk8.6/generic/tkEvent.c
@@ -0,0 +1,2147 @@
+/*
+ * tkEvent.c --
+ *
+ * This file provides basic low-level facilities for managing X events in
+ * Tk.
+ *
+ * Copyright (c) 1990-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1998-2000 Ajuba Solutions.
+ * Copyright (c) 2004 George Peter Staplin
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tkInt.h"
+
+/*
+ * There's a potential problem if a handler is deleted while it's current
+ * (i.e. its function is executing), since Tk_HandleEvent will need to read
+ * the handler's "nextPtr" field when the function returns. To handle this
+ * problem, structures of the type below indicate the next handler to be
+ * processed for any (recursively nested) dispatches in progress. The
+ * nextHandler fields get updated if the handlers pointed to are deleted.
+ * Tk_HandleEvent also needs to know if the entire window gets deleted; the
+ * winPtr field is set to zero if that particular window gets deleted.
+ */
+
+typedef struct InProgress {
+ XEvent *eventPtr; /* Event currently being handled. */
+ TkWindow *winPtr; /* Window for event. Gets set to None if
+ * window is deleted while event is being
+ * handled. */
+ TkEventHandler *nextHandler;/* Next handler in search. */
+ struct InProgress *nextPtr; /* Next higher nested search. */
+} InProgress;
+
+/*
+ * For each call to Tk_CreateGenericHandler or Tk_CreateClientMessageHandler,
+ * an instance of the following structure will be created. All of the active
+ * handlers are linked into a list.
+ */
+
+typedef struct GenericHandler {
+ Tk_GenericProc *proc; /* Function to dispatch on all X events. */
+ ClientData clientData; /* Client data to pass to function. */
+ int deleteFlag; /* Flag to set when this handler is
+ * deleted. */
+ struct GenericHandler *nextPtr;
+ /* Next handler in list of all generic
+ * handlers, or NULL for end of list. */
+} GenericHandler;
+
+/*
+ * There's a potential problem if Tk_HandleEvent is entered recursively. A
+ * handler cannot be deleted physically until we have returned from calling
+ * it. Otherwise, we're looking at unallocated memory in advancing to its
+ * `next' entry. We deal with the problem by using the `delete flag' and
+ * deleting handlers only when it's known that there's no handler active.
+ */
+
+/*
+ * The following structure is used for queueing X-style events on the Tcl
+ * event queue.
+ */
+
+typedef struct TkWindowEvent {
+ Tcl_Event header; /* Standard information for all events. */
+ XEvent event; /* The X event. */
+} TkWindowEvent;
+
+/*
+ * Array of event masks corresponding to each X event:
+ */
+
+static const unsigned long realEventMasks[MappingNotify+1] = {
+ 0,
+ 0,
+ KeyPressMask, /* KeyPress */
+ KeyReleaseMask, /* KeyRelease */
+ ButtonPressMask, /* ButtonPress */
+ ButtonReleaseMask, /* ButtonRelease */
+ PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
+ |Button1MotionMask|Button2MotionMask|Button3MotionMask
+ |Button4MotionMask|Button5MotionMask,
+ /* MotionNotify */
+ EnterWindowMask, /* EnterNotify */
+ LeaveWindowMask, /* LeaveNotify */
+ FocusChangeMask, /* FocusIn */
+ FocusChangeMask, /* FocusOut */
+ KeymapStateMask, /* KeymapNotify */
+ ExposureMask, /* Expose */
+ ExposureMask, /* GraphicsExpose */
+ ExposureMask, /* NoExpose */
+ VisibilityChangeMask, /* VisibilityNotify */
+ SubstructureNotifyMask, /* CreateNotify */
+ StructureNotifyMask, /* DestroyNotify */
+ StructureNotifyMask, /* UnmapNotify */
+ StructureNotifyMask, /* MapNotify */
+ SubstructureRedirectMask, /* MapRequest */
+ StructureNotifyMask, /* ReparentNotify */
+ StructureNotifyMask, /* ConfigureNotify */
+ SubstructureRedirectMask, /* ConfigureRequest */
+ StructureNotifyMask, /* GravityNotify */
+ ResizeRedirectMask, /* ResizeRequest */
+ StructureNotifyMask, /* CirculateNotify */
+ SubstructureRedirectMask, /* CirculateRequest */
+ PropertyChangeMask, /* PropertyNotify */
+ 0, /* SelectionClear */
+ 0, /* SelectionRequest */
+ 0, /* SelectionNotify */
+ ColormapChangeMask, /* ColormapNotify */
+ 0, /* ClientMessage */
+ 0 /* Mapping Notify */
+};
+
+static const unsigned long virtualEventMasks[TK_LASTEVENT-VirtualEvent] = {
+ VirtualEventMask, /* VirtualEvents */
+ ActivateMask, /* ActivateNotify */
+ ActivateMask, /* DeactivateNotify */
+ MouseWheelMask /* MouseWheelEvent */
+};
+
+/*
+ * For each exit handler created with a call to TkCreateExitHandler or
+ * TkCreateThreadExitHandler there is a structure of the following type:
+ */
+
+typedef struct ExitHandler {
+ Tcl_ExitProc *proc; /* Function to call when process exits. */
+ ClientData clientData; /* One word of information to pass to proc. */
+ struct ExitHandler *nextPtr;/* Next in list of all exit handlers for this
+ * application, or NULL for end of list. */
+} ExitHandler;
+
+/*
+ * The structure below is used to store Data for the Event module that must be
+ * kept thread-local. The "dataKey" is used to fetch the thread-specific
+ * storage for the current thread.
+ */
+
+typedef struct ThreadSpecificData {
+ int handlersActive; /* The following variable has a non-zero value
+ * when a handler is active. */
+ InProgress *pendingPtr; /* Topmost search in progress, or NULL if
+ * none. */
+
+ /*
+ * List of generic handler records.
+ */
+
+ GenericHandler *genericList;/* First handler in the list, or NULL. */
+ GenericHandler *lastGenericPtr;
+ /* Last handler in list. */
+
+ /*
+ * List of client message handler records.
+ */
+
+ GenericHandler *cmList; /* First handler in the list, or NULL. */
+ GenericHandler *lastCmPtr; /* Last handler in list. */
+
+ /*
+ * If someone has called Tk_RestrictEvents, the information below keeps
+ * track of it.
+ */
+
+ Tk_RestrictProc *restrictProc;
+ /* Function to call. NULL means no
+ * restrictProc is currently in effect. */
+ ClientData restrictArg; /* Argument to pass to restrictProc. */
+ ExitHandler *firstExitPtr; /* First in list of all exit handlers for this
+ * thread. */
+ int inExit; /* True when this thread is exiting. This is
+ * used as a hack to decide to close the
+ * standard channels. */
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+
+/*
+ * There are both per-process and per-thread exit handlers. The first list is
+ * controlled by a mutex. The other is in thread local storage.
+ */
+
+static ExitHandler *firstExitPtr = NULL;
+ /* First in list of all exit handlers for
+ * application. */
+TCL_DECLARE_MUTEX(exitMutex)
+
+/*
+ * Prototypes for functions that are only referenced locally within this file.
+ */
+
+static void CleanUpTkEvent(XEvent *eventPtr);
+static void DelayedMotionProc(ClientData clientData);
+static int GetButtonMask(unsigned int Button);
+static unsigned long GetEventMaskFromXEvent(XEvent *eventPtr);
+static TkWindow * GetTkWindowFromXEvent(XEvent *eventPtr);
+static void InvokeClientMessageHandlers(ThreadSpecificData *tsdPtr,
+ Tk_Window tkwin, XEvent *eventPtr);
+static int InvokeFocusHandlers(TkWindow **winPtrPtr,
+ unsigned long mask, XEvent *eventPtr);
+static int InvokeGenericHandlers(ThreadSpecificData *tsdPtr,
+ XEvent *eventPtr);
+static int InvokeMouseHandlers(TkWindow *winPtr,
+ unsigned long mask, XEvent *eventPtr);
+static Window ParentXId(Display *display, Window w);
+static int RefreshKeyboardMappingIfNeeded(XEvent *eventPtr);
+static int TkXErrorHandler(ClientData clientData,
+ XErrorEvent *errEventPtr);
+static void UpdateButtonEventState(XEvent *eventPtr);
+static int WindowEventProc(Tcl_Event *evPtr, int flags);
+#ifdef TK_USE_INPUT_METHODS
+static void CreateXIC(TkWindow *winPtr);
+#endif /* TK_USE_INPUT_METHODS */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeFocusHandlers --
+ *
+ * Call focus-related code to look at FocusIn, FocusOut, Enter, and Leave
+ * events; depending on its return value, ignore the event.
+ *
+ * Results:
+ * 0 further processing can be done on the event.
+ * 1 we are done with the event passed.
+ *
+ * Side effects:
+ * The *winPtrPtr in the caller may be changed to the TkWindow for the
+ * window with focus.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InvokeFocusHandlers(
+ TkWindow **winPtrPtr,
+ unsigned long mask,
+ XEvent *eventPtr)
+{
+ if ((mask & (FocusChangeMask|EnterWindowMask|LeaveWindowMask))
+ && (TkFocusFilterEvent(*winPtrPtr, eventPtr) == 0)) {
+ return 1;
+ }
+
+ /*
+ * Only key-related events are directed according to the focus.
+ */
+
+ if (mask & (KeyPressMask|KeyReleaseMask)) {
+ (*winPtrPtr)->dispPtr->lastEventTime = eventPtr->xkey.time;
+ *winPtrPtr = TkFocusKeyEvent(*winPtrPtr, eventPtr);
+ if (*winPtrPtr == NULL) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeMouseHandlers --
+ *
+ * Call a grab-related function to do special processing on pointer
+ * events.
+ *
+ * Results:
+ * 0 further processing can be done on the event.
+ * 1 we are done with the event passed.
+ *
+ * Side effects:
+ * New events may be queued from TkPointerEvent and grabs may be added
+ * and/or removed. The eventPtr may be changed by TkPointerEvent in some
+ * cases.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InvokeMouseHandlers(
+ TkWindow *winPtr,
+ unsigned long mask,
+ XEvent *eventPtr)
+{
+ if (mask & (ButtonPressMask|ButtonReleaseMask|PointerMotionMask
+ |EnterWindowMask|LeaveWindowMask)) {
+
+ if (mask & (ButtonPressMask|ButtonReleaseMask)) {
+ winPtr->dispPtr->lastEventTime = eventPtr->xbutton.time;
+ } else if (mask & PointerMotionMask) {
+ winPtr->dispPtr->lastEventTime = eventPtr->xmotion.time;
+ } else {
+ winPtr->dispPtr->lastEventTime = eventPtr->xcrossing.time;
+ }
+
+ if (TkPointerEvent(eventPtr, winPtr) == 0) {
+ /*
+ * The event should be ignored to make grab work correctly (as the
+ * comment for TkPointerEvent states).
+ */
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateXIC --
+ *
+ * Create the X input context for our winPtr.
+ * XIM is only ever enabled on Unix.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifdef TK_USE_INPUT_METHODS
+static void
+CreateXIC(
+ TkWindow *winPtr)
+{
+ TkDisplay *dispPtr = winPtr->dispPtr;
+ long im_event_mask = 0L;
+ const char *preedit_attname = NULL;
+ XVaNestedList preedit_attlist = NULL;
+
+ if (dispPtr->inputStyle & XIMPreeditPosition) {
+ XPoint spot = {0, 0};
+
+ preedit_attname = XNPreeditAttributes;
+ preedit_attlist = XVaCreateNestedList(0,
+ XNSpotLocation, &spot,
+ XNFontSet, dispPtr->inputXfs,
+ NULL);
+ }
+
+ winPtr->inputContext = XCreateIC(dispPtr->inputMethod,
+ XNInputStyle, dispPtr->inputStyle,
+ XNClientWindow, winPtr->window,
+ XNFocusWindow, winPtr->window,
+ preedit_attname, preedit_attlist,
+ NULL);
+
+ if (preedit_attlist) {
+ XFree(preedit_attlist);
+ }
+
+
+ if (winPtr->inputContext == NULL) {
+ /* XCreateIC failed. */
+ return;
+ }
+
+ /*
+ * Adjust the window's event mask if the IM requires it.
+ */
+ XGetICValues(winPtr->inputContext, XNFilterEvents, &im_event_mask, NULL);
+ if ((winPtr->atts.event_mask & im_event_mask) != im_event_mask) {
+ winPtr->atts.event_mask |= im_event_mask;
+ XSelectInput(winPtr->display, winPtr->window, winPtr->atts.event_mask);
+ }
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTkWindowFromXEvent --
+ *
+ * Attempt to find which TkWindow is associated with an event. If it
+ * fails we attempt to get the TkWindow from the parent for a property
+ * notification.
+ *
+ * Results:
+ * The TkWindow associated with the event or NULL.
+ *
+ * Side effects:
+ * TkSelPropProc may influence selection on windows not known to Tk.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static TkWindow *
+GetTkWindowFromXEvent(
+ XEvent *eventPtr)
+{
+ TkWindow *winPtr;
+ Window parentXId, handlerWindow = eventPtr->xany.window;
+
+ if ((eventPtr->xany.type == StructureNotifyMask)
+ && (eventPtr->xmap.event != eventPtr->xmap.window)) {
+ handlerWindow = eventPtr->xmap.event;
+ }
+
+ winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow);
+
+ if (winPtr == NULL) {
+ /*
+ * There isn't a TkWindow structure for this window. However, if the
+ * event is a PropertyNotify event then call the selection manager (it
+ * deals beneath-the-table with certain properties). Also, if the
+ * window's parent is a Tk window that has the TK_PROP_PROPCHANGE flag
+ * set, then we must propagate the PropertyNotify event up to the
+ * parent.
+ */
+
+ if (eventPtr->type != PropertyNotify) {
+ return NULL;
+ }
+ TkSelPropProc(eventPtr);
+ parentXId = ParentXId(eventPtr->xany.display, handlerWindow);
+ if (parentXId == None) {
+ return NULL;
+ }
+ winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, parentXId);
+ if (winPtr == NULL) {
+ return NULL;
+ }
+ if (!(winPtr->flags & TK_PROP_PROPCHANGE)) {
+ return NULL;
+ }
+ }
+ return winPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetEventMaskFromXEvent --
+ *
+ * The event type is looked up in our eventMasks tables, and may be
+ * changed to a different mask depending on the state of the event and
+ * window members.
+ *
+ * Results:
+ * The mask for the event.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static unsigned long
+GetEventMaskFromXEvent(
+ XEvent *eventPtr)
+{
+ unsigned long mask;
+
+ /*
+ * Get the event mask from the correct table. Note that there are two
+ * tables here because that means we no longer need this code to rely on
+ * the exact value of VirtualEvent, which has caused us problems in the
+ * past when X11 changed the value of LASTEvent. [Bug ???]
+ */
+
+ if (eventPtr->xany.type <= MappingNotify) {
+ mask = realEventMasks[eventPtr->xany.type];
+ } else if (eventPtr->xany.type >= VirtualEvent
+ && eventPtr->xany.type<TK_LASTEVENT) {
+ mask = virtualEventMasks[eventPtr->xany.type - VirtualEvent];
+ } else {
+ mask = 0;
+ }
+
+ /*
+ * Events selected by StructureNotify require special handling. They look
+ * the same as those selected by SubstructureNotify. The only difference
+ * is whether the "event" and "window" fields are the same. Compare the
+ * two fields and convert StructureNotify to SubstructureNotify if
+ * necessary.
+ */
+
+ if (mask == StructureNotifyMask) {
+ if (eventPtr->xmap.event != eventPtr->xmap.window) {
+ mask = SubstructureNotifyMask;
+ }
+ }
+ return mask;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RefreshKeyboardMappingIfNeeded --
+ *
+ * If the event is a MappingNotify event, find its display and refresh
+ * the keyboard mapping information for the display.
+ *
+ * Results:
+ * 0 if the event was not a MappingNotify event
+ * 1 if the event was a MappingNotify event
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+RefreshKeyboardMappingIfNeeded(
+ XEvent *eventPtr)
+{
+ TkDisplay *dispPtr;
+
+ if (eventPtr->type == MappingNotify) {
+ dispPtr = TkGetDisplay(eventPtr->xmapping.display);
+ if (dispPtr != NULL) {
+ XRefreshKeyboardMapping(&eventPtr->xmapping);
+ dispPtr->bindInfoStale = 1;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetButtonMask --
+ *
+ * Return the proper Button${n}Mask for the button.
+ *
+ * Results:
+ * A button mask.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetButtonMask(
+ unsigned int button)
+{
+ switch (button) {
+ case 1:
+ return Button1Mask;
+ case 2:
+ return Button2Mask;
+ case 3:
+ return Button3Mask;
+ case 4:
+ return Button4Mask;
+ case 5:
+ return Button5Mask;
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateButtonEventState --
+ *
+ * Update the button event state in our TkDisplay using the XEvent
+ * passed. We also may modify the the XEvent passed to fit some aspects
+ * of our TkDisplay.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The TkDisplay's private button state may be modified. The eventPtr's
+ * state may be updated to reflect masks stored in our TkDisplay that the
+ * event doesn't contain. The eventPtr may also be modified to not
+ * contain a button state for the window in which it was not pressed in.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateButtonEventState(
+ XEvent *eventPtr)
+{
+ TkDisplay *dispPtr;
+ int allButtonsMask = Button1Mask | Button2Mask | Button3Mask
+ | Button4Mask | Button5Mask;
+
+ switch (eventPtr->type) {
+ case ButtonPress:
+ dispPtr = TkGetDisplay(eventPtr->xbutton.display);
+ dispPtr->mouseButtonWindow = eventPtr->xbutton.window;
+ eventPtr->xbutton.state |= dispPtr->mouseButtonState;
+
+ dispPtr->mouseButtonState |= GetButtonMask(eventPtr->xbutton.button);
+ break;
+
+ case ButtonRelease:
+ dispPtr = TkGetDisplay(eventPtr->xbutton.display);
+ dispPtr->mouseButtonWindow = None;
+ dispPtr->mouseButtonState &= ~GetButtonMask(eventPtr->xbutton.button);
+ eventPtr->xbutton.state |= dispPtr->mouseButtonState;
+ break;
+
+ case MotionNotify:
+ dispPtr = TkGetDisplay(eventPtr->xmotion.display);
+ if (dispPtr->mouseButtonState & allButtonsMask) {
+ if (eventPtr->xbutton.window != dispPtr->mouseButtonWindow) {
+ /*
+ * This motion event should not be interpreted as a button
+ * press + motion event since this is not the same window the
+ * button was pressed down in.
+ */
+
+ dispPtr->mouseButtonState &= ~allButtonsMask;
+ dispPtr->mouseButtonWindow = None;
+ } else {
+ eventPtr->xmotion.state |= dispPtr->mouseButtonState;
+ }
+ }
+ break;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeClientMessageHandlers --
+ *
+ * Iterate the list of handlers and invoke the function pointer for each.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Handlers may be deleted and events may be sent to handlers.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InvokeClientMessageHandlers(
+ ThreadSpecificData *tsdPtr,
+ Tk_Window tkwin,
+ XEvent *eventPtr)
+{
+ GenericHandler *prevPtr, *tmpPtr, *curPtr = tsdPtr->cmList;
+
+ for (prevPtr = NULL; curPtr != NULL; ) {
+ if (curPtr->deleteFlag) {
+ if (!tsdPtr->handlersActive) {
+ /*
+ * This handler needs to be deleted and there are no calls
+ * pending through any handlers, so now is a safe time to
+ * delete it.
+ */
+
+ tmpPtr = curPtr->nextPtr;
+ if (prevPtr == NULL) {
+ tsdPtr->cmList = tmpPtr;
+ } else {
+ prevPtr->nextPtr = tmpPtr;
+ }
+ if (tmpPtr == NULL) {
+ tsdPtr->lastCmPtr = prevPtr;
+ }
+ ckfree(curPtr);
+ curPtr = tmpPtr;
+ continue;
+ }
+ } else {
+ int done;
+
+ tsdPtr->handlersActive++;
+ done = (*(Tk_ClientMessageProc *)curPtr->proc)(tkwin, eventPtr);
+ tsdPtr->handlersActive--;
+ if (done) {
+ break;
+ }
+ }
+ prevPtr = curPtr;
+ curPtr = curPtr->nextPtr;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvokeGenericHandlers --
+ *
+ * Iterate the list of handlers and invoke the function pointer for each.
+ * If the handler invoked returns a non-zero value then we are done.
+ *
+ * Results:
+ * 0 when the event wasn't handled by a handler. Non-zero when it was
+ * processed and handled by a handler.
+ *
+ * Side effects:
+ * Handlers may be deleted and events may be sent to handlers.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InvokeGenericHandlers(
+ ThreadSpecificData *tsdPtr,
+ XEvent *eventPtr)
+{
+ GenericHandler *prevPtr, *tmpPtr, *curPtr = tsdPtr->genericList;
+
+ for (prevPtr = NULL; curPtr != NULL; ) {
+ if (curPtr->deleteFlag) {
+ if (!tsdPtr->handlersActive) {
+ /*
+ * This handler needs to be deleted and there are no calls
+ * pending through the handler, so now is a safe time to
+ * delete it.
+ */
+
+ tmpPtr = curPtr->nextPtr;
+ if (prevPtr == NULL) {
+ tsdPtr->genericList = tmpPtr;
+ } else {
+ prevPtr->nextPtr = tmpPtr;
+ }
+ if (tmpPtr == NULL) {
+ tsdPtr->lastGenericPtr = prevPtr;
+ }
+ ckfree(curPtr);
+ curPtr = tmpPtr;
+ continue;
+ }
+ } else {
+ int done;
+
+ tsdPtr->handlersActive++;
+ done = curPtr->proc(curPtr->clientData, eventPtr);
+ tsdPtr->handlersActive--;
+ if (done) {
+ return done;
+ }
+ }
+ prevPtr = curPtr;
+ curPtr = curPtr->nextPtr;
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_CreateEventHandler --
+ *
+ * Arrange for a given function to be invoked whenever events from a
+ * given class occur in a given window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * From now on, whenever an event of the type given by mask occurs for
+ * token and is processed by Tk_HandleEvent, proc will be called. See the
+ * manual entry for details of the calling sequence and return value for
+ * proc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_CreateEventHandler(
+ Tk_Window token, /* Token for window in which to create
+ * handler. */
+ unsigned long mask, /* Events for which proc should be called. */
+ Tk_EventProc *proc, /* Function to call for each selected event */
+ ClientData clientData) /* Arbitrary data to pass to proc. */
+{
+ register TkEventHandler *handlerPtr;
+ register TkWindow *winPtr = (TkWindow *) token;
+
+ /*
+ * Skim through the list of existing handlers to (a) compute the overall
+ * event mask for the window (so we can pass this new value to the X
+ * system) and (b) see if there's already a handler declared with the same
+ * callback and clientData (if so, just change the mask). If no existing
+ * handler matches, then create a new handler.
+ */
+
+ if (winPtr->handlerList == NULL) {
+ /*
+ * No event handlers defined at all, so must create.
+ */
+
+ handlerPtr = ckalloc(sizeof(TkEventHandler));
+ winPtr->handlerList = handlerPtr;
+ } else {
+ int found = 0;
+
+ for (handlerPtr = winPtr->handlerList; ;
+ handlerPtr = handlerPtr->nextPtr) {
+ if ((handlerPtr->proc == proc)
+ && (handlerPtr->clientData == clientData)) {
+ handlerPtr->mask = mask;
+ found = 1;
+ }
+ if (handlerPtr->nextPtr == NULL) {
+ break;
+ }
+ }
+
+ /*
+ * If we found anything, we're done because we do not need to use
+ * XSelectInput; Tk always selects on all events anyway in order to
+ * support binding on classes, 'all' and other bind-tags.
+ */
+
+ if (found) {
+ return;
+ }
+
+ /*
+ * No event handler matched, so create a new one.
+ */
+
+ handlerPtr->nextPtr = ckalloc(sizeof(TkEventHandler));
+ handlerPtr = handlerPtr->nextPtr;
+ }
+
+ /*
+ * Initialize the new event handler.
+ */
+
+ handlerPtr->mask = mask;
+ handlerPtr->proc = proc;
+ handlerPtr->clientData = clientData;
+ handlerPtr->nextPtr = NULL;
+
+ /*
+ * No need to call XSelectInput: Tk always selects on all events for all
+ * windows (needed to support bindings on classes and "all").
+ */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_DeleteEventHandler --
+ *
+ * Delete a previously-created handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If there existed a handler as described by the parameters, the handler
+ * is deleted so that proc will not be invoked again.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_DeleteEventHandler(
+ Tk_Window token, /* Same as corresponding arguments passed */
+ unsigned long mask, /* previously to Tk_CreateEventHandler. */
+ Tk_EventProc *proc,
+ ClientData clientData)
+{
+ register TkEventHandler *handlerPtr;
+ register InProgress *ipPtr;
+ TkEventHandler *prevPtr;
+ register TkWindow *winPtr = (TkWindow *) token;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ /*
+ * Find the event handler to be deleted, or return immediately if it
+ * doesn't exist.
+ */
+
+ for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ;
+ prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) {
+ if (handlerPtr == NULL) {
+ return;
+ }
+ if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc)
+ && (handlerPtr->clientData == clientData)) {
+ break;
+ }
+ }
+
+ /*
+ * If Tk_HandleEvent is about to process this handler, tell it to process
+ * the next one instead.
+ */
+
+ for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
+ if (ipPtr->nextHandler == handlerPtr) {
+ ipPtr->nextHandler = handlerPtr->nextPtr;
+ }
+ }
+
+ /*
+ * Free resources associated with the handler.
+ */
+
+ if (prevPtr == NULL) {
+ winPtr->handlerList = handlerPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = handlerPtr->nextPtr;
+ }
+ ckfree(handlerPtr);
+
+ /*
+ * No need to call XSelectInput: Tk always selects on all events for all
+ * windows (needed to support bindings on classes and "all").
+ */
+}
+
+/*----------------------------------------------------------------------
+ *
+ * Tk_CreateGenericHandler --
+ *
+ * Register a function to be called on each X event, regardless of
+ * display or window. Generic handlers are useful for capturing events
+ * that aren't associated with windows, or events for windows not managed
+ * by Tk.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * From now on, whenever an X event is given to Tk_HandleEvent, invoke
+ * proc, giving it clientData and the event as arguments.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_CreateGenericHandler(
+ Tk_GenericProc *proc, /* Function to call on every event. */
+ ClientData clientData) /* One-word value to pass to proc. */
+{
+ GenericHandler *handlerPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ handlerPtr = ckalloc(sizeof(GenericHandler));
+
+ handlerPtr->proc = proc;
+ handlerPtr->clientData = clientData;
+ handlerPtr->deleteFlag = 0;
+ handlerPtr->nextPtr = NULL;
+ if (tsdPtr->genericList == NULL) {
+ tsdPtr->genericList = handlerPtr;
+ } else {
+ tsdPtr->lastGenericPtr->nextPtr = handlerPtr;
+ }
+ tsdPtr->lastGenericPtr = handlerPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_DeleteGenericHandler --
+ *
+ * Delete a previously-created generic handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * If there existed a handler as described by the parameters, that
+ * handler is logically deleted so that proc will not be invoked again.
+ * The physical deletion happens in the event loop in Tk_HandleEvent.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_DeleteGenericHandler(
+ Tk_GenericProc *proc,
+ ClientData clientData)
+{
+ GenericHandler * handler;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ for (handler=tsdPtr->genericList ; handler ; handler=handler->nextPtr) {
+ if ((handler->proc == proc) && (handler->clientData == clientData)) {
+ handler->deleteFlag = 1;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------
+ *
+ * Tk_CreateClientMessageHandler --
+ *
+ * Register a function to be called on each ClientMessage event.
+ * ClientMessage handlers are useful for Drag&Drop extensions.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * From now on, whenever a ClientMessage event is received that isn't a
+ * WM_PROTOCOL event or SelectionEvent, invoke proc, giving it tkwin and
+ * the event as arguments.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_CreateClientMessageHandler(
+ Tk_ClientMessageProc *proc) /* Function to call on event. */
+{
+ GenericHandler *handlerPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ /*
+ * We use a GenericHandler struct, because it's basically the same, except
+ * with an extra clientData field we'll never use.
+ */
+
+ handlerPtr = ckalloc(sizeof(GenericHandler));
+
+ handlerPtr->proc = (Tk_GenericProc *) proc;
+ handlerPtr->clientData = NULL; /* never used */
+ handlerPtr->deleteFlag = 0;
+ handlerPtr->nextPtr = NULL;
+ if (tsdPtr->cmList == NULL) {
+ tsdPtr->cmList = handlerPtr;
+ } else {
+ tsdPtr->lastCmPtr->nextPtr = handlerPtr;
+ }
+ tsdPtr->lastCmPtr = handlerPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_DeleteClientMessageHandler --
+ *
+ * Delete a previously-created ClientMessage handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * If there existed a handler as described by the parameters, that
+ * handler is logically deleted so that proc will not be invoked again.
+ * The physical deletion happens in the event loop in
+ * TkClientMessageEventProc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_DeleteClientMessageHandler(
+ Tk_ClientMessageProc *proc)
+{
+ GenericHandler * handler;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ for (handler=tsdPtr->cmList ; handler!=NULL ; handler=handler->nextPtr) {
+ if (handler->proc == (Tk_GenericProc *) proc) {
+ handler->deleteFlag = 1;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkEventInit --
+ *
+ * This functions initializes all the event module structures used by the
+ * current thread. It must be called before any other function in this
+ * file is called.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkEventInit(void)
+{
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ tsdPtr->handlersActive = 0;
+ tsdPtr->pendingPtr = NULL;
+ tsdPtr->genericList = NULL;
+ tsdPtr->lastGenericPtr = NULL;
+ tsdPtr->cmList = NULL;
+ tsdPtr->lastCmPtr = NULL;
+ tsdPtr->restrictProc = NULL;
+ tsdPtr->restrictArg = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkXErrorHandler --
+ *
+ * TkXErrorHandler is an error handler, to be installed via
+ * Tk_CreateErrorHandler, that will set a flag if an X error occurred.
+ *
+ * Results:
+ * Always returns 0, indicating that the X error was handled.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TkXErrorHandler(
+ ClientData clientData, /* Pointer to flag we set. */
+ XErrorEvent *errEventPtr) /* X error info. */
+{
+ int *error = clientData;
+
+ *error = 1;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParentXId --
+ *
+ * Returns the parent of the given window, or "None" if the window
+ * doesn't exist.
+ *
+ * Results:
+ * Returns an X window ID.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Window
+ParentXId(
+ Display *display,
+ Window w)
+{
+ Tk_ErrorHandler handler;
+ int gotXError;
+ Status status;
+ Window parent;
+ Window root;
+ Window *childList;
+ unsigned int nChildren;
+
+ /*
+ * Handle errors ourselves.
+ */
+
+ gotXError = 0;
+ handler = Tk_CreateErrorHandler(display, -1, -1, -1,
+ TkXErrorHandler, &gotXError);
+
+ /*
+ * Get the parent window.
+ */
+
+ status = XQueryTree(display, w, &root, &parent, &childList, &nChildren);
+
+ /*
+ * Do some cleanup; gotta return "None" if we got an error.
+ */
+
+ Tk_DeleteErrorHandler(handler);
+ XSync(display, False);
+ if (status != 0 && childList != NULL) {
+ XFree(childList);
+ }
+ if (status == 0) {
+ parent = None;
+ }
+
+ return parent;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_HandleEvent --
+ *
+ * Given an event, invoke all the handlers that have been registered for
+ * the event.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the handlers.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_HandleEvent(
+ XEvent *eventPtr) /* Event to dispatch. */
+{
+ register TkEventHandler *handlerPtr;
+ TkWindow *winPtr;
+ unsigned long mask;
+ InProgress ip;
+ Tcl_Interp *interp = NULL;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ UpdateButtonEventState(eventPtr);
+
+ /*
+ * If the generic handler processed this event we are done and can return.
+ */
+
+ if (InvokeGenericHandlers(tsdPtr, eventPtr)) {
+ goto releaseEventResources;
+ }
+
+ if (RefreshKeyboardMappingIfNeeded(eventPtr)) {
+ /*
+ * We are done with a MappingNotify event.
+ */
+
+ goto releaseEventResources;
+ }
+
+ mask = GetEventMaskFromXEvent(eventPtr);
+ winPtr = GetTkWindowFromXEvent(eventPtr);
+
+ if (winPtr == NULL) {
+ goto releaseEventResources;
+ }
+
+ /*
+ * Once a window has started getting deleted, don't process any more
+ * events for it except for the DestroyNotify event. This check is needed
+ * because a DestroyNotify handler could re-invoke the event loop, causing
+ * other pending events to be handled for the window (the window doesn't
+ * get totally expunged from our tables until after the DestroyNotify
+ * event has been completely handled).
+ */
+
+ if ((winPtr->flags & TK_ALREADY_DEAD)
+ && (eventPtr->type != DestroyNotify)) {
+ goto releaseEventResources;
+ }
+
+ if (winPtr->mainPtr != NULL) {
+ int result;
+
+ interp = winPtr->mainPtr->interp;
+
+ /*
+ * Protect interpreter for this window from possible deletion while we
+ * are dealing with the event for this window. Thus, widget writers do
+ * not have to worry about protecting the interpreter in their own
+ * code.
+ */
+
+ Tcl_Preserve(interp);
+
+ result = ((InvokeFocusHandlers(&winPtr, mask, eventPtr))
+ || (InvokeMouseHandlers(winPtr, mask, eventPtr)));
+
+ if (result) {
+ goto releaseInterpreter;
+ }
+ }
+
+ /*
+ * Create the input context for the window if it hasn't already been done
+ * (XFilterEvent needs this context). When the event is a FocusIn event,
+ * set the input context focus to the receiving window. This code is only
+ * ever active for X11.
+ */
+
+#ifdef TK_USE_INPUT_METHODS
+ if ((winPtr->dispPtr->flags & TK_DISPLAY_USE_IM)) {
+ if (!(winPtr->flags & (TK_CHECKED_IC|TK_ALREADY_DEAD))) {
+ winPtr->flags |= TK_CHECKED_IC;
+ if (winPtr->dispPtr->inputMethod != NULL) {
+ CreateXIC(winPtr);
+ }
+ }
+ if (eventPtr->type == FocusIn && winPtr->inputContext != NULL) {
+ XSetICFocus(winPtr->inputContext);
+ }
+ }
+#endif /*TK_USE_INPUT_METHODS*/
+
+ /*
+ * For events where it hasn't already been done, update the current time
+ * in the display.
+ */
+
+ if (eventPtr->type == PropertyNotify) {
+ winPtr->dispPtr->lastEventTime = eventPtr->xproperty.time;
+ }
+
+ /*
+ * There's a potential interaction here with Tk_DeleteEventHandler. Read
+ * the documentation for pendingPtr.
+ */
+
+ ip.eventPtr = eventPtr;
+ ip.winPtr = winPtr;
+ ip.nextHandler = NULL;
+ ip.nextPtr = tsdPtr->pendingPtr;
+ tsdPtr->pendingPtr = &ip;
+ if (mask == 0) {
+ if ((eventPtr->type == SelectionClear)
+ || (eventPtr->type == SelectionRequest)
+ || (eventPtr->type == SelectionNotify)) {
+ TkSelEventProc((Tk_Window) winPtr, eventPtr);
+ } else if (eventPtr->type == ClientMessage) {
+ if (eventPtr->xclient.message_type ==
+ Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS")) {
+ TkWmProtocolEventProc(winPtr, eventPtr);
+ } else {
+ InvokeClientMessageHandlers(tsdPtr, (Tk_Window) winPtr,
+ eventPtr);
+ }
+ }
+ } else {
+ for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {
+ if (handlerPtr->mask & mask) {
+ ip.nextHandler = handlerPtr->nextPtr;
+ handlerPtr->proc(handlerPtr->clientData, eventPtr);
+ handlerPtr = ip.nextHandler;
+ } else {
+ handlerPtr = handlerPtr->nextPtr;
+ }
+ }
+
+ /*
+ * Pass the event to the "bind" command mechanism. But, don't do this
+ * for SubstructureNotify events. The "bind" command doesn't support
+ * them anyway, and it's easier to filter out these events here than
+ * in the lower-level functions.
+ */
+
+ /*
+ * ...well, except when we use the tkwm patches, in which case we DO
+ * handle CreateNotify events, so we gotta pass 'em through.
+ */
+
+ if ((ip.winPtr != None)
+ && ((mask != SubstructureNotifyMask)
+ || (eventPtr->type == CreateNotify))) {
+ TkBindEventProc(winPtr, eventPtr);
+ }
+ }
+ tsdPtr->pendingPtr = ip.nextPtr;
+
+ /*
+ * Release the interpreter for this window so that it can be potentially
+ * deleted if requested.
+ */
+
+ releaseInterpreter:
+ if (interp != NULL) {
+ Tcl_Release(interp);
+ }
+
+ /*
+ * Release the user_data from the event (if it is a virtual event and the
+ * field was non-NULL in the first place.) Note that this is done using a
+ * Tcl_Obj interface, and we set the field back to NULL afterwards out of
+ * paranoia. Also clean up any cached %A substitutions from key events.
+ */
+
+ releaseEventResources:
+ CleanUpTkEvent(eventPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkEventDeadWindow --
+ *
+ * This function is invoked when it is determined that a window is dead.
+ * It cleans up event-related information about the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Various things get cleaned up and recycled.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkEventDeadWindow(
+ TkWindow *winPtr) /* Information about the window that is being
+ * deleted. */
+{
+ register TkEventHandler *handlerPtr;
+ register InProgress *ipPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ /*
+ * While deleting all the handlers, be careful to check for Tk_HandleEvent
+ * being about to process one of the deleted handlers. If it is, tell it
+ * to quit (all of the handlers are being deleted).
+ */
+
+ while (winPtr->handlerList != NULL) {
+ handlerPtr = winPtr->handlerList;
+ winPtr->handlerList = handlerPtr->nextPtr;
+ for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
+ ipPtr = ipPtr->nextPtr) {
+ if (ipPtr->nextHandler == handlerPtr) {
+ ipPtr->nextHandler = NULL;
+ }
+ if (ipPtr->winPtr == winPtr) {
+ ipPtr->winPtr = None;
+ }
+ }
+ ckfree(handlerPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkCurrentTime --
+ *
+ * Try to deduce the current time. "Current time" means the time of the
+ * event that led to the current code being executed, which means the
+ * time in the most recently-nested invocation of Tk_HandleEvent.
+ *
+ * Results:
+ * The return value is the time from the current event, or CurrentTime if
+ * there is no current event or if the current event contains no time.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Time
+TkCurrentTime(
+ TkDisplay *dispPtr) /* Display for which the time is desired. */
+{
+ register XEvent *eventPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ if (tsdPtr->pendingPtr == NULL) {
+ return dispPtr->lastEventTime;
+ }
+ eventPtr = tsdPtr->pendingPtr->eventPtr;
+ switch (eventPtr->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ return eventPtr->xbutton.time;
+ case KeyPress:
+ case KeyRelease:
+ return eventPtr->xkey.time;
+ case MotionNotify:
+ return eventPtr->xmotion.time;
+ case EnterNotify:
+ case LeaveNotify:
+ return eventPtr->xcrossing.time;
+ case PropertyNotify:
+ return eventPtr->xproperty.time;
+ }
+ return dispPtr->lastEventTime;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_RestrictEvents --
+ *
+ * This function is used to globally restrict the set of events that will
+ * be dispatched. The restriction is done by filtering all incoming X
+ * events through a function that determines whether they are to be
+ * processed immediately, deferred, or discarded.
+ *
+ * Results:
+ * The return value is the previous restriction function in effect, if
+ * there was one, or NULL if there wasn't.
+ *
+ * Side effects:
+ * From now on, proc will be called to determine whether to process,
+ * defer or discard each incoming X event.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tk_RestrictProc *
+Tk_RestrictEvents(
+ Tk_RestrictProc *proc, /* Function to call for each incoming event */
+ ClientData arg, /* Arbitrary argument to pass to proc. */
+ ClientData *prevArgPtr) /* Place to store information about previous
+ * argument. */
+{
+ Tk_RestrictProc *prev;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ prev = tsdPtr->restrictProc;
+ *prevArgPtr = tsdPtr->restrictArg;
+ tsdPtr->restrictProc = proc;
+ tsdPtr->restrictArg = arg;
+ return prev;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_CollapseMotionEvents --
+ *
+ * This function controls whether we collapse motion events in a
+ * particular display or not.
+ *
+ * Results:
+ * The return value is the previous collapse value in effect.
+ *
+ * Side effects:
+ * Filtering of motion events may be changed after calling this.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_CollapseMotionEvents(
+ Display *display, /* Display handling these events. */
+ int collapse) /* Boolean value that specifies whether motion
+ * events should be collapsed. */
+{
+ TkDisplay *dispPtr = (TkDisplay *) display;
+ int prev = (dispPtr->flags & TK_DISPLAY_COLLAPSE_MOTION_EVENTS);
+
+ if (collapse) {
+ dispPtr->flags |= TK_DISPLAY_COLLAPSE_MOTION_EVENTS;
+ } else {
+ dispPtr->flags &= ~TK_DISPLAY_COLLAPSE_MOTION_EVENTS;
+ }
+ return prev;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_QueueWindowEvent --
+ *
+ * Given an X-style window event, this function adds it to the Tcl event
+ * queue at the given position. This function also performs mouse motion
+ * event collapsing if possible.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Adds stuff to the event queue, which will eventually be processed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_QueueWindowEvent(
+ XEvent *eventPtr, /* Event to add to queue. This function copies
+ * it before adding it to the queue. */
+ Tcl_QueuePosition position) /* Where to put it on the queue:
+ * TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, or
+ * TCL_QUEUE_MARK. */
+{
+ TkWindowEvent *wevPtr;
+ TkDisplay *dispPtr;
+
+ /*
+ * Find our display structure for the event's display.
+ */
+
+ for (dispPtr = TkGetDisplayList(); ; dispPtr = dispPtr->nextPtr) {
+ if (dispPtr == NULL) {
+ return;
+ }
+ if (dispPtr->display == eventPtr->xany.display) {
+ break;
+ }
+ }
+
+ /*
+ * Don't filter motion events if the user defaulting to true (1), which
+ * could be set to false (0) when the user wishes to receive all the
+ * motion data)
+ */
+
+ if (!(dispPtr->flags & TK_DISPLAY_COLLAPSE_MOTION_EVENTS)) {
+ wevPtr = ckalloc(sizeof(TkWindowEvent));
+ wevPtr->header.proc = WindowEventProc;
+ wevPtr->event = *eventPtr;
+ Tcl_QueueEvent(&wevPtr->header, position);
+ return;
+ }
+
+ if ((dispPtr->delayedMotionPtr != NULL) && (position == TCL_QUEUE_TAIL)) {
+ if ((eventPtr->type == MotionNotify) && (eventPtr->xmotion.window
+ == dispPtr->delayedMotionPtr->event.xmotion.window)) {
+ /*
+ * The new event is a motion event in the same window as the saved
+ * motion event. Just replace the saved event with the new one.
+ */
+
+ dispPtr->delayedMotionPtr->event = *eventPtr;
+ return;
+ } else if ((eventPtr->type != GraphicsExpose)
+ && (eventPtr->type != NoExpose)
+ && (eventPtr->type != Expose)) {
+ /*
+ * The new event may conflict with the saved motion event. Queue
+ * the saved motion event now so that it will be processed before
+ * the new event.
+ */
+
+ Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, position);
+ dispPtr->delayedMotionPtr = NULL;
+ Tcl_CancelIdleCall(DelayedMotionProc, dispPtr);
+ }
+ }
+
+ wevPtr = ckalloc(sizeof(TkWindowEvent));
+ wevPtr->header.proc = WindowEventProc;
+ wevPtr->event = *eventPtr;
+ if ((eventPtr->type == MotionNotify) && (position == TCL_QUEUE_TAIL)) {
+ /*
+ * The new event is a motion event so don't queue it immediately; save
+ * it around in case another motion event arrives that it can be
+ * collapsed with.
+ */
+
+ if (dispPtr->delayedMotionPtr != NULL) {
+ Tcl_Panic("Tk_QueueWindowEvent found unexpected delayed motion event");
+ }
+ dispPtr->delayedMotionPtr = wevPtr;
+ Tcl_DoWhenIdle(DelayedMotionProc, dispPtr);
+ } else {
+ Tcl_QueueEvent(&wevPtr->header, position);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkQueueEventForAllChildren --
+ *
+ * Given an XEvent, recursively queue the event for this window and all
+ * non-toplevel children of the given window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Events queued.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkQueueEventForAllChildren(
+ TkWindow *winPtr, /* Window to which event is sent. */
+ XEvent *eventPtr) /* The event to be sent. */
+{
+ TkWindow *childPtr;
+
+ if (!Tk_IsMapped(winPtr)) {
+ return;
+ }
+
+ eventPtr->xany.window = winPtr->window;
+ Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);
+
+ childPtr = winPtr->childList;
+ while (childPtr != NULL) {
+ if (!Tk_TopWinHierarchy(childPtr)) {
+ TkQueueEventForAllChildren(childPtr, eventPtr);
+ }
+ childPtr = childPtr->nextPtr;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WindowEventProc --
+ *
+ * This function is called by Tcl_DoOneEvent when a window event reaches
+ * the front of the event queue. This function is responsible for
+ * actually handling the event.
+ *
+ * Results:
+ * Returns 1 if the event was handled, meaning it should be removed from
+ * the queue. Returns 0 if the event was not handled, meaning it should
+ * stay on the queue. The event isn't handled if the TCL_WINDOW_EVENTS
+ * bit isn't set in flags, if a restrict proc prevents the event from
+ * being handled.
+ *
+ * Side effects:
+ * Whatever the event handlers for the event do.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+WindowEventProc(
+ Tcl_Event *evPtr, /* Event to service. */
+ int flags) /* Flags that indicate what events to handle,
+ * such as TCL_WINDOW_EVENTS. */
+{
+ TkWindowEvent *wevPtr = (TkWindowEvent *) evPtr;
+ Tk_RestrictAction result;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ if (!(flags & TCL_WINDOW_EVENTS)) {
+ return 0;
+ }
+ if (tsdPtr->restrictProc != NULL) {
+ result = tsdPtr->restrictProc(tsdPtr->restrictArg, &wevPtr->event);
+ if (result != TK_PROCESS_EVENT) {
+ if (result == TK_DEFER_EVENT) {
+ return 0;
+ } else {
+ /*
+ * TK_DELETE_EVENT: return and say we processed the event,
+ * even though we didn't do anything at all.
+ */
+
+ CleanUpTkEvent(&wevPtr->event);
+ return 1;
+ }
+ }
+ }
+ Tk_HandleEvent(&wevPtr->event);
+ CleanUpTkEvent(&wevPtr->event);
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CleanUpTkEvent --
+ *
+ * This function is called to remove and deallocate any information in
+ * the event which is not directly in the event structure itself. It may
+ * be called multiple times per event, so it takes care to set the
+ * cleared pointer fields to NULL afterwards.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Makes the event no longer have any external resources.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+CleanUpTkEvent(
+ XEvent *eventPtr)
+{
+ switch (eventPtr->type) {
+ case KeyPress:
+ case KeyRelease: {
+ TkKeyEvent *kePtr = (TkKeyEvent *) eventPtr;
+
+ if (kePtr->charValuePtr != NULL) {
+ ckfree(kePtr->charValuePtr);
+ kePtr->charValuePtr = NULL;
+ kePtr->charValueLen = 0;
+ }
+ break;
+ }
+
+ case VirtualEvent: {
+ XVirtualEvent *vePtr = (XVirtualEvent *) eventPtr;
+
+ if (vePtr->user_data != NULL) {
+ Tcl_DecrRefCount(vePtr->user_data);
+ vePtr->user_data = NULL;
+ }
+ break;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DelayedMotionProc --
+ *
+ * This function is invoked as an idle handler when a mouse motion event
+ * has been delayed. It queues the delayed event so that it will finally
+ * be serviced.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The delayed mouse motion event gets added to the Tcl event queue for
+ * servicing.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DelayedMotionProc(
+ ClientData clientData) /* Pointer to display containing a delayed
+ * motion event to be serviced. */
+{
+ TkDisplay *dispPtr = clientData;
+
+ if (dispPtr->delayedMotionPtr == NULL) {
+ Tcl_Panic("DelayedMotionProc found no delayed mouse motion event");
+ }
+ Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, TCL_QUEUE_TAIL);
+ dispPtr->delayedMotionPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkCreateExitHandler --
+ *
+ * Same as Tcl_CreateExitHandler, but private to Tk.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects.
+ * Sets a handler with Tcl_CreateExitHandler if this is the first call.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkCreateExitHandler(
+ Tcl_ExitProc *proc, /* Function to invoke. */
+ ClientData clientData) /* Arbitrary value to pass to proc. */
+{
+ ExitHandler *exitPtr;
+
+ exitPtr = ckalloc(sizeof(ExitHandler));
+ exitPtr->proc = proc;
+ exitPtr->clientData = clientData;
+ Tcl_MutexLock(&exitMutex);
+
+ /*
+ * The call to TclInExit() is disabled here. That's a private Tcl routine,
+ * and calling it is causing some trouble with portability of building Tk.
+ * We should avoid private Tcl routines generally.
+ *
+ * In this case, the TclInExit() call is being used only to prevent a
+ * Tcl_CreateExitHandler() call when Tcl finalization is in progress.
+ * That's a situation that shouldn't happen anyway. Recent changes within
+ * Tcl_Finalize now cause a Tcl_Panic() to happen if exit handlers get
+ * added after exit handling is complete. By disabling the guard here,
+ * that panic will serve to help us find the buggy conditions and correct
+ * them.
+ *
+ * We can restore this guard if we find we must (hopefully getting public
+ * access to TclInExit() if we discover extensions really do need this),
+ * but during alpha development, this is a good time to dig in and find
+ * the root causes of finalization bugs.
+ */
+
+ if (firstExitPtr == NULL/* && !TclInExit()*/) {
+ Tcl_CreateExitHandler(TkFinalize, NULL);
+ }
+ exitPtr->nextPtr = firstExitPtr;
+ firstExitPtr = exitPtr;
+ Tcl_MutexUnlock(&exitMutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkDeleteExitHandler --
+ *
+ * Same as Tcl_DeleteExitHandler, but private to Tk.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects.
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkDeleteExitHandler(
+ Tcl_ExitProc *proc, /* Function that was previously registered. */
+ ClientData clientData) /* Arbitrary value to pass to proc. */
+{
+ ExitHandler *exitPtr, *prevPtr;
+
+ Tcl_MutexLock(&exitMutex);
+ for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL;
+ prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) {
+ if ((exitPtr->proc == proc)
+ && (exitPtr->clientData == clientData)) {
+ if (prevPtr == NULL) {
+ firstExitPtr = exitPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = exitPtr->nextPtr;
+ }
+ ckfree(exitPtr);
+ break;
+ }
+ }
+ Tcl_MutexUnlock(&exitMutex);
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkCreateThreadExitHandler --
+ *
+ * Same as Tcl_CreateThreadExitHandler, but private to Tk.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Proc will be invoked with clientData as argument when the application
+ * exits.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkCreateThreadExitHandler(
+ Tcl_ExitProc *proc, /* Function to invoke. */
+ ClientData clientData) /* Arbitrary value to pass to proc. */
+{
+ ExitHandler *exitPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ exitPtr = ckalloc(sizeof(ExitHandler));
+ exitPtr->proc = proc;
+ exitPtr->clientData = clientData;
+
+ /*
+ * See comments in TkCreateExitHandler().
+ */
+
+ if (tsdPtr->firstExitPtr == NULL/* && !TclInExit()*/) {
+ Tcl_CreateThreadExitHandler(TkFinalizeThread, NULL);
+ }
+ exitPtr->nextPtr = tsdPtr->firstExitPtr;
+ tsdPtr->firstExitPtr = exitPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkDeleteThreadExitHandler --
+ *
+ * Same as Tcl_DeleteThreadExitHandler, but private to Tk.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If there is an exit handler corresponding to proc and clientData then
+ * it is cancelled; if no such handler exists then nothing happens.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkDeleteThreadExitHandler(
+ Tcl_ExitProc *proc, /* Function that was previously registered. */
+ ClientData clientData) /* Arbitrary value to pass to proc. */
+{
+ ExitHandler *exitPtr, *prevPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ for (prevPtr = NULL, exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL;
+ prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) {
+ if ((exitPtr->proc == proc)
+ && (exitPtr->clientData == clientData)) {
+ if (prevPtr == NULL) {
+ tsdPtr->firstExitPtr = exitPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = exitPtr->nextPtr;
+ }
+ ckfree(exitPtr);
+ return;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkFinalize --
+ *
+ * Runs our private exit handlers and removes itself from Tcl. This is
+ * benificial should we want to protect from dangling pointers should the
+ * Tk shared library be unloaded prior to Tcl which can happen on windows
+ * should the process be forcefully exiting from an exception handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects.
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkFinalize(
+ ClientData clientData) /* Arbitrary value to pass to proc. */
+{
+ ExitHandler *exitPtr;
+
+#if defined(_WIN32) && !defined(STATIC_BUILD)
+ if (!tclStubsPtr) {
+ return;
+ }
+#endif
+
+ Tcl_DeleteExitHandler(TkFinalize, NULL);
+
+ Tcl_MutexLock(&exitMutex);
+ for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) {
+ /*
+ * Be careful to remove the handler from the list before invoking its
+ * callback. This protects us against double-freeing if the callback
+ * should call TkDeleteExitHandler on itself.
+ */
+
+ firstExitPtr = exitPtr->nextPtr;
+ Tcl_MutexUnlock(&exitMutex);
+ exitPtr->proc(exitPtr->clientData);
+ ckfree(exitPtr);
+ Tcl_MutexLock(&exitMutex);
+ }
+ firstExitPtr = NULL;
+ Tcl_MutexUnlock(&exitMutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkFinalizeThread --
+ *
+ * Runs our private thread exit handlers and removes itself from Tcl.
+ * This is beneficial should we want to protect from dangling pointers
+ * should the Tk shared library be unloaded prior to Tcl which can happen
+ * on Windows should the process be forcefully exiting from an exception
+ * handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects.
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkFinalizeThread(
+ ClientData clientData) /* Arbitrary value to pass to proc. */
+{
+ ExitHandler *exitPtr;
+ ThreadSpecificData *tsdPtr =
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+
+ Tcl_DeleteThreadExitHandler(TkFinalizeThread, NULL);
+
+ if (tsdPtr != NULL) {
+ tsdPtr->inExit = 1;
+
+ for (exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL;
+ exitPtr = tsdPtr->firstExitPtr) {
+ /*
+ * Be careful to remove the handler from the list before invoking
+ * its callback. This protects us against double-freeing if the
+ * callback should call TkDeleteThreadExitHandler on itself.
+ */
+
+ tsdPtr->firstExitPtr = exitPtr->nextPtr;
+ exitPtr->proc(exitPtr->clientData);
+ ckfree(exitPtr);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_MainLoop --
+ *
+ * Call Tcl_DoOneEvent over and over again in an infinite loop as long as
+ * there exist any main windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arbitrary; depends on handlers for events.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_MainLoop(void)
+{
+ while (Tk_GetNumMainWindows() > 0) {
+ Tcl_DoOneEvent(0);
+ }
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */