Skip to content

Commit 86c81cb

Browse files
committed
Report pen device types on Android
Based on the heuristic that internal `InputDevice`s are direct and external are indirect. Ideally, this would be in SDL, but I'm not sure how to pitch a cross-platform API.
1 parent a846a03 commit 86c81cb

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

osu.Framework.Android/AndroidGameSurface.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// See the LICENCE file in the repository root for full licence text.
33

44
using System;
5+
using System.Diagnostics;
56
using Android.Content;
67
using Android.Runtime;
78
using Org.Libsdl.App;
@@ -11,6 +12,7 @@
1112
using Android.Views;
1213
using AndroidX.Core.View;
1314
using AndroidX.Window.Layout;
15+
using osu.Framework.Input.StateChanges;
1416

1517
namespace osu.Framework.Android
1618
{
@@ -20,6 +22,10 @@ internal class AndroidGameSurface : SDLSurface
2022

2123
public BindableSafeArea SafeAreaPadding { get; } = new BindableSafeArea();
2224

25+
private const TabletPenDeviceType default_pen_device_type = TabletPenDeviceType.Direct;
26+
27+
public volatile TabletPenDeviceType LastPenDeviceType = default_pen_device_type;
28+
2329
public AndroidGameSurface(AndroidGameActivity activity, Context? context)
2430
: base(context)
2531
{
@@ -117,5 +123,61 @@ private void updateSafeArea(WindowInsets? windowInsets)
117123
Bottom = windowArea.Bottom - usableWindowArea.Bottom,
118124
};
119125
}
126+
127+
private TabletPenDeviceType getPenDeviceType(MotionEvent e)
128+
{
129+
var device = e.Device;
130+
131+
if (device == null)
132+
return default_pen_device_type;
133+
134+
return device.IsExternal ? TabletPenDeviceType.Indirect : TabletPenDeviceType.Direct;
135+
}
136+
137+
/// <summary>
138+
/// Sets <see cref="LastPenDeviceType"/> iff <c>SDLGenericMotionListener_API14.onGenericMotion()</c> would call <c>onNativePen()</c>.
139+
/// </summary>
140+
public override bool DispatchGenericMotionEvent(MotionEvent? e)
141+
{
142+
Debug.Assert(e != null);
143+
144+
switch (e.ActionMasked)
145+
{
146+
case MotionEventActions.HoverEnter:
147+
case MotionEventActions.HoverMove:
148+
case MotionEventActions.HoverExit:
149+
for (int i = 0; i < e.PointerCount; i++)
150+
{
151+
var toolType = e.GetToolType(i);
152+
153+
if (toolType == MotionEventToolType.Stylus || toolType == MotionEventToolType.Eraser)
154+
LastPenDeviceType = getPenDeviceType(e);
155+
}
156+
157+
break;
158+
}
159+
160+
return base.DispatchGenericMotionEvent(e);
161+
}
162+
163+
/// <summary>
164+
/// Sets <see cref="LastPenDeviceType"/> iff <c>SDLSurface.onGenericMotion()</c> would call <c>onNativePen()</c>.
165+
/// </summary>
166+
public override bool OnTouch(View? view, MotionEvent? e)
167+
{
168+
Debug.Assert(e != null);
169+
170+
// SDLSurface does some weird checks here for event action index, but it doesn't really matter as we only expect one pen at a time
171+
172+
for (int i = 0; i < e.PointerCount; i++)
173+
{
174+
var toolType = e.GetToolType(i);
175+
176+
if (toolType == MotionEventToolType.Stylus || toolType == MotionEventToolType.Eraser)
177+
LastPenDeviceType = getPenDeviceType(e);
178+
}
179+
180+
return base.OnTouch(view, e);
181+
}
120182
}
121183
}

osu.Framework.Android/AndroidGameWindow.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// See the LICENCE file in the repository root for full licence text.
33

44
using System;
5+
using osu.Framework.Input.StateChanges;
56
using osu.Framework.Platform;
67
using osu.Framework.Platform.SDL3;
8+
using SDL;
79

810
namespace osu.Framework.Android
911
{
@@ -16,6 +18,8 @@ public AndroidGameWindow(GraphicsSurfaceType surfaceType, string appName)
1618
{
1719
}
1820

21+
protected override TabletPenDeviceType GetPenDeviceType(SDL_PenID id) => AndroidGameActivity.Surface.LastPenDeviceType;
22+
1923
public override void Create()
2024
{
2125
base.Create();

0 commit comments

Comments
 (0)