JoystickButtonAssigner.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. using OpenTK.Input;
  2. using Ryujinx.Common.Configuration.Hid;
  3. using System.Collections.Generic;
  4. using System;
  5. using System.IO;
  6. namespace Ryujinx.Ui.Input
  7. {
  8. class JoystickButtonAssigner : ButtonAssigner
  9. {
  10. private int _index;
  11. private double _triggerThreshold;
  12. private JoystickState _currState;
  13. private JoystickState _prevState;
  14. private JoystickButtonDetector _detector;
  15. public JoystickButtonAssigner(int index, double triggerThreshold)
  16. {
  17. _index = index;
  18. _triggerThreshold = triggerThreshold;
  19. _detector = new JoystickButtonDetector();
  20. }
  21. public void Init()
  22. {
  23. _currState = Joystick.GetState(_index);
  24. _prevState = _currState;
  25. }
  26. public void ReadInput()
  27. {
  28. _prevState = _currState;
  29. _currState = Joystick.GetState(_index);
  30. CollectButtonStats();
  31. }
  32. public bool HasAnyButtonPressed()
  33. {
  34. return _detector.HasAnyButtonPressed();
  35. }
  36. public bool ShouldCancel()
  37. {
  38. return Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsAnyKeyDown;
  39. }
  40. public string GetPressedButton()
  41. {
  42. List<ControllerInputId> pressedButtons = _detector.GetPressedButtons();
  43. // Reverse list so axis button take precedence when more than one button is recognized.
  44. pressedButtons.Reverse();
  45. return pressedButtons.Count > 0 ? pressedButtons[0].ToString() : "";
  46. }
  47. private void CollectButtonStats()
  48. {
  49. JoystickCapabilities capabilities = Joystick.GetCapabilities(_index);
  50. ControllerInputId pressedButton;
  51. // Buttons
  52. for (int i = 0; i != capabilities.ButtonCount; i++)
  53. {
  54. if (_currState.IsButtonDown(i) && _prevState.IsButtonUp(i))
  55. {
  56. Enum.TryParse($"Button{i}", out pressedButton);
  57. _detector.AddInput(pressedButton, 1);
  58. }
  59. if (_currState.IsButtonUp(i) && _prevState.IsButtonDown(i))
  60. {
  61. Enum.TryParse($"Button{i}", out pressedButton);
  62. _detector.AddInput(pressedButton, -1);
  63. }
  64. }
  65. // Axis
  66. for (int i = 0; i != capabilities.AxisCount; i++)
  67. {
  68. float axisValue = _currState.GetAxis(i);
  69. Enum.TryParse($"Axis{i}", out pressedButton);
  70. _detector.AddInput(pressedButton, axisValue);
  71. }
  72. // Hats
  73. for (int i = 0; i != capabilities.HatCount; i++)
  74. {
  75. string currPos = GetHatPosition(_currState.GetHat((JoystickHat)i));
  76. string prevPos = GetHatPosition(_prevState.GetHat((JoystickHat)i));
  77. if (currPos == prevPos)
  78. {
  79. continue;
  80. }
  81. if (currPos != "")
  82. {
  83. Enum.TryParse($"Hat{i}{currPos}", out pressedButton);
  84. _detector.AddInput(pressedButton, 1);
  85. }
  86. if (prevPos != "")
  87. {
  88. Enum.TryParse($"Hat{i}{prevPos}", out pressedButton);
  89. _detector.AddInput(pressedButton, -1);
  90. }
  91. }
  92. }
  93. private string GetHatPosition(JoystickHatState hatState)
  94. {
  95. if (hatState.IsUp) return "Up";
  96. if (hatState.IsDown) return "Down";
  97. if (hatState.IsLeft) return "Left";
  98. if (hatState.IsRight) return "Right";
  99. return "";
  100. }
  101. private class JoystickButtonDetector
  102. {
  103. private Dictionary<ControllerInputId, InputSummary> _stats;
  104. public JoystickButtonDetector()
  105. {
  106. _stats = new Dictionary<ControllerInputId, InputSummary>();
  107. }
  108. public bool HasAnyButtonPressed()
  109. {
  110. foreach (var inputSummary in _stats.Values)
  111. {
  112. if (checkButtonPressed(inputSummary))
  113. {
  114. return true;
  115. }
  116. }
  117. return false;
  118. }
  119. public List<ControllerInputId> GetPressedButtons()
  120. {
  121. List<ControllerInputId> pressedButtons = new List<ControllerInputId>();
  122. foreach (var kvp in _stats)
  123. {
  124. if (!checkButtonPressed(kvp.Value))
  125. {
  126. continue;
  127. }
  128. pressedButtons.Add(kvp.Key);
  129. }
  130. return pressedButtons;
  131. }
  132. public void AddInput(ControllerInputId button, float value)
  133. {
  134. InputSummary inputSummary;
  135. if (!_stats.TryGetValue(button, out inputSummary))
  136. {
  137. inputSummary = new InputSummary();
  138. _stats.Add(button, inputSummary);
  139. }
  140. inputSummary.AddInput(value);
  141. }
  142. public override string ToString()
  143. {
  144. TextWriter writer = new StringWriter();
  145. foreach (var kvp in _stats)
  146. {
  147. writer.WriteLine($"Button {kvp.Key} -> {kvp.Value}");
  148. }
  149. return writer.ToString();
  150. }
  151. private bool checkButtonPressed(InputSummary sequence)
  152. {
  153. float distance = Math.Abs(sequence.Min - sequence.Avg) + Math.Abs(sequence.Max - sequence.Avg);
  154. return distance > 1.5; // distance range [0, 2]
  155. }
  156. }
  157. private class InputSummary
  158. {
  159. public float Min, Max, Sum, Avg;
  160. public int NumSamples;
  161. public InputSummary()
  162. {
  163. Min = float.MaxValue;
  164. Max = float.MinValue;
  165. Sum = 0;
  166. NumSamples = 0;
  167. Avg = 0;
  168. }
  169. public void AddInput(float value)
  170. {
  171. Min = Math.Min(Min, value);
  172. Max = Math.Max(Max, value);
  173. Sum += value;
  174. NumSamples += 1;
  175. Avg = Sum / NumSamples;
  176. }
  177. public override string ToString()
  178. {
  179. return $"Avg: {Avg} Min: {Min} Max: {Max} Sum: {Sum} NumSamples: {NumSamples}";
  180. }
  181. }
  182. }
  183. }