From 3cbe372b181992100ecb13921a8c704d1ff957e9 Mon Sep 17 00:00:00 2001 From: Babib3l Date: Thu, 19 Mar 2026 20:45:48 +0100 Subject: [PATCH] Simplify gameplay keyboard physical-key paths --- assets/Locales/KeyboardLayout.json | 27 +------ src/Ryujinx.Input.SDL3/SDL3Keyboard.cs | 43 +++++----- src/Ryujinx.Input/IKeyboard.cs | 7 +- .../KeyboardInputMappingHelper.cs | 52 ++++++------- src/Ryujinx.Input/KeyboardStateSnapshot.cs | 4 + src/Ryujinx.Input/PhysicalKeyExtensions.cs | 14 ---- src/Ryujinx/Input/AvaloniaKeyboard.cs | 15 +--- src/Ryujinx/Input/AvaloniaKeyboardDriver.cs | 78 +++++++++++++------ .../UI/Models/Input/StickVisualizer.cs | 16 ++-- 9 files changed, 120 insertions(+), 136 deletions(-) delete mode 100644 src/Ryujinx.Input/PhysicalKeyExtensions.cs diff --git a/assets/Locales/KeyboardLayout.json b/assets/Locales/KeyboardLayout.json index 86d9772c1..6b02f4834 100644 --- a/assets/Locales/KeyboardLayout.json +++ b/assets/Locales/KeyboardLayout.json @@ -375,31 +375,6 @@ "zh_TW": "右 ⌘" } }, - { - "ID": "KeyMenu", - "Translations": { - "ar_SA": "زر القائمة", - "de_DE": "", - "el_GR": "", - "en_US": "Menu", - "es_ES": null, - "fr_FR": null, - "he_IL": "", - "it_IT": "Menù", - "ja_JP": "", - "ko_KR": "메뉴", - "no_NO": "Meny", - "pl_PL": "", - "pt_BR": null, - "ru_RU": "Меню", - "sv_SE": "Meny", - "th_TH": "เมนู", - "tr_TR": "Menü", - "uk_UA": "Меню", - "zh_CN": "菜单键", - "zh_TW": "功能表鍵" - } - }, { "ID": "KeyUp", "Translations": { @@ -1926,4 +1901,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/Ryujinx.Input.SDL3/SDL3Keyboard.cs b/src/Ryujinx.Input.SDL3/SDL3Keyboard.cs index d72b31c20..93a9369e4 100644 --- a/src/Ryujinx.Input.SDL3/SDL3Keyboard.cs +++ b/src/Ryujinx.Input.SDL3/SDL3Keyboard.cs @@ -8,23 +8,19 @@ using System.Runtime.CompilerServices; using System.Threading; using SDL; using static SDL.SDL3; +using ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey; namespace Ryujinx.Input.SDL3 { class SDL3Keyboard : IKeyboard { - private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, Key From) - { - public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not Key.Unbound; - } - private readonly Lock _userMappingLock = new(); #pragma warning disable IDE0052 // Remove unread private member private readonly SDL3KeyboardDriver _driver; #pragma warning restore IDE0052 private StandardKeyboardInputConfig _configuration; - private readonly List _buttonsUserMapping; + private readonly List _buttonsUserMapping; private static readonly SDL_Keycode[] _keysDriverMapping = @@ -193,9 +189,9 @@ namespace Ryujinx.Input.SDL3 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe static int ToSDL3Scancode(Key key) + private unsafe static int ToSDL3Scancode(ConfigPhysicalKey key) { - if (key is >= Key.Unknown and <= Key.Menu) + if (key is >= ConfigPhysicalKey.Unknown and <= ConfigPhysicalKey.Menu) { return -1; } @@ -203,18 +199,18 @@ namespace Ryujinx.Input.SDL3 return (int)SDL_GetScancodeFromKey(_keysDriverMapping[(int)key], null); } - private static SDL_Keymod GetKeyboardModifierMask(Key key) + private static SDL_Keymod GetKeyboardModifierMask(ConfigPhysicalKey key) { return key switch { - Key.ShiftLeft => SDL_Keymod.SDL_KMOD_LSHIFT, - Key.ShiftRight => SDL_Keymod.SDL_KMOD_RSHIFT, - Key.ControlLeft => SDL_Keymod.SDL_KMOD_LCTRL, - Key.ControlRight => SDL_Keymod.SDL_KMOD_RCTRL, - Key.AltLeft => SDL_Keymod.SDL_KMOD_LALT, - Key.AltRight => SDL_Keymod.SDL_KMOD_RALT, - Key.WinLeft => SDL_Keymod.SDL_KMOD_LGUI, - Key.WinRight => SDL_Keymod.SDL_KMOD_RGUI, + ConfigPhysicalKey.ShiftLeft => SDL_Keymod.SDL_KMOD_LSHIFT, + ConfigPhysicalKey.ShiftRight => SDL_Keymod.SDL_KMOD_RSHIFT, + ConfigPhysicalKey.ControlLeft => SDL_Keymod.SDL_KMOD_LCTRL, + ConfigPhysicalKey.ControlRight => SDL_Keymod.SDL_KMOD_RCTRL, + ConfigPhysicalKey.AltLeft => SDL_Keymod.SDL_KMOD_LALT, + ConfigPhysicalKey.AltRight => SDL_Keymod.SDL_KMOD_RALT, + ConfigPhysicalKey.WinLeft => SDL_Keymod.SDL_KMOD_LGUI, + ConfigPhysicalKey.WinRight => SDL_Keymod.SDL_KMOD_RGUI, // NOTE: Menu key isn't supported by SDL3. _ => SDL_Keymod.SDL_KMOD_NONE }; @@ -230,9 +226,9 @@ namespace Ryujinx.Input.SDL3 rawKeyboardState = SDL_GetKeyboardState(null); } - bool[] keysState = new bool[(int)Key.Count]; + bool[] keysState = new bool[(int)ConfigPhysicalKey.Count]; - for (Key key = 0; key < Key.Count; key++) + for (ConfigPhysicalKey key = 0; key < ConfigPhysicalKey.Count; key++) { int index = ToSDL3Scancode(key); if (index == -1) @@ -274,9 +270,9 @@ namespace Ryujinx.Input.SDL3 return result; } - foreach (ButtonMappingEntry entry in _buttonsUserMapping) + foreach (KeyboardInputMappingHelper.KeyboardButtonMapping entry in _buttonsUserMapping) { - if (entry.From == Key.Unknown || entry.From == Key.Unbound || entry.To == GamepadButtonInputId.Unbound) + if (!entry.IsValid) { continue; } @@ -327,10 +323,7 @@ namespace Ryujinx.Input.SDL3 _buttonsUserMapping.Clear(); - foreach (KeyboardInputMappingHelper.KeyboardButtonMapping mapping in KeyboardInputMappingHelper.BuildButtonMappings(_configuration)) - { - _buttonsUserMapping.Add(new ButtonMappingEntry(mapping.To, mapping.From)); - } + _buttonsUserMapping.AddRange(KeyboardInputMappingHelper.BuildButtonMappings(_configuration)); } } diff --git a/src/Ryujinx.Input/IKeyboard.cs b/src/Ryujinx.Input/IKeyboard.cs index c51d5aea3..74ac50457 100644 --- a/src/Ryujinx.Input/IKeyboard.cs +++ b/src/Ryujinx.Input/IKeyboard.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Runtime.CompilerServices; +using ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey; namespace Ryujinx.Input { @@ -33,12 +34,12 @@ namespace Ryujinx.Input { if (_keyState is null) { - _keyState = new bool[(int)Key.Count]; + _keyState = new bool[(int)ConfigPhysicalKey.Count]; } - for (Key key = 0; key < Key.Count; key++) + for (ConfigPhysicalKey key = 0; key < ConfigPhysicalKey.Count; key++) { - _keyState[(int)key] = keyboard.IsPressed(key); + _keyState[(int)key] = keyboard.IsPressed((Key)(int)key); } return new KeyboardStateSnapshot(_keyState); diff --git a/src/Ryujinx.Input/KeyboardInputMappingHelper.cs b/src/Ryujinx.Input/KeyboardInputMappingHelper.cs index 7b9c5a4ef..8c4aeb3bc 100644 --- a/src/Ryujinx.Input/KeyboardInputMappingHelper.cs +++ b/src/Ryujinx.Input/KeyboardInputMappingHelper.cs @@ -8,36 +8,36 @@ namespace Ryujinx.Input { public static class KeyboardInputMappingHelper { - public readonly record struct KeyboardButtonMapping(GamepadButtonInputId To, Key From) + public readonly record struct KeyboardButtonMapping(GamepadButtonInputId To, ConfigPhysicalKey From) { - public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not Key.Unknown and not Key.Unbound; + public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not ConfigPhysicalKey.Unknown and not ConfigPhysicalKey.Unbound; } public static KeyboardButtonMapping[] BuildButtonMappings(StandardKeyboardInputConfig configuration) => [ // Left JoyCon - new(GamepadButtonInputId.LeftStick, configuration.LeftJoyconStick.StickButton.ToInputKey()), - new(GamepadButtonInputId.DpadUp, configuration.LeftJoycon.DpadUp.ToInputKey()), - new(GamepadButtonInputId.DpadDown, configuration.LeftJoycon.DpadDown.ToInputKey()), - new(GamepadButtonInputId.DpadLeft, configuration.LeftJoycon.DpadLeft.ToInputKey()), - new(GamepadButtonInputId.DpadRight, configuration.LeftJoycon.DpadRight.ToInputKey()), - new(GamepadButtonInputId.Minus, configuration.LeftJoycon.ButtonMinus.ToInputKey()), - new(GamepadButtonInputId.LeftShoulder, configuration.LeftJoycon.ButtonL.ToInputKey()), - new(GamepadButtonInputId.LeftTrigger, configuration.LeftJoycon.ButtonZl.ToInputKey()), - new(GamepadButtonInputId.SingleRightTrigger0, configuration.LeftJoycon.ButtonSr.ToInputKey()), - new(GamepadButtonInputId.SingleLeftTrigger0, configuration.LeftJoycon.ButtonSl.ToInputKey()), + new(GamepadButtonInputId.LeftStick, configuration.LeftJoyconStick.StickButton), + new(GamepadButtonInputId.DpadUp, configuration.LeftJoycon.DpadUp), + new(GamepadButtonInputId.DpadDown, configuration.LeftJoycon.DpadDown), + new(GamepadButtonInputId.DpadLeft, configuration.LeftJoycon.DpadLeft), + new(GamepadButtonInputId.DpadRight, configuration.LeftJoycon.DpadRight), + new(GamepadButtonInputId.Minus, configuration.LeftJoycon.ButtonMinus), + new(GamepadButtonInputId.LeftShoulder, configuration.LeftJoycon.ButtonL), + new(GamepadButtonInputId.LeftTrigger, configuration.LeftJoycon.ButtonZl), + new(GamepadButtonInputId.SingleRightTrigger0, configuration.LeftJoycon.ButtonSr), + new(GamepadButtonInputId.SingleLeftTrigger0, configuration.LeftJoycon.ButtonSl), // Right JoyCon - new(GamepadButtonInputId.RightStick, configuration.RightJoyconStick.StickButton.ToInputKey()), - new(GamepadButtonInputId.A, configuration.RightJoycon.ButtonA.ToInputKey()), - new(GamepadButtonInputId.B, configuration.RightJoycon.ButtonB.ToInputKey()), - new(GamepadButtonInputId.X, configuration.RightJoycon.ButtonX.ToInputKey()), - new(GamepadButtonInputId.Y, configuration.RightJoycon.ButtonY.ToInputKey()), - new(GamepadButtonInputId.Plus, configuration.RightJoycon.ButtonPlus.ToInputKey()), - new(GamepadButtonInputId.RightShoulder, configuration.RightJoycon.ButtonR.ToInputKey()), - new(GamepadButtonInputId.RightTrigger, configuration.RightJoycon.ButtonZr.ToInputKey()), - new(GamepadButtonInputId.SingleRightTrigger1, configuration.RightJoycon.ButtonSr.ToInputKey()), - new(GamepadButtonInputId.SingleLeftTrigger1, configuration.RightJoycon.ButtonSl.ToInputKey()), + new(GamepadButtonInputId.RightStick, configuration.RightJoyconStick.StickButton), + new(GamepadButtonInputId.A, configuration.RightJoycon.ButtonA), + new(GamepadButtonInputId.B, configuration.RightJoycon.ButtonB), + new(GamepadButtonInputId.X, configuration.RightJoycon.ButtonX), + new(GamepadButtonInputId.Y, configuration.RightJoycon.ButtonY), + new(GamepadButtonInputId.Plus, configuration.RightJoycon.ButtonPlus), + new(GamepadButtonInputId.RightShoulder, configuration.RightJoycon.ButtonR), + new(GamepadButtonInputId.RightTrigger, configuration.RightJoycon.ButtonZr), + new(GamepadButtonInputId.SingleRightTrigger1, configuration.RightJoycon.ButtonSr), + new(GamepadButtonInputId.SingleLeftTrigger1, configuration.RightJoycon.ButtonSl), ]; public static (short X, short Y) GetStickValues(ref KeyboardStateSnapshot snapshot, JoyconConfigKeyboardStick stickConfig) @@ -45,22 +45,22 @@ namespace Ryujinx.Input short stickX = 0; short stickY = 0; - if (snapshot.IsPressed(stickConfig.StickUp.ToInputKey())) + if (snapshot.IsPressed(stickConfig.StickUp)) { stickY += 1; } - if (snapshot.IsPressed(stickConfig.StickDown.ToInputKey())) + if (snapshot.IsPressed(stickConfig.StickDown)) { stickY -= 1; } - if (snapshot.IsPressed(stickConfig.StickRight.ToInputKey())) + if (snapshot.IsPressed(stickConfig.StickRight)) { stickX += 1; } - if (snapshot.IsPressed(stickConfig.StickLeft.ToInputKey())) + if (snapshot.IsPressed(stickConfig.StickLeft)) { stickX -= 1; } diff --git a/src/Ryujinx.Input/KeyboardStateSnapshot.cs b/src/Ryujinx.Input/KeyboardStateSnapshot.cs index 9b40b46db..91765ceb8 100644 --- a/src/Ryujinx.Input/KeyboardStateSnapshot.cs +++ b/src/Ryujinx.Input/KeyboardStateSnapshot.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey; namespace Ryujinx.Input { @@ -25,5 +26,8 @@ namespace Ryujinx.Input /// True if the given key is pressed [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsPressed(Key key) => KeysState[(int)key]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsPressed(ConfigPhysicalKey key) => KeysState[(int)key]; } } diff --git a/src/Ryujinx.Input/PhysicalKeyExtensions.cs b/src/Ryujinx.Input/PhysicalKeyExtensions.cs deleted file mode 100644 index f2359d0d8..000000000 --- a/src/Ryujinx.Input/PhysicalKeyExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey; - -namespace Ryujinx.Input -{ - public static class PhysicalKeyExtensions - { - public static Key ToInputKey(this ConfigPhysicalKey key) - { - return key is >= ConfigPhysicalKey.Unknown and < ConfigPhysicalKey.Count - ? (Key)(int)key - : Key.Unknown; - } - } -} diff --git a/src/Ryujinx/Input/AvaloniaKeyboard.cs b/src/Ryujinx/Input/AvaloniaKeyboard.cs index 198c60971..19613adef 100644 --- a/src/Ryujinx/Input/AvaloniaKeyboard.cs +++ b/src/Ryujinx/Input/AvaloniaKeyboard.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Ava.Input { internal class AvaloniaKeyboard : IKeyboard { - private readonly List _buttonsUserMapping; + private readonly List _buttonsUserMapping; private readonly AvaloniaKeyboardDriver _driver; private readonly KeyboardInputMode _mode; private StandardKeyboardInputConfig _configuration; @@ -25,12 +25,6 @@ namespace Ryujinx.Ava.Input public bool IsConnected => true; public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None; - - private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, Key From) - { - public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not Key.Unknown and not Key.Unbound; - } - public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name, KeyboardInputMode mode) { _buttonsUserMapping = []; @@ -58,7 +52,7 @@ namespace Ryujinx.Ava.Input return result; } - foreach (ButtonMappingEntry entry in _buttonsUserMapping) + foreach (KeyboardInputMappingHelper.KeyboardButtonMapping entry in _buttonsUserMapping) { if (!entry.IsValid) { @@ -117,10 +111,7 @@ namespace Ryujinx.Ava.Input _buttonsUserMapping.Clear(); - foreach (KeyboardInputMappingHelper.KeyboardButtonMapping mapping in KeyboardInputMappingHelper.BuildButtonMappings(_configuration)) - { - _buttonsUserMapping.Add(new ButtonMappingEntry(mapping.To, mapping.From)); - } + _buttonsUserMapping.AddRange(KeyboardInputMappingHelper.BuildButtonMappings(_configuration)); } } diff --git a/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs b/src/Ryujinx/Input/AvaloniaKeyboardDriver.cs index 5528d0d6f..7010e14b1 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 ConfigPhysicalKey = Ryujinx.Common.Configuration.Hid.PhysicalKey; using Key = Ryujinx.Input.Key; namespace Ryujinx.Ava.Input @@ -13,7 +14,7 @@ namespace Ryujinx.Ava.Input private static readonly string[] _keyboardIdentifers = ["0"]; private readonly Control _control; private readonly Dictionary _semanticPressedKeys; - private readonly Dictionary _physicalPressedKeys; + private readonly Dictionary _physicalPressedKeys; private readonly KeyboardInputMode _defaultMode; public event EventHandler KeyPressed; @@ -98,12 +99,21 @@ namespace Ryujinx.Ava.Input return false; } - return GetPressedKeys(mode).ContainsKey(key); + return mode == KeyboardInputMode.Physical + ? _physicalPressedKeys.ContainsKey((ConfigPhysicalKey)(int)key) + : _semanticPressedKeys.ContainsKey(key); } internal void Clear(KeyboardInputMode mode) { - GetPressedKeys(mode).Clear(); + if (mode == KeyboardInputMode.Physical) + { + _physicalPressedKeys.Clear(); + } + else + { + _semanticPressedKeys.Clear(); + } } public void Clear() @@ -112,11 +122,6 @@ namespace Ryujinx.Ava.Input _physicalPressedKeys.Clear(); } - private Dictionary GetPressedKeys(KeyboardInputMode mode) - { - return mode == KeyboardInputMode.Physical ? _physicalPressedKeys : _semanticPressedKeys; - } - private static void UpdateKeyState(Dictionary pressedKeys, Key key, bool isPressed) { if (key is Key.Unknown or Key.Unbound) @@ -151,24 +156,53 @@ namespace Ryujinx.Ava.Input } } - private void UpdateKeyStates(KeyEventArgs args, bool isPressed) + private static void UpdateKeyState(Dictionary pressedKeys, ConfigPhysicalKey key, bool isPressed) { - UpdateKeyState(_semanticPressedKeys, GetInputKey(args, KeyboardInputMode.Semantic), isPressed); - UpdateKeyState(_physicalPressedKeys, GetInputKey(args, KeyboardInputMode.Physical), isPressed); - } - - private static Key GetInputKey(KeyEventArgs args, KeyboardInputMode mode) - { - if (mode == KeyboardInputMode.Physical) + if (key is ConfigPhysicalKey.Unknown or ConfigPhysicalKey.Unbound) { - Key physicalKey = AvaloniaKeyboardMappingHelper.ToInputKey(args.PhysicalKey); - - return physicalKey != Key.Unknown - ? physicalKey - : AvaloniaKeyboardMappingHelper.ToInputKey(args.Key); + return; } - return AvaloniaKeyboardMappingHelper.ToInputKey(args.PhysicalKey, args.Key); + if (isPressed) + { + if (pressedKeys.TryGetValue(key, out int count)) + { + pressedKeys[key] = count + 1; + } + else + { + pressedKeys[key] = 1; + } + + return; + } + + if (pressedKeys.TryGetValue(key, out int currentCount)) + { + if (currentCount <= 1) + { + pressedKeys.Remove(key); + } + else + { + pressedKeys[key] = currentCount - 1; + } + } + } + + private void UpdateKeyStates(KeyEventArgs args, bool isPressed) + { + UpdateKeyState(_semanticPressedKeys, AvaloniaKeyboardMappingHelper.ToInputKey(args.PhysicalKey, args.Key), isPressed); + UpdateKeyState(_physicalPressedKeys, GetPhysicalInputKey(args), isPressed); + } + + private static ConfigPhysicalKey GetPhysicalInputKey(KeyEventArgs args) + { + Key key = AvaloniaKeyboardMappingHelper.ToInputKey(args.PhysicalKey); + + return key is >= Key.Unknown and < Key.Count + ? (ConfigPhysicalKey)(int)key + : ConfigPhysicalKey.Unknown; } public void Dispose() diff --git a/src/Ryujinx/UI/Models/Input/StickVisualizer.cs b/src/Ryujinx/UI/Models/Input/StickVisualizer.cs index 936268601..da5d1853b 100644 --- a/src/Ryujinx/UI/Models/Input/StickVisualizer.cs +++ b/src/Ryujinx/UI/Models/Input/StickVisualizer.cs @@ -154,42 +154,42 @@ namespace Ryujinx.Ava.UI.Models.Input { KeyboardStateSnapshot snapshot = keyboard.GetKeyboardStateSnapshot(); - if (snapshot.IsPressed(KeyboardConfig.LeftStickRight.ToInputKey())) + if (snapshot.IsPressed(KeyboardConfig.LeftStickRight)) { leftBuffer.Item1 += 1; } - if (snapshot.IsPressed(KeyboardConfig.LeftStickLeft.ToInputKey())) + if (snapshot.IsPressed(KeyboardConfig.LeftStickLeft)) { leftBuffer.Item1 -= 1; } - if (snapshot.IsPressed(KeyboardConfig.LeftStickUp.ToInputKey())) + if (snapshot.IsPressed(KeyboardConfig.LeftStickUp)) { leftBuffer.Item2 += 1; } - if (snapshot.IsPressed(KeyboardConfig.LeftStickDown.ToInputKey())) + if (snapshot.IsPressed(KeyboardConfig.LeftStickDown)) { leftBuffer.Item2 -= 1; } - if (snapshot.IsPressed(KeyboardConfig.RightStickRight.ToInputKey())) + if (snapshot.IsPressed(KeyboardConfig.RightStickRight)) { rightBuffer.Item1 += 1; } - if (snapshot.IsPressed(KeyboardConfig.RightStickLeft.ToInputKey())) + if (snapshot.IsPressed(KeyboardConfig.RightStickLeft)) { rightBuffer.Item1 -= 1; } - if (snapshot.IsPressed(KeyboardConfig.RightStickUp.ToInputKey())) + if (snapshot.IsPressed(KeyboardConfig.RightStickUp)) { rightBuffer.Item2 += 1; } - if (snapshot.IsPressed(KeyboardConfig.RightStickDown.ToInputKey())) + if (snapshot.IsPressed(KeyboardConfig.RightStickDown)) { rightBuffer.Item2 -= 1; }