mirror of
https://github.com/Ryubing/Ryujinx.git
synced 2025-11-27 01:32:24 -05:00
Move solution and projects to src
This commit is contained in:
107
src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
Normal file
107
src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.HLE.HOS.Kernel.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.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.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public class Hid
|
||||
{
|
||||
private readonly Switch _device;
|
||||
|
||||
private readonly SharedMemoryStorage _storage;
|
||||
|
||||
internal ref SharedMemory SharedMemory => ref _storage.GetRef<SharedMemory>(0);
|
||||
|
||||
internal const int SharedMemEntryCount = 17;
|
||||
|
||||
public DebugPadDevice DebugPad;
|
||||
public TouchDevice Touchscreen;
|
||||
public MouseDevice Mouse;
|
||||
public KeyboardDevice Keyboard;
|
||||
public NpadDevices Npads;
|
||||
|
||||
private static void CheckTypeSizeOrThrow<T>(int expectedSize)
|
||||
{
|
||||
if (Unsafe.SizeOf<T>() != expectedSize)
|
||||
{
|
||||
throw new InvalidStructLayoutException<T>(expectedSize);
|
||||
}
|
||||
}
|
||||
|
||||
static Hid()
|
||||
{
|
||||
CheckTypeSizeOrThrow<RingLifo<DebugPadState>>(0x2c8);
|
||||
CheckTypeSizeOrThrow<RingLifo<TouchScreenState>>(0x2C38);
|
||||
CheckTypeSizeOrThrow<RingLifo<MouseState>>(0x350);
|
||||
CheckTypeSizeOrThrow<RingLifo<KeyboardState>>(0x3D8);
|
||||
CheckTypeSizeOrThrow<Array10<NpadState>>(0x32000);
|
||||
CheckTypeSizeOrThrow<SharedMemory>(Horizon.HidSize);
|
||||
}
|
||||
|
||||
internal Hid(in Switch device, SharedMemoryStorage storage)
|
||||
{
|
||||
_device = device;
|
||||
_storage = storage;
|
||||
|
||||
SharedMemory = SharedMemory.Create();
|
||||
|
||||
InitDevices();
|
||||
}
|
||||
|
||||
private void InitDevices()
|
||||
{
|
||||
DebugPad = new DebugPadDevice(_device, true);
|
||||
Touchscreen = new TouchDevice(_device, true);
|
||||
Mouse = new MouseDevice(_device, false);
|
||||
Keyboard = new KeyboardDevice(_device, false);
|
||||
Npads = new NpadDevices(_device, true);
|
||||
}
|
||||
|
||||
public void RefreshInputConfig(List<InputConfig> inputConfig)
|
||||
{
|
||||
ControllerConfig[] npadConfig = new ControllerConfig[inputConfig.Count];
|
||||
|
||||
for (int i = 0; i < npadConfig.Length; ++i)
|
||||
{
|
||||
npadConfig[i].Player = (PlayerIndex)inputConfig[i].PlayerIndex;
|
||||
npadConfig[i].Type = (ControllerType)inputConfig[i].ControllerType;
|
||||
}
|
||||
|
||||
_device.Hid.Npads.Configure(npadConfig);
|
||||
}
|
||||
|
||||
public ControllerKeys UpdateStickButtons(JoystickPosition leftStick, JoystickPosition rightStick)
|
||||
{
|
||||
const int stickButtonThreshold = short.MaxValue / 2;
|
||||
ControllerKeys result = 0;
|
||||
|
||||
result |= (leftStick.Dx < -stickButtonThreshold) ? ControllerKeys.LStickLeft : result;
|
||||
result |= (leftStick.Dx > stickButtonThreshold) ? ControllerKeys.LStickRight : result;
|
||||
result |= (leftStick.Dy < -stickButtonThreshold) ? ControllerKeys.LStickDown : result;
|
||||
result |= (leftStick.Dy > stickButtonThreshold) ? ControllerKeys.LStickUp : result;
|
||||
|
||||
result |= (rightStick.Dx < -stickButtonThreshold) ? ControllerKeys.RStickLeft : result;
|
||||
result |= (rightStick.Dx > stickButtonThreshold) ? ControllerKeys.RStickRight : result;
|
||||
result |= (rightStick.Dy < -stickButtonThreshold) ? ControllerKeys.RStickDown : result;
|
||||
result |= (rightStick.Dy > stickButtonThreshold) ? ControllerKeys.RStickUp : result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static ulong GetTimestampTicks()
|
||||
{
|
||||
return (ulong)PerformanceCounter.ElapsedMilliseconds * 19200;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs
Normal file
14
src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public abstract class BaseDevice
|
||||
{
|
||||
protected readonly Switch _device;
|
||||
public bool Active;
|
||||
|
||||
public BaseDevice(Switch device, bool active)
|
||||
{
|
||||
_device = device;
|
||||
Active = active;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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
|
||||
{
|
||||
public DebugPadDevice(Switch device, bool active) : base(device, active) { }
|
||||
|
||||
public void Update()
|
||||
{
|
||||
ref RingLifo<DebugPadState> lifo = ref _device.Hid.SharedMemory.DebugPad;
|
||||
|
||||
ref DebugPadState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
DebugPadState newState = new DebugPadState();
|
||||
|
||||
if (Active)
|
||||
{
|
||||
// TODO: This is a debug device only present in dev environment, do we want to support it?
|
||||
}
|
||||
|
||||
newState.SamplingNumber = previousEntry.SamplingNumber + 1;
|
||||
|
||||
lifo.Write(ref newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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
|
||||
{
|
||||
public KeyboardDevice(Switch device, bool active) : base(device, active) { }
|
||||
|
||||
public void Update(KeyboardInput keyState)
|
||||
{
|
||||
ref RingLifo<KeyboardState> lifo = ref _device.Hid.SharedMemory.Keyboard;
|
||||
|
||||
if (!Active)
|
||||
{
|
||||
lifo.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ref KeyboardState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
KeyboardState newState = new KeyboardState
|
||||
{
|
||||
SamplingNumber = previousEntry.SamplingNumber + 1,
|
||||
};
|
||||
|
||||
keyState.Keys.AsSpan().CopyTo(newState.Keys.RawData.AsSpan());
|
||||
newState.Modifiers = (KeyboardModifier)keyState.Modifier;
|
||||
|
||||
lifo.Write(ref newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs
Normal file
36
src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
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, uint buttons = 0, int scrollX = 0, int scrollY = 0, bool connected = false)
|
||||
{
|
||||
ref RingLifo<MouseState> lifo = ref _device.Hid.SharedMemory.Mouse;
|
||||
|
||||
ref MouseState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
MouseState newState = new MouseState()
|
||||
{
|
||||
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;
|
||||
newState.Attributes = connected ? MouseAttribute.IsConnected : MouseAttribute.None;
|
||||
}
|
||||
|
||||
lifo.Write(ref newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
635
src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
Normal file
635
src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
Normal file
@@ -0,0 +1,635 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
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;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public class NpadDevices : BaseDevice
|
||||
{
|
||||
private const int NoMatchNotifyFrequencyMs = 2000;
|
||||
private int _activeCount;
|
||||
private long _lastNotifyTimestamp;
|
||||
|
||||
public const int MaxControllers = 9; // Players 1-8 and Handheld
|
||||
private ControllerType[] _configuredTypes;
|
||||
private KEvent[] _styleSetUpdateEvents;
|
||||
private bool[] _supportedPlayers;
|
||||
private static VibrationValue _neutralVibrationValue = new VibrationValue
|
||||
{
|
||||
AmplitudeLow = 0f,
|
||||
FrequencyLow = 160f,
|
||||
AmplitudeHigh = 0f,
|
||||
FrequencyHigh = 320f
|
||||
};
|
||||
|
||||
internal NpadJoyHoldType JoyHold { get; set; }
|
||||
internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
|
||||
internal ControllerType SupportedStyleSets { get; set; }
|
||||
|
||||
public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>>();
|
||||
public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new Dictionary<PlayerIndex, (VibrationValue, VibrationValue)>();
|
||||
|
||||
public NpadDevices(Switch device, bool active = true) : base(device, active)
|
||||
{
|
||||
_configuredTypes = new ControllerType[MaxControllers];
|
||||
|
||||
SupportedStyleSets = ControllerType.Handheld | ControllerType.JoyconPair |
|
||||
ControllerType.JoyconLeft | ControllerType.JoyconRight |
|
||||
ControllerType.ProController;
|
||||
|
||||
_supportedPlayers = new bool[MaxControllers];
|
||||
_supportedPlayers.AsSpan().Fill(true);
|
||||
|
||||
_styleSetUpdateEvents = new KEvent[MaxControllers];
|
||||
for (int i = 0; i < _styleSetUpdateEvents.Length; ++i)
|
||||
{
|
||||
_styleSetUpdateEvents[i] = new KEvent(_device.System.KernelContext);
|
||||
}
|
||||
|
||||
_activeCount = 0;
|
||||
|
||||
JoyHold = NpadJoyHoldType.Vertical;
|
||||
}
|
||||
|
||||
internal ref KEvent GetStyleSetUpdateEvent(PlayerIndex player)
|
||||
{
|
||||
return ref _styleSetUpdateEvents[(int)player];
|
||||
}
|
||||
|
||||
internal void ClearSupportedPlayers()
|
||||
{
|
||||
_supportedPlayers.AsSpan().Clear();
|
||||
}
|
||||
|
||||
internal void SetSupportedPlayer(PlayerIndex player, bool supported = true)
|
||||
{
|
||||
_supportedPlayers[(int)player] = supported;
|
||||
}
|
||||
|
||||
internal IEnumerable<PlayerIndex> GetSupportedPlayers()
|
||||
{
|
||||
for (int i = 0; i < _supportedPlayers.Length; ++i)
|
||||
{
|
||||
if (_supportedPlayers[i])
|
||||
{
|
||||
yield return (PlayerIndex)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Validate(int playerMin, int playerMax, ControllerType acceptedTypes, out int configuredCount, out PlayerIndex primaryIndex)
|
||||
{
|
||||
primaryIndex = PlayerIndex.Unknown;
|
||||
configuredCount = 0;
|
||||
|
||||
for (int i = 0; i < MaxControllers; ++i)
|
||||
{
|
||||
ControllerType npad = _configuredTypes[i];
|
||||
|
||||
if (npad == ControllerType.Handheld && _device.System.State.DockedMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ControllerType currentType = (ControllerType)_device.Hid.SharedMemory.Npads[i].InternalState.StyleSet;
|
||||
|
||||
if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i])
|
||||
{
|
||||
configuredCount++;
|
||||
if (primaryIndex == PlayerIndex.Unknown)
|
||||
{
|
||||
primaryIndex = (PlayerIndex)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (configuredCount < playerMin || configuredCount > playerMax || primaryIndex == PlayerIndex.Unknown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Configure(params ControllerConfig[] configs)
|
||||
{
|
||||
_configuredTypes = new ControllerType[MaxControllers];
|
||||
|
||||
for (int i = 0; i < configs.Length; ++i)
|
||||
{
|
||||
PlayerIndex player = configs[i].Player;
|
||||
ControllerType controllerType = configs[i].Type;
|
||||
|
||||
if (player > PlayerIndex.Handheld)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Player must be Player1-8 or Handheld");
|
||||
}
|
||||
|
||||
if (controllerType == ControllerType.Handheld)
|
||||
{
|
||||
player = PlayerIndex.Handheld;
|
||||
}
|
||||
|
||||
_configuredTypes[(int)player] = controllerType;
|
||||
|
||||
Logger.Info?.Print(LogClass.Hid, $"Configured Controller {controllerType} to {player}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(IList<GamepadInput> states)
|
||||
{
|
||||
Remap();
|
||||
|
||||
Span<bool> updated = stackalloc bool[10];
|
||||
|
||||
// Update configured inputs
|
||||
for (int i = 0; i < states.Count; ++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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Remap()
|
||||
{
|
||||
// Remap/Init if necessary
|
||||
for (int i = 0; i < MaxControllers; ++i)
|
||||
{
|
||||
ControllerType config = _configuredTypes[i];
|
||||
|
||||
// Remove Handheld config when Docked
|
||||
if (config == ControllerType.Handheld && _device.System.State.DockedMode)
|
||||
{
|
||||
config = ControllerType.None;
|
||||
}
|
||||
|
||||
// Auto-remap ProController and JoyconPair
|
||||
if (config == ControllerType.JoyconPair && (SupportedStyleSets & ControllerType.JoyconPair) == 0 && (SupportedStyleSets & ControllerType.ProController) != 0)
|
||||
{
|
||||
config = ControllerType.ProController;
|
||||
}
|
||||
else if (config == ControllerType.ProController && (SupportedStyleSets & ControllerType.ProController) == 0 && (SupportedStyleSets & ControllerType.JoyconPair) != 0)
|
||||
{
|
||||
config = ControllerType.JoyconPair;
|
||||
}
|
||||
|
||||
// Check StyleSet and PlayerSet
|
||||
if ((config & SupportedStyleSets) == 0 || !_supportedPlayers[i])
|
||||
{
|
||||
config = ControllerType.None;
|
||||
}
|
||||
|
||||
SetupNpad((PlayerIndex)i, config);
|
||||
}
|
||||
|
||||
if (_activeCount == 0 && PerformanceCounter.ElapsedMilliseconds > _lastNotifyTimestamp + NoMatchNotifyFrequencyMs)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Hid, $"No matching controllers found. Application requests '{SupportedStyleSets}' on '{string.Join(", ", GetSupportedPlayers())}'");
|
||||
_lastNotifyTimestamp = PerformanceCounter.ElapsedMilliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupNpad(PlayerIndex player, ControllerType type)
|
||||
{
|
||||
ref NpadInternalState controller = ref _device.Hid.SharedMemory.Npads[(int)player].InternalState;
|
||||
|
||||
ControllerType oldType = (ControllerType)controller.StyleSet;
|
||||
|
||||
if (oldType == type)
|
||||
{
|
||||
return; // Already configured
|
||||
}
|
||||
|
||||
controller = NpadInternalState.Create(); // Reset it
|
||||
|
||||
if (type == ControllerType.None)
|
||||
{
|
||||
_styleSetUpdateEvents[(int)player].ReadableEvent.Signal(); // Signal disconnect
|
||||
_activeCount--;
|
||||
|
||||
Logger.Info?.Print(LogClass.Hid, $"Disconnected Controller {oldType} from {player}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Allow customizing colors at config
|
||||
controller.JoyAssignmentMode = NpadJoyAssignmentMode.Dual;
|
||||
controller.FullKeyColor.FullKeyBody = (uint)NpadColor.BodyGray;
|
||||
controller.FullKeyColor.FullKeyButtons = (uint)NpadColor.ButtonGray;
|
||||
controller.JoyColor.LeftBody = (uint)NpadColor.BodyNeonBlue;
|
||||
controller.JoyColor.LeftButtons = (uint)NpadColor.ButtonGray;
|
||||
controller.JoyColor.RightBody = (uint)NpadColor.BodyNeonRed;
|
||||
controller.JoyColor.RightButtons = (uint)NpadColor.ButtonGray;
|
||||
|
||||
controller.SystemProperties = NpadSystemProperties.IsPoweredJoyDual |
|
||||
NpadSystemProperties.IsPoweredJoyLeft |
|
||||
NpadSystemProperties.IsPoweredJoyRight;
|
||||
|
||||
controller.BatteryLevelJoyDual = NpadBatteryLevel.Percent100;
|
||||
controller.BatteryLevelJoyLeft = NpadBatteryLevel.Percent100;
|
||||
controller.BatteryLevelJoyRight = NpadBatteryLevel.Percent100;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ControllerType.ProController:
|
||||
controller.StyleSet = NpadStyleTag.FullKey;
|
||||
controller.DeviceType = DeviceType.FullKey;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||
NpadSystemProperties.IsPlusAvailable |
|
||||
NpadSystemProperties.IsMinusAvailable;
|
||||
controller.AppletFooterUiType = AppletFooterUiType.SwitchProController;
|
||||
break;
|
||||
case ControllerType.Handheld:
|
||||
controller.StyleSet = NpadStyleTag.Handheld;
|
||||
controller.DeviceType = DeviceType.HandheldLeft |
|
||||
DeviceType.HandheldRight;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||
NpadSystemProperties.IsPlusAvailable |
|
||||
NpadSystemProperties.IsMinusAvailable;
|
||||
controller.AppletFooterUiType = AppletFooterUiType.HandheldJoyConLeftJoyConRight;
|
||||
break;
|
||||
case ControllerType.JoyconPair:
|
||||
controller.StyleSet = NpadStyleTag.JoyDual;
|
||||
controller.DeviceType = DeviceType.JoyLeft |
|
||||
DeviceType.JoyRight;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented |
|
||||
NpadSystemProperties.IsPlusAvailable |
|
||||
NpadSystemProperties.IsMinusAvailable;
|
||||
controller.AppletFooterUiType = _device.System.State.DockedMode ? AppletFooterUiType.JoyDual : AppletFooterUiType.HandheldJoyConLeftJoyConRight;
|
||||
break;
|
||||
case ControllerType.JoyconLeft:
|
||||
controller.StyleSet = NpadStyleTag.JoyLeft;
|
||||
controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||
controller.DeviceType = DeviceType.JoyLeft;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
|
||||
NpadSystemProperties.IsMinusAvailable;
|
||||
controller.AppletFooterUiType = _device.System.State.DockedMode ? AppletFooterUiType.JoyDualLeftOnly : AppletFooterUiType.HandheldJoyConLeftOnly;
|
||||
break;
|
||||
case ControllerType.JoyconRight:
|
||||
controller.StyleSet = NpadStyleTag.JoyRight;
|
||||
controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||
controller.DeviceType = DeviceType.JoyRight;
|
||||
controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented |
|
||||
NpadSystemProperties.IsPlusAvailable;
|
||||
controller.AppletFooterUiType = _device.System.State.DockedMode ? AppletFooterUiType.JoyDualRightOnly : AppletFooterUiType.HandheldJoyConRightOnly;
|
||||
break;
|
||||
case ControllerType.Pokeball:
|
||||
controller.StyleSet = NpadStyleTag.Palma;
|
||||
controller.DeviceType = DeviceType.Palma;
|
||||
controller.AppletFooterUiType = AppletFooterUiType.None;
|
||||
break;
|
||||
}
|
||||
|
||||
_styleSetUpdateEvents[(int)player].ReadableEvent.Signal();
|
||||
_activeCount++;
|
||||
|
||||
Logger.Info?.Print(LogClass.Hid, $"Connected Controller {type} to {player}");
|
||||
}
|
||||
|
||||
private ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad)
|
||||
{
|
||||
switch (npad.StyleSet)
|
||||
{
|
||||
case NpadStyleTag.FullKey:
|
||||
return ref npad.FullKey;
|
||||
case NpadStyleTag.Handheld:
|
||||
return ref npad.Handheld;
|
||||
case NpadStyleTag.JoyDual:
|
||||
return ref npad.JoyDual;
|
||||
case NpadStyleTag.JoyLeft:
|
||||
return ref npad.JoyLeft;
|
||||
case NpadStyleTag.JoyRight:
|
||||
return ref npad.JoyRight;
|
||||
case NpadStyleTag.Palma:
|
||||
return ref npad.Palma;
|
||||
default:
|
||||
return ref npad.SystemExt;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUnusedInputIfNotEqual(ref RingLifo<NpadCommonState> currentlyUsed, ref RingLifo<NpadCommonState> possiblyUnused)
|
||||
{
|
||||
if (!Unsafe.AreSame(ref currentlyUsed, ref possiblyUnused))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!Unsafe.AreSame(ref currentlyUsed, ref possiblyUnused))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (state.PlayerId == PlayerIndex.Unknown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState;
|
||||
|
||||
if (currentNpad.StyleSet == NpadStyleTag.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
|
||||
|
||||
NpadCommonState newState = new NpadCommonState
|
||||
{
|
||||
Buttons = (NpadButton)state.Buttons,
|
||||
AnalogStickL = new AnalogStickState
|
||||
{
|
||||
X = state.LStick.Dx,
|
||||
Y = state.LStick.Dy,
|
||||
},
|
||||
AnalogStickR = new AnalogStickState
|
||||
{
|
||||
X = state.RStick.Dx,
|
||||
Y = state.RStick.Dy,
|
||||
}
|
||||
};
|
||||
|
||||
newState.Attributes = NpadAttribute.IsConnected;
|
||||
|
||||
switch (currentNpad.StyleSet)
|
||||
{
|
||||
case NpadStyleTag.Handheld:
|
||||
case NpadStyleTag.FullKey:
|
||||
newState.Attributes |= NpadAttribute.IsWired;
|
||||
break;
|
||||
case NpadStyleTag.JoyDual:
|
||||
newState.Attributes |= NpadAttribute.IsLeftConnected |
|
||||
NpadAttribute.IsRightConnected;
|
||||
break;
|
||||
case NpadStyleTag.JoyLeft:
|
||||
newState.Attributes |= NpadAttribute.IsLeftConnected;
|
||||
break;
|
||||
case NpadStyleTag.JoyRight:
|
||||
newState.Attributes |= NpadAttribute.IsRightConnected;
|
||||
break;
|
||||
}
|
||||
|
||||
WriteNewInputEntry(ref lifo, ref newState);
|
||||
|
||||
// Mirror data to Default layout just in case
|
||||
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 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)
|
||||
{
|
||||
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++;
|
||||
|
||||
if (i >= states.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (state.PlayerId == PlayerIndex.Unknown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState;
|
||||
|
||||
if (currentNpad.StyleSet == NpadStyleTag.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HidVector accel = new HidVector()
|
||||
{
|
||||
X = state.Accelerometer.X,
|
||||
Y = state.Accelerometer.Y,
|
||||
Z = state.Accelerometer.Z
|
||||
};
|
||||
|
||||
HidVector gyro = new HidVector()
|
||||
{
|
||||
X = state.Gyroscope.X,
|
||||
Y = state.Gyroscope.Y,
|
||||
Z = state.Gyroscope.Z
|
||||
};
|
||||
|
||||
HidVector rotation = new HidVector()
|
||||
{
|
||||
X = state.Rotation.X,
|
||||
Y = state.Rotation.Y,
|
||||
Z = state.Rotation.Z
|
||||
};
|
||||
|
||||
SixAxisSensorState newState = new SixAxisSensorState
|
||||
{
|
||||
Acceleration = accel,
|
||||
AngularVelocity = gyro,
|
||||
Angle = rotation,
|
||||
Attributes = SixAxisSensorAttribute.IsConnected
|
||||
};
|
||||
|
||||
state.Orientation.AsSpan().CopyTo(newState.Direction.AsSpan());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!needUpdateRight && !isRightPair)
|
||||
{
|
||||
SixAxisSensorState emptyState = new SixAxisSensorState();
|
||||
|
||||
emptyState.Attributes = SixAxisSensorAttribute.IsConnected;
|
||||
|
||||
WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref emptyState);
|
||||
}
|
||||
|
||||
return needUpdateRight;
|
||||
}
|
||||
|
||||
private void UpdateDisconnectedInputSixAxis(PlayerIndex index)
|
||||
{
|
||||
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
|
||||
|
||||
SixAxisSensorState newState = new SixAxisSensorState();
|
||||
|
||||
newState.Attributes = SixAxisSensorAttribute.IsConnected;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void UpdateRumbleQueue(PlayerIndex index, Dictionary<byte, VibrationValue> dualVibrationValues)
|
||||
{
|
||||
if (RumbleQueues.TryGetValue(index, out ConcurrentQueue<(VibrationValue, VibrationValue)> currentQueue))
|
||||
{
|
||||
if (!dualVibrationValues.TryGetValue(0, out VibrationValue leftVibrationValue))
|
||||
{
|
||||
leftVibrationValue = _neutralVibrationValue;
|
||||
}
|
||||
|
||||
if (!dualVibrationValues.TryGetValue(1, out VibrationValue rightVibrationValue))
|
||||
{
|
||||
rightVibrationValue = _neutralVibrationValue;
|
||||
}
|
||||
|
||||
if (!LastVibrationValues.TryGetValue(index, out (VibrationValue, VibrationValue) dualVibrationValue) || !leftVibrationValue.Equals(dualVibrationValue.Item1) || !rightVibrationValue.Equals(dualVibrationValue.Item2))
|
||||
{
|
||||
currentQueue.Enqueue((leftVibrationValue, rightVibrationValue));
|
||||
|
||||
LastVibrationValues[index] = (leftVibrationValue, rightVibrationValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public VibrationValue GetLastVibrationValue(PlayerIndex index, byte position)
|
||||
{
|
||||
if (!LastVibrationValues.TryGetValue(index, out (VibrationValue, VibrationValue) dualVibrationValue))
|
||||
{
|
||||
return _neutralVibrationValue;
|
||||
}
|
||||
|
||||
return (position == 0) ? dualVibrationValue.Item1 : dualVibrationValue.Item2;
|
||||
}
|
||||
|
||||
public ConcurrentQueue<(VibrationValue, VibrationValue)> GetRumbleQueue(PlayerIndex index)
|
||||
{
|
||||
if (!RumbleQueues.TryGetValue(index, out ConcurrentQueue<(VibrationValue, VibrationValue)> rumbleQueue))
|
||||
{
|
||||
rumbleQueue = new ConcurrentQueue<(VibrationValue, VibrationValue)>();
|
||||
_device.Hid.Npads.RumbleQueues[index] = rumbleQueue;
|
||||
}
|
||||
|
||||
return rumbleQueue;
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs
Normal file
48
src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
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
|
||||
{
|
||||
public class TouchDevice : BaseDevice
|
||||
{
|
||||
public TouchDevice(Switch device, bool active) : base(device, active) { }
|
||||
|
||||
public void Update(params TouchPoint[] points)
|
||||
{
|
||||
ref RingLifo<TouchScreenState> lifo = ref _device.Hid.SharedMemory.TouchScreen;
|
||||
|
||||
ref TouchScreenState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
TouchScreenState newState = new TouchScreenState
|
||||
{
|
||||
SamplingNumber = previousEntry.SamplingNumber + 1
|
||||
};
|
||||
|
||||
if (Active)
|
||||
{
|
||||
newState.TouchesCount = points.Length;
|
||||
|
||||
int pointsLength = Math.Min(points.Length, newState.Touches.Length);
|
||||
|
||||
for (int i = 0; i < pointsLength; ++i)
|
||||
{
|
||||
TouchPoint pi = points[i];
|
||||
newState.Touches[i] = new TouchState
|
||||
{
|
||||
DeltaTime = newState.SamplingNumber,
|
||||
Attribute = pi.Attribute,
|
||||
X = pi.X,
|
||||
Y = pi.Y,
|
||||
FingerId = (uint)i,
|
||||
DiameterX = pi.DiameterX,
|
||||
DiameterY = pi.DiameterY,
|
||||
RotationAngle = pi.Angle
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
lifo.Write(ref newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct ControllerConfig
|
||||
{
|
||||
public PlayerIndex Player;
|
||||
public ControllerType Type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct GamepadInput
|
||||
{
|
||||
public PlayerIndex PlayerId;
|
||||
public ControllerKeys Buttons;
|
||||
public JoystickPosition LStick;
|
||||
public JoystickPosition RStick;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct JoystickPosition
|
||||
{
|
||||
public int Dx;
|
||||
public int Dy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct KeyboardInput
|
||||
{
|
||||
public int Modifier;
|
||||
public ulong[] Keys;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct SixAxisInput
|
||||
{
|
||||
public PlayerIndex PlayerId;
|
||||
public Vector3 Accelerometer;
|
||||
public Vector3 Gyroscope;
|
||||
public Vector3 Rotation;
|
||||
public float[] Orientation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct TouchPoint
|
||||
{
|
||||
public TouchAttribute Attribute;
|
||||
public uint X;
|
||||
public uint Y;
|
||||
public uint DiameterX;
|
||||
public uint DiameterY;
|
||||
public uint Angle;
|
||||
}
|
||||
}
|
||||
46
src/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs
Normal file
46
src/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
|
||||
{
|
||||
static class HidUtils
|
||||
{
|
||||
public static PlayerIndex GetIndexFromNpadIdType(NpadIdType npadIdType)
|
||||
=> npadIdType switch
|
||||
{
|
||||
NpadIdType.Player1 => PlayerIndex.Player1,
|
||||
NpadIdType.Player2 => PlayerIndex.Player2,
|
||||
NpadIdType.Player3 => PlayerIndex.Player3,
|
||||
NpadIdType.Player4 => PlayerIndex.Player4,
|
||||
NpadIdType.Player5 => PlayerIndex.Player5,
|
||||
NpadIdType.Player6 => PlayerIndex.Player6,
|
||||
NpadIdType.Player7 => PlayerIndex.Player7,
|
||||
NpadIdType.Player8 => PlayerIndex.Player8,
|
||||
NpadIdType.Handheld => PlayerIndex.Handheld,
|
||||
NpadIdType.Unknown => PlayerIndex.Unknown,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(npadIdType))
|
||||
};
|
||||
|
||||
public static NpadIdType GetNpadIdTypeFromIndex(PlayerIndex index)
|
||||
=> index switch
|
||||
{
|
||||
PlayerIndex.Player1 => NpadIdType.Player1,
|
||||
PlayerIndex.Player2 => NpadIdType.Player2,
|
||||
PlayerIndex.Player3 => NpadIdType.Player3,
|
||||
PlayerIndex.Player4 => NpadIdType.Player4,
|
||||
PlayerIndex.Player5 => NpadIdType.Player5,
|
||||
PlayerIndex.Player6 => NpadIdType.Player6,
|
||||
PlayerIndex.Player7 => NpadIdType.Player7,
|
||||
PlayerIndex.Player8 => NpadIdType.Player8,
|
||||
PlayerIndex.Handheld => NpadIdType.Handheld,
|
||||
PlayerIndex.Unknown => NpadIdType.Unknown,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index))
|
||||
};
|
||||
|
||||
public static bool IsValidNpadIdType(NpadIdType npadIdType)
|
||||
{
|
||||
return (npadIdType >= NpadIdType.Player1 && npadIdType <= NpadIdType.Player8) ||
|
||||
npadIdType == NpadIdType.Handheld ||
|
||||
npadIdType == NpadIdType.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
|
||||
{
|
||||
class IActiveApplicationDeviceList : IpcService
|
||||
{
|
||||
public IActiveApplicationDeviceList() { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// ActivateVibrationDevice(nn::hid::VibrationDeviceHandle)
|
||||
public ResultCode ActivateVibrationDevice(ServiceCtx context)
|
||||
{
|
||||
int vibrationDeviceHandle = context.RequestData.ReadInt32();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
|
||||
{
|
||||
class IAppletResource : IpcService
|
||||
{
|
||||
private KSharedMemory _hidSharedMem;
|
||||
private int _hidSharedMemHandle;
|
||||
|
||||
public IAppletResource(KSharedMemory hidSharedMem)
|
||||
{
|
||||
_hidSharedMem = hidSharedMem;
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetSharedMemoryHandle() -> handle<copy>
|
||||
public ResultCode GetSharedMemoryHandle(ServiceCtx context)
|
||||
{
|
||||
if (_hidSharedMemHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_hidSharedMem, out _hidSharedMemHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_hidSharedMemHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum NpadHandheldActivationMode
|
||||
{
|
||||
Dual,
|
||||
Single,
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum NpadJoyDeviceType
|
||||
{
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct AccelerometerParameters
|
||||
{
|
||||
public float X;
|
||||
public float Y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum GyroscopeZeroDriftMode
|
||||
{
|
||||
Loose,
|
||||
Standard,
|
||||
Tight
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct SensorFusionParameters
|
||||
{
|
||||
public float RevisePower;
|
||||
public float ReviseRange;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct VibrationDeviceHandle
|
||||
{
|
||||
public byte DeviceType;
|
||||
public byte PlayerId;
|
||||
public byte Position;
|
||||
public byte Reserved;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum VibrationDevicePosition
|
||||
{
|
||||
None,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum VibrationDeviceType
|
||||
{
|
||||
None,
|
||||
LinearResonantActuator,
|
||||
GcErm
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct VibrationDeviceValue
|
||||
{
|
||||
public VibrationDeviceType DeviceType;
|
||||
public VibrationDevicePosition Position;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct VibrationValue
|
||||
{
|
||||
public float AmplitudeLow;
|
||||
public float FrequencyLow;
|
||||
public float AmplitudeHigh;
|
||||
public float FrequencyHigh;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is VibrationValue value &&
|
||||
AmplitudeLow == value.AmplitudeLow &&
|
||||
AmplitudeHigh == value.AmplitudeHigh;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(AmplitudeLow, AmplitudeHigh);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs
Normal file
8
src/Ryujinx.HLE/HOS/Services/Hid/IHidDebugServer.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
[Service("hid:dbg")]
|
||||
class IHidDebugServer : IpcService
|
||||
{
|
||||
public IHidDebugServer(ServiceCtx context) { }
|
||||
}
|
||||
}
|
||||
1800
src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
Normal file
1800
src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
Normal file
File diff suppressed because it is too large
Load Diff
76
src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs
Normal file
76
src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
[Service("hid:sys")]
|
||||
class IHidSystemServer : IpcService
|
||||
{
|
||||
public IHidSystemServer(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(303)]
|
||||
// ApplyNpadSystemCommonPolicy(u64)
|
||||
public ResultCode ApplyNpadSystemCommonPolicy(ServiceCtx context)
|
||||
{
|
||||
ulong commonPolicy = context.RequestData.ReadUInt64();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { commonPolicy });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(306)]
|
||||
// GetLastActiveNpad(u32) -> u8, u8
|
||||
public ResultCode GetLastActiveNpad(ServiceCtx context)
|
||||
{
|
||||
// TODO: RequestData seems to have garbage data, reading an extra uint seems to fix the issue.
|
||||
context.RequestData.ReadUInt32();
|
||||
|
||||
ResultCode resultCode = GetAppletFooterUiTypeImpl(context, out AppletFooterUiType appletFooterUiType);
|
||||
|
||||
context.ResponseData.Write((byte)appletFooterUiType);
|
||||
context.ResponseData.Write((byte)0);
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
[CommandCmif(307)]
|
||||
// GetNpadSystemExtStyle() -> u64
|
||||
public ResultCode GetNpadSystemExtStyle(ServiceCtx context)
|
||||
{
|
||||
foreach (PlayerIndex playerIndex in context.Device.Hid.Npads.GetSupportedPlayers())
|
||||
{
|
||||
if (HidUtils.GetNpadIdTypeFromIndex(playerIndex) > NpadIdType.Handheld)
|
||||
{
|
||||
return ResultCode.InvalidNpadIdType;
|
||||
}
|
||||
}
|
||||
|
||||
context.ResponseData.Write((ulong)context.Device.Hid.Npads.SupportedStyleSets);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(314)] // 9.0.0+
|
||||
// GetAppletFooterUiType(u32) -> u8
|
||||
public ResultCode GetAppletFooterUiType(ServiceCtx context)
|
||||
{
|
||||
ResultCode resultCode = GetAppletFooterUiTypeImpl(context, out AppletFooterUiType appletFooterUiType);
|
||||
|
||||
context.ResponseData.Write((byte)appletFooterUiType);
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
private ResultCode GetAppletFooterUiTypeImpl(ServiceCtx context, out AppletFooterUiType appletFooterUiType)
|
||||
{
|
||||
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||
PlayerIndex playerIndex = HidUtils.GetIndexFromNpadIdType(npadIdType);
|
||||
|
||||
appletFooterUiType = context.Device.Hid.SharedMemory.Npads[(int)playerIndex].InternalState.AppletFooterUiType;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs
Normal file
8
src/Ryujinx.HLE/HOS/Services/Hid/IHidbusServer.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
[Service("hidbus")]
|
||||
class IHidbusServer : IpcService
|
||||
{
|
||||
public IHidbusServer(ServiceCtx context) { }
|
||||
}
|
||||
}
|
||||
8
src/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs
Normal file
8
src/Ryujinx.HLE/HOS/Services/Hid/ISystemServer.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
[Service("xcd:sys")]
|
||||
class ISystemServer : IpcService
|
||||
{
|
||||
public ISystemServer(ServiceCtx context) { }
|
||||
}
|
||||
}
|
||||
240
src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs
Normal file
240
src/Ryujinx.HLE/HOS/Services/Hid/Irs/IIrSensorServer.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Irs.Types;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs
|
||||
{
|
||||
[Service("irs")]
|
||||
class IIrSensorServer : IpcService
|
||||
{
|
||||
private int _irsensorSharedMemoryHandle = 0;
|
||||
|
||||
public IIrSensorServer(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(302)]
|
||||
// ActivateIrsensor(nn::applet::AppletResourceUserId, pid)
|
||||
public ResultCode ActivateIrsensor(ServiceCtx context)
|
||||
{
|
||||
ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
|
||||
// NOTE: This seems to initialize the shared memory for irs service.
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(303)]
|
||||
// DeactivateIrsensor(nn::applet::AppletResourceUserId, pid)
|
||||
public ResultCode DeactivateIrsensor(ServiceCtx context)
|
||||
{
|
||||
ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
|
||||
// NOTE: This seems to deinitialize the shared memory for irs service.
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(304)]
|
||||
// GetIrsensorSharedMemoryHandle(nn::applet::AppletResourceUserId, pid) -> handle<copy>
|
||||
public ResultCode GetIrsensorSharedMemoryHandle(ServiceCtx context)
|
||||
{
|
||||
// NOTE: Shared memory should use the appletResourceUserId.
|
||||
// ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
|
||||
if (_irsensorSharedMemoryHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(context.Device.System.IirsSharedMem, out _irsensorSharedMemoryHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_irsensorSharedMemoryHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(305)]
|
||||
// StopImageProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId)
|
||||
public ResultCode StopImageProcessor(ServiceCtx context)
|
||||
{
|
||||
IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>();
|
||||
ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
|
||||
CheckCameraHandle(irCameraHandle);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle.PlayerNumber, irCameraHandle.DeviceType });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(306)]
|
||||
// RunMomentProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedMomentProcessorConfig)
|
||||
public ResultCode RunMomentProcessor(ServiceCtx context)
|
||||
{
|
||||
IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>();
|
||||
ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
var packedMomentProcessorConfig = context.RequestData.ReadStruct<PackedMomentProcessorConfig>();
|
||||
|
||||
CheckCameraHandle(irCameraHandle);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle.PlayerNumber, irCameraHandle.DeviceType, packedMomentProcessorConfig.ExposureTime });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(307)]
|
||||
// RunClusteringProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedClusteringProcessorConfig)
|
||||
public ResultCode RunClusteringProcessor(ServiceCtx context)
|
||||
{
|
||||
IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>();
|
||||
ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
var packedClusteringProcessorConfig = context.RequestData.ReadStruct<PackedClusteringProcessorConfig>();
|
||||
|
||||
CheckCameraHandle(irCameraHandle);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle.PlayerNumber, irCameraHandle.DeviceType, packedClusteringProcessorConfig.ExposureTime });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(308)]
|
||||
// RunImageTransferProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedImageTransferProcessorConfig, u64 TransferMemorySize, TransferMemoryHandle)
|
||||
public ResultCode RunImageTransferProcessor(ServiceCtx context)
|
||||
{
|
||||
IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>();
|
||||
ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
var packedImageTransferProcessorConfig = context.RequestData.ReadStruct<PackedImageTransferProcessorConfig>();
|
||||
|
||||
CheckCameraHandle(irCameraHandle);
|
||||
|
||||
// TODO: Handle the Transfer Memory.
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle.PlayerNumber, irCameraHandle.DeviceType, packedImageTransferProcessorConfig.ExposureTime });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(309)]
|
||||
// GetImageTransferProcessorState(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId)
|
||||
public ResultCode GetImageTransferProcessorState(ServiceCtx context)
|
||||
{
|
||||
IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>();
|
||||
ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
|
||||
// ulong imageTransferBufferAddress = context.Request.ReceiveBuff[0].Position;
|
||||
ulong imageTransferBufferSize = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
if (imageTransferBufferSize == 0)
|
||||
{
|
||||
return ResultCode.InvalidBufferSize;
|
||||
}
|
||||
|
||||
CheckCameraHandle(irCameraHandle);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle.PlayerNumber, irCameraHandle.DeviceType });
|
||||
|
||||
// TODO: Uses the buffer to copy the JoyCon IR data (by using a JoyCon driver) and update the following struct.
|
||||
context.ResponseData.WriteStruct(new ImageTransferProcessorState()
|
||||
{
|
||||
SamplingNumber = 0,
|
||||
AmbientNoiseLevel = 0
|
||||
});
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(310)]
|
||||
// RunTeraPluginProcessor(pid, nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, PackedTeraPluginProcessorConfig)
|
||||
public ResultCode RunTeraPluginProcessor(ServiceCtx context)
|
||||
{
|
||||
IrCameraHandle irCameraHandle = context.RequestData.ReadStruct<IrCameraHandle>();
|
||||
ulong appletResourceUserId = context.RequestData.ReadUInt64();
|
||||
var packedTeraPluginProcessorConfig = context.RequestData.ReadStruct<PackedTeraPluginProcessorConfig>();
|
||||
|
||||
CheckCameraHandle(irCameraHandle);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle.PlayerNumber, irCameraHandle.DeviceType, packedTeraPluginProcessorConfig.RequiredMcuVersion });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(311)]
|
||||
// GetNpadIrCameraHandle(u32) -> nn::irsensor::IrCameraHandle
|
||||
public ResultCode GetNpadIrCameraHandle(ServiceCtx context)
|
||||
{
|
||||
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||
|
||||
if (npadIdType > NpadIdType.Player8 &&
|
||||
npadIdType != NpadIdType.Unknown &&
|
||||
npadIdType != NpadIdType.Handheld)
|
||||
{
|
||||
return ResultCode.NpadIdOutOfRange;
|
||||
}
|
||||
|
||||
PlayerIndex irCameraHandle = HidUtils.GetIndexFromNpadIdType(npadIdType);
|
||||
|
||||
context.ResponseData.Write((int)irCameraHandle);
|
||||
|
||||
// NOTE: If the irCameraHandle pointer is null this error is returned, Doesn't occur in our case.
|
||||
// return ResultCode.HandlePointerIsNull;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(314)] // 3.0.0+
|
||||
// CheckFirmwareVersion(nn::irsensor::IrCameraHandle, nn::irsensor::PackedMcuVersion, nn::applet::AppletResourceUserId, pid)
|
||||
public ResultCode CheckFirmwareVersion(ServiceCtx context)
|
||||
{
|
||||
int irCameraHandle = context.RequestData.ReadInt32();
|
||||
short packedMcuVersionMajor = context.RequestData.ReadInt16();
|
||||
short packedMcuVersionMinor = context.RequestData.ReadInt16();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle, packedMcuVersionMajor, packedMcuVersionMinor });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(318)] // 4.0.0+
|
||||
// StopImageProcessorAsync(nn::irsensor::IrCameraHandle, nn::applet::AppletResourceUserId, pid)
|
||||
public ResultCode StopImageProcessorAsync(ServiceCtx context)
|
||||
{
|
||||
int irCameraHandle = context.RequestData.ReadInt32();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, irCameraHandle });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(319)] // 4.0.0+
|
||||
// ActivateIrsensorWithFunctionLevel(nn::applet::AppletResourceUserId, nn::irsensor::PackedFunctionLevel, pid)
|
||||
public ResultCode ActivateIrsensorWithFunctionLevel(ServiceCtx context)
|
||||
{
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
long packedFunctionLevel = context.RequestData.ReadInt64();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceIrs, new { appletResourceUserId, packedFunctionLevel });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private ResultCode CheckCameraHandle(IrCameraHandle irCameraHandle)
|
||||
{
|
||||
if (irCameraHandle.DeviceType == 1 || (PlayerIndex)irCameraHandle.PlayerNumber >= PlayerIndex.Unknown)
|
||||
{
|
||||
return ResultCode.InvalidCameraHandle;
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs
|
||||
{
|
||||
[Service("irs:sys")]
|
||||
class IIrSensorSystemServer : IpcService
|
||||
{
|
||||
public IIrSensorSystemServer(ServiceCtx context) { }
|
||||
}
|
||||
}
|
||||
15
src/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs
Normal file
15
src/Ryujinx.HLE/HOS/Services/Hid/Irs/ResultCode.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs
|
||||
{
|
||||
public enum ResultCode
|
||||
{
|
||||
ModuleId = 205,
|
||||
ErrorCodeShift = 9,
|
||||
|
||||
Success = 0,
|
||||
|
||||
InvalidCameraHandle = (204 << ErrorCodeShift) | ModuleId,
|
||||
InvalidBufferSize = (207 << ErrorCodeShift) | ModuleId,
|
||||
HandlePointerIsNull = (212 << ErrorCodeShift) | ModuleId,
|
||||
NpadIdOutOfRange = (709 << ErrorCodeShift) | ModuleId
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||
struct ImageTransferProcessorState
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public uint AmbientNoiseLevel;
|
||||
public uint Reserved;
|
||||
}
|
||||
}
|
||||
12
src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs
Normal file
12
src/Ryujinx.HLE/HOS/Services/Hid/Irs/Types/IrCameraHandle.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x4)]
|
||||
struct IrCameraHandle
|
||||
{
|
||||
public byte PlayerNumber;
|
||||
public byte DeviceType;
|
||||
public ushort Reserved;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x28)]
|
||||
struct PackedClusteringProcessorConfig
|
||||
{
|
||||
public long ExposureTime;
|
||||
public byte LightTarget;
|
||||
public byte Gain;
|
||||
public byte IsNegativeImageUsed;
|
||||
public byte Reserved1;
|
||||
public uint Reserved2;
|
||||
public ushort WindowOfInterestX;
|
||||
public ushort WindowOfInterestY;
|
||||
public ushort WindowOfInterestWidth;
|
||||
public ushort WindowOfInterestHeight;
|
||||
public uint RequiredMcuVersion;
|
||||
public uint ObjectPixelCountMin;
|
||||
public uint ObjectPixelCountMax;
|
||||
public byte ObjectIntensityMin;
|
||||
public byte IsExternalLightFilterEnabled;
|
||||
public ushort Reserved3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x18)]
|
||||
struct PackedImageTransferProcessorConfig
|
||||
{
|
||||
public long ExposureTime;
|
||||
public byte LightTarget;
|
||||
public byte Gain;
|
||||
public byte IsNegativeImageUsed;
|
||||
public byte Reserved1;
|
||||
public uint Reserved2;
|
||||
public uint RequiredMcuVersion;
|
||||
public byte Format;
|
||||
public byte Reserved3;
|
||||
public ushort Reserved4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
struct PackedMomentProcessorConfig
|
||||
{
|
||||
public long ExposureTime;
|
||||
public byte LightTarget;
|
||||
public byte Gain;
|
||||
public byte IsNegativeImageUsed;
|
||||
public byte Reserved1;
|
||||
public uint Reserved2;
|
||||
public ushort WindowOfInterestX;
|
||||
public ushort WindowOfInterestY;
|
||||
public ushort WindowOfInterestWidth;
|
||||
public ushort WindowOfInterestHeight;
|
||||
public uint RequiredMcuVersion;
|
||||
public byte Preprocess;
|
||||
public byte PreprocessIntensityThreshold;
|
||||
public ushort Reserved3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Irs.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x8)]
|
||||
struct PackedTeraPluginProcessorConfig
|
||||
{
|
||||
public uint RequiredMcuVersion;
|
||||
public byte Mode;
|
||||
public byte Unknown1;
|
||||
public byte Unknown2;
|
||||
public byte Unknown3;
|
||||
}
|
||||
}
|
||||
15
src/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs
Normal file
15
src/Ryujinx.HLE/HOS/Services/Hid/ResultCode.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
enum ResultCode
|
||||
{
|
||||
ModuleId = 202,
|
||||
ErrorCodeShift = 9,
|
||||
|
||||
Success = 0,
|
||||
|
||||
InvalidNpadDeviceType = (122 << ErrorCodeShift) | ModuleId,
|
||||
InvalidNpadIdType = (123 << ErrorCodeShift) | ModuleId,
|
||||
InvalidDeviceIndex = (124 << ErrorCodeShift) | ModuleId,
|
||||
InvalidBufferSize = (131 << ErrorCodeShift) | ModuleId
|
||||
}
|
||||
}
|
||||
30
src/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs
Normal file
30
src/Ryujinx.HLE/HOS/Services/Hid/Types/AppletFooterUiType.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types
|
||||
{
|
||||
[Flags]
|
||||
enum AppletFooterUiType : byte
|
||||
{
|
||||
None,
|
||||
HandheldNone,
|
||||
HandheldJoyConLeftOnly,
|
||||
HandheldJoyConRightOnly,
|
||||
HandheldJoyConLeftJoyConRight,
|
||||
JoyDual,
|
||||
JoyDualLeftOnly,
|
||||
JoyDualRightOnly,
|
||||
JoyLeftHorizontal,
|
||||
JoyLeftVertical,
|
||||
JoyRightHorizontal,
|
||||
JoyRightVertical,
|
||||
SwitchProController,
|
||||
CompatibleProController,
|
||||
CompatibleJoyCon,
|
||||
LarkHvc1,
|
||||
LarkHvc2,
|
||||
LarkNesLeft,
|
||||
LarkNesRight,
|
||||
Lucia,
|
||||
Verification
|
||||
}
|
||||
}
|
||||
9
src/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs
Normal file
9
src/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types
|
||||
{
|
||||
struct HidVector
|
||||
{
|
||||
public float X;
|
||||
public float Y;
|
||||
public float Z;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
[Flags]
|
||||
public enum ControllerKeys : long
|
||||
{
|
||||
A = 1 << 0,
|
||||
B = 1 << 1,
|
||||
X = 1 << 2,
|
||||
Y = 1 << 3,
|
||||
LStick = 1 << 4,
|
||||
RStick = 1 << 5,
|
||||
L = 1 << 6,
|
||||
R = 1 << 7,
|
||||
Zl = 1 << 8,
|
||||
Zr = 1 << 9,
|
||||
Plus = 1 << 10,
|
||||
Minus = 1 << 11,
|
||||
DpadLeft = 1 << 12,
|
||||
DpadUp = 1 << 13,
|
||||
DpadRight = 1 << 14,
|
||||
DpadDown = 1 << 15,
|
||||
LStickLeft = 1 << 16,
|
||||
LStickUp = 1 << 17,
|
||||
LStickRight = 1 << 18,
|
||||
LStickDown = 1 << 19,
|
||||
RStickLeft = 1 << 20,
|
||||
RStickUp = 1 << 21,
|
||||
RStickRight = 1 << 22,
|
||||
RStickDown = 1 << 23,
|
||||
SlLeft = 1 << 24,
|
||||
SrLeft = 1 << 25,
|
||||
SlRight = 1 << 26,
|
||||
SrRight = 1 << 27,
|
||||
|
||||
// Generic Catch-all
|
||||
Up = DpadUp | LStickUp | RStickUp,
|
||||
Down = DpadDown | LStickDown | RStickDown,
|
||||
Left = DpadLeft | LStickLeft | RStickLeft,
|
||||
Right = DpadRight | LStickRight | RStickRight,
|
||||
Sl = SlLeft | SlRight,
|
||||
Sr = SrLeft | SrRight
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
[Flags]
|
||||
public enum ControllerType : int
|
||||
{
|
||||
None,
|
||||
ProController = 1 << 0,
|
||||
Handheld = 1 << 1,
|
||||
JoyconPair = 1 << 2,
|
||||
JoyconLeft = 1 << 3,
|
||||
JoyconRight = 1 << 4,
|
||||
Invalid = 1 << 5,
|
||||
Pokeball = 1 << 6,
|
||||
SystemExternal = 1 << 29,
|
||||
System = 1 << 30
|
||||
}
|
||||
}
|
||||
37
src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs
Normal file
37
src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum NpadColor : uint
|
||||
{
|
||||
BodyGray = 0x828282,
|
||||
BodyNeonRed = 0xFF3C28,
|
||||
BodyNeonBlue = 0x0AB9E6,
|
||||
BodyNeonYellow = 0xE6FF00,
|
||||
BodyNeonGreen = 0x1EDC00,
|
||||
BodyNeonPink = 0xFF3278,
|
||||
BodyRed = 0xE10F00,
|
||||
BodyBlue = 0x4655F5,
|
||||
BodyNeonPurple = 0xB400E6,
|
||||
BodyNeonOrange = 0xFAA005,
|
||||
BodyPokemonLetsGoPikachu = 0xFFDC00,
|
||||
BodyPokemonLetsGoEevee = 0xC88C32,
|
||||
BodyNintendoLaboCreatorsContestEdition = 0xD7AA73,
|
||||
BodyAnimalCrossingSpecialEditionLeftJoyCon = 0x82FF96,
|
||||
BodyAnimalCrossingSpecialEditionRightJoyCon = 0x96F5F5,
|
||||
|
||||
ButtonGray = 0x0F0F0F,
|
||||
ButtonNeonRed = 0x1E0A0A,
|
||||
ButtonNeonBlue = 0x001E1E,
|
||||
ButtonNeonYellow = 0x142800,
|
||||
ButtonNeonGreen = 0x002800,
|
||||
ButtonNeonPink = 0x28001E,
|
||||
ButtonRed = 0x280A0A,
|
||||
ButtonBlue = 0x00000A,
|
||||
ButtonNeonPurple = 0x140014,
|
||||
ButtonNeonOrange = 0x0F0A00,
|
||||
ButtonPokemonLetsGoPikachu = 0x322800,
|
||||
ButtonPokemonLetsGoEevee = 0x281900,
|
||||
ButtonNintendoLaboCreatorsContestEdition = 0x1E1914,
|
||||
ButtonAnimalCrossingSpecialEditionLeftJoyCon = 0x0A1E0A,
|
||||
ButtonAnimalCrossingSpecialEditionRightJoyCon = 0x0A1E28
|
||||
}
|
||||
}
|
||||
16
src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs
Normal file
16
src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum NpadIdType : int
|
||||
{
|
||||
Player1 = 0,
|
||||
Player2 = 1,
|
||||
Player3 = 2,
|
||||
Player4 = 3,
|
||||
Player5 = 4,
|
||||
Player6 = 5,
|
||||
Player7 = 6,
|
||||
Player8 = 7,
|
||||
Unknown = 16,
|
||||
Handheld = 32
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum NpadStyleIndex : byte
|
||||
{
|
||||
FullKey = 3,
|
||||
Handheld = 4,
|
||||
JoyDual = 5,
|
||||
JoyLeft = 6,
|
||||
JoyRight = 7,
|
||||
SystemExt = 32,
|
||||
System = 33
|
||||
}
|
||||
}
|
||||
17
src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs
Normal file
17
src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/PlayerIndex.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum PlayerIndex : int
|
||||
{
|
||||
Player1 = 0,
|
||||
Player2 = 1,
|
||||
Player3 = 2,
|
||||
Player4 = 3,
|
||||
Player5 = 4,
|
||||
Player6 = 5,
|
||||
Player7 = 6,
|
||||
Player8 = 7,
|
||||
Handheld = 8,
|
||||
Unknown = 9,
|
||||
Auto = 10 // Shouldn't be used directly
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types
|
||||
{
|
||||
enum NpadJoyHoldType
|
||||
{
|
||||
Vertical,
|
||||
Horizontal
|
||||
}
|
||||
}
|
||||
@@ -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, ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public T Object;
|
||||
|
||||
public ulong ReadSamplingNumberAtomic()
|
||||
{
|
||||
return Interlocked.Read(ref SamplingNumber);
|
||||
}
|
||||
|
||||
public void SetObject(ref T obj)
|
||||
{
|
||||
ulong samplingNumber = ISampledDataStruct.GetSamplingNumber(ref obj);
|
||||
|
||||
Interlocked.Exchange(ref SamplingNumber, samplingNumber);
|
||||
|
||||
Thread.MemoryBarrier();
|
||||
|
||||
Object = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a "marker interface" to add some compile-time safety to a convention-based optimization.
|
||||
///
|
||||
/// Any struct implementing this interface should:
|
||||
/// - use <c>StructLayoutAttribute</c> (and related attributes) to explicity control how the struct is laid out in memory.
|
||||
/// - ensure that the method <c>ISampledDataStruct.GetSamplingNumberFieldOffset()</c> correctly returns the offset, in bytes,
|
||||
/// to the ulong "Sampling Number" field within the struct. Most types have it as the first field, so the default offset is 0.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// <c>
|
||||
/// [StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
/// struct DebugPadState : ISampledDataStruct
|
||||
/// {
|
||||
/// public ulong SamplingNumber; // 1st field, so no need to add special handling to GetSamplingNumberFieldOffset()
|
||||
/// // other members...
|
||||
/// }
|
||||
///
|
||||
/// [StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
/// struct SixAxisSensorState : ISampledDataStruct
|
||||
/// {
|
||||
/// public ulong DeltaTime;
|
||||
/// public ulong SamplingNumber; // Not the first field - needs special handling in GetSamplingNumberFieldOffset()
|
||||
/// // other members...
|
||||
/// }
|
||||
/// </c>
|
||||
/// </summary>
|
||||
internal interface ISampledDataStruct
|
||||
{
|
||||
// No Instance Members - marker interface only
|
||||
|
||||
public static ulong GetSamplingNumber<T>(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct
|
||||
{
|
||||
ReadOnlySpan<T> structSpan = MemoryMarshal.CreateReadOnlySpan(ref sampledDataStruct, 1);
|
||||
|
||||
ReadOnlySpan<byte> byteSpan = MemoryMarshal.Cast<T, byte>(structSpan);
|
||||
|
||||
int fieldOffset = GetSamplingNumberFieldOffset(ref sampledDataStruct);
|
||||
|
||||
if (fieldOffset > 0)
|
||||
{
|
||||
byteSpan = byteSpan.Slice(fieldOffset);
|
||||
}
|
||||
|
||||
ulong value = BinaryPrimitives.ReadUInt64LittleEndian(byteSpan);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static int GetSamplingNumberFieldOffset<T>(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct
|
||||
{
|
||||
return sampledDataStruct switch
|
||||
{
|
||||
Npad.SixAxisSensorState _ => sizeof(ulong),
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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, ISampledDataStruct
|
||||
{
|
||||
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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct DebugPadState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public DebugPadAttribute Attributes;
|
||||
public DebugPadButton Buttons;
|
||||
public AnalogStickState AnalogStickR;
|
||||
public AnalogStickState AnalogStickL;
|
||||
}
|
||||
}
|
||||
@@ -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,20 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
|
||||
{
|
||||
[Flags]
|
||||
enum KeyboardModifier : ulong
|
||||
{
|
||||
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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct KeyboardState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public KeyboardModifier Modifiers;
|
||||
public KeyboardKey Keys;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct MouseState : ISampledDataStruct
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct NpadCommonState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public NpadButton Buttons;
|
||||
public AnalogStickState AnalogStickL;
|
||||
public AnalogStickState AnalogStickR;
|
||||
public NpadAttribute Attributes;
|
||||
private uint _reserved;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct NpadGcTriggerState : ISampledDataStruct
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public ulong SamplingNumber;
|
||||
public uint TriggerL;
|
||||
public uint TriggerR;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
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 AppletFooterUiType AppletFooterUiType;
|
||||
private Reserved2Struct _reserved2;
|
||||
public RingLifo<NpadGcTriggerState> GcTrigger;
|
||||
public NpadLarkType LarkTypeLeftAndMain;
|
||||
public NpadLarkType LarkTypeRight;
|
||||
public NpadLuciaType LuciaType;
|
||||
public uint Unknown43EC;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 123, Pack = 1)]
|
||||
private struct Reserved2Struct {}
|
||||
|
||||
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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct SixAxisSensorState : ISampledDataStruct
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
public 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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct TouchScreenState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public int TouchesCount;
|
||||
private int _reserved;
|
||||
public Array16<TouchState> Touches;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user