tkUnixEvent.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:18k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkUnixEvent.c --
  3.  *
  4.  * This file implements an event source for X displays for the
  5.  * UNIX version of Tk.
  6.  *
  7.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * RCS: @(#) $Id: tkUnixEvent.c,v 1.11.2.7 2006/12/22 19:06:12 dkf Exp $
  13.  */
  14. #include "tkInt.h"
  15. #include "tkUnixInt.h"
  16. #include <signal.h>
  17. /*
  18.  * The following static indicates whether this module has been initialized
  19.  * in the current thread.
  20.  */
  21. typedef struct ThreadSpecificData {
  22.     int initialized;
  23. } ThreadSpecificData;
  24. static Tcl_ThreadDataKey dataKey;
  25. #if defined(TK_USE_INPUT_METHODS) && defined(PEEK_XCLOSEIM)
  26. /*
  27.  * Structure used to peek into internal XIM data structure.
  28.  * This is only known to work with XFree86.
  29.  */
  30. struct XIMPeek {
  31.     void *junk1, *junk2;
  32.     XIC  ic_chain;
  33. };
  34. #endif
  35. /*
  36.  * Prototypes for procedures that are referenced only in this file:
  37.  */
  38. static void DisplayCheckProc _ANSI_ARGS_((ClientData clientData,
  39.     int flags));
  40. static void DisplayExitHandler _ANSI_ARGS_((
  41.     ClientData clientData));
  42. static void DisplayFileProc _ANSI_ARGS_((ClientData clientData,
  43.     int flags));
  44. static void DisplaySetupProc _ANSI_ARGS_((ClientData clientData,
  45.     int flags));
  46. static void TransferXEventsToTcl _ANSI_ARGS_((Display *display));
  47. #ifdef TK_USE_INPUT_METHODS
  48. static void OpenIM _ANSI_ARGS_((TkDisplay *dispPtr));
  49. #endif
  50. /*
  51.  *----------------------------------------------------------------------
  52.  *
  53.  * TkCreateXEventSource --
  54.  *
  55.  * This procedure is called during Tk initialization to create
  56.  * the event source for X Window events.
  57.  *
  58.  * Results:
  59.  * None.
  60.  *
  61.  * Side effects:
  62.  * A new event source is created.
  63.  *
  64.  *----------------------------------------------------------------------
  65.  */
  66. void
  67. TkCreateXEventSource()
  68. {
  69.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  70.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  71.     if (!tsdPtr->initialized) {
  72. tsdPtr->initialized = 1;
  73. Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
  74. TkCreateExitHandler(DisplayExitHandler, NULL);
  75.     }
  76. }
  77. /*
  78.  *----------------------------------------------------------------------
  79.  *
  80.  * DisplayExitHandler --
  81.  *
  82.  * This function is called during finalization to clean up the
  83.  * display module.
  84.  *
  85.  * Results:
  86.  * None.
  87.  *
  88.  * Side effects:
  89.  * None.
  90.  *
  91.  *----------------------------------------------------------------------
  92.  */
  93. static void
  94. DisplayExitHandler(clientData)
  95.     ClientData clientData; /* Not used. */
  96. {
  97.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  98.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  99.     Tcl_DeleteEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
  100.     tsdPtr->initialized = 0;
  101. }
  102. /*
  103.  *----------------------------------------------------------------------
  104.  *
  105.  * TkpOpenDisplay --
  106.  *
  107.  * Allocates a new TkDisplay, opens the X display, and establishes
  108.  * the file handler for the connection.
  109.  *
  110.  * Results:
  111.  * A pointer to a Tk display structure.
  112.  *
  113.  * Side effects:
  114.  * Opens a display.
  115.  *
  116.  *----------------------------------------------------------------------
  117.  */
  118. TkDisplay *
  119. TkpOpenDisplay(display_name)
  120.     CONST char *display_name;
  121. {
  122.     TkDisplay *dispPtr;
  123.     Display *display = XOpenDisplay(display_name);
  124.     if (display == NULL) {
  125. return NULL;
  126.     }
  127.     dispPtr = (TkDisplay *) ckalloc(sizeof(TkDisplay));
  128.     memset(dispPtr, 0, sizeof(TkDisplay));
  129.     dispPtr->display = display;
  130. #ifdef TK_USE_INPUT_METHODS
  131.     OpenIM(dispPtr);
  132. #endif
  133.     Tcl_CreateFileHandler(ConnectionNumber(display), TCL_READABLE,
  134.     DisplayFileProc, (ClientData) dispPtr);
  135.     return dispPtr;
  136. }
  137. /*
  138.  *----------------------------------------------------------------------
  139.  *
  140.  * TkpCloseDisplay --
  141.  *
  142.  * Cancels notifier callbacks and closes a display.  
  143.  *
  144.  * Results:
  145.  * None.
  146.  *
  147.  * Side effects:
  148.  * Deallocates the displayPtr and unix-specific resources.
  149.  *
  150.  *----------------------------------------------------------------------
  151.  */
  152. void
  153. TkpCloseDisplay(dispPtr)
  154.     TkDisplay *dispPtr;
  155. {
  156.     TkSendCleanup(dispPtr);
  157.     TkFreeXId(dispPtr);
  158.     TkWmCleanup(dispPtr);
  159. #ifdef TK_USE_INPUT_METHODS
  160. #if TK_XIM_SPOT
  161.     if (dispPtr->inputXfs) {
  162. XFreeFontSet(dispPtr->display, dispPtr->inputXfs);
  163.     }
  164. #endif
  165.     if (dispPtr->inputMethod) {
  166. /*
  167.  * Calling XCloseIM with an input context that has not
  168.  * been freed can cause a crash. This crash has been
  169.  * reproduced under Linux systems with XFree86 3.3
  170.  * and may have also been seen under Solaris 2.3.
  171.  * The crash is caused by a double free of memory
  172.  * inside the X library. Memory that was already
  173.  * deallocated may be accessed again inside XCloseIM.
  174.  * This bug can be avoided by making sure that a
  175.  * call to XDestroyIC is made for each XCreateIC call.
  176.  * This bug has been fixed in XFree86 4.2.99.2.
  177.  * The internal layout of the XIM structure changed
  178.  * in the XFree86 4.2 release so the test should
  179.  * not be run for with these new releases.
  180.  */
  181. #if defined(TK_USE_INPUT_METHODS) && defined(PEEK_XCLOSEIM)
  182. int do_peek = 0;
  183. struct XIMPeek *peek;
  184. if (strstr(ServerVendor(dispPtr->display), "XFree86")) {
  185.     int vendrel = VendorRelease(dispPtr->display);
  186.     if (vendrel < 336) {
  187.         /* 3.3.4 and 3.3.5 */
  188.         do_peek = 1;
  189.     } else if (vendrel < 3900) {
  190.         /* Other 3.3.x versions */
  191.         do_peek = 1;
  192.     } else if (vendrel < 40000000) {
  193.         /* 4.0.x versions */
  194.         do_peek = 1;
  195.     } else {
  196.         /* Newer than 4.0 */
  197.         do_peek = 0;
  198.     }
  199. }
  200. if (do_peek) {
  201.     peek = (struct XIMPeek *) dispPtr->inputMethod;
  202.     if (peek->ic_chain != NULL)
  203.         panic("input contexts not freed before XCloseIM");
  204. }
  205. #endif
  206. XCloseIM(dispPtr->inputMethod);
  207.     }
  208. #endif
  209.     if (dispPtr->display != 0) {
  210. Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display));
  211. (void) XSync(dispPtr->display, False);
  212. (void) XCloseDisplay(dispPtr->display);
  213.     }
  214. }
  215. /*
  216.  *----------------------------------------------------------------------
  217.  *
  218.  * TkClipCleanup --
  219.  *
  220.  * This procedure is called to cleanup resources associated with
  221.  * claiming clipboard ownership and for receiving selection get
  222.  * results.  This function is called in tkWindow.c.  This has to be
  223.  * called by the display cleanup function because we still need the
  224.  * access display elements.
  225.  *
  226.  * Results:
  227.  * None.
  228.  *
  229.  * Side effects:
  230.  * Resources are freed - the clipboard may no longer be used.
  231.  *
  232.  *----------------------------------------------------------------------
  233.  */
  234. void
  235. TkClipCleanup(dispPtr)
  236.     TkDisplay *dispPtr; /* display associated with clipboard */
  237. {
  238.     if (dispPtr->clipWindow != NULL) {
  239. Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom,
  240. dispPtr->applicationAtom);
  241. Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom,
  242. dispPtr->windowAtom);
  243. Tk_DestroyWindow(dispPtr->clipWindow);
  244. Tcl_Release((ClientData) dispPtr->clipWindow);
  245. dispPtr->clipWindow = NULL;
  246.     }
  247. }
  248. /*
  249.  *----------------------------------------------------------------------
  250.  *
  251.  * DisplaySetupProc --
  252.  *
  253.  * This procedure implements the setup part of the UNIX X display
  254.  * event source.  It is invoked by Tcl_DoOneEvent before entering
  255.  * the notifier to check for events on all displays.
  256.  *
  257.  * Results:
  258.  * None.
  259.  *
  260.  * Side effects:
  261.  * If data is queued on a display inside Xlib, then the maximum
  262.  * block time will be set to 0 to ensure that the notifier returns
  263.  * control to Tcl even if there is no more data on the X connection.
  264.  *
  265.  *----------------------------------------------------------------------
  266.  */
  267. static void
  268. DisplaySetupProc(clientData, flags)
  269.     ClientData clientData; /* Not used. */
  270.     int flags;
  271. {
  272.     TkDisplay *dispPtr;
  273.     static Tcl_Time blockTime = { 0, 0 };
  274.     if (!(flags & TCL_WINDOW_EVENTS)) {
  275. return;
  276.     }
  277.     for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
  278.  dispPtr = dispPtr->nextPtr) {
  279. /*
  280.  * Flush the display. If data is pending on the X queue, set
  281.  * the block time to zero.  This ensures that we won't block
  282.  * in the notifier if there is data in the X queue, but not on
  283.  * the server socket.
  284.  */
  285. XFlush(dispPtr->display);
  286. if (QLength(dispPtr->display) > 0) {
  287.     Tcl_SetMaxBlockTime(&blockTime);
  288. }
  289.     }
  290. }
  291. /*
  292.  *----------------------------------------------------------------------
  293.  *
  294.  *  TransferXEventsToTcl
  295.  *
  296.  *      Transfer events from the X event queue to the Tk event queue.
  297.  *
  298.  * Results:
  299.  * None.
  300.  *
  301.  * Side effects:
  302.  * Moves queued X events onto the Tcl event queue.
  303.  *
  304.  *----------------------------------------------------------------------
  305.  */
  306. static void
  307. TransferXEventsToTcl(display)
  308.     Display *display;
  309. {
  310.     XEvent event;
  311.     /*
  312.      * Transfer events from the X event queue to the Tk event queue
  313.      * after XIM event filtering.  KeyPress and KeyRelease events
  314.      * are filtered in Tk_HandleEvent instead of here, so that Tk's
  315.      * focus management code can redirect them.
  316.      */
  317.     while (QLength(display) > 0) {
  318. XNextEvent(display, &event);
  319. if (event.type != KeyPress && event.type != KeyRelease) {
  320.     if (XFilterEvent(&event, None)) {
  321. continue;
  322.     }
  323. }
  324. Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  325.     }
  326. }
  327. /*
  328.  *----------------------------------------------------------------------
  329.  *
  330.  * DisplayCheckProc --
  331.  *
  332.  * This procedure checks for events sitting in the X event
  333.  * queue.
  334.  *
  335.  * Results:
  336.  * None.
  337.  *
  338.  * Side effects:
  339.  * Moves queued events onto the Tcl event queue.
  340.  *
  341.  *----------------------------------------------------------------------
  342.  */
  343. static void
  344. DisplayCheckProc(clientData, flags)
  345.     ClientData clientData; /* Not used. */
  346.     int flags;
  347. {
  348.     TkDisplay *dispPtr;
  349.     if (!(flags & TCL_WINDOW_EVENTS)) {
  350. return;
  351.     }
  352.     for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
  353.  dispPtr = dispPtr->nextPtr) {
  354. XFlush(dispPtr->display);
  355. TransferXEventsToTcl(dispPtr->display);
  356.     }
  357. }
  358. /*
  359.  *----------------------------------------------------------------------
  360.  *
  361.  * DisplayFileProc --
  362.  *
  363.  * This procedure implements the file handler for the X connection.
  364.  *
  365.  * Results:
  366.  * None.
  367.  *
  368.  * Side effects:
  369.  * Makes entries on the Tcl event queue for all the events available
  370.  * from all the displays.
  371.  *
  372.  *----------------------------------------------------------------------
  373.  */
  374. static void
  375. DisplayFileProc(clientData, flags)
  376.     ClientData clientData; /* The display pointer. */
  377.     int flags; /* Should be TCL_READABLE. */
  378. {
  379.     TkDisplay *dispPtr = (TkDisplay *) clientData;
  380.     Display *display = dispPtr->display;
  381.     int numFound;
  382.     XFlush(display);
  383.     numFound = XEventsQueued(display, QueuedAfterReading);
  384.     if (numFound == 0) {
  385. /*
  386.  * Things are very tricky if there aren't any events readable
  387.  * at this point (after all, there was supposedly data
  388.  * available on the connection).  A couple of things could
  389.  * have occurred:
  390.  * 
  391.  * One possibility is that there were only error events in the
  392.  * input from the server.  If this happens, we should return
  393.  * (we don't want to go to sleep in XNextEvent below, since
  394.  * this would block out other sources of input to the
  395.  * process).
  396.  *
  397.  * Another possibility is that our connection to the server
  398.  * has been closed.  This will not necessarily be detected in
  399.  * XEventsQueued (!!), so if we just return then there will be
  400.  * an infinite loop.  To detect such an error, generate a NoOp
  401.  * protocol request to exercise the connection to the server,
  402.  * then return.  However, must disable SIGPIPE while sending
  403.  * the request, or else the process will die from the signal
  404.  * and won't invoke the X error function to print a nice (?!)
  405.  * message.
  406.  */
  407. void (*oldHandler)();
  408. oldHandler = (void (*)()) signal(SIGPIPE, SIG_IGN);
  409. XNoOp(display);
  410. XFlush(display);
  411. (void) signal(SIGPIPE, oldHandler);
  412.     }
  413.     
  414.     TransferXEventsToTcl(display);
  415. }
  416. /*
  417.  *----------------------------------------------------------------------
  418.  *
  419.  * TkUnixDoOneXEvent --
  420.  *
  421.  * This routine waits for an X event to be processed or for a timeout to
  422.  * occur. The timeout is specified as an absolute time. This routine is
  423.  * called when Tk needs to wait for a particular X event without letting
  424.  * arbitrary events be processed. The caller will typically call
  425.  * Tk_RestrictEvents to set up an event filter before calling this
  426.  * routine. This routine will service at most one event per invocation.
  427.  *
  428.  * Results:
  429.  * Returns 0 if the timeout has expired, otherwise returns 1.
  430.  *
  431.  * Side effects:
  432.  * Can invoke arbitrary Tcl scripts.
  433.  *
  434.  *----------------------------------------------------------------------
  435.  */
  436. int
  437. TkUnixDoOneXEvent(timePtr)
  438.     Tcl_Time *timePtr; /* Specifies the absolute time when the call
  439.  * should time out. */
  440. {
  441.     TkDisplay *dispPtr;
  442.     static fd_mask readMask[MASK_SIZE];
  443.     struct timeval blockTime, *timeoutPtr;
  444.     Tcl_Time now;
  445.     int fd, index, numFound, numFdBits = 0;
  446.     fd_mask bit;
  447.     /*
  448.      * Look for queued events first. 
  449.      */
  450.     if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
  451. return 1;
  452.     }
  453.     /*
  454.      * Compute the next block time and check to see if we have timed out. Note
  455.      * that HP-UX defines tv_sec to be unsigned so we have to be careful in
  456.      * our arithmetic.
  457.      */
  458.     if (timePtr) {
  459. TclpGetTime(&now);
  460. blockTime.tv_sec = timePtr->sec;
  461. blockTime.tv_usec = timePtr->usec - now.usec;
  462. if (blockTime.tv_usec < 0) {
  463.     now.sec += 1;
  464.     blockTime.tv_usec += 1000000;
  465. }
  466. if (blockTime.tv_sec < now.sec) {
  467.     blockTime.tv_sec = 0;
  468.     blockTime.tv_usec = 0;
  469. } else {
  470.     blockTime.tv_sec -= now.sec;
  471. }
  472. timeoutPtr = &blockTime;
  473.     } else {
  474. timeoutPtr = NULL;
  475.     }
  476.     /*
  477.      * Set up the select mask for all of the displays. If a display has data
  478.      * pending, then we want to poll instead of blocking.
  479.      */
  480.     memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask));
  481.     for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
  482.     dispPtr = dispPtr->nextPtr) {
  483. XFlush(dispPtr->display);
  484. if (QLength(dispPtr->display) > 0) {
  485.     blockTime.tv_sec = 0;
  486.     blockTime.tv_usec = 0;
  487. }
  488. fd = ConnectionNumber(dispPtr->display);
  489. /*
  490.  * Assume there are always at least 'fd' bits in the 'readMask' array.
  491.  */
  492. index = fd/(NBBY*sizeof(fd_mask));
  493. bit = ((fd_mask)1) << (fd%(NBBY*sizeof(fd_mask)));
  494. readMask[index] |= bit;
  495. if (numFdBits <= fd) {
  496.     numFdBits = fd+1;
  497. }
  498.     }
  499.     numFound = select(numFdBits, (SELECT_MASK *) &readMask[0], NULL, NULL,
  500.     timeoutPtr);
  501.     if (numFound <= 0) {
  502. /*
  503.  * Some systems don't clear the masks after an error, so we have to do
  504.  * it here.
  505.  */
  506. memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask));
  507.     }
  508.     /*
  509.      * Process any new events on the display connections.
  510.      */
  511.     for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
  512.     dispPtr = dispPtr->nextPtr) {
  513. fd = ConnectionNumber(dispPtr->display);
  514. index = fd/(NBBY*sizeof(fd_mask));
  515. bit = ((fd_mask)1) << (fd%(NBBY*sizeof(fd_mask)));
  516. if ((readMask[index] & bit) || (QLength(dispPtr->display) > 0)) {
  517.     DisplayFileProc((ClientData)dispPtr, TCL_READABLE);
  518. }
  519.     }
  520.     if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
  521. return 1;
  522.     }
  523.     /*
  524.      * Check to see if we timed out.
  525.      */
  526.     if (timePtr) {
  527. TclpGetTime(&now);
  528. if ((now.sec > timePtr->sec) || ((now.sec == timePtr->sec)
  529. && (now.usec > timePtr->usec))) {
  530.     return 0;
  531. }
  532.     }
  533.     /*
  534.      * We had an event but we did not generate a Tcl event from it. Behave as
  535.      * though we dealt with it. (JYL&SS)
  536.      */
  537.     return 1;
  538. }
  539. /*
  540.  *----------------------------------------------------------------------
  541.  *
  542.  * TkpSync --
  543.  *
  544.  * This routine ensures that all pending X requests have been
  545.  * seen by the server, and that any pending X events have been
  546.  * moved onto the Tk event queue.
  547.  *
  548.  * Results:
  549.  * None.
  550.  *
  551.  * Side effects:
  552.  * Places new events on the Tk event queue.
  553.  *
  554.  *----------------------------------------------------------------------
  555.  */
  556. void
  557. TkpSync(display)
  558.     Display *display; /* Display to sync. */
  559. {
  560.     XSync(display, False);
  561.     /*
  562.      * Transfer events from the X event queue to the Tk event queue.
  563.      */
  564.     TransferXEventsToTcl(display);
  565. }
  566. #ifdef TK_USE_INPUT_METHODS
  567. /* 
  568.  *--------------------------------------------------------------
  569.  *
  570.  * OpenIM --
  571.  *
  572.  * Tries to open an X input method, associated with the
  573.  * given display.  Right now we can only deal with a bare-bones
  574.  * input style:  no preedit, and no status.
  575.  *
  576.  * Results:
  577.  * Stores the input method in dispPtr->inputMethod;  if there isn't
  578.  * a suitable input method, then NULL is stored in dispPtr->inputMethod.
  579.  *
  580.  * Side effects:
  581.  * An input method gets opened.
  582.  *
  583.  *--------------------------------------------------------------
  584.  */
  585. static void
  586. OpenIM(dispPtr)
  587.     TkDisplay *dispPtr; /* Tk's structure for the display. */
  588. {
  589.     unsigned short i;
  590.     XIMStyles *stylePtr;
  591.     if (XSetLocaleModifiers("") == NULL) {
  592. goto error;
  593.     }
  594.     dispPtr->inputMethod = XOpenIM(dispPtr->display, NULL, NULL, NULL);
  595.     if (dispPtr->inputMethod == NULL) {
  596. return;
  597.     }
  598.     if ((XGetIMValues(dispPtr->inputMethod, XNQueryInputStyle, &stylePtr,
  599.     NULL) != NULL) || (stylePtr == NULL)) {
  600. goto error;
  601.     }
  602. #if TK_XIM_SPOT
  603.     /*
  604.      * If we want to do over-the-spot XIM, we have to check that this
  605.      * mode is supported.  If not we will fall-through to the check below.
  606.      */
  607.     for (i = 0; i < stylePtr->count_styles; i++) {
  608. if (stylePtr->supported_styles[i]
  609. == (XIMPreeditPosition | XIMStatusNothing)) {
  610.     dispPtr->flags |= TK_DISPLAY_XIM_SPOT;
  611.     XFree(stylePtr);
  612.     return;
  613. }
  614.     }
  615. #endif
  616.     for (i = 0; i < stylePtr->count_styles; i++) {
  617. if (stylePtr->supported_styles[i]
  618. == (XIMPreeditNothing | XIMStatusNothing)) {
  619.     XFree(stylePtr);
  620.     return;
  621. }
  622.     }
  623.     XFree(stylePtr);
  624.     error:
  625.     if (dispPtr->inputMethod) {
  626. /*
  627.  * This call should not suffer from any core dumping problems
  628.  * since we have not allocated any input contexts.
  629.  */
  630. XCloseIM(dispPtr->inputMethod);
  631. dispPtr->inputMethod = NULL;
  632.     }
  633. }
  634. #endif /* TK_USE_INPUT_METHODS */