Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Auto-Assign Controllers #706

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
186ed4f
feat: add option for automatic controller assignment in settings
uncavo-hdmi Jan 26, 2025
2fe157e
Merge remote-tracking branch 'upstream/master' into auto-assign-contr…
uncavo-hdmi Jan 26, 2025
24cef89
refactor: clean up logging and improve IgnoreApplet logic in settings
uncavo-hdmi Jan 26, 2025
97be01d
refactor: streamline configuration creation for controllers in NpadMa…
uncavo-hdmi Jan 28, 2025
ab4bb0a
refactor: remove auto-assign option from NpadManager initialization a…
uncavo-hdmi Feb 1, 2025
3ff9d1e
refactor: enhance AutoAssignController to utilize ViewModel and impro…
uncavo-hdmi Feb 2, 2025
9b7dc6f
simplify RefreshControllers logic and introduce GetOrderedConfig for …
uncavo-hdmi Feb 2, 2025
9cccaac
minor fix to RefreshControllers logic
uncavo-hdmi Feb 2, 2025
5513de9
code clean up; fix not loading EnableAutoAssign.Value from config fil…
uncavo-hdmi Feb 4, 2025
7999a97
update GetOrderedConfig to return if new controllers connected and co…
uncavo-hdmi Feb 4, 2025
6c8a60d
enhance AutoAssignController to set player colors and enable LED func…
uncavo-hdmi Feb 4, 2025
f07efb7
Relocate AutoAssignController.cs to a more appropriate directory
uncavo-hdmi Feb 7, 2025
287d68c
namespace correction
uncavo-hdmi Feb 7, 2025
5034ef1
minor fix: swapped LoadConfiguration() and LoadDevice(). The previous…
uncavo-hdmi Feb 7, 2025
5b88a2d
enhance AutoAssignController to trigger configuration updates on game…
uncavo-hdmi Feb 9, 2025
62dfbb5
edited NpadManager to better support auto-assign; updated initializat…
uncavo-hdmi Feb 9, 2025
8cc74da
Improved assignment logic in AutoAssignController.cs
uncavo-hdmi Feb 12, 2025
e238ea8
fixed save config logic
uncavo-hdmi Feb 17, 2025
b8cb70e
possible refactor. to be tested
uncavo-hdmi Feb 17, 2025
fd9bce0
fixed led live update while in settings
uncavo-hdmi Feb 18, 2025
38ecb3d
added function to handle gamepad led while in settings; added rainbow…
uncavo-hdmi Feb 18, 2025
cc90528
fixed some problems with rainbow led. also, rainbow won't be visible …
uncavo-hdmi Feb 18, 2025
55f3a4b
simplified logic
uncavo-hdmi Feb 18, 2025
da268eb
merge with upstream
uncavo-hdmi Feb 19, 2025
72c3ca7
changed var name; fixed class name; removed hashset argument from Get…
uncavo-hdmi Feb 22, 2025
c3bfa67
updated locales.json AutoAssignTooltip description
uncavo-hdmi Feb 22, 2025
538a9c8
class to static; added a controller check on GetConfiguredControllers
uncavo-hdmi Feb 22, 2025
c6e8204
minor fixes
uncavo-hdmi Feb 22, 2025
6ecdf69
removed unnecessary logging, formatting and imports.
uncavo-hdmi Feb 22, 2025
f024c8a
fix incorrect ReloadConfiguration placement
uncavo-hdmi Feb 28, 2025
aa9a37b
merge with upstream
uncavo-hdmi Mar 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private void HandleJoyStickDisconnected(int joystickInstanceId)
}

private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId)
{
{
if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE)
{
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId))
Expand Down
11 changes: 9 additions & 2 deletions src/Ryujinx.Input/HLE/NpadManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using PlayerIndex = Ryujinx.HLE.HOS.Services.Hid.PlayerIndex;
using Switch = Ryujinx.HLE.Switch;


namespace Ryujinx.Input.HLE
{
public class NpadManager : IDisposable
Expand All @@ -38,6 +39,8 @@ public class NpadManager : IDisposable
private bool _enableMouse;
private Switch _device;

public bool AutoAssignEnabled { get; set; } = false;

public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver)
{
_controllers = new NpadController[MaxControllers];
Expand Down Expand Up @@ -84,12 +87,14 @@ private void HandleOnGamepadDisconnected(string obj)
}
}

if (AutoAssignEnabled) return;
ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse);
}
}

private void HandleOnGamepadConnected(string id)
{
if (AutoAssignEnabled) return;
// Force input reload
ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse);
}
Expand Down Expand Up @@ -171,7 +176,7 @@ public void ReloadConfiguration(List<InputConfig> inputConfig, bool enableKeyboa
_device.Hid.RefreshInputConfig(validInputs);
}
}

