diff options
Diffstat (limited to 'unix/tkUnixEvent.c')
-rw-r--r-- | unix/tkUnixEvent.c | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/unix/tkUnixEvent.c b/unix/tkUnixEvent.c new file mode 100644 index 0000000..ace4cc3 --- /dev/null +++ b/unix/tkUnixEvent.c @@ -0,0 +1,498 @@ +/* + * tkUnixEvent.c -- + * + * This file implements an event source for X displays for the + * UNIX version of Tk. + * + * Copyright (c) 1995-1997 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: @(#) tkUnixEvent.c 1.17 97/09/11 12:51:04 + */ + +#include "tkInt.h" +#include "tkUnixInt.h" +#include <signal.h> + +/* + * The following static indicates whether this module has been initialized. + */ + +static int initialized = 0; + +/* + * Prototypes for procedures that are referenced only in this file: + */ + +static void DisplayCheckProc _ANSI_ARGS_((ClientData clientData, + int flags)); +static void DisplayExitHandler _ANSI_ARGS_(( + ClientData clientData)); +static void DisplayFileProc _ANSI_ARGS_((ClientData clientData, + int flags)); +static void DisplaySetupProc _ANSI_ARGS_((ClientData clientData, + int flags)); + +/* + *---------------------------------------------------------------------- + * + * TkCreateXEventSource -- + * + * This procedure is called during Tk initialization to create + * the event source for X Window events. + * + * Results: + * None. + * + * Side effects: + * A new event source is created. + * + *---------------------------------------------------------------------- + */ + +void +TkCreateXEventSource() +{ + if (!initialized) { + initialized = 1; + Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL); + Tcl_CreateExitHandler(DisplayExitHandler, NULL); + } +} + +/* + *---------------------------------------------------------------------- + * + * DisplayExitHandler -- + * + * This function is called during finalization to clean up the + * display module. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +DisplayExitHandler(clientData) + ClientData clientData; /* Not used. */ +{ + Tcl_DeleteEventSource(DisplaySetupProc, DisplayCheckProc, NULL); + initialized = 0; +} + +/* + *---------------------------------------------------------------------- + * + * TkpOpenDisplay -- + * + * Allocates a new TkDisplay, opens the X display, and establishes + * the file handler for the connection. + * + * Results: + * A pointer to a Tk display structure. + * + * Side effects: + * Opens a display. + * + *---------------------------------------------------------------------- + */ + +TkDisplay * +TkpOpenDisplay(display_name) + char *display_name; +{ + TkDisplay *dispPtr; + Display *display = XOpenDisplay(display_name); + + if (display == NULL) { + return NULL; + } + dispPtr = (TkDisplay *) ckalloc(sizeof(TkDisplay)); + dispPtr->display = display; + Tcl_CreateFileHandler(ConnectionNumber(display), TCL_READABLE, + DisplayFileProc, (ClientData) dispPtr); + return dispPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TkpCloseDisplay -- + * + * Cancels notifier callbacks and closes a display. + * + * Results: + * None. + * + * Side effects: + * Deallocates the displayPtr. + * + *---------------------------------------------------------------------- + */ + +void +TkpCloseDisplay(displayPtr) + TkDisplay *displayPtr; +{ + TkDisplay *dispPtr = (TkDisplay *) displayPtr; + + if (dispPtr->display != 0) { + Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display)); + + (void) XSync(dispPtr->display, False); + (void) XCloseDisplay(dispPtr->display); + } + + ckfree((char *) dispPtr); +} + +/* + *---------------------------------------------------------------------- + * + * DisplaySetupProc -- + * + * This procedure implements the setup part of the UNIX X display + * event source. It is invoked by Tcl_DoOneEvent before entering + * the notifier to check for events on all displays. + * + * Results: + * None. + * + * Side effects: + * If data is queued on a display inside Xlib, then the maximum + * block time will be set to 0 to ensure that the notifier returns + * control to Tcl even if there is no more data on the X connection. + * + *---------------------------------------------------------------------- + */ + +static void +DisplaySetupProc(clientData, flags) + ClientData clientData; /* Not used. */ + int flags; +{ + TkDisplay *dispPtr; + static Tcl_Time blockTime = { 0, 0 }; + + if (!(flags & TCL_WINDOW_EVENTS)) { + return; + } + + for (dispPtr = tkDisplayList; dispPtr != NULL; + dispPtr = dispPtr->nextPtr) { + + /* + * Flush the display. If data is pending on the X queue, set + * the block time to zero. This ensures that we won't block + * in the notifier if there is data in the X queue, but not on + * the server socket. + */ + + XFlush(dispPtr->display); + if (XQLength(dispPtr->display) > 0) { + Tcl_SetMaxBlockTime(&blockTime); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * DisplayCheckProc -- + * + * This procedure checks for events sitting in the X event + * queue. + * + * Results: + * None. + * + * Side effects: + * Moves queued events onto the Tcl event queue. + * + *---------------------------------------------------------------------- + */ + +static void +DisplayCheckProc(clientData, flags) + ClientData clientData; /* Not used. */ + int flags; +{ + TkDisplay *dispPtr; + XEvent event; + int numFound; + + if (!(flags & TCL_WINDOW_EVENTS)) { + return; + } + + for (dispPtr = tkDisplayList; dispPtr != NULL; + dispPtr = dispPtr->nextPtr) { + XFlush(dispPtr->display); + numFound = XQLength(dispPtr->display); + + /* + * Transfer events from the X event queue to the Tk event queue. + */ + + while (numFound > 0) { + XNextEvent(dispPtr->display, &event); + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + numFound--; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * DisplayFileProc -- + * + * This procedure implements the file handler for the X connection. + * + * Results: + * None. + * + * Side effects: + * Makes entries on the Tcl event queue for all the events available + * from all the displays. + * + *---------------------------------------------------------------------- + */ + +static void +DisplayFileProc(clientData, flags) + ClientData clientData; /* The display pointer. */ + int flags; /* Should be TCL_READABLE. */ +{ + TkDisplay *dispPtr = (TkDisplay *) clientData; + Display *display = dispPtr->display; + XEvent event; + int numFound; + + XFlush(display); + numFound = XEventsQueued(display, QueuedAfterReading); + if (numFound == 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 request, 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); + } + + /* + * Transfer events from the X event queue to the Tk event queue. + */ + + while (numFound > 0) { + XNextEvent(display, &event); + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + numFound--; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkUnixDoOneXEvent -- + * + * This routine waits for an X event to be processed or for + * a timeout to occur. The timeout is specified as an absolute + * time. This routine is called when Tk needs to wait for a + * particular X event without letting arbitrary events be + * processed. The caller will typically call Tk_RestrictEvents + * to set up an event filter before calling this routine. This + * routine will service at most one event per invocation. + * + * Results: + * Returns 0 if the timeout has expired, otherwise returns 1. + * + * Side effects: + * Can invoke arbitrary Tcl scripts. + * + *---------------------------------------------------------------------- + */ + +int +TkUnixDoOneXEvent(timePtr) + Tcl_Time *timePtr; /* Specifies the absolute time when the + * call should time out. */ +{ + TkDisplay *dispPtr; + static fd_mask readMask[MASK_SIZE]; + struct timeval blockTime, *timeoutPtr; + Tcl_Time now; + int fd, index, bit, numFound, numFdBits = 0; + + /* + * Look for queued events first. + */ + + if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) { + return 1; + } + + /* + * Compute the next block time and check to see if we have timed out. + * Note that HP-UX defines tv_sec to be unsigned so we have to be + * careful in our arithmetic. + */ + + if (timePtr) { + TclpGetTime(&now); + blockTime.tv_sec = timePtr->sec; + blockTime.tv_usec = timePtr->usec - now.usec; + if (blockTime.tv_usec < 0) { + now.sec += 1; + blockTime.tv_usec += 1000000; + } + if (blockTime.tv_sec < now.sec) { + blockTime.tv_sec = 0; + blockTime.tv_usec = 0; + } else { + blockTime.tv_sec -= now.sec; + } + timeoutPtr = &blockTime; + } else { + timeoutPtr = NULL; + } + + /* + * Set up the select mask for all of the displays. If a display has + * data pending, then we want to poll instead of blocking. + */ + + memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask)); + for (dispPtr = tkDisplayList; dispPtr != NULL; + dispPtr = dispPtr->nextPtr) { + XFlush(dispPtr->display); + if (XQLength(dispPtr->display) > 0) { + blockTime.tv_sec = 0; + blockTime.tv_usec = 0; + } + fd = ConnectionNumber(dispPtr->display); + index = fd/(NBBY*sizeof(fd_mask)); + bit = 1 << (fd%(NBBY*sizeof(fd_mask))); + readMask[index] |= bit; + if (numFdBits <= fd) { + numFdBits = fd+1; + } + } + + numFound = select(numFdBits, (SELECT_MASK *) &readMask[0], NULL, NULL, + timeoutPtr); + if (numFound <= 0) { + /* + * Some systems don't clear the masks after an error, so + * we have to do it here. + */ + + memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask)); + } + + /* + * Process any new events on the display connections. + */ + + for (dispPtr = tkDisplayList; dispPtr != NULL; + dispPtr = dispPtr->nextPtr) { + fd = ConnectionNumber(dispPtr->display); + index = fd/(NBBY*sizeof(fd_mask)); + bit = 1 << (fd%(NBBY*sizeof(fd_mask))); + if ((readMask[index] & bit) || (XQLength(dispPtr->display) > 0)) { + DisplayFileProc((ClientData)dispPtr, TCL_READABLE); + } + } + if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) { + return 1; + } + + /* + * Check to see if we timed out. + */ + + if (timePtr) { + TclpGetTime(&now); + if ((now.sec > timePtr->sec) || ((now.sec == timePtr->sec) + && (now.usec > timePtr->usec))) { + return 0; + } + } + + /* + * We had an event but we did not generate a Tcl event from it. Behave + * as though we dealt with it. (JYL&SS) + */ + + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * TkpSync -- + * + * This routine ensures that all pending X requests have been + * seen by the server, and that any pending X events have been + * moved onto the Tk event queue. + * + * Results: + * None. + * + * Side effects: + * Places new events on the Tk event queue. + * + *---------------------------------------------------------------------- + */ + +void +TkpSync(display) + Display *display; /* Display to sync. */ +{ + int numFound = 0; + XEvent event; + + XSync(display, False); + + /* + * Transfer events from the X event queue to the Tk event queue. + */ + + numFound = XQLength(display); + while (numFound > 0) { + XNextEvent(display, &event); + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + numFound--; + } +} |