|
@@ -1,16 +1,17 @@
|
|
|
using System;
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
|
|
|
+using System.Runtime.CompilerServices;
|
|
|
using Ryujinx.Common;
|
|
using Ryujinx.Common;
|
|
|
using Ryujinx.Common.Logging;
|
|
using Ryujinx.Common.Logging;
|
|
|
-using Ryujinx.Common.Memory;
|
|
|
|
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
|
|
|
+using Ryujinx.HLE.HOS.Services.Hid.Types;
|
|
|
|
|
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
|
|
|
|
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
|
|
|
|
|
|
|
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
{
|
|
{
|
|
|
public class NpadDevices : BaseDevice
|
|
public class NpadDevices : BaseDevice
|
|
|
{
|
|
{
|
|
|
- private const BatteryCharge DefaultBatteryCharge = BatteryCharge.Percent100;
|
|
|
|
|
-
|
|
|
|
|
private const int NoMatchNotifyFrequencyMs = 2000;
|
|
private const int NoMatchNotifyFrequencyMs = 2000;
|
|
|
private int _activeCount;
|
|
private int _activeCount;
|
|
|
private long _lastNotifyTimestamp;
|
|
private long _lastNotifyTimestamp;
|
|
@@ -86,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ControllerType currentType = _device.Hid.SharedMemory.Npads[i].Header.Type;
|
|
|
|
|
|
|
+ ControllerType currentType = (ControllerType)_device.Hid.SharedMemory.Npads[i].InternalState.StyleSet;
|
|
|
|
|
|
|
|
if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i])
|
|
if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i])
|
|
|
{
|
|
{
|
|
@@ -135,12 +136,24 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
{
|
|
{
|
|
|
Remap();
|
|
Remap();
|
|
|
|
|
|
|
|
- UpdateAllEntries();
|
|
|
|
|
|
|
+ Span<bool> updated = stackalloc bool[10];
|
|
|
|
|
|
|
|
// Update configured inputs
|
|
// Update configured inputs
|
|
|
for (int i = 0; i < states.Count; ++i)
|
|
for (int i = 0; i < states.Count; ++i)
|
|
|
{
|
|
{
|
|
|
- UpdateInput(states[i]);
|
|
|
|
|
|
|
+ GamepadInput state = states[i];
|
|
|
|
|
+
|
|
|
|
|
+ updated[(int)state.PlayerId] = true;
|
|
|
|
|
+
|
|
|
|
|
+ UpdateInput(state);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < updated.Length; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!updated[i])
|
|
|
|
|
+ {
|
|
|
|
|
+ UpdateDisconnectedInput((PlayerIndex)i);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -185,16 +198,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
|
|
|
|
|
private void SetupNpad(PlayerIndex player, ControllerType type)
|
|
private void SetupNpad(PlayerIndex player, ControllerType type)
|
|
|
{
|
|
{
|
|
|
- ref ShMemNpad controller = ref _device.Hid.SharedMemory.Npads[(int)player];
|
|
|
|
|
|
|
+ ref NpadInternalState controller = ref _device.Hid.SharedMemory.Npads[(int)player].InternalState;
|
|
|
|
|
|
|
|
- ControllerType oldType = controller.Header.Type;
|
|
|
|
|
|
|
+ ControllerType oldType = (ControllerType)controller.StyleSet;
|
|
|
|
|
|
|
|
if (oldType == type)
|
|
if (oldType == type)
|
|
|
{
|
|
{
|
|
|
return; // Already configured
|
|
return; // Already configured
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- controller = new ShMemNpad(); // Zero it
|
|
|
|
|
|
|
+ controller = NpadInternalState.Create(); // Reset it
|
|
|
|
|
|
|
|
if (type == ControllerType.None)
|
|
if (type == ControllerType.None)
|
|
|
{
|
|
{
|
|
@@ -207,87 +220,151 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// TODO: Allow customizing colors at config
|
|
// TODO: Allow customizing colors at config
|
|
|
- NpadStateHeader defaultHeader = new NpadStateHeader
|
|
|
|
|
- {
|
|
|
|
|
- IsHalf = false,
|
|
|
|
|
- SingleColorBody = NpadColor.BodyGray,
|
|
|
|
|
- SingleColorButtons = NpadColor.ButtonGray,
|
|
|
|
|
- LeftColorBody = NpadColor.BodyNeonBlue,
|
|
|
|
|
- LeftColorButtons = NpadColor.ButtonGray,
|
|
|
|
|
- RightColorBody = NpadColor.BodyNeonRed,
|
|
|
|
|
- RightColorButtons = NpadColor.ButtonGray
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- controller.SystemProperties = NpadSystemProperties.PowerInfo0Connected |
|
|
|
|
|
- NpadSystemProperties.PowerInfo1Connected |
|
|
|
|
|
- NpadSystemProperties.PowerInfo2Connected;
|
|
|
|
|
-
|
|
|
|
|
- controller.BatteryState.ToSpan().Fill(DefaultBatteryCharge);
|
|
|
|
|
|
|
+ controller.JoyAssignmentMode = NpadJoyAssignmentMode.Dual;
|
|
|
|
|
+ controller.FullKeyColor.FullKeyBody = (uint)NpadColor.BodyGray;
|
|
|
|
|
+ controller.FullKeyColor.FullKeyButtons = (uint)NpadColor.ButtonGray;
|
|
|
|
|
+ controller.JoyColor.LeftBody = (uint)NpadColor.BodyNeonBlue;
|
|
|
|
|
+ controller.JoyColor.LeftButtons = (uint)NpadColor.ButtonGray;
|
|
|
|
|
+ controller.JoyColor.RightBody = (uint)NpadColor.BodyNeonRed;
|
|
|
|
|
+ controller.JoyColor.RightButtons = (uint)NpadColor.ButtonGray;
|
|
|
|
|
+
|
|
|
|
|
+ controller.SystemProperties = NpadSystemProperties.IsPoweredJoyDual |
|
|
|
|
|
+ NpadSystemProperties.IsPoweredJoyLeft |
|
|
|
|
|
+ NpadSystemProperties.IsPoweredJoyRight;
|
|
|
|
|
+
|
|
|
|
|
+ controller.BatteryLevelJoyDual = NpadBatteryLevel.Percent100;
|
|
|
|
|
+ controller.BatteryLevelJoyLeft = NpadBatteryLevel.Percent100;
|
|
|
|
|
+ controller.BatteryLevelJoyRight = NpadBatteryLevel.Percent100;
|
|
|
|
|
|
|
|
switch (type)
|
|
switch (type)
|
|
|
{
|
|
{
|
|
|
case ControllerType.ProController:
|
|
case ControllerType.ProController:
|
|
|
- defaultHeader.Type = ControllerType.ProController;
|
|
|
|
|
|
|
+ controller.StyleSet = NpadStyleTag.FullKey;
|
|
|
controller.DeviceType = DeviceType.FullKey;
|
|
controller.DeviceType = DeviceType.FullKey;
|
|
|
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
|
|
|
|
|
- NpadSystemProperties.PlusButtonCapability |
|
|
|
|
|
- NpadSystemProperties.MinusButtonCapability;
|
|
|
|
|
|
|
+ controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
|
|
|
|
+ NpadSystemProperties.IsPlusAvailable |
|
|
|
|
|
+ NpadSystemProperties.IsMinusAvailable;
|
|
|
break;
|
|
break;
|
|
|
case ControllerType.Handheld:
|
|
case ControllerType.Handheld:
|
|
|
- defaultHeader.Type = ControllerType.Handheld;
|
|
|
|
|
|
|
+ controller.StyleSet = NpadStyleTag.Handheld;
|
|
|
controller.DeviceType = DeviceType.HandheldLeft |
|
|
controller.DeviceType = DeviceType.HandheldLeft |
|
|
|
DeviceType.HandheldRight;
|
|
DeviceType.HandheldRight;
|
|
|
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
|
|
|
|
|
- NpadSystemProperties.PlusButtonCapability |
|
|
|
|
|
- NpadSystemProperties.MinusButtonCapability;
|
|
|
|
|
|
|
+ controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
|
|
|
|
+ NpadSystemProperties.IsPlusAvailable |
|
|
|
|
|
+ NpadSystemProperties.IsMinusAvailable;
|
|
|
break;
|
|
break;
|
|
|
case ControllerType.JoyconPair:
|
|
case ControllerType.JoyconPair:
|
|
|
- defaultHeader.Type = ControllerType.JoyconPair;
|
|
|
|
|
|
|
+ controller.StyleSet = NpadStyleTag.JoyDual;
|
|
|
controller.DeviceType = DeviceType.JoyLeft |
|
|
controller.DeviceType = DeviceType.JoyLeft |
|
|
|
DeviceType.JoyRight;
|
|
DeviceType.JoyRight;
|
|
|
- controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
|
|
|
|
|
- NpadSystemProperties.PlusButtonCapability |
|
|
|
|
|
- NpadSystemProperties.MinusButtonCapability;
|
|
|
|
|
|
|
+ controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
|
|
|
|
+ NpadSystemProperties.IsPlusAvailable |
|
|
|
|
|
+ NpadSystemProperties.IsMinusAvailable;
|
|
|
break;
|
|
break;
|
|
|
case ControllerType.JoyconLeft:
|
|
case ControllerType.JoyconLeft:
|
|
|
- defaultHeader.Type = ControllerType.JoyconLeft;
|
|
|
|
|
- defaultHeader.IsHalf = true;
|
|
|
|
|
|
|
+ controller.StyleSet = NpadStyleTag.JoyLeft;
|
|
|
|
|
+ controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
|
|
controller.DeviceType = DeviceType.JoyLeft;
|
|
controller.DeviceType = DeviceType.JoyLeft;
|
|
|
- controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
|
|
|
|
|
- NpadSystemProperties.MinusButtonCapability;
|
|
|
|
|
|
|
+ controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
|
|
|
|
|
+ NpadSystemProperties.IsMinusAvailable;
|
|
|
break;
|
|
break;
|
|
|
case ControllerType.JoyconRight:
|
|
case ControllerType.JoyconRight:
|
|
|
- defaultHeader.Type = ControllerType.JoyconRight;
|
|
|
|
|
- defaultHeader.IsHalf = true;
|
|
|
|
|
|
|
+ controller.StyleSet = NpadStyleTag.JoyRight;
|
|
|
|
|
+ controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
|
|
controller.DeviceType = DeviceType.JoyRight;
|
|
controller.DeviceType = DeviceType.JoyRight;
|
|
|
- controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
|
|
|
|
|
- NpadSystemProperties.PlusButtonCapability;
|
|
|
|
|
|
|
+ controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
|
|
|
|
|
+ NpadSystemProperties.IsPlusAvailable;
|
|
|
break;
|
|
break;
|
|
|
case ControllerType.Pokeball:
|
|
case ControllerType.Pokeball:
|
|
|
- defaultHeader.Type = ControllerType.Pokeball;
|
|
|
|
|
|
|
+ controller.StyleSet = NpadStyleTag.Palma;
|
|
|
controller.DeviceType = DeviceType.Palma;
|
|
controller.DeviceType = DeviceType.Palma;
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- controller.Header = defaultHeader;
|
|
|
|
|
-
|
|
|
|
|
_styleSetUpdateEvents[(int)player].ReadableEvent.Signal();
|
|
_styleSetUpdateEvents[(int)player].ReadableEvent.Signal();
|
|
|
_activeCount++;
|
|
_activeCount++;
|
|
|
|
|
|
|
|
Logger.Info?.Print(LogClass.Hid, $"Connected Controller {type} to {player}");
|
|
Logger.Info?.Print(LogClass.Hid, $"Connected Controller {type} to {player}");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static NpadLayoutsIndex ControllerTypeToNpadLayout(ControllerType controllerType)
|
|
|
|
|
- => controllerType switch
|
|
|
|
|
|
|
+ private ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad)
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (npad.StyleSet)
|
|
|
|
|
+ {
|
|
|
|
|
+ case NpadStyleTag.FullKey:
|
|
|
|
|
+ return ref npad.FullKey;
|
|
|
|
|
+ case NpadStyleTag.Handheld:
|
|
|
|
|
+ return ref npad.Handheld;
|
|
|
|
|
+ case NpadStyleTag.JoyDual:
|
|
|
|
|
+ return ref npad.JoyDual;
|
|
|
|
|
+ case NpadStyleTag.JoyLeft:
|
|
|
|
|
+ return ref npad.JoyLeft;
|
|
|
|
|
+ case NpadStyleTag.JoyRight:
|
|
|
|
|
+ return ref npad.JoyRight;
|
|
|
|
|
+ case NpadStyleTag.Palma:
|
|
|
|
|
+ return ref npad.Palma;
|
|
|
|
|
+ default:
|
|
|
|
|
+ return ref npad.SystemExt;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void UpdateUnusedInputIfNotEqual(ref RingLifo<NpadCommonState> currentlyUsed, ref RingLifo<NpadCommonState> possiblyUnused)
|
|
|
|
|
+ {
|
|
|
|
|
+ bool isEquals;
|
|
|
|
|
+
|
|
|
|
|
+ unsafe
|
|
|
|
|
+ {
|
|
|
|
|
+ var aPointer = Unsafe.AsPointer(ref currentlyUsed);
|
|
|
|
|
+ var bPointer = Unsafe.AsPointer(ref possiblyUnused);
|
|
|
|
|
+
|
|
|
|
|
+ isEquals = aPointer == bPointer;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!isEquals)
|
|
|
|
|
+ {
|
|
|
|
|
+ NpadCommonState newState = new NpadCommonState();
|
|
|
|
|
+
|
|
|
|
|
+ WriteNewInputEntry(ref possiblyUnused, ref newState);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void WriteNewInputEntry(ref RingLifo<NpadCommonState> lifo, ref NpadCommonState state)
|
|
|
|
|
+ {
|
|
|
|
|
+ ref NpadCommonState previousEntry = ref lifo.GetCurrentEntryRef();
|
|
|
|
|
+
|
|
|
|
|
+ state.SamplingNumber = previousEntry.SamplingNumber + 1;
|
|
|
|
|
+
|
|
|
|
|
+ lifo.Write(ref state);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void UpdateUnusedSixInputIfNotEqual(ref RingLifo<SixAxisSensorState> currentlyUsed, ref RingLifo<SixAxisSensorState> possiblyUnused)
|
|
|
{
|
|
{
|
|
|
- ControllerType.ProController => NpadLayoutsIndex.ProController,
|
|
|
|
|
- ControllerType.Handheld => NpadLayoutsIndex.Handheld,
|
|
|
|
|
- ControllerType.JoyconPair => NpadLayoutsIndex.JoyDual,
|
|
|
|
|
- ControllerType.JoyconLeft => NpadLayoutsIndex.JoyLeft,
|
|
|
|
|
- ControllerType.JoyconRight => NpadLayoutsIndex.JoyRight,
|
|
|
|
|
- ControllerType.Pokeball => NpadLayoutsIndex.Pokeball,
|
|
|
|
|
- _ => NpadLayoutsIndex.SystemExternal
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ bool isEquals;
|
|
|
|
|
+
|
|
|
|
|
+ unsafe
|
|
|
|
|
+ {
|
|
|
|
|
+ var aPointer = Unsafe.AsPointer(ref currentlyUsed);
|
|
|
|
|
+ var bPointer = Unsafe.AsPointer(ref possiblyUnused);
|
|
|
|
|
+
|
|
|
|
|
+ isEquals = aPointer == bPointer;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!isEquals)
|
|
|
|
|
+ {
|
|
|
|
|
+ SixAxisSensorState newState = new SixAxisSensorState();
|
|
|
|
|
+
|
|
|
|
|
+ WriteNewSixInputEntry(ref possiblyUnused, ref newState);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void WriteNewSixInputEntry(ref RingLifo<SixAxisSensorState> lifo, ref SixAxisSensorState state)
|
|
|
|
|
+ {
|
|
|
|
|
+ ref SixAxisSensorState previousEntry = ref lifo.GetCurrentEntryRef();
|
|
|
|
|
+
|
|
|
|
|
+ state.SamplingNumber = previousEntry.SamplingNumber + 1;
|
|
|
|
|
+
|
|
|
|
|
+ lifo.Write(ref state);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
private void UpdateInput(GamepadInput state)
|
|
private void UpdateInput(GamepadInput state)
|
|
|
{
|
|
{
|
|
@@ -296,43 +373,88 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId];
|
|
|
|
|
|
|
+ ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState;
|
|
|
|
|
|
|
|
- if (currentNpad.Header.Type == ControllerType.None)
|
|
|
|
|
|
|
+ if (currentNpad.StyleSet == NpadStyleTag.None)
|
|
|
{
|
|
{
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ref NpadLayout currentLayout = ref currentNpad.Layouts[(int)ControllerTypeToNpadLayout(currentNpad.Header.Type)];
|
|
|
|
|
- ref NpadState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
|
|
|
|
|
|
|
+ ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
|
|
|
|
|
+
|
|
|
|
|
+ NpadCommonState newState = new NpadCommonState
|
|
|
|
|
+ {
|
|
|
|
|
+ Buttons = (NpadButton)state.Buttons,
|
|
|
|
|
+ AnalogStickL = new AnalogStickState
|
|
|
|
|
+ {
|
|
|
|
|
+ X = state.LStick.Dx,
|
|
|
|
|
+ Y = state.LStick.Dy,
|
|
|
|
|
+ },
|
|
|
|
|
+ AnalogStickR = new AnalogStickState
|
|
|
|
|
+ {
|
|
|
|
|
+ X = state.RStick.Dx,
|
|
|
|
|
+ Y = state.RStick.Dy,
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ newState.Attributes = NpadAttribute.IsConnected;
|
|
|
|
|
+
|
|
|
|
|
+ switch (currentNpad.StyleSet)
|
|
|
|
|
+ {
|
|
|
|
|
+ case NpadStyleTag.Handheld:
|
|
|
|
|
+ case NpadStyleTag.FullKey:
|
|
|
|
|
+ newState.Attributes |= NpadAttribute.IsWired;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NpadStyleTag.JoyDual:
|
|
|
|
|
+ newState.Attributes |= NpadAttribute.IsLeftConnected |
|
|
|
|
|
+ NpadAttribute.IsRightConnected;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NpadStyleTag.JoyLeft:
|
|
|
|
|
+ newState.Attributes |= NpadAttribute.IsLeftConnected;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case NpadStyleTag.JoyRight:
|
|
|
|
|
+ newState.Attributes |= NpadAttribute.IsRightConnected;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- currentEntry.Buttons = state.Buttons;
|
|
|
|
|
- currentEntry.LStickX = state.LStick.Dx;
|
|
|
|
|
- currentEntry.LStickY = state.LStick.Dy;
|
|
|
|
|
- currentEntry.RStickX = state.RStick.Dx;
|
|
|
|
|
- currentEntry.RStickY = state.RStick.Dy;
|
|
|
|
|
|
|
+ WriteNewInputEntry(ref lifo, ref newState);
|
|
|
|
|
|
|
|
// Mirror data to Default layout just in case
|
|
// Mirror data to Default layout just in case
|
|
|
- ref NpadLayout mainLayout = ref currentNpad.Layouts[(int)NpadLayoutsIndex.SystemExternal];
|
|
|
|
|
- mainLayout.Entries[(int)mainLayout.Header.LatestEntry] = currentEntry;
|
|
|
|
|
|
|
+ if (!currentNpad.StyleSet.HasFlag(NpadStyleTag.SystemExt))
|
|
|
|
|
+ {
|
|
|
|
|
+ WriteNewInputEntry(ref currentNpad.SystemExt, ref newState);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.FullKey);
|
|
|
|
|
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Handheld);
|
|
|
|
|
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyDual);
|
|
|
|
|
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyLeft);
|
|
|
|
|
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyRight);
|
|
|
|
|
+ UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Palma);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private static SixAxixLayoutsIndex ControllerTypeToSixAxisLayout(ControllerType controllerType)
|
|
|
|
|
- => controllerType switch
|
|
|
|
|
|
|
+ private void UpdateDisconnectedInput(PlayerIndex index)
|
|
|
{
|
|
{
|
|
|
- ControllerType.ProController => SixAxixLayoutsIndex.ProController,
|
|
|
|
|
- ControllerType.Handheld => SixAxixLayoutsIndex.Handheld,
|
|
|
|
|
- ControllerType.JoyconPair => SixAxixLayoutsIndex.JoyDualLeft,
|
|
|
|
|
- ControllerType.JoyconLeft => SixAxixLayoutsIndex.JoyLeft,
|
|
|
|
|
- ControllerType.JoyconRight => SixAxixLayoutsIndex.JoyRight,
|
|
|
|
|
- ControllerType.Pokeball => SixAxixLayoutsIndex.Pokeball,
|
|
|
|
|
- _ => SixAxixLayoutsIndex.SystemExternal
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
|
|
|
|
|
+
|
|
|
|
|
+ NpadCommonState newState = new NpadCommonState();
|
|
|
|
|
+
|
|
|
|
|
+ WriteNewInputEntry(ref currentNpad.FullKey, ref newState);
|
|
|
|
|
+ WriteNewInputEntry(ref currentNpad.Handheld, ref newState);
|
|
|
|
|
+ WriteNewInputEntry(ref currentNpad.JoyDual, ref newState);
|
|
|
|
|
+ WriteNewInputEntry(ref currentNpad.JoyLeft, ref newState);
|
|
|
|
|
+ WriteNewInputEntry(ref currentNpad.JoyRight, ref newState);
|
|
|
|
|
+ WriteNewInputEntry(ref currentNpad.Palma, ref newState);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
public void UpdateSixAxis(IList<SixAxisInput> states)
|
|
public void UpdateSixAxis(IList<SixAxisInput> states)
|
|
|
{
|
|
{
|
|
|
|
|
+ Span<bool> updated = stackalloc bool[10];
|
|
|
|
|
+
|
|
|
for (int i = 0; i < states.Count; ++i)
|
|
for (int i = 0; i < states.Count; ++i)
|
|
|
{
|
|
{
|
|
|
|
|
+ updated[(int)states[i].PlayerId] = true;
|
|
|
|
|
+
|
|
|
if (SetSixAxisState(states[i]))
|
|
if (SetSixAxisState(states[i]))
|
|
|
{
|
|
{
|
|
|
i++;
|
|
i++;
|
|
@@ -345,6 +467,40 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
SetSixAxisState(states[i], true);
|
|
SetSixAxisState(states[i], true);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = 0; i < updated.Length; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!updated[i])
|
|
|
|
|
+ {
|
|
|
|
|
+ UpdateDisconnectedInputSixAxis((PlayerIndex)i);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private ref RingLifo<SixAxisSensorState> GetSixAxisSensorLifo(ref NpadInternalState npad, bool isRightPair)
|
|
|
|
|
+ {
|
|
|
|
|
+ switch (npad.StyleSet)
|
|
|
|
|
+ {
|
|
|
|
|
+ case NpadStyleTag.FullKey:
|
|
|
|
|
+ return ref npad.FullKeySixAxisSensor;
|
|
|
|
|
+ case NpadStyleTag.Handheld:
|
|
|
|
|
+ return ref npad.HandheldSixAxisSensor;
|
|
|
|
|
+ case NpadStyleTag.JoyDual:
|
|
|
|
|
+ if (isRightPair)
|
|
|
|
|
+ {
|
|
|
|
|
+ return ref npad.JoyDualRightSixAxisSensor;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ return ref npad.JoyDualSixAxisSensor;
|
|
|
|
|
+ }
|
|
|
|
|
+ case NpadStyleTag.JoyLeft:
|
|
|
|
|
+ return ref npad.JoyLeftSixAxisSensor;
|
|
|
|
|
+ case NpadStyleTag.JoyRight:
|
|
|
|
|
+ return ref npad.JoyRightSixAxisSensor;
|
|
|
|
|
+ default:
|
|
|
|
|
+ throw new NotImplementedException($"{npad.StyleSet}");
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private bool SetSixAxisState(SixAxisInput state, bool isRightPair = false)
|
|
private bool SetSixAxisState(SixAxisInput state, bool isRightPair = false)
|
|
@@ -354,9 +510,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId];
|
|
|
|
|
|
|
+ ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState;
|
|
|
|
|
|
|
|
- if (currentNpad.Header.Type == ControllerType.None)
|
|
|
|
|
|
|
+ if (currentNpad.StyleSet == NpadStyleTag.None)
|
|
|
{
|
|
{
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
@@ -382,87 +538,57 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|
|
Z = state.Rotation.Z
|
|
Z = state.Rotation.Z
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- ref NpadSixAxis currentLayout = ref currentNpad.Sixaxis[(int)ControllerTypeToSixAxisLayout(currentNpad.Header.Type) + (isRightPair ? 1 : 0)];
|
|
|
|
|
- ref SixAxisState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
|
|
|
|
|
|
|
+ SixAxisSensorState newState = new SixAxisSensorState
|
|
|
|
|
+ {
|
|
|
|
|
+ Acceleration = accel,
|
|
|
|
|
+ AngularVelocity = gyro,
|
|
|
|
|
+ Angle = rotation,
|
|
|
|
|
+ Attributes = SixAxisSensorAttribute.IsConnected
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- int previousEntryIndex = (int)(currentLayout.Header.LatestEntry == 0 ?
|
|
|
|
|
- currentLayout.Header.MaxEntryIndex : currentLayout.Header.LatestEntry - 1);
|
|
|
|
|
|
|
+ state.Orientation.AsSpan().CopyTo(newState.Direction.ToSpan());
|
|
|
|
|
|
|
|
- ref SixAxisState previousEntry = ref currentLayout.Entries[previousEntryIndex];
|
|
|
|
|
|
|
+ ref RingLifo<SixAxisSensorState> lifo = ref GetSixAxisSensorLifo(ref currentNpad, isRightPair);
|
|
|
|
|
|
|
|
- currentEntry.Accelerometer = accel;
|
|
|
|
|
- currentEntry.Gyroscope = gyro;
|
|
|
|
|
- currentEntry.Rotations = rotation;
|
|
|
|
|
|
|
+ WriteNewSixInputEntry(ref lifo, ref newState);
|
|
|
|
|
|
|
|
- unsafe
|
|
|
|
|
|
|
+ bool needUpdateRight = currentNpad.StyleSet == NpadStyleTag.JoyDual && !isRightPair;
|
|
|
|
|
+
|
|
|
|
|
+ if (!isRightPair)
|
|
|
{
|
|
{
|
|
|
- for (int i = 0; i < 9; i++)
|
|
|
|
|
- {
|
|
|
|
|
- currentEntry.Orientation[i] = state.Orientation[i];
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.FullKeySixAxisSensor);
|
|
|
|
|
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.HandheldSixAxisSensor);
|
|
|
|
|
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyDualSixAxisSensor);
|
|
|
|
|
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyLeftSixAxisSensor);
|
|
|
|
|
+ UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyRightSixAxisSensor);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return currentNpad.Header.Type == ControllerType.JoyconPair && !isRightPair;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void UpdateAllEntries()
|
|
|
|
|
- {
|
|
|
|
|
- ref Array10<ShMemNpad> controllers = ref _device.Hid.SharedMemory.Npads;
|
|
|
|
|
- for (int i = 0; i < controllers.Length; ++i)
|
|
|
|
|
|
|
+ if (!needUpdateRight)
|
|
|
{
|
|
{
|
|
|
- ref Array7<NpadLayout> layouts = ref controllers[i].Layouts;
|
|
|
|
|
- for (int l = 0; l < layouts.Length; ++l)
|
|
|
|
|
- {
|
|
|
|
|
- ref NpadLayout currentLayout = ref layouts[l];
|
|
|
|
|
- int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex);
|
|
|
|
|
|
|
+ SixAxisSensorState emptyState = new SixAxisSensorState();
|
|
|
|
|
|
|
|
- ref NpadState currentEntry = ref currentLayout.Entries[currentIndex];
|
|
|
|
|
- NpadState previousEntry = currentLayout.Entries[previousIndex];
|
|
|
|
|
|
|
+ emptyState.Attributes = SixAxisSensorAttribute.IsConnected;
|
|
|
|
|
|
|
|
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
|
|
|
|
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
|
|
|
|
-
|
|
|
|
|
- if (controllers[i].Header.Type == ControllerType.None)
|
|
|
|
|
- {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- currentEntry.ConnectionState = NpadConnectionState.ControllerStateConnected;
|
|
|
|
|
|
|
+ WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref emptyState);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- switch (controllers[i].Header.Type)
|
|
|
|
|
- {
|
|
|
|
|
- case ControllerType.Handheld:
|
|
|
|
|
- case ControllerType.ProController:
|
|
|
|
|
- currentEntry.ConnectionState |= NpadConnectionState.ControllerStateWired;
|
|
|
|
|
- break;
|
|
|
|
|
- case ControllerType.JoyconPair:
|
|
|
|
|
- currentEntry.ConnectionState |= NpadConnectionState.JoyLeftConnected |
|
|
|
|
|
- NpadConnectionState.JoyRightConnected;
|
|
|
|
|
- break;
|
|
|
|
|
- case ControllerType.JoyconLeft:
|
|
|
|
|
- currentEntry.ConnectionState |= NpadConnectionState.JoyLeftConnected;
|
|
|
|
|
- break;
|
|
|
|
|
- case ControllerType.JoyconRight:
|
|
|
|
|
- currentEntry.ConnectionState |= NpadConnectionState.JoyRightConnected;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return needUpdateRight;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- ref Array6<NpadSixAxis> sixaxis = ref controllers[i].Sixaxis;
|
|
|
|
|
- for (int l = 0; l < sixaxis.Length; ++l)
|
|
|
|
|
- {
|
|
|
|
|
- ref NpadSixAxis currentLayout = ref sixaxis[l];
|
|
|
|
|
- int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex);
|
|
|
|
|
|
|
+ private void UpdateDisconnectedInputSixAxis(PlayerIndex index)
|
|
|
|
|
+ {
|
|
|
|
|
+ ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
|
|
|
|
|
|
|
|
- ref SixAxisState currentEntry = ref currentLayout.Entries[currentIndex];
|
|
|
|
|
- SixAxisState previousEntry = currentLayout.Entries[previousIndex];
|
|
|
|
|
|
|
+ SixAxisSensorState newState = new SixAxisSensorState();
|
|
|
|
|
|
|
|
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
|
|
|
|
- currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
|
|
|
|
|
|
+ newState.Attributes = SixAxisSensorAttribute.IsConnected;
|
|
|
|
|
|
|
|
- currentEntry._unknown2 = 1;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ WriteNewSixInputEntry(ref currentNpad.FullKeySixAxisSensor, ref newState);
|
|
|
|
|
+ WriteNewSixInputEntry(ref currentNpad.HandheldSixAxisSensor, ref newState);
|
|
|
|
|
+ WriteNewSixInputEntry(ref currentNpad.JoyDualSixAxisSensor, ref newState);
|
|
|
|
|
+ WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref newState);
|
|
|
|
|
+ WriteNewSixInputEntry(ref currentNpad.JoyLeftSixAxisSensor, ref newState);
|
|
|
|
|
+ WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|