Input cycling hotkeys

This commit is contained in:
Barış Hamil 2025-06-20 18:06:05 +03:00
parent 75d6cc4946
commit e4a39ffa81
9 changed files with 520 additions and 139 deletions

View file

@ -0,0 +1,200 @@
using System;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
namespace Ryujinx.Common.Configuration.Hid
{
/// <summary>
/// Provides default input configurations for keyboard and controller devices
/// </summary>
public static class DefaultInputConfigurationProvider
{
/// <summary>
/// Creates a default keyboard input configuration
/// </summary>
/// <param name="id">Device ID</param>
/// <param name="name">Device name</param>
/// <param name="playerIndex">Player index</param>
/// <param name="controllerType">Controller type (defaults to ProController)</param>
/// <returns>Default keyboard input configuration</returns>
public static StandardKeyboardInputConfig CreateDefaultKeyboardConfig(string id, string name, PlayerIndex playerIndex, ControllerType controllerType = ControllerType.ProController)
{
return new StandardKeyboardInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.WindowKeyboard,
Id = id,
Name = name,
ControllerType = ControllerType.ProController,
PlayerIndex = playerIndex,
LeftJoycon = new LeftJoyconCommonConfig<Key>
{
DpadUp = Key.Up,
DpadDown = Key.Down,
DpadLeft = Key.Left,
DpadRight = Key.Right,
ButtonMinus = Key.Minus,
ButtonL = Key.E,
ButtonZl = Key.Q,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
},
LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = Key.W,
StickDown = Key.S,
StickLeft = Key.A,
StickRight = Key.D,
StickButton = Key.F,
},
RightJoycon = new RightJoyconCommonConfig<Key>
{
ButtonA = Key.Z,
ButtonB = Key.X,
ButtonX = Key.C,
ButtonY = Key.V,
ButtonPlus = Key.Plus,
ButtonR = Key.U,
ButtonZr = Key.O,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
},
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = Key.I,
StickDown = Key.K,
StickLeft = Key.J,
StickRight = Key.L,
StickButton = Key.H,
},
};
}
/// <summary>
/// Creates a default controller input configuration
/// </summary>
/// <param name="id">Device ID</param>
/// <param name="name">Device name</param>
/// <param name="playerIndex">Player index</param>
/// <param name="isNintendoStyle">Whether to use Nintendo-style button mapping</param>
/// <returns>Default controller input configuration</returns>
public static StandardControllerInputConfig CreateDefaultControllerConfig(string id, string name, PlayerIndex playerIndex, bool isNintendoStyle = false)
{
// Split the ID for controller configs
string cleanId = id.Split(" ")[0];
return new StandardControllerInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Id = cleanId,
Name = name,
ControllerType = ControllerType.ProController,
PlayerIndex = playerIndex,
DeadzoneLeft = 0.1f,
DeadzoneRight = 0.1f,
RangeLeft = 1.0f,
RangeRight = 1.0f,
TriggerThreshold = 0.5f,
LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
{
DpadUp = ConfigGamepadInputId.DpadUp,
DpadDown = ConfigGamepadInputId.DpadDown,
DpadLeft = ConfigGamepadInputId.DpadLeft,
DpadRight = ConfigGamepadInputId.DpadRight,
ButtonMinus = ConfigGamepadInputId.Minus,
ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
},
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Left,
StickButton = ConfigGamepadInputId.LeftStick,
InvertStickX = false,
InvertStickY = false,
},
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
{
ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B,
ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A,
ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y,
ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X,
ButtonPlus = ConfigGamepadInputId.Plus,
ButtonR = ConfigGamepadInputId.RightShoulder,
ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
},
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Right,
StickButton = ConfigGamepadInputId.RightStick,
InvertStickX = false,
InvertStickY = false,
},
Motion = new StandardMotionConfigController
{
EnableMotion = true,
MotionBackend = MotionInputBackendType.GamepadDriver,
GyroDeadzone = 1,
Sensitivity = 100,
},
Rumble = new RumbleConfigController
{
EnableRumble = false,
WeakRumble = 1f,
StrongRumble = 1f,
},
Led = new LedConfigController
{
EnableLed = false,
TurnOffLed = false,
UseRainbow = false,
LedColor = 0xFFFFFFFF,
}
};
}
/// <summary>
/// Gets the short name of a gamepad by removing SDL prefix and truncating if too long
/// </summary>
/// <param name="name">Full gamepad name</param>
/// <param name="maxLength">Maximum length before truncation (default: 50)</param>
/// <returns>Short gamepad name</returns>
public static string GetShortGamepadName(string name, int maxLength = 50)
{
const string SdlGamepadNamePrefix = "SDL2 Gamepad ";
const string Ellipsis = "...";
// First remove SDL prefix if present
string shortName = name;
if (name.StartsWith(SdlGamepadNamePrefix))
{
shortName = name[SdlGamepadNamePrefix.Length..];
}
// Then truncate if too long
if (shortName.Length > maxLength)
{
return $"{shortName.AsSpan(0, maxLength - Ellipsis.Length)}{Ellipsis}";
}
return shortName;
}
/// <summary>
/// Determines if a controller uses Nintendo-style button mapping
/// </summary>
/// <param name="name">Controller name</param>
/// <returns>True if Nintendo-style mapping should be used</returns>
public static bool IsNintendoStyleController(string name)
{
return name.Contains("Nintendo");
}
}
}

View file

@ -15,5 +15,9 @@ namespace Ryujinx.Common.Configuration.Hid
public Key CustomVSyncIntervalDecrement { get; set; }
public Key TurboMode { get; set; }
public bool TurboModeWhileHeld { get; set; }
public Key CycleInputDevicePlayer1 { get; set; }
public Key CycleInputDevicePlayer2 { get; set; }
public Key CycleInputDevicePlayer3 { get; set; }
public Key CycleInputDevicePlayer4 { get; set; }
}
}

View file

@ -15,5 +15,9 @@ namespace Ryujinx.Ava.Common
CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement,
TurboMode,
CycleInputDevicePlayer1,
CycleInputDevicePlayer2,
CycleInputDevicePlayer3,
CycleInputDevicePlayer4,
}
}

View file

@ -40,6 +40,11 @@ using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using System.Linq;
using SkiaSharp;
using SPB.Graphics.Vulkan;
using System;
@ -1333,6 +1338,18 @@ namespace Ryujinx.Ava.Systems
_viewModel.Volume = Device.GetVolume();
break;
case KeyboardHotkeyState.CycleInputDevicePlayer1:
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player1);
break;
case KeyboardHotkeyState.CycleInputDevicePlayer2:
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player2);
break;
case KeyboardHotkeyState.CycleInputDevicePlayer3:
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player3);
break;
case KeyboardHotkeyState.CycleInputDevicePlayer4:
CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex.Player4);
break;
case KeyboardHotkeyState.None:
(_keyboardInterface as AvaloniaKeyboard).Clear();
break;
@ -1366,6 +1383,118 @@ namespace Ryujinx.Ava.Systems
return true;
}
private void CycleInputDevice(HLE.HOS.Services.Hid.PlayerIndex playerIndex)
{
// Get current input configuration
List<InputConfig> currentConfig = new(ConfigurationState.Instance.Hid.InputConfig.Value);
// Find current configuration for this player
InputConfig playerConfig = currentConfig.FirstOrDefault(x => x.PlayerIndex == (PlayerIndex)playerIndex);
// Get available devices from InputManager
List<(DeviceType Type, string Id, string Name)> availableDevices = [];
// Add disabled option
availableDevices.Add((DeviceType.None, "disabled", "Disabled"));
// Add keyboard devices
foreach (string id in _inputManager.KeyboardDriver.GamepadsIds)
{
using var gamepad = _inputManager.KeyboardDriver.GetGamepad(id);
if (gamepad != null)
{
availableDevices.Add((DeviceType.Keyboard, id, gamepad.Name));
}
}
// Add controller devices
int controllerNumber = 0;
foreach (string id in _inputManager.GamepadDriver.GamepadsIds)
{
using var gamepad = _inputManager.GamepadDriver.GetGamepad(id);
if (gamepad != null)
{
string name = $"{DefaultInputConfigurationProvider.GetShortGamepadName(gamepad.Name)} ({controllerNumber++})";
availableDevices.Add((DeviceType.Controller, id, name));
}
}
// Find current device index
int currentIndex = 0;
if (playerConfig != null)
{
DeviceType currentType = DeviceType.None;
if (playerConfig is StandardKeyboardInputConfig)
currentType = DeviceType.Keyboard;
else if (playerConfig is StandardControllerInputConfig)
currentType = DeviceType.Controller;
currentIndex = availableDevices.FindIndex(x => x.Type == currentType && x.Id == playerConfig.Id);
if (currentIndex == -1) currentIndex = 0;
}
// Cycle to next device
int nextIndex = (currentIndex + 1) % availableDevices.Count;
var nextDevice = availableDevices[nextIndex];
// Remove existing configuration for this player
currentConfig.RemoveAll(x => x.PlayerIndex == (PlayerIndex)playerIndex);
// Add new configuration if not disabled
if (nextDevice.Type != DeviceType.None)
{
InputConfig newConfig = CreateDefaultInputConfig(nextDevice, (PlayerIndex)playerIndex);
if (newConfig != null)
{
currentConfig.Add(newConfig);
}
}
// Apply the new configuration
ConfigurationState.Instance.Hid.InputConfig.Value = currentConfig;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
// Reload the input system
NpadManager.ReloadConfiguration(currentConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
// Show controller overlay
ShowControllerOverlay(currentConfig);
}
private InputConfig CreateDefaultInputConfig((DeviceType Type, string Id, string Name) device, PlayerIndex playerIndex)
{
if (device.Type == DeviceType.Keyboard)
{
return DefaultInputConfigurationProvider.CreateDefaultKeyboardConfig(device.Id, device.Name, playerIndex);
}
else if (device.Type == DeviceType.Controller)
{
bool isNintendoStyle = DefaultInputConfigurationProvider.IsNintendoStyleController(device.Name);
return DefaultInputConfigurationProvider.CreateDefaultControllerConfig(device.Id, device.Name, playerIndex, isNintendoStyle);
}
return null;
}
private void ShowControllerOverlay(List<InputConfig> inputConfigs)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
try
{
var overlayWindow = new UI.Windows.ControllerOverlayWindow(_topLevel as Avalonia.Controls.Window);
overlayWindow.ShowControllerBindings(inputConfigs);
overlayWindow.Show();
}
catch (Exception ex)
{
Logger.Error?.Print(LogClass.Application, $"Failed to show controller overlay: {ex.Message}");
}
});
}
private KeyboardHotkeyState GetHotkeyState()
{
KeyboardHotkeyState state = KeyboardHotkeyState.None;
@ -1418,6 +1547,22 @@ namespace Ryujinx.Ava.Systems
{
state = KeyboardHotkeyState.TurboMode;
}
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer1))
{
state = KeyboardHotkeyState.CycleInputDevicePlayer1;
}
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer2))
{
state = KeyboardHotkeyState.CycleInputDevicePlayer2;
}
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer3))
{
state = KeyboardHotkeyState.CycleInputDevicePlayer3;
}
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CycleInputDevicePlayer4))
{
state = KeyboardHotkeyState.CycleInputDevicePlayer4;
}
return state;
}

