diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/LedConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/LedConfigController.cs index 21727d62e..93b75d32c 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/LedConfigController.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/LedConfigController.cs @@ -2,14 +2,24 @@ { public class LedConfigController { - /// - /// Packed RGB int of the color - /// - public uint LedColor { get; set; } - /// /// Enable LED color changing by the emulator /// public bool EnableLed { get; set; } + + /// + /// Ignores the color and disables the LED entirely. + /// + public bool TurnOffLed { get; set; } + + /// + /// Ignores the color and uses the rainbow color functionality for the LED. + /// + public bool UseRainbow { get; set; } + + /// + /// Packed RGB int of the color + /// + public uint LedColor { get; set; } } } diff --git a/src/Ryujinx.Common/Utilities/Rainbow.cs b/src/Ryujinx.Common/Utilities/Rainbow.cs new file mode 100644 index 000000000..42222f157 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/Rainbow.cs @@ -0,0 +1,76 @@ +using System; +using System.Drawing; + +namespace Ryujinx.Common.Utilities +{ + public class Rainbow + { + public const float Speed = 1; + + public static Color Color { get; private set; } = Color.Blue; + + public static void Tick() + { + Color = HsbToRgb( + (Color.GetHue() + Speed) / 360, + 1, + 1 + ); + + RainbowColorUpdated?.Invoke(Color.ToArgb()); + } + + public static event Action RainbowColorUpdated; + + private static Color HsbToRgb(float hue, float saturation, float brightness) + { + int r = 0, g = 0, b = 0; + if (saturation == 0) + { + r = g = b = (int)(brightness * 255.0f + 0.5f); + } + else + { + float h = (hue - (float)Math.Floor(hue)) * 6.0f; + float f = h - (float)Math.Floor(h); + float p = brightness * (1.0f - saturation); + float q = brightness * (1.0f - saturation * f); + float t = brightness * (1.0f - (saturation * (1.0f - f))); + switch ((int)h) + { + case 0: + r = (int)(brightness * 255.0f + 0.5f); + g = (int)(t * 255.0f + 0.5f); + b = (int)(p * 255.0f + 0.5f); + break; + case 1: + r = (int)(q * 255.0f + 0.5f); + g = (int)(brightness * 255.0f + 0.5f); + b = (int)(p * 255.0f + 0.5f); + break; + case 2: + r = (int)(p * 255.0f + 0.5f); + g = (int)(brightness * 255.0f + 0.5f); + b = (int)(t * 255.0f + 0.5f); + break; + case 3: + r = (int)(p * 255.0f + 0.5f); + g = (int)(q * 255.0f + 0.5f); + b = (int)(brightness * 255.0f + 0.5f); + break; + case 4: + r = (int)(t * 255.0f + 0.5f); + g = (int)(p * 255.0f + 0.5f); + b = (int)(brightness * 255.0f + 0.5f); + break; + case 5: + r = (int)(brightness * 255.0f + 0.5f); + g = (int)(p * 255.0f + 0.5f); + b = (int)(q * 255.0f + 0.5f); + break; + } + } + return Color.FromArgb(Convert.ToByte(255), Convert.ToByte(r), Convert.ToByte(g), Convert.ToByte(b)); + } + } +} diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index ed22c3661..00d079a2b 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -1,6 +1,8 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.Services.Hid; using SDL2; using System; using System.Collections.Generic; @@ -86,7 +88,7 @@ namespace Ryujinx.Input.SDL2 Id = driverId; Features = GetFeaturesFlag(); _triggerThreshold = 0.0f; - + // Enable motion tracking if (Features.HasFlag(GamepadFeaturesFlag.Motion)) { @@ -102,6 +104,18 @@ namespace Ryujinx.Input.SDL2 } } + public void SetLed(uint packedRgb) + { + if (!Features.HasFlag(GamepadFeaturesFlag.Led)) return; + + 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) + Logger.Error?.Print(LogClass.Hid, "LED is not supported on this game controller."); + } + private GamepadFeaturesFlag GetFeaturesFlag() { GamepadFeaturesFlag result = GamepadFeaturesFlag.None; @@ -112,9 +126,7 @@ namespace Ryujinx.Input.SDL2 result |= GamepadFeaturesFlag.Motion; } - int error = SDL_GameControllerRumble(_gamepadHandle, 0, 0, 100); - - if (error == 0) + if (SDL_GameControllerHasRumble(_gamepadHandle) == SDL_bool.SDL_TRUE) { result |= GamepadFeaturesFlag.Rumble; } @@ -220,6 +232,17 @@ namespace Ryujinx.Input.SDL2 { _configuration = (StandardControllerInputConfig)configuration; + if (Features.HasFlag(GamepadFeaturesFlag.Led) && _configuration.Led.EnableLed) + { + if (_configuration.Led.TurnOffLed) + (this as IGamepad).ClearLed(); + else if (_configuration.Led.UseRainbow) + Rainbow.RainbowColorUpdated += clr => SetLed((uint)clr); + else + SetLed(_configuration.Led.LedColor); + + } + _buttonsUserMapping.Clear(); // First update sticks diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index c580e4e7d..251f53cba 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -173,5 +173,16 @@ namespace Ryujinx.Input.SDL2 return new SDL2Gamepad(gamepadHandle, id); } + + public IEnumerable GetGamepads() + { + lock (_gamepadsIds) + { + foreach (string gamepadId in _gamepadsIds) + { + yield return GetGamepad(gamepadId); + } + } + } } } diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs index 8d6a30d11..270af5114 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.Logging; using System; using System.Collections.Generic; using System.Numerics; @@ -385,6 +386,11 @@ namespace Ryujinx.Input.SDL2 } } + public void SetLed(uint packedRgb) + { + Logger.Info?.Print(LogClass.UI, "SetLed called on an SDL2Keyboard"); + } + public void SetTriggerThreshold(float triggerThreshold) { // No operations diff --git a/src/Ryujinx.Input.SDL2/SDL2Mouse.cs b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs index 37b356b76..eb86fa799 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Mouse.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Logging; using System; using System.Drawing; using System.Numerics; @@ -76,6 +77,11 @@ namespace Ryujinx.Input.SDL2 throw new NotImplementedException(); } + public void SetLed(uint packedRgb) + { + Logger.Info?.Print(LogClass.UI, "SetLed called on an SDL2Mouse"); + } + public void SetTriggerThreshold(float triggerThreshold) { throw new NotImplementedException(); diff --git a/src/Ryujinx.Input.SDL2/SDL2MouseDriver.cs b/src/Ryujinx.Input.SDL2/SDL2MouseDriver.cs index 768ea8c62..7a9679901 100644 --- a/src/Ryujinx.Input.SDL2/SDL2MouseDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2MouseDriver.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using System; +using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Numerics; @@ -164,6 +165,8 @@ namespace Ryujinx.Input.SDL2 return new SDL2Mouse(this); } + public IEnumerable GetGamepads() => [GetGamepad("0")]; + public void Dispose() { if (_isDisposed) diff --git a/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs b/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs index 965f7935a..69e12bda0 100644 --- a/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDLKeyboardDriver.cs @@ -1,5 +1,6 @@ using Ryujinx.SDL2.Common; using System; +using System.Collections.Generic; namespace Ryujinx.Input.SDL2 { @@ -51,5 +52,13 @@ namespace Ryujinx.Input.SDL2 return new SDL2Keyboard(this, _keyboardIdentifers[0], "All keyboards"); } + + public IEnumerable GetGamepads() + { + foreach (var keyboardId in _keyboardIdentifers) + { + yield return GetGamepad(keyboardId); + } + } } } diff --git a/src/Ryujinx.Input/IGamepad.cs b/src/Ryujinx.Input/IGamepad.cs index 3853f2819..832950660 100644 --- a/src/Ryujinx.Input/IGamepad.cs +++ b/src/Ryujinx.Input/IGamepad.cs @@ -65,6 +65,15 @@ namespace Ryujinx.Input /// The configuration of the gamepad void SetConfiguration(InputConfig configuration); + /// + /// Set the LED on the gamepad to a given color. + /// + /// Does nothing on a controller without LED functionality. + /// The packed RGB integer. + void SetLed(uint packedRgb); + + public void ClearLed() => SetLed(0); + /// /// Starts a rumble effect on the gamepad. /// diff --git a/src/Ryujinx.Input/IGamepadDriver.cs b/src/Ryujinx.Input/IGamepadDriver.cs index 625c3e694..25b2295db 100644 --- a/src/Ryujinx.Input/IGamepadDriver.cs +++ b/src/Ryujinx.Input/IGamepadDriver.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Ryujinx.Input { @@ -33,6 +34,11 @@ namespace Ryujinx.Input /// The unique id of the gamepad /// An instance of associated to the gamepad id given or null if not found IGamepad GetGamepad(string id); + + /// + /// Returns an of the connected gamepads. + /// + IEnumerable GetGamepads(); /// /// Clear the internal state of the driver. diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 851c07867..47c5e60c5 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -167,6 +168,8 @@ namespace Ryujinx.SDL2.Common HandleSDLEvent(ref evnt); } }); + + Rainbow.Tick(); waitHandle.Wait(WaitTimeMs); } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 4df3eab0d..31f27a965 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -587,6 +587,11 @@ namespace Ryujinx.Ava return; } + foreach (IGamepad gamepad in RyujinxApp.MainWindow.InputManager.GamepadDriver.GetGamepads()) + { + gamepad?.ClearLed(); + } + _isStopped = true; Stop(); } diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 8ceef5f67..698af9d17 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -7628,7 +7628,57 @@ "ar_SA": "", "de_DE": "", "el_GR": "", - "en_US": "Custom LED", + "en_US": "LED", + "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": "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": "ControllerSettingsLedColorRainbow", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Rainbow", "es_ES": "", "fr_FR": "", "he_IL": "", diff --git a/src/Ryujinx/Input/AvaloniaKeyboard.cs b/src/Ryujinx/Input/AvaloniaKeyboard.cs index 0b63af2d9..031d8b033 100644 --- a/src/Ryujinx/Input/AvaloniaKeyboard.cs +++ b/src/Ryujinx/Input/AvaloniaKeyboard.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.Logging; using Ryujinx.Input; using System; using System.Collections.Generic; @@ -143,6 +144,11 @@ namespace Ryujinx.Ava.Input } } + public void SetLed(uint packedRgb) + { + Logger.Info?.Print(LogClass.UI, "SetLed called on an AvaloniaKeyboard"); + } + public void SetTriggerThreshold(float triggerThreshold) { } public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { } diff --git a/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs b/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs index 9f87e821a..214652265 100644 --- a/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs +++ b/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs @@ -59,6 +59,8 @@ namespace Ryujinx.Ava.Input return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance[LocaleKeys.AllKeyboards]); } + public IEnumerable GetGamepads() => [GetGamepad("0")]; + protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/src/Ryujinx/Input/AvaloniaMouse.cs b/src/Ryujinx/Input/AvaloniaMouse.cs index 1aa2d586a..52a341a01 100644 --- a/src/Ryujinx/Input/AvaloniaMouse.cs +++ b/src/Ryujinx/Input/AvaloniaMouse.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Logging; using Ryujinx.Input; using System; using System.Drawing; @@ -74,6 +75,11 @@ namespace Ryujinx.Ava.Input throw new NotImplementedException(); } + public void SetLed(uint packedRgb) + { + Logger.Info?.Print(LogClass.UI, "SetLed called on an AvaloniaMouse"); + } + public void SetTriggerThreshold(float triggerThreshold) { throw new NotImplementedException(); diff --git a/src/Ryujinx/Input/AvaloniaMouseDriver.cs b/src/Ryujinx/Input/AvaloniaMouseDriver.cs index e71bbf64a..be1441101 100644 --- a/src/Ryujinx/Input/AvaloniaMouseDriver.cs +++ b/src/Ryujinx/Input/AvaloniaMouseDriver.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Input; using Ryujinx.Input; using System; +using System.Collections.Generic; using System.Numerics; using MouseButton = Ryujinx.Input.MouseButton; using Size = System.Drawing.Size; @@ -134,6 +135,8 @@ namespace Ryujinx.Ava.Input return new AvaloniaMouse(this); } + public IEnumerable GetGamepads() => [GetGamepad("0")]; + public void Dispose() { if (_isDisposed) diff --git a/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs b/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs index ea7dd34c3..6f0f7f47f 100644 --- a/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs +++ b/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs @@ -388,30 +388,6 @@ namespace Ryujinx.Ava.UI.Models.Input } } - private bool _enableLedChanging; - - public bool EnableLedChanging - { - get => _enableLedChanging; - set - { - _enableLedChanging = value; - OnPropertyChanged(); - } - } - - private Color _ledColor; - - public Color LedColor - { - get => _ledColor; - set - { - _ledColor = value; - OnPropertyChanged(); - } - } - private bool _enableMotion; public bool EnableMotion { @@ -433,6 +409,58 @@ namespace Ryujinx.Ava.UI.Models.Input OnPropertyChanged(); } } + + private bool _enableLedChanging; + + public bool EnableLedChanging + { + get => _enableLedChanging; + set + { + _enableLedChanging = value; + OnPropertyChanged(); + } + } + + public bool ShowLedColorPicker => !TurnOffLed && !UseRainbowLed; + + private bool _turnOffLed; + + public bool TurnOffLed + { + get => _turnOffLed; + set + { + _turnOffLed = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(ShowLedColorPicker)); + } + } + + private bool _useRainbowLed; + + public bool UseRainbowLed + { + get => _useRainbowLed; + set + { + _useRainbowLed = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(ShowLedColorPicker)); + } + } + + private Color _ledColor; + + public Color LedColor + { + get => _ledColor; + set + { + _ledColor = value; + OnPropertyChanged(); + } + } public GamepadInputConfig(InputConfig config) { @@ -512,6 +540,8 @@ namespace Ryujinx.Ava.UI.Models.Input if (controllerInput.Led != null) { EnableLedChanging = controllerInput.Led.EnableLed; + TurnOffLed = controllerInput.Led.TurnOffLed; + UseRainbowLed = controllerInput.Led.UseRainbow; uint rawColor = controllerInput.Led.LedColor; byte alpha = (byte)(rawColor >> 24); byte red = (byte)(rawColor >> 16); @@ -579,6 +609,8 @@ namespace Ryujinx.Ava.UI.Models.Input Led = new LedConfigController { EnableLed = EnableLedChanging, + TurnOffLed = this.TurnOffLed, + UseRainbow = UseRainbowLed, LedColor = LedColor.ToUInt32() }, Version = InputConfig.CurrentVersion, diff --git a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs index 0380ef598..d291f09a0 100644 --- a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs @@ -1,5 +1,8 @@ using Avalonia.Svg.Skia; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using FluentAvalonia.UI.Controls; +using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Views.Input; @@ -57,6 +60,16 @@ namespace Ryujinx.Ava.UI.ViewModels.Input 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() { IsLeft = ParentModel.IsLeft; diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 3d1bd5f4a..c59ec540c 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Svg.Skia; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; +using Gommon; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Helpers; @@ -54,7 +55,18 @@ namespace Ryujinx.Ava.UI.ViewModels.Input private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); 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 PlayerIndexes { get; set; } public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; } @@ -69,8 +81,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input public bool IsRight { get; set; } public bool IsLeft { get; set; } - public bool HasLed => false; //temporary - //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 event Action NotifyChangesEvent; diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml index db7040f4b..1662f4a3d 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml @@ -429,7 +429,7 @@ - + + + - + + + + + + diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs index 52a6d51b9..81483ce0e 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs @@ -4,11 +4,14 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using FluentAvalonia.UI.Controls; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Input; using Ryujinx.Input.Assigner; +using System.Linq; using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; namespace Ryujinx.Ava.UI.Views.Input @@ -82,7 +85,7 @@ namespace Ryujinx.Ava.UI.Views.Input private void Button_IsCheckedChanged(object sender, RoutedEventArgs e) { - if (sender is ToggleButton button) + if (sender is ToggleButton button) { if (button.IsChecked is true) { @@ -103,7 +106,9 @@ namespace Ryujinx.Ava.UI.Views.Input 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); _currentAssigner.ButtonAssigned += (sender, e) => @@ -231,8 +236,31 @@ namespace Ryujinx.Ava.UI.Views.Input protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); + + foreach (IGamepad gamepad in RyujinxApp.MainWindow.InputManager.GamepadDriver.GetGamepads()) + { + gamepad?.ClearLed(); + } + _currentAssigner?.Cancel(); _currentAssigner = null; } + + private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args) + { + if (!args.NewColor.HasValue) return; + if (DataContext is not ControllerInputViewModel cVm) return; + if (!cVm.Config.EnableLedChanging) return; + + cVm.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32()); + } + + private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) + { + if (DataContext is not ControllerInputViewModel cVm) return; + if (!cVm.Config.EnableLedChanging) return; + + cVm.ParentModel.SelectedGamepad.SetLed(cVm.Config.LedColor.ToUInt32()); + } } } diff --git a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs index db8e0f6bb..67deb9723 100644 --- a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs @@ -4,9 +4,14 @@ using Avalonia.Input; using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.HLE.FileSystem; +using Ryujinx.Input; using System; +using System.Linq; +using Key = Avalonia.Input.Key; namespace Ryujinx.Ava.UI.Windows { @@ -106,6 +111,12 @@ namespace Ryujinx.Ava.UI.Windows protected override void OnClosing(WindowClosingEventArgs e) { HotkeysPage.Dispose(); + + foreach (IGamepad gamepad in RyujinxApp.MainWindow.InputManager.GamepadDriver.GetGamepads()) + { + gamepad?.ClearLed(); + } + InputPage.Dispose(); base.OnClosing(e); } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index 1b00a8fa4..3ccac2647 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -1,3 +1,4 @@ +using Avalonia.Media; using Gommon; using Ryujinx.Ava.Utilities.Configuration.System; using Ryujinx.Ava.Utilities.Configuration.UI; @@ -421,7 +422,9 @@ namespace Ryujinx.Ava.Utilities.Configuration config.Led = new LedConfigController { EnableLed = false, - LedColor = 328189 + TurnOffLed = false, + UseRainbow = false, + LedColor = new Color(255, 5, 1, 253).ToUInt32() }; } })