DZInput.c

/*
    File:       DZInput.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:
 
        (BWS)   Brent Schorsch
        (sjb)   Steve Bollinger
 
    Change History (most recent first):
 
      <SP15>    10/20/98    BWS     Pause and Quit are now separate needs
        <14>     6/18/98    sjb     InputSprocket.h comes from <> place
*/
 
/*
 *  File:       DZInput.c
 *
 *  Contents:   Handles input devices.
 *
 *  Copyright © 1996 Apple Computer, Inc.
 */
 
#include <TextUtils.h>
 
#define USE_OLD_INPUT_SPROCKET_LABELS 0
#define USE_OLD_ISPNEED_STRUCT 0
#include <InputSprocket.h>
 
#include "DZGame.h"
#include "DZInput.h"
#include "DZResource.h"
 
#define USE_MOUSE_AND_KEYBOARD 1
 
#define     ISpSymmetricAxisToFloat(axis)   ((((float) axis) - kISpAxisMiddle) / (kISpAxisMaximum-kISpAxisMiddle))
#define     ISpAsymmetricAxisToFloat(axis)  (((float) axis) / (kISpAxisMaximum))
 
/*
enum {
    kElement_Fire,
    kElement_Pause,
    kElement_ShowHUD,
    kElement_ShowFPS,
    kElement_Turn,
    kElement_COUNT
};
*/
 
enum
{
    kNeed_Fire,
    kNeed_Roll,
    kNeed_Pitch,
    kNeed_Throttle,
    kNeed_ThrottleUp,
    kNeed_ThrottleDown,
    kNeed_ThrottleFull,
    kNeed_ThrottleZero,
    kNeed_InertialDampers,
    kNeed_Yaw,
    kNeed_YawLeft,
    kNeed_YawCenter,
    kNeed_YawRight,
    kNeed_ShowHUD,
    kNeed_ShowFPS,
    kNeed_ShowThrottle,
    kNeed_ShowVelocity,
    kNeed_InstantStop,
    kNeed_Pause,
    kNeed_COUNT
};
 
#define     kNeedsVersion       1
 
static Boolean                  gInputActive = false;
static ISpElementListReference  gInputEventList = NULL;
static ISpElementListReference  gInputHoldDownEventList = NULL;
static ISpElementListReference  gInputYawEventList = NULL;
static ISpElementListReference  gInputThrottleEventList = NULL;
static ISpElementReference      gInputElement[kNeed_COUNT] = {NULL, NULL, NULL, NULL, NULL};
 
 
/* =============================================================================
 *      Input_Init (external)
 *
 *  Initializes the Input stuff.
 * ========================================================================== */