public void UnblockInputUpdates()
{
lock (_lock)
Expand Down Expand Up @@ -202,11 +207,13 @@ public void BlockInputUpdates()
}
}

public void Initialize(Switch device, List<InputConfig> inputConfig, bool enableKeyboard, bool enableMouse)
public void Initialize(Switch device, List<InputConfig> inputConfig, bool enableKeyboard, bool enableMouse, bool enableAutoAssign)
{
_device = device;
_device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE;

AutoAssignEnabled = enableAutoAssign;

ReloadConfiguration(inputConfig, enableKeyboard, enableMouse);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Ryujinx/AppHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ public void Start()

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;
Expand Down
52 changes: 51 additions & 1 deletion src/Ryujinx/Assets/locales.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -16222,6 +16247,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.\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.\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": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": "。"
}
},
{
"ID": "RegionTooltip",
"Translations": {
Expand Down Expand Up @@ -24298,4 +24348,4 @@
}
}
]
}
}
4 changes: 3 additions & 1 deletion src/Ryujinx/Headless/HeadlessRyujinx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public partial class HeadlessRyujinx
private static List<InputConfig> _inputConfiguration = [];
private static bool _enableKeyboard;
private static bool _enableMouse;
private static bool _enableAutoAssign;

private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());

Expand Down Expand Up @@ -230,6 +231,7 @@ static void Load(string[] originalArgs, Options option)
_inputConfiguration ??= [];
_enableKeyboard = option.EnableKeyboard;
_enableMouse = option.EnableMouse;
_enableAutoAssign = option.EnableAutoAssign;

LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1);
LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2);
Expand Down Expand Up @@ -371,7 +373,7 @@ private static void ExecutionEntrypoint()

DisplaySleep.Prevent();

_window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse);
_window.Initialize(_emulationContext, _inputConfiguration, _enableKeyboard, _enableMouse, _enableAutoAssign);

_window.Execute();

Expand Down
6 changes: 6 additions & 0 deletions src/Ryujinx/Headless/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public void InheritMainConfig(string[] originalArgs, ConfigurationState configur

if (NeedsOverride(nameof(EnableMouse)))
EnableMouse = configurationState.Hid.EnableMouse;

if (NeedsOverride(nameof(EnableAutoAssign)))
EnableAutoAssign = configurationState.Hid.EnableAutoAssign;

if (NeedsOverride(nameof(HideCursorMode)))
HideCursorMode = configurationState.HideCursor;
Expand Down Expand Up @@ -272,6 +275,9 @@ private static string OptionName(string propertyName) =>

[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; }
Expand Down
4 changes: 2 additions & 2 deletions src/Ryujinx/Headless/Windows/WindowBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public WindowBase(
SDL2Driver.Instance.Initialize();
}

public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse)
public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enableKeyboard, bool enableMouse, bool enableAutoAssign)
{
Device = device;

Expand All @@ -132,7 +132,7 @@ public void Initialize(Switch device, List<InputConfig> inputConfigs, bool enabl

Renderer = renderer;

NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse);
NpadManager.Initialize(device, inputConfigs, enableKeyboard, enableMouse, enableAutoAssign);
TouchScreenManager.Initialize(device);
}

Expand Down
75 changes: 75 additions & 0 deletions src/Ryujinx/Input/AutoAssignController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Ryujinx.Ava.Input
{
public class AutoAssignController
{
private readonly InputManager _inputManager;
private readonly MainWindowViewModel _viewModel;
private readonly ConfigurationState _configurationState;

public event Action ConfigurationUpdated;

public AutoAssignController(InputManager inputManager, MainWindowViewModel mainWindowViewModel)
{
_inputManager = inputManager;
_viewModel = mainWindowViewModel;
_configurationState = ConfigurationState.Instance;

_inputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_inputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;

RefreshControllers();
}

private void HandleOnGamepadConnected(string id)
{
Logger.Warning?.Print(LogClass.Application, $"Gamepad connected: {id}");
RefreshControllers();
}

private void HandleOnGamepadDisconnected(string id)
{
Logger.Warning?.Print(LogClass.Application, $"Gamepad disconnected: {id}");
RefreshControllers();
}

public void RefreshControllers()
{
if (!_configurationState.Hid.EnableAutoAssign) return;

List<IGamepad> controllers = _inputManager.GamepadDriver.GetGamepads().ToList();
List<InputConfig> oldConfig = _configurationState.Hid.InputConfig.Value.Where(x => x != null).ToList();

List<InputConfig> 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
// oldConfig to match the new order since probably a controller was disconnected
// or an old controller was reconnected
newConfig = ControllerAssignmentManager.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();
}
}
}
Loading