Source/ISp_Sample.c

/*
    File:       ISp_Sample.c
 
    Contains:   xxx put contents here xxx
 
    Version:    xxx put version here xxx
 
    Copyright:  © 1999 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:
 
        (BWS)   Brent Schorsch
 
    Change History (most recent first):
 
       <SP2>      7/1/99    BWS     Use some new need flags
       <SP1>      7/1/99    BWS     first checked in
*/
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ  Includes
 
#define USE_OLD_INPUT_SPROCKET_LABELS 0
#define USE_OLD_ISPNEED_STRUCT 0
 
#include <InputSprocket.h>
#include <CodeFragments.h>
#include <FixMath.h>
#include <Fonts.h>
#include <MacWindows.h>
#include <ToolUtils.h>
#include <TextUtils.h>
 
#include "ISp_Sample.h"
#include "ISp_SampleResources.h"
#include "ErrorAlert.h"
#include "EventHandler.h"
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ  Private Definitions
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ  Private Types
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ  Private Variables
Boolean gKeyboardTypingEnabled = true;
Boolean gMouseAsMacOSCursorEnabled = true;
 
ISpElementReference         gInputElements[kNeed_NeedCount];
ISpElementListReference     gEventsElementList = NULL;
ISpElementListReference     gYawElementList = NULL;
ISpElementListReference     gThrottleElementList = NULL;
 
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ  Private Functions
 
Boolean Input_ISpConfigureEventProc (EventRecord * inEvent);
 
static void     Input_GetRoll (Input_GameState * gameState);
static void     Input_GetPitch (Input_GameState * gameState);
static void     Input_GetYaw (Input_GameState * gameState);
static void     Input_GetThrottle (Input_GameState * gameState);
 
