Source/EventHandler.c

/*
    File:       EventHandler.c
 
    Contains:   xxx put contents here xxx
 
    Version:    xxx put version here xxx
 
    Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
 
    File Ownership:
 
        DRI:                xxx put dri here xxx
 
        Other Contact:      xxx put other contact here xxx
 
        Technology:         xxx put technology here xxx
 
    Writers:
 
        (cjd)   Chris DeSalvo
        (sjb)   Steve Bollinger
        (BWS)   Brent Schorsch
 
    Change History (most recent first):
 
      <SP21>    10/20/98    BWS     Pause and Quit are now separate needs
        <20>      7/1/98    cjd     Added CPU load axis to InputSprocket data set
        <19>     6/18/98    sjb     InputSprocket.h comes from <> place
        <18>     6/12/98    BWS     Changed to use InputSprocket 68k
*/
 
//¥ ------------------------------------------------------------------------------------------  ¥
//¥
//¥ Copyright © 1996 Apple Computer, Inc., All Rights Reserved
//¥
//¥
//¥     You may incorporate this sample code into your applications without
//¥     restriction, though the sample code has been provided "AS IS" and the
//¥     responsibility for its operation is 100% yours.  However, what you are
//¥     not permitted to do is to redistribute the source as "DSC Sample Code"
//¥     after having made changes. If you're going to re-distribute the source,
//¥     we require that you make it clear in the source that the code was
//¥     descended from Apple Sample Code, but that you've made changes.
//¥
//¥     Authors:
//¥         Chris De Salvo
//¥         Michael Evans
//¥
//¥ ------------------------------------------------------------------------------------------  ¥
 
//¥ ------------------------------  Includes
 
#include <DiskInit.h>
#include <ToolUtils.h>
 
#define USE_OLD_ISPNEED_STRUCT          0
#define USE_OLD_INPUT_SPROCKET_LABELS   0
 
#include <DrawSprocket.h>
#include <InputSprocket.h>
 
#include "ErrorHandler.h"
#include "EventHandler.h"
#include "Graphics.h"
#include "MenuHandler.h"
#include "SprocketInvaders.h"
 
//¥ ------------------------------  Private Definitions
//¥ ------------------------------  Private Types
//¥ ------------------------------  Private Variables
 
static SInt32   gEventTime;
static SInt16   gSleepTime;
 
//¥ ------------------------------  Private Functions
 
static void EventDispatch(EventRecord *theEvent);
static Boolean DefaultMouseDown(EventRecord *theEvent);
static Boolean DefaultKeyDown(EventRecord *theEvent);
static Boolean DoDiskEvent(EventRecord *theEvent);
static void DoSuspend(void);
 
//¥ ------------------------------  Public Variables
 
Boolean             gDone = false;
SInt32              gDeltaTime;
EventHandlerSet     gEventHandlers;
ISpElementReference gInputElements[numInputs];
GameKeys            gGameKeys;
 
//¥ --------------------    EventInit
 
