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:
Mary 2021-05-02 22:01:30 +02:00 committed by GitHub
parent d278aaa5cd
commit f404c39b7e
77 changed files with 1395 additions and 624 deletions

View file

@ -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
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public struct KeyboardInput
{
public int Modifier;
public int[] Keys;
public ulong[] Keys;
}
}