View file

@ -32,6 +32,14 @@ namespace Ryujinx.Ava.UI.Models.Input
[ObservableProperty] private bool _turboModeWhileHeld;
[ObservableProperty] private Key _cycleInputDevicePlayer1;
[ObservableProperty] private Key _cycleInputDevicePlayer2;
[ObservableProperty] private Key _cycleInputDevicePlayer3;
[ObservableProperty] private Key _cycleInputDevicePlayer4;
public HotkeyConfig(KeyboardHotkeys config)
{
if (config == null)
@ -50,6 +58,10 @@ namespace Ryujinx.Ava.UI.Models.Input
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
TurboMode = config.TurboMode;
TurboModeWhileHeld = config.TurboModeWhileHeld;
CycleInputDevicePlayer1 = config.CycleInputDevicePlayer1;
CycleInputDevicePlayer2 = config.CycleInputDevicePlayer2;
CycleInputDevicePlayer3 = config.CycleInputDevicePlayer3;
CycleInputDevicePlayer4 = config.CycleInputDevicePlayer4;
}
public KeyboardHotkeys GetConfig() =>
@ -67,7 +79,11 @@ namespace Ryujinx.Ava.UI.Models.Input
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
TurboMode = TurboMode,
TurboModeWhileHeld = TurboModeWhileHeld
TurboModeWhileHeld = TurboModeWhileHeld,
CycleInputDevicePlayer1 = CycleInputDevicePlayer1,
CycleInputDevicePlayer2 = CycleInputDevicePlayer2,
CycleInputDevicePlayer3 = CycleInputDevicePlayer3,
CycleInputDevicePlayer4 = CycleInputDevicePlayer4
};
}
}