short
EventInit(void)
{
SInt16      modifiers = 0;
EventRecord dummy;
OSStatus    theError;
 
//¥ This structure defines the input needs that we'll be requesting from InputSprocket
ISpNeed     myNeeds[numInputs] =
{
    {
        "\pPlayer 1 Movement",
        200,
        1,
        0,
        kISpElementKind_Axis,
        kISpElementLabel_Axis_XAxis,
        0,
        0,
        0,
        0
    },
    {
        "\pPlayer 1 Fire",
        201,
        1,
        0,
        kISpElementKind_Button,
        kISpElementLabel_Btn_Fire,
        0,
        0,
        0,
        0
    },
    {
        "\pPlayer 2 Movement",
        203,
        2,
        0,
        kISpElementKind_Axis,
        kISpElementLabel_Axis_XAxis,
        0,
        0,
        0,
        0
    },
    {
        "\pPlayer 2 Fire",
        204,
        2,
        0,
        kISpElementKind_Button,
        kISpElementLabel_Btn_Fire,
        0,
        0,
        0,
        0
    },
    {
        "\pAbort Game",
        202,
        0,
        0,
        kISpElementKind_Button,
        kISpElementLabel_Btn_Quit,
        0,
        0,
        0,
        0
    },
    {
        "\pToggle Sound",
        205,
        0,
        0,
        kISpElementKind_Button,
        kISpElementLabel_None,
        kISpNeedFlag_Utility,
        0,
        0,
        0
    },
    {
        "\pSoundSprocket CPU Load",
        206,
        0,
        0,
        kISpElementKind_Axis,
        kISpElementLabel_None,
        0,
        0,
        0,
        0
    }
};
 
    EventAvail(everyEvent, &dummy);
    modifiers |= dummy.modifiers;
    EventAvail(everyEvent, &dummy);
    modifiers |= dummy.modifiers;
    EventAvail(everyEvent, &dummy);
    modifiers |= dummy.modifiers;
 
    RegisterEventHandlers(DefaultKeyDown, DefaultKeyDown, DefaultMouseDown, nil, nil);
    gEventHandlers.diskHandler = DoDiskEvent;
 
    //¥ This sets up how often we'll call WaitNextEvent() in out event loop.
    //¥ WNE is still emulated so it's sort of a time waster if you call it too often.
    //¥ So, we check DblTime which is the user-selected double-click speed.  We make sure
    //¥ that we check inputs at least often enough to notice double-clicks.
    gEventTime = GetDblTime();
    gEventTime /= 2;
 
    //¥ This is how much time we'll give to background apps when we're not actively playing
    //¥ a game.
    gSleepTime = 32767;
 
    //¥ Setup the input sprocket elements
    theError = ISpElement_NewVirtualFromNeeds(numInputs, myNeeds, gInputElements, 0);
    if (theError)
        FatalError("Could not create ISp virtual controls from needs.");
 
    //¥ Init InputSprocket and tell it our needs
    theError = ISpInit(numInputs, myNeeds, gInputElements, 'SInv', '0002', 0, 128, 0);
    if (theError)
        FatalError("Could not initialize ISp.");
 
    //¥ Turn on the keyboard and mouse handlers
    ISpDevices_ActivateClass (kISpDeviceClass_Keyboard);
    ISpDevices_ActivateClass (kISpDeviceClass_Mouse);
    ISpDevices_ActivateClass (kISpDeviceClass_SpeechRecognition);
 
    ISpSuspend ();
 
    return (modifiers);
}
 
//¥ --------------------    EventLoop
 
void
EventLoop(void)
{
EventRecord theEvent;
 
static SInt32   lastTime = 0L;
 
SInt32          thisTime;
Boolean         wasProcessed;
 
    while (false == gDone)
    {
        thisTime = TickCount();
 
        //¥ We don't call WNE while we're playing the game
        if (false == gGameInProgress)
        {
            if (WaitNextEvent(everyEvent, &theEvent, gSleepTime, nil))
            {
                //¥ See if DrawSprocket wants to handle this event
                DSpProcessEvent(&theEvent, &wasProcessed);
 
                //¥ If DSp punted then we process the event
                if (false == wasProcessed)
                    EventDispatch(&theEvent);
            }
            else
            {
                //¥ Run any idle tasks
                if (gEventHandlers.idleHandler)
                {
                    (*gEventHandlers.idleHandler)(&theEvent);
                }
            }
        }
 
        //¥ gDeltaTime tells the game engine how much time has passed since the last
        //¥ game loop.
        if (lastTime > 0)
            gDeltaTime = thisTime - lastTime;
 
        //¥ We must tell it that at least SOME time has passed.
        if (gDeltaTime < 1)
            gDeltaTime = 1;
 
        if (gGameInProgress)
        {
            ISpTickle();
            GameLoop();
        }
 
        lastTime = TickCount();
    }
}
 
//¥ --------------------    EventDispatch
 
static void
EventDispatch(EventRecord *theEvent)
{
    switch (theEvent->what)
    {
        case mouseDown:
            if (gEventHandlers.clickHandler != nil)
                if ((*gEventHandlers.clickHandler)(theEvent))
                {
                    break;
                }
 
            DefaultMouseDown(theEvent);
            break;
 
        case keyDown:
            if (gEventHandlers.keyHandler != nil)
                if ((*gEventHandlers.keyHandler)(theEvent))
                {
                    break;
                }
 
            DefaultKeyDown(theEvent);
            break;
 
        case autoKey:
            if (gEventHandlers.autoKeyHandler != nil)
                if ((*gEventHandlers.autoKeyHandler)(theEvent))
                {
                    break;
                }
 
            DefaultKeyDown(theEvent);
            break;
 
        case updateEvt:
            if (gEventHandlers.updateHandler != nil)
            {
                if ((*gEventHandlers.updateHandler)(theEvent))
                {
                }
            }
 
            BeginUpdate((WindowRef) theEvent->message);
            GraphicsDoUpdateEvent();
            EndUpdate((WindowRef) theEvent->message);
            break;
 
        case diskEvt:
            if (gEventHandlers.diskHandler != nil)
                (*gEventHandlers.diskHandler)(theEvent);
            break;
 
        case osEvt:
            if (theEvent->message & 0x01000000)     //¥ Suspend/resume event
            {
                if (theEvent->message & 0x00000001) //¥ Resume
                {
                }
                else
                {
                    DoSuspend();                    //¥ Suspend
                }
            }
            break;
 
        case kHighLevelEvent:
            AEProcessAppleEvent(theEvent);
            break;
    }
}
 
