aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Input.SDL2/SDL2Keyboard.cs')
-rw-r--r--src/Ryujinx.Input.SDL2/SDL2Keyboard.cs419
1 files changed, 419 insertions, 0 deletions
diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs
new file mode 100644
index 00000000..edadae15
--- /dev/null
+++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs
@@ -0,0 +1,419 @@
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Configuration.Hid.Keyboard;
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using static SDL2.SDL;
+
+using ConfigKey = Ryujinx.Common.Configuration.Hid.Key;
+
+namespace Ryujinx.Input.SDL2
+{
+ class SDL2Keyboard : IKeyboard
+ {
+ private class ButtonMappingEntry
+ {
+ public readonly GamepadButtonInputId To;
+ public readonly Key From;
+
+ public ButtonMappingEntry(GamepadButtonInputId to, Key from)
+ {
+ To = to;
+ From = from;
+ }
+ }
+
+ private object _userMappingLock = new object();
+
+ private readonly SDL2KeyboardDriver _driver;
+ private StandardKeyboardInputConfig _configuration;
+ private List<ButtonMappingEntry> _buttonsUserMapping;
+
+ private static readonly SDL_Keycode[] _keysDriverMapping = new SDL_Keycode[(int)Key.Count]
+ {
+ // INVALID
+ SDL_Keycode.SDLK_0,
+ // Presented as modifiers, so invalid here.
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+
+ SDL_Keycode.SDLK_F1,
+ SDL_Keycode.SDLK_F2,
+ SDL_Keycode.SDLK_F3,
+ SDL_Keycode.SDLK_F4,
+ SDL_Keycode.SDLK_F5,
+ SDL_Keycode.SDLK_F6,
+ SDL_Keycode.SDLK_F7,
+ SDL_Keycode.SDLK_F8,
+ SDL_Keycode.SDLK_F9,
+ SDL_Keycode.SDLK_F10,
+ SDL_Keycode.SDLK_F11,
+ SDL_Keycode.SDLK_F12,
+ SDL_Keycode.SDLK_F13,
+ SDL_Keycode.SDLK_F14,
+ SDL_Keycode.SDLK_F15,
+ SDL_Keycode.SDLK_F16,
+ SDL_Keycode.SDLK_F17,
+ SDL_Keycode.SDLK_F18,
+ SDL_Keycode.SDLK_F19,
+ SDL_Keycode.SDLK_F20,
+ SDL_Keycode.SDLK_F21,
+ SDL_Keycode.SDLK_F22,
+ SDL_Keycode.SDLK_F23,
+ SDL_Keycode.SDLK_F24,
+
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_0,
+
+ SDL_Keycode.SDLK_UP,
+ SDL_Keycode.SDLK_DOWN,
+ SDL_Keycode.SDLK_LEFT,
+ SDL_Keycode.SDLK_RIGHT,
+ SDL_Keycode.SDLK_RETURN,
+ SDL_Keycode.SDLK_ESCAPE,
+ SDL_Keycode.SDLK_SPACE,
+ SDL_Keycode.SDLK_TAB,
+ SDL_Keycode.SDLK_BACKSPACE,
+ SDL_Keycode.SDLK_INSERT,
+ SDL_Keycode.SDLK_DELETE,
+ SDL_Keycode.SDLK_PAGEUP,
+ SDL_Keycode.SDLK_PAGEDOWN,
+ SDL_Keycode.SDLK_HOME,
+ SDL_Keycode.SDLK_END,
+ SDL_Keycode.SDLK_CAPSLOCK,
+ SDL_Keycode.SDLK_SCROLLLOCK,
+ SDL_Keycode.SDLK_PRINTSCREEN,
+ SDL_Keycode.SDLK_PAUSE,
+ SDL_Keycode.SDLK_NUMLOCKCLEAR,
+ SDL_Keycode.SDLK_CLEAR,
+ SDL_Keycode.SDLK_KP_0,
+ SDL_Keycode.SDLK_KP_1,
+ SDL_Keycode.SDLK_KP_2,
+ SDL_Keycode.SDLK_KP_3,
+ SDL_Keycode.SDLK_KP_4,
+ SDL_Keycode.SDLK_KP_5,
+ SDL_Keycode.SDLK_KP_6,
+ SDL_Keycode.SDLK_KP_7,
+ SDL_Keycode.SDLK_KP_8,
+ SDL_Keycode.SDLK_KP_9,
+ SDL_Keycode.SDLK_KP_DIVIDE,
+ SDL_Keycode.SDLK_KP_MULTIPLY,
+ SDL_Keycode.SDLK_KP_MINUS,
+ SDL_Keycode.SDLK_KP_PLUS,
+ SDL_Keycode.SDLK_KP_DECIMAL,
+ SDL_Keycode.SDLK_KP_ENTER,
+ SDL_Keycode.SDLK_a,
+ SDL_Keycode.SDLK_b,
+ SDL_Keycode.SDLK_c,
+ SDL_Keycode.SDLK_d,
+ SDL_Keycode.SDLK_e,
+ SDL_Keycode.SDLK_f,
+ SDL_Keycode.SDLK_g,
+ SDL_Keycode.SDLK_h,
+ SDL_Keycode.SDLK_i,
+ SDL_Keycode.SDLK_j,
+ SDL_Keycode.SDLK_k,
+ SDL_Keycode.SDLK_l,
+ SDL_Keycode.SDLK_m,
+ SDL_Keycode.SDLK_n,
+ SDL_Keycode.SDLK_o,
+ SDL_Keycode.SDLK_p,
+ SDL_Keycode.SDLK_q,
+ SDL_Keycode.SDLK_r,
+ SDL_Keycode.SDLK_s,
+ SDL_Keycode.SDLK_t,
+ SDL_Keycode.SDLK_u,
+ SDL_Keycode.SDLK_v,
+ SDL_Keycode.SDLK_w,
+ SDL_Keycode.SDLK_x,
+ SDL_Keycode.SDLK_y,
+ SDL_Keycode.SDLK_z,
+ SDL_Keycode.SDLK_0,
+ SDL_Keycode.SDLK_1,
+ SDL_Keycode.SDLK_2,
+ SDL_Keycode.SDLK_3,
+ SDL_Keycode.SDLK_4,
+ SDL_Keycode.SDLK_5,
+ SDL_Keycode.SDLK_6,
+ SDL_Keycode.SDLK_7,
+ SDL_Keycode.SDLK_8,
+ SDL_Keycode.SDLK_9,
+ SDL_Keycode.SDLK_BACKQUOTE,
+ SDL_Keycode.SDLK_BACKQUOTE,
+ SDL_Keycode.SDLK_MINUS,
+ SDL_Keycode.SDLK_PLUS,
+ SDL_Keycode.SDLK_LEFTBRACKET,
+ SDL_Keycode.SDLK_RIGHTBRACKET,
+ SDL_Keycode.SDLK_SEMICOLON,
+ SDL_Keycode.SDLK_QUOTE,
+ SDL_Keycode.SDLK_COMMA,
+ SDL_Keycode.SDLK_PERIOD,
+ SDL_Keycode.SDLK_SLASH,
+ SDL_Keycode.SDLK_BACKSLASH,
+
+ // Invalids
+ SDL_Keycode.SDLK_0,
+ };
+
+ public SDL2Keyboard(SDL2KeyboardDriver driver, string id, string name)
+ {
+ _driver = driver;
+ Id = id;
+ Name = name;
+ _buttonsUserMapping = new List<ButtonMappingEntry>();
+ }
+
+ private bool HasConfiguration => _configuration != null;
+
+ public string Id { get; }
+
+ public string Name { get; }
+
+ public bool IsConnected => true;
+
+ public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
+
+ public void Dispose()
+ {
+ // No operations
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int ToSDL2Scancode(Key key)
+ {
+ if (key >= Key.Unknown && key <= Key.Menu)
+ {
+ return -1;
+ }
+
+ return (int)SDL_GetScancodeFromKey(_keysDriverMapping[(int)key]);
+ }
+
+ private static SDL_Keymod GetKeyboardModifierMask(Key key)
+ {
+ switch (key)
+ {
+ case Key.ShiftLeft:
+ return SDL_Keymod.KMOD_LSHIFT;
+ case Key.ShiftRight:
+ return SDL_Keymod.KMOD_RSHIFT;
+ case Key.ControlLeft:
+ return SDL_Keymod.KMOD_LCTRL;
+ case Key.ControlRight:
+ return SDL_Keymod.KMOD_RCTRL;
+ case Key.AltLeft:
+ return SDL_Keymod.KMOD_LALT;
+ case Key.AltRight:
+ return SDL_Keymod.KMOD_RALT;
+ case Key.WinLeft:
+ return SDL_Keymod.KMOD_LGUI;
+ case Key.WinRight:
+ return SDL_Keymod.KMOD_RGUI;
+ // NOTE: Menu key isn't supported by SDL2.
+ case Key.Menu:
+ default:
+ return SDL_Keymod.KMOD_NONE;
+ }
+ }
+
+ public KeyboardStateSnapshot GetKeyboardStateSnapshot()
+ {
+ ReadOnlySpan<byte> rawKeyboardState;
+ SDL_Keymod rawKeyboardModifierState = SDL_GetModState();
+
+ unsafe
+ {
+ IntPtr statePtr = SDL_GetKeyboardState(out int numKeys);
+
+ rawKeyboardState = new ReadOnlySpan<byte>((byte*)statePtr, numKeys);
+ }
+
+ bool[] keysState = new bool[(int)Key.Count];
+
+ for (Key key = 0; key < Key.Count; key++)
+ {
+ int index = ToSDL2Scancode(key);
+ if (index == -1)
+ {
+ SDL_Keymod modifierMask = GetKeyboardModifierMask(key);
+
+ if (modifierMask == SDL_Keymod.KMOD_NONE)
+ {
+ continue;
+ }
+
+ keysState[(int)key] = (rawKeyboardModifierState & modifierMask) == modifierMask;
+ }
+ else
+ {
+ keysState[(int)key] = rawKeyboardState[index] == 1;
+ }
+ }
+
+ return new KeyboardStateSnapshot(keysState);
+ }
+
+ private static float ConvertRawStickValue(short value)
+ {
+ const float ConvertRate = 1.0f / (short.MaxValue + 0.5f);
+
+ return value * ConvertRate;
+ }
+
+ private static (short, short) GetStickValues(ref KeyboardStateSnapshot snapshot, JoyconConfigKeyboardStick<ConfigKey> stickConfig)
+ {
+ short stickX = 0;
+ short stickY = 0;
+
+ if (snapshot.IsPressed((Key)stickConfig.StickUp))
+ {
+ stickY += 1;
+ }
+
+ if (snapshot.IsPressed((Key)stickConfig.StickDown))
+ {
+ stickY -= 1;
+ }
+
+ if (snapshot.IsPressed((Key)stickConfig.StickRight))
+ {
+ stickX += 1;
+ }
+
+ if (snapshot.IsPressed((Key)stickConfig.StickLeft))
+ {
+ stickX -= 1;
+ }
+
+ Vector2 stick = Vector2.Normalize(new Vector2(stickX, stickY));
+
+ return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue));
+ }
+
+ public GamepadStateSnapshot GetMappedStateSnapshot()
+ {
+ KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot();
+ GamepadStateSnapshot result = default;
+
+ lock (_userMappingLock)
+ {
+ if (!HasConfiguration)
+ {
+ return result;
+ }
+
+ foreach (ButtonMappingEntry entry in _buttonsUserMapping)
+ {
+ if (entry.From == Key.Unknown || entry.From == Key.Unbound || entry.To == GamepadButtonInputId.Unbound)
+ {
+ continue;
+ }
+
+ // Do not touch state of button already pressed
+ if (!result.IsPressed(entry.To))
+ {
+ result.SetPressed(entry.To, rawState.IsPressed(entry.From));
+ }
+ }
+
+ (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick);
+ (short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick);
+
+ result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY));
+ result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY));
+ }
+
+ return result;
+ }
+
+ public GamepadStateSnapshot GetStateSnapshot()
+ {
+ throw new NotSupportedException();
+ }
+
+ public (float, float) GetStick(StickInputId inputId)
+ {
+ throw new NotSupportedException();
+ }
+
+ public bool IsPressed(GamepadButtonInputId inputId)
+ {
+ throw new NotSupportedException();
+ }
+
+ public bool IsPressed(Key key)
+ {
+ // We only implement GetKeyboardStateSnapshot.
+ throw new NotSupportedException();
+ }
+
+ public void SetConfiguration(InputConfig configuration)
+ {
+ lock (_userMappingLock)
+ {
+ _configuration = (StandardKeyboardInputConfig)configuration;
+
+ // First clear the buttons mapping
+ _buttonsUserMapping.Clear();
+
+ // Then configure left joycon
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (Key)_configuration.LeftJoycon.DpadDown));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl));
+
+ // Finally configure right joycon
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (Key)_configuration.RightJoycon.ButtonA));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (Key)_configuration.RightJoycon.ButtonB));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (Key)_configuration.RightJoycon.ButtonX));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (Key)_configuration.RightJoycon.ButtonY));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (Key)_configuration.RightJoycon.ButtonPlus));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (Key)_configuration.RightJoycon.ButtonR));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr));
+ _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
+ }
+ }
+
+ public void SetTriggerThreshold(float triggerThreshold)
+ {
+ // No operations
+ }
+
+ public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
+ {
+ // No operations
+ }
+
+ public Vector3 GetMotionData(MotionInputId inputId)
+ {
+ // No operations
+
+ return Vector3.Zero;
+ }
+ }
+} \ No newline at end of file