[HID] Fixed HD Rumble latency (#104)

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/104
This commit is contained in:
Max
2026-05-20 13:00:09 +00:00
committed by sh0inx
parent d94b759e89
commit ce340e5d0b
20 changed files with 261 additions and 64 deletions

View File

@@ -12250,6 +12250,56 @@
"zh_TW": "弱震動調節:"
}
},
{
"ID": "ControllerSettingsRumbleUseHDRumble",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Enable HD Rumble",
"es_ES": "Activa vibración HD",
"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": "HDRumbleTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Sends more data to the controller for better rumble.\n\nCurrently only supports first-party Nintendo Switch controllers.\n\nLeave ON if you're using JoyCons or a Pro Controller.",
"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": "DialogMessageSaveNotAvailableMessage",
"Translations": {

View File

@@ -16,5 +16,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
/// Enable Rumble
/// </summary>
public bool EnableRumble { get; set; }
/// <summary>
/// Enable HD Rumble support
/// </summary
public bool UseHDRumble { get; set; }
}
}

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using SDL;
using static SDL.SDL3;
@@ -11,8 +12,9 @@ namespace Ryujinx.Input.SDL3
public unsafe class NpadHdRumble : IDisposable
{
private readonly SDL_hid_device* _hidHandle;
private int _globalCount;
private ulong _lastWriteTicks;
private NpadHdRumble(SDL_hid_device* hidHandle)
{
@@ -28,7 +30,7 @@ namespace Ryujinx.Input.SDL3
}
ushort product = SDL_GetGamepadProduct(gamepadHandle);
if (product != 0x2006 && product != 0x2007 && product != 0x2009 && product != 0x200e)
if (!Enum.IsDefined(typeof(HDRumbleSupported), product))
{
return null;
}
@@ -37,7 +39,7 @@ namespace Ryujinx.Input.SDL3
}
// Some of the code was translated from https://github.com/MIZUSHIKI/JoyShockLibrary-plus-HDRumble
private void WriteHdRumble(
private bool WriteHdRumble(
int encLeftLowFreq, int encLeftLowAmp,
int encLeftHighFreq, int encLeftHighAmp,
int encRightLowFreq, int encRightLowAmp,
@@ -65,26 +67,35 @@ namespace Ryujinx.Input.SDL3
fixed (byte* ptr = buf)
{
SDL_hid_write(_hidHandle, ptr, (nuint)buf.Length);
if (SendHDRumble(ptr, (nuint)buf.Length) >= 0)
{
return true;
}
if (!String.IsNullOrEmpty(SDL_GetError()))
{
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
}
return false;
}
}
private static int EncodeLowFreq(float lowFreq)
{
float lf = Math.Clamp(lowFreq, 40.875885f, 626.286133f);
return (int)Math.Round(32 * Math.Log2(lf * 0.1f)) - 0x40;
return (int) Math.Round(32 * Math.Log2(lf * 0.1f) - 0x40);
}
private static int EncodeHighFreq(float highFreq)
{
float hf = Math.Clamp(highFreq, 81.75177f, 1252.572266f);
return ((int)Math.Round(32 * Math.Log2(hf * 0.1f)) - 0x60) * 4;
return (int) Math.Round((32 * Math.Log2(hf * 0.1f) - 0x60) * 4);
}
private static int EncodeLowAmp(float rawAmp)
{
int encodedAmp = 0;
double encodedAmp = 0;
if (rawAmp is > 0 and < 0.012f)
{
@@ -92,15 +103,15 @@ namespace Ryujinx.Input.SDL3
}
else if (rawAmp is >= 0.012f and < 0.112f)
{
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
encodedAmp = 4 * Math.Log2(rawAmp * 110f);
}
else if (rawAmp is >= 0.112f and < 0.225f)
{
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
encodedAmp = 16 * Math.Log2(rawAmp * 17f);
}
else if (rawAmp is >= 0.225f and <= 1f)
{
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
encodedAmp = 32 * Math.Log2(rawAmp * 8.7f);
}
return (int)Math.Floor(encodedAmp / 2.0) + 64;
@@ -108,7 +119,7 @@ namespace Ryujinx.Input.SDL3
private static int EncodeHighAmp(float rawAmp)
{
int encodedAmp = 0;
double encodedAmp = 0;
if (rawAmp is > 0 and < 0.012f)
{
@@ -116,23 +127,23 @@ namespace Ryujinx.Input.SDL3
}
else if (rawAmp is >= 0.012f and < 0.112f)
{
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
encodedAmp = 4 * Math.Log2(rawAmp * 110f);
}
else if (rawAmp is >= 0.112f and < 0.225f)
{
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
encodedAmp = 16 * Math.Log2(rawAmp * 17f);
}
else if (rawAmp is >= 0.225f and <= 1f)
{
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
encodedAmp = 32 * Math.Log2(rawAmp * 8.7f);
}
return encodedAmp * 2;
return (int) Math.Round(encodedAmp * 2);
}
public bool HdRumble(VibrationValue left, VibrationValue right)
{
WriteHdRumble(EncodeLowFreq(left.FrequencyLow),
return WriteHdRumble(EncodeLowFreq(left.FrequencyLow),
EncodeLowAmp(left.AmplitudeLow),
EncodeHighFreq(left.FrequencyHigh),
EncodeHighAmp(left.AmplitudeHigh),
@@ -140,7 +151,34 @@ namespace Ryujinx.Input.SDL3
EncodeLowAmp(right.AmplitudeLow),
EncodeHighFreq(right.FrequencyHigh),
EncodeHighAmp(right.AmplitudeHigh));
return true;
}
private int SendHDRumble(byte* data, nuint length)
{
int result = 0;
ulong currentTicks = SDL_GetTicks();
// Ditch rumble if we haven't hit the poll-rate yet.
// TODO: figure out a better way to do this
// While the polling check makes the rumble accurate, it also causes it to miss signals.
if ((currentTicks - _lastWriteTicks) < 8) // https://docs.handheldlegend.com/s/progcc-3/doc/lag-comparison-aAR1mV3JLX
{
return result;
}
SDL_LockJoysticks();
{
// Fun fact: Mario Kart 8 Deluxe sends rumble packets
// where the amplitude is zero, but the frequency isn't.
result = SDL_hid_write(_hidHandle, data, length);
if (result >= 0)
{
_lastWriteTicks = currentTicks;
}
}
SDL_UnlockJoysticks();
return result;
}
public void Dispose()
@@ -148,4 +186,18 @@ namespace Ryujinx.Input.SDL3
SDL_hid_close(_hidHandle);
}
}
public enum HDRumbleSupported : ushort
{
JoyConLeft = 0x2006,
JoyConRight = 0x2007,
JoyconPair = 0x2008,
ProController = 0x2009,
JoyconGrip = 0x200e,
Joycon2Right = 0x2066,
Joycon2Left = 0x2067,
Joycon2Pair = 0x2068,
Switch2ProController = 0x2069,
GamecubeController = 0x2073
}
}

