From ab4bb0a885a87259bb18d34762b41cbc9bd926ac Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sat, 1 Feb 2025 19:36:55 +0100 Subject: [PATCH] refactor: remove auto-assign option from NpadManager initialization and update related components --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 2 +- src/Ryujinx.Input/HLE/NpadManager.cs | 106 +++-------- src/Ryujinx/AppHost.cs | 2 +- src/Ryujinx/AutoAssignController.cs | 179 ++++++++++++++++++ src/Ryujinx/Headless/HeadlessRyujinx.cs | 3 +- src/Ryujinx/Headless/Windows/WindowBase.cs | 4 +- src/Ryujinx/Program.cs | 3 +- .../UI/ViewModels/Input/InputViewModel.cs | 6 +- src/Ryujinx/UI/Windows/MainWindow.axaml.cs | 2 + 9 files changed, 222 insertions(+), 85 deletions(-) create mode 100644 src/Ryujinx/AutoAssignController.cs diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index 95d46e4ce..68664e8c3 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -97,7 +97,7 @@ namespace Ryujinx.Input.SDL2 private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId) { - Logger.Warning?.Print(LogClass.Application, "(SDL2) Joystick connected: " + joystickDeviceId + " " + joystickInstanceId); + Logger.Warning?.Print(LogClass.Application, "(SDL2GamepadDriver) Joystick connected: " + joystickDeviceId + " " + joystickInstanceId); if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE) { if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId)) diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 6ae5ce83e..d5b3dec94 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -89,14 +89,14 @@ namespace Ryujinx.Input.HLE } } - ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse, _enableAutoAssign); + ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); } } private void HandleOnGamepadConnected(string id) { // Force input reload - ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse, _enableAutoAssign); + ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -123,86 +123,41 @@ namespace Ryujinx.Input.HLE return controller.GamepadDriver != null; } - public void ReloadConfiguration(List inputConfig, bool enableKeyboard, bool enableMouse, bool enableAutoAssign) + public void ReloadConfiguration(List inputConfig, bool enableKeyboard, bool enableMouse) { lock (_lock) { NpadController[] oldControllers = _controllers.ToArray(); List validInputs = new(); - - // if auto assign is disabled, we want to keep the old logic with profiles. - if (!enableAutoAssign) + + foreach (InputConfig inputConfigEntry in inputConfig) { - foreach (InputConfig inputConfigEntry in inputConfig) + NpadController controller; + int index = (int)inputConfigEntry.PlayerIndex; + + if (oldControllers[index] != null) { - NpadController controller; - int index = (int)inputConfigEntry.PlayerIndex; - - if (oldControllers[index] != null) - { - // Try reuse the existing controller. - controller = oldControllers[index]; - oldControllers[index] = null; - } - else - { - controller = new(_cemuHookClient); - } - - bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry); - - if (!isValid) - { - _controllers[index] = null; - controller.Dispose(); - } - else - { - _controllers[index] = controller; - validInputs.Add(inputConfigEntry); - } + // Try reuse the existing controller. + controller = oldControllers[index]; + oldControllers[index] = null; } - } - else - { - List controllers = _gamepadDriver.GetGamepads().ToList(); - - foreach (IGamepad activeController in controllers) + else { - NpadController controller; - int index = controllers.FindIndex(x => x == activeController); - - // Also if old controller exists, try to reuse it (and create their config too). - InputConfig config = CreateConfigFromController(activeController); - - config.PlayerIndex = (Common.Configuration.Hid.PlayerIndex)index; - - if (oldControllers[index] != null) - { - // Try reuse the existing controller. - controller = oldControllers[index]; - oldControllers[index] = null; - } - else - { - controller = new(_cemuHookClient); - } - - // TODO: call function to get config from controller here - - bool isValid = DriverConfigurationUpdate(ref controller, config); - - if (!isValid) - { - _controllers[index] = null; - controller.Dispose(); - } - else - { - _controllers[index] = controller; - validInputs.Add(config); - } + controller = new(_cemuHookClient); + } + + bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry); + + if (!isValid) + { + _controllers[index] = null; + controller.Dispose(); + } + else + { + _controllers[index] = controller; + validInputs.Add(inputConfigEntry); } } @@ -214,10 +169,9 @@ namespace Ryujinx.Input.HLE oldControllers[i] = null; } - _inputConfig = (enableAutoAssign) ? validInputs : inputConfig; + _inputConfig = inputConfig; _enableKeyboard = enableKeyboard; _enableMouse = enableMouse; - _enableAutoAssign = enableAutoAssign; _device.Hid.RefreshInputConfig(validInputs); @@ -350,12 +304,12 @@ namespace Ryujinx.Input.HLE } } - public void Initialize(Switch device, List inputConfig, bool enableKeyboard, bool enableMouse, bool enableAutoAssign) + public void Initialize(Switch device, List inputConfig, bool enableKeyboard, bool enableMouse) { _device = device; _device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE; - ReloadConfiguration(inputConfig, enableKeyboard, enableMouse, enableAutoAssign); + ReloadConfiguration(inputConfig, enableKeyboard, enableMouse); } public void Update(float aspectRatio = 1) diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index cfb3b63bd..d161644c0 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -483,7 +483,7 @@ namespace Ryujinx.Ava DisplaySleep.Prevent(); - NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse, ConfigurationState.Instance.Hid.EnableAutoAssign); + NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); TouchScreenManager.Initialize(Device); _viewModel.IsGameRunning = true; diff --git a/src/Ryujinx/AutoAssignController.cs b/src/Ryujinx/AutoAssignController.cs new file mode 100644 index 000000000..76d7b2b01 --- /dev/null +++ b/src/Ryujinx/AutoAssignController.cs @@ -0,0 +1,179 @@ +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; +using Ryujinx.Ava.Utilities.Configuration; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Controller.Motion; +using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; +using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; +using Ryujinx.Common.Logging; +using Ryujinx.Input; +using Ryujinx.Input.HLE; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.Ava +{ + public class AutoAssignController + { + private readonly InputManager _inputManager; + private readonly ConfigurationState _configurationState; + + private readonly IGamepad[] _controllers; + + public AutoAssignController(InputManager inputManager) + { + _configurationState = ConfigurationState.Instance; + _inputManager = inputManager; + _inputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; + _inputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; + } + + public void RefreshControllers(List inputConfig = null) + { + if (!_configurationState.Hid.EnableAutoAssign) return; + + List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); + + if (controllers.Count == 0) return; + + MainWindow _mainWindow = RyujinxApp.MainWindow; + + // Get every controller config and update the configuration state + List newConfig = new(); + List oldConfigs = (inputConfig != null) ? inputConfig : ConfigurationState.Instance.Hid.InputConfig.Value.Where(x => x != null).ToList(); + + int index = 0; + foreach (var controller in controllers) + { + InputConfig config = oldConfigs.FirstOrDefault(x => x.Id == controller.Id) ?? CreateConfigFromController(controller); + config.PlayerIndex = (PlayerIndex)index; + newConfig.Add(config); + index++; + } + + _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); + + ConfigurationState.Instance.Hid.InputConfig.Value = newConfig; + } + + private void HandleOnGamepadConnected(string id) + { + Logger.Warning?.Print(LogClass.Application, $"Gamepad disconnected: {id}"); + RefreshControllers(); + } + + private void HandleOnGamepadDisconnected(string id) + { + Logger.Warning?.Print(LogClass.Application, $"Gamepad disconnected: {id}"); + RefreshControllers(); + } + + private InputConfig CreateConfigFromController(IGamepad controller) + { + if (controller == null) return null; + + Logger.Warning?.Print(LogClass.Application, $"Creating config for controller: {controller.Id}"); + + string id = controller.Id.Split(" ")[0]; + bool isNintendoStyle = controller.Name.Contains("Nintendo"); + ControllerType controllerType; + + if (isNintendoStyle && !controller.Name.Contains("(L/R)")) + { + if (controller.Name.Contains("(L)")) + { + controllerType = ControllerType.JoyconLeft; + } + else if (controller.Name.Contains("(R)")) + { + controllerType = ControllerType.JoyconRight; + } + else + { + controllerType = ControllerType.ProController; + } + } + else + { + // if it's not a nintendo controller, we assume it's a pro controller or a joycon pair + controllerType = ControllerType.ProController; + } + + InputConfig config = new StandardControllerInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.GamepadSDL2, + Id = id, + ControllerType = controllerType, + DeadzoneLeft = 0.1f, + DeadzoneRight = 0.1f, + RangeLeft = 1.0f, + RangeRight = 1.0f, + TriggerThreshold = 0.5f, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = (controllerType == ControllerType.JoyconLeft) ? ConfigGamepadInputId.Y : ConfigGamepadInputId.DpadUp, + DpadDown = (controllerType == ControllerType.JoyconLeft) ? ConfigGamepadInputId.A : ConfigGamepadInputId.DpadDown, + DpadLeft = (controllerType == ControllerType.JoyconLeft) ? ConfigGamepadInputId.B : ConfigGamepadInputId.DpadLeft, + DpadRight = (controllerType == ControllerType.JoyconLeft) ? ConfigGamepadInputId.X : ConfigGamepadInputId.DpadRight, + ButtonMinus = (controllerType == ControllerType.JoyconLeft) ? ConfigGamepadInputId.Plus : ConfigGamepadInputId.Minus, + ButtonL = ConfigGamepadInputId.LeftShoulder, + ButtonZl = ConfigGamepadInputId.LeftTrigger, + ButtonSl = (controllerType == ControllerType.JoyconLeft) ? ConfigGamepadInputId.LeftShoulder : ConfigGamepadInputId.Unbound, + ButtonSr = (controllerType == ControllerType.JoyconLeft) ? ConfigGamepadInputId.RightShoulder : ConfigGamepadInputId.Unbound, + }, + LeftJoyconStick = new JoyconConfigControllerStick + { + Joystick = ConfigStickInputId.Left, + StickButton = ConfigGamepadInputId.LeftStick, + InvertStickX = false, + InvertStickY = false, + Rotate90CW = (controllerType == ControllerType.JoyconLeft), + }, + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = ConfigGamepadInputId.B, + ButtonB = (controllerType == ControllerType.JoyconRight) ? ConfigGamepadInputId.Y : ConfigGamepadInputId.A, + ButtonX = (controllerType == ControllerType.JoyconRight) ? ConfigGamepadInputId.A : ConfigGamepadInputId.Y, + ButtonY = ConfigGamepadInputId.X, + ButtonPlus = ConfigGamepadInputId.Plus, + ButtonR = ConfigGamepadInputId.RightShoulder, + ButtonZr = ConfigGamepadInputId.RightTrigger, + ButtonSl = (controllerType == ControllerType.JoyconRight) ? ConfigGamepadInputId.LeftShoulder : ConfigGamepadInputId.Unbound, + ButtonSr = (controllerType == ControllerType.JoyconRight) ? ConfigGamepadInputId.RightShoulder : ConfigGamepadInputId.Unbound, + }, + RightJoyconStick = new JoyconConfigControllerStick + { + Joystick = (controllerType == ControllerType.JoyconRight) ? ConfigStickInputId.Left : ConfigStickInputId.Right, + StickButton = ConfigGamepadInputId.RightStick, + InvertStickX = (controllerType == ControllerType.JoyconRight), + InvertStickY = (controllerType == ControllerType.JoyconRight), + Rotate90CW = (controllerType == ControllerType.JoyconRight), + }, + Motion = new StandardMotionConfigController + { + MotionBackend = MotionInputBackendType.GamepadDriver, + EnableMotion = true, + Sensitivity = 100, + GyroDeadzone = 1, + }, + Rumble = new RumbleConfigController + { + StrongRumble = 1f, + WeakRumble = 1f, + EnableRumble = false, + }, + Led = new LedConfigController + { + EnableLed = false, + TurnOffLed = false, + UseRainbow = false, + LedColor = 0, + }, + }; + + return config; + } + } +} diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 237c63ad4..ea0727897 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Headless private static LibHacHorizonManager _libHacHorizonManager; private static UserChannelPersistence _userChannelPersistence; private static InputManager _inputManager; + private static AutoAssignController _autoAssignController; private static Switch _emulationContext; private static WindowBase _window; private static WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; @@ -370,7 +371,7 @@ namespace Ryujinx.Headless DisplaySleep.Prevent(); - _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse, _enableAutoAssign); + _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse); _window.Execute(); diff --git a/src/Ryujinx/Headless/Windows/WindowBase.cs b/src/Ryujinx/Headless/Windows/WindowBase.cs index cf3e846be..8fd445199 100644 --- a/src/Ryujinx/Headless/Windows/WindowBase.cs +++ b/src/Ryujinx/Headless/Windows/WindowBase.cs @@ -120,7 +120,7 @@ namespace Ryujinx.Headless SDL2Driver.Instance.Initialize(); } - public void Initialize(Switch device, List inputConfigs, bool enableKeyboard, bool enableMouse, bool enableAutoAssign) + public void Initialize(Switch device, List inputConfigs, bool enableKeyboard, bool enableMouse) { Device = device; @@ -133,7 +133,7 @@ namespace Ryujinx.Headless Renderer = renderer; - NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse, enableAutoAssign); + NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse); TouchScreenManager.Initialize(device); } diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 1f0df9b2f..853e87f0e 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -19,6 +19,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Headless; +using Ryujinx.Input.SDL2; using Ryujinx.SDL2.Common; using System; using System.Collections.Generic; @@ -130,7 +131,7 @@ namespace Ryujinx.Ava SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input); ReloadConfig(); - + WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor(); // Logging system information. diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index a33259377..c177d8bba 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -852,11 +852,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input } } - _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse, ConfigurationState.Instance.Hid.EnableAutoAssign); - + //_mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); // Atomically replace and signal input change. // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event. - ConfigurationState.Instance.Hid.InputConfig.Value = newConfig; + //ConfigurationState.Instance.Hid.InputConfig.Value = newConfig; + _mainWindow.AutoAssignController.RefreshControllers(newConfig); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index 76002d1ab..fe8f3a554 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -63,6 +63,7 @@ namespace Ryujinx.Ava.UI.Windows public LibHacHorizonManager LibHacHorizonManager { get; private set; } public InputManager InputManager { get; private set; } + public AutoAssignController AutoAssignController { get; private set; } public SettingsWindow SettingsWindow { get; set; } @@ -109,6 +110,7 @@ namespace Ryujinx.Ava.UI.Windows if (Program.PreviewerDetached) { InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver()); + AutoAssignController = new AutoAssignController(InputManager); _ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it); this.ScalingChanged += OnScalingChanged;