static SInt32   ISpAxisToSampleAxis (ISpAxisData axisValue, SInt32 min, SInt32 max);
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ  Public Variables
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_Available
Boolean  Input_Available (void)
{
    Boolean iSpAvailable = false;
    
    if ((ProcPtr) ISpGetVersion != (ProcPtr) kUnresolvedCFragSymbolAddress)
    {
        NumVersion iSpNumVersion = ISpGetVersion();
        UInt32 inputSprocketVersion = * (UInt32 *) &(iSpNumVersion);
        
        // require InputSprocket 1.2.0 (we need delta types)
        if (inputSprocketVersion >= 0x01200000)
            iSpAvailable = true;
    }
    
    return iSpAvailable;
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_Initialize
OSStatus Input_Initialize (void)
{
    OSStatus    theError;
 
    //¥ This structure defines the input needs that we'll be requesting from InputSprocket
    ISpNeed     tempNeed;
    ISpNeed     myNeeds[kNeed_NeedCount];
    
    tempNeed.reserved1 = 0;
    tempNeed.reserved2 = 0;
    tempNeed.reserved3 = 0;
 
    //¥ we'll init all the player 1 items now (everything but quit unless we add a second player)
    tempNeed.playerNum = 1;
    
    // First we init (player 1) items that are not part of a group (group 0)
    tempNeed.group = 0;
 
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_FireWeapon + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_FireWeapon;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_Btn_Fire;
    tempNeed.flags = kISpNeedFlag_Button_ActiveWhenDown | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_FireWeapon] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_StartPause + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_StartPause;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpNeedFlag_NoAutoConfig | kISpElementLabel_Btn_StartPause;
    tempNeed.flags = kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_StartPause] = tempNeed;
 
    // Now group 1, which is for changing the current weapon
    tempNeed.group = 1;
    tempNeed.flags = kISpNeedFlag_EventsOnly;
 
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_NextWeapon + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_NextWeapon;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_Btn_Next;
    myNeeds[kNeed_NextWeapon] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_PreviousWeapon + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_PreviousWeapon;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_Btn_Previous;
    myNeeds[kNeed_PreviousWeapon] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Weapon_MachineGun + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Weapon_MachineGun;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_NoConfig;
    myNeeds[kNeed_Weapon_MachineGun] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Weapon_Cannon + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Weapon_Cannon;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    myNeeds[kNeed_Weapon_Cannon] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Weapon_Laser + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Weapon_Laser;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_NoConfig;
    myNeeds[kNeed_Weapon_Laser] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Weapon_Missle + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Weapon_Missle;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_NoConfig;
    myNeeds[kNeed_Weapon_Missle] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Weapon_PrecisionBomb + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Weapon_PrecisionBomb;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_NoConfig;
    myNeeds[kNeed_Weapon_PrecisionBomb] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Weapon_ClusterBomb + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Weapon_ClusterBomb;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_NoConfig;
    myNeeds[kNeed_Weapon_ClusterBomb] = tempNeed;
 
    // Now group 2, which is for changing roll
    tempNeed.group = 2;
 
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Roll + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Roll;
    tempNeed.theKind = kISpElementKind_Axis;
    tempNeed.theLabel = kISpElementLabel_Axis_Roll;
    tempNeed.flags = kISpNeedFlag_Axis_AlreadyDelta;
    myNeeds[kNeed_Roll] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Roll_AsDelta + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Roll_AsDelta;
    tempNeed.theKind = kISpElementKind_Delta;
    tempNeed.theLabel = kISpElementLabel_Delta_Roll;
    tempNeed.flags = kISpNeedFlag_Delta_AlreadyAxis;
    myNeeds[kNeed_Roll_AsDelta] = tempNeed;
    
    // Now group 3, which is for changing pitch
    tempNeed.group = 3;
 
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Pitch + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Pitch;
    tempNeed.theKind = kISpElementKind_Axis;
    tempNeed.theLabel = kISpElementLabel_Axis_Pitch;
    tempNeed.flags = kISpNeedFlag_Axis_AlreadyDelta;
    myNeeds[kNeed_Pitch] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Pitch_AsDelta + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Pitch_AsDelta;
    tempNeed.theKind = kISpElementKind_Delta;
    tempNeed.theLabel = kISpElementLabel_Delta_Pitch;
    tempNeed.flags = kISpNeedFlag_Delta_AlreadyAxis;
    myNeeds[kNeed_Pitch_AsDelta] = tempNeed;
    
    // Now group 4, which is for changing yaw
    tempNeed.group = 4;
 
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Yaw + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Yaw;
    tempNeed.theKind = kISpElementKind_Axis;
    tempNeed.theLabel = kISpElementLabel_Axis_Yaw;
    tempNeed.flags = kISpNeedFlag_Axis_AlreadyDelta | kISpNeedFlag_Axis_AlreadyButton;
    myNeeds[kNeed_Yaw] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Yaw_AsDelta + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Yaw_AsDelta;
    tempNeed.theKind = kISpElementKind_Delta;
    tempNeed.theLabel = kISpElementLabel_Delta_Yaw;
    tempNeed.flags = kISpNeedFlag_Delta_AlreadyAxis | kISpNeedFlag_Delta_AlreadyButton;
    myNeeds[kNeed_Yaw_AsDelta] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Yaw_Left + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Yaw_Left;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_Button_AlreadyAxis | kISpNeedFlag_Button_AlreadyDelta | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_Yaw_Left] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Yaw_Center + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Yaw_Center;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_Button_AlreadyAxis | kISpNeedFlag_Button_AlreadyDelta | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_Yaw_Center] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Yaw_Right + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Yaw_Right;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_Button_AlreadyAxis | kISpNeedFlag_Button_AlreadyDelta | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_Yaw_Right] = tempNeed;
    
    // Now group 5, which is for changing throttle
    tempNeed.group = 5;
 
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Throttle + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Throttle;
    tempNeed.theKind = kISpElementKind_Axis;
    tempNeed.theLabel = kISpElementLabel_Axis_Throttle;
    tempNeed.flags = kISpNeedFlag_Axis_Asymetric | kISpNeedFlag_Axis_AlreadyButton;
    myNeeds[kNeed_Throttle] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Throttle_Min + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Throttle_Min;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_Button_AlreadyAxis | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_Throttle_Min] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Throttle_Decrease + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Throttle_Decrease;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_Button_AlreadyAxis | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_Throttle_Decrease] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Throttle_Increase + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Throttle_Increase;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_Button_AlreadyAxis | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_Throttle_Increase] = tempNeed;
    
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Throttle_Max + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Throttle_Max;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_None;
    tempNeed.flags = kISpNeedFlag_Button_AlreadyAxis | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_Throttle_Max] = tempNeed;
    
    //¥ now we init utility needs that affect all players (just have quit for now)
    tempNeed.playerNum = 0;
    tempNeed.group = 0;
 
    GetIndString (tempNeed.name, kSTRn_NeedNames, kNeed_Quit + 1);
    tempNeed.iconSuiteResourceId = 1000 + kNeed_Quit;
    tempNeed.theKind = kISpElementKind_Button;
    tempNeed.theLabel = kISpElementLabel_Btn_Quit;
    tempNeed.flags = kISpNeedFlag_NoAutoConfig | kISpNeedFlag_EventsOnly;
    myNeeds[kNeed_Quit] = tempNeed;
    
    //¥ Alright, now that the array is set up, we can call ISp to init stuff
    theError = ISpStartup ();
    if (theError)
        ErrorAlert("\pCould not Initialize InputSprocket.", theError, true);
 
    //¥ Setup the input sprocket elements
    theError = ISpElement_NewVirtualFromNeeds(kNeed_NeedCount, myNeeds, gInputElements, 0);
    if (theError)
        ErrorAlert("\pCould not create ISp virtual controls from needs.", theError, true);
 
    //¥ Init InputSprocket and tell it our needs
    theError = ISpInit (kNeed_NeedCount, myNeeds, gInputElements, 
                    kISpSampleCreator, kISpSampleNeedsVersion, 
                    0, ksetl_ISpSample, 0); 
    if (theError)
        ErrorAlert("\pCould not initialize high-level ISp.", theError, true);
 
    //¥ Create a element list containg all the 'normal' buttons (we get events on these)
    theError = ISpElementList_New(0, NULL, &gEventsElementList, 0);
    if (theError)
        ErrorAlert("\pCould not create button element list.", theError, true);
    
    //¥ we set the refcon to the need enum value, so we can use it later
    //¥ doing some shortcut error checking for readability
    theError  = ISpElementList_AddElements (gEventsElementList, 
                    kNeed_FireWeapon,       1, &gInputElements[kNeed_FireWeapon]);
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_StartPause,       1, &gInputElements[kNeed_StartPause]);
 
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_NextWeapon,       1, &gInputElements[kNeed_NextWeapon]);
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_PreviousWeapon,   1, &gInputElements[kNeed_PreviousWeapon]);
 
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_Weapon_MachineGun, 1, &gInputElements[kNeed_Weapon_MachineGun]);
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_Weapon_Cannon,    1, &gInputElements[kNeed_Weapon_Cannon]);
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_Weapon_Laser,     1, &gInputElements[kNeed_Weapon_Laser]);
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_Weapon_Missle,    1, &gInputElements[kNeed_Weapon_Missle]);
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_Weapon_PrecisionBomb,1, &gInputElements[kNeed_Weapon_PrecisionBomb]);
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_Weapon_ClusterBomb,1, &gInputElements[kNeed_Weapon_ClusterBomb]);
 
    theError |= ISpElementList_AddElements (gEventsElementList, 
                    kNeed_Quit,             1, &gInputElements[kNeed_Quit]);
 
    if (theError)
        ErrorAlert("\pCould not fill button element list. Error number may be inaccurate.", theError, true);
 
    // create element list for rudder
    //¥¥ we need to treat this differently from regular buttons, because we don't know whether the
    //¥¥Êaxis will be moved or the button will be hit, we need to support both at once
    theError = ISpElementList_New(0, NULL, &gYawElementList, 0);
    if (theError)
        ErrorAlert("\pCould not create yaw element list.", theError, true);
 
    //¥ doing some shortcut error checking for readability
    theError  = ISpElementList_AddElements (gYawElementList, 
                    kNeed_Yaw_Left, 1, &gInputElements[kNeed_Yaw_Left]);
    theError |= ISpElementList_AddElements (gYawElementList, 
                    kNeed_Yaw_Center, 1, &gInputElements[kNeed_Yaw_Center]);
    theError |= ISpElementList_AddElements (gYawElementList, 
                    kNeed_Yaw_Right, 1, &gInputElements[kNeed_Yaw_Right]);
    if (theError)
        ErrorAlert("\pCould not fill yaw element list. Error number may be inaccurate.", theError, true);
 
    // create element list for throttle
    //¥¥ we need to treat this differently from regular buttons, because we don't know whether the
    //¥¥Êaxis will be moved or the button will be hit, we need to support both at once
    theError = ISpElementList_New(0, NULL, &gThrottleElementList, 0);
    if (theError)
        ErrorAlert("\pCould not create throttle element list.", theError, true);
    
    //¥ doing some shortcut error checking for readability
    theError  = ISpElementList_AddElements (gThrottleElementList, 
                    kNeed_Throttle_Min, 1, &gInputElements[kNeed_Throttle_Min]);
    theError |= ISpElementList_AddElements (gThrottleElementList, 
                    kNeed_Throttle_Decrease, 1, &gInputElements[kNeed_Throttle_Decrease]);
    theError |= ISpElementList_AddElements (gThrottleElementList, 
                    kNeed_Throttle_Increase, 1, &gInputElements[kNeed_Throttle_Increase]);
    theError |= ISpElementList_AddElements (gThrottleElementList, 
                    kNeed_Throttle_Max, 1, &gInputElements[kNeed_Throttle_Max]);
    if (theError)
        ErrorAlert("\pCould not fill throttle element list. Error number may be inaccurate.", theError, true);
    
    //¥ Enable speech input (we commit to calling ISpTickle at WNE time)
    ISpDevices_ActivateClass (kISpDeviceClass_SpeechRecognition);
    
    //¥ we leave kayboard and mouse disabled. We will enable them when the 'game' is active
    
    return noErr;
}
 
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_Terminate
OSStatus Input_Terminate (void)
{
    return ISpShutdown ();
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_InitializeState
void    Input_InitializeState (Input_GameState * gameState)
{
    ISpAxisData         axisValue;
    OSStatus            error = noErr;
    
    gameState->gameInProgress = false;
    gameState->gamePaused = false;
 
    gameState->fireWeaponState = false;
    gameState->fireWeaponCount = 0;
    
    gameState->currentWeapon = kWeapon_MachineGun;
    
    gameState->rollInput = 0;
    gameState->pitchInput = 0;
    gameState->yawInput = 0;
    gameState->throttleInput = 0;
    
    gameState->deltaRoll = 0;
    gameState->deltaPitch = 0;
    gameState->deltaYaw = 0;
 
    error = ISpElement_GetSimpleState(gInputElements[kNeed_Roll], &axisValue);
    if (!error) gameState->rollInput = ISpAxisToSampleAxis (axisValue, kMin_Roll, kMax_Roll);
    
    error = ISpElement_GetSimpleState(gInputElements[kNeed_Pitch], &axisValue);
    if (!error) gameState->pitchInput = ISpAxisToSampleAxis (axisValue, kMin_Pitch, kMax_Pitch);
    
    error = ISpElement_GetSimpleState(gInputElements[kNeed_Yaw], &axisValue);
    if (!error) gameState->yawInput = ISpAxisToSampleAxis (axisValue, kMin_Yaw, kMax_Yaw);
    
    error = ISpElement_GetSimpleState(gInputElements[kNeed_Throttle], &axisValue);
    if (!error) gameState->throttleInput = ISpAxisToSampleAxis (axisValue, kMin_Throttle, kMax_Throttle);
}
    
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_Suspend
OSStatus Input_Suspend (void)
{
    return ISpSuspend ();
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_Resume
OSStatus Input_Resume (void)
{
    return ISpResume ();
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_ShowConfigureDialog
void    Input_ShowConfigureDialog (void)
{
    // we want to configure the mouse and keyboard, so they must be on before configure
    Input_DisableKeyboardForTyping();
    Input_DisableMouseForCursor();
 
    ISpConfigure (&Input_ISpConfigureEventProc);
    
    // restore the mouse and keyboard, so we can use for MacOS things
    Input_EnableKeyboardForTyping();
    Input_EnableMouseForCursor();
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_ISpConfigureEventProc (private)
Boolean  Input_ISpConfigureEventProc (EventRecord * inEvent)
{
#pragma unused(inEvent)
    // maintain network connections, etc here
    
    return false;
}
 
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_GetButtonEvents
void    Input_GetButtonEvents (Input_GameState * gameState)
{
    OSStatus            error = noErr;
    ISpElementEvent     event;
    Boolean             wasEvent;
    
    
    // give time to some non-interrupt driven input drivers (like speech recognition)
    ISpTickle ();
    
    // get all pending events
    do
    {
        error = ISpElementList_GetNextEvent (gEventsElementList, sizeof (event), &event, &wasEvent);
        
        if (wasEvent && !error)
        {
            switch (event.refCon)
            {
                case kNeed_FireWeapon:
                    if (event.data == kISpButtonDown)
                    {
                        gameState->fireWeaponState = true;
                        gameState->fireWeaponCount++;
                    }
                    else // (event.data == kISpButtonUp)
                        gameState->fireWeaponState = false;
                    break;
                    
                case kNeed_StartPause:
                    if (event.data == kISpButtonDown)
                    {
                        if (!gameState->gameInProgress)
                            gameState->gameInProgress = true;
                        else
                            gameState->gamePaused = !gameState->gamePaused;
                    }
                    break;
                    
                case kNeed_NextWeapon:
                    if (event.data == kISpButtonDown)
                    {
                        gameState->currentWeapon++;
                        if (gameState->currentWeapon >= kWeapon_WeaponCount)
                            gameState->currentWeapon = 0;
                    }
                    break;
                    
                case kNeed_PreviousWeapon:
                    if (event.data == kISpButtonDown)
                    {
                        gameState->currentWeapon--;
                        if (gameState->currentWeapon < 0)
                            gameState->currentWeapon = kWeapon_WeaponCount - 1;
                    }
                    break;
                    
                case kNeed_Weapon_MachineGun:
                    if (event.data == kISpButtonDown)
                        gameState->currentWeapon = kWeapon_MachineGun;
                    break;
                    
                case kNeed_Weapon_Cannon:
                    if (event.data == kISpButtonDown)
                        gameState->currentWeapon = kWeapon_Cannon;
                    break;
                    
                case kNeed_Weapon_Laser:
                    if (event.data == kISpButtonDown)
                        gameState->currentWeapon = kWeapon_Laser;
                    break;
                    
                case kNeed_Weapon_Missle:
                    if (event.data == kISpButtonDown)
                        gameState->currentWeapon = kWeapon_Missle;
                    break;
                    
                case kNeed_Weapon_PrecisionBomb:
                    if (event.data == kISpButtonDown)
                        gameState->currentWeapon = kWeapon_PrecisionBomb;
                    break;
                    
                case kNeed_Weapon_ClusterBomb:
                    if (event.data == kISpButtonDown)
                        gameState->currentWeapon = kWeapon_ClusterBomb;
                    break;
                    
                case kNeed_Quit:
                    gameState->gameInProgress = false;
                    break;
            }
        }
    }
    while (wasEvent && !error);
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_PollAxisValues
void    Input_PollAxisValues (Input_GameState * gameState)
{
    Input_GetRoll (gameState);
    Input_GetPitch (gameState);
    Input_GetYaw (gameState);
    Input_GetThrottle (gameState);
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_GetRoll
void    Input_GetRoll (Input_GameState * gameState)
{
    OSStatus            error = noErr;
    ISpElementEvent     event;
    Boolean             wasEvent;
    ISpAxisData         axisValue;
    SInt32              rollValue = gameState->rollInput;
    
    // we check the axis, to see if _it_ was moved, if so, we use that value
    error = ISpElement_GetNextEvent (gInputElements[kNeed_Roll], sizeof (event), &event, &wasEvent);
    if (!error && wasEvent)
    {
        error = ISpElement_GetSimpleState(gInputElements[kNeed_Roll], &axisValue);
        if (!error) 
            rollValue = ISpAxisToSampleAxis (axisValue, kMin_Roll, kMax_Roll);
 
        ISpElement_Flush(gInputElements[kNeed_Roll]);
    }
    
    gameState->rollInput = rollValue;
    
    //¥ also check the delta values
    gameState->deltaRoll = 0;
    do
    {
        error = ISpElement_GetNextEvent (gInputElements[kNeed_Roll_AsDelta], sizeof (event), &event, &wasEvent);
        if (wasEvent && !error)
            gameState->deltaRoll += (Fixed) event.data;
    }
    while (wasEvent && !error);
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_GetPitch
void    Input_GetPitch (Input_GameState * gameState)
{
    OSStatus            error = noErr;
    ISpElementEvent     event;
    Boolean             wasEvent;
    ISpAxisData         axisValue;
    SInt32              pitchValue = gameState->pitchInput;
 
    // we check the axis, to see if _it_ was moved, if so, we use that value
    error = ISpElement_GetNextEvent (gInputElements[kNeed_Pitch], sizeof (event), &event, &wasEvent);
    if (!error && wasEvent)
    {
        error = ISpElement_GetSimpleState(gInputElements[kNeed_Pitch], &axisValue);
        if (!error) 
            pitchValue = ISpAxisToSampleAxis (axisValue, kMin_Pitch, kMax_Pitch);
 
        ISpElement_Flush(gInputElements[kNeed_Pitch]);
    }
    
    gameState->pitchInput = pitchValue;
    
    //¥ also check the delta values
    gameState->deltaPitch = 0;
    do
    {
        error = ISpElement_GetNextEvent (gInputElements[kNeed_Pitch_AsDelta], sizeof (event), &event, &wasEvent);
        if (wasEvent && !error)
            gameState->deltaPitch += (Fixed) event.data;
    }
    while (wasEvent && !error);
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_GetYaw
void    Input_GetYaw (Input_GameState * gameState)
{
    OSStatus            error = noErr;
    ISpElementEvent     event;
    Boolean             wasEvent;
    ISpAxisData         axisValue;
    SInt32              yawValue = gameState->yawInput;
    
    // we check the axis, to see if _it_ was moved, if so, we use that value
    error = ISpElement_GetNextEvent (gInputElements[kNeed_Yaw], sizeof (event), &event, &wasEvent);
    if (!error && wasEvent)
    {
        // we wish to ignore all button presses _prior_ to this moment
        ISpElementList_Flush(gYawElementList);
 
        // get the current value
        error = ISpElement_GetSimpleState(gInputElements[kNeed_Yaw], &axisValue);
        if (!error) 
            yawValue = ISpAxisToSampleAxis (axisValue, kMin_Yaw, kMax_Yaw);
 
        ISpElement_Flush(gInputElements[kNeed_Yaw]);
    }
    // otherwise, we check to see if one of the yaw buttons was pressed
    else do
    {
        error = ISpElementList_GetNextEvent (gYawElementList, sizeof (event), &event, &wasEvent);
        
        // only process valid keydown events (all the yaw events ignore button ups)
        if (wasEvent && !error && (event.data == kISpButtonDown))
        {
            switch (event.refCon)
            {
                case kNeed_Yaw_Left:
                    yawValue -= kIncrement_Yaw;
                    if (yawValue < kMin_Yaw) yawValue = kMin_Yaw; 
                    break;
                case kNeed_Yaw_Center:
                    yawValue = kMin_Yaw + ((kMax_Yaw - kMin_Yaw) / 2);
                    break;
                case kNeed_Yaw_Right:
                    yawValue += kIncrement_Yaw;
                    if (yawValue > kMax_Yaw) yawValue = kMax_Yaw; 
                    break;
            }
        }
    }
    while (wasEvent && !error);
    
    gameState->yawInput = yawValue;
    
    //¥ also check the delta values
    gameState->deltaYaw = 0;
    do
    {
        error = ISpElement_GetNextEvent (gInputElements[kNeed_Yaw_AsDelta], sizeof (event), &event, &wasEvent);
        if (wasEvent && !error)
            gameState->deltaYaw += (Fixed) event.data;
    }
    while (wasEvent && !error);
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_GetThrottle
void    Input_GetThrottle (Input_GameState * gameState)
{
    OSStatus            error = noErr;
    ISpElementEvent     event;
    Boolean             wasEvent;
    ISpAxisData         axisValue;
    SInt32              throttleValue = gameState->throttleInput;
    
    // we check the axis, to see if _it_ was moved, if so, we use that value
    error = ISpElement_GetNextEvent (gInputElements[kNeed_Throttle], sizeof (event), &event, &wasEvent);
    if (!error && wasEvent)
    {
        // we wish to ignore all button presses _prior_ to this moment
        ISpElementList_Flush(gThrottleElementList);
 
        // get the current value
        error = ISpElement_GetSimpleState(gInputElements[kNeed_Throttle], &axisValue);
        if (!error) 
            throttleValue = ISpAxisToSampleAxis (axisValue, kMin_Throttle, kMax_Throttle);
 
        ISpElement_Flush(gInputElements[kNeed_Throttle]);
    }
    // otherwise, we check to see if one of the throttle buttons was pressed
    else do
    {
        error = ISpElementList_GetNextEvent (gThrottleElementList, sizeof (event), &event, &wasEvent);
        
        // only process valid keydown events (all the throttle events ignore button ups)
        if (wasEvent && !error && (event.data == kISpButtonDown))
        {
            switch (event.refCon)
            {
                case kNeed_Throttle_Min:
                    throttleValue = kMin_Throttle;
                    break;
                case kNeed_Throttle_Decrease:
                    throttleValue -= kIncrement_Throttle;
                    if (throttleValue < kMin_Throttle) throttleValue = kMin_Throttle; 
                    break;
                case kNeed_Throttle_Increase:
                    throttleValue += kIncrement_Throttle;
                    if (throttleValue > kMax_Throttle) throttleValue = kMax_Throttle; 
                    break;
                case kNeed_Throttle_Max:
                    throttleValue = kMax_Throttle;
                    break;
            }
        }
    }
    while (wasEvent && !error);
    
    gameState->throttleInput = throttleValue;
}
 
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_EnableKeyboardForTyping
void    Input_EnableKeyboardForTyping (void)
{
    // Note, deactivating inside ISp means we can use for normal MacOS
    if (!gKeyboardTypingEnabled)
        ISpDevices_DeactivateClass (kISpDeviceClass_Keyboard);
    
    gKeyboardTypingEnabled = true;
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_DisableKeyboardForTyping
void    Input_DisableKeyboardForTyping (void)
{
    // Note, activating inside ISp means unavailable for normal MacOS
    if (gKeyboardTypingEnabled)
        ISpDevices_ActivateClass (kISpDeviceClass_Keyboard);
    
    gKeyboardTypingEnabled = false;
}
 
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_EnableMouseForCursor
void    Input_EnableMouseForCursor (void)
{
    // Note, deactivating inside ISp means we can use for normal MacOS
    if (!gMouseAsMacOSCursorEnabled)
        ISpDevices_DeactivateClass (kISpDeviceClass_Mouse);
    
    gMouseAsMacOSCursorEnabled = true;
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    Input_DisableMouseForCursor
void    Input_DisableMouseForCursor (void)
{
    // Note, activating inside ISp means unavailable for normal MacOS
    if (gMouseAsMacOSCursorEnabled)
        ISpDevices_ActivateClass (kISpDeviceClass_Mouse);
    
    gMouseAsMacOSCursorEnabled = false;
}
 
//¥ ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ    ISpAxisToSampleAxis
SInt32  ISpAxisToSampleAxis (ISpAxisData axisValue, SInt32 min, SInt32 max)
{
    UInt32 divisor = kISpAxisMaximum / (max - min);
    
    return ((axisValue / divisor) + min);
}