From 0682f6eea9a92380d8be694f85fab2e87f647f9e Mon Sep 17 00:00:00 2001 From: MutantAura Date: Thu, 30 May 2024 01:01:18 +0100 Subject: [PATCH] Initial implementation of analog stick visualization. --- .../Input/ControllerInputViewModel.cs | 73 +++++++++++++++++++ .../UI/ViewModels/Input/InputViewModel.cs | 2 + .../UI/Views/Input/ControllerInputView.axaml | 73 +++++++++++++++++-- 3 files changed, 143 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs index 6ee79a371..9d529a852 100644 --- a/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/ControllerInputViewModel.cs @@ -1,11 +1,28 @@ using Avalonia.Svg.Skia; +using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Models.Input; using Ryujinx.Ava.UI.Views.Input; +using Ryujinx.Input; +using System.Threading; +using System.Threading.Tasks; namespace Ryujinx.Ava.UI.ViewModels.Input { public class ControllerInputViewModel : BaseModel { + private const int StickUiPollMs = 50; // Milliseconds per poll. + private const float CanvasCenterOffset = 75f/2f; + private const int StickScaleFactor = 30; + + private IGamepad _selectedGamepad; + + // Offset from origin for UI stick visualization. + private (float, float) _uiStickLeft; + private (float, float) _uiStickRight; + + internal CancellationTokenSource _pollTokenSource = new(); + private CancellationToken _pollToken; + private GamepadInputConfig _config; public GamepadInputConfig Config { @@ -54,6 +71,39 @@ namespace Ryujinx.Ava.UI.ViewModels.Input } } + public (float, float) UiStickLeft + { + get => (_uiStickLeft.Item1 * StickScaleFactor, _uiStickLeft.Item2 * StickScaleFactor); + set + { + _uiStickLeft = value; + + OnPropertyChanged(); + OnPropertyChanged(nameof(UiStickRightX)); + OnPropertyChanged(nameof(UiStickRightY)); + } + } + + public (float, float) UiStickRight + { + get => (_uiStickRight.Item1 * StickScaleFactor, _uiStickRight.Item2 * StickScaleFactor); + set + { + _uiStickRight = value; + + OnPropertyChanged(); + OnPropertyChanged(nameof(UiStickLeftX)); + OnPropertyChanged(nameof(UiStickLeftY)); + } + } + + public float canvasCenter => CanvasCenterOffset; + + public float UiStickLeftX => UiStickLeft.Item1 + CanvasCenterOffset; + public float UiStickLeftY => UiStickLeft.Item2 + CanvasCenterOffset; + public float UiStickRightX => UiStickRight.Item1 + CanvasCenterOffset; + public float UiStickRightY => UiStickRight.Item2 + CanvasCenterOffset; + public readonly InputViewModel ParentModel; public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config) @@ -62,6 +112,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input model.NotifyChangesEvent += OnParentModelChanged; OnParentModelChanged(); Config = config; + + _pollTokenSource = new(); + _pollToken = _pollTokenSource.Token; + + Task.Run(() => PollSticks(_pollToken)); } public async void ShowMotionConfig() @@ -74,6 +129,24 @@ namespace Ryujinx.Ava.UI.ViewModels.Input await RumbleInputView.Show(this); } + private async Task PollSticks(CancellationToken token) + { + while (!token.IsCancellationRequested) + { + _selectedGamepad = ParentModel.SelectedGamepad; + + if (_selectedGamepad != null && _selectedGamepad is not AvaloniaKeyboard) + { + UiStickLeft = _selectedGamepad.GetStick(StickInputId.Left); + UiStickRight = _selectedGamepad.GetStick(StickInputId.Right); + } + + await Task.Delay(StickUiPollMs); + } + + _pollTokenSource.Dispose(); + } + public void OnParentModelChanged() { IsLeft = ParentModel.IsLeft; diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 89cc6496d..8b34233ab 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -879,6 +879,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; + (ConfigViewModel as ControllerInputViewModel)._pollTokenSource?.Cancel(); + _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates(); SelectedGamepad?.Dispose(); diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml index 08bdf90f4..3889e7080 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml @@ -318,12 +318,75 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + MinHeight="90"> + + + + + + + + + + + + + + +