199 lines
5.6 KiB
C#
199 lines
5.6 KiB
C#
|
using System.Collections.Generic;
|
||
|
using System;
|
||
|
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 IGamepad _gamepad;
|
||
|
|
||
|
private GamepadStateSnapshot _currState;
|
||
|
|
||
|
private GamepadStateSnapshot _prevState;
|
||
|
|
||
|
private JoystickButtonDetector _detector;
|
||
|
|
||
|
private 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 HasAnyButtonPressed()
|
||
|
{
|
||
|
return _detector.HasAnyButtonPressed();
|
||
|
}
|
||
|
|
||
|
public bool ShouldCancel()
|
||
|
{
|
||
|
return _gamepad == null || !_gamepad.IsConnected;
|
||
|
}
|
||
|
|
||
|
public string GetPressedButton()
|
||
|
{
|
||
|
IEnumerable<GamepadButtonInputId> pressedButtons = _detector.GetPressedButtons();
|
||
|
|
||
|
if (pressedButtons.Any())
|
||
|
{
|
||
|
return !_forStick ? pressedButtons.First().ToString() : ((StickInputId)pressedButtons.First()).ToString();
|
||
|
}
|
||
|
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
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 Dictionary<GamepadButtonInputId, InputSummary> _stats;
|
||
|
|
||
|
public JoystickButtonDetector()
|
||
|
{
|
||
|
_stats = new Dictionary<GamepadButtonInputId, InputSummary>();
|
||
|
}
|
||
|
|
||
|
public bool HasAnyButtonPressed()
|
||
|
{
|
||
|
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)
|
||
|
{
|
||
|
InputSummary inputSummary;
|
||
|
|
||
|
if (!_stats.TryGetValue(button, out inputSummary))
|
||
|
{
|
||
|
inputSummary = new InputSummary();
|
||
|
_stats.Add(button, inputSummary);
|
||
|
}
|
||
|
|
||
|
inputSummary.AddInput(value);
|
||
|
}
|
||
|
|
||
|
public override string ToString()
|
||
|
{
|
||
|
StringWriter writer = new StringWriter();
|
||
|
|
||
|
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}";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|