From b8cb70ef32b0850b8b364f80a35ce0c55580d6ac Mon Sep 17 00:00:00 2001 From: uncavo-hdmi Date: Mon, 17 Feb 2025 22:17:54 +0100 Subject: [PATCH] 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; + } +} + +}