1702 lines
48 KiB
C
1702 lines
48 KiB
C
|
/*
|
|||
|
* tkEvent.c --
|
|||
|
*
|
|||
|
* This file provides basic event-managing facilities,
|
|||
|
* whereby procedure callbacks may be attached to
|
|||
|
* certain events.
|
|||
|
*
|
|||
|
* Copyright (c) 1990-1993 The Regents of the University of California.
|
|||
|
* All rights reserved.
|
|||
|
*
|
|||
|
* Permission is hereby granted, without written agreement and without
|
|||
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|||
|
* software and its documentation for any purpose, provided that the
|
|||
|
* above copyright notice and the following two paragraphs appear in
|
|||
|
* all copies of this software.
|
|||
|
*
|
|||
|
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
|||
|
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
|
|||
|
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
|
|||
|
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
*
|
|||
|
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
|||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|||
|
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
|||
|
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
|
|||
|
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|||
|
*/
|
|||
|
|
|||
|
#ifndef lint
|
|||
|
static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkEvent.c,v 1.77 93/10/07 09:59:04 ouster Exp $ SPRITE (Berkeley)";
|
|||
|
#endif
|
|||
|
|
|||
|
#include "tkConfig.h"
|
|||
|
#include "tkInt.h"
|
|||
|
#include <errno.h>
|
|||
|
#include <signal.h>
|
|||
|
|
|||
|
/*
|
|||
|
* For each timer callback that's pending, there is one record
|
|||
|
* of the following type, chained together in a list sorted by
|
|||
|
* time (earliest event first).
|
|||
|
*/
|
|||
|
|
|||
|
typedef struct TimerEvent {
|
|||
|
struct timeval time; /* When timer is to fire. */
|
|||
|
void (*proc) _ANSI_ARGS_((ClientData clientData));
|
|||
|
/* Procedure to call. */
|
|||
|
ClientData clientData; /* Argument to pass to proc. */
|
|||
|
Tk_TimerToken token; /* Identifies event so it can be
|
|||
|
* deleted. */
|
|||
|
struct TimerEvent *nextPtr; /* Next event in queue, or NULL for
|
|||
|
* end of queue. */
|
|||
|
} TimerEvent;
|
|||
|
|
|||
|
static TimerEvent *timerQueue; /* First event in queue. */
|
|||
|
|
|||
|
/*
|
|||
|
* The information below is used to provide read, write, and
|
|||
|
* exception masks to select during calls to Tk_DoOneEvent.
|
|||
|
*/
|
|||
|
|
|||
|
static int readCount; /* Number of files for which we */
|
|||
|
static int writeCount; /* care about each event type. */
|
|||
|
static int exceptCount;
|
|||
|
static fd_mask masks[3*MASK_SIZE];
|
|||
|
/* Integer array containing official
|
|||
|
* copies of the three sets of
|
|||
|
* masks. */
|
|||
|
static fd_mask ready[3*MASK_SIZE];
|
|||
|
/* Temporary copy of masks, passed
|
|||
|
* to select and modified by kernel
|
|||
|
* to indicate which files are
|
|||
|
* actually ready. */
|
|||
|
static fd_mask *readPtr; /* Pointers to the portions of */
|
|||
|
static fd_mask *writePtr; /* *readyPtr for reading, writing, */
|
|||
|
static fd_mask *exceptPtr; /* and excepting. Will be NULL if
|
|||
|
* corresponding count (e.g. readCount
|
|||
|
* is zero. */
|
|||
|
static int numFds = 0; /* Number of valid bits in mask
|
|||
|
* arrays (this value is passed
|
|||
|
* to select). */
|
|||
|
|
|||
|
/*
|
|||
|
* For each file registered in a call to Tk_CreateFileHandler,
|
|||
|
* and for each display that's currently active, there is one
|
|||
|
* record of the following type. All of these records are
|
|||
|
* chained together into a single list.
|
|||
|
*/
|
|||
|
|
|||
|
typedef struct FileEvent {
|
|||
|
int fd; /* Descriptor number for this file. */
|
|||
|
int isDisplay; /* Non-zero means that this file descriptor
|
|||
|
* corresponds to a display and should be
|
|||
|
* treated specially. */
|
|||
|
fd_mask *readPtr; /* Pointer to word in ready array
|
|||
|
* for this file's read mask bit. */
|
|||
|
fd_mask *writePtr; /* Same for write mask bit. */
|
|||
|
fd_mask *exceptPtr; /* Same for except mask bit. */
|
|||
|
fd_mask mask; /* Value to AND with mask word to
|
|||
|
* select just this file's bit. */
|
|||
|
void (*proc) _ANSI_ARGS_((ClientData clientData, int mask));
|
|||
|
/* Procedure to call. NULL means
|
|||
|
* this is a display. */
|
|||
|
ClientData clientData; /* Argument to pass to proc. For
|
|||
|
* displays, this is a (Display *). */
|
|||
|
struct FileEvent *nextPtr; /* Next in list of all files we
|
|||
|
* care about (NULL for end of
|
|||
|
* list). */
|
|||
|
} FileEvent;
|
|||
|
|
|||
|
static FileEvent *fileList; /* List of all file events. */
|
|||
|
|
|||
|
/*
|
|||
|
* There is one of the following structures for each of the
|
|||
|
* handlers declared in a call to Tk_DoWhenIdle. All of the
|
|||
|
* currently-active handlers are linked together into a list.
|
|||
|
*/
|
|||
|
|
|||
|
typedef struct IdleHandler {
|
|||
|
void (*proc) _ANSI_ARGS_((ClientData clientData));
|
|||
|
/* Procedure to call. */
|
|||
|
ClientData clientData; /* Value to pass to proc. */
|
|||
|
int generation; /* Used to distinguish older handlers from
|
|||
|
* recently-created ones. */
|
|||
|
struct IdleHandler *nextPtr;/* Next in list of active handlers. */
|
|||
|
} IdleHandler;
|
|||
|
|
|||
|
static IdleHandler *idleList = NULL;
|
|||
|
/* First in list of all idle handlers. */
|
|||
|
static IdleHandler *lastIdlePtr = NULL;
|
|||
|
/* Last in list (or NULL for empty list). */
|
|||
|
static int idleGeneration = 0; /* Used to fill in the "generation" fields
|
|||
|
* of IdleHandler structures. Increments
|
|||
|
* each time Tk_DoOneEvent starts calling
|
|||
|
* idle handlers, so that all old handlers
|
|||
|
* can be called without calling any of the
|
|||
|
* new ones created by old ones. */
|
|||
|
|
|||
|
/*
|
|||
|
* 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;
|
|||
|
|
|||
|
/*
|
|||
|
* Array of event masks corresponding to each X event:
|
|||
|
*/
|
|||
|
|
|||
|
static unsigned long eventMasks[] = {
|
|||
|
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 */
|
|||
|
};
|
|||
|
|
|||
|
/*
|
|||
|
* If someone has called Tk_RestrictEvents, the information below
|
|||
|
* keeps track of it.
|
|||
|
*/
|
|||
|
|
|||
|
static Bool (*restrictProc) _ANSI_ARGS_((Display *display, XEvent *eventPtr,
|
|||
|
char *arg)); /* Procedure to call. NULL means no
|
|||
|
* restrictProc is currently in effect. */
|
|||
|
static char *restrictArg; /* Argument to pass to restrictProc. */
|
|||
|
|
|||
|
/*
|
|||
|
* The following array keeps track of the last TK_NEVENTS X events, for
|
|||
|
* memory dump analysis. The tracing is only done if tkEventDebug is set
|
|||
|
* to 1.
|
|||
|
*/
|
|||
|
|
|||
|
#define TK_NEVENTS 32
|
|||
|
static XEvent eventTrace[TK_NEVENTS];
|
|||
|
static int traceIndex = 0;
|
|||
|
int tkEventDebug = 0;
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* 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;
|
|||
|
|
|||
|
/*
|
|||
|
* 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.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
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.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_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.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_HandleEvent(eventPtr)
|
|||
|
XEvent *eventPtr; /* Event to dispatch. */
|
|||
|
{
|
|||
|
register TkEventHandler *handlerPtr;
|
|||
|
register GenericHandler *genericPtr;
|
|||
|
register GenericHandler *genPrevPtr;
|
|||
|
TkWindow *winPtr;
|
|||
|
register unsigned long mask;
|
|||
|
InProgress ip;
|
|||
|
Window handlerWindow;
|
|||
|
|
|||
|
/*
|
|||
|
* First off, look for a special trigger event left around by the
|
|||
|
* grab module. If it's found, call the grab module and discard
|
|||
|
* the event.
|
|||
|
*/
|
|||
|
|
|||
|
if ((eventPtr->xany.type == -1) && (eventPtr->xany.window == None)) {
|
|||
|
TkGrabTriggerProc(eventPtr);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* 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) {
|
|||
|
TkDisplay *dispPtr;
|
|||
|
|
|||
|
for (dispPtr = tkDisplayList; dispPtr != NULL;
|
|||
|
dispPtr = dispPtr->nextPtr) {
|
|||
|
if (dispPtr->display != eventPtr->xmapping.display) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
XRefreshKeyboardMapping(&eventPtr->xmapping);
|
|||
|
dispPtr->bindInfoStale = 1;
|
|||
|
break;
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Events selected by StructureNotify look the same as those
|
|||
|
* selected by SubstructureNotify; the only difference is
|
|||
|
* whether the "event" and "window" fields are the same.
|
|||
|
* Check it out 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;
|
|||
|
}
|
|||
|
}
|
|||
|
if (XFindContext(eventPtr->xany.display, handlerWindow,
|
|||
|
tkWindowContext, (caddr_t *) &winPtr) != 0) {
|
|||
|
|
|||
|
/*
|
|||
|
* 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;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* 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)) {
|
|||
|
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).
|
|||
|
*/
|
|||
|
|
|||
|
if (mask & (KeyPressMask|KeyReleaseMask)) {
|
|||
|
TkWindow *focusPtr;
|
|||
|
int winX, winY, focusX, focusY;
|
|||
|
|
|||
|
winPtr->dispPtr->lastEventTime = eventPtr->xkey.time;
|
|||
|
if (winPtr->mainPtr->focusPtr == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
focusPtr = winPtr->mainPtr->focusPtr;
|
|||
|
if ((focusPtr->display != winPtr->display)
|
|||
|
|| (focusPtr->screenNum != winPtr->screenNum)) {
|
|||
|
eventPtr->xkey.x = -1;
|
|||
|
eventPtr->xkey.y = -1;
|
|||
|
} else {
|
|||
|
Tk_GetRootCoords((Tk_Window) winPtr, &winX, &winY);
|
|||
|
Tk_GetRootCoords((Tk_Window) focusPtr, &focusX, &focusY);
|
|||
|
eventPtr->xkey.x -= focusX - winX;
|
|||
|
eventPtr->xkey.y -= focusY - winY;
|
|||
|
}
|
|||
|
eventPtr->xkey.window = focusPtr->window;
|
|||
|
winPtr = focusPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* 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) {
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* 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 = pendingPtr;
|
|||
|
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)
|
|||
|
&& (eventPtr->xclient.message_type ==
|
|||
|
Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"))) {
|
|||
|
TkWmProtocolEventProc(winPtr, eventPtr);
|
|||
|
}
|
|||
|
} 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.
|
|||
|
*/
|
|||
|
|
|||
|
if ((ip.winPtr != None) && (mask != SubstructureNotifyMask)) {
|
|||
|
TkBindEventProc(winPtr, eventPtr);
|
|||
|
}
|
|||
|
}
|
|||
|
pendingPtr = ip.nextPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_CreateFileHandler --
|
|||
|
*
|
|||
|
* Arrange for a given procedure to be invoked whenever
|
|||
|
* a given file becomes readable or writable.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* From now on, whenever the I/O channel given by fd becomes
|
|||
|
* ready in the way indicated by mask, proc will be invoked.
|
|||
|
* See the manual entry for details on the calling sequence
|
|||
|
* to proc. If fd is already registered then the old mask
|
|||
|
* and proc and clientData values will be replaced with
|
|||
|
* new ones.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_CreateFileHandler(fd, mask, proc, clientData)
|
|||
|
int fd; /* Integer identifier for stream. */
|
|||
|
int mask; /* OR'ed combination of TK_READABLE,
|
|||
|
* TK_WRITABLE, and TK_EXCEPTION:
|
|||
|
* indicates conditions under which
|
|||
|
* proc should be called. TK_IS_DISPLAY
|
|||
|
* indicates that this is a display and that
|
|||
|
* clientData is the (Display *) for it,
|
|||
|
* and that events should be handled
|
|||
|
* automatically.*/
|
|||
|
Tk_FileProc *proc; /* Procedure to call for each
|
|||
|
* selected event. */
|
|||
|
ClientData clientData; /* Arbitrary data to pass to proc. */
|
|||
|
{
|
|||
|
register FileEvent *filePtr;
|
|||
|
int index;
|
|||
|
|
|||
|
if (fd >= OPEN_MAX) {
|
|||
|
panic("Tk_CreatefileHandler can't handle file id %d", fd);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Make sure the file isn't already registered. Create a
|
|||
|
* new record in the normal case where there's no existing
|
|||
|
* record.
|
|||
|
*/
|
|||
|
|
|||
|
for (filePtr = fileList; filePtr != NULL;
|
|||
|
filePtr = filePtr->nextPtr) {
|
|||
|
if (filePtr->fd == fd) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
index = fd/(NBBY*sizeof(fd_mask));
|
|||
|
if (filePtr == NULL) {
|
|||
|
filePtr = (FileEvent *) ckalloc(sizeof(FileEvent));
|
|||
|
filePtr->fd = fd;
|
|||
|
filePtr->isDisplay = 0;
|
|||
|
filePtr->readPtr = &ready[index];
|
|||
|
filePtr->writePtr = &ready[index+MASK_SIZE];
|
|||
|
filePtr->exceptPtr = &ready[index+2*MASK_SIZE];
|
|||
|
filePtr->mask = 1 << (fd%(NBBY*sizeof(fd_mask)));
|
|||
|
filePtr->nextPtr = fileList;
|
|||
|
fileList = filePtr;
|
|||
|
} else {
|
|||
|
if (masks[index] & filePtr->mask) {
|
|||
|
readCount--;
|
|||
|
*filePtr->readPtr &= ~filePtr->mask;
|
|||
|
masks[index] &= ~filePtr->mask;
|
|||
|
}
|
|||
|
if (masks[index+MASK_SIZE] & filePtr->mask) {
|
|||
|
writeCount--;
|
|||
|
*filePtr->writePtr &= ~filePtr->mask;
|
|||
|
masks[index+MASK_SIZE] &= ~filePtr->mask;
|
|||
|
}
|
|||
|
if (masks[index+2*MASK_SIZE] & filePtr->mask) {
|
|||
|
exceptCount--;
|
|||
|
*filePtr->exceptPtr &= ~filePtr->mask;
|
|||
|
masks[index+2*MASK_SIZE] &= ~filePtr->mask;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* The remainder of the initialization below is done
|
|||
|
* regardless of whether or not this is a new record
|
|||
|
* or a modification of an old one.
|
|||
|
*/
|
|||
|
|
|||
|
if (mask & TK_READABLE) {
|
|||
|
masks[index] |= filePtr->mask;
|
|||
|
readCount++;
|
|||
|
}
|
|||
|
readPtr = (readCount == 0) ? (fd_mask *) NULL : &ready[0];
|
|||
|
|
|||
|
if (mask & TK_WRITABLE) {
|
|||
|
masks[index+MASK_SIZE] |= filePtr->mask;
|
|||
|
writeCount++;
|
|||
|
}
|
|||
|
writePtr = (writeCount == 0) ? (fd_mask *) NULL : &ready[MASK_SIZE];
|
|||
|
|
|||
|
if (mask & TK_EXCEPTION) {
|
|||
|
masks[index+2*MASK_SIZE] |= filePtr->mask;
|
|||
|
exceptCount++;
|
|||
|
}
|
|||
|
exceptPtr = (exceptCount == 0) ? (fd_mask *) NULL : &ready[2*MASK_SIZE];
|
|||
|
|
|||
|
if (mask & TK_IS_DISPLAY) {
|
|||
|
filePtr->isDisplay = 1;
|
|||
|
} else {
|
|||
|
filePtr->isDisplay = 0;
|
|||
|
}
|
|||
|
|
|||
|
filePtr->proc = proc;
|
|||
|
filePtr->clientData = clientData;
|
|||
|
|
|||
|
if (numFds <= fd) {
|
|||
|
numFds = fd+1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_DeleteFileHandler --
|
|||
|
*
|
|||
|
* Cancel a previously-arranged callback arrangement for
|
|||
|
* a file.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* If a callback was previously registered on fd, remove it.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_DeleteFileHandler(fd)
|
|||
|
int fd; /* Stream id for which to remove
|
|||
|
* callback procedure. */
|
|||
|
{
|
|||
|
register FileEvent *filePtr;
|
|||
|
FileEvent *prevPtr;
|
|||
|
int index;
|
|||
|
|
|||
|
/*
|
|||
|
* Find the entry for the given file (and return if there
|
|||
|
* isn't one).
|
|||
|
*/
|
|||
|
|
|||
|
for (prevPtr = NULL, filePtr = fileList; ;
|
|||
|
prevPtr = filePtr, filePtr = filePtr->nextPtr) {
|
|||
|
if (filePtr == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
if (filePtr->fd == fd) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Clean up information in the callback record.
|
|||
|
*/
|
|||
|
|
|||
|
index = filePtr->fd/(NBBY*sizeof(fd_mask));
|
|||
|
if (masks[index] & filePtr->mask) {
|
|||
|
readCount--;
|
|||
|
*filePtr->readPtr &= ~filePtr->mask;
|
|||
|
masks[index] &= ~filePtr->mask;
|
|||
|
}
|
|||
|
if (masks[index+MASK_SIZE] & filePtr->mask) {
|
|||
|
writeCount--;
|
|||
|
*filePtr->writePtr &= ~filePtr->mask;
|
|||
|
masks[index+MASK_SIZE] &= ~filePtr->mask;
|
|||
|
}
|
|||
|
if (masks[index+2*MASK_SIZE] & filePtr->mask) {
|
|||
|
exceptCount--;
|
|||
|
*filePtr->exceptPtr &= ~filePtr->mask;
|
|||
|
masks[index+2*MASK_SIZE] &= ~filePtr->mask;
|
|||
|
}
|
|||
|
if (prevPtr == NULL) {
|
|||
|
fileList = filePtr->nextPtr;
|
|||
|
} else {
|
|||
|
prevPtr->nextPtr = filePtr->nextPtr;
|
|||
|
}
|
|||
|
ckfree((char *) filePtr);
|
|||
|
|
|||
|
/*
|
|||
|
* Recompute numFds.
|
|||
|
*/
|
|||
|
|
|||
|
numFds = 0;
|
|||
|
for (filePtr = fileList; filePtr != NULL;
|
|||
|
filePtr = filePtr->nextPtr) {
|
|||
|
if (numFds <= filePtr->fd) {
|
|||
|
numFds = filePtr->fd+1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_CreateTimerHandler --
|
|||
|
*
|
|||
|
* Arrange for a given procedure to be invoked at a particular
|
|||
|
* time in the future.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* The return value is a token for the timer event, which
|
|||
|
* may be used to delete the event before it fires.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* When milliseconds have elapsed, proc will be invoked
|
|||
|
* exactly once.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
Tk_TimerToken
|
|||
|
Tk_CreateTimerHandler(milliseconds, proc, clientData)
|
|||
|
int milliseconds; /* How many milliseconds to wait
|
|||
|
* before invoking proc. */
|
|||
|
Tk_TimerProc *proc; /* Procedure to invoke. */
|
|||
|
ClientData clientData; /* Arbitrary data to pass to proc. */
|
|||
|
{
|
|||
|
register TimerEvent *timerPtr, *tPtr2, *prevPtr;
|
|||
|
static int id = 0;
|
|||
|
|
|||
|
timerPtr = (TimerEvent *) ckalloc(sizeof(TimerEvent));
|
|||
|
|
|||
|
/*
|
|||
|
* Compute when the event should fire.
|
|||
|
*/
|
|||
|
|
|||
|
(void) gettimeofday(&timerPtr->time, (struct timezone *) NULL);
|
|||
|
timerPtr->time.tv_sec += milliseconds/1000;
|
|||
|
timerPtr->time.tv_usec += (milliseconds%1000)*1000;
|
|||
|
if (timerPtr->time.tv_usec > 1000000) {
|
|||
|
timerPtr->time.tv_usec -= 1000000;
|
|||
|
timerPtr->time.tv_sec += 1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Fill in other fields for the event.
|
|||
|
*/
|
|||
|
|
|||
|
timerPtr->proc = proc;
|
|||
|
timerPtr->clientData = clientData;
|
|||
|
id++;
|
|||
|
timerPtr->token = (Tk_TimerToken) id;
|
|||
|
|
|||
|
/*
|
|||
|
* Add the event to the queue in the correct position
|
|||
|
* (ordered by event firing time).
|
|||
|
*/
|
|||
|
|
|||
|
for (tPtr2 = timerQueue, prevPtr = NULL; tPtr2 != NULL;
|
|||
|
prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
|
|||
|
if ((tPtr2->time.tv_sec > timerPtr->time.tv_sec)
|
|||
|
|| ((tPtr2->time.tv_sec == timerPtr->time.tv_sec)
|
|||
|
&& (tPtr2->time.tv_usec > timerPtr->time.tv_usec))) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (prevPtr == NULL) {
|
|||
|
timerPtr->nextPtr = timerQueue;
|
|||
|
timerQueue = timerPtr;
|
|||
|
} else {
|
|||
|
timerPtr->nextPtr = prevPtr->nextPtr;
|
|||
|
prevPtr->nextPtr = timerPtr;
|
|||
|
}
|
|||
|
return timerPtr->token;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_DeleteTimerHandler --
|
|||
|
*
|
|||
|
* Delete a previously-registered timer handler.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Destroy the timer callback identified by TimerToken,
|
|||
|
* so that its associated procedure will not be called.
|
|||
|
* If the callback has already fired, or if the given
|
|||
|
* token doesn't exist, then nothing happens.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_DeleteTimerHandler(token)
|
|||
|
Tk_TimerToken token; /* Result previously returned by
|
|||
|
* Tk_DeleteTimerHandler. */
|
|||
|
{
|
|||
|
register TimerEvent *timerPtr, *prevPtr;
|
|||
|
|
|||
|
for (timerPtr = timerQueue, prevPtr = NULL; timerPtr != NULL;
|
|||
|
prevPtr = timerPtr, timerPtr = timerPtr->nextPtr) {
|
|||
|
if (timerPtr->token != token) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (prevPtr == NULL) {
|
|||
|
timerQueue = timerPtr->nextPtr;
|
|||
|
} else {
|
|||
|
prevPtr->nextPtr = timerPtr->nextPtr;
|
|||
|
}
|
|||
|
ckfree((char *) timerPtr);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_DoWhenIdle --
|
|||
|
*
|
|||
|
* Arrange for proc to be invoked the next time the
|
|||
|
* system is idle (i.e., just before the next time
|
|||
|
* that Tk_DoOneEvent would have to wait for something
|
|||
|
* to happen).
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Proc will eventually be called, with clientData
|
|||
|
* as argument. See the manual entry for details.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_DoWhenIdle(proc, clientData)
|
|||
|
Tk_IdleProc *proc; /* Procedure to invoke. */
|
|||
|
ClientData clientData; /* Arbitrary value to pass to proc. */
|
|||
|
{
|
|||
|
register IdleHandler *idlePtr;
|
|||
|
|
|||
|
idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
|
|||
|
idlePtr->proc = proc;
|
|||
|
idlePtr->clientData = clientData;
|
|||
|
idlePtr->generation = idleGeneration;
|
|||
|
idlePtr->nextPtr = NULL;
|
|||
|
if (lastIdlePtr == NULL) {
|
|||
|
idleList = idlePtr;
|
|||
|
} else {
|
|||
|
lastIdlePtr->nextPtr = idlePtr;
|
|||
|
}
|
|||
|
lastIdlePtr = idlePtr;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_CancelIdleCall --
|
|||
|
*
|
|||
|
* If there are any when-idle calls requested to a given procedure
|
|||
|
* with given clientData, cancel all of them.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* If the proc/clientData combination were on the when-idle list,
|
|||
|
* they are removed so that they will never be called.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_CancelIdleCall(proc, clientData)
|
|||
|
Tk_IdleProc *proc; /* Procedure that was previously registered. */
|
|||
|
ClientData clientData; /* Arbitrary value to pass to proc. */
|
|||
|
{
|
|||
|
register IdleHandler *idlePtr, *prevPtr;
|
|||
|
IdleHandler *nextPtr;
|
|||
|
|
|||
|
for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
|
|||
|
prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
|
|||
|
while ((idlePtr->proc == proc)
|
|||
|
&& (idlePtr->clientData == clientData)) {
|
|||
|
nextPtr = idlePtr->nextPtr;
|
|||
|
ckfree((char *) idlePtr);
|
|||
|
idlePtr = nextPtr;
|
|||
|
if (prevPtr == NULL) {
|
|||
|
idleList = idlePtr;
|
|||
|
} else {
|
|||
|
prevPtr->nextPtr = idlePtr;
|
|||
|
}
|
|||
|
if (idlePtr == NULL) {
|
|||
|
lastIdlePtr = prevPtr;
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_DoOneEvent --
|
|||
|
*
|
|||
|
* Process a single event of some sort. If there's no
|
|||
|
* work to do, wait for an event to occur, then process
|
|||
|
* it.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* The return value is 1 if the procedure actually found
|
|||
|
* an event to process. If no event was found then 0 is
|
|||
|
* returned.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* May delay execution of process while waiting for an
|
|||
|
* X event, X error, file-ready event, or timer event.
|
|||
|
* The handling of the event could cause additional
|
|||
|
* side effects. Collapses sequences of mouse-motion
|
|||
|
* events for the same window into a single event by
|
|||
|
* delaying motion event processing.
|
|||
|
*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
Tk_DoOneEvent(flags)
|
|||
|
int flags; /* Miscellaneous flag values: may be any
|
|||
|
* combination of TK_DONT_WAIT, TK_X_EVENTS,
|
|||
|
* TK_FILE_EVENTS, TK_TIMER_EVENTS, and
|
|||
|
* TK_IDLE_EVENTS. */
|
|||
|
{
|
|||
|
register FileEvent *filePtr;
|
|||
|
struct timeval curTime, timeout, *timeoutPtr;
|
|||
|
int numFound;
|
|||
|
static XEvent delayedMotionEvent; /* Used to hold motion events that
|
|||
|
* are being saved until later. */
|
|||
|
static int eventDelayed = 0; /* Non-zero means there is an event
|
|||
|
* in delayedMotionEvent. */
|
|||
|
|
|||
|
if ((flags & TK_ALL_EVENTS) == 0) {
|
|||
|
flags |= TK_ALL_EVENTS;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Phase One: see if there's already something ready
|
|||
|
* (either a file or a display) that was left over
|
|||
|
* from before (i.e don't do a select, just check the
|
|||
|
* bits from the last select).
|
|||
|
*/
|
|||
|
|
|||
|
checkFiles:
|
|||
|
if (tcl_AsyncReady) {
|
|||
|
(void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
for (filePtr = fileList; filePtr != NULL;
|
|||
|
filePtr = filePtr->nextPtr) {
|
|||
|
int mask;
|
|||
|
|
|||
|
/*
|
|||
|
* Displays: flush output, check for queued events,
|
|||
|
* and read events from the server if display is ready.
|
|||
|
* If there are any events, process one and then
|
|||
|
* return.
|
|||
|
*/
|
|||
|
|
|||
|
if (filePtr->isDisplay) {
|
|||
|
Display *display = (Display *) filePtr->clientData;
|
|||
|
XEvent event;
|
|||
|
|
|||
|
if (!(flags & TK_X_EVENTS)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
XFlush(display);
|
|||
|
if ((*filePtr->readPtr) & filePtr->mask) {
|
|||
|
*filePtr->readPtr &= ~filePtr->mask;
|
|||
|
if (XEventsQueued(display, QueuedAfterReading) == 0) {
|
|||
|
|
|||
|
/*
|
|||
|
* Things are very tricky if there aren't any events
|
|||
|
* readable at this point (after all, there was
|
|||
|
* supposedly data available on the connection).
|
|||
|
* A couple of things could have occurred:
|
|||
|
*
|
|||
|
* One possibility is that there were only error events
|
|||
|
* in the input from the server. If this happens,
|
|||
|
* we should return (we don't want to go to sleep
|
|||
|
* in XNextEvent below, since this would block out
|
|||
|
* other sources of input to the process).
|
|||
|
*
|
|||
|
* Another possibility is that our connection to the
|
|||
|
* server has been closed. This will not necessarily
|
|||
|
* be detected in XEventsQueued (!!), so if we just
|
|||
|
* return then there will be an infinite loop. To
|
|||
|
* detect such an error, generate a NoOp protocol
|
|||
|
* request to exercise the connection to the server,
|
|||
|
* then return. However, must disable SIGPIPE while
|
|||
|
* sending the event, or else the process will die
|
|||
|
* from the signal and won't invoke the X error
|
|||
|
* function to print a nice message.
|
|||
|
*/
|
|||
|
|
|||
|
void (*oldHandler)();
|
|||
|
|
|||
|
oldHandler = (void (*)()) signal(SIGPIPE, SIG_IGN);
|
|||
|
XNoOp(display);
|
|||
|
XFlush(display);
|
|||
|
(void) signal(SIGPIPE, oldHandler);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
if (restrictProc != NULL) {
|
|||
|
if (!XCheckIfEvent(display, &event, restrictProc,
|
|||
|
restrictArg)) {
|
|||
|
return 1;
|
|||
|
}
|
|||
|
} else {
|
|||
|
XNextEvent(display, &event);
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (QLength(display) == 0) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (restrictProc != NULL) {
|
|||
|
if (!XCheckIfEvent(display, &event, restrictProc,
|
|||
|
restrictArg)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
} else {
|
|||
|
XNextEvent(display, &event);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Got an event. Deal with mouse-motion-collapsing and
|
|||
|
* event-delaying here. If there's already an event delayed,
|
|||
|
* then process that event if it's incompatible with the new
|
|||
|
* event (new event not mouse motion, or window changed, or
|
|||
|
* state changed). If the new event is mouse motion, then
|
|||
|
* don't process it now; delay it until later in the hopes
|
|||
|
* that it can be merged with other mouse motion events
|
|||
|
* immediately following.
|
|||
|
*/
|
|||
|
|
|||
|
if (tkEventDebug) {
|
|||
|
eventTrace[traceIndex] = event;
|
|||
|
traceIndex = (traceIndex+1) % TK_NEVENTS;
|
|||
|
}
|
|||
|
|
|||
|
if (eventDelayed) {
|
|||
|
if (((event.type != MotionNotify)
|
|||
|
&& (event.type != GraphicsExpose)
|
|||
|
&& (event.type != NoExpose)
|
|||
|
&& (event.type != Expose))
|
|||
|
|| (event.xmotion.display
|
|||
|
!= delayedMotionEvent.xmotion.display)
|
|||
|
|| (event.xmotion.window
|
|||
|
!= delayedMotionEvent.xmotion.window)) {
|
|||
|
XEvent copy;
|
|||
|
|
|||
|
/*
|
|||
|
* Must copy the event out of delayedMotionEvent before
|
|||
|
* processing it, in order to allow recursive calls to
|
|||
|
* Tk_DoOneEvent as part of the handler.
|
|||
|
*/
|
|||
|
|
|||
|
copy = delayedMotionEvent;
|
|||
|
eventDelayed = 0;
|
|||
|
Tk_HandleEvent(©);
|
|||
|
}
|
|||
|
}
|
|||
|
if (event.type == MotionNotify) {
|
|||
|
delayedMotionEvent = event;
|
|||
|
eventDelayed = 1;
|
|||
|
} else {
|
|||
|
Tk_HandleEvent(&event);
|
|||
|
}
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Not a display: if the file is ready, call the
|
|||
|
* appropriate handler.
|
|||
|
*/
|
|||
|
|
|||
|
if (((*filePtr->readPtr | *filePtr->writePtr
|
|||
|
| *filePtr->exceptPtr) & filePtr->mask) == 0) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
if (!(flags & TK_FILE_EVENTS)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
mask = 0;
|
|||
|
if (*filePtr->readPtr & filePtr->mask) {
|
|||
|
mask |= TK_READABLE;
|
|||
|
*filePtr->readPtr &= ~filePtr->mask;
|
|||
|
}
|
|||
|
if (*filePtr->writePtr & filePtr->mask) {
|
|||
|
mask |= TK_WRITABLE;
|
|||
|
*filePtr->writePtr &= ~filePtr->mask;
|
|||
|
}
|
|||
|
if (*filePtr->exceptPtr & filePtr->mask) {
|
|||
|
mask |= TK_EXCEPTION;
|
|||
|
*filePtr->exceptPtr &= ~filePtr->mask;
|
|||
|
}
|
|||
|
(*filePtr->proc)(filePtr->clientData, mask);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Phase Two: get the current time and see if any timer
|
|||
|
* events are ready to fire. If so, fire one and return.
|
|||
|
*/
|
|||
|
|
|||
|
checkTime:
|
|||
|
if ((timerQueue != NULL) && (flags & TK_TIMER_EVENTS)) {
|
|||
|
register TimerEvent *timerPtr = timerQueue;
|
|||
|
|
|||
|
(void) gettimeofday(&curTime, (struct timezone *) NULL);
|
|||
|
if ((timerPtr->time.tv_sec < curTime.tv_sec)
|
|||
|
|| ((timerPtr->time.tv_sec == curTime.tv_sec)
|
|||
|
&& (timerPtr->time.tv_usec < curTime.tv_usec))) {
|
|||
|
timerQueue = timerPtr->nextPtr;
|
|||
|
(*timerPtr->proc)(timerPtr->clientData);
|
|||
|
ckfree((char *) timerPtr);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Phase Three: if there is a delayed motion event, process it
|
|||
|
* now, before any DoWhenIdle handlers. Better to process before
|
|||
|
* idle handlers than after, because the goal of idle handlers is
|
|||
|
* to delay until after all pending events have been processed.
|
|||
|
* Must free up delayedMotionEvent *before* calling Tk_HandleEvent,
|
|||
|
* so that the event handler can call Tk_DoOneEvent recursively
|
|||
|
* without infinite looping.
|
|||
|
*/
|
|||
|
|
|||
|
if ((eventDelayed) && (flags & TK_X_EVENTS)) {
|
|||
|
XEvent copy;
|
|||
|
|
|||
|
copy = delayedMotionEvent;
|
|||
|
eventDelayed = 0;
|
|||
|
Tk_HandleEvent(©);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Phase Four: if there are DoWhenIdle requests pending (or
|
|||
|
* if we're not allowed to block), then do a select with an
|
|||
|
* instantaneous timeout. If a ready file is found, then go
|
|||
|
* back to process it.
|
|||
|
*/
|
|||
|
|
|||
|
if (((idleList != NULL) && (flags & TK_IDLE_EVENTS))
|
|||
|
|| (flags & TK_DONT_WAIT)) {
|
|||
|
if (flags & (TK_X_EVENTS|TK_FILE_EVENTS)) {
|
|||
|
memcpy((VOID *) ready, (VOID *) masks,
|
|||
|
3*MASK_SIZE*sizeof(fd_mask));
|
|||
|
timeout.tv_sec = timeout.tv_usec = 0;
|
|||
|
numFound = select(numFds, (SELECT_MASK *) readPtr,
|
|||
|
(SELECT_MASK *) writePtr, (SELECT_MASK *) exceptPtr,
|
|||
|
&timeout);
|
|||
|
if (numFound == -1) {
|
|||
|
/*
|
|||
|
* Some systems don't clear the masks after an error, so
|
|||
|
* we have to do it here.
|
|||
|
*/
|
|||
|
|
|||
|
memset((VOID *) ready, 0, 3*MASK_SIZE*sizeof(fd_mask));
|
|||
|
}
|
|||
|
if ((numFound > 0) || ((numFound == -1) && (errno == EINTR))) {
|
|||
|
goto checkFiles;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Phase Five: process all pending DoWhenIdle requests.
|
|||
|
*/
|
|||
|
|
|||
|
if ((idleList != NULL) && (flags & TK_IDLE_EVENTS)) {
|
|||
|
register IdleHandler *idlePtr;
|
|||
|
int oldGeneration;
|
|||
|
|
|||
|
oldGeneration = idleList->generation;
|
|||
|
idleGeneration++;
|
|||
|
|
|||
|
/*
|
|||
|
* The code below is trickier than it may look, for the following
|
|||
|
* reasons:
|
|||
|
*
|
|||
|
* 1. New handlers can get added to the list while the current
|
|||
|
* one is being processed. If new ones get added, we don't
|
|||
|
* want to process them during this pass through the list (want
|
|||
|
* to check for other work to do first). This is implemented
|
|||
|
* using the generation number in the handler: new handlers
|
|||
|
* will have a different generation than any of the ones currently
|
|||
|
* on the list.
|
|||
|
* 2. The handler can call Tk_DoOneEvent, so we have to remove
|
|||
|
* the hander from the list before calling it. Otherwise an
|
|||
|
* infinite loop could result.
|
|||
|
* 3. Tk_CancelIdleCall can be called to remove an element from
|
|||
|
* the list while a handler is executing, so the list could
|
|||
|
* change structure during the call.
|
|||
|
*/
|
|||
|
|
|||
|
for (idlePtr = idleList;
|
|||
|
((idlePtr != NULL) && (idlePtr->generation == oldGeneration));
|
|||
|
idlePtr = idleList) {
|
|||
|
idleList = idlePtr->nextPtr;
|
|||
|
if (idleList == NULL) {
|
|||
|
lastIdlePtr = NULL;
|
|||
|
}
|
|||
|
(*idlePtr->proc)(idlePtr->clientData);
|
|||
|
ckfree((char *) idlePtr);
|
|||
|
}
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Phase Six: do a select to wait for either one of the
|
|||
|
* files to become ready or for the first timer event to
|
|||
|
* fire. Then go back to process the event.
|
|||
|
*/
|
|||
|
|
|||
|
if ((flags & TK_DONT_WAIT)
|
|||
|
|| !(flags & (TK_TIMER_EVENTS|TK_FILE_EVENTS|TK_X_EVENTS))) {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
if ((timerQueue == NULL) || !(flags & TK_TIMER_EVENTS)) {
|
|||
|
timeoutPtr = NULL;
|
|||
|
} else {
|
|||
|
timeoutPtr = &timeout;
|
|||
|
timeout.tv_sec = timerQueue->time.tv_sec - curTime.tv_sec;
|
|||
|
timeout.tv_usec = timerQueue->time.tv_usec - curTime.tv_usec;
|
|||
|
if (timeout.tv_usec < 0) {
|
|||
|
timeout.tv_sec -= 1;
|
|||
|
timeout.tv_usec += 1000000;
|
|||
|
}
|
|||
|
}
|
|||
|
memcpy((VOID *) ready, (VOID *) masks, 3*MASK_SIZE*sizeof(fd_mask));
|
|||
|
numFound = select(numFds, (SELECT_MASK *) readPtr,
|
|||
|
(SELECT_MASK *) writePtr, (SELECT_MASK *) exceptPtr,
|
|||
|
timeoutPtr);
|
|||
|
if (numFound == -1) {
|
|||
|
/*
|
|||
|
* Some systems don't clear the masks after an error, so
|
|||
|
* we have to do it here.
|
|||
|
*/
|
|||
|
|
|||
|
memset((VOID *) ready, 0, 3*MASK_SIZE*sizeof(fd_mask));
|
|||
|
}
|
|||
|
if (numFound == 0) {
|
|||
|
goto checkTime;
|
|||
|
}
|
|||
|
goto checkFiles;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_MainLoop --
|
|||
|
*
|
|||
|
* Call Tk_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_NumMainWindows > 0) {
|
|||
|
Tk_DoOneEvent(0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_Sleep --
|
|||
|
*
|
|||
|
* Delay execution for the specified number of milliseconds.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Time passes.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_Sleep(ms)
|
|||
|
int ms; /* Number of milliseconds to sleep. */
|
|||
|
{
|
|||
|
static struct timeval delay;
|
|||
|
|
|||
|
delay.tv_sec = ms/1000;
|
|||
|
delay.tv_usec = (ms%1000)*1000;
|
|||
|
(void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
|
|||
|
(SELECT_MASK *) 0, &delay);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* 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 or deferred.
|
|||
|
*
|
|||
|
* 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
|
|||
|
* or defer each incoming X event.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
Tk_RestrictProc *
|
|||
|
Tk_RestrictEvents(proc, arg, prevArgPtr)
|
|||
|
Tk_RestrictProc *proc; /* X "if" procedure to call for each
|
|||
|
* incoming event. See "XIfEvent" doc.
|
|||
|
* for details. */
|
|||
|
char *arg; /* Arbitrary argument to pass to proc. */
|
|||
|
char **prevArgPtr; /* Place to store information about previous
|
|||
|
* argument. */
|
|||
|
{
|
|||
|
Bool (*prev) _ANSI_ARGS_((Display *display, XEvent *eventPtr, char *arg));
|
|||
|
|
|||
|
prev = restrictProc;
|
|||
|
*prevArgPtr = restrictArg;
|
|||
|
restrictProc = proc;
|
|||
|
restrictArg = arg;
|
|||
|
return prev;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* 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;
|
|||
|
|
|||
|
/*
|
|||
|
* 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 = 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;
|
|||
|
|
|||
|
if (pendingPtr == NULL) {
|
|||
|
return dispPtr->lastEventTime;
|
|||
|
}
|
|||
|
eventPtr = 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;
|
|||
|
}
|