tkEvent.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:43k
- /*
- * 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.
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tkEvent.c,v 1.17.2.8 2006/01/20 18:42:04 jenglish Exp $
- */
- #include "tkPort.h"
- #include "tkInt.h"
- #include <signal.h>
- /*
- * There's a potential problem if a handler is deleted while it's
- * current (i.e. its procedure is executing), since Tk_HandleEvent
- * will need to read the handler's "nextPtr" field when the procedure
- * 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, 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; /* Procedure to dispatch on all X events. */
- ClientData clientData; /* Client data to pass to procedure. */
- 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 unsigned long eventMasks[TK_LASTEVENT] = {
- 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 */
- VirtualEventMask, /* VirtualEvents */
- ActivateMask, /* ActivateNotify */
- ActivateMask, /* DeactivateNotify */
- MouseWheelMask /* MouseWheelEvent */
- };
- /*
- * 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. */
- GenericHandler *genericList; /* First handler in the list, or NULL. */
- GenericHandler *lastGenericPtr; /* Last handler in list. */
- 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;
- /* Procedure to call. NULL means no
- * restrictProc is currently in effect. */
- ClientData restrictArg; /* Argument to pass to restrictProc. */
- } ThreadSpecificData;
- static Tcl_ThreadDataKey dataKey;
- /*
- * Prototypes for procedures that are only referenced locally within
- * this file.
- */
- static void DelayedMotionProc _ANSI_ARGS_((ClientData clientData));
- static int WindowEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
- int flags));
- static int TkXErrorHandler _ANSI_ARGS_((ClientData clientData,
- XErrorEvent *errEventPtr));
- /*
- *--------------------------------------------------------------
- *
- * Tk_CreateEventHandler --
- *
- * Arrange for a given procedure 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(token, mask, proc, clientData)
- Tk_Window token; /* Token for window in which to
- * create handler. */
- unsigned long mask; /* Events for which proc should
- * be called. */
- Tk_EventProc *proc; /* Procedure to call for each
- * selected event */
- ClientData clientData; /* Arbitrary data to pass to proc. */
- {
- register TkEventHandler *handlerPtr;
- register TkWindow *winPtr = (TkWindow *) token;
- int found;
- /*
- * 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.
- */
- found = 0;
- if (winPtr->handlerList == NULL) {
- handlerPtr = (TkEventHandler *) ckalloc(
- (unsigned) sizeof(TkEventHandler));
- winPtr->handlerList = handlerPtr;
- goto initHandler;
- } else {
- for (handlerPtr = winPtr->handlerList; ;
- handlerPtr = handlerPtr->nextPtr) {
- if ((handlerPtr->proc == proc)
- && (handlerPtr->clientData == clientData)) {
- handlerPtr->mask = mask;
- found = 1;
- }
- if (handlerPtr->nextPtr == NULL) {
- break;
- }
- }
- }
- /*
- * Create a new handler if no matching old handler was found.
- */
- if (!found) {
- handlerPtr->nextPtr = (TkEventHandler *)
- ckalloc(sizeof(TkEventHandler));
- handlerPtr = handlerPtr->nextPtr;
- initHandler:
- 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(token, mask, proc, clientData)
- 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 = (ThreadSpecificData *)
- 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((char *) 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 procedure 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(proc, clientData)
- Tk_GenericProc *proc; /* Procedure to call on every event. */
- ClientData clientData; /* One-word value to pass to proc. */
- {
- GenericHandler *handlerPtr;
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
-
- handlerPtr = (GenericHandler *) 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(proc, clientData)
- Tk_GenericProc *proc;
- ClientData clientData;
- {
- GenericHandler * handler;
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- 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 procedure 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(proc)
- Tk_ClientMessageProc *proc; /* Procedure to call on event. */
- {
- GenericHandler *handlerPtr;
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- 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 = (GenericHandler *)
- 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(proc)
- Tk_ClientMessageProc *proc;
- {
- GenericHandler * handler;
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- 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 procedures initializes all the event module
- * structures used by the current thread. It must be
- * called before any other procedure in this file is
- * called.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- void
- TkEventInit _ANSI_ARGS_((void))
- {
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- 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, errEventPtr)
- ClientData clientData; /* Pointer to flag we set */
- XErrorEvent *errEventPtr; /* X error info */
- {
- int *error;
- error = (int *) 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, w)
- 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, (ClientData) (&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(eventPtr)
- XEvent *eventPtr; /* Event to dispatch. */
- {
- register TkEventHandler *handlerPtr;
- register GenericHandler *genericPtr;
- register GenericHandler *genPrevPtr;
- TkWindow *winPtr;
- unsigned long mask;
- InProgress ip;
- Window handlerWindow;
- Window parentXId;
- TkDisplay *dispPtr;
- Tcl_Interp *interp = (Tcl_Interp *) NULL;
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
- /*
- * Hack for simulated X-events: Correct the state field
- * of the event record to match with the ButtonPress
- * and ButtonRelease events.
- */
- if (eventPtr->type==ButtonPress) {
- dispPtr = TkGetDisplay(eventPtr->xbutton.display);
- dispPtr->mouseButtonWindow = eventPtr->xbutton.window;
- eventPtr->xbutton.state |= dispPtr->mouseButtonState;
- switch (eventPtr->xbutton.button) {
- case 1: dispPtr->mouseButtonState |= Button1Mask; break;
- case 2: dispPtr->mouseButtonState |= Button2Mask; break;
- case 3: dispPtr->mouseButtonState |= Button3Mask; break;
- }
- } else if (eventPtr->type==ButtonRelease) {
- dispPtr = TkGetDisplay(eventPtr->xbutton.display);
- dispPtr->mouseButtonWindow = 0;
- switch (eventPtr->xbutton.button) {
- case 1: dispPtr->mouseButtonState &= ~Button1Mask; break;
- case 2: dispPtr->mouseButtonState &= ~Button2Mask; break;
- case 3: dispPtr->mouseButtonState &= ~Button3Mask; break;
- }
- eventPtr->xbutton.state |= dispPtr->mouseButtonState;
- } else if (eventPtr->type==MotionNotify) {
- dispPtr = TkGetDisplay(eventPtr->xmotion.display);
- if (dispPtr->mouseButtonState & (Button1Mask|Button2Mask|Button3Mask)) {
- 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 &=
- ~(Button1Mask|Button2Mask|Button3Mask);
- dispPtr->mouseButtonWindow = 0;
- } else {
- eventPtr->xmotion.state |= dispPtr->mouseButtonState;
- }
- }
- }
- /*
- * Next, invoke all the generic event handlers (those that are
- * invoked for all events). If a generic event handler reports that
- * an event is fully processed, go no further.
- */
- for (genPrevPtr = NULL, genericPtr = tsdPtr->genericList;
- genericPtr != NULL; ) {
- if (genericPtr->deleteFlag) {
- if (!tsdPtr->handlersActive) {
- GenericHandler *tmpPtr;
- /*
- * 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 = genericPtr->nextPtr;
- if (genPrevPtr == NULL) {
- tsdPtr->genericList = tmpPtr;
- } else {
- genPrevPtr->nextPtr = tmpPtr;
- }
- if (tmpPtr == NULL) {
- tsdPtr->lastGenericPtr = genPrevPtr;
- }
- (void) ckfree((char *) genericPtr);
- genericPtr = tmpPtr;
- continue;
- }
- } else {
- int done;
- tsdPtr->handlersActive++;
- done = (*genericPtr->proc)(genericPtr->clientData, eventPtr);
- tsdPtr->handlersActive--;
- if (done) {
- return;
- }
- }
- genPrevPtr = genericPtr;
- genericPtr = genPrevPtr->nextPtr;
- }
- /*
- * If the event is a MappingNotify event, find its display and
- * refresh the keyboard mapping information for the display.
- * After that there's nothing else to do with the event, so just
- * quit.
- */
- if (eventPtr->type == MappingNotify) {
- dispPtr = TkGetDisplay(eventPtr->xmapping.display);
- if (dispPtr != NULL) {
- XRefreshKeyboardMapping(&eventPtr->xmapping);
- dispPtr->bindInfoStale = 1;
- }
- return;
- }
- /*
- * 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.
- */
- handlerWindow = eventPtr->xany.window;
- mask = eventMasks[eventPtr->xany.type];
- if (mask == StructureNotifyMask) {
- if (eventPtr->xmap.event != eventPtr->xmap.window) {
- mask = SubstructureNotifyMask;
- 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;
- }
- TkSelPropProc(eventPtr);
- /* Get handlerWindow's parent. */
- parentXId = ParentXId(eventPtr->xany.display, handlerWindow);
- if (parentXId == None) {
- return;
- }
- winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, parentXId);
- if (winPtr == NULL) {
- return;
- }
- if (!(winPtr->flags & TK_PROP_PROPCHANGE)) {
- return;
- }
- handlerWindow = parentXId;
- }
- /*
- * 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)) {
- return;
- }
- if (winPtr->mainPtr != NULL) {
- /*
- * 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.
- */
-
- interp = winPtr->mainPtr->interp;
- Tcl_Preserve((ClientData) interp);
-
- /*
- * Call focus-related code to look at FocusIn, FocusOut, Enter,
- * and Leave events; depending on its return value, ignore the
- * event.
- */
-
- if ((mask & (FocusChangeMask|EnterWindowMask|LeaveWindowMask))
- && !TkFocusFilterEvent(winPtr, eventPtr)) {
- Tcl_Release((ClientData) interp);
- return;
- }
-
- /*
- * Redirect KeyPress and KeyRelease events to the focus window,
- * or ignore them entirely if there is no focus window. We also
- * route the MouseWheel event to the focus window. The MouseWheel
- * event is an extension to the X event set. Currently, it is only
- * available on the Windows version of Tk.
- */
-
- #ifdef MAC_OSX_TK
- /* MouseWheel events are not focus specific on Mac OS X */
- if (mask & (KeyPressMask|KeyReleaseMask)) {
- #else
- if (mask & (KeyPressMask|KeyReleaseMask|MouseWheelMask)) {
- #endif
- winPtr->dispPtr->lastEventTime = eventPtr->xkey.time;
- winPtr = TkFocusKeyEvent(winPtr, eventPtr);
- if (winPtr == NULL) {
- Tcl_Release((ClientData) interp);
- return;
- }
- }
-
- /*
- * Call a grab-related procedure to do special processing on
- * pointer events.
- */
-
- 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) {
- goto done;
- }
- }
- }
- #ifdef TK_USE_INPUT_METHODS
- /*
- * Pass the event to the input method(s), if there are any, and
- * discard the event if the input method(s) insist. Create the
- * input context for the window if it hasn't already been done
- * (XFilterEvent needs this context). XIM is only ever enabled on
- * Unix, but this hasn't been factored out of the generic code yet.
- */
- dispPtr = winPtr->dispPtr;
- if ((dispPtr->flags & TK_DISPLAY_USE_IM)) {
- long im_event_mask = 0L;
- if (!(winPtr->flags & (TK_CHECKED_IC|TK_ALREADY_DEAD))) {
- winPtr->flags |= TK_CHECKED_IC;
- if (dispPtr->inputMethod != NULL) {
- #if TK_XIM_SPOT
- if (dispPtr->flags & TK_DISPLAY_XIM_SPOT) {
- XVaNestedList preedit_attr;
- XPoint spot = {0, 0};
- if (dispPtr->inputXfs == NULL) {
- /*
- * We only need to create one XFontSet
- */
- char **missing_list;
- int missing_count;
- char *def_string;
- dispPtr->inputXfs = XCreateFontSet(dispPtr->display,
- "-*-*-*-R-Normal--14-130-75-75-*-*",
- &missing_list, &missing_count, &def_string);
- if (missing_count > 0) {
- XFreeStringList(missing_list);
- }
- }
- preedit_attr = XVaCreateNestedList(0, XNSpotLocation,
- &spot, XNFontSet, dispPtr->inputXfs, NULL);
- if (winPtr->inputContext != NULL)
- panic("inputContext not NULL");
- winPtr->inputContext = XCreateIC(dispPtr->inputMethod,
- XNInputStyle, XIMPreeditPosition|XIMStatusNothing,
- XNClientWindow, winPtr->window,
- XNFocusWindow, winPtr->window,
- XNPreeditAttributes, preedit_attr,
- NULL);
- XFree(preedit_attr);
- } else {
- if (winPtr->inputContext != NULL)
- panic("inputContext not NULL");
- winPtr->inputContext = XCreateIC(dispPtr->inputMethod,
- XNInputStyle, XIMPreeditNothing|XIMStatusNothing,
- XNClientWindow, winPtr->window,
- XNFocusWindow, winPtr->window,
- NULL);
- }
- #else
- if (winPtr->inputContext != NULL)
- panic("inputContext not NULL");
- winPtr->inputContext = XCreateIC(dispPtr->inputMethod,
- XNInputStyle, XIMPreeditNothing|XIMStatusNothing,
- XNClientWindow, winPtr->window,
- XNFocusWindow, winPtr->window,
- NULL);
- #endif
- }
- }
- if (winPtr->inputContext != NULL &&
- (eventPtr->xany.type == FocusIn)) {
- XGetICValues(winPtr->inputContext,
- XNFilterEvents, &im_event_mask, NULL);
- if (im_event_mask != 0L) {
- XSelectInput(winPtr->display, winPtr->window,
- winPtr->atts.event_mask | im_event_mask);
- XSetICFocus(winPtr->inputContext);
- }
- }
- if (eventPtr->type == KeyPress || eventPtr->type == KeyRelease) {
- if (XFilterEvent(eventPtr, None)) {
- goto done;
- }
- }
- }
- #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 {
- /*
- * Finally, invoke any ClientMessage event handlers.
- */
- for (genPrevPtr = NULL, genericPtr = tsdPtr->cmList;
- genericPtr != NULL; ) {
- if (genericPtr->deleteFlag) {
- if (!tsdPtr->handlersActive) {
- GenericHandler *tmpPtr;
- /*
- * 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 = genericPtr->nextPtr;
- if (genPrevPtr == NULL) {
- tsdPtr->cmList = tmpPtr;
- } else {
- genPrevPtr->nextPtr = tmpPtr;
- }
- if (tmpPtr == NULL) {
- tsdPtr->lastCmPtr = genPrevPtr;
- }
- (void) ckfree((char *) genericPtr);
- genericPtr = tmpPtr;
- continue;
- }
- } else {
- int done;
- tsdPtr->handlersActive++;
- done = (*(Tk_ClientMessageProc *)genericPtr->proc)
- ((Tk_Window) winPtr, eventPtr);
- tsdPtr->handlersActive--;
- if (done) {
- break;
- }
- }
- genPrevPtr = genericPtr;
- genericPtr = genPrevPtr->nextPtr;
- }
- }
- }
- } else {
- for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {
- if ((handlerPtr->mask & mask) != 0) {
- 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 procedures.
- */
- /*
- * ...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;
- done:
- /*
- * Release the interpreter for this window so that it can be potentially
- * deleted if requested.
- */
-
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_Release((ClientData) interp);
- }
- }
- /*
- *--------------------------------------------------------------
- *
- * TkEventDeadWindow --
- *
- * This procedure 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(winPtr)
- TkWindow *winPtr; /* Information about the window
- * that is being deleted. */
- {
- register TkEventHandler *handlerPtr;
- register InProgress *ipPtr;
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- 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((char *) 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(dispPtr)
- TkDisplay *dispPtr; /* Display for which the time is desired. */
- {
- register XEvent *eventPtr;
- ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- 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 procedure 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 procedure that determines
- * whether they are to be processed immediately, deferred, or
- * discarded.
- *
- * Results:
- * The return value is the previous restriction procedure 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(proc, arg, prevArgPtr)
- Tk_RestrictProc *proc; /* Procedure 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 = (ThreadSpecificData *)
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
- prev = tsdPtr->restrictProc;
- *prevArgPtr = tsdPtr->restrictArg;
- tsdPtr->restrictProc = proc;
- tsdPtr->restrictArg = arg;
- return prev;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tk_CollapseMotionEvents --
- *
- * This procedure 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, collapse)
- 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 procedure adds it to the
- * Tcl event queue at the given position. This procedure 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(eventPtr, position)
- XEvent *eventPtr; /* Event to add to queue. This
- * procedures 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 = (TkWindowEvent *) 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, (ClientData) dispPtr);
- }
- }
- wevPtr = (TkWindowEvent *) 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) {
- panic("Tk_QueueWindowEvent found unexpected delayed motion event");
- }
- dispPtr->delayedMotionPtr = wevPtr;
- Tcl_DoWhenIdle(DelayedMotionProc, (ClientData) 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(winPtr, eventPtr)
- 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 procedure is called by Tcl_DoOneEvent when a window event
- * reaches the front of the event queue. This procedure 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(evPtr, flags)
- 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 = (ThreadSpecificData *)
- 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.
- */
- return 1;
- }
- }
- }
- Tk_HandleEvent(&wevPtr->event);
- return 1;
- }
- /*
- *----------------------------------------------------------------------
- *
- * DelayedMotionProc --
- *
- * This procedure 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 clientData; /* Pointer to display containing a delayed
- * motion event to be serviced. */
- {
- TkDisplay *dispPtr = (TkDisplay *) clientData;
- if (dispPtr->delayedMotionPtr == NULL) {
- panic("DelayedMotionProc found no delayed mouse motion event");
- }
- Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, TCL_QUEUE_TAIL);
- dispPtr->delayedMotionPtr = NULL;
- }
- /*
- *--------------------------------------------------------------
- *
- * 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()
- {
- while (Tk_GetNumMainWindows() > 0) {
- Tcl_DoOneEvent(0);
- }
- }