void Input_Init(
    void)
{
    UInt32      itr;
                    
    ISpNeed     needs[kNeed_COUNT] =
    {
        { "\pFire", kIconSuiteID_Fire, 0, 0,
            kISpElementKind_Button,
            kISpElementLabel_Btn_Fire,
            0, 0, 0, 0
        },
        { "\pRoll", kIconSuiteID_Roll, 0, 0,
            kISpElementKind_Axis,
            kISpElementLabel_Axis_Roll,
            0, 0, 0, 0
        },
        { "\pPitch", kIconSuiteID_Pitch, 0, 0,
            kISpElementKind_Axis,
            kISpElementLabel_Axis_Pitch,
            0, 0, 0, 0
        },
        { "\pThrottle", kIconSuiteID_Throttle, 0, 1,
            kISpElementKind_Axis,
            kISpElementLabel_Axis_Throttle,
            kISpNeedFlag_Axis_AlreadyButton | kISpNeedFlag_Axis_Asymetric, 0, 0, 0
        },
        { "\pIncrease Throttle", kIconSuiteID_ThrottleUp, 0, 1,
            kISpElementKind_Button,
            kISpElementLabel_None,
            kISpNeedFlag_Button_AlreadyAxis, 0, 0, 0
        },
        { "\pDecrease Throttle", kIconSuiteID_ThrottleDown, 0, 1,
            kISpElementKind_Button,
            kISpElementLabel_None,
            kISpNeedFlag_Button_AlreadyAxis, 0, 0, 0
        },
        { "\pThrottle Max", kIconSuiteID_ThrottleMax, 0, 1,
            kISpElementKind_Button,
            kISpElementLabel_None,
            kISpNeedFlag_Button_AlreadyAxis, 0, 0, 0
        },
        { "\pThrottle Min", kIconSuiteID_ThrottleMin, 0, 1,
            kISpElementKind_Button,
            kISpElementLabel_None,
            kISpNeedFlag_Button_AlreadyAxis, 0, 0, 0
        },
        { "\pInertial Dampers", kIconSuiteID_InertialDampers, 0, 0,
            kISpElementKind_Button,
            kISpElementLabel_None,
            0, 0, 0, 0
        },
        { "\pRudder", kIconSuiteID_Rudder, 0, 2,
            kISpElementKind_Axis,
            kISpElementLabel_Axis_Rudder,
            kISpNeedFlag_Axis_AlreadyButton, 0, 0, 0
        },
        { "\pYaw Left", kIconSuiteID_RudderLeft, 0, 2,
            kISpElementKind_Button,
            kISpElementLabel_Btn_LookLeft,
            kISpNeedFlag_Button_AlreadyAxis, 0, 0, 0
        },
        { "\pYaw Center", kIconSuiteID_RudderCenter, 0, 2,
            kISpElementKind_Button,
            kISpElementLabel_None,
            kISpNeedFlag_Button_AlreadyAxis, 0, 0, 0
        },
        { "\pYaw Right", kIconSuiteID_RudderRight, 0, 2,
            kISpElementKind_Button,
            kISpElementLabel_Btn_LookRight,
            kISpNeedFlag_Button_AlreadyAxis, 0, 0, 0
        },
        { "\pShow HUD", kIconSuiteID_ShowHUD, 0, 0,
            kISpElementKind_Button,
            kISpElementLabel_None,
            0, 0, 0, 0
        },
        { "\pShow FPS", kIconSuiteID_ShowFPS, 0, 0,
            kISpElementKind_Button,
            kISpElementLabel_None,
            0, 0, 0, 0
        },
        { "\pShow Throttle", kIconSuiteID_ShowThrottle, 0, 0,
            kISpElementKind_Button,
            kISpElementLabel_None,
            0, 0, 0, 0
        },
        { "\pShow Velocity", kIconSuiteID_ShowVelocity, 0, 0,
            kISpElementKind_Button,
            kISpElementLabel_None,
            0, 0, 0, 0
        },
        { "\pInstant Stop", kIconSuiteID_InstantStop, 0, 0,
            kISpElementKind_Button,
            kISpElementLabel_None,
            0, 0, 0, 0
        },
        { "\pPause", kIconSuiteID_Pause, 0, 0,
            kISpElementKind_Button,
            kISpElementLabel_Btn_StartPause,
            0, 0, 0, 0
        }
    };
    
    // Get the names for the needs
    for (itr = 0; itr < kNeed_COUNT; itr++)
        GetIndString(needs[itr].name,       kStrListID_NeedsNames,  itr+1);
    
    #if USE_MOUSE_AND_KEYBOARD
        
        // Enable the mouse
        ISpDevices_ActivateClass (kISpDeviceClass_Mouse);
        
        // Enable the keyboard
        ISpDevices_ActivateClass (kISpDeviceClass_Keyboard);
    #endif
    
    // Set our virtual elements
    ISpElement_NewVirtualFromNeeds(kNeed_COUNT, needs, gInputElement, 0);
    
    // Autoconfigure our virtual elements based on our needs
    ISpInit(kNeed_COUNT, needs, gInputElement, ' dz ', kNeedsVersion, 0, kSetListID, 0);
    
    // Build two lists of elements, which we'll poll for events 
    // the 'hold down' list reports both up and down events
    ISpElementList_New(0, NULL, &gInputEventList, 0);
    ISpElementList_New(0, NULL, &gInputHoldDownEventList, 0);
    
    // Add the virtual elements one at a time so we can assign a refcon
    ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_Fire_On,1, &gInputElement[kNeed_Fire]);
 
    ISpElementList_AddElements(gInputHoldDownEventList, kInputEvent_InertialDampers_On,
                                                                            1, &gInputElement[kNeed_InertialDampers]);
    
    ISpElementList_AddElements(gInputEventList, kInputEvent_InstantStop,    1, &gInputElement[kNeed_InstantStop]);
 
    ISpElementList_AddElements(gInputEventList, kInputEvent_ShowHUD,        1, &gInputElement[kNeed_ShowHUD]);
    ISpElementList_AddElements(gInputEventList, kInputEvent_ShowFPS,        1, &gInputElement[kNeed_ShowFPS]);
    ISpElementList_AddElements(gInputEventList, kInputEvent_ShowThrottle,   1, &gInputElement[kNeed_ShowThrottle]);
    ISpElementList_AddElements(gInputEventList, kInputEvent_ShowVelocity,   1, &gInputElement[kNeed_ShowVelocity]);
    
    ISpElementList_AddElements(gInputEventList, kInputEvent_Pause,          1, &gInputElement[kNeed_Pause]);
 
 
    // Build our list of elements used for the rudder (yaw) when from buttons
    ISpElementList_New(0, NULL, &gInputYawEventList, 0);
    
    // Add the virtual elements one at a time so we can assign a refcon
    ISpElementList_AddElements(gInputYawEventList, kNeed_YawLeft, 1, &gInputElement[kNeed_YawLeft]);
    ISpElementList_AddElements(gInputYawEventList, kNeed_YawCenter, 1, &gInputElement[kNeed_YawCenter]);
    ISpElementList_AddElements(gInputYawEventList, kNeed_YawRight, 1, &gInputElement[kNeed_YawRight]);
 
 
    // Build our list of elements used for the throttle when from buttons
    ISpElementList_New(0, NULL, &gInputThrottleEventList, 0);
    
    // Add the virtual elements one at a time so we can assign a refcon
    ISpElementList_AddElements(gInputThrottleEventList, kNeed_ThrottleUp, 1, &gInputElement[kNeed_ThrottleUp]);
    ISpElementList_AddElements(gInputThrottleEventList, kNeed_ThrottleDown, 1, &gInputElement[kNeed_ThrottleDown]);
    ISpElementList_AddElements(gInputThrottleEventList, kNeed_ThrottleFull, 1, &gInputElement[kNeed_ThrottleFull]);
    ISpElementList_AddElements(gInputThrottleEventList, kNeed_ThrottleZero, 1, &gInputElement[kNeed_ThrottleZero]);
 
    // Start off suspended (because the game is stopped)
    ISpSuspend();
}
 
 
/* =============================================================================
 *      Input_Exit (external)
 *
 *  Prepares for exit.
 * ========================================================================== */
