mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2025-04-27 16:00:51 -07:00
* Refactor * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Update src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Update src/Ryujinx.Input/ButtonValueType.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Add empty line * Requested renames * Update src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Make parent models private readonly * Fix ControllerInputView * Make line shorter * Mac keys in locale * Double line break * Fix build * Get rid of _isValid * Fix potential race condition * Rename HasAnyButtonPressed to IsAnyButtonPressed * Use switches * Simplify enumeration --------- Co-authored-by: Ac_K <Acoustik666@gmail.com> Co-authored-by: gdkchan <gab.dark.100@gmail.com> Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
193 lines
5.5 KiB
C#
193 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace Ryujinx.Input.Assigner
|
|
{
|
|
/// <summary>
|
|
/// <see cref="IButtonAssigner"/> implementation for regular <see cref="IGamepad"/>.
|
|
/// </summary>
|
|
public class GamepadButtonAssigner : IButtonAssigner
|
|
{
|
|
private readonly IGamepad _gamepad;
|
|
|
|
private GamepadStateSnapshot _currState;
|
|
|
|
private GamepadStateSnapshot _prevState;
|
|
|
|
private readonly JoystickButtonDetector _detector;
|
|
|
|
private readonly bool _forStick;
|
|
|
|
public GamepadButtonAssigner(IGamepad gamepad, float triggerThreshold, bool forStick)
|
|
{
|
|
_gamepad = gamepad;
|
|
_detector = new JoystickButtonDetector();
|
|
_forStick = forStick;
|
|
|
|
_gamepad?.SetTriggerThreshold(triggerThreshold);
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
if (_gamepad != null)
|
|
{
|
|
_currState = _gamepad.GetStateSnapshot();
|
|
_prevState = _currState;
|
|
}
|
|
}
|
|
|
|
public void ReadInput()
|
|
{
|
|
if (_gamepad != null)
|
|
{
|
|
_prevState = _currState;
|
|
_currState = _gamepad.GetStateSnapshot();
|
|
}
|
|
|
|
CollectButtonStats();
|
|
}
|
|
|
|
public bool IsAnyButtonPressed()
|
|
{
|
|
return _detector.IsAnyButtonPressed();
|
|
}
|
|
|
|
public bool ShouldCancel()
|
|
{
|
|
return _gamepad == null || !_gamepad.IsConnected;
|
|
}
|
|
|
|
public Button? GetPressedButton()
|
|
{
|
|
IEnumerable<GamepadButtonInputId> pressedButtons = _detector.GetPressedButtons();
|
|
|
|
return !_forStick ? new(pressedButtons.FirstOrDefault()) : new((StickInputId)pressedButtons.FirstOrDefault());
|
|
}
|
|
|
|
private void CollectButtonStats()
|
|
{
|
|
if (_forStick)
|
|
{
|
|
for (StickInputId inputId = StickInputId.Left; inputId < StickInputId.Count; inputId++)
|
|
{
|
|
(float x, float y) = _currState.GetStick(inputId);
|
|
|
|
float value;
|
|
|
|
if (x != 0.0f)
|
|
{
|
|
value = x;
|
|
}
|
|
else if (y != 0.0f)
|
|
{
|
|
value = y;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_detector.AddInput((GamepadButtonInputId)inputId, value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (GamepadButtonInputId inputId = GamepadButtonInputId.A; inputId < GamepadButtonInputId.Count; inputId++)
|
|
{
|
|
if (_currState.IsPressed(inputId) && !_prevState.IsPressed(inputId))
|
|
{
|
|
_detector.AddInput(inputId, 1);
|
|
}
|
|
|
|
if (!_currState.IsPressed(inputId) && _prevState.IsPressed(inputId))
|
|
{
|
|
_detector.AddInput(inputId, -1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private class JoystickButtonDetector
|
|
{
|
|
private readonly Dictionary<GamepadButtonInputId, InputSummary> _stats;
|
|
|
|
public JoystickButtonDetector()
|
|
{
|
|
_stats = new Dictionary<GamepadButtonInputId, InputSummary>();
|
|
}
|
|
|
|
public bool IsAnyButtonPressed()
|
|
{
|
|
return _stats.Values.Any(CheckButtonPressed);
|
|
}
|
|
|
|
public IEnumerable<GamepadButtonInputId> GetPressedButtons()
|
|
{
|
|
return _stats.Where(kvp => CheckButtonPressed(kvp.Value)).Select(kvp => kvp.Key);
|
|
}
|
|
|
|
public void AddInput(GamepadButtonInputId button, float value)
|
|
{
|
|
|
|
if (!_stats.TryGetValue(button, out InputSummary inputSummary))
|
|
{
|
|
inputSummary = new InputSummary();
|
|
_stats.Add(button, inputSummary);
|
|
}
|
|
|
|
inputSummary.AddInput(value);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
StringWriter writer = new();
|
|
|
|
foreach (var kvp in _stats)
|
|
{
|
|
writer.WriteLine($"Button {kvp.Key} -> {kvp.Value}");
|
|
}
|
|
|
|
return writer.ToString();
|
|
}
|
|
|
|
private bool CheckButtonPressed(InputSummary sequence)
|
|
{
|
|
float distance = Math.Abs(sequence.Min - sequence.Avg) + Math.Abs(sequence.Max - sequence.Avg);
|
|
return distance > 1.5; // distance range [0, 2]
|
|
}
|
|
}
|
|
|
|
private class InputSummary
|
|
{
|
|
public float Min, Max, Sum, Avg;
|
|
|
|
public int NumSamples;
|
|
|
|
public InputSummary()
|
|
{
|
|
Min = float.MaxValue;
|
|
Max = float.MinValue;
|
|
Sum = 0;
|
|
NumSamples = 0;
|
|
Avg = 0;
|
|
}
|
|
|
|
public void AddInput(float value)
|
|
{
|
|
Min = Math.Min(Min, value);
|
|
Max = Math.Max(Max, value);
|
|
Sum += value;
|
|
NumSamples += 1;
|
|
Avg = Sum / NumSamples;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"Avg: {Avg} Min: {Min} Max: {Max} Sum: {Sum} NumSamples: {NumSamples}";
|
|
}
|
|
}
|
|
}
|
|
}
|