tkEvent.c revision 3f54fd611f536639ec30dd53c48e5ec1897cc7d9
/*
* 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.18 96/09/12 09:25:22
*/
#include "tkInt.h"
#include "tkPort.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 {
* window is deleted while event is being
* handled. */
} InProgress;
/* 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 {
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. */
/* First handler in the list, or 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 {
/*
* 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 */
/* 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. */
/*
* Prototypes for procedures that are only referenced locally within
* this file.
*/
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.
*
*--------------------------------------------------------------
*/
void
* create handler. */
unsigned long mask; /* Events for which proc should
* be called. */
* selected event */
{
register TkEventHandler *handlerPtr;
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;
(unsigned) sizeof(TkEventHandler));
goto initHandler;
} else {
found = 1;
}
break;
}
}
}
/*
* Create a new handler if no matching old handler was found.
*/
if (!found) {
ckalloc(sizeof(TkEventHandler));
}
/*
* 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
unsigned long mask; /* previously to Tk_CreateEventHandler. */
{
register TkEventHandler *handlerPtr;
register InProgress *ipPtr;
/*
* Find the event handler to be deleted, or return
* immediately if it doesn't exist.
*/
if (handlerPtr == NULL) {
return;
}
break;
}
}
/*
* If Tk_HandleEvent is about to process this handler, tell it to
* process the next one instead.
*/
}
}
/*
* Free resources associated with the handler.
*/
} else {
}
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
{
handlerPtr->deleteFlag = 0;
if (genericList == NULL) {
} else {
}
}
/*
*--------------------------------------------------------------
*
* 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_HandleEvent --
*
* Given an event, invoke all the handlers that have
* been registered for the event.
*
* Results:
* None.
*
* Side effects:
* Depends on the handlers.
*
*--------------------------------------------------------------
*/
void
{
register TkEventHandler *handlerPtr;
register GenericHandler *genericPtr;
register GenericHandler *genPrevPtr;
unsigned long mask;
/*
* 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.
*/
if (genericPtr->deleteFlag) {
if (!genericHandlersActive) {
/*
* This handler needs to be deleted and there are no
* calls pending through the handler, so now is a safe
* time to delete it.
*/
if (genPrevPtr == NULL) {
} else {
}
}
(void) ckfree((char *) genericPtr);
genericPtr = tmpPtr;
continue;
}
} else {
int done;
if (done) {
return;
}
}
}
/*
* 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.
*/
}
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.
*/
if (mask == StructureNotifyMask) {
}
}
/*
* 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).
*/
}
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).
*/
return;
}
/*
* 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.
*/
/*
* Call focus-related code to look at FocusIn, FocusOut, Enter,
* and Leave events; depending on its return value, ignore the
* event.
*/
return;
}
/*
* Redirect KeyPress and KeyRelease events to the focus window,
* or ignore them entirely if there is no focus window. Map the
* x and y coordinates to make sense in the context of the focus
* window, if possible (make both -1 if the map-from and map-to
* windows don't share the same screen).
*/
return;
}
} else {
}
}
/*
* Call a grab-related procedure to do special processing on
* pointer events.
*/
} else if (mask & PointerMotionMask) {
} else {
}
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).
*/
}
}
goto done;
}
#endif /* TK_USE_INPUT_METHODS */
/*
* For events where it hasn't already been done, update the current
* time in the display.
*/
}
/*
* There's a potential interaction here with Tk_DeleteEventHandler.
* Read the documentation for pendingPtr.
*/
pendingPtr = &ip;
if (mask == 0) {
}
} else {
} else {
}
}
/*
* 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.
*/
}
}
done:
/*
* Release the interpreter for this window so that it can be potentially
* deleted if requested.
*/
}
}
/*
*--------------------------------------------------------------
*
* 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
* that is being deleted. */
{
register TkEventHandler *handlerPtr;
register InProgress *ipPtr;
/*
* 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).
*/
}
}
}
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.
*
*----------------------------------------------------------------------
*/
{
if (pendingPtr == NULL) {
return dispPtr->lastEventTime;
}
case ButtonPress:
case ButtonRelease:
case KeyPress:
case KeyRelease:
case MotionNotify:
case EnterNotify:
case LeaveNotify:
case PropertyNotify:
}
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.
*
*----------------------------------------------------------------------
*/
* event. */
* argument. */
{
prev = restrictProc;
restrictProc = proc;
restrictArg = arg;
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
* procedures copies it before adding
* it to the queue. */
* TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
* or TCL_QUEUE_MARK. */
{
/*
* Find our display structure for the event's display.
*/
return;
}
break;
}
}
/*
* 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.
*/
return;
/*
* 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.
*/
}
}
/*
* 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.
*/
panic("Tk_QueueWindowEvent found unexpected delayed motion event");
}
} else {
}
}
/*
*---------------------------------------------------------------------------
*
* 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
{
if (!Tk_IsTopLevel(childPtr)) {
}
}
}
/*
*----------------------------------------------------------------------
*
* 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
int flags; /* Flags that indicate what events to
* handle, such as TCL_WINDOW_EVENTS. */
{
if (!(flags & TCL_WINDOW_EVENTS)) {
return 0;
}
if (restrictProc != NULL) {
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;
}
}
}
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
* motion event to be serviced. */
{
panic("DelayedMotionProc found no delayed mouse motion event");
}
}
/*
*--------------------------------------------------------------
*
* 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
{
while (Tk_GetNumMainWindows() > 0) {
Tcl_DoOneEvent(0);
}
}