void Input_Exit(
    void)
{
    if (gInputActive)
    {
        Input_Activate(false);
    }
    
    if (gInputEventList != NULL)
    {
        ISpElementList_Dispose(gInputEventList);
        gInputEventList = NULL;
    }
    
    if (gInputHoldDownEventList != NULL)
    {
        ISpElementList_Dispose(gInputHoldDownEventList);
        gInputHoldDownEventList = NULL;
    }
    
    ISpStop();
    
    ISpElement_DisposeVirtual(kNeed_COUNT, gInputElement);
}
 
 
/* =============================================================================
 *      Input_Configure (external)
 *
 *  Show the configuration dialog.
 * ========================================================================== */
void Input_Configure(
    void)
{
    ISpConfigure(NULL);
}
 
#if 0
/* =============================================================================
 *      Input_GetState (external)
 *
 *  This routine handles the elements that are polled.  It returns in outXTurn
 *  and outYTurn the "turn" controls in the ±1 range.
 * ========================================================================== */
void Input_GetState(
    float*              outXTurn,
    float*              outYTurn)
{
    ISpMovementData     movementData;
    double              xTurn = 0.0;
    double              yTurn = 0.0;
    
    if (gInputActive)
    {
        // Get the data
        ISpElement_GetComplexState(
                gInputElement[kNeed_Turn],
                sizeof(ISpMovementData),
                &movementData);
        
        // Turn 'em into ±1 float range
        xTurn = movementData.xAxis;
        xTurn -= kISpAxisMiddle;
        xTurn /= kISpAxisMaximum-kISpAxisMiddle;
        
        yTurn = movementData.yAxis;
        yTurn -= kISpAxisMiddle;
        yTurn /= kISpAxisMaximum-kISpAxisMiddle;
        yTurn *= -1;    // reverse axis
    }
    
    // Return the values
    *outXTurn = xTurn;
    *outYTurn = yTurn;
}
#endif
 
