add basic touch and button input interface

This commit is contained in:
Emmanuel Hansen 2023-06-25 17:08:50 +00:00
parent 24b8d7c981
commit c27a12df2c
6 changed files with 794 additions and 6 deletions

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -38,6 +39,33 @@ namespace LibRyujinx.Sample
[DllImport(dll, EntryPoint = "graphics_renderer_set_vsync")] [DllImport(dll, EntryPoint = "graphics_renderer_set_vsync")]
internal extern static void SetVsyncState(bool enabled); internal extern static void SetVsyncState(bool enabled);
[DllImport(dll, EntryPoint = "input_initialize")]
internal extern static void InitializeInput(int width, int height);
[DllImport(dll, EntryPoint = "input_set_client_size")]
internal extern static void SetClientSize(int width, int height);
[DllImport(dll, EntryPoint = "input_set_touch_point")]
internal extern static void SetTouchPoint(int x, int y);
[DllImport(dll, EntryPoint = "input_release_touch_point")]
internal extern static void ReleaseTouchPoint();
[DllImport(dll, EntryPoint = "input_update")]
internal extern static void UpdateInput();
[DllImport(dll, EntryPoint = "input_set_button_pressed")]
public extern static void SetButtonPressed(GamepadButtonInputId button, IntPtr idPtr);
[DllImport(dll, EntryPoint = "input_set_button_released")]
public extern static void SetButtonReleased(GamepadButtonInputId button, IntPtr idPtr);
[DllImport(dll, EntryPoint = "input_set_stick_axis")]
public extern static void SetStickAxis(StickInputId stick, Vector2 axes, IntPtr idPtr);
[DllImport(dll, EntryPoint = "input_connect_gamepad")]
public extern static IntPtr ConnectGamepad(int index);
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -53,11 +81,13 @@ namespace LibRyujinx.Sample
public bool EnableSpirvCompilationOnVulkan = true; public bool EnableSpirvCompilationOnVulkan = true;
public bool EnableTextureRecompression = false; public bool EnableTextureRecompression = false;
public BackendThreading BackendThreading = BackendThreading.Auto; public BackendThreading BackendThreading = BackendThreading.Auto;
public AspectRatio AspectRatio = AspectRatio.Fixed16x9;
public GraphicsConfiguration() public GraphicsConfiguration()
{ {
} }
} }
public enum GraphicsBackend public enum GraphicsBackend
{ {
Vulkan, Vulkan,
@ -78,4 +108,77 @@ namespace LibRyujinx.Sample
public IntPtr VkRequiredExtensions; public IntPtr VkRequiredExtensions;
public int VkRequiredExtensionsCount; public int VkRequiredExtensionsCount;
} }
public enum AspectRatio
{
Fixed4x3,
Fixed16x9,
Fixed16x10,
Fixed21x9,
Fixed32x9,
Stretched
}
/// <summary>
/// Represent a button from a gamepad.
/// </summary>
public enum GamepadButtonInputId : byte
{
Unbound,
A,
B,
X,
Y,
LeftStick,
RightStick,
LeftShoulder,
RightShoulder,
// Likely axis
LeftTrigger,
// Likely axis
RightTrigger,
DpadUp,
DpadDown,
DpadLeft,
DpadRight,
// Special buttons
Minus,
Plus,
Back = Minus,
Start = Plus,
Guide,
Misc1,
// Xbox Elite paddle
Paddle1,
Paddle2,
Paddle3,
Paddle4,
// PS5 touchpad button
Touchpad,
// Virtual buttons for single joycon
SingleLeftTrigger0,
SingleRightTrigger0,
SingleLeftTrigger1,
SingleRightTrigger1,
Count
}
public enum StickInputId : byte
{
Unbound,
Left,
Right,
Count
}
} }

View File