//¥ ------------------------------  DefaultMouseDown
 
static Boolean
DefaultMouseDown(EventRecord *theEvent)
{
SInt16      whatPart;
SInt32      menuResult;
WindowRef   whichWindow;
 
    whatPart = FindWindow(theEvent->where, &whichWindow);
 
    switch (whatPart)
    {
        case inGoAway:
            break;
 
        case inMenuBar:
            DrawMenuBar();
            menuResult = MenuSelect(theEvent->where);
 
            if (HiWord(menuResult) != 0)
                MenuDoChoice(menuResult);
            break;
 
        case inSysWindow:
            SystemClick(theEvent, whichWindow);
            break;
    }
 
    return (true);
}
 
//¥ ------------------------------  DefaultKeyDown
 
static Boolean
DefaultKeyDown(EventRecord *theEvent)
{
SInt8   theKey;
SInt8   theCode;
SInt32  menuResult;
 
    theKey = theEvent->message & charCodeMask;
    theCode = (theEvent->message & keyCodeMask) >> 8;
    if ((theEvent->modifiers & cmdKey) != 0)
    {
        menuResult = MenuKey(theKey);
        if (HiWord(menuResult) != 0)
            MenuDoChoice(menuResult);
 
        return (true);
    }
    
    if ('e' == theKey)
        GraphicsDoUpdateEvent();
    
    return (true);
}
 
//¥ ------------------------------  DoDiskEvent
 
static Boolean
DoDiskEvent(EventRecord *theEvent)
{
Point   thePoint;
SInt16  value;
 
    if (theEvent->message & 0xFFFF0000)     //¥ Error mounting disk
    {
        SetPt(&thePoint, 100, 100);
        value = DIBadMount(thePoint, theEvent->message);
    }
 
    return (true);
}
 
//¥ ------------------------------  RegisterEventHandlers
 
void
RegisterEventHandlers(EventHandlerProc keyDown, EventHandlerProc autoKey, EventHandlerProc mouseDown,
                    EventHandlerProc update, EventHandlerProc idle)
{
    gEventHandlers.keyHandler = keyDown;
    gEventHandlers.autoKeyHandler = autoKey;
    gEventHandlers.clickHandler = mouseDown;
    gEventHandlers.updateHandler = update;
    gEventHandlers.idleHandler = idle;
}
 
//¥ ------------------------------  ModifyEventHandlers
 
void
ModifyEventHandlers(EventHandlerProc keyDown, EventHandlerProc autoKey, EventHandlerProc mouseDown,
                    EventHandlerProc update, EventHandlerProc idle)
{
    if (keyDown)
        gEventHandlers.keyHandler = keyDown;
 
    if (autoKey)
        gEventHandlers.autoKeyHandler = autoKey;
 
    if (mouseDown)
        gEventHandlers.clickHandler = mouseDown;
 
    if (update)
        gEventHandlers.updateHandler = update;
 
    if (idle)
        gEventHandlers.idleHandler = idle;
}
 
//¥ --------------------    DoSuspend
//¥
//¥ This is a small, local event loop.  All it does is fetch screen update
//¥ events and wait to get put back in the foreground.
 
static void
DoSuspend(void)
{
EventRecord theEvent;
 
    while (true)
    {
        //¥ Only check for resume and update events.  While doing so, give TONS of time to other apps
        if (WaitNextEvent(osMask, &theEvent, 32767, nil))
        {
            if (theEvent.message & 0x01000000)      //¥ Suspend/resume event
            {
                if (theEvent.message & 0x00000001)  //¥ Resume
                {
                Boolean dummy;
 
                    //¥ Make sure that DrawSprocket knows that we've resumed
                    DSpProcessEvent(&theEvent, &dummy);
                }
                break;
            }
        }
    }
}