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:
parent
5034ef18c9
commit
5b88a2dd89
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user