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()
};
}
})