📄 tkevent.c
字号:
/* * 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. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkEvent.c 1.20 96/09/20 09:33:38 */#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;static InProgress *pendingPtr = NULL; /* Topmost search in progress, or * NULL if none. *//* * 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;static GenericHandler *genericList = NULL; /* First handler in the list, or NULL. */static GenericHandler *lastGenericPtr = NULL; /* Last handler in list. *//* * 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 variable has a non-zero value when a handler is active. */static int genericHandlersActive = 0;/* * 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 */};/* * If someone has called Tk_RestrictEvents, the information below * keeps track of it. */static Tk_RestrictProc *restrictProc; /* Procedure to call. NULL means no * restrictProc is currently in effect. */static ClientData restrictArg; /* Argument to pass to restrictProc. *//* * 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));/* *-------------------------------------------------------------- * * 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. * *-------------------------------------------------------------- */voidTk_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. * *-------------------------------------------------------------- */voidTk_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; /* * 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 = 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. * *-------------------------------------------------------------- */voidTk_CreateGenericHandler(proc, clientData) Tk_GenericProc *proc; /* Procedure to call on every event. */ ClientData clientData; /* One-word value to pass to proc. */{ GenericHandler *handlerPtr; handlerPtr = (GenericHandler *) ckalloc (sizeof (GenericHandler)); handlerPtr->proc = proc; handlerPtr->clientData = clientData; handlerPtr->deleteFlag = 0; handlerPtr->nextPtr = NULL; if (genericList == NULL) { genericList = handlerPtr; } else { lastGenericPtr->nextPtr = handlerPtr; } 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. * *-------------------------------------------------------------- */voidTk_DeleteGenericHandler(proc, clientData) Tk_GenericProc *proc; ClientData clientData;{ GenericHandler * handler; for (handler = genericList; handler; handler = handler->nextPtr) { if ((handler->proc == proc) && (handler->clientData == clientData)) { handler->deleteFlag = 1; } }}/* *-------------------------------------------------------------- * * Tk_HandleEvent -- * * Given an event, invoke all the handlers that have * been registered for the event. * * Results: * None. * * Side effects: * Depends on the handlers. * *-------------------------------------------------------------- */voidTk_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; TkDisplay *dispPtr; Tcl_Interp *interp = (Tcl_Interp *) NULL; /* * 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 = genericList; genericPtr != NULL; ) { if (genericPtr->deleteFlag) { if (!genericHandlersActive) { 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) { genericList = tmpPtr; } else { genPrevPtr->nextPtr = tmpPtr; } if (tmpPtr == NULL) { lastGenericPtr = genPrevPtr; } (void) ckfree((char *) genericPtr); genericPtr = tmpPtr; continue; } } else { int done; genericHandlersActive++; done = (*genericPtr->proc)(genericPtr->clientData, eventPtr); genericHandlersActive--; 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). */ if (eventPtr->type == PropertyNotify) { TkSelPropProc(eventPtr); } return; } /* * 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -