NpadDevices.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using Ryujinx.Common;
  5. using Ryujinx.Common.Logging;
  6. using Ryujinx.HLE.HOS.Kernel.Threading;
  7. using Ryujinx.HLE.HOS.Services.Hid.Types;
  8. using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
  9. using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
  10. namespace Ryujinx.HLE.HOS.Services.Hid
  11. {
  12. public class NpadDevices : BaseDevice
  13. {
  14. private const int NoMatchNotifyFrequencyMs = 2000;
  15. private int _activeCount;
  16. private long _lastNotifyTimestamp;
  17. public const int MaxControllers = 9; // Players 1-8 and Handheld
  18. private ControllerType[] _configuredTypes;
  19. private KEvent[] _styleSetUpdateEvents;
  20. private bool[] _supportedPlayers;
  21. internal NpadJoyHoldType JoyHold { get; set; }
  22. internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
  23. internal ControllerType SupportedStyleSets { get; set; }
  24. public NpadDevices(Switch device, bool active = true) : base(device, active)
  25. {
  26. _configuredTypes = new ControllerType[MaxControllers];
  27. SupportedStyleSets = ControllerType.Handheld | ControllerType.JoyconPair |
  28. ControllerType.JoyconLeft | ControllerType.JoyconRight |
  29. ControllerType.ProController;
  30. _supportedPlayers = new bool[MaxControllers];
  31. _supportedPlayers.AsSpan().Fill(true);
  32. _styleSetUpdateEvents = new KEvent[MaxControllers];
  33. for (int i = 0; i < _styleSetUpdateEvents.Length; ++i)
  34. {
  35. _styleSetUpdateEvents[i] = new KEvent(_device.System.KernelContext);
  36. }
  37. _activeCount = 0;
  38. JoyHold = NpadJoyHoldType.Vertical;
  39. }
  40. internal ref KEvent GetStyleSetUpdateEvent(PlayerIndex player)
  41. {
  42. return ref _styleSetUpdateEvents[(int)player];
  43. }
  44. internal void ClearSupportedPlayers()
  45. {
  46. _supportedPlayers.AsSpan().Clear();
  47. }
  48. internal void SetSupportedPlayer(PlayerIndex player, bool supported = true)
  49. {
  50. _supportedPlayers[(int)player] = supported;
  51. }
  52. internal IEnumerable<PlayerIndex> GetSupportedPlayers()
  53. {
  54. for (int i = 0; i < _supportedPlayers.Length; ++i)
  55. {
  56. if (_supportedPlayers[i])
  57. {
  58. yield return (PlayerIndex)i;
  59. }
  60. }
  61. }
  62. public bool Validate(int playerMin, int playerMax, ControllerType acceptedTypes, out int configuredCount, out PlayerIndex primaryIndex)
  63. {
  64. primaryIndex = PlayerIndex.Unknown;
  65. configuredCount = 0;
  66. for (int i = 0; i < MaxControllers; ++i)
  67. {
  68. ControllerType npad = _configuredTypes[i];
  69. if (npad == ControllerType.Handheld && _device.System.State.DockedMode)
  70. {
  71. continue;
  72. }
  73. ControllerType currentType = (ControllerType)_device.Hid.SharedMemory.Npads[i].InternalState.StyleSet;
  74. if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i])
  75. {
  76. configuredCount++;
  77. if (primaryIndex == PlayerIndex.Unknown)
  78. {
  79. primaryIndex = (PlayerIndex)i;
  80. }
  81. }
  82. }
  83. if (configuredCount < playerMin || configuredCount > playerMax || primaryIndex == PlayerIndex.Unknown)
  84. {
  85. return false;
  86. }
  87. return true;
  88. }
  89. public void Configure(params ControllerConfig[] configs)
  90. {
  91. _configuredTypes = new ControllerType[MaxControllers];
  92. for (int i = 0; i < configs.Length; ++i)
  93. {
  94. PlayerIndex player = configs[i].Player;
  95. ControllerType controllerType = configs[i].Type;
  96. if (player > PlayerIndex.Handheld)
  97. {
  98. throw new ArgumentOutOfRangeException("Player must be Player1-8 or Handheld");
  99. }
  100. if (controllerType == ControllerType.Handheld)
  101. {
  102. player = PlayerIndex.Handheld;
  103. }
  104. _configuredTypes[(int)player] = controllerType;
  105. Logger.Info?.Print(LogClass.Hid, $"Configured Controller {controllerType} to {player}");
  106. }
  107. }
  108. public void Update(IList<GamepadInput> states)
  109. {
  110. Remap();
  111. Span<bool> updated = stackalloc bool[10];
  112. // Update configured inputs
  113. for (int i = 0; i < states.Count; ++i)
  114. {
  115. GamepadInput state = states[i];
  116. updated[(int)state.PlayerId] = true;
  117. UpdateInput(state);
  118. }
  119. for (int i = 0; i < updated.Length; i++)
  120. {
  121. if (!updated[i])
  122. {
  123. UpdateDisconnectedInput((PlayerIndex)i);
  124. }
  125. }
  126. }
  127. private void Remap()
  128. {
  129. // Remap/Init if necessary
  130. for (int i = 0; i < MaxControllers; ++i)
  131. {
  132. ControllerType config = _configuredTypes[i];
  133. // Remove Handheld config when Docked
  134. if (config == ControllerType.Handheld && _device.System.State.DockedMode)
  135. {
  136. config = ControllerType.None;
  137. }
  138. // Auto-remap ProController and JoyconPair
  139. if (config == ControllerType.JoyconPair && (SupportedStyleSets & ControllerType.JoyconPair) == 0 && (SupportedStyleSets & ControllerType.ProController) != 0)
  140. {
  141. config = ControllerType.ProController;
  142. }
  143. else if (config == ControllerType.ProController && (SupportedStyleSets & ControllerType.ProController) == 0 && (SupportedStyleSets & ControllerType.JoyconPair) != 0)
  144. {
  145. config = ControllerType.JoyconPair;
  146. }
  147. // Check StyleSet and PlayerSet
  148. if ((config & SupportedStyleSets) == 0 || !_supportedPlayers[i])
  149. {
  150. config = ControllerType.None;
  151. }
  152. SetupNpad((PlayerIndex)i, config);
  153. }
  154. if (_activeCount == 0 && PerformanceCounter.ElapsedMilliseconds > _lastNotifyTimestamp + NoMatchNotifyFrequencyMs)
  155. {
  156. Logger.Warning?.Print(LogClass.Hid, $"No matching controllers found. Application requests '{SupportedStyleSets}' on '{string.Join(", ", GetSupportedPlayers())}'");
  157. _lastNotifyTimestamp = PerformanceCounter.ElapsedMilliseconds;
  158. }
  159. }
  160. private void SetupNpad(PlayerIndex player, ControllerType type)
  161. {
  162. ref NpadInternalState controller = ref _device.Hid.SharedMemory.Npads[(int)player].InternalState;
  163. ControllerType oldType = (ControllerType)controller.StyleSet;
  164. if (oldType == type)
  165. {
  166. return; // Already configured
  167. }
  168. controller = NpadInternalState.Create(); // Reset it
  169. if (type == ControllerType.None)
  170. {
  171. _styleSetUpdateEvents[(int)player].ReadableEvent.Signal(); // Signal disconnect
  172. _activeCount--;
  173. Logger.Info?.Print(LogClass.Hid, $"Disconnected Controller {oldType} from {player}");
  174. return;
  175. }
  176. // TODO: Allow customizing colors at config
  177. controller.JoyAssignmentMode = NpadJoyAssignmentMode.Dual;
  178. controller.FullKeyColor.FullKeyBody = (uint)NpadColor.BodyGray;
  179. controller.FullKeyColor.FullKeyButtons = (uint)NpadColor.ButtonGray;
  180. controller.JoyColor.LeftBody = (uint)NpadColor.BodyNeonBlue;
  181. controller.JoyColor.LeftButtons = (uint)NpadColor.ButtonGray;
  182. controller.JoyColor.RightBody = (uint)NpadColor.BodyNeonRed;
  183. controller.JoyColor.RightButtons = (uint)NpadColor.ButtonGray;
  184. controller.SystemProperties = NpadSystemProperties.IsPoweredJoyDual |
  185. NpadSystemProperties.IsPoweredJoyLeft |
  186. NpadSystemProperties.IsPoweredJoyRight;
  187. controller.BatteryLevelJoyDual = NpadBatteryLevel.Percent100;
  188. controller.BatteryLevelJoyLeft = NpadBatteryLevel.Percent100;
  189. controller.BatteryLevelJoyRight = NpadBatteryLevel.Percent100;
  190. switch (type)
  191. {
  192. case ControllerType.ProController:
  193. controller.StyleSet = NpadStyleTag.FullKey;
  194. controller.DeviceType = DeviceType.FullKey;
  195. controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
  196. NpadSystemProperties.IsPlusAvailable |
  197. NpadSystemProperties.IsMinusAvailable;
  198. controller.AppletFooterUiType = AppletFooterUiType.SwitchProController;
  199. break;
  200. case ControllerType.Handheld:
  201. controller.StyleSet = NpadStyleTag.Handheld;
  202. controller.DeviceType = DeviceType.HandheldLeft |
  203. DeviceType.HandheldRight;
  204. controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
  205. NpadSystemProperties.IsPlusAvailable |
  206. NpadSystemProperties.IsMinusAvailable;
  207. controller.AppletFooterUiType = AppletFooterUiType.HandheldJoyConLeftJoyConRight;
  208. break;
  209. case ControllerType.JoyconPair:
  210. controller.StyleSet = NpadStyleTag.JoyDual;
  211. controller.DeviceType = DeviceType.JoyLeft |
  212. DeviceType.JoyRight;
  213. controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
  214. NpadSystemProperties.IsPlusAvailable |
  215. NpadSystemProperties.IsMinusAvailable;
  216. controller.AppletFooterUiType = _device.System.State.DockedMode ? AppletFooterUiType.JoyDual : AppletFooterUiType.HandheldJoyConLeftJoyConRight;
  217. break;
  218. case ControllerType.JoyconLeft:
  219. controller.StyleSet = NpadStyleTag.JoyLeft;
  220. controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
  221. controller.DeviceType = DeviceType.JoyLeft;
  222. controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
  223. NpadSystemProperties.IsMinusAvailable;
  224. controller.AppletFooterUiType = _device.System.State.DockedMode ? AppletFooterUiType.JoyDualLeftOnly : AppletFooterUiType.HandheldJoyConLeftOnly;
  225. break;
  226. case ControllerType.JoyconRight:
  227. controller.StyleSet = NpadStyleTag.JoyRight;
  228. controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
  229. controller.DeviceType = DeviceType.JoyRight;
  230. controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
  231. NpadSystemProperties.IsPlusAvailable;
  232. controller.AppletFooterUiType = _device.System.State.DockedMode ? AppletFooterUiType.JoyDualRightOnly : AppletFooterUiType.HandheldJoyConRightOnly;
  233. break;
  234. case ControllerType.Pokeball:
  235. controller.StyleSet = NpadStyleTag.Palma;
  236. controller.DeviceType = DeviceType.Palma;
  237. controller.AppletFooterUiType = AppletFooterUiType.None;
  238. break;
  239. }
  240. _styleSetUpdateEvents[(int)player].ReadableEvent.Signal();
  241. _activeCount++;
  242. Logger.Info?.Print(LogClass.Hid, $"Connected Controller {type} to {player}");
  243. }
  244. private ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad)
  245. {
  246. switch (npad.StyleSet)
  247. {
  248. case NpadStyleTag.FullKey:
  249. return ref npad.FullKey;
  250. case NpadStyleTag.Handheld:
  251. return ref npad.Handheld;
  252. case NpadStyleTag.JoyDual:
  253. return ref npad.JoyDual;
  254. case NpadStyleTag.JoyLeft:
  255. return ref npad.JoyLeft;
  256. case NpadStyleTag.JoyRight:
  257. return ref npad.JoyRight;
  258. case NpadStyleTag.Palma:
  259. return ref npad.Palma;
  260. default:
  261. return ref npad.SystemExt;
  262. }
  263. }
  264. private void UpdateUnusedInputIfNotEqual(ref RingLifo<NpadCommonState> currentlyUsed, ref RingLifo<NpadCommonState> possiblyUnused)
  265. {
  266. bool isEquals;
  267. unsafe
  268. {
  269. var aPointer = Unsafe.AsPointer(ref currentlyUsed);
  270. var bPointer = Unsafe.AsPointer(ref possiblyUnused);
  271. isEquals = aPointer == bPointer;
  272. }
  273. if (!isEquals)
  274. {
  275. NpadCommonState newState = new NpadCommonState();
  276. WriteNewInputEntry(ref possiblyUnused, ref newState);
  277. }
  278. }
  279. private void WriteNewInputEntry(ref RingLifo<NpadCommonState> lifo, ref NpadCommonState state)
  280. {
  281. ref NpadCommonState previousEntry = ref lifo.GetCurrentEntryRef();
  282. state.SamplingNumber = previousEntry.SamplingNumber + 1;
  283. lifo.Write(ref state);
  284. }
  285. private void UpdateUnusedSixInputIfNotEqual(ref RingLifo<SixAxisSensorState> currentlyUsed, ref RingLifo<SixAxisSensorState> possiblyUnused)
  286. {
  287. bool isEquals;
  288. unsafe
  289. {
  290. var aPointer = Unsafe.AsPointer(ref currentlyUsed);
  291. var bPointer = Unsafe.AsPointer(ref possiblyUnused);
  292. isEquals = aPointer == bPointer;
  293. }
  294. if (!isEquals)
  295. {
  296. SixAxisSensorState newState = new SixAxisSensorState();
  297. WriteNewSixInputEntry(ref possiblyUnused, ref newState);
  298. }
  299. }
  300. private void WriteNewSixInputEntry(ref RingLifo<SixAxisSensorState> lifo, ref SixAxisSensorState state)
  301. {
  302. ref SixAxisSensorState previousEntry = ref lifo.GetCurrentEntryRef();
  303. state.SamplingNumber = previousEntry.SamplingNumber + 1;
  304. lifo.Write(ref state);
  305. }
  306. private void UpdateInput(GamepadInput state)
  307. {
  308. if (state.PlayerId == PlayerIndex.Unknown)
  309. {
  310. return;
  311. }
  312. ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState;
  313. if (currentNpad.StyleSet == NpadStyleTag.None)
  314. {
  315. return;
  316. }
  317. ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
  318. NpadCommonState newState = new NpadCommonState
  319. {
  320. Buttons = (NpadButton)state.Buttons,
  321. AnalogStickL = new AnalogStickState
  322. {
  323. X = state.LStick.Dx,
  324. Y = state.LStick.Dy,
  325. },
  326. AnalogStickR = new AnalogStickState
  327. {
  328. X = state.RStick.Dx,
  329. Y = state.RStick.Dy,
  330. }
  331. };
  332. newState.Attributes = NpadAttribute.IsConnected;
  333. switch (currentNpad.StyleSet)
  334. {
  335. case NpadStyleTag.Handheld:
  336. case NpadStyleTag.FullKey:
  337. newState.Attributes |= NpadAttribute.IsWired;
  338. break;
  339. case NpadStyleTag.JoyDual:
  340. newState.Attributes |= NpadAttribute.IsLeftConnected |
  341. NpadAttribute.IsRightConnected;
  342. break;
  343. case NpadStyleTag.JoyLeft:
  344. newState.Attributes |= NpadAttribute.IsLeftConnected;
  345. break;
  346. case NpadStyleTag.JoyRight:
  347. newState.Attributes |= NpadAttribute.IsRightConnected;
  348. break;
  349. }
  350. WriteNewInputEntry(ref lifo, ref newState);
  351. // Mirror data to Default layout just in case
  352. if (!currentNpad.StyleSet.HasFlag(NpadStyleTag.SystemExt))
  353. {
  354. WriteNewInputEntry(ref currentNpad.SystemExt, ref newState);
  355. }
  356. UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.FullKey);
  357. UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Handheld);
  358. UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyDual);
  359. UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyLeft);
  360. UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyRight);
  361. UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Palma);
  362. }
  363. private void UpdateDisconnectedInput(PlayerIndex index)
  364. {
  365. ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
  366. NpadCommonState newState = new NpadCommonState();
  367. WriteNewInputEntry(ref currentNpad.FullKey, ref newState);
  368. WriteNewInputEntry(ref currentNpad.Handheld, ref newState);
  369. WriteNewInputEntry(ref currentNpad.JoyDual, ref newState);
  370. WriteNewInputEntry(ref currentNpad.JoyLeft, ref newState);
  371. WriteNewInputEntry(ref currentNpad.JoyRight, ref newState);
  372. WriteNewInputEntry(ref currentNpad.Palma, ref newState);
  373. }
  374. public void UpdateSixAxis(IList<SixAxisInput> states)
  375. {
  376. Span<bool> updated = stackalloc bool[10];
  377. for (int i = 0; i < states.Count; ++i)
  378. {
  379. updated[(int)states[i].PlayerId] = true;
  380. if (SetSixAxisState(states[i]))
  381. {
  382. i++;
  383. if (i >= states.Count)
  384. {
  385. return;
  386. }
  387. SetSixAxisState(states[i], true);
  388. }
  389. }
  390. for (int i = 0; i < updated.Length; i++)
  391. {
  392. if (!updated[i])
  393. {
  394. UpdateDisconnectedInputSixAxis((PlayerIndex)i);
  395. }
  396. }
  397. }
  398. private ref RingLifo<SixAxisSensorState> GetSixAxisSensorLifo(ref NpadInternalState npad, bool isRightPair)
  399. {
  400. switch (npad.StyleSet)
  401. {
  402. case NpadStyleTag.FullKey:
  403. return ref npad.FullKeySixAxisSensor;
  404. case NpadStyleTag.Handheld:
  405. return ref npad.HandheldSixAxisSensor;
  406. case NpadStyleTag.JoyDual:
  407. if (isRightPair)
  408. {
  409. return ref npad.JoyDualRightSixAxisSensor;
  410. }
  411. else
  412. {
  413. return ref npad.JoyDualSixAxisSensor;
  414. }
  415. case NpadStyleTag.JoyLeft:
  416. return ref npad.JoyLeftSixAxisSensor;
  417. case NpadStyleTag.JoyRight:
  418. return ref npad.JoyRightSixAxisSensor;
  419. default:
  420. throw new NotImplementedException($"{npad.StyleSet}");
  421. }
  422. }
  423. private bool SetSixAxisState(SixAxisInput state, bool isRightPair = false)
  424. {
  425. if (state.PlayerId == PlayerIndex.Unknown)
  426. {
  427. return false;
  428. }
  429. ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState;
  430. if (currentNpad.StyleSet == NpadStyleTag.None)
  431. {
  432. return false;
  433. }
  434. HidVector accel = new HidVector()
  435. {
  436. X = state.Accelerometer.X,
  437. Y = state.Accelerometer.Y,
  438. Z = state.Accelerometer.Z
  439. };
  440. HidVector gyro = new HidVector()
  441. {
  442. X = state.Gyroscope.X,
  443. Y = state.Gyroscope.Y,
  444. Z = state.Gyroscope.Z
  445. };
  446. HidVector rotation = new HidVector()
  447. {
  448. X = state.Rotation.X,
  449. Y = state.Rotation.Y,
  450. Z = state.Rotation.Z
  451. };
  452. SixAxisSensorState newState = new SixAxisSensorState
  453. {
  454. Acceleration = accel,
  455. AngularVelocity = gyro,
  456. Angle = rotation,
  457. Attributes = SixAxisSensorAttribute.IsConnected
  458. };
  459. state.Orientation.AsSpan().CopyTo(newState.Direction.ToSpan());
  460. ref RingLifo<SixAxisSensorState> lifo = ref GetSixAxisSensorLifo(ref currentNpad, isRightPair);
  461. WriteNewSixInputEntry(ref lifo, ref newState);
  462. bool needUpdateRight = currentNpad.StyleSet == NpadStyleTag.JoyDual && !isRightPair;
  463. if (!isRightPair)
  464. {
  465. UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.FullKeySixAxisSensor);
  466. UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.HandheldSixAxisSensor);
  467. UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyDualSixAxisSensor);
  468. UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyLeftSixAxisSensor);
  469. UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyRightSixAxisSensor);
  470. }
  471. if (!needUpdateRight)
  472. {
  473. SixAxisSensorState emptyState = new SixAxisSensorState();
  474. emptyState.Attributes = SixAxisSensorAttribute.IsConnected;
  475. WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref emptyState);
  476. }
  477. return needUpdateRight;
  478. }
  479. private void UpdateDisconnectedInputSixAxis(PlayerIndex index)
  480. {
  481. ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
  482. SixAxisSensorState newState = new SixAxisSensorState();
  483. newState.Attributes = SixAxisSensorAttribute.IsConnected;
  484. WriteNewSixInputEntry(ref currentNpad.FullKeySixAxisSensor, ref newState);
  485. WriteNewSixInputEntry(ref currentNpad.HandheldSixAxisSensor, ref newState);
  486. WriteNewSixInputEntry(ref currentNpad.JoyDualSixAxisSensor, ref newState);
  487. WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref newState);
  488. WriteNewSixInputEntry(ref currentNpad.JoyLeftSixAxisSensor, ref newState);
  489. WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState);
  490. }
  491. }
  492. }