View file

@ -526,18 +526,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
}
}
private static string GetShortGamepadName(string str)
{
const string Ellipsis = "...";
const int MaxSize = 50;
if (str.Length > MaxSize)
{
return $"{str.AsSpan(0, MaxSize - Ellipsis.Length)}{Ellipsis}";
}
return str;
}
private static string GetShortGamepadId(string str)
{
@ -551,7 +540,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{
string GetGamepadName(IGamepad gamepad, int controllerNumber)
{
return $"{GetShortGamepadName(gamepad.Name)} ({controllerNumber})";
return $"{DefaultInputConfigurationProvider.GetShortGamepadName(gamepad.Name)} ({controllerNumber})";
}
string GetUniqueGamepadName(IGamepad gamepad, ref int controllerNumber)
@ -579,7 +568,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (gamepad != null)
{
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
Devices.Add((DeviceType.Keyboard, id, $"{DefaultInputConfigurationProvider.GetShortGamepadName(gamepad.Name)}"));
}
}
@ -652,138 +641,21 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
InputConfig config;
if (activeDevice.Type == DeviceType.Keyboard)
{
string id = activeDevice.Id;
string name = activeDevice.Name;
config = new StandardKeyboardInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.WindowKeyboard,
Id = id,
Name = name,
ControllerType = ControllerType.ProController,
LeftJoycon = new LeftJoyconCommonConfig<Key>
{
DpadUp = Key.Up,
DpadDown = Key.Down,
DpadLeft = Key.Left,
DpadRight = Key.Right,
ButtonMinus = Key.Minus,
ButtonL = Key.E,
ButtonZl = Key.Q,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
},
LeftJoyconStick =
new JoyconConfigKeyboardStick<Key>
{
StickUp = Key.W,
StickDown = Key.S,
StickLeft = Key.A,
StickRight = Key.D,
StickButton = Key.F,
},
RightJoycon = new RightJoyconCommonConfig<Key>
{
ButtonA = Key.Z,
ButtonB = Key.X,
ButtonX = Key.C,
ButtonY = Key.V,
ButtonPlus = Key.Plus,
ButtonR = Key.U,
ButtonZr = Key.O,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
},
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{
StickUp = Key.I,
StickDown = Key.K,
StickLeft = Key.J,
StickRight = Key.L,
StickButton = Key.H,
},
};
config = DefaultInputConfigurationProvider.CreateDefaultKeyboardConfig(activeDevice.Id, activeDevice.Name, _playerId);
}
else if (activeDevice.Type == DeviceType.Controller)
{
bool isNintendoStyle = Devices.ToList().FirstOrDefault(x => x.Id == activeDevice.Id).Name.Contains("Nintendo");
string id = activeDevice.Id.Split(" ")[0];
string name = activeDevice.Name;
config = new StandardControllerInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Id = id,
Name = name,
ControllerType = ControllerType.ProController,
DeadzoneLeft = 0.1f,
DeadzoneRight = 0.1f,
RangeLeft = 1.0f,
RangeRight = 1.0f,
TriggerThreshold = 0.5f,
LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
{
DpadUp = ConfigGamepadInputId.DpadUp,
DpadDown = ConfigGamepadInputId.DpadDown,
DpadLeft = ConfigGamepadInputId.DpadLeft,
DpadRight = ConfigGamepadInputId.DpadRight,
ButtonMinus = ConfigGamepadInputId.Minus,
ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
},
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Left,
StickButton = ConfigGamepadInputId.LeftStick,
InvertStickX = false,
InvertStickY = false,
},
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
{
ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B,
ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A,
ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y,
ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X,
ButtonPlus = ConfigGamepadInputId.Plus,
ButtonR = ConfigGamepadInputId.RightShoulder,
ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
},
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Right,
StickButton = ConfigGamepadInputId.RightStick,
InvertStickX = false,
InvertStickY = false,
},
Motion = new StandardMotionConfigController
{
MotionBackend = MotionInputBackendType.GamepadDriver,
EnableMotion = true,
Sensitivity = 100,
GyroDeadzone = 1,
},
Rumble = new RumbleConfigController
{
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false,
},
};
bool isNintendoStyle = DefaultInputConfigurationProvider.IsNintendoStyleController(activeDevice.Name);
config = DefaultInputConfigurationProvider.CreateDefaultControllerConfig(activeDevice.Id, activeDevice.Name, _playerId, isNintendoStyle);
}
else
{
config = new InputConfig();
config = new InputConfig
{
PlayerIndex = _playerId
};
}
config.PlayerIndex = _playerId;
return config;
}

