From e9ecbd44fcb7763369c2186c4d673f3deb8e3538 Mon Sep 17 00:00:00 2001 From: madwind Date: Mon, 23 Dec 2024 17:57:55 +0800 Subject: [PATCH 01/10] Add a virtual controller to merge Joy-Cons. --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 20 +- src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs | 229 ++++++++++++++++++++ src/Ryujinx.Input/HLE/NpadController.cs | 19 +- src/Ryujinx.Input/MotionInputId.cs | 12 + 4 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index c580e4e7d..eefae8fb4 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -1,6 +1,7 @@ using Ryujinx.SDL2.Common; using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using static SDL2.SDL; @@ -11,7 +12,7 @@ namespace Ryujinx.Input.SDL2 private readonly Dictionary _gamepadsInstanceIdsMapping; private readonly List _gamepadsIds; private readonly Lock _lock = new(); - + private readonly SDL2JoyConPair joyConPair; public ReadOnlySpan GamepadsIds { get @@ -36,7 +37,7 @@ namespace Ryujinx.Input.SDL2 SDL2Driver.Instance.Initialize(); SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected; SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected; - + joyConPair = new SDL2JoyConPair(); // Add already connected gamepads int numJoysticks = SDL_NumJoysticks(); @@ -89,6 +90,10 @@ namespace Ryujinx.Input.SDL2 lock (_lock) { _gamepadsIds.Remove(id); + if (joyConPair.GetJoyConPair(_gamepadsIds) == null) + { + _gamepadsIds.Remove(joyConPair.Id); + } } OnGamepadDisconnected?.Invoke(id); @@ -120,8 +125,12 @@ namespace Ryujinx.Input.SDL2 _gamepadsIds.Insert(joystickDeviceId, id); else _gamepadsIds.Add(id); + if (joyConPair.GetJoyConPair(_gamepadsIds) != null) + { + _gamepadsIds.Remove(joyConPair.Id); + _gamepadsIds.Add(joyConPair.Id); + } } - OnGamepadConnected?.Invoke(id); } } @@ -157,6 +166,11 @@ namespace Ryujinx.Input.SDL2 public IGamepad GetGamepad(string id) { + if (id == joyConPair.Id) + { + return joyConPair.GetJoyConPair(_gamepadsIds); + } + int joystickIndex = GetJoystickIndexByGamepadId(id); if (joystickIndex == -1) diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs new file mode 100644 index 000000000..e3ca336df --- /dev/null +++ b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs @@ -0,0 +1,229 @@ +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using static SDL2.SDL; + +namespace Ryujinx.Input.SDL2 +{ + internal class SDL2JoyConPair : IGamepad + { + private IGamepad _left; + private IGamepad _right; + + private StandardControllerInputConfig _configuration; + private readonly StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count] +{ + StickInputId.Unbound, + StickInputId.Left, + StickInputId.Right, +}; + private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From) + { + public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound; + } + + private readonly List _buttonsUserMapping; + public SDL2JoyConPair() + { + _buttonsUserMapping = new List(20); + } + + private readonly object _userMappingLock = new(); + + public GamepadFeaturesFlag Features => (_left?.Features ?? GamepadFeaturesFlag.None) | (_right?.Features ?? GamepadFeaturesFlag.None); + + public string Id => "JoyConPair"; + + public string Name => "Nintendo Switch Joy-Con (L/R)"; + private static readonly string leftName = "Nintendo Switch Joy-Con (L)"; + private static readonly string rightName = "Nintendo Switch Joy-Con (R)"; + public bool IsConnected => (_left != null && _left.IsConnected) && (_right != null && _right.IsConnected); + + public void Dispose() + { + _left?.Dispose(); + _right?.Dispose(); + } + public GamepadStateSnapshot GetMappedStateSnapshot() + { + GamepadStateSnapshot rawState = GetStateSnapshot(); + GamepadStateSnapshot result = default; + + lock (_userMappingLock) + { + if (_buttonsUserMapping.Count == 0) + return rawState; + + + // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator + foreach (ButtonMappingEntry entry in _buttonsUserMapping) + { + if (!entry.IsValid) + continue; + + // Do not touch state of button already pressed + if (!result.IsPressed(entry.To)) + { + result.SetPressed(entry.To, rawState.IsPressed(entry.From)); + } + } + + (float leftStickX, float leftStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Left]); + (float rightStickX, float rightStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Right]); + + result.SetStick(StickInputId.Left, leftStickX, leftStickY); + result.SetStick(StickInputId.Right, rightStickX, rightStickY); + } + + return result; + } + + public Vector3 GetMotionData(MotionInputId inputId) + { + return inputId switch + { + MotionInputId.SecondAccelerometer => _right.GetMotionData(MotionInputId.Accelerometer), + MotionInputId.SecondGyroscope => _right.GetMotionData(MotionInputId.Gyroscope), + _ => _left.GetMotionData(inputId) + }; + } + + public GamepadStateSnapshot GetStateSnapshot() + { + return IGamepad.GetStateSnapshot(this); + } + + public (float, float) GetStick(StickInputId inputId) + { + if (inputId == StickInputId.Left) + { + (float x, float y) = _left.GetStick(StickInputId.Left); + return (y, -x); + } + else if (inputId == StickInputId.Right) + { + (float x, float y) = _right.GetStick(StickInputId.Left); + return (-y, x); + } + return (0, 0); + } + + public bool IsPressed(GamepadButtonInputId inputId) + { + return inputId switch + { + GamepadButtonInputId.LeftStick => _left.IsPressed(GamepadButtonInputId.LeftStick), + GamepadButtonInputId.DpadUp => _left.IsPressed(GamepadButtonInputId.Y), + GamepadButtonInputId.DpadDown => _left.IsPressed(GamepadButtonInputId.A), + GamepadButtonInputId.DpadLeft => _left.IsPressed(GamepadButtonInputId.B), + GamepadButtonInputId.DpadRight => _left.IsPressed(GamepadButtonInputId.X), + GamepadButtonInputId.Minus => _left.IsPressed(GamepadButtonInputId.Start), + GamepadButtonInputId.LeftShoulder => _left.IsPressed(GamepadButtonInputId.Paddle2), + GamepadButtonInputId.LeftTrigger => _left.IsPressed(GamepadButtonInputId.Paddle4), + GamepadButtonInputId.SingleRightTrigger0 => _left.IsPressed(GamepadButtonInputId.LeftShoulder), + GamepadButtonInputId.SingleLeftTrigger0 => _left.IsPressed(GamepadButtonInputId.RightShoulder), + + GamepadButtonInputId.RightStick => _right.IsPressed(GamepadButtonInputId.LeftStick), + GamepadButtonInputId.A => _right.IsPressed(GamepadButtonInputId.B), + GamepadButtonInputId.B => _right.IsPressed(GamepadButtonInputId.Y), + GamepadButtonInputId.X => _right.IsPressed(GamepadButtonInputId.A), + GamepadButtonInputId.Y => _right.IsPressed(GamepadButtonInputId.X), + GamepadButtonInputId.Plus => _right.IsPressed(GamepadButtonInputId.Start), + GamepadButtonInputId.RightShoulder => _right.IsPressed(GamepadButtonInputId.Paddle1), + GamepadButtonInputId.RightTrigger => _right.IsPressed(GamepadButtonInputId.Paddle3), + GamepadButtonInputId.SingleRightTrigger1 => _right.IsPressed(GamepadButtonInputId.LeftShoulder), + GamepadButtonInputId.SingleLeftTrigger1 => _right.IsPressed(GamepadButtonInputId.RightShoulder), + + _ => false + }; + } + + public void Rumble(float lowFrequency, float highFrequency, uint durationMs) + { + if (lowFrequency != 0) + { + _right.Rumble(lowFrequency, lowFrequency, durationMs); + } + if (highFrequency != 0) + { + _left.Rumble(highFrequency, highFrequency, durationMs); + } + if (lowFrequency == 0 && highFrequency == 0) + { + _left.Rumble(lowFrequency, highFrequency, durationMs); + _right.Rumble(lowFrequency, highFrequency, durationMs); + } + } + + public void SetConfiguration(InputConfig configuration) + { + lock (_userMappingLock) + { + + _configuration = (StandardControllerInputConfig)configuration; + _left.SetConfiguration(configuration); + _right.SetConfiguration(configuration); + + _buttonsUserMapping.Clear(); + + // First update sticks + _stickUserMapping[(int)StickInputId.Left] = (StickInputId)_configuration.LeftJoyconStick.Joystick; + _stickUserMapping[(int)StickInputId.Right] = (StickInputId)_configuration.RightJoyconStick.Joystick; + + // Then left joycon + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (GamepadButtonInputId)_configuration.LeftJoycon.DpadUp)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (GamepadButtonInputId)_configuration.LeftJoycon.DpadDown)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (GamepadButtonInputId)_configuration.LeftJoycon.DpadRight)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonL)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl)); + + // Finally right joycon + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (GamepadButtonInputId)_configuration.RightJoyconStick.StickButton)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (GamepadButtonInputId)_configuration.RightJoycon.ButtonA)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (GamepadButtonInputId)_configuration.RightJoycon.ButtonB)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (GamepadButtonInputId)_configuration.RightJoycon.ButtonX)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (GamepadButtonInputId)_configuration.RightJoycon.ButtonY)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (GamepadButtonInputId)_configuration.RightJoycon.ButtonR)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (GamepadButtonInputId)_configuration.RightJoycon.ButtonZr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSl)); + + SetTriggerThreshold(_configuration.TriggerThreshold); + } + + } + + public void SetTriggerThreshold(float triggerThreshold) + { + _left.SetTriggerThreshold(triggerThreshold); + _right.SetTriggerThreshold(triggerThreshold); + } + + public SDL2JoyConPair GetJoyConPair(List _gamepadsIds) + { + this.Dispose(); + var gamepadNames = _gamepadsIds.Where(gamepadId => gamepadId != Id).Select((gamepadId, index) => SDL_GameControllerNameForIndex(index)).ToList(); + int leftIndex = gamepadNames.IndexOf(leftName); + int rightIndex = gamepadNames.IndexOf(rightName); + + if (leftIndex != -1 && rightIndex != -1) + { + nint leftGamepadHandle = SDL_GameControllerOpen(leftIndex); + nint rightGamepadHandle = SDL_GameControllerOpen(rightIndex); + _left = new SDL2Gamepad(leftGamepadHandle, _gamepadsIds[leftIndex]); + _right = new SDL2Gamepad(rightGamepadHandle, _gamepadsIds[leftIndex]); + return this; + } + return null; + } + } +} diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index 380745283..94cf35ad1 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -266,6 +266,7 @@ namespace Ryujinx.Input.HLE if (motionConfig.MotionBackend != MotionInputBackendType.CemuHook) { _leftMotionInput = new MotionInput(); + _rightMotionInput = new MotionInput(); } else { @@ -298,7 +299,20 @@ namespace Ryujinx.Input.HLE if (controllerConfig.ControllerType == ConfigControllerType.JoyconPair) { - _rightMotionInput = _leftMotionInput; + if (gamepad.Id== "JoyConPair") + { + Vector3 rightAccelerometer = gamepad.GetMotionData(MotionInputId.SecondAccelerometer); + Vector3 rightGyroscope = gamepad.GetMotionData(MotionInputId.SecondGyroscope); + + rightAccelerometer = new Vector3(rightAccelerometer.X, -rightAccelerometer.Z, rightAccelerometer.Y); + rightGyroscope = new Vector3(rightGyroscope.X, -rightGyroscope.Z, rightGyroscope.Y); + + _rightMotionInput.Update(rightAccelerometer, rightGyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone); + } + else + { + _rightMotionInput = _leftMotionInput; + } } } } @@ -333,6 +347,7 @@ namespace Ryujinx.Input.HLE // Reset states State = default; _leftMotionInput = null; + _rightMotionInput = null; } } @@ -545,7 +560,7 @@ namespace Ryujinx.Input.HLE _gamepad.Rumble(low, high, uint.MaxValue); - Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " + + Logger.Info?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " + $"L.low.amp={leftVibrationValue.AmplitudeLow}, " + $"L.high.amp={leftVibrationValue.AmplitudeHigh}, " + $"R.low.amp={rightVibrationValue.AmplitudeLow}, " + diff --git a/src/Ryujinx.Input/MotionInputId.cs b/src/Ryujinx.Input/MotionInputId.cs index 8aeb043a9..c3768da96 100644 --- a/src/Ryujinx.Input/MotionInputId.cs +++ b/src/Ryujinx.Input/MotionInputId.cs @@ -21,5 +21,17 @@ namespace Ryujinx.Input /// /// Values are in degrees Gyroscope, + + /// + /// Second accelerometer. + /// + /// Values are in m/s^2 + SecondAccelerometer, + + /// + /// Second gyroscope. + /// + /// Values are in degrees + SecondGyroscope } } -- 2.47.1 From 86f9544910ca1d7b6824d0691b2035dd9ba985db Mon Sep 17 00:00:00 2001 From: IvonWei Date: Mon, 23 Dec 2024 18:54:11 +0800 Subject: [PATCH 02/10] Update NpadController.cs back to Debug --- src/Ryujinx.Input/HLE/NpadController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index 94cf35ad1..5517767be 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -560,7 +560,7 @@ namespace Ryujinx.Input.HLE _gamepad.Rumble(low, high, uint.MaxValue); - Logger.Info?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " + + Logger.Debug.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " + $"L.low.amp={leftVibrationValue.AmplitudeLow}, " + $"L.high.amp={leftVibrationValue.AmplitudeHigh}, " + $"R.low.amp={rightVibrationValue.AmplitudeLow}, " + -- 2.47.1 From ad7d9d1ce02efb9f6956af4266634aaa880f1146 Mon Sep 17 00:00:00 2001 From: IvonWei Date: Mon, 23 Dec 2024 18:55:49 +0800 Subject: [PATCH 03/10] Update NpadController.cs add ? --- src/Ryujinx.Input/HLE/NpadController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index 5517767be..53426f71a 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -560,7 +560,7 @@ namespace Ryujinx.Input.HLE _gamepad.Rumble(low, high, uint.MaxValue); - Logger.Debug.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " + + Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " + $"L.low.amp={leftVibrationValue.AmplitudeLow}, " + $"L.high.amp={leftVibrationValue.AmplitudeHigh}, " + $"R.low.amp={rightVibrationValue.AmplitudeLow}, " + -- 2.47.1 From fec197d9ec9431cc790da803e497977fe7202715 Mon Sep 17 00:00:00 2001 From: madwind Date: Wed, 25 Dec 2024 10:39:07 +0800 Subject: [PATCH 04/10] log powerLevel --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 27 +++++--- src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs | 76 +++++++++++---------- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index eefae8fb4..8c2fd82d7 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -1,7 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.SDL2.Common; using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using static SDL2.SDL; @@ -12,7 +12,8 @@ namespace Ryujinx.Input.SDL2 private readonly Dictionary _gamepadsInstanceIdsMapping; private readonly List _gamepadsIds; private readonly Lock _lock = new(); - private readonly SDL2JoyConPair joyConPair; + private readonly SDL2JoyConPair _joyConPair; + public ReadOnlySpan GamepadsIds { get @@ -37,7 +38,7 @@ namespace Ryujinx.Input.SDL2 SDL2Driver.Instance.Initialize(); SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected; SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected; - joyConPair = new SDL2JoyConPair(); + _joyConPair = new SDL2JoyConPair(); // Add already connected gamepads int numJoysticks = SDL_NumJoysticks(); @@ -90,9 +91,9 @@ namespace Ryujinx.Input.SDL2 lock (_lock) { _gamepadsIds.Remove(id); - if (joyConPair.GetJoyConPair(_gamepadsIds) == null) + if (_joyConPair.GetGamepad(_gamepadsIds) == null) { - _gamepadsIds.Remove(joyConPair.Id); + _gamepadsIds.Remove(_joyConPair.Id); } } @@ -125,12 +126,15 @@ namespace Ryujinx.Input.SDL2 _gamepadsIds.Insert(joystickDeviceId, id); else _gamepadsIds.Add(id); - if (joyConPair.GetJoyConPair(_gamepadsIds) != null) + var powerLevel = SDL_JoystickCurrentPowerLevel(GetJoystickIndexByGamepadId(id)); + Logger.Info?.Print(LogClass.Hid, $"Gamepad connected: {id}, power level: {powerLevel}"); + if (_joyConPair.GetGamepad(_gamepadsIds) != null) { - _gamepadsIds.Remove(joyConPair.Id); - _gamepadsIds.Add(joyConPair.Id); + _gamepadsIds.Remove(_joyConPair.Id); + _gamepadsIds.Add(_joyConPair.Id); } } + OnGamepadConnected?.Invoke(id); } } @@ -166,9 +170,12 @@ namespace Ryujinx.Input.SDL2 public IGamepad GetGamepad(string id) { - if (id == joyConPair.Id) + if (id == _joyConPair.Id) { - return joyConPair.GetJoyConPair(_gamepadsIds); + lock (_lock) + { + return _joyConPair.GetGamepad(_gamepadsIds); + } } int joystickIndex = GetJoystickIndexByGamepadId(id); diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs index e3ca336df..0b8f2f72d 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs @@ -1,9 +1,9 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; -using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Threading; using static SDL2.SDL; namespace Ryujinx.Input.SDL2 @@ -14,33 +14,29 @@ namespace Ryujinx.Input.SDL2 private IGamepad _right; private StandardControllerInputConfig _configuration; - private readonly StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count] -{ + private readonly StickInputId[] _stickUserMapping = + [ StickInputId.Unbound, StickInputId.Left, - StickInputId.Right, -}; + StickInputId.Right + ]; private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From) { public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound; } - private readonly List _buttonsUserMapping; - public SDL2JoyConPair() - { - _buttonsUserMapping = new List(20); - } + private readonly List _buttonsUserMapping = new(20); - private readonly object _userMappingLock = new(); + private readonly Lock _userMappingLock = new(); public GamepadFeaturesFlag Features => (_left?.Features ?? GamepadFeaturesFlag.None) | (_right?.Features ?? GamepadFeaturesFlag.None); public string Id => "JoyConPair"; public string Name => "Nintendo Switch Joy-Con (L/R)"; - private static readonly string leftName = "Nintendo Switch Joy-Con (L)"; - private static readonly string rightName = "Nintendo Switch Joy-Con (R)"; - public bool IsConnected => (_left != null && _left.IsConnected) && (_right != null && _right.IsConnected); + private const string _leftName = "Nintendo Switch Joy-Con (L)"; + private const string _rightName = "Nintendo Switch Joy-Con (R)"; + public bool IsConnected => _left is { IsConnected: true } && _right is { IsConnected: true }; public void Dispose() { @@ -98,17 +94,23 @@ namespace Ryujinx.Input.SDL2 public (float, float) GetStick(StickInputId inputId) { - if (inputId == StickInputId.Left) + switch (inputId) { - (float x, float y) = _left.GetStick(StickInputId.Left); - return (y, -x); + case StickInputId.Left: + { + (float x, float y) = _left.GetStick(StickInputId.Left); + return (y, -x); + } + case StickInputId.Right: + { + (float x, float y) = _right.GetStick(StickInputId.Left); + return (-y, x); + } + case StickInputId.Unbound: + case StickInputId.Count: + default: + return (0, 0); } - else if (inputId == StickInputId.Right) - { - (float x, float y) = _right.GetStick(StickInputId.Left); - return (-y, x); - } - return (0, 0); } public bool IsPressed(GamepadButtonInputId inputId) @@ -153,8 +155,8 @@ namespace Ryujinx.Input.SDL2 } if (lowFrequency == 0 && highFrequency == 0) { - _left.Rumble(lowFrequency, highFrequency, durationMs); - _right.Rumble(lowFrequency, highFrequency, durationMs); + _left.Rumble(0, 0, durationMs); + _right.Rumble(0, 0, durationMs); } } @@ -208,22 +210,24 @@ namespace Ryujinx.Input.SDL2 _right.SetTriggerThreshold(triggerThreshold); } - public SDL2JoyConPair GetJoyConPair(List _gamepadsIds) + public IGamepad GetGamepad(List gamepadsIds) { this.Dispose(); - var gamepadNames = _gamepadsIds.Where(gamepadId => gamepadId != Id).Select((gamepadId, index) => SDL_GameControllerNameForIndex(index)).ToList(); - int leftIndex = gamepadNames.IndexOf(leftName); - int rightIndex = gamepadNames.IndexOf(rightName); + var gamepadNames = gamepadsIds.Where(gamepadId => gamepadId != Id) + .Select((_, index) => SDL_GameControllerNameForIndex(index)).ToList(); + int leftIndex = gamepadNames.IndexOf(_leftName); + int rightIndex = gamepadNames.IndexOf(_rightName); - if (leftIndex != -1 && rightIndex != -1) + if (leftIndex == -1 || rightIndex == -1) { - nint leftGamepadHandle = SDL_GameControllerOpen(leftIndex); - nint rightGamepadHandle = SDL_GameControllerOpen(rightIndex); - _left = new SDL2Gamepad(leftGamepadHandle, _gamepadsIds[leftIndex]); - _right = new SDL2Gamepad(rightGamepadHandle, _gamepadsIds[leftIndex]); - return this; + return null; } - return null; + + nint leftGamepadHandle = SDL_GameControllerOpen(leftIndex); + nint rightGamepadHandle = SDL_GameControllerOpen(rightIndex); + _left = new SDL2Gamepad(leftGamepadHandle, gamepadsIds[leftIndex]); + _right = new SDL2Gamepad(rightGamepadHandle, gamepadsIds[leftIndex]); + return this; } } } -- 2.47.1 From e509ffa716d8bb92bc45147142402e4c9b5ec8d1 Mon Sep 17 00:00:00 2001 From: madwind Date: Wed, 25 Dec 2024 16:57:36 +0800 Subject: [PATCH 05/10] delay 2000ms before ShowPowerLevel --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 22 ++-- src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs | 106 ++++++++++++++------ 2 files changed, 87 insertions(+), 41 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index 8c2fd82d7..cd35f4a0d 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common.Logging; using Ryujinx.SDL2.Common; using System; using System.Collections.Generic; @@ -12,7 +11,7 @@ namespace Ryujinx.Input.SDL2 private readonly Dictionary _gamepadsInstanceIdsMapping; private readonly List _gamepadsIds; private readonly Lock _lock = new(); - private readonly SDL2JoyConPair _joyConPair; + private readonly SDL2JoyConPair joyConPair; public ReadOnlySpan GamepadsIds { @@ -38,7 +37,7 @@ namespace Ryujinx.Input.SDL2 SDL2Driver.Instance.Initialize(); SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected; SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected; - _joyConPair = new SDL2JoyConPair(); + joyConPair = new SDL2JoyConPair(); // Add already connected gamepads int numJoysticks = SDL_NumJoysticks(); @@ -91,9 +90,9 @@ namespace Ryujinx.Input.SDL2 lock (_lock) { _gamepadsIds.Remove(id); - if (_joyConPair.GetGamepad(_gamepadsIds) == null) + if (joyConPair.GetGamepad(_gamepadsIds) == null) { - _gamepadsIds.Remove(_joyConPair.Id); + _gamepadsIds.Remove(joyConPair.Id); } } @@ -126,12 +125,10 @@ namespace Ryujinx.Input.SDL2 _gamepadsIds.Insert(joystickDeviceId, id); else _gamepadsIds.Add(id); - var powerLevel = SDL_JoystickCurrentPowerLevel(GetJoystickIndexByGamepadId(id)); - Logger.Info?.Print(LogClass.Hid, $"Gamepad connected: {id}, power level: {powerLevel}"); - if (_joyConPair.GetGamepad(_gamepadsIds) != null) + if (joyConPair.GetGamepad(_gamepadsIds) != null) { - _gamepadsIds.Remove(_joyConPair.Id); - _gamepadsIds.Add(_joyConPair.Id); + _gamepadsIds.Remove(joyConPair.Id); + _gamepadsIds.Add(joyConPair.Id); } } @@ -170,11 +167,11 @@ namespace Ryujinx.Input.SDL2 public IGamepad GetGamepad(string id) { - if (id == _joyConPair.Id) + if (id == joyConPair.Id) { lock (_lock) { - return _joyConPair.GetGamepad(_gamepadsIds); + return joyConPair.GetGamepad(_gamepadsIds); } } @@ -192,6 +189,7 @@ namespace Ryujinx.Input.SDL2 return null; } + Console.WriteLine("Game controller opened" + SDL_GameControllerName(gamepadHandle)); return new SDL2Gamepad(gamepadHandle, id); } } diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs index 0b8f2f72d..a5d93a794 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs @@ -1,10 +1,12 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Logging; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Threading; using static SDL2.SDL; +using Timer = System.Timers.Timer; namespace Ryujinx.Input.SDL2 { @@ -12,14 +14,16 @@ namespace Ryujinx.Input.SDL2 { private IGamepad _left; private IGamepad _right; - + private Timer timer; private StandardControllerInputConfig _configuration; + private readonly StickInputId[] _stickUserMapping = [ StickInputId.Unbound, StickInputId.Left, StickInputId.Right ]; + private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From) { public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound; @@ -29,13 +33,14 @@ namespace Ryujinx.Input.SDL2 private readonly Lock _userMappingLock = new(); - public GamepadFeaturesFlag Features => (_left?.Features ?? GamepadFeaturesFlag.None) | (_right?.Features ?? GamepadFeaturesFlag.None); + public GamepadFeaturesFlag Features => (_left?.Features ?? GamepadFeaturesFlag.None) | + (_right?.Features ?? GamepadFeaturesFlag.None); public string Id => "JoyConPair"; public string Name => "Nintendo Switch Joy-Con (L/R)"; - private const string _leftName = "Nintendo Switch Joy-Con (L)"; - private const string _rightName = "Nintendo Switch Joy-Con (R)"; + private const string LeftName = "Nintendo Switch Joy-Con (L)"; + private const string RightName = "Nintendo Switch Joy-Con (R)"; public bool IsConnected => _left is { IsConnected: true } && _right is { IsConnected: true }; public void Dispose() @@ -43,6 +48,7 @@ namespace Ryujinx.Input.SDL2 _left?.Dispose(); _right?.Dispose(); } + public GamepadStateSnapshot GetMappedStateSnapshot() { GamepadStateSnapshot rawState = GetStateSnapshot(); @@ -149,10 +155,12 @@ namespace Ryujinx.Input.SDL2 { _right.Rumble(lowFrequency, lowFrequency, durationMs); } + if (highFrequency != 0) { _left.Rumble(highFrequency, highFrequency, durationMs); } + if (lowFrequency == 0 && highFrequency == 0) { _left.Rumble(0, 0, durationMs); @@ -164,7 +172,6 @@ namespace Ryujinx.Input.SDL2 { lock (_userMappingLock) { - _configuration = (StandardControllerInputConfig)configuration; _left.SetConfiguration(configuration); _right.SetConfiguration(configuration); @@ -176,32 +183,51 @@ namespace Ryujinx.Input.SDL2 _stickUserMapping[(int)StickInputId.Right] = (StickInputId)_configuration.RightJoyconStick.Joystick; // Then left joycon - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (GamepadButtonInputId)_configuration.LeftJoycon.DpadUp)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (GamepadButtonInputId)_configuration.LeftJoycon.DpadDown)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (GamepadButtonInputId)_configuration.LeftJoycon.DpadRight)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonL)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, + (GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, + (GamepadButtonInputId)_configuration.LeftJoycon.DpadUp)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, + (GamepadButtonInputId)_configuration.LeftJoycon.DpadDown)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, + (GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, + (GamepadButtonInputId)_configuration.LeftJoycon.DpadRight)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, + (GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, + (GamepadButtonInputId)_configuration.LeftJoycon.ButtonL)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, + (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, + (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, + (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl)); // Finally right joycon - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (GamepadButtonInputId)_configuration.RightJoyconStick.StickButton)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (GamepadButtonInputId)_configuration.RightJoycon.ButtonA)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (GamepadButtonInputId)_configuration.RightJoycon.ButtonB)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (GamepadButtonInputId)_configuration.RightJoycon.ButtonX)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (GamepadButtonInputId)_configuration.RightJoycon.ButtonY)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (GamepadButtonInputId)_configuration.RightJoycon.ButtonR)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (GamepadButtonInputId)_configuration.RightJoycon.ButtonZr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, + (GamepadButtonInputId)_configuration.RightJoyconStick.StickButton)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonA)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonB)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonX)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonY)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonR)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonZr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonSr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, + (GamepadButtonInputId)_configuration.RightJoycon.ButtonSl)); SetTriggerThreshold(_configuration.TriggerThreshold); } - } public void SetTriggerThreshold(float triggerThreshold) @@ -215,8 +241,8 @@ namespace Ryujinx.Input.SDL2 this.Dispose(); var gamepadNames = gamepadsIds.Where(gamepadId => gamepadId != Id) .Select((_, index) => SDL_GameControllerNameForIndex(index)).ToList(); - int leftIndex = gamepadNames.IndexOf(_leftName); - int rightIndex = gamepadNames.IndexOf(_rightName); + int leftIndex = gamepadNames.IndexOf(LeftName); + int rightIndex = gamepadNames.IndexOf(RightName); if (leftIndex == -1 || rightIndex == -1) { @@ -225,9 +251,31 @@ namespace Ryujinx.Input.SDL2 nint leftGamepadHandle = SDL_GameControllerOpen(leftIndex); nint rightGamepadHandle = SDL_GameControllerOpen(rightIndex); + + if (leftGamepadHandle == nint.Zero || rightGamepadHandle == nint.Zero) + { + return null; + } + _left = new SDL2Gamepad(leftGamepadHandle, gamepadsIds[leftIndex]); - _right = new SDL2Gamepad(rightGamepadHandle, gamepadsIds[leftIndex]); + _right = new SDL2Gamepad(rightGamepadHandle, gamepadsIds[rightIndex]); + ShowPowerLevel(leftGamepadHandle, rightGamepadHandle); return this; } + + private void ShowPowerLevel(nint leftGamepadHandle, nint rightGamepadHandle) + { + timer?.Stop(); + timer = new Timer(2000); + timer.Elapsed += (_, _) => + { + timer.Stop(); + var leftLevel = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(leftGamepadHandle)); + var rightLevel = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(rightGamepadHandle)); + Logger.Info?.Print(LogClass.Hid, $"Left power level: {leftLevel}, Right power level: {rightLevel}"); + }; + timer.AutoReset = false; + timer.Start(); + } } } -- 2.47.1 From c4dea0ee28e823c2989b8246ff8e95325a6b3260 Mon Sep 17 00:00:00 2001 From: madwind Date: Thu, 26 Dec 2024 11:54:52 +0800 Subject: [PATCH 06/10] add SQL_JOYBATTERYUPDATED , OnJoyBatteryUpdated --- src/Ryujinx.SDL2.Common/SDL2Driver.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 851c07867..3b184ee91 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -25,14 +25,17 @@ namespace Ryujinx.SDL2.Common public static Action MainThreadDispatcher { get; set; } - private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_VIDEO; + private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | + SDL_INIT_AUDIO | SDL_INIT_VIDEO; private bool _isRunning; private uint _refereceCount; private Thread _worker; + private const uint SQL_JOYBATTERYUPDATED = 1543; public event Action OnJoyStickConnected; public event Action OnJoystickDisconnected; + public event Action OnJoyBatteryUpdated; private ConcurrentDictionary> _registeredWindowHandlers; @@ -78,12 +81,14 @@ namespace Ryujinx.SDL2.Common // First ensure that we only enable joystick events (for connected/disconnected). if (SDL_GameControllerEventState(SDL_IGNORE) != SDL_IGNORE) { - Logger.Error?.PrintMsg(LogClass.Application, "Couldn't change the state of game controller events."); + Logger.Error?.PrintMsg(LogClass.Application, + "Couldn't change the state of game controller events."); } if (SDL_JoystickEventState(SDL_ENABLE) < 0) { - Logger.Error?.PrintMsg(LogClass.Application, $"Failed to enable joystick event polling: {SDL_GetError()}"); + Logger.Error?.PrintMsg(LogClass.Application, + $"Failed to enable joystick event polling: {SDL_GetError()}"); } // Disable all joysticks information, we don't need them no need to flood the event queue for that. @@ -143,7 +148,12 @@ namespace Ryujinx.SDL2.Common OnJoystickDisconnected?.Invoke(evnt.cbutton.which); } - else if (evnt.type is SDL_EventType.SDL_WINDOWEVENT or SDL_EventType.SDL_MOUSEBUTTONDOWN or SDL_EventType.SDL_MOUSEBUTTONUP) + else if ((uint)evnt.type == SQL_JOYBATTERYUPDATED) + { + OnJoyBatteryUpdated?.Invoke(evnt.cbutton.which, (SDL_JoystickPowerLevel)evnt.user.code); + } + else if (evnt.type is SDL_EventType.SDL_WINDOWEVENT or SDL_EventType.SDL_MOUSEBUTTONDOWN + or SDL_EventType.SDL_MOUSEBUTTONUP) { if (_registeredWindowHandlers.TryGetValue(evnt.window.windowID, out Action handler)) { -- 2.47.1 From 7863e97cb01cee1a67ca598f866d60c2d2001d70 Mon Sep 17 00:00:00 2001 From: madwind Date: Thu, 26 Dec 2024 11:58:00 +0800 Subject: [PATCH 07/10] invoke OnGamepadConnected and OnGamepadDisconnected --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 45 +++++--- src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs | 121 +++++++++----------- 2 files changed, 85 insertions(+), 81 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index cd35f4a0d..965a8cfab 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.SDL2.Common; using System; using System.Collections.Generic; @@ -11,7 +12,6 @@ namespace Ryujinx.Input.SDL2 private readonly Dictionary _gamepadsInstanceIdsMapping; private readonly List _gamepadsIds; private readonly Lock _lock = new(); - private readonly SDL2JoyConPair joyConPair; public ReadOnlySpan GamepadsIds { @@ -37,7 +37,8 @@ namespace Ryujinx.Input.SDL2 SDL2Driver.Instance.Initialize(); SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected; SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected; - joyConPair = new SDL2JoyConPair(); + SDL2Driver.Instance.OnJoyBatteryUpdated += HandleJoyBatteryUpdated; + // Add already connected gamepads int numJoysticks = SDL_NumJoysticks(); @@ -84,23 +85,30 @@ namespace Ryujinx.Input.SDL2 private void HandleJoyStickDisconnected(int joystickInstanceId) { + bool joyConPairDisconnected = false; if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id)) return; lock (_lock) { _gamepadsIds.Remove(id); - if (joyConPair.GetGamepad(_gamepadsIds) == null) + if (!SDL2JoyConPair.IsCombinable(_gamepadsIds)) { - _gamepadsIds.Remove(joyConPair.Id); + _gamepadsIds.Remove(SDL2JoyConPair.Id); + joyConPairDisconnected = true; } } OnGamepadDisconnected?.Invoke(id); + if (joyConPairDisconnected) + { + OnGamepadDisconnected?.Invoke(SDL2JoyConPair.Id); + } } private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId) { + bool joyConPairConnected = false; if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE) { if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId)) @@ -125,18 +133,29 @@ namespace Ryujinx.Input.SDL2 _gamepadsIds.Insert(joystickDeviceId, id); else _gamepadsIds.Add(id); - if (joyConPair.GetGamepad(_gamepadsIds) != null) + if (SDL2JoyConPair.IsCombinable(_gamepadsIds)) { - _gamepadsIds.Remove(joyConPair.Id); - _gamepadsIds.Add(joyConPair.Id); + _gamepadsIds.Remove(SDL2JoyConPair.Id); + _gamepadsIds.Add(SDL2JoyConPair.Id); + joyConPairConnected = true; } } OnGamepadConnected?.Invoke(id); + if (joyConPairConnected) + { + OnGamepadConnected?.Invoke(SDL2JoyConPair.Id); + } } } } + private void HandleJoyBatteryUpdated(int joystickDeviceId, SDL_JoystickPowerLevel powerLevel) + { + var apiPowerLevel = SDL_JoystickCurrentPowerLevel(SDL_JoystickFromInstanceID(joystickDeviceId)); + Logger.Info?.Print(LogClass.Hid, $"From user code: {powerLevel}, From api: {apiPowerLevel}"); + } + protected virtual void Dispose(bool disposing) { if (disposing) @@ -167,11 +186,11 @@ namespace Ryujinx.Input.SDL2 public IGamepad GetGamepad(string id) { - if (id == joyConPair.Id) + if (id == SDL2JoyConPair.Id) { lock (_lock) { - return joyConPair.GetGamepad(_gamepadsIds); + return SDL2JoyConPair.GetGamepad(_gamepadsIds); } } @@ -184,13 +203,7 @@ namespace Ryujinx.Input.SDL2 nint gamepadHandle = SDL_GameControllerOpen(joystickIndex); - if (gamepadHandle == nint.Zero) - { - return null; - } - - Console.WriteLine("Game controller opened" + SDL_GameControllerName(gamepadHandle)); - return new SDL2Gamepad(gamepadHandle, id); + return gamepadHandle == nint.Zero ? null : new SDL2Gamepad(gamepadHandle, id); } } } diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs index a5d93a794..c2fb9557a 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs @@ -1,20 +1,15 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Common.Logging; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Threading; using static SDL2.SDL; -using Timer = System.Timers.Timer; namespace Ryujinx.Input.SDL2 { - internal class SDL2JoyConPair : IGamepad + internal class SDL2JoyConPair(IGamepad left, IGamepad right) : IGamepad { - private IGamepad _left; - private IGamepad _right; - private Timer timer; private StandardControllerInputConfig _configuration; private readonly StickInputId[] _stickUserMapping = @@ -33,20 +28,21 @@ namespace Ryujinx.Input.SDL2 private readonly Lock _userMappingLock = new(); - public GamepadFeaturesFlag Features => (_left?.Features ?? GamepadFeaturesFlag.None) | - (_right?.Features ?? GamepadFeaturesFlag.None); + public GamepadFeaturesFlag Features => (left?.Features ?? GamepadFeaturesFlag.None) | + (right?.Features ?? GamepadFeaturesFlag.None); - public string Id => "JoyConPair"; + public const string Id = "JoyConPair"; + string IGamepad.Id => Id; public string Name => "Nintendo Switch Joy-Con (L/R)"; private const string LeftName = "Nintendo Switch Joy-Con (L)"; private const string RightName = "Nintendo Switch Joy-Con (R)"; - public bool IsConnected => _left is { IsConnected: true } && _right is { IsConnected: true }; + public bool IsConnected => left is { IsConnected: true } && right is { IsConnected: true }; public void Dispose() { - _left?.Dispose(); - _right?.Dispose(); + left?.Dispose(); + right?.Dispose(); } public GamepadStateSnapshot GetMappedStateSnapshot() @@ -87,9 +83,9 @@ namespace Ryujinx.Input.SDL2 { return inputId switch { - MotionInputId.SecondAccelerometer => _right.GetMotionData(MotionInputId.Accelerometer), - MotionInputId.SecondGyroscope => _right.GetMotionData(MotionInputId.Gyroscope), - _ => _left.GetMotionData(inputId) + MotionInputId.SecondAccelerometer => right.GetMotionData(MotionInputId.Accelerometer), + MotionInputId.SecondGyroscope => right.GetMotionData(MotionInputId.Gyroscope), + _ => left.GetMotionData(inputId) }; } @@ -104,12 +100,12 @@ namespace Ryujinx.Input.SDL2 { case StickInputId.Left: { - (float x, float y) = _left.GetStick(StickInputId.Left); + (float x, float y) = left.GetStick(StickInputId.Left); return (y, -x); } case StickInputId.Right: { - (float x, float y) = _right.GetStick(StickInputId.Left); + (float x, float y) = right.GetStick(StickInputId.Left); return (-y, x); } case StickInputId.Unbound: @@ -123,27 +119,27 @@ namespace Ryujinx.Input.SDL2 { return inputId switch { - GamepadButtonInputId.LeftStick => _left.IsPressed(GamepadButtonInputId.LeftStick), - GamepadButtonInputId.DpadUp => _left.IsPressed(GamepadButtonInputId.Y), - GamepadButtonInputId.DpadDown => _left.IsPressed(GamepadButtonInputId.A), - GamepadButtonInputId.DpadLeft => _left.IsPressed(GamepadButtonInputId.B), - GamepadButtonInputId.DpadRight => _left.IsPressed(GamepadButtonInputId.X), - GamepadButtonInputId.Minus => _left.IsPressed(GamepadButtonInputId.Start), - GamepadButtonInputId.LeftShoulder => _left.IsPressed(GamepadButtonInputId.Paddle2), - GamepadButtonInputId.LeftTrigger => _left.IsPressed(GamepadButtonInputId.Paddle4), - GamepadButtonInputId.SingleRightTrigger0 => _left.IsPressed(GamepadButtonInputId.LeftShoulder), - GamepadButtonInputId.SingleLeftTrigger0 => _left.IsPressed(GamepadButtonInputId.RightShoulder), + GamepadButtonInputId.LeftStick => left.IsPressed(GamepadButtonInputId.LeftStick), + GamepadButtonInputId.DpadUp => left.IsPressed(GamepadButtonInputId.Y), + GamepadButtonInputId.DpadDown => left.IsPressed(GamepadButtonInputId.A), + GamepadButtonInputId.DpadLeft => left.IsPressed(GamepadButtonInputId.B), + GamepadButtonInputId.DpadRight => left.IsPressed(GamepadButtonInputId.X), + GamepadButtonInputId.Minus => left.IsPressed(GamepadButtonInputId.Start), + GamepadButtonInputId.LeftShoulder => left.IsPressed(GamepadButtonInputId.Paddle2), + GamepadButtonInputId.LeftTrigger => left.IsPressed(GamepadButtonInputId.Paddle4), + GamepadButtonInputId.SingleRightTrigger0 => left.IsPressed(GamepadButtonInputId.LeftShoulder), + GamepadButtonInputId.SingleLeftTrigger0 => left.IsPressed(GamepadButtonInputId.RightShoulder), - GamepadButtonInputId.RightStick => _right.IsPressed(GamepadButtonInputId.LeftStick), - GamepadButtonInputId.A => _right.IsPressed(GamepadButtonInputId.B), - GamepadButtonInputId.B => _right.IsPressed(GamepadButtonInputId.Y), - GamepadButtonInputId.X => _right.IsPressed(GamepadButtonInputId.A), - GamepadButtonInputId.Y => _right.IsPressed(GamepadButtonInputId.X), - GamepadButtonInputId.Plus => _right.IsPressed(GamepadButtonInputId.Start), - GamepadButtonInputId.RightShoulder => _right.IsPressed(GamepadButtonInputId.Paddle1), - GamepadButtonInputId.RightTrigger => _right.IsPressed(GamepadButtonInputId.Paddle3), - GamepadButtonInputId.SingleRightTrigger1 => _right.IsPressed(GamepadButtonInputId.LeftShoulder), - GamepadButtonInputId.SingleLeftTrigger1 => _right.IsPressed(GamepadButtonInputId.RightShoulder), + GamepadButtonInputId.RightStick => right.IsPressed(GamepadButtonInputId.LeftStick), + GamepadButtonInputId.A => right.IsPressed(GamepadButtonInputId.B), + GamepadButtonInputId.B => right.IsPressed(GamepadButtonInputId.Y), + GamepadButtonInputId.X => right.IsPressed(GamepadButtonInputId.A), + GamepadButtonInputId.Y => right.IsPressed(GamepadButtonInputId.X), + GamepadButtonInputId.Plus => right.IsPressed(GamepadButtonInputId.Start), + GamepadButtonInputId.RightShoulder => right.IsPressed(GamepadButtonInputId.Paddle1), + GamepadButtonInputId.RightTrigger => right.IsPressed(GamepadButtonInputId.Paddle3), + GamepadButtonInputId.SingleRightTrigger1 => right.IsPressed(GamepadButtonInputId.LeftShoulder), + GamepadButtonInputId.SingleLeftTrigger1 => right.IsPressed(GamepadButtonInputId.RightShoulder), _ => false }; @@ -153,18 +149,18 @@ namespace Ryujinx.Input.SDL2 { if (lowFrequency != 0) { - _right.Rumble(lowFrequency, lowFrequency, durationMs); + right.Rumble(lowFrequency, lowFrequency, durationMs); } if (highFrequency != 0) { - _left.Rumble(highFrequency, highFrequency, durationMs); + left.Rumble(highFrequency, highFrequency, durationMs); } if (lowFrequency == 0 && highFrequency == 0) { - _left.Rumble(0, 0, durationMs); - _right.Rumble(0, 0, durationMs); + left.Rumble(0, 0, durationMs); + right.Rumble(0, 0, durationMs); } } @@ -173,8 +169,8 @@ namespace Ryujinx.Input.SDL2 lock (_userMappingLock) { _configuration = (StandardControllerInputConfig)configuration; - _left.SetConfiguration(configuration); - _right.SetConfiguration(configuration); + left.SetConfiguration(configuration); + right.SetConfiguration(configuration); _buttonsUserMapping.Clear(); @@ -232,18 +228,29 @@ namespace Ryujinx.Input.SDL2 public void SetTriggerThreshold(float triggerThreshold) { - _left.SetTriggerThreshold(triggerThreshold); - _right.SetTriggerThreshold(triggerThreshold); + left.SetTriggerThreshold(triggerThreshold); + right.SetTriggerThreshold(triggerThreshold); } - public IGamepad GetGamepad(List gamepadsIds) + public static bool IsCombinable(List gamepadsIds) + { + (int leftIndex, int rightIndex) = DetectJoyConPair(gamepadsIds); + return leftIndex >= 0 && rightIndex >= 0; + } + + private static (int leftIndex, int rightIndex) DetectJoyConPair(List gamepadsIds) { - this.Dispose(); var gamepadNames = gamepadsIds.Where(gamepadId => gamepadId != Id) .Select((_, index) => SDL_GameControllerNameForIndex(index)).ToList(); int leftIndex = gamepadNames.IndexOf(LeftName); int rightIndex = gamepadNames.IndexOf(RightName); + return (leftIndex, rightIndex); + } + + public static IGamepad GetGamepad(List gamepadsIds) + { + (int leftIndex, int rightIndex) = DetectJoyConPair(gamepadsIds); if (leftIndex == -1 || rightIndex == -1) { return null; @@ -257,25 +264,9 @@ namespace Ryujinx.Input.SDL2 return null; } - _left = new SDL2Gamepad(leftGamepadHandle, gamepadsIds[leftIndex]); - _right = new SDL2Gamepad(rightGamepadHandle, gamepadsIds[rightIndex]); - ShowPowerLevel(leftGamepadHandle, rightGamepadHandle); - return this; - } - private void ShowPowerLevel(nint leftGamepadHandle, nint rightGamepadHandle) - { - timer?.Stop(); - timer = new Timer(2000); - timer.Elapsed += (_, _) => - { - timer.Stop(); - var leftLevel = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(leftGamepadHandle)); - var rightLevel = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(rightGamepadHandle)); - Logger.Info?.Print(LogClass.Hid, $"Left power level: {leftLevel}, Right power level: {rightLevel}"); - }; - timer.AutoReset = false; - timer.Start(); + return new SDL2JoyConPair(new SDL2Gamepad(leftGamepadHandle, gamepadsIds[leftIndex]), + new SDL2Gamepad(rightGamepadHandle, gamepadsIds[rightIndex])); } } } -- 2.47.1 From 17233d30da931e9ede25061ef6207e201b3cd7d6 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 26 Dec 2024 00:29:00 -0600 Subject: [PATCH 08/10] misc: give various threads dedicated names Move all title ID lists into a TitleIDs class in Ryujinx.Common, with helpers. Unify & simplify Auto graphics backend selection logic --- .../Translation/PTC/PtcProfiler.cs | 12 +- src/Ryujinx.Common/TitleIDs.cs | 190 ++++++++++++++++++ .../Queries/CounterQueue.cs | 2 +- .../Queries/CounterQueue.cs | 2 +- .../HOS/Kernel/Threading/KThread.cs | 2 +- .../DiscordIntegrationModule.cs | 148 +------------- src/Ryujinx/AppHost.cs | 40 +--- src/Ryujinx/UI/Renderer/RendererHost.axaml.cs | 25 +-- 8 files changed, 212 insertions(+), 209 deletions(-) create mode 100644 src/Ryujinx.Common/TitleIDs.cs diff --git a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs index 7e630ae10..bdb9abd05 100644 --- a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -1,4 +1,5 @@ using ARMeilleure.State; +using Humanizer; using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; @@ -58,8 +59,8 @@ namespace ARMeilleure.Translation.PTC { _ptc = ptc; - _timer = new Timer(SaveInterval * 1000d); - _timer.Elapsed += PreSave; + _timer = new Timer(SaveInterval.Seconds()); + _timer.Elapsed += TimerElapsed; _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); @@ -72,6 +73,9 @@ namespace ARMeilleure.Translation.PTC Enabled = false; } + private void TimerElapsed(object _, ElapsedEventArgs __) + => new Thread(PreSave) { Name = "Ptc.DiskWriter" }.Start(); + public void AddEntry(ulong address, ExecutionMode mode, bool highCq) { if (IsAddressInStaticCodeRange(address)) @@ -262,7 +266,7 @@ namespace ARMeilleure.Translation.PTC compressedStream.SetLength(0L); } - private void PreSave(object source, ElapsedEventArgs e) + private void PreSave() { _waitEvent.Reset(); @@ -428,7 +432,7 @@ namespace ARMeilleure.Translation.PTC { _disposed = true; - _timer.Elapsed -= PreSave; + _timer.Elapsed -= TimerElapsed; _timer.Dispose(); Wait(); diff --git a/src/Ryujinx.Common/TitleIDs.cs b/src/Ryujinx.Common/TitleIDs.cs new file mode 100644 index 000000000..b75ee1299 --- /dev/null +++ b/src/Ryujinx.Common/TitleIDs.cs @@ -0,0 +1,190 @@ +using Gommon; +using Ryujinx.Common.Configuration; +using System; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common +{ + public static class TitleIDs + { + public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend) + { + switch (currentBackend) + { + case GraphicsBackend.OpenGl when OperatingSystem.IsMacOS(): + return GraphicsBackend.Vulkan; + case GraphicsBackend.Vulkan or GraphicsBackend.OpenGl or GraphicsBackend.Metal: + return currentBackend; + } + + if (!(OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture is Architecture.Arm64)) + return GraphicsBackend.Vulkan; + + return GreatMetalTitles.ContainsIgnoreCase(titleId) ? GraphicsBackend.Metal : GraphicsBackend.Vulkan; + } + + public static readonly string[] GreatMetalTitles = + [ + "01006f8002326000", // Animal Crossings: New Horizons + "01009bf0072d4000", // Captain Toad: Treasure Tracker + "0100a5c00d162000", // Cuphead + "010023800d64a000", // Deltarune + "010028600EBDA000", // Mario 3D World + "0100152000022000", // Mario Kart 8 Deluxe + "01005CA01580E000", // Persona 5 + "01008C0016544000", // Sea of Stars + "01006A800016E000", // Smash Ultimate + "0100000000010000", // Super Mario Odyessy + ]; + + public static string GetDiscordGameAsset(string titleId) + => DiscordGameAssetKeys.Contains(titleId) ? titleId : "game"; + + public static readonly string[] DiscordGameAssetKeys = + [ + "010055d009f78000", // Fire Emblem: Three Houses + "0100a12011cc8000", // Fire Emblem: Shadow Dragon + "0100a6301214e000", // Fire Emblem Engage + "0100f15003e64000", // Fire Emblem Warriors + "010071f0143ea000", // Fire Emblem Warriors: Three Hopes + + "01007e3006dda000", // Kirby Star Allies + "01004d300c5ae000", // Kirby and the Forgotten Land + "01006b601380e000", // Kirby's Return to Dream Land Deluxe + "01003fb00c5a8000", // Super Kirby Clash + "0100227010460000", // Kirby Fighters 2 + "0100a8e016236000", // Kirby's Dream Buffet + + "01007ef00011e000", // The Legend of Zelda: Breath of the Wild + "01006bb00c6f0000", // The Legend of Zelda: Link's Awakening + "01002da013484000", // The Legend of Zelda: Skyward Sword HD + "0100f2c0115b6000", // The Legend of Zelda: Tears of the Kingdom + "01008cf01baac000", // The Legend of Zelda: Echoes of Wisdom + "01000b900d8b0000", // Cadence of Hyrule + "0100ae00096ea000", // Hyrule Warriors: Definitive Edition + "01002b00111a2000", // Hyrule Warriors: Age of Calamity + + "010048701995e000", // Luigi's Mansion 2 HD + "0100dca0064a6000", // Luigi's Mansion 3 + + "010093801237c000", // Metroid Dread + "010012101468c000", // Metroid Prime Remastered + + "0100000000010000", // SUPER MARIO ODYSSEY + "0100ea80032ea000", // Super Mario Bros. U Deluxe + "01009b90006dc000", // Super Mario Maker 2 + "010049900f546000", // Super Mario 3D All-Stars + "010049900F546001", // ^ 64 + "010049900F546002", // ^ Sunshine + "010049900F546003", // ^ Galaxy + "010028600ebda000", // Super Mario 3D World + Bowser's Fury + "010015100b514000", // Super Mario Bros. Wonder + "0100152000022000", // Mario Kart 8 Deluxe + "010036b0034e4000", // Super Mario Party + "01006fe013472000", // Mario Party Superstars + "0100965017338000", // Super Mario Party Jamboree + "01006d0017f7a000", // Mario & Luigi: Brothership + "010067300059a000", // Mario + Rabbids: Kingdom Battle + "0100317013770000", // Mario + Rabbids: Sparks of Hope + "0100a3900c3e2000", // Paper Mario: The Origami King + "0100ecd018ebe000", // Paper Mario: The Thousand-Year Door + "0100bc0018138000", // Super Mario RPG + "0100bde00862a000", // Mario Tennis Aces + "0100c9c00e25c000", // Mario Golf: Super Rush + "010019401051c000", // Mario Strikers: Battle League + "010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020 + "0100b99019412000", // Mario vs. Donkey Kong + + "0100aa80194b0000", // Pikmin 1 + "0100d680194b2000", // Pikmin 2 + "0100f4c009322000", // Pikmin 3 Deluxe + "0100b7c00933a000", // Pikmin 4 + + "010003f003a34000", // Pokémon: Let's Go Pikachu! + "0100187003a36000", // Pokémon: Let's Go Eevee! + "0100abf008968000", // Pokémon Sword + "01008db008c2c000", // Pokémon Shield + "0100000011d90000", // Pokémon Brilliant Diamond + "010018e011d92000", // Pokémon Shining Pearl + "01001f5010dfa000", // Pokémon Legends: Arceus + "0100a3d008c5c000", // Pokémon Scarlet + "01008f6008c5e000", // Pokémon Violet + "0100b3f000be2000", // Pokkén Tournament DX + "0100f4300bf2c000", // New Pokémon Snap + + "01003bc0000a0000", // Splatoon 2 (US) + "0100f8f0000a2000", // Splatoon 2 (EU) + "01003c700009c000", // Splatoon 2 (JP) + "0100c2500fc20000", // Splatoon 3 + "0100ba0018500000", // Splatoon 3: Splatfest World Premiere + + "010040600c5ce000", // Tetris 99 + "0100277011f1a000", // Super Mario Bros. 35 + "0100ad9012510000", // PAC-MAN 99 + "0100ccf019c8c000", // F-ZERO 99 + "0100d870045b6000", // NES - Nintendo Switch Online + "01008d300c50c000", // SNES - Nintendo Switch Online + "0100c9a00ece6000", // N64 - Nintendo Switch Online + "0100e0601c632000", // N64 - Nintendo Switch Online 18+ + "0100c62011050000", // GB - Nintendo Switch Online + "010012f017576000", // GBA - Nintendo Switch Online + + "01000320000cc000", // 1-2 Switch + "0100300012f2a000", // Advance Wars 1+2: Re-Boot Camp + "01006f8002326000", // Animal Crossing: New Horizons + "0100620012d6e000", // Big Brain Academy: Brain vs. Brain + "010018300d006000", // BOXBOY! + BOXGIRL! + "0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze + "0100ed000d390000", // Dr. Kawashima's Brain Training + "010067b017588000", // Endless Ocean Luminous + "0100d2f00d5c0000", // Nintendo Switch Sports + "01006b5012b32000", // Part Time UFO + "0100704000B3A000", // Snipperclips + "01006a800016e000", // Super Smash Bros. Ultimate + "0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore + + "010076f0049a2000", // Bayonetta + "01007960049a0000", // Bayonetta 2 + "01004a4010fea000", // Bayonetta 3 + "0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon + + "0100dcd01525a000", // Persona 3 Portable + "010062b01525c000", // Persona 4 Golden + "010075a016a3a000", // Persona 4 Arena Ultimax + "01005ca01580e000", // Persona 5 Royal + "0100801011c3e000", // Persona 5 Strikers + "010087701b092000", // Persona 5 Tactica + + "01009aa000faa000", // Sonic Mania + "01004ad014bf0000", // Sonic Frontiers + "01005ea01c0fc000", // SONIC X SHADOW GENERATIONS + "01005ea01c0fc001", // ^ + + "010056e00853a000", // A Hat in Time + "0100dbf01000a000", // Burnout Paradise Remastered + "0100744001588000", // Cars 3: Driven to Win + "0100b41013c82000", // Cruis'n Blast + "01001b300b9be000", // Diablo III: Eternal Collection + "01008c8012920000", // Dying Light Platinum Edition + "010073c01af34000", // LEGO Horizon Adventures + "0100770008dd8000", // Monster Hunter Generations Ultimate + "0100b04011742000", // Monster Hunter Rise + "0100853015e86000", // No Man's Sky + "01007bb017812000", // Portal + "0100abd01785c000", // Portal 2 + "01008e200c5c2000", // Muse Dash + "01007820196a6000", // Red Dead Redemption + "01002f7013224000", // Rune Factory 5 + "01008d100d43e000", // Saints Row IV + "0100de600beee000", // Saints Row: The Third - The Full Package + "01001180021fa000", // Shovel Knight: Specter of Torment + "0100d7a01b7a2000", // Star Wars: Bounty Hunter + "0100800015926000", // Suika Game + "0100e46006708000", // Terraria + "01000a10041ea000", // The Elder Scrolls V: Skyrim + "010057a01e4d4000", // TSUKIHIME -A piece of blue glass moon- + "010080b00ad66000", // Undertale + ]; + } +} diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index 7e0311407..88cdec983 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries _current = new CounterQueueEvent(this, glType, 0); - _consumerThread = new Thread(EventConsumer); + _consumerThread = new Thread(EventConsumer) { Name = "CPU.CounterQueue." + (int)type }; _consumerThread.Start(); } diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index fa10f13b9..8dd94a42d 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries _current = new CounterQueueEvent(this, type, 0); - _consumerThread = new Thread(EventConsumer); + _consumerThread = new Thread(EventConsumer) { Name = "CPU.CounterQueue." + (int)type }; _consumerThread.Start(); } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 4abc0ddf3..35ff74cb3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -181,7 +181,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading is64Bits = true; } - HostThread = new Thread(ThreadStart); + HostThread = new Thread(ThreadStart) { Name = "HLE.KThread" }; Context = owner?.CreateExecutionContext() ?? new ProcessExecutionContext(); diff --git a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs b/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs index 338d28531..0cb9779ff 100644 --- a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs +++ b/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs @@ -76,7 +76,7 @@ namespace Ryujinx.UI.Common { Assets = new Assets { - LargeImageKey = _discordGameAssetKeys.Contains(procRes.ProgramIdText) ? procRes.ProgramIdText : "game", + LargeImageKey = TitleIDs.GetDiscordGameAsset(procRes.ProgramIdText), LargeImageText = TruncateToByteLength($"{appMeta.Title} (v{procRes.DisplayVersion})"), SmallImageKey = "ryujinx", SmallImageText = TruncateToByteLength(_description) @@ -122,151 +122,5 @@ namespace Ryujinx.UI.Common { _discordClient?.Dispose(); } - - private static readonly string[] _discordGameAssetKeys = - [ - "010055d009f78000", // Fire Emblem: Three Houses - "0100a12011cc8000", // Fire Emblem: Shadow Dragon - "0100a6301214e000", // Fire Emblem Engage - "0100f15003e64000", // Fire Emblem Warriors - "010071f0143ea000", // Fire Emblem Warriors: Three Hopes - - "01007e3006dda000", // Kirby Star Allies - "01004d300c5ae000", // Kirby and the Forgotten Land - "01006b601380e000", // Kirby's Return to Dream Land Deluxe - "01003fb00c5a8000", // Super Kirby Clash - "0100227010460000", // Kirby Fighters 2 - "0100a8e016236000", // Kirby's Dream Buffet - - "01007ef00011e000", // The Legend of Zelda: Breath of the Wild - "01006bb00c6f0000", // The Legend of Zelda: Link's Awakening - "01002da013484000", // The Legend of Zelda: Skyward Sword HD - "0100f2c0115b6000", // The Legend of Zelda: Tears of the Kingdom - "01008cf01baac000", // The Legend of Zelda: Echoes of Wisdom - "01000b900d8b0000", // Cadence of Hyrule - "0100ae00096ea000", // Hyrule Warriors: Definitive Edition - "01002b00111a2000", // Hyrule Warriors: Age of Calamity - - "010048701995e000", // Luigi's Mansion 2 HD - "0100dca0064a6000", // Luigi's Mansion 3 - - "010093801237c000", // Metroid Dread - "010012101468c000", // Metroid Prime Remastered - - "0100000000010000", // SUPER MARIO ODYSSEY - "0100ea80032ea000", // Super Mario Bros. U Deluxe - "01009b90006dc000", // Super Mario Maker 2 - "010049900f546000", // Super Mario 3D All-Stars - "010049900F546001", // ^ 64 - "010049900F546002", // ^ Sunshine - "010049900F546003", // ^ Galaxy - "010028600ebda000", // Super Mario 3D World + Bowser's Fury - "010015100b514000", // Super Mario Bros. Wonder - "0100152000022000", // Mario Kart 8 Deluxe - "010036b0034e4000", // Super Mario Party - "01006fe013472000", // Mario Party Superstars - "0100965017338000", // Super Mario Party Jamboree - "01006d0017f7a000", // Mario & Luigi: Brothership - "010067300059a000", // Mario + Rabbids: Kingdom Battle - "0100317013770000", // Mario + Rabbids: Sparks of Hope - "0100a3900c3e2000", // Paper Mario: The Origami King - "0100ecd018ebe000", // Paper Mario: The Thousand-Year Door - "0100bc0018138000", // Super Mario RPG - "0100bde00862a000", // Mario Tennis Aces - "0100c9c00e25c000", // Mario Golf: Super Rush - "010019401051c000", // Mario Strikers: Battle League - "010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020 - "0100b99019412000", // Mario vs. Donkey Kong - - "0100aa80194b0000", // Pikmin 1 - "0100d680194b2000", // Pikmin 2 - "0100f4c009322000", // Pikmin 3 Deluxe - "0100b7c00933a000", // Pikmin 4 - - "010003f003a34000", // Pokémon: Let's Go Pikachu! - "0100187003a36000", // Pokémon: Let's Go Eevee! - "0100abf008968000", // Pokémon Sword - "01008db008c2c000", // Pokémon Shield - "0100000011d90000", // Pokémon Brilliant Diamond - "010018e011d92000", // Pokémon Shining Pearl - "01001f5010dfa000", // Pokémon Legends: Arceus - "0100a3d008c5c000", // Pokémon Scarlet - "01008f6008c5e000", // Pokémon Violet - "0100b3f000be2000", // Pokkén Tournament DX - "0100f4300bf2c000", // New Pokémon Snap - - "01003bc0000a0000", // Splatoon 2 (US) - "0100f8f0000a2000", // Splatoon 2 (EU) - "01003c700009c000", // Splatoon 2 (JP) - "0100c2500fc20000", // Splatoon 3 - "0100ba0018500000", // Splatoon 3: Splatfest World Premiere - - "010040600c5ce000", // Tetris 99 - "0100277011f1a000", // Super Mario Bros. 35 - "0100ad9012510000", // PAC-MAN 99 - "0100ccf019c8c000", // F-ZERO 99 - "0100d870045b6000", // NES - Nintendo Switch Online - "01008d300c50c000", // SNES - Nintendo Switch Online - "0100c9a00ece6000", // N64 - Nintendo Switch Online - "0100e0601c632000", // N64 - Nintendo Switch Online 18+ - "0100c62011050000", // GB - Nintendo Switch Online - "010012f017576000", // GBA - Nintendo Switch Online - - "01000320000cc000", // 1-2 Switch - "0100300012f2a000", // Advance Wars 1+2: Re-Boot Camp - "01006f8002326000", // Animal Crossing: New Horizons - "0100620012d6e000", // Big Brain Academy: Brain vs. Brain - "010018300d006000", // BOXBOY! + BOXGIRL! - "0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze - "0100ed000d390000", // Dr. Kawashima's Brain Training - "010067b017588000", // Endless Ocean Luminous - "0100d2f00d5c0000", // Nintendo Switch Sports - "01006b5012b32000", // Part Time UFO - "0100704000B3A000", // Snipperclips - "01006a800016e000", // Super Smash Bros. Ultimate - "0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore - - "010076f0049a2000", // Bayonetta - "01007960049a0000", // Bayonetta 2 - "01004a4010fea000", // Bayonetta 3 - "0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon - - "0100dcd01525a000", // Persona 3 Portable - "010062b01525c000", // Persona 4 Golden - "010075a016a3a000", // Persona 4 Arena Ultimax - "01005ca01580e000", // Persona 5 Royal - "0100801011c3e000", // Persona 5 Strikers - "010087701b092000", // Persona 5 Tactica - - "01009aa000faa000", // Sonic Mania - "01004ad014bf0000", // Sonic Frontiers - "01005ea01c0fc000", // SONIC X SHADOW GENERATIONS - "01005ea01c0fc001", // ^ - - "010056e00853a000", // A Hat in Time - "0100dbf01000a000", // Burnout Paradise Remastered - "0100744001588000", // Cars 3: Driven to Win - "0100b41013c82000", // Cruis'n Blast - "01001b300b9be000", // Diablo III: Eternal Collection - "01008c8012920000", // Dying Light Platinum Edition - "010073c01af34000", // LEGO Horizon Adventures - "0100770008dd8000", // Monster Hunter Generations Ultimate - "0100b04011742000", // Monster Hunter Rise - "0100853015e86000", // No Man's Sky - "01007bb017812000", // Portal - "0100abd01785c000", // Portal 2 - "01008e200c5c2000", // Muse Dash - "01007820196a6000", // Red Dead Redemption - "01002f7013224000", // Rune Factory 5 - "01008d100d43e000", // Saints Row IV - "0100de600beee000", // Saints Row: The Third - The Full Package - "01001180021fa000", // Shovel Knight: Specter of Torment - "0100d7a01b7a2000", // Star Wars: Bounty Hunter - "0100800015926000", // Suika Game - "0100e46006708000", // Terraria - "01000a10041ea000", // The Elder Scrolls V: Skyrim - "010057a01e4d4000", // TSUKIHIME -A piece of blue glass moon- - "010080b00ad66000", // Undertale - ]; } } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 86b5eafa1..f976ecdf1 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -143,23 +143,6 @@ namespace Ryujinx.Ava public ulong ApplicationId { get; private set; } public bool ScreenshotRequested { get; set; } - public bool ShouldInitMetal - { - get - { - return OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && - ( - ( - ( - ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Auto && - RendererHost.KnownGreatMetalTitles.ContainsIgnoreCase(ApplicationId.ToString("X16")) - ) || - ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal - ) - ); - } - } - public AppHost( RendererHost renderer, InputManager inputManager, @@ -912,27 +895,20 @@ namespace Ryujinx.Ava VirtualFileSystem.ReloadKeySet(); // Initialize Renderer. - IRenderer renderer; - GraphicsBackend backend = ConfigurationState.Instance.Graphics.GraphicsBackend; + GraphicsBackend backend = TitleIDs.SelectGraphicsBackend(ApplicationId.ToString("X16"), ConfigurationState.Instance.Graphics.GraphicsBackend); - if (ShouldInitMetal) + IRenderer renderer = backend switch { #pragma warning disable CA1416 // This call site is reachable on all platforms - // The condition does a check for Mac, on top of checking if it's an ARM Mac. This isn't a problem. - renderer = new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal)!.CreateSurface); + // SelectGraphicsBackend does a check for Mac, on top of checking if it's an ARM Mac. This isn't a problem. + GraphicsBackend.Metal => new MetalRenderer((RendererHost.EmbeddedWindow as EmbeddedWindowMetal)!.CreateSurface), #pragma warning restore CA1416 - } - else if (backend == GraphicsBackend.Vulkan || (backend == GraphicsBackend.Auto && !ShouldInitMetal)) - { - renderer = VulkanRenderer.Create( + GraphicsBackend.Vulkan => VulkanRenderer.Create( ConfigurationState.Instance.Graphics.PreferredGpu, (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface, - VulkanHelper.GetRequiredInstanceExtensions); - } - else - { - renderer = new OpenGLRenderer(); - } + VulkanHelper.GetRequiredInstanceExtensions), + _ => new OpenGLRenderer() + }; BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; diff --git a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs index 93481a075..71c0e1432 100644 --- a/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs +++ b/src/Ryujinx/UI/Renderer/RendererHost.axaml.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Gommon; +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.UI.Common.Configuration; @@ -31,20 +32,6 @@ namespace Ryujinx.Ava.UI.Renderer Initialize(); } - public static readonly string[] KnownGreatMetalTitles = - [ - "01006f8002326000", // Animal Crossings: New Horizons - "01009bf0072d4000", // Captain Toad: Treasure Tracker - "0100a5c00d162000", // Cuphead - "010023800d64a000", // Deltarune - "010028600EBDA000", // Mario 3D World - "0100152000022000", // Mario Kart 8 Deluxe - "01005CA01580E000", // Persona 5 - "01008C0016544000", // Sea of Stars - "01006A800016E000", // Smash Ultimate - "0100000000010000", // Super Mario Odyessy - ]; - public GraphicsBackend Backend => EmbeddedWindow switch { @@ -58,16 +45,8 @@ namespace Ryujinx.Ava.UI.Renderer { InitializeComponent(); - switch (ConfigurationState.Instance.Graphics.GraphicsBackend.Value) + switch (TitleIDs.SelectGraphicsBackend(titleId, ConfigurationState.Instance.Graphics.GraphicsBackend)) { - case GraphicsBackend.Auto: - EmbeddedWindow = - OperatingSystem.IsMacOS() && - RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && - KnownGreatMetalTitles.ContainsIgnoreCase(titleId) - ? new EmbeddedWindowMetal() - : new EmbeddedWindowVulkan(); - break; case GraphicsBackend.OpenGl: EmbeddedWindow = new EmbeddedWindowOpenGL(); break; -- 2.47.1 From 20fdbff964557e4553ea49db804f0293c729f7be Mon Sep 17 00:00:00 2001 From: madwind Date: Thu, 26 Dec 2024 14:47:40 +0800 Subject: [PATCH 09/10] clean log --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index 965a8cfab..3e3681145 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -152,8 +152,7 @@ namespace Ryujinx.Input.SDL2 private void HandleJoyBatteryUpdated(int joystickDeviceId, SDL_JoystickPowerLevel powerLevel) { - var apiPowerLevel = SDL_JoystickCurrentPowerLevel(SDL_JoystickFromInstanceID(joystickDeviceId)); - Logger.Info?.Print(LogClass.Hid, $"From user code: {powerLevel}, From api: {apiPowerLevel}"); + Logger.Info?.Print(LogClass.Hid, $"{SDL_GameControllerNameForIndex(joystickDeviceId) } power level: {powerLevel}"); } protected virtual void Dispose(bool disposing) -- 2.47.1 From 05e2467e1cce1fcdb06ed57083f270a59d19d114 Mon Sep 17 00:00:00 2001 From: IvonWei Date: Thu, 26 Dec 2024 15:56:20 +0800 Subject: [PATCH 10/10] Update release.yml --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 59c31c71b..3d4fc86c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 RYUJINX_BASE_VERSION: "1.2" RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release" - RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev" + RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "madwind" RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx" RELEASE: 1 -- 2.47.1