Skip to content

Commit e1b844f

Browse files
committed
Merge branch 'master' into add-decimal-type
2 parents 6d04d54 + 6bbcfd8 commit e1b844f

26 files changed

+443
-152
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ jobs:
104104

105105
build-only-android:
106106
name: Build only (Android)
107-
runs-on: windows-latest
107+
runs-on: windows-2019
108108
timeout-minutes: 60
109109
steps:
110110
- name: Checkout
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using osu.Framework.Allocation;
2-
using osu.Framework.Platform;
32
using NUnit.Framework;
43

54
namespace TemplateGame.Game.Tests.Visual
@@ -10,15 +9,10 @@ public partial class TestSceneTemplateGameGame : TemplateGameTestScene
109
// Add visual tests to ensure correct behaviour of your game: https://github.com/ppy/osu-framework/wiki/Development-and-Testing
1110
// You can make changes to classes associated with the tests and they will recompile and update immediately.
1211

13-
private TemplateGameGame game;
14-
1512
[BackgroundDependencyLoader]
16-
private void load(GameHost host)
13+
private void load()
1714
{
18-
game = new TemplateGameGame();
19-
game.SetHost(host);
20-
21-
AddGame(game);
15+
AddGame(new TemplateGameGame());
2216
}
2317
}
2418
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using osu.Framework.Allocation;
2-
using osu.Framework.Platform;
32
using NUnit.Framework;
43

54
namespace FlappyDon.Game.Tests.Visual
@@ -11,14 +10,10 @@ namespace FlappyDon.Game.Tests.Visual
1110
[TestFixture]
1211
public partial class TestSceneFlappyDonGame : FlappyDonTestScene
1312
{
14-
private FlappyDonGame game;
15-
1613
[BackgroundDependencyLoader]
17-
private void load(GameHost host)
14+
private void load()
1815
{
19-
game = new FlappyDonGame();
20-
game.SetHost(host);
21-
AddGame(game);
16+
AddGame(new FlappyDonGame());
2217
}
2318
}
2419
}

osu.Framework.Tests/Visual/Input/TestSceneInputManager.cs

+5
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ private void load()
213213
setCursorConfineRect(false);
214214

215215
AddStep("Reset handlers", () => host.ResetInputHandlers());
216+
217+
AddLabel("Input handlers");
218+
219+
foreach (var h in host.AvailableInputHandlers)
220+
AddToggleStep($"{h.Description} enabled", v => h.Enabled.Value = v);
216221
}
217222

218223
private void setCursorSensitivityConfig(double sensitivity)

osu.Framework.Tests/Visual/Input/TestScenePassThroughInputManager.cs

+112
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using osu.Framework.Graphics.Shapes;
1111
using osu.Framework.Input;
1212
using osu.Framework.Input.Events;
13+
using osu.Framework.Input.StateChanges;
1314
using osu.Framework.Input.States;
1415
using osu.Framework.Testing;
1516
using osu.Framework.Testing.Input;
@@ -272,6 +273,63 @@ public void TestMouseTouchProductionOnPassThrough()
272273
AddAssert("pass-through handled mouse", () => testInputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left);
273274
}
274275

