home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / src / out-of-phase-102-c / OutOfPhase 1.02 Source / OutOfPhase Folder / Level 0 Macintosh 29Sep94 / EventLoop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-23  |  29.0 KB  |  997 lines  |  [TEXT/KAHL]

  1. /* EventLoop.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #ifdef THINK_C
  25.     #pragma options(pack_enums)
  26. #endif
  27. #include <AppleEvents.h>
  28. #include <Events.h>
  29. #include <Windows.h>
  30. #include <Menus.h>
  31. #include <Quickdraw.h>
  32. #include <Desk.h>
  33. #include <ToolUtils.h>
  34. #include <GestaltEqu.h>
  35. #include <Power.h>
  36. #ifdef THINK_C
  37.     #pragma options(!pack_enums)
  38. #endif
  39.  
  40. #include "MyMalloc.h"
  41. #include "EventLoop.h"
  42. #include "Memory.h"
  43. #include "Menus.h"
  44.  
  45.  
  46. /* how many 1/60ths of a second occur between mouse image update events */
  47. #define CURSORUPDATEDELAY (15)
  48.  
  49. /* how many 1/60ths of a second should we leave menu hilited */
  50. #define MENUDELAY (4)
  51.  
  52. /* how many 1/60ths of a second between WaitNextEvents() when calling */
  53. /* RelinquishCPUJudiciously while application is in the foreground. */
  54. #define JUDICIOUSDELAYFOREGROUND (19)
  55.  
  56. /* same as JUDICIOUSDELAYFOREGROUND, except for when application is in background */
  57. #define JUDICIOUSDELAYBACKGROUND (5)
  58.  
  59. /* timeout factor for event loop when mouse is down (to facilitate mouse tracking) */
  60. #define VERYSMALLTIMEINTERVAL (1)
  61.  
  62. /* what timeout should RelinquishCPU (not judiciously) use */
  63. #define RELINQUISHCPUNORMALDELAY (5)
  64.  
  65. /* how long should we beep or invert the menu bar */
  66. #define BEEPDURATION (20)
  67.  
  68. /* possible states that the menu handling stuff could be in */
  69. typedef enum
  70.     {
  71.         eNoMenu EXECUTE(= -8764),
  72.         eMenuPendingMouse,
  73.         eMenuPendingKey,
  74.         eMenuSelected
  75.     } MenuStates;
  76.  
  77.  
  78. /* NIL = no mouse down; otherwise, it's the window that the mouse went down in */
  79. /* so that the mouse up event can be reported to the same window. */
  80. static WindowPtr            LastMouseDownInThisWindow = NIL;
  81.  
  82. /* when did we last check the mouse cursor image. */
  83. static long                        LastCursorCheck = 0;
  84.  
  85. /* when was the last time we actually waited for an event (this is used by */
  86. /* RelinquishCPUJudiciously to keep from doing it too often) */
  87. static long                        LastEventTime = 0;
  88.  
  89. /* what is the current timeout for WaitNextEvent */
  90. static long                        SleepTime = CURSORUPDATEDELAY;
  91.  
  92. /* what window was the last one that was active (for detecting changes) */
  93. static WindowPtr            LastActiveWindow = NIL;
  94.  
  95. /* flag indicating whether we are in the foreground or not */
  96. static MyBoolean            RunningInForeground = True;
  97.  
  98. /* sticky flag that remembers if the user tried to cancel during RelinquishCPU */
  99. static MyBoolean            CancelPending = False;
  100.  
  101. /* buffer for keys received during RelinquishCPU.  if it is NIL, then the buffer */
  102. /* does not exist. */
  103. static EventRecord*        KeyboardEventBuffer = NIL;
  104.  
  105. /* this is used during RelinquishCPUJudiciously to keep PowerBooks from */
  106. /* going into idle state */
  107. static MyBoolean            IsThisAPowerBook = False;
  108.  
  109. /* current delay for RelinquishCPUJudiciously.  It depends on RunningInForeground */
  110. static long                        JudiciousInterval = JUDICIOUSDELAYFOREGROUND;
  111.  
  112. /* flag that tells whether we should make beeping noises or not */
  113. static MyBoolean            MakeErrorBeeps = True;
  114.  
  115.  
  116. #if (CURRENTPROCTYPE == PROC68000) || (CURRENTPROCTYPE == PROC68020)
  117.     /* efficiency hack for 680x0 Macs */
  118.     #define TickCount() (*((volatile unsigned long*)0x016a))
  119. #else
  120.     /* 'nicer' for PowerPC */
  121. #endif
  122.  
  123.  
  124. /* initialize internal event loop data structures */
  125. MyBoolean                    Eep_InitEventLoop(void)
  126.     {
  127.         OSErr                        Error;
  128.         long                        Result;
  129.  
  130.         /* allocate keyboard buffer.  if this fails & it returns NIL, it's ok. */
  131.         KeyboardEventBuffer = (EventRecord*)AllocPtrCanFail(0,"KeyboardEventBuffer");
  132.         /* figure out if we are running on a sleepy powerbook */
  133.         Error = Gestalt(gestaltPowerMgrAttr,&Result);
  134.         IsThisAPowerBook = ((Result & (1 << gestaltPMgrExists)) != 0) && (Error == noErr);
  135.         return True;
  136.     }
  137.  
  138.  
  139. /* dispose of any internal event loop structures */
  140. void                            Eep_ShutdownEventLoop(void)
  141.     {
  142.         if (KeyboardEventBuffer != NIL)
  143.             {
  144.                 ReleasePtr((char*)KeyboardEventBuffer);
  145.             }
  146.         KeyboardEventBuffer = NIL;
  147.     }
  148.  
  149.  
  150. /* returns the ID number of the current window.  Returns 0 if there are no windows */
  151. static WinType*    GetCurrentWindow(void)
  152.     {
  153.         if ((FrontWindow() != NIL) && RunningInForeground)
  154.             {
  155.                 CheckPtrExistence((WinType*)GetWRefCon(FrontWindow()));
  156.                 return (WinType*)GetWRefCon(FrontWindow());
  157.             }
  158.          else
  159.             {
  160.                 return NIL;
  161.             }
  162.     }
  163.  
  164.  
  165. /* local routine that converts Toolbox modifier flags to our own modifier flags */
  166. static short            FormModifiers(short Modifiers)
  167.     {
  168.         short                        Value;
  169.  
  170.         Value = 0;
  171.         if ((Modifiers & shiftKey) != 0)
  172.             {
  173.                 Value |= eShiftKey;
  174.             }
  175.         if ((Modifiers & controlKey) != 0)
  176.             {
  177.                 Value |= eControlKey;
  178.             }
  179.         if ((Modifiers & cmdKey) != 0)
  180.             {
  181.                 Value |= eCommandKey;
  182.             }
  183.         if ((Modifiers & optionKey) != 0)
  184.             {
  185.                 Value |= eOptionKey;
  186.             }
  187.         if ((Modifiers & alphaLock) != 0)
  188.             {
  189.                 Value |= eCapsLockKey;
  190.             }
  191.         if ((Modifiers & btnState) != 0)
  192.             {
  193.                 Value |= eMouseDownFlag;
  194.             }
  195.         return Value;
  196.     }
  197.  
  198.  
  199. /* Fetch an event from the event queue and return it.  Only some of the parameters */
  200. /* returned may be valid; see the enumeration comments above for EventType to see */
  201. /* which.  Mouse coordinates are always local to the current window, or undefined */
  202. /* if there is no current window.  If there are no events, the routine will return */
  203. /* after some amount of time.  This routine may call the Menu manager, so menus */
  204. /* should be initialized before this routine is called.  Any parameter may be passed */
  205. /* as NIL if the user doesn't care about the result.  Window changes do not occur */
  206. /* if the mouse is down.  If the current window is a dialog box, then a window */
  207. /* change will never be returned for another window.  Mouse up events are always */
  208. /* returned with the same window as the mosue down event, even if the mouse is no */
  209. /* longer in the window. */
  210. EventType                    GetAnEvent(OrdType* Xloc, OrdType* Yloc, ModifierFlags* Modifiers,
  211.                                         WinType** Window, MenuItemType** MenuCommand, char* KeyPressed)
  212.     {
  213.         /* the event.  statically allocated so that it is still valid when called */
  214.         /* again.  this is used for remembering the event that triggered a menu thing. */
  215.         static EventRecord        MyEvent;
  216.         /* state variable for menu handling */
  217.         static MenuStates            MenuState = eNoMenu;
  218.         /* when was the last menu selected (for slowing down menu bar flash) */
  219.         static unsigned long    WhenMenuWasSelected;
  220.  
  221.         WindowPtr                            WhichWindow;
  222.         long                                    MenuCommandInteger;
  223.  
  224.  
  225.         /* redraw the menu bar if it has changed */
  226.         Eep_RedrawMenuBar();
  227.  
  228.         /* the cancel pending flag for RelinquishCPU is sticky, so that if the user */
  229.         /* cancels once, it continues to return True.  It gets cleared the next time */
  230.         /* the program tries to handle a "real" event. */
  231.         CancelPending = False;
  232.  
  233.         /* redraw any windows that couldn't be redrawn when the event was received. */
  234.         PerformDeferredUpdates();
  235.  
  236.         /* main loop */
  237.      LoopPoint:
  238.  
  239.         /* check to see if active window has changed */
  240.         if (LastActiveWindow != FrontWindow())
  241.             {
  242.                 LastActiveWindow = FrontWindow();
  243.                 if (Window != NIL)
  244.                     {
  245.                         *Window = GetCurrentWindow();
  246.                     }
  247.                 return eActiveWindowChanged;
  248.             }
  249.  
  250.         /* if there are any queued up keypresses from RelinquishCPU, report them now */
  251.         if ((KeyboardEventBuffer != NIL) && (PtrSize((char*)KeyboardEventBuffer) > 0))
  252.             {
  253.                 EventRecord*    Temp;
  254.                 long                    OldSize;
  255.  
  256.                 CheckPtrExistence(KeyboardEventBuffer);
  257.                 /* a keypress was buffered during a RelinquishCPU call */
  258.                 MyEvent = KeyboardEventBuffer[0]; /* save the event */
  259.                 OldSize = PtrSize((char*)KeyboardEventBuffer);
  260.                 PRNGCHK(KeyboardEventBuffer,&(KeyboardEventBuffer[1]),
  261.                     OldSize - sizeof(EventRecord));
  262.                 PRNGCHK(KeyboardEventBuffer,&(KeyboardEventBuffer[0]),
  263.                     OldSize - sizeof(EventRecord));
  264.                 MoveData((char*)&(KeyboardEventBuffer[1]),(char*)&(KeyboardEventBuffer[0]),
  265.                     OldSize - sizeof(EventRecord));
  266.                 Temp = (EventRecord*)ResizePtr((char*)KeyboardEventBuffer,
  267.                     OldSize - sizeof(EventRecord));
  268.                 if (Temp == NIL)
  269.                     {
  270.                         /* if we run out of memory, then we just lose all the buffered keypresses */
  271.                         ReleasePtr((char*)KeyboardEventBuffer);
  272.                         KeyboardEventBuffer = (EventRecord*)AllocPtrCanFail(0,"KeyboardEventBuffer");
  273.                     }
  274.                  else
  275.                     {
  276.                         KeyboardEventBuffer = Temp;
  277.                     }
  278.                 /* now, jump so that we fake the event */
  279.                 goto HandleEventSwitchPoint;
  280.             }
  281.  
  282.         /* handle any menu operations that are in progress. */
  283.         switch (MenuState)
  284.             {
  285.                 default:
  286.                     EXECUTE(PRERR(ForceAbort,"GetAnEvent:  bad MenuState"));
  287.                     break;
  288.                 case eNoMenu:
  289.                     break; /* continue on through */
  290.                 case eMenuPendingMouse:
  291.                     /* let system process event */
  292.                     /* MyEvent is still valid from last time through */
  293.                     MenuCommandInteger = MenuSelect(MyEvent.where);
  294.                     /* if the menu was actually chosen, then return it */
  295.                     if (MenuCommand != NIL)
  296.                         {
  297.                             *MenuCommand = Eep_MMID2ItemID(MenuCommandInteger);
  298.                             if (*MenuCommand != NIL)
  299.                                 {
  300.                                     /* we only report a menu choice if the user actually chose something */
  301.                                     MenuState = eMenuSelected;
  302.                                     WhenMenuWasSelected = TickCount();
  303.                                     if (Modifiers != NIL)
  304.                                         {
  305.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  306.                                         }
  307.                                     if (Window != NIL)
  308.                                         {
  309.                                             *Window = GetCurrentWindow();
  310.                                         }
  311.                                     MenuState = eMenuSelected;
  312.                                     return eMenuCommand;
  313.                                 }
  314.                         }
  315.                     /* if menu wasn't chosen, then reset menu state & get another event */
  316.                     MenuState = eNoMenu;
  317.                     goto LoopPoint;
  318.                 case eMenuPendingKey:
  319.                     /* convert the key to a menu thing */
  320.                     /* MyEvent is still valid from last time through */
  321.                     MenuCommandInteger = MenuKey(MyEvent.message & charCodeMask);
  322.                     /* convert the menu command into something we can handle */
  323.                     if (MenuCommand != NIL)
  324.                         {
  325.                             /* only do menus if they can handle them */
  326.                             *MenuCommand = Eep_MMID2ItemID(MenuCommandInteger);
  327.                             /* if the menu actually happened, then report it */
  328.                             if (*MenuCommand != NIL)
  329.                                 {
  330.                                     WhenMenuWasSelected = TickCount();
  331.                                     if (Modifiers != NIL)
  332.                                         {
  333.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  334.                                         }
  335.                                     if (Window != NIL)
  336.                                         {
  337.                                             *Window = GetCurrentWindow();
  338.                                         }
  339.                                     MenuState = eMenuSelected;
  340.                                     return eMenuCommand;
  341.                                 }
  342.                         }
  343.                     /* if the key doesn't correspond to a menu item, then schlep on over */
  344.                     /* to the normal keypress handler */
  345.                     MenuState = eNoMenu;
  346.                     goto FinishKeypressEvent;
  347.                     break;
  348.                 case eMenuSelected:
  349.                     /* make a small delay so that the menu is actually visible */
  350.                     while (TickCount() - WhenMenuWasSelected < MENUDELAY)
  351.                         {
  352.                             /* hideous delay loop to flash menu so user can see it. */
  353.                         }
  354.                     HiliteMenu(0);
  355.                     MenuState = eNoMenu;
  356.                     break;
  357.             }
  358.  
  359.         /* call the event routine.  the ugly parameter decides whether a mouse is */
  360.         /* down and uses that to speed up mouse tracking */
  361.         WaitNextEvent(everyEvent,&MyEvent,((LastMouseDownInThisWindow != NIL)
  362.             && (SleepTime > VERYSMALLTIMEINTERVAL)) ? VERYSMALLTIMEINTERVAL : SleepTime,NIL);
  363.  
  364.         /* remember last event time for RelinquishCPU */
  365.         LastEventTime = TickCount();
  366.  
  367.         /* decode the event */
  368.      HandleEventSwitchPoint:
  369.         switch (MyEvent.what)
  370.             {
  371.                 case nullEvent:
  372.                     if (TickCount() - LastCursorCheck >= CURSORUPDATEDELAY)
  373.                         {
  374.                             if (FrontWindow() != NIL)
  375.                                 {
  376.                                     LastCursorCheck = TickCount();
  377.                                     SetPort(FrontWindow());
  378.                                     GlobalToLocal(&MyEvent.where);
  379.                                     if (Xloc != NIL)
  380.                                         {
  381.                                             *Xloc = MyEvent.where.h;
  382.                                         }
  383.                                     if (Yloc != NIL)
  384.                                         {
  385.                                             *Yloc = MyEvent.where.v;
  386.                                         }
  387.                                     if (Modifiers != NIL)
  388.                                         {
  389.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  390.                                         }
  391.                                     if (Window != NIL)
  392.                                         {
  393.                                             *Window = GetCurrentWindow();
  394.                                         }
  395.                                     return eCheckCursor;
  396.                                 }
  397.                              else
  398.                                 {
  399.                                     SetCursor(&qd.arrow);
  400.                                 }
  401.                         }
  402.                     if (FrontWindow() != NIL)
  403.                         {
  404.                             SetPort(FrontWindow());
  405.                             GlobalToLocal(&MyEvent.where);
  406.                             if (Xloc != NIL)
  407.                                 {
  408.                                     *Xloc = MyEvent.where.h;
  409.                                 }
  410.                             if (Yloc != NIL)
  411.                                 {
  412.                                     *Yloc = MyEvent.where.v;
  413.                                 }
  414.                         }
  415.                     if (Modifiers != NIL)
  416.                         {
  417.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  418.                         }
  419.                     if (Window != NIL)
  420.                         {
  421.                             *Window = GetCurrentWindow();
  422.                         }
  423.                     return eNoEvent;
  424.  
  425.                 case mouseDown:
  426.                     switch (FindWindow(MyEvent.where,&WhichWindow))
  427.                         {
  428.                             case inSysWindow:
  429.                                 EXECUTE(PRERR(AllowResume,"GetAnEvent:  FindWindow returned inSysWindow"));
  430.                                 /* SystemClick(&MyEvent,WhichWindow); */
  431.                                 goto LoopPoint;
  432.                             case inMenuBar:
  433.                                 MenuState = eMenuPendingMouse;
  434.                                 WipeMenusClean();
  435.                                 if (Modifiers != NIL)
  436.                                     {
  437.                                         *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  438.                                     }
  439.                                 if (Window != NIL)
  440.                                     {
  441.                                         *Window = GetCurrentWindow();
  442.                                     }
  443.                                 return eMenuStarting;
  444.                             case inDrag:
  445.                                 if ((FrontWindow() != NIL) /* make sure there's a front window */
  446.                                     && (
  447.                                         /* front window must be a document window */
  448.                                         (GetWindowKind((WinType*)GetWRefCon(FrontWindow())) == eDocumentWindow)
  449.                                         /* or front window must be the window in question */
  450.                                         || (FrontWindow() == WhichWindow)
  451.                                         /* or command key must be down to prevent window switch */
  452.                                         || ((MyEvent.modifiers & cmdKey) != 0)))
  453.                                     {
  454.                                         Rect                    BoundsRect;
  455.                                         RgnHandle            BoundsRegion;
  456.  
  457.                                         BoundsRegion = GetGrayRgn();
  458.                                         BoundsRect = (**BoundsRegion).rgnBBox;
  459.                                         InsetRect(&BoundsRect,4,4);
  460.                                         DragWindow(WhichWindow,MyEvent.where,&BoundsRect);
  461.                                     }
  462.                                  else
  463.                                     {
  464.                                         ErrorBeep();
  465.                                     }
  466.                                 goto LoopPoint;
  467.                             case inContent:
  468.                             case inGrow:
  469.                                 if (FrontWindow() != WhichWindow)
  470.                                     {
  471.                                         if ((FrontWindow() == NIL) || (GetWindowKind(
  472.                                             (WinType*)GetWRefCon(FrontWindow())) == eDocumentWindow))
  473.                                             {
  474.                                                 SelectWindow(WhichWindow);
  475.                                             }
  476.                                          else
  477.                                             {
  478.                                                 ErrorBeep();
  479.                                             }
  480.                                         goto LoopPoint;
  481.                                     }
  482.                                 LastMouseDownInThisWindow = WhichWindow;
  483.                                 SetPort(WhichWindow);
  484.                                 GlobalToLocal(&MyEvent.where);
  485.                                 if (Modifiers != NIL)
  486.                                     {
  487.                                         *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  488.                                     }
  489.                                 if (Xloc != NIL)
  490.                                     {
  491.                                         *Xloc = MyEvent.where.h;
  492.                                     }
  493.                                 if (Yloc != NIL)
  494.                                     {
  495.                                         *Yloc = MyEvent.where.v;
  496.                                     }
  497.                                 if (Window != NIL)
  498.                                     {
  499.                                         *Window = (WinType*)GetWRefCon(WhichWindow);
  500.                                     }
  501.                                 return eMouseDown;
  502.                             case inGoAway:
  503.                                 if (TrackGoAway(WhichWindow,MyEvent.where))
  504.                                     {
  505.                                         if (Window != NIL)
  506.                                             {
  507.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  508.                                             }
  509.                                         return eWindowClosing;
  510.                                     }
  511.                                 goto LoopPoint;
  512.                             case inZoomIn:
  513.                                 SetPort(WhichWindow);
  514.                                 if (TrackBox(WhichWindow,MyEvent.where,inZoomIn))
  515.                                     {
  516.                                         ZoomWindow(WhichWindow,inZoomIn,False);
  517.                                         if (Window != NIL)
  518.                                             {
  519.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  520.                                             }
  521.                                         return eWindowResized;
  522.                                     }
  523.                                 goto LoopPoint;
  524.                             case inZoomOut:
  525.                                 SetPort(WhichWindow);
  526.                                 if (TrackBox(WhichWindow,MyEvent.where,inZoomOut))
  527.                                     {
  528.                                         ZoomWindow(WhichWindow,inZoomOut,False);
  529.                                         if (Window != NIL)
  530.                                             {
  531.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  532.                                             }
  533.                                         return eWindowResized;
  534.                                     }
  535.                                 goto LoopPoint;
  536.                             default:
  537.                                 goto LoopPoint;
  538.                         }
  539.                     break;
  540.  
  541.                 case mouseUp:
  542.                     if (LastMouseDownInThisWindow == NIL)
  543.                         {
  544.                             /* orphaned mouse-up, probably munched during RelinquishCPU */
  545.                             goto LoopPoint;
  546.                         }
  547.                     CheckPtrExistence((WinType*)GetWRefCon(LastMouseDownInThisWindow));
  548.                     SetPort(LastMouseDownInThisWindow);
  549.                     GlobalToLocal(&MyEvent.where);
  550.                     if (Modifiers != NIL)
  551.                         {
  552.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  553.                         }
  554.                     if (Xloc != NIL)
  555.                         {
  556.                             *Xloc = MyEvent.where.h;
  557.                         }
  558.                     if (Yloc != NIL)
  559.                         {
  560.                             *Yloc = MyEvent.where.v;
  561.                         }
  562.                     if (Window != NIL)
  563.                         {
  564.                             *Window = (WinType*)GetWRefCon(LastMouseDownInThisWindow);
  565.                         }
  566.                     LastMouseDownInThisWindow = NIL;
  567.                     return eMouseUp;
  568.  
  569.                 case keyDown:
  570.                 case autoKey:
  571. #if DEBUG
  572.                     if ((MyEvent.modifiers & cmdKey) && (MyEvent.modifiers & shiftKey)
  573.                         && ((MyEvent.message & charCodeMask) == 'h'))
  574.                         {
  575.                             CheckFragmentation();
  576.                             goto LoopPoint;
  577.                         }
  578. #endif
  579.                     if ((MyEvent.modifiers & cmdKey) != 0)
  580.                         {
  581.                             if ((MyEvent.message & charCodeMask) == '.')
  582.                                 {
  583.                                     if (KeyPressed != NIL)
  584.                                         {
  585.                                             *KeyPressed = eCancelKey;
  586.                                         }
  587.                                     goto KeypressCancelSkipCode;
  588.                                 }
  589.                             MenuState = eMenuPendingKey;
  590.                             WipeMenusClean();
  591.                             if (Modifiers != NIL)
  592.                                 {
  593.                                     *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  594.                                 }
  595.                             if (Window != NIL)
  596.                                 {
  597.                                     *Window = GetCurrentWindow();
  598.                                 }
  599.                             return eMenuStarting;
  600.                         }
  601.                     /* the menu keypress handler jumps here to handle commanded keys */
  602.                  FinishKeypressEvent:
  603.                     if (KeyPressed != NIL)
  604.                         {
  605.                             *KeyPressed = MyEvent.message & charCodeMask;
  606.                         }
  607.                     /* jump here if *KeyPressed is eCancelKey and you don't want */
  608.                     /* to clobber it by storing something else in it */
  609.                  KeypressCancelSkipCode:
  610.                     if (FrontWindow() != NIL)
  611.                         {
  612.                             SetPort(FrontWindow());
  613.                             GlobalToLocal(&MyEvent.where);
  614.                             if (Xloc != NIL)
  615.                                 {
  616.                                     *Xloc = MyEvent.where.h;
  617.                                 }
  618.                             if (Yloc != NIL)
  619.                                 {
  620.                                     *Yloc = MyEvent.where.v;
  621.                                 }
  622.                         }
  623.                     if (Modifiers != NIL)
  624.                         {
  625.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  626.                         }
  627.                     if (Window != NIL)
  628.                         {
  629.                             *Window = GetCurrentWindow();
  630.                         }
  631.                     return eKeyPressed;
  632.  
  633.                 case keyUp:
  634.                     goto LoopPoint;
  635.  
  636.                 case updateEvt:
  637.                     BeginUpdate((WindowPtr)MyEvent.message);
  638.                     CallWindowUpdate((WinType*)GetWRefCon((WindowPtr)MyEvent.message));
  639.                     EndUpdate((WindowPtr)MyEvent.message);
  640.                     goto LoopPoint;
  641.  
  642.                 case activateEvt:
  643.                     /* we'll catch this when we test FrontWindow() */
  644.                     goto LoopPoint;
  645.  
  646.                 case osEvt:
  647.                     switch ((unsigned char)((MyEvent.message >> 24) & 0x000000ff))
  648.                         {
  649.                             case suspendResumeMessage:
  650.                                 if (!(MyEvent.message & resumeFlag))
  651.                                     {
  652.                                         /* suspend */
  653.                                         JudiciousInterval = JUDICIOUSDELAYBACKGROUND;
  654.                                         RunningInForeground = False;
  655.                                         if (Window != NIL)
  656.                                             {
  657.                                                 *Window = 0;
  658.                                             }
  659.                                     }
  660.                                  else
  661.                                     {
  662.                                         /* resume */
  663.                                         JudiciousInterval = JUDICIOUSDELAYFOREGROUND;
  664.                                         RunningInForeground = True;
  665.                                         if (Window != NIL)
  666.                                             {
  667.                                                 *Window = GetCurrentWindow();
  668.                                             }
  669.                                     }
  670.                                 return eActiveWindowChanged;
  671.                             case mouseMovedMessage:
  672.                                 break;
  673.                             default:
  674.                                 break;
  675.                         }
  676.                     goto LoopPoint;
  677.  
  678.                 case kHighLevelEvent:
  679.                     AEProcessAppleEvent(&MyEvent);
  680.                     goto LoopPoint;
  681.  
  682.                 default:
  683.                     goto LoopPoint;
  684.             }
  685.     }
  686.  
  687.  
  688. /* set the amount of time to wait for an event.  The default is 1/4 of a second */
  689. /* time units are in seconds (can be fractional since they are floating point values) */
  690. double                SetEventSleepTime(double TheSleepTime)
  691.     {
  692.         unsigned long            OldSleepTime;
  693.  
  694.         OldSleepTime = SleepTime;
  695.         SleepTime = (TheSleepTime * 60) + 0.5;
  696.         return (double)OldSleepTime / 60 + 0.5;
  697.     }
  698.  
  699.  
  700. /* used by RelinquishCPUCheckCancel.  RelinquishCPUCheckCancel continually */
  701. /* resets it's value to VERYSMALLTIMEINVERVAL, but if it is changed, then */
  702. /* the new delay will take effect for one call of RelinquishCPUCheckCancel. */
  703. /* RelinquishCPUJudiciouslyCheckCancel uses this to get the processor back */
  704. /* as soon as possible. */
  705. static long                    RelinqCPUDelay = RELINQUISHCPUNORMALDELAY;
  706.  
  707. /* relinquishes CPU for 1 tick and checks to see if the user hit escape or cmd-. */
  708. /* (or perhaps other cancel signals) and returns True if the user is trying to */
  709. /* cancel.  Through the use of the global variable CancelPending, cancels are */
  710. /* "sticky" so that subsequent calls will return True as well, until GetAnEvent is */
  711. /* called. */
  712. MyBoolean            RelinquishCPUCheckCancel(void)
  713.     {
  714.         EventRecord            StupidEvent;
  715.         WindowPtr                WhichWindow;
  716.  
  717.         if (LastMouseDownInThisWindow != NIL)
  718.             {
  719.                 /* since we munch mouse events, we don't want to do this during a mouse */
  720.                 /* down since we'll probably lose the mouse up.  It's rather silly for the */
  721.                 /* user to hit cancel while holding the mouse down anyway... */
  722.                 return CancelPending;
  723.             }
  724.      TryAgainPoint:
  725.         WaitNextEvent(keyDownMask | keyUpMask | mDownMask | mUpMask | updateMask
  726.             | osMask | autoKeyMask | diskMask,&StupidEvent,RelinqCPUDelay,NIL);
  727.         LastEventTime = TickCount();
  728.         switch (StupidEvent.what)
  729.             {
  730.                 case updateEvt:
  731.                     BeginUpdate((WindowPtr)StupidEvent.message);
  732.                     MarkForDeferredUpdate((WinType*)GetWRefCon((WindowPtr)StupidEvent.message));
  733.                     EndUpdate((WindowPtr)StupidEvent.message);
  734.                     goto TryAgainPoint;
  735.                 case mouseDown:
  736.                     switch (FindWindow(StupidEvent.where,&WhichWindow))
  737.                         {
  738.                             default:
  739.                                 break;
  740.                             case inSysWindow:
  741.                                 EXECUTE(PRERR(AllowResume,"GetAnEvent:  FindWindow returned inSysWindow"));
  742.                                 /* SystemClick(&StupidEvent,WhichWindow); */
  743.                                 break;
  744.                             case inDrag:
  745.                                 if ((FrontWindow() == WhichWindow) || ((FrontWindow() != NIL)
  746.                                     && ((GetWindowKind((WinType*)GetWRefCon(FrontWindow()))
  747.                                     == eDocumentWindow) || (GetWindowKind((WinType*)GetWRefCon(
  748.                                     FrontWindow())) == eModelessDialogWindow))
  749.                                     && ((StupidEvent.modifiers & cmdKey) != 0)))
  750.                                     {
  751.                                         Rect                    BoundsRect;
  752.                                         RgnHandle            BoundsRegion;
  753.  
  754.                                         BoundsRegion = GetGrayRgn();
  755.                                         BoundsRect = (**BoundsRegion).rgnBBox;
  756.                                         InsetRect(&BoundsRect,4,4);
  757.                                         DragWindow(WhichWindow,StupidEvent.where,&BoundsRect);
  758.                                     }
  759.                                  else
  760.                                     {
  761.                                         ErrorBeep();
  762.                                     }
  763.                                 break;
  764.                         }
  765.                     break;
  766.                 case keyDown:
  767.                     if ((((StupidEvent.message & charCodeMask) == '.')
  768.                         && ((StupidEvent.modifiers & cmdKey) != 0)) ||
  769.                         ((StupidEvent.message & charCodeMask) == 27))
  770.                         {
  771.                             CancelPending = True; /* stick */
  772.                         }
  773.                     else if (KeyboardEventBuffer != NIL)
  774.                         {
  775.                             EventRecord*        Temp;
  776.                             long                        OldSize;
  777.  
  778.                             CheckPtrExistence(KeyboardEventBuffer);
  779.                             /* maybe the keypress was wanted, so we should save it */
  780.                             OldSize = PtrSize((char*)KeyboardEventBuffer);
  781.                             Temp = (EventRecord*)ResizePtr((char*)KeyboardEventBuffer,
  782.                                 OldSize + sizeof(EventRecord));
  783.                             if (Temp != NIL)
  784.                                 {
  785.                                     Temp[OldSize / sizeof(EventRecord)] = StupidEvent;
  786.                                     KeyboardEventBuffer = Temp;
  787.                                 }
  788.                         }
  789.                     break;
  790.                 default:
  791.                     break;
  792.             }
  793.         RelinqCPUDelay = RELINQUISHCPUNORMALDELAY;
  794.         return CancelPending; /* sticky */
  795.     }
  796.  
  797.  
  798. /* similar to RelinquishCPUCheckCancel but in cooperative multitasking systems, */
  799. /* it gives much better performance for the application by not releasing the */
  800. /* processor nearly as often */
  801. MyBoolean            RelinquishCPUJudiciouslyCheckCancel(void)
  802.     {
  803.         if (TickCount() - LastEventTime < JudiciousInterval)
  804.             {
  805.                 return CancelPending; /* sticky */
  806.             }
  807.         RelinqCPUDelay = 0; /* come back right away */
  808.         if (IsThisAPowerBook)
  809.             {
  810.                 /* if some CPU intensive task is going on, then keep the processor */
  811.                 /* from getting sleepy */
  812.                 IdleUpdate();
  813.             }
  814.         return RelinquishCPUCheckCancel();
  815.     }
  816.  
  817.  
  818. /* read the system timer (in seconds).  The timer returns real time (not process */
  819. /* time) but not relative to any known time.  The value may roll over from an */
  820. /* undefined large number to 0. */
  821. double                ReadTimer(void)
  822.     {
  823.         return ((double)TickCount()) / 60;
  824.     }
  825.  
  826.  
  827. /* find the true difference between two timer values even if one has rolled over */
  828. double                TimerDifference(double Now, double Then)
  829.     {
  830.         return (double)((unsigned long)(60 * Now + 0.5)
  831.             - (unsigned long)(60 * Then + 0.5)) / 60;
  832.     }
  833.  
  834.  
  835. /* get the current mouse position.  If there is no current window, the */
  836. /* results are undefined.  Either of the parameters can be NIL if the user */
  837. /* doesn't care about the result */
  838. void                    GetMousePosition(OrdType* Xloc, OrdType* Yloc)
  839.     {
  840.         Point            MouseLoc;
  841.  
  842.         if (FrontWindow() != NIL)
  843.             {
  844.                 SetPort(FrontWindow());
  845.                 GetMouse(&MouseLoc);
  846.                 if (Xloc != NIL)
  847.                     {
  848.                         *Xloc = MouseLoc.h;
  849.                     }
  850.                 if (Yloc != NIL)
  851.                     {
  852.                         *Yloc = MouseLoc.v;
  853.                     }
  854.             }
  855.     }
  856.  
  857.  
  858. /* read the state of the modifier keys on the keyboard.  On systems that don't */
  859. /* allow this, the function may return the modifiers as they were at the last */
  860. /* known time */
  861. ModifierFlags    CheckModifiers(void)
  862.     {
  863.         EventRecord            StupidEvent;
  864.  
  865.         WaitNextEvent(0,&StupidEvent,0,NIL);
  866.         return (ModifierFlags)FormModifiers(StupidEvent.modifiers);
  867.     }
  868.  
  869.  
  870. /* set an implementation defined version of the specified cursor */
  871. void                    SetArrowCursor(void)
  872.     {
  873.         SetCursor(&qd.arrow);
  874.     }
  875.  
  876.  
  877. /* set an implementation defined version of the specified cursor */
  878. void                    SetIBeamCursor(void)
  879.     {
  880.         CursHandle        DaCursor;
  881.  
  882.         DaCursor = GetCursor(iBeamCursor);
  883.         HLock((Handle)DaCursor);
  884.         SetCursor(*DaCursor);
  885.         HUnlock((Handle)DaCursor);
  886.     }
  887.  
  888.  
  889. /* set an implementation defined version of the specified cursor */
  890. void                    SetWatchCursor(void)
  891.     {
  892.         CursHandle        DaCursor;
  893.  
  894.         DaCursor = GetCursor(watchCursor);
  895.         HLock((Handle)DaCursor);
  896.         SetCursor(*DaCursor);
  897.         HUnlock((Handle)DaCursor);
  898.     }
  899.  
  900.  
  901. /* set an implementation defined version of the specified cursor */
  902. void                    SetCrossHairCursor(void)
  903.     {
  904.         CursHandle        DaCursor;
  905.  
  906.         DaCursor = GetCursor(crossCursor);
  907.         HLock((Handle)DaCursor);
  908.         SetCursor(*DaCursor);
  909.         HUnlock((Handle)DaCursor);
  910.     }
  911.  
  912.  
  913. /* set the cursor tho the image and mask specified.  If the implementation's cursor */
  914. /* is larger than 16x16, then the adjustment is implementation defined.  On the */
  915. /* Macintosh, cursors are 16x16, so no adjustment is necessary */
  916. /* the most significant bit of the word is leftmost */
  917. void                            SetTheCursor(short HotPointX, short HotPointY,
  918.                                         unsigned short CursorImage[16], unsigned short CursorMask[16])
  919.     {
  920.         Cursor                    TheCurs;
  921.         int                            Scan;
  922.  
  923.         ERROR((HotPointX < 0) || (HotPointX >= 16) || (HotPointY < 0) || (HotPointY >= 16),
  924.             PRERR(AllowResume,"SetTheCursor:  hot point out of range"));
  925.         for (Scan = 0; Scan < 16; Scan += 1)
  926.             {
  927.                 TheCurs.data[Scan] = CursorImage[Scan];
  928.                 TheCurs.mask[Scan] = CursorMask[Scan];
  929.             }
  930.         TheCurs.hotSpot.h = HotPointX;
  931.         TheCurs.hotSpot.v = HotPointY;
  932.         SetCursor(&TheCurs);
  933.     }
  934.  
  935.  
  936. /* get the number of seconds to wait before toggling an insertion point */
  937. double                GetCursorBlinkRate(void)
  938.     {
  939.         return ((double)GetCaretTime()) / 60;
  940.     }
  941.  
  942.  
  943. /* get the maximum time between clicks for which they are considered a double click */
  944. double                GetDoubleClickInterval(void)
  945.     {
  946.         return ((double)GetDblTime()) / 60;
  947.     }
  948.  
  949.  
  950. /* emit a not too annoying beep to indicate an error occurred */
  951. void                    ErrorBeep(void)
  952.     {
  953.         if (MakeErrorBeeps)
  954.             {
  955.                 SysBeep(BEEPDURATION);
  956.             }
  957.          else
  958.             {
  959.                 unsigned long            StartTime;
  960.  
  961.                 FlashMenuBar(0);
  962.                 StartTime = TickCount();
  963.                 while (TickCount() - StartTime < MENUDELAY)
  964.                     {
  965.                         /* hideous delay loop to flash menu bar so user can see it. */
  966.                         RelinquishCPUCheckCancel();
  967.                     }
  968.                 FlashMenuBar(0);
  969.             }
  970.     }
  971.  
  972.  
  973. /* enable or disable error beeping.  True enables it.  the old value is returned. */
  974. MyBoolean                    SetErrorBeepEnable(MyBoolean ShouldWeBeep)
  975.     {
  976.         MyBoolean                OldValue;
  977.  
  978.         OldValue = MakeErrorBeeps;
  979.         MakeErrorBeeps = ShouldWeBeep;
  980.         return OldValue;
  981.     }
  982.  
  983.  
  984. /* this routine is called when a window is dying, so any locally cached pointers */
  985. /* to windows have to be discarded */
  986. void                    Eep_WindowDying(WinType* Window)
  987.     {
  988.         if ((WinType*)GetWRefCon(LastMouseDownInThisWindow) == Window)
  989.             {
  990.                 LastMouseDownInThisWindow = NIL;
  991.             }
  992.         if ((WinType*)GetWRefCon(LastActiveWindow) == Window)
  993.             {
  994.                 LastActiveWindow = NIL;
  995.             }
  996.     }
  997.