View File

@@ -197,10 +197,12 @@ namespace Ryujinx.Input.SDL3
return _hdRumble?.HdRumble(left, right) ?? false;
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
return;
{
return false;
}
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
@@ -219,6 +221,15 @@ namespace Ryujinx.Input.SDL3
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
}
if (!String.IsNullOrEmpty(SDL_GetError()))
{
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
return false;
}
return true;
}
public Vector3 GetMotionData(MotionInputId inputId)

View File

@@ -162,7 +162,7 @@ namespace Ryujinx.Input.SDL3
public void SetTriggerThreshold(float triggerThreshold)
{
// No operations
}
public bool HDRumble(VibrationValue left, VibrationValue right)
@@ -170,10 +170,12 @@ namespace Ryujinx.Input.SDL3
return _hdRumble?.HdRumble(left, right) ?? false;
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
return;
{
return false;
}
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
@@ -192,6 +194,15 @@ namespace Ryujinx.Input.SDL3
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
}
if (!String.IsNullOrEmpty(SDL_GetError()))
{
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
return false;
}
return true;
}
public Vector3 GetMotionData(MotionInputId inputId)

View File

@@ -1,4 +1,7 @@
using Gommon;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
@@ -61,7 +64,14 @@ namespace Ryujinx.Input.SDL3
return left.IsPressed(inputId) || right.IsPressed(inputId);
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
public bool HDRumble(VibrationValue left, VibrationValue right)
{
// return _hdRumble?.HdRumble(left, right) ?? false;
// TODO: Track rumble and motion on both controllers
return false;
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
if (lowFrequency != 0)
{
@@ -78,6 +88,15 @@ namespace Ryujinx.Input.SDL3
left.Rumble(0, 0, durationMs);
right.Rumble(0, 0, durationMs);
}
if (!SDL_GetError().IsNullOrEmpty())
{
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
SDL_ClearError();
return false;
}
return true;
}
public void SetConfiguration(InputConfig configuration)

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Collections.Generic;
using System.Numerics;
@@ -396,9 +397,14 @@ namespace Ryujinx.Input.SDL3
// No operations
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
public bool HDRumble(VibrationValue left, VibrationValue right)
{
// No operations
return false;
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
return false;
}
public Vector3 GetMotionData(MotionInputId inputId)

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Drawing;
using System.Numerics;
@@ -67,7 +68,12 @@ namespace Ryujinx.Input.SDL3
throw new NotImplementedException();
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
public bool HDRumble(VibrationValue left, VibrationValue right)
{
throw new NotImplementedException();
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
throw new NotImplementedException();
}

