summaryrefslogtreecommitdiffstats
path: root/unix/tkUnixEvent.c
diff options
context:
space:
mode:
authordkf <dkf@noemail.net>2006-12-22 19:14:54 (GMT)
committerdkf <dkf@noemail.net>2006-12-22 19:14:54 (GMT)
commit4cf8a33f90826daef0a2be60863acd15d9dc8566 (patch)
treeb99d40739c86aaead16c335876bd27493f50838b /unix/tkUnixEvent.c
parent0c2e8fce9c27e4d15f949bdd25034a28fcd8520b (diff)
downloadtk-4cf8a33f90826daef0a2be60863acd15d9dc8566.zip
tk-4cf8a33f90826daef0a2be60863acd15d9dc8566.tar.gz
tk-4cf8a33f90826daef0a2be60863acd15d9dc8566.tar.bz2
Fix [Bug 1522467] using fix similar to that used in Tcl recently.
FossilOrigin-Name: 5c2dc8498481c92750b523c75a68332fe86b2dbb
Diffstat (limited to 'unix/tkUnixEvent.c')
-rw-r--r--unix/tkUnixEvent.c1405
1 files changed, 704 insertions, 701 deletions
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 <signal.h>
-
-/*
- * 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 <signal.h>
+
+/*
+ * 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:
+ */