NpadManager.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using Ryujinx.Common.Configuration.Hid;
  2. using Ryujinx.Common.Configuration.Hid.Controller;
  3. using Ryujinx.Common.Configuration.Hid.Keyboard;
  4. using Ryujinx.HLE.HOS.Services.Hid;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Diagnostics;
  8. using System.Runtime.CompilerServices;
  9. using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client;
  10. using Switch = Ryujinx.HLE.Switch;
  11. namespace Ryujinx.Input.HLE
  12. {
  13. public class NpadManager : IDisposable
  14. {
  15. private CemuHookClient _cemuHookClient;
  16. private object _lock = new object();
  17. private bool _blockInputUpdates;
  18. private const int MaxControllers = 9;
  19. private NpadController[] _controllers;
  20. private readonly IGamepadDriver _keyboardDriver;
  21. private readonly IGamepadDriver _gamepadDriver;
  22. private bool _isDisposed;
  23. private List<InputConfig> _inputConfig;
  24. private bool _enableKeyboard;
  25. private Switch _device;
  26. public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver)
  27. {
  28. _controllers = new NpadController[MaxControllers];
  29. _cemuHookClient = new CemuHookClient(this);
  30. _keyboardDriver = keyboardDriver;
  31. _gamepadDriver = gamepadDriver;
  32. _inputConfig = new List<InputConfig>();
  33. _enableKeyboard = false;
  34. _gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
  35. _gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
  36. }
  37. private void RefreshInputConfigForHLE()
  38. {
  39. lock (_lock)
  40. {
  41. _device.Hid.RefreshInputConfig(_inputConfig);
  42. }
  43. }
  44. private void HandleOnGamepadDisconnected(string obj)
  45. {
  46. // Force input reload
  47. ReloadConfiguration(_inputConfig, _enableKeyboard);
  48. }
  49. private void HandleOnGamepadConnected(string id)
  50. {
  51. // Force input reload
  52. ReloadConfiguration(_inputConfig, _enableKeyboard);
  53. }
  54. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  55. private bool DriverConfigurationUpdate(ref NpadController controller, InputConfig config)
  56. {
  57. IGamepadDriver targetDriver = _gamepadDriver;
  58. if (config is StandardControllerInputConfig)
  59. {
  60. targetDriver = _gamepadDriver;
  61. }
  62. else if (config is StandardKeyboardInputConfig)
  63. {
  64. targetDriver = _keyboardDriver;
  65. }
  66. Debug.Assert(targetDriver != null, "Unknown input configuration!");
  67. if (controller.GamepadDriver != targetDriver || controller.Id != config.Id)
  68. {
  69. return controller.UpdateDriverConfiguration(targetDriver, config);
  70. }
  71. else
  72. {
  73. return controller.GamepadDriver != null;
  74. }
  75. }
  76. public void ReloadConfiguration(List<InputConfig> inputConfig, bool enableKeyboard)
  77. {
  78. lock (_lock)
  79. {
  80. for (int i = 0; i < _controllers.Length; i++)
  81. {
  82. _controllers[i]?.Dispose();
  83. _controllers[i] = null;
  84. }
  85. foreach (InputConfig inputConfigEntry in inputConfig)
  86. {
  87. NpadController controller = new NpadController(_cemuHookClient);
  88. bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry);
  89. if (!isValid)
  90. {
  91. controller.Dispose();
  92. }
  93. else
  94. {
  95. _controllers[(int)inputConfigEntry.PlayerIndex] = controller;
  96. }
  97. }
  98. _inputConfig = inputConfig;
  99. _enableKeyboard = enableKeyboard;
  100. _device.Hid.RefreshInputConfig(inputConfig);
  101. }
  102. }
  103. public void UnblockInputUpdates()
  104. {
  105. lock (_lock)
  106. {
  107. _blockInputUpdates = false;
  108. }
  109. }
  110. public void BlockInputUpdates()
  111. {
  112. lock (_lock)
  113. {
  114. _blockInputUpdates = true;
  115. }
  116. }
  117. public void Initialize(Switch device, List<InputConfig> inputConfig, bool enableKeyboard)
  118. {
  119. _device = device;
  120. _device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE;
  121. ReloadConfiguration(inputConfig, enableKeyboard);
  122. }
  123. public void Update()
  124. {
  125. lock (_lock)
  126. {
  127. List<GamepadInput> hleInputStates = new List<GamepadInput>();
  128. List<SixAxisInput> hleMotionStates = new List<SixAxisInput>(NpadDevices.MaxControllers);
  129. KeyboardInput? hleKeyboardInput = null;
  130. foreach (InputConfig inputConfig in _inputConfig)
  131. {
  132. GamepadInput inputState = default;
  133. SixAxisInput motionState = default;
  134. NpadController controller = _controllers[(int)inputConfig.PlayerIndex];
  135. // Do we allow input updates and is a controller connected?
  136. if (!_blockInputUpdates && controller != null)
  137. {
  138. DriverConfigurationUpdate(ref controller, inputConfig);
  139. controller.UpdateUserConfiguration(inputConfig);
  140. controller.Update();
  141. inputState = controller.GetHLEInputState();
  142. inputState.Buttons |= _device.Hid.UpdateStickButtons(inputState.LStick, inputState.RStick);
  143. motionState = controller.GetHLEMotionState();
  144. if (_enableKeyboard)
  145. {
  146. hleKeyboardInput = controller.GetHLEKeyboardInput();
  147. }
  148. }
  149. else
  150. {
  151. // Ensure that orientation isn't null
  152. motionState.Orientation = new float[9];
  153. }
  154. inputState.PlayerId = (Ryujinx.HLE.HOS.Services.Hid.PlayerIndex)inputConfig.PlayerIndex;
  155. motionState.PlayerId = (Ryujinx.HLE.HOS.Services.Hid.PlayerIndex)inputConfig.PlayerIndex;
  156. hleInputStates.Add(inputState);
  157. hleMotionStates.Add(motionState);
  158. }
  159. _device.Hid.Npads.Update(hleInputStates);
  160. _device.Hid.Npads.UpdateSixAxis(hleMotionStates);
  161. if (hleKeyboardInput.HasValue)
  162. {
  163. _device.Hid.Keyboard.Update(hleKeyboardInput.Value);
  164. }
  165. _device.TamperMachine.UpdateInput(hleInputStates);
  166. }
  167. }
  168. internal InputConfig GetPlayerInputConfigByIndex(int index)
  169. {
  170. lock (_lock)
  171. {
  172. return _inputConfig.Find(x => x.PlayerIndex == (Ryujinx.Common.Configuration.Hid.PlayerIndex)index);
  173. }
  174. }
  175. protected virtual void Dispose(bool disposing)
  176. {
  177. if (disposing)
  178. {
  179. lock (_lock)
  180. {
  181. if (!_isDisposed)
  182. {
  183. _cemuHookClient.Dispose();
  184. _gamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
  185. _gamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
  186. for (int i = 0; i < _controllers.Length; i++)
  187. {
  188. _controllers[i]?.Dispose();
  189. }
  190. _isDisposed = true;
  191. }
  192. }
  193. }
  194. }
  195. public void Dispose()
  196. {
  197. Dispose(true);
  198. }
  199. }
  200. }