mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-06-05 03:49:17 +00:00
Fix macOS Caps Lock rebinding by buffering one-shot key-down events during keyboard assignment
This commit is contained in:
@@ -8,22 +8,28 @@ namespace Ryujinx.Input.Assigner
|
|||||||
private readonly IKeyboard _keyboard;
|
private readonly IKeyboard _keyboard;
|
||||||
|
|
||||||
private KeyboardStateSnapshot _keyboardState;
|
private KeyboardStateSnapshot _keyboardState;
|
||||||
|
private Button? _pressedButton;
|
||||||
|
|
||||||
public KeyboardKeyAssigner(IKeyboard keyboard)
|
public KeyboardKeyAssigner(IKeyboard keyboard)
|
||||||
{
|
{
|
||||||
_keyboard = keyboard;
|
_keyboard = keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize() { }
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_pressedButton = null;
|
||||||
|
}
|
||||||
|
|
||||||
public void ReadInput()
|
public void ReadInput()
|
||||||
{
|
{
|
||||||
_keyboardState = _keyboard.GetKeyboardStateSnapshot();
|
_keyboardState = _keyboard.GetKeyboardStateSnapshot();
|
||||||
|
|
||||||
|
_pressedButton ??= GetPressedButtonFromState() ?? GetPressedButtonFromBufferedPress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAnyButtonPressed()
|
public bool IsAnyButtonPressed()
|
||||||
{
|
{
|
||||||
return GetPressedButton() is not null;
|
return _pressedButton is not null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShouldCancel()
|
public bool ShouldCancel()
|
||||||
@@ -32,26 +38,33 @@ namespace Ryujinx.Input.Assigner
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Button? GetPressedButton()
|
public Button? GetPressedButton()
|
||||||
|
{
|
||||||
|
return !ShouldCancel() ? _pressedButton : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Button? GetPressedButtonFromState()
|
||||||
{
|
{
|
||||||
Key aliasedKey = GetAliasedPressedKey();
|
Key aliasedKey = GetAliasedPressedKey();
|
||||||
|
|
||||||
if (aliasedKey != Key.Unknown)
|
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++)
|
for (Key key = Key.Unknown; key < Key.Count; key++)
|
||||||
{
|
{
|
||||||
if (_keyboardState.IsPressed(key))
|
if (_keyboardState.IsPressed(key))
|
||||||
{
|
{
|
||||||
keyPressed = new(key);
|
return new Button(key);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return !ShouldCancel() ? keyPressed : null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Button? GetPressedButtonFromBufferedPress()
|
||||||
|
{
|
||||||
|
return _keyboard.TryConsumePressedKey(out Key key) ? new Button(key) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Key GetAliasedPressedKey()
|
private Key GetAliasedPressedKey()
|
||||||
|
|||||||
@@ -44,5 +44,16 @@ namespace Ryujinx.Input
|
|||||||
|
|
||||||
return new KeyboardStateSnapshot(_keyState);
|
return new KeyboardStateSnapshot(_keyState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to consume a recently pressed key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The pressed key, if available.</param>
|
||||||
|
/// <returns>True if a key press was consumed.</returns>
|
||||||
|
bool TryConsumePressedKey(out Key key)
|
||||||
|
{
|
||||||
|
key = Key.Unknown;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
public void SetConfiguration(InputConfig configuration)
|
||||||
{
|
{
|
||||||
lock (_userMappingLock)
|
lock (_userMappingLock)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Ryujinx.Ava.Common.Locale;
|
|||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey;
|
using ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey;
|
||||||
using Key = Ryujinx.Input.Key;
|
using Key = Ryujinx.Input.Key;
|
||||||
|
|
||||||
@@ -16,6 +17,9 @@ namespace Ryujinx.Ava.Input
|
|||||||
private readonly HashSet<Key> _semanticPressedKeys;
|
private readonly HashSet<Key> _semanticPressedKeys;
|
||||||
private readonly HashSet<ConfigPhysicalKey> _physicalPressedKeys;
|
private readonly HashSet<ConfigPhysicalKey> _physicalPressedKeys;
|
||||||
private readonly Dictionary<Key, ConfigPhysicalKey> _observedPhysicalKeysBySemanticKey;
|
private readonly Dictionary<Key, ConfigPhysicalKey> _observedPhysicalKeysBySemanticKey;
|
||||||
|
private readonly Queue<Key> _semanticPressedKeyQueue;
|
||||||
|
private readonly Queue<Key> _physicalPressedKeyQueue;
|
||||||
|
private readonly Lock _pressedKeyQueueLock;
|
||||||
private readonly KeyboardInputMode _defaultMode;
|
private readonly KeyboardInputMode _defaultMode;
|
||||||
|
|
||||||
public event EventHandler<KeyEventArgs> KeyPressed;
|
public event EventHandler<KeyEventArgs> KeyPressed;
|
||||||
@@ -31,6 +35,9 @@ namespace Ryujinx.Ava.Input
|
|||||||
_semanticPressedKeys = [];
|
_semanticPressedKeys = [];
|
||||||
_physicalPressedKeys = [];
|
_physicalPressedKeys = [];
|
||||||
_observedPhysicalKeysBySemanticKey = [];
|
_observedPhysicalKeysBySemanticKey = [];
|
||||||
|
_semanticPressedKeyQueue = [];
|
||||||
|
_physicalPressedKeyQueue = [];
|
||||||
|
_pressedKeyQueueLock = new();
|
||||||
_defaultMode = defaultMode;
|
_defaultMode = defaultMode;
|
||||||
|
|
||||||
_control.KeyDown += OnKeyPress;
|
_control.KeyDown += OnKeyPress;
|
||||||
@@ -107,21 +114,47 @@ namespace Ryujinx.Ava.Input
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal void Clear(KeyboardInputMode mode)
|
internal void Clear(KeyboardInputMode mode)
|
||||||
|
{
|
||||||
|
lock (_pressedKeyQueueLock)
|
||||||
{
|
{
|
||||||
if (mode == KeyboardInputMode.Physical)
|
if (mode == KeyboardInputMode.Physical)
|
||||||
{
|
{
|
||||||
_physicalPressedKeys.Clear();
|
_physicalPressedKeys.Clear();
|
||||||
|
_physicalPressedKeyQueue.Clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_semanticPressedKeys.Clear();
|
_semanticPressedKeys.Clear();
|
||||||
|
_semanticPressedKeyQueue.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (_pressedKeyQueueLock)
|
||||||
{
|
{
|
||||||
_semanticPressedKeys.Clear();
|
_semanticPressedKeys.Clear();
|
||||||
_physicalPressedKeys.Clear();
|
_physicalPressedKeys.Clear();
|
||||||
|
_semanticPressedKeyQueue.Clear();
|
||||||
|
_physicalPressedKeyQueue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool TryConsumePressedKey(KeyboardInputMode mode, out Key key)
|
||||||
|
{
|
||||||
|
lock (_pressedKeyQueueLock)
|
||||||
|
{
|
||||||
|
Queue<Key> queue = mode == KeyboardInputMode.Physical ? _physicalPressedKeyQueue : _semanticPressedKeyQueue;
|
||||||
|
|
||||||
|
if (queue.TryDequeue(out key))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
key = Key.Unknown;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateKeyState(HashSet<Key> pressedKeys, Key key, bool isPressed)
|
private static void UpdateKeyState(HashSet<Key> pressedKeys, Key key, bool isPressed)
|
||||||
@@ -161,10 +194,28 @@ namespace Ryujinx.Ava.Input
|
|||||||
Key semanticKey = AvaloniaKeyboardMappingHelper.ToInputKey(args.Key);
|
Key semanticKey = AvaloniaKeyboardMappingHelper.ToInputKey(args.Key);
|
||||||
Key resolvedSemanticKey = AvaloniaKeyboardMappingHelper.ToInputKey(args.PhysicalKey, args.Key);
|
Key resolvedSemanticKey = AvaloniaKeyboardMappingHelper.ToInputKey(args.PhysicalKey, args.Key);
|
||||||
ConfigPhysicalKey physicalKey = GetPhysicalInputKey(args, semanticKey);
|
ConfigPhysicalKey physicalKey = GetPhysicalInputKey(args, semanticKey);
|
||||||
|
bool semanticWasPressed = _semanticPressedKeys.Contains(resolvedSemanticKey);
|
||||||
|
bool physicalWasPressed = _physicalPressedKeys.Contains(physicalKey);
|
||||||
|
|
||||||
UpdateKeyState(_semanticPressedKeys, resolvedSemanticKey, isPressed);
|
UpdateKeyState(_semanticPressedKeys, resolvedSemanticKey, isPressed);
|
||||||
UpdateKeyState(_physicalPressedKeys, physicalKey, 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 &&
|
if (isPressed &&
|
||||||
semanticKey is not Key.Unknown and not Key.Unbound &&
|
semanticKey is not Key.Unknown and not Key.Unbound &&
|
||||||
physicalKey is not ConfigPhysicalKey.Unknown and not ConfigPhysicalKey.Unbound)
|
physicalKey is not ConfigPhysicalKey.Unknown and not ConfigPhysicalKey.Unbound)
|
||||||
|
|||||||
Reference in New Issue
Block a user