diff options
Diffstat (limited to 'src/Ryujinx.Input/HLE/NpadManager.cs')
-rw-r--r-- | src/Ryujinx.Input/HLE/NpadManager.cs | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs new file mode 100644 index 00000000..5290ecbb --- /dev/null +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -0,0 +1,320 @@ +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.HLE.HOS.Services.Hid; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client; +using Switch = Ryujinx.HLE.Switch; + +namespace Ryujinx.Input.HLE +{ + public class NpadManager : IDisposable + { + private CemuHookClient _cemuHookClient; + + private object _lock = new object(); + + private bool _blockInputUpdates; + + private const int MaxControllers = 9; + + private NpadController[] _controllers; + + private readonly IGamepadDriver _keyboardDriver; + private readonly IGamepadDriver _gamepadDriver; + private readonly IGamepadDriver _mouseDriver; + private bool _isDisposed; + + private List<InputConfig> _inputConfig; + private bool _enableKeyboard; + private bool _enableMouse; + private Switch _device; + + public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver) + { + _controllers = new NpadController[MaxControllers]; + _cemuHookClient = new CemuHookClient(this); + + _keyboardDriver = keyboardDriver; + _gamepadDriver = gamepadDriver; + _mouseDriver = mouseDriver; + _inputConfig = new List<InputConfig>(); + + _gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; + _gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; + } + + private void RefreshInputConfigForHLE() + { + lock (_lock) + { + List<InputConfig> validInputs = new List<InputConfig>(); + foreach (var inputConfigEntry in _inputConfig) + { + if (_controllers[(int)inputConfigEntry.PlayerIndex] != null) + { + validInputs.Add(inputConfigEntry); + } + } + + _device.Hid.RefreshInputConfig(validInputs); + } + } + + private void HandleOnGamepadDisconnected(string obj) + { + // Force input reload + ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); + } + + private void HandleOnGamepadConnected(string id) + { + // Force input reload + ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool DriverConfigurationUpdate(ref NpadController controller, InputConfig config) + { + IGamepadDriver targetDriver = _gamepadDriver; + + if (config is StandardControllerInputConfig) + { + targetDriver = _gamepadDriver; + } + else if (config is StandardKeyboardInputConfig) + { + targetDriver = _keyboardDriver; + } + + Debug.Assert(targetDriver != null, "Unknown input configuration!"); + + if (controller.GamepadDriver != targetDriver || controller.Id != config.Id) + { + return controller.UpdateDriverConfiguration(targetDriver, config); + } + else + { + return controller.GamepadDriver != null; + } + } + + public void ReloadConfiguration(List<InputConfig> inputConfig, bool enableKeyboard, bool enableMouse) + { + lock (_lock) + { + for (int i = 0; i < _controllers.Length; i++) + { + _controllers[i]?.Dispose(); + _controllers[i] = null; + } + + List<InputConfig> validInputs = new List<InputConfig>(); + + foreach (InputConfig inputConfigEntry in inputConfig) + { + NpadController controller = new NpadController(_cemuHookClient); + + bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry); + + if (!isValid) + { + controller.Dispose(); + } + else + { + _controllers[(int)inputConfigEntry.PlayerIndex] = controller; + validInputs.Add(inputConfigEntry); + } + } + + _inputConfig = inputConfig; + _enableKeyboard = enableKeyboard; + _enableMouse = enableMouse; + + _device.Hid.RefreshInputConfig(validInputs); + } + } + + public void UnblockInputUpdates() + { + lock (_lock) + { + _blockInputUpdates = false; + } + } + + public void BlockInputUpdates() + { + lock (_lock) + { + _blockInputUpdates = true; + } + } + + public void Initialize(Switch device, List<InputConfig> inputConfig, bool enableKeyboard, bool enableMouse) + { + _device = device; + _device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE; + + ReloadConfiguration(inputConfig, enableKeyboard, enableMouse); + } + + public void Update(float aspectRatio = 1) + { + lock (_lock) + { + List<GamepadInput> hleInputStates = new List<GamepadInput>(); + List<SixAxisInput> hleMotionStates = new List<SixAxisInput>(NpadDevices.MaxControllers); + + KeyboardInput? hleKeyboardInput = null; + + foreach (InputConfig inputConfig in _inputConfig) + { + GamepadInput inputState = default; + (SixAxisInput, SixAxisInput) motionState = default; + + NpadController controller = _controllers[(int)inputConfig.PlayerIndex]; + Ryujinx.HLE.HOS.Services.Hid.PlayerIndex playerIndex = (Ryujinx.HLE.HOS.Services.Hid.PlayerIndex)inputConfig.PlayerIndex; + + bool isJoyconPair = false; + + // Do we allow input updates and is a controller connected? + if (!_blockInputUpdates && controller != null) + { + DriverConfigurationUpdate(ref controller, inputConfig); + + controller.UpdateUserConfiguration(inputConfig); + controller.Update(); + controller.UpdateRumble(_device.Hid.Npads.GetRumbleQueue(playerIndex)); + + inputState = controller.GetHLEInputState(); + + inputState.Buttons |= _device.Hid.UpdateStickButtons(inputState.LStick, inputState.RStick); + + isJoyconPair = inputConfig.ControllerType == Common.Configuration.Hid.ControllerType.JoyconPair; + + var altMotionState = isJoyconPair ? controller.GetHLEMotionState(true) : default; + + motionState = (controller.GetHLEMotionState(), altMotionState); + + if (_enableKeyboard) + { + hleKeyboardInput = controller.GetHLEKeyboardInput(); + } + } + else + { + // Ensure that orientation isn't null + motionState.Item1.Orientation = new float[9]; + } + + inputState.PlayerId = playerIndex; + motionState.Item1.PlayerId = playerIndex; + + hleInputStates.Add(inputState); + hleMotionStates.Add(motionState.Item1); + + if (isJoyconPair && !motionState.Item2.Equals(default)) + { + motionState.Item2.PlayerId = playerIndex; + + hleMotionStates.Add(motionState.Item2); + } + } + + _device.Hid.Npads.Update(hleInputStates); + _device.Hid.Npads.UpdateSixAxis(hleMotionStates); + + if (hleKeyboardInput.HasValue) + { + _device.Hid.Keyboard.Update(hleKeyboardInput.Value); + } + + if (_enableMouse) + { + var mouse = _mouseDriver.GetGamepad("0") as IMouse; + + var mouseInput = IMouse.GetMouseStateSnapshot(mouse); + + uint buttons = 0; + + if (mouseInput.IsPressed(MouseButton.Button1)) + { + buttons |= 1 << 0; + } + + if (mouseInput.IsPressed(MouseButton.Button2)) + { + buttons |= 1 << 1; + } + + if (mouseInput.IsPressed(MouseButton.Button3)) + { + buttons |= 1 << 2; + } + + if (mouseInput.IsPressed(MouseButton.Button4)) + { + buttons |= 1 << 3; + } + + if (mouseInput.IsPressed(MouseButton.Button5)) + { + buttons |= 1 << 4; + } + + var position = IMouse.GetScreenPosition(mouseInput.Position, mouse.ClientSize, aspectRatio); + + _device.Hid.Mouse.Update((int)position.X, (int)position.Y, buttons, (int)mouseInput.Scroll.X, (int)mouseInput.Scroll.Y, true); + } + else + { + _device.Hid.Mouse.Update(0, 0); + } + + _device.TamperMachine.UpdateInput(hleInputStates); + } + } + + internal InputConfig GetPlayerInputConfigByIndex(int index) + { + lock (_lock) + { + return _inputConfig.Find(x => x.PlayerIndex == (Ryujinx.Common.Configuration.Hid.PlayerIndex)index); + } + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + lock (_lock) + { + if (!_isDisposed) + { + _cemuHookClient.Dispose(); + + _gamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; + _gamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; + + for (int i = 0; i < _controllers.Length; i++) + { + _controllers[i]?.Dispose(); + } + + _isDisposed = true; + } + } + } + } + + public void Dispose() + { + Dispose(true); + } + } +} |