276+
[Test]
277+
public void TestPenInputPassThrough()
278+
{
279+
MouseBox outer = null!;
280+
MouseBox inner = null!;
281+
282+
addTestInputManagerStep();
283+
AddStep("setup hierarchy", () =>
284+
{
285+
Add(outer = new MouseBox
286+
{
287+
Alpha = 0.5f,
288+
Depth = 1,
289+
RelativeSizeAxes = Axes.Both,
290+
});
291+
292+
testInputManager.Add(inner = new MouseBox
293+
{
294+
Alpha = 0.5f,
295+
RelativeSizeAxes = Axes.Both,
296+
});
297+
});
298+
299+
AddStep("move pen to box", () => InputManager.MovePenTo(testInputManager));
300+
301+
AddAssert("ensure parent manager produced mouse", () => InputManager.CurrentState.Mouse.Position == testInputManager.ScreenSpaceDrawQuad.Centre);
302+
AddAssert("ensure pass-through produced mouse", () => testInputManager.CurrentState.Mouse.Position == testInputManager.ScreenSpaceDrawQuad.Centre);
303+
304+
AddAssert("outer box received 1 pen event", () => outer.PenEvents, () => Is.EqualTo(1));
305+
AddAssert("outer box received no mouse events", () => outer.MouseEvents, () => Is.EqualTo(0));
306+
307+
AddAssert("inner box received 1 pen event", () => inner.PenEvents, () => Is.EqualTo(1));
308+
AddAssert("inner box received no mouse events", () => inner.MouseEvents, () => Is.EqualTo(0));
309+
310+
AddStep("press pen", () => InputManager.PressPen());
311+
312+
AddAssert("ensure parent manager produced mouse", () => InputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left);
313+
AddAssert("ensure pass-through produced mouse", () => testInputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left);
314+
315+
AddAssert("outer box received 2 pen events", () => outer.PenEvents, () => Is.EqualTo(2));
316+
AddAssert("outer box received no mouse events", () => outer.MouseEvents, () => Is.EqualTo(0));
317+
318+
AddAssert("inner box received 2 pen events", () => inner.PenEvents, () => Is.EqualTo(2));
319+
AddAssert("inner box received no mouse events", () => inner.MouseEvents, () => Is.EqualTo(0));
320+
321+
AddStep("release pen", () => InputManager.ReleasePen());
322+
323+
AddAssert("ensure parent manager produced mouse", () => InputManager.CurrentState.Mouse.Buttons.HasAnyButtonPressed, () => Is.False);
324+
AddAssert("ensure pass-through produced mouse", () => testInputManager.CurrentState.Mouse.Buttons.HasAnyButtonPressed, () => Is.False);
325+
326+
AddAssert("outer box received 3 pen events", () => outer.PenEvents, () => Is.EqualTo(3));
327+
AddAssert("outer box received no mouse events", () => outer.MouseEvents, () => Is.EqualTo(0));
328+
329+
AddAssert("inner box received 3 pen events", () => inner.PenEvents, () => Is.EqualTo(3));
330+
AddAssert("inner box received no mouse events", () => inner.MouseEvents, () => Is.EqualTo(0));
331+
}
332+
275333
public partial class TestInputManager : ManualInputManager
276334
{
277335
public readonly TestSceneInputManager.ContainingInputManagerStatusText Status;
@@ -291,5 +349,59 @@ public partial class HandlingBox : Box
291349

292350
protected override bool Handle(UIEvent e) => OnHandle?.Invoke(e) ?? false;
293351
}
352+
353+
public partial class MouseBox : Box
354+
{
355+
public int MouseEvents { get; private set; }
356+
public int PenEvents { get; private set; }
357+
358+
protected override bool OnMouseMove(MouseMoveEvent e)
359+
{
360+
switch (e.CurrentState.Mouse.LastSource)
361+
{
362+
case ISourcedFromPen:
363+
PenEvents++;
364+
break;
365+
366+
default:
367+
MouseEvents++;
368+
break;
369+
}
370+
371+
return base.OnMouseMove(e);
372+
}
373+
374+
protected override bool OnMouseDown(MouseDownEvent e)
375+
{
376+
switch (e.CurrentState.Mouse.LastSource)
377+
{
378+
case ISourcedFromPen:
379+
PenEvents++;
380+
break;
381+
382+
default:
383+
MouseEvents++;
384+
break;
385+
}
386+
387+
return base.OnMouseDown(e);
388+
}
389+
390+
protected override void OnMouseUp(MouseUpEvent e)
391+
{
392+
switch (e.CurrentState.Mouse.LastSource)
393+
{
394+
case ISourcedFromPen:
395+
PenEvents++;
396+
break;
397+
398+
default:
399+
MouseEvents++;
400+
break;
401+
}
402+
403+
base.OnMouseUp(e);
404+
}
405+
}
294406
}
295407
}

osu.Framework.iOS/IOSFilePresenter.cs

+11-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.IO;
6+
using CoreGraphics;
67
using Foundation;
78
using UIKit;
89
using UniformTypeIdentifiers;
@@ -26,16 +27,24 @@ public bool OpenFile(string filename)
2627
if (documentInteraction.PresentPreview(true))
2728
return true;
2829

30+
// Since file menus on iPadOS appear in a popover-like style, UIDocumentInteractionController requires specifying
31+
// a rectangle in the present functions to display the menu as a popover around that rectangle.
32+
// Ultimately, we want to be given a rectangle by the game so the popover doesn't look out of place,
33+
// but for the time being, specify CGRectZero to make the popover display at the top left.
2934
var gameView = window.ViewController.View!;
30-
return documentInteraction.PresentOpenInMenu(gameView.Bounds, gameView, true);
35+
return documentInteraction.PresentOpenInMenu(CGRect.Empty, gameView, true);
3136
}
3237

3338
public bool PresentFile(string filename)
3439
{
3540
setupViewController(filename);
3641

42+
// Since file menus on iPadOS appear in a popover-like style, UIDocumentInteractionController requires specifying
43+
// a rectangle in the present functions to display the menu as a popover around that rectangle.
44+
// Ultimately, we want to be given a rectangle by the game so the popover doesn't look out of place,
45+
// but for the time being, specify CGRectZero to make the popover display at the top left.
3746
var gameView = window.ViewController.View!;
38-
return documentInteraction.PresentOptionsMenu(gameView.Bounds, gameView, true);
47+
return documentInteraction.PresentOptionsMenu(CGRect.Empty, gameView, true);
3948
}
4049

4150
private void setupViewController(string filename)

osu.Framework.iOS/IOSGameHost.cs

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class IOSGameHost : SDLGameHost
3030

3131
private IOSFilePresenter presenter = null!;
3232

