mirror of
https://github.com/GreemDev/Ryujinx.git
synced 2025-08-30 07:25:05 +00:00
Add option to change controller LED color (#572)
Some checks are pending
Canary release job / Create tag (push) Waiting to run
Canary release job / Release for linux-arm64 (push) Waiting to run
Canary release job / Release for linux-x64 (push) Waiting to run
Canary release job / Release for win-x64 (push) Waiting to run
Canary release job / Release MacOS universal (push) Waiting to run
Some checks are pending
Canary release job / Create tag (push) Waiting to run
Canary release job / Release for linux-arm64 (push) Waiting to run
Canary release job / Release for linux-x64 (push) Waiting to run
Canary release job / Release for win-x64 (push) Waiting to run
Canary release job / Release MacOS universal (push) Waiting to run
This allows the user to change the controller LED while using Ryujinx. Useful for PS4 and PS5 controllers as an example. You can also use a spectrum-cycling Rainbow color option, or turn the LED off for DualSense controllers. --------- Co-authored-by: Evan Husted <greem@greemdev.net>
This commit is contained in:
parent
c06f16c5e6
commit
1ce37ec317
24 changed files with 399 additions and 44 deletions
|
|
@ -2,14 +2,24 @@
|
|||
{
|
||||
public class LedConfigController
|
||||
{
|
||||
/// <summary>
|
||||
/// Packed RGB int of the color
|
||||
/// </summary>
|
||||
public uint LedColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable LED color changing by the emulator
|
||||
/// </summary>
|
||||
public bool EnableLed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ignores the color and disables the LED entirely.
|
||||
/// </summary>
|
||||
public bool TurnOffLed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ignores the color and uses the rainbow color functionality for the LED.
|
||||
/// </summary>
|
||||
public bool UseRainbow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Packed RGB int of the color
|
||||
/// </summary>
|
||||
public uint LedColor { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
76
src/Ryujinx.Common/Utilities/Rainbow.cs
Normal file
76
src/Ryujinx.Common/Utilities/Rainbow.cs
Normal file
|
|
@ -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<int> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -173,5 +173,16 @@ namespace Ryujinx.Input.SDL2
|
|||
|
||||
return new SDL2Gamepad(gamepadHandle, id);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads()
|
||||
{
|
||||
lock (_gamepadsIds)
|
||||
{
|
||||
foreach (string gamepadId in _gamepadsIds)
|
||||
{
|
||||
yield return GetGamepad(gamepadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<IGamepad> GetGamepads() => [GetGamepad("0")];
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
|
|
|
|||
|
|
@ -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<IGamepad> GetGamepads()
|
||||
{
|
||||
foreach (var keyboardId in _keyboardIdentifers)
|
||||
{
|
||||
yield return GetGamepad(keyboardId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,15 @@ namespace Ryujinx.Input
|
|||
/// <param name="configuration">The configuration of the gamepad</param>
|
||||
void SetConfiguration(InputConfig configuration);
|
||||
|
||||
/// <summary>
|
||||
/// Set the LED on the gamepad to a given color.
|
||||
/// </summary>
|
||||
/// <remarks>Does nothing on a controller without LED functionality.</remarks>
|
||||
/// <param name="packedRgb">The packed RGB integer.</param>
|
||||
void SetLed(uint packedRgb);
|
||||
|
||||
public void ClearLed() => SetLed(0);
|
||||
|
||||
/// <summary>
|
||||
/// Starts a rumble effect on the gamepad.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Input
|
||||
{
|
||||
|
|
@ -33,6 +34,11 @@ namespace Ryujinx.Input
|
|||
/// <param name="id">The unique id of the gamepad</param>
|
||||
/// <returns>An instance of <see cref="IGamepad"/> associated to the gamepad id given or null if not found</returns>
|
||||
IGamepad GetGamepad(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an <see cref="IEnumerable{T}"/> of the connected gamepads.
|
||||
/// </summary>
|
||||
IEnumerable<IGamepad> GetGamepads();
|
||||
|
||||
/// <summary>
|
||||
/// Clear the internal state of the driver.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -587,6 +587,11 @@ namespace Ryujinx.Ava
|
|||
return;
|
||||
}
|
||||
|
||||
foreach (IGamepad gamepad in RyujinxApp.MainWindow.InputManager.GamepadDriver.GetGamepads())
|
||||
{
|
||||
gamepad?.ClearLed();
|
||||
}
|
||||
|
||||
_isStopped = true;
|
||||
Stop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": "",
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ namespace Ryujinx.Ava.Input
|
|||
return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance[LocaleKeys.AllKeyboards]);
|
||||
}
|
||||
|
||||
public IEnumerable<IGamepad> GetGamepads() => [GetGamepad("0")];
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<IGamepad> GetGamepads() => [GetGamepad("0")];
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<PlayerModel> 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;
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@
|
|||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<!-- Motion + Rumble -->
|
||||
<!-- Motion, Rumble, LED -->
|
||||
<StackPanel
|
||||
Margin="0,10,0,0"
|
||||
Spacing="5"
|
||||
|
|
@ -495,25 +495,47 @@
|
|||
Margin="0,-1,0,0">
|
||||
<Grid IsVisible="{Binding ParentModel.HasLed}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox
|
||||
Margin="10"
|
||||
Margin="10, 10, 5, 10"
|
||||
MinWidth="0"
|
||||
Grid.Column="0"
|
||||
IsChecked="{Binding Config.EnableLedChanging, Mode=TwoWay}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsLedColor}" />
|
||||
</CheckBox>
|
||||
<ui:ColorPickerButton
|
||||
<CheckBox
|
||||
Margin="5, 10, 5, 10"
|
||||
MinWidth="0"
|
||||
Grid.Column="1"
|
||||
Margin="10"
|
||||
IsVisible="{Binding ParentModel.CanClearLed}"
|
||||
IsChecked="{Binding Config.TurnOffLed, Mode=TwoWay}"
|
||||
Command="{Binding LedDisabledChanged}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsLedColorDisable}" />
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
Margin="5, 10 5,10"
|
||||
MinWidth="0"
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding !Config.TurnOffLed}"
|
||||
IsChecked="{Binding Config.UseRainbowLed, Mode=TwoWay}">
|
||||
<TextBlock Text="{ext:Locale ControllerSettingsLedColorRainbow}" />
|
||||
</CheckBox>
|
||||
<ui:ColorPickerButton
|
||||
Grid.Column="3"
|
||||
IsEnabled="{Binding Config.ShowLedColorPicker}"
|
||||
Margin="5, 10, 10, 10"
|
||||
IsMoreButtonVisible="False"
|
||||
UseColorPalette="False"
|
||||
UseColorTriangle="False"
|
||||
UseColorWheel="False"
|
||||
ShowAcceptDismissButtons="False"
|
||||
IsAlphaEnabled="False"
|
||||
AttachedToVisualTree="ColorPickerButton_OnAttachedToVisualTree"
|
||||
ColorChanged="ColorPickerButton_OnColorChanged"
|
||||
Color="{Binding Config.LedColor, Mode=TwoWay}">
|
||||
</ui:ColorPickerButton>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
};
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue