mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-08-02 15:37:11 +02:00
hid: Rewrite shared memory management (#2257)
* hid: Rewrite shared memory management This entirely rewrite our ancient (and original) HID shared memory interface to be more usable and accurate. HID update logics were updated to reflect those changes but should work still the same way it previously did. This need heavy testing just in case to avoid possible regressions. * Silence warnings * Address gdkchan's comments * Address Ac_K's comments * Address one missing nit
This commit is contained in:
parent
d278aaa5cd
commit
f404c39b7e
77 changed files with 1395 additions and 624 deletions
|
@ -12,18 +12,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
_device = device;
|
||||
Active = active;
|
||||
}
|
||||
|
||||
internal static int UpdateEntriesHeader(ref CommonEntriesHeader header, out int previousEntry)
|
||||
{
|
||||
header.NumEntries = SharedMemEntryCount;
|
||||
header.MaxEntryIndex = SharedMemEntryCount - 1;
|
||||
|
||||
previousEntry = (int)header.LatestEntry;
|
||||
header.LatestEntry = (header.LatestEntry + 1) % SharedMemEntryCount;
|
||||
|
||||
header.TimestampTicks = GetTimestampTicks();
|
||||
|
||||
return (int)header.LatestEntry; // EntryCount shouldn't overflow int
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public class DebugPadDevice : BaseDevice
|
||||
|
@ -6,20 +9,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
|
||||
public void Update()
|
||||
{
|
||||
ref ShMemDebugPad debugPad = ref _device.Hid.SharedMemory.DebugPad;
|
||||
ref RingLifo<DebugPadState> lifo = ref _device.Hid.SharedMemory.DebugPad;
|
||||
|
||||
int currentIndex = UpdateEntriesHeader(ref debugPad.Header, out int previousIndex);
|
||||
ref DebugPadState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
if (!Active)
|
||||
DebugPadState newState = new DebugPadState();
|
||||
|
||||
if (Active)
|
||||
{
|
||||
return;
|
||||
// TODO: This is a debug device only present in dev environment, do we want to support it?
|
||||
}
|
||||
|
||||
ref DebugPadEntry currentEntry = ref debugPad.Entries[currentIndex];
|
||||
DebugPadEntry previousEntry = debugPad.Entries[previousIndex];
|
||||
newState.SamplingNumber = previousEntry.SamplingNumber + 1;
|
||||
|
||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
||||
lifo.Write(ref newState);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public class KeyboardDevice : BaseDevice
|
||||
|
@ -6,27 +10,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
|
||||
public unsafe void Update(KeyboardInput keyState)
|
||||
{
|
||||
ref ShMemKeyboard keyboard = ref _device.Hid.SharedMemory.Keyboard;
|
||||
|
||||
int currentIndex = UpdateEntriesHeader(ref keyboard.Header, out int previousIndex);
|
||||
ref RingLifo<KeyboardState> lifo = ref _device.Hid.SharedMemory.Keyboard;
|
||||
|
||||
if (!Active)
|
||||
{
|
||||
lifo.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ref KeyboardState currentEntry = ref keyboard.Entries[currentIndex];
|
||||
KeyboardState previousEntry = keyboard.Entries[previousIndex];
|
||||
ref KeyboardState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
KeyboardState newState = new KeyboardState
|
||||
{
|
||||
currentEntry.Keys[i] = (uint)keyState.Keys[i];
|
||||
}
|
||||
SamplingNumber = previousEntry.SamplingNumber + 1,
|
||||
};
|
||||
|
||||
currentEntry.Modifier = (ulong)keyState.Modifier;
|
||||
keyState.Keys.AsSpan().CopyTo(newState.Keys.RawData.ToSpan());
|
||||
newState.Modifiers = (KeyboardModifier)keyState.Modifier;
|
||||
|
||||
lifo.Write(ref newState);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,37 +1,35 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public class MouseDevice : BaseDevice
|
||||
{
|
||||
public MouseDevice(Switch device, bool active) : base(device, active) { }
|
||||
|
||||
public void Update(int mouseX, int mouseY, int buttons = 0, int scrollX = 0, int scrollY = 0)
|
||||
public void Update(int mouseX, int mouseY, uint buttons = 0, int scrollX = 0, int scrollY = 0)
|
||||
{
|
||||
ref ShMemMouse mouse = ref _device.Hid.SharedMemory.Mouse;
|
||||
ref RingLifo<MouseState> lifo = ref _device.Hid.SharedMemory.Mouse;
|
||||
|
||||
int currentIndex = UpdateEntriesHeader(ref mouse.Header, out int previousIndex);
|
||||
|
||||
if (!Active)
|
||||
ref MouseState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
MouseState newState = new MouseState()
|
||||
{
|
||||
return;
|
||||
SamplingNumber = previousEntry.SamplingNumber + 1,
|
||||
};
|
||||
|
||||
if (Active)
|
||||
{
|
||||
newState.Buttons = (MouseButton)buttons;
|
||||
newState.X = mouseX;
|
||||
newState.Y = mouseY;
|
||||
newState.DeltaX = mouseX - previousEntry.DeltaX;
|
||||
newState.DeltaY = mouseY - previousEntry.DeltaY;
|
||||
newState.WheelDeltaX = scrollX;
|
||||
newState.WheelDeltaY = scrollY;
|
||||
}
|
||||
|
||||
ref MouseState currentEntry = ref mouse.Entries[currentIndex];
|
||||
MouseState previousEntry = mouse.Entries[previousIndex];
|
||||
|
||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
||||
|
||||
currentEntry.Buttons = (ulong)buttons;
|
||||
|
||||
currentEntry.Position = new MousePosition
|
||||
{
|
||||
X = mouseX,
|
||||
Y = mouseY,
|
||||
VelocityX = mouseX - previousEntry.Position.X,
|
||||
VelocityY = mouseY - previousEntry.Position.Y,
|
||||
ScrollVelocityX = scrollX,
|
||||
ScrollVelocityY = scrollY
|
||||
};
|
||||
lifo.Write(ref newState);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
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
|
||||
{
|
||||
public class NpadDevices : BaseDevice
|
||||
{
|
||||
private const BatteryCharge DefaultBatteryCharge = BatteryCharge.Percent100;
|
||||
|
||||
private const int NoMatchNotifyFrequencyMs = 2000;
|
||||
private int _activeCount;
|
||||
private long _lastNotifyTimestamp;
|
||||
|
@ -86,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
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])
|
||||
{
|
||||
|
@ -135,12 +136,24 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
{
|
||||
Remap();
|
||||
|
||||
UpdateAllEntries();
|
||||
Span<bool> updated = stackalloc bool[10];
|
||||
|
||||
// Update configured inputs
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return; // Already configured
|
||||
}
|
||||
|
||||
controller = new ShMemNpad(); // Zero it
|
||||
controller = NpadInternalState.Create(); // Reset it
|
||||
|
||||
if (type == ControllerType.None)
|
||||
{
|
||||
|
@ -207,87 +220,151 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
}
|
||||
|
||||
// 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.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.PowerInfo0Connected |
|
||||
NpadSystemProperties.PowerInfo1Connected |
|
||||
NpadSystemProperties.PowerInfo2Connected;
|
||||
controller.SystemProperties = NpadSystemProperties.IsPoweredJoyDual |
|
||||
NpadSystemProperties.IsPoweredJoyLeft |
|
||||
NpadSystemProperties.IsPoweredJoyRight;
|
||||
|
||||
controller.BatteryState.ToSpan().Fill(DefaultBatteryCharge);
|
||||
controller.BatteryLevelJoyDual = NpadBatteryLevel.Percent100;
|
||||
controller.BatteryLevelJoyLeft = NpadBatteryLevel.Percent100;
|
||||
controller.BatteryLevelJoyRight = NpadBatteryLevel.Percent100;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ControllerType.ProController:
|
||||
defaultHeader.Type = ControllerType.ProController;
|
||||
controller.StyleSet = NpadStyleTag.FullKey;
|
||||
controller.DeviceType = DeviceType.FullKey;
|
||||
controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
|
||||
NpadSystemProperties.PlusButtonCapability |
|
||||
NpadSystemProperties.MinusButtonCapability;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||
NpadSystemProperties.IsPlusAvailable |
|
||||
NpadSystemProperties.IsMinusAvailable;
|
||||
break;
|
||||
case ControllerType.Handheld:
|
||||
defaultHeader.Type = ControllerType.Handheld;
|
||||
controller.StyleSet = NpadStyleTag.Handheld;
|
||||
controller.DeviceType = DeviceType.HandheldLeft |
|
||||
DeviceType.HandheldRight;
|
||||
controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
|
||||
NpadSystemProperties.PlusButtonCapability |
|
||||
NpadSystemProperties.MinusButtonCapability;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||
NpadSystemProperties.IsPlusAvailable |
|
||||
NpadSystemProperties.IsMinusAvailable;
|
||||
break;
|
||||
case ControllerType.JoyconPair:
|
||||
defaultHeader.Type = ControllerType.JoyconPair;
|
||||
controller.StyleSet = NpadStyleTag.JoyDual;
|
||||
controller.DeviceType = DeviceType.JoyLeft |
|
||||
DeviceType.JoyRight;
|
||||
controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
|
||||
NpadSystemProperties.PlusButtonCapability |
|
||||
NpadSystemProperties.MinusButtonCapability;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||
NpadSystemProperties.IsPlusAvailable |
|
||||
NpadSystemProperties.IsMinusAvailable;
|
||||
break;
|
||||
case ControllerType.JoyconLeft:
|
||||
defaultHeader.Type = ControllerType.JoyconLeft;
|
||||
defaultHeader.IsHalf = true;
|
||||
controller.StyleSet = NpadStyleTag.JoyLeft;
|
||||
controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||
controller.DeviceType = DeviceType.JoyLeft;
|
||||
controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
|
||||
NpadSystemProperties.MinusButtonCapability;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
|
||||
NpadSystemProperties.IsMinusAvailable;
|
||||
break;
|
||||
case ControllerType.JoyconRight:
|
||||
defaultHeader.Type = ControllerType.JoyconRight;
|
||||
defaultHeader.IsHalf = true;
|
||||
controller.StyleSet = NpadStyleTag.JoyRight;
|
||||
controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||
controller.DeviceType = DeviceType.JoyRight;
|
||||
controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
|
||||
NpadSystemProperties.PlusButtonCapability;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
|
||||
NpadSystemProperties.IsPlusAvailable;
|
||||
break;
|
||||
case ControllerType.Pokeball:
|
||||
defaultHeader.Type = ControllerType.Pokeball;
|
||||
controller.StyleSet = NpadStyleTag.Palma;
|
||||
controller.DeviceType = DeviceType.Palma;
|
||||
break;
|
||||
}
|
||||
|
||||
controller.Header = defaultHeader;
|
||||
|
||||
_styleSetUpdateEvents[(int)player].ReadableEvent.Signal();
|
||||
_activeCount++;
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
};
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -296,43 +373,88 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
currentEntry.Buttons = state.Buttons;
|
||||
currentEntry.LStickX = state.LStick.Dx;
|
||||
currentEntry.LStickY = state.LStick.Dy;
|
||||
currentEntry.RStickX = state.RStick.Dx;
|
||||
currentEntry.RStickY = state.RStick.Dy;
|
||||
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;
|
||||
}
|
||||
|
||||
WriteNewInputEntry(ref lifo, ref newState);
|
||||
|
||||
// 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)
|
||||
{
|
||||
Span<bool> updated = stackalloc bool[10];
|
||||
|
||||
for (int i = 0; i < states.Count; ++i)
|
||||
{
|
||||
updated[(int)states[i].PlayerId] = true;
|
||||
|
||||
if (SetSixAxisState(states[i]))
|
||||
{
|
||||
i++;
|
||||
|
@ -345,6 +467,40 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
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)
|
||||
|
@ -354,9 +510,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
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;
|
||||
}
|
||||
|
@ -382,87 +538,57 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
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];
|
||||
|
||||
int previousEntryIndex = (int)(currentLayout.Header.LatestEntry == 0 ?
|
||||
currentLayout.Header.MaxEntryIndex : currentLayout.Header.LatestEntry - 1);
|
||||
|
||||
ref SixAxisState previousEntry = ref currentLayout.Entries[previousEntryIndex];
|
||||
|
||||
currentEntry.Accelerometer = accel;
|
||||
currentEntry.Gyroscope = gyro;
|
||||
currentEntry.Rotations = rotation;
|
||||
|
||||
unsafe
|
||||
SixAxisSensorState newState = new SixAxisSensorState
|
||||
{
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
currentEntry.Orientation[i] = state.Orientation[i];
|
||||
}
|
||||
Acceleration = accel,
|
||||
AngularVelocity = gyro,
|
||||
Angle = rotation,
|
||||
Attributes = SixAxisSensorAttribute.IsConnected
|
||||
};
|
||||
|
||||
state.Orientation.AsSpan().CopyTo(newState.Direction.ToSpan());
|
||||
|
||||
ref RingLifo<SixAxisSensorState> lifo = ref GetSixAxisSensorLifo(ref currentNpad, isRightPair);
|
||||
|
||||
WriteNewSixInputEntry(ref lifo, ref newState);
|
||||
|
||||
bool needUpdateRight = currentNpad.StyleSet == NpadStyleTag.JoyDual && !isRightPair;
|
||||
|
||||
if (!isRightPair)
|
||||
{
|
||||
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;
|
||||
if (!needUpdateRight)
|
||||
{
|
||||
SixAxisSensorState emptyState = new SixAxisSensorState();
|
||||
|
||||
emptyState.Attributes = SixAxisSensorAttribute.IsConnected;
|
||||
|
||||
WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref emptyState);
|
||||
}
|
||||
|
||||
return needUpdateRight;
|
||||
}
|
||||
|
||||
private void UpdateAllEntries()
|
||||
private void UpdateDisconnectedInputSixAxis(PlayerIndex index)
|
||||
{
|
||||
ref Array10<ShMemNpad> controllers = ref _device.Hid.SharedMemory.Npads;
|
||||
for (int i = 0; i < controllers.Length; ++i)
|
||||
{
|
||||
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);
|
||||
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
|
||||
|
||||
ref NpadState currentEntry = ref currentLayout.Entries[currentIndex];
|
||||
NpadState previousEntry = currentLayout.Entries[previousIndex];
|
||||
SixAxisSensorState newState = new SixAxisSensorState();
|
||||
|
||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
||||
newState.Attributes = SixAxisSensorAttribute.IsConnected;
|
||||
|
||||
if (controllers[i].Header.Type == ControllerType.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
currentEntry.ConnectionState = NpadConnectionState.ControllerStateConnected;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ref SixAxisState currentEntry = ref currentLayout.Entries[currentIndex];
|
||||
SixAxisState previousEntry = currentLayout.Entries[previousIndex];
|
||||
|
||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
|
@ -8,39 +10,38 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
|
||||
public void Update(params TouchPoint[] points)
|
||||
{
|
||||
ref ShMemTouchScreen touchscreen = ref _device.Hid.SharedMemory.TouchScreen;
|
||||
ref RingLifo<TouchScreenState> lifo = ref _device.Hid.SharedMemory.TouchScreen;
|
||||
|
||||
int currentIndex = UpdateEntriesHeader(ref touchscreen.Header, out int previousIndex);
|
||||
ref TouchScreenState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
if (!Active)
|
||||
TouchScreenState newState = new TouchScreenState
|
||||
{
|
||||
return;
|
||||
}
|
||||
SamplingNumber = previousEntry.SamplingNumber + 1
|
||||
};
|
||||
|
||||
ref TouchScreenState currentEntry = ref touchscreen.Entries[currentIndex];
|
||||
TouchScreenState previousEntry = touchscreen.Entries[previousIndex];
|
||||
|
||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
||||
|
||||
currentEntry.NumTouches = (ulong)points.Length;
|
||||
|
||||
int pointsLength = Math.Min(points.Length, currentEntry.Touches.Length);
|
||||
|
||||
for (int i = 0; i < pointsLength; ++i)
|
||||
if (Active)
|
||||
{
|
||||
TouchPoint pi = points[i];
|
||||
currentEntry.Touches[i] = new TouchScreenStateData
|
||||
newState.TouchesCount = points.Length;
|
||||
|
||||
int pointsLength = Math.Min(points.Length, newState.Touches.Length);
|
||||
|
||||
for (int i = 0; i < pointsLength; ++i)
|
||||
{
|
||||
SampleTimestamp = currentEntry.SampleTimestamp,
|
||||
X = pi.X,
|
||||
Y = pi.Y,
|
||||
TouchIndex = (uint)i,
|
||||
DiameterX = pi.DiameterX,
|
||||
DiameterY = pi.DiameterY,
|
||||
Angle = pi.Angle
|
||||
};
|
||||
TouchPoint pi = points[i];
|
||||
newState.Touches[i] = new TouchState
|
||||
{
|
||||
DeltaTime = newState.SamplingNumber,
|
||||
X = pi.X,
|
||||
Y = pi.Y,
|
||||
FingerId = (uint)i,
|
||||
DiameterX = pi.DiameterX,
|
||||
DiameterY = pi.DiameterY,
|
||||
RotationAngle = pi.Angle
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
lifo.Write(ref newState);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
public struct KeyboardInput
|
||||
{
|
||||
public int Modifier;
|
||||
public int[] Keys;
|
||||
public ulong[] Keys;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue