/ src / Ryujinx.Input.SDL2 / SDL2Keyboard.cs
SDL2Keyboard.cs
  1  using Ryujinx.Common.Configuration.Hid;
  2  using Ryujinx.Common.Configuration.Hid.Keyboard;
  3  using System;
  4  using System.Collections.Generic;
  5  using System.Numerics;
  6  using System.Runtime.CompilerServices;
  7  using static SDL2.SDL;
  8  
  9  using ConfigKey = Ryujinx.Common.Configuration.Hid.Key;
 10  
 11  namespace Ryujinx.Input.SDL2
 12  {
 13      class SDL2Keyboard : IKeyboard
 14      {
 15          private class ButtonMappingEntry
 16          {
 17              public readonly GamepadButtonInputId To;
 18              public readonly Key From;
 19  
 20              public ButtonMappingEntry(GamepadButtonInputId to, Key from)
 21              {
 22                  To = to;
 23                  From = from;
 24              }
 25          }
 26  
 27          private readonly object _userMappingLock = new();
 28  
 29  #pragma warning disable IDE0052 // Remove unread private member
 30          private readonly SDL2KeyboardDriver _driver;
 31  #pragma warning restore IDE0052
 32          private StandardKeyboardInputConfig _configuration;
 33          private readonly List<ButtonMappingEntry> _buttonsUserMapping;
 34  
 35          private static readonly SDL_Keycode[] _keysDriverMapping = new SDL_Keycode[(int)Key.Count]
 36          {
 37              // INVALID
 38              SDL_Keycode.SDLK_0,
 39              // Presented as modifiers, so invalid here.
 40              SDL_Keycode.SDLK_0,
 41              SDL_Keycode.SDLK_0,
 42              SDL_Keycode.SDLK_0,
 43              SDL_Keycode.SDLK_0,
 44              SDL_Keycode.SDLK_0,
 45              SDL_Keycode.SDLK_0,
 46              SDL_Keycode.SDLK_0,
 47              SDL_Keycode.SDLK_0,
 48              SDL_Keycode.SDLK_0,
 49  
 50              SDL_Keycode.SDLK_F1,
 51              SDL_Keycode.SDLK_F2,
 52              SDL_Keycode.SDLK_F3,
 53              SDL_Keycode.SDLK_F4,
 54              SDL_Keycode.SDLK_F5,
 55              SDL_Keycode.SDLK_F6,
 56              SDL_Keycode.SDLK_F7,
 57              SDL_Keycode.SDLK_F8,
 58              SDL_Keycode.SDLK_F9,
 59              SDL_Keycode.SDLK_F10,
 60              SDL_Keycode.SDLK_F11,
 61              SDL_Keycode.SDLK_F12,
 62              SDL_Keycode.SDLK_F13,
 63              SDL_Keycode.SDLK_F14,
 64              SDL_Keycode.SDLK_F15,
 65              SDL_Keycode.SDLK_F16,
 66              SDL_Keycode.SDLK_F17,
 67              SDL_Keycode.SDLK_F18,
 68              SDL_Keycode.SDLK_F19,
 69              SDL_Keycode.SDLK_F20,
 70              SDL_Keycode.SDLK_F21,
 71              SDL_Keycode.SDLK_F22,
 72              SDL_Keycode.SDLK_F23,
 73              SDL_Keycode.SDLK_F24,
 74  
 75              SDL_Keycode.SDLK_0,
 76              SDL_Keycode.SDLK_0,
 77              SDL_Keycode.SDLK_0,
 78              SDL_Keycode.SDLK_0,
 79              SDL_Keycode.SDLK_0,
 80              SDL_Keycode.SDLK_0,
 81              SDL_Keycode.SDLK_0,
 82              SDL_Keycode.SDLK_0,
 83              SDL_Keycode.SDLK_0,
 84              SDL_Keycode.SDLK_0,
 85              SDL_Keycode.SDLK_0,
 86  
 87              SDL_Keycode.SDLK_UP,
 88              SDL_Keycode.SDLK_DOWN,
 89              SDL_Keycode.SDLK_LEFT,
 90              SDL_Keycode.SDLK_RIGHT,
 91              SDL_Keycode.SDLK_RETURN,
 92              SDL_Keycode.SDLK_ESCAPE,
 93              SDL_Keycode.SDLK_SPACE,
 94              SDL_Keycode.SDLK_TAB,
 95              SDL_Keycode.SDLK_BACKSPACE,
 96              SDL_Keycode.SDLK_INSERT,
 97              SDL_Keycode.SDLK_DELETE,
 98              SDL_Keycode.SDLK_PAGEUP,
 99              SDL_Keycode.SDLK_PAGEDOWN,
100              SDL_Keycode.SDLK_HOME,
101              SDL_Keycode.SDLK_END,
102              SDL_Keycode.SDLK_CAPSLOCK,
103              SDL_Keycode.SDLK_SCROLLLOCK,
104              SDL_Keycode.SDLK_PRINTSCREEN,
105              SDL_Keycode.SDLK_PAUSE,
106              SDL_Keycode.SDLK_NUMLOCKCLEAR,
107              SDL_Keycode.SDLK_CLEAR,
108              SDL_Keycode.SDLK_KP_0,
109              SDL_Keycode.SDLK_KP_1,
110              SDL_Keycode.SDLK_KP_2,
111              SDL_Keycode.SDLK_KP_3,
112              SDL_Keycode.SDLK_KP_4,
113              SDL_Keycode.SDLK_KP_5,
114              SDL_Keycode.SDLK_KP_6,
115              SDL_Keycode.SDLK_KP_7,
116              SDL_Keycode.SDLK_KP_8,
117              SDL_Keycode.SDLK_KP_9,
118              SDL_Keycode.SDLK_KP_DIVIDE,
119              SDL_Keycode.SDLK_KP_MULTIPLY,
120              SDL_Keycode.SDLK_KP_MINUS,
121              SDL_Keycode.SDLK_KP_PLUS,
122              SDL_Keycode.SDLK_KP_DECIMAL,
123              SDL_Keycode.SDLK_KP_ENTER,
124              SDL_Keycode.SDLK_a,
125              SDL_Keycode.SDLK_b,
126              SDL_Keycode.SDLK_c,
127              SDL_Keycode.SDLK_d,
128              SDL_Keycode.SDLK_e,
129              SDL_Keycode.SDLK_f,
130              SDL_Keycode.SDLK_g,
131              SDL_Keycode.SDLK_h,
132              SDL_Keycode.SDLK_i,
133              SDL_Keycode.SDLK_j,
134              SDL_Keycode.SDLK_k,
135              SDL_Keycode.SDLK_l,
136              SDL_Keycode.SDLK_m,
137              SDL_Keycode.SDLK_n,
138              SDL_Keycode.SDLK_o,
139              SDL_Keycode.SDLK_p,
140              SDL_Keycode.SDLK_q,
141              SDL_Keycode.SDLK_r,
142              SDL_Keycode.SDLK_s,
143              SDL_Keycode.SDLK_t,
144              SDL_Keycode.SDLK_u,
145              SDL_Keycode.SDLK_v,
146              SDL_Keycode.SDLK_w,
147              SDL_Keycode.SDLK_x,
148              SDL_Keycode.SDLK_y,
149              SDL_Keycode.SDLK_z,
150              SDL_Keycode.SDLK_0,
151              SDL_Keycode.SDLK_1,
152              SDL_Keycode.SDLK_2,
153              SDL_Keycode.SDLK_3,
154              SDL_Keycode.SDLK_4,
155              SDL_Keycode.SDLK_5,
156              SDL_Keycode.SDLK_6,
157              SDL_Keycode.SDLK_7,
158              SDL_Keycode.SDLK_8,
159              SDL_Keycode.SDLK_9,
160              SDL_Keycode.SDLK_BACKQUOTE,
161              SDL_Keycode.SDLK_BACKQUOTE,
162              SDL_Keycode.SDLK_MINUS,
163              SDL_Keycode.SDLK_PLUS,
164              SDL_Keycode.SDLK_LEFTBRACKET,
165              SDL_Keycode.SDLK_RIGHTBRACKET,
166              SDL_Keycode.SDLK_SEMICOLON,
167              SDL_Keycode.SDLK_QUOTE,
168              SDL_Keycode.SDLK_COMMA,
169              SDL_Keycode.SDLK_PERIOD,
170              SDL_Keycode.SDLK_SLASH,
171              SDL_Keycode.SDLK_BACKSLASH,
172  
173              // Invalids
174              SDL_Keycode.SDLK_0,
175          };
176  
177          public SDL2Keyboard(SDL2KeyboardDriver driver, string id, string name)
178          {
179              _driver = driver;
180              Id = id;
181              Name = name;
182              _buttonsUserMapping = new List<ButtonMappingEntry>();
183          }
184  
185          private bool HasConfiguration => _configuration != null;
186  
187          public string Id { get; }
188  
189          public string Name { get; }
190  
191          public bool IsConnected => true;
192  
193          public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
194  
195          public void Dispose()
196          {
197              // No operations
198          }
199  
200          [MethodImpl(MethodImplOptions.AggressiveInlining)]
201          private static int ToSDL2Scancode(Key key)
202          {
203              if (key >= Key.Unknown && key <= Key.Menu)
204              {
205                  return -1;
206              }
207  
208              return (int)SDL_GetScancodeFromKey(_keysDriverMapping[(int)key]);
209          }
210  
211          private static SDL_Keymod GetKeyboardModifierMask(Key key)
212          {
213              return key switch
214              {
215                  Key.ShiftLeft => SDL_Keymod.KMOD_LSHIFT,
216                  Key.ShiftRight => SDL_Keymod.KMOD_RSHIFT,
217                  Key.ControlLeft => SDL_Keymod.KMOD_LCTRL,
218                  Key.ControlRight => SDL_Keymod.KMOD_RCTRL,
219                  Key.AltLeft => SDL_Keymod.KMOD_LALT,
220                  Key.AltRight => SDL_Keymod.KMOD_RALT,
221                  Key.WinLeft => SDL_Keymod.KMOD_LGUI,
222                  Key.WinRight => SDL_Keymod.KMOD_RGUI,
223                  // NOTE: Menu key isn't supported by SDL2.
224                  _ => SDL_Keymod.KMOD_NONE,
225              };
226          }
227  
228          public KeyboardStateSnapshot GetKeyboardStateSnapshot()
229          {
230              ReadOnlySpan<byte> rawKeyboardState;
231              SDL_Keymod rawKeyboardModifierState = SDL_GetModState();
232  
233              unsafe
234              {
235                  IntPtr statePtr = SDL_GetKeyboardState(out int numKeys);
236  
237                  rawKeyboardState = new ReadOnlySpan<byte>((byte*)statePtr, numKeys);
238              }
239  
240              bool[] keysState = new bool[(int)Key.Count];
241  
242              for (Key key = 0; key < Key.Count; key++)
243              {
244                  int index = ToSDL2Scancode(key);
245                  if (index == -1)
246                  {
247                      SDL_Keymod modifierMask = GetKeyboardModifierMask(key);
248  
249                      if (modifierMask == SDL_Keymod.KMOD_NONE)
250                      {
251                          continue;
252                      }
253  
254                      keysState[(int)key] = (rawKeyboardModifierState & modifierMask) == modifierMask;
255                  }
256                  else
257                  {
258                      keysState[(int)key] = rawKeyboardState[index] == 1;
259                  }
260              }
261  
262              return new KeyboardStateSnapshot(keysState);
263          }
264  
265          private static float ConvertRawStickValue(short value)
266          {
267              const float ConvertRate = 1.0f / (short.MaxValue + 0.5f);
268  
269              return value * ConvertRate;
270          }
271  
272          private static (short, short) GetStickValues(ref KeyboardStateSnapshot snapshot, JoyconConfigKeyboardStick<ConfigKey> stickConfig)
273          {
274              short stickX = 0;
275              short stickY = 0;
276  
277              if (snapshot.IsPressed((Key)stickConfig.StickUp))
278              {
279                  stickY += 1;
280              }
281  
282              if (snapshot.IsPressed((Key)stickConfig.StickDown))
283              {
284                  stickY -= 1;
285              }
286  
287              if (snapshot.IsPressed((Key)stickConfig.StickRight))
288              {
289                  stickX += 1;
290              }
291  
292              if (snapshot.IsPressed((Key)stickConfig.StickLeft))
293              {
294                  stickX -= 1;
295              }
296  
297              Vector2 stick = Vector2.Normalize(new Vector2(stickX, stickY));
298  
299              return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue));
300          }
301  
302          public GamepadStateSnapshot GetMappedStateSnapshot()
303          {
304              KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot();
305              GamepadStateSnapshot result = default;
306  
307              lock (_userMappingLock)
308              {
309                  if (!HasConfiguration)
310                  {
311                      return result;
312                  }
313  
314                  foreach (ButtonMappingEntry entry in _buttonsUserMapping)
315                  {
316                      if (entry.From == Key.Unknown || entry.From == Key.Unbound || entry.To == GamepadButtonInputId.Unbound)
317                      {
318                          continue;
319                      }
320  
321                      // Do not touch state of button already pressed
322                      if (!result.IsPressed(entry.To))
323                      {
324                          result.SetPressed(entry.To, rawState.IsPressed(entry.From));
325                      }
326                  }
327  
328                  (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick);
329                  (short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick);
330  
331                  result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY));
332                  result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY));
333              }
334  
335              return result;
336          }
337  
338          public GamepadStateSnapshot GetStateSnapshot()
339          {
340              throw new NotSupportedException();
341          }
342  
343          public (float, float) GetStick(StickInputId inputId)
344          {
345              throw new NotSupportedException();
346          }
347  
348          public bool IsPressed(GamepadButtonInputId inputId)
349          {
350              throw new NotSupportedException();
351          }
352  
353          public bool IsPressed(Key key)
354          {
355              // We only implement GetKeyboardStateSnapshot.
356              throw new NotSupportedException();
357          }
358  
359          public void SetConfiguration(InputConfig configuration)
360          {
361              lock (_userMappingLock)
362              {
363                  _configuration = (StandardKeyboardInputConfig)configuration;
364  
365                  // First clear the buttons mapping
366                  _buttonsUserMapping.Clear();
367  
368                  // Then configure left joycon
369                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton));
370                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp));
371                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (Key)_configuration.LeftJoycon.DpadDown));
372                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft));
373                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight));
374                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus));
375                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL));
376                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl));
377                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));
378                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl));
379  
380                  // Finally configure right joycon
381                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton));
382                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (Key)_configuration.RightJoycon.ButtonA));
383                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (Key)_configuration.RightJoycon.ButtonB));
384                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (Key)_configuration.RightJoycon.ButtonX));
385                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (Key)_configuration.RightJoycon.ButtonY));
386                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (Key)_configuration.RightJoycon.ButtonPlus));
387                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (Key)_configuration.RightJoycon.ButtonR));
388                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
389                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr));
390                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
391              }
392          }
393  
394          public void SetTriggerThreshold(float triggerThreshold)
395          {
396              // No operations
397          }
398  
399          public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
400          {
401              // No operations
402          }
403  
404          public Vector3 GetMotionData(MotionInputId inputId)
405          {
406              // No operations
407  
408              return Vector3.Zero;
409          }
410      }
411  }