diff --git a/Ryujinx.Input/HLE/InputManager.cs b/Ryujinx.Input/HLE/InputManager.cs
index 277e8ec226..699e521d8c 100644
--- a/Ryujinx.Input/HLE/InputManager.cs
+++ b/Ryujinx.Input/HLE/InputManager.cs
@@ -6,6 +6,7 @@ namespace Ryujinx.Input.HLE
{
public IGamepadDriver KeyboardDriver { get; private set; }
public IGamepadDriver GamepadDriver { get; private set; }
+ public IGamepadDriver MouseDriver { get; private set; }
public InputManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver)
{
@@ -13,10 +14,27 @@ namespace Ryujinx.Input.HLE
GamepadDriver = gamepadDriver;
}
+ public void SetMouseDriver(IGamepadDriver mouseDriver)
+ {
+ MouseDriver?.Dispose();
+
+ MouseDriver = mouseDriver;
+ }
+
public NpadManager CreateNpadManager()
{
return new NpadManager(KeyboardDriver, GamepadDriver);
}
+
+ public TouchScreenManager CreateTouchScreenManager()
+ {
+ if (MouseDriver == null)
+ {
+ throw new InvalidOperationException("Mouse Driver has not been initialized.");
+ }
+
+ return new TouchScreenManager(MouseDriver.GetGamepad("0") as IMouse);
+ }
protected virtual void Dispose(bool disposing)
{
@@ -24,6 +42,7 @@ namespace Ryujinx.Input.HLE
{
KeyboardDriver?.Dispose();
GamepadDriver?.Dispose();
+ MouseDriver?.Dispose();
}
}
diff --git a/Ryujinx.Input/HLE/TouchScreenManager.cs b/Ryujinx.Input/HLE/TouchScreenManager.cs
new file mode 100644
index 0000000000..ffa8eeacc5
--- /dev/null
+++ b/Ryujinx.Input/HLE/TouchScreenManager.cs
@@ -0,0 +1,57 @@
+using Ryujinx.HLE;
+using Ryujinx.HLE.HOS.Services.Hid;
+using System;
+
+namespace Ryujinx.Input.HLE
+{
+ public class TouchScreenManager : IDisposable
+ {
+ private readonly IMouse _mouse;
+ private Switch _device;
+
+ public TouchScreenManager(IMouse mouse)
+ {
+ _mouse = mouse;
+ }
+
+ public void Initialize(Switch device)
+ {
+ _device = device;
+ }
+
+ public bool Update(bool isFocused, float aspectRatio = 0)
+ {
+ if (!isFocused)
+ {
+ _device.Hid.Touchscreen.Update();
+
+ return false;
+ }
+
+ if (aspectRatio > 0)
+ {
+ var snapshot = IMouse.GetMouseStateSnapshot(_mouse);
+ var touchPosition = IMouse.GetTouchPosition(snapshot.Position, _mouse.ClientSize, aspectRatio);
+
+ TouchPoint currentPoint = new TouchPoint
+ {
+ X = (uint)touchPosition.X,
+ Y = (uint)touchPosition.Y,
+
+ // Placeholder values till more data is acquired
+ DiameterX = 10,
+ DiameterY = 10,
+ Angle = 90
+ };
+
+ _device.Hid.Touchscreen.Update(currentPoint);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public void Dispose() { }
+ }
+}
\ No newline at end of file
diff --git a/Ryujinx.Input/IMouse.cs b/Ryujinx.Input/IMouse.cs
new file mode 100644
index 0000000000..37de0229e9
--- /dev/null
+++ b/Ryujinx.Input/IMouse.cs
@@ -0,0 +1,100 @@
+using System.Drawing;
+using System.Numerics;
+
+namespace Ryujinx.Input
+{
+ ///
+ /// Represent an emulated mouse.
+ ///
+ public interface IMouse : IGamepad
+ {
+ private const int SwitchPanelWidth = 1280;
+ private const int SwitchPanelHeight = 720;
+
+ ///
+ /// Check if a given button is pressed on the mouse.
+ ///
+ /// The button
+ /// True if the given button is pressed on the mouse
+ bool IsButtonPressed(MouseButton button);
+
+ ///
+ /// Get the position of the mouse in the client.
+ ///
+ Vector2 GetPosition();
+
+ ///
+ /// Get the client size.
+ ///
+ Size ClientSize { get; }
+
+ ///
+ /// Get the button states of the mouse.
+ ///
+ bool[] Buttons { get; }
+
+ ///
+ /// Get a snaphost of the state of a mouse.
+ ///
+ /// The mouse to do a snapshot of
+ /// A snaphost of the state of the mouse.
+ public static MouseStateSnapshot GetMouseStateSnapshot(IMouse mouse)
+ {
+ var position = mouse.GetPosition();
+ bool[] buttons = new bool[(int)MouseButton.Count];
+
+ mouse.Buttons.CopyTo(buttons, 0);
+
+ return new MouseStateSnapshot(buttons, position);
+ }
+
+ ///
+ /// Get the touch position of a mouse position relative to the app's view
+ ///
+ /// The position of the mouse in the client
+ /// The size of the client
+ /// The aspect ratio of the view
+ /// A snaphost of the state of the mouse.
+ public static Vector2 GetTouchPosition(Vector2 mousePosition, Size clientSize, float aspectRatio)
+ {
+ float mouseX = mousePosition.X;
+ float mouseY = mousePosition.Y;
+
+ float aspectWidth = SwitchPanelHeight * aspectRatio;
+
+ int screenWidth = clientSize.Width;
+ int screenHeight = clientSize.Height;
+
+ if (clientSize.Width > clientSize.Height * aspectWidth / SwitchPanelHeight)
+ {
+ screenWidth = (int)(clientSize.Height * aspectWidth) / SwitchPanelHeight;
+ }
+ else
+ {
+ screenHeight = (clientSize.Width * SwitchPanelHeight) / (int)aspectWidth;
+ }
+
+ int startX = (clientSize.Width - screenWidth) >> 1;
+ int startY = (clientSize.Height - screenHeight) >> 1;
+
+ int endX = startX + screenWidth;
+ int endY = startY + screenHeight;
+
+ if (mouseX >= startX &&
+ mouseY >= startY &&
+ mouseX < endX &&
+ mouseY < endY)
+ {
+ int screenMouseX = (int)mouseX - startX;
+ int screenMouseY = (int)mouseY - startY;
+
+ mouseX = (screenMouseX * (int)aspectWidth) / screenWidth;
+ mouseY = (screenMouseY * SwitchPanelHeight) / screenHeight;
+
+ return new Vector2(mouseX, mouseY);
+ }
+
+ return new Vector2();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ryujinx.Input/MouseButton.cs b/Ryujinx.Input/MouseButton.cs
new file mode 100644
index 0000000000..ab76421674
--- /dev/null
+++ b/Ryujinx.Input/MouseButton.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.Input
+{
+ public enum MouseButton : byte
+ {
+ Button1,
+ Button2,
+ Button3,
+ Button4,
+ Button5,
+ Button6,
+ Button7,
+ Button8,
+ Button9,
+ Count
+ }
+}
\ No newline at end of file
diff --git a/Ryujinx.Input/MouseStateSnapshot.cs b/Ryujinx.Input/MouseStateSnapshot.cs
new file mode 100644
index 0000000000..4fbfeebd1b
--- /dev/null
+++ b/Ryujinx.Input/MouseStateSnapshot.cs
@@ -0,0 +1,34 @@
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Input
+{
+ ///
+ /// A snapshot of a .
+ ///
+ public class MouseStateSnapshot
+ {
+ private bool[] _buttonState;
+
+ public Vector2 Position { get; }
+
+ ///
+ /// Create a new .
+ ///
+ /// The keys state
+ public MouseStateSnapshot(bool[] buttonState, Vector2 position)
+ {
+ _buttonState = buttonState;
+
+ Position = position;
+ }
+
+ ///
+ /// Check if a given button is pressed.
+ ///
+ /// The button
+ /// True if the given button is pressed
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsPressed(MouseButton button) => _buttonState[(int)button];
+ }
+}
\ No newline at end of file
diff --git a/Ryujinx/Input/GTK3/GTK3Mouse.cs b/Ryujinx/Input/GTK3/GTK3Mouse.cs
new file mode 100644
index 0000000000..eb0c8c9a08
--- /dev/null
+++ b/Ryujinx/Input/GTK3/GTK3Mouse.cs
@@ -0,0 +1,84 @@
+using Ryujinx.Common.Configuration.Hid;
+using System;
+using System.Drawing;
+using System.Numerics;
+
+namespace Ryujinx.Input.GTK3
+{
+ public class GTK3Mouse : IMouse
+ {
+ private GTK3MouseDriver _driver;
+
+ public GamepadFeaturesFlag Features => throw new NotImplementedException();
+
+ public string Id => "0";
+
+ public string Name => "GTKMouse";
+
+ public bool IsConnected => true;
+
+ public bool[] Buttons => _driver.PressedButtons;
+
+ public GTK3Mouse(GTK3MouseDriver driver)
+ {
+ _driver = driver;
+ }
+
+ public Size ClientSize => _driver.GetClientSize();
+
+ public Vector2 GetPosition()
+ {
+ return _driver.CurrentPosition;
+ }
+
+ public GamepadStateSnapshot GetMappedStateSnapshot()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Vector3 GetMotionData(MotionInputId inputId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public GamepadStateSnapshot GetStateSnapshot()
+ {
+ throw new NotImplementedException();
+ }
+
+ public (float, float) GetStick(StickInputId inputId)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsButtonPressed(MouseButton button)
+ {
+ return _driver.IsButtonPressed(button);
+ }
+
+ 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 void Dispose()
+ {
+ _driver = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ryujinx/Input/GTK3/GTK3MouseDriver.cs b/Ryujinx/Input/GTK3/GTK3MouseDriver.cs
new file mode 100644
index 0000000000..015f581765
--- /dev/null
+++ b/Ryujinx/Input/GTK3/GTK3MouseDriver.cs
@@ -0,0 +1,97 @@
+using Gdk;
+using Gtk;
+using System;
+using System.Numerics;
+using Size = System.Drawing.Size;
+
+namespace Ryujinx.Input.GTK3
+{
+ public class GTK3MouseDriver : IGamepadDriver
+ {
+ private Widget _widget;
+ private bool _isDisposed;
+
+ public bool[] PressedButtons { get; }
+
+ public Vector2 CurrentPosition { get; private set; }
+
+ public GTK3MouseDriver(Widget parent)
+ {
+ _widget = parent;
+
+ _widget.MotionNotifyEvent += Parent_MotionNotifyEvent;
+ _widget.ButtonPressEvent += Parent_ButtonPressEvent;
+ _widget.ButtonReleaseEvent += Parent_ButtonReleaseEvent;
+
+ PressedButtons = new bool[(int)MouseButton.Count];
+ }
+
+ [GLib.ConnectBefore]
+ private void Parent_ButtonReleaseEvent(object o, ButtonReleaseEventArgs args)
+ {
+ PressedButtons[args.Event.Button - 1] = false;
+ }
+
+ [GLib.ConnectBefore]
+ private void Parent_ButtonPressEvent(object o, ButtonPressEventArgs args)
+ {
+ PressedButtons[args.Event.Button - 1] = true;
+ }
+
+ [GLib.ConnectBefore]
+ private void Parent_MotionNotifyEvent(object o, MotionNotifyEventArgs args)
+ {
+ if (args.Event.Device.InputSource == InputSource.Mouse)
+ {
+ CurrentPosition = new Vector2((float)args.Event.X, (float)args.Event.Y);
+ }
+ }
+
+ public bool IsButtonPressed(MouseButton button)
+ {
+ return PressedButtons[(int) button];
+ }
+
+ public Size GetClientSize()
+ {
+ return new Size(_widget.AllocatedWidth, _widget.AllocatedHeight);
+ }
+
+ public string DriverName => "GTK3";
+
+ public event Action OnGamepadConnected
+ {
+ add { }
+ remove { }
+ }
+
+ public event Action OnGamepadDisconnected
+ {
+ add { }
+ remove { }
+ }
+
+ public ReadOnlySpan GamepadsIds => new[] {"0"};
+
+ public IGamepad GetGamepad(string id)
+ {
+ return new GTK3Mouse(this);
+ }
+
+ public void Dispose()
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+
+ _isDisposed = true;
+
+ _widget.MotionNotifyEvent -= Parent_MotionNotifyEvent;
+ _widget.ButtonPressEvent -= Parent_ButtonPressEvent;
+ _widget.ButtonReleaseEvent -= Parent_ButtonReleaseEvent;
+
+ _widget = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs
index 00882ba0c2..4ba87a1b10 100644
--- a/Ryujinx/Ui/RendererWidgetBase.cs
+++ b/Ryujinx/Ui/RendererWidgetBase.cs
@@ -8,6 +8,7 @@ using Ryujinx.Configuration;
using Ryujinx.Graphics.GAL;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.Input;
+using Ryujinx.Input.GTK3;
using Ryujinx.Input.HLE;
using Ryujinx.Ui.Widgets;
using System;
@@ -28,6 +29,7 @@ namespace Ryujinx.Ui
public ManualResetEvent WaitEvent { get; set; }
public NpadManager NpadManager { get; }
+ public TouchScreenManager TouchScreenManager { get; }
public Switch Device { get; private set; }
public IRenderer Renderer { get; private set; }
@@ -37,10 +39,6 @@ namespace Ryujinx.Ui
private bool _isStopped;
private bool _isFocused;
- private double _mouseX;
- private double _mouseY;
- private bool _mousePressed;
-
private bool _toggleFullscreen;
private bool _toggleDockedMode;
@@ -69,8 +67,12 @@ namespace Ryujinx.Ui
public RendererWidgetBase(InputManager inputManager, GraphicsDebugLevel glLogLevel)
{
+ var mouseDriver = new GTK3MouseDriver(this);
+
_inputManager = inputManager;
+ _inputManager.SetMouseDriver(mouseDriver);
NpadManager = _inputManager.CreateNpadManager();
+ TouchScreenManager = _inputManager.CreateTouchScreenManager();
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
WaitEvent = new ManualResetEvent(false);
@@ -145,37 +147,8 @@ namespace Ryujinx.Ui
_isFocused = ParentWindow.State.HasFlag(Gdk.WindowState.Focused);
}
- protected override bool OnButtonPressEvent(EventButton evnt)
- {
- _mouseX = evnt.X;
- _mouseY = evnt.Y;
-
- if (evnt.Button == 1)
- {
- _mousePressed = true;
- }
-
- return false;
- }
-
- protected override bool OnButtonReleaseEvent(EventButton evnt)
- {
- if (evnt.Button == 1)
- {
- _mousePressed = false;
- }
-
- return false;
- }
-
protected override bool OnMotionNotifyEvent(EventMotion evnt)
{
- if (evnt.Device.InputSource == InputSource.Mouse)
- {
- _mouseX = evnt.X;
- _mouseY = evnt.Y;
- }
-
if (_hideCursorOnIdle)
{
_lastCursorMoveTime = Stopwatch.GetTimestamp();
@@ -300,6 +273,7 @@ namespace Ryujinx.Ui
Renderer?.Window.SetSize(_windowWidth, _windowHeight);
NpadManager.Initialize(device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard);
+ TouchScreenManager.Initialize(device);
}
public void Render()
@@ -412,6 +386,7 @@ namespace Ryujinx.Ui
public void Exit()
{
+ TouchScreenManager?.Dispose();
NpadManager?.Dispose();
if (_isStopped)
@@ -507,60 +482,14 @@ namespace Ryujinx.Ui
bool hasTouch = false;
// Get screen touch position from left mouse click
- // OpenTK always captures mouse events, even if out of focus, so check if window is focused.
- if (_isFocused && _mousePressed)
+ if (_isFocused && (_inputManager.MouseDriver as GTK3MouseDriver).IsButtonPressed(MouseButton.Button1))
{
- float aspectWidth = SwitchPanelHeight * ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat();
-
- int screenWidth = AllocatedWidth;
- int screenHeight = AllocatedHeight;
-
- if (AllocatedWidth > AllocatedHeight * aspectWidth / SwitchPanelHeight)
- {
- screenWidth = (int)(AllocatedHeight * aspectWidth) / SwitchPanelHeight;
- }
- else
- {
- screenHeight = (AllocatedWidth * SwitchPanelHeight) / (int)aspectWidth;
- }
-
- int startX = (AllocatedWidth - screenWidth) >> 1;
- int startY = (AllocatedHeight - screenHeight) >> 1;
-
- int endX = startX + screenWidth;
- int endY = startY + screenHeight;
-
- if (_mouseX >= startX &&
- _mouseY >= startY &&
- _mouseX < endX &&
- _mouseY < endY)
- {
- int screenMouseX = (int)_mouseX - startX;
- int screenMouseY = (int)_mouseY - startY;
-
- int mX = (screenMouseX * (int)aspectWidth) / screenWidth;
- int mY = (screenMouseY * SwitchPanelHeight) / screenHeight;
-
- TouchPoint currentPoint = new TouchPoint
- {
- X = (uint)mX,
- Y = (uint)mY,
-
- // Placeholder values till more data is acquired
- DiameterX = 10,
- DiameterY = 10,
- Angle = 90
- };
-
- hasTouch = true;
-
- Device.Hid.Touchscreen.Update(currentPoint);
- }
+ hasTouch = TouchScreenManager.Update(true, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
}
if (!hasTouch)
{
- Device.Hid.Touchscreen.Update();
+ TouchScreenManager.Update(false);
}
Device.Hid.DebugPad.Update();
@@ -568,7 +497,6 @@ namespace Ryujinx.Ui
return true;
}
-
[Flags]
private enum KeyboardHotkeyState
{