diff --git a/osu.Framework.Tests/Visual/Input/TestScenePassThroughInputManager.cs b/osu.Framework.Tests/Visual/Input/TestScenePassThroughInputManager.cs index 5893123a1f..671825069b 100644 --- a/osu.Framework.Tests/Visual/Input/TestScenePassThroughInputManager.cs +++ b/osu.Framework.Tests/Visual/Input/TestScenePassThroughInputManager.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Framework.Input.Events; -using osu.Framework.Input.StateChanges; using osu.Framework.Input.States; using osu.Framework.Testing; using osu.Framework.Testing.Input; @@ -273,63 +272,6 @@ public void TestMouseTouchProductionOnPassThrough() AddAssert("pass-through handled mouse", () => testInputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left); } - [Test] - public void TestPenInputPassThrough() - { - MouseBox outer = null!; - MouseBox inner = null!; - - addTestInputManagerStep(); - AddStep("setup hierarchy", () => - { - Add(outer = new MouseBox - { - Alpha = 0.5f, - Depth = 1, - RelativeSizeAxes = Axes.Both, - }); - - testInputManager.Add(inner = new MouseBox - { - Alpha = 0.5f, - RelativeSizeAxes = Axes.Both, - }); - }); - - AddStep("move pen to box", () => InputManager.MovePenTo(testInputManager)); - - AddAssert("ensure parent manager produced mouse", () => InputManager.CurrentState.Mouse.Position == testInputManager.ScreenSpaceDrawQuad.Centre); - AddAssert("ensure pass-through produced mouse", () => testInputManager.CurrentState.Mouse.Position == testInputManager.ScreenSpaceDrawQuad.Centre); - - AddAssert("outer box received 1 pen event", () => outer.PenEvents, () => Is.EqualTo(1)); - AddAssert("outer box received no mouse events", () => outer.MouseEvents, () => Is.EqualTo(0)); - - AddAssert("inner box received 1 pen event", () => inner.PenEvents, () => Is.EqualTo(1)); - AddAssert("inner box received no mouse events", () => inner.MouseEvents, () => Is.EqualTo(0)); - - AddStep("press pen", () => InputManager.PressPen()); - - AddAssert("ensure parent manager produced mouse", () => InputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left); - AddAssert("ensure pass-through produced mouse", () => testInputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left); - - AddAssert("outer box received 2 pen events", () => outer.PenEvents, () => Is.EqualTo(2)); - AddAssert("outer box received no mouse events", () => outer.MouseEvents, () => Is.EqualTo(0)); - - AddAssert("inner box received 2 pen events", () => inner.PenEvents, () => Is.EqualTo(2)); - AddAssert("inner box received no mouse events", () => inner.MouseEvents, () => Is.EqualTo(0)); - - AddStep("release pen", () => InputManager.ReleasePen()); - - AddAssert("ensure parent manager produced mouse", () => InputManager.CurrentState.Mouse.Buttons.HasAnyButtonPressed, () => Is.False); - AddAssert("ensure pass-through produced mouse", () => testInputManager.CurrentState.Mouse.Buttons.HasAnyButtonPressed, () => Is.False); - - AddAssert("outer box received 3 pen events", () => outer.PenEvents, () => Is.EqualTo(3)); - AddAssert("outer box received no mouse events", () => outer.MouseEvents, () => Is.EqualTo(0)); - - AddAssert("inner box received 3 pen events", () => inner.PenEvents, () => Is.EqualTo(3)); - AddAssert("inner box received no mouse events", () => inner.MouseEvents, () => Is.EqualTo(0)); - } - public partial class TestInputManager : ManualInputManager { public readonly TestSceneInputManager.ContainingInputManagerStatusText Status; @@ -353,53 +295,22 @@ public partial class HandlingBox : Box public partial class MouseBox : Box { public int MouseEvents { get; private set; } - public int PenEvents { get; private set; } protected override bool OnMouseMove(MouseMoveEvent e) { - switch (e.CurrentState.Mouse.LastSource) - { - case ISourcedFromPen: - PenEvents++; - break; - - default: - MouseEvents++; - break; - } - + MouseEvents++; return base.OnMouseMove(e); } protected override bool OnMouseDown(MouseDownEvent e) { - switch (e.CurrentState.Mouse.LastSource) - { - case ISourcedFromPen: - PenEvents++; - break; - - default: - MouseEvents++; - break; - } - + MouseEvents++; return base.OnMouseDown(e); } protected override void OnMouseUp(MouseUpEvent e) { - switch (e.CurrentState.Mouse.LastSource) - { - case ISourcedFromPen: - PenEvents++; - break; - - default: - MouseEvents++; - break; - } - + MouseEvents++; base.OnMouseUp(e); } } diff --git a/osu.Framework.Tests/Visual/Input/TestSceneTouchInput.cs b/osu.Framework.Tests/Visual/Input/TestSceneTouchInput.cs index 6e1e91d198..b2939e1769 100644 --- a/osu.Framework.Tests/Visual/Input/TestSceneTouchInput.cs +++ b/osu.Framework.Tests/Visual/Input/TestSceneTouchInput.cs @@ -203,8 +203,8 @@ public void TestMouseInputAppliedFromLatestTouch() AddStep("retrieve receptors", () => { - firstReceptor = receptors[(int)TouchSource.Touch1]; - lastReceptor = receptors[(int)TouchSource.Touch10]; + firstReceptor = receptors.First(); + lastReceptor = receptors.Last(); }); AddStep("activate first", () => diff --git a/osu.Framework.Tests/Visual/Input/TestSceneTouchVisualiser.cs b/osu.Framework.Tests/Visual/Input/TestSceneTouchVisualiser.cs index 631228ad01..7dcacfa850 100644 --- a/osu.Framework.Tests/Visual/Input/TestSceneTouchVisualiser.cs +++ b/osu.Framework.Tests/Visual/Input/TestSceneTouchVisualiser.cs @@ -35,7 +35,7 @@ public TestSceneTouchVisualiser() public partial class TouchVisualiser : CompositeDrawable { - private readonly Drawable[] drawableTouches = new Drawable[10]; + private readonly Drawable[] drawableTouches = new Drawable[TouchState.MAX_TOUCH_COUNT]; public TouchVisualiser() { diff --git a/osu.Framework/Input/Handlers/Pen/PenHandler.cs b/osu.Framework/Input/Handlers/Pen/PenHandler.cs index 09df050a1c..0bc37051e0 100644 --- a/osu.Framework/Input/Handlers/Pen/PenHandler.cs +++ b/osu.Framework/Input/Handlers/Pen/PenHandler.cs @@ -6,6 +6,7 @@ using osu.Framework.Platform.SDL3; using osu.Framework.Statistics; using osuTK; +using osuTK.Input; namespace osu.Framework.Input.Handlers.Pen { @@ -45,22 +46,31 @@ public override bool Initialize(GameHost host) return true; } - // iPadOS doesn't support external tablets, so we are sure it's direct Apple Pencil input. - // Other platforms support both direct and indirect tablet input, but SDL doesn't provide any information on the current device type. - private static readonly TabletPenDeviceType device_type = RuntimeInfo.OS == RuntimeInfo.Platform.iOS ? TabletPenDeviceType.Direct : TabletPenDeviceType.Unknown; + // Pen input is not necessarily direct on mobile platforms (specifically Android, where external tablets are supported), + // but until users experience issues with this, consider it "direct" for now. + private static readonly TabletPenDeviceType device_type = RuntimeInfo.IsMobile ? TabletPenDeviceType.Direct : TabletPenDeviceType.Unknown; + + private bool penDown; + private Vector2 currentPosition; private void handlePenMove(Vector2 position) { - enqueueInput(new MousePositionAbsoluteInputFromPen - { - Position = position, - DeviceType = device_type - }); + currentPosition = position; + + if (penDown && device_type == TabletPenDeviceType.Direct) + enqueueInput(new TouchInput(new Input.Touch(TouchSource.PenTouch, position), true)); + else + enqueueInput(new MousePositionAbsoluteInput { Position = position }); } private void handlePenTouch(bool pressed) { - enqueueInput(new MouseButtonInputFromPen(pressed) { DeviceType = device_type }); + penDown = pressed; + + if (device_type == TabletPenDeviceType.Direct) + enqueueInput(new TouchInput(new Input.Touch(TouchSource.PenTouch, currentPosition), pressed)); + else + enqueueInput(new MouseButtonInput(MouseButton.Left, pressed)); } private void handlePenButton(TabletPenButton button, bool pressed) diff --git a/osu.Framework/Input/Handlers/Tablet/OpenTabletDriverHandler.cs b/osu.Framework/Input/Handlers/Tablet/OpenTabletDriverHandler.cs index faf28c953f..82ff331747 100644 --- a/osu.Framework/Input/Handlers/Tablet/OpenTabletDriverHandler.cs +++ b/osu.Framework/Input/Handlers/Tablet/OpenTabletDriverHandler.cs @@ -15,6 +15,7 @@ using osu.Framework.Platform; using osu.Framework.Statistics; using osuTK; +using MouseButton = osuTK.Input.MouseButton; namespace osu.Framework.Input.Handlers.Tablet { @@ -88,21 +89,17 @@ public override bool Initialize(GameHost host) return true; } - private TabletPenDeviceType lastTabletDeviceType = TabletPenDeviceType.Unknown; - void IAbsolutePointer.SetPosition(System.Numerics.Vector2 pos) { - lastTabletDeviceType = TabletPenDeviceType.Unknown; - enqueueInput(new MousePositionAbsoluteInputFromPen { Position = new Vector2(pos.X, pos.Y), DeviceType = lastTabletDeviceType }); + enqueueInput(new MousePositionAbsoluteInput { Position = new Vector2(pos.X, pos.Y) }); } void IRelativePointer.SetPosition(System.Numerics.Vector2 delta) { - lastTabletDeviceType = TabletPenDeviceType.Indirect; - enqueueInput(new MousePositionRelativeInputFromPen { Delta = new Vector2(delta.X, delta.Y), DeviceType = lastTabletDeviceType }); + enqueueInput(new MousePositionRelativeInput { Delta = new Vector2(delta.X, delta.Y) }); } - void IPressureHandler.SetPressure(float percentage) => enqueueInput(new MouseButtonInputFromPen(percentage > 0) { DeviceType = lastTabletDeviceType }); + void IPressureHandler.SetPressure(float percentage) => enqueueInput(new MouseButtonInput(MouseButton.Left, percentage > 0)); private void handleTabletsChanged(object? sender, IEnumerable tablets) { diff --git a/osu.Framework/Input/PassThroughInputManager.cs b/osu.Framework/Input/PassThroughInputManager.cs index df415debea..82cce921ea 100644 --- a/osu.Framework/Input/PassThroughInputManager.cs +++ b/osu.Framework/Input/PassThroughInputManager.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input.Events; @@ -100,35 +99,6 @@ protected override bool Handle(UIEvent e) if (e is MouseEvent && e.CurrentState.Mouse.LastSource is ISourcedFromTouch) return false; - // Synthesize pen inputs from pen events - if (e is MouseEvent && e.CurrentState.Mouse.LastSource is ISourcedFromPen penInput) - { - switch (e) - { - case MouseDownEvent penDown: - Debug.Assert(penDown.Button == MouseButton.Left); - new MouseButtonInputFromPen(true) { DeviceType = penInput.DeviceType }.Apply(CurrentState, this); - return false; - - case MouseUpEvent penUp: - Debug.Assert(penUp.Button == MouseButton.Left); - new MouseButtonInputFromPen(false) { DeviceType = penInput.DeviceType }.Apply(CurrentState, this); - return false; - - case MouseMoveEvent penMove: - if (penMove.ScreenSpaceMousePosition != CurrentState.Mouse.Position) - { - new MousePositionAbsoluteInputFromPen - { - Position = penMove.ScreenSpaceMousePosition, - DeviceType = penInput.DeviceType - }.Apply(CurrentState, this); - } - - return false; - } - } - switch (e) { case MouseDownEvent mouseDown: diff --git a/osu.Framework/Input/StateChanges/ISourcedFromPen.cs b/osu.Framework/Input/StateChanges/ISourcedFromPen.cs deleted file mode 100644 index afce3ee864..0000000000 --- a/osu.Framework/Input/StateChanges/ISourcedFromPen.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Framework.Input.StateChanges -{ - /// - /// Denotes a simulated mouse input that was made by a tablet/pen device. - /// - public interface ISourcedFromPen : IInput - { - /// - /// The type of the tablet or pen device that made this input. - /// - TabletPenDeviceType DeviceType { get; } - } -} diff --git a/osu.Framework/Input/StateChanges/MouseButtonInputFromPen.cs b/osu.Framework/Input/StateChanges/MouseButtonInputFromPen.cs deleted file mode 100644 index ded6ff3f50..0000000000 --- a/osu.Framework/Input/StateChanges/MouseButtonInputFromPen.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osuTK.Input; - -namespace osu.Framework.Input.StateChanges -{ - public class MouseButtonInputFromPen : MouseButtonInput, ISourcedFromPen - { - public MouseButtonInputFromPen(bool isPressed) - : base(MouseButton.Left, isPressed) - { - } - - public required TabletPenDeviceType DeviceType { get; init; } - } -} diff --git a/osu.Framework/Input/StateChanges/MousePositionAbsoluteInputFromPen.cs b/osu.Framework/Input/StateChanges/MousePositionAbsoluteInputFromPen.cs deleted file mode 100644 index 9af14987b1..0000000000 --- a/osu.Framework/Input/StateChanges/MousePositionAbsoluteInputFromPen.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Framework.Input.StateChanges -{ - public class MousePositionAbsoluteInputFromPen : MousePositionAbsoluteInput, ISourcedFromPen - { - public required TabletPenDeviceType DeviceType { get; init; } - } -} diff --git a/osu.Framework/Input/StateChanges/MousePositionRelativeInputFromPen.cs b/osu.Framework/Input/StateChanges/MousePositionRelativeInputFromPen.cs deleted file mode 100644 index fe6d4cfa51..0000000000 --- a/osu.Framework/Input/StateChanges/MousePositionRelativeInputFromPen.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Framework.Input.StateChanges -{ - public class MousePositionRelativeInputFromPen : MousePositionRelativeInput, ISourcedFromPen - { - public required TabletPenDeviceType DeviceType { get; init; } - } -} diff --git a/osu.Framework/Input/TouchSource.cs b/osu.Framework/Input/TouchSource.cs index f44942138a..5433200742 100644 --- a/osu.Framework/Input/TouchSource.cs +++ b/osu.Framework/Input/TouchSource.cs @@ -57,5 +57,10 @@ public enum TouchSource /// The tenth and last available touch source. /// Touch10, + + /// + /// A touch source that represents a pen/stylus. + /// + PenTouch, } } diff --git a/osu.Framework/Testing/Input/ManualInputManager.cs b/osu.Framework/Testing/Input/ManualInputManager.cs index 544b670440..4cb2d40827 100644 --- a/osu.Framework/Testing/Input/ManualInputManager.cs +++ b/osu.Framework/Testing/Input/ManualInputManager.cs @@ -128,12 +128,6 @@ public void Keys(PlatformAction action) public void MoveTouchTo(Touch touch) => Input(new TouchInput(touch, CurrentState.Touch.IsActive(touch.Source))); - public void MovePenTo(Drawable drawable, Vector2? offset = null, TabletPenDeviceType deviceType = TabletPenDeviceType.Unknown) - => MovePenTo(drawable.ToScreenSpace(drawable.LayoutRectangle.Centre) + (offset ?? Vector2.Zero), deviceType); - - public void MovePenTo(Vector2 position, TabletPenDeviceType deviceType = TabletPenDeviceType.Unknown) - => Input(new MousePositionAbsoluteInputFromPen { Position = position, DeviceType = deviceType }); - public new bool TriggerClick() => throw new InvalidOperationException($"To trigger a click via a {nameof(ManualInputManager)} use {nameof(Click)} instead."); @@ -171,9 +165,6 @@ public void Click(MouseButton button) public void PressMidiKey(MidiKey key, byte velocity) => Input(new MidiKeyInput(key, velocity, true)); public void ReleaseMidiKey(MidiKey key, byte velocity) => Input(new MidiKeyInput(key, velocity, false)); - public void PressPen(TabletPenDeviceType deviceType = TabletPenDeviceType.Unknown) => Input(new MouseButtonInputFromPen(true) { DeviceType = deviceType }); - public void ReleasePen(TabletPenDeviceType deviceType = TabletPenDeviceType.Unknown) => Input(new MouseButtonInputFromPen(false) { DeviceType = deviceType }); - public void PressTabletPenButton(TabletPenButton penButton) => Input(new TabletPenButtonInput(penButton, true)); public void ReleaseTabletPenButton(TabletPenButton penButton) => Input(new TabletPenButtonInput(penButton, false));