From 13d2498405289d230dcaace9924e10ff67003448 Mon Sep 17 00:00:00 2001 From: Vova Date: Tue, 22 Oct 2024 20:35:31 +1000 Subject: [PATCH 01/30] test --- src/Ryujinx/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 5087d5d82..a79b99d17 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -31,6 +31,7 @@ namespace Ryujinx.Ava { internal partial class Program { + // public static double WindowScaleFactor { get; set; } public static double DesktopScaleFactor { get; set; } = 1.0; public static string Version { get; private set; } -- 2.47.1 From 8074a4dd870a283936c21fad437bbf8b919a38db Mon Sep 17 00:00:00 2001 From: Vova Date: Wed, 30 Oct 2024 22:17:29 +1000 Subject: [PATCH 02/30] Fixed a visual bug in "input settings", when switching between players the settings were reset to default --- src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index c133f25fa..090ce000f 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -100,6 +100,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { _playerId = PlayerIndex.Player1; } + _isLoaded = false; LoadConfiguration(); LoadDevice(); -- 2.47.1 From d83da7d2fbdde2c73cd52e0f3b8b404f69bb2337 Mon Sep 17 00:00:00 2001 From: Vova Date: Sat, 2 Nov 2024 22:42:57 +1000 Subject: [PATCH 03/30] Fixed the logic of saving the input section. Added a new dialog box when changing parameters --- src/Ryujinx/Assets/Locales/en_US.json | 1 + src/Ryujinx/UI/Helpers/ContentDialogHelper.cs | 18 ++++++ .../UI/ViewModels/Input/InputViewModel.cs | 10 ++++ .../Views/Input/ControllerInputView.axaml.cs | 60 ++++++++++++++++++- src/Ryujinx/UI/Views/Input/InputView.axaml | 2 +- src/Ryujinx/UI/Views/Input/InputView.axaml.cs | 34 +++++++++-- 6 files changed, 118 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx/Assets/Locales/en_US.json b/src/Ryujinx/Assets/Locales/en_US.json index 68b48146b..a826e49f1 100644 --- a/src/Ryujinx/Assets/Locales/en_US.json +++ b/src/Ryujinx/Assets/Locales/en_US.json @@ -407,6 +407,7 @@ "AvatarSetBackgroundColor": "Set Background Color", "AvatarClose": "Close", "ControllerSettingsLoadProfileToolTip": "Load Profile", + "ControllerSettingsViewProfileToolTip": "View Profile", "ControllerSettingsAddProfileToolTip": "Add Profile", "ControllerSettingsRemoveProfileToolTip": "Remove Profile", "ControllerSettingsSaveProfileToolTip": "Save Profile", diff --git a/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs b/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs index 67a3642a9..bd8c1e3a7 100644 --- a/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs +++ b/src/Ryujinx/UI/Helpers/ContentDialogHelper.cs @@ -226,6 +226,24 @@ namespace Ryujinx.Ava.UI.Helpers (int)Symbol.Help, primaryButtonResult); + internal static async Task CreateConfirmationDialogExtended( + string primaryText, + string secondaryText, + string acceptButtonText, + string noacceptButtonText, + string cancelButtonText, + string title, + UserResult primaryButtonResult = UserResult.Yes) + => await ShowTextDialog( + string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle] : title, + primaryText, + secondaryText, + acceptButtonText, + noacceptButtonText, + cancelButtonText, + (int)Symbol.Help, + primaryButtonResult); + internal static async Task CreateLocalizedConfirmationDialog(string primaryText, string secondaryText) => await CreateConfirmationDialog( primaryText, diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 090ce000f..54f278cec 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -44,6 +44,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input private readonly MainWindow _mainWindow; private PlayerIndex _playerId; + private PlayerIndex _playerIdChoose; private int _controller; private string _controllerImage; private int _device; @@ -83,6 +84,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input } } + public PlayerIndex PlayerIdChoose + { + get => _playerIdChoose; + set { } + } + public PlayerIndex PlayerId { get => _playerId; @@ -90,6 +97,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { if (IsModified) { + + _playerIdChoose = value; return; } @@ -99,6 +108,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input if (!Enum.IsDefined(typeof(PlayerIndex), _playerId)) { _playerId = PlayerIndex.Player1; + } _isLoaded = false; diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs index b76648da7..47e6ad53d 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs @@ -4,11 +4,14 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using DiscordRPC; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Logging; using Ryujinx.Input; using Ryujinx.Input.Assigner; +using System; using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; namespace Ryujinx.Ava.UI.Views.Input @@ -27,6 +30,16 @@ namespace Ryujinx.Ava.UI.Views.Input { button.IsCheckedChanged += Button_IsCheckedChanged; } + + if (visual is CheckBox check) + { + check.IsCheckedChanged += CheckBox_IsCheckedChanged; + } + + if (visual is Slider slider) + { + slider.PropertyChanged += Slider_IsCheckedChanged; + } } } @@ -40,9 +53,52 @@ namespace Ryujinx.Ava.UI.Views.Input } } + private float _changeSlider = -1.0f; + + private void Slider_IsCheckedChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (sender is Slider check) + { + if ((bool)check.IsPointerOver && _changeSlider == -1.0f) + { + _changeSlider = (float)check.Value; + + } + else if (!(bool)check.IsPointerOver) + { + _changeSlider = -1.0f; + } + + if (_changeSlider != -1.0f && _changeSlider != (float)check.Value) + { + + var viewModel = (DataContext as ControllerInputViewModel); + viewModel.ParentModel.IsModified = true; + //Logger.Notice.Print(LogClass.Application, $"IsEnabled: {temp}\" \"{check.Value} {check.IsPointerOver}"); + _changeSlider = (float)check.Value; + } + } + } + + private void CheckBox_IsCheckedChanged(object sender, RoutedEventArgs e) + { + if (sender is CheckBox check) + { + if ((bool)check.IsPointerOver) + { + + var viewModel = (DataContext as ControllerInputViewModel); + viewModel.ParentModel.IsModified = true; + _currentAssigner?.Cancel(); + _currentAssigner = null; + } + } + } + + private void Button_IsCheckedChanged(object sender, RoutedEventArgs e) { - if (sender is ToggleButton button) + if (sender is ToggleButton button ) { if ((bool)button.IsChecked) { @@ -149,7 +205,7 @@ namespace Ryujinx.Ava.UI.Views.Input } else { - if (_currentAssigner != null) + if (_currentAssigner != null ) { _currentAssigner.Cancel(); _currentAssigner = null; diff --git a/src/Ryujinx/UI/Views/Input/InputView.axaml b/src/Ryujinx/UI/Views/Input/InputView.axaml index 851c9c626..b5bfa666d 100644 --- a/src/Ryujinx/UI/Views/Input/InputView.axaml +++ b/src/Ryujinx/UI/Views/Input/InputView.axaml @@ -108,7 +108,7 @@ ToolTip.Tip="{ext:Locale ControllerSettingsLoadProfileToolTip}" Command="{Binding LoadProfile}"> diff --git a/src/Ryujinx/UI/Views/Input/InputView.axaml.cs b/src/Ryujinx/UI/Views/Input/InputView.axaml.cs index 356381a8a..5fda7ef6a 100644 --- a/src/Ryujinx/UI/Views/Input/InputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/InputView.axaml.cs @@ -25,17 +25,27 @@ namespace Ryujinx.Ava.UI.Views.Input private async void PlayerIndexBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { + if (PlayerIndexBox != null) + { + if (PlayerIndexBox.SelectedIndex != (int)ViewModel.PlayerId) + { + PlayerIndexBox.SelectedIndex = (int)ViewModel.PlayerId; + } + } + if (ViewModel.IsModified && !_dialogOpen) { _dialogOpen = true; - var result = await ContentDialogHelper.CreateConfirmationDialog( + var result = await ContentDialogHelper.CreateConfirmationDialogExtended( LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmMessage], LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmSubMessage], LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.Cancel], LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + if (result == UserResult.Yes) { ViewModel.Save(); @@ -43,14 +53,30 @@ namespace Ryujinx.Ava.UI.Views.Input _dialogOpen = false; + if (result == UserResult.Cancel) + { + + return; + } + ViewModel.IsModified = false; - if (e.AddedItems.Count > 0) + if (result != UserResult.Cancel) { - var player = (PlayerModel)e.AddedItems[0]; - ViewModel.PlayerId = player.Id; + ViewModel.PlayerId = ViewModel.PlayerIdChoose; + } + + if (result == UserResult.Cancel) + { + if (e.AddedItems.Count > 0) + { + ViewModel.IsModified = true; + var player = (PlayerModel)e.AddedItems[0]; + ViewModel.PlayerId = player.Id; + } } } + } public void Dispose() -- 2.47.1 From b9012e291baa69dee2bb7d11c50d2a3e7600a6be Mon Sep 17 00:00:00 2001 From: Vova Date: Sun, 10 Nov 2024 14:32:45 +1000 Subject: [PATCH 04/30] code cleaning --- src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs index 47e6ad53d..c900ea532 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs @@ -55,7 +55,7 @@ namespace Ryujinx.Ava.UI.Views.Input private float _changeSlider = -1.0f; - private void Slider_IsCheckedChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + private void Slider_IsCheckedChanged(object sender, AvaloniaPropertyChangedEventArgs e) { if (sender is Slider check) { @@ -74,7 +74,6 @@ namespace Ryujinx.Ava.UI.Views.Input var viewModel = (DataContext as ControllerInputViewModel); viewModel.ParentModel.IsModified = true; - //Logger.Notice.Print(LogClass.Application, $"IsEnabled: {temp}\" \"{check.Value} {check.IsPointerOver}"); _changeSlider = (float)check.Value; } } -- 2.47.1 From e9ecbd44fcb7763369c2186c4d673f3deb8e3538 Mon Sep 17 00:00:00 2001 From: madwind Date: Mon, 23 Dec 2024 17:57:55 +0800 Subject: [PATCH 05/30] 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 06/30] 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 07/30] 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 08/30] 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 09/30] 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 10/30] 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 11/30] 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 20fdbff964557e4553ea49db804f0293c729f7be Mon Sep 17 00:00:00 2001 From: madwind Date: Thu, 26 Dec 2024 14:47:40 +0800 Subject: [PATCH 12/30] 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 6dec7ff8ba5518f0b091163217d14b74a43f7656 Mon Sep 17 00:00:00 2001 From: madwind Date: Sat, 28 Dec 2024 09:07:22 +0800 Subject: [PATCH 13/30] fix motionData --- src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs index c2fb9557a..4c825dd1a 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; +using System; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -81,12 +82,23 @@ namespace Ryujinx.Input.SDL2 public Vector3 GetMotionData(MotionInputId inputId) { - return inputId switch + Vector3 motionData; + switch (inputId) { - MotionInputId.SecondAccelerometer => right.GetMotionData(MotionInputId.Accelerometer), - MotionInputId.SecondGyroscope => right.GetMotionData(MotionInputId.Gyroscope), - _ => left.GetMotionData(inputId) - }; + case MotionInputId.Accelerometer: + case MotionInputId.Gyroscope: + motionData = left.GetMotionData(inputId); + return new Vector3(-motionData.Z, motionData.Y, motionData.X); + case MotionInputId.SecondAccelerometer: + motionData = right.GetMotionData(MotionInputId.Accelerometer); + return new Vector3(motionData.Z, motionData.Y, -motionData.X); + case MotionInputId.SecondGyroscope: + motionData = right.GetMotionData(MotionInputId.Gyroscope); + return new Vector3(motionData.Z, motionData.Y, -motionData.X); + case MotionInputId.Invalid: + default: + return Vector3.Zero; + } } public GamepadStateSnapshot GetStateSnapshot() -- 2.47.1 From 68c03051ad240525a4d7d99afc6da8e11675a9fe Mon Sep 17 00:00:00 2001 From: madwind Date: Sun, 29 Dec 2024 00:55:26 +0800 Subject: [PATCH 14/30] For the JoyCon controller, wrap SDL2Gamepad as SDL2JoyCon to use a suitable layout and correct the motion sensing and joystick orientation. --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 14 +- src/Ryujinx.Input.SDL2/SDL2JoyCon.cs | 134 ++++++++++++++ src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs | 182 +++----------------- 3 files changed, 166 insertions(+), 164 deletions(-) create mode 100644 src/Ryujinx.Input.SDL2/SDL2JoyCon.cs diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index 3e3681145..2c73d1604 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -152,7 +152,8 @@ namespace Ryujinx.Input.SDL2 private void HandleJoyBatteryUpdated(int joystickDeviceId, SDL_JoystickPowerLevel powerLevel) { - Logger.Info?.Print(LogClass.Hid, $"{SDL_GameControllerNameForIndex(joystickDeviceId) } power level: {powerLevel}"); + Logger.Info?.Print(LogClass.Hid, + $"{SDL_GameControllerNameForIndex(joystickDeviceId)} power level: {powerLevel}"); } protected virtual void Dispose(bool disposing) @@ -201,8 +202,17 @@ namespace Ryujinx.Input.SDL2 } nint gamepadHandle = SDL_GameControllerOpen(joystickIndex); + if (gamepadHandle == nint.Zero) + { + return null; + } - return gamepadHandle == nint.Zero ? null : new SDL2Gamepad(gamepadHandle, id); + if (SDL_GameControllerName(gamepadHandle).StartsWith(SDL2JoyCon.Prefix)) + { + return new SDL2JoyCon(gamepadHandle, id); + } + + return new SDL2Gamepad(gamepadHandle, id); } } } diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs b/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs new file mode 100644 index 000000000..f7e0ccbf9 --- /dev/null +++ b/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs @@ -0,0 +1,134 @@ +using Ryujinx.Common.Configuration.Hid; +using System.Numerics; + +namespace Ryujinx.Input.SDL2 +{ + internal class SDL2JoyCon : IGamepad + { + readonly IGamepad _gamepad; + private enum JoyConType + { + Left , Right + } + + public const string Prefix = "Nintendo Switch Joy-Con"; + public const string LeftName = "Nintendo Switch Joy-Con (L)"; + public const string RightName = "Nintendo Switch Joy-Con (R)"; + + private readonly JoyConType _joyConType; + + public SDL2JoyCon(nint gamepadHandle, string driverId) + { + _gamepad = new SDL2Gamepad(gamepadHandle, driverId); + _joyConType = Name switch + { + LeftName => JoyConType.Left, + RightName => JoyConType.Right, + _ => JoyConType.Left + }; + } + + public Vector3 GetMotionData(MotionInputId inputId) + { + var motionData = _gamepad.GetMotionData(inputId); + return _joyConType switch + { + JoyConType.Left => new Vector3(-motionData.Z, motionData.Y, motionData.X), + JoyConType.Right => new Vector3(motionData.Z, motionData.Y, -motionData.X), + _ => Vector3.Zero + }; + } + + public void SetTriggerThreshold(float triggerThreshold) + { + _gamepad.SetTriggerThreshold(triggerThreshold); + } + + public void SetConfiguration(InputConfig configuration) + { + _gamepad.SetConfiguration(configuration); + } + + public void Rumble(float lowFrequency, float highFrequency, uint durationMs) + { + _gamepad.Rumble(lowFrequency, highFrequency, durationMs); + } + + public GamepadStateSnapshot GetMappedStateSnapshot() + { + return GetStateSnapshot(); + } + + public GamepadStateSnapshot GetStateSnapshot() + { + return IGamepad.GetStateSnapshot(this); + } + + + public (float, float) GetStick(StickInputId inputId) + { + if (inputId == StickInputId.Left) + { + switch (_joyConType) + { + case JoyConType.Left: + { + (float x, float y) = _gamepad.GetStick(inputId); + return (y, -x); + } + case JoyConType.Right: + { + (float x, float y) = _gamepad.GetStick(inputId); + return (-y, x); + } + } + } + return (0, 0); + } + + public GamepadFeaturesFlag Features => _gamepad.Features; + public string Id => _gamepad.Id; + public string Name => _gamepad.Name; + public bool IsConnected => _gamepad.IsConnected; + + public bool IsPressed(GamepadButtonInputId inputId) + { + return _joyConType switch + { + JoyConType.Left => inputId switch + { + GamepadButtonInputId.LeftStick => _gamepad.IsPressed(GamepadButtonInputId.LeftStick), + GamepadButtonInputId.DpadUp => _gamepad.IsPressed(GamepadButtonInputId.Y), + GamepadButtonInputId.DpadDown => _gamepad.IsPressed(GamepadButtonInputId.A), + GamepadButtonInputId.DpadLeft => _gamepad.IsPressed(GamepadButtonInputId.B), + GamepadButtonInputId.DpadRight => _gamepad.IsPressed(GamepadButtonInputId.X), + GamepadButtonInputId.Minus => _gamepad.IsPressed(GamepadButtonInputId.Start), + GamepadButtonInputId.LeftShoulder => _gamepad.IsPressed(GamepadButtonInputId.Paddle2), + GamepadButtonInputId.LeftTrigger => _gamepad.IsPressed(GamepadButtonInputId.Paddle4), + GamepadButtonInputId.SingleRightTrigger0 => _gamepad.IsPressed(GamepadButtonInputId.RightShoulder), + GamepadButtonInputId.SingleLeftTrigger0 => _gamepad.IsPressed(GamepadButtonInputId.LeftShoulder), + _ => false + }, + JoyConType.Right => inputId switch + { + GamepadButtonInputId.RightStick => _gamepad.IsPressed(GamepadButtonInputId.LeftStick), + GamepadButtonInputId.A => _gamepad.IsPressed(GamepadButtonInputId.B), + GamepadButtonInputId.B => _gamepad.IsPressed(GamepadButtonInputId.Y), + GamepadButtonInputId.X => _gamepad.IsPressed(GamepadButtonInputId.A), + GamepadButtonInputId.Y => _gamepad.IsPressed(GamepadButtonInputId.X), + GamepadButtonInputId.Plus => _gamepad.IsPressed(GamepadButtonInputId.Start), + GamepadButtonInputId.RightShoulder => _gamepad.IsPressed(GamepadButtonInputId.Paddle1), + GamepadButtonInputId.RightTrigger => _gamepad.IsPressed(GamepadButtonInputId.Paddle3), + GamepadButtonInputId.SingleRightTrigger1 => _gamepad.IsPressed(GamepadButtonInputId.RightShoulder), + GamepadButtonInputId.SingleLeftTrigger1 => _gamepad.IsPressed(GamepadButtonInputId.LeftShoulder), + _ => false + } + }; + } + + public void Dispose() + { + _gamepad.Dispose(); + } + } +} diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs index 4c825dd1a..5ff11f987 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Threading; using static SDL2.SDL; namespace Ryujinx.Input.SDL2 @@ -20,15 +19,6 @@ namespace Ryujinx.Input.SDL2 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 = new(20); - - private readonly Lock _userMappingLock = new(); - public GamepadFeaturesFlag Features => (left?.Features ?? GamepadFeaturesFlag.None) | (right?.Features ?? GamepadFeaturesFlag.None); @@ -36,8 +26,6 @@ namespace Ryujinx.Input.SDL2 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 void Dispose() @@ -48,57 +36,19 @@ namespace Ryujinx.Input.SDL2 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; + return GetStateSnapshot(); } public Vector3 GetMotionData(MotionInputId inputId) { - Vector3 motionData; - switch (inputId) + return inputId switch { - case MotionInputId.Accelerometer: - case MotionInputId.Gyroscope: - motionData = left.GetMotionData(inputId); - return new Vector3(-motionData.Z, motionData.Y, motionData.X); - case MotionInputId.SecondAccelerometer: - motionData = right.GetMotionData(MotionInputId.Accelerometer); - return new Vector3(motionData.Z, motionData.Y, -motionData.X); - case MotionInputId.SecondGyroscope: - motionData = right.GetMotionData(MotionInputId.Gyroscope); - return new Vector3(motionData.Z, motionData.Y, -motionData.X); - case MotionInputId.Invalid: - default: - return Vector3.Zero; - } + MotionInputId.Accelerometer or + MotionInputId.Gyroscope => left.GetMotionData(inputId), + MotionInputId.SecondAccelerometer => right.GetMotionData(MotionInputId.Accelerometer), + MotionInputId.SecondGyroscope => right.GetMotionData(MotionInputId.Gyroscope), + _ => Vector3.Zero + }; } public GamepadStateSnapshot GetStateSnapshot() @@ -108,53 +58,17 @@ namespace Ryujinx.Input.SDL2 public (float, float) GetStick(StickInputId inputId) { - switch (inputId) + return inputId switch { - 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); - } + StickInputId.Left => left.GetStick(StickInputId.Left), + StickInputId.Right => right.GetStick(StickInputId.Left), + _ => (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 - }; + return left.IsPressed(inputId) || right.IsPressed(inputId); } public void Rumble(float lowFrequency, float highFrequency, uint durationMs) @@ -178,64 +92,8 @@ namespace Ryujinx.Input.SDL2 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); - } + left.SetConfiguration(configuration); + right.SetConfiguration(configuration); } public void SetTriggerThreshold(float triggerThreshold) @@ -254,8 +112,8 @@ namespace Ryujinx.Input.SDL2 { 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(SDL2JoyCon.LeftName); + int rightIndex = gamepadNames.IndexOf(SDL2JoyCon.RightName); return (leftIndex, rightIndex); } @@ -277,8 +135,8 @@ namespace Ryujinx.Input.SDL2 } - return new SDL2JoyConPair(new SDL2Gamepad(leftGamepadHandle, gamepadsIds[leftIndex]), - new SDL2Gamepad(rightGamepadHandle, gamepadsIds[rightIndex])); + return new SDL2JoyConPair(new SDL2JoyCon(leftGamepadHandle, gamepadsIds[leftIndex]), + new SDL2JoyCon(rightGamepadHandle, gamepadsIds[rightIndex])); } } } -- 2.47.1 From 8e50dd9fa6f1069dd7c5efca72afb487eeb33a54 Mon Sep 17 00:00:00 2001 From: madwind Date: Sun, 29 Dec 2024 01:49:25 +0800 Subject: [PATCH 15/30] fix right JoyCon stick --- src/Ryujinx.Input.SDL2/SDL2JoyCon.cs | 30 +++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs b/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs index f7e0ccbf9..b25243932 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs @@ -67,23 +67,21 @@ namespace Ryujinx.Input.SDL2 public (float, float) GetStick(StickInputId inputId) { - if (inputId == StickInputId.Left) + switch (inputId) { - switch (_joyConType) - { - case JoyConType.Left: - { - (float x, float y) = _gamepad.GetStick(inputId); - return (y, -x); - } - case JoyConType.Right: - { - (float x, float y) = _gamepad.GetStick(inputId); - return (-y, x); - } - } - } - return (0, 0); + case StickInputId.Left when _joyConType == JoyConType.Left: + { + (float x, float y) = _gamepad.GetStick(inputId); + return (y, -x); + } + case StickInputId.Right when _joyConType == JoyConType.Right: + { + (float x, float y) = _gamepad.GetStick(StickInputId.Left); + return (-y, x); + } + default: + return (0, 0); + } } public GamepadFeaturesFlag Features => _gamepad.Features; -- 2.47.1 From 69dfd8c60e53bec1ad1753566536ea0c845b5489 Mon Sep 17 00:00:00 2001 From: madwind Date: Sun, 29 Dec 2024 02:11:31 +0800 Subject: [PATCH 16/30] fix right Stick --- src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs index 5ff11f987..e08b298d4 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Input.SDL2 return inputId switch { StickInputId.Left => left.GetStick(StickInputId.Left), - StickInputId.Right => right.GetStick(StickInputId.Left), + StickInputId.Right => right.GetStick(StickInputId.Right), _ => (0, 0) }; } -- 2.47.1 From 7a451ab16049b24387b42db8ef703685465968d0 Mon Sep 17 00:00:00 2001 From: madwind Date: Mon, 30 Dec 2024 22:01:21 +0800 Subject: [PATCH 17/30] fix GetMappedStateSnapshot --- src/Ryujinx.Input.SDL2/SDL2JoyCon.cs | 417 ++++++++++++++++++++++----- 1 file changed, 347 insertions(+), 70 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs b/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs index b25243932..027785b96 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyCon.cs @@ -1,14 +1,69 @@ using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Logging; +using System; +using System.Collections.Generic; using System.Numerics; +using System.Threading; +using static SDL2.SDL; namespace Ryujinx.Input.SDL2 { internal class SDL2JoyCon : IGamepad { - readonly IGamepad _gamepad; + private bool HasConfiguration => _configuration != null; + + private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From) + { + public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound; + } + + private StandardControllerInputConfig _configuration; + + private readonly Dictionary _leftButtonsDriverMapping = new() + { + { GamepadButtonInputId.LeftStick , SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK }, + {GamepadButtonInputId.DpadUp ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y}, + {GamepadButtonInputId.DpadDown ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A}, + {GamepadButtonInputId.DpadLeft ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B}, + {GamepadButtonInputId.DpadRight ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X}, + {GamepadButtonInputId.Minus ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START}, + {GamepadButtonInputId.LeftShoulder,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE2}, + {GamepadButtonInputId.LeftTrigger,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE4}, + {GamepadButtonInputId.SingleRightTrigger0,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, + {GamepadButtonInputId.SingleLeftTrigger0,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, + }; + private readonly Dictionary _rightButtonsDriverMapping = new() + { + {GamepadButtonInputId.RightStick,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK}, + {GamepadButtonInputId.A,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B}, + {GamepadButtonInputId.B,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y}, + {GamepadButtonInputId.X,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A}, + {GamepadButtonInputId.Y,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X}, + {GamepadButtonInputId.Plus,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START}, + {GamepadButtonInputId.RightShoulder,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE1}, + {GamepadButtonInputId.RightTrigger,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE3}, + {GamepadButtonInputId.SingleRightTrigger1,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, + {GamepadButtonInputId.SingleLeftTrigger1,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER} + }; + + private readonly Dictionary _buttonsDriverMapping; + private readonly Lock _userMappingLock = new(); + + private readonly List _buttonsUserMapping; + + private readonly StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count] + { + StickInputId.Unbound, StickInputId.Left, StickInputId.Right, + }; + + public GamepadFeaturesFlag Features { get; } + + private nint _gamepadHandle; + private enum JoyConType { - Left , Right + Left, Right } public const string Prefix = "Nintendo Switch Joy-Con"; @@ -19,44 +74,205 @@ namespace Ryujinx.Input.SDL2 public SDL2JoyCon(nint gamepadHandle, string driverId) { - _gamepad = new SDL2Gamepad(gamepadHandle, driverId); - _joyConType = Name switch + _gamepadHandle = gamepadHandle; + _buttonsUserMapping = new List(10); + + Name = SDL_GameControllerName(_gamepadHandle); + Id = driverId; + Features = GetFeaturesFlag(); + + // Enable motion tracking + if (Features.HasFlag(GamepadFeaturesFlag.Motion)) { - LeftName => JoyConType.Left, - RightName => JoyConType.Right, - _ => JoyConType.Left - }; + if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, + SDL_bool.SDL_TRUE) != 0) + { + Logger.Error?.Print(LogClass.Hid, + $"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_ACCEL}."); + } + + if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO, + SDL_bool.SDL_TRUE) != 0) + { + Logger.Error?.Print(LogClass.Hid, + $"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_GYRO}."); + } + } + + switch (Name) + { + case LeftName: + { + _buttonsDriverMapping = _leftButtonsDriverMapping; + _joyConType = JoyConType.Left; + break; + } + case RightName: + { + _buttonsDriverMapping = _rightButtonsDriverMapping; + _joyConType = JoyConType.Right; + break; + } + } } - public Vector3 GetMotionData(MotionInputId inputId) + private GamepadFeaturesFlag GetFeaturesFlag() { - var motionData = _gamepad.GetMotionData(inputId); - return _joyConType switch + GamepadFeaturesFlag result = GamepadFeaturesFlag.None; + + if (SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) == SDL_bool.SDL_TRUE && + SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO) == SDL_bool.SDL_TRUE) { - JoyConType.Left => new Vector3(-motionData.Z, motionData.Y, motionData.X), - JoyConType.Right => new Vector3(motionData.Z, motionData.Y, -motionData.X), - _ => Vector3.Zero - }; + result |= GamepadFeaturesFlag.Motion; + } + + int error = SDL_GameControllerRumble(_gamepadHandle, 0, 0, 100); + + if (error == 0) + { + result |= GamepadFeaturesFlag.Rumble; + } + + return result; } + public string Id { get; } + public string Name { get; } + public bool IsConnected => SDL_GameControllerGetAttached(_gamepadHandle) == SDL_bool.SDL_TRUE; + + protected virtual void Dispose(bool disposing) + { + if (disposing && _gamepadHandle != nint.Zero) + { + SDL_GameControllerClose(_gamepadHandle); + + _gamepadHandle = nint.Zero; + } + } + + public void Dispose() + { + Dispose(true); + } + + public void SetTriggerThreshold(float triggerThreshold) { - _gamepad.SetTriggerThreshold(triggerThreshold); - } - public void SetConfiguration(InputConfig configuration) - { - _gamepad.SetConfiguration(configuration); } public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { - _gamepad.Rumble(lowFrequency, highFrequency, durationMs); + if (!Features.HasFlag(GamepadFeaturesFlag.Rumble)) + return; + + ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue); + ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue); + + if (durationMs == uint.MaxValue) + { + if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY) != + 0) + Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); + } + else if (durationMs > SDL_HAPTIC_INFINITY) + { + Logger.Error?.Print(LogClass.Hid, $"Unsupported rumble duration {durationMs}"); + } + else + { + if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs) != 0) + Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); + } } - public GamepadStateSnapshot GetMappedStateSnapshot() + public Vector3 GetMotionData(MotionInputId inputId) { - return GetStateSnapshot(); + SDL_SensorType sensorType = inputId switch + { + MotionInputId.Accelerometer => SDL_SensorType.SDL_SENSOR_ACCEL, + MotionInputId.Gyroscope => SDL_SensorType.SDL_SENSOR_GYRO, + _ => SDL_SensorType.SDL_SENSOR_INVALID + }; + + if (!Features.HasFlag(GamepadFeaturesFlag.Motion) || sensorType is SDL_SensorType.SDL_SENSOR_INVALID) + return Vector3.Zero; + + const int ElementCount = 3; + + unsafe + { + float* values = stackalloc float[ElementCount]; + + int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (nint)values, ElementCount); + + if (result != 0) + return Vector3.Zero; + + Vector3 value = _joyConType switch + { + JoyConType.Left => new Vector3(-values[2], values[1], values[0]), + JoyConType.Right => new Vector3(values[2], values[1], -values[0]) + }; + + return inputId switch + { + MotionInputId.Gyroscope => RadToDegree(value), + MotionInputId.Accelerometer => GsToMs2(value), + _ => value + }; + } + } + + private static Vector3 RadToDegree(Vector3 rad) => rad * (180 / MathF.PI); + + private static Vector3 GsToMs2(Vector3 gs) => gs / SDL_STANDARD_GRAVITY; + + public void SetConfiguration(InputConfig configuration) + { + lock (_userMappingLock) + { + _configuration = (StandardControllerInputConfig)configuration; + + _buttonsUserMapping.Clear(); + + // First update sticks + _stickUserMapping[(int)StickInputId.Left] = (StickInputId)_configuration.LeftJoyconStick.Joystick; + _stickUserMapping[(int)StickInputId.Right] = (StickInputId)_configuration.RightJoyconStick.Joystick; + + + switch (_joyConType) + { + case JoyConType.Left: + _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)); + break; + case JoyConType.Right: + _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)); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + SetTriggerThreshold(_configuration.TriggerThreshold); + } } public GamepadStateSnapshot GetStateSnapshot() @@ -64,69 +280,130 @@ namespace Ryujinx.Input.SDL2 return IGamepad.GetStateSnapshot(this); } + public GamepadStateSnapshot GetMappedStateSnapshot() + { + GamepadStateSnapshot rawState = GetStateSnapshot(); + GamepadStateSnapshot result = default; - public (float, float) GetStick(StickInputId inputId) + 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; + } + + + private static float ConvertRawStickValue(short value) + { + const float ConvertRate = 1.0f / (short.MaxValue + 0.5f); + + return value * ConvertRate; + } + + private JoyconConfigControllerStick + GetLogicalJoyStickConfig(StickInputId inputId) { switch (inputId) { - case StickInputId.Left when _joyConType == JoyConType.Left: - { - (float x, float y) = _gamepad.GetStick(inputId); - return (y, -x); - } - case StickInputId.Right when _joyConType == JoyConType.Right: - { - (float x, float y) = _gamepad.GetStick(StickInputId.Left); - return (-y, x); - } - default: - return (0, 0); + case StickInputId.Left: + if (_configuration.RightJoyconStick.Joystick == + Common.Configuration.Hid.Controller.StickInputId.Left) + return _configuration.RightJoyconStick; + else + return _configuration.LeftJoyconStick; + case StickInputId.Right: + if (_configuration.LeftJoyconStick.Joystick == + Common.Configuration.Hid.Controller.StickInputId.Right) + return _configuration.LeftJoyconStick; + else + return _configuration.RightJoyconStick; } + + return null; } - public GamepadFeaturesFlag Features => _gamepad.Features; - public string Id => _gamepad.Id; - public string Name => _gamepad.Name; - public bool IsConnected => _gamepad.IsConnected; - public bool IsPressed(GamepadButtonInputId inputId) + public (float, float) GetStick(StickInputId inputId) { - return _joyConType switch + if (inputId == StickInputId.Unbound) + return (0.0f, 0.0f); + + if (inputId == StickInputId.Left && _joyConType == JoyConType.Right || inputId == StickInputId.Right && _joyConType == JoyConType.Left) { - JoyConType.Left => inputId switch + return (0.0f, 0.0f); + } + + (short stickX, short stickY) = GetStickXY(); + + float resultX = ConvertRawStickValue(stickX); + float resultY = -ConvertRawStickValue(stickY); + + if (HasConfiguration) + { + var joyconStickConfig = GetLogicalJoyStickConfig(inputId); + + if (joyconStickConfig != null) { - GamepadButtonInputId.LeftStick => _gamepad.IsPressed(GamepadButtonInputId.LeftStick), - GamepadButtonInputId.DpadUp => _gamepad.IsPressed(GamepadButtonInputId.Y), - GamepadButtonInputId.DpadDown => _gamepad.IsPressed(GamepadButtonInputId.A), - GamepadButtonInputId.DpadLeft => _gamepad.IsPressed(GamepadButtonInputId.B), - GamepadButtonInputId.DpadRight => _gamepad.IsPressed(GamepadButtonInputId.X), - GamepadButtonInputId.Minus => _gamepad.IsPressed(GamepadButtonInputId.Start), - GamepadButtonInputId.LeftShoulder => _gamepad.IsPressed(GamepadButtonInputId.Paddle2), - GamepadButtonInputId.LeftTrigger => _gamepad.IsPressed(GamepadButtonInputId.Paddle4), - GamepadButtonInputId.SingleRightTrigger0 => _gamepad.IsPressed(GamepadButtonInputId.RightShoulder), - GamepadButtonInputId.SingleLeftTrigger0 => _gamepad.IsPressed(GamepadButtonInputId.LeftShoulder), - _ => false - }, - JoyConType.Right => inputId switch - { - GamepadButtonInputId.RightStick => _gamepad.IsPressed(GamepadButtonInputId.LeftStick), - GamepadButtonInputId.A => _gamepad.IsPressed(GamepadButtonInputId.B), - GamepadButtonInputId.B => _gamepad.IsPressed(GamepadButtonInputId.Y), - GamepadButtonInputId.X => _gamepad.IsPressed(GamepadButtonInputId.A), - GamepadButtonInputId.Y => _gamepad.IsPressed(GamepadButtonInputId.X), - GamepadButtonInputId.Plus => _gamepad.IsPressed(GamepadButtonInputId.Start), - GamepadButtonInputId.RightShoulder => _gamepad.IsPressed(GamepadButtonInputId.Paddle1), - GamepadButtonInputId.RightTrigger => _gamepad.IsPressed(GamepadButtonInputId.Paddle3), - GamepadButtonInputId.SingleRightTrigger1 => _gamepad.IsPressed(GamepadButtonInputId.RightShoulder), - GamepadButtonInputId.SingleLeftTrigger1 => _gamepad.IsPressed(GamepadButtonInputId.LeftShoulder), - _ => false + if (joyconStickConfig.InvertStickX) + resultX = -resultX; + + if (joyconStickConfig.InvertStickY) + resultY = -resultY; + + if (joyconStickConfig.Rotate90CW) + { + float temp = resultX; + resultX = resultY; + resultY = -temp; + } } + } + + return inputId switch + { + StickInputId.Left when _joyConType == JoyConType.Left => (resultY, -resultX), + StickInputId.Right when _joyConType == JoyConType.Right => (-resultY, resultX), + _ => (0.0f, 0.0f) }; } - public void Dispose() + private (short, short) GetStickXY() { - _gamepad.Dispose(); + return ( + SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX), + SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY)); + } + + public bool IsPressed(GamepadButtonInputId inputId) + { + if (!_buttonsDriverMapping.TryGetValue(inputId, out var button)) + { + return false; + } + + return SDL_GameControllerGetButton(_gamepadHandle, button) == 1; } } } -- 2.47.1 From 4399edaa9f1241450b4c8e1aa62cc73a440e5783 Mon Sep 17 00:00:00 2001 From: madwind Date: Thu, 2 Jan 2025 11:50:20 +0800 Subject: [PATCH 18/30] Fix typo: SQL_JOYBATTERYUPDATED => SDL_JOYBATTERYUPDATED --- src/Ryujinx.SDL2.Common/SDL2Driver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 3b184ee91..d41cbe945 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -32,7 +32,7 @@ namespace Ryujinx.SDL2.Common private uint _refereceCount; private Thread _worker; - private const uint SQL_JOYBATTERYUPDATED = 1543; + private const uint SDL_JOYBATTERYUPDATED = 1543; public event Action OnJoyStickConnected; public event Action OnJoystickDisconnected; public event Action OnJoyBatteryUpdated; @@ -148,7 +148,7 @@ namespace Ryujinx.SDL2.Common OnJoystickDisconnected?.Invoke(evnt.cbutton.which); } - else if ((uint)evnt.type == SQL_JOYBATTERYUPDATED) + else if ((uint)evnt.type == SDL_JOYBATTERYUPDATED) { OnJoyBatteryUpdated?.Invoke(evnt.cbutton.which, (SDL_JoystickPowerLevel)evnt.user.code); } -- 2.47.1 From 37b4dd21337a356eb6ba9792a4bbb0093365cff2 Mon Sep 17 00:00:00 2001 From: Vova Date: Wed, 8 Jan 2025 21:43:18 +1000 Subject: [PATCH 19/30] Added new option "exit by pressing plus and minus buttons" to the input section. --- src/Ryujinx.Input.SDL2/SDL2Gamepad.cs | 38 +++++- src/Ryujinx.Input.SDL2/SDL2Keyboard.cs | 5 + src/Ryujinx.Input.SDL2/SDL2Mouse.cs | 4 + src/Ryujinx.Input/HLE/NpadController.cs | 12 +- src/Ryujinx.Input/HLE/NpadManager.cs | 10 +- src/Ryujinx.Input/IGamepad.cs | 6 + src/Ryujinx/AppHost.cs | 42 +++++- src/Ryujinx/Assets/locales.json | 125 ++++++++++++++++++ src/Ryujinx/Headless/HeadlessRyujinx.cs | 6 +- src/Ryujinx/Headless/Metal/MetalWindow.cs | 5 +- src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs | 5 +- src/Ryujinx/Headless/Options.cs | 9 +- src/Ryujinx/Headless/Vulkan/VulkanWindow.cs | 5 +- src/Ryujinx/Headless/WindowBase.cs | 5 +- src/Ryujinx/Input/AvaloniaKeyboard.cs | 5 + src/Ryujinx/Input/AvaloniaMouse.cs | 5 + .../UI/ViewModels/MainWindowViewModel.cs | 7 + .../UI/ViewModels/SettingsViewModel.cs | 17 +++ .../UI/Views/Settings/SettingsInputView.axaml | 16 ++- src/Ryujinx/UI/Windows/MainWindow.axaml.cs | 11 +- .../Configuration/ConfigurationFileFormat.cs | 7 +- .../ConfigurationState.Migration.cs | 5 + .../Configuration/ConfigurationState.Model.cs | 6 + .../Configuration/ConfigurationState.cs | 2 + 24 files changed, 333 insertions(+), 25 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index 12bfab4bb..26aa496a3 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -253,11 +253,23 @@ namespace Ryujinx.Input.SDL2 return IGamepad.GetStateSnapshot(this); } + private static bool hotButtonMinus = false; + private static bool HotExit = false; + + public bool spetialExit() + { + if (hotButtonMinus) + { + hotButtonMinus = false; + return HotExit; + } + return HotExit = false; + } + public GamepadStateSnapshot GetMappedStateSnapshot() { GamepadStateSnapshot rawState = GetStateSnapshot(); GamepadStateSnapshot result = default; - lock (_userMappingLock) { if (_buttonsUserMapping.Count == 0) @@ -270,6 +282,28 @@ namespace Ryujinx.Input.SDL2 if (!entry.IsValid) continue; + if (GamepadButtonInputId.Minus == entry.To) + { + if (rawState.IsPressed(entry.From) && !hotButtonMinus) + { + hotButtonMinus = true; + } + else if (!result.IsPressed(entry.From) && hotButtonMinus) + { + hotButtonMinus = false; + } + } + + if (GamepadButtonInputId.Plus == entry.To) + { + if (rawState.IsPressed(entry.To) && hotButtonMinus) + { + + HotExit = true; + } + + } + // Do not touch state of button already pressed if (!result.IsPressed(entry.To)) { @@ -376,5 +410,7 @@ namespace Ryujinx.Input.SDL2 return SDL_GameControllerGetButton(_gamepadHandle, _buttonsDriverMapping[(int)inputId]) == 1; } + + } } diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs index 8d6a30d11..0fd25a54e 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs @@ -329,6 +329,11 @@ namespace Ryujinx.Input.SDL2 return result; } + public bool spetialExit() + { + return false; + } + public GamepadStateSnapshot GetStateSnapshot() { throw new NotSupportedException(); diff --git a/src/Ryujinx.Input.SDL2/SDL2Mouse.cs b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs index 37b356b76..d3f64d5ed 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Mouse.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs @@ -25,6 +25,10 @@ namespace Ryujinx.Input.SDL2 { _driver = driver; } + public bool spetialExit() + { + return false; + } public Vector2 GetPosition() { diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index 380745283..991311d02 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -3,6 +3,7 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.HOS.Services.Hid; using System; using System.Collections.Concurrent; @@ -273,15 +274,21 @@ namespace Ryujinx.Input.HLE } } - public void Update() + public bool Update() { + // _gamepad may be altered by other threads var gamepad = _gamepad; - + if (gamepad != null && GamepadDriver != null) { State = gamepad.GetMappedStateSnapshot(); + if (gamepad.spetialExit()) + { + return true; + } + if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion) { if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver) @@ -334,6 +341,7 @@ namespace Ryujinx.Input.HLE State = default; _leftMotionInput = null; } + return false; } public GamepadInput GetHLEInputState() diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 08f222a91..6955f1d2e 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -200,8 +200,10 @@ namespace Ryujinx.Input.HLE ReloadConfiguration(inputConfig, enableKeyboard, enableMouse); } - public void Update(float aspectRatio = 1) + public bool Update(float aspectRatio = 1) { + bool spetialExit = false; + lock (_lock) { List hleInputStates = new(); @@ -225,9 +227,9 @@ namespace Ryujinx.Input.HLE DriverConfigurationUpdate(ref controller, inputConfig); controller.UpdateUserConfiguration(inputConfig); - controller.Update(); + spetialExit = controller.Update(); //hotkey press check controller.UpdateRumble(_device.Hid.Npads.GetRumbleQueue(playerIndex)); - + inputState = controller.GetHLEInputState(); inputState.Buttons |= _device.Hid.UpdateStickButtons(inputState.LStick, inputState.RStick); @@ -315,6 +317,8 @@ namespace Ryujinx.Input.HLE _device.TamperMachine.UpdateInput(hleInputStates); } + + return spetialExit; } internal InputConfig GetPlayerInputConfigByIndex(int index) diff --git a/src/Ryujinx.Input/IGamepad.cs b/src/Ryujinx.Input/IGamepad.cs index 3853f2819..8e6a8e33f 100644 --- a/src/Ryujinx.Input/IGamepad.cs +++ b/src/Ryujinx.Input/IGamepad.cs @@ -79,6 +79,12 @@ namespace Ryujinx.Input /// A remapped snaphost of the state of the gamepad. GamepadStateSnapshot GetMappedStateSnapshot(); + /// + /// Gets the state if the minus and plus buttons were pressed on the gamepad. + /// + /// returns true if the buttons were pressed. + bool spetialExit(); + /// /// Get a snaphost of the state of the gamepad. /// diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index a35a79e86..c16cd5e11 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -18,6 +18,7 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Renderer; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Views.Main; using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities.AppLibrary; @@ -70,6 +71,7 @@ namespace Ryujinx.Ava private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. private const int TargetFps = 60; private const float VolumeDelta = 0.05f; + static bool spetialExit = false; private static readonly Cursor _invisibleCursor = new(StandardCursorType.None); private readonly nint _invisibleCursorWin; @@ -96,6 +98,7 @@ namespace Ryujinx.Ava private bool _isCursorInRenderer = true; private bool _ignoreCursorState = false; + private enum CursorStates { CursorIsHidden, @@ -503,10 +506,15 @@ namespace Ryujinx.Ava _viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value; MainLoop(); - + Exit(); } + public bool IsSpecialExit() + { + return spetialExit; + } + private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args) { if (Device != null) @@ -589,6 +597,7 @@ namespace Ryujinx.Ava _isStopped = true; Stop(); + } public void DisposeContext() @@ -1023,12 +1032,12 @@ namespace Ryujinx.Ava } private void MainLoop() - { + { while (UpdateFrame()) { - // Polling becomes expensive if it's not slept. Thread.Sleep(1); } + } private void RenderLoop() @@ -1135,6 +1144,7 @@ namespace Ryujinx.Ava string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; string vSyncMode = Device.VSyncMode.ToString(); + UpdateShaderCount(); if (GraphicsConfig.ResScale != 1) @@ -1200,7 +1210,29 @@ namespace Ryujinx.Ava return false; } - NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); + if (NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat())) + { + if (ConfigurationState.Instance.Hid.SpetialExitEmulator.Value == 1) + { + spetialExit = true; + } + + + _isActive = false; + } + + if (NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat())) + { + if (ConfigurationState.Instance.Hid.SpetialExitEmulator.Value == 1) + { + spetialExit = true; // close App + } + if (ConfigurationState.Instance.Hid.SpetialExitEmulator.Value > 0) + { + + _isActive = false; //close game + } + } if (_viewModel.IsActive) { @@ -1335,6 +1367,8 @@ namespace Ryujinx.Ava Device.Hid.DebugPad.Update(); + + return true; } diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 0951ad632..634cc60eb 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -147,6 +147,31 @@ "zh_TW": "滑鼠直接存取" } }, + { + "ID": "SettingsExtraCloseApp", + "Translations": { + "ar_SA": "خروج سريع من التطبيق", + "de_DE": "Schneller Ausstieg aus der Anwendung", + "el_GR": "Γρήγορη έξοδος από την εφαρμογή", + "en_US": "Quick Exit from Application", + "es_ES": "Salida rápida de la aplicación", + "fr_FR": "Sortie rapide de l'application", + "he_IL": "יציאה מהירה מהאפליקציה", + "it_IT": "Uscita rapida dall'applicazione", + "ja_JP": "アプリケーションからの迅速な終了", + "ko_KR": "애플리케이션에서 빠른 종료", + "no_NO": "Rask avslutning av applikasjonen", + "pl_PL": "Szybkie wyjście z aplikacji", + "pt_BR": "Saída rápida do aplicativo", + "ru_RU": "Быстрый выход из приложения", + "sv_SE": "Snabb avslutning från applikationen", + "th_TH": "ออกจากแอปพลิเคชันอย่างรวดเร็ว", + "tr_TR": "Uygulamadan Hızlı Çıkış", + "uk_UA": "Швидкий вихід з програми", + "zh_CN": "快速退出应用程序", + "zh_TW": "快速退出應用程式" + } + }, { "ID": "SettingsTabSystemMemoryManagerMode", "Translations": { @@ -11047,6 +11072,81 @@ "zh_TW": "淺色" } }, + { + "ID": "SettingsTabInputDisableExitHotKey", + "Translations": { + "ar_SA": "الخروج السريع معطل", + "de_DE": "Schneller Ausstieg deaktiviert", + "el_GR": "Η γρήγορη έξοδος είναι απενεργοποιημένη", + "en_US": "Quick exit disabled", + "es_ES": "Salida rápida desactivada", + "fr_FR": "Sortie rapide désactivée", + "he_IL": "יציאה מהירה מושבתת", + "it_IT": "Uscita rapida disabilitata", + "ja_JP": "クイック終了が無効です", + "ko_KR": "빠른 종료 비활성화됨", + "no_NO": "Rask avslutning er deaktivert", + "pl_PL": "Szybkie wyjście wyłączone", + "pt_BR": "Saída rápida desativada", + "ru_RU": "Быстрый выход выключен", + "sv_SE": "Snabb avslutning inaktiverad", + "th_TH": "ปิดใช้งานออกอย่างรวดเร็ว", + "tr_TR": "Hızlı çıkış devre dışı bırakıldı", + "uk_UA": "Швидкий вихід вимкнено", + "zh_CN": "快速退出已禁用", + "zh_TW": "快速退出已停用" + } + }, + { + "ID": "SettingsTabInputHotkeyIsCloseApp", + "Translations": { + "ar_SA": "إغلاق التطبيق بالضغط على الزرين '+' و '-'.", + "de_DE": "App schließen mit den '+' und '-' Tasten.", + "el_GR": "Κλείσιμο της εφαρμογής με τα κουμπιά '+' και '-'.", + "en_US": "Close app by '+' and '-' buttons.", + "es_ES": "Cerrar la aplicación pulsando los botones '+' y '-'.", + "fr_FR": "Fermer l'application avec les boutons '+' et '-'.", + "he_IL": "סגור את האפליקציה בלחיצה על '+' ו-'-'.", + "it_IT": "Chiudi l'app premendo i pulsanti '+' e '-'.", + "ja_JP": "「+」と「-」ボタンを押してアプリを終了します。", + "ko_KR": "'+' 및 '-' 버튼을 눌러 앱을 종료합니다.", + "no_NO": "Lukk appen med '+' og '-' knappene.", + "pl_PL": "Zamknij aplikację przyciskiem '+' i '-'.", + "pt_BR": "Fechar o aplicativo pressionando os botões '+' e '-'.", + "ru_RU": "Закрыть приложение нажатием '+' и '-'.", + "sv_SE": "Stäng appen med '+' och '-' knapparna.", + "th_TH": "ปิดแอปโดยกดปุ่ม '+' และ '-'.", + "tr_TR": "'+' ve '-' düğmelerine basarak uygulamayı kapatın.", + "uk_UA": "Закрити додаток натисканням '+' та '-'.", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "SettingsTabInputHotkeyIsCloseGame", + "Translations": { + "ar_SA": "الخروج من اللعبة بالضغط على الزرين '+' و '-'.", + "de_DE": "Spiel beenden mit den '+' und '-' Tasten.", + "el_GR": "Έξοδος από το παιχνίδι με τα κουμπιά '+' και '-'.", + "en_US": "Exit game by '+' and '-' buttons.", + "es_ES": "Salir del juego pulsando los botones '+' y '-'.", + "fr_FR": "Quitter le jeu avec les boutons '+' et '-'.", + "he_IL": "יציאה מהמשחק בלחיצה על '+' ו-'-'.", + "it_IT": "Esci dal gioco premendo i pulsanti '+' e '-'.", + "ja_JP": "「+」と「-」ボタンを押してゲームを終了します。", + "ko_KR": "'+' 및 '-' 버튼을 눌러 게임을 종료합니다.", + "no_NO": "Avslutt spillet med '+' og '-' knappene.", + "pl_PL": "Wyjście z gry przyciskiem '+' i '-'.", + "pt_BR": "Sair do jogo pressionando os botões '+' e '-'.", + "ru_RU": "Выйти из игры нажатием '+' и '-'.", + "sv_SE": "Avsluta spelet med '+' och '-' knapparna.", + "th_TH": "ออกจากเกมโดยกดปุ่ม '+' และ '-'.", + "tr_TR": "'+' ve '-' düğmelerine basarak oyundan çıkın.", + "uk_UA": "Вийти з гри натисканням '+' та '-'.", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "ControllerSettingsConfigureGeneral", "Translations": { @@ -15147,6 +15247,31 @@ "zh_TW": "支援滑鼠直接存取 (HID)。遊戲可將滑鼠作為指向裝置使用。\n\n僅適用於在 Switch 硬體上原生支援滑鼠控制的遊戲,這類遊戲很少。\n\n啟用後,觸控螢幕功能可能無法使用。\n\n如果不確定,請保持關閉狀態。" } }, + { + "ID": "spetialExitTooltip", + "Translations": { + "ar_SA": "يقوم بتفعيل مفاتيح الاختصار 'زائد' و 'ناقص'.\nاضغط على زرّي زائد وناقص في نفس الوقت لتنفيذ إحدى العمليات:\n\n1) إغلاق التطبيق باستخدام مفاتيح الاختصار.\n2) الخروج من اللعبة دون إغلاق التطبيق.\n\nيعمل فقط مع أذرع التحكم.", + "de_DE": "Aktiviert die Hotkeys 'Plus' und 'Minus'.\nDrücken Sie gleichzeitig die Plus- und Minus-Tasten, um eine der folgenden Aktionen auszuführen:\n\n1) Schließt die Anwendung durch Drücken der Hotkeys.\n2) Beendet das Spiel, ohne die Anwendung zu schließen.\n\nFunktioniert nur mit Gamepads.", + "el_GR": "Ενεργοποιεί τα πλήκτρα πρόσβασης 'συν' και 'πλην'.\nΠατήστε ταυτόχρονα τα κουμπιά συν και πλην για μία από τις ενέργειες:\n\n1) Κλείνει την εφαρμογή πατώντας τα πλήκτρα πρόσβασης.\n2) Εξέρχεται από το παιχνίδι χωρίς να κλείσει η εφαρμογή.\n\nΛειτουργεί μόνο με gamepads.", + "en_US": "Activates the hot keys 'plus' and 'minus'.\nPress buttons plus and minus at the same time to get one of the actions:\n\n1) Closes the application by pressing the hot keys.\n2) Exits the game without closing the application.\n\nWorks only with gamepads.", + "es_ES": "Activa las teclas rápidas 'más' y 'menos'.\nPresiona los botones más y menos al mismo tiempo para realizar una de las siguientes acciones:\n\n1) Cierra la aplicación presionando las teclas rápidas.\n2) Salir del juego sin cerrar la aplicación.\n\nFunciona solo con mandos.", + "fr_FR": "Active les raccourcis 'plus' et 'moins'.\nAppuyez simultanément sur les boutons plus et moins pour effectuer l'une des actions suivantes :\n\n1) Ferme l'application en appuyant sur les raccourcis.\n2) Quitte le jeu sans fermer l'application.\n\nFonctionne uniquement avec les manettes.", + "he_IL": "מפעיל את המקשים הקצרים 'פלוס' ו-'מינוס'.\nלחץ על הכפתורים פלוס ומינוס בו זמנית כדי לבצע אחת מהפעולות:\n\n1) סוגר את היישום באמצעות המקשים הקצרים.\n2) יוצא מהמשחק מבלי לסגור את היישום.\n\nפועל רק עם בקרי משחק.", + "it_IT": "Attiva i tasti rapidi 'più' e 'meno'.\nPremere i pulsanti più e meno contemporaneamente per eseguire una delle seguenti azioni:\n\n1) Chiude l'applicazione premendo i tasti rapidi.\n2) Esce dal gioco senza chiudere l'applicazione.\n\nFunziona solo con i gamepad.", + "ja_JP": "ホットキー「プラス」と「マイナス」を有効化します。\nプラスとマイナスのボタンを同時に押して、次のいずれかの操作を実行します:\n\n1) ホットキーを押すことでアプリを閉じます。\n2) アプリを閉じずにゲームを終了します。\n\nゲームパッドでのみ動作します。", + "ko_KR": "'플러스' 및 '마이너스' 단축키를 활성화합니다.\n플러스 및 마이너스 버튼을 동시에 눌러 다음 작업 중 하나를 수행합니다:\n\n1) 단축키를 눌러 애플리케이션을 닫습니다.\n2) 애플리케이션을 닫지 않고 게임을 종료합니다.\n\n게임패드에서만 작동합니다.", + "no_NO": "Aktiverer hurtigtastene 'pluss' og 'minus'.\nTrykk på knappene pluss og minus samtidig for å utføre en av følgende handlinger:\n\n1) Lukker applikasjonen ved å trykke på hurtigtastene.\n2) Avslutter spillet uten å lukke applikasjonen.\n\nFungerer kun med spillkontroller.", + "pl_PL": "Aktywuje klawisze skrótu 'plus' i 'minus'.\nNaciśnij jednocześnie przyciski plus i minus, aby wykonać jedną z akcji:\n\n1) Zamknij aplikację, naciskając klawisze skrótu.\n2) Wyjdź z gry bez zamykania aplikacji.\n\nDziała tylko z gamepadami.", + "pt_BR": "Ativa as teclas de atalho 'mais' e 'menos'.\nPressione os botões mais e menos ao mesmo tempo para realizar uma das ações:\n\n1) Fecha o aplicativo pressionando as teclas de atalho.\n2) Sai do jogo sem fechar o aplicativo.\n\nFunciona apenas com gamepads.", + "ru_RU": "Активирует горячие клавиши 'плюс' и 'минус'.\nНажмите одновременно кнопки плюс и минус чтобы получить одно из действий:\n\n1) Закрывает приложение по нажатию горячих кнопок.\n2) Выходит из игры без закрытия приложения.\n\nРаботает только с геймпадами.", + "sv_SE": "Aktiverar snabbtangenterna 'plus' och 'minus'.\nTryck samtidigt på plus- och minusknapparna för att utföra en av följande åtgärder:\n\n1) Stänger applikationen med snabbtangenterna.\n2) Avslutar spelet utan att stänga applikationen.\n\nFungerar bara med spelkontroller.", + "th_TH": "เปิดใช้งานปุ่มลัด '+' และ '-'.\nกดปุ่ม '+' และ '-' พร้อมกันเพื่อทำการอย่างใดอย่างหนึ่ง:\n\n1) ปิดแอปพลิเคชันด้วยการกดปุ่มลัด\n2) ออกจากเกมโดยไม่ปิดแอปพลิเคชัน\n\nใช้งานได้เฉพาะกับจอยเกม", + "tr_TR": "'Artı' ve 'eksi' kısayol tuşlarını etkinleştirir.\nArtı ve eksi tuşlarına aynı anda basarak aşağıdaki işlemlerden birini gerçekleştirin:\n\n1) Kısayol tuşlarına basarak uygulamayı kapatır.\n2) Uygulamayı kapatmadan oyundan çıkar.\n\nYalnızca gamepad'lerle çalışır.", + "uk_UA": "Активує гарячі клавіші '+' та '-'.\nНатисніть одночасно кнопки плюс та мінус для виконання однієї з дій:\n\n1) Закриває додаток за допомогою гарячих клавіш.\n2) Виходить із гри без закриття додатка.\n\nПрацює лише з геймпадами.", + "zh_CN": "激活快捷键“加号”和“减号”。\n同时按下加号和减号按钮以执行以下操作之一:\n\n1) 按快捷键关闭应用程序。\n2) 退出游戏而不关闭应用程序。\n\n仅适用于游戏手柄。", + "zh_TW": "啟用快捷鍵「加號」和「減號」。\n同時按下加號和減號按鈕以執行以下其中一項操作:\n\n1) 按快捷鍵關閉應用程式。\n2) 離開遊戲而不關閉應用程式。\n\n僅適用於遊戲手柄。" + } + }, { "ID": "RegionTooltip", "Translations": { diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 5730254f7..4be23245d 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -350,11 +350,11 @@ namespace Ryujinx.Headless { return options.GraphicsBackend switch { - GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet), + GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet, options.spetialExit), GraphicsBackend.Metal => OperatingSystem.IsMacOS() ? - new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet) : + new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet, options.spetialExit) : throw new Exception("Attempted to use Metal renderer on non-macOS platform!"), - _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet) + _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet, options.spetialExit) }; } diff --git a/src/Ryujinx/Headless/Metal/MetalWindow.cs b/src/Ryujinx/Headless/Metal/MetalWindow.cs index a2693c69d..3afe65ae1 100644 --- a/src/Ryujinx/Headless/Metal/MetalWindow.cs +++ b/src/Ryujinx/Headless/Metal/MetalWindow.cs @@ -23,8 +23,9 @@ namespace Ryujinx.Headless AspectRatio aspectRatio, bool enableMouse, HideCursorMode hideCursorMode, - bool ignoreControllerApplet) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) { } + bool ignoreControllerApplet, + int SpetialExitEmulator) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, SpetialExitEmulator) { } public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_METAL; diff --git a/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs b/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs index c00a0648f..eedf74017 100644 --- a/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs +++ b/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs @@ -118,8 +118,9 @@ namespace Ryujinx.Headless AspectRatio aspectRatio, bool enableMouse, HideCursorMode hideCursorMode, - bool ignoreControllerApplet) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) + bool ignoreControllerApplet, + int SpetialExitEmulator) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, SpetialExitEmulator) { _glLogLevel = glLogLevel; } diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index 0d7e46285..c4cedb4e2 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -150,7 +150,10 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(IgnoreControllerApplet))) IgnoreControllerApplet = configurationState.IgnoreApplet; - + + if (NeedsOverride(nameof(spetialExit))) + spetialExit = configurationState.Hid.SpetialExitEmulator; + return; bool NeedsOverride(string argKey) => originalArgs.None(arg => arg.TrimStart('-').EqualsIgnoreCase(OptionName(argKey))); @@ -274,6 +277,9 @@ namespace Ryujinx.Headless [Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")] public bool EnableMouse { get; set; } + [Option("enable-press-hotkeys-to-exit", Required = false, Default = 0, HelpText = "press the minus and plus buttons to: 0 -disable, 1 - exit app, 2 - exit game.")] + public int spetialExit { get; set; } + [Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")] public HideCursorMode HideCursorMode { get; set; } @@ -414,6 +420,7 @@ namespace Ryujinx.Headless [Option("ignore-controller-applet", Required = false, Default = false, HelpText = "Enable ignoring the controller applet when your game loses connection to your controller.")] public bool IgnoreControllerApplet { get; set; } + // Values [Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)] diff --git a/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs b/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs index 92caad34e..d31586718 100644 --- a/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs +++ b/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs @@ -18,8 +18,9 @@ namespace Ryujinx.Headless AspectRatio aspectRatio, bool enableMouse, HideCursorMode hideCursorMode, - bool ignoreControllerApplet) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) + bool ignoreControllerApplet, + int SpetialExitEmulator) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, SpetialExitEmulator) { _glLogLevel = glLogLevel; } diff --git a/src/Ryujinx/Headless/WindowBase.cs b/src/Ryujinx/Headless/WindowBase.cs index d89638cc1..f30280257 100644 --- a/src/Ryujinx/Headless/WindowBase.cs +++ b/src/Ryujinx/Headless/WindowBase.cs @@ -88,6 +88,7 @@ namespace Ryujinx.Headless private readonly AspectRatio _aspectRatio; private readonly bool _enableMouse; + private readonly int _SpetialExitEmulator; private readonly bool _ignoreControllerApplet; public WindowBase( @@ -96,7 +97,8 @@ namespace Ryujinx.Headless AspectRatio aspectRatio, bool enableMouse, HideCursorMode hideCursorMode, - bool ignoreControllerApplet) + bool ignoreControllerApplet, + int SpetialExitEmulator) { MouseDriver = new SDL2MouseDriver(hideCursorMode); _inputManager = inputManager; @@ -112,6 +114,7 @@ namespace Ryujinx.Headless _gpuDoneEvent = new ManualResetEvent(false); _aspectRatio = aspectRatio; _enableMouse = enableMouse; + _SpetialExitEmulator = SpetialExitEmulator; _ignoreControllerApplet = ignoreControllerApplet; HostUITheme = new HeadlessHostUiTheme(); diff --git a/src/Ryujinx/Input/AvaloniaKeyboard.cs b/src/Ryujinx/Input/AvaloniaKeyboard.cs index 0b63af2d9..12a4d6f46 100644 --- a/src/Ryujinx/Input/AvaloniaKeyboard.cs +++ b/src/Ryujinx/Input/AvaloniaKeyboard.cs @@ -30,6 +30,11 @@ namespace Ryujinx.Ava.Input public readonly Key From = from; } + public bool spetialExit() + { + return false; + } + public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name) { _buttonsUserMapping = []; diff --git a/src/Ryujinx/Input/AvaloniaMouse.cs b/src/Ryujinx/Input/AvaloniaMouse.cs index 1aa2d586a..871c16bc7 100644 --- a/src/Ryujinx/Input/AvaloniaMouse.cs +++ b/src/Ryujinx/Input/AvaloniaMouse.cs @@ -13,6 +13,11 @@ namespace Ryujinx.Ava.Input public string Id => "0"; public string Name => "AvaloniaMouse"; + public bool spetialExit() + { + return false; + } + public bool IsConnected => true; public GamepadFeaturesFlag Features => throw new NotImplementedException(); public bool[] Buttons => _driver.PressedButtons; diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index e11d855a6..25e1136b8 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -1046,6 +1046,7 @@ namespace Ryujinx.Ava.UI.ViewModels private void InitializeGame() { + RendererHostControl.WindowCreated += RendererHost_Created; AppHost.StatusUpdatedEvent += Update_StatusBar; @@ -1055,7 +1056,13 @@ namespace Ryujinx.Ava.UI.ViewModels AppHost?.Start(); + if (AppHost?.IsSpecialExit() == true) + { + Window.ForceExit(); + } + AppHost?.DisposeContext(); + } private async Task HandleRelaunch() diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 2678bbf98..bda93b83d 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -128,6 +128,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableDockedMode { get; set; } public bool EnableKeyboard { get; set; } public bool EnableMouse { get; set; } + public int EnableSpetialExit { get; set; } public VSyncMode VSyncMode { get => _vSyncMode; @@ -259,6 +260,8 @@ namespace Ryujinx.Ava.UI.ViewModels public int OpenglDebugLevel { get; set; } public int MemoryMode { get; set; } public int BaseStyleIndex { get; set; } + + public int GraphicsBackendIndex { get => _graphicsBackendIndex; @@ -511,6 +514,13 @@ namespace Ryujinx.Ava.UI.ViewModels EnableDockedMode = config.System.EnableDockedMode; EnableKeyboard = config.Hid.EnableKeyboard; EnableMouse = config.Hid.EnableMouse; + EnableSpetialExit = config.Hid.SpetialExitEmulator.Value switch + { + 0=> 0, //"Hotkey 'Exit' is Disabled" + 1=> 1, //"Close app. by hotkey" + 2=> 2, // "Close game by hotkey" + _ => 0 //"Hotkey 'Exit' is Disabled" + }; // Keyboard Hotkeys KeyboardHotkey = new HotkeyConfig(config.Hid.Hotkeys.Value); @@ -618,6 +628,13 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableDockedMode.Value = EnableDockedMode; config.Hid.EnableKeyboard.Value = EnableKeyboard; config.Hid.EnableMouse.Value = EnableMouse; + config.Hid.SpetialExitEmulator.Value = EnableSpetialExit switch + { + 0 => 0, //"Hotkey 'Exit' is Disabled", + 1 => 1, //"Close app. by hotkey", + 2 => 2, //"Close game by hotkey", + _ => 0, //"Hotkey 'Exit' is Disabled" + }; // Keyboard Hotkeys config.Hid.Hotkeys.Value = KeyboardHotkey.GetConfig(); diff --git a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml index b0edc51a5..187491233 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml @@ -1,4 +1,4 @@ - + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index e5a815b28..baee0f66f 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -45,6 +45,7 @@ namespace Ryujinx.Ava.UI.Windows internal readonly AvaHostUIHandler UiHandler; private bool _isLoading; + private bool _isExitWithoutConfirm = false; private bool _applicationsLoadedOnce; private UserChannelPersistence _userChannelPersistence; @@ -574,11 +575,11 @@ namespace Ryujinx.Ava.UI.Windows protected override void OnClosing(WindowClosingEventArgs e) { - if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit) + if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit && !_isExitWithoutConfirm) { e.Cancel = true; - ConfirmExit(); + ConfirmExit(); return; } @@ -619,6 +620,12 @@ namespace Ryujinx.Ava.UI.Windows base.OnClosing(e); } + public void ForceExit() { + + _isExitWithoutConfirm = true; + Close(); + } + private void ConfirmExit() { Dispatcher.UIThread.InvokeAsync(async () => diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs index 947dd5c8f..96d5543ab 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Utilities.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 59; + public const int CurrentVersion = 60; /// /// Version of the configuration file format @@ -366,6 +366,11 @@ namespace Ryujinx.Ava.Utilities.Configuration /// public bool EnableMouse { get; set; } + /// + /// Allows you to choose from three options: do nothing, exit the application, exit the emulator + /// + public int SpetialExitEmulator { get; set; } + /// /// Hotkey Keyboard Bindings /// diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index ec66bcaac..c627fe29a 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -136,6 +136,7 @@ namespace Ryujinx.Ava.Utilities.Configuration Hid.EnableKeyboard.Value = cff.EnableKeyboard; Hid.EnableMouse.Value = cff.EnableMouse; + Hid.SpetialExitEmulator.Value = cff.SpetialExitEmulator; Hid.Hotkeys.Value = cff.Hotkeys; Hid.InputConfig.Value = cff.InputConfig ?? []; @@ -414,6 +415,10 @@ namespace Ryujinx.Ava.Utilities.Configuration // This was accidentally enabled by default when it was PRed. That is not what we want, // so as a compromise users who want to use it will simply need to re-enable it once after updating. cff.IgnoreApplet = false; + }), + (60, static cff => + { + cff.SpetialExitEmulator = 1; }) ); } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs index fe5f2c3ad..6965c8efc 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs @@ -420,6 +420,11 @@ namespace Ryujinx.Ava.Utilities.Configuration /// public ReactiveObject EnableMouse { get; private set; } + /// + /// Allows you to choose from three options: do nothing, exit the application, exit the emulator + /// + public ReactiveObject SpetialExitEmulator { get; private set; } + /// /// Hotkey Keyboard Bindings /// @@ -436,6 +441,7 @@ namespace Ryujinx.Ava.Utilities.Configuration { EnableKeyboard = new ReactiveObject(); EnableMouse = new ReactiveObject(); + SpetialExitEmulator = new ReactiveObject(); Hotkeys = new ReactiveObject(); InputConfig = new ReactiveObject>(); } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 95ec62e83..20c120f7d 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -128,6 +128,7 @@ namespace Ryujinx.Ava.Utilities.Configuration ShowConsole = UI.ShowConsole, EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, + SpetialExitEmulator = Hid.SpetialExitEmulator, Hotkeys = Hid.Hotkeys, KeyboardConfig = [], ControllerConfig = [], @@ -241,6 +242,7 @@ namespace Ryujinx.Ava.Utilities.Configuration UI.WindowStartup.WindowMaximized.Value = false; Hid.EnableKeyboard.Value = false; Hid.EnableMouse.Value = false; + Hid.SpetialExitEmulator.Value = 0; Hid.Hotkeys.Value = new KeyboardHotkeys { ToggleVSyncMode = Key.F1, -- 2.47.1 From b6667a8352fb4fc07728581b1a325e77047dd622 Mon Sep 17 00:00:00 2001 From: Vova Date: Wed, 8 Jan 2025 22:45:33 +1000 Subject: [PATCH 20/30] multiple fixes, variable typo fixes, adherence to a certain style. Fixed initialization of the new function, defaults to 0 --- src/Ryujinx.Input.SDL2/SDL2Gamepad.cs | 10 ++++----- src/Ryujinx.Input.SDL2/SDL2Keyboard.cs | 2 +- src/Ryujinx.Input.SDL2/SDL2Mouse.cs | 2 +- src/Ryujinx.Input/HLE/NpadController.cs | 2 +- src/Ryujinx.Input/HLE/NpadManager.cs | 8 ++++--- src/Ryujinx.Input/IGamepad.cs | 2 +- src/Ryujinx/AppHost.cs | 14 ++++++------ src/Ryujinx/Assets/locales.json | 2 +- src/Ryujinx/Headless/HeadlessRyujinx.cs | 6 ++--- src/Ryujinx/Headless/Metal/MetalWindow.cs | 4 ++-- src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs | 4 ++-- src/Ryujinx/Headless/Options.cs | 6 ++--- src/Ryujinx/Headless/Vulkan/VulkanWindow.cs | 4 ++-- src/Ryujinx/Headless/WindowBase.cs | 6 ++--- src/Ryujinx/Input/AvaloniaKeyboard.cs | 2 +- src/Ryujinx/Input/AvaloniaMouse.cs | 2 +- .../UI/ViewModels/SettingsViewModel.cs | 22 +++++++++---------- .../UI/Views/Settings/SettingsInputView.axaml | 4 ++-- .../Configuration/ConfigurationFileFormat.cs | 2 +- .../ConfigurationState.Migration.cs | 4 ++-- .../Configuration/ConfigurationState.Model.cs | 4 ++-- .../Configuration/ConfigurationState.cs | 4 ++-- 22 files changed, 59 insertions(+), 57 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index 26aa496a3..7e8bd4167 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -254,16 +254,16 @@ namespace Ryujinx.Input.SDL2 } private static bool hotButtonMinus = false; - private static bool HotExit = false; + private static bool hotExit = false; - public bool spetialExit() + public bool SpecialExit() { if (hotButtonMinus) { hotButtonMinus = false; - return HotExit; + return hotExit; } - return HotExit = false; + return hotExit = false; } public GamepadStateSnapshot GetMappedStateSnapshot() @@ -299,7 +299,7 @@ namespace Ryujinx.Input.SDL2 if (rawState.IsPressed(entry.To) && hotButtonMinus) { - HotExit = true; + hotExit = true; } } diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs index 0fd25a54e..ab01a9bbc 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs @@ -329,7 +329,7 @@ namespace Ryujinx.Input.SDL2 return result; } - public bool spetialExit() + public bool SpecialExit() { return false; } diff --git a/src/Ryujinx.Input.SDL2/SDL2Mouse.cs b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs index d3f64d5ed..da0622db3 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Mouse.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Input.SDL2 { _driver = driver; } - public bool spetialExit() + public bool SpecialExit() { return false; } diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index 991311d02..f41c2af7f 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -284,7 +284,7 @@ namespace Ryujinx.Input.HLE { State = gamepad.GetMappedStateSnapshot(); - if (gamepad.spetialExit()) + if (gamepad.SpecialExit()) { return true; } diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 6955f1d2e..9d07d9c22 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -202,7 +202,7 @@ namespace Ryujinx.Input.HLE public bool Update(float aspectRatio = 1) { - bool spetialExit = false; + bool specialExit = false; lock (_lock) { @@ -227,7 +227,9 @@ namespace Ryujinx.Input.HLE DriverConfigurationUpdate(ref controller, inputConfig); controller.UpdateUserConfiguration(inputConfig); - spetialExit = controller.Update(); //hotkey press check + + specialExit = controller.Update(); //hotkey press check + controller.UpdateRumble(_device.Hid.Npads.GetRumbleQueue(playerIndex)); inputState = controller.GetHLEInputState(); @@ -318,7 +320,7 @@ namespace Ryujinx.Input.HLE _device.TamperMachine.UpdateInput(hleInputStates); } - return spetialExit; + return specialExit; } internal InputConfig GetPlayerInputConfigByIndex(int index) diff --git a/src/Ryujinx.Input/IGamepad.cs b/src/Ryujinx.Input/IGamepad.cs index 8e6a8e33f..f52703e19 100644 --- a/src/Ryujinx.Input/IGamepad.cs +++ b/src/Ryujinx.Input/IGamepad.cs @@ -83,7 +83,7 @@ namespace Ryujinx.Input /// Gets the state if the minus and plus buttons were pressed on the gamepad. /// /// returns true if the buttons were pressed. - bool spetialExit(); + bool SpecialExit(); /// /// Get a snaphost of the state of the gamepad. diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index c16cd5e11..c98c7c964 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Ava private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. private const int TargetFps = 60; private const float VolumeDelta = 0.05f; - static bool spetialExit = false; + static bool SpecialExit = false; private static readonly Cursor _invisibleCursor = new(StandardCursorType.None); private readonly nint _invisibleCursorWin; @@ -512,7 +512,7 @@ namespace Ryujinx.Ava public bool IsSpecialExit() { - return spetialExit; + return SpecialExit; } private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args) @@ -1212,9 +1212,9 @@ namespace Ryujinx.Ava if (NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat())) { - if (ConfigurationState.Instance.Hid.SpetialExitEmulator.Value == 1) + if (ConfigurationState.Instance.Hid.specialExitEmulator.Value == 1) { - spetialExit = true; + SpecialExit = true; } @@ -1223,11 +1223,11 @@ namespace Ryujinx.Ava if (NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat())) { - if (ConfigurationState.Instance.Hid.SpetialExitEmulator.Value == 1) + if (ConfigurationState.Instance.Hid.specialExitEmulator.Value == 1) { - spetialExit = true; // close App + SpecialExit = true; // close App } - if (ConfigurationState.Instance.Hid.SpetialExitEmulator.Value > 0) + if (ConfigurationState.Instance.Hid.specialExitEmulator.Value > 0) { _isActive = false; //close game diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 634cc60eb..7c4978f0f 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -15248,7 +15248,7 @@ } }, { - "ID": "spetialExitTooltip", + "ID": "SpecialExitTooltip", "Translations": { "ar_SA": "يقوم بتفعيل مفاتيح الاختصار 'زائد' و 'ناقص'.\nاضغط على زرّي زائد وناقص في نفس الوقت لتنفيذ إحدى العمليات:\n\n1) إغلاق التطبيق باستخدام مفاتيح الاختصار.\n2) الخروج من اللعبة دون إغلاق التطبيق.\n\nيعمل فقط مع أذرع التحكم.", "de_DE": "Aktiviert die Hotkeys 'Plus' und 'Minus'.\nDrücken Sie gleichzeitig die Plus- und Minus-Tasten, um eine der folgenden Aktionen auszuführen:\n\n1) Schließt die Anwendung durch Drücken der Hotkeys.\n2) Beendet das Spiel, ohne die Anwendung zu schließen.\n\nFunktioniert nur mit Gamepads.", diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 4be23245d..98636d4ff 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -350,11 +350,11 @@ namespace Ryujinx.Headless { return options.GraphicsBackend switch { - GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet, options.spetialExit), + GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet, options.SpecialExit), GraphicsBackend.Metal => OperatingSystem.IsMacOS() ? - new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet, options.spetialExit) : + new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet, options.SpecialExit) : throw new Exception("Attempted to use Metal renderer on non-macOS platform!"), - _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet, options.spetialExit) + _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet, options.SpecialExit) }; } diff --git a/src/Ryujinx/Headless/Metal/MetalWindow.cs b/src/Ryujinx/Headless/Metal/MetalWindow.cs index 3afe65ae1..1ae8f5ee4 100644 --- a/src/Ryujinx/Headless/Metal/MetalWindow.cs +++ b/src/Ryujinx/Headless/Metal/MetalWindow.cs @@ -24,8 +24,8 @@ namespace Ryujinx.Headless bool enableMouse, HideCursorMode hideCursorMode, bool ignoreControllerApplet, - int SpetialExitEmulator) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, SpetialExitEmulator) { } + int specialExitEmulator) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, specialExitEmulator) { } public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_METAL; diff --git a/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs b/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs index eedf74017..ca4f48861 100644 --- a/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs +++ b/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs @@ -119,8 +119,8 @@ namespace Ryujinx.Headless bool enableMouse, HideCursorMode hideCursorMode, bool ignoreControllerApplet, - int SpetialExitEmulator) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, SpetialExitEmulator) + int specialExitEmulator) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, specialExitEmulator) { _glLogLevel = glLogLevel; } diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index c4cedb4e2..62edea497 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -151,8 +151,8 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(IgnoreControllerApplet))) IgnoreControllerApplet = configurationState.IgnoreApplet; - if (NeedsOverride(nameof(spetialExit))) - spetialExit = configurationState.Hid.SpetialExitEmulator; + if (NeedsOverride(nameof(SpecialExit))) + SpecialExit = configurationState.Hid.specialExitEmulator; return; @@ -278,7 +278,7 @@ namespace Ryujinx.Headless public bool EnableMouse { get; set; } [Option("enable-press-hotkeys-to-exit", Required = false, Default = 0, HelpText = "press the minus and plus buttons to: 0 -disable, 1 - exit app, 2 - exit game.")] - public int spetialExit { get; set; } + public int SpecialExit { get; set; } [Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")] public HideCursorMode HideCursorMode { get; set; } diff --git a/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs b/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs index d31586718..9819bbc9b 100644 --- a/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs +++ b/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs @@ -19,8 +19,8 @@ namespace Ryujinx.Headless bool enableMouse, HideCursorMode hideCursorMode, bool ignoreControllerApplet, - int SpetialExitEmulator) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, SpetialExitEmulator) + int specialExitEmulator) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, specialExitEmulator) { _glLogLevel = glLogLevel; } diff --git a/src/Ryujinx/Headless/WindowBase.cs b/src/Ryujinx/Headless/WindowBase.cs index f30280257..4e4d5a4d4 100644 --- a/src/Ryujinx/Headless/WindowBase.cs +++ b/src/Ryujinx/Headless/WindowBase.cs @@ -88,7 +88,7 @@ namespace Ryujinx.Headless private readonly AspectRatio _aspectRatio; private readonly bool _enableMouse; - private readonly int _SpetialExitEmulator; + private readonly int _specialExitEmulator; private readonly bool _ignoreControllerApplet; public WindowBase( @@ -98,7 +98,7 @@ namespace Ryujinx.Headless bool enableMouse, HideCursorMode hideCursorMode, bool ignoreControllerApplet, - int SpetialExitEmulator) + int specialExitEmulator) { MouseDriver = new SDL2MouseDriver(hideCursorMode); _inputManager = inputManager; @@ -114,7 +114,7 @@ namespace Ryujinx.Headless _gpuDoneEvent = new ManualResetEvent(false); _aspectRatio = aspectRatio; _enableMouse = enableMouse; - _SpetialExitEmulator = SpetialExitEmulator; + _specialExitEmulator = specialExitEmulator; _ignoreControllerApplet = ignoreControllerApplet; HostUITheme = new HeadlessHostUiTheme(); diff --git a/src/Ryujinx/Input/AvaloniaKeyboard.cs b/src/Ryujinx/Input/AvaloniaKeyboard.cs index 12a4d6f46..8936513ca 100644 --- a/src/Ryujinx/Input/AvaloniaKeyboard.cs +++ b/src/Ryujinx/Input/AvaloniaKeyboard.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Ava.Input public readonly Key From = from; } - public bool spetialExit() + public bool SpecialExit() { return false; } diff --git a/src/Ryujinx/Input/AvaloniaMouse.cs b/src/Ryujinx/Input/AvaloniaMouse.cs index 871c16bc7..cdcdc2106 100644 --- a/src/Ryujinx/Input/AvaloniaMouse.cs +++ b/src/Ryujinx/Input/AvaloniaMouse.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Ava.Input public string Id => "0"; public string Name => "AvaloniaMouse"; - public bool spetialExit() + public bool SpecialExit() { return false; } diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index bda93b83d..bbb78e912 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -128,7 +128,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool EnableDockedMode { get; set; } public bool EnableKeyboard { get; set; } public bool EnableMouse { get; set; } - public int EnableSpetialExit { get; set; } + public int EnableSpecialExit { get; set; } public VSyncMode VSyncMode { get => _vSyncMode; @@ -514,12 +514,12 @@ namespace Ryujinx.Ava.UI.ViewModels EnableDockedMode = config.System.EnableDockedMode; EnableKeyboard = config.Hid.EnableKeyboard; EnableMouse = config.Hid.EnableMouse; - EnableSpetialExit = config.Hid.SpetialExitEmulator.Value switch + EnableSpecialExit = config.Hid.specialExitEmulator.Value switch { - 0=> 0, //"Hotkey 'Exit' is Disabled" - 1=> 1, //"Close app. by hotkey" - 2=> 2, // "Close game by hotkey" - _ => 0 //"Hotkey 'Exit' is Disabled" + 0=> 0, // "Hotkey 'Exit' is Disabled" + 1=> 1, // "Close app. by hotkey" + 2=> 2, // "Close game by hotkey" + _ => 0 // "Hotkey 'Exit' is Disabled" }; // Keyboard Hotkeys @@ -628,12 +628,12 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableDockedMode.Value = EnableDockedMode; config.Hid.EnableKeyboard.Value = EnableKeyboard; config.Hid.EnableMouse.Value = EnableMouse; - config.Hid.SpetialExitEmulator.Value = EnableSpetialExit switch + config.Hid.specialExitEmulator.Value = EnableSpecialExit switch { - 0 => 0, //"Hotkey 'Exit' is Disabled", - 1 => 1, //"Close app. by hotkey", - 2 => 2, //"Close game by hotkey", - _ => 0, //"Hotkey 'Exit' is Disabled" + 0 => 0, // "Hotkey 'Exit' is Disabled", + 1 => 1, // "Close app. by hotkey", + 2 => 2, // "Close game by hotkey", + _ => 0, // "Hotkey 'Exit' is Disabled" }; // Keyboard Hotkeys diff --git a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml index 187491233..033352548 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml @@ -58,8 +58,8 @@ - diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs index 96d5543ab..9fb2169d9 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs @@ -369,7 +369,7 @@ namespace Ryujinx.Ava.Utilities.Configuration /// /// Allows you to choose from three options: do nothing, exit the application, exit the emulator /// - public int SpetialExitEmulator { get; set; } + public int specialExitEmulator { get; set; } /// /// Hotkey Keyboard Bindings diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index c627fe29a..2308a1508 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -136,7 +136,7 @@ namespace Ryujinx.Ava.Utilities.Configuration Hid.EnableKeyboard.Value = cff.EnableKeyboard; Hid.EnableMouse.Value = cff.EnableMouse; - Hid.SpetialExitEmulator.Value = cff.SpetialExitEmulator; + Hid.specialExitEmulator.Value = cff.specialExitEmulator; Hid.Hotkeys.Value = cff.Hotkeys; Hid.InputConfig.Value = cff.InputConfig ?? []; @@ -418,7 +418,7 @@ namespace Ryujinx.Ava.Utilities.Configuration }), (60, static cff => { - cff.SpetialExitEmulator = 1; + cff.specialExitEmulator = 0; }) ); } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs index 6965c8efc..e19b128a6 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs @@ -423,7 +423,7 @@ namespace Ryujinx.Ava.Utilities.Configuration /// /// Allows you to choose from three options: do nothing, exit the application, exit the emulator /// - public ReactiveObject SpetialExitEmulator { get; private set; } + public ReactiveObject specialExitEmulator { get; private set; } /// /// Hotkey Keyboard Bindings @@ -441,7 +441,7 @@ namespace Ryujinx.Ava.Utilities.Configuration { EnableKeyboard = new ReactiveObject(); EnableMouse = new ReactiveObject(); - SpetialExitEmulator = new ReactiveObject(); + specialExitEmulator = new ReactiveObject(); Hotkeys = new ReactiveObject(); InputConfig = new ReactiveObject>(); } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 20c120f7d..861dae74b 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -128,7 +128,7 @@ namespace Ryujinx.Ava.Utilities.Configuration ShowConsole = UI.ShowConsole, EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, - SpetialExitEmulator = Hid.SpetialExitEmulator, + specialExitEmulator = Hid.specialExitEmulator, Hotkeys = Hid.Hotkeys, KeyboardConfig = [], ControllerConfig = [], @@ -242,7 +242,7 @@ namespace Ryujinx.Ava.Utilities.Configuration UI.WindowStartup.WindowMaximized.Value = false; Hid.EnableKeyboard.Value = false; Hid.EnableMouse.Value = false; - Hid.SpetialExitEmulator.Value = 0; + Hid.specialExitEmulator.Value = 0; Hid.Hotkeys.Value = new KeyboardHotkeys { ToggleVSyncMode = Key.F1, -- 2.47.1 From c20452be61e6c7ce89ac6d8db885a9950553be41 Mon Sep 17 00:00:00 2001 From: Vova Date: Wed, 8 Jan 2025 23:00:41 +1000 Subject: [PATCH 21/30] Fixed a bug where the emulator would still terminate the game when pressing a hotkey (unnecessary check removed) --- src/Ryujinx/AppHost.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index c98c7c964..910f1213e 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1210,17 +1210,6 @@ namespace Ryujinx.Ava return false; } - if (NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat())) - { - if (ConfigurationState.Instance.Hid.specialExitEmulator.Value == 1) - { - SpecialExit = true; - } - - - _isActive = false; - } - if (NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat())) { if (ConfigurationState.Instance.Hid.specialExitEmulator.Value == 1) @@ -1229,7 +1218,6 @@ namespace Ryujinx.Ava } if (ConfigurationState.Instance.Hid.specialExitEmulator.Value > 0) { - _isActive = false; //close game } } -- 2.47.1 From 1c6390cbfbcbaaec6a977391e4e1ed99814fadbe Mon Sep 17 00:00:00 2001 From: Vova Date: Wed, 8 Jan 2025 23:05:58 +1000 Subject: [PATCH 22/30] minor bugs fixed --- src/Ryujinx.Input/HLE/NpadManager.cs | 1 - src/Ryujinx/UI/ViewModels/SettingsViewModel.cs | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 9d07d9c22..4ebb8401d 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -229,7 +229,6 @@ namespace Ryujinx.Input.HLE controller.UpdateUserConfiguration(inputConfig); specialExit = controller.Update(); //hotkey press check - controller.UpdateRumble(_device.Hid.Npads.GetRumbleQueue(playerIndex)); inputState = controller.GetHLEInputState(); diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index bbb78e912..0950b3174 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -516,10 +516,10 @@ namespace Ryujinx.Ava.UI.ViewModels EnableMouse = config.Hid.EnableMouse; EnableSpecialExit = config.Hid.specialExitEmulator.Value switch { - 0=> 0, // "Hotkey 'Exit' is Disabled" - 1=> 1, // "Close app. by hotkey" - 2=> 2, // "Close game by hotkey" - _ => 0 // "Hotkey 'Exit' is Disabled" + 0=> 0, // "Hotkey 'Exit' is Disabled" + 1=> 1, // "Close app. by hotkey" + 2=> 2, // "Close game by hotkey" + _=> 0 // "Hotkey 'Exit' is Disabled" }; // Keyboard Hotkeys -- 2.47.1 From 0c503e10aee982ed54fca18a9ad2998225e82643 Mon Sep 17 00:00:00 2001 From: Vladimir Sokolov Date: Wed, 8 Jan 2025 23:09:09 +1000 Subject: [PATCH 23/30] Update SettingsViewModel.cs --- src/Ryujinx/UI/ViewModels/SettingsViewModel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 0950b3174..462b6c559 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -516,10 +516,10 @@ namespace Ryujinx.Ava.UI.ViewModels EnableMouse = config.Hid.EnableMouse; EnableSpecialExit = config.Hid.specialExitEmulator.Value switch { - 0=> 0, // "Hotkey 'Exit' is Disabled" - 1=> 1, // "Close app. by hotkey" - 2=> 2, // "Close game by hotkey" - _=> 0 // "Hotkey 'Exit' is Disabled" + 0 => 0, // "Hotkey 'Exit' is Disabled" + 1 => 1, // "Close app. by hotkey" + 2 => 2, // "Close game by hotkey" + _ => 0 // "Hotkey 'Exit' is Disabled" }; // Keyboard Hotkeys -- 2.47.1 From 3352d70ea4ef3d07f18e28232d2c6efa102a420c Mon Sep 17 00:00:00 2001 From: Vladimir Sokolov Date: Sat, 11 Jan 2025 10:43:38 +1000 Subject: [PATCH 24/30] Update SettingsViewModel.cs small fix --- src/Ryujinx/UI/ViewModels/SettingsViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 462b6c559..e99db5d74 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -519,7 +519,7 @@ namespace Ryujinx.Ava.UI.ViewModels 0 => 0, // "Hotkey 'Exit' is Disabled" 1 => 1, // "Close app. by hotkey" 2 => 2, // "Close game by hotkey" - _ => 0 // "Hotkey 'Exit' is Disabled" + _ => 0 }; // Keyboard Hotkeys @@ -633,7 +633,7 @@ namespace Ryujinx.Ava.UI.ViewModels 0 => 0, // "Hotkey 'Exit' is Disabled", 1 => 1, // "Close app. by hotkey", 2 => 2, // "Close game by hotkey", - _ => 0, // "Hotkey 'Exit' is Disabled" + _ => 0 }; // Keyboard Hotkeys -- 2.47.1 From 01e22f1c6707220570aab0e9b4adc471ff05b839 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Fri, 10 Jan 2025 21:13:39 -0600 Subject: [PATCH 25/30] newline brace --- src/Ryujinx/UI/Windows/MainWindow.axaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index baee0f66f..f5d23642c 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -620,8 +620,8 @@ namespace Ryujinx.Ava.UI.Windows base.OnClosing(e); } - public void ForceExit() { - + public void ForceExit() + { _isExitWithoutConfirm = true; Close(); } -- 2.47.1 From 78e7a3085aa189800c12daf05dd2c9efe11cdcba Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Fri, 10 Jan 2025 21:16:31 -0600 Subject: [PATCH 26/30] add back a comment that was removed for no reason --- src/Ryujinx/AppHost.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 910f1213e..d5b6cce50 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1032,12 +1032,12 @@ namespace Ryujinx.Ava } private void MainLoop() - { + { while (UpdateFrame()) { + // Polling becomes expensive if it's not slept. Thread.Sleep(1); } - } private void RenderLoop() -- 2.47.1 From 11f1922a82e870c37a849d6ea8a5d8801a6fe692 Mon Sep 17 00:00:00 2001 From: Vova Date: Sat, 11 Jan 2025 22:20:08 +1000 Subject: [PATCH 27/30] fix specialExitEmulator -> SpecialExitEmulator, Added description to clarify function --- src/Ryujinx/AppHost.cs | 4 ++-- src/Ryujinx/Headless/Options.cs | 2 +- src/Ryujinx/UI/ViewModels/SettingsViewModel.cs | 4 ++-- .../Utilities/Configuration/ConfigurationFileFormat.cs | 2 +- .../Configuration/ConfigurationState.Migration.cs | 2 +- .../Utilities/Configuration/ConfigurationState.Model.cs | 8 +++++--- src/Ryujinx/Utilities/Configuration/ConfigurationState.cs | 4 ++-- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index d5b6cce50..c94e869d5 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -1212,11 +1212,11 @@ namespace Ryujinx.Ava if (NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat())) { - if (ConfigurationState.Instance.Hid.specialExitEmulator.Value == 1) + if (ConfigurationState.Instance.Hid.SpecialExitEmulator.Value == 1) { SpecialExit = true; // close App } - if (ConfigurationState.Instance.Hid.specialExitEmulator.Value > 0) + if (ConfigurationState.Instance.Hid.SpecialExitEmulator.Value > 0) { _isActive = false; //close game } diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index 62edea497..c575416be 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -152,7 +152,7 @@ namespace Ryujinx.Headless IgnoreControllerApplet = configurationState.IgnoreApplet; if (NeedsOverride(nameof(SpecialExit))) - SpecialExit = configurationState.Hid.specialExitEmulator; + SpecialExit = configurationState.Hid.SpecialExitEmulator; return; diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index e99db5d74..ad82b4d62 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -514,7 +514,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableDockedMode = config.System.EnableDockedMode; EnableKeyboard = config.Hid.EnableKeyboard; EnableMouse = config.Hid.EnableMouse; - EnableSpecialExit = config.Hid.specialExitEmulator.Value switch + EnableSpecialExit = config.Hid.SpecialExitEmulator.Value switch { 0 => 0, // "Hotkey 'Exit' is Disabled" 1 => 1, // "Close app. by hotkey" @@ -628,7 +628,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableDockedMode.Value = EnableDockedMode; config.Hid.EnableKeyboard.Value = EnableKeyboard; config.Hid.EnableMouse.Value = EnableMouse; - config.Hid.specialExitEmulator.Value = EnableSpecialExit switch + config.Hid.SpecialExitEmulator.Value = EnableSpecialExit switch { 0 => 0, // "Hotkey 'Exit' is Disabled", 1 => 1, // "Close app. by hotkey", diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs index 9fb2169d9..ab2e36aae 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs @@ -369,7 +369,7 @@ namespace Ryujinx.Ava.Utilities.Configuration /// /// Allows you to choose from three options: do nothing, exit the application, exit the emulator /// - public int specialExitEmulator { get; set; } + public int SpecialExitEmulator { get; set; } /// /// Hotkey Keyboard Bindings diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index 2308a1508..4fb189c76 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -136,7 +136,7 @@ namespace Ryujinx.Ava.Utilities.Configuration Hid.EnableKeyboard.Value = cff.EnableKeyboard; Hid.EnableMouse.Value = cff.EnableMouse; - Hid.specialExitEmulator.Value = cff.specialExitEmulator; + Hid.SpecialExitEmulator.Value = cff.SpecialExitEmulator; Hid.Hotkeys.Value = cff.Hotkeys; Hid.InputConfig.Value = cff.InputConfig ?? []; diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs index e19b128a6..85bede2d5 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs @@ -421,9 +421,11 @@ namespace Ryujinx.Ava.Utilities.Configuration public ReactiveObject EnableMouse { get; private set; } /// - /// Allows you to choose from three options: do nothing, exit the application, exit the emulator + /// Allows you to choose one of several behaviors when pressing hotkeys: + /// 0 - Do nothing, 1 - Close the emulator application, 2 - Exit the game. /// - public ReactiveObject specialExitEmulator { get; private set; } + public ReactiveObject SpecialExitEmulator { get; private set; } + /// /// Hotkey Keyboard Bindings @@ -441,7 +443,7 @@ namespace Ryujinx.Ava.Utilities.Configuration { EnableKeyboard = new ReactiveObject(); EnableMouse = new ReactiveObject(); - specialExitEmulator = new ReactiveObject(); + SpecialExitEmulator = new ReactiveObject(); Hotkeys = new ReactiveObject(); InputConfig = new ReactiveObject>(); } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 861dae74b..b68f97826 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -128,7 +128,7 @@ namespace Ryujinx.Ava.Utilities.Configuration ShowConsole = UI.ShowConsole, EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, - specialExitEmulator = Hid.specialExitEmulator, + SpecialExitEmulator = Hid.SpecialExitEmulator, Hotkeys = Hid.Hotkeys, KeyboardConfig = [], ControllerConfig = [], @@ -242,7 +242,7 @@ namespace Ryujinx.Ava.Utilities.Configuration UI.WindowStartup.WindowMaximized.Value = false; Hid.EnableKeyboard.Value = false; Hid.EnableMouse.Value = false; - Hid.specialExitEmulator.Value = 0; + Hid.SpecialExitEmulator.Value = 0; Hid.Hotkeys.Value = new KeyboardHotkeys { ToggleVSyncMode = Key.F1, -- 2.47.1 From f75efbea5400075efd22c461977b363cfd6a0e71 Mon Sep 17 00:00:00 2001 From: Vova Date: Sat, 11 Jan 2025 22:27:28 +1000 Subject: [PATCH 28/30] oops, rename to"SpecialExitEmulator" --- .../Utilities/Configuration/ConfigurationState.Migration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index 4fb189c76..f341c5f15 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -418,7 +418,7 @@ namespace Ryujinx.Ava.Utilities.Configuration }), (60, static cff => { - cff.specialExitEmulator = 0; + cff.SpecialExitEmulator = 0; }) ); } -- 2.47.1 From 2ec032c48bcc642f6b654e44460df25c3b121cb6 Mon Sep 17 00:00:00 2001 From: Vova Date: Sat, 11 Jan 2025 22:37:33 +1000 Subject: [PATCH 29/30] Added description in another file --- src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs index ab2e36aae..a0cfaec0c 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs @@ -367,7 +367,8 @@ namespace Ryujinx.Ava.Utilities.Configuration public bool EnableMouse { get; set; } /// - /// Allows you to choose from three options: do nothing, exit the application, exit the emulator + /// Allows you to choose one of several behaviors when pressing hotkeys: + /// 0 - Do nothing, 1 - Close the emulator application, 2 - Exit the game. /// public int SpecialExitEmulator { get; set; } -- 2.47.1 From 9356b68f26238ac304f607067816fe5277518904 Mon Sep 17 00:00:00 2001 From: madwind Date: Mon, 13 Jan 2025 08:41:32 +0800 Subject: [PATCH 30/30] Add a '*' to label the virtual controller. --- src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs index e08b298d4..4e0c01ef6 100644 --- a/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs +++ b/src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Input.SDL2 public const string Id = "JoyConPair"; string IGamepad.Id => Id; - public string Name => "Nintendo Switch Joy-Con (L/R)"; + public string Name => "* Nintendo Switch Joy-Con (L/R)"; public bool IsConnected => left is { IsConnected: true } && right is { IsConnected: true }; public void Dispose() -- 2.47.1