diff --git a/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs
index 1f626625f..186bbdd55 100644
--- a/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs
+++ b/src/Ryujinx.Input/Assigner/KeyboardKeyAssigner.cs
@@ -8,22 +8,28 @@ namespace Ryujinx.Input.Assigner
private readonly IKeyboard _keyboard;
private KeyboardStateSnapshot _keyboardState;
+ private Button? _pressedButton;
public KeyboardKeyAssigner(IKeyboard keyboard)
{
_keyboard = keyboard;
}
- public void Initialize() { }
+ public void Initialize()
+ {
+ _pressedButton = null;
+ }
public void ReadInput()
{
_keyboardState = _keyboard.GetKeyboardStateSnapshot();
+
+ _pressedButton ??= GetPressedButtonFromState() ?? GetPressedButtonFromBufferedPress();
}
public bool IsAnyButtonPressed()
{
- return GetPressedButton() is not null;
+ return _pressedButton is not null;
}
public bool ShouldCancel()
@@ -32,26 +38,33 @@ namespace Ryujinx.Input.Assigner
}
public Button? GetPressedButton()
+ {
+ return !ShouldCancel() ? _pressedButton : null;
+ }
+
+ private Button? GetPressedButtonFromState()
{
Key aliasedKey = GetAliasedPressedKey();
if (aliasedKey != Key.Unknown)
{
- return !ShouldCancel() ? new Button(aliasedKey) : null;
+ return new Button(aliasedKey);
}
- Button? keyPressed = null;
-
for (Key key = Key.Unknown; key < Key.Count; key++)
{
if (_keyboardState.IsPressed(key))
{
- keyPressed = new(key);
- break;
+ return new Button(key);
}
}
- return !ShouldCancel() ? keyPressed : null;
+ return null;
+ }
+
+ private Button? GetPressedButtonFromBufferedPress()
+ {
+ return _keyboard.TryConsumePressedKey(out Key key) ? new Button(key) : null;
}
private Key GetAliasedPressedKey()
diff --git a/src/Ryujinx.Input/IKeyboard.cs b/src/Ryujinx.Input/IKeyboard.cs
index 74ac50457..9476fb1aa 100644
--- a/src/Ryujinx.Input/IKeyboard.cs
+++ b/src/Ryujinx.Input/IKeyboard.cs
@@ -44,5 +44,16 @@ namespace Ryujinx.Input
return new KeyboardStateSnapshot(_keyState);
}
+
+ ///
+ /// Try to consume a recently pressed key.
+ ///
+ /// The pressed key, if available.
+ /// True if a key press was consumed.
+ bool TryConsumePressedKey(out Key key)
+ {
+ key = Key.Unknown;
+ return false;
+ }
}
}
diff --git a/src/Ryujinx/Input/AvaloniaKeyboard.cs b/src/Ryujinx/Input/AvaloniaKeyboard.cs
index bc36d357e..c31a317d1 100644
--- a/src/Ryujinx/Input/AvaloniaKeyboard.cs
+++ b/src/Ryujinx/Input/AvaloniaKeyboard.cs
@@ -98,6 +98,19 @@ namespace Ryujinx.Ava.Input
}
}
+ public bool TryConsumePressedKey(out Key key)
+ {
+ try
+ {
+ return _driver.TryConsumePressedKey(_mode, out key);
+ }
+ catch
+ {
+ key = Key.Unknown;
+ return false;
+ }
+ }
+
public void SetConfiguration(InputConfig configuration)
{
lock (_userMappingLock)
diff --git a/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs b/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs
index f7f9858ec..9925330f3 100644
--- a/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs
+++ b/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs
@@ -4,6 +4,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Input;
using System;
using System.Collections.Generic;
+using System.Threading;
using ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey;
using Key = Ryujinx.Input.Key;
@@ -16,6 +17,9 @@ namespace Ryujinx.Ava.Input
private readonly HashSet _semanticPressedKeys;
private readonly HashSet _physicalPressedKeys;
private readonly Dictionary _observedPhysicalKeysBySemanticKey;
+ private readonly Queue _semanticPressedKeyQueue;
+ private readonly Queue _physicalPressedKeyQueue;
+ private readonly Lock _pressedKeyQueueLock;
private readonly KeyboardInputMode _defaultMode;
public event EventHandler KeyPressed;
@@ -31,6 +35,9 @@ namespace Ryujinx.Ava.Input
_semanticPressedKeys = [];
_physicalPressedKeys = [];
_observedPhysicalKeysBySemanticKey = [];
+ _semanticPressedKeyQueue = [];
+ _physicalPressedKeyQueue = [];
+ _pressedKeyQueueLock = new();
_defaultMode = defaultMode;
_control.KeyDown += OnKeyPress;
@@ -108,20 +115,46 @@ namespace Ryujinx.Ava.Input
internal void Clear(KeyboardInputMode mode)
{
- if (mode == KeyboardInputMode.Physical)
+ lock (_pressedKeyQueueLock)
{
- _physicalPressedKeys.Clear();
- }
- else
- {
- _semanticPressedKeys.Clear();
+ if (mode == KeyboardInputMode.Physical)
+ {
+ _physicalPressedKeys.Clear();
+ _physicalPressedKeyQueue.Clear();
+ }
+ else
+ {
+ _semanticPressedKeys.Clear();
+ _semanticPressedKeyQueue.Clear();
+ }
}
}
public void Clear()
{
- _semanticPressedKeys.Clear();
- _physicalPressedKeys.Clear();
+ lock (_pressedKeyQueueLock)
+ {
+ _semanticPressedKeys.Clear();
+ _physicalPressedKeys.Clear();
+ _semanticPressedKeyQueue.Clear();
+ _physicalPressedKeyQueue.Clear();
+ }
+ }
+
+ internal bool TryConsumePressedKey(KeyboardInputMode mode, out Key key)
+ {
+ lock (_pressedKeyQueueLock)
+ {
+ Queue queue = mode == KeyboardInputMode.Physical ? _physicalPressedKeyQueue : _semanticPressedKeyQueue;
+
+ if (queue.TryDequeue(out key))
+ {
+ return true;
+ }
+ }
+
+ key = Key.Unknown;
+ return false;
}
private static void UpdateKeyState(HashSet pressedKeys, Key key, bool isPressed)
@@ -161,10 +194,28 @@ namespace Ryujinx.Ava.Input
Key semanticKey = AvaloniaKeyboardMappingHelper.ToInputKey(args.Key);
Key resolvedSemanticKey = AvaloniaKeyboardMappingHelper.ToInputKey(args.PhysicalKey, args.Key);
ConfigPhysicalKey physicalKey = GetPhysicalInputKey(args, semanticKey);
+ bool semanticWasPressed = _semanticPressedKeys.Contains(resolvedSemanticKey);
+ bool physicalWasPressed = _physicalPressedKeys.Contains(physicalKey);
UpdateKeyState(_semanticPressedKeys, resolvedSemanticKey, isPressed);
UpdateKeyState(_physicalPressedKeys, physicalKey, isPressed);
+ if (isPressed)
+ {
+ lock (_pressedKeyQueueLock)
+ {
+ if (!semanticWasPressed && resolvedSemanticKey is not Key.Unknown and not Key.Unbound)
+ {
+ _semanticPressedKeyQueue.Enqueue(resolvedSemanticKey);
+ }
+
+ if (!physicalWasPressed && physicalKey is not ConfigPhysicalKey.Unknown and not ConfigPhysicalKey.Unbound)
+ {
+ _physicalPressedKeyQueue.Enqueue((Key)(int)physicalKey);
+ }
+ }
+ }
+
if (isPressed &&
semanticKey is not Key.Unknown and not Key.Unbound &&
physicalKey is not ConfigPhysicalKey.Unknown and not ConfigPhysicalKey.Unbound)