From 186ed4f984f9c25394cb1718206e411662842797 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sun, 26 Jan 2025 21:20:27 +0100 Subject: [PATCH 01/28] feat: add option for automatic controller assignment in settings --- .../Services/Hid/HidDevices/NpadDevices.cs | 1 + src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 2 + src/Ryujinx.Input/HLE/NpadManager.cs | 195 +++++++++++++++--- src/Ryujinx/AppHost.cs | 2 +- src/Ryujinx/Assets/locales.json | 50 +++++ src/Ryujinx/Headless/HeadlessRyujinx.cs | 4 +- src/Ryujinx/Headless/Options.cs | 6 + src/Ryujinx/Headless/Windows/WindowBase.cs | 4 +- .../UI/ViewModels/Input/InputViewModel.cs | 4 +- .../UI/ViewModels/SettingsViewModel.cs | 3 + .../UI/Views/Settings/SettingsInputView.axaml | 6 + .../Configuration/ConfigurationFileFormat.cs | 5 + .../Configuration/ConfigurationState.Model.cs | 6 + .../Configuration/ConfigurationState.cs | 2 + 14 files changed, 252 insertions(+), 38 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index 834bee6f0..0e622f3ad 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -175,6 +175,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid private void Remap() { + Logger.Warning?.Print(LogClass.Hid, $"Connected controllers"); // Remap/Init if necessary for (int i = 0; i < MaxControllers; ++i) { diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index 251f53cba..95d46e4ce 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.SDL2.Common; using System; using System.Collections.Generic; @@ -96,6 +97,7 @@ namespace Ryujinx.Input.SDL2 private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId) { + Logger.Warning?.Print(LogClass.Application, "(SDL2) 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 08f222a91..35dbc1605 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -1,5 +1,8 @@ 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.Configuration.Hid.Keyboard; using Ryujinx.HLE.HOS.Services.Hid; using System; @@ -13,6 +16,7 @@ using ControllerType = Ryujinx.Common.Configuration.Hid.ControllerType; using PlayerIndex = Ryujinx.HLE.HOS.Services.Hid.PlayerIndex; using Switch = Ryujinx.HLE.Switch; + namespace Ryujinx.Input.HLE { public class NpadManager : IDisposable @@ -35,6 +39,7 @@ namespace Ryujinx.Input.HLE private List _inputConfig; private bool _enableKeyboard; private bool _enableMouse; + private bool _enableAutoAssign; private Switch _device; public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver) @@ -83,14 +88,14 @@ namespace Ryujinx.Input.HLE } } - ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); + ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse, _enableAutoAssign); } } private void HandleOnGamepadConnected(string id) { // Force input reload - ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); + ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse, _enableAutoAssign); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -117,7 +122,7 @@ namespace Ryujinx.Input.HLE return controller.GamepadDriver != null; } - public void ReloadConfiguration(List inputConfig, bool enableKeyboard, bool enableMouse) + public void ReloadConfiguration(List inputConfig, bool enableKeyboard, bool enableMouse, bool enableAutoAssign) { lock (_lock) { @@ -125,52 +130,178 @@ namespace Ryujinx.Input.HLE List validInputs = new(); - foreach (InputConfig inputConfigEntry in inputConfig) + // if auto assign is disabled, we want to keep the old logic with profiles. + if (!enableAutoAssign) { - NpadController controller; - int index = (int)inputConfigEntry.PlayerIndex; + foreach (InputConfig inputConfigEntry in inputConfig) + { + 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); - } + 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); + bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry); - if (!isValid) - { - _controllers[index] = null; - controller.Dispose(); - } - else - { - _controllers[index] = controller; - validInputs.Add(inputConfigEntry); + if (!isValid) + { + _controllers[index] = null; + controller.Dispose(); + } + else + { + _controllers[index] = controller; + validInputs.Add(inputConfigEntry); + } } } - + else + { + List controllers = _gamepadDriver.GetGamepads().ToList(); + + foreach (IGamepad activeController in controllers) + { + NpadController controller; + int index = controllers.FindIndex(x => x == activeController); + + // TODO: Implement a function to determine if pro controller or single joycon (L/R) and to create the appropriate config. + // Also if old controller exists, try to reuse it (and create their config too). + bool isNintendoStyle = controllers.FirstOrDefault(x => x.Id == activeController.Id).Name.Contains("Nintendo"); + string id = activeController.Id.Split(" ")[0]; + InputConfig config = new StandardControllerInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.GamepadSDL2, + Id = id, + ControllerType = ControllerType.ProController, + DeadzoneLeft = 0.1f, + DeadzoneRight = 0.1f, + RangeLeft = 1.0f, + RangeRight = 1.0f, + TriggerThreshold = 0.5f, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = ConfigGamepadInputId.DpadUp, + DpadDown = ConfigGamepadInputId.DpadDown, + DpadLeft = ConfigGamepadInputId.DpadLeft, + DpadRight = ConfigGamepadInputId.DpadRight, + ButtonMinus = ConfigGamepadInputId.Minus, + ButtonL = ConfigGamepadInputId.LeftShoulder, + ButtonZl = ConfigGamepadInputId.LeftTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, + }, + LeftJoyconStick = new JoyconConfigControllerStick + { + Joystick = ConfigStickInputId.Left, + StickButton = ConfigGamepadInputId.LeftStick, + InvertStickX = false, + InvertStickY = false, + }, + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, + ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, + ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, + ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, + ButtonPlus = ConfigGamepadInputId.Plus, + ButtonR = ConfigGamepadInputId.RightShoulder, + ButtonZr = ConfigGamepadInputId.RightTrigger, + ButtonSl = ConfigGamepadInputId.Unbound, + ButtonSr = ConfigGamepadInputId.Unbound, + }, + RightJoyconStick = new JoyconConfigControllerStick + { + Joystick = ConfigStickInputId.Right, + StickButton = ConfigGamepadInputId.RightStick, + InvertStickX = false, + InvertStickY = false, + }, + 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, + }, + }; + + 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); + } + } + } + for (int i = 0; i < oldControllers.Length; i++) { // Disconnect any controllers that weren't reused by the new configuration. - + oldControllers[i]?.Dispose(); oldControllers[i] = null; } - - _inputConfig = inputConfig; + + _inputConfig = (enableAutoAssign) ? validInputs : inputConfig; _enableKeyboard = enableKeyboard; _enableMouse = enableMouse; + _enableAutoAssign = enableAutoAssign; _device.Hid.RefreshInputConfig(validInputs); + } } + private InputConfig CreateConfigFromController(IGamepad controller) + { + InputConfig config; + + return null; + } + public void UnblockInputUpdates() { lock (_lock) @@ -192,12 +323,12 @@ namespace Ryujinx.Input.HLE } } - public void Initialize(Switch device, List inputConfig, bool enableKeyboard, bool enableMouse) + public void Initialize(Switch device, List inputConfig, bool enableKeyboard, bool enableMouse, bool enableAutoAssign) { _device = device; _device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE; - ReloadConfiguration(inputConfig, enableKeyboard, enableMouse); + ReloadConfiguration(inputConfig, enableKeyboard, enableMouse, enableAutoAssign); } public void Update(float aspectRatio = 1) diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 65c279fc4..5d6d0fc18 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -482,7 +482,7 @@ namespace Ryujinx.Ava DisplaySleep.Prevent(); - NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); + NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse, ConfigurationState.Instance.Hid.EnableAutoAssign); TouchScreenManager.Initialize(Device); _viewModel.IsGameRunning = true; diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 698af9d17..317dfa804 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -147,6 +147,31 @@ "zh_TW": "滑鼠直接存取" } }, + { + "ID": "SettingsTabInputAutoAssign", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Auto-assign controllers", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "Assegnamento controller automatico", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsTabSystemMemoryManagerMode", "Translations": { @@ -15322,6 +15347,31 @@ "zh_TW": "支援滑鼠直接存取 (HID)。遊戲可將滑鼠作為指向裝置使用。\n\n僅適用於在 Switch 硬體上原生支援滑鼠控制的遊戲,這類遊戲很少。\n\n啟用後,觸控螢幕功能可能無法使用。\n\n如果不確定,請保持關閉狀態。" } }, + { + "ID": "AutoAssignTooltip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Automatic controllers assignment support. Automatically assigns connected controllers to each player.\n\nThis feature may override custom player-to-controller assignments.\n\nLeave OFF if you prefer to manually assign controllers.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "Supporto per l'assegnazione automatica dei controller. Assegna automaticamente i controller connessi a ciascun giocatore.\\n\\nQuesta funzionalità potrebbe sovrascrivere le configurazioni personalizzate controller-giocatore.\\n\\nLascia disattivato se preferisci assegnare i controller manualmente.", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "。" + } + }, { "ID": "RegionTooltip", "Translations": { diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 787aaca62..09b3a8841 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Headless private static List _inputConfiguration = []; private static bool _enableKeyboard; private static bool _enableMouse; + private static bool _enableAutoAssign; private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); @@ -228,6 +229,7 @@ namespace Ryujinx.Headless _inputConfiguration ??= []; _enableKeyboard = option.EnableKeyboard; _enableMouse = option.EnableMouse; + _enableAutoAssign = option.EnableAutoAssign; LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); @@ -368,7 +370,7 @@ namespace Ryujinx.Headless DisplaySleep.Prevent(); - _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse); + _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse, _enableAutoAssign); _window.Execute(); diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index f527e9811..2846be20e 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -27,6 +27,9 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(EnableMouse))) EnableMouse = configurationState.Hid.EnableMouse; + + if (NeedsOverride(nameof(EnableAutoAssign))) + EnableAutoAssign = configurationState.Hid.EnableAutoAssign; if (NeedsOverride(nameof(HideCursorMode))) HideCursorMode = configurationState.HideCursor; @@ -273,6 +276,9 @@ namespace Ryujinx.Headless [Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")] public bool EnableMouse { get; set; } + + [Option("enable-auto-assign", Required = false, Default = false, HelpText = "Enable or disable auto-assigning controllers to players.")] + public bool EnableAutoAssign { get; set; } [Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")] public HideCursorMode HideCursorMode { get; set; } diff --git a/src/Ryujinx/Headless/Windows/WindowBase.cs b/src/Ryujinx/Headless/Windows/WindowBase.cs index 068c32062..6bbf4dca6 100644 --- a/src/Ryujinx/Headless/Windows/WindowBase.cs +++ b/src/Ryujinx/Headless/Windows/WindowBase.cs @@ -118,7 +118,7 @@ namespace Ryujinx.Headless SDL2Driver.Instance.Initialize(); } - public void Initialize(Switch device, List inputConfigs, bool enableKeyboard, bool enableMouse) + public void Initialize(Switch device, List inputConfigs, bool enableKeyboard, bool enableMouse, bool enableAutoAssign) { Device = device; @@ -131,7 +131,7 @@ namespace Ryujinx.Headless Renderer = renderer; - NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse); + NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse, enableAutoAssign); TouchScreenManager.Initialize(device); } diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index c59ec540c..9ba80d488 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -851,8 +851,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input newConfig[i] = config; } } - - _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); + + _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse, ConfigurationState.Instance.Hid.EnableAutoAssign); // Atomically replace and signal input change. // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event. diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index b2311cfc7..69a866bd1 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -128,6 +128,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableDockedMode { get; set; } public bool EnableKeyboard { get; set; } public bool EnableMouse { get; set; } + public bool EnableAutoAssign { get; set; } public VSyncMode VSyncMode { get => _vSyncMode; @@ -510,6 +511,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableDockedMode = config.System.EnableDockedMode; EnableKeyboard = config.Hid.EnableKeyboard; EnableMouse = config.Hid.EnableMouse; + EnableAutoAssign = config.Hid.EnableAutoAssign; // Keyboard Hotkeys KeyboardHotkey = new HotkeyConfig(config.Hid.Hotkeys.Value); @@ -617,6 +619,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableDockedMode.Value = EnableDockedMode; config.Hid.EnableKeyboard.Value = EnableKeyboard; config.Hid.EnableMouse.Value = EnableMouse; + config.Hid.EnableAutoAssign.Value = EnableAutoAssign; // Keyboard Hotkeys config.Hid.Hotkeys.Value = KeyboardHotkey.GetConfig(); diff --git a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml index b0edc51a5..920c5994e 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml @@ -58,6 +58,12 @@ + + + diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs index 4bbc5e622..e984ee4b2 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs @@ -370,6 +370,11 @@ namespace Ryujinx.Ava.Utilities.Configuration /// Enable or disable mouse support (Independent from controllers binding) /// public bool EnableMouse { get; set; } + + /// + /// Enable or disable automatic controller assignment for players + /// + public bool EnableAutoAssign { get; set; } /// /// Hotkey Keyboard Bindings diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs index 0d08536d7..72dadc69b 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs @@ -432,6 +432,11 @@ namespace Ryujinx.Ava.Utilities.Configuration /// Enable or disable mouse support (Independent from controllers binding) /// public ReactiveObject EnableMouse { get; private set; } + + /// + /// Enable or disable auto-assigning controllers to players + /// + public ReactiveObject EnableAutoAssign { get; private set; } /// /// Hotkey Keyboard Bindings @@ -449,6 +454,7 @@ namespace Ryujinx.Ava.Utilities.Configuration { EnableKeyboard = new ReactiveObject(); EnableMouse = new ReactiveObject(); + EnableAutoAssign = new ReactiveObject(); Hotkeys = new ReactiveObject(); InputConfig = new ReactiveObject>(); } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 80b3e5c03..49f1b125d 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -129,6 +129,7 @@ namespace Ryujinx.Ava.Utilities.Configuration ShowConsole = UI.ShowConsole, EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, + EnableAutoAssign = Hid.EnableAutoAssign, Hotkeys = Hid.Hotkeys, KeyboardConfig = [], ControllerConfig = [], @@ -243,6 +244,7 @@ namespace Ryujinx.Ava.Utilities.Configuration UI.WindowStartup.WindowMaximized.Value = false; Hid.EnableKeyboard.Value = false; Hid.EnableMouse.Value = false; + Hid.EnableAutoAssign.Value = false; Hid.Hotkeys.Value = new KeyboardHotkeys { ToggleVSyncMode = Key.F1, -- 2.47.2 From 24cef89b6ce7dccd399fa882a735284f38a760c4 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sun, 26 Jan 2025 23:52:44 +0100 Subject: [PATCH 02/28] refactor: clean up logging and improve IgnoreApplet logic in settings --- src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs | 1 - src/Ryujinx.Input/HLE/NpadManager.cs | 2 +- src/Ryujinx/UI/ViewModels/SettingsViewModel.cs | 2 +- src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index 0e622f3ad..834bee6f0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -175,7 +175,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid private void Remap() { - Logger.Warning?.Print(LogClass.Hid, $"Connected controllers"); // Remap/Init if necessary for (int i = 0; i < MaxControllers; ++i) { diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index ef91ce8e4..0cd51e918 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -173,7 +173,7 @@ namespace Ryujinx.Input.HLE NpadController controller; int index = controllers.FindIndex(x => x == activeController); - // TODO: Implement a function to determine if pro controller or single joycon (L/R) and to create the appropriate config. + // TODO: Implement a function to determine if pro controller or single joycon (L/R) and create the appropriate config. // Also if old controller exists, try to reuse it (and create their config too). bool isNintendoStyle = controllers.FirstOrDefault(x => x.Id == activeController.Id).Name.Contains("Nintendo"); string id = activeController.Id.Split(" ")[0]; diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index b637766a8..f7543d2bb 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -638,7 +638,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.System.DramSize.Value = DramSize; config.System.IgnoreMissingServices.Value = IgnoreMissingServices; - config.System.IgnoreApplet.Value = IgnoreApplet; + config.System.IgnoreApplet.Value = (EnableAutoAssign) || IgnoreApplet; // CPU config.System.EnablePtc.Value = EnablePptc; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml index d7cf60787..9d8902026 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml @@ -313,6 +313,7 @@ -- 2.47.2 From 97be01d4739397e14d7a772a7da2597351d1238a Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Tue, 28 Jan 2025 22:47:55 +0100 Subject: [PATCH 03/28] refactor: streamline configuration creation for controllers in NpadManager --- src/Ryujinx.Input/HLE/NpadManager.cs | 178 +++++++++++++++------------ 1 file changed, 102 insertions(+), 76 deletions(-) diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 0cd51e918..6ae5ce83e 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -172,81 +172,9 @@ namespace Ryujinx.Input.HLE { NpadController controller; int index = controllers.FindIndex(x => x == activeController); - - // TODO: Implement a function to determine if pro controller or single joycon (L/R) and create the appropriate config. + // Also if old controller exists, try to reuse it (and create their config too). - bool isNintendoStyle = controllers.FirstOrDefault(x => x.Id == activeController.Id).Name.Contains("Nintendo"); - string id = activeController.Id.Split(" ")[0]; - InputConfig config = new StandardControllerInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.GamepadSDL2, - Id = id, - ControllerType = ControllerType.ProController, - DeadzoneLeft = 0.1f, - DeadzoneRight = 0.1f, - RangeLeft = 1.0f, - RangeRight = 1.0f, - TriggerThreshold = 0.5f, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = ConfigGamepadInputId.DpadUp, - DpadDown = ConfigGamepadInputId.DpadDown, - DpadLeft = ConfigGamepadInputId.DpadLeft, - DpadRight = ConfigGamepadInputId.DpadRight, - ButtonMinus = ConfigGamepadInputId.Minus, - ButtonL = ConfigGamepadInputId.LeftShoulder, - ButtonZl = ConfigGamepadInputId.LeftTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - LeftJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Left, - StickButton = ConfigGamepadInputId.LeftStick, - InvertStickX = false, - InvertStickY = false, - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, - ButtonPlus = ConfigGamepadInputId.Plus, - ButtonR = ConfigGamepadInputId.RightShoulder, - ButtonZr = ConfigGamepadInputId.RightTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - RightJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Right, - StickButton = ConfigGamepadInputId.RightStick, - InvertStickX = false, - InvertStickY = false, - }, - 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, - }, - }; + InputConfig config = CreateConfigFromController(activeController); config.PlayerIndex = (Common.Configuration.Hid.PlayerIndex)index; @@ -298,9 +226,107 @@ namespace Ryujinx.Input.HLE private InputConfig CreateConfigFromController(IGamepad controller) { - InputConfig config; + if (controller == null) return null; - return null; + 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; } public void UnblockInputUpdates() -- 2.47.2 From ab4bb0a885a87259bb18d34762b41cbc9bd926ac Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sat, 1 Feb 2025 19:36:55 +0100 Subject: [PATCH 04/28] 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; -- 2.47.2 From 3ff9d1e12808b27347bacb931f7ada335faf55ab Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sun, 2 Feb 2025 16:35:30 +0100 Subject: [PATCH 05/28] refactor: enhance AutoAssignController to utilize ViewModel and improve controller refresh logic --- src/Ryujinx.Input/HLE/NpadManager.cs | 122 ++---------------- src/Ryujinx/AutoAssignController.cs | 53 +++++--- .../UI/ViewModels/Input/InputViewModel.cs | 10 +- .../UI/ViewModels/SettingsViewModel.cs | 10 +- src/Ryujinx/UI/Windows/MainWindow.axaml.cs | 2 +- 5 files changed, 59 insertions(+), 138 deletions(-) diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index d5b3dec94..198989444 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -4,6 +4,7 @@ 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.Configuration.Hid.Keyboard; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Hid; using System; using System.Collections.Generic; @@ -40,7 +41,6 @@ namespace Ryujinx.Input.HLE private List _inputConfig; private bool _enableKeyboard; private bool _enableMouse; - private bool _enableAutoAssign; private Switch _device; public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver) @@ -53,7 +53,7 @@ namespace Ryujinx.Input.HLE _mouseDriver = mouseDriver; _inputConfig = new List(); - _gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; + //_gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; } @@ -89,7 +89,7 @@ namespace Ryujinx.Input.HLE } } - ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); + //ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); } } @@ -130,7 +130,7 @@ namespace Ryujinx.Input.HLE NpadController[] oldControllers = _controllers.ToArray(); List validInputs = new(); - + foreach (InputConfig inputConfigEntry in inputConfig) { NpadController controller; @@ -160,129 +160,23 @@ namespace Ryujinx.Input.HLE validInputs.Add(inputConfigEntry); } } - + for (int i = 0; i < oldControllers.Length; i++) { // Disconnect any controllers that weren't reused by the new configuration. - + oldControllers[i]?.Dispose(); oldControllers[i] = null; } - + _inputConfig = inputConfig; _enableKeyboard = enableKeyboard; _enableMouse = enableMouse; _device.Hid.RefreshInputConfig(validInputs); - } } - - private InputConfig CreateConfigFromController(IGamepad controller) - { - if (controller == null) return null; - - 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; - } - + public void UnblockInputUpdates() { lock (_lock) diff --git a/src/Ryujinx/AutoAssignController.cs b/src/Ryujinx/AutoAssignController.cs index 76d7b2b01..8e0a1a411 100644 --- a/src/Ryujinx/AutoAssignController.cs +++ b/src/Ryujinx/AutoAssignController.cs @@ -17,16 +17,20 @@ namespace Ryujinx.Ava public class AutoAssignController { private readonly InputManager _inputManager; + private readonly MainWindowViewModel _viewModel; private readonly ConfigurationState _configurationState; private readonly IGamepad[] _controllers; - public AutoAssignController(InputManager inputManager) + public AutoAssignController(InputManager inputManager, MainWindowViewModel mainWindowViewModel) { - _configurationState = ConfigurationState.Instance; _inputManager = inputManager; + _viewModel = mainWindowViewModel; + _configurationState = ConfigurationState.Instance; _inputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _inputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; + + RefreshControllers(); } public void RefreshControllers(List inputConfig = null) @@ -34,32 +38,47 @@ namespace Ryujinx.Ava if (!_configurationState.Hid.EnableAutoAssign) return; List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); - - if (controllers.Count == 0) return; - - MainWindow _mainWindow = RyujinxApp.MainWindow; + //if (controllers.Count == 0) return; // 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(); + + Logger.Warning?.Print(LogClass.Application, $"inputConfig: {inputConfig != null}"); - int index = 0; - foreach (var controller in controllers) + if (inputConfig != null) { - InputConfig config = oldConfigs.FirstOrDefault(x => x.Id == controller.Id) ?? CreateConfigFromController(controller); - config.PlayerIndex = (PlayerIndex)index; - newConfig.Add(config); - index++; + newConfig = inputConfig; } - - _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); + else + { + if (!_configurationState.Hid.EnableAutoAssign) return; + List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); + + int index = 0; + foreach (var controller in controllers) + { + if(controller == null) continue; + InputConfig config = oldConfig.FirstOrDefault(x => x.Id == controller.Id) ?? CreateConfigFromController(controller); + config.PlayerIndex = (PlayerIndex)index; + newConfig.Add(config); + index++; + } + } + + _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); + + Logger.Warning?.Print(LogClass.Application, $"(AAC) NpadManager: {_viewModel.AppHost?.NpadManager.GetHashCode()}"); - ConfigurationState.Instance.Hid.InputConfig.Value = newConfig; + _configurationState.Hid.InputConfig.Value = newConfig; + + Logger.Warning?.Print(LogClass.Application, $"inputConfig: {newConfig.Count}"); + + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } private void HandleOnGamepadConnected(string id) { - Logger.Warning?.Print(LogClass.Application, $"Gamepad disconnected: {id}"); + Logger.Warning?.Print(LogClass.Application, $"Gamepad connected: {id}"); RefreshControllers(); } diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index c177d8bba..d00712d81 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -810,7 +810,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { IsModified = false; - List newConfig = new(); + List newConfig = []; newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value); @@ -851,12 +851,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input newConfig[i] = config; } } - - //_mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); + + _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; - _mainWindow.AutoAssignController.RefreshControllers(newConfig); + ConfigurationState.Instance.Hid.InputConfig.Value = newConfig; ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index f7543d2bb..1d1d2731a 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -620,6 +620,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableDockedMode.Value = EnableDockedMode; config.Hid.EnableKeyboard.Value = EnableKeyboard; config.Hid.EnableMouse.Value = EnableMouse; + bool activatingAutoAssign = EnableAutoAssign && !config.Hid.EnableAutoAssign; config.Hid.EnableAutoAssign.Value = EnableAutoAssign; // Keyboard Hotkeys @@ -715,7 +716,14 @@ namespace Ryujinx.Ava.UI.ViewModels MainWindow.UpdateGraphicsConfig(); RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged(); - SaveSettingsEvent?.Invoke(); + if(activatingAutoAssign) + { + RyujinxApp.MainWindow.AutoAssignController.RefreshControllers(); + } + else + { + SaveSettingsEvent?.Invoke(); + } GameDirectoryChanged = false; AutoloadDirectoryChanged = false; diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index fe8f3a554..507f6324b 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -110,7 +110,7 @@ namespace Ryujinx.Ava.UI.Windows if (Program.PreviewerDetached) { InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver()); - AutoAssignController = new AutoAssignController(InputManager); + AutoAssignController = new AutoAssignController(InputManager, ViewModel); _ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it); this.ScalingChanged += OnScalingChanged; -- 2.47.2 From 9b7dc6f4ee11885c52db0f40c0ebf6c3ef3b738e Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sun, 2 Feb 2025 22:40:47 +0100 Subject: [PATCH 06/28] simplify RefreshControllers logic and introduce GetOrderedConfig for better controller management --- src/Ryujinx/AutoAssignController.cs | 76 ++++++++++++++++++----------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/Ryujinx/AutoAssignController.cs b/src/Ryujinx/AutoAssignController.cs index 8e0a1a411..df16ac75b 100644 --- a/src/Ryujinx/AutoAssignController.cs +++ b/src/Ryujinx/AutoAssignController.cs @@ -16,6 +16,8 @@ namespace Ryujinx.Ava { public class AutoAssignController { + private const int MaxControllers = 9; + private readonly InputManager _inputManager; private readonly MainWindowViewModel _viewModel; private readonly ConfigurationState _configurationState; @@ -33,46 +35,21 @@ namespace Ryujinx.Ava RefreshControllers(); } - public void RefreshControllers(List inputConfig = null) + public void RefreshControllers() { if (!_configurationState.Hid.EnableAutoAssign) return; - - List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); //if (controllers.Count == 0) return; // Get every controller config and update the configuration state - List newConfig = new(); - - Logger.Warning?.Print(LogClass.Application, $"inputConfig: {inputConfig != null}"); - if (inputConfig != null) - { - newConfig = inputConfig; - } - else - { - if (!_configurationState.Hid.EnableAutoAssign) return; - List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); - - int index = 0; - foreach (var controller in controllers) - { - if(controller == null) continue; - InputConfig config = oldConfig.FirstOrDefault(x => x.Id == controller.Id) ?? CreateConfigFromController(controller); - config.PlayerIndex = (PlayerIndex)index; - newConfig.Add(config); - index++; - } - } + List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); + List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); + List newConfig = GetOrderedConfig(controllers, oldConfig); _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); - Logger.Warning?.Print(LogClass.Application, $"(AAC) NpadManager: {_viewModel.AppHost?.NpadManager.GetHashCode()}"); - _configurationState.Hid.InputConfig.Value = newConfig; - Logger.Warning?.Print(LogClass.Application, $"inputConfig: {newConfig.Count}"); - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } @@ -87,6 +64,47 @@ namespace Ryujinx.Ava Logger.Warning?.Print(LogClass.Application, $"Gamepad disconnected: {id}"); RefreshControllers(); } + + private List GetOrderedConfig(List controllers, List oldConfig) + { + // Dictionary to store assigned PlayerIndexes + Dictionary playerIndexMap = new(); + + // Convert oldConfig into a dictionary for quick lookup by controller Id + Dictionary oldConfigMap = oldConfig.ToDictionary(x => x.Id, x => x); + + foreach (var controller in controllers) + { + if (controller == null) continue; + + // If the controller already has a config in oldConfig, use it + if (oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig)) + { + // Use the existing PlayerIndex from oldConfig and add it to the map + playerIndexMap[(int)existingConfig.PlayerIndex] = existingConfig; + } + else + { + // Find the first available PlayerIndex (0 to MaxControllers) + for (int i = 0; i < MaxControllers-1; i++) + { + if (!playerIndexMap.ContainsKey(i)) // Check if the PlayerIndex is available + { + // Create a new InputConfig and assign PlayerIndex + InputConfig newConfig = CreateConfigFromController(controller); + newConfig.PlayerIndex = (PlayerIndex)i; + + // Add the new config to the map with the available PlayerIndex + playerIndexMap[i] = newConfig; + break; + } + } + } + } + + // Return the sorted list of InputConfigs, ordered by PlayerIndex + return playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(); + } private InputConfig CreateConfigFromController(IGamepad controller) { -- 2.47.2 From 9cccaac9d384700bad143c92a6bbe32e139200ac Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sun, 2 Feb 2025 22:56:34 +0100 Subject: [PATCH 07/28] minor fix to RefreshControllers logic --- src/Ryujinx/AutoAssignController.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Ryujinx/AutoAssignController.cs b/src/Ryujinx/AutoAssignController.cs index df16ac75b..167a46995 100644 --- a/src/Ryujinx/AutoAssignController.cs +++ b/src/Ryujinx/AutoAssignController.cs @@ -46,6 +46,13 @@ namespace Ryujinx.Ava List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); List newConfig = GetOrderedConfig(controllers, oldConfig); + int index = 0; + foreach (var config in newConfig) + { + config.PlayerIndex = (PlayerIndex)index; + index++; + } + _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); _configurationState.Hid.InputConfig.Value = newConfig; -- 2.47.2 From 5513de93e5d98a677503638f69223923f50b1621 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Tue, 4 Feb 2025 11:50:08 +0100 Subject: [PATCH 08/28] code clean up; fix not loading EnableAutoAssign.Value from config file on startup --- src/Ryujinx/AutoAssignController.cs | 3 --- .../Utilities/Configuration/ConfigurationState.Migration.cs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Ryujinx/AutoAssignController.cs b/src/Ryujinx/AutoAssignController.cs index 167a46995..06c636dc2 100644 --- a/src/Ryujinx/AutoAssignController.cs +++ b/src/Ryujinx/AutoAssignController.cs @@ -22,8 +22,6 @@ namespace Ryujinx.Ava private readonly MainWindowViewModel _viewModel; private readonly ConfigurationState _configurationState; - private readonly IGamepad[] _controllers; - public AutoAssignController(InputManager inputManager, MainWindowViewModel mainWindowViewModel) { _inputManager = inputManager; @@ -74,7 +72,6 @@ namespace Ryujinx.Ava private List GetOrderedConfig(List controllers, List oldConfig) { - // Dictionary to store assigned PlayerIndexes Dictionary playerIndexMap = new(); // Convert oldConfig into a dictionary for quick lookup by controller Id diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index 3ccac2647..5df0a01bf 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -138,6 +138,7 @@ namespace Ryujinx.Ava.Utilities.Configuration Hid.EnableKeyboard.Value = cff.EnableKeyboard; Hid.EnableMouse.Value = cff.EnableMouse; + Hid.EnableAutoAssign.Value = cff.EnableAutoAssign; Hid.Hotkeys.Value = cff.Hotkeys; Hid.InputConfig.Value = cff.InputConfig ?? []; -- 2.47.2 From 7999a973f38b6417e2378dac54dc316bda26b20f Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Tue, 4 Feb 2025 14:51:48 +0100 Subject: [PATCH 09/28] update GetOrderedConfig to return if new controllers connected and conditionally update configuration --- src/Ryujinx/AutoAssignController.cs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx/AutoAssignController.cs b/src/Ryujinx/AutoAssignController.cs index 06c636dc2..78b35fd65 100644 --- a/src/Ryujinx/AutoAssignController.cs +++ b/src/Ryujinx/AutoAssignController.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); - List newConfig = GetOrderedConfig(controllers, oldConfig); + (List newConfig, bool hasNewControllersConnected) = GetOrderedConfig(controllers, oldConfig); int index = 0; foreach (var config in newConfig) @@ -53,9 +53,12 @@ namespace Ryujinx.Ava _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); - _configurationState.Hid.InputConfig.Value = newConfig; - - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + // update the configuration state only if there are more controllers than before + if(hasNewControllersConnected) + { + _configurationState.Hid.InputConfig.Value = newConfig; + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + } } private void HandleOnGamepadConnected(string id) @@ -70,9 +73,10 @@ namespace Ryujinx.Ava RefreshControllers(); } - private List GetOrderedConfig(List controllers, List oldConfig) + private (List, bool) GetOrderedConfig(List controllers, List oldConfig) { Dictionary playerIndexMap = new(); + int existingControllers = 0; // Convert oldConfig into a dictionary for quick lookup by controller Id Dictionary oldConfigMap = oldConfig.ToDictionary(x => x.Id, x => x); @@ -86,6 +90,8 @@ namespace Ryujinx.Ava { // Use the existing PlayerIndex from oldConfig and add it to the map playerIndexMap[(int)existingConfig.PlayerIndex] = existingConfig; + + existingControllers++; } else { @@ -106,8 +112,8 @@ namespace Ryujinx.Ava } } - // Return the sorted list of InputConfigs, ordered by PlayerIndex - return playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(); + // Return the sorted list of InputConfigs, ordered by PlayerIndex, and a bool indicating if new controllers were connected + return (playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(), controllers.Count > existingControllers); } private InputConfig CreateConfigFromController(IGamepad controller) -- 2.47.2 From 6c8a60db089cbc955b175f65b6df9631a170b682 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Tue, 4 Feb 2025 17:02:25 +0100 Subject: [PATCH 10/28] enhance AutoAssignController to set player colors and enable LED functionality; update gamepad connection handling to load configuration while in Input settings. --- src/Ryujinx/AutoAssignController.cs | 21 +++++++++++++++++-- src/Ryujinx/Headless/HeadlessRyujinx.cs | 1 - .../UI/ViewModels/Input/InputViewModel.cs | 7 ++++++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx/AutoAssignController.cs b/src/Ryujinx/AutoAssignController.cs index 78b35fd65..a44ff4f58 100644 --- a/src/Ryujinx/AutoAssignController.cs +++ b/src/Ryujinx/AutoAssignController.cs @@ -1,4 +1,5 @@ -using Ryujinx.Ava.UI.ViewModels; +using Avalonia.Media; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -16,6 +17,17 @@ namespace Ryujinx.Ava { public class AutoAssignController { + private readonly uint[] _playerColors = + [ + 0xFFFF0000, // Player 1 - Red + 0xFF0000FF, // Player 2 - Blue + 0xFF00FF00, // Player 3 - Green + 0xFFFFFF00, // Player 4 - Yellow + 0xFFFF00FF, // Player 5 - Magenta + 0xFFFFA500, // Player 6 - Orange + 0xFF00FFFF, // Player 7 - Cyan + 0xFF800080 // Player 8 - Purple + ]; private const int MaxControllers = 9; private readonly InputManager _inputManager; @@ -48,6 +60,11 @@ namespace Ryujinx.Ava foreach (var config in newConfig) { config.PlayerIndex = (PlayerIndex)index; + if (config is StandardControllerInputConfig standardConfig) + { + Logger.Warning?.Print(LogClass.Application, $"Setting color for player {index+1}"); + standardConfig.Led.LedColor = _playerColors[index]; + } index++; } @@ -213,7 +230,7 @@ namespace Ryujinx.Ava }, Led = new LedConfigController { - EnableLed = false, + EnableLed = true, TurnOffLed = false, UseRainbow = false, LedColor = 0, diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index ea0727897..d61691405 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -38,7 +38,6 @@ 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; diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index d00712d81..e89e308bf 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -363,7 +363,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input private void HandleOnGamepadConnected(string id) { - Dispatcher.UIThread.Post(LoadDevices); + Dispatcher.UIThread.Post(() => + { + LoadDevices(); + LoadConfiguration(); + LoadDevice(); + }); } private string GetCurrentGamepadId() -- 2.47.2 From f07efb751e69023ebe744c6a144c6d2b4cbb3de9 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Fri, 7 Feb 2025 14:45:29 +0100 Subject: [PATCH 11/28] Relocate AutoAssignController.cs to a more appropriate directory --- src/Ryujinx/{ => Input}/AutoAssignController.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Ryujinx/{ => Input}/AutoAssignController.cs (100%) diff --git a/src/Ryujinx/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs similarity index 100% rename from src/Ryujinx/AutoAssignController.cs rename to src/Ryujinx/Input/AutoAssignController.cs -- 2.47.2 From 287d68c2cc8004165aad9b452e66866a197c57d7 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Fri, 7 Feb 2025 14:51:46 +0100 Subject: [PATCH 12/28] namespace correction --- src/Ryujinx/Input/AutoAssignController.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index a44ff4f58..71b443fa6 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -1,6 +1,4 @@ -using Avalonia.Media; -using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.Ava.UI.Windows; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; @@ -13,7 +11,7 @@ using Ryujinx.Input.HLE; using System.Collections.Generic; using System.Linq; -namespace Ryujinx.Ava +namespace Ryujinx.Ava.Input { public class AutoAssignController { -- 2.47.2 From 5034ef18c97e85eeb3380cce172e4878302703d5 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Fri, 7 Feb 2025 15:23:07 +0100 Subject: [PATCH 13/28] minor fix: swapped LoadConfiguration() and LoadDevice(). The previous order caused the configuration to load incorrectly. --- src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index e89e308bf..a88c7d64b 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -366,8 +366,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input Dispatcher.UIThread.Post(() => { LoadDevices(); - LoadConfiguration(); LoadDevice(); + LoadConfiguration(); }); } -- 2.47.2 From 5b88a2dd89f707e97fd2e9e3e7414bf1cd43cef5 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sun, 9 Feb 2025 16:07:12 +0100 Subject: [PATCH 14/28] enhance AutoAssignController to trigger configuration updates on gamepad connection changes; improve controller assignment logic and ensure proper LED color settings --- src/Ryujinx/Input/AutoAssignController.cs | 139 +++++++++--------- .../UI/ViewModels/Input/InputViewModel.cs | 18 ++- 2 files changed, 87 insertions(+), 70 deletions(-) diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index 71b443fa6..3af467d37 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -8,6 +8,7 @@ using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInpu using Ryujinx.Common.Logging; using Ryujinx.Input; using Ryujinx.Input.HLE; +using System; using System.Collections.Generic; using System.Linq; @@ -31,6 +32,8 @@ namespace Ryujinx.Ava.Input private readonly InputManager _inputManager; private readonly MainWindowViewModel _viewModel; private readonly ConfigurationState _configurationState; + + public event Action ConfigurationUpdated; public AutoAssignController(InputManager inputManager, MainWindowViewModel mainWindowViewModel) { @@ -43,39 +46,6 @@ namespace Ryujinx.Ava.Input RefreshControllers(); } - public void RefreshControllers() - { - if (!_configurationState.Hid.EnableAutoAssign) return; - //if (controllers.Count == 0) return; - - // Get every controller config and update the configuration state - - List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); - List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); - (List newConfig, bool hasNewControllersConnected) = GetOrderedConfig(controllers, oldConfig); - - int index = 0; - foreach (var config in newConfig) - { - config.PlayerIndex = (PlayerIndex)index; - if (config is StandardControllerInputConfig standardConfig) - { - Logger.Warning?.Print(LogClass.Application, $"Setting color for player {index+1}"); - standardConfig.Led.LedColor = _playerColors[index]; - } - index++; - } - - _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); - - // update the configuration state only if there are more controllers than before - if(hasNewControllersConnected) - { - _configurationState.Hid.InputConfig.Value = newConfig; - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } - } - private void HandleOnGamepadConnected(string id) { Logger.Warning?.Print(LogClass.Application, $"Gamepad connected: {id}"); @@ -88,50 +58,85 @@ namespace Ryujinx.Ava.Input RefreshControllers(); } - private (List, bool) GetOrderedConfig(List controllers, List oldConfig) + public void RefreshControllers() { - Dictionary playerIndexMap = new(); - int existingControllers = 0; - - // Convert oldConfig into a dictionary for quick lookup by controller Id - Dictionary oldConfigMap = oldConfig.ToDictionary(x => x.Id, x => x); - - foreach (var controller in controllers) + if (!_configurationState.Hid.EnableAutoAssign) return; + + // Get every controller config and update the configuration state + List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); + List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); + (List newConfig, bool hasNewControllersConnected) = GetOrderedConfig(controllers, oldConfig); + + _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); + + // update the configuration state only if there are new controllers connected + if(hasNewControllersConnected) { - if (controller == null) continue; + _configurationState.Hid.InputConfig.Value = newConfig; + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + } + ConfigurationUpdated?.Invoke(); + } - // If the controller already has a config in oldConfig, use it + private (List, bool) GetOrderedConfig(List controllers, List oldConfig) + { + Dictionary oldConfigMap = oldConfig.Where(c => c?.Id != null).ToDictionary(x => x.Id); + Dictionary playerIndexMap = new(); + HashSet usedIndices = new(); + int recognizedControllersCount = 0; + + // Assign controllers that have an old config + List remainingControllers = controllers.Where(c => c?.Id != null).ToList(); + foreach (var controller in remainingControllers.ToList()) + { if (oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig)) { - // Use the existing PlayerIndex from oldConfig and add it to the map - playerIndexMap[(int)existingConfig.PlayerIndex] = existingConfig; - - existingControllers++; - } - else - { - // Find the first available PlayerIndex (0 to MaxControllers) - for (int i = 0; i < MaxControllers-1; i++) + int desiredIndex = (int)existingConfig.PlayerIndex; + // Check if the desired index is valid and available + if (desiredIndex < 0 || desiredIndex >= MaxControllers || usedIndices.Contains(desiredIndex)) { - if (!playerIndexMap.ContainsKey(i)) // Check if the PlayerIndex is available - { - // Create a new InputConfig and assign PlayerIndex - InputConfig newConfig = CreateConfigFromController(controller); - newConfig.PlayerIndex = (PlayerIndex)i; - - // Add the new config to the map with the available PlayerIndex - playerIndexMap[i] = newConfig; - break; - } + // Find the first available index + desiredIndex = Enumerable.Range(0, MaxControllers).First(i => !usedIndices.Contains(i)); } + existingConfig.PlayerIndex = (PlayerIndex)desiredIndex; + usedIndices.Add(desiredIndex); + playerIndexMap[desiredIndex] = existingConfig; + recognizedControllersCount++; + + remainingControllers.Remove(controller); } } - // Return the sorted list of InputConfigs, ordered by PlayerIndex, and a bool indicating if new controllers were connected - return (playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(), controllers.Count > existingControllers); + // Assign remaining (new) controllers + foreach (var controller in remainingControllers.OrderBy(c => c.Id)) + { + InputConfig config = CreateConfigFromController(controller); + int freeIndex = Enumerable.Range(0, MaxControllers).First(i => !usedIndices.Contains(i)); + config.PlayerIndex = (PlayerIndex)freeIndex; + usedIndices.Add(freeIndex); + playerIndexMap[freeIndex] = config; + } + + List orderedConfigs = playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(); + + // Sequential reassignment of PlayerIndex and LED colors + int index = 0; + foreach (var config in orderedConfigs) + { + config.PlayerIndex = (PlayerIndex)index; + if (config is StandardControllerInputConfig standardConfig) + { + Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index+1}"); + standardConfig.Led.LedColor = _playerColors[index]; + } + index++; + } + + bool hasNewControllersConnected = (controllers.Count > recognizedControllersCount); + return (orderedConfigs, hasNewControllersConnected); } - - private InputConfig CreateConfigFromController(IGamepad controller) + + private static InputConfig CreateConfigFromController(IGamepad controller) { if (controller == null) return null; @@ -158,7 +163,7 @@ namespace Ryujinx.Ava.Input } else { - // if it's not a nintendo controller, we assume it's a pro controller or a joycon pair + // if it's not a nintendo controller, we assume it's a pro controller or a joy-con pair controllerType = ControllerType.ProController; } diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index a88c7d64b..75ba27960 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -245,6 +245,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; + _mainWindow.AutoAssignController.ConfigurationUpdated += OnConfigurationUpdated; + _mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates(); _isLoaded = false; @@ -358,16 +360,25 @@ namespace Ryujinx.Ava.UI.ViewModels.Input private void HandleOnGamepadDisconnected(string id) { + if(ConfigurationState.Instance.Hid.EnableAutoAssign) return; Dispatcher.UIThread.Post(LoadDevices); } private void HandleOnGamepadConnected(string id) { - Dispatcher.UIThread.Post(() => - { + if(ConfigurationState.Instance.Hid.EnableAutoAssign) return; + Dispatcher.UIThread.Post(LoadDevices); + } + + private void OnConfigurationUpdated() + { + Dispatcher.UIThread.Post(() => { LoadDevices(); + _isLoaded = false; + LoadConfiguration(); LoadDevice(); - LoadConfiguration(); + _isLoaded = true; + NotifyChanges(); }); } @@ -888,6 +899,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; + _mainWindow.AutoAssignController.ConfigurationUpdated -= OnConfigurationUpdated; _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates(); -- 2.47.2 From 62dfbb5dcbe2647235c196b042994509e0e14f3e Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sun, 9 Feb 2025 18:10:32 +0100 Subject: [PATCH 15/28] edited NpadManager to better support auto-assign; updated initialization methods; changed some settings for LED color --- src/Ryujinx.Input/HLE/NpadManager.cs | 12 +++++++++--- src/Ryujinx/AppHost.cs | 2 +- src/Ryujinx/Headless/HeadlessRyujinx.cs | 2 +- src/Ryujinx/Headless/Windows/WindowBase.cs | 4 ++-- src/Ryujinx/Input/AutoAssignController.cs | 2 +- src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs | 6 ++++++ 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 198989444..b551d8b02 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -43,6 +43,8 @@ namespace Ryujinx.Input.HLE private bool _enableMouse; private Switch _device; + public bool AutoAssignEnabled { get; set; } = false; + public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver) { _controllers = new NpadController[MaxControllers]; @@ -53,7 +55,7 @@ namespace Ryujinx.Input.HLE _mouseDriver = mouseDriver; _inputConfig = new List(); - //_gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; + _gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; } @@ -89,12 +91,14 @@ namespace Ryujinx.Input.HLE } } - //ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); + if (AutoAssignEnabled) return; + ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); } } private void HandleOnGamepadConnected(string id) { + if (AutoAssignEnabled) return; // Force input reload ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); } @@ -198,11 +202,13 @@ namespace Ryujinx.Input.HLE } } - public void Initialize(Switch device, List inputConfig, bool enableKeyboard, bool enableMouse) + public void Initialize(Switch device, List inputConfig, bool enableKeyboard, bool enableMouse, bool enableAutoAssign) { _device = device; _device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE; + AutoAssignEnabled = enableAutoAssign; + ReloadConfiguration(inputConfig, enableKeyboard, enableMouse); } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index d161644c0..cfb3b63bd 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); + NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse, ConfigurationState.Instance.Hid.EnableAutoAssign); TouchScreenManager.Initialize(Device); _viewModel.IsGameRunning = true; diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index d61691405..237c63ad4 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -370,7 +370,7 @@ namespace Ryujinx.Headless DisplaySleep.Prevent(); - _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse); + _window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse, _enableAutoAssign); _window.Execute(); diff --git a/src/Ryujinx/Headless/Windows/WindowBase.cs b/src/Ryujinx/Headless/Windows/WindowBase.cs index 8fd445199..cf3e846be 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) + public void Initialize(Switch device, List inputConfigs, bool enableKeyboard, bool enableMouse, bool enableAutoAssign) { Device = device; @@ -133,7 +133,7 @@ namespace Ryujinx.Headless Renderer = renderer; - NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse); + NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse, enableAutoAssign); TouchScreenManager.Initialize(device); } diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index 3af467d37..58978531d 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -127,7 +127,7 @@ namespace Ryujinx.Ava.Input if (config is StandardControllerInputConfig standardConfig) { Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index+1}"); - standardConfig.Led.LedColor = _playerColors[index]; + standardConfig.Led = new LedConfigController { EnableLed = true, LedColor = _playerColors[index] }; } index++; } diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 75ba27960..800deed01 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -868,6 +868,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input } } + if (_mainWindow.ViewModel.AppHost != null) + { + _mainWindow.ViewModel.AppHost.NpadManager.AutoAssignEnabled = + ConfigurationState.Instance.Hid.EnableAutoAssign; + } + _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); // Atomically replace and signal input change. -- 2.47.2 From 8cc74dab08e9f36c113eb2cd9a5061425b0893b1 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Wed, 12 Feb 2025 19:44:16 +0100 Subject: [PATCH 16/28] Improved assignment logic in AutoAssignController.cs --- src/Ryujinx/Input/AutoAssignController.cs | 155 ++++++++++++++++------ 1 file changed, 118 insertions(+), 37 deletions(-) diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index 58978531d..5ebba82f6 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -1,4 +1,5 @@ -using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Models.Input; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; @@ -61,79 +62,159 @@ namespace Ryujinx.Ava.Input public void RefreshControllers() { if (!_configurationState.Hid.EnableAutoAssign) return; - - // Get every controller config and update the configuration state + List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); - (List newConfig, bool hasNewControllersConnected) = GetOrderedConfig(controllers, oldConfig); - + + (List newConfig, bool hasNewControllersConnected) = GetOrderedConfig(controllers, oldConfig); + _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); - - // update the configuration state only if there are new controllers connected - if(hasNewControllersConnected) + + if (hasNewControllersConnected) { - _configurationState.Hid.InputConfig.Value = newConfig; ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } + else + { + // we must switch the order of the controllers in oldConfig to match the new order + newConfig = ReorderControllers(newConfig, oldConfig); + } + + _configurationState.Hid.InputConfig.Value = newConfig; ConfigurationUpdated?.Invoke(); } + /// + /// Ensures that the order of controllers remains the same unless a new controller is connected. + /// + private List ReorderControllers(List newConfig, List oldConfig) + { + List reorderedConfig = oldConfig.Select(config => new GamepadInputConfig(config).GetConfig()).ToList(); + + foreach (var config in newConfig) + { + InputConfig substitute = reorderedConfig.FirstOrDefault(x => x.Id == config.Id); + InputConfig toBeReplaced = reorderedConfig.FirstOrDefault(x => x.PlayerIndex == config.PlayerIndex); + + if (substitute == null || toBeReplaced == null || substitute.PlayerIndex == toBeReplaced.PlayerIndex) continue; + + (substitute.PlayerIndex, toBeReplaced.PlayerIndex) = (toBeReplaced.PlayerIndex, substitute.PlayerIndex); + } + + return reorderedConfig; + } + + /// + /// Orders controllers, preserving existing mappings while assigning new controllers to available slots. + /// private (List, bool) GetOrderedConfig(List controllers, List oldConfig) - { - Dictionary oldConfigMap = oldConfig.Where(c => c?.Id != null).ToDictionary(x => x.Id); + { + Dictionary oldConfigMap = oldConfig.Where(c => c?.Id != null) + .ToDictionary(x => x.Id); + Dictionary playerIndexMap = new(); HashSet usedIndices = new(); int recognizedControllersCount = 0; - // Assign controllers that have an old config List remainingControllers = controllers.Where(c => c?.Id != null).ToList(); - foreach (var controller in remainingControllers.ToList()) + + // Assign controllers that have an existing configuration + AssignExistingControllers(remainingControllers, oldConfigMap, playerIndexMap, usedIndices, ref recognizedControllersCount); + + // Assign new controllers + AssignNewControllers(remainingControllers, playerIndexMap, usedIndices); + + List orderedConfigs = playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(); + + // Sequentially update PlayerIndex and LED colors + UpdatePlayerIndicesAndLEDs(orderedConfigs); + + bool hasNewControllersConnected = controllers.Count > recognizedControllersCount; + return (orderedConfigs, hasNewControllersConnected); + } + + /// + /// Assigns controllers with existing configurations while keeping their PlayerIndex if available. + /// + private void AssignExistingControllers( + List controllers, + Dictionary oldConfigMap, + Dictionary playerIndexMap, + HashSet usedIndices, + ref int recognizedControllersCount) + { + foreach (var controller in controllers.ToList()) { if (oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig)) { int desiredIndex = (int)existingConfig.PlayerIndex; - // Check if the desired index is valid and available + + // Ensure the index is valid and available if (desiredIndex < 0 || desiredIndex >= MaxControllers || usedIndices.Contains(desiredIndex)) { - // Find the first available index - desiredIndex = Enumerable.Range(0, MaxControllers).First(i => !usedIndices.Contains(i)); + desiredIndex = GetFirstAvailableIndex(usedIndices); } - existingConfig.PlayerIndex = (PlayerIndex)desiredIndex; + + InputConfig config = new GamepadInputConfig(existingConfig).GetConfig(); + config.PlayerIndex = (PlayerIndex)desiredIndex; usedIndices.Add(desiredIndex); - playerIndexMap[desiredIndex] = existingConfig; + playerIndexMap[desiredIndex] = config; recognizedControllersCount++; - - remainingControllers.Remove(controller); + + controllers.Remove(controller); } } + } - // Assign remaining (new) controllers - foreach (var controller in remainingControllers.OrderBy(c => c.Id)) + /// + /// Assigns new controllers to the first available PlayerIndex. + /// + private void AssignNewControllers( + List controllers, + Dictionary playerIndexMap, + HashSet usedIndices) + { + foreach (var controller in controllers) { InputConfig config = CreateConfigFromController(controller); - int freeIndex = Enumerable.Range(0, MaxControllers).First(i => !usedIndices.Contains(i)); + int freeIndex = GetFirstAvailableIndex(usedIndices); config.PlayerIndex = (PlayerIndex)freeIndex; usedIndices.Add(freeIndex); playerIndexMap[freeIndex] = config; } + } - List orderedConfigs = playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(); - - // Sequential reassignment of PlayerIndex and LED colors - int index = 0; - foreach (var config in orderedConfigs) + /// + /// Finds the first available PlayerIndex that isn't in use. + /// + private int GetFirstAvailableIndex(HashSet usedIndices) + { + for (int i = 0; i < MaxControllers; i++) { - config.PlayerIndex = (PlayerIndex)index; - if (config is StandardControllerInputConfig standardConfig) - { - Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index+1}"); - standardConfig.Led = new LedConfigController { EnableLed = true, LedColor = _playerColors[index] }; - } - index++; + if (!usedIndices.Contains(i)) return i; } + return -1; // Should not happen unless MaxControllers is exceeded + } - bool hasNewControllersConnected = (controllers.Count > recognizedControllersCount); - return (orderedConfigs, hasNewControllersConnected); + /// + /// Updates PlayerIndex and LED configurations sequentially. + /// + private void UpdatePlayerIndicesAndLEDs(List orderedConfigs) + { + for (int index = 0; index < orderedConfigs.Count; index++) + { + orderedConfigs[index].PlayerIndex = (PlayerIndex)index; + + if (orderedConfigs[index] is StandardControllerInputConfig standardConfig) + { + Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}"); + standardConfig.Led = new LedConfigController + { + EnableLed = true, + LedColor = _playerColors[index] + }; + } + } } private static InputConfig CreateConfigFromController(IGamepad controller) -- 2.47.2 From e238ea85c9be62f493939b6616ab79eef804ad01 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Mon, 17 Feb 2025 20:59:51 +0100 Subject: [PATCH 17/28] fixed save config logic --- src/Ryujinx/Input/AutoAssignController.cs | 30 +++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index 5ebba82f6..84c35cef2 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -67,20 +67,24 @@ namespace Ryujinx.Ava.Input List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); (List newConfig, bool hasNewControllersConnected) = GetOrderedConfig(controllers, oldConfig); - + _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); - if (hasNewControllersConnected) + if (!hasNewControllersConnected) { - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } - else - { - // we must switch the order of the controllers in oldConfig to match the new order + // there is no *new* controller, we must switch the order of the controllers in + // oldConfig to match the new order since probably a controller was disconnected + // or an old controller was reconnected newConfig = ReorderControllers(newConfig, oldConfig); } _configurationState.Hid.InputConfig.Value = newConfig; + + // we want to save the configuration only if a *new* controller was connected + if(hasNewControllersConnected) + { + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + } ConfigurationUpdated?.Invoke(); } @@ -119,10 +123,10 @@ namespace Ryujinx.Ava.Input List remainingControllers = controllers.Where(c => c?.Id != null).ToList(); // Assign controllers that have an existing configuration - AssignExistingControllers(remainingControllers, oldConfigMap, playerIndexMap, usedIndices, ref recognizedControllersCount); + AddExistingControllers(remainingControllers, oldConfigMap, playerIndexMap, usedIndices, ref recognizedControllersCount); // Assign new controllers - AssignNewControllers(remainingControllers, playerIndexMap, usedIndices); + AddNewControllers(remainingControllers, playerIndexMap, usedIndices); List orderedConfigs = playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(); @@ -134,9 +138,9 @@ namespace Ryujinx.Ava.Input } /// - /// Assigns controllers with existing configurations while keeping their PlayerIndex if available. + /// Adds controllers with existing configurations while keeping their PlayerIndex if available. /// - private void AssignExistingControllers( + private void AddExistingControllers( List controllers, Dictionary oldConfigMap, Dictionary playerIndexMap, @@ -167,9 +171,9 @@ namespace Ryujinx.Ava.Input } /// - /// Assigns new controllers to the first available PlayerIndex. + /// Adds new controllers to the first available PlayerIndex. /// - private void AssignNewControllers( + private void AddNewControllers( List controllers, Dictionary playerIndexMap, HashSet usedIndices) -- 2.47.2 From b8cb70ef32b0850b8b364f80a35ce0c55580d6ac Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Mon, 17 Feb 2025 22:17:54 +0100 Subject: [PATCH 18/28] possible refactor. to be tested --- src/Ryujinx/Input/AutoAssignController.cs | 269 +----------------- .../Input/ControllerAssignmentManager.cs | 259 +++++++++++++++++ 2 files changed, 269 insertions(+), 259 deletions(-) create mode 100644 src/Ryujinx/Input/ControllerAssignmentManager.cs diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index 84c35cef2..7568d19ac 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -17,33 +17,23 @@ namespace Ryujinx.Ava.Input { public class AutoAssignController { - private readonly uint[] _playerColors = - [ - 0xFFFF0000, // Player 1 - Red - 0xFF0000FF, // Player 2 - Blue - 0xFF00FF00, // Player 3 - Green - 0xFFFFFF00, // Player 4 - Yellow - 0xFFFF00FF, // Player 5 - Magenta - 0xFFFFA500, // Player 6 - Orange - 0xFF00FFFF, // Player 7 - Cyan - 0xFF800080 // Player 8 - Purple - ]; - private const int MaxControllers = 9; - private readonly InputManager _inputManager; private readonly MainWindowViewModel _viewModel; private readonly ConfigurationState _configurationState; + private readonly ControllerConfigurator _controllerConfigurator; public event Action ConfigurationUpdated; - + public AutoAssignController(InputManager inputManager, MainWindowViewModel mainWindowViewModel) { _inputManager = inputManager; _viewModel = mainWindowViewModel; _configurationState = ConfigurationState.Instance; + _controllerConfigurator = new ControllerConfigurator(); + _inputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _inputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; - + RefreshControllers(); } @@ -52,7 +42,7 @@ namespace Ryujinx.Ava.Input Logger.Warning?.Print(LogClass.Application, $"Gamepad connected: {id}"); RefreshControllers(); } - + private void HandleOnGamepadDisconnected(string id) { Logger.Warning?.Print(LogClass.Application, $"Gamepad disconnected: {id}"); @@ -66,8 +56,9 @@ namespace Ryujinx.Ava.Input List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); - (List newConfig, bool hasNewControllersConnected) = GetOrderedConfig(controllers, oldConfig); - + List newConfig = _controllerConfigurator.GetConfiguredControllers( + controllers, oldConfig, new HashSet(), out bool hasNewControllersConnected); + _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); if (!hasNewControllersConnected) @@ -75,7 +66,7 @@ namespace Ryujinx.Ava.Input // there is no *new* controller, we must switch the order of the controllers in // oldConfig to match the new order since probably a controller was disconnected // or an old controller was reconnected - newConfig = ReorderControllers(newConfig, oldConfig); + newConfig = _controllerConfigurator.ReorderControllers(newConfig, oldConfig); } _configurationState.Hid.InputConfig.Value = newConfig; @@ -87,245 +78,5 @@ namespace Ryujinx.Ava.Input } ConfigurationUpdated?.Invoke(); } - - /// - /// Ensures that the order of controllers remains the same unless a new controller is connected. - /// - private List ReorderControllers(List newConfig, List oldConfig) - { - List reorderedConfig = oldConfig.Select(config => new GamepadInputConfig(config).GetConfig()).ToList(); - - foreach (var config in newConfig) - { - InputConfig substitute = reorderedConfig.FirstOrDefault(x => x.Id == config.Id); - InputConfig toBeReplaced = reorderedConfig.FirstOrDefault(x => x.PlayerIndex == config.PlayerIndex); - - if (substitute == null || toBeReplaced == null || substitute.PlayerIndex == toBeReplaced.PlayerIndex) continue; - - (substitute.PlayerIndex, toBeReplaced.PlayerIndex) = (toBeReplaced.PlayerIndex, substitute.PlayerIndex); - } - - return reorderedConfig; - } - - /// - /// Orders controllers, preserving existing mappings while assigning new controllers to available slots. - /// - private (List, bool) GetOrderedConfig(List controllers, List oldConfig) - { - Dictionary oldConfigMap = oldConfig.Where(c => c?.Id != null) - .ToDictionary(x => x.Id); - - Dictionary playerIndexMap = new(); - HashSet usedIndices = new(); - int recognizedControllersCount = 0; - - List remainingControllers = controllers.Where(c => c?.Id != null).ToList(); - - // Assign controllers that have an existing configuration - AddExistingControllers(remainingControllers, oldConfigMap, playerIndexMap, usedIndices, ref recognizedControllersCount); - - // Assign new controllers - AddNewControllers(remainingControllers, playerIndexMap, usedIndices); - - List orderedConfigs = playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(); - - // Sequentially update PlayerIndex and LED colors - UpdatePlayerIndicesAndLEDs(orderedConfigs); - - bool hasNewControllersConnected = controllers.Count > recognizedControllersCount; - return (orderedConfigs, hasNewControllersConnected); - } - - /// - /// Adds controllers with existing configurations while keeping their PlayerIndex if available. - /// - private void AddExistingControllers( - List controllers, - Dictionary oldConfigMap, - Dictionary playerIndexMap, - HashSet usedIndices, - ref int recognizedControllersCount) - { - foreach (var controller in controllers.ToList()) - { - if (oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig)) - { - int desiredIndex = (int)existingConfig.PlayerIndex; - - // Ensure the index is valid and available - if (desiredIndex < 0 || desiredIndex >= MaxControllers || usedIndices.Contains(desiredIndex)) - { - desiredIndex = GetFirstAvailableIndex(usedIndices); - } - - InputConfig config = new GamepadInputConfig(existingConfig).GetConfig(); - config.PlayerIndex = (PlayerIndex)desiredIndex; - usedIndices.Add(desiredIndex); - playerIndexMap[desiredIndex] = config; - recognizedControllersCount++; - - controllers.Remove(controller); - } - } - } - - /// - /// Adds new controllers to the first available PlayerIndex. - /// - private void AddNewControllers( - List controllers, - Dictionary playerIndexMap, - HashSet usedIndices) - { - foreach (var controller in controllers) - { - InputConfig config = CreateConfigFromController(controller); - int freeIndex = GetFirstAvailableIndex(usedIndices); - config.PlayerIndex = (PlayerIndex)freeIndex; - usedIndices.Add(freeIndex); - playerIndexMap[freeIndex] = config; - } - } - - /// - /// Finds the first available PlayerIndex that isn't in use. - /// - private int GetFirstAvailableIndex(HashSet usedIndices) - { - for (int i = 0; i < MaxControllers; i++) - { - if (!usedIndices.Contains(i)) return i; - } - return -1; // Should not happen unless MaxControllers is exceeded - } - - /// - /// Updates PlayerIndex and LED configurations sequentially. - /// - private void UpdatePlayerIndicesAndLEDs(List orderedConfigs) - { - for (int index = 0; index < orderedConfigs.Count; index++) - { - orderedConfigs[index].PlayerIndex = (PlayerIndex)index; - - if (orderedConfigs[index] is StandardControllerInputConfig standardConfig) - { - Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}"); - standardConfig.Led = new LedConfigController - { - EnableLed = true, - LedColor = _playerColors[index] - }; - } - } - } - - private static 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 joy-con 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 = true, - TurnOffLed = false, - UseRainbow = false, - LedColor = 0, - }, - }; - - return config; - } } } diff --git a/src/Ryujinx/Input/ControllerAssignmentManager.cs b/src/Ryujinx/Input/ControllerAssignmentManager.cs new file mode 100644 index 000000000..9b0fb70ad --- /dev/null +++ b/src/Ryujinx/Input/ControllerAssignmentManager.cs @@ -0,0 +1,259 @@ +using Ryujinx.Ava.UI.Models.Input; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Controller.Motion; +using Ryujinx.Common.Logging; +using Ryujinx.Input; +using System.Collections.Generic; +using System.Linq; +using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; + +namespace Ryujinx.Ava.Input +{ + public class ControllerConfigurator +{ + private readonly uint[] _playerColors = + [ + 0xFFFF0000, // Player 1 - Red + 0xFF0000FF, // Player 2 - Blue + 0xFF00FF00, // Player 3 - Green + 0xFFFFFF00, // Player 4 - Yellow + 0xFFFF00FF, // Player 5 - Magenta + 0xFFFFA500, // Player 6 - Orange + 0xFF00FFFF, // Player 7 - Cyan + 0xFF800080 // Player 8 - Purple + ]; + + private const int MaxControllers = 9; + + public List ReorderControllers(List newConfig, List oldConfig) + { + List reorderedConfig = oldConfig.Select(config => new GamepadInputConfig(config).GetConfig()).ToList(); + + foreach (var config in newConfig) + { + InputConfig substitute = reorderedConfig.FirstOrDefault(x => x.Id == config.Id); + InputConfig toBeReplaced = reorderedConfig.FirstOrDefault(x => x.PlayerIndex == config.PlayerIndex); + + if (substitute == null || toBeReplaced == null || substitute.PlayerIndex == toBeReplaced.PlayerIndex) continue; + + (substitute.PlayerIndex, toBeReplaced.PlayerIndex) = (toBeReplaced.PlayerIndex, substitute.PlayerIndex); + } + + return reorderedConfig; + } + + public List GetConfiguredControllers( + List controllers, + List oldConfig, + HashSet usedIndices, + out bool hasNewControllersConnected) + { + Dictionary oldConfigMap = oldConfig + .Where(c => c?.Id != null) + .ToDictionary(x => x.Id); + + Dictionary playerIndexMap = new(); + int recognizedControllersCount = 0; + + List remainingControllers = controllers.Where(c => c?.Id != null).ToList(); + + // Add controllers with existing configurations + AddExistingControllers(remainingControllers, oldConfigMap, playerIndexMap, usedIndices, ref recognizedControllersCount); + + // Add new controllers + AddNewControllers(remainingControllers, playerIndexMap, usedIndices); + + List orderedConfigs = playerIndexMap + .OrderBy(x => x.Key) + .Select(x => x.Value) + .ToList(); + + // Update player indices and LED colors + UpdatePlayerIndicesAndLEDs(orderedConfigs); + + hasNewControllersConnected = controllers.Count > recognizedControllersCount; + return orderedConfigs; + } + + private void AddExistingControllers( + List controllers, + Dictionary oldConfigMap, + Dictionary playerIndexMap, + HashSet usedIndices, + ref int recognizedControllersCount) + { + foreach (var controller in controllers.ToList()) + { + if (oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig)) + { + int desiredIndex = (int)existingConfig.PlayerIndex; + + // Ensure the index is valid and available + if (desiredIndex < 0 || desiredIndex >= MaxControllers || usedIndices.Contains(desiredIndex)) + { + desiredIndex = GetFirstAvailableIndex(usedIndices); + } + + InputConfig config = new GamepadInputConfig(existingConfig).GetConfig(); + config.PlayerIndex = (PlayerIndex)desiredIndex; + usedIndices.Add(desiredIndex); + playerIndexMap[desiredIndex] = config; + recognizedControllersCount++; + + controllers.Remove(controller); + } + } + } + + private void AddNewControllers( + List controllers, + Dictionary playerIndexMap, + HashSet usedIndices) + { + foreach (var controller in controllers) + { + InputConfig config = CreateConfigFromController(controller); + int freeIndex = GetFirstAvailableIndex(usedIndices); + config.PlayerIndex = (PlayerIndex)freeIndex; + usedIndices.Add(freeIndex); + playerIndexMap[freeIndex] = config; + } + } + + private int GetFirstAvailableIndex(HashSet usedIndices) + { + for (int i = 0; i < MaxControllers; i++) + { + if (!usedIndices.Contains(i)) return i; + } + return -1; // Should not happen unless MaxControllers is exceeded + } + + private void UpdatePlayerIndicesAndLEDs(List orderedConfigs) + { + for (int index = 0; index < orderedConfigs.Count; index++) + { + orderedConfigs[index].PlayerIndex = (PlayerIndex)index; + + if (orderedConfigs[index] is StandardControllerInputConfig standardConfig) + { + Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}"); + standardConfig.Led = new LedConfigController + { + EnableLed = true, + LedColor = _playerColors[index] + }; + } + } + } + + private static 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 joy-con 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) ? GamepadInputId.Y : GamepadInputId.DpadUp, + DpadDown = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.A : GamepadInputId.DpadDown, + DpadLeft = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.B : GamepadInputId.DpadLeft, + DpadRight = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.X : GamepadInputId.DpadRight, + ButtonMinus = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.Plus : GamepadInputId.Minus, + ButtonL = GamepadInputId.LeftShoulder, + ButtonZl = GamepadInputId.LeftTrigger, + ButtonSl = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.LeftShoulder : GamepadInputId.Unbound, + ButtonSr = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.RightShoulder : GamepadInputId.Unbound, + }, + LeftJoyconStick = new JoyconConfigControllerStick + { + Joystick = StickInputId.Left, + StickButton = GamepadInputId.LeftStick, + InvertStickX = false, + InvertStickY = false, + Rotate90CW = (controllerType == ControllerType.JoyconLeft), + }, + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = GamepadInputId.B, + ButtonB = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.Y : GamepadInputId.A, + ButtonX = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.A : GamepadInputId.Y, + ButtonY = GamepadInputId.X, + ButtonPlus = GamepadInputId.Plus, + ButtonR = GamepadInputId.RightShoulder, + ButtonZr = GamepadInputId.RightTrigger, + ButtonSl = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.LeftShoulder : GamepadInputId.Unbound, + ButtonSr = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.RightShoulder : GamepadInputId.Unbound, + }, + RightJoyconStick = new JoyconConfigControllerStick + { + Joystick = (controllerType == ControllerType.JoyconRight) ? StickInputId.Left : StickInputId.Right, + StickButton = GamepadInputId.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 = true, + TurnOffLed = false, + UseRainbow = false, + LedColor = 0, + }, + }; + + return config; + } +} + +} -- 2.47.2 From fd9bce0f6b481f0508c437dbf641a330eacb3f08 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Tue, 18 Feb 2025 11:27:30 +0100 Subject: [PATCH 19/28] fixed led live update while in settings --- .../Input/ControllerAssignmentManager.cs | 2 ++ .../UI/ViewModels/Input/InputViewModel.cs | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx/Input/ControllerAssignmentManager.cs b/src/Ryujinx/Input/ControllerAssignmentManager.cs index 9b0fb70ad..4cc77cbc5 100644 --- a/src/Ryujinx/Input/ControllerAssignmentManager.cs +++ b/src/Ryujinx/Input/ControllerAssignmentManager.cs @@ -94,6 +94,8 @@ namespace Ryujinx.Ava.Input { desiredIndex = GetFirstAvailableIndex(usedIndices); } + + if(desiredIndex == -1) continue; InputConfig config = new GamepadInputConfig(existingConfig).GetConfig(); config.PlayerIndex = (PlayerIndex)desiredIndex; diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 800deed01..427bdf4e2 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -375,10 +375,23 @@ namespace Ryujinx.Ava.UI.ViewModels.Input Dispatcher.UIThread.Post(() => { LoadDevices(); _isLoaded = false; - LoadConfiguration(); + LoadConfiguration(); LoadDevice(); _isLoaded = true; - NotifyChanges(); + + if (ConfigViewModel is not ControllerInputViewModel controllerInputViewModel) return; + GamepadInputConfig inputConfig = controllerInputViewModel.Config; + + if (inputConfig is not { EnableLedChanging: true }) return; + + if (inputConfig.TurnOffLed) + { + SelectedGamepad.ClearLed(); + } + else + { + SelectedGamepad.SetLed(inputConfig.LedColor.ToUInt32()); + } }); } -- 2.47.2 From 38ecb3d5bc4fd17eb2562e960720b7b20fad0a8e Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Tue, 18 Feb 2025 13:40:34 +0100 Subject: [PATCH 20/28] added function to handle gamepad led while in settings; added rainbow color handling in the new function. --- src/Ryujinx/Program.cs | 1 - .../UI/ViewModels/Input/InputViewModel.cs | 35 ++++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 853e87f0e..a6a991f84 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -19,7 +19,6 @@ 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; diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 427bdf4e2..db4d66f99 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -379,22 +379,31 @@ namespace Ryujinx.Ava.UI.ViewModels.Input LoadDevice(); _isLoaded = true; - if (ConfigViewModel is not ControllerInputViewModel controllerInputViewModel) return; - GamepadInputConfig inputConfig = controllerInputViewModel.Config; - - if (inputConfig is not { EnableLedChanging: true }) return; - - if (inputConfig.TurnOffLed) - { - SelectedGamepad.ClearLed(); - } - else - { - SelectedGamepad.SetLed(inputConfig.LedColor.ToUInt32()); - } + UpdateGamepadLed(); }); } + private void UpdateGamepadLed() + { + if (ConfigViewModel is not ControllerInputViewModel controllerInputViewModel) return; + GamepadInputConfig inputConfig = controllerInputViewModel.Config; + + if (inputConfig is not { EnableLedChanging: true }) return; + + if (inputConfig.TurnOffLed) + { + SelectedGamepad.ClearLed(); + } + else if(inputConfig.UseRainbowLed) + { + SelectedGamepad.SetLed((uint)Rainbow.Color.ToArgb()); + } + else + { + SelectedGamepad.SetLed(inputConfig.LedColor.ToUInt32()); + } + } + private string GetCurrentGamepadId() { if (_device < 0) -- 2.47.2 From cc905280cdf1ec44a3ea053008e6e51bdb0b74f5 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Tue, 18 Feb 2025 14:38:30 +0100 Subject: [PATCH 21/28] fixed some problems with rainbow led. also, rainbow won't be visible in settings (no easy way to constantly update it) --- src/Ryujinx/Input/ControllerAssignmentManager.cs | 1 + src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs | 11 ++++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx/Input/ControllerAssignmentManager.cs b/src/Ryujinx/Input/ControllerAssignmentManager.cs index 4cc77cbc5..9e8bca7bd 100644 --- a/src/Ryujinx/Input/ControllerAssignmentManager.cs +++ b/src/Ryujinx/Input/ControllerAssignmentManager.cs @@ -140,6 +140,7 @@ namespace Ryujinx.Ava.Input if (orderedConfigs[index] is StandardControllerInputConfig standardConfig) { + if(standardConfig.Led.UseRainbow) continue; Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}"); standardConfig.Led = new LedConfigController { diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index db4d66f99..e79a2617a 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -387,18 +387,15 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { if (ConfigViewModel is not ControllerInputViewModel controllerInputViewModel) return; GamepadInputConfig inputConfig = controllerInputViewModel.Config; - + if (inputConfig is not { EnableLedChanging: true }) return; - + if (inputConfig.TurnOffLed) { SelectedGamepad.ClearLed(); - } - else if(inputConfig.UseRainbowLed) - { - SelectedGamepad.SetLed((uint)Rainbow.Color.ToArgb()); } - else + + if (!inputConfig.TurnOffLed && !inputConfig.UseRainbowLed) { SelectedGamepad.SetLed(inputConfig.LedColor.ToUInt32()); } -- 2.47.2 From 55f3a4b7ff68b669fd13c7bf85fa31f765f4c42e Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Tue, 18 Feb 2025 14:43:00 +0100 Subject: [PATCH 22/28] simplified logic --- .../Input/ControllerAssignmentManager.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx/Input/ControllerAssignmentManager.cs b/src/Ryujinx/Input/ControllerAssignmentManager.cs index 9e8bca7bd..11f98a208 100644 --- a/src/Ryujinx/Input/ControllerAssignmentManager.cs +++ b/src/Ryujinx/Input/ControllerAssignmentManager.cs @@ -138,16 +138,18 @@ namespace Ryujinx.Ava.Input { orderedConfigs[index].PlayerIndex = (PlayerIndex)index; - if (orderedConfigs[index] is StandardControllerInputConfig standardConfig) + if (orderedConfigs[index] is not StandardControllerInputConfig standardConfig || + standardConfig.Led.UseRainbow) { - if(standardConfig.Led.UseRainbow) continue; - Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}"); - standardConfig.Led = new LedConfigController - { - EnableLed = true, - LedColor = _playerColors[index] - }; + continue; } + + Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}"); + standardConfig.Led = new LedConfigController + { + EnableLed = true, + LedColor = _playerColors[index] + }; } } -- 2.47.2 From 72c3ca7769dcdde9b73d9892905d04335b93adc6 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sat, 22 Feb 2025 10:15:41 +0100 Subject: [PATCH 23/28] changed var name; fixed class name; removed hashset argument from GetConfiguredController function. --- src/Ryujinx/Assets/locales.json | 2 +- src/Ryujinx/Input/AutoAssignController.cs | 17 +- .../Input/ControllerAssignmentManager.cs | 414 +++++++++--------- 3 files changed, 215 insertions(+), 218 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 8688b95f8..aafa7200d 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -24173,4 +24173,4 @@ } } ] -} +} \ No newline at end of file diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index 7568d19ac..360d0af64 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -1,11 +1,6 @@ -using Ryujinx.Ava.UI.Models.Input; -using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.ViewModels; 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; @@ -20,7 +15,7 @@ namespace Ryujinx.Ava.Input private readonly InputManager _inputManager; private readonly MainWindowViewModel _viewModel; private readonly ConfigurationState _configurationState; - private readonly ControllerConfigurator _controllerConfigurator; + private readonly ControllerAssignmentManager _assignmentManager; public event Action ConfigurationUpdated; @@ -29,7 +24,7 @@ namespace Ryujinx.Ava.Input _inputManager = inputManager; _viewModel = mainWindowViewModel; _configurationState = ConfigurationState.Instance; - _controllerConfigurator = new ControllerConfigurator(); + _assignmentManager = new ControllerAssignmentManager(); _inputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _inputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; @@ -56,8 +51,8 @@ namespace Ryujinx.Ava.Input List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); - List newConfig = _controllerConfigurator.GetConfiguredControllers( - controllers, oldConfig, new HashSet(), out bool hasNewControllersConnected); + List newConfig = _assignmentManager.GetConfiguredControllers( + controllers, oldConfig, out bool hasNewControllersConnected); _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); @@ -66,7 +61,7 @@ namespace Ryujinx.Ava.Input // there is no *new* controller, we must switch the order of the controllers in // oldConfig to match the new order since probably a controller was disconnected // or an old controller was reconnected - newConfig = _controllerConfigurator.ReorderControllers(newConfig, oldConfig); + newConfig = ControllerAssignmentManager.ReorderControllers(newConfig, oldConfig); } _configurationState.Hid.InputConfig.Value = newConfig; diff --git a/src/Ryujinx/Input/ControllerAssignmentManager.cs b/src/Ryujinx/Input/ControllerAssignmentManager.cs index 11f98a208..8b24cb073 100644 --- a/src/Ryujinx/Input/ControllerAssignmentManager.cs +++ b/src/Ryujinx/Input/ControllerAssignmentManager.cs @@ -10,83 +10,86 @@ using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; namespace Ryujinx.Ava.Input { - public class ControllerConfigurator -{ - private readonly uint[] _playerColors = - [ - 0xFFFF0000, // Player 1 - Red - 0xFF0000FF, // Player 2 - Blue - 0xFF00FF00, // Player 3 - Green - 0xFFFFFF00, // Player 4 - Yellow - 0xFFFF00FF, // Player 5 - Magenta - 0xFFFFA500, // Player 6 - Orange - 0xFF00FFFF, // Player 7 - Cyan - 0xFF800080 // Player 8 - Purple - ]; - - private const int MaxControllers = 9; - - public List ReorderControllers(List newConfig, List oldConfig) + public class ControllerAssignmentManager { - List reorderedConfig = oldConfig.Select(config => new GamepadInputConfig(config).GetConfig()).ToList(); + private readonly uint[] _playerColors = + [ + 0xFFFF0000, // Player 1 - Red + 0xFF0000FF, // Player 2 - Blue + 0xFF00FF00, // Player 3 - Green + 0xFFFFFF00, // Player 4 - Yellow + 0xFFFF00FF, // Player 5 - Magenta + 0xFFFFA500, // Player 6 - Orange + 0xFF00FFFF, // Player 7 - Cyan + 0xFF800080 // Player 8 - Purple + ]; - foreach (var config in newConfig) + private const int MaxControllers = 9; + + public static List ReorderControllers(List newConfig, List oldConfig) { - InputConfig substitute = reorderedConfig.FirstOrDefault(x => x.Id == config.Id); - InputConfig toBeReplaced = reorderedConfig.FirstOrDefault(x => x.PlayerIndex == config.PlayerIndex); + List reorderedConfig = oldConfig.Select(config => new GamepadInputConfig(config).GetConfig()).ToList(); - if (substitute == null || toBeReplaced == null || substitute.PlayerIndex == toBeReplaced.PlayerIndex) continue; + foreach (var config in newConfig) + { + InputConfig substitute = reorderedConfig.FirstOrDefault(x => x.Id == config.Id); + InputConfig toBeReplaced = reorderedConfig.FirstOrDefault(x => x.PlayerIndex == config.PlayerIndex); - (substitute.PlayerIndex, toBeReplaced.PlayerIndex) = (toBeReplaced.PlayerIndex, substitute.PlayerIndex); + if (substitute == null || toBeReplaced == null || substitute.PlayerIndex == toBeReplaced.PlayerIndex) continue; + + (substitute.PlayerIndex, toBeReplaced.PlayerIndex) = (toBeReplaced.PlayerIndex, substitute.PlayerIndex); + } + + return reorderedConfig; + } + + public List GetConfiguredControllers( + List controllers, + List oldConfig, + out bool hasNewControllersConnected) + { + Dictionary oldConfigMap = oldConfig + .Where(c => c?.Id != null) + .ToDictionary(x => x.Id); + + Dictionary playerIndexMap = new(); + HashSet usedIndices = []; + int recognizedControllersCount = 0; + + List remainingControllers = controllers.Where(c => c?.Id != null).ToList(); + + // Add controllers with existing configurations + AddExistingControllers(remainingControllers, oldConfigMap, playerIndexMap, usedIndices, ref recognizedControllersCount); + + // Add new controllers + AddNewControllers(remainingControllers, playerIndexMap, usedIndices); + + List orderedConfigs = playerIndexMap + .OrderBy(x => x.Key) + .Select(x => x.Value) + .ToList(); + + // Update player indices and LED colors + UpdatePlayerIndicesAndLEDs(orderedConfigs); + + hasNewControllersConnected = controllers.Count > recognizedControllersCount; + return orderedConfigs; } - return reorderedConfig; - } - - public List GetConfiguredControllers( - List controllers, - List oldConfig, - HashSet usedIndices, - out bool hasNewControllersConnected) - { - Dictionary oldConfigMap = oldConfig - .Where(c => c?.Id != null) - .ToDictionary(x => x.Id); - - Dictionary playerIndexMap = new(); - int recognizedControllersCount = 0; - - List remainingControllers = controllers.Where(c => c?.Id != null).ToList(); - - // Add controllers with existing configurations - AddExistingControllers(remainingControllers, oldConfigMap, playerIndexMap, usedIndices, ref recognizedControllersCount); - - // Add new controllers - AddNewControllers(remainingControllers, playerIndexMap, usedIndices); - - List orderedConfigs = playerIndexMap - .OrderBy(x => x.Key) - .Select(x => x.Value) - .ToList(); - - // Update player indices and LED colors - UpdatePlayerIndicesAndLEDs(orderedConfigs); - - hasNewControllersConnected = controllers.Count > recognizedControllersCount; - return orderedConfigs; - } - - private void AddExistingControllers( - List controllers, - Dictionary oldConfigMap, - Dictionary playerIndexMap, - HashSet usedIndices, - ref int recognizedControllersCount) - { - foreach (var controller in controllers.ToList()) + private static void AddExistingControllers( + List controllers, + Dictionary oldConfigMap, + Dictionary playerIndexMap, + HashSet usedIndices, + ref int recognizedControllersCount) { - if (oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig)) + foreach (var controller in controllers.ToList()) { + if (!oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig)) + { + continue; + } + int desiredIndex = (int)existingConfig.PlayerIndex; // Ensure the index is valid and available @@ -94,7 +97,7 @@ namespace Ryujinx.Ava.Input { desiredIndex = GetFirstAvailableIndex(usedIndices); } - + if(desiredIndex == -1) continue; InputConfig config = new GamepadInputConfig(existingConfig).GetConfig(); @@ -106,159 +109,158 @@ namespace Ryujinx.Ava.Input controllers.Remove(controller); } } - } - private void AddNewControllers( - List controllers, - Dictionary playerIndexMap, - HashSet usedIndices) - { - foreach (var controller in controllers) + private static void AddNewControllers( + List controllers, + Dictionary playerIndexMap, + HashSet usedIndices) { - InputConfig config = CreateConfigFromController(controller); - int freeIndex = GetFirstAvailableIndex(usedIndices); - config.PlayerIndex = (PlayerIndex)freeIndex; - usedIndices.Add(freeIndex); - playerIndexMap[freeIndex] = config; - } - } - - private int GetFirstAvailableIndex(HashSet usedIndices) - { - for (int i = 0; i < MaxControllers; i++) - { - if (!usedIndices.Contains(i)) return i; - } - return -1; // Should not happen unless MaxControllers is exceeded - } - - private void UpdatePlayerIndicesAndLEDs(List orderedConfigs) - { - for (int index = 0; index < orderedConfigs.Count; index++) - { - orderedConfigs[index].PlayerIndex = (PlayerIndex)index; - - if (orderedConfigs[index] is not StandardControllerInputConfig standardConfig || - standardConfig.Led.UseRainbow) + foreach (var controller in controllers) { - continue; + InputConfig config = CreateConfigFromController(controller); + int freeIndex = GetFirstAvailableIndex(usedIndices); + config.PlayerIndex = (PlayerIndex)freeIndex; + usedIndices.Add(freeIndex); + playerIndexMap[freeIndex] = config; } - - Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}"); - standardConfig.Led = new LedConfigController - { - EnableLed = true, - LedColor = _playerColors[index] - }; } - } - private static InputConfig CreateConfigFromController(IGamepad controller) + private static int GetFirstAvailableIndex(HashSet usedIndices) { - 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)")) + for (int i = 0; i < MaxControllers; i++) { - if (controller.Name.Contains("(L)")) + if (!usedIndices.Contains(i)) return i; + } + return -1; // Should not happen unless MaxControllers is exceeded + } + + private void UpdatePlayerIndicesAndLEDs(List orderedConfigs) + { + for (int index = 0; index < orderedConfigs.Count; index++) + { + orderedConfigs[index].PlayerIndex = (PlayerIndex)index; + + if (orderedConfigs[index] is not StandardControllerInputConfig standardConfig || + standardConfig.Led.UseRainbow) { - controllerType = ControllerType.JoyconLeft; + continue; } - else if (controller.Name.Contains("(R)")) + + Logger.Warning?.Print(LogClass.Application, $"Setting color for Player{index + 1}"); + standardConfig.Led = new LedConfigController { - controllerType = ControllerType.JoyconRight; + EnableLed = true, + LedColor = _playerColors[index] + }; + } + } + + private static 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 joy-con 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) ? GamepadInputId.Y : GamepadInputId.DpadUp, + DpadDown = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.A : GamepadInputId.DpadDown, + DpadLeft = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.B : GamepadInputId.DpadLeft, + DpadRight = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.X : GamepadInputId.DpadRight, + ButtonMinus = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.Plus : GamepadInputId.Minus, + ButtonL = GamepadInputId.LeftShoulder, + ButtonZl = GamepadInputId.LeftTrigger, + ButtonSl = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.LeftShoulder : GamepadInputId.Unbound, + ButtonSr = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.RightShoulder : GamepadInputId.Unbound, + }, + LeftJoyconStick = new JoyconConfigControllerStick + { + Joystick = StickInputId.Left, + StickButton = GamepadInputId.LeftStick, + InvertStickX = false, + InvertStickY = false, + Rotate90CW = (controllerType == ControllerType.JoyconLeft), + }, + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = GamepadInputId.B, + ButtonB = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.Y : GamepadInputId.A, + ButtonX = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.A : GamepadInputId.Y, + ButtonY = GamepadInputId.X, + ButtonPlus = GamepadInputId.Plus, + ButtonR = GamepadInputId.RightShoulder, + ButtonZr = GamepadInputId.RightTrigger, + ButtonSl = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.LeftShoulder : GamepadInputId.Unbound, + ButtonSr = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.RightShoulder : GamepadInputId.Unbound, + }, + RightJoyconStick = new JoyconConfigControllerStick + { + Joystick = (controllerType == ControllerType.JoyconRight) ? StickInputId.Left : StickInputId.Right, + StickButton = GamepadInputId.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 = true, + TurnOffLed = false, + UseRainbow = false, + LedColor = 0, + }, + }; + + return config; } - else - { - // if it's not a nintendo controller, we assume it's a pro controller or a joy-con 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) ? GamepadInputId.Y : GamepadInputId.DpadUp, - DpadDown = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.A : GamepadInputId.DpadDown, - DpadLeft = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.B : GamepadInputId.DpadLeft, - DpadRight = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.X : GamepadInputId.DpadRight, - ButtonMinus = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.Plus : GamepadInputId.Minus, - ButtonL = GamepadInputId.LeftShoulder, - ButtonZl = GamepadInputId.LeftTrigger, - ButtonSl = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.LeftShoulder : GamepadInputId.Unbound, - ButtonSr = (controllerType == ControllerType.JoyconLeft) ? GamepadInputId.RightShoulder : GamepadInputId.Unbound, - }, - LeftJoyconStick = new JoyconConfigControllerStick - { - Joystick = StickInputId.Left, - StickButton = GamepadInputId.LeftStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = (controllerType == ControllerType.JoyconLeft), - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = GamepadInputId.B, - ButtonB = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.Y : GamepadInputId.A, - ButtonX = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.A : GamepadInputId.Y, - ButtonY = GamepadInputId.X, - ButtonPlus = GamepadInputId.Plus, - ButtonR = GamepadInputId.RightShoulder, - ButtonZr = GamepadInputId.RightTrigger, - ButtonSl = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.LeftShoulder : GamepadInputId.Unbound, - ButtonSr = (controllerType == ControllerType.JoyconRight) ? GamepadInputId.RightShoulder : GamepadInputId.Unbound, - }, - RightJoyconStick = new JoyconConfigControllerStick - { - Joystick = (controllerType == ControllerType.JoyconRight) ? StickInputId.Left : StickInputId.Right, - StickButton = GamepadInputId.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 = true, - TurnOffLed = false, - UseRainbow = false, - LedColor = 0, - }, - }; - - return config; - } -} + } } -- 2.47.2 From c3bfa67393915fd1af3be1b336ee8fb30a77daeb Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sat, 22 Feb 2025 10:28:14 +0100 Subject: [PATCH 24/28] updated locales.json AutoAssignTooltip description --- src/Ryujinx/Assets/locales.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index aafa7200d..da027be3c 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -16153,11 +16153,11 @@ "ar_SA": "", "de_DE": "", "el_GR": "", - "en_US": "Automatic controllers assignment support. Automatically assigns connected controllers to each player.\n\nThis feature may override custom player-to-controller assignments.\n\nLeave OFF if you prefer to manually assign controllers.", + "en_US": "Automatic controllers assignment support.\n\nAutomatically assigns connected controllers to each player.\n\nManual configuration remains available, even when this option is activated.\n\nLeave OFF if you prefer to manually assign controllers.", "es_ES": "", "fr_FR": "", "he_IL": "", - "it_IT": "Supporto per l'assegnazione automatica dei controller. Assegna automaticamente i controller connessi a ciascun giocatore.\\n\\nQuesta funzionalità potrebbe sovrascrivere le configurazioni personalizzate controller-giocatore.\\n\\nLascia disattivato se preferisci assegnare i controller manualmente.", + "it_IT": "Supporto per l'assegnazione automatica dei controller.\n\nAssegna automaticamente i controller connessi a ciascun giocatore.\n\nÈ possibile configurare manualmente i controller anche quando questa opzione è attivata.\n\nLascia disattivato se preferisci assegnare i controller manualmente.", "ja_JP": "", "ko_KR": "", "no_NO": "", -- 2.47.2 From 538a9c83bd3436de0be0638dd66e692dc1883419 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sat, 22 Feb 2025 17:04:42 +0100 Subject: [PATCH 25/28] class to static; added a controller check on GetConfiguredControllers --- src/Ryujinx/Input/AutoAssignController.cs | 4 +--- src/Ryujinx/Input/ControllerAssignmentManager.cs | 14 ++++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index 360d0af64..49fceed71 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -15,7 +15,6 @@ namespace Ryujinx.Ava.Input private readonly InputManager _inputManager; private readonly MainWindowViewModel _viewModel; private readonly ConfigurationState _configurationState; - private readonly ControllerAssignmentManager _assignmentManager; public event Action ConfigurationUpdated; @@ -24,7 +23,6 @@ namespace Ryujinx.Ava.Input _inputManager = inputManager; _viewModel = mainWindowViewModel; _configurationState = ConfigurationState.Instance; - _assignmentManager = new ControllerAssignmentManager(); _inputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _inputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; @@ -51,7 +49,7 @@ namespace Ryujinx.Ava.Input List controllers = _inputManager.GamepadDriver.GetGamepads().ToList(); List oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList(); - List newConfig = _assignmentManager.GetConfiguredControllers( + List newConfig = ControllerAssignmentManager.GetConfiguredControllers( controllers, oldConfig, out bool hasNewControllersConnected); _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); diff --git a/src/Ryujinx/Input/ControllerAssignmentManager.cs b/src/Ryujinx/Input/ControllerAssignmentManager.cs index 8b24cb073..9e9612d12 100644 --- a/src/Ryujinx/Input/ControllerAssignmentManager.cs +++ b/src/Ryujinx/Input/ControllerAssignmentManager.cs @@ -10,9 +10,9 @@ using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; namespace Ryujinx.Ava.Input { - public class ControllerAssignmentManager + public static class ControllerAssignmentManager { - private readonly uint[] _playerColors = + private static readonly uint[] _playerColors = [ 0xFFFF0000, // Player 1 - Red 0xFF0000FF, // Player 2 - Blue @@ -43,11 +43,17 @@ namespace Ryujinx.Ava.Input return reorderedConfig; } - public List GetConfiguredControllers( + public static List GetConfiguredControllers( List controllers, List oldConfig, out bool hasNewControllersConnected) { + if(controllers == null || controllers.Count == 0) + { + hasNewControllersConnected = false; + return []; + } + Dictionary oldConfigMap = oldConfig .Where(c => c?.Id != null) .ToDictionary(x => x.Id); @@ -134,7 +140,7 @@ namespace Ryujinx.Ava.Input return -1; // Should not happen unless MaxControllers is exceeded } - private void UpdatePlayerIndicesAndLEDs(List orderedConfigs) + private static void UpdatePlayerIndicesAndLEDs(List orderedConfigs) { for (int index = 0; index < orderedConfigs.Count; index++) { -- 2.47.2 From c6e82046dd0331bedfbb75fffa5f915cf1864b95 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sat, 22 Feb 2025 18:37:01 +0100 Subject: [PATCH 26/28] minor fixes --- src/Ryujinx/Input/AutoAssignController.cs | 2 +- src/Ryujinx/Input/ControllerAssignmentManager.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index 49fceed71..f3d59b01f 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -52,7 +52,6 @@ namespace Ryujinx.Ava.Input List newConfig = ControllerAssignmentManager.GetConfiguredControllers( controllers, oldConfig, out bool hasNewControllersConnected); - _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); if (!hasNewControllersConnected) { @@ -62,6 +61,7 @@ namespace Ryujinx.Ava.Input newConfig = ControllerAssignmentManager.ReorderControllers(newConfig, oldConfig); } + _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); _configurationState.Hid.InputConfig.Value = newConfig; // we want to save the configuration only if a *new* controller was connected diff --git a/src/Ryujinx/Input/ControllerAssignmentManager.cs b/src/Ryujinx/Input/ControllerAssignmentManager.cs index 9e9612d12..d161b2e7d 100644 --- a/src/Ryujinx/Input/ControllerAssignmentManager.cs +++ b/src/Ryujinx/Input/ControllerAssignmentManager.cs @@ -14,8 +14,8 @@ namespace Ryujinx.Ava.Input { private static readonly uint[] _playerColors = [ - 0xFFFF0000, // Player 1 - Red - 0xFF0000FF, // Player 2 - Blue + 0xFF0000FF, // Player 1 - Blue + 0xFFFF0000, // Player 2 - Red 0xFF00FF00, // Player 3 - Green 0xFFFFFF00, // Player 4 - Yellow 0xFFFF00FF, // Player 5 - Magenta @@ -28,6 +28,8 @@ namespace Ryujinx.Ava.Input public static List ReorderControllers(List newConfig, List oldConfig) { + if(newConfig == null || oldConfig == null || newConfig.Count == 0 || oldConfig.Count == 0) return []; + List reorderedConfig = oldConfig.Select(config => new GamepadInputConfig(config).GetConfig()).ToList(); foreach (var config in newConfig) -- 2.47.2 From 6ecdf690be93e1e8d672123261a167cd727c2421 Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Sat, 22 Feb 2025 18:51:00 +0100 Subject: [PATCH 27/28] removed unnecessary logging, formatting and imports. --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 4 +--- src/Ryujinx.Input/HLE/NpadManager.cs | 4 ---- src/Ryujinx/Input/AutoAssignController.cs | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index bfd81d0b8..61e1aed12 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Logging; using Ryujinx.SDL2.Common; using System; using System.Collections.Generic; @@ -96,8 +95,7 @@ namespace Ryujinx.Input.SDL2 } private void HandleJoyStickConnected(int joystickDeviceId, int 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 044b2c489..0286f023f 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -1,10 +1,6 @@ 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.Configuration.Hid.Keyboard; -using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Hid; using System; using System.Collections.Generic; diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index f3d59b01f..a5ed2e725 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -52,7 +52,6 @@ namespace Ryujinx.Ava.Input List newConfig = ControllerAssignmentManager.GetConfiguredControllers( controllers, oldConfig, out bool hasNewControllersConnected); - if (!hasNewControllersConnected) { // there is no *new* controller, we must switch the order of the controllers in -- 2.47.2 From f024c8a69519e7352e358edeb548934128f611bf Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Fri, 28 Feb 2025 17:16:55 +0100 Subject: [PATCH 28/28] fix incorrect ReloadConfiguration placement --- src/Ryujinx/Input/AutoAssignController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx/Input/AutoAssignController.cs b/src/Ryujinx/Input/AutoAssignController.cs index a5ed2e725..e6fc90a16 100644 --- a/src/Ryujinx/Input/AutoAssignController.cs +++ b/src/Ryujinx/Input/AutoAssignController.cs @@ -52,6 +52,8 @@ namespace Ryujinx.Ava.Input List newConfig = ControllerAssignmentManager.GetConfiguredControllers( controllers, oldConfig, out bool hasNewControllersConnected); + _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); + if (!hasNewControllersConnected) { // there is no *new* controller, we must switch the order of the controllers in @@ -60,7 +62,6 @@ namespace Ryujinx.Ava.Input newConfig = ControllerAssignmentManager.ReorderControllers(newConfig, oldConfig); } - _viewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, _configurationState.Hid.EnableKeyboard, _configurationState.Hid.EnableMouse); _configurationState.Hid.InputConfig.Value = newConfig; // we want to save the configuration only if a *new* controller was connected -- 2.47.2