Allow the ability to turn off the LED entirely

Only works for DualSense; in my testing, my DualShock 4 ignored the requests to set the LED to off.
This commit is contained in:
Evan Husted 2025-01-23 20:29:59 -06:00
parent 6619453aed
commit 5f02765130
10 changed files with 107 additions and 17 deletions

View File

@ -2,14 +2,19 @@
{ {
public class LedConfigController public class LedConfigController
{ {
/// <summary>
/// Packed RGB int of the color
/// </summary>
public uint LedColor { get; set; }
/// <summary> /// <summary>
/// Enable LED color changing by the emulator /// Enable LED color changing by the emulator
/// </summary> /// </summary>
public bool EnableLed { get; set; } public bool EnableLed { get; set; }
/// <summary>
/// Ignores the color and disables the LED entirely.
/// </summary>
public bool TurnOffLed { get; set; }
/// <summary>
/// Packed RGB int of the color
/// </summary>
public uint LedColor { get; set; }
} }
} }

View File

@ -106,11 +106,11 @@ namespace Ryujinx.Input.SDL2
public void SetLed(uint packedRgb) public void SetLed(uint packedRgb)
{ {
if (!Features.HasFlag(GamepadFeaturesFlag.Led)) return; if (!Features.HasFlag(GamepadFeaturesFlag.Led)) return;
byte red = (byte)(packedRgb >> 16);
byte green = (byte)(packedRgb >> 8);
byte blue = (byte)(packedRgb % 256);
byte red = packedRgb > 0 ? (byte)(packedRgb >> 16) : (byte)0;
byte green = packedRgb > 0 ? (byte)(packedRgb >> 8) : (byte)0;
byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0;
if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0) if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0)
Logger.Error?.Print(LogClass.Hid, "LED is not supported on this game controller."); Logger.Error?.Print(LogClass.Hid, "LED is not supported on this game controller.");
} }
@ -232,7 +232,12 @@ namespace Ryujinx.Input.SDL2
_configuration = (StandardControllerInputConfig)configuration; _configuration = (StandardControllerInputConfig)configuration;
if (Features.HasFlag(GamepadFeaturesFlag.Led) && _configuration.Led.EnableLed) if (Features.HasFlag(GamepadFeaturesFlag.Led) && _configuration.Led.EnableLed)
SetLed(_configuration.Led.LedColor); {
if (_configuration.Led.TurnOffLed)
(this as IGamepad).ClearLed();
else
SetLed(_configuration.Led.LedColor);
}
_buttonsUserMapping.Clear(); _buttonsUserMapping.Clear();

View File

@ -72,6 +72,8 @@ namespace Ryujinx.Input
/// <param name="packedRgb">The packed RGB integer.</param> /// <param name="packedRgb">The packed RGB integer.</param>
void SetLed(uint packedRgb); void SetLed(uint packedRgb);
public void ClearLed() => SetLed(0);
/// <summary> /// <summary>
/// Starts a rumble effect on the gamepad. /// Starts a rumble effect on the gamepad.
/// </summary> /// </summary>

View File

@ -7647,6 +7647,31 @@
"zh_TW": "" "zh_TW": ""
} }
}, },
{
"ID": "ControllerSettingsLedColorDisable",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Disable",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"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": "ControllerSettingsSave", "ID": "ControllerSettingsSave",
"Translations": { "Translations": {

View File

@ -400,6 +400,18 @@ namespace Ryujinx.Ava.UI.Models.Input
} }
} }
private bool _turnOffLed;
public bool TurnOffLed
{
get => _turnOffLed;
set
{
_turnOffLed = value;
OnPropertyChanged();
}
}
private Color _ledColor; private Color _ledColor;
public Color LedColor public Color LedColor
@ -512,6 +524,7 @@ namespace Ryujinx.Ava.UI.Models.Input
if (controllerInput.Led != null) if (controllerInput.Led != null)
{ {
EnableLedChanging = controllerInput.Led.EnableLed; EnableLedChanging = controllerInput.Led.EnableLed;
TurnOffLed = controllerInput.Led.TurnOffLed;
uint rawColor = controllerInput.Led.LedColor; uint rawColor = controllerInput.Led.LedColor;
byte alpha = (byte)(rawColor >> 24); byte alpha = (byte)(rawColor >> 24);
byte red = (byte)(rawColor >> 16); byte red = (byte)(rawColor >> 16);
@ -579,6 +592,7 @@ namespace Ryujinx.Ava.UI.Models.Input
Led = new LedConfigController Led = new LedConfigController
{ {
EnableLed = EnableLedChanging, EnableLed = EnableLedChanging,
TurnOffLed = this.TurnOffLed,
LedColor = LedColor.ToUInt32() LedColor = LedColor.ToUInt32()
}, },
Version = InputConfig.CurrentVersion, Version = InputConfig.CurrentVersion,

View File

@ -1,6 +1,8 @@
using Avalonia.Svg.Skia; using Avalonia.Svg.Skia;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Models.Input;
using Ryujinx.Ava.UI.Views.Input; using Ryujinx.Ava.UI.Views.Input;
@ -58,6 +60,16 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
await RumbleInputView.Show(this); await RumbleInputView.Show(this);
} }
public RelayCommand LedDisabledChanged => Commands.Create(() =>
{
if (!Config.EnableLedChanging) return;
if (Config.TurnOffLed)
ParentModel.SelectedGamepad.ClearLed();
else
ParentModel.SelectedGamepad.SetLed(Config.LedColor.ToUInt32());
});
public void OnParentModelChanged() public void OnParentModelChanged()
{ {
IsLeft = ParentModel.IsLeft; IsLeft = ParentModel.IsLeft;

View File

@ -3,6 +3,7 @@ using Avalonia.Controls;
using Avalonia.Svg.Skia; using Avalonia.Svg.Skia;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input; using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
@ -54,7 +55,18 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public IGamepadDriver AvaloniaKeyboardDriver { get; } public IGamepadDriver AvaloniaKeyboardDriver { get; }
public IGamepad SelectedGamepad { get; private set; }
private IGamepad _selectedGamepad;
public IGamepad SelectedGamepad
{
get => _selectedGamepad;
private set
{
_selectedGamepad = value;
OnPropertiesChanged(nameof(HasLed), nameof(CanClearLed));
}
}
public ObservableCollection<PlayerModel> PlayerIndexes { get; set; } public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; } public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
@ -70,6 +82,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsLeft { get; set; } public bool IsLeft { get; set; }
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led); public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
public bool IsModified { get; set; } public bool IsModified { get; set; }
public event Action NotifyChangesEvent; public event Action NotifyChangesEvent;

