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
0e9823d7e6
commit
3443023a08
77 changed files with 1395 additions and 624 deletions
|
@ -4,6 +4,7 @@ using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.Services.Hid.HidServer.HidUtils;
|
using static Ryujinx.HLE.HOS.Services.Hid.HidServer.HidUtils;
|
||||||
|
|
|
@ -3,6 +3,14 @@ using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
|
@ -12,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
|
|
||||||
private readonly ulong _hidMemoryAddress;
|
private readonly ulong _hidMemoryAddress;
|
||||||
|
|
||||||
internal ref HidSharedMemory SharedMemory => ref _device.Memory.GetRef<HidSharedMemory>(_hidMemoryAddress);
|
internal ref SharedMemory SharedMemory => ref _device.Memory.GetRef<SharedMemory>(_hidMemoryAddress);
|
||||||
|
|
||||||
internal const int SharedMemEntryCount = 17;
|
internal const int SharedMemEntryCount = 17;
|
||||||
|
|
||||||
|
@ -22,32 +30,22 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
public KeyboardDevice Keyboard;
|
public KeyboardDevice Keyboard;
|
||||||
public NpadDevices Npads;
|
public NpadDevices Npads;
|
||||||
|
|
||||||
|
private static void CheckTypeSizeOrThrow<T>(int expectedSize)
|
||||||
|
{
|
||||||
|
if (Unsafe.SizeOf<T>() != expectedSize)
|
||||||
|
{
|
||||||
|
throw new InvalidStructLayoutException<T>(expectedSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Hid()
|
static Hid()
|
||||||
{
|
{
|
||||||
if (Unsafe.SizeOf<ShMemDebugPad>() != 0x400)
|
CheckTypeSizeOrThrow<RingLifo<DebugPadState>>(0x2c8);
|
||||||
{
|
CheckTypeSizeOrThrow<RingLifo<TouchScreenState>>(0x2C38);
|
||||||
throw new InvalidStructLayoutException<ShMemDebugPad>(0x400);
|
CheckTypeSizeOrThrow<RingLifo<MouseState>>(0x350);
|
||||||
}
|
CheckTypeSizeOrThrow<RingLifo<KeyboardState>>(0x3D8);
|
||||||
if (Unsafe.SizeOf<ShMemTouchScreen>() != 0x3000)
|
CheckTypeSizeOrThrow<Array10<NpadState>>(0x32000);
|
||||||
{
|
CheckTypeSizeOrThrow<SharedMemory>(Horizon.HidSize);
|
||||||
throw new InvalidStructLayoutException<ShMemTouchScreen>(0x3000);
|
|
||||||
}
|
|
||||||
if (Unsafe.SizeOf<ShMemKeyboard>() != 0x400)
|
|
||||||
{
|
|
||||||
throw new InvalidStructLayoutException<ShMemKeyboard>(0x400);
|
|
||||||
}
|
|
||||||
if (Unsafe.SizeOf<ShMemMouse>() != 0x400)
|
|
||||||
{
|
|
||||||
throw new InvalidStructLayoutException<ShMemMouse>(0x400);
|
|
||||||
}
|
|
||||||
if (Unsafe.SizeOf<ShMemNpad>() != 0x5000)
|
|
||||||
{
|
|
||||||
throw new InvalidStructLayoutException<ShMemNpad>(0x5000);
|
|
||||||
}
|
|
||||||
if (Unsafe.SizeOf<HidSharedMemory>() != Horizon.HidSize)
|
|
||||||
{
|
|
||||||
throw new InvalidStructLayoutException<HidSharedMemory>(Horizon.HidSize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Hid(in Switch device, ulong sharedHidMemoryAddress)
|
public Hid(in Switch device, ulong sharedHidMemoryAddress)
|
||||||
|
@ -55,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
_device = device;
|
_device = device;
|
||||||
_hidMemoryAddress = sharedHidMemoryAddress;
|
_hidMemoryAddress = sharedHidMemoryAddress;
|
||||||
|
|
||||||
device.Memory.ZeroFill(sharedHidMemoryAddress, Horizon.HidSize);
|
SharedMemory = SharedMemory.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitDevices()
|
public void InitDevices()
|
||||||
|
|
|
@ -12,18 +12,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
_device = device;
|
_device = device;
|
||||||
Active = active;
|
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
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public class DebugPadDevice : BaseDevice
|
public class DebugPadDevice : BaseDevice
|
||||||
|
@ -6,20 +9,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
|
|
||||||
public void Update()
|
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];
|
newState.SamplingNumber = previousEntry.SamplingNumber + 1;
|
||||||
DebugPadEntry previousEntry = debugPad.Entries[previousIndex];
|
|
||||||
|
|
||||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
lifo.Write(ref newState);
|
||||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public class KeyboardDevice : BaseDevice
|
public class KeyboardDevice : BaseDevice
|
||||||
|
@ -6,27 +10,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
|
|
||||||
public unsafe void Update(KeyboardInput keyState)
|
public unsafe void Update(KeyboardInput keyState)
|
||||||
{
|
{
|
||||||
ref ShMemKeyboard keyboard = ref _device.Hid.SharedMemory.Keyboard;
|
ref RingLifo<KeyboardState> lifo = ref _device.Hid.SharedMemory.Keyboard;
|
||||||
|
|
||||||
int currentIndex = UpdateEntriesHeader(ref keyboard.Header, out int previousIndex);
|
|
||||||
|
|
||||||
if (!Active)
|
if (!Active)
|
||||||
{
|
{
|
||||||
|
lifo.Clear();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref KeyboardState currentEntry = ref keyboard.Entries[currentIndex];
|
ref KeyboardState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||||
KeyboardState previousEntry = keyboard.Entries[previousIndex];
|
|
||||||
|
|
||||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
KeyboardState newState = new KeyboardState
|
||||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
{
|
{
|
||||||
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
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public class MouseDevice : BaseDevice
|
public class MouseDevice : BaseDevice
|
||||||
{
|
{
|
||||||
public MouseDevice(Switch device, bool active) : base(device, active) { }
|
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);
|
ref MouseState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||||
|
|
||||||
if (!Active)
|
MouseState newState = new MouseState()
|
||||||
{
|
{
|
||||||
return;
|
SamplingNumber = previousEntry.SamplingNumber + 1,
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
lifo.Write(ref newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
controller.JoyAssignmentMode = NpadJoyAssignmentMode.Dual;
|
||||||
{
|
controller.FullKeyColor.FullKeyBody = (uint)NpadColor.BodyGray;
|
||||||
IsHalf = false,
|
controller.FullKeyColor.FullKeyButtons = (uint)NpadColor.ButtonGray;
|
||||||
SingleColorBody = NpadColor.BodyGray,
|
controller.JoyColor.LeftBody = (uint)NpadColor.BodyNeonBlue;
|
||||||
SingleColorButtons = NpadColor.ButtonGray,
|
controller.JoyColor.LeftButtons = (uint)NpadColor.ButtonGray;
|
||||||
LeftColorBody = NpadColor.BodyNeonBlue,
|
controller.JoyColor.RightBody = (uint)NpadColor.BodyNeonRed;
|
||||||
LeftColorButtons = NpadColor.ButtonGray,
|
controller.JoyColor.RightButtons = (uint)NpadColor.ButtonGray;
|
||||||
RightColorBody = NpadColor.BodyNeonRed,
|
|
||||||
RightColorButtons = NpadColor.ButtonGray
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.SystemProperties = NpadSystemProperties.PowerInfo0Connected |
|
controller.SystemProperties = NpadSystemProperties.IsPoweredJoyDual |
|
||||||
NpadSystemProperties.PowerInfo1Connected |
|
NpadSystemProperties.IsPoweredJoyLeft |
|
||||||
NpadSystemProperties.PowerInfo2Connected;
|
NpadSystemProperties.IsPoweredJoyRight;
|
||||||
|
|
||||||
controller.BatteryState.ToSpan().Fill(DefaultBatteryCharge);
|
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 |
|
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||||
NpadSystemProperties.PlusButtonCapability |
|
NpadSystemProperties.IsPlusAvailable |
|
||||||
NpadSystemProperties.MinusButtonCapability;
|
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 |
|
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||||
NpadSystemProperties.PlusButtonCapability |
|
NpadSystemProperties.IsPlusAvailable |
|
||||||
NpadSystemProperties.MinusButtonCapability;
|
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 |
|
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||||
NpadSystemProperties.PlusButtonCapability |
|
NpadSystemProperties.IsPlusAvailable |
|
||||||
NpadSystemProperties.MinusButtonCapability;
|
NpadSystemProperties.IsMinusAvailable;
|
||||||
break;
|
break;
|
||||||
case ControllerType.JoyconLeft:
|
case ControllerType.JoyconLeft:
|
||||||
defaultHeader.Type = ControllerType.JoyconLeft;
|
controller.StyleSet = NpadStyleTag.JoyLeft;
|
||||||
defaultHeader.IsHalf = true;
|
controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||||
controller.DeviceType = DeviceType.JoyLeft;
|
controller.DeviceType = DeviceType.JoyLeft;
|
||||||
controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
|
controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
|
||||||
NpadSystemProperties.MinusButtonCapability;
|
NpadSystemProperties.IsMinusAvailable;
|
||||||
break;
|
break;
|
||||||
case ControllerType.JoyconRight:
|
case ControllerType.JoyconRight:
|
||||||
defaultHeader.Type = ControllerType.JoyconRight;
|
controller.StyleSet = NpadStyleTag.JoyRight;
|
||||||
defaultHeader.IsHalf = true;
|
controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||||
controller.DeviceType = DeviceType.JoyRight;
|
controller.DeviceType = DeviceType.JoyRight;
|
||||||
controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
|
controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
|
||||||
NpadSystemProperties.PlusButtonCapability;
|
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)
|
private ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad)
|
||||||
=> controllerType switch
|
|
||||||
{
|
{
|
||||||
ControllerType.ProController => NpadLayoutsIndex.ProController,
|
switch (npad.StyleSet)
|
||||||
ControllerType.Handheld => NpadLayoutsIndex.Handheld,
|
{
|
||||||
ControllerType.JoyconPair => NpadLayoutsIndex.JoyDual,
|
case NpadStyleTag.FullKey:
|
||||||
ControllerType.JoyconLeft => NpadLayoutsIndex.JoyLeft,
|
return ref npad.FullKey;
|
||||||
ControllerType.JoyconRight => NpadLayoutsIndex.JoyRight,
|
case NpadStyleTag.Handheld:
|
||||||
ControllerType.Pokeball => NpadLayoutsIndex.Pokeball,
|
return ref npad.Handheld;
|
||||||
_ => NpadLayoutsIndex.SystemExternal
|
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)
|
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 RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
|
||||||
ref NpadState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
|
|
||||||
|
|
||||||
currentEntry.Buttons = state.Buttons;
|
NpadCommonState newState = new NpadCommonState
|
||||||
currentEntry.LStickX = state.LStick.Dx;
|
{
|
||||||
currentEntry.LStickY = state.LStick.Dy;
|
Buttons = (NpadButton)state.Buttons,
|
||||||
currentEntry.RStickX = state.RStick.Dx;
|
AnalogStickL = new AnalogStickState
|
||||||
currentEntry.RStickY = state.RStick.Dy;
|
{
|
||||||
|
X = state.LStick.Dx,
|
||||||
|
Y = state.LStick.Dy,
|
||||||
|
},
|
||||||
|
AnalogStickR = new AnalogStickState
|
||||||
|
{
|
||||||
|
X = state.RStick.Dx,
|
||||||
|
Y = state.RStick.Dy,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Mirror data to Default layout just in case
|
newState.Attributes = NpadAttribute.IsConnected;
|
||||||
ref NpadLayout mainLayout = ref currentNpad.Layouts[(int)NpadLayoutsIndex.SystemExternal];
|
|
||||||
mainLayout.Entries[(int)mainLayout.Header.LatestEntry] = currentEntry;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SixAxixLayoutsIndex ControllerTypeToSixAxisLayout(ControllerType controllerType)
|
WriteNewInputEntry(ref lifo, ref newState);
|
||||||
=> controllerType switch
|
|
||||||
|
// Mirror data to Default layout just in case
|
||||||
|
if (!currentNpad.StyleSet.HasFlag(NpadStyleTag.SystemExt))
|
||||||
{
|
{
|
||||||
ControllerType.ProController => SixAxixLayoutsIndex.ProController,
|
WriteNewInputEntry(ref currentNpad.SystemExt, ref newState);
|
||||||
ControllerType.Handheld => SixAxixLayoutsIndex.Handheld,
|
}
|
||||||
ControllerType.JoyconPair => SixAxixLayoutsIndex.JoyDualLeft,
|
|
||||||
ControllerType.JoyconLeft => SixAxixLayoutsIndex.JoyLeft,
|
UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.FullKey);
|
||||||
ControllerType.JoyconRight => SixAxixLayoutsIndex.JoyRight,
|
UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Handheld);
|
||||||
ControllerType.Pokeball => SixAxixLayoutsIndex.Pokeball,
|
UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyDual);
|
||||||
_ => SixAxixLayoutsIndex.SystemExternal
|
UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyLeft);
|
||||||
};
|
UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyRight);
|
||||||
|
UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Palma);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDisconnectedInput(PlayerIndex index)
|
||||||
|
{
|
||||||
|
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)];
|
SixAxisSensorState newState = new SixAxisSensorState
|
||||||
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
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 9; 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)
|
||||||
{
|
{
|
||||||
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;
|
if (!needUpdateRight)
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAllEntries()
|
|
||||||
{
|
{
|
||||||
ref Array10<ShMemNpad> controllers = ref _device.Hid.SharedMemory.Npads;
|
SixAxisSensorState emptyState = new SixAxisSensorState();
|
||||||
for (int i = 0; i < controllers.Length; ++i)
|
|
||||||
|
emptyState.Attributes = SixAxisSensorAttribute.IsConnected;
|
||||||
|
|
||||||
|
WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref emptyState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return needUpdateRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDisconnectedInputSixAxis(PlayerIndex index)
|
||||||
{
|
{
|
||||||
ref Array7<NpadLayout> layouts = ref controllers[i].Layouts;
|
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
|
||||||
for (int l = 0; l < layouts.Length; ++l)
|
|
||||||
{
|
|
||||||
ref NpadLayout currentLayout = ref layouts[l];
|
|
||||||
int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex);
|
|
||||||
|
|
||||||
ref NpadState currentEntry = ref currentLayout.Entries[currentIndex];
|
SixAxisSensorState newState = new SixAxisSensorState();
|
||||||
NpadState previousEntry = currentLayout.Entries[previousIndex];
|
|
||||||
|
|
||||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
newState.Attributes = SixAxisSensorAttribute.IsConnected;
|
||||||
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
|
|
||||||
|
|
||||||
if (controllers[i].Header.Type == ControllerType.None)
|
WriteNewSixInputEntry(ref currentNpad.FullKeySixAxisSensor, ref newState);
|
||||||
{
|
WriteNewSixInputEntry(ref currentNpad.HandheldSixAxisSensor, ref newState);
|
||||||
continue;
|
WriteNewSixInputEntry(ref currentNpad.JoyDualSixAxisSensor, ref newState);
|
||||||
}
|
WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref newState);
|
||||||
|
WriteNewSixInputEntry(ref currentNpad.JoyLeftSixAxisSensor, ref newState);
|
||||||
currentEntry.ConnectionState = NpadConnectionState.ControllerStateConnected;
|
WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
|
@ -8,39 +10,38 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
|
|
||||||
public void Update(params TouchPoint[] points)
|
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];
|
if (Active)
|
||||||
TouchScreenState previousEntry = touchscreen.Entries[previousIndex];
|
{
|
||||||
|
newState.TouchesCount = points.Length;
|
||||||
|
|
||||||
currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
|
int pointsLength = Math.Min(points.Length, newState.Touches.Length);
|
||||||
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)
|
for (int i = 0; i < pointsLength; ++i)
|
||||||
{
|
{
|
||||||
TouchPoint pi = points[i];
|
TouchPoint pi = points[i];
|
||||||
currentEntry.Touches[i] = new TouchScreenStateData
|
newState.Touches[i] = new TouchState
|
||||||
{
|
{
|
||||||
SampleTimestamp = currentEntry.SampleTimestamp,
|
DeltaTime = newState.SamplingNumber,
|
||||||
X = pi.X,
|
X = pi.X,
|
||||||
Y = pi.Y,
|
Y = pi.Y,
|
||||||
TouchIndex = (uint)i,
|
FingerId = (uint)i,
|
||||||
DiameterX = pi.DiameterX,
|
DiameterX = pi.DiameterX,
|
||||||
DiameterY = pi.DiameterY,
|
DiameterY = pi.DiameterY,
|
||||||
Angle = pi.Angle
|
RotationAngle = pi.Angle
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifo.Write(ref newState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
public struct KeyboardInput
|
public struct KeyboardInput
|
||||||
{
|
{
|
||||||
public int Modifier;
|
public int Modifier;
|
||||||
public int[] Keys;
|
public ulong[] Keys;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -134,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
// Initialize entries to avoid issues with some games.
|
// Initialize entries to avoid issues with some games.
|
||||||
|
|
||||||
KeyboardInput emptyInput = new KeyboardInput();
|
KeyboardInput emptyInput = new KeyboardInput();
|
||||||
emptyInput.Keys = new int[8];
|
emptyInput.Keys = new ulong[4];
|
||||||
|
|
||||||
for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
|
for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct Boolean32
|
|
||||||
{
|
|
||||||
private uint _value;
|
|
||||||
public static implicit operator bool(Boolean32 value) => (value._value & 1) != 0;
|
|
||||||
public static implicit operator Boolean32(bool value) => new Boolean32() { _value = value ? 1u : 0u };
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types
|
||||||
{
|
{
|
||||||
struct HidVector
|
struct HidVector
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
public enum NpadColor : int
|
public enum NpadColor : uint
|
||||||
{
|
{
|
||||||
BodyGray = 0x828282,
|
BodyGray = 0x828282,
|
||||||
BodyNeonRed = 0xFF3C28,
|
BodyNeonRed = 0xFF3C28,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types
|
||||||
{
|
{
|
||||||
enum NpadJoyHoldType
|
enum NpadJoyHoldType
|
||||||
{
|
{
|
|
@ -1,11 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct CommonEntriesHeader
|
|
||||||
{
|
|
||||||
public ulong TimestampTicks;
|
|
||||||
public ulong NumEntries;
|
|
||||||
public ulong LatestEntry;
|
|
||||||
public ulong MaxEntryIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
unsafe struct ShMemDebugPad
|
|
||||||
{
|
|
||||||
public CommonEntriesHeader Header;
|
|
||||||
public Array17<DebugPadEntry> Entries;
|
|
||||||
fixed byte _padding[0x138];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
unsafe struct DebugPadEntry
|
|
||||||
{
|
|
||||||
public ulong SampleTimestamp;
|
|
||||||
public ulong SampleTimestamp2;
|
|
||||||
fixed byte _unknown[0x18];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
// TODO: Add missing structs
|
|
||||||
unsafe struct HidSharedMemory
|
|
||||||
{
|
|
||||||
public ShMemDebugPad DebugPad;
|
|
||||||
public ShMemTouchScreen TouchScreen;
|
|
||||||
public ShMemMouse Mouse;
|
|
||||||
public ShMemKeyboard Keyboard;
|
|
||||||
public fixed byte BasicXpad[0x4 * 0x400];
|
|
||||||
public fixed byte HomeButton[0x200];
|
|
||||||
public fixed byte SleepButton[0x200];
|
|
||||||
public fixed byte CaptureButton[0x200];
|
|
||||||
public fixed byte InputDetector[0x10 * 0x80];
|
|
||||||
public fixed byte UniquePad[0x10 * 0x400];
|
|
||||||
public Array10<ShMemNpad> Npads;
|
|
||||||
public fixed byte Gesture[0x800];
|
|
||||||
public fixed byte ConsoleSixAxisSensor[0x20];
|
|
||||||
fixed byte _padding[0x3de0];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
unsafe struct ShMemKeyboard
|
|
||||||
{
|
|
||||||
public CommonEntriesHeader Header;
|
|
||||||
public Array17<KeyboardState> Entries;
|
|
||||||
fixed byte _padding[0x28];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
unsafe struct KeyboardState
|
|
||||||
{
|
|
||||||
public ulong SampleTimestamp;
|
|
||||||
public ulong SampleTimestamp2;
|
|
||||||
public ulong Modifier;
|
|
||||||
public fixed uint Keys[8];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
|
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
unsafe struct ShMemMouse
|
|
||||||
{
|
|
||||||
public CommonEntriesHeader Header;
|
|
||||||
public Array17<MouseState> Entries;
|
|
||||||
fixed byte _padding[0xB0];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct MousePosition
|
|
||||||
{
|
|
||||||
public int X;
|
|
||||||
public int Y;
|
|
||||||
public int VelocityX;
|
|
||||||
public int VelocityY;
|
|
||||||
public int ScrollVelocityX;
|
|
||||||
public int ScrollVelocityY;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct MouseState
|
|
||||||
{
|
|
||||||
public ulong SampleTimestamp;
|
|
||||||
public ulong SampleTimestamp2;
|
|
||||||
public MousePosition Position;
|
|
||||||
public ulong Buttons;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
enum BatteryCharge : int
|
|
||||||
{
|
|
||||||
Percent0 = 0,
|
|
||||||
Percent25 = 1,
|
|
||||||
Percent50 = 2,
|
|
||||||
Percent75 = 3,
|
|
||||||
Percent100 = 4
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
enum DeviceType : int
|
|
||||||
{
|
|
||||||
FullKey = 1 << 0,
|
|
||||||
DebugPad = 1 << 1,
|
|
||||||
HandheldLeft = 1 << 2,
|
|
||||||
HandheldRight = 1 << 3,
|
|
||||||
JoyLeft = 1 << 4,
|
|
||||||
JoyRight = 1 << 5,
|
|
||||||
Palma = 1 << 6, // Poké Ball Plus
|
|
||||||
FamicomLeft = 1 << 7,
|
|
||||||
FamicomRight = 1 << 8,
|
|
||||||
NESLeft = 1 << 9,
|
|
||||||
NESRight = 1 << 10,
|
|
||||||
HandheldFamicomLeft = 1 << 11,
|
|
||||||
HandheldFamicomRight = 1 << 12,
|
|
||||||
HandheldNESLeft = 1 << 13,
|
|
||||||
HandheldNESRight = 1 << 14,
|
|
||||||
Lucia = 1 << 15,
|
|
||||||
System = 1 << 31
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
// TODO: Add missing structs
|
|
||||||
unsafe struct ShMemNpad
|
|
||||||
{
|
|
||||||
public NpadStateHeader Header;
|
|
||||||
public Array7<NpadLayout> Layouts; // One for each NpadLayoutsIndex
|
|
||||||
public Array6<NpadSixAxis> Sixaxis;
|
|
||||||
public DeviceType DeviceType;
|
|
||||||
uint _padding1;
|
|
||||||
public NpadSystemProperties SystemProperties;
|
|
||||||
public uint NpadSystemButtonProperties;
|
|
||||||
public Array3<BatteryCharge> BatteryState;
|
|
||||||
public fixed byte NfcXcdDeviceHandleHeader[0x20];
|
|
||||||
public fixed byte NfcXcdDeviceHandleState[0x20 * 2];
|
|
||||||
public ulong Mutex;
|
|
||||||
public fixed byte NpadGcTriggerHeader[0x20];
|
|
||||||
public fixed byte NpadGcTriggerState[0x18 * 17];
|
|
||||||
fixed byte _padding2[0xC38];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
enum NpadColorDescription : int
|
|
||||||
{
|
|
||||||
ColorDescriptionColorsNonexistent = (1 << 1)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
enum NpadConnectionState : long
|
|
||||||
{
|
|
||||||
ControllerStateConnected = (1 << 0),
|
|
||||||
ControllerStateWired = (1 << 1),
|
|
||||||
JoyLeftConnected = (1 << 2),
|
|
||||||
JoyRightConnected = (1 << 4)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct NpadLayout
|
|
||||||
{
|
|
||||||
public CommonEntriesHeader Header;
|
|
||||||
public Array17<NpadState> Entries;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
enum NpadLayoutsIndex : int
|
|
||||||
{
|
|
||||||
ProController = 0,
|
|
||||||
Handheld = 1,
|
|
||||||
JoyDual = 2,
|
|
||||||
JoyLeft = 3,
|
|
||||||
JoyRight = 4,
|
|
||||||
Pokeball = 5,
|
|
||||||
SystemExternal = 6
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct NpadSixAxis
|
|
||||||
{
|
|
||||||
public CommonEntriesHeader Header;
|
|
||||||
public Array17<SixAxisState> Entries;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct NpadState
|
|
||||||
{
|
|
||||||
public ulong SampleTimestamp;
|
|
||||||
public ulong SampleTimestamp2;
|
|
||||||
public ControllerKeys Buttons;
|
|
||||||
public int LStickX;
|
|
||||||
public int LStickY;
|
|
||||||
public int RStickX;
|
|
||||||
public int RStickY;
|
|
||||||
public NpadConnectionState ConnectionState;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct NpadStateHeader
|
|
||||||
{
|
|
||||||
public ControllerType Type;
|
|
||||||
public Boolean32 IsHalf;
|
|
||||||
public NpadColorDescription SingleColorsDescriptor;
|
|
||||||
public NpadColor SingleColorBody;
|
|
||||||
public NpadColor SingleColorButtons;
|
|
||||||
public NpadColorDescription SplitColorsDescriptor;
|
|
||||||
public NpadColor LeftColorBody;
|
|
||||||
public NpadColor LeftColorButtons;
|
|
||||||
public NpadColor RightColorBody;
|
|
||||||
public NpadColor RightColorButtons;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
enum NpadSystemProperties : long
|
|
||||||
{
|
|
||||||
PowerInfo0Charging = 1 << 0,
|
|
||||||
PowerInfo1Charging = 1 << 1,
|
|
||||||
PowerInfo2Charging = 1 << 2,
|
|
||||||
PowerInfo0Connected = 1 << 3,
|
|
||||||
PowerInfo1Connected = 1 << 4,
|
|
||||||
PowerInfo2Connected = 1 << 5,
|
|
||||||
UnsupportedButtonPressedNpadSystem = 1 << 9,
|
|
||||||
UnsupportedButtonPressedNpadSystemExt = 1 << 10,
|
|
||||||
AbxyButtonOriented = 1 << 11,
|
|
||||||
SlSrButtonOriented = 1 << 12,
|
|
||||||
PlusButtonCapability = 1 << 13,
|
|
||||||
MinusButtonCapability = 1 << 14,
|
|
||||||
DirectionalButtonsSupported = 1 << 15
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
enum SixAxixLayoutsIndex : int
|
|
||||||
{
|
|
||||||
ProController = 0,
|
|
||||||
Handheld = 1,
|
|
||||||
JoyDualLeft = 2,
|
|
||||||
JoyDualRight = 3,
|
|
||||||
JoyLeft = 4,
|
|
||||||
JoyRight = 5,
|
|
||||||
Pokeball = 6,
|
|
||||||
SystemExternal = 7
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
unsafe struct SixAxisState
|
|
||||||
{
|
|
||||||
public ulong SampleTimestamp;
|
|
||||||
ulong _unknown1;
|
|
||||||
public ulong SampleTimestamp2;
|
|
||||||
public HidVector Accelerometer;
|
|
||||||
public HidVector Gyroscope;
|
|
||||||
public HidVector Rotations;
|
|
||||||
public fixed float Orientation[9];
|
|
||||||
public ulong _unknown2;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
unsafe struct ShMemTouchScreen
|
|
||||||
{
|
|
||||||
public CommonEntriesHeader Header;
|
|
||||||
public Array17<TouchScreenState> Entries;
|
|
||||||
fixed byte _padding[0x3c8];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
using Ryujinx.Common.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct TouchScreenState
|
|
||||||
{
|
|
||||||
public ulong SampleTimestamp;
|
|
||||||
public ulong SampleTimestamp2;
|
|
||||||
public ulong NumTouches;
|
|
||||||
public Array16<TouchScreenStateData> Touches;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
|
||||||
{
|
|
||||||
struct TouchScreenStateData
|
|
||||||
{
|
|
||||||
public ulong SampleTimestamp;
|
|
||||||
#pragma warning disable CS0169
|
|
||||||
uint _padding;
|
|
||||||
#pragma warning restore CS0169
|
|
||||||
public uint TouchIndex;
|
|
||||||
public uint X;
|
|
||||||
public uint Y;
|
|
||||||
public uint DiameterX;
|
|
||||||
public uint DiameterY;
|
|
||||||
public uint Angle;
|
|
||||||
#pragma warning disable CS0169
|
|
||||||
uint _padding2;
|
|
||||||
#pragma warning restore CS0169
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||||
|
{
|
||||||
|
struct AnalogStickState
|
||||||
|
{
|
||||||
|
public int X;
|
||||||
|
public int Y;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||||
|
{
|
||||||
|
struct AtomicStorage<T> where T: unmanaged
|
||||||
|
{
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public T Object;
|
||||||
|
|
||||||
|
public ulong ReadSamplingNumberAtomic()
|
||||||
|
{
|
||||||
|
return Interlocked.Read(ref SamplingNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetObject(ref T obj)
|
||||||
|
{
|
||||||
|
ISampledData samplingProvider = obj as ISampledData;
|
||||||
|
|
||||||
|
Interlocked.Exchange(ref SamplingNumber, samplingProvider.SamplingNumber);
|
||||||
|
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
Object = obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||||
|
{
|
||||||
|
interface ISampledData
|
||||||
|
{
|
||||||
|
ulong SamplingNumber { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||||
|
{
|
||||||
|
struct RingLifo<T> where T: unmanaged
|
||||||
|
{
|
||||||
|
private const ulong MaxEntries = 17;
|
||||||
|
|
||||||
|
#pragma warning disable CS0169
|
||||||
|
private ulong _unused;
|
||||||
|
#pragma warning restore CS0169
|
||||||
|
#pragma warning disable CS0414
|
||||||
|
private ulong _bufferCount;
|
||||||
|
#pragma warning restore CS0414
|
||||||
|
private ulong _index;
|
||||||
|
private ulong _count;
|
||||||
|
private Array17<AtomicStorage<T>> _storage;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private ulong ReadCurrentIndex()
|
||||||
|
{
|
||||||
|
return Interlocked.Read(ref _index);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private ulong ReadCurrentCount()
|
||||||
|
{
|
||||||
|
return Interlocked.Read(ref _count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static ulong GetNextIndexForWrite(ulong index)
|
||||||
|
{
|
||||||
|
return (index + 1) % MaxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref AtomicStorage<T> GetCurrentAtomicEntryRef()
|
||||||
|
{
|
||||||
|
ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), 1);
|
||||||
|
|
||||||
|
if (countAvailaible == 0)
|
||||||
|
{
|
||||||
|
_storage[0] = default;
|
||||||
|
|
||||||
|
return ref _storage[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong index = ReadCurrentIndex();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible) % MaxEntries);
|
||||||
|
|
||||||
|
ref AtomicStorage<T> result = ref _storage[inputEntryIndex];
|
||||||
|
|
||||||
|
ulong samplingNumber0 = result.ReadSamplingNumberAtomic();
|
||||||
|
ulong samplingNumber1 = result.ReadSamplingNumberAtomic();
|
||||||
|
|
||||||
|
if (samplingNumber0 != samplingNumber1 && (result.SamplingNumber - result.SamplingNumber) != 1)
|
||||||
|
{
|
||||||
|
ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible);
|
||||||
|
|
||||||
|
countAvailaible = Math.Min(tempCount, 1);
|
||||||
|
index = ReadCurrentIndex();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref T GetCurrentEntryRef()
|
||||||
|
{
|
||||||
|
return ref GetCurrentAtomicEntryRef().Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ReadOnlySpan<AtomicStorage<T>> ReadEntries(uint maxCount)
|
||||||
|
{
|
||||||
|
ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), maxCount);
|
||||||
|
|
||||||
|
if (countAvailaible == 0)
|
||||||
|
{
|
||||||
|
return ReadOnlySpan<AtomicStorage<T>>.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong index = ReadCurrentIndex();
|
||||||
|
|
||||||
|
AtomicStorage<T>[] result = new AtomicStorage<T>[countAvailaible];
|
||||||
|
|
||||||
|
for (ulong i = 0; i < countAvailaible; i++)
|
||||||
|
{
|
||||||
|
int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible + i) % MaxEntries);
|
||||||
|
int outputEntryIndex = (int)(countAvailaible - i - 1);
|
||||||
|
|
||||||
|
ulong samplingNumber0 = _storage[inputEntryIndex].ReadSamplingNumberAtomic();
|
||||||
|
result[outputEntryIndex] = _storage[inputEntryIndex];
|
||||||
|
ulong samplingNumber1 = _storage[inputEntryIndex].ReadSamplingNumberAtomic();
|
||||||
|
|
||||||
|
if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1))
|
||||||
|
{
|
||||||
|
ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible);
|
||||||
|
|
||||||
|
countAvailaible = Math.Min(tempCount, maxCount);
|
||||||
|
index = ReadCurrentIndex();
|
||||||
|
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Write(ref T value)
|
||||||
|
{
|
||||||
|
ulong targetIndex = GetNextIndexForWrite(ReadCurrentIndex());
|
||||||
|
|
||||||
|
_storage[(int)targetIndex].SetObject(ref value);
|
||||||
|
|
||||||
|
Interlocked.Exchange(ref _index, targetIndex);
|
||||||
|
|
||||||
|
ulong count = ReadCurrentCount();
|
||||||
|
|
||||||
|
if (count < (MaxEntries - 1))
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref _count, 0);
|
||||||
|
Interlocked.Exchange(ref _index, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RingLifo<T> Create()
|
||||||
|
{
|
||||||
|
return new RingLifo<T>
|
||||||
|
{
|
||||||
|
_bufferCount = MaxEntries
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum DebugPadAttribute : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Connected = 1 << 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum DebugPadButton : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
A = 1 << 0,
|
||||||
|
B = 1 << 1,
|
||||||
|
X = 1 << 2,
|
||||||
|
Y = 1 << 3,
|
||||||
|
L = 1 << 4,
|
||||||
|
R = 1 << 5,
|
||||||
|
ZL = 1 << 6,
|
||||||
|
ZR = 1 << 7,
|
||||||
|
Start = 1 << 8,
|
||||||
|
Select = 1 << 9,
|
||||||
|
Left = 1 << 10,
|
||||||
|
Up = 1 << 11,
|
||||||
|
Right = 1 << 12,
|
||||||
|
Down = 1 << 13
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad
|
||||||
|
{
|
||||||
|
struct DebugPadState : ISampledData
|
||||||
|
{
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public DebugPadAttribute Attributes;
|
||||||
|
public DebugPadButton Buttons;
|
||||||
|
public AnalogStickState AnalogStickR;
|
||||||
|
public AnalogStickState AnalogStickL;
|
||||||
|
|
||||||
|
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
|
||||||
|
{
|
||||||
|
struct KeyboardKey
|
||||||
|
{
|
||||||
|
public Array4<ulong> RawData;
|
||||||
|
|
||||||
|
public bool this[KeyboardKeyShift index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (RawData[(int)index / 64] & (1UL << ((int)index & 63))) != 0;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
int arrayIndex = (int)index / 64;
|
||||||
|
ulong mask = 1UL << ((int)index & 63);
|
||||||
|
|
||||||
|
RawData[arrayIndex] &= ~mask;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
RawData[arrayIndex] |= mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
|
||||||
|
{
|
||||||
|
enum KeyboardKeyShift
|
||||||
|
{
|
||||||
|
A = 4,
|
||||||
|
B = 5,
|
||||||
|
C = 6,
|
||||||
|
D = 7,
|
||||||
|
E = 8,
|
||||||
|
F = 9,
|
||||||
|
G = 10,
|
||||||
|
H = 11,
|
||||||
|
I = 12,
|
||||||
|
J = 13,
|
||||||
|
K = 14,
|
||||||
|
L = 15,
|
||||||
|
M = 16,
|
||||||
|
N = 17,
|
||||||
|
O = 18,
|
||||||
|
P = 19,
|
||||||
|
Q = 20,
|
||||||
|
R = 21,
|
||||||
|
S = 22,
|
||||||
|
T = 23,
|
||||||
|
U = 24,
|
||||||
|
V = 25,
|
||||||
|
W = 26,
|
||||||
|
X = 27,
|
||||||
|
Y = 28,
|
||||||
|
Z = 29,
|
||||||
|
D1 = 30,
|
||||||
|
D2 = 31,
|
||||||
|
D3 = 32,
|
||||||
|
D4 = 33,
|
||||||
|
D5 = 34,
|
||||||
|
D6 = 35,
|
||||||
|
D7 = 36,
|
||||||
|
D8 = 37,
|
||||||
|
D9 = 38,
|
||||||
|
D0 = 39,
|
||||||
|
Return = 40,
|
||||||
|
Escape = 41,
|
||||||
|
Backspace = 42,
|
||||||
|
Tab = 43,
|
||||||
|
Space = 44,
|
||||||
|
Minus = 45,
|
||||||
|
Plus = 46,
|
||||||
|
OpenBracket = 47,
|
||||||
|
CloseBracket = 48,
|
||||||
|
Pipe = 49,
|
||||||
|
Tilde = 50,
|
||||||
|
Semicolon = 51,
|
||||||
|
Quote = 52,
|
||||||
|
Backquote = 53,
|
||||||
|
Comma = 54,
|
||||||
|
Period = 55,
|
||||||
|
Slash = 56,
|
||||||
|
CapsLock = 57,
|
||||||
|
F1 = 58,
|
||||||
|
F2 = 59,
|
||||||
|
F3 = 60,
|
||||||
|
F4 = 61,
|
||||||
|
F5 = 62,
|
||||||
|
F6 = 63,
|
||||||
|
F7 = 64,
|
||||||
|
F8 = 65,
|
||||||
|
F9 = 66,
|
||||||
|
F10 = 67,
|
||||||
|
F11 = 68,
|
||||||
|
F12 = 69,
|
||||||
|
PrintScreen = 70,
|
||||||
|
ScrollLock = 71,
|
||||||
|
Pause = 72,
|
||||||
|
Insert = 73,
|
||||||
|
Home = 74,
|
||||||
|
PageUp = 75,
|
||||||
|
Delete = 76,
|
||||||
|
End = 77,
|
||||||
|
PageDown = 78,
|
||||||
|
RightArrow = 79,
|
||||||
|
LeftArrow = 80,
|
||||||
|
DownArrow = 81,
|
||||||
|
UpArrow = 82,
|
||||||
|
NumLock = 83,
|
||||||
|
NumPadDivide = 84,
|
||||||
|
NumPadMultiply = 85,
|
||||||
|
NumPadSubtract = 86,
|
||||||
|
NumPadAdd = 87,
|
||||||
|
NumPadEnter = 88,
|
||||||
|
NumPad1 = 89,
|
||||||
|
NumPad2 = 90,
|
||||||
|
NumPad3 = 91,
|
||||||
|
NumPad4 = 92,
|
||||||
|
NumPad5 = 93,
|
||||||
|
NumPad6 = 94,
|
||||||
|
NumPad7 = 95,
|
||||||
|
NumPad8 = 96,
|
||||||
|
NumPad9 = 97,
|
||||||
|
NumPad0 = 98,
|
||||||
|
NumPadDot = 99,
|
||||||
|
Backslash = 100,
|
||||||
|
Application = 101,
|
||||||
|
Power = 102,
|
||||||
|
NumPadEquals = 103,
|
||||||
|
F13 = 104,
|
||||||
|
F14 = 105,
|
||||||
|
F15 = 106,
|
||||||
|
F16 = 107,
|
||||||
|
F17 = 108,
|
||||||
|
F18 = 109,
|
||||||
|
F19 = 110,
|
||||||
|
F20 = 111,
|
||||||
|
F21 = 112,
|
||||||
|
F22 = 113,
|
||||||
|
F23 = 114,
|
||||||
|
F24 = 115,
|
||||||
|
NumPadComma = 133,
|
||||||
|
Ro = 135,
|
||||||
|
KatakanaHiragana = 136,
|
||||||
|
Yen = 137,
|
||||||
|
Henkan = 138,
|
||||||
|
Muhenkan = 139,
|
||||||
|
NumPadCommaPc98 = 140,
|
||||||
|
HangulEnglish = 144,
|
||||||
|
Hanja = 145,
|
||||||
|
Katakana = 146,
|
||||||
|
Hiragana = 147,
|
||||||
|
ZenkakuHankaku = 148,
|
||||||
|
LeftControl = 224,
|
||||||
|
LeftShift = 225,
|
||||||
|
LeftAlt = 226,
|
||||||
|
LeftGui = 227,
|
||||||
|
RightControl = 228,
|
||||||
|
RightShift = 229,
|
||||||
|
RightAlt = 230,
|
||||||
|
RightGui = 231
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
|
||||||
|
{
|
||||||
|
// TODO: This seems entirely wrong
|
||||||
|
[Flags]
|
||||||
|
enum KeyboardModifier : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Control = 1 << 0,
|
||||||
|
Shift = 1 << 1,
|
||||||
|
LeftAlt = 1 << 2,
|
||||||
|
RightAlt = 1 << 3,
|
||||||
|
Gui = 1 << 4,
|
||||||
|
CapsLock = 1 << 8,
|
||||||
|
ScrollLock = 1 << 9,
|
||||||
|
NumLock = 1 << 10,
|
||||||
|
Katakana = 1 << 11,
|
||||||
|
Hiragana = 1 << 12
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
|
||||||
|
{
|
||||||
|
struct KeyboardState : ISampledData
|
||||||
|
{
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public KeyboardModifier Modifiers;
|
||||||
|
public KeyboardKey Keys;
|
||||||
|
|
||||||
|
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum MouseAttribute : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Transferable = 1 << 0,
|
||||||
|
IsConnected = 1 << 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum MouseButton : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Left = 1 << 0,
|
||||||
|
Right = 1 << 1,
|
||||||
|
Middle = 1 << 2,
|
||||||
|
Forward = 1 << 3,
|
||||||
|
Back = 1 << 4
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
|
||||||
|
{
|
||||||
|
struct MouseState : ISampledData
|
||||||
|
{
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public int X;
|
||||||
|
public int Y;
|
||||||
|
public int DeltaX;
|
||||||
|
public int DeltaY;
|
||||||
|
public int WheelDeltaX;
|
||||||
|
public int WheelDeltaY;
|
||||||
|
public MouseButton Buttons;
|
||||||
|
public MouseAttribute Attributes;
|
||||||
|
|
||||||
|
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum DeviceType : int
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
FullKey = 1 << 0,
|
||||||
|
DebugPad = 1 << 1,
|
||||||
|
HandheldLeft = 1 << 2,
|
||||||
|
HandheldRight = 1 << 3,
|
||||||
|
JoyLeft = 1 << 4,
|
||||||
|
JoyRight = 1 << 5,
|
||||||
|
Palma = 1 << 6,
|
||||||
|
LarkHvcLeft = 1 << 7,
|
||||||
|
LarkHvcRight = 1 << 8,
|
||||||
|
LarkNesLeft = 1 << 9,
|
||||||
|
LarkNesRight = 1 << 10,
|
||||||
|
HandheldLarkHvcLeft = 1 << 11,
|
||||||
|
HandheldLarkHvcRight = 1 << 12,
|
||||||
|
HandheldLarkNesLeft = 1 << 13,
|
||||||
|
HandheldLarkNesRight = 1 << 14,
|
||||||
|
Lucia = 1 << 15,
|
||||||
|
|
||||||
|
System = 1 << 31
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum NpadAttribute : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
IsConnected = 1 << 0,
|
||||||
|
IsWired = 1 << 1,
|
||||||
|
IsLeftConnected = 1 << 2,
|
||||||
|
IsLeftWired = 1 << 3,
|
||||||
|
IsRightConnected = 1 << 4,
|
||||||
|
IsRightWired = 1 << 5
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
enum NpadBatteryLevel : int
|
||||||
|
{
|
||||||
|
Percent0,
|
||||||
|
Percent25,
|
||||||
|
Percent50,
|
||||||
|
Percent75,
|
||||||
|
Percent100
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum NpadButton : ulong
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
A = 1 << 0,
|
||||||
|
B = 1 << 1,
|
||||||
|
X = 1 << 2,
|
||||||
|
Y = 1 << 3,
|
||||||
|
StickL = 1 << 4,
|
||||||
|
StickR = 1 << 5,
|
||||||
|
L = 1 << 6,
|
||||||
|
R = 1 << 7,
|
||||||
|
ZL = 1 << 8,
|
||||||
|
ZR = 1 << 9,
|
||||||
|
Plus = 1 << 10,
|
||||||
|
Minus = 1 << 11,
|
||||||
|
Left = 1 << 12,
|
||||||
|
Up = 1 << 13,
|
||||||
|
Right = 1 << 14,
|
||||||
|
Down = 1 << 15,
|
||||||
|
StickLLeft = 1 << 16,
|
||||||
|
StickLUp = 1 << 17,
|
||||||
|
StickLRight = 1 << 18,
|
||||||
|
StickLDown = 1 << 19,
|
||||||
|
StickRLeft = 1 << 20,
|
||||||
|
StickRUp = 1 << 21,
|
||||||
|
StickRRight = 1 << 22,
|
||||||
|
StickRDown = 1 << 23,
|
||||||
|
LeftSL = 1 << 24,
|
||||||
|
LeftSR = 1 << 25,
|
||||||
|
RightSL = 1 << 26,
|
||||||
|
RightSR = 1 << 27,
|
||||||
|
Palma = 1 << 28,
|
||||||
|
|
||||||
|
// FIXME: Probably a button on Lark.
|
||||||
|
Unknown29 = 1 << 29,
|
||||||
|
|
||||||
|
HandheldLeftB = 1 << 30
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
enum NpadColorAttribute : uint
|
||||||
|
{
|
||||||
|
Ok,
|
||||||
|
ReadError,
|
||||||
|
NoController
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
struct NpadCommonState : ISampledData
|
||||||
|
{
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public NpadButton Buttons;
|
||||||
|
public AnalogStickState AnalogStickL;
|
||||||
|
public AnalogStickState AnalogStickR;
|
||||||
|
public NpadAttribute Attributes;
|
||||||
|
private uint _reserved;
|
||||||
|
|
||||||
|
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
struct NpadFullKeyColorState
|
||||||
|
{
|
||||||
|
public NpadColorAttribute Attribute;
|
||||||
|
public uint FullKeyBody;
|
||||||
|
public uint FullKeyButtons;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
struct NpadGcTriggerState : ISampledData
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public uint TriggerL;
|
||||||
|
public uint TriggerR;
|
||||||
|
#pragma warning restore CS0649
|
||||||
|
|
||||||
|
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
struct NpadInternalState
|
||||||
|
{
|
||||||
|
public NpadStyleTag StyleSet;
|
||||||
|
public NpadJoyAssignmentMode JoyAssignmentMode;
|
||||||
|
public NpadFullKeyColorState FullKeyColor;
|
||||||
|
public NpadJoyColorState JoyColor;
|
||||||
|
public RingLifo<NpadCommonState> FullKey;
|
||||||
|
public RingLifo<NpadCommonState> Handheld;
|
||||||
|
public RingLifo<NpadCommonState> JoyDual;
|
||||||
|
public RingLifo<NpadCommonState> JoyLeft;
|
||||||
|
public RingLifo<NpadCommonState> JoyRight;
|
||||||
|
public RingLifo<NpadCommonState> Palma;
|
||||||
|
public RingLifo<NpadCommonState> SystemExt;
|
||||||
|
public RingLifo<SixAxisSensorState> FullKeySixAxisSensor;
|
||||||
|
public RingLifo<SixAxisSensorState> HandheldSixAxisSensor;
|
||||||
|
public RingLifo<SixAxisSensorState> JoyDualSixAxisSensor;
|
||||||
|
public RingLifo<SixAxisSensorState> JoyDualRightSixAxisSensor;
|
||||||
|
public RingLifo<SixAxisSensorState> JoyLeftSixAxisSensor;
|
||||||
|
public RingLifo<SixAxisSensorState> JoyRightSixAxisSensor;
|
||||||
|
public DeviceType DeviceType;
|
||||||
|
private uint _reserved1;
|
||||||
|
public NpadSystemProperties SystemProperties;
|
||||||
|
public NpadSystemButtonProperties SystemButtonProperties;
|
||||||
|
public NpadBatteryLevel BatteryLevelJoyDual;
|
||||||
|
public NpadBatteryLevel BatteryLevelJoyLeft;
|
||||||
|
public NpadBatteryLevel BatteryLevelJoyRight;
|
||||||
|
public uint AppletFooterUiAttributes;
|
||||||
|
public byte AppletFooterUiType;
|
||||||
|
private unsafe fixed byte _reserved2[0x7B];
|
||||||
|
public RingLifo<NpadGcTriggerState> GcTrigger;
|
||||||
|
public NpadLarkType LarkTypeLeftAndMain;
|
||||||
|
public NpadLarkType LarkTypeRight;
|
||||||
|
public NpadLuciaType LuciaType;
|
||||||
|
public uint Unknown43EC;
|
||||||
|
|
||||||
|
public static NpadInternalState Create()
|
||||||
|
{
|
||||||
|
return new NpadInternalState
|
||||||
|
{
|
||||||
|
FullKey = RingLifo<NpadCommonState>.Create(),
|
||||||
|
Handheld = RingLifo<NpadCommonState>.Create(),
|
||||||
|
JoyDual = RingLifo<NpadCommonState>.Create(),
|
||||||
|
JoyLeft = RingLifo<NpadCommonState>.Create(),
|
||||||
|
JoyRight = RingLifo<NpadCommonState>.Create(),
|
||||||
|
Palma = RingLifo<NpadCommonState>.Create(),
|
||||||
|
SystemExt = RingLifo<NpadCommonState>.Create(),
|
||||||
|
FullKeySixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
|
||||||
|
HandheldSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
|
||||||
|
JoyDualSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
|
||||||
|
JoyDualRightSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
|
||||||
|
JoyLeftSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
|
||||||
|
JoyRightSixAxisSensor = RingLifo<SixAxisSensorState>.Create(),
|
||||||
|
GcTrigger = RingLifo<NpadGcTriggerState>.Create(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
enum NpadJoyAssignmentMode : uint
|
||||||
|
{
|
||||||
|
Dual,
|
||||||
|
Single
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
struct NpadJoyColorState
|
||||||
|
{
|
||||||
|
public NpadColorAttribute Attribute;
|
||||||
|
public uint LeftBody;
|
||||||
|
public uint LeftButtons;
|
||||||
|
public uint RightBody;
|
||||||
|
public uint RightButtons;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
enum NpadLarkType : uint
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
H1,
|
||||||
|
H2,
|
||||||
|
NL,
|
||||||
|
NR
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
enum NpadLuciaType
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
J,
|
||||||
|
E,
|
||||||
|
U
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x5000)]
|
||||||
|
struct NpadState
|
||||||
|
{
|
||||||
|
public NpadInternalState InternalState;
|
||||||
|
|
||||||
|
public static NpadState Create()
|
||||||
|
{
|
||||||
|
return new NpadState
|
||||||
|
{
|
||||||
|
InternalState = NpadInternalState.Create()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Nintendo pad style
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
enum NpadStyleTag : uint
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No type.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pro controller.
|
||||||
|
/// </summary>
|
||||||
|
FullKey = 1 << 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Joy-Con controller in handheld mode.
|
||||||
|
/// </summary>
|
||||||
|
Handheld = 1 << 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Joy-Con controller in dual mode.
|
||||||
|
/// </summary>
|
||||||
|
JoyDual = 1 << 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Joy-Con left controller in single mode.
|
||||||
|
/// </summary>
|
||||||
|
JoyLeft = 1 << 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Joy-Con right controller in single mode.
|
||||||
|
/// </summary>
|
||||||
|
JoyRight = 1 << 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GameCube controller.
|
||||||
|
/// </summary>
|
||||||
|
Gc = 1 << 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Poké Ball Plus controller.
|
||||||
|
/// </summary>
|
||||||
|
Palma = 1 << 6,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// NES and Famicom controller.
|
||||||
|
/// </summary>
|
||||||
|
Lark = 1 << 7,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// NES and Famicom controller in handheld mode.
|
||||||
|
/// </summary>
|
||||||
|
HandheldLark = 1 << 8,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SNES controller.
|
||||||
|
/// </summary>
|
||||||
|
Lucia = 1 << 9,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generic external controller.
|
||||||
|
/// </summary>
|
||||||
|
SystemExt = 1 << 29,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generic controller.
|
||||||
|
/// </summary>
|
||||||
|
System = 1 << 30
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum NpadSystemButtonProperties : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
IsUnintendedHomeButtonInputProtectionEnabled = 1 << 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum NpadSystemProperties : ulong
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
IsChargingJoyDual = 1 << 0,
|
||||||
|
IsChargingJoyLeft = 1 << 1,
|
||||||
|
IsChargingJoyRight = 1 << 2,
|
||||||
|
IsPoweredJoyDual = 1 << 3,
|
||||||
|
IsPoweredJoyLeft = 1 << 4,
|
||||||
|
IsPoweredJoyRight = 1 << 5,
|
||||||
|
IsUnsuportedButtonPressedOnNpadSystem = 1 << 9,
|
||||||
|
IsUnsuportedButtonPressedOnNpadSystemExt = 1 << 10,
|
||||||
|
IsAbxyButtonOriented = 1 << 11,
|
||||||
|
IsSlSrButtonOriented = 1 << 12,
|
||||||
|
IsPlusAvailable = 1 << 13,
|
||||||
|
IsMinusAvailable = 1 << 14,
|
||||||
|
IsDirectionalButtonsAvailable = 1 << 15
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum SixAxisSensorAttribute : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
IsConnected = 1 << 0,
|
||||||
|
IsInterpolated = 1 << 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||||
|
{
|
||||||
|
struct SixAxisSensorState : ISampledData
|
||||||
|
{
|
||||||
|
public ulong DeltaTime;
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public HidVector Acceleration;
|
||||||
|
public HidVector AngularVelocity;
|
||||||
|
public HidVector Angle;
|
||||||
|
public Array9<float> Direction;
|
||||||
|
public SixAxisSensorAttribute Attributes;
|
||||||
|
private uint _reserved;
|
||||||
|
|
||||||
|
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represent the shared memory shared between applications for input.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x40000)]
|
||||||
|
struct SharedMemory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Debug controller.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public RingLifo<DebugPadState> DebugPad;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Touchscreen.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x400)]
|
||||||
|
public RingLifo<TouchScreenState> TouchScreen;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mouse.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x3400)]
|
||||||
|
public RingLifo<MouseState> Mouse;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keyboard.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x3800)]
|
||||||
|
public RingLifo<KeyboardState> Keyboard;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nintendo Pads.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x9A00)]
|
||||||
|
public Array10<NpadState> Npads;
|
||||||
|
|
||||||
|
public static SharedMemory Create()
|
||||||
|
{
|
||||||
|
SharedMemory result = new SharedMemory
|
||||||
|
{
|
||||||
|
DebugPad = RingLifo<DebugPadState>.Create(),
|
||||||
|
TouchScreen = RingLifo<TouchScreenState>.Create(),
|
||||||
|
Mouse = RingLifo<MouseState>.Create(),
|
||||||
|
Keyboard = RingLifo<KeyboardState>.Create(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < result.Npads.Length; i++)
|
||||||
|
{
|
||||||
|
result.Npads[i] = NpadState.Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum TouchAttribute : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Start = 1 << 0,
|
||||||
|
End = 1 << 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen
|
||||||
|
{
|
||||||
|
struct TouchScreenState : ISampledData
|
||||||
|
{
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public int TouchesCount;
|
||||||
|
private int _reserved;
|
||||||
|
public Array16<TouchState> Touches;
|
||||||
|
|
||||||
|
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen
|
||||||
|
{
|
||||||
|
struct TouchState
|
||||||
|
{
|
||||||
|
public ulong DeltaTime;
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
public TouchAttribute Attribute;
|
||||||
|
#pragma warning restore CS0649
|
||||||
|
public uint FingerId;
|
||||||
|
public uint X;
|
||||||
|
public uint Y;
|
||||||
|
public uint DiameterX;
|
||||||
|
public uint DiameterY;
|
||||||
|
public uint RotationAngle;
|
||||||
|
#pragma warning disable CS0169
|
||||||
|
private uint _reserved;
|
||||||
|
#pragma warning restore CS0169
|
||||||
|
}
|
||||||
|
}
|
|
@ -456,14 +456,14 @@ namespace Ryujinx.Input.HLE
|
||||||
KeyboardInput hidKeyboard = new KeyboardInput
|
KeyboardInput hidKeyboard = new KeyboardInput
|
||||||
{
|
{
|
||||||
Modifier = 0,
|
Modifier = 0,
|
||||||
Keys = new int[0x8]
|
Keys = new ulong[0x4]
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (HLEKeyboardMappingEntry entry in KeyMapping)
|
foreach (HLEKeyboardMappingEntry entry in KeyMapping)
|
||||||
{
|
{
|
||||||
int value = keyboardState.IsPressed(entry.TargetKey) ? 1 : 0;
|
ulong value = keyboardState.IsPressed(entry.TargetKey) ? 1UL : 0UL;
|
||||||
|
|
||||||
hidKeyboard.Keys[entry.Target / 0x20] |= (value << (entry.Target % 0x20));
|
hidKeyboard.Keys[entry.Target / 0x40] |= (value << (entry.Target % 0x40));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (HLEKeyboardMappingEntry entry in KeyModifierMapping)
|
foreach (HLEKeyboardMappingEntry entry in KeyModifierMapping)
|
||||||
|
|
Loading…
Reference in a new issue