diff --git a/SteamController/Devices/SteamAction.cs b/SteamController/Devices/SteamAction.cs new file mode 100644 index 0000000..f9d3720 --- /dev/null +++ b/SteamController/Devices/SteamAction.cs @@ -0,0 +1,417 @@ +using hidapi; +using PowerControl.External; +using static CommonHelpers.Log; + +namespace SteamController.Devices +{ + public abstract class SteamAction + { + public SteamController? Controller { get; internal set; } + public String Name { get; internal set; } = ""; + + /// This is action controlled by Lizard mode + public bool LizardButton { get; internal set; } + public bool LizardMouse { get; internal set; } + public DateTime LastUpdated { get; protected set; } = DateTime.Now; + public double DeltaTime { get; protected set; } + + internal abstract void Reset(); + internal abstract bool BeforeUpdate(byte[] buffer); + internal abstract void Update(); + + internal SteamAction() + { + } + + protected void UpdateTime() + { + var now = DateTime.Now; + DeltaTime = (now - LastUpdated).TotalSeconds; + LastUpdated = now; + } + + protected bool ValueCanBeUsed + { + get + { + if (LizardButton && Controller?.LizardButtons == true) + return false; + if (LizardMouse && Controller?.LizardMouse == true) + return false; + return true; + } + } + } + + public class SteamButton : SteamAction + { + public static readonly TimeSpan DefaultHoldDuration = TimeSpan.FromMilliseconds(10); + public static readonly TimeSpan DefaultFirstHold = TimeSpan.FromMilliseconds(75); + public static readonly TimeSpan DefaultRepeatHold = TimeSpan.FromMilliseconds(150); + + private bool rawValue, rawLastValue; + + public bool Value + { + get { return ValueCanBeUsed ? rawValue : false; } + } + public bool LastValue + { + get { return ValueCanBeUsed ? rawLastValue : false; } + } + + /// Last press was already consumed by other + internal object? Consumed { get; private set; } + + /// Set on raising edge + private DateTime? HoldSince { get; set; } + private DateTime? HoldRepeated { get; set; } + + internal SteamButton() + { + } + + public static implicit operator bool(SteamButton button) => button.Hold(DefaultHoldDuration, null); + + /// Generated when button is pressed for the first time + public bool JustPressed() + { + if (!LastValue && Value) + return true; + return false; + } + + /// Generated on failing edge of key press + public bool Pressed(TimeSpan? duration = null) + { + // We expect Last to be true, and now to be false (failing edge) + if (!(LastValue && !Value)) + return false; + + if (Consumed is not null) + return false; + + if (duration.HasValue && HoldSince?.Add(duration.Value) >= DateTime.Now) + return false; + + return true; + } + + private bool Consume(object consume) + { + if (Consumed is not null && Consumed != consume) + return false; + + Consumed = consume; + return true; + } + + public bool Hold(object? consume) + { + return Hold(null, consume); + } + + /// Generated when button was hold for a given period + public bool Hold(TimeSpan? duration, object? consume) + { + if (!Value) + return false; + + if (Consumed is not null && Consumed != consume) + return false; + + if (duration.HasValue && HoldSince?.Add(duration.Value) >= DateTime.Now) + return false; + + if (consume is not null) + Consumed = consume; + + return true; + } + + public bool HoldOnce(object consume) + { + return HoldOnce(null, consume); + } + + /// Generated when button was hold for a given period + /// but triggered exactly once + public bool HoldOnce(TimeSpan? duration, object consume) + { + if (!Hold(duration)) + return false; + + Consumed = consume; + return true; + } + + /// Generated when button was hold for a given period + /// but triggered exactly after previously being hold + public bool HoldChain(TimeSpan? duration, object previousConsume, object replaceConsme) + { + if (!Hold(duration, previousConsume)) + return false; + + Consumed = replaceConsme; + return true; + } + + /// Generated when button was repeated for a given period + /// but triggered exactly once + public bool HoldRepeat(TimeSpan duration, TimeSpan repeatEvery, object consume) + { + // always generate at least one keypress + if (Pressed(duration)) + return true; + + if (!Hold(duration, consume)) + return false; + + // first keypress + if (!HoldRepeated.HasValue) + { + HoldRepeated = DateTime.Now; + return true; + } + + // repeated keypress + if (HoldRepeated.Value.Add(repeatEvery) <= DateTime.Now) + { + HoldRepeated = DateTime.Now; + return true; + } + + return false; + } + + public bool HoldRepeat(object consume) + { + return HoldRepeat(DefaultFirstHold, DefaultRepeatHold, consume); + } + + internal override void Reset() + { + rawLastValue = rawValue; + rawValue = false; + HoldSince = null; + HoldRepeated = null; + Consumed = null; + } + + internal void SetValue(bool newValue) + { + rawLastValue = rawValue; + rawValue = newValue; + UpdateTime(); + + if (!rawLastValue && rawValue) + { + HoldSince = DateTime.Now; + HoldRepeated = null; + } + } + + internal override bool BeforeUpdate(byte[] buffer) + { + return true; + } + + internal override void Update() + { + if (!Value) + Consumed = null; + } + + public override string? ToString() + { + if (Name != "") + return String.Format("{0}: {1} (last: {2})", Name, Value, LastValue); + return base.ToString(); + } + } + + public class SteamButton2 : SteamButton + { + private int offset; + private uint mask; + + internal SteamButton2(int offset, object mask) + { + this.offset = offset; + this.mask = (uint)mask.GetHashCode(); + + while (this.mask > 0xFF) + { + this.mask >>= 8; + this.offset++; + } + } + + internal override bool BeforeUpdate(byte[] buffer) + { + if (offset < buffer.Length) + { + SetValue((buffer[offset] & mask) != 0); + return true; + } + else + { + SetValue((buffer[offset] & mask) != 0); + return false; + } + } + } + + public class SteamAxis : SteamAction + { + public const short VirtualLeftThreshold = short.MinValue / 2; + public const short VirtualRightThreshold = short.MaxValue / 2; + + private int offset; + private short rawValue, rawLastValue; + + public SteamButton? ActiveButton { get; internal set; } + public SteamButton? VirtualLeft { get; internal set; } + public SteamButton? VirtualRight { get; internal set; } + public short Deadzone { get; internal set; } + public short MinChange { get; internal set; } + + public short Value + { + get { return ValueCanBeUsed ? rawValue : (short)0; } + } + public short LastValue + { + get { return ValueCanBeUsed ? rawLastValue : (short)0; } + } + + public SteamAxis(int offset) + { + this.offset = offset; + } + + public static implicit operator bool(SteamAxis button) => button.Active; + public static implicit operator short(SteamAxis button) + { + return Math.Abs(button.Value) > button.Deadzone ? button.Value : (short)0; + } + + public bool Active + { + get + { + return ActiveButton?.Value ?? true; + } + } + + public enum ScaledMode + { + Absolute, + AbsoluteTime, + Delta, + DeltaTime + } + + public double Scaled(double min, double max, ScaledMode mode) + { + int value = 0; + + switch (mode) + { + case ScaledMode.Absolute: + if (Math.Abs(Value) < Deadzone) + return 0.0; + value = Value; + break; + + case ScaledMode.AbsoluteTime: + if (Math.Abs(Value) < Deadzone) + return 0.0; + value = (int)(Value * DeltaTime); + break; + + case ScaledMode.Delta: + value = Value - LastValue; + if (Math.Abs(Value) < MinChange) + return 0.0; + break; + + case ScaledMode.DeltaTime: + value = Value - LastValue; + if (Math.Abs(Value) < MinChange) + return 0.0; + value = (int)(value * DeltaTime); + break; + } + + if (value == 0) + return 0.0; + + double factor = (double)(value - short.MinValue) / (short.MaxValue - short.MinValue); + return factor * (max - min) + min; + } + + public double Scaled(double range, ScaledMode mode) + { + return Scaled(-range, range, mode); + } + + public int Scaled(int min, int max, ScaledMode mode) + { + return (int)Scaled((double)min, (double)max, mode); + } + + public int Scaled(int range, ScaledMode mode) + { + return Scaled(-range, range, mode); + } + + internal override void Reset() + { + rawLastValue = rawValue; + rawValue = 0; + } + + internal void SetValue(short newValue) + { + rawLastValue = rawValue; + rawValue = newValue; + UpdateTime(); + + // first time pressed, reset value as this is a Pad + if (ActiveButton is not null && ActiveButton.JustPressed()) + rawLastValue = newValue; + + if (VirtualRight is not null) + VirtualRight.SetValue(newValue > VirtualRightThreshold); + + if (VirtualLeft is not null) + VirtualLeft.SetValue(newValue < VirtualLeftThreshold); + } + + internal override bool BeforeUpdate(byte[] buffer) + { + if (offset + 1 < buffer.Length) + { + SetValue(BitConverter.ToInt16(buffer, offset)); + return true; + } + else + { + SetValue(0); + return false; + } + } + + internal override void Update() + { + } + + public override string? ToString() + { + if (Name != "") + return String.Format("{0}: {1} (last: {2})", Name, Value, LastValue); + return base.ToString(); + } + } +} diff --git a/SteamController/Devices/SteamControllerActions.cs b/SteamController/Devices/SteamControllerActions.cs index b033e09..edd3e80 100644 --- a/SteamController/Devices/SteamControllerActions.cs +++ b/SteamController/Devices/SteamControllerActions.cs @@ -6,417 +6,6 @@ namespace SteamController.Devices { public partial class SteamController { - public abstract class SteamAction - { - public SteamController? Controller { get; internal set; } - public String Name { get; internal set; } = ""; - - /// This is action controlled by Lizard mode - public bool LizardButton { get; internal set; } - public bool LizardMouse { get; internal set; } - public DateTime LastUpdated { get; protected set; } = DateTime.Now; - public double DeltaTime { get; protected set; } - - internal abstract void Reset(); - internal abstract bool BeforeUpdate(byte[] buffer); - internal abstract void Update(); - - internal SteamAction() - { - } - - protected void UpdateTime() - { - var now = DateTime.Now; - DeltaTime = (now - LastUpdated).TotalSeconds; - LastUpdated = now; - } - - protected bool ValueCanBeUsed - { - get - { - if (LizardButton && Controller?.LizardButtons == true) - return false; - if (LizardMouse && Controller?.LizardMouse == true) - return false; - return true; - } - } - } - - public class SteamButton : SteamAction - { - public static readonly TimeSpan DefaultHoldDuration = TimeSpan.FromMilliseconds(10); - public static readonly TimeSpan DefaultFirstHold = TimeSpan.FromMilliseconds(75); - public static readonly TimeSpan DefaultRepeatHold = TimeSpan.FromMilliseconds(150); - - private bool rawValue, rawLastValue; - - public bool Value - { - get { return ValueCanBeUsed ? rawValue : false; } - } - public bool LastValue - { - get { return ValueCanBeUsed ? rawLastValue : false; } - } - - /// Last press was already consumed by other - internal object? Consumed { get; private set; } - - /// Set on raising edge - private DateTime? HoldSince { get; set; } - private DateTime? HoldRepeated { get; set; } - - internal SteamButton() - { - } - - public static implicit operator bool(SteamButton button) => button.Hold(DefaultHoldDuration, null); - - /// Generated when button is pressed for the first time - public bool JustPressed() - { - if (!LastValue && Value) - return true; - return false; - } - - /// Generated on failing edge of key press - public bool Pressed(TimeSpan? duration = null) - { - // We expect Last to be true, and now to be false (failing edge) - if (!(LastValue && !Value)) - return false; - - if (Consumed is not null) - return false; - - if (duration.HasValue && HoldSince?.Add(duration.Value) >= DateTime.Now) - return false; - - return true; - } - - private bool Consume(object consume) - { - if (Consumed is not null && Consumed != consume) - return false; - - Consumed = consume; - return true; - } - - public bool Hold(object? consume = null) - { - return Hold(null, consume); - } - - /// Generated when button was hold for a given period - public bool Hold(TimeSpan? duration, object? consume = null) - { - if (!Value) - return false; - - if (Consumed is not null && Consumed != consume) - return false; - - if (duration.HasValue && HoldSince?.Add(duration.Value) >= DateTime.Now) - return false; - - if (consume is not null) - Consumed = consume; - - return true; - } - - public bool HoldOnce(object consume) - { - return HoldOnce(null, consume); - } - - /// Generated when button was hold for a given period - /// but triggered exactly once - public bool HoldOnce(TimeSpan? duration, object consume) - { - if (!Hold(duration)) - return false; - - Consumed = consume; - return true; - } - - /// Generated when button was hold for a given period - /// but triggered exactly after previously being hold - public bool HoldChain(TimeSpan? duration, object previousConsume, object replaceConsme) - { - if (!Hold(duration, previousConsume)) - return false; - - Consumed = replaceConsme; - return true; - } - - /// Generated when button was repeated for a given period - /// but triggered exactly once - public bool HoldRepeat(TimeSpan duration, TimeSpan repeatEvery, object consume) - { - // always generate at least one keypress - if (Pressed(duration)) - return true; - - if (!Hold(duration, consume)) - return false; - - // first keypress - if (!HoldRepeated.HasValue) - { - HoldRepeated = DateTime.Now; - return true; - } - - // repeated keypress - if (HoldRepeated.Value.Add(repeatEvery) <= DateTime.Now) - { - HoldRepeated = DateTime.Now; - return true; - } - - return false; - } - - public bool HoldRepeat(object consume) - { - return HoldRepeat(DefaultFirstHold, DefaultRepeatHold, consume); - } - - internal override void Reset() - { - rawLastValue = rawValue; - rawValue = false; - HoldSince = null; - HoldRepeated = null; - Consumed = null; - } - - internal void SetValue(bool newValue) - { - rawLastValue = rawValue; - rawValue = newValue; - UpdateTime(); - - if (!rawLastValue && rawValue) - { - HoldSince = DateTime.Now; - HoldRepeated = null; - } - } - - internal override bool BeforeUpdate(byte[] buffer) - { - return true; - } - - internal override void Update() - { - if (!Value) - Consumed = null; - } - - public override string? ToString() - { - if (Name != "") - return String.Format("{0}: {1} (last: {2})", Name, Value, LastValue); - return base.ToString(); - } - } - - public class SteamButton2 : SteamButton - { - private int offset; - private uint mask; - - internal SteamButton2(int offset, object mask) - { - this.offset = offset; - this.mask = (uint)mask.GetHashCode(); - - while (this.mask > 0xFF) - { - this.mask >>= 8; - this.offset++; - } - } - - internal override bool BeforeUpdate(byte[] buffer) - { - if (offset < buffer.Length) - { - SetValue((buffer[offset] & mask) != 0); - return true; - } - else - { - SetValue((buffer[offset] & mask) != 0); - return false; - } - } - } - - public class SteamAxis : SteamAction - { - public const short VirtualLeftThreshold = short.MinValue / 2; - public const short VirtualRightThreshold = short.MaxValue / 2; - - private int offset; - private short rawValue, rawLastValue; - - public SteamButton? ActiveButton { get; internal set; } - public SteamButton? VirtualLeft { get; internal set; } - public SteamButton? VirtualRight { get; internal set; } - public short Deadzone { get; internal set; } - public short MinChange { get; internal set; } - - public short Value - { - get { return ValueCanBeUsed ? rawValue : (short)0; } - } - public short LastValue - { - get { return ValueCanBeUsed ? rawLastValue : (short)0; } - } - - public SteamAxis(int offset) - { - this.offset = offset; - } - - public static implicit operator bool(SteamAxis button) => button.Active; - public static implicit operator short(SteamAxis button) - { - return Math.Abs(button.Value) > button.Deadzone ? button.Value : (short)0; - } - - public bool Active - { - get - { - return ActiveButton?.Value ?? true; - } - } - - public enum ScaledMode - { - Absolute, - AbsoluteTime, - Delta, - DeltaTime - } - - public double Scaled(double min, double max, ScaledMode mode) - { - int value = 0; - - switch (mode) - { - case ScaledMode.Absolute: - if (Math.Abs(Value) < Deadzone) - return 0.0; - value = Value; - break; - - case ScaledMode.AbsoluteTime: - if (Math.Abs(Value) < Deadzone) - return 0.0; - value = (int)(Value * DeltaTime); - break; - - case ScaledMode.Delta: - value = Value - LastValue; - if (Math.Abs(Value) < MinChange) - return 0.0; - break; - - case ScaledMode.DeltaTime: - value = Value - LastValue; - if (Math.Abs(Value) < MinChange) - return 0.0; - value = (int)(value * DeltaTime); - break; - } - - if (value == 0) - return 0.0; - - double factor = (double)(value - short.MinValue) / (short.MaxValue - short.MinValue); - return factor * (max - min) + min; - } - - public double Scaled(double range, ScaledMode mode) - { - return Scaled(-range, range, mode); - } - - public int Scaled(int min, int max, ScaledMode mode) - { - return (int)Scaled((double)min, (double)max, mode); - } - - public int Scaled(int range, ScaledMode mode) - { - return Scaled(-range, range, mode); - } - - internal override void Reset() - { - rawLastValue = rawValue; - rawValue = 0; - } - - internal void SetValue(short newValue) - { - rawLastValue = rawValue; - rawValue = newValue; - UpdateTime(); - - // first time pressed, reset value as this is a Pad - if (ActiveButton is not null && ActiveButton.JustPressed()) - rawLastValue = newValue; - - if (VirtualRight is not null) - VirtualRight.SetValue(newValue > VirtualRightThreshold); - - if (VirtualLeft is not null) - VirtualLeft.SetValue(newValue < VirtualLeftThreshold); - } - - internal override bool BeforeUpdate(byte[] buffer) - { - if (offset + 1 < buffer.Length) - { - SetValue(BitConverter.ToInt16(buffer, offset)); - return true; - } - else - { - SetValue(0); - return false; - } - } - - internal override void Update() - { - } - - public override string? ToString() - { - if (Name != "") - return String.Format("{0}: {1} (last: {2})", Name, Value, LastValue); - return base.ToString(); - } - } - public SteamAction?[] AllActions { get; private set; } public SteamButton?[] AllButtons { get; private set; } public SteamAxis?[] AllAxises { get; private set; } diff --git a/SteamController/Devices/SteamControllerLizard.cs b/SteamController/Devices/SteamControllerLizard.cs index 5987d0b..2cdda74 100644 --- a/SteamController/Devices/SteamControllerLizard.cs +++ b/SteamController/Devices/SteamControllerLizard.cs @@ -6,7 +6,7 @@ namespace SteamController.Devices { public partial class SteamController { - public const int LizardModeUpdateInterval = 250; + private const int LizardModeUpdateInterval = 250; public bool LizardMouse { get; set; } = true; public bool LizardButtons { get; set; } = true; diff --git a/SteamController/Profiles/DefaultGuideShortcutsProfile.cs b/SteamController/Profiles/DefaultGuideShortcutsProfile.cs index fa15e15..e042e69 100644 --- a/SteamController/Profiles/DefaultGuideShortcutsProfile.cs +++ b/SteamController/Profiles/DefaultGuideShortcutsProfile.cs @@ -114,11 +114,11 @@ namespace SteamController.Profiles { if (c.Steam.LPadX) { - c.Mouse.HorizontalScroll(c.Steam.LPadX.Scaled(Context.PadToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)); + c.Mouse.HorizontalScroll(c.Steam.LPadX.Scaled(Context.PadToWhellSensitivity, Devices.SteamAxis.ScaledMode.Delta)); } if (c.Steam.LPadY) { - c.Mouse.VerticalScroll(c.Steam.LPadY.Scaled(Context.PadToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)); + c.Mouse.VerticalScroll(c.Steam.LPadY.Scaled(Context.PadToWhellSensitivity, Devices.SteamAxis.ScaledMode.Delta)); } } @@ -127,8 +127,8 @@ namespace SteamController.Profiles if (c.Steam.RightThumbX || c.Steam.RightThumbY) { c.Mouse.MoveBy( - c.Steam.RightThumbX.Scaled(Context.JoystickToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.AbsoluteTime), - -c.Steam.RightThumbY.Scaled(Context.JoystickToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.AbsoluteTime) + c.Steam.RightThumbX.Scaled(Context.JoystickToMouseSensitivity, Devices.SteamAxis.ScaledMode.AbsoluteTime), + -c.Steam.RightThumbY.Scaled(Context.JoystickToMouseSensitivity, Devices.SteamAxis.ScaledMode.AbsoluteTime) ); } } @@ -149,8 +149,8 @@ namespace SteamController.Profiles if (c.Steam.RPadX || c.Steam.RPadY) { c.Mouse.MoveBy( - c.Steam.RPadX.Scaled(Context.PadToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta), - -c.Steam.RPadY.Scaled(Context.PadToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta) + c.Steam.RPadX.Scaled(Context.PadToMouseSensitivity, Devices.SteamAxis.ScaledMode.Delta), + -c.Steam.RPadY.Scaled(Context.PadToMouseSensitivity, Devices.SteamAxis.ScaledMode.Delta) ); } }