diff --git a/Facepunch.Steamworks.Test/InputTest.cs b/Facepunch.Steamworks.Test/InputTest.cs index ad108e3..2e1ed20 100644 --- a/Facepunch.Steamworks.Test/InputTest.cs +++ b/Facepunch.Steamworks.Test/InputTest.cs @@ -20,6 +20,8 @@ namespace Steamworks foreach ( var controller in SteamInput.Controllers ) { Console.Write( $"Controller: {controller}" ); + + var state = controller.GetDigitalState( "fire" ); } } } diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs index f66238d..2bc2129 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs @@ -26,17 +26,14 @@ namespace Steamworks _GetActiveActionSetLayers = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 80) ); _GetDigitalActionHandle = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 88) ); _GetDigitalActionData = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 96) ); - _GetDigitalActionData_Windows = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 96) ); _GetDigitalActionOrigins = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 104) ); _GetAnalogActionHandle = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 112) ); _GetAnalogActionData = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 120) ); - _GetAnalogActionData_Windows = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 120) ); _GetAnalogActionOrigins = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 128) ); _GetGlyphForActionOrigin = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 136) ); _GetStringForActionOrigin = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 144) ); _StopAnalogActionMomentum = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 152) ); _GetMotionData = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 160) ); - _GetMotionData_Windows = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 160) ); _TriggerVibration = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 168) ); _SetLEDColor = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 176) ); _TriggerHapticPulse = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, 184) ); @@ -67,17 +64,14 @@ namespace Steamworks _GetActiveActionSetLayers = null; _GetDigitalActionHandle = null; _GetDigitalActionData = null; - _GetDigitalActionData_Windows = null; _GetDigitalActionOrigins = null; _GetAnalogActionHandle = null; _GetAnalogActionData = null; - _GetAnalogActionData_Windows = null; _GetAnalogActionOrigins = null; _GetGlyphForActionOrigin = null; _GetStringForActionOrigin = null; _StopAnalogActionMomentum = null; _GetMotionData = null; - _GetMotionData_Windows = null; _TriggerVibration = null; _SetLEDColor = null; _TriggerHapticPulse = null; @@ -228,21 +222,12 @@ namespace Steamworks #region FunctionMeta [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate InputDigitalActionData_t FGetDigitalActionData( IntPtr self, InputHandle_t inputHandle, InputDigitalActionHandle_t digitalActionHandle ); + private delegate DigitalState FGetDigitalActionData( IntPtr self, InputHandle_t inputHandle, InputDigitalActionHandle_t digitalActionHandle ); private FGetDigitalActionData _GetDigitalActionData; - [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate InputDigitalActionData_t FGetDigitalActionData_Windows( IntPtr self, InputHandle_t inputHandle, InputDigitalActionHandle_t digitalActionHandle ); - private FGetDigitalActionData_Windows _GetDigitalActionData_Windows; #endregion - internal InputDigitalActionData_t GetDigitalActionData( InputHandle_t inputHandle, InputDigitalActionHandle_t digitalActionHandle ) + internal DigitalState GetDigitalActionData( InputHandle_t inputHandle, InputDigitalActionHandle_t digitalActionHandle ) { - if ( Config.Os == OsType.Windows ) - { - var retVal = _GetDigitalActionData_Windows( Self, inputHandle, digitalActionHandle ); - return retVal; - } - return _GetDigitalActionData( Self, inputHandle, digitalActionHandle ); } @@ -270,21 +255,12 @@ namespace Steamworks #region FunctionMeta [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate InputAnalogActionData_t FGetAnalogActionData( IntPtr self, InputHandle_t inputHandle, InputAnalogActionHandle_t analogActionHandle ); + private delegate AnalogState FGetAnalogActionData( IntPtr self, InputHandle_t inputHandle, InputAnalogActionHandle_t analogActionHandle ); private FGetAnalogActionData _GetAnalogActionData; - [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate InputAnalogActionData_t FGetAnalogActionData_Windows( IntPtr self, InputHandle_t inputHandle, InputAnalogActionHandle_t analogActionHandle ); - private FGetAnalogActionData_Windows _GetAnalogActionData_Windows; #endregion - internal InputAnalogActionData_t GetAnalogActionData( InputHandle_t inputHandle, InputAnalogActionHandle_t analogActionHandle ) + internal AnalogState GetAnalogActionData( InputHandle_t inputHandle, InputAnalogActionHandle_t analogActionHandle ) { - if ( Config.Os == OsType.Windows ) - { - var retVal = _GetAnalogActionData_Windows( Self, inputHandle, analogActionHandle ); - return retVal; - } - return _GetAnalogActionData( Self, inputHandle, analogActionHandle ); } @@ -334,21 +310,12 @@ namespace Steamworks #region FunctionMeta [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate InputMotionData_t FGetMotionData( IntPtr self, InputHandle_t inputHandle ); + private delegate MotionState FGetMotionData( IntPtr self, InputHandle_t inputHandle ); private FGetMotionData _GetMotionData; - [UnmanagedFunctionPointer( CallingConvention.ThisCall )] - private delegate InputMotionData_t FGetMotionData_Windows( IntPtr self, InputHandle_t inputHandle ); - private FGetMotionData_Windows _GetMotionData_Windows; #endregion - internal InputMotionData_t GetMotionData( InputHandle_t inputHandle ) + internal MotionState GetMotionData( InputHandle_t inputHandle ) { - if ( Config.Os == OsType.Windows ) - { - var retVal = _GetMotionData_Windows( Self, inputHandle ); - return retVal; - } - return _GetMotionData( Self, inputHandle ); } diff --git a/Facepunch.Steamworks/Generated/SteamEnums.cs b/Facepunch.Steamworks/Generated/SteamEnums.cs index 0cb5b89..32a5027 100644 --- a/Facepunch.Steamworks/Generated/SteamEnums.cs +++ b/Facepunch.Steamworks/Generated/SteamEnums.cs @@ -1100,7 +1100,7 @@ namespace Steamworks // // EInputSourceMode // - internal enum InputSourceMode : int + public enum InputSourceMode : int { None = 0, Dpad = 1, diff --git a/Facepunch.Steamworks/Generated/SteamStructs.cs b/Facepunch.Steamworks/Generated/SteamStructs.cs index bafdfbc..1763aa8 100644 --- a/Facepunch.Steamworks/Generated/SteamStructs.cs +++ b/Facepunch.Steamworks/Generated/SteamStructs.cs @@ -12508,95 +12508,6 @@ namespace Steamworks.Data #endregion } - [StructLayout( LayoutKind.Sequential, Pack = 4 )] - internal struct InputAnalogActionData_t - { - internal InputSourceMode EMode; // eMode EInputSourceMode - internal float X; // x float - internal float Y; // y float - internal byte BActive; // bActive byte - - #region Marshalling - internal static InputAnalogActionData_t Fill( IntPtr p ) => Config.PackSmall ? ((InputAnalogActionData_t)(InputAnalogActionData_t) Marshal.PtrToStructure( p, typeof(InputAnalogActionData_t) )) : ((InputAnalogActionData_t)(Pack8) Marshal.PtrToStructure( p, typeof(Pack8) )); - #endregion - #region Packed Versions - - [StructLayout( LayoutKind.Sequential, Pack = 8 )] - public struct Pack8 - { - internal InputSourceMode EMode; // eMode EInputSourceMode - internal float X; // x float - internal float Y; // y float - internal byte BActive; // bActive byte - - public static implicit operator InputAnalogActionData_t ( InputAnalogActionData_t.Pack8 d ) => new InputAnalogActionData_t{ EMode = d.EMode,X = d.X,Y = d.Y,BActive = d.BActive, }; - public static implicit operator InputAnalogActionData_t.Pack8 ( InputAnalogActionData_t d ) => new InputAnalogActionData_t.Pack8{ EMode = d.EMode,X = d.X,Y = d.Y,BActive = d.BActive, }; - } - #endregion - } - - [StructLayout( LayoutKind.Sequential, Pack = 4 )] - internal struct InputMotionData_t - { - internal float RotQuatX; // rotQuatX float - internal float RotQuatY; // rotQuatY float - internal float RotQuatZ; // rotQuatZ float - internal float RotQuatW; // rotQuatW float - internal float PosAccelX; // posAccelX float - internal float PosAccelY; // posAccelY float - internal float PosAccelZ; // posAccelZ float - internal float RotVelX; // rotVelX float - internal float RotVelY; // rotVelY float - internal float RotVelZ; // rotVelZ float - - #region Marshalling - internal static InputMotionData_t Fill( IntPtr p ) => Config.PackSmall ? ((InputMotionData_t)(InputMotionData_t) Marshal.PtrToStructure( p, typeof(InputMotionData_t) )) : ((InputMotionData_t)(Pack8) Marshal.PtrToStructure( p, typeof(Pack8) )); - #endregion - #region Packed Versions - - [StructLayout( LayoutKind.Sequential, Pack = 8 )] - public struct Pack8 - { - internal float RotQuatX; // rotQuatX float - internal float RotQuatY; // rotQuatY float - internal float RotQuatZ; // rotQuatZ float - internal float RotQuatW; // rotQuatW float - internal float PosAccelX; // posAccelX float - internal float PosAccelY; // posAccelY float - internal float PosAccelZ; // posAccelZ float - internal float RotVelX; // rotVelX float - internal float RotVelY; // rotVelY float - internal float RotVelZ; // rotVelZ float - - public static implicit operator InputMotionData_t ( InputMotionData_t.Pack8 d ) => new InputMotionData_t{ RotQuatX = d.RotQuatX,RotQuatY = d.RotQuatY,RotQuatZ = d.RotQuatZ,RotQuatW = d.RotQuatW,PosAccelX = d.PosAccelX,PosAccelY = d.PosAccelY,PosAccelZ = d.PosAccelZ,RotVelX = d.RotVelX,RotVelY = d.RotVelY,RotVelZ = d.RotVelZ, }; - public static implicit operator InputMotionData_t.Pack8 ( InputMotionData_t d ) => new InputMotionData_t.Pack8{ RotQuatX = d.RotQuatX,RotQuatY = d.RotQuatY,RotQuatZ = d.RotQuatZ,RotQuatW = d.RotQuatW,PosAccelX = d.PosAccelX,PosAccelY = d.PosAccelY,PosAccelZ = d.PosAccelZ,RotVelX = d.RotVelX,RotVelY = d.RotVelY,RotVelZ = d.RotVelZ, }; - } - #endregion - } - - [StructLayout( LayoutKind.Sequential, Pack = 4 )] - internal struct InputDigitalActionData_t - { - internal byte BState; // bState byte - internal byte BActive; // bActive byte - - #region Marshalling - internal static InputDigitalActionData_t Fill( IntPtr p ) => Config.PackSmall ? ((InputDigitalActionData_t)(InputDigitalActionData_t) Marshal.PtrToStructure( p, typeof(InputDigitalActionData_t) )) : ((InputDigitalActionData_t)(Pack8) Marshal.PtrToStructure( p, typeof(Pack8) )); - #endregion - #region Packed Versions - - [StructLayout( LayoutKind.Sequential, Pack = 8 )] - public struct Pack8 - { - internal byte BState; // bState byte - internal byte BActive; // bActive byte - - public static implicit operator InputDigitalActionData_t ( InputDigitalActionData_t.Pack8 d ) => new InputDigitalActionData_t{ BState = d.BState,BActive = d.BActive, }; - public static implicit operator InputDigitalActionData_t.Pack8 ( InputDigitalActionData_t d ) => new InputDigitalActionData_t.Pack8{ BState = d.BState,BActive = d.BActive, }; - } - #endregion - } - [StructLayout( LayoutKind.Sequential, Pack = 4 )] internal struct SteamInventoryDefinitionUpdate_t { diff --git a/Facepunch.Steamworks/SteamClient.cs b/Facepunch.Steamworks/SteamClient.cs index c0f3969..2fbee51 100644 --- a/Facepunch.Steamworks/SteamClient.cs +++ b/Facepunch.Steamworks/SteamClient.cs @@ -87,6 +87,8 @@ namespace Steamworks initialized = false; + SteamInput.Shutdown(); + ShutdownInterfaces(); SteamApps.Shutdown(); SteamUtils.Shutdown(); @@ -103,7 +105,7 @@ namespace Steamworks SteamParties.Shutdown(); SteamNetworkingUtils.Shutdown(); SteamNetworkingSockets.Shutdown(); - SteamInput.Shutdown(); + ServerList.Base.Shutdown(); SteamAPI.Shutdown(); diff --git a/Facepunch.Steamworks/SteamInput.cs b/Facepunch.Steamworks/SteamInput.cs index 764b89f..c3c088e 100644 --- a/Facepunch.Steamworks/SteamInput.cs +++ b/Facepunch.Steamworks/SteamInput.cs @@ -1,15 +1,11 @@ -using System; +using Steamworks.Data; using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using Steamworks.Data; namespace Steamworks { public static class SteamInput { - internal const int STEAM_CONTROLLER_MAX_COUNT = 16; + internal const int STEAM_CONTROLLER_MAX_COUNT = 16; static ISteamInput _internal; internal static ISteamInput Internal @@ -40,11 +36,20 @@ namespace Steamworks // None? } + /// + /// You shouldn't really need to call this because it get called by RunCallbacks on SteamClient + /// but Valve think it might be a nice idea if you call it right before you get input info - + /// just to make sure the info you're getting is 100% up to date. + /// + public static void RunFrame() + { + Internal.RunFrame(); + } static InputHandle_t[] queryArray = new InputHandle_t[STEAM_CONTROLLER_MAX_COUNT]; /// - /// Return a list of connected controllers. Will return null if none found. + /// Return a list of connected controllers. /// public static IEnumerable Controllers { @@ -56,9 +61,38 @@ namespace Steamworks { yield return new Controller( queryArray[i] ); } - } } + internal static Dictionary DigitalHandles = new Dictionary(); + internal static InputDigitalActionHandle_t GetDigitalActionHandle( string name ) + { + if ( DigitalHandles.TryGetValue( name, out var val ) ) + return val; + + val = Internal.GetDigitalActionHandle( name ); + DigitalHandles.Add( name, val ); + return val; + } + + internal static Dictionary AnalogHandles = new Dictionary(); + internal static InputAnalogActionHandle_t GetAnalogActionHandle( string name ) + { + if ( AnalogHandles.TryGetValue( name, out var val ) ) + return val; + + val = Internal.GetAnalogActionHandle( name ); + AnalogHandles.Add( name, val ); + return val; + } + + /// + /// Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls. + /// + public static ActionSet GetActionSet( string name ) + { + return new ActionSet( Internal.GetActionSetHandle( name ) ); + } + } } \ No newline at end of file diff --git a/Facepunch.Steamworks/Structs/ActionSet.cs b/Facepunch.Steamworks/Structs/ActionSet.cs new file mode 100644 index 0000000..2aa5400 --- /dev/null +++ b/Facepunch.Steamworks/Structs/ActionSet.cs @@ -0,0 +1,15 @@ +using Steamworks.Data; +using System.Collections.Generic; + +namespace Steamworks +{ + public struct ActionSet + { + internal InputActionSetHandle_t Handle; + + internal ActionSet( InputActionSetHandle_t handle ) + { + this.Handle = handle; + } + } +} \ No newline at end of file diff --git a/Facepunch.Steamworks/Structs/Controller.cs b/Facepunch.Steamworks/Structs/Controller.cs index 35bcbf8..6886b74 100644 --- a/Facepunch.Steamworks/Structs/Controller.cs +++ b/Facepunch.Steamworks/Structs/Controller.cs @@ -1,18 +1,96 @@ using Steamworks.Data; +using System.Collections.Generic; +using System.Runtime.InteropServices; namespace Steamworks { public struct Controller { - private InputHandle_t inputHandle; + internal InputHandle_t Handle; internal Controller( InputHandle_t inputHandle_t ) { - this.inputHandle = inputHandle_t; + this.Handle = inputHandle_t; } - public InputType InputType => SteamInput.Internal.GetInputTypeForHandle( inputHandle ); + public ulong Id => Handle.Value; + public InputType InputType => SteamInput.Internal.GetInputTypeForHandle( Handle ); - public override string ToString() => $"{InputType}.{inputHandle.Value}"; + /// + /// Reconfigure the controller to use the specified action set (ie 'Menu', 'Walk' or 'Drive') + /// This is cheap, and can be safely called repeatedly. It's often easier to repeatedly call it in + /// our state loops, instead of trying to place it in all of your state transitions. + /// + public ActionSet ActionSet + { + get => new ActionSet( SteamInput.Internal.GetCurrentActionSet( Handle ) ); + set => SteamInput.Internal.ActivateActionSet( Handle, value.Handle ); + } + + public void DeactivateLayer( ActionSet layer ) => SteamInput.Internal.DeactivateActionSetLayer( Handle, layer.Handle ); + public void ActivateLayer( ActionSet layer ) => SteamInput.Internal.ActivateActionSetLayer( Handle, layer.Handle ); + public void ClearLayers( ActionSet layer ) => SteamInput.Internal.DeactivateAllActionSetLayers( Handle ); + + + /// + /// Returns the current state of the supplied digital game action + /// + public DigitalState GetDigitalState( string actionName ) + { + return SteamInput.Internal.GetDigitalActionData( Handle, SteamInput.GetDigitalActionHandle( actionName ) ); + } + + /// + /// Returns the current state of these supplied analog game action + /// + public AnalogState GetAnalogState( string actionName ) + { + return SteamInput.Internal.GetAnalogActionData( Handle, SteamInput.GetAnalogActionHandle( actionName ) ); + } + + + public override string ToString() => $"{InputType}.{Handle.Value}"; + + + public static bool operator ==( Controller a, Controller b ) => a.Equals( b ); + public static bool operator !=( Controller a, Controller b ) => !(a == b); + public override bool Equals( object p ) => this.Equals( (Controller)p ); + public override int GetHashCode() => Handle.GetHashCode(); + public bool Equals( Controller p ) => p.Handle == Handle; + } + + [StructLayout( LayoutKind.Sequential, Pack = 1 )] + public struct AnalogState + { + public InputSourceMode EMode; // eMode EInputSourceMode + public float X; // x float + public float Y; // y float + internal byte BActive; // bActive byte + public bool Active => BActive != 0; + } + + [StructLayout( LayoutKind.Sequential, Pack = 1 )] + internal struct MotionState + { + public float RotQuatX; // rotQuatX float + public float RotQuatY; // rotQuatY float + public float RotQuatZ; // rotQuatZ float + public float RotQuatW; // rotQuatW float + public float PosAccelX; // posAccelX float + public float PosAccelY; // posAccelY float + public float PosAccelZ; // posAccelZ float + public float RotVelX; // rotVelX float + public float RotVelY; // rotVelY float + public float RotVelZ; // rotVelZ float + } + + [StructLayout( LayoutKind.Sequential, Pack = 1 )] + public struct DigitalState + { + internal byte BState; // bState byte + internal byte BActive; // bActive byte + + public bool Pressed => BState != 0; + public bool Active => BActive != 0; } } \ No newline at end of file diff --git a/Generator/Cleanup.cs b/Generator/Cleanup.cs index f16a88a..ddf3dc9 100644 --- a/Generator/Cleanup.cs +++ b/Generator/Cleanup.cs @@ -40,6 +40,9 @@ public static class Cleanup type = type.Replace( "SteamNetworkingSocketsDebugOutputType", "DebugOutputType" ); type = type.Replace( "SteamNetworkingGetConfigValueResult", "NetConfigResult" ); type = type.Replace( "SteamInputType", "InputType" ); + type = type.Replace( "InputDigitalActionData_t", "DigitalState" ); + type = type.Replace( "InputAnalogActionData_t", "AnalogState" ); + type = type.Replace( "InputMotionData_t", "MotionState" ); return type; } @@ -50,6 +53,9 @@ public static class Cleanup if ( type == "LeaderboardSort" ) return false; if ( type == "LeaderboardDisplay" ) return false; if ( type == "AppId" ) return false; + if ( type == "AnalogState" ) return false; + if ( type == "DigitalState" ) return false; + if ( type == "MotionState" ) return false; return true; } @@ -77,6 +83,7 @@ public static class Cleanup if ( name == "RoomEnter" ) return "public"; if ( name == "P2PSessionError" ) return "public"; if ( name == "InputType" ) return "public"; + if ( name == "InputSourceMode" ) return "public"; return "internal"; } diff --git a/Generator/CodeWriter/Types/BaseType.cs b/Generator/CodeWriter/Types/BaseType.cs index a5e8ba0..7e22c7d 100644 --- a/Generator/CodeWriter/Types/BaseType.cs +++ b/Generator/CodeWriter/Types/BaseType.cs @@ -159,6 +159,14 @@ internal class StructType : BaseType return AsArgument(); } + + static string[] SpecialTypes = new string[] + { + "DigitalState", + "AnalogState", + }; + + public override bool IsReturnedWeird => SpecialTypes.Contains( TypeName ); } internal class SteamApiCallType : BaseType