aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Input/HLE/NpadController.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Input/HLE/NpadController.cs')
-rw-r--r--src/Ryujinx.Input/HLE/NpadController.cs569
1 files changed, 569 insertions, 0 deletions
diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs
new file mode 100644
index 00000000..46c0fc33
--- /dev/null
+++ b/src/Ryujinx.Input/HLE/NpadController.cs
@@ -0,0 +1,569 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Configuration.Hid.Controller;
+using Ryujinx.Common.Configuration.Hid.Controller.Motion;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Hid;
+using System;
+using System.Collections.Concurrent;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client;
+using ConfigControllerType = Ryujinx.Common.Configuration.Hid.ControllerType;
+
+namespace Ryujinx.Input.HLE
+{
+ public class NpadController : IDisposable
+ {
+ private class HLEButtonMappingEntry
+ {
+ public readonly GamepadButtonInputId DriverInputId;
+ public readonly ControllerKeys HLEInput;
+
+ public HLEButtonMappingEntry(GamepadButtonInputId driverInputId, ControllerKeys hleInput)
+ {
+ DriverInputId = driverInputId;
+ HLEInput = hleInput;
+ }
+ }
+
+ private static readonly HLEButtonMappingEntry[] _hleButtonMapping = new HLEButtonMappingEntry[]
+ {
+ new HLEButtonMappingEntry(GamepadButtonInputId.A, ControllerKeys.A),
+ new HLEButtonMappingEntry(GamepadButtonInputId.B, ControllerKeys.B),
+ new HLEButtonMappingEntry(GamepadButtonInputId.X, ControllerKeys.X),
+ new HLEButtonMappingEntry(GamepadButtonInputId.Y, ControllerKeys.Y),
+ new HLEButtonMappingEntry(GamepadButtonInputId.LeftStick, ControllerKeys.LStick),
+ new HLEButtonMappingEntry(GamepadButtonInputId.RightStick, ControllerKeys.RStick),
+ new HLEButtonMappingEntry(GamepadButtonInputId.LeftShoulder, ControllerKeys.L),
+ new HLEButtonMappingEntry(GamepadButtonInputId.RightShoulder, ControllerKeys.R),
+ new HLEButtonMappingEntry(GamepadButtonInputId.LeftTrigger, ControllerKeys.Zl),
+ new HLEButtonMappingEntry(GamepadButtonInputId.RightTrigger, ControllerKeys.Zr),
+ new HLEButtonMappingEntry(GamepadButtonInputId.DpadUp, ControllerKeys.DpadUp),
+ new HLEButtonMappingEntry(GamepadButtonInputId.DpadDown, ControllerKeys.DpadDown),
+ new HLEButtonMappingEntry(GamepadButtonInputId.DpadLeft, ControllerKeys.DpadLeft),
+ new HLEButtonMappingEntry(GamepadButtonInputId.DpadRight, ControllerKeys.DpadRight),
+ new HLEButtonMappingEntry(GamepadButtonInputId.Minus, ControllerKeys.Minus),
+ new HLEButtonMappingEntry(GamepadButtonInputId.Plus, ControllerKeys.Plus),
+
+ new HLEButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, ControllerKeys.SlLeft),
+ new HLEButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, ControllerKeys.SrLeft),
+ new HLEButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, ControllerKeys.SlRight),
+ new HLEButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, ControllerKeys.SrRight),
+ };
+
+ private class HLEKeyboardMappingEntry
+ {
+ public readonly Key TargetKey;
+ public readonly byte Target;
+
+ public HLEKeyboardMappingEntry(Key targetKey, byte target)
+ {
+ TargetKey = targetKey;
+ Target = target;
+ }
+ }
+
+ private static readonly HLEKeyboardMappingEntry[] KeyMapping = new HLEKeyboardMappingEntry[]
+ {
+ new HLEKeyboardMappingEntry(Key.A, 0x4),
+ new HLEKeyboardMappingEntry(Key.B, 0x5),
+ new HLEKeyboardMappingEntry(Key.C, 0x6),
+ new HLEKeyboardMappingEntry(Key.D, 0x7),
+ new HLEKeyboardMappingEntry(Key.E, 0x8),
+ new HLEKeyboardMappingEntry(Key.F, 0x9),
+ new HLEKeyboardMappingEntry(Key.G, 0xA),
+ new HLEKeyboardMappingEntry(Key.H, 0xB),
+ new HLEKeyboardMappingEntry(Key.I, 0xC),
+ new HLEKeyboardMappingEntry(Key.J, 0xD),
+ new HLEKeyboardMappingEntry(Key.K, 0xE),
+ new HLEKeyboardMappingEntry(Key.L, 0xF),
+ new HLEKeyboardMappingEntry(Key.M, 0x10),
+ new HLEKeyboardMappingEntry(Key.N, 0x11),
+ new HLEKeyboardMappingEntry(Key.O, 0x12),
+ new HLEKeyboardMappingEntry(Key.P, 0x13),
+ new HLEKeyboardMappingEntry(Key.Q, 0x14),
+ new HLEKeyboardMappingEntry(Key.R, 0x15),
+ new HLEKeyboardMappingEntry(Key.S, 0x16),
+ new HLEKeyboardMappingEntry(Key.T, 0x17),
+ new HLEKeyboardMappingEntry(Key.U, 0x18),
+ new HLEKeyboardMappingEntry(Key.V, 0x19),
+ new HLEKeyboardMappingEntry(Key.W, 0x1A),
+ new HLEKeyboardMappingEntry(Key.X, 0x1B),
+ new HLEKeyboardMappingEntry(Key.Y, 0x1C),
+ new HLEKeyboardMappingEntry(Key.Z, 0x1D),
+
+ new HLEKeyboardMappingEntry(Key.Number1, 0x1E),
+ new HLEKeyboardMappingEntry(Key.Number2, 0x1F),
+ new HLEKeyboardMappingEntry(Key.Number3, 0x20),
+ new HLEKeyboardMappingEntry(Key.Number4, 0x21),
+ new HLEKeyboardMappingEntry(Key.Number5, 0x22),
+ new HLEKeyboardMappingEntry(Key.Number6, 0x23),
+ new HLEKeyboardMappingEntry(Key.Number7, 0x24),
+ new HLEKeyboardMappingEntry(Key.Number8, 0x25),
+ new HLEKeyboardMappingEntry(Key.Number9, 0x26),
+ new HLEKeyboardMappingEntry(Key.Number0, 0x27),
+
+ new HLEKeyboardMappingEntry(Key.Enter, 0x28),
+ new HLEKeyboardMappingEntry(Key.Escape, 0x29),
+ new HLEKeyboardMappingEntry(Key.BackSpace, 0x2A),
+ new HLEKeyboardMappingEntry(Key.Tab, 0x2B),
+ new HLEKeyboardMappingEntry(Key.Space, 0x2C),
+ new HLEKeyboardMappingEntry(Key.Minus, 0x2D),
+ new HLEKeyboardMappingEntry(Key.Plus, 0x2E),
+ new HLEKeyboardMappingEntry(Key.BracketLeft, 0x2F),
+ new HLEKeyboardMappingEntry(Key.BracketRight, 0x30),
+ new HLEKeyboardMappingEntry(Key.BackSlash, 0x31),
+ new HLEKeyboardMappingEntry(Key.Tilde, 0x32),
+ new HLEKeyboardMappingEntry(Key.Semicolon, 0x33),
+ new HLEKeyboardMappingEntry(Key.Quote, 0x34),
+ new HLEKeyboardMappingEntry(Key.Grave, 0x35),
+ new HLEKeyboardMappingEntry(Key.Comma, 0x36),
+ new HLEKeyboardMappingEntry(Key.Period, 0x37),
+ new HLEKeyboardMappingEntry(Key.Slash, 0x38),
+ new HLEKeyboardMappingEntry(Key.CapsLock, 0x39),
+
+ new HLEKeyboardMappingEntry(Key.F1, 0x3a),
+ new HLEKeyboardMappingEntry(Key.F2, 0x3b),
+ new HLEKeyboardMappingEntry(Key.F3, 0x3c),
+ new HLEKeyboardMappingEntry(Key.F4, 0x3d),
+ new HLEKeyboardMappingEntry(Key.F5, 0x3e),
+ new HLEKeyboardMappingEntry(Key.F6, 0x3f),
+ new HLEKeyboardMappingEntry(Key.F7, 0x40),
+ new HLEKeyboardMappingEntry(Key.F8, 0x41),
+ new HLEKeyboardMappingEntry(Key.F9, 0x42),
+ new HLEKeyboardMappingEntry(Key.F10, 0x43),
+ new HLEKeyboardMappingEntry(Key.F11, 0x44),
+ new HLEKeyboardMappingEntry(Key.F12, 0x45),
+
+ new HLEKeyboardMappingEntry(Key.PrintScreen, 0x46),
+ new HLEKeyboardMappingEntry(Key.ScrollLock, 0x47),
+ new HLEKeyboardMappingEntry(Key.Pause, 0x48),
+ new HLEKeyboardMappingEntry(Key.Insert, 0x49),
+ new HLEKeyboardMappingEntry(Key.Home, 0x4A),
+ new HLEKeyboardMappingEntry(Key.PageUp, 0x4B),
+ new HLEKeyboardMappingEntry(Key.Delete, 0x4C),
+ new HLEKeyboardMappingEntry(Key.End, 0x4D),
+ new HLEKeyboardMappingEntry(Key.PageDown, 0x4E),
+ new HLEKeyboardMappingEntry(Key.Right, 0x4F),
+ new HLEKeyboardMappingEntry(Key.Left, 0x50),
+ new HLEKeyboardMappingEntry(Key.Down, 0x51),
+ new HLEKeyboardMappingEntry(Key.Up, 0x52),
+
+ new HLEKeyboardMappingEntry(Key.NumLock, 0x53),
+ new HLEKeyboardMappingEntry(Key.KeypadDivide, 0x54),
+ new HLEKeyboardMappingEntry(Key.KeypadMultiply, 0x55),
+ new HLEKeyboardMappingEntry(Key.KeypadSubtract, 0x56),
+ new HLEKeyboardMappingEntry(Key.KeypadAdd, 0x57),
+ new HLEKeyboardMappingEntry(Key.KeypadEnter, 0x58),
+ new HLEKeyboardMappingEntry(Key.Keypad1, 0x59),
+ new HLEKeyboardMappingEntry(Key.Keypad2, 0x5A),
+ new HLEKeyboardMappingEntry(Key.Keypad3, 0x5B),
+ new HLEKeyboardMappingEntry(Key.Keypad4, 0x5C),
+ new HLEKeyboardMappingEntry(Key.Keypad5, 0x5D),
+ new HLEKeyboardMappingEntry(Key.Keypad6, 0x5E),
+ new HLEKeyboardMappingEntry(Key.Keypad7, 0x5F),
+ new HLEKeyboardMappingEntry(Key.Keypad8, 0x60),
+ new HLEKeyboardMappingEntry(Key.Keypad9, 0x61),
+ new HLEKeyboardMappingEntry(Key.Keypad0, 0x62),
+ new HLEKeyboardMappingEntry(Key.KeypadDecimal, 0x63),
+
+ new HLEKeyboardMappingEntry(Key.F13, 0x68),
+ new HLEKeyboardMappingEntry(Key.F14, 0x69),
+ new HLEKeyboardMappingEntry(Key.F15, 0x6A),
+ new HLEKeyboardMappingEntry(Key.F16, 0x6B),
+ new HLEKeyboardMappingEntry(Key.F17, 0x6C),
+ new HLEKeyboardMappingEntry(Key.F18, 0x6D),
+ new HLEKeyboardMappingEntry(Key.F19, 0x6E),
+ new HLEKeyboardMappingEntry(Key.F20, 0x6F),
+ new HLEKeyboardMappingEntry(Key.F21, 0x70),
+ new HLEKeyboardMappingEntry(Key.F22, 0x71),
+ new HLEKeyboardMappingEntry(Key.F23, 0x72),
+ new HLEKeyboardMappingEntry(Key.F24, 0x73),
+
+ new HLEKeyboardMappingEntry(Key.ControlLeft, 0xE0),
+ new HLEKeyboardMappingEntry(Key.ShiftLeft, 0xE1),
+ new HLEKeyboardMappingEntry(Key.AltLeft, 0xE2),
+ new HLEKeyboardMappingEntry(Key.WinLeft, 0xE3),
+ new HLEKeyboardMappingEntry(Key.ControlRight, 0xE4),
+ new HLEKeyboardMappingEntry(Key.ShiftRight, 0xE5),
+ new HLEKeyboardMappingEntry(Key.AltRight, 0xE6),
+ new HLEKeyboardMappingEntry(Key.WinRight, 0xE7),
+ };
+
+ private static readonly HLEKeyboardMappingEntry[] KeyModifierMapping = new HLEKeyboardMappingEntry[]
+ {
+ new HLEKeyboardMappingEntry(Key.ControlLeft, 0),
+ new HLEKeyboardMappingEntry(Key.ShiftLeft, 1),
+ new HLEKeyboardMappingEntry(Key.AltLeft, 2),
+ new HLEKeyboardMappingEntry(Key.WinLeft, 3),
+ new HLEKeyboardMappingEntry(Key.ControlRight, 4),
+ new HLEKeyboardMappingEntry(Key.ShiftRight, 5),
+ new HLEKeyboardMappingEntry(Key.AltRight, 6),
+ new HLEKeyboardMappingEntry(Key.WinRight, 7),
+ new HLEKeyboardMappingEntry(Key.CapsLock, 8),
+ new HLEKeyboardMappingEntry(Key.ScrollLock, 9),
+ new HLEKeyboardMappingEntry(Key.NumLock, 10),
+ };
+
+ private bool _isValid;
+ private string _id;
+
+ private MotionInput _leftMotionInput;
+ private MotionInput _rightMotionInput;
+
+ private IGamepad _gamepad;
+ private InputConfig _config;
+
+ public IGamepadDriver GamepadDriver { get; private set; }
+ public GamepadStateSnapshot State { get; private set; }
+
+ public string Id => _id;
+
+ private CemuHookClient _cemuHookClient;
+
+ public NpadController(CemuHookClient cemuHookClient)
+ {
+ State = default;
+ _id = null;
+ _isValid = false;
+ _cemuHookClient = cemuHookClient;
+ }
+
+ public bool UpdateDriverConfiguration(IGamepadDriver gamepadDriver, InputConfig config)
+ {
+ GamepadDriver = gamepadDriver;
+
+ _gamepad?.Dispose();
+
+ _id = config.Id;
+ _gamepad = GamepadDriver.GetGamepad(_id);
+ _isValid = _gamepad != null;
+
+ UpdateUserConfiguration(config);
+
+ return _isValid;
+ }
+
+ public void UpdateUserConfiguration(InputConfig config)
+ {
+ if (config is StandardControllerInputConfig controllerConfig)
+ {
+ bool needsMotionInputUpdate = _config == null || (_config is StandardControllerInputConfig oldControllerConfig &&
+ (oldControllerConfig.Motion.EnableMotion != controllerConfig.Motion.EnableMotion) &&
+ (oldControllerConfig.Motion.MotionBackend != controllerConfig.Motion.MotionBackend));
+
+ if (needsMotionInputUpdate)
+ {
+ UpdateMotionInput(controllerConfig.Motion);
+ }
+ }
+ else
+ {
+ // Non-controller doesn't have motions.
+ _leftMotionInput = null;
+ }
+
+ _config = config;
+
+ if (_isValid)
+ {
+ _gamepad.SetConfiguration(config);
+ }
+ }
+
+ private void UpdateMotionInput(MotionConfigController motionConfig)
+ {
+ if (motionConfig.MotionBackend != MotionInputBackendType.CemuHook)
+ {
+ _leftMotionInput = new MotionInput();
+ }
+ else
+ {
+ _leftMotionInput = null;
+ }
+ }
+
+ public void Update()
+ {
+ if (_isValid && GamepadDriver != null)
+ {
+ State = _gamepad.GetMappedStateSnapshot();
+
+ if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion)
+ {
+ if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
+ {
+ if (_gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion))
+ {
+ Vector3 accelerometer = _gamepad.GetMotionData(MotionInputId.Accelerometer);
+ Vector3 gyroscope = _gamepad.GetMotionData(MotionInputId.Gyroscope);
+
+ accelerometer = new Vector3(accelerometer.X, -accelerometer.Z, accelerometer.Y);
+ gyroscope = new Vector3(gyroscope.X, -gyroscope.Z, gyroscope.Y);
+
+ _leftMotionInput.Update(accelerometer, gyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
+
+ if (controllerConfig.ControllerType == ConfigControllerType.JoyconPair)
+ {
+ _rightMotionInput = _leftMotionInput;
+ }
+ }
+ }
+ else if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.CemuHook && controllerConfig.Motion is CemuHookMotionConfigController cemuControllerConfig)
+ {
+ int clientId = (int)controllerConfig.PlayerIndex;
+
+ // First of all ensure we are registered
+ _cemuHookClient.RegisterClient(clientId, cemuControllerConfig.DsuServerHost, cemuControllerConfig.DsuServerPort);
+
+ // Then request and retrieve the data
+ _cemuHookClient.RequestData(clientId, cemuControllerConfig.Slot);
+ _cemuHookClient.TryGetData(clientId, cemuControllerConfig.Slot, out _leftMotionInput);
+
+ if (controllerConfig.ControllerType == ConfigControllerType.JoyconPair)
+ {
+ if (!cemuControllerConfig.MirrorInput)
+ {
+ _cemuHookClient.RequestData(clientId, cemuControllerConfig.AltSlot);
+ _cemuHookClient.TryGetData(clientId, cemuControllerConfig.AltSlot, out _rightMotionInput);
+ }
+ else
+ {
+ _rightMotionInput = _leftMotionInput;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // Reset states
+ State = default;
+ _leftMotionInput = null;
+ }
+ }
+
+ public GamepadInput GetHLEInputState()
+ {
+ GamepadInput state = new GamepadInput();
+
+ // First update all buttons
+ foreach (HLEButtonMappingEntry entry in _hleButtonMapping)
+ {
+ if (State.IsPressed(entry.DriverInputId))
+ {
+ state.Buttons |= entry.HLEInput;
+ }
+ }
+
+ if (_gamepad is IKeyboard)
+ {
+ (float leftAxisX, float leftAxisY) = State.GetStick(StickInputId.Left);
+ (float rightAxisX, float rightAxisY) = State.GetStick(StickInputId.Right);
+
+ state.LStick = new JoystickPosition
+ {
+ Dx = ClampAxis(leftAxisX),
+ Dy = ClampAxis(leftAxisY)
+ };
+
+ state.RStick = new JoystickPosition
+ {
+ Dx = ClampAxis(rightAxisX),
+ Dy = ClampAxis(rightAxisY)
+ };
+ }
+ else if (_config is StandardControllerInputConfig controllerConfig)
+ {
+ (float leftAxisX, float leftAxisY) = State.GetStick(StickInputId.Left);
+ (float rightAxisX, float rightAxisY) = State.GetStick(StickInputId.Right);
+
+ state.LStick = ClampToCircle(ApplyDeadzone(leftAxisX, leftAxisY, controllerConfig.DeadzoneLeft), controllerConfig.RangeLeft);
+ state.RStick = ClampToCircle(ApplyDeadzone(rightAxisX, rightAxisY, controllerConfig.DeadzoneRight), controllerConfig.RangeRight);
+ }
+
+ return state;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static JoystickPosition ApplyDeadzone(float x, float y, float deadzone)
+ {
+ float magnitudeClamped = Math.Min(MathF.Sqrt(x * x + y * y), 1f);
+
+ if (magnitudeClamped <= deadzone)
+ {
+ return new JoystickPosition() {Dx = 0, Dy = 0};
+ }
+
+ return new JoystickPosition()
+ {
+ Dx = ClampAxis((x / magnitudeClamped) * ((magnitudeClamped - deadzone) / (1 - deadzone))),
+ Dy = ClampAxis((y / magnitudeClamped) * ((magnitudeClamped - deadzone) / (1 - deadzone)))
+ };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static short ClampAxis(float value)
+ {
+ if (Math.Sign(value) < 0)
+ {
+ return (short)Math.Max(value * -short.MinValue, short.MinValue);
+ }
+
+ return (short)Math.Min(value * short.MaxValue, short.MaxValue);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static JoystickPosition ClampToCircle(JoystickPosition position, float range)
+ {
+ Vector2 point = new Vector2(position.Dx, position.Dy) * range;
+
+ if (point.Length() > short.MaxValue)
+ {
+ point = point / point.Length() * short.MaxValue;
+ }
+
+ return new JoystickPosition
+ {
+ Dx = (int)point.X,
+ Dy = (int)point.Y
+ };
+ }
+
+ public SixAxisInput GetHLEMotionState(bool isJoyconRightPair = false)
+ {
+ float[] orientationForHLE = new float[9];
+ Vector3 gyroscope;
+ Vector3 accelerometer;
+ Vector3 rotation;
+
+ MotionInput motionInput = _leftMotionInput;
+
+ if (isJoyconRightPair)
+ {
+ if (_rightMotionInput == null)
+ {
+ return default;
+ }
+
+ motionInput = _rightMotionInput;
+ }
+
+ if (motionInput != null)
+ {
+ gyroscope = Truncate(motionInput.Gyroscrope * 0.0027f, 3);
+ accelerometer = Truncate(motionInput.Accelerometer, 3);
+ rotation = Truncate(motionInput.Rotation * 0.0027f, 3);
+
+ Matrix4x4 orientation = motionInput.GetOrientation();
+
+ orientationForHLE[0] = Math.Clamp(orientation.M11, -1f, 1f);
+ orientationForHLE[1] = Math.Clamp(orientation.M12, -1f, 1f);
+ orientationForHLE[2] = Math.Clamp(orientation.M13, -1f, 1f);
+ orientationForHLE[3] = Math.Clamp(orientation.M21, -1f, 1f);
+ orientationForHLE[4] = Math.Clamp(orientation.M22, -1f, 1f);
+ orientationForHLE[5] = Math.Clamp(orientation.M23, -1f, 1f);
+ orientationForHLE[6] = Math.Clamp(orientation.M31, -1f, 1f);
+ orientationForHLE[7] = Math.Clamp(orientation.M32, -1f, 1f);
+ orientationForHLE[8] = Math.Clamp(orientation.M33, -1f, 1f);
+ }
+ else
+ {
+ gyroscope = new Vector3();
+ accelerometer = new Vector3();
+ rotation = new Vector3();
+ }
+
+ return new SixAxisInput()
+ {
+ Accelerometer = accelerometer,
+ Gyroscope = gyroscope,
+ Rotation = rotation,
+ Orientation = orientationForHLE
+ };
+ }
+
+ private static Vector3 Truncate(Vector3 value, int decimals)
+ {
+ float power = MathF.Pow(10, decimals);
+
+ value.X = float.IsNegative(value.X) ? MathF.Ceiling(value.X * power) / power : MathF.Floor(value.X * power) / power;
+ value.Y = float.IsNegative(value.Y) ? MathF.Ceiling(value.Y * power) / power : MathF.Floor(value.Y * power) / power;
+ value.Z = float.IsNegative(value.Z) ? MathF.Ceiling(value.Z * power) / power : MathF.Floor(value.Z * power) / power;
+
+ return value;
+ }
+
+ public KeyboardInput? GetHLEKeyboardInput()
+ {
+ if (_gamepad is IKeyboard keyboard)
+ {
+ KeyboardStateSnapshot keyboardState = keyboard.GetKeyboardStateSnapshot();
+
+ KeyboardInput hidKeyboard = new KeyboardInput
+ {
+ Modifier = 0,
+ Keys = new ulong[0x4]
+ };
+
+ foreach (HLEKeyboardMappingEntry entry in KeyMapping)
+ {
+ ulong value = keyboardState.IsPressed(entry.TargetKey) ? 1UL : 0UL;
+
+ hidKeyboard.Keys[entry.Target / 0x40] |= (value << (entry.Target % 0x40));
+ }
+
+ foreach (HLEKeyboardMappingEntry entry in KeyModifierMapping)
+ {
+ int value = keyboardState.IsPressed(entry.TargetKey) ? 1 : 0;
+
+ hidKeyboard.Modifier |= value << entry.Target;
+ }
+
+ return hidKeyboard;
+ }
+
+ return null;
+ }
+
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _gamepad?.Dispose();
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ public void UpdateRumble(ConcurrentQueue<(VibrationValue, VibrationValue)> queue)
+ {
+ if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue))
+ {
+ if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble)
+ {
+ VibrationValue leftVibrationValue = dualVibrationValue.Item1;
+ VibrationValue rightVibrationValue = dualVibrationValue.Item2;
+
+ float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
+ float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
+
+ _gamepad.Rumble(low, high, uint.MaxValue);
+
+ Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
+ $"L.low.amp={leftVibrationValue.AmplitudeLow}, " +
+ $"L.high.amp={leftVibrationValue.AmplitudeHigh}, " +
+ $"R.low.amp={rightVibrationValue.AmplitudeLow}, " +
+ $"R.high.amp={rightVibrationValue.AmplitudeHigh} " +
+ $"--> ({low}, {high})");
+ }
+ }
+ }
+ }
+}