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 }