View File

@ -495,6 +495,7 @@
Margin="0,-1,0,0"> Margin="0,-1,0,0">
<Grid IsVisible="{Binding ParentModel.HasLed}"> <Grid IsVisible="{Binding ParentModel.HasLed}">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
@ -505,8 +506,18 @@
IsChecked="{Binding Config.EnableLedChanging, Mode=TwoWay}"> IsChecked="{Binding Config.EnableLedChanging, Mode=TwoWay}">
<TextBlock Text="{ext:Locale ControllerSettingsLedColor}" /> <TextBlock Text="{ext:Locale ControllerSettingsLedColor}" />
</CheckBox> </CheckBox>
<ui:ColorPickerButton <CheckBox
Margin="10"
MinWidth="0"
Grid.Column="1" Grid.Column="1"
IsVisible="{Binding ParentModel.CanClearLed}"
IsChecked="{Binding Config.TurnOffLed, Mode=TwoWay}"
Command="{Binding LedDisabledChanged}">
<TextBlock Text="{ext:Locale ControllerSettingsLedColorDisable}" />
</CheckBox>
<ui:ColorPickerButton
Grid.Column="2"
IsEnabled="{Binding !Config.TurnOffLed}"
Margin="10" Margin="10"
IsMoreButtonVisible="False" IsMoreButtonVisible="False"
UseColorPalette="False" UseColorPalette="False"

View File

@ -83,7 +83,7 @@ namespace Ryujinx.Ava.UI.Views.Input
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e) private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
{ {
if (sender is ToggleButton button) if (sender is ToggleButton button)
{ {
if (button.IsChecked is true) if (button.IsChecked is true)
{ {
@ -104,7 +104,9 @@ namespace Ryujinx.Ava.UI.Views.Input
var viewModel = (DataContext as ControllerInputViewModel); var viewModel = (DataContext as ControllerInputViewModel);
IKeyboard keyboard = (IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations. IKeyboard keyboard =
(IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver
.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
IButtonAssigner assigner = CreateButtonAssigner(isStick); IButtonAssigner assigner = CreateButtonAssigner(isStick);
_currentAssigner.ButtonAssigned += (sender, e) => _currentAssigner.ButtonAssigned += (sender, e) =>
@ -235,13 +237,13 @@ namespace Ryujinx.Ava.UI.Views.Input
_currentAssigner?.Cancel(); _currentAssigner?.Cancel();
_currentAssigner = null; _currentAssigner = null;
} }
private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args) private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args)
{ {
if (!args.NewColor.HasValue) return; if (!args.NewColor.HasValue) return;
if (DataContext is not ControllerInputViewModel cVm) return; if (DataContext is not ControllerInputViewModel cVm) return;
if (!cVm.Config.EnableLedChanging) return; if (!cVm.Config.EnableLedChanging) return;
cVm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32()); cVm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
} }
@ -249,7 +251,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
if (DataContext is not ControllerInputViewModel cVm) return; if (DataContext is not ControllerInputViewModel cVm) return;
if (!cVm.Config.EnableLedChanging) return; if (!cVm.Config.EnableLedChanging) return;
cVm.ParentModel.SelectedGamepad.SetLed(cVm.Config.LedColor.ToUInt32()); cVm.ParentModel.SelectedGamepad.SetLed(cVm.Config.LedColor.ToUInt32());
} }
} }

View File

@ -422,6 +422,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
config.Led = new LedConfigController config.Led = new LedConfigController
{ {
EnableLed = false, EnableLed = false,
TurnOffLed = false,
LedColor = new Color(255, 5, 1, 253).ToUInt32() LedColor = new Color(255, 5, 1, 253).ToUInt32()
}; };
} }