View File

@@ -5,7 +5,6 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -555,34 +554,37 @@ namespace Ryujinx.Input.HLE
{
if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue))
{
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble)
if (_config is not StandardControllerInputConfig controllerConfig ||
!controllerConfig.Rumble.EnableRumble)
{
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
leftVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
leftVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
rightVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
rightVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
if (_gamepad?.HDRumble(leftVibrationValue, rightVibrationValue) == false)
{
_gamepad?.Rumble(low, high, uint.MaxValue);
}
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
$"L.low.amp={leftVibrationValue.AmplitudeLow}, " +
$"L.high.amp={leftVibrationValue.AmplitudeHigh}, " +
$"L.low.freq={leftVibrationValue.FrequencyLow}, " +
$"L.high.freq={leftVibrationValue.FrequencyHigh}, " +
$"R.low.amp={rightVibrationValue.AmplitudeLow}, " +
$"R.high.amp={rightVibrationValue.AmplitudeHigh} " +
$"R.low.freq={rightVibrationValue.FrequencyLow}, " +
$"R.high.freq={rightVibrationValue.FrequencyHigh}");
return;
}
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
leftVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
leftVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
rightVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
rightVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
if (!controllerConfig.Rumble.UseHDRumble || _gamepad?.HDRumble(leftVibrationValue, rightVibrationValue) == false)
{
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15)));
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85)));
_gamepad?.Rumble(low, high, 0xFFFFFFFF);
}
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
// Value=value/multiplier * multiplier (result)
$"L.low.amp={leftVibrationValue.AmplitudeLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.AmplitudeLow}), " +
$"L.high.amp={leftVibrationValue.AmplitudeHigh / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.AmplitudeHigh}), " +
$"L.low.freq={leftVibrationValue.FrequencyLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.FrequencyLow}), " +
$"L.high.freq={leftVibrationValue.FrequencyHigh / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.FrequencyHigh}), " +
$"R.low.amp={rightVibrationValue.AmplitudeLow / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.AmplitudeLow}), " +
$"R.high.amp={rightVibrationValue.AmplitudeHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.AmplitudeHigh}), " +
$"R.low.freq={rightVibrationValue.FrequencyLow / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.FrequencyLow}), " +
$"R.high.freq={rightVibrationValue.FrequencyHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.FrequencyHigh})");
}
}
}

View File

@@ -80,10 +80,7 @@ namespace Ryujinx.Input
/// </summary>
/// <param name="left">The vibration data for the left side</param>
/// <param name="right">The vibration data for the right side</param>
bool HDRumble(VibrationValue left, VibrationValue right)
{
return false;
}
bool HDRumble(VibrationValue left, VibrationValue right);
/// <summary>
/// Starts a rumble effect on the gamepad.
@@ -91,10 +88,10 @@ namespace Ryujinx.Input
/// <param name="lowFrequency">The intensity of the low frequency from 0.0f to 1.0f</param>
/// <param name="highFrequency">The intensity of the high frequency from 0.0f to 1.0f</param>
/// <param name="durationMs">The duration of the rumble effect in milliseconds.</param>
void Rumble(float lowFrequency, float highFrequency, uint durationMs);
bool Rumble(float lowFrequency, float highFrequency, uint durationMs);
/// <summary>
/// Get a snaphost of the state of the gamepad that is remapped with the informations from the <see cref="InputConfig"/> set via <see cref="SetConfiguration(InputConfig)"/>.
/// Get a snaphost of the state of the gamepad that is remapped with the information from the <see cref="InputConfig"/> set via <see cref="SetConfiguration(InputConfig)"/>.
/// </summary>
/// <returns>A remapped snaphost of the state of the gamepad.</returns>
GamepadStateSnapshot GetMappedStateSnapshot();