float Input_GetRoll (void)
{
    ISpAxisData         axisValue = kISpAxisMiddle;
    
    (void) ISpElement_GetSimpleState(gInputElement[kNeed_Roll], &axisValue);
    return ISpSymmetricAxisToFloat (axisValue);
}
 
float Input_GetPitch (void)
{
    ISpAxisData         axisValue = kISpAxisMiddle;
    
    (void) ISpElement_GetSimpleState(gInputElement[kNeed_Pitch], &axisValue);
    return (-1.0 * ISpSymmetricAxisToFloat (axisValue));
}
 
float Input_GetYaw (void)
{
    static  float       gYawValue = 0.0;
    static  Boolean     gYawInitalized = false;
    
    float               yawValue = gYawValue;
    OSStatus            error = noErr;
    ISpAxisData         axisValue;
    Boolean             wasEvent;
    ISpElementEvent     event;
    
    // should probably put the initialization check somewhere else, so does not do it every time
    if (!gYawInitalized)
    {
        (void) ISpElement_GetSimpleState(gInputElement[kNeed_Yaw], &axisValue);
        gYawValue = ISpSymmetricAxisToFloat (axisValue);
 
        gYawInitalized = true;
    }
        
    // we check the axis, to see if IT was moved, if so, we use that value
    wasEvent = false;
    error = ISpElement_GetNextEvent (gInputElement[kNeed_Yaw], sizeof (event), &event, &wasEvent);
    if (!error && wasEvent)
    {
        error = ISpElement_GetSimpleState(gInputElement[kNeed_Yaw], &axisValue);
        if (!error) yawValue = ISpSymmetricAxisToFloat (axisValue);
 
        ISpElement_Flush(gInputElement[kNeed_Yaw]);
    }
    else do
    {
        error = ISpElementList_GetNextEvent (gInputYawEventList, sizeof (event), &event, &wasEvent);
        
        // only process valid keydown events (all the yaw events ignore button ups)
        if (wasEvent && !error && (event.data == kISpButtonDown))
        {
            UInt32 keyFunction = event.refCon; // because we set it this way
            switch (keyFunction)
            {
                case kNeed_YawLeft:
                    yawValue -= 0.1;
                    if (yawValue < -1.0) yawValue = -1.0; 
                    break;
                case kNeed_YawCenter:
                    yawValue = 0.0;
                    break;
                case kNeed_YawRight:
                    yawValue += 0.1;
                    if (yawValue > 1.0) yawValue = 1.0; 
                    break;
            }
        }
    }
    while (wasEvent && !error);
    
    gYawValue = yawValue;
    
    return yawValue;
}
 
