mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-10-11 01:28:51 +02:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6fdf838a1 | ||
|
|
a398002918 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"file_format_version" : "1.0.0",
|
"file_format_version" : "1.0.0",
|
||||||
"ICD": {
|
"ICD": {
|
||||||
"library_path": "./libMoltenVK.dylib",
|
"library_path": "../../libMoltenVK.dylib",
|
||||||
"api_version" : "1.4.0",
|
"api_version" : "1.4.0",
|
||||||
"is_portability_driver" : true
|
"is_portability_driver" : true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"ICD": {
|
"ICD": {
|
||||||
"api_version": "1.3.0",
|
"api_version": "1.3.0",
|
||||||
"library_path": "./libvulkan_kosmickrisp.dylib"
|
"library_path": "../../libvulkan_kosmickrisp.dylib"
|
||||||
},
|
},
|
||||||
"file_format_version": "1.0.1"
|
"file_format_version": "1.0.1"
|
||||||
}
|
}
|
||||||
@@ -16,10 +16,5 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
|
|||||||
/// Enable Rumble
|
/// Enable Rumble
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableRumble { get; set; }
|
public bool EnableRumble { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enable HD Rumble support
|
|
||||||
/// </summary
|
|
||||||
public bool UseHDRumble { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,13 +126,8 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
public static string[] GetAllAvailableResources(string path, string ext = "")
|
public static string[] GetAllAvailableResources(string path, string ext = "")
|
||||||
{
|
{
|
||||||
(Assembly assembly, string resourcePath) = ResolveManifestPath(path);
|
return ResolveManifestPath(path).Item1.GetManifestResourceNames()
|
||||||
|
.Where(r => r.EndsWith(ext))
|
||||||
string manifestPath = assembly.GetName().Name + "." + resourcePath.Replace('/', '.');
|
|
||||||
|
|
||||||
return assembly.GetManifestResourceNames()
|
|
||||||
.Where(r => r.StartsWith(manifestPath + ".", StringComparison.Ordinal))
|
|
||||||
.Where(r => r.EndsWith(ext, StringComparison.Ordinal))
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1790,7 +1790,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
{
|
{
|
||||||
(_, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
(_, varType) = IoMap.GetSpirvBuiltIn(ioVariable);
|
||||||
|
|
||||||
if (context.Definitions.Stage.IsVtg() && IoMap.IsPerVertexBuiltIn(ioVariable))
|
if (IoMap.IsPerVertexBuiltIn(ioVariable))
|
||||||
{
|
{
|
||||||
perVertexBuiltIn = ioVariable;
|
perVertexBuiltIn = ioVariable;
|
||||||
ioVariable = IoVariable.Position;
|
ioVariable = IoVariable.Position;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
string[] searchPaths =
|
string[] searchPaths =
|
||||||
[
|
[
|
||||||
Path.Combine(contentsDir, "Resources", "vulkan", "icd.d", fileName),
|
Path.Combine(contentsDir, "Resources", "vulkan", "icd.d", fileName),
|
||||||
Path.Combine(contentsDir, fileName)
|
Path.Combine(contentsDir, "vulkan", "icd.d", fileName)
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (string icdPath in searchPaths)
|
foreach (string icdPath in searchPaths)
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||||||
{
|
{
|
||||||
MemoryArrange.MemoryArrange4GiB or
|
MemoryArrange.MemoryArrange4GiB or
|
||||||
MemoryArrange.MemoryArrange4GiBSystemDev or
|
MemoryArrange.MemoryArrange4GiBSystemDev or
|
||||||
MemoryArrange.MemoryArrange6GiBAppletDev => 3173 * MiB,
|
MemoryArrange.MemoryArrange6GiBAppletDev => 3152 * MiB,
|
||||||
MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB,
|
MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB,
|
||||||
MemoryArrange.MemoryArrange6GiB => 4804 * MiB,
|
MemoryArrange.MemoryArrange6GiB => 4783 * MiB,
|
||||||
MemoryArrange.MemoryArrange8GiB => 6852 * MiB,
|
MemoryArrange.MemoryArrange8GiB => 6831 * MiB,
|
||||||
MemoryArrange.MemoryArrange10GiB => 8900 * MiB,
|
MemoryArrange.MemoryArrange10GiB => 8879 * MiB,
|
||||||
MemoryArrange.MemoryArrange12GiB => 10948 * MiB,
|
MemoryArrange.MemoryArrange12GiB => 10927 * MiB,
|
||||||
_ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."),
|
_ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private readonly Dictionary<ulong, List<KThread>> _condVarThreads;
|
private readonly Dictionary<ulong, List<KThread>> _condVarThreads;
|
||||||
private readonly Dictionary<ulong, List<KThread>> _arbiterThreads;
|
private readonly Dictionary<ulong, List<KThread>> _arbiterThreads;
|
||||||
|
private readonly ByDynamicPriority _byDynamicPriority;
|
||||||
|
|
||||||
public KAddressArbiter(KernelContext context)
|
public KAddressArbiter(KernelContext context)
|
||||||
{
|
{
|
||||||
@@ -22,6 +23,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
_condVarThreads = [];
|
_condVarThreads = [];
|
||||||
_arbiterThreads = [];
|
_arbiterThreads = [];
|
||||||
|
_byDynamicPriority = new ByDynamicPriority();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
|
public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle)
|
||||||
@@ -140,9 +142,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
if (_condVarThreads.TryGetValue(condVarAddress, out List<KThread> threads))
|
if (_condVarThreads.TryGetValue(condVarAddress, out List<KThread> threads))
|
||||||
{
|
{
|
||||||
int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread);
|
int i = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if (threads.Count > 0)
|
||||||
|
{
|
||||||
|
i = threads.BinarySearch(currentThread, _byDynamicPriority);
|
||||||
|
if (i < 0) i = ~i;
|
||||||
|
}
|
||||||
|
|
||||||
threads.Insert(i, currentThread);
|
threads.Insert(i, currentThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -325,9 +332,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
|
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
|
||||||
{
|
{
|
||||||
int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread);
|
int i = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if (threads.Count > 0)
|
||||||
|
{
|
||||||
|
i = threads.BinarySearch(currentThread, _byDynamicPriority);
|
||||||
|
if (i < 0) i = ~i;
|
||||||
|
}
|
||||||
|
|
||||||
threads.Insert(i, currentThread);
|
threads.Insert(i, currentThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -412,9 +424,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
|
if (_arbiterThreads.TryGetValue(address, out List<KThread> threads))
|
||||||
{
|
{
|
||||||
int i = FindDynamicPriorityFifoInsertionIndex(threads, currentThread);
|
int i = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if (threads.Count > 0)
|
||||||
|
{
|
||||||
|
i = threads.BinarySearch(currentThread, _byDynamicPriority);
|
||||||
|
if (i < 0) i = ~i;
|
||||||
|
}
|
||||||
|
|
||||||
threads.Insert(i, currentThread);
|
threads.Insert(i, currentThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -610,28 +627,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
return validCount;
|
return validCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int FindDynamicPriorityFifoInsertionIndex(List<KThread> threads, KThread currentThread)
|
private class ByDynamicPriority : IComparer<KThread>
|
||||||
{
|
{
|
||||||
int low = 0;
|
public int Compare(KThread x, KThread y)
|
||||||
int high = threads.Count;
|
|
||||||
|
|
||||||
// Lower numeric values represent higher priorities. Use upper-bound insertion
|
|
||||||
// to preserve FIFO order among waiters with the same dynamic priority.
|
|
||||||
while (low < high)
|
|
||||||
{
|
{
|
||||||
int middle = low + ((high - low) >> 1);
|
return x!.DynamicPriority.CompareTo(y!.DynamicPriority);
|
||||||
|
|
||||||
if (threads[middle].DynamicPriority <= currentThread.DynamicPriority)
|
|
||||||
{
|
|
||||||
low = middle + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
high = middle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return low;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -783,28 +783,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Cheat> cheats = mods.Cheats;
|
List<Cheat> cheats = mods.Cheats;
|
||||||
Dictionary<string, ulong> processExes = new();
|
Dictionary<string, ulong> processExes = tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses, (k, v) => new { k, v })
|
||||||
|
.ToDictionary(x => x.k[..Math.Min(Cheat.CheatIdSize, x.k.Length)], x => x.v);
|
||||||
foreach ((string buildId, ulong codeAddress) in tamperInfo.BuildIds.Zip(tamperInfo.CodeAddresses))
|
|
||||||
{
|
|
||||||
string normalizedBuildId = buildId[..Math.Min(Cheat.CheatIdSize, buildId.Length)];
|
|
||||||
|
|
||||||
if (processExes.TryGetValue(normalizedBuildId, out ulong existingAddress))
|
|
||||||
{
|
|
||||||
if (existingAddress != codeAddress)
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(
|
|
||||||
LogClass.ModLoader,
|
|
||||||
$"Duplicate BuildId prefix '{normalizedBuildId}' has different code addresses. " +
|
|
||||||
$"Existing: 0x{existingAddress:X}, duplicate: 0x{codeAddress:X}. " +
|
|
||||||
$"Keeping the first one.");
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
processExes.Add(normalizedBuildId, codeAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Cheat cheat in cheats)
|
foreach (Cheat cheat in cheats)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,14 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
return _applicationServiceServer.IsUserRegistrationRequestPermitted(context);
|
return _applicationServiceServer.IsUserRegistrationRequestPermitted(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandCmif(51)] // 1.0.0 – 18.1.0
|
[CommandCmif(51)]
|
||||||
// TrySelectUserWithoutInteraction(bool) -> nn::account::Uid
|
|
||||||
public ResultCode TrySelectUserWithoutInteractionDeprecated(ServiceCtx context)
|
|
||||||
{
|
|
||||||
return _applicationServiceServer.TrySelectUserWithoutInteraction(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(52)] // 19.0.0+
|
|
||||||
// TrySelectUserWithoutInteraction(bool) -> nn::account::Uid
|
// TrySelectUserWithoutInteraction(bool) -> nn::account::Uid
|
||||||
public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context)
|
public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
private readonly bool[] _supportedPlayers;
|
private readonly bool[] _supportedPlayers;
|
||||||
private VibrationValue _neutralVibrationValue = new()
|
private VibrationValue _neutralVibrationValue = new()
|
||||||
{
|
{
|
||||||
AmplitudeLow = 0.01f,
|
AmplitudeLow = 0f,
|
||||||
FrequencyLow = 160f,
|
FrequencyLow = 160f,
|
||||||
AmplitudeHigh = 0f,
|
AmplitudeHigh = 0f,
|
||||||
FrequencyHigh = 320f,
|
FrequencyHigh = 320f,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using SDL;
|
using SDL;
|
||||||
using static SDL.SDL3;
|
using static SDL.SDL3;
|
||||||
@@ -13,276 +12,140 @@ namespace Ryujinx.Input.SDL3
|
|||||||
{
|
{
|
||||||
private readonly SDL_hid_device* _hidHandle;
|
private readonly SDL_hid_device* _hidHandle;
|
||||||
|
|
||||||
private byte[] _buffer;
|
|
||||||
private static ushort _vendor;
|
|
||||||
private static ushort _product;
|
|
||||||
|
|
||||||
private int _globalCount;
|
private int _globalCount;
|
||||||
private ulong _lastWriteTicks;
|
|
||||||
|
|
||||||
private NpadHdRumble(SDL_hid_device* hidHandle)
|
private NpadHdRumble(SDL_hid_device* hidHandle)
|
||||||
{
|
{
|
||||||
_hidHandle = hidHandle;
|
_hidHandle = hidHandle;
|
||||||
InitializeDevice();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NpadHdRumble Create(SDL_Gamepad* gamepadHandle)
|
public static NpadHdRumble Create(SDL_Gamepad* gamepadHandle)
|
||||||
{
|
{
|
||||||
_vendor = SDL_GetGamepadVendor(gamepadHandle);
|
ushort vendor = SDL_GetGamepadVendor(gamepadHandle);
|
||||||
if (!Enum.IsDefined(typeof(HDRumbleSupportedVendor), _vendor))
|
if (vendor != 0x057e)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_product = SDL_GetGamepadProduct(gamepadHandle);
|
ushort product = SDL_GetGamepadProduct(gamepadHandle);
|
||||||
if (!Enum.IsDefined(typeof(HDRumbleSupportedProduct), _product))
|
if (product != 0x2006 && product != 0x2007 && product != 0x2009 && product != 0x200e)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int serialNumber = 0;
|
return new NpadHdRumble(SDL_hid_open(vendor, product, 0));
|
||||||
string? serial = SDL_GetGamepadSerial(gamepadHandle);
|
|
||||||
if (serial is not null)
|
|
||||||
{
|
|
||||||
int.TryParse(serial, out serialNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new NpadHdRumble(SDL_hid_open(_vendor, _product, serialNumber));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some of the code was translated from https://github.com/MIZUSHIKI/JoyShockLibrary-plus-HDRumble
|
// Some of the code was translated from https://github.com/MIZUSHIKI/JoyShockLibrary-plus-HDRumble
|
||||||
private bool WriteNintendoHdRumble(VibrationValue left, VibrationValue right)
|
private void WriteHdRumble(
|
||||||
|
int encLeftLowFreq, int encLeftLowAmp,
|
||||||
|
int encLeftHighFreq, int encLeftHighAmp,
|
||||||
|
int encRightLowFreq, int encRightLowAmp,
|
||||||
|
int encRightHighFreq, int encRightHighAmp)
|
||||||
{
|
{
|
||||||
int leftLowAmp = EncodeLowAmp(left.AmplitudeLow);
|
byte[] buf = new byte[10];
|
||||||
int leftLowFreq = EncodeLowFreq(left.FrequencyLow) + (leftLowAmp >> 8);
|
|
||||||
int leftHighFreq = EncodeHighFreq(left.FrequencyHigh);
|
buf[0] = 0x10;
|
||||||
int leftHighAmp = EncodeHighAmp(left.AmplitudeHigh) + (leftHighFreq >> 8);
|
buf[1] = (byte)((++_globalCount) & 0xF);
|
||||||
|
|
||||||
int rightLowAmp = EncodeLowAmp(right.AmplitudeLow);
|
buf[2] = (byte)(encLeftHighFreq & 0xFF);
|
||||||
int rightLowFreq = EncodeLowFreq(right.FrequencyLow) + (rightLowAmp >> 8);
|
buf[3] = (byte)(encLeftHighAmp + ((encLeftHighFreq >> 8) & 0xFF));
|
||||||
int rightHighFreq = EncodeHighFreq(right.FrequencyHigh);
|
buf[4] = (byte)(encLeftLowFreq + ((encLeftLowAmp >> 8) & 0xFF));
|
||||||
int rightHighAmp = EncodeHighAmp(right.AmplitudeHigh) + (rightHighFreq >> 8);
|
buf[5] = (byte)(encLeftLowAmp & 0xFF);
|
||||||
|
|
||||||
_buffer[0] = 0x10;
|
buf[6] = (byte)(encRightHighFreq & 0xFF);
|
||||||
_buffer[1] = (byte)((_globalCount++) & 0xF);
|
buf[7] = (byte)(encRightHighAmp + ((encRightHighFreq >> 8) & 0xFF));
|
||||||
|
buf[8] = (byte)(encRightLowFreq + ((encRightLowAmp >> 8) & 0xFF));
|
||||||
// Left LRA
|
buf[9] = (byte)(encRightLowAmp & 0xFF);
|
||||||
_buffer[2] = (byte)(leftLowFreq & 0xFF);
|
|
||||||
_buffer[3] = (byte)(leftHighAmp & 0xFF);
|
|
||||||
_buffer[4] = (byte)(leftHighFreq & 0xFF);
|
|
||||||
_buffer[5] = (byte)(leftLowAmp & 0xFF);
|
|
||||||
|
|
||||||
// Right LRA
|
|
||||||
_buffer[6] = (byte)(rightLowFreq & 0xFF);
|
|
||||||
_buffer[7] = (byte)(rightHighAmp & 0xFF);
|
|
||||||
_buffer[8] = (byte)(rightHighFreq & 0xFF);
|
|
||||||
_buffer[9] = (byte)(rightLowAmp & 0xFF);
|
|
||||||
|
|
||||||
if (_globalCount > 0xF)
|
if (_globalCount > 0xF)
|
||||||
{
|
{
|
||||||
_globalCount = 0x0;
|
_globalCount = 0x0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* ptr = _buffer)
|
fixed (byte* ptr = buf)
|
||||||
{
|
{
|
||||||
if (SendHdRumble(ptr, (nuint)_buffer.Length) >= 0)
|
SDL_hid_write(_hidHandle, ptr, (nuint)buf.Length);
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
|
|
||||||
SDL_ClearError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static int EncodeLowFreq(float lowFreq)
|
private static int EncodeLowFreq(float lowFreq)
|
||||||
{
|
{
|
||||||
return (int)Math.Clamp(32 * Math.Log2(lowFreq * 0.1f) - 0x40, 81.75177f, 1252.572266f);
|
float lf = Math.Clamp(lowFreq, 40.875885f, 626.286133f);
|
||||||
|
return (int)Math.Round(32 * Math.Log2(lf * 0.1f)) - 0x40;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int EncodeHighFreq(float highFreq)
|
private static int EncodeHighFreq(float highFreq)
|
||||||
{
|
{
|
||||||
return (int)Math.Clamp(32 * Math.Log2(highFreq * 0.1f) - 0x60, 81.75177f, 1252.572266f);
|
float hf = Math.Clamp(highFreq, 81.75177f, 1252.572266f);
|
||||||
|
return ((int)Math.Round(32 * Math.Log2(hf * 0.1f)) - 0x60) * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int EncodeLowAmp(float rawAmp)
|
private static int EncodeLowAmp(float rawAmp)
|
||||||
{
|
{
|
||||||
double encodedAmp = 0;
|
int encodedAmp = 0;
|
||||||
|
|
||||||
if (rawAmp is > 0 and < 0.012f)
|
if (rawAmp is > 0 and < 0.012f)
|
||||||
|
{
|
||||||
encodedAmp = 1;
|
encodedAmp = 1;
|
||||||
|
}
|
||||||
else if (rawAmp is >= 0.012f and < 0.112f)
|
else if (rawAmp is >= 0.012f and < 0.112f)
|
||||||
encodedAmp = 4 * Math.Log2(rawAmp * 110f);
|
{
|
||||||
|
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
|
||||||
|
}
|
||||||
else if (rawAmp is >= 0.112f and < 0.225f)
|
else if (rawAmp is >= 0.112f and < 0.225f)
|
||||||
encodedAmp = 16 * Math.Log2(rawAmp * 17f);
|
{
|
||||||
|
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
|
||||||
|
}
|
||||||
else if (rawAmp is >= 0.225f and <= 1f)
|
else if (rawAmp is >= 0.225f and <= 1f)
|
||||||
encodedAmp = 32 * Math.Log2(rawAmp * 8.7f);
|
{
|
||||||
|
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
|
||||||
encodedAmp = Math.Round((encodedAmp / 2.0) + 64.0);
|
}
|
||||||
encodedAmp = Math.Clamp(encodedAmp, 0.0, 100.2867);
|
|
||||||
return (int)Math.Round(encodedAmp);
|
return (int)Math.Floor(encodedAmp / 2.0) + 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int EncodeHighAmp(float rawAmp)
|
private static int EncodeHighAmp(float rawAmp)
|
||||||
{
|
{
|
||||||
double encodedAmp = 0;
|
int encodedAmp = 0;
|
||||||
|
|
||||||
if (rawAmp is > 0 and < 0.012f)
|
if (rawAmp is > 0 and < 0.012f)
|
||||||
|
{
|
||||||
encodedAmp = 1;
|
encodedAmp = 1;
|
||||||
|
}
|
||||||
else if (rawAmp is >= 0.012f and < 0.112f)
|
else if (rawAmp is >= 0.012f and < 0.112f)
|
||||||
encodedAmp = 4 * Math.Log2(rawAmp * 110f);
|
{
|
||||||
|
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
|
||||||
|
}
|
||||||
else if (rawAmp is >= 0.112f and < 0.225f)
|
else if (rawAmp is >= 0.112f and < 0.225f)
|
||||||
encodedAmp = 16 * Math.Log2(rawAmp * 17f);
|
{
|
||||||
|
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
|
||||||
|
}
|
||||||
else if (rawAmp is >= 0.225f and <= 1f)
|
else if (rawAmp is >= 0.225f and <= 1f)
|
||||||
encodedAmp = 32 * Math.Log2(rawAmp * 8.7f);
|
{
|
||||||
|
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
|
||||||
encodedAmp = Math.Round(encodedAmp / 2.0);
|
}
|
||||||
encodedAmp = Math.Clamp(encodedAmp, 0.0, 100.2867);
|
|
||||||
return (int)encodedAmp;
|
return encodedAmp * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HdRumble(VibrationValue left, VibrationValue right)
|
public bool HdRumble(VibrationValue left, VibrationValue right)
|
||||||
{
|
{
|
||||||
if(_product is (ushort) HDRumbleSupportedProduct.ProController
|
WriteHdRumble(EncodeLowFreq(left.FrequencyLow),
|
||||||
or (ushort) HDRumbleSupportedProduct.JoyconLeft
|
EncodeLowAmp(left.AmplitudeLow),
|
||||||
or (ushort) HDRumbleSupportedProduct.JoyconRight
|
EncodeHighFreq(left.FrequencyHigh),
|
||||||
or (ushort) HDRumbleSupportedProduct.JoyconPair
|
EncodeHighAmp(left.AmplitudeHigh),
|
||||||
or (ushort) HDRumbleSupportedProduct.JoyconGrip)
|
EncodeLowFreq(right.FrequencyLow),
|
||||||
{
|
EncodeLowAmp(right.AmplitudeLow),
|
||||||
return WriteNintendoHdRumble(left, right);
|
EncodeHighFreq(right.FrequencyHigh),
|
||||||
}
|
EncodeHighAmp(right.AmplitudeHigh));
|
||||||
|
return true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
if ((currentTicks - _lastWriteTicks) <= GetPollRate())
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = SDL_hid_write(_hidHandle, data, length);
|
|
||||||
if (result >= 0)
|
|
||||||
{
|
|
||||||
_lastWriteTicks = currentTicks;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeDevice()
|
|
||||||
{
|
|
||||||
if (_vendor is (ushort)HDRumbleSupportedVendor.Nintendo)
|
|
||||||
{
|
|
||||||
_buffer = new byte[10];
|
|
||||||
byte[] init = new byte[64];
|
|
||||||
|
|
||||||
// Pro Controller and Charge Grip
|
|
||||||
if (_product
|
|
||||||
is (ushort)HDRumbleSupportedProduct.ProController
|
|
||||||
or (ushort)HDRumbleSupportedProduct.JoyconGrip)
|
|
||||||
{
|
|
||||||
SDL_LockJoysticks();
|
|
||||||
fixed (byte* ptr = init)
|
|
||||||
{
|
|
||||||
init[0] = 0x80;
|
|
||||||
init[1] = 0x05; // Allow bluetooth timeout TODO: use 0x04 to force USB only (toggle?)
|
|
||||||
SDL_hid_write(_hidHandle, ptr, 64);
|
|
||||||
}
|
|
||||||
SDL_UnlockJoysticks();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Joycons
|
|
||||||
if (_product
|
|
||||||
is (ushort)HDRumbleSupportedProduct.JoyconLeft
|
|
||||||
or (ushort)HDRumbleSupportedProduct.JoyconRight
|
|
||||||
or (ushort)HDRumbleSupportedProduct.JoyconPair)
|
|
||||||
{
|
|
||||||
|
|
||||||
SDL_LockJoysticks();
|
|
||||||
fixed (byte* ptr = init)
|
|
||||||
{
|
|
||||||
// we could write data to the controller here (see above)
|
|
||||||
}
|
|
||||||
SDL_UnlockJoysticks();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong GetPollRate()
|
|
||||||
{
|
|
||||||
ulong pollRate = 0;
|
|
||||||
if (_vendor is (ushort)HDRumbleSupportedVendor.Nintendo)
|
|
||||||
{
|
|
||||||
// https://docs.handheldlegend.com/s/progcc-3/doc/lag-comparison-aAR1mV3JLX
|
|
||||||
pollRate = (ulong) 16.67;
|
|
||||||
if (_product is (ushort)HDRumbleSupportedProduct.ProController
|
|
||||||
&& SDL_hid_get_device_info(_hidHandle)->bus_type == SDL_hid_bus_type.SDL_HID_API_BUS_USB)
|
|
||||||
{
|
|
||||||
pollRate = (ulong) 8.33;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pollRate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
SDL_hid_close(_hidHandle);
|
SDL_hid_close(_hidHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum HDRumbleSupportedVendor : ushort
|
|
||||||
{
|
|
||||||
Nintendo = 0x057e,
|
|
||||||
Valve = 0x28de,
|
|
||||||
Sony = 0x054c
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum HDRumbleSupportedProduct : ushort
|
|
||||||
{
|
|
||||||
// TODO: Currently, HD Rumble only supports the Pro Controller and JoyCons.
|
|
||||||
// We need to initialize and report to each device differently.
|
|
||||||
|
|
||||||
// Nintendo Switch: 0x057e
|
|
||||||
JoyconLeft = 0x2006,
|
|
||||||
JoyconRight = 0x2007,
|
|
||||||
JoyconPair = 0x2008,
|
|
||||||
ProController = 0x2009,
|
|
||||||
JoyconGrip = 0x200e,
|
|
||||||
|
|
||||||
// Nintendo Switch 2: 0x057e
|
|
||||||
Joycon2Right = 0x2066,
|
|
||||||
Joycon2Left = 0x2067,
|
|
||||||
Joycon2Pair = 0x2068,
|
|
||||||
Switch2ProController = 0x2069,
|
|
||||||
GamecubeController = 0x2073,
|
|
||||||
|
|
||||||
// Valve Steam Family: 0x28de
|
|
||||||
// https://github.com/libsdl-org/SDL/issues/9148
|
|
||||||
SteamDeck = 0x11ff,
|
|
||||||
SteamDeckVirtualDevice = 0x1205,
|
|
||||||
SteamController = 0x1106,
|
|
||||||
|
|
||||||
// PlayStation Dualsense: 0x054c
|
|
||||||
Dualsense = 0x0ce6
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,12 +177,10 @@ namespace Ryujinx.Input.SDL3
|
|||||||
return _hdRumble?.HdRumble(left, right) ?? false;
|
return _hdRumble?.HdRumble(left, right) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
{
|
||||||
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
||||||
{
|
return;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
||||||
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
|
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
|
||||||
@@ -201,15 +199,6 @@ namespace Ryujinx.Input.SDL3
|
|||||||
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
|
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
|
||||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
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)
|
public Vector3 GetMotionData(MotionInputId inputId)
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ namespace Ryujinx.Input.SDL3
|
|||||||
|
|
||||||
public void SetTriggerThreshold(float triggerThreshold)
|
public void SetTriggerThreshold(float triggerThreshold)
|
||||||
{
|
{
|
||||||
// No operations
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HDRumble(VibrationValue left, VibrationValue right)
|
public bool HDRumble(VibrationValue left, VibrationValue right)
|
||||||
@@ -171,12 +171,10 @@ namespace Ryujinx.Input.SDL3
|
|||||||
return _hdRumble?.HdRumble(left, right) ?? false;
|
return _hdRumble?.HdRumble(left, right) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
{
|
||||||
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
||||||
{
|
return;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
||||||
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
|
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
|
||||||
@@ -195,15 +193,6 @@ namespace Ryujinx.Input.SDL3
|
|||||||
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
|
if (!SDL_RumbleGamepad(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs))
|
||||||
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
|
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)
|
public Vector3 GetMotionData(MotionInputId inputId)
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using Gommon;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@@ -64,14 +61,7 @@ namespace Ryujinx.Input.SDL3
|
|||||||
return left.IsPressed(inputId) || right.IsPressed(inputId);
|
return left.IsPressed(inputId) || right.IsPressed(inputId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HDRumble(VibrationValue left, VibrationValue right)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
|
||||||
// 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)
|
if (lowFrequency != 0)
|
||||||
{
|
{
|
||||||
@@ -88,15 +78,6 @@ namespace Ryujinx.Input.SDL3
|
|||||||
left.Rumble(0, 0, durationMs);
|
left.Rumble(0, 0, durationMs);
|
||||||
right.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)
|
public void SetConfiguration(InputConfig configuration)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
using SDL;
|
using SDL;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@@ -386,14 +385,9 @@ namespace Ryujinx.Input.SDL3
|
|||||||
// No operations
|
// No operations
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HDRumble(VibrationValue left, VibrationValue right)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
{
|
||||||
return false;
|
// No operations
|
||||||
}
|
|
||||||
|
|
||||||
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector3 GetMotionData(MotionInputId inputId)
|
public Vector3 GetMotionData(MotionInputId inputId)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@@ -67,12 +66,7 @@ namespace Ryujinx.Input.SDL3
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HDRumble(VibrationValue left, VibrationValue right)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -554,37 +554,34 @@ namespace Ryujinx.Input.HLE
|
|||||||
{
|
{
|
||||||
if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue))
|
if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue))
|
||||||
{
|
{
|
||||||
if (_config is not StandardControllerInputConfig controllerConfig ||
|
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble)
|
||||||
!controllerConfig.Rumble.EnableRumble)
|
|
||||||
{
|
{
|
||||||
return;
|
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}");
|
||||||
}
|
}
|
||||||
|
|
||||||
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.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({leftVibrationValue.AmplitudeHigh}), " +
|
|
||||||
$"L.low.freq={leftVibrationValue.FrequencyLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.FrequencyLow}), " +
|
|
||||||
$"L.high.freq={leftVibrationValue.FrequencyHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({leftVibrationValue.FrequencyHigh}), " +
|
|
||||||
$"R.low.amp={rightVibrationValue.AmplitudeLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({rightVibrationValue.AmplitudeLow}), " +
|
|
||||||
$"R.high.amp={rightVibrationValue.AmplitudeHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.AmplitudeHigh}), " +
|
|
||||||
$"R.low.freq={rightVibrationValue.FrequencyLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({rightVibrationValue.FrequencyLow}), " +
|
|
||||||
$"R.high.freq={rightVibrationValue.FrequencyHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.FrequencyHigh})");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,10 @@ namespace Ryujinx.Input
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="left">The vibration data for the left side</param>
|
/// <param name="left">The vibration data for the left side</param>
|
||||||
/// <param name="right">The vibration data for the right side</param>
|
/// <param name="right">The vibration data for the right side</param>
|
||||||
bool HDRumble(VibrationValue left, VibrationValue right);
|
bool HDRumble(VibrationValue left, VibrationValue right)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts a rumble effect on the gamepad.
|
/// Starts a rumble effect on the gamepad.
|
||||||
@@ -79,10 +82,10 @@ namespace Ryujinx.Input
|
|||||||
/// <param name="lowFrequency">The intensity of the low frequency from 0.0f to 1.0f</param>
|
/// <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="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>
|
/// <param name="durationMs">The duration of the rumble effect in milliseconds.</param>
|
||||||
bool Rumble(float lowFrequency, float highFrequency, uint durationMs);
|
void Rumble(float lowFrequency, float highFrequency, uint durationMs);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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)"/>.
|
/// 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)"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A remapped snaphost of the state of the gamepad.</returns>
|
/// <returns>A remapped snaphost of the state of the gamepad.</returns>
|
||||||
GamepadStateSnapshot GetMappedStateSnapshot();
|
GamepadStateSnapshot GetMappedStateSnapshot();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 61;
|
public const int CurrentVersion = 62;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the configuration file format
|
/// Version of the configuration file format
|
||||||
|
|||||||
@@ -1431,7 +1431,6 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
EnableRumble = false,
|
EnableRumble = false,
|
||||||
StrongRumble = 1f,
|
StrongRumble = 1f,
|
||||||
WeakRumble = 1f,
|
WeakRumble = 1f,
|
||||||
UseHDRumble = false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -464,8 +464,6 @@
|
|||||||
"ControllerSettingsRumble": "Rumble",
|
"ControllerSettingsRumble": "Rumble",
|
||||||
"ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier",
|
"ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier",
|
||||||
"ControllerSettingsRumbleWeakMultiplier": "Weak Rumble Multiplier",
|
"ControllerSettingsRumbleWeakMultiplier": "Weak Rumble Multiplier",
|
||||||
"ControllerSettingsRumbleUseHDRumble": "Enable HD Rumble",
|
|
||||||
"HDRumbleTooltip": "EXPERIMENTAL.\n\nSends more data to the controller for better rumble.\n\nCurrently only supports first-party Nintendo Switch controllers.\n\nLeave OFF if unsure.",
|
|
||||||
"DialogMessageSaveNotAvailableMessage": "There is no savedata for {0} [{1:x16}]",
|
"DialogMessageSaveNotAvailableMessage": "There is no savedata for {0} [{1:x16}]",
|
||||||
"DialogMessageSaveNotAvailableCreateSaveMessage": "Would you like to create savedata for this game?",
|
"DialogMessageSaveNotAvailableCreateSaveMessage": "Would you like to create savedata for this game?",
|
||||||
"DialogConfirmationTitle": "Ryujinx - Confirmation",
|
"DialogConfirmationTitle": "Ryujinx - Confirmation",
|
||||||
|
|||||||
@@ -223,7 +223,6 @@ namespace Ryujinx.Headless
|
|||||||
StrongRumble = 1f,
|
StrongRumble = 1f,
|
||||||
WeakRumble = 1f,
|
WeakRumble = 1f,
|
||||||
EnableRumble = false,
|
EnableRumble = false,
|
||||||
UseHDRumble = true
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -150,20 +149,9 @@ namespace Ryujinx.Ava.Input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTriggerThreshold(float triggerThreshold)
|
public void SetTriggerThreshold(float triggerThreshold) { }
|
||||||
{
|
|
||||||
// No operations
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HDRumble(VibrationValue left, VibrationValue right)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { }
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3 GetMotionData(MotionInputId inputId) => Vector3.Zero;
|
public Vector3 GetMotionData(MotionInputId inputId) => Vector3.Zero;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
@@ -64,13 +63,8 @@ namespace Ryujinx.Ava.Input
|
|||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HDRumble(VibrationValue left, VibrationValue right)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,17 +91,15 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64' OR '$(RuntimeIdentifier)' == 'osx-x64'">
|
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64' OR '$(RuntimeIdentifier)' == 'osx-x64'">
|
||||||
<Content Include="..\..\distribution\macos\vulkan\icd.d\MoltenVK_icd-flat.json">
|
<Content Include="..\..\distribution\macos\vulkan\icd.d\MoltenVK_icd-flat.json"
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
CopyToOutputDirectory="PreserveNewest"
|
||||||
<TargetPath>MoltenVK_icd.json</TargetPath>
|
Link="vulkan\icd.d\MoltenVK_icd.json" />
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">
|
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">
|
||||||
<Content Include="..\..\distribution\macos\vulkan\icd.d\libkosmickrisp_icd-flat.json">
|
<Content Include="..\..\distribution\macos\vulkan\icd.d\libkosmickrisp_icd-flat.json"
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
CopyToOutputDirectory="PreserveNewest"
|
||||||
<TargetPath>libkosmickrisp_icd.json</TargetPath>
|
Link="vulkan\icd.d\libkosmickrisp_icd.json" />
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'" >
|
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64' OR '$(RuntimeIdentifier)' == 'linux-arm64'" >
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
|
|
||||||
public float WeakRumble { get; set; }
|
public float WeakRumble { get; set; }
|
||||||
public float StrongRumble { get; set; }
|
public float StrongRumble { get; set; }
|
||||||
public bool UseHDRumble { get; set; }
|
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public ControllerType ControllerType { get; set; }
|
public ControllerType ControllerType { get; set; }
|
||||||
@@ -203,7 +202,6 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
EnableRumble = controllerInput.Rumble.EnableRumble;
|
EnableRumble = controllerInput.Rumble.EnableRumble;
|
||||||
WeakRumble = controllerInput.Rumble.WeakRumble;
|
WeakRumble = controllerInput.Rumble.WeakRumble;
|
||||||
StrongRumble = controllerInput.Rumble.StrongRumble;
|
StrongRumble = controllerInput.Rumble.StrongRumble;
|
||||||
UseHDRumble = controllerInput.Rumble.UseHDRumble;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,7 +259,6 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
EnableRumble = EnableRumble,
|
EnableRumble = EnableRumble,
|
||||||
WeakRumble = WeakRumble,
|
WeakRumble = WeakRumble,
|
||||||
StrongRumble = StrongRumble,
|
StrongRumble = StrongRumble,
|
||||||
UseHDRumble = UseHDRumble,
|
|
||||||
},
|
},
|
||||||
Version = InputConfig.CurrentVersion,
|
Version = InputConfig.CurrentVersion,
|
||||||
DeadzoneLeft = DeadzoneLeft,
|
DeadzoneLeft = DeadzoneLeft,
|
||||||
|
|||||||
@@ -678,7 +678,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
StrongRumble = 1f,
|
StrongRumble = 1f,
|
||||||
WeakRumble = 1f,
|
WeakRumble = 1f,
|
||||||
EnableRumble = false,
|
EnableRumble = false,
|
||||||
UseHDRumble = false
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
public partial class RumbleInputViewModel : BaseModel
|
public partial class RumbleInputViewModel : BaseModel
|
||||||
{
|
{
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial float StrongRumble { get; set; }
|
private float _strongRumble;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial float WeakRumble { get; set; }
|
private float _weakRumble;
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
public partial bool EnableHDRumble { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,15 +55,6 @@
|
|||||||
Margin="5,0"
|
Margin="5,0"
|
||||||
Text="{Binding WeakRumble, StringFormat=\{0:0.00\}}" />
|
Text="{Binding WeakRumble, StringFormat=\{0:0.00\}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<CheckBox
|
|
||||||
Margin="5"
|
|
||||||
IsChecked="{Binding EnableHDRumble}">
|
|
||||||
<TextBlock
|
|
||||||
Margin="0,3,0,0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{locale:Locale ControllerSettingsRumbleUseHDRumble}"
|
|
||||||
ToolTip.Tip="{locale:Locale HDRumbleTooltip}" />
|
|
||||||
</CheckBox>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
{
|
{
|
||||||
StrongRumble = config.StrongRumble,
|
StrongRumble = config.StrongRumble,
|
||||||
WeakRumble = config.WeakRumble,
|
WeakRumble = config.WeakRumble,
|
||||||
EnableHDRumble = config.UseHDRumble
|
|
||||||
};
|
};
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -50,7 +49,6 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
GamepadInputConfig config = viewModel.Config;
|
GamepadInputConfig config = viewModel.Config;
|
||||||
config.StrongRumble = content._viewModel.StrongRumble;
|
config.StrongRumble = content._viewModel.StrongRumble;
|
||||||
config.WeakRumble = content._viewModel.WeakRumble;
|
config.WeakRumble = content._viewModel.WeakRumble;
|
||||||
config.UseHDRumble = content._viewModel.EnableHDRumble;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await contentDialog.ShowAsync();
|
await contentDialog.ShowAsync();
|
||||||
|
|||||||
Reference in New Issue
Block a user