mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-20 20:25:48 +00:00
Compare commits
4 Commits
Canary-1.3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81468c1d25 | ||
|
|
004a12005e | ||
|
|
a3eda287b5 | ||
|
|
ce340e5d0b |
@@ -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": {
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -221,6 +221,7 @@ namespace Ryujinx.Headless
|
||||
StrongRumble = 1f,
|
||||
WeakRumble = 1f,
|
||||
EnableRumble = false,
|
||||
UseHDRumble = true
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
private const uint MbIconwarning = 0x30;
|
||||
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Version = ReleaseInformation.Version;
|
||||
@@ -54,17 +55,39 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
|
||||
{
|
||||
_ = Win32NativeInterop.MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run on an outdated version of Windows. Exiting...");
|
||||
_ = Win32NativeInterop.MessageBoxA(nint.Zero,
|
||||
"You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n",
|
||||
$"Ryujinx {Version}", MbIconwarning);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
var programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
|
||||
string onedriveFiles = Environment.GetEnvironmentVariable("Onedrive");
|
||||
string onedriveConsumerFiles = Environment.GetEnvironmentVariable("OnedriveConsumer");
|
||||
string onedriveCommercialFiles = Environment.GetEnvironmentVariable("OnedriveCommercial");
|
||||
|
||||
// Apparently not everyone has OneDrive shoved onto their system.
|
||||
if ((onedriveFiles is not null && Environment.CurrentDirectory.StartsWithIgnoreCase(onedriveFiles))
|
||||
|| (onedriveConsumerFiles is not null && Environment.CurrentDirectory.StartsWithIgnoreCase(onedriveConsumerFiles))
|
||||
|| (onedriveCommercialFiles is not null && Environment.CurrentDirectory.StartsWithIgnoreCase(onedriveCommercialFiles)))
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run from a OneDrive folder. Exiting...");
|
||||
_ = Win32NativeInterop.MessageBoxA(nint.Zero,
|
||||
"Ryujinx is not intended to be run from a OneDrive folder. Please move it out and relaunch.",
|
||||
$"Ryujinx {Version}", MbIconwarning);
|
||||
return 0;
|
||||
}
|
||||
|
||||
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
|
||||
|
||||
if (Environment.CurrentDirectory.StartsWithIgnoreCase(programFiles) ||
|
||||
Environment.CurrentDirectory.StartsWithIgnoreCase(programFilesX86))
|
||||
{
|
||||
_ = Win32NativeInterop.MessageBoxA(nint.Zero, "Ryujinx is not intended to be run from the Program Files folder. Please move it out and relaunch.", $"Ryujinx {Version}", MbIconwarning);
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run from the Program Files folder. Exiting...");
|
||||
_ = Win32NativeInterop.MessageBoxA(nint.Zero,
|
||||
"Ryujinx is not intended to be run from the Program Files folder. Please move it out and relaunch.",
|
||||
$"Ryujinx {Version}", MbIconwarning);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -74,10 +97,70 @@ namespace Ryujinx.Ava
|
||||
// ...but this reads like it checks if the current is in/has the Windows admin role? lol
|
||||
if (new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator))
|
||||
{
|
||||
_ = Win32NativeInterop.MessageBoxA(nint.Zero, "Ryujinx is not intended to be run as administrator.", $"Ryujinx {Version}", MbIconwarning);
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run as administrator. Exiting...");
|
||||
_ = Win32NativeInterop.MessageBoxA(nint.Zero, "Ryujinx is not intended to be run as administrator.",
|
||||
$"Ryujinx {Version}", MbIconwarning);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else // Unix
|
||||
{
|
||||
// sudo check
|
||||
[DllImport("libc")]
|
||||
static extern uint geteuid();
|
||||
bool root = geteuid().Equals(0);
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
if (root)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run as administrator. Exiting...");
|
||||
macOSNativeInterop.SimpleMessageBox($"Ryujinx {Version}",
|
||||
"Ryujinx is not intended to be run as administrator.", "Ok");
|
||||
return 0;
|
||||
}
|
||||
|
||||
string downloadFiles = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads");
|
||||
|
||||
if (Environment.CurrentDirectory.StartsWithIgnoreCase(downloadFiles))
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run from the Downloads folder. Exiting...");
|
||||
macOSNativeInterop.SimpleMessageBox($"Ryujinx {Version}",
|
||||
"Ryujinx is not intended to be run from the Downloads folder.", "Ok");
|
||||
return 0;
|
||||
}
|
||||
|
||||
string icloudFiles = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library/Mobile Documents/com~apple~CloudDocs");
|
||||
|
||||
if (Environment.CurrentDirectory.StartsWithIgnoreCase(icloudFiles))
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run from the iCloud folder. Exiting...");
|
||||
macOSNativeInterop.SimpleMessageBox($"Ryujinx {Version}",
|
||||
"Ryujinx is not intended to be run from the iCloud folder.", "Ok");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
{
|
||||
if (root)
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Application, "Ryujinx is not intended to be run as administrator. Exiting...");
|
||||
LinuxSDLInterop.SimpleMessageBox($"Ryujinx {Version}", "Ryujinx is not intended to be run as administrator.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
string container = Environment.GetEnvironmentVariable("container");
|
||||
|
||||
if (container is not null && container.EqualsIgnoreCase("flatpak"))
|
||||
{
|
||||
Logger.Info?.PrintMsg(LogClass.Application, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
|
||||
Logger.Warning?.PrintMsg(LogClass.Application, "This is very likely an unofficial build, Ryujinx does NOT have a flatpak!");
|
||||
Logger.Info?.PrintMsg(LogClass.Application, "Please visit https://ryujinx.app/ for our official builds.");
|
||||
Logger.Info?.PrintMsg(LogClass.Application, "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
|
||||
bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps");
|
||||
@@ -310,7 +393,7 @@ namespace Ryujinx.Ava
|
||||
"never" => HideCursorMode.Never,
|
||||
"onidle" => HideCursorMode.OnIdle,
|
||||
"always" => HideCursorMode.Always,
|
||||
_ => ConfigurationState.Instance.HideCursor,
|
||||
_ => ConfigurationState.Instance.HideCursor
|
||||
};
|
||||
|
||||
// Check if memoryManagerMode was overridden.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -333,6 +333,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
EnableRumble = false,
|
||||
StrongRumble = 1f,
|
||||
WeakRumble = 1f,
|
||||
UseHDRumble = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1070,6 +1070,23 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
||||
_ => FormattedValue.ForceReset
|
||||
};
|
||||
|
||||
private static FormattedValue TomodachiLifeLTD_Status(SingleValue value)
|
||||
{
|
||||
MessagePackObject messagePackObject = value.Matched.PackedValue;
|
||||
MessagePackObjectDictionary messagePackObjectDictionary = messagePackObject.AsDictionary();
|
||||
|
||||
int miiCount = messagePackObjectDictionary["MiiNum"].AsInt32();
|
||||
int fountainLevel = messagePackObjectDictionary["FountainLevel"].AsInt32();
|
||||
|
||||
return $"Looking after {"Mii".ToQuantity(miiCount)}, with an island level of {fountainLevel}";
|
||||
}
|
||||
|
||||
private static FormattedValue AnimalCrossingNewHorizons_AppCommon(SingleValue value)
|
||||
{
|
||||
MessagePackObject messagePackObject = value.Matched.PackedValue;
|
||||
MessagePackObjectDictionary messagePackObjectDictionary = messagePackObject.AsDictionary();
|
||||
|
||||
return $"Living on {messagePackObjectDictionary["LandName"].AsString()} Island";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,19 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
||||
"based on what game you first launch.\n\nNSO emulators do not print any Play Report information past the first game launch so it's all we got.")
|
||||
.AddValueFormatter("launch_title_id", NsoEmulator_LaunchedGame)
|
||||
)
|
||||
.AddSpec(
|
||||
[ "010051f0207b2000", "0100ca502552a000" ], // Tomodachi Life: Living the Dream + Demo
|
||||
spec => spec
|
||||
.WithDescription(
|
||||
"based on your total Mii count and island level.")
|
||||
.AddValueFormatter("Common", TomodachiLifeLTD_Status)
|
||||
)
|
||||
.AddSpec(
|
||||
"01006f8002326000", // Animal Crossing New Horizons
|
||||
spec => spec
|
||||
.WithDescription("based on your island name.")
|
||||
.AddValueFormatter("AppCmn", AnimalCrossingNewHorizons_AppCommon)
|
||||
)
|
||||
);
|
||||
|
||||
private static string Playing(string game) => $"Playing {game}";
|
||||
|
||||
30
src/Ryujinx/UI/Helpers/LinuxSDLInterop.cs
Normal file
30
src/Ryujinx/UI/Helpers/LinuxSDLInterop.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
public class LinuxSDLInterop
|
||||
{
|
||||
// TODO: add a parameter for prompt style
|
||||
// TODO: look into adding text for the button
|
||||
// TODO: check success of prompt box
|
||||
public static int SimpleMessageBox(string caption, string text)
|
||||
{
|
||||
const string sdl = "SDL2";
|
||||
|
||||
[DllImport(sdl)]
|
||||
static extern int SDL_Init(uint flags);
|
||||
|
||||
[DllImport(sdl, CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern int SDL_ShowSimpleMessageBox(uint flags, string title, string message, IntPtr window);
|
||||
|
||||
[DllImport(sdl)]
|
||||
static extern void SDL_Quit();
|
||||
|
||||
SDL_Init(0);
|
||||
SDL_ShowSimpleMessageBox(32 /* 32 = warning style */, caption, text, IntPtr.Zero);
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/Ryujinx/UI/Helpers/macOSNativeInterop.cs
Normal file
62
src/Ryujinx/UI/Helpers/macOSNativeInterop.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
public class macOSNativeInterop
|
||||
{
|
||||
// TODO: add a parameter for prompt style
|
||||
// TODO: check success of prompt box
|
||||
public static int SimpleMessageBox(string caption, string text, string button)
|
||||
{
|
||||
|
||||
// Grab what we need to make the message box.
|
||||
const string ObjCRuntime = "/usr/lib/libobjc.A.dylib";
|
||||
const string FoundationFramework = "/System/Library/Frameworks/Foundation.framework/Foundation";
|
||||
const string AppKitFramework = "/System/Library/Frameworks/AppKit.framework/AppKit";
|
||||
|
||||
[DllImport(ObjCRuntime, EntryPoint = "sel_registerName")]
|
||||
static extern IntPtr GetSelector(string name);
|
||||
|
||||
[DllImport(ObjCRuntime, EntryPoint = "objc_getClass")]
|
||||
static extern IntPtr GetClass(string name);
|
||||
|
||||
[DllImport(FoundationFramework, EntryPoint = "objc_msgSend")]
|
||||
static extern IntPtr SendMessage(IntPtr target, IntPtr selector);
|
||||
|
||||
[DllImport(FoundationFramework, EntryPoint = "objc_msgSend")]
|
||||
static extern IntPtr SendMessageWithParameter(IntPtr target, IntPtr selector, IntPtr param);
|
||||
|
||||
[DllImport(ObjCRuntime)]
|
||||
static extern IntPtr dlopen(string path, int mode);
|
||||
|
||||
dlopen(AppKitFramework, 0x1); // have to invoke AppKit so that NSAlert doesn't return a null pointer
|
||||
|
||||
IntPtr NSStringClass = GetClass("NSString");
|
||||
IntPtr Selector = GetSelector("stringWithUTF8String:");
|
||||
IntPtr SharedApp = SendMessage(GetClass("NSApplication"), GetSelector("sharedApplication"));
|
||||
IntPtr NSAlert = SendMessage(GetClass("NSAlert"), GetSelector("alloc"));
|
||||
IntPtr AlertInstance = SendMessage(NSAlert, GetSelector("init"));
|
||||
|
||||
// Create caption, text, and button text.
|
||||
IntPtr boxCaption = SendMessageWithParameter(NSStringClass, Selector, Marshal.StringToHGlobalAnsi(caption));
|
||||
IntPtr boxText = SendMessageWithParameter(NSStringClass, Selector, Marshal.StringToHGlobalAnsi(text));
|
||||
IntPtr boxButton = SendMessageWithParameter(NSStringClass, Selector, Marshal.StringToHGlobalAnsi(button));
|
||||
|
||||
// Set up the window.
|
||||
SendMessageWithParameter(SharedApp, GetSelector("setActivationPolicy:"), IntPtr.Zero); // Give it a window.
|
||||
SendMessageWithParameter(SharedApp, GetSelector("activateIgnoringOtherApps:"), (IntPtr) 1); // Force it to the front.
|
||||
|
||||
// Set up the message box.
|
||||
SendMessageWithParameter(AlertInstance, GetSelector("setAlertStyle:"), IntPtr.Zero); // Set style to warning.
|
||||
SendMessageWithParameter(AlertInstance, GetSelector("setMessageText:"), boxCaption);
|
||||
SendMessageWithParameter(AlertInstance, GetSelector("setInformativeText:"), boxText);
|
||||
SendMessageWithParameter(AlertInstance, GetSelector("addButtonWithTitle:"), boxButton);
|
||||
|
||||
// Send prompt to user, then clean up.
|
||||
SendMessage(AlertInstance, GetSelector("runModal"));
|
||||
SendMessage(AlertInstance, GetSelector("release"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -789,6 +789,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
StrongRumble = 1f,
|
||||
WeakRumble = 1f,
|
||||
EnableRumble = false,
|
||||
UseHDRumble = true
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,5 +9,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
[ObservableProperty]
|
||||
public partial float WeakRumble { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool EnableHDRumble { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user