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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclXtNotify.c --
  3.  *
  4.  * This file contains the notifier driver implementation for the
  5.  * Xt intrinsics.
  6.  *
  7.  * Copyright (c) 1997 by 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: tclXtNotify.c,v 1.4 1999/07/02 06:05:34 welch Exp $
  13.  */
  14. #include <X11/Intrinsic.h>
  15. #include <tclInt.h>
  16. /*
  17.  * This structure is used to keep track of the notifier info for a 
  18.  * a registered file.
  19.  */
  20. typedef struct FileHandler {
  21.     int fd;
  22.     int mask; /* Mask of desired events: TCL_READABLE, etc. */
  23.     int readyMask; /* Events that have been seen since the
  24.    last time FileHandlerEventProc was called
  25.    for this file. */
  26.     XtInputId read; /* Xt read callback handle. */
  27.     XtInputId write; /* Xt write callback handle. */
  28.     XtInputId except; /* Xt exception callback handle. */
  29.     Tcl_FileProc *proc; /* Procedure to call, in the style of
  30.  * Tcl_CreateFileHandler. */
  31.     ClientData clientData; /* Argument to pass to proc. */
  32.     struct FileHandler *nextPtr;/* Next in list of all files we care about. */
  33. } FileHandler;
  34. /*
  35.  * The following structure is what is added to the Tcl event queue when
  36.  * file handlers are ready to fire.
  37.  */
  38. typedef struct FileHandlerEvent {
  39.     Tcl_Event header; /* Information that is standard for
  40.  * all events. */
  41.     int fd; /* File descriptor that is ready.  Used
  42.  * to find the FileHandler structure for
  43.  * the file (can't point directly to the
  44.  * FileHandler structure because it could
  45.  * go away while the event is queued). */
  46. } FileHandlerEvent;
  47. /*
  48.  * The following static structure contains the state information for the
  49.  * Xt based implementation of the Tcl notifier.
  50.  */
  51. static struct NotifierState {
  52.     XtAppContext appContext; /* The context used by the Xt
  53.                                          * notifier. Can be set with
  54.                                          * TclSetAppContext. */
  55.     int appContextCreated; /* Was it created by us? */
  56.     XtIntervalId currentTimeout; /* Handle of current timer. */
  57.     FileHandler *firstFileHandlerPtr; /* Pointer to head of file handler
  58.  * list. */
  59. } notifier;
  60. /*
  61.  * The following static indicates whether this module has been initialized.
  62.  */
  63. static int initialized = 0;
  64. /*
  65.  * Static routines defined in this file.
  66.  */
  67. static int FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  68.     int flags));
  69. static void FileProc _ANSI_ARGS_((caddr_t clientData,
  70.     int *source, XtInputId *id));
  71. void InitNotifier _ANSI_ARGS_((void));
  72. static void NotifierExitHandler _ANSI_ARGS_((
  73.     ClientData clientData));
  74. static void TimerProc _ANSI_ARGS_((caddr_t clientData,
  75.     XtIntervalId *id));
  76. static void CreateFileHandler _ANSI_ARGS_((int fd, int mask, 
  77. Tcl_FileProc * proc, ClientData clientData));
  78. static void DeleteFileHandler _ANSI_ARGS_((int fd));
  79. static void SetTimer _ANSI_ARGS_((Tcl_Time * timePtr));
  80. static int WaitForEvent _ANSI_ARGS_((Tcl_Time * timePtr));
  81. /*
  82.  * Functions defined in this file for use by users of the Xt Notifier:
  83.  */
  84. EXTERN XtAppContext TclSetAppContext _ANSI_ARGS_((XtAppContext ctx));
  85. /*
  86.  *----------------------------------------------------------------------
  87.  *
  88.  * TclSetAppContext --
  89.  *
  90.  * Set the notifier application context.
  91.  *
  92.  * Results:
  93.  * None.
  94.  *
  95.  * Side effects:
  96.  * Sets the application context used by the notifier. Panics if
  97.  * the context is already set when called.
  98.  *
  99.  *----------------------------------------------------------------------
  100.  */
  101. XtAppContext
  102. TclSetAppContext(appContext)
  103.     XtAppContext appContext;
  104. {
  105.     if (!initialized) {
  106.         InitNotifier();
  107.     }
  108.     /*
  109.      * If we already have a context we check whether we were asked to set a
  110.      * new context. If so, we panic because we try to prevent switching
  111.      * contexts by mistake. Otherwise, we return the one we have.
  112.      */
  113.     
  114.     if (notifier.appContext != NULL) {
  115.         if (appContext != NULL) {
  116.     /*
  117.              * We already have a context. We do not allow switching contexts
  118.              * after initialization, so we panic.
  119.              */
  120.         
  121.             panic("TclSetAppContext:  multiple application contexts");
  122.         }
  123.     } else {
  124.         /*
  125.          * If we get here we have not yet gotten a context, so either create
  126.          * one or use the one supplied by our caller.
  127.          */
  128.         if (appContext == NULL) {
  129.     /*
  130.              * We must create a new context and tell our caller what it is, so
  131.              * she can use it too.
  132.              */
  133.     
  134.             notifier.appContext = XtCreateApplicationContext();
  135.             notifier.appContextCreated = 1;
  136.         } else {
  137.     /*
  138.              * Otherwise we remember the context that our caller gave us
  139.              * and use it.
  140.              */
  141.     
  142.             notifier.appContextCreated = 0;
  143.             notifier.appContext = appContext;
  144.         }
  145.     }
  146.     
  147.     return notifier.appContext;
  148. }
  149. /*
  150.  *----------------------------------------------------------------------
  151.  *
  152.  * InitNotifier --
  153.  *
  154.  * Initializes the notifier state.
  155.  *
  156.  * Results:
  157.  * None.
  158.  *
  159.  * Side effects:
  160.  * Creates a new exit handler.
  161.  *
  162.  *----------------------------------------------------------------------
  163.  */
  164. void
  165. InitNotifier()
  166. {
  167.     Tcl_NotifierProcs notifier;
  168.     /*
  169.      * Only reinitialize if we are not in exit handling. The notifier
  170.      * can get reinitialized after its own exit handler has run, because
  171.      * of exit handlers for the I/O and timer sub-systems (order dependency).
  172.      */
  173.     if (TclInExit()) {
  174.         return;
  175.     }
  176.     notifier.createFileHandlerProc = CreateFileHandler;
  177.     notifier.deleteFileHandlerProc = DeleteFileHandler;
  178.     notifier.setTimerProc = SetTimer;
  179.     notifier.waitForEventProc = WaitForEvent;
  180.     Tcl_SetNotifier(&notifier);
  181.     /*
  182.      * DO NOT create the application context yet; doing so would prevent
  183.      * external applications from setting it for us to their own ones.
  184.      */
  185.     
  186.     initialized = 1;
  187.     memset(&notifier, 0, sizeof(notifier));
  188.     Tcl_CreateExitHandler(NotifierExitHandler, NULL);
  189. }
  190. /*
  191.  *----------------------------------------------------------------------
  192.  *
  193.  * NotifierExitHandler --
  194.  *
  195.  * This function is called to cleanup the notifier state before
  196.  * Tcl is unloaded.
  197.  *
  198.  * Results:
  199.  * None.
  200.  *
  201.  * Side effects:
  202.  * Destroys the notifier window.
  203.  *
  204.  *----------------------------------------------------------------------
  205.  */
  206. static void
  207. NotifierExitHandler(
  208.     ClientData clientData) /* Not used. */
  209. {
  210.     if (notifier.currentTimeout != 0) {
  211.         XtRemoveTimeOut(notifier.currentTimeout);
  212.     }
  213.     for (; notifier.firstFileHandlerPtr != NULL; ) {
  214.         Tcl_DeleteFileHandler(notifier.firstFileHandlerPtr->fd);
  215.     }
  216.     if (notifier.appContextCreated) {
  217.         XtDestroyApplicationContext(notifier.appContext);
  218.         notifier.appContextCreated = 0;
  219.         notifier.appContext = NULL;
  220.     }
  221.     initialized = 0;
  222. }
  223. /*
  224.  *----------------------------------------------------------------------
  225.  *
  226.  * SetTimer --
  227.  *
  228.  * This procedure sets the current notifier timeout value.
  229.  *
  230.  * Results:
  231.  * None.
  232.  *
  233.  * Side effects:
  234.  * Replaces any previous timer.
  235.  *
  236.  *----------------------------------------------------------------------
  237.  */
  238. static void
  239. SetTimer(timePtr)
  240.     Tcl_Time *timePtr; /* Timeout value, may be NULL. */
  241. {
  242.     long timeout;
  243.     if (!initialized) {
  244. InitNotifier();
  245.     }
  246.     TclSetAppContext(NULL);
  247.     if (notifier.currentTimeout != 0) {
  248. XtRemoveTimeOut(notifier.currentTimeout);
  249.     }
  250.     if (timePtr) {
  251. timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
  252. notifier.currentTimeout =
  253.             XtAppAddTimeOut(notifier.appContext, (unsigned long) timeout,
  254.                     TimerProc, NULL);
  255.     } else {
  256. notifier.currentTimeout = 0;
  257.     }
  258. }
  259. /*
  260.  *----------------------------------------------------------------------
  261.  *
  262.  * TimerProc --
  263.  *
  264.  * This procedure is the XtTimerCallbackProc used to handle
  265.  * timeouts.
  266.  *
  267.  * Results:
  268.  * None.
  269.  *
  270.  * Side effects:
  271.  *      Processes all queued events.
  272.  *
  273.  *----------------------------------------------------------------------
  274.  */
  275. static void
  276. TimerProc(data, id)
  277.     caddr_t data; /* Not used. */
  278.     XtIntervalId *id;
  279. {
  280.     if (*id != notifier.currentTimeout) {
  281. return;
  282.     }
  283.     notifier.currentTimeout = 0;
  284.     Tcl_ServiceAll();
  285. }
  286. /*
  287.  *----------------------------------------------------------------------
  288.  *
  289.  * CreateFileHandler --
  290.  *
  291.  * This procedure registers a file handler with the Xt notifier.
  292.  *
  293.  * Results:
  294.  * None.
  295.  *
  296.  * Side effects:
  297.  * Creates a new file handler structure and registers one or more
  298.  * input procedures with Xt.
  299.  *
  300.  *----------------------------------------------------------------------
  301.  */
  302. static void
  303. CreateFileHandler(fd, mask, proc, clientData)
  304.     int fd; /* Handle of stream to watch. */
  305.     int mask; /* OR'ed combination of TCL_READABLE,
  306.  * TCL_WRITABLE, and TCL_EXCEPTION:
  307.  * indicates conditions under which
  308.  * proc should be called. */
  309.     Tcl_FileProc *proc; /* Procedure to call for each
  310.  * selected event. */
  311.     ClientData clientData; /* Arbitrary data to pass to proc. */
  312. {
  313.     FileHandler *filePtr;
  314.     if (!initialized) {
  315. InitNotifier();
  316.     }
  317.     TclSetAppContext(NULL);
  318.     for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
  319.     filePtr = filePtr->nextPtr) {
  320. if (filePtr->fd == fd) {
  321.     break;
  322. }
  323.     }
  324.     if (filePtr == NULL) {
  325. filePtr = (FileHandler*) ckalloc(sizeof(FileHandler));
  326. filePtr->fd = fd;
  327. filePtr->read = 0;
  328. filePtr->write = 0;
  329. filePtr->except = 0;
  330. filePtr->readyMask = 0;
  331. filePtr->mask = 0;
  332. filePtr->nextPtr = notifier.firstFileHandlerPtr;
  333. notifier.firstFileHandlerPtr = filePtr;
  334.     }
  335.     filePtr->proc = proc;
  336.     filePtr->clientData = clientData;
  337.     /*
  338.      * Register the file with the Xt notifier, if it hasn't been done yet.
  339.      */
  340.     if (mask & TCL_READABLE) {
  341. if (!(filePtr->mask & TCL_READABLE)) {
  342.     filePtr->read =
  343.                 XtAppAddInput(notifier.appContext, fd, XtInputReadMask,
  344.                         FileProc, filePtr);
  345. }
  346.     } else {
  347. if (filePtr->mask & TCL_READABLE) {
  348.     XtRemoveInput(filePtr->read);
  349. }
  350.     }
  351.     if (mask & TCL_WRITABLE) {
  352. if (!(filePtr->mask & TCL_WRITABLE)) {
  353.     filePtr->write =
  354.                 XtAppAddInput(notifier.appContext, fd, XtInputWriteMask,
  355.                         FileProc, filePtr);
  356. }
  357.     } else {
  358. if (filePtr->mask & TCL_WRITABLE) {
  359.     XtRemoveInput(filePtr->write);
  360. }
  361.     }
  362.     if (mask & TCL_EXCEPTION) {
  363. if (!(filePtr->mask & TCL_EXCEPTION)) {
  364.     filePtr->except =
  365.                 XtAppAddInput(notifier.appContext, fd, XtInputExceptMask,
  366.                         FileProc, filePtr);
  367. }
  368.     } else {
  369. if (filePtr->mask & TCL_EXCEPTION) {
  370.     XtRemoveInput(filePtr->except);
  371. }
  372.     }
  373.     filePtr->mask = mask;
  374. }
  375. /*
  376.  *----------------------------------------------------------------------
  377.  *
  378.  * DeleteFileHandler --
  379.  *
  380.  * Cancel a previously-arranged callback arrangement for
  381.  * a file.
  382.  *
  383.  * Results:
  384.  * None.
  385.  *
  386.  * Side effects:
  387.  * If a callback was previously registered on file, remove it.
  388.  *
  389.  *----------------------------------------------------------------------
  390.  */
  391. static void
  392. DeleteFileHandler(fd)
  393.     int fd; /* Stream id for which to remove
  394.  * callback procedure. */
  395. {
  396.     FileHandler *filePtr, *prevPtr;
  397.     if (!initialized) {
  398. InitNotifier();
  399.     }
  400.     TclSetAppContext(NULL);
  401.     /*
  402.      * Find the entry for the given file (and return if there
  403.      * isn't one).
  404.      */
  405.     for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
  406.     prevPtr = filePtr, filePtr = filePtr->nextPtr) {
  407. if (filePtr == NULL) {
  408.     return;
  409. }
  410. if (filePtr->fd == fd) {
  411.     break;
  412. }
  413.     }
  414.     /*
  415.      * Clean up information in the callback record.
  416.      */
  417.     if (prevPtr == NULL) {
  418. notifier.firstFileHandlerPtr = filePtr->nextPtr;
  419.     } else {
  420. prevPtr->nextPtr = filePtr->nextPtr;
  421.     }
  422.     if (filePtr->mask & TCL_READABLE) {
  423. XtRemoveInput(filePtr->read);
  424.     }
  425.     if (filePtr->mask & TCL_WRITABLE) {
  426. XtRemoveInput(filePtr->write);
  427.     }
  428.     if (filePtr->mask & TCL_EXCEPTION) {
  429. XtRemoveInput(filePtr->except);
  430.     }
  431.     ckfree((char *) filePtr);
  432. }
  433. /*
  434.  *----------------------------------------------------------------------
  435.  *
  436.  * FileProc --
  437.  *
  438.  * These procedures are called by Xt when a file becomes readable,
  439.  * writable, or has an exception.
  440.  *
  441.  * Results:
  442.  * None.
  443.  *
  444.  * Side effects:
  445.  * Makes an entry on the Tcl event queue if the event is
  446.  * interesting.
  447.  *
  448.  *----------------------------------------------------------------------
  449.  */
  450. static void
  451. FileProc(clientData, fd, id)
  452.     caddr_t clientData;
  453.     int *fd;
  454.     XtInputId *id;
  455. {
  456.     FileHandler *filePtr = (FileHandler *)clientData;
  457.     FileHandlerEvent *fileEvPtr;
  458.     int mask = 0;
  459.     /*
  460.      * Determine which event happened.
  461.      */
  462.     if (*id == filePtr->read) {
  463. mask = TCL_READABLE;
  464.     } else if (*id == filePtr->write) {
  465. mask = TCL_WRITABLE;
  466.     } else if (*id == filePtr->except) {
  467. mask = TCL_EXCEPTION;
  468.     }
  469.     /*
  470.      * Ignore unwanted or duplicate events.
  471.      */
  472.     if (!(filePtr->mask & mask) || (filePtr->readyMask & mask)) {
  473. return;
  474.     }
  475.     
  476.     /*
  477.      * This is an interesting event, so put it onto the event queue.
  478.      */
  479.     filePtr->readyMask |= mask;
  480.     fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent));
  481.     fileEvPtr->header.proc = FileHandlerEventProc;
  482.     fileEvPtr->fd = filePtr->fd;
  483.     Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
  484.     /*
  485.      * Process events on the Tcl event queue before returning to Xt.
  486.      */
  487.     Tcl_ServiceAll();
  488. }
  489. /*
  490.  *----------------------------------------------------------------------
  491.  *
  492.  * FileHandlerEventProc --
  493.  *
  494.  * This procedure is called by Tcl_ServiceEvent when a file event
  495.  * reaches the front of the event queue.  This procedure is
  496.  * responsible for actually handling the event by invoking the
  497.  * callback for the file handler.
  498.  *
  499.  * Results:
  500.  * Returns 1 if the event was handled, meaning it should be removed
  501.  * from the queue.  Returns 0 if the event was not handled, meaning
  502.  * it should stay on the queue.  The only time the event isn't
  503.  * handled is if the TCL_FILE_EVENTS flag bit isn't set.
  504.  *
  505.  * Side effects:
  506.  * Whatever the file handler's callback procedure does.
  507.  *
  508.  *----------------------------------------------------------------------
  509.  */
  510. static int
  511. FileHandlerEventProc(evPtr, flags)
  512.     Tcl_Event *evPtr; /* Event to service. */
  513.     int flags; /* Flags that indicate what events to
  514.  * handle, such as TCL_FILE_EVENTS. */
  515. {
  516.     FileHandler *filePtr;
  517.     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
  518.     int mask;
  519.     if (!(flags & TCL_FILE_EVENTS)) {
  520. return 0;
  521.     }
  522.     /*
  523.      * Search through the file handlers to find the one whose handle matches
  524.      * the event.  We do this rather than keeping a pointer to the file
  525.      * handler directly in the event, so that the handler can be deleted
  526.      * while the event is queued without leaving a dangling pointer.
  527.      */
  528.     for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
  529.     filePtr = filePtr->nextPtr) {
  530. if (filePtr->fd != fileEvPtr->fd) {
  531.     continue;
  532. }
  533. /*
  534.  * The code is tricky for two reasons:
  535.  * 1. The file handler's desired events could have changed
  536.  *    since the time when the event was queued, so AND the
  537.  *    ready mask with the desired mask.
  538.  * 2. The file could have been closed and re-opened since
  539.  *    the time when the event was queued.  This is why the
  540.  *    ready mask is stored in the file handler rather than
  541.  *    the queued event:  it will be zeroed when a new
  542.  *    file handler is created for the newly opened file.
  543.  */
  544. mask = filePtr->readyMask & filePtr->mask;
  545. filePtr->readyMask = 0;
  546. if (mask != 0) {
  547.     (*filePtr->proc)(filePtr->clientData, mask);
  548. }
  549. break;
  550.     }
  551.     return 1;
  552. }
  553. /*
  554.  *----------------------------------------------------------------------
  555.  *
  556.  * WaitForEvent --
  557.  *
  558.  * This function is called by Tcl_DoOneEvent to wait for new
  559.  * events on the message queue.  If the block time is 0, then
  560.  * Tcl_WaitForEvent just polls without blocking.
  561.  *
  562.  * Results:
  563.  * Returns 1 if an event was found, else 0.  This ensures that
  564.  * Tcl_DoOneEvent will return 1, even if the event is handled
  565.  * by non-Tcl code.
  566.  *
  567.  * Side effects:
  568.  * Queues file events that are detected by the select.
  569.  *
  570.  *----------------------------------------------------------------------
  571.  */
  572. static int
  573. WaitForEvent(
  574.     Tcl_Time *timePtr) /* Maximum block time, or NULL. */
  575. {
  576.     int timeout;
  577.     if (!initialized) {
  578. InitNotifier();
  579.     }
  580.     TclSetAppContext(NULL);
  581.     if (timePtr) {
  582.         timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
  583.         if (timeout == 0) {
  584.             if (XtAppPending(notifier.appContext)) {
  585.                 goto process;
  586.             } else {
  587.                 return 0;
  588.             }
  589.         } else {
  590.             Tcl_SetTimer(timePtr);
  591.         }
  592.     }
  593. process:
  594.     XtAppProcessEvent(notifier.appContext, XtIMAll);
  595.     return 1;
  596. }