View file

@ -120,6 +120,30 @@
<TextBlock Text="{ext:Locale SettingsTabHotkeysOnlyWhilePressed}" Margin="10,0" />
<CheckBox IsChecked="{Binding KeyboardHotkey.TurboModeWhileHeld}" />
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysCycleInputDevicePlayer1}" Classes="settingHeader" />
<ToggleButton Name="CycleInputDevicePlayer1">
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer1, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysCycleInputDevicePlayer2}" Classes="settingHeader" />
<ToggleButton Name="CycleInputDevicePlayer2">
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer2, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysCycleInputDevicePlayer3}" Classes="settingHeader" />
<ToggleButton Name="CycleInputDevicePlayer3">
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer3, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
<StackPanel>
<TextBlock Text="{ext:Locale SettingsTabHotkeysCycleInputDevicePlayer4}" Classes="settingHeader" />
<ToggleButton Name="CycleInputDevicePlayer4">
<TextBlock Text="{Binding KeyboardHotkey.CycleInputDevicePlayer4, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>

View file

@ -82,7 +82,11 @@ namespace Ryujinx.Ava.UI.Views.Settings
{ "VolumeDown", () => viewModel.KeyboardHotkey.VolumeDown = Key.Unbound },
{ "CustomVSyncIntervalIncrement", () => viewModel.KeyboardHotkey.CustomVSyncIntervalIncrement = Key.Unbound },
{ "CustomVSyncIntervalDecrement", () => viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = Key.Unbound },
{ "TurboMode", () => viewModel.KeyboardHotkey.TurboMode = Key.Unbound }
{ "TurboMode", () => viewModel.KeyboardHotkey.TurboMode = Key.Unbound },
{ "CycleInputDevicePlayer1", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer1 = Key.Unbound },
{ "CycleInputDevicePlayer2", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer2 = Key.Unbound },
{ "CycleInputDevicePlayer3", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer3 = Key.Unbound },
{ "CycleInputDevicePlayer4", () => viewModel.KeyboardHotkey.CycleInputDevicePlayer4 = Key.Unbound }
};
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
@ -162,6 +166,18 @@ namespace Ryujinx.Ava.UI.Views.Settings
case "TurboMode":
ViewModel.KeyboardHotkey.TurboMode = buttonValue.AsHidType<Key>();
break;
case "CycleInputDevicePlayer1":
ViewModel.KeyboardHotkey.CycleInputDevicePlayer1 = buttonValue.AsHidType<Key>();
break;
case "CycleInputDevicePlayer2":
ViewModel.KeyboardHotkey.CycleInputDevicePlayer2 = buttonValue.AsHidType<Key>();
break;
case "CycleInputDevicePlayer3":
ViewModel.KeyboardHotkey.CycleInputDevicePlayer3 = buttonValue.AsHidType<Key>();
break;
case "CycleInputDevicePlayer4":
ViewModel.KeyboardHotkey.CycleInputDevicePlayer4 = buttonValue.AsHidType<Key>();
break;
}
});
}