Fallback to keyboard on controller disconnect

This commit is contained in:
Babib3l
2026-03-22 01:05:17 +01:00
parent d1464eb5f2
commit 9aeb0c8c8c
2 changed files with 128 additions and 12 deletions

View File

@@ -36,6 +36,7 @@ namespace Ryujinx.Input.HLE
private bool _isDisposed; private bool _isDisposed;
private List<InputConfig> _inputConfig; private List<InputConfig> _inputConfig;
private List<InputConfig> _requestedInputConfig;
private bool _enableKeyboard; private bool _enableKeyboard;
private bool _enableMouse; private bool _enableMouse;
private Switch _device; private Switch _device;
@@ -52,6 +53,7 @@ namespace Ryujinx.Input.HLE
_gamepadDriver = gamepadDriver; _gamepadDriver = gamepadDriver;
_mouseDriver = mouseDriver; _mouseDriver = mouseDriver;
_inputConfig = []; _inputConfig = [];
_requestedInputConfig = [];
_gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; _gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
@@ -89,14 +91,14 @@ namespace Ryujinx.Input.HLE
} }
} }
ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); ReloadConfiguration(_requestedInputConfig, _enableKeyboard, _enableMouse);
} }
} }
private void HandleOnGamepadConnected(string id) private void HandleOnGamepadConnected(string id)
{ {
// Force input reload // Force input reload
ReloadConfiguration(_inputConfig, _enableKeyboard, _enableMouse); ReloadConfiguration(_requestedInputConfig, _enableKeyboard, _enableMouse);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -127,11 +129,13 @@ namespace Ryujinx.Input.HLE
{ {
lock (_lock) lock (_lock)
{ {
_requestedInputConfig = inputConfig?.ToList() ?? [];
NpadController[] oldControllers = _controllers.ToArray(); NpadController[] oldControllers = _controllers.ToArray();
List<InputConfig> validInputs = []; List<InputConfig> validInputs = [];
foreach (InputConfig inputConfigEntry in inputConfig) foreach (InputConfig inputConfigEntry in _requestedInputConfig)
{ {
NpadController controller; NpadController controller;
int index = (int)inputConfigEntry.PlayerIndex; int index = (int)inputConfigEntry.PlayerIndex;
@@ -147,7 +151,17 @@ namespace Ryujinx.Input.HLE
controller = new(_cemuHookClient); controller = new(_cemuHookClient);
} }
bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry); InputConfig activeConfig = inputConfigEntry;
bool isValid = DriverConfigurationUpdate(ref controller, activeConfig);
if (!isValid &&
enableKeyboard &&
inputConfigEntry is StandardControllerInputConfig &&
TryGetKeyboardFallback(inputConfigEntry, out StandardKeyboardInputConfig fallbackConfig))
{
activeConfig = fallbackConfig;
isValid = DriverConfigurationUpdate(ref controller, activeConfig);
}
if (!isValid) if (!isValid)
{ {
@@ -157,7 +171,7 @@ namespace Ryujinx.Input.HLE
else else
{ {
_controllers[index] = controller; _controllers[index] = controller;
validInputs.Add(inputConfigEntry); validInputs.Add(activeConfig);
} }
} }
@@ -169,7 +183,7 @@ namespace Ryujinx.Input.HLE
oldControllers[i] = null; oldControllers[i] = null;
} }
_inputConfig = inputConfig; _inputConfig = validInputs;
_enableKeyboard = enableKeyboard; _enableKeyboard = enableKeyboard;
_enableMouse = enableMouse; _enableMouse = enableMouse;
@@ -177,6 +191,79 @@ namespace Ryujinx.Input.HLE
} }
} }
private bool TryGetKeyboardFallback(InputConfig inputConfig, out StandardKeyboardInputConfig fallbackConfig)
{
fallbackConfig = null;
ReadOnlySpan<string> keyboardIds = _keyboardDriver.GamepadsIds;
if (keyboardIds.IsEmpty)
{
return false;
}
string keyboardId = keyboardIds[0];
using IGamepad keyboard = _keyboardDriver.GetGamepad(keyboardId);
if (keyboard == null)
{
return false;
}
fallbackConfig = new StandardKeyboardInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.WindowKeyboard,
Id = keyboardId,
Name = keyboard.Name,
PlayerIndex = inputConfig.PlayerIndex,
ControllerType = inputConfig.ControllerType,
LeftJoycon = new LeftJoyconCommonConfig<PhysicalKey>
{
DpadUp = PhysicalKey.Up,
DpadDown = PhysicalKey.Down,
DpadLeft = PhysicalKey.Left,
DpadRight = PhysicalKey.Right,
ButtonMinus = PhysicalKey.Minus,
ButtonL = PhysicalKey.E,
ButtonZl = PhysicalKey.Q,
ButtonSl = PhysicalKey.Unbound,
ButtonSr = PhysicalKey.Unbound,
},
LeftJoyconStick = new JoyconConfigKeyboardStick<PhysicalKey>
{
StickUp = PhysicalKey.W,
StickDown = PhysicalKey.S,
StickLeft = PhysicalKey.A,
StickRight = PhysicalKey.D,
StickButton = PhysicalKey.F,
},
RightJoycon = new RightJoyconCommonConfig<PhysicalKey>
{
ButtonA = PhysicalKey.Z,
ButtonB = PhysicalKey.X,
ButtonX = PhysicalKey.C,
ButtonY = PhysicalKey.V,
ButtonPlus = PhysicalKey.Plus,
ButtonR = PhysicalKey.U,
ButtonZr = PhysicalKey.O,
ButtonSl = PhysicalKey.Unbound,
ButtonSr = PhysicalKey.Unbound,
},
RightJoyconStick = new JoyconConfigKeyboardStick<PhysicalKey>
{
StickUp = PhysicalKey.I,
StickDown = PhysicalKey.K,
StickLeft = PhysicalKey.J,
StickRight = PhysicalKey.L,
StickButton = PhysicalKey.H,
},
};
return true;
}
public void UnblockInputUpdates() public void UnblockInputUpdates()
{ {
lock (_lock) lock (_lock)
@@ -334,7 +421,7 @@ namespace Ryujinx.Input.HLE
} }
} }
internal InputConfig GetPlayerInputConfigByIndex(int index) public InputConfig GetPlayerInputConfigByIndex(int index)
{ {
lock (_lock) lock (_lock)
{ {

View File

@@ -348,15 +348,19 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private void LoadConfiguration(InputConfig inputConfig = null) private void LoadConfiguration(InputConfig inputConfig = null)
{ {
InputConfig persistedConfig;
if (UseGlobalConfig && Program.UseExtraConfig) if (UseGlobalConfig && Program.UseExtraConfig)
{ {
Config = inputConfig ?? ConfigurationState.InstanceExtra.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId); persistedConfig = ConfigurationState.InstanceExtra.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId);
} }
else else
{ {
Config = inputConfig ?? ConfigurationState.Instance.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId); persistedConfig = ConfigurationState.Instance.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId);
} }
Config = inputConfig ?? GetDisplayedInputConfig(persistedConfig);
if (Config is StandardKeyboardInputConfig keyboardInputConfig) if (Config is StandardKeyboardInputConfig keyboardInputConfig)
{ {
ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig), VisualStick); ConfigViewModel = new KeyboardInputViewModel(this, new KeyboardInputConfig(keyboardInputConfig), VisualStick);
@@ -368,6 +372,18 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
} }
private InputConfig GetDisplayedInputConfig(InputConfig persistedConfig)
{
if (persistedConfig is not StandardControllerInputConfig)
{
return persistedConfig;
}
InputConfig activeConfig = _mainWindow?.ViewModel.AppHost?.NpadManager?.GetPlayerInputConfigByIndex((int)_playerId);
return activeConfig is StandardKeyboardInputConfig ? activeConfig : persistedConfig;
}
private void FindPairedDeviceInConfigFile() private void FindPairedDeviceInConfigFile()
{ {
// This function allows you to output a message about the device configuration found in the file // This function allows you to output a message about the device configuration found in the file
@@ -475,11 +491,24 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
_isChangeTrackingActive = false; // Disable configuration change tracking _isChangeTrackingActive = false; // Disable configuration change tracking
bool shouldApplyKeyboardFallback = Config is StandardControllerInputConfig controllerConfig && controllerConfig.Id == id;
LoadDevices(); LoadDevices();
IsModified = true; if (shouldApplyKeyboardFallback)
RevertChanges(); {
FindPairedDeviceInConfigFile(); LoadConfiguration();
LoadDevice();
NotificationIsVisible = false;
IsModified = false;
NotifyChanges();
}
else
{
IsModified = true;
RevertChanges();
FindPairedDeviceInConfigFile();
}
_isChangeTrackingActive = true; // Enable configuration change tracking _isChangeTrackingActive = true; // Enable configuration change tracking