@ -1,5 +1,7 @@
using LibRyujinx.Sample; using LibRyujinx.Sample;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop; using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework; using OpenTK.Windowing.GraphicsLibraryFramework;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -13,7 +15,12 @@ namespace LibRyujinx.NativeSample
public delegate IntPtr GetProcAddress(string name); public delegate IntPtr GetProcAddress(string name);
public delegate IntPtr CreateSurface(IntPtr instance); public delegate IntPtr CreateSurface(IntPtr instance);
private bool _run;
private bool _isVulkan; private bool _isVulkan;
private Vector2 _lastPosition;
private bool _mousePressed;
private nint _gamepadIdPtr;
private string? _gamepadId;
public NativeWindow(NativeWindowSettings nativeWindowSettings) : base(nativeWindowSettings) public NativeWindow(NativeWindowSettings nativeWindowSettings) : base(nativeWindowSettings)
{ {
@ -55,6 +62,7 @@ namespace LibRyujinx.NativeSample
}; };
var success = LibRyujinxInterop.InitializeGraphicsRenderer(_isVulkan ? GraphicsBackend.Vulkan : GraphicsBackend.OpenGl, nativeGraphicsInterop); var success = LibRyujinxInterop.InitializeGraphicsRenderer(_isVulkan ? GraphicsBackend.Vulkan : GraphicsBackend.OpenGl, nativeGraphicsInterop);
success = LibRyujinxInterop.InitializeDevice(); success = LibRyujinxInterop.InitializeDevice();
LibRyujinxInterop.InitializeInput(ClientSize.X, ClientSize.Y);
var path = Marshal.StringToHGlobalAnsi(gamePath); var path = Marshal.StringToHGlobalAnsi(gamePath);
var loaded = LibRyujinxInterop.LoadApplication(path); var loaded = LibRyujinxInterop.LoadApplication(path);
@ -62,19 +70,28 @@ namespace LibRyujinx.NativeSample
Marshal.FreeHGlobal(path); Marshal.FreeHGlobal(path);
} }
_gamepadIdPtr = LibRyujinxInterop.ConnectGamepad(0);
_gamepadId = Marshal.PtrToStringAnsi(_gamepadIdPtr);
if (!_isVulkan) if (!_isVulkan)
{ {
Context.MakeNoneCurrent(); Context.MakeNoneCurrent();
} }
_run = true;
var thread = new Thread(new ThreadStart(RunLoop)); var thread = new Thread(new ThreadStart(RunLoop));
thread.Start(); thread.Start();
UpdateLoop();
thread.Join(); thread.Join();
foreach(var ptr in pointers) foreach(var ptr in pointers)
{ {
Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr);
} }
Marshal.FreeHGlobal(_gamepadIdPtr);
} }
public void RunLoop() public void RunLoop()
@ -89,15 +106,17 @@ namespace LibRyujinx.NativeSample
Context.SwapInterval = 0; Context.SwapInterval = 0;
} }
Task.Run(async () => /* Task.Run(async () =>
{ {
await Task.Delay(1000); await Task.Delay(1000);
LibRyujinxInterop.SetVsyncState(false); LibRyujinxInterop.SetVsyncState(true);
}); });*/
LibRyujinxInterop.RunLoop(); LibRyujinxInterop.RunLoop();
_run = false;
if (!_isVulkan) if (!_isVulkan)
{ {
Context.MakeNoneCurrent(); Context.MakeNoneCurrent();
@ -111,5 +130,120 @@ namespace LibRyujinx.NativeSample
this.Context.SwapBuffers(); this.Context.SwapBuffers();
} }
} }
protected override void OnMouseMove(MouseMoveEventArgs e)
{
base.OnMouseMove(e);
_lastPosition = e.Position;
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
if(e.Button == MouseButton.Left)
{
_mousePressed = true;
}
}
protected override void OnResize(ResizeEventArgs e)
{
base.OnResize(e);
if (_run)
{
LibRyujinxInterop.SetRendererSize(e.Width, e.Height);
LibRyujinxInterop.SetClientSize(e.Width, e.Height);
}
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
if (e.Button == MouseButton.Left)
{
_mousePressed = false;
}
}
protected override void OnKeyUp(KeyboardKeyEventArgs e)
{
base.OnKeyUp(e);
if (_gamepadIdPtr != IntPtr.Zero)
{
var key = GetKeyMapping(e.Key);
LibRyujinxInterop.SetButtonReleased(key, _gamepadIdPtr);
}
}
protected override void OnKeyDown(KeyboardKeyEventArgs e)
{
base.OnKeyDown(e);
if (_gamepadIdPtr != IntPtr.Zero)
{
var key = GetKeyMapping(e.Key);
LibRyujinxInterop.SetButtonPressed(key, _gamepadIdPtr);
}
}
public void UpdateLoop()
{
while(_run)
{
ProcessWindowEvents(true);
ProcessInputEvents();
ProcessWindowEvents(IsEventDriven);
if (_mousePressed)
{
LibRyujinxInterop.SetTouchPoint((int)_lastPosition.X, (int)_lastPosition.Y);
}
else
{
LibRyujinxInterop.ReleaseTouchPoint();
}
LibRyujinxInterop.UpdateInput();
Thread.Sleep(1);
}
}
public GamepadButtonInputId GetKeyMapping(Keys key)
{
if(_keyMapping.TryGetValue(key, out var mapping))
{
return mapping;
}
return GamepadButtonInputId.Unbound;
}
private Dictionary<Keys, GamepadButtonInputId> _keyMapping = new Dictionary<Keys, GamepadButtonInputId>()
{
{Keys.A, GamepadButtonInputId.A },
{Keys.S, GamepadButtonInputId.B },
{Keys.Z, GamepadButtonInputId.X },
{Keys.X, GamepadButtonInputId.Y },
{Keys.Equal, GamepadButtonInputId.Plus },
{Keys.Minus, GamepadButtonInputId.Minus },
{Keys.Q, GamepadButtonInputId.LeftShoulder },
{Keys.D1, GamepadButtonInputId.LeftTrigger },
{Keys.W, GamepadButtonInputId.RightShoulder },
{Keys.D2, GamepadButtonInputId.RightTrigger },
{Keys.E, GamepadButtonInputId.LeftStick },
{Keys.R, GamepadButtonInputId.RightStick },
{Keys.Up, GamepadButtonInputId.DpadUp },
{Keys.Down, GamepadButtonInputId.DpadDown },
{Keys.Left, GamepadButtonInputId.DpadLeft },
{Keys.Right, GamepadButtonInputId.DpadRight },
{Keys.U, GamepadButtonInputId.SingleLeftTrigger0 },
{Keys.D7, GamepadButtonInputId.SingleLeftTrigger1 },
{Keys.O, GamepadButtonInputId.SingleRightTrigger0 },
{Keys.D9, GamepadButtonInputId.SingleRightTrigger1 }
};
} }
} }

