/ src / Ryujinx / Input / AvaloniaKeyboard.cs
AvaloniaKeyboard.cs
  1  using Ryujinx.Common.Configuration.Hid;
  2  using Ryujinx.Common.Configuration.Hid.Keyboard;
  3  using Ryujinx.Input;
  4  using System;
  5  using System.Collections.Generic;
  6  using System.Numerics;
  7  using ConfigKey = Ryujinx.Common.Configuration.Hid.Key;
  8  using Key = Ryujinx.Input.Key;
  9  
 10  namespace Ryujinx.Ava.Input
 11  {
 12      internal class AvaloniaKeyboard : IKeyboard
 13      {
 14          private readonly List<ButtonMappingEntry> _buttonsUserMapping;
 15          private readonly AvaloniaKeyboardDriver _driver;
 16          private StandardKeyboardInputConfig _configuration;
 17  
 18          private readonly object _userMappingLock = new();
 19  
 20          public string Id { get; }
 21          public string Name { get; }
 22  
 23          public bool IsConnected => true;
 24          public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
 25  
 26          private class ButtonMappingEntry
 27          {
 28              public readonly Key From;
 29              public readonly GamepadButtonInputId To;
 30  
 31              public ButtonMappingEntry(GamepadButtonInputId to, Key from)
 32              {
 33                  To = to;
 34                  From = from;
 35              }
 36          }
 37  
 38          public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name)
 39          {
 40              _buttonsUserMapping = new List<ButtonMappingEntry>();
 41  
 42              _driver = driver;
 43              Id = id;
 44              Name = name;
 45          }
 46  
 47          public KeyboardStateSnapshot GetKeyboardStateSnapshot()
 48          {
 49              return IKeyboard.GetStateSnapshot(this);
 50          }
 51  
 52          public GamepadStateSnapshot GetMappedStateSnapshot()
 53          {
 54              KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot();
 55              GamepadStateSnapshot result = default;
 56  
 57              lock (_userMappingLock)
 58              {
 59                  if (_configuration == null)
 60                  {
 61                      return result;
 62                  }
 63  
 64                  foreach (ButtonMappingEntry entry in _buttonsUserMapping)
 65                  {
 66                      if (entry.From == Key.Unknown || entry.From == Key.Unbound || entry.To == GamepadButtonInputId.Unbound)
 67                      {
 68                          continue;
 69                      }
 70  
 71                      // NOTE: Do not touch state of the button already pressed.
 72                      if (!result.IsPressed(entry.To))
 73                      {
 74                          result.SetPressed(entry.To, rawState.IsPressed(entry.From));
 75                      }
 76                  }
 77  
 78                  (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick);
 79                  (short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick);
 80  
 81                  result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY));
 82                  result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY));
 83              }
 84  
 85              return result;
 86          }
 87  
 88          public GamepadStateSnapshot GetStateSnapshot()
 89          {
 90              throw new NotSupportedException();
 91          }
 92  
 93          public (float, float) GetStick(StickInputId inputId)
 94          {
 95              throw new NotSupportedException();
 96          }
 97  
 98          public bool IsPressed(GamepadButtonInputId inputId)
 99          {
100              throw new NotSupportedException();
101          }
102  
103          public bool IsPressed(Key key)
104          {
105              try
106              {
107                  return _driver.IsPressed(key);
108              }
109              catch
110              {
111                  return false;
112              }
113          }
114  
115          public void SetConfiguration(InputConfig configuration)
116          {
117              lock (_userMappingLock)
118              {
119                  _configuration = (StandardKeyboardInputConfig)configuration;
120  
121                  _buttonsUserMapping.Clear();
122  
123  #pragma warning disable IDE0055 // Disable formatting
124                  // Left JoyCon
125                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick,           (Key)_configuration.LeftJoyconStick.StickButton));
126                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp,              (Key)_configuration.LeftJoycon.DpadUp));
127                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown,            (Key)_configuration.LeftJoycon.DpadDown));
128                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft,            (Key)_configuration.LeftJoycon.DpadLeft));
129                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight,           (Key)_configuration.LeftJoycon.DpadRight));
130                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus,               (Key)_configuration.LeftJoycon.ButtonMinus));
131                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder,        (Key)_configuration.LeftJoycon.ButtonL));
132                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger,         (Key)_configuration.LeftJoycon.ButtonZl));
133                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));
134                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0,  (Key)_configuration.LeftJoycon.ButtonSl));
135  
136                  // Right JoyCon
137                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick,          (Key)_configuration.RightJoyconStick.StickButton));
138                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A,                   (Key)_configuration.RightJoycon.ButtonA));
139                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B,                   (Key)_configuration.RightJoycon.ButtonB));
140                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X,                   (Key)_configuration.RightJoycon.ButtonX));
141                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y,                   (Key)_configuration.RightJoycon.ButtonY));
142                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus,                (Key)_configuration.RightJoycon.ButtonPlus));
143                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder,       (Key)_configuration.RightJoycon.ButtonR));
144                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger,        (Key)_configuration.RightJoycon.ButtonZr));
145                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr));
146                  _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1,  (Key)_configuration.RightJoycon.ButtonSl));
147  #pragma warning restore IDE0055
148              }
149          }
150  
151          public void SetTriggerThreshold(float triggerThreshold) { }
152  
153          public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { }
154  
155          public Vector3 GetMotionData(MotionInputId inputId) => Vector3.Zero;
156  
157          private static float ConvertRawStickValue(short value)
158          {
159              const float ConvertRate = 1.0f / (short.MaxValue + 0.5f);
160  
161              return value * ConvertRate;
162          }
163  
164          private static (short, short) GetStickValues(ref KeyboardStateSnapshot snapshot, JoyconConfigKeyboardStick<ConfigKey> stickConfig)
165          {
166              short stickX = 0;
167              short stickY = 0;
168  
169              if (snapshot.IsPressed((Key)stickConfig.StickUp))
170              {
171                  stickY += 1;
172              }
173  
174              if (snapshot.IsPressed((Key)stickConfig.StickDown))
175              {
176                  stickY -= 1;
177              }
178  
179              if (snapshot.IsPressed((Key)stickConfig.StickRight))
180              {
181                  stickX += 1;
182              }
183  
184              if (snapshot.IsPressed((Key)stickConfig.StickLeft))
185              {
186                  stickX -= 1;
187              }
188  
189              Vector2 stick = new(stickX, stickY);
190  
191              stick = Vector2.Normalize(stick);
192  
193              return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue));
194          }
195  
196          public void Clear()
197          {
198              _driver?.Clear();
199          }
200  
201          public void Dispose() { }
202      }
203  }