enhance AutoAssignController to trigger configuration updates on gamepad connection changes; improve controller assignment logic and ensure proper LED color settings

This commit is contained in:
uncavo-hdmi 2025-02-09 16:07:12 +01:00
parent 5034ef18c9
commit 5b88a2dd89
2 changed files with 87 additions and 70 deletions

View File

@ -8,6 +8,7 @@ using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInpu
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -32,6 +33,8 @@ namespace Ryujinx.Ava.Input
private readonly MainWindowViewModel _viewModel; private readonly MainWindowViewModel _viewModel;
private readonly ConfigurationState _configurationState; private readonly ConfigurationState _configurationState;
public event Action ConfigurationUpdated;
public AutoAssignController(InputManager inputManager, MainWindowViewModel mainWindowViewModel) public AutoAssignController(InputManager inputManager, MainWindowViewModel mainWindowViewModel)
{ {
_inputManager = inputManager; _inputManager = inputManager;
@ -43,39 +46,6 @@ namespace Ryujinx.Ava.Input
RefreshControllers(); RefreshControllers();
} }
public void RefreshControllers()
{
if (!_configurationState.Hid.EnableAutoAssign) return;
//if (controllers.Count == 0) return;
// Get every controller config and update the configuration state
List<IGamepad> controllers = _inputManager.GamepadDriver.GetGamepads().ToList();
List<InputConfig> oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList();
(List<InputConfig> 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) private void HandleOnGamepadConnected(string id)
{ {
Logger.Warning?.Print(LogClass.Application, $"Gamepad connected: {id}"); Logger.Warning?.Print(LogClass.Application, $"Gamepad connected: {id}");
@ -88,50 +58,85 @@ namespace Ryujinx.Ava.Input
RefreshControllers(); RefreshControllers();
} }
public void RefreshControllers()
{
if (!_configurationState.Hid.EnableAutoAssign) return;
// Get every controller config and update the configuration state
List<IGamepad> controllers = _inputManager.GamepadDriver.GetGamepads().ToList();
List<InputConfig> oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList();
(List<InputConfig> 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)
{
_configurationState.Hid.InputConfig.Value = newConfig;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
}
ConfigurationUpdated?.Invoke();
}
private (List<InputConfig>, bool) GetOrderedConfig(List<IGamepad> controllers, List<InputConfig> oldConfig) private (List<InputConfig>, bool) GetOrderedConfig(List<IGamepad> controllers, List<InputConfig> oldConfig)
{ {
Dictionary<string, InputConfig> oldConfigMap = oldConfig.Where(c => c?.Id != null).ToDictionary(x => x.Id);
Dictionary<int, InputConfig> playerIndexMap = new(); Dictionary<int, InputConfig> playerIndexMap = new();
int existingControllers = 0; HashSet<int> usedIndices = new();
int recognizedControllersCount = 0;
// Convert oldConfig into a dictionary for quick lookup by controller Id // Assign controllers that have an old config
Dictionary<string, InputConfig> oldConfigMap = oldConfig.ToDictionary(x => x.Id, x => x); List<IGamepad> remainingControllers = controllers.Where(c => c?.Id != null).ToList();
foreach (var controller in remainingControllers.ToList())
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)) if (oldConfigMap.TryGetValue(controller.Id, out InputConfig existingConfig))
{ {
// Use the existing PlayerIndex from oldConfig and add it to the map int desiredIndex = (int)existingConfig.PlayerIndex;
playerIndexMap[(int)existingConfig.PlayerIndex] = existingConfig; // Check if the desired index is valid and available
if (desiredIndex < 0 || desiredIndex >= MaxControllers || usedIndices.Contains(desiredIndex))
existingControllers++;
}
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 // Find the first available index
{ desiredIndex = Enumerable.Range(0, MaxControllers).First(i => !usedIndices.Contains(i));
// 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;
}
} }
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 // Assign remaining (new) controllers
return (playerIndexMap.OrderBy(x => x.Key).Select(x => x.Value).ToList(), controllers.Count > existingControllers); 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<InputConfig> 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; if (controller == null) return null;
@ -158,7 +163,7 @@ namespace Ryujinx.Ava.Input
} }
else 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; controllerType = ControllerType.ProController;
} }

View File

@ -245,6 +245,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
_mainWindow.AutoAssignController.ConfigurationUpdated += OnConfigurationUpdated;
_mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates(); _mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates();
_isLoaded = false; _isLoaded = false;
@ -358,16 +360,25 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private void HandleOnGamepadDisconnected(string id) private void HandleOnGamepadDisconnected(string id)
{ {
if(ConfigurationState.Instance.Hid.EnableAutoAssign) return;
Dispatcher.UIThread.Post(LoadDevices); Dispatcher.UIThread.Post(LoadDevices);
} }
private void HandleOnGamepadConnected(string id) 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(); LoadDevices();
LoadDevice(); _isLoaded = false;
LoadConfiguration(); LoadConfiguration();
LoadDevice();
_isLoaded = true;
NotifyChanges();
}); });
} }
@ -888,6 +899,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
_mainWindow.AutoAssignController.ConfigurationUpdated -= OnConfigurationUpdated;
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates(); _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();