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">
+
+
+
+
+
+
+
+
+