//=========== Copyright Valve Corporation, All rights reserved. ===============// // // Purpose: Native Steam Controller Interface //=============================================================================// #include "inputsystem.h" #include "key_translation.h" #include "filesystem.h" #include "steam/isteamcontroller.h" #include "math.h" #include "steam/steam_api.h" // memdbgon must be the last include file in a .cpp file!!! #include static CSteamAPIContext s_SteamAPIContext; CSteamAPIContext *steamapicontext = &s_SteamAPIContext; ConVar sc_joystick_map( "sc_joystick_map", "1", FCVAR_ARCHIVE, "How to map the analog joystick deadzone and extents 0 = Scaled Cross, 1 = Concentric Mapping to Square." ); bool CInputSystem::InitializeSteamControllers() { static bool s_bSteamControllerInitAttempted = false; // we only initialize SteamAPI once, prevent multiple calls static bool s_bSteamControllerInitSucceeded = false; // cached result from first SteamController()->Init() call if ( !s_bSteamControllerInitAttempted ) { s_bSteamControllerInitAttempted = true; SteamAPI_InitSafe(); s_SteamAPIContext.Init(); if( s_SteamAPIContext.SteamController() ) { m_flLastSteamControllerInput = -FLT_MAX; s_bSteamControllerInitSucceeded = steamapicontext->SteamController()->Init(); if ( s_bSteamControllerInitSucceeded ) { DevMsg( "Successfully Initialized Steam Controller Configuration." ); } else { DevMsg( "Failed to Initialize Steam Controller Configuration." ); } m_nJoystickBaseline = m_nJoystickCount; s_SteamAPIContext.SteamController()->ActivateActionSet( STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS, s_SteamAPIContext.SteamController()->GetActionSetHandle( "MenuControls" ) ); } } return s_bSteamControllerInitSucceeded; } struct SDigitalMenuAction { const char *strName; ButtonCode_t buttonCode; ControllerDigitalActionHandle_t handle; bool bState; bool bActive; }; static SDigitalMenuAction g_DigitalMenuActions[] = { { "menu_left", KEY_XSTICK1_LEFT, 0, false, false }, { "menu_right", KEY_XSTICK1_RIGHT, 0, false, false }, { "menu_up", KEY_XSTICK1_UP, 0, false, false }, { "menu_down", KEY_XSTICK1_DOWN, 0, false, false }, { "menu_cancel", KEY_XBUTTON_B, 0, false, false }, { "menu_select", KEY_XBUTTON_A, 0, false, false }, { "menu_x", KEY_XBUTTON_X, 0, false, false }, { "menu_y", KEY_XBUTTON_Y, 0, false, false }, { "pause_menu", KEY_ESCAPE, 0, false, false }, // Command is actually in the FPS Controls game action set. { "vote_yes", KEY_F1, 0, false, false }, // Command is actually in the FPS Controls game action set. { "vote_no", KEY_F2, 0, false, false }, // Command is actually in the FPS Controls game action set. }; static void InitDigitalMenuActions() { for ( int i = 0; i != ARRAYSIZE( g_DigitalMenuActions ); ++i ) { g_DigitalMenuActions[i].handle = steamapicontext->SteamController()->GetDigitalActionHandle( g_DigitalMenuActions[i].strName ); } } static const char *g_pSteamControllerMode = "MenuControls"; bool CInputSystem::PollSteamControllers( void ) { unsigned int unNumConnected = 0; if ( InitializeSteamControllers() ) { ISteamController& ctrl = *s_SteamAPIContext.SteamController(); ctrl.RunFrame(); ControllerHandle_t handles[MAX_STEAM_CONTROLLERS]; int nControllers = ctrl.GetConnectedControllers( handles ); SetInputDeviceConnected( INPUT_DEVICE_STEAM_CONTROLLER, nControllers > 0 ); if ( nControllers > 0 ) { static bool s_bInitialized = false; if ( s_bInitialized == false ) { ctrl.ActivateActionSet( STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS, ctrl.GetActionSetHandle( g_pSteamControllerMode ) ); InitDigitalMenuActions(); s_bInitialized = true; } for ( int i = 0; i != ARRAYSIZE( g_DigitalMenuActions ); ++i ) { SDigitalMenuAction& action = g_DigitalMenuActions[ i ]; bool bNewState = false; bool bNewActive = false; for ( uint64 j = 0; j < nControllers; ++j ) { ControllerDigitalActionData_t data = steamapicontext->SteamController()->GetDigitalActionData( handles[ j ], action.handle ); if ( data.bActive ) { bNewActive = true; } if ( data.bState && data.bActive ) { bNewState = true; } } bNewActive = bNewActive && ( action.bActive || !bNewState ); if ( action.bActive && bNewState != action.bState ) { if ( bNewState ) { SetCurrentInputDevice( INPUT_DEVICE_STEAM_CONTROLLER ); PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, action.buttonCode, action.buttonCode ); } else { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, action.buttonCode, action.buttonCode ); } } action.bState = bNewState; action.bActive = bNewActive; } } } m_unNumSteamControllerConnected = unNumConnected; return unNumConnected > 0; } bool CInputSystem::IsSteamControllerActive() const { if ( steamapicontext && steamapicontext->SteamController() ) { uint64 nControllerHandles[STEAM_CONTROLLER_MAX_COUNT]; return steamapicontext->SteamController()->GetConnectedControllers( nControllerHandles ) > 0; } return false; } static const int g_nControllerModeMaxStackSize = 16; static const void *g_pControllerModeObjectStack[g_nControllerModeMaxStackSize]; static const char *g_pControllerModeStack[g_nControllerModeMaxStackSize]; static int g_nControllerModeStackSize = 0; static const char *g_pBaseControllerMode = "MenuControls"; static const char *GetCurrentSteamControllerMode() { if ( g_nControllerModeStackSize == 0 ) { return g_pBaseControllerMode; } return g_pControllerModeStack[ g_nControllerModeStackSize-1 ]; } void CInputSystem::SetSteamControllerMode( const char *pSteamControllerMode, const void *obj ) { if ( !s_SteamAPIContext.SteamController() ) { return; } if ( obj == NULL ) { g_pBaseControllerMode = pSteamControllerMode; } else { int nIndex = 0; while ( nIndex < g_nControllerModeStackSize && g_pControllerModeObjectStack[ nIndex ] != obj ) { ++nIndex; } Assert( nIndex < g_nControllerModeMaxStackSize ); if ( nIndex == g_nControllerModeMaxStackSize ) { return; } if ( pSteamControllerMode != NULL ) { g_pControllerModeObjectStack[ nIndex ] = obj; g_pControllerModeStack[ nIndex ] = pSteamControllerMode; if ( nIndex == g_nControllerModeStackSize ) { ++g_nControllerModeStackSize; } } else if ( nIndex < g_nControllerModeStackSize ) { for ( int i = nIndex+1; i < g_nControllerModeStackSize; ++i ) { g_pControllerModeObjectStack[ i - 1 ] = g_pControllerModeObjectStack[ i ]; g_pControllerModeStack[ i - 1 ] = g_pControllerModeStack[ i ]; } --g_nControllerModeStackSize; } } const char *pNewMode = GetCurrentSteamControllerMode(); if ( V_strcmp( pNewMode, g_pSteamControllerMode ) == 0 ) { return; } g_pSteamControllerMode = pNewMode; s_SteamAPIContext.SteamController()->ActivateActionSet( STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS, s_SteamAPIContext.SteamController()->GetActionSetHandle( pNewMode ) ); } CON_COMMAND( steam_controller_status, "Spew report of steam controller status" ) { if ( !s_SteamAPIContext.SteamController() ) { Msg( "Steam controller API is unavailable" ); return; } ControllerDigitalActionHandle_t handleAction = s_SteamAPIContext.SteamController()->GetActionSetHandle( GetCurrentSteamControllerMode() ); Msg( "Steam controller mode: %s (%d)\n", GetCurrentSteamControllerMode(), (int)handleAction ); ControllerHandle_t handles[MAX_STEAM_CONTROLLERS]; int nControllers = s_SteamAPIContext.SteamController()->GetConnectedControllers( handles ); Msg( "Controllers connected: %d\n", nControllers ); for ( int i = 0; i < nControllers; ++i ) { Msg( " Controller %d, action set = %d\n", i, (int)s_SteamAPIContext.SteamController()->GetCurrentActionSet( handles[ i ] ) ); for ( int j = 0; j < STEAM_CONTROLLER_MAX_DIGITAL_ACTIONS; ++j ) { ControllerDigitalActionData_t data = s_SteamAPIContext.SteamController()->GetDigitalActionData( handles[ i ], (int)j ); if ( data.bState && data.bActive ) { Msg( " active action: %d\n", j ); } } } }