Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert pen input flow and map pen to touch on mobile platforms #6523

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
}
Expand Down
4 changes: 2 additions & 2 deletions osu.Framework.Tests/Visual/Input/TestSceneTouchInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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", () =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
28 changes: 19 additions & 9 deletions osu.Framework/Input/Handlers/Pen/PenHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using osu.Framework.Platform.SDL3;
using osu.Framework.Statistics;
using osuTK;
using osuTK.Input;

namespace osu.Framework.Input.Handlers.Pen
{
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 4 additions & 7 deletions osu.Framework/Input/Handlers/Tablet/OpenTabletDriverHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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<TabletReference> tablets)
{
Expand Down
30 changes: 0 additions & 30 deletions osu.Framework/Input/PassThroughInputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
16 changes: 0 additions & 16 deletions osu.Framework/Input/StateChanges/ISourcedFromPen.cs

This file was deleted.

17 changes: 0 additions & 17 deletions osu.Framework/Input/StateChanges/MouseButtonInputFromPen.cs

This file was deleted.

This file was deleted.

This file was deleted.

5 changes: 5 additions & 0 deletions osu.Framework/Input/TouchSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,10 @@ public enum TouchSource
/// The tenth and last available touch source.
/// </summary>
Touch10,

/// <summary>
/// A touch source that represents a pen/stylus.
/// </summary>
PenTouch,
}
}
9 changes: 0 additions & 9 deletions osu.Framework/Testing/Input/ManualInputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.");

Expand Down Expand Up @@ -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));

Expand Down
Loading