View File

@@ -221,6 +221,7 @@ namespace Ryujinx.Headless
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false,
UseHDRumble = true
},
};
}

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.Input;
using System;
using System.Collections.Generic;
@@ -149,9 +150,20 @@ namespace Ryujinx.Ava.Input
Logger.Info?.Print(LogClass.UI, "SetLed called on an AvaloniaKeyboard");
}
public void SetTriggerThreshold(float triggerThreshold) { }
public void SetTriggerThreshold(float triggerThreshold)
{
// No operations
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { }
public bool HDRumble(VibrationValue left, VibrationValue right)
{
return false;
}
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
return false;
}
public Vector3 GetMotionData(MotionInputId inputId) => Vector3.Zero;

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.Input;
using System;
using System.Drawing;
@@ -64,8 +65,13 @@ namespace Ryujinx.Ava.Input
{
throw new NotImplementedException();
}
public bool HDRumble(VibrationValue left, VibrationValue right)
{
throw new NotImplementedException();
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
throw new NotImplementedException();
}

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Systems.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 72;
public const int CurrentVersion = 73;
/// <summary>
/// Version of the configuration file format

View File

@@ -333,6 +333,7 @@ namespace Ryujinx.Ava.Systems.Configuration
EnableRumble = false,
StrongRumble = 1f,
WeakRumble = 1f,
UseHDRumble = true
};
}
}

View File

@@ -20,6 +20,7 @@ namespace Ryujinx.Ava.UI.Models.Input
public float WeakRumble { get; set; }
public float StrongRumble { get; set; }
public bool UseHDRumble { get; set; }
public string Id { get; set; }
@@ -236,6 +237,7 @@ namespace Ryujinx.Ava.UI.Models.Input
EnableRumble = controllerInput.Rumble.EnableRumble;
WeakRumble = controllerInput.Rumble.WeakRumble;
StrongRumble = controllerInput.Rumble.StrongRumble;
UseHDRumble = controllerInput.Rumble.UseHDRumble;
}
if (controllerInput.Led != null)
@@ -307,6 +309,7 @@ namespace Ryujinx.Ava.UI.Models.Input
EnableRumble = EnableRumble,
WeakRumble = WeakRumble,
StrongRumble = StrongRumble,
UseHDRumble = UseHDRumble,
},
Led = new LedConfigController
{

View File

@@ -789,6 +789,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false,
UseHDRumble = true
},
};
}

View File

@@ -9,5 +9,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
[ObservableProperty]
public partial float WeakRumble { get; set; }
[ObservableProperty]
public partial bool EnableHDRumble { get; set; }
}
}

View File

@@ -53,6 +53,15 @@
Margin="5,0"
Text="{Binding WeakRumble, StringFormat=\{0:0.00\}}" />
</StackPanel>
<CheckBox
Margin="5"
IsChecked="{Binding EnableHDRumble}">
<TextBlock
Margin="0,3,0,0"
VerticalAlignment="Center"
Text="{ext:Locale ControllerSettingsRumbleUseHDRumble}"
ToolTip.Tip="{ext:Locale HDRumbleTooltip}" />
</CheckBox>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -22,6 +22,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{
StrongRumble = config.StrongRumble,
WeakRumble = config.WeakRumble,
EnableHDRumble = config.UseHDRumble
};
InitializeComponent();
@@ -45,6 +46,7 @@ namespace Ryujinx.Ava.UI.Views.Input
GamepadInputConfig config = viewModel.Config;
config.StrongRumble = content.ViewModel.StrongRumble;
config.WeakRumble = content.ViewModel.WeakRumble;
config.UseHDRumble = content.ViewModel.EnableHDRumble;
};
await contentDialog.ShowAsync();