NpadController.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. using Ryujinx.Common;
  2. using Ryujinx.Common.Configuration.Hid;
  3. using Ryujinx.Common.Configuration.Hid.Controller;
  4. using Ryujinx.Common.Configuration.Hid.Controller.Motion;
  5. using Ryujinx.Common.Logging;
  6. using Ryujinx.HLE.HOS.Services.Hid;
  7. using System;
  8. using System.Collections.Concurrent;
  9. using System.Numerics;
  10. using System.Runtime.CompilerServices;
  11. using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client;
  12. using ConfigControllerType = Ryujinx.Common.Configuration.Hid.ControllerType;
  13. namespace Ryujinx.Input.HLE
  14. {
  15. public class NpadController : IDisposable
  16. {
  17. private class HLEButtonMappingEntry
  18. {
  19. public readonly GamepadButtonInputId DriverInputId;
  20. public readonly ControllerKeys HLEInput;
  21. public HLEButtonMappingEntry(GamepadButtonInputId driverInputId, ControllerKeys hleInput)
  22. {
  23. DriverInputId = driverInputId;
  24. HLEInput = hleInput;
  25. }
  26. }
  27. private static readonly HLEButtonMappingEntry[] _hleButtonMapping =
  28. [
  29. new(GamepadButtonInputId.A, ControllerKeys.A),
  30. new(GamepadButtonInputId.B, ControllerKeys.B),
  31. new(GamepadButtonInputId.X, ControllerKeys.X),
  32. new(GamepadButtonInputId.Y, ControllerKeys.Y),
  33. new(GamepadButtonInputId.LeftStick, ControllerKeys.LStick),
  34. new(GamepadButtonInputId.RightStick, ControllerKeys.RStick),
  35. new(GamepadButtonInputId.LeftShoulder, ControllerKeys.L),
  36. new(GamepadButtonInputId.RightShoulder, ControllerKeys.R),
  37. new(GamepadButtonInputId.LeftTrigger, ControllerKeys.Zl),
  38. new(GamepadButtonInputId.RightTrigger, ControllerKeys.Zr),
  39. new(GamepadButtonInputId.DpadUp, ControllerKeys.DpadUp),
  40. new(GamepadButtonInputId.DpadDown, ControllerKeys.DpadDown),
  41. new(GamepadButtonInputId.DpadLeft, ControllerKeys.DpadLeft),
  42. new(GamepadButtonInputId.DpadRight, ControllerKeys.DpadRight),
  43. new(GamepadButtonInputId.Minus, ControllerKeys.Minus),
  44. new(GamepadButtonInputId.Plus, ControllerKeys.Plus),
  45. new(GamepadButtonInputId.SingleLeftTrigger0, ControllerKeys.SlLeft),
  46. new(GamepadButtonInputId.SingleRightTrigger0, ControllerKeys.SrLeft),
  47. new(GamepadButtonInputId.SingleLeftTrigger1, ControllerKeys.SlRight),
  48. new(GamepadButtonInputId.SingleRightTrigger1, ControllerKeys.SrRight)
  49. ];
  50. private class HLEKeyboardMappingEntry
  51. {
  52. public readonly Key TargetKey;
  53. public readonly byte Target;
  54. public HLEKeyboardMappingEntry(Key targetKey, byte target)
  55. {
  56. TargetKey = targetKey;
  57. Target = target;
  58. }
  59. }
  60. private static readonly HLEKeyboardMappingEntry[] _keyMapping =
  61. [
  62. new(Key.A, 0x4),
  63. new(Key.B, 0x5),
  64. new(Key.C, 0x6),
  65. new(Key.D, 0x7),
  66. new(Key.E, 0x8),
  67. new(Key.F, 0x9),
  68. new(Key.G, 0xA),
  69. new(Key.H, 0xB),
  70. new(Key.I, 0xC),
  71. new(Key.J, 0xD),
  72. new(Key.K, 0xE),
  73. new(Key.L, 0xF),
  74. new(Key.M, 0x10),
  75. new(Key.N, 0x11),
  76. new(Key.O, 0x12),
  77. new(Key.P, 0x13),
  78. new(Key.Q, 0x14),
  79. new(Key.R, 0x15),
  80. new(Key.S, 0x16),
  81. new(Key.T, 0x17),
  82. new(Key.U, 0x18),
  83. new(Key.V, 0x19),
  84. new(Key.W, 0x1A),
  85. new(Key.X, 0x1B),
  86. new(Key.Y, 0x1C),
  87. new(Key.Z, 0x1D),
  88. new(Key.Number1, 0x1E),
  89. new(Key.Number2, 0x1F),
  90. new(Key.Number3, 0x20),
  91. new(Key.Number4, 0x21),
  92. new(Key.Number5, 0x22),
  93. new(Key.Number6, 0x23),
  94. new(Key.Number7, 0x24),
  95. new(Key.Number8, 0x25),
  96. new(Key.Number9, 0x26),
  97. new(Key.Number0, 0x27),
  98. new(Key.Enter, 0x28),
  99. new(Key.Escape, 0x29),
  100. new(Key.BackSpace, 0x2A),
  101. new(Key.Tab, 0x2B),
  102. new(Key.Space, 0x2C),
  103. new(Key.Minus, 0x2D),
  104. new(Key.Plus, 0x2E),
  105. new(Key.BracketLeft, 0x2F),
  106. new(Key.BracketRight, 0x30),
  107. new(Key.BackSlash, 0x31),
  108. new(Key.Tilde, 0x32),
  109. new(Key.Semicolon, 0x33),
  110. new(Key.Quote, 0x34),
  111. new(Key.Grave, 0x35),
  112. new(Key.Comma, 0x36),
  113. new(Key.Period, 0x37),
  114. new(Key.Slash, 0x38),
  115. new(Key.CapsLock, 0x39),
  116. new(Key.F1, 0x3a),
  117. new(Key.F2, 0x3b),
  118. new(Key.F3, 0x3c),
  119. new(Key.F4, 0x3d),
  120. new(Key.F5, 0x3e),
  121. new(Key.F6, 0x3f),
  122. new(Key.F7, 0x40),
  123. new(Key.F8, 0x41),
  124. new(Key.F9, 0x42),
  125. new(Key.F10, 0x43),
  126. new(Key.F11, 0x44),
  127. new(Key.F12, 0x45),
  128. new(Key.PrintScreen, 0x46),
  129. new(Key.ScrollLock, 0x47),
  130. new(Key.Pause, 0x48),
  131. new(Key.Insert, 0x49),
  132. new(Key.Home, 0x4A),
  133. new(Key.PageUp, 0x4B),
  134. new(Key.Delete, 0x4C),
  135. new(Key.End, 0x4D),
  136. new(Key.PageDown, 0x4E),
  137. new(Key.Right, 0x4F),
  138. new(Key.Left, 0x50),
  139. new(Key.Down, 0x51),
  140. new(Key.Up, 0x52),
  141. new(Key.NumLock, 0x53),
  142. new(Key.KeypadDivide, 0x54),
  143. new(Key.KeypadMultiply, 0x55),
  144. new(Key.KeypadSubtract, 0x56),
  145. new(Key.KeypadAdd, 0x57),
  146. new(Key.KeypadEnter, 0x58),
  147. new(Key.Keypad1, 0x59),
  148. new(Key.Keypad2, 0x5A),
  149. new(Key.Keypad3, 0x5B),
  150. new(Key.Keypad4, 0x5C),
  151. new(Key.Keypad5, 0x5D),
  152. new(Key.Keypad6, 0x5E),
  153. new(Key.Keypad7, 0x5F),
  154. new(Key.Keypad8, 0x60),
  155. new(Key.Keypad9, 0x61),
  156. new(Key.Keypad0, 0x62),
  157. new(Key.KeypadDecimal, 0x63),
  158. new(Key.F13, 0x68),
  159. new(Key.F14, 0x69),
  160. new(Key.F15, 0x6A),
  161. new(Key.F16, 0x6B),
  162. new(Key.F17, 0x6C),
  163. new(Key.F18, 0x6D),
  164. new(Key.F19, 0x6E),
  165. new(Key.F20, 0x6F),
  166. new(Key.F21, 0x70),
  167. new(Key.F22, 0x71),
  168. new(Key.F23, 0x72),
  169. new(Key.F24, 0x73),
  170. new(Key.ControlLeft, 0xE0),
  171. new(Key.ShiftLeft, 0xE1),
  172. new(Key.AltLeft, 0xE2),
  173. new(Key.WinLeft, 0xE3),
  174. new(Key.ControlRight, 0xE4),
  175. new(Key.ShiftRight, 0xE5),
  176. new(Key.AltRight, 0xE6),
  177. new(Key.WinRight, 0xE7)
  178. ];
  179. private static readonly HLEKeyboardMappingEntry[] _keyModifierMapping =
  180. [
  181. new(Key.ControlLeft, 0),
  182. new(Key.ShiftLeft, 1),
  183. new(Key.AltLeft, 2),
  184. new(Key.WinLeft, 3),
  185. new(Key.ControlRight, 4),
  186. new(Key.ShiftRight, 5),
  187. new(Key.AltRight, 6),
  188. new(Key.WinRight, 7),
  189. new(Key.CapsLock, 8),
  190. new(Key.ScrollLock, 9),
  191. new(Key.NumLock, 10)
  192. ];
  193. private MotionInput _leftMotionInput;
  194. private MotionInput _rightMotionInput;
  195. private IGamepad _gamepad;
  196. private InputConfig _config;
  197. public IGamepadDriver GamepadDriver { get; private set; }
  198. public GamepadStateSnapshot State { get; private set; }
  199. public string Id { get; private set; }
  200. private readonly CemuHookClient _cemuHookClient;
  201. public NpadController(CemuHookClient cemuHookClient)
  202. {
  203. State = default;
  204. Id = null;
  205. _cemuHookClient = cemuHookClient;
  206. }
  207. public bool UpdateDriverConfiguration(IGamepadDriver gamepadDriver, InputConfig config)
  208. {
  209. GamepadDriver = gamepadDriver;
  210. _gamepad?.Dispose();
  211. Id = config.Id;
  212. _gamepad = GamepadDriver.GetGamepad(Id);
  213. UpdateUserConfiguration(config);
  214. return _gamepad != null;
  215. }
  216. public void UpdateUserConfiguration(InputConfig config)
  217. {
  218. if (config is StandardControllerInputConfig controllerConfig)
  219. {
  220. bool needsMotionInputUpdate = _config is not StandardControllerInputConfig oldControllerConfig ||
  221. ((oldControllerConfig.Motion.EnableMotion != controllerConfig.Motion.EnableMotion) &&
  222. (oldControllerConfig.Motion.MotionBackend != controllerConfig.Motion.MotionBackend));
  223. if (needsMotionInputUpdate)
  224. {
  225. UpdateMotionInput(controllerConfig.Motion);
  226. }
  227. }
  228. else
  229. {
  230. // Non-controller doesn't have motions.
  231. _leftMotionInput = null;
  232. }
  233. _config = config;
  234. _gamepad?.SetConfiguration(config);
  235. }
  236. private void UpdateMotionInput(MotionConfigController motionConfig)
  237. {
  238. if (motionConfig.MotionBackend != MotionInputBackendType.CemuHook)
  239. {
  240. _leftMotionInput = new MotionInput();
  241. }
  242. else
  243. {
  244. _leftMotionInput = null;
  245. }
  246. }
  247. public void Update()
  248. {
  249. // _gamepad may be altered by other threads
  250. IGamepad gamepad = _gamepad;
  251. if (gamepad != null && GamepadDriver != null)
  252. {
  253. State = gamepad.GetMappedStateSnapshot();
  254. if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion)
  255. {
  256. if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
  257. {
  258. if (gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion))
  259. {
  260. Vector3 accelerometer = gamepad.GetMotionData(MotionInputId.Accelerometer);
  261. Vector3 gyroscope = gamepad.GetMotionData(MotionInputId.Gyroscope);
  262. accelerometer = new Vector3(accelerometer.X, -accelerometer.Z, accelerometer.Y);
  263. gyroscope = new Vector3(gyroscope.X, -gyroscope.Z, gyroscope.Y);
  264. _leftMotionInput.Update(accelerometer, gyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
  265. if (controllerConfig.ControllerType == ConfigControllerType.JoyconPair)
  266. {
  267. _rightMotionInput = _leftMotionInput;
  268. }
  269. }
  270. }
  271. else if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.CemuHook && controllerConfig.Motion is CemuHookMotionConfigController cemuControllerConfig)
  272. {
  273. int clientId = (int)controllerConfig.PlayerIndex;
  274. // First of all ensure we are registered
  275. _cemuHookClient.RegisterClient(clientId, cemuControllerConfig.DsuServerHost, cemuControllerConfig.DsuServerPort);
  276. // Then request and retrieve the data
  277. _cemuHookClient.RequestData(clientId, cemuControllerConfig.Slot);
  278. _cemuHookClient.TryGetData(clientId, cemuControllerConfig.Slot, out _leftMotionInput);
  279. if (controllerConfig.ControllerType == ConfigControllerType.JoyconPair)
  280. {
  281. if (!cemuControllerConfig.MirrorInput)
  282. {
  283. _cemuHookClient.RequestData(clientId, cemuControllerConfig.AltSlot);
  284. _cemuHookClient.TryGetData(clientId, cemuControllerConfig.AltSlot, out _rightMotionInput);
  285. }
  286. else
  287. {
  288. _rightMotionInput = _leftMotionInput;
  289. }
  290. }
  291. }
  292. }
  293. }
  294. else
  295. {
  296. // Reset states
  297. State = default;
  298. _leftMotionInput = null;
  299. }
  300. }
  301. public GamepadInput GetHLEInputState()
  302. {
  303. GamepadInput state = new();
  304. // First update all buttons
  305. foreach (HLEButtonMappingEntry entry in _hleButtonMapping)
  306. {
  307. if (State.IsPressed(entry.DriverInputId))
  308. {
  309. state.Buttons |= entry.HLEInput;
  310. }
  311. }
  312. if (_gamepad is IKeyboard)
  313. {
  314. (float leftAxisX, float leftAxisY) = State.GetStick(StickInputId.Left);
  315. (float rightAxisX, float rightAxisY) = State.GetStick(StickInputId.Right);
  316. state.LStick = new JoystickPosition
  317. {
  318. Dx = ClampAxis(leftAxisX),
  319. Dy = ClampAxis(leftAxisY),
  320. };
  321. state.RStick = new JoystickPosition
  322. {
  323. Dx = ClampAxis(rightAxisX),
  324. Dy = ClampAxis(rightAxisY),
  325. };
  326. }
  327. else if (_config is StandardControllerInputConfig controllerConfig)
  328. {
  329. (float leftAxisX, float leftAxisY) = State.GetStick(StickInputId.Left);
  330. (float rightAxisX, float rightAxisY) = State.GetStick(StickInputId.Right);
  331. state.LStick = ClampToCircle(ApplyDeadzone(leftAxisX, leftAxisY, controllerConfig.DeadzoneLeft), controllerConfig.RangeLeft);
  332. state.RStick = ClampToCircle(ApplyDeadzone(rightAxisX, rightAxisY, controllerConfig.DeadzoneRight), controllerConfig.RangeRight);
  333. }
  334. return state;
  335. }
  336. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  337. private static JoystickPosition ApplyDeadzone(float x, float y, float deadzone)
  338. {
  339. float magnitudeClamped = Math.Min(MathF.Sqrt(x * x + y * y), 1f);
  340. if (magnitudeClamped <= deadzone)
  341. {
  342. return new JoystickPosition { Dx = 0, Dy = 0 };
  343. }
  344. return new JoystickPosition
  345. {
  346. Dx = ClampAxis((x / magnitudeClamped) * ((magnitudeClamped - deadzone) / (1 - deadzone))),
  347. Dy = ClampAxis((y / magnitudeClamped) * ((magnitudeClamped - deadzone) / (1 - deadzone))),
  348. };
  349. }
  350. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  351. private static short ClampAxis(float value)
  352. {
  353. if (Math.Sign(value) < 0)
  354. {
  355. return (short)Math.Max(value * -short.MinValue, short.MinValue);
  356. }
  357. return (short)Math.Min(value * short.MaxValue, short.MaxValue);
  358. }
  359. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  360. private static JoystickPosition ClampToCircle(JoystickPosition position, float range)
  361. {
  362. Vector2 point = new Vector2(position.Dx, position.Dy) * range;
  363. if (point.Length() > short.MaxValue)
  364. {
  365. point = point / point.Length() * short.MaxValue;
  366. }
  367. return new JoystickPosition
  368. {
  369. Dx = (int)point.X,
  370. Dy = (int)point.Y,
  371. };
  372. }
  373. public SixAxisInput GetHLEMotionState(bool isJoyconRightPair = false)
  374. {
  375. float[] orientationForHLE = new float[9];
  376. Vector3 gyroscope;
  377. Vector3 accelerometer;
  378. Vector3 rotation;
  379. MotionInput motionInput = _leftMotionInput;
  380. if (isJoyconRightPair)
  381. {
  382. if (_rightMotionInput == null)
  383. {
  384. return default;
  385. }
  386. motionInput = _rightMotionInput;
  387. }
  388. if (motionInput != null)
  389. {
  390. gyroscope = Truncate(motionInput.Gyroscrope * 0.0027f, 3);
  391. accelerometer = Truncate(motionInput.Accelerometer, 3);
  392. rotation = Truncate(motionInput.Rotation * 0.0027f, 3);
  393. Matrix4x4 orientation = motionInput.GetOrientation();
  394. orientationForHLE[0] = Math.Clamp(orientation.M11, -1f, 1f);
  395. orientationForHLE[1] = Math.Clamp(orientation.M12, -1f, 1f);
  396. orientationForHLE[2] = Math.Clamp(orientation.M13, -1f, 1f);
  397. orientationForHLE[3] = Math.Clamp(orientation.M21, -1f, 1f);
  398. orientationForHLE[4] = Math.Clamp(orientation.M22, -1f, 1f);
  399. orientationForHLE[5] = Math.Clamp(orientation.M23, -1f, 1f);
  400. orientationForHLE[6] = Math.Clamp(orientation.M31, -1f, 1f);
  401. orientationForHLE[7] = Math.Clamp(orientation.M32, -1f, 1f);
  402. orientationForHLE[8] = Math.Clamp(orientation.M33, -1f, 1f);
  403. }
  404. else
  405. {
  406. gyroscope = new Vector3();
  407. accelerometer = new Vector3();
  408. rotation = new Vector3();
  409. }
  410. return new SixAxisInput
  411. {
  412. Accelerometer = accelerometer,
  413. Gyroscope = gyroscope,
  414. Rotation = rotation,
  415. Orientation = orientationForHLE,
  416. };
  417. }
  418. private static Vector3 Truncate(Vector3 value, int decimals)
  419. {
  420. float power = MathF.Pow(10, decimals);
  421. value.X = float.IsNegative(value.X) ? MathF.Ceiling(value.X * power) / power : MathF.Floor(value.X * power) / power;
  422. value.Y = float.IsNegative(value.Y) ? MathF.Ceiling(value.Y * power) / power : MathF.Floor(value.Y * power) / power;
  423. value.Z = float.IsNegative(value.Z) ? MathF.Ceiling(value.Z * power) / power : MathF.Floor(value.Z * power) / power;
  424. return value;
  425. }
  426. public static KeyboardInput GetHLEKeyboardInput(IGamepadDriver KeyboardDriver)
  427. {
  428. IKeyboard keyboard = KeyboardDriver.GetGamepad("0") as IKeyboard;
  429. KeyboardStateSnapshot keyboardState = keyboard.GetKeyboardStateSnapshot();
  430. KeyboardInput hidKeyboard = new()
  431. {
  432. Modifier = 0,
  433. Keys = new ulong[0x4],
  434. };
  435. foreach (HLEKeyboardMappingEntry entry in _keyMapping)
  436. {
  437. ulong value = keyboardState.IsPressed(entry.TargetKey) ? 1UL : 0UL;
  438. hidKeyboard.Keys[entry.Target / 0x40] |= (value << (entry.Target % 0x40));
  439. }
  440. foreach (HLEKeyboardMappingEntry entry in _keyModifierMapping)
  441. {
  442. int value = keyboardState.IsPressed(entry.TargetKey) ? 1 : 0;
  443. hidKeyboard.Modifier |= value << entry.Target;
  444. }
  445. return hidKeyboard;
  446. }
  447. protected virtual void Dispose(bool disposing)
  448. {
  449. if (disposing)
  450. {
  451. _gamepad?.Dispose();
  452. }
  453. }
  454. public void Dispose()
  455. {
  456. GC.SuppressFinalize(this);
  457. Dispose(true);
  458. }
  459. public void UpdateRumble(ConcurrentQueue<(VibrationValue, VibrationValue)> queue)
  460. {
  461. if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue))
  462. {
  463. if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble)
  464. {
  465. VibrationValue leftVibrationValue = dualVibrationValue.Item1;
  466. VibrationValue rightVibrationValue = dualVibrationValue.Item2;
  467. float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
  468. float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
  469. _gamepad.Rumble(low, high, uint.MaxValue);
  470. Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
  471. $"L.low.amp={leftVibrationValue.AmplitudeLow}, " +
  472. $"L.high.amp={leftVibrationValue.AmplitudeHigh}, " +
  473. $"R.low.amp={rightVibrationValue.AmplitudeLow}, " +
  474. $"R.high.amp={rightVibrationValue.AmplitudeHigh} " +
  475. $"--> ({low}, {high})");
  476. }
  477. }
  478. }
  479. }
  480. }