From aa19d9cdf55a78236628cc3a4c302868f150aded Mon Sep 17 00:00:00 2001 From: General4878 Date: Sun, 10 Aug 2025 13:11:09 +0200 Subject: [PATCH 1/5] FLM: Created MoveByFauxLizard method which mimmicks the mouse movement and haptic feedback of Lizard Mouse without needing Lizard Mouse enabled which resolves the mouse stutter bug in later versions Steam Deck firmware. --- SteamController/Devices/MouseController.cs | 2 +- .../Devices/MouseControllerFauxLizard.cs | 177 ++++++++++++++++++ .../Profiles/Default/GuideShortcutsProfile.cs | 12 +- 3 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 SteamController/Devices/MouseControllerFauxLizard.cs diff --git a/SteamController/Devices/MouseController.cs b/SteamController/Devices/MouseController.cs index 34a593e..4431d82 100644 --- a/SteamController/Devices/MouseController.cs +++ b/SteamController/Devices/MouseController.cs @@ -2,7 +2,7 @@ using WindowsInput; namespace SteamController.Devices { - public class MouseController : IDisposable + public partial class MouseController : IDisposable { private struct Accum { diff --git a/SteamController/Devices/MouseControllerFauxLizard.cs b/SteamController/Devices/MouseControllerFauxLizard.cs new file mode 100644 index 0000000..8a6b99b --- /dev/null +++ b/SteamController/Devices/MouseControllerFauxLizard.cs @@ -0,0 +1,177 @@ +using static SteamController.Devices.SteamController; + +namespace SteamController.Devices +{ + public partial class MouseController : IDisposable + { + // Adjustable parameters (tweakable settings) + private const int bufferSize = 4; // Input smoothing buffer size + private const int gestureRadius = 30; // Gesture commit threshold (pixels) + private const double gestureFlushRate = 0.1; // Rate at which gesture flush decays + private const int velocityWindowSize = 20; // Velocity smoothing window size + private const double minGlideMagnitude = 0.8; // Minimum velocity to start glide + private const double minGlideVelocity = 0.15; // Minimum velocity to continue glide + private const double hapticTriggerDelta = 45; // Distance threshold for haptic feedback + private const sbyte hapticStrength = 5; // Haptic feedback intensity + + // Functional constants (derived once) + private readonly double gestureRadiusSq = gestureRadius * gestureRadius; + private readonly double minGlideMagnitudeSq = minGlideMagnitude * minGlideMagnitude; + private readonly double minGlideVelocitySq = minGlideVelocity * minGlideVelocity; + + // Runtime state + private Queue bufX = new(), bufY = new(); + private double totalDeltaX = 0, totalDeltaY = 0; + private bool gestureCommitted = false; + private double gestureFlushX = 0, gestureFlushY = 0; + + private bool isGliding = false; + private DateTime releaseTime; + private double releaseVelocityX = 0, releaseVelocityY = 0; + private Queue velocityHistoryX = new(), velocityHistoryY = new(); + private double velocitySumX = 0, velocitySumY = 0; + + private double hapticDelta = 0; + + // Main movement logic + public void MoveByFauxLizard(double dx, double dy, Context c) + { + bool isTouched = c.Steam.BtnRPadTouch?.LastValue ?? false; + + if (!isTouched) + { + // Start glide if gesture was committed and velocity is high enough + if (!isGliding && gestureCommitted) + { + double magSq = releaseVelocityX * releaseVelocityX + releaseVelocityY * releaseVelocityY; + if (magSq >= minGlideMagnitudeSq) + { + releaseTime = DateTime.Now; + isGliding = true; + } + } + + // Continue glide if active + if (isGliding) + { + double elapsed = (DateTime.Now - releaseTime).TotalSeconds; + double glideX = ApplyReleaseGlide(releaseVelocityX, elapsed); + double glideY = ApplyReleaseGlide(releaseVelocityY, elapsed); + + double glideMagSq = glideX * glideX + glideY * glideY; + if (glideMagSq < minGlideVelocitySq) + { + isGliding = false; + releaseVelocityX = releaseVelocityY = 0; + } + else + { + MoveBy(glideX, glideY); + } + } + + // Reset gesture state + gestureCommitted = false; + totalDeltaX = totalDeltaY = hapticDelta = 0; + return; + } + + isGliding = false; + + // Smooth input deltas + double smoothedX = SmoothDelta(dx, bufX); + double smoothedY = SmoothDelta(dy, bufY); + + // Apply acceleration ramp + double rampedX = ApplyAccelerationRamp(smoothedX); + double rampedY = ApplyAccelerationRamp(smoothedY); + + // Accumulate gesture until committed + if (!gestureCommitted) + { + totalDeltaX += rampedX; + totalDeltaY += rampedY; + + double gestureMagSq = totalDeltaX * totalDeltaX + totalDeltaY * totalDeltaY; + if (gestureMagSq > gestureRadiusSq) + { + gestureCommitted = true; + gestureFlushX = totalDeltaX; + gestureFlushY = totalDeltaY; + + velocityHistoryX.Clear(); + velocityHistoryY.Clear(); + velocitySumX = velocitySumY = 0; + } + else return; + } + + // Track velocity using rolling sum + velocityHistoryX.Enqueue(rampedX); + velocitySumX += rampedX; + if (velocityHistoryX.Count > velocityWindowSize) + velocitySumX -= velocityHistoryX.Dequeue(); + + velocityHistoryY.Enqueue(rampedY); + velocitySumY += rampedY; + if (velocityHistoryY.Count > velocityWindowSize) + velocitySumY -= velocityHistoryY.Dequeue(); + + releaseVelocityX = velocitySumX / velocityHistoryX.Count; + releaseVelocityY = velocitySumY / velocityHistoryY.Count; + + // Apply gesture flush + double flushedX = gestureFlushX * gestureFlushRate; + double flushedY = gestureFlushY * gestureFlushRate; + gestureFlushX -= flushedX; + gestureFlushY -= flushedY; + + // Final movement vector + double finalX = flushedX + rampedX; + double finalY = flushedY + rampedY; + + // Haptic trigger + double finalMagSq = finalX * finalX + finalY * finalY; + hapticDelta += Math.Sqrt(finalMagSq); // Only one sqrt per frame + + if (hapticDelta >= hapticTriggerDelta) + { + c.Steam.SendHaptic(HapticPad.Right, HapticStyle.Weak, hapticStrength); + hapticDelta -= hapticTriggerDelta; + } + + MoveBy(finalX, finalY); + } + + // Input smoothing + private double SmoothDelta(double raw, Queue buffer) + { + buffer.Enqueue(raw); + if (buffer.Count > bufferSize) buffer.Dequeue(); + + double sum = 0; + foreach (var v in buffer) sum += v; + return sum / buffer.Count; + } + + // Acceleration ramp + private double ApplyAccelerationRamp(double delta) + { + double steepness = 0.1; + double midpoint = 6.0; + double maxBoost = 2.0; + double baseBoost = 1.7; + + double absDelta = Math.Abs(delta); + double sigmoidBoost = 1.0 + (maxBoost - 1.0) / (1.0 + Math.Exp(-steepness * (absDelta - midpoint))); + return delta * Math.Max(sigmoidBoost, baseBoost); + } + + // Glide decay + private double ApplyReleaseGlide(double velocity, double elapsed) + { + double rampedRate = 3.0 + Math.Pow(elapsed * 7.0, 2); + return velocity * Math.Exp(-elapsed * rampedRate); + } + } +} \ No newline at end of file diff --git a/SteamController/Profiles/Default/GuideShortcutsProfile.cs b/SteamController/Profiles/Default/GuideShortcutsProfile.cs index 6a88c41..48f3740 100644 --- a/SteamController/Profiles/Default/GuideShortcutsProfile.cs +++ b/SteamController/Profiles/Default/GuideShortcutsProfile.cs @@ -174,13 +174,11 @@ namespace SteamController.Profiles.Default c.Mouse[Devices.MouseController.Button.Left] = c.Steam.BtnRPadPress; } - if (c.Steam.RPadX || c.Steam.RPadY) - { - c.Mouse.MoveBy( - c.Steam.RPadX.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), - -c.Steam.RPadY.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10) - ); - } + c.Mouse.MoveByFauxLizard( + c.Steam.RPadX.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), + -c.Steam.RPadY.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), + c + ); } } } From 12a5ddc6bd4ca1d4356ad8e6bea90bcfa88468b5 Mon Sep 17 00:00:00 2001 From: General4878 Date: Sat, 13 Sep 2025 16:33:30 +0200 Subject: [PATCH 2/5] FLM: Send haptic on pad presses when both lizard mouse and buttons are disabled --- .../Profiles/Default/GuideShortcutsProfile.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/SteamController/Profiles/Default/GuideShortcutsProfile.cs b/SteamController/Profiles/Default/GuideShortcutsProfile.cs index 48f3740..c45106f 100644 --- a/SteamController/Profiles/Default/GuideShortcutsProfile.cs +++ b/SteamController/Profiles/Default/GuideShortcutsProfile.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using ExternalHelpers; using PowerControl.Helpers; using WindowsInput; +using static SteamController.Devices.SteamController; namespace SteamController.Profiles.Default { @@ -120,6 +121,15 @@ namespace SteamController.Profiles.Default protected void EmulateScrollOnLPad(Context c) { + //Send haptic for pad presses + if (!c.Steam.LizardButtons && !c.Steam.LizardMouse) + { + if (c.Steam.BtnLPadPress.Pressed() || c.Steam.BtnLPadPress.JustPressed()) + { + c.Steam.SendHaptic(HapticPad.Left, HapticStyle.Strong, 8); + } + } + if (c.Steam.LPadX) { c.Mouse.HorizontalScroll( @@ -174,6 +184,15 @@ namespace SteamController.Profiles.Default c.Mouse[Devices.MouseController.Button.Left] = c.Steam.BtnRPadPress; } + //Send haptic for pad presses + if (!c.Steam.LizardButtons && !c.Steam.LizardMouse) + { + if (c.Steam.BtnRPadPress.Pressed() || c.Steam.BtnRPadPress.JustPressed()) + { + c.Steam.SendHaptic(HapticPad.Right, HapticStyle.Strong, 8); + } + } + c.Mouse.MoveByFauxLizard( c.Steam.RPadX.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), -c.Steam.RPadY.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), From c536393054bde52336eeb9d4ea76bdf85cac6466 Mon Sep 17 00:00:00 2001 From: General4878 Date: Sat, 13 Sep 2025 18:05:23 +0200 Subject: [PATCH 3/5] FLM: Removed built in haptic on drag from MoveByFauxLizard into its own method and added haptic feedback for LPad drag --- .../Devices/MouseControllerFauxLizard.cs | 70 ++++++++++++++----- .../Profiles/Default/GuideShortcutsProfile.cs | 64 ++++++++++++----- 2 files changed, 100 insertions(+), 34 deletions(-) diff --git a/SteamController/Devices/MouseControllerFauxLizard.cs b/SteamController/Devices/MouseControllerFauxLizard.cs index 8a6b99b..8c997a6 100644 --- a/SteamController/Devices/MouseControllerFauxLizard.cs +++ b/SteamController/Devices/MouseControllerFauxLizard.cs @@ -11,15 +11,14 @@ namespace SteamController.Devices private const int velocityWindowSize = 20; // Velocity smoothing window size private const double minGlideMagnitude = 0.8; // Minimum velocity to start glide private const double minGlideVelocity = 0.15; // Minimum velocity to continue glide - private const double hapticTriggerDelta = 45; // Distance threshold for haptic feedback - private const sbyte hapticStrength = 5; // Haptic feedback intensity + private const double hapticTriggerDelta = 30; // Distance threshold for haptic feedback on each pad // Functional constants (derived once) private readonly double gestureRadiusSq = gestureRadius * gestureRadius; private readonly double minGlideMagnitudeSq = minGlideMagnitude * minGlideMagnitude; private readonly double minGlideVelocitySq = minGlideVelocity * minGlideVelocity; - // Runtime state + // Runtime state RPad private Queue bufX = new(), bufY = new(); private double totalDeltaX = 0, totalDeltaY = 0; private bool gestureCommitted = false; @@ -31,13 +30,13 @@ namespace SteamController.Devices private Queue velocityHistoryX = new(), velocityHistoryY = new(); private double velocitySumX = 0, velocitySumY = 0; - private double hapticDelta = 0; + // Runtime state haptics + private double hapticDeltaR = 0; + private double hapticDeltaL = 0; // Main movement logic - public void MoveByFauxLizard(double dx, double dy, Context c) + public void MoveByFauxLizard(double dx, double dy, bool isTouched) { - bool isTouched = c.Steam.BtnRPadTouch?.LastValue ?? false; - if (!isTouched) { // Start glide if gesture was committed and velocity is high enough @@ -72,7 +71,7 @@ namespace SteamController.Devices // Reset gesture state gestureCommitted = false; - totalDeltaX = totalDeltaY = hapticDelta = 0; + totalDeltaX = totalDeltaY; return; } @@ -130,19 +129,54 @@ namespace SteamController.Devices double finalX = flushedX + rampedX; double finalY = flushedY + rampedY; - // Haptic trigger - double finalMagSq = finalX * finalX + finalY * finalY; - hapticDelta += Math.Sqrt(finalMagSq); // Only one sqrt per frame - - if (hapticDelta >= hapticTriggerDelta) - { - c.Steam.SendHaptic(HapticPad.Right, HapticStyle.Weak, hapticStrength); - hapticDelta -= hapticTriggerDelta; - } - MoveBy(finalX, finalY); } + // Checks whether the pads have been dragged enough to trigger a haptic feedback + public bool HapticDragRFauxLizard(double dx, double dy, bool isTouched) + { + if (isTouched) + { + double finalMagSq = dx * dx + dy * dy; + + if (hapticDeltaR < hapticTriggerDelta) + hapticDeltaR += Math.Sqrt(finalMagSq); + + if (hapticDeltaR >= hapticTriggerDelta) + { + hapticDeltaR -= hapticTriggerDelta; + + return true; + } + } + else + hapticDeltaR = 0; + + return false; + } + + public bool HapticDragLFauxLizard(double dx, double dy, bool isTouched) + { + if (isTouched) + { + double finalMagSq = dx * dx + dy * dy; + + if (hapticDeltaL < hapticTriggerDelta) + hapticDeltaL += Math.Sqrt(finalMagSq); + + if (hapticDeltaL >= hapticTriggerDelta) + { + hapticDeltaL -= hapticTriggerDelta; + + return true; + } + } + else + hapticDeltaL = 0; + + return false; + } + // Input smoothing private double SmoothDelta(double raw, Queue buffer) { diff --git a/SteamController/Profiles/Default/GuideShortcutsProfile.cs b/SteamController/Profiles/Default/GuideShortcutsProfile.cs index c45106f..72fb822 100644 --- a/SteamController/Profiles/Default/GuideShortcutsProfile.cs +++ b/SteamController/Profiles/Default/GuideShortcutsProfile.cs @@ -121,15 +121,6 @@ namespace SteamController.Profiles.Default protected void EmulateScrollOnLPad(Context c) { - //Send haptic for pad presses - if (!c.Steam.LizardButtons && !c.Steam.LizardMouse) - { - if (c.Steam.BtnLPadPress.Pressed() || c.Steam.BtnLPadPress.JustPressed()) - { - c.Steam.SendHaptic(HapticPad.Left, HapticStyle.Strong, 8); - } - } - if (c.Steam.LPadX) { c.Mouse.HorizontalScroll( @@ -150,6 +141,31 @@ namespace SteamController.Profiles.Default ) ); } + + if (!c.Steam.LizardButtons && !c.Steam.LizardMouse) + { + // Send haptic for pad presses + if (c.Steam.BtnLPadPress.Pressed() || c.Steam.BtnLPadPress.JustPressed()) + { + c.Steam.SendHaptic(HapticPad.Left, HapticStyle.Strong, 8); + } + + // Send haptic for pad drag + if (c.Mouse.HapticDragLFauxLizard( + c.Steam.LPadX.GetDeltaValue( + 150, + Devices.DeltaValueMode.Delta, + 10 + ), + c.Steam.LPadY.GetDeltaValue( + 150, + Devices.DeltaValueMode.Delta, + 10 + ), + c.Steam.BtnLPadTouch?.LastValue ?? false + )) + c.Steam.SendHaptic(HapticPad.Left, HapticStyle.Weak, 5); + } } protected void EmulateMouseOnRStick(Context c) @@ -184,20 +200,36 @@ namespace SteamController.Profiles.Default c.Mouse[Devices.MouseController.Button.Left] = c.Steam.BtnRPadPress; } - //Send haptic for pad presses + c.Mouse.MoveByFauxLizard( + c.Steam.RPadX.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), + -c.Steam.RPadY.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), + c.Steam.BtnRPadTouch?.LastValue ?? false + ); + if (!c.Steam.LizardButtons && !c.Steam.LizardMouse) { + // Send haptic for pad presses if (c.Steam.BtnRPadPress.Pressed() || c.Steam.BtnRPadPress.JustPressed()) { c.Steam.SendHaptic(HapticPad.Right, HapticStyle.Strong, 8); } - } - c.Mouse.MoveByFauxLizard( - c.Steam.RPadX.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), - -c.Steam.RPadY.GetDeltaValue(Context.PadToMouseSensitivity, Devices.DeltaValueMode.Delta, 10), - c - ); + // Send haptic for pad drag + if (c.Mouse.HapticDragRFauxLizard( + c.Steam.RPadX.GetDeltaValue( + 150, + Devices.DeltaValueMode.Delta, + 10 + ), + c.Steam.RPadY.GetDeltaValue( + 150, + Devices.DeltaValueMode.Delta, + 10 + ), + c.Steam.BtnRPadTouch?.LastValue ?? false + )) + c.Steam.SendHaptic(HapticPad.Right, HapticStyle.Weak, 5); + } } } } From a0243c74a65ccbc5ba2b5efb2872ceafff686ed0 Mon Sep 17 00:00:00 2001 From: General4878 Date: Sat, 13 Sep 2025 19:07:33 +0200 Subject: [PATCH 4/5] FLM: Fixed bug where total deltas were not set to zero. --- SteamController/Devices/MouseControllerFauxLizard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SteamController/Devices/MouseControllerFauxLizard.cs b/SteamController/Devices/MouseControllerFauxLizard.cs index 8c997a6..9b20642 100644 --- a/SteamController/Devices/MouseControllerFauxLizard.cs +++ b/SteamController/Devices/MouseControllerFauxLizard.cs @@ -71,7 +71,7 @@ namespace SteamController.Devices // Reset gesture state gestureCommitted = false; - totalDeltaX = totalDeltaY; + totalDeltaX = totalDeltaY = 0; return; } From 1d15ebebde5423d43fa9fae38288678bdc8aed46 Mon Sep 17 00:00:00 2001 From: General4878 Date: Sun, 14 Sep 2025 17:01:54 +0200 Subject: [PATCH 5/5] FLM: Added trigger buffer to haptic feedback to prevent minor movements from causing feedback. --- .../Devices/MouseControllerFauxLizard.cs | 117 +++++++++++++++--- .../Profiles/Default/GuideShortcutsProfile.cs | 8 +- 2 files changed, 102 insertions(+), 23 deletions(-) diff --git a/SteamController/Devices/MouseControllerFauxLizard.cs b/SteamController/Devices/MouseControllerFauxLizard.cs index 9b20642..e38fac6 100644 --- a/SteamController/Devices/MouseControllerFauxLizard.cs +++ b/SteamController/Devices/MouseControllerFauxLizard.cs @@ -5,18 +5,20 @@ namespace SteamController.Devices public partial class MouseController : IDisposable { // Adjustable parameters (tweakable settings) - private const int bufferSize = 4; // Input smoothing buffer size - private const int gestureRadius = 30; // Gesture commit threshold (pixels) - private const double gestureFlushRate = 0.1; // Rate at which gesture flush decays - private const int velocityWindowSize = 20; // Velocity smoothing window size - private const double minGlideMagnitude = 0.8; // Minimum velocity to start glide - private const double minGlideVelocity = 0.15; // Minimum velocity to continue glide - private const double hapticTriggerDelta = 30; // Distance threshold for haptic feedback on each pad + private const int bufferSize = 4; // Input smoothing buffer size + private const int gestureRadius = 30; // Gesture commit threshold (pixels) + private const double gestureFlushRate = 0.1; // Rate at which gesture flush decays + private const int velocityWindowSize = 20; // Velocity smoothing window size + private const double minGlideMagnitude = 0.8; // Minimum velocity to start glide + private const double minGlideVelocity = 0.15; // Minimum velocity to continue glide + private const double hapticTriggerDelta = 6000; // Distance required for haptic feedback on each pad + private const double hapticTriggerThreshold = 600; // Only triggers haptic if moved this much, prevents triggering on minor drifts // Functional constants (derived once) private readonly double gestureRadiusSq = gestureRadius * gestureRadius; private readonly double minGlideMagnitudeSq = minGlideMagnitude * minGlideMagnitude; private readonly double minGlideVelocitySq = minGlideVelocity * minGlideVelocity; + private readonly double hapticTriggerThresholdSq = hapticTriggerThreshold * hapticTriggerThreshold; // Runtime state RPad private Queue bufX = new(), bufY = new(); @@ -31,8 +33,21 @@ namespace SteamController.Devices private double velocitySumX = 0, velocitySumY = 0; // Runtime state haptics + private int hapticDriftBufferSize = 250; + private double hapticDeltaR = 0; + double[] hapticDriftRX = new double[250]; + double[] hapticDriftRY = new double[250]; + int hapticDriftRIndex = 0; + double hapticDriftSumRX = 0; + double hapticDriftSumRY = 0; + private double hapticDeltaL = 0; + double[] hapticDriftLX = new double[250]; + double[] hapticDriftLY = new double[250]; + int hapticDriftLIndex = 0; + double hapticDriftSumLX = 0; + double hapticDriftSumLY = 0; // Main movement logic public void MoveByFauxLizard(double dx, double dy, bool isTouched) @@ -137,20 +152,52 @@ namespace SteamController.Devices { if (isTouched) { - double finalMagSq = dx * dx + dy * dy; + // Update circular buffer and drift sums + hapticDriftSumRX -= hapticDriftRX[hapticDriftRIndex]; + hapticDriftSumRY -= hapticDriftRY[hapticDriftRIndex]; - if (hapticDeltaR < hapticTriggerDelta) - hapticDeltaR += Math.Sqrt(finalMagSq); + hapticDriftRX[hapticDriftRIndex] = dx; + hapticDriftRY[hapticDriftRIndex] = dy; - if (hapticDeltaR >= hapticTriggerDelta) + hapticDriftSumRX += dx; + hapticDriftSumRY += dy; + + hapticDriftRIndex = (hapticDriftRIndex + 1) % hapticDriftBufferSize; + + // Compute drift magnitude squared + double driftMagSq = hapticDriftSumRX * hapticDriftSumRX + hapticDriftSumRY * hapticDriftSumRY; + + if (driftMagSq > hapticTriggerThresholdSq) { - hapticDeltaR -= hapticTriggerDelta; + // Compute instantaneous movement magnitude + double deltaMagSq = dx * dx + dy * dy; + if(hapticDeltaR < hapticTriggerDelta) + hapticDeltaR += Math.Sqrt(deltaMagSq); - return true; + if (hapticDeltaR >= hapticTriggerDelta) + { + // Reset drift buffer and trigger haptic + hapticDriftSumRX = hapticDriftSumRY = 0; + hapticDriftRIndex = 0; + Array.Clear(hapticDriftRX, 0, hapticDriftBufferSize); + Array.Clear(hapticDriftRY, 0, hapticDriftBufferSize); + + hapticDeltaR -= hapticTriggerDelta; + + return true; + } } } else + { + // Reset everything on release + hapticDriftSumRX = hapticDriftSumRY = 0; + hapticDriftRIndex = 0; + Array.Clear(hapticDriftRX, 0, hapticDriftBufferSize); + Array.Clear(hapticDriftRY, 0, hapticDriftBufferSize); + hapticDeltaR = 0; + } return false; } @@ -159,20 +206,52 @@ namespace SteamController.Devices { if (isTouched) { - double finalMagSq = dx * dx + dy * dy; + // Update circular buffer and drift sums + hapticDriftSumLX -= hapticDriftLX[hapticDriftLIndex]; + hapticDriftSumLY -= hapticDriftLY[hapticDriftLIndex]; - if (hapticDeltaL < hapticTriggerDelta) - hapticDeltaL += Math.Sqrt(finalMagSq); + hapticDriftLX[hapticDriftLIndex] = dx; + hapticDriftLY[hapticDriftLIndex] = dy; - if (hapticDeltaL >= hapticTriggerDelta) + hapticDriftSumLX += dx; + hapticDriftSumLY += dy; + + hapticDriftLIndex = (hapticDriftLIndex + 1) % hapticDriftBufferSize; + + // Compute drift magnitude squared + double driftMagSq = hapticDriftSumLX * hapticDriftSumLX + hapticDriftSumLY * hapticDriftSumLY; + + if (driftMagSq > hapticTriggerThresholdSq) { - hapticDeltaL -= hapticTriggerDelta; + // Compute instantaneous movement magnitude + double deltaMagSq = dx * dx + dy * dy; + if (hapticDeltaL < hapticTriggerDelta) + hapticDeltaL += Math.Sqrt(deltaMagSq); - return true; + if (hapticDeltaL >= hapticTriggerDelta) + { + // Reset drift buffer and trigger haptic + hapticDriftSumLX = hapticDriftSumLY = 0; + hapticDriftLIndex = 0; + Array.Clear(hapticDriftLX, 0, hapticDriftBufferSize); + Array.Clear(hapticDriftLY, 0, hapticDriftBufferSize); + + hapticDeltaL -= hapticTriggerDelta; + + return true; + } } } else + { + // Reset everything on release + hapticDriftSumLX = hapticDriftSumLY = 0; + hapticDriftLIndex = 0; + Array.Clear(hapticDriftLX, 0, hapticDriftBufferSize); + Array.Clear(hapticDriftLY, 0, hapticDriftBufferSize); + hapticDeltaL = 0; + } return false; } diff --git a/SteamController/Profiles/Default/GuideShortcutsProfile.cs b/SteamController/Profiles/Default/GuideShortcutsProfile.cs index 72fb822..3726411 100644 --- a/SteamController/Profiles/Default/GuideShortcutsProfile.cs +++ b/SteamController/Profiles/Default/GuideShortcutsProfile.cs @@ -153,12 +153,12 @@ namespace SteamController.Profiles.Default // Send haptic for pad drag if (c.Mouse.HapticDragLFauxLizard( c.Steam.LPadX.GetDeltaValue( - 150, + 32766, Devices.DeltaValueMode.Delta, 10 ), c.Steam.LPadY.GetDeltaValue( - 150, + 32766, Devices.DeltaValueMode.Delta, 10 ), @@ -217,12 +217,12 @@ namespace SteamController.Profiles.Default // Send haptic for pad drag if (c.Mouse.HapticDragRFauxLizard( c.Steam.RPadX.GetDeltaValue( - 150, + 32766, Devices.DeltaValueMode.Delta, 10 ), c.Steam.RPadY.GetDeltaValue( - 150, + 32766, Devices.DeltaValueMode.Delta, 10 ),