float Input_GetThrottle (void)
{
    static  float       gThrottleValue = 0.0;
    static  Boolean     gThrottleInitialized = false;
    
    float               throttleValue = gThrottleValue;
    OSStatus            error = noErr;
    ISpAxisData         axisValue;
    Boolean             wasEvent;
    ISpElementEvent     event;
    
    // should probably put the initialization check somewhere else, so does not do it every time
    if (!gThrottleInitialized)
    {
        (void) ISpElement_GetSimpleState(gInputElement[kNeed_Throttle], &axisValue);
        gThrottleValue = ISpAsymmetricAxisToFloat (axisValue);
 
        gThrottleInitialized = true;
    }
    
    // we check the axis, to see if IT was moved, if so, we use that value
    wasEvent = false;
    error = ISpElement_GetNextEvent (gInputElement[kNeed_Throttle], sizeof (event), &event, &wasEvent);
    if (!error && wasEvent)
    {
        error = ISpElement_GetSimpleState(gInputElement[kNeed_Throttle], &axisValue);
        if (!error) throttleValue = ISpAsymmetricAxisToFloat (axisValue);
 
        ISpElement_Flush(gInputElement[kNeed_Throttle]);
    }
    else do
    {
        error = ISpElementList_GetNextEvent (gInputThrottleEventList, sizeof (event), &event, &wasEvent);
        
        // only process valid keydown events (all the throttle events ignore button ups)
        if (wasEvent && !error && (event.data == kISpButtonDown))
        {
            UInt32 keyFunction = event.refCon; // because we set it this way
            switch (keyFunction)
            {
                case kNeed_ThrottleUp:
                    throttleValue += 0.05;
                    if (throttleValue > 1.0) throttleValue = 1.0; 
                    break;
                case kNeed_ThrottleDown:
                    throttleValue -= 0.05;
                    if (throttleValue < 0.0) throttleValue = 0.0; 
                    break;
                case kNeed_ThrottleFull:
                    throttleValue = 1.0;
                    break;
                case kNeed_ThrottleZero:
                    throttleValue = 0.0;
                    break;
            }
        }
    }
    while (wasEvent && !error);
    
    gThrottleValue = throttleValue;
    
    return throttleValue;
}
 
 
 
/* =============================================================================
 *      Input_GetEvent (external)
 *
 *  This routine handles the elements that are event-based.  It returns an
 *  event code indicating which button has gone down.  Note that we ignore
 *  button-up transitions.  Should be called repeatedly until it return
 *  kInputEvent_None.
 * ========================================================================== */
TInputEvent Input_GetEvent(
    void)
{
    OSErr               err;
    TInputEvent         result = kInputEvent_None;
    ISpElementEvent     event;
    Boolean             gotEvent;
    
    if (gInputActive)
    {
        err = ISpElementList_GetNextEvent(gInputHoldDownEventList, sizeof(event), &event, &gotEvent);
        if (err == noErr && gotEvent)
        {
            result = (TInputEvent) event.refCon;
            if (event.data == kISpButtonUp)
                result += 1;    // Note: we rely on off being on+1 (ie kInputEvent_InertialDampers_Off == kInputEvent_InertialDampers_On + 1)
        }
        else
        {
            err = ISpElementList_GetNextEvent(gInputEventList, sizeof(event), &event, &gotEvent);
            
            if (err == noErr && gotEvent && event.data == kISpButtonDown)
                result = (TInputEvent) event.refCon;
        }
    }
    
    return result;
}
 
 
/* =============================================================================
 *      Input_Activate (external)
 *
 *  On deactivation, we suspend InputSprocket.  On activation we resume it and
 *  flush the event queue.  We only allow activation when the game is in "Play"
 *  state.
 * ========================================================================== */
void Input_Activate(
    Boolean             inActivate)
{
    Boolean             doActivate;
    
    doActivate = inActivate;
    if (doActivate && Game_GetState() != kGameState_Playing)
    {
        doActivate = false;
    }
    
    if (gInputActive != doActivate)
    {
        gInputActive = doActivate;
        
        if (gInputActive)
        {
            #if USE_MOUSE_AND_KEYBOARD
                HideCursor();
            #endif
            
            ISpResume();
            ISpElementList_Flush(gInputEventList);
        }
        else
        {
            ISpSuspend();
            
            #if USE_MOUSE_AND_KEYBOARD
                ShowCursor();
            #endif
        }
    }
}