From a66df8ec622f8e3177809819dd20fbfc87d357bf Mon Sep 17 00:00:00 2001 From: dkf Date: Fri, 22 Dec 2006 19:14:54 +0000 Subject: Fix [Bug 1522467] using fix similar to that used in Tcl recently. --- ChangeLog | 4 + unix/tkUnixEvent.c | 1405 ++++++++++++++++++++++++++-------------------------- 2 files changed, 708 insertions(+), 701 deletions(-) diff --git a/ChangeLog b/ChangeLog index c2e298a..366819d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2006-12-22 Donal K. Fellows + * unix/tkUnixEvent.c (TkUnixDoOneXEvent): Made correct on AMD64 and + other similar 64-bit systems where fd_mask is not 'unsigned int' in + effect. [Bug 1522467] + * library/msgs/es_ES.msg (removed): * library/msgs/es.msg: Fixed translation fault that was present in all Spanish-speaking locales. [Bug 1111213] diff --git a/unix/tkUnixEvent.c b/unix/tkUnixEvent.c index 500b447..54b4abf 100644 --- a/unix/tkUnixEvent.c +++ b/unix/tkUnixEvent.c @@ -1,701 +1,704 @@ -/* - * 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. - * - * RCS: @(#) $Id: tkUnixEvent.c,v 1.20 2006/01/20 18:58:55 jenglish Exp $ - */ - -#include "tkInt.h" -#include "tkUnixInt.h" -#include - -/* - * The following static indicates whether this module has been initialized - * in the current thread. - */ - -typedef struct ThreadSpecificData { - int initialized; -} ThreadSpecificData; -static Tcl_ThreadDataKey dataKey; - -#if defined(TK_USE_INPUT_METHODS) && defined(PEEK_XCLOSEIM) -/* - * Structure used to peek into internal XIM data structure. This is only known - * to work with XFree86. - */ - -struct XIMPeek { - void *junk1, *junk2; - XIC ic_chain; -}; -#endif - -/* - * Prototypes for functions that are referenced only in this file: - */ - -static void DisplayCheckProc(ClientData clientData, int flags); -static void DisplayExitHandler(ClientData clientData); -static void DisplayFileProc(ClientData clientData, int flags); -static void DisplaySetupProc(ClientData clientData, int flags); -static void TransferXEventsToTcl(Display *display); -#ifdef TK_USE_INPUT_METHODS -static void OpenIM(TkDisplay *dispPtr); -#endif - -/* - *---------------------------------------------------------------------- - * - * TkCreateXEventSource -- - * - * This function 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(void) -{ - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - - if (!tsdPtr->initialized) { - tsdPtr->initialized = 1; - Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL); - TkCreateExitHandler(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) /* Not used. */ -{ - ThreadSpecificData *tsdPtr = (ThreadSpecificData *) - Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); - - Tcl_DeleteEventSource(DisplaySetupProc, DisplayCheckProc, NULL); - tsdPtr->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( - CONST char *displayNameStr) -{ - TkDisplay *dispPtr; - Display *display = XOpenDisplay(displayNameStr); - - if (display == NULL) { - return NULL; - } - dispPtr = (TkDisplay *) ckalloc(sizeof(TkDisplay)); - memset(dispPtr, 0, sizeof(TkDisplay)); - dispPtr->display = display; -#ifdef TK_USE_INPUT_METHODS - OpenIM(dispPtr); -#endif - 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 and unix-specific resources. - * - *---------------------------------------------------------------------- - */ - -void -TkpCloseDisplay( - TkDisplay *dispPtr) -{ - TkSendCleanup(dispPtr); - - TkFreeXId(dispPtr); - - TkWmCleanup(dispPtr); - -#ifdef TK_USE_INPUT_METHODS -#if TK_XIM_SPOT - if (dispPtr->inputXfs) { - XFreeFontSet(dispPtr->display, dispPtr->inputXfs); - } -#endif - if (dispPtr->inputMethod) { - /* - * Calling XCloseIM with an input context that has not been freed can - * cause a crash. This crash has been reproduced under Linux systems - * with XFree86 3.3 and may have also been seen under Solaris 2.3. - * The crash is caused by a double free of memory inside the X - * library. Memory that was already deallocated may be accessed again - * inside XCloseIM. This bug can be avoided by making sure that a call - * to XDestroyIC is made for each XCreateIC call. This bug has been - * fixed in XFree86 4.2.99.2. The internal layout of the XIM structure - * changed in the XFree86 4.2 release so the test should not be run - * for with these new releases. - */ - -#if defined(TK_USE_INPUT_METHODS) && defined(PEEK_XCLOSEIM) - int do_peek = 0; - struct XIMPeek *peek; - - if (strstr(ServerVendor(dispPtr->display), "XFree86")) { - int vendrel = VendorRelease(dispPtr->display); - if (vendrel < 336) { - /* 3.3.4 and 3.3.5 */ - do_peek = 1; - } else if (vendrel < 3900) { - /* Other 3.3.x versions */ - do_peek = 1; - } else if (vendrel < 40000000) { - /* 4.0.x versions */ - do_peek = 1; - } else { - /* Newer than 4.0 */ - do_peek = 0; - } - } - - if (do_peek) { - peek = (struct XIMPeek *) dispPtr->inputMethod; - if (peek->ic_chain != NULL) { - Tcl_Panic("input contexts not freed before XCloseIM"); - } - } -#endif - XCloseIM(dispPtr->inputMethod); - } -#endif - - if (dispPtr->display != 0) { - Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display)); - (void) XSync(dispPtr->display, False); - (void) XCloseDisplay(dispPtr->display); - } -} - -/* - *---------------------------------------------------------------------- - * - * TkClipCleanup -- - * - * This function is called to cleanup resources associated with claiming - * clipboard ownership and for receiving selection get results. This - * function is called in tkWindow.c. This has to be called by the display - * cleanup function because we still need the access display elements. - * - * Results: - * None. - * - * Side effects: - * Resources are freed - the clipboard may no longer be used. - * - *---------------------------------------------------------------------- - */ - -void -TkClipCleanup( - TkDisplay *dispPtr) /* Display associated with clipboard */ -{ - if (dispPtr->clipWindow != NULL) { - Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom, - dispPtr->applicationAtom); - Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom, - dispPtr->windowAtom); - - Tk_DestroyWindow(dispPtr->clipWindow); - Tcl_Release((ClientData) dispPtr->clipWindow); - dispPtr->clipWindow = NULL; - } -} - -/* - *---------------------------------------------------------------------- - * - * DisplaySetupProc -- - * - * This function 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 clientData, /* Not used. */ - int flags) -{ - TkDisplay *dispPtr; - static Tcl_Time blockTime = { 0, 0 }; - - if (!(flags & TCL_WINDOW_EVENTS)) { - return; - } - - for (dispPtr = TkGetDisplayList(); 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 (QLength(dispPtr->display) > 0) { - Tcl_SetMaxBlockTime(&blockTime); - } - } -} - -/* - *---------------------------------------------------------------------- - * - * TransferXEventsToTcl - * - * Transfer events from the X event queue to the Tk event queue. - * - * Results: - * None. - * - * Side effects: - * Moves queued X events onto the Tcl event queue. - * - *---------------------------------------------------------------------- - */ - -static void -TransferXEventsToTcl( - Display *display) -{ - XEvent event; - - /* - * Transfer events from the X event queue to the Tk event queue - * after XIM event filtering. KeyPress and KeyRelease events - * are filtered in Tk_HandleEvent instead of here, so that Tk's - * focus management code can redirect them. - */ - while (QLength(display) > 0) { - XNextEvent(display, &event); - if (event.type != KeyPress && event.type != KeyRelease) { - if (XFilterEvent(&event, None)) { - continue; - } - } - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); - } -} - -/* - *---------------------------------------------------------------------- - * - * DisplayCheckProc -- - * - * This function 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 clientData, /* Not used. */ - int flags) -{ - TkDisplay *dispPtr; - - if (!(flags & TCL_WINDOW_EVENTS)) { - return; - } - - for (dispPtr = TkGetDisplayList(); dispPtr != NULL; - dispPtr = dispPtr->nextPtr) { - XFlush(dispPtr->display); - TransferXEventsToTcl(dispPtr->display); - } -} - -/* - *---------------------------------------------------------------------- - * - * DisplayFileProc -- - * - * This function 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 clientData, /* The display pointer. */ - int flags) /* Should be TCL_READABLE. */ -{ - TkDisplay *dispPtr = (TkDisplay *) clientData; - Display *display = dispPtr->display; - 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); - } - - TransferXEventsToTcl(display); -} - -/* - *---------------------------------------------------------------------- - * - * 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( - 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) { - Tcl_GetTime(&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 = TkGetDisplayList(); dispPtr != NULL; - dispPtr = dispPtr->nextPtr) { - XFlush(dispPtr->display); - if (QLength(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 = TkGetDisplayList(); 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) || (QLength(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) { - Tcl_GetTime(&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 to sync. */ -{ - XSync(display, False); - - /* - * Transfer events from the X event queue to the Tk event queue. - */ - - TransferXEventsToTcl(display); -} -#ifdef TK_USE_INPUT_METHODS - -/* - *-------------------------------------------------------------- - * - * OpenIM -- - * - * Tries to open an X input method, associated with the given display. - * Right now we can only deal with a bare-bones input style: no preedit, - * and no status. - * - * Results: - * Stores the input method in dispPtr->inputMethod; if there isn't a - * suitable input method, then NULL is stored in dispPtr->inputMethod. - * - * Side effects: - * An input method gets opened. - * - *-------------------------------------------------------------- - */ - -static void -OpenIM( - TkDisplay *dispPtr) /* Tk's structure for the display. */ -{ - unsigned short i; - XIMStyles *stylePtr; - - if (XSetLocaleModifiers("") == NULL) { - goto error; - } - - dispPtr->inputMethod = XOpenIM(dispPtr->display, NULL, NULL, NULL); - if (dispPtr->inputMethod == NULL) { - return; - } - - if ((XGetIMValues(dispPtr->inputMethod, XNQueryInputStyle, &stylePtr, - NULL) != NULL) || (stylePtr == NULL)) { - goto error; - } - -#if TK_XIM_SPOT - /* - * If we want to do over-the-spot XIM, we have to check that this mode is - * supported. If not we will fall-through to the check below. - */ - for (i = 0; i < stylePtr->count_styles; i++) { - if (stylePtr->supported_styles[i] - == (XIMPreeditPosition | XIMStatusNothing)) { - dispPtr->flags |= TK_DISPLAY_XIM_SPOT; - XFree(stylePtr); - return; - } - } -#endif /* TK_XIM_SPOT */ - - for (i = 0; i < stylePtr->count_styles; i++) { - if (stylePtr->supported_styles[i] - == (XIMPreeditNothing | XIMStatusNothing)) { - XFree(stylePtr); - return; - } - } - XFree(stylePtr); - - error: - - if (dispPtr->inputMethod) { - /* - * This call should not suffer from any core dumping problems since we - * have not allocated any input contexts. - */ - - XCloseIM(dispPtr->inputMethod); - dispPtr->inputMethod = NULL; - } -} -#endif /* TK_USE_INPUT_METHODS */ - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ +/* + * 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. + * + * RCS: @(#) $Id: tkUnixEvent.c,v 1.21 2006/12/22 19:14:54 dkf Exp $ + */ + +#include "tkInt.h" +#include "tkUnixInt.h" +#include + +/* + * The following static indicates whether this module has been initialized in + * the current thread. + */ + +typedef struct ThreadSpecificData { + int initialized; +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +#if defined(TK_USE_INPUT_METHODS) && defined(PEEK_XCLOSEIM) +/* + * Structure used to peek into internal XIM data structure. This is only known + * to work with XFree86. + */ + +struct XIMPeek { + void *junk1, *junk2; + XIC ic_chain; +}; +#endif + +/* + * Prototypes for functions that are referenced only in this file: + */ + +static void DisplayCheckProc(ClientData clientData, int flags); +static void DisplayExitHandler(ClientData clientData); +static void DisplayFileProc(ClientData clientData, int flags); +static void DisplaySetupProc(ClientData clientData, int flags); +static void TransferXEventsToTcl(Display *display); +#ifdef TK_USE_INPUT_METHODS +static void OpenIM(TkDisplay *dispPtr); +#endif + +/* + *---------------------------------------------------------------------- + * + * TkCreateXEventSource -- + * + * This function 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(void) +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (!tsdPtr->initialized) { + tsdPtr->initialized = 1; + Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL); + TkCreateExitHandler(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) /* Not used. */ +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + Tcl_DeleteEventSource(DisplaySetupProc, DisplayCheckProc, NULL); + tsdPtr->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( + CONST char *displayNameStr) +{ + TkDisplay *dispPtr; + Display *display = XOpenDisplay(displayNameStr); + + if (display == NULL) { + return NULL; + } + dispPtr = (TkDisplay *) ckalloc(sizeof(TkDisplay)); + memset(dispPtr, 0, sizeof(TkDisplay)); + dispPtr->display = display; +#ifdef TK_USE_INPUT_METHODS + OpenIM(dispPtr); +#endif + 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 and unix-specific resources. + * + *---------------------------------------------------------------------- + */ + +void +TkpCloseDisplay( + TkDisplay *dispPtr) +{ + TkSendCleanup(dispPtr); + + TkFreeXId(dispPtr); + + TkWmCleanup(dispPtr); + +#ifdef TK_USE_INPUT_METHODS +#if TK_XIM_SPOT + if (dispPtr->inputXfs) { + XFreeFontSet(dispPtr->display, dispPtr->inputXfs); + } +#endif + if (dispPtr->inputMethod) { + /* + * Calling XCloseIM with an input context that has not been freed can + * cause a crash. This crash has been reproduced under Linux systems + * with XFree86 3.3 and may have also been seen under Solaris 2.3. The + * crash is caused by a double free of memory inside the X library. + * Memory that was already deallocated may be accessed again inside + * XCloseIM. This bug can be avoided by making sure that a call to + * XDestroyIC is made for each XCreateIC call. This bug has been fixed + * in XFree86 4.2.99.2. The internal layout of the XIM structure + * changed in the XFree86 4.2 release so the test should not be run + * for with these new releases. + */ + +#if defined(TK_USE_INPUT_METHODS) && defined(PEEK_XCLOSEIM) + int do_peek = 0; + struct XIMPeek *peek; + + if (strstr(ServerVendor(dispPtr->display), "XFree86")) { + int vendrel = VendorRelease(dispPtr->display); + + if (vendrel < 336) { + /* 3.3.4 and 3.3.5 */ + do_peek = 1; + } else if (vendrel < 3900) { + /* Other 3.3.x versions */ + do_peek = 1; + } else if (vendrel < 40000000) { + /* 4.0.x versions */ + do_peek = 1; + } else { + /* Newer than 4.0 */ + do_peek = 0; + } + } + + if (do_peek) { + peek = (struct XIMPeek *) dispPtr->inputMethod; + if (peek->ic_chain != NULL) { + Tcl_Panic("input contexts not freed before XCloseIM"); + } + } +#endif + XCloseIM(dispPtr->inputMethod); + } +#endif + + if (dispPtr->display != 0) { + Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display)); + (void) XSync(dispPtr->display, False); + (void) XCloseDisplay(dispPtr->display); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkClipCleanup -- + * + * This function is called to cleanup resources associated with claiming + * clipboard ownership and for receiving selection get results. This + * function is called in tkWindow.c. This has to be called by the display + * cleanup function because we still need the access display elements. + * + * Results: + * None. + * + * Side effects: + * Resources are freed - the clipboard may no longer be used. + * + *---------------------------------------------------------------------- + */ + +void +TkClipCleanup( + TkDisplay *dispPtr) /* Display associated with clipboard */ +{ + if (dispPtr->clipWindow != NULL) { + Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom, + dispPtr->applicationAtom); + Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom, + dispPtr->windowAtom); + + Tk_DestroyWindow(dispPtr->clipWindow); + Tcl_Release((ClientData) dispPtr->clipWindow); + dispPtr->clipWindow = NULL; + } +} + +/* + *---------------------------------------------------------------------- + * + * DisplaySetupProc -- + * + * This function 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 clientData, /* Not used. */ + int flags) +{ + TkDisplay *dispPtr; + static Tcl_Time blockTime = { 0, 0 }; + + if (!(flags & TCL_WINDOW_EVENTS)) { + return; + } + + for (dispPtr = TkGetDisplayList(); 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 (QLength(dispPtr->display) > 0) { + Tcl_SetMaxBlockTime(&blockTime); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TransferXEventsToTcl -- + * + * Transfer events from the X event queue to the Tk event queue. + * + * Results: + * None. + * + * Side effects: + * Moves queued X events onto the Tcl event queue. + * + *---------------------------------------------------------------------- + */ + +static void +TransferXEventsToTcl( + Display *display) +{ + XEvent event; + + /* + * Transfer events from the X event queue to the Tk event queue after XIM + * event filtering. KeyPress and KeyRelease events are filtered in + * Tk_HandleEvent instead of here, so that Tk's focus management code can + * redirect them. + */ + + while (QLength(display) > 0) { + XNextEvent(display, &event); + if (event.type != KeyPress && event.type != KeyRelease) { + if (XFilterEvent(&event, None)) { + continue; + } + } + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + } +} + +/* + *---------------------------------------------------------------------- + * + * DisplayCheckProc -- + * + * This function 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 clientData, /* Not used. */ + int flags) +{ + TkDisplay *dispPtr; + + if (!(flags & TCL_WINDOW_EVENTS)) { + return; + } + + for (dispPtr = TkGetDisplayList(); dispPtr != NULL; + dispPtr = dispPtr->nextPtr) { + XFlush(dispPtr->display); + TransferXEventsToTcl(dispPtr->display); + } +} + +/* + *---------------------------------------------------------------------- + * + * DisplayFileProc -- + * + * This function 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 clientData, /* The display pointer. */ + int flags) /* Should be TCL_READABLE. */ +{ + TkDisplay *dispPtr = (TkDisplay *) clientData; + Display *display = dispPtr->display; + 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); + } + + TransferXEventsToTcl(display); +} + +/* + *---------------------------------------------------------------------- + * + * 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( + 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, numFound, numFdBits = 0; + fd_mask bit; + + /* + * 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) { + Tcl_GetTime(&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(readMask, 0, MASK_SIZE*sizeof(fd_mask)); + for (dispPtr = TkGetDisplayList(); dispPtr != NULL; + dispPtr = dispPtr->nextPtr) { + XFlush(dispPtr->display); + if (QLength(dispPtr->display) > 0) { + blockTime.tv_sec = 0; + blockTime.tv_usec = 0; + } + fd = ConnectionNumber(dispPtr->display); + index = fd/(NBBY*sizeof(fd_mask)); + bit = ((fd_mask)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(readMask, 0, MASK_SIZE*sizeof(fd_mask)); + } + + /* + * Process any new events on the display connections. + */ + + for (dispPtr = TkGetDisplayList(); dispPtr != NULL; + dispPtr = dispPtr->nextPtr) { + fd = ConnectionNumber(dispPtr->display); + index = fd/(NBBY*sizeof(fd_mask)); + bit = ((fd_mask)1) << (fd%(NBBY*sizeof(fd_mask))); + if ((readMask[index] & bit) || (QLength(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) { + Tcl_GetTime(&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 to sync. */ +{ + XSync(display, False); + + /* + * Transfer events from the X event queue to the Tk event queue. + */ + + TransferXEventsToTcl(display); +} +#ifdef TK_USE_INPUT_METHODS + +/* + *-------------------------------------------------------------- + * + * OpenIM -- + * + * Tries to open an X input method, associated with the given display. + * Right now we can only deal with a bare-bones input style: no preedit, + * and no status. + * + * Results: + * Stores the input method in dispPtr->inputMethod; if there isn't a + * suitable input method, then NULL is stored in dispPtr->inputMethod. + * + * Side effects: + * An input method gets opened. + * + *-------------------------------------------------------------- + */ + +static void +OpenIM( + TkDisplay *dispPtr) /* Tk's structure for the display. */ +{ + unsigned short i; + XIMStyles *stylePtr; + + if (XSetLocaleModifiers("") == NULL) { + goto error; + } + + dispPtr->inputMethod = XOpenIM(dispPtr->display, NULL, NULL, NULL); + if (dispPtr->inputMethod == NULL) { + return; + } + + if ((XGetIMValues(dispPtr->inputMethod, XNQueryInputStyle, &stylePtr, + NULL) != NULL) || (stylePtr == NULL)) { + goto error; + } + +#if TK_XIM_SPOT + /* + * If we want to do over-the-spot XIM, we have to check that this mode is + * supported. If not we will fall-through to the check below. + */ + + for (i = 0; i < stylePtr->count_styles; i++) { + if (stylePtr->supported_styles[i] + == (XIMPreeditPosition | XIMStatusNothing)) { + dispPtr->flags |= TK_DISPLAY_XIM_SPOT; + XFree(stylePtr); + return; + } + } +#endif /* TK_XIM_SPOT */ + + for (i = 0; i < stylePtr->count_styles; i++) { + if (stylePtr->supported_styles[i] + == (XIMPreeditNothing | XIMStatusNothing)) { + XFree(stylePtr); + return; + } + } + XFree(stylePtr); + + error: + if (dispPtr->inputMethod) { + /* + * This call should not suffer from any core dumping problems since we + * have not allocated any input contexts. + */ + + XCloseIM(dispPtr->inputMethod); + dispPtr->inputMethod = NULL; + } +} +#endif /* TK_USE_INPUT_METHODS */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ -- cgit v0.12