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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclMacNotify.c --
  3.  *
  4.  * This file contains Macintosh-specific procedures for the notifier,
  5.  * which is the lowest-level part of the Tcl event loop.  This file
  6.  * works together with ../generic/tclNotify.c.
  7.  *
  8.  * The Mac notifier only polls for system and OS events, so it is process
  9.  * wide, rather than thread specific.  However, this means that the convert
  10.  * event proc will have to arbitrate which events go to which threads.
  11.  *
  12.  * Copyright (c) 1995-1996 Sun Microsystems, Inc.
  13.  *
  14.  * See the file "license.terms" for information on usage and redistribution
  15.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  16.  *
  17.  * RCS: @(#) $Id: tclMacNotify.c,v 1.8.4.1 2003/03/21 03:24:08 dgp Exp $
  18.  */
  19. #include "tclInt.h"
  20. #include "tclPort.h"
  21. #include "tclMac.h"
  22. #include "tclMacInt.h"
  23. #include <signal.h>
  24. #include <Events.h>
  25. #include <LowMem.h>
  26. #include <Processes.h>
  27. #include <Timer.h>
  28. #include <Threads.h>
  29. /* 
  30.  * This is necessary to work around a bug in Apple's Universal header files
  31.  * for the CFM68K libraries.
  32.  */
  33. #ifdef __CFM68K__
  34. #undef GetEventQueue
  35. extern pascal QHdrPtr GetEventQueue(void)
  36.  THREEWORDINLINE(0x2EBC, 0x0000, 0x014A);
  37. #pragma import list GetEventQueue
  38. #define GetEvQHdr() GetEventQueue()
  39. #endif
  40. /*
  41.  * Need this for replacing Tcl_SetTimer and Tcl_WaitForEvent defined 
  42.  * in THIS file with ones defined in the stub table.
  43.  */
  44.  
  45. extern TclStubs tclStubs;
  46. extern Tcl_NotifierProcs tclOriginalNotifier;
  47. /*
  48.  * The follwing static indicates whether this module has been initialized.
  49.  */
  50. static int initialized = 0;
  51. /*
  52.  * The following structure contains the state information for the
  53.  * notifier module.
  54.  */
  55. static struct {
  56.     int timerActive; /* 1 if timer is running. */
  57.     Tcl_Time timer; /* Time when next timer event is expected. */
  58.     int flags; /* OR'ed set of flags defined below. */
  59.     Point lastMousePosition; /* Last known mouse location. */
  60.     RgnHandle utilityRgn; /* Region used as the mouse region for
  61.  * WaitNextEvent and the update region when
  62.  * checking for events. */   
  63.     Tcl_MacConvertEventPtr eventProcPtr;
  64. /* This pointer holds the address of the
  65.  * function that will handle all incoming
  66.  * Macintosh events. */
  67. } notifier;
  68. /*
  69.  * The following defines are used in the flags field of the notifier struct.
  70.  */
  71. #define NOTIFY_IDLE (1<<1) /* Tcl_ServiceIdle should be called. */
  72. #define NOTIFY_TIMER (1<<2) /* Tcl_ServiceTimer should be called. */
  73. /*
  74.  * Prototypes for procedures that are referenced only in this file:
  75.  */
  76. static int HandleMacEvents _ANSI_ARGS_((void));
  77. static void InitNotifier _ANSI_ARGS_((void));
  78. static void NotifierExitHandler _ANSI_ARGS_((
  79.     ClientData clientData));
  80. /*
  81.  *----------------------------------------------------------------------
  82.  *
  83.  * Tcl_InitNotifier --
  84.  *
  85.  * Initializes the platform specific notifier state.  There is no thread
  86.  * specific platform notifier on the Mac, so this really doesn't do 
  87.  * anything.  However, we need to return the ThreadID, since the generic
  88.  * notifier hands this back to us in AlertThread.
  89.  *
  90.  * Results:
  91.  * Returns the threadID for this thread.  
  92.  *
  93.  * Side effects:
  94.  * None.
  95.  *
  96.  *----------------------------------------------------------------------
  97.  */
  98. ClientData
  99. Tcl_InitNotifier()
  100. {
  101.     
  102. #ifdef TCL_THREADS
  103.     ThreadID curThread;
  104.     if (TclMacHaveThreads()) {
  105.         GetCurrentThread(&curThread);
  106.         return (ClientData) curThread;
  107.     } else {
  108.         return NULL;
  109.     }
  110. #else
  111.     return NULL;
  112. #endif
  113. }
  114. /*
  115.  *----------------------------------------------------------------------
  116.  *
  117.  * Tcl_FinalizeNotifier --
  118.  *
  119.  * This function is called to cleanup the notifier state before
  120.  * a thread is terminated.  There is no platform thread specific
  121.  * notifier, so this does nothing.
  122.  *
  123.  * Results:
  124.  * None.
  125.  *
  126.  * Side effects:
  127.  * None.
  128.  *
  129.  *----------------------------------------------------------------------
  130.  */
  131. void
  132. Tcl_FinalizeNotifier(clientData)
  133.     ClientData clientData; /* Pointer to notifier data. */
  134. {
  135.     /* Nothing to do on the Mac */
  136. }
  137. /*
  138.  *----------------------------------------------------------------------
  139.  *
  140.  * Tcl_AlertNotifier --
  141.  *
  142.  * Wake up the specified notifier from any thread. This routine
  143.  * is called by the platform independent notifier code whenever
  144.  * the Tcl_ThreadAlert routine is called.  This routine is
  145.  * guaranteed not to be called on a given notifier after
  146.  * Tcl_FinalizeNotifier is called for that notifier.
  147.  *
  148.  * Results:
  149.  * None.
  150.  *
  151.  * Side effects:
  152.  * Calls YieldToThread from this thread.
  153.  *
  154.  *----------------------------------------------------------------------
  155.  */
  156. void
  157. Tcl_AlertNotifier(clientData)
  158.     ClientData clientData; /* Pointer to thread data. */
  159. {
  160. #ifdef TCL_THREADS
  161.     if (TclMacHaveThreads()) {
  162.         YieldToThread((ThreadID) clientData);
  163.     }
  164. #endif
  165. }
  166. /*
  167.  *----------------------------------------------------------------------
  168.  *
  169.  * InitNotifier --
  170.  *
  171.  * Initializes the notifier structure.  Note - this function is never
  172.  * used.
  173.  *
  174.  * Results:
  175.  * None.
  176.  *
  177.  * Side effects:
  178.  * Creates a new exit handler.
  179.  *
  180.  *----------------------------------------------------------------------
  181.  */
  182. static void
  183. InitNotifier(void)
  184. {
  185.     initialized = 1;
  186.     memset(&notifier, 0, sizeof(notifier));
  187.     Tcl_CreateExitHandler(NotifierExitHandler, NULL);
  188. }
  189. /*
  190.  *----------------------------------------------------------------------
  191.  *
  192.  * NotifierExitHandler --
  193.  *
  194.  * This function is called to cleanup the notifier state before
  195.  * Tcl is unloaded.  This function is never used, since InitNotifier
  196.  * isn't either.
  197.  *
  198.  * Results:
  199.  * None.
  200.  *
  201.  * Side effects:
  202.  * None.
  203.  *
  204.  *----------------------------------------------------------------------
  205.  */
  206. static void
  207. NotifierExitHandler(
  208.     ClientData clientData) /* Not used. */
  209. {
  210.     initialized = 0;
  211. }
  212. /*
  213.  *----------------------------------------------------------------------
  214.  *
  215.  * HandleMacEvents --
  216.  *
  217.  * This function checks for events from the Macintosh event queue.
  218.  *
  219.  * Results:
  220.  * Returns 1 if event found, 0 otherwise.
  221.  *
  222.  * Side effects:
  223.  * Pulls events off of the Mac event queue and then calls
  224.  * convertEventProc.
  225.  *
  226.  *----------------------------------------------------------------------
  227.  */
  228. static int
  229. HandleMacEvents(void)
  230. {
  231.     EventRecord theEvent;
  232.     int eventFound = 0, needsUpdate = 0;
  233.     Point currentMouse;
  234.     WindowRef windowRef;
  235.     Rect mouseRect;
  236.     /*
  237.      * Check for mouse moved events.  These events aren't placed on the
  238.      * system event queue unless we call WaitNextEvent.
  239.      */
  240.     GetGlobalMouseTcl(&currentMouse);
  241.     if ((notifier.eventProcPtr != NULL) &&
  242.     !EqualPt(currentMouse, notifier.lastMousePosition)) {
  243. notifier.lastMousePosition = currentMouse;
  244. theEvent.what = nullEvent;
  245. if ((*notifier.eventProcPtr)(&theEvent) == true) {
  246.     eventFound = 1;
  247. }
  248.     }
  249.     /*
  250.      * Check for update events.  Since update events aren't generated
  251.      * until we call GetNextEvent, we may need to force a call to
  252.      * GetNextEvent, even if the queue is empty.
  253.      */
  254.     for (windowRef = FrontWindow(); windowRef != NULL;
  255.     windowRef = GetNextWindow(windowRef)) {
  256. GetWindowUpdateRgn(windowRef, notifier.utilityRgn);
  257. if (!EmptyRgn(notifier.utilityRgn)) {
  258.     needsUpdate = 1;
  259.     break;
  260. }
  261.     }
  262.     
  263.     /*
  264.      * Process events from the OS event queue.
  265.      */
  266.     while (needsUpdate || (GetEvQHdr()->qHead != NULL)) {
  267. GetGlobalMouseTcl(&currentMouse);
  268. SetRect(&mouseRect, currentMouse.h, currentMouse.v,
  269. currentMouse.h + 1, currentMouse.v + 1);
  270. RectRgn(notifier.utilityRgn, &mouseRect);
  271. WaitNextEvent(everyEvent, &theEvent, 5, notifier.utilityRgn);
  272. needsUpdate = 0;
  273. if ((notifier.eventProcPtr != NULL)
  274. && ((*notifier.eventProcPtr)(&theEvent) == true)) {
  275.     eventFound = 1;
  276. }
  277.     }
  278.     
  279.     return eventFound;
  280. }
  281. /*
  282.  *----------------------------------------------------------------------
  283.  *
  284.  * Tcl_SetTimer --
  285.  *
  286.  * This procedure sets the current notifier timer value.  The
  287.  * notifier will ensure that Tcl_ServiceAll() is called after
  288.  * the specified interval, even if no events have occurred.
  289.  *
  290.  * Results:
  291.  * None.
  292.  *
  293.  * Side effects:
  294.  * Replaces any previous timer.
  295.  *
  296.  *----------------------------------------------------------------------
  297.  */
  298. void
  299. Tcl_SetTimer(
  300.     Tcl_Time *timePtr) /* New value for interval timer. */
  301. {
  302.     /*
  303.      * Allow the notifier to be hooked.  This may not make sense
  304.      * on the Mac, but mirrors the UNIX hook.
  305.      */
  306.     if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) {
  307. tclStubs.tcl_SetTimer(timePtr);
  308. return;
  309.     }
  310.     if (!timePtr) {
  311. notifier.timerActive = 0;
  312.     } else {
  313. /*
  314.  * Compute when the timer should fire.
  315.  */
  316. Tcl_GetTime(&notifier.timer);
  317. notifier.timer.sec += timePtr->sec;
  318. notifier.timer.usec += timePtr->usec;
  319. if (notifier.timer.usec >= 1000000) {
  320.     notifier.timer.usec -= 1000000;
  321.     notifier.timer.sec += 1;
  322. }
  323. notifier.timerActive = 1;
  324.     }
  325. }
  326. /*
  327.  *----------------------------------------------------------------------
  328.  *
  329.  * Tcl_ServiceModeHook --
  330.  *
  331.  * This function is invoked whenever the service mode changes.
  332.  *
  333.  * Results:
  334.  * None.
  335.  *
  336.  * Side effects:
  337.  * None.
  338.  *
  339.  *----------------------------------------------------------------------
  340.  */
  341. void
  342. Tcl_ServiceModeHook(mode)
  343.     int mode; /* Either TCL_SERVICE_ALL, or
  344.  * TCL_SERVICE_NONE. */
  345. {
  346. }
  347. /*
  348.  *----------------------------------------------------------------------
  349.  *
  350.  * Tcl_WaitForEvent --
  351.  *
  352.  * This function is called by Tcl_DoOneEvent to wait for new
  353.  * events on the message queue.  If the block time is 0, then
  354.  * Tcl_WaitForEvent just polls the event queue without blocking.
  355.  *
  356.  * Results:
  357.  * Always returns 0.
  358.  *
  359.  * Side effects:
  360.  * None.
  361.  *
  362.  *----------------------------------------------------------------------
  363.  */
  364. int
  365. Tcl_WaitForEvent(
  366.     Tcl_Time *timePtr) /* Maximum block time. */
  367. {
  368.     int found;
  369.     EventRecord macEvent;
  370.     long sleepTime = 5;
  371.     long ms;
  372.     Point currentMouse;
  373.     void * timerToken;
  374.     Rect mouseRect;
  375.     /*
  376.      * Allow the notifier to be hooked.  This may not make
  377.      * sense on the Mac, but mirrors the UNIX hook.
  378.      */
  379.     if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) {
  380. return tclStubs.tcl_WaitForEvent(timePtr);
  381.     }
  382.     /*
  383.      * Compute the next timeout value.
  384.      */
  385.     if (!timePtr) {
  386. ms = INT_MAX;
  387.     } else {
  388. ms = (timePtr->sec * 1000) + (timePtr->usec / 1000);
  389.     }
  390.     timerToken = TclMacStartTimer((long) ms);
  391.    
  392.     /*
  393.      * Poll the Mac event sources.  This loop repeats until something
  394.      * happens: a timeout, a socket event, mouse motion, or some other
  395.      * window event.  Note that we don't call WaitNextEvent if another
  396.      * event is found to avoid context switches.  This effectively gives
  397.      * events coming in via WaitNextEvent a slightly lower priority.
  398.      */
  399.     found = 0;
  400.     if (notifier.utilityRgn == NULL) {
  401. notifier.utilityRgn = NewRgn();
  402.     }
  403.     while (!found) {
  404. /*
  405.  * Check for generated and queued events.
  406.  */
  407. if (HandleMacEvents()) {
  408.     found = 1;
  409. }
  410. /*
  411.  * Check for time out.
  412.  */
  413. if (!found && TclMacTimerExpired(timerToken)) {
  414.     found = 1;
  415. }
  416. /*
  417.  * Check for window events.  We may receive a NULL event for
  418.  * various reasons. 1) the timer has expired, 2) a mouse moved
  419.  * event is occuring or 3) the os is giving us time for idle
  420.  * events.  Note that we aren't sharing the processor very
  421.  * well here.  We really ought to do a better job of calling
  422.  * WaitNextEvent for time slicing purposes.
  423.  */
  424. if (!found) {
  425.     /*
  426.      * Set up mouse region so we will wake if the mouse is moved.
  427.      * We do this by defining the smallest possible region around
  428.      * the current mouse position.
  429.      */
  430.     GetGlobalMouseTcl(&currentMouse);
  431.     SetRect(&mouseRect, currentMouse.h, currentMouse.v,
  432.     currentMouse.h + 1, currentMouse.v + 1);
  433.     RectRgn(notifier.utilityRgn, &mouseRect);
  434.     WaitNextEvent(everyEvent, &macEvent, sleepTime,
  435.     notifier.utilityRgn);
  436.     if (notifier.eventProcPtr != NULL) {
  437. if ((*notifier.eventProcPtr)(&macEvent) == true) {
  438.     found = 1;
  439. }
  440.     }
  441. }
  442.     }
  443.     TclMacRemoveTimer(timerToken);
  444.     
  445.     /*
  446.      * Yield time to nay other thread at this point.  If we find that the
  447.      * apps thrash too switching between threads, we can put a timer here,
  448.      * and only yield when the timer fires.
  449.      */
  450.      
  451.     if (TclMacHaveThreads()) {
  452.         YieldToAnyThread();
  453.     }
  454.     
  455.     return 0;
  456. }
  457. /*
  458.  *----------------------------------------------------------------------
  459.  *
  460.  * Tcl_Sleep --
  461.  *
  462.  * Delay execution for the specified number of milliseconds.  This
  463.  * is not a very good call to make.  It will block the system -
  464.  * you will not even be able to switch applications.
  465.  *
  466.  * Results:
  467.  * None.
  468.  *
  469.  * Side effects:
  470.  * Time passes.
  471.  *
  472.  *----------------------------------------------------------------------
  473.  */
  474. void
  475. Tcl_Sleep(
  476.     int ms) /* Number of milliseconds to sleep. */
  477. {
  478.     EventRecord dummy;
  479.     void *timerToken;
  480.     
  481.     if (ms <= 0) {
  482. return;
  483.     }
  484.     
  485.     timerToken = TclMacStartTimer((long) ms);
  486.     while (1) {
  487. WaitNextEvent(0, &dummy, (ms / 16.66) + 1, NULL);
  488.         if (TclMacHaveThreads()) {
  489.     YieldToAnyThread();
  490. }
  491. if (TclMacTimerExpired(timerToken)) {
  492.     break;
  493. }
  494.     }
  495.     TclMacRemoveTimer(timerToken);
  496. }
  497. /*
  498.  *----------------------------------------------------------------------
  499.  *
  500.  * Tcl_MacSetEventProc --
  501.  *
  502.  * This function sets the event handling procedure for the 
  503.  * application.  This function will be passed all incoming Mac
  504.  * events.  This function usually controls the console or some
  505.  * other entity like Tk.
  506.  *
  507.  * Results:
  508.  * None.
  509.  *
  510.  * Side effects:
  511.  * Changes the event handling function.
  512.  *
  513.  *----------------------------------------------------------------------
  514.  */
  515. void
  516. Tcl_MacSetEventProc(
  517.     Tcl_MacConvertEventPtr procPtr)
  518. {
  519.     notifier.eventProcPtr = procPtr;
  520. }