View File

@ -18,7 +18,7 @@ namespace LibRyujinx.NativeSample
Size = new Vector2i(800, 600), Size = new Vector2i(800, 600),
Title = "Ryujinx", Title = "Ryujinx",
API = ContextAPI.NoAPI, API = ContextAPI.NoAPI,
IsEventDriven = true, IsEventDriven = false,
// This is needed to run on macos // This is needed to run on macos
Flags = ContextFlags.ForwardCompatible, Flags = ContextFlags.ForwardCompatible,
}; };

View File

@ -207,6 +207,7 @@ namespace LibRyujinx
public bool EnableSpirvCompilationOnVulkan = true; public bool EnableSpirvCompilationOnVulkan = true;
public bool EnableTextureRecompression = false; public bool EnableTextureRecompression = false;
public BackendThreading BackendThreading = BackendThreading.Auto; public BackendThreading BackendThreading = BackendThreading.Auto;
public AspectRatio AspectRatio = AspectRatio.Fixed16x9;
public GraphicsConfiguration() public GraphicsConfiguration()
{ {

View File

@ -1,12 +1,562 @@
using System; using DiscordRPC;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
using Ryujinx.Ui.Common.Configuration;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
using StickInputId = Ryujinx.Input.StickInputId;
namespace LibRyujinx namespace LibRyujinx
{ {
public static partial class LibRyujinx public static partial class LibRyujinx
{ {
private static VirtualGamepadDriver? _gamepadDriver;
private static VirtualTouchScreen? _virtualTouchScreen;
private static VirtualTouchScreenDriver? _touchScreenDriver;
private static TouchScreenManager? _touchScreenManager;
private static InputManager? _inputManager;
private static NpadManager _npadManager;
private static InputConfig[] _configs;
public static void InitializeInput(int width, int height)
{
if(SwitchDevice!.InputManager != null)
{
throw new InvalidOperationException("Input is already initialized");
}
_gamepadDriver = new VirtualGamepadDriver(4);
_configs = new InputConfig[4];
_virtualTouchScreen = new VirtualTouchScreen();
_touchScreenDriver = new VirtualTouchScreenDriver(_virtualTouchScreen);
_inputManager = new InputManager(null, _gamepadDriver);
_inputManager.SetMouseDriver(_touchScreenDriver);
_npadManager = _inputManager.CreateNpadManager();
SwitchDevice!.InputManager = _inputManager;
_touchScreenManager = _inputManager.CreateTouchScreenManager();
_touchScreenManager.Initialize(SwitchDevice!.EmulationContext);
_npadManager.Initialize(SwitchDevice.EmulationContext, new List<InputConfig>(), false, false);
_virtualTouchScreen.ClientSize = new Size(width, height);
}
public static void SetClientSize(int width, int height)
{
_virtualTouchScreen!.ClientSize = new Size(width, height);
}
public static void SetTouchPoint(int x, int y)
{
_virtualTouchScreen?.SetPosition(x, y);
}
public static void ReleaseTouchPoint()
{
_virtualTouchScreen?.ReleaseTouch();
}
public static void SetButtonPressed(GamepadButtonInputId button, string id)
{
_gamepadDriver?.SetButtonPressed(button, id);
}
public static void SetButtonReleased(GamepadButtonInputId button, string id)
{
_gamepadDriver?.SetButtonReleased(button, id);
}
public static void SetStickAxis(StickInputId stick, Vector2 axes, string deviceId)
{
_gamepadDriver?.SetStickAxis(stick, axes, deviceId);
}
public static string ConnectGamepad(int index)
{
var gamepad = _gamepadDriver?.GetGamepad(index);
if (gamepad != null)
{
var config = CreateDefaultInputConfig();
config.Id = gamepad.Id;
config.PlayerIndex = (PlayerIndex)index;
_configs[index] = config;
}
_npadManager?.ReloadConfiguration(_configs.Where(x => x != null).ToList(), false, false);
return gamepad?.Id ?? string.Empty;
}
private static InputConfig CreateDefaultInputConfig()
{
return new StandardControllerInputConfig
{
Version = InputConfig.CurrentVersion,
Backend = InputBackendType.GamepadSDL2,
Id = null,
ControllerType = ControllerType.JoyconPair,
DeadzoneLeft = 0.1f,
DeadzoneRight = 0.1f,
RangeLeft = 1.0f,
RangeRight = 1.0f,
TriggerThreshold = 0.5f,
LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
{
DpadUp = ConfigGamepadInputId.DpadUp,
DpadDown = ConfigGamepadInputId.DpadDown,
DpadLeft = ConfigGamepadInputId.DpadLeft,
DpadRight = ConfigGamepadInputId.DpadRight,
ButtonMinus = ConfigGamepadInputId.Minus,
ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
},
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Left,
StickButton = ConfigGamepadInputId.LeftStick,
InvertStickX = false,
InvertStickY = false,
Rotate90CW = false,
},
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
{
ButtonA = ConfigGamepadInputId.A,
ButtonB = ConfigGamepadInputId.B,
ButtonX = ConfigGamepadInputId.X,
ButtonY = ConfigGamepadInputId.Y,
ButtonPlus = ConfigGamepadInputId.Plus,
ButtonR = ConfigGamepadInputId.RightShoulder,
ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
},
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{
Joystick = ConfigStickInputId.Right,
StickButton = ConfigGamepadInputId.RightStick,
InvertStickX = false,
InvertStickY = false,
Rotate90CW = false,
},
Motion = new StandardMotionConfigController
{
MotionBackend = MotionInputBackendType.GamepadDriver,
EnableMotion = true,
Sensitivity = 100,
GyroDeadzone = 1,
},
Rumble = new RumbleConfigController
{
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false
}
};
}
public static void UpdateInput()
{
_npadManager?.Update(GraphicsConfiguration.AspectRatio.ToFloat());
if(!_touchScreenManager!.Update(true, _virtualTouchScreen!.IsButtonPressed(MouseButton.Button1), GraphicsConfiguration.AspectRatio.ToFloat()))
{
SwitchDevice!.EmulationContext?.Hid.Touchscreen.Update();
}
}
// Native Methods
[UnmanagedCallersOnly(EntryPoint = "input_initialize")]
public static void InitializeInputNative(int width, int height)
{
InitializeInput(width, height);
}
[UnmanagedCallersOnly(EntryPoint = "input_set_client_size")]
public static void SetClientSizeNative(int width, int height)
{
SetClientSize(width, height);
}
[UnmanagedCallersOnly(EntryPoint = "input_set_touch_point")]
public static void SetTouchPointNative(int x, int y)
{
SetTouchPoint(x, y);
}
[UnmanagedCallersOnly(EntryPoint = "input_release_touch_point")]
public static void ReleaseTouchPointNative()
{
ReleaseTouchPoint();
}
[UnmanagedCallersOnly(EntryPoint = "input_update")]
public static void UpdateInputNative()
{
UpdateInput();
}
[UnmanagedCallersOnly(EntryPoint = "input_set_button_pressed")]
public static void SetButtonPressedNative(GamepadButtonInputId button, IntPtr idPtr)
{
var id = Marshal.PtrToStringAnsi(idPtr);
SetButtonPressed(button, id);
}
[UnmanagedCallersOnly(EntryPoint = "input_set_button_released")]
public static void SetButtonReleased(GamepadButtonInputId button, IntPtr idPtr)
{
var id = Marshal.PtrToStringAnsi(idPtr);
SetButtonReleased(button, id);
}
[UnmanagedCallersOnly(EntryPoint = "input_set_stick_axis")]
public static void SetStickAxisNative(StickInputId stick, Vector2 axes, IntPtr idPtr)
{
var id = Marshal.PtrToStringAnsi(idPtr);
SetStickAxis(stick, axes, id);
}
[UnmanagedCallersOnly(EntryPoint = "input_connect_gamepad")]
public static IntPtr ConnectGamepadNative(int index)
{
var id = ConnectGamepad(index);
return Marshal.StringToHGlobalAnsi(id);
}
}
public class VirtualTouchScreen : IMouse
{
public Size ClientSize { get; set; }
public bool[] Buttons { get; }
public VirtualTouchScreen()
{
Buttons = new bool[2];
}
public Vector2 CurrentPosition { get; private set; }
public Vector2 Scroll { get; private set; }
public string Id => "0";
public string Name => "AvaloniaMouse";
public bool IsConnected => true;
public GamepadFeaturesFlag Features => throw new NotImplementedException();
public void Dispose()
{
}
public GamepadStateSnapshot GetMappedStateSnapshot()
{
throw new NotImplementedException();
}
public void SetPosition(int x, int y)
{
CurrentPosition = new Vector2(x, y);
Buttons[0] = true;
}
public void ReleaseTouch()
{
Buttons[0] = false;
}
public Vector3 GetMotionData(MotionInputId inputId)
{
throw new NotImplementedException();
}
public Vector2 GetPosition()
{
return CurrentPosition;
}
public Vector2 GetScroll()
{
return Scroll;
}
public GamepadStateSnapshot GetStateSnapshot()
{
throw new NotImplementedException();
}
public (float, float) GetStick(Ryujinx.Input.StickInputId inputId)
{
throw new NotImplementedException();
}
public bool IsButtonPressed(MouseButton button)
{
return Buttons[0];
}
public bool IsPressed(GamepadButtonInputId inputId)
{
throw new NotImplementedException();
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
throw new NotImplementedException();
}
public void SetConfiguration(InputConfig configuration)
{
throw new NotImplementedException();
}
public void SetTriggerThreshold(float triggerThreshold)
{
throw new NotImplementedException();
}
}
public class VirtualTouchScreenDriver : IGamepadDriver
{
private readonly VirtualTouchScreen _virtualTouchScreen;
public VirtualTouchScreenDriver(VirtualTouchScreen virtualTouchScreen)
{
_virtualTouchScreen = virtualTouchScreen;
}
public string DriverName => "VirtualTouchDriver";
public ReadOnlySpan<string> GamepadsIds => new[] { "0" };
public event Action<string> OnGamepadConnected
{
add { }
remove { }
}
public event Action<string> OnGamepadDisconnected
{
add { }
remove { }
}
public void Dispose()
{
}
public IGamepad GetGamepad(string id)
{
return _virtualTouchScreen;
}
}
public class VirtualGamepadDriver : IGamepadDriver
{
private readonly int _controllerCount;
public ReadOnlySpan<string> GamepadsIds => _gamePads.Keys.ToArray();
public string DriverName => "SDL2";
public event Action<string> OnGamepadConnected;
public event Action<string> OnGamepadDisconnected;
private Dictionary<string, VirtualGamepad> _gamePads;
public VirtualGamepadDriver(int controllerCount)
{
_gamePads = new Dictionary<string, VirtualGamepad>();
for (int joystickIndex = 0; joystickIndex < controllerCount; joystickIndex++)
{
HandleJoyStickConnected(joystickIndex);
}
_controllerCount = controllerCount;
}
private string GenerateGamepadId(int joystickIndex)
{
return "VirtualGamePad-" + joystickIndex;
}
private void HandleJoyStickConnected(int joystickDeviceId)
{
string id = GenerateGamepadId(joystickDeviceId);
_gamePads[id] = new VirtualGamepad(this, id);
OnGamepadConnected?.Invoke(id);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Simulate a full disconnect when disposing
var ids = GamepadsIds;
foreach (string id in ids)
{
OnGamepadDisconnected?.Invoke(id);
}
_gamePads.Clear();
}
}
public void Dispose()
{
Dispose(true);
}
public IGamepad GetGamepad(string id)
{
return _gamePads[id];
}
public IGamepad GetGamepad(int index)
{
string id = GenerateGamepadId(index);
return _gamePads[id];
}
public void SetStickAxis(StickInputId stick, Vector2 axes, string deviceId)
{
if(_gamePads.TryGetValue(deviceId, out var gamePad))
{
gamePad.StickInputs[(int)stick] = axes;
}
}
public void SetButtonPressed(GamepadButtonInputId button, string deviceId)
{
if (_gamePads.TryGetValue(deviceId, out var gamePad))
{
gamePad.ButtonInputs[(int)button] = true;
}
}
public void SetButtonReleased(GamepadButtonInputId button, string deviceId)
{
if (_gamePads.TryGetValue(deviceId, out var gamePad))
{
gamePad.ButtonInputs[(int)button] = false;
}
}
}
public class VirtualGamepad : IGamepad
{
private readonly VirtualGamepadDriver _driver;
private bool[] _buttonInputs;
private Vector2[] _stickInputs;
public VirtualGamepad(VirtualGamepadDriver driver, string id)
{
_buttonInputs = new bool[(int)GamepadButtonInputId.Count];
_stickInputs = new Vector2[(int)StickInputId.Count];
_driver = driver;
Id = id;
}
public void Dispose() { }
public GamepadFeaturesFlag Features { get; }
public string Id { get; }
public string Name => Id;
public bool IsConnected { get; }
public Vector2[] StickInputs { get => _stickInputs; set => _stickInputs = value; }
public bool[] ButtonInputs { get => _buttonInputs; set => _buttonInputs = value; }
public bool IsPressed(GamepadButtonInputId inputId)
{
return _buttonInputs[(int)inputId];
}
public (float, float) GetStick(StickInputId inputId)
{
var v = _stickInputs[(int)inputId];
return (v.X, v.Y);
}
public Vector3 GetMotionData(MotionInputId inputId)
{
return new Vector3();
}
public void SetTriggerThreshold(float triggerThreshold)
{
//throw new System.NotImplementedException();
}
public void SetConfiguration(InputConfig configuration)
{
//throw new System.NotImplementedException();
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
//throw new System.NotImplementedException();
}
public GamepadStateSnapshot GetMappedStateSnapshot()
{
GamepadStateSnapshot result = default;
foreach (var button in Enum.GetValues<GamepadButtonInputId>())
{
// Do not touch state of button already pressed
if (button != GamepadButtonInputId.Count && !result.IsPressed(button))
{
result.SetPressed(button, IsPressed(button));
}
}
(float leftStickX, float leftStickY) = GetStick(StickInputId.Left);
(float rightStickX, float rightStickY) = GetStick(StickInputId.Right);
result.SetStick(StickInputId.Left, leftStickX, leftStickY);
result.SetStick(StickInputId.Right, rightStickX, rightStickY);
return result;
}
public GamepadStateSnapshot GetStateSnapshot()
{
return new GamepadStateSnapshot();
}
} }
} }

View File

@ -132,7 +132,7 @@ namespace LibRyujinx
"UTC", "UTC",
MemoryManagerMode.HostMappedUnsafe, MemoryManagerMode.HostMappedUnsafe,
false, false,
AspectRatio.Fixed16x9, LibRyujinx.GraphicsConfiguration.AspectRatio,
0, 0,
true, true,
""); "");