33+
public override bool OnScreenKeyboardOverlapsGameWindow => true;
34+
3335
public IOSGameHost()
3436
: base(string.Empty)
3537
{

osu.Framework/FrameworkEnvironment.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ static FrameworkEnvironment()
5353
if (DebugUtils.IsDebugBuild)
5454
AllowInsecureRequests = parseBool(Environment.GetEnvironmentVariable("OSU_INSECURE_REQUESTS")) ?? false;
5555

56-
UseSDL3 = RuntimeInfo.IsMobile || (parseBool(Environment.GetEnvironmentVariable("OSU_SDL3")) ?? false);
56+
UseSDL3 = RuntimeInfo.IsMobile || (parseBool(Environment.GetEnvironmentVariable("OSU_SDL3")) ?? true);
5757
}
5858

5959
private static bool? parseBool(string? value)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
2+
// See the LICENCE file in the repository root for full licence text.
3+
4+
using osu.Framework.Input.StateChanges;
5+
using osu.Framework.Platform;
6+
using osu.Framework.Platform.SDL3;
7+
using osu.Framework.Statistics;
8+
using osuTK;
9+
10+
namespace osu.Framework.Input.Handlers.Pen
11+
{
12+
/// <summary>
13+
/// SDL3 pen handler
14+
/// </summary>
15+
public class PenHandler : InputHandler
16+
{
17+
private static readonly GlobalStatistic<ulong> statistic_total_events = GlobalStatistics.Get<ulong>(StatisticGroupFor<PenHandler>(), "Total events");
18+
19+
public override bool IsActive => true;
20+
21+
public override bool Initialize(GameHost host)
22+
{
23+
if (!base.Initialize(host))
24+
return false;
25+
26+
if (host.Window is not SDL3Window window)
27+
return false;
28+
29+
Enabled.BindValueChanged(enabled =>
30+
{
31+
if (enabled.NewValue)
32+
{
33+
window.PenMove += handlePenMove;
34+
window.PenTouch += handlePenTouch;
35+
window.PenButton += handlePenButton;
36+
}
37+
else
38+
{
39+
window.PenMove -= handlePenMove;
40+
window.PenTouch -= handlePenTouch;
41+
window.PenButton -= handlePenButton;
42+
}
43+
}, true);
44+
45+
return true;
46+
}
47+
48+
// iPadOS doesn't support external tablets, so we are sure it's direct Apple Pencil input.
49+
// Other platforms support both direct and indirect tablet input, but SDL doesn't provide any information on the current device type.
50+
private static readonly TabletPenDeviceType device_type = RuntimeInfo.OS == RuntimeInfo.Platform.iOS ? TabletPenDeviceType.Direct : TabletPenDeviceType.Unknown;
51+
52+
private void handlePenMove(Vector2 position)
53+
{
54+
enqueueInput(new MousePositionAbsoluteInputFromPen
55+
{
56+
Position = position,
57+
DeviceType = device_type
58+
});
59+
}
60+
61+
private void handlePenTouch(bool pressed)
62+
{
63+
enqueueInput(new MouseButtonInputFromPen(pressed) { DeviceType = device_type });
64+
}
65+
66+
private void handlePenButton(TabletPenButton button, bool pressed)
67+
{
68+
enqueueInput(new TabletPenButtonInput(button, pressed));
69+
}
70+
71+
private void enqueueInput(IInput input)
72+
{
73+
PendingInputs.Enqueue(input);
74+
FrameStatistics.Increment(StatisticsCounterType.TabletEvents);
75+
statistic_total_events.Value++;
76+
}
77+
}
78+
}

osu.Framework/Input/Handlers/Tablet/OpenTabletDriverHandler.cs

+13-3
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,21 @@ public override bool Initialize(GameHost host)
8888
return true;
8989
}
9090

91-
void IAbsolutePointer.SetPosition(System.Numerics.Vector2 pos) => enqueueInput(new MousePositionAbsoluteInput { Position = new Vector2(pos.X, pos.Y) });
91+
private TabletPenDeviceType lastTabletDeviceType = TabletPenDeviceType.Unknown;
9292

93-
void IRelativePointer.SetPosition(System.Numerics.Vector2 delta) => enqueueInput(new MousePositionRelativeInput { Delta = new Vector2(delta.X, delta.Y) });
93+
void IAbsolutePointer.SetPosition(System.Numerics.Vector2 pos)
94+
{
95+
lastTabletDeviceType = TabletPenDeviceType.Unknown;
96+
enqueueInput(new MousePositionAbsoluteInputFromPen { Position = new Vector2(pos.X, pos.Y), DeviceType = lastTabletDeviceType });
97+
}
98+
99+
void IRelativePointer.SetPosition(System.Numerics.Vector2 delta)
100+
{
101+
lastTabletDeviceType = TabletPenDeviceType.Indirect;
102+
enqueueInput(new MousePositionRelativeInputFromPen { Delta = new Vector2(delta.X, delta.Y), DeviceType = lastTabletDeviceType });
103+
}
94104

95-
void IPressureHandler.SetPressure(float percentage) => enqueueInput(new MouseButtonInput(osuTK.Input.MouseButton.Left, percentage > 0));
105+
void IPressureHandler.SetPressure(float percentage) => enqueueInput(new MouseButtonInputFromPen(percentage > 0) { DeviceType = lastTabletDeviceType });
96106

97107
private void handleTabletsChanged(object? sender, IEnumerable<TabletReference> tablets)
98108
{

0 commit comments

Comments
 (0)