home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / mac / tclMacNotify.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  9.7 KB  |  417 lines  |  [TEXT/CWIE]

  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.  * Copyright (c) 1995-1996 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tclMacNotify.c 1.36 97/05/07 19:09:29
  14.  */
  15.  
  16. #include "tclInt.h"
  17. #include "tclPort.h"
  18. #include "tclMac.h"
  19. #include "tclMacInt.h"
  20. #include <signal.h>
  21. #include <Events.h>
  22. #include <LowMem.h>
  23. #include <Processes.h>
  24. #include <Timer.h>
  25.  
  26.  
  27. /* 
  28.  * This is necessary to work around a bug in Apple's Universal header files
  29.  * for the CFM68K libraries.
  30.  */
  31.  
  32. #ifdef __CFM68K__
  33. #undef GetEventQueue
  34. extern pascal QHdrPtr GetEventQueue(void)
  35.  THREEWORDINLINE(0x2EBC, 0x0000, 0x014A);
  36. #pragma import list GetEventQueue
  37. #define GetEvQHdr() GetEventQueue()
  38. #endif
  39.  
  40. /*
  41.  * The follwing static indicates whether this module has been initialized.
  42.  */
  43.  
  44. static int initialized = 0;
  45.  
  46. /*
  47.  * The following structure contains the state information for the
  48.  * notifier module.
  49.  */
  50.  
  51. static struct {
  52.     int timerActive;        /* 1 if timer is running. */
  53.     Tcl_Time timer;        /* Time when next timer event is expected. */
  54.     int flags;            /* OR'ed set of flags defined below. */
  55.     Point lastMousePosition;    /* Last known mouse location. */
  56.     RgnHandle utilityRgn;    /* Region used as the mouse region for
  57.                  * WaitNextEvent and the update region when
  58.                  * checking for events. */   
  59.     Tcl_MacConvertEventPtr eventProcPtr;
  60.                 /* This pointer holds the address of the
  61.                  * function that will handle all incoming
  62.                  * Macintosh events. */
  63. } notifier;
  64.  
  65. /*
  66.  * The following defines are used in the flags field of the notifier struct.
  67.  */
  68.  
  69. #define NOTIFY_IDLE    (1<<1)    /* Tcl_ServiceIdle should be called. */
  70. #define NOTIFY_TIMER    (1<<2)    /* Tcl_ServiceTimer should be called. */
  71.  
  72. /*
  73.  * Prototypes for procedures that are referenced only in this file:
  74.  */
  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.  *
  84.  * InitNotifier --
  85.  *
  86.  *    Initializes the notifier structure.
  87.  *
  88.  * Results:
  89.  *    None.
  90.  *
  91.  * Side effects:
  92.  *    Creates a new exit handler.
  93.  *
  94.  *----------------------------------------------------------------------
  95.  */
  96.  
  97. static void
  98. InitNotifier(void)
  99. {
  100.     initialized = 1;
  101.     memset(¬ifier, 0, sizeof(notifier));
  102.     Tcl_CreateExitHandler(NotifierExitHandler, NULL);
  103. }
  104.  
  105. /*
  106.  *----------------------------------------------------------------------
  107.  *
  108.  * NotifierExitHandler --
  109.  *
  110.  *    This function is called to cleanup the notifier state before
  111.  *    Tcl is unloaded.
  112.  *
  113.  * Results:
  114.  *    None.
  115.  *
  116.  * Side effects:
  117.  *    None.
  118.  *
  119.  *----------------------------------------------------------------------
  120.  */
  121.  
  122. static void
  123. NotifierExitHandler(
  124.     ClientData clientData)    /* Not used. */
  125. {
  126.     initialized = 0;
  127. }
  128.  
  129. /*
  130.  *----------------------------------------------------------------------
  131.  *
  132.  * HandleMacEvents --
  133.  *
  134.  *    This function checks for events from the Macintosh event queue.
  135.  *
  136.  * Results:
  137.  *    Returns 1 if event found, 0 otherwise.
  138.  *
  139.  * Side effects:
  140.  *    Pulls events off of the Mac event queue and then calls
  141.  *    convertEventProc.
  142.  *
  143.  *----------------------------------------------------------------------
  144.  */
  145.  
  146. static int
  147. HandleMacEvents(void)
  148. {
  149.     EventRecord theEvent;
  150.     int eventFound = 0, needsUpdate = 0;
  151.     Point currentMouse;
  152.     WindowRef windowRef;
  153.     Rect mouseRect;
  154.  
  155.     /*
  156.      * Check for mouse moved events.  These events aren't placed on the
  157.      * system event queue unless we call WaitNextEvent.
  158.      */
  159.  
  160.     GetGlobalMouse(¤tMouse);
  161.     if ((notifier.eventProcPtr != NULL) &&
  162.         !EqualPt(currentMouse, notifier.lastMousePosition)) {
  163.     notifier.lastMousePosition = currentMouse;
  164.     theEvent.what = nullEvent;
  165.     if ((*notifier.eventProcPtr)(&theEvent) == true) {
  166.         eventFound = 1;
  167.     }
  168.     }
  169.  
  170.     /*
  171.      * Check for update events.  Since update events aren't generated
  172.      * until we call GetNextEvent, we may need to force a call to
  173.      * GetNextEvent, even if the queue is empty.
  174.      */
  175.  
  176.     for (windowRef = FrontWindow(); windowRef != NULL;
  177.         windowRef = GetNextWindow(windowRef)) {
  178.     GetWindowUpdateRgn(windowRef, notifier.utilityRgn);
  179.     if (!EmptyRgn(notifier.utilityRgn)) {
  180.         needsUpdate = 1;
  181.         break;
  182.     }
  183.     }
  184.     
  185.     /*
  186.      * Process events from the OS event queue.
  187.      */
  188.  
  189.     while (needsUpdate || (GetEvQHdr()->qHead != NULL)) {
  190.     GetGlobalMouse(¤tMouse);
  191.     SetRect(&mouseRect, currentMouse.h, currentMouse.v,
  192.         currentMouse.h + 1, currentMouse.v + 1);
  193.     RectRgn(notifier.utilityRgn, &mouseRect);
  194.     
  195.     WaitNextEvent(everyEvent, &theEvent, 5, notifier.utilityRgn);
  196.     needsUpdate = 0;
  197.     if ((notifier.eventProcPtr != NULL)
  198.         && ((*notifier.eventProcPtr)(&theEvent) == true)) {
  199.         eventFound = 1;
  200.     }
  201.     }
  202.     
  203.     return eventFound;
  204. }
  205.  
  206. /*
  207.  *----------------------------------------------------------------------
  208.  *
  209.  * Tcl_SetTimer --
  210.  *
  211.  *    This procedure sets the current notifier timer value.  The
  212.  *    notifier will ensure that Tcl_ServiceAll() is called after
  213.  *    the specified interval, even if no events have occurred.
  214.  *
  215.  * Results:
  216.  *    None.
  217.  *
  218.  * Side effects:
  219.  *    Replaces any previous timer.
  220.  *
  221.  *----------------------------------------------------------------------
  222.  */
  223.  
  224. void
  225. Tcl_SetTimer(
  226.     Tcl_Time *timePtr)        /* New value for interval timer. */
  227. {
  228.     if (!timePtr) {
  229.     notifier.timerActive = 0;
  230.     } else {
  231.     /*
  232.      * Compute when the timer should fire.
  233.      */
  234.     
  235.     TclpGetTime(¬ifier.timer);
  236.     notifier.timer.sec += timePtr->sec;
  237.     notifier.timer.usec += timePtr->usec;
  238.     if (notifier.timer.usec >= 1000000) {
  239.         notifier.timer.usec -= 1000000;
  240.         notifier.timer.sec += 1;
  241.     }
  242.     notifier.timerActive = 1;
  243.     }
  244. }
  245.  
  246. /*
  247.  *----------------------------------------------------------------------
  248.  *
  249.  * Tcl_WaitForEvent --
  250.  *
  251.  *    This function is called by Tcl_DoOneEvent to wait for new
  252.  *    events on the message queue.  If the block time is 0, then
  253.  *    Tcl_WaitForEvent just polls the event queue without blocking.
  254.  *
  255.  * Results:
  256.  *    Always returns 0.
  257.  *
  258.  * Side effects:
  259.  *    None.
  260.  *
  261.  *----------------------------------------------------------------------
  262.  */
  263.  
  264. int
  265. Tcl_WaitForEvent(
  266.     Tcl_Time *timePtr)        /* Maximum block time. */
  267. {
  268.     int found;
  269.     EventRecord macEvent;
  270.     long sleepTime = 5;
  271.     long ms;
  272.     Point currentMouse;
  273.     void * timerToken;
  274.     Rect mouseRect;
  275.  
  276.     /*
  277.      * Compute the next timeout value.
  278.      */
  279.  
  280.     if (!timePtr) {
  281.     ms = INT_MAX;
  282.     } else {
  283.     ms = (timePtr->sec * 1000) + (timePtr->usec / 1000);
  284.     }
  285.     timerToken = TclMacStartTimer((long) ms);
  286.    
  287.     /*
  288.      * Poll the Mac event sources.  This loop repeats until something
  289.      * happens: a timeout, a socket event, mouse motion, or some other
  290.      * window event.  Note that we don't call WaitNextEvent if another
  291.      * event is found to avoid context switches.  This effectively gives
  292.      * events coming in via WaitNextEvent a slightly lower priority.
  293.      */
  294.  
  295.     found = 0;
  296.     if (notifier.utilityRgn == NULL) {
  297.     notifier.utilityRgn = NewRgn();
  298.     }
  299.  
  300.     while (!found) {
  301.     /*
  302.      * Check for generated and queued events.
  303.      */
  304.  
  305.     if (HandleMacEvents()) {
  306.         found = 1;
  307.     }
  308.  
  309.     /*
  310.      * Check for time out.
  311.      */
  312.  
  313.     if (!found && TclMacTimerExpired(timerToken)) {
  314.         found = 1;
  315.     }
  316.  
  317.     /*
  318.      * Check for window events.  We may receive a NULL event for
  319.      * various reasons. 1) the timer has expired, 2) a mouse moved
  320.      * event is occuring or 3) the os is giving us time for idle
  321.      * events.  Note that we aren't sharing the processor very
  322.      * well here.  We really ought to do a better job of calling
  323.      * WaitNextEvent for time slicing purposes.
  324.      */
  325.  
  326.     if (!found) {
  327.         /*
  328.          * Set up mouse region so we will wake if the mouse is moved.
  329.          * We do this by defining the smallest possible region around
  330.          * the current mouse position.
  331.          */
  332.  
  333.         GetGlobalMouse(¤tMouse);
  334.         SetRect(&mouseRect, currentMouse.h, currentMouse.v,
  335.             currentMouse.h + 1, currentMouse.v + 1);
  336.         RectRgn(notifier.utilityRgn, &mouseRect);
  337.     
  338.         WaitNextEvent(everyEvent, &macEvent, sleepTime,
  339.             notifier.utilityRgn);
  340.  
  341.         if (notifier.eventProcPtr != NULL) {
  342.         if ((*notifier.eventProcPtr)(&macEvent) == true) {
  343.             found = 1;
  344.         }
  345.         }
  346.     }
  347.     }
  348.     TclMacRemoveTimer(timerToken);
  349.     return 0;
  350. }
  351.  
  352. /*
  353.  *----------------------------------------------------------------------
  354.  *
  355.  * Tcl_Sleep --
  356.  *
  357.  *    Delay execution for the specified number of milliseconds.  This
  358.  *    is not a very good call to make.  It will block the system -
  359.  *    you will not even be able to switch applications.
  360.  *
  361.  * Results:
  362.  *    None.
  363.  *
  364.  * Side effects:
  365.  *    Time passes.
  366.  *
  367.  *----------------------------------------------------------------------
  368.  */
  369.  
  370. void
  371. Tcl_Sleep(
  372.     int ms)            /* Number of milliseconds to sleep. */
  373. {
  374.     EventRecord dummy;
  375.     void *timerToken;
  376.     
  377.     if (ms <= 0) {
  378.     return;
  379.     }
  380.     
  381.     timerToken = TclMacStartTimer((long) ms);
  382.     while (1) {
  383.     WaitNextEvent(0, &dummy, (ms / 16.66) + 1, NULL);
  384.     
  385.     if (TclMacTimerExpired(timerToken)) {
  386.         break;
  387.     }
  388.     }
  389.     TclMacRemoveTimer(timerToken);
  390. }
  391.  
  392. /*
  393.  *----------------------------------------------------------------------
  394.  *
  395.  * Tcl_MacSetEventProc --
  396.  *
  397.  *    This function sets the event handling procedure for the 
  398.  *    application.  This function will be passed all incoming Mac
  399.  *    events.  This function usually controls the console or some
  400.  *    other entity like Tk.
  401.  *
  402.  * Results:
  403.  *    None.
  404.  *
  405.  * Side effects:
  406.  *    Changes the event handling function.
  407.  *
  408.  *----------------------------------------------------------------------
  409.  */
  410.  
  411. void
  412. Tcl_MacSetEventProc(
  413.     Tcl_MacConvertEventPtr procPtr)
  414. {
  415.     notifier.eventProcPtr = procPtr;
  416. }
  417.