A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Linq;
6using NUnit.Framework;
7using osu.Framework.Graphics;
8using osu.Framework.Graphics.Shapes;
9using osu.Framework.Input;
10using osu.Framework.Input.Events;
11using osu.Framework.Input.States;
12using osu.Framework.Testing;
13using osu.Framework.Testing.Input;
14using osuTK;
15using osuTK.Input;
16
17namespace osu.Framework.Tests.Visual.Input
18{
19 public class TestScenePassThroughInputManager : ManualInputManagerTestScene
20 {
21 public TestScenePassThroughInputManager()
22 {
23 RelativeSizeAxes = Axes.Both;
24 }
25
26 private TestInputManager testInputManager;
27 private InputState state;
28 private ButtonStates<MouseButton> mouse;
29 private ButtonStates<Key> keyboard;
30 private ButtonStates<JoystickButton> joystick;
31
32 private void addTestInputManagerStep()
33 {
34 AddStep("Add InputManager", () =>
35 {
36 testInputManager = new TestInputManager();
37 Add(testInputManager);
38 state = testInputManager.CurrentState;
39 mouse = state.Mouse.Buttons;
40 keyboard = state.Keyboard.Keys;
41 joystick = state.Joystick.Buttons;
42 });
43 }
44
45 [SetUp]
46 public new void SetUp() => Schedule(() =>
47 {
48 ChildrenEnumerable = Enumerable.Empty<Drawable>();
49 });
50
51 [Test]
52 public void ReceiveInitialState()
53 {
54 AddStep("Press mouse left", () => InputManager.PressButton(MouseButton.Left));
55 AddStep("Press A", () => InputManager.PressKey(Key.A));
56 AddStep("Press Joystick", () => InputManager.PressJoystickButton(JoystickButton.Button1));
57 addTestInputManagerStep();
58 AddAssert("mouse left not pressed", () => !mouse.IsPressed(MouseButton.Left));
59 AddAssert("A pressed", () => keyboard.IsPressed(Key.A));
60 AddAssert("Joystick pressed", () => joystick.IsPressed(JoystickButton.Button1));
61 AddStep("Release", () =>
62 {
63 InputManager.ReleaseButton(MouseButton.Left);
64 InputManager.ReleaseKey(Key.A);
65 InputManager.ReleaseJoystickButton(JoystickButton.Button1);
66 });
67 AddAssert("All released", () => !mouse.HasAnyButtonPressed && !keyboard.HasAnyButtonPressed && !joystick.HasAnyButtonPressed);
68 }
69
70 [Test]
71 public void UseParentInputChange()
72 {
73 addTestInputManagerStep();
74 AddStep("Press buttons", () =>
75 {
76 InputManager.PressButton(MouseButton.Left);
77 InputManager.PressKey(Key.A);
78 InputManager.PressJoystickButton(JoystickButton.Button1);
79 });
80 AddAssert("pressed", () => mouse.IsPressed(MouseButton.Left) && keyboard.IsPressed(Key.A) && joystick.IsPressed(JoystickButton.Button1));
81 AddStep("UseParentInput = false", () => testInputManager.UseParentInput = false);
82 AddAssert("still pressed", () => mouse.IsPressed(MouseButton.Left) && keyboard.IsPressed(Key.A) && joystick.IsPressed(JoystickButton.Button1));
83 AddStep("Release on parent", () =>
84 {
85 InputManager.ReleaseButton(MouseButton.Left);
86 InputManager.ReleaseKey(Key.A);
87 InputManager.ReleaseJoystickButton(JoystickButton.Button1);
88 });
89 AddAssert("doen't affect child", () => mouse.IsPressed(MouseButton.Left) && keyboard.IsPressed(Key.A) && joystick.IsPressed(JoystickButton.Button1));
90 AddStep("UseParentInput = true", () => testInputManager.UseParentInput = true);
91 AddAssert("all synced", () => !mouse.IsPressed(MouseButton.Left) && !keyboard.IsPressed(Key.A) && !joystick.IsPressed(JoystickButton.Button1));
92 }
93
94 [Test]
95 public void TestUpReceivedOnDownFromSync()
96 {
97 addTestInputManagerStep();
98 AddStep("UseParentInput = false", () => testInputManager.UseParentInput = false);
99 AddStep("press keyboard", () => InputManager.PressKey(Key.A));
100 AddAssert("key not pressed", () => !testInputManager.CurrentState.Keyboard.Keys.HasAnyButtonPressed);
101
102 AddStep("UseParentInput = true", () => testInputManager.UseParentInput = true);
103 AddAssert("key pressed", () => testInputManager.CurrentState.Keyboard.Keys.Single() == Key.A);
104
105 AddStep("release keyboard", () => InputManager.ReleaseKey(Key.A));
106 AddAssert("key released", () => !testInputManager.CurrentState.Keyboard.Keys.HasAnyButtonPressed);
107 }
108
109 [Test]
110 public void MouseDownNoSync()
111 {
112 addTestInputManagerStep();
113 AddStep("UseParentInput = false", () => testInputManager.UseParentInput = false);
114 AddStep("Press left", () => InputManager.PressButton(MouseButton.Left));
115 AddStep("UseParentInput = true", () => testInputManager.UseParentInput = true);
116 AddAssert("not pressed", () => !mouse.IsPressed(MouseButton.Left));
117 }
118
119 [Test]
120 public void NoMouseUp()
121 {
122 addTestInputManagerStep();
123 AddStep("Press left", () => InputManager.PressButton(MouseButton.Left));
124 AddStep("UseParentInput = false", () => testInputManager.UseParentInput = false);
125 AddStep("Release and press", () =>
126 {
127 InputManager.ReleaseButton(MouseButton.Left);
128 InputManager.PressButton(MouseButton.Left);
129 });
130 AddStep("UseParentInput = true", () => testInputManager.UseParentInput = true);
131 AddAssert("pressed", () => mouse.IsPressed(MouseButton.Left));
132 AddAssert("mouse up count == 0", () => testInputManager.Status.MouseUpCount == 0);
133 }
134
135 [Test]
136 public void TestTouchInput()
137 {
138 addTestInputManagerStep();
139 AddStep("begin first touch", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, Vector2.Zero)));
140 AddAssert("synced properly", () =>
141 testInputManager.CurrentState.Touch.ActiveSources.Single() == TouchSource.Touch1 &&
142 testInputManager.CurrentState.Touch.TouchPositions[(int)TouchSource.Touch1] == Vector2.Zero);
143
144 AddStep("UseParentInput = false", () => testInputManager.UseParentInput = false);
145 AddStep("end first touch", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, Vector2.Zero)));
146 AddStep("begin second touch", () => InputManager.BeginTouch(new Touch(TouchSource.Touch2, Vector2.One)));
147
148 AddStep("UseParentInput = true", () => testInputManager.UseParentInput = true);
149 AddAssert("synced properly", () =>
150 testInputManager.CurrentState.Touch.ActiveSources.Single() == TouchSource.Touch2 &&
151 testInputManager.CurrentState.Touch.TouchPositions[(int)TouchSource.Touch2] == Vector2.One);
152
153 AddStep("end second touch", () => InputManager.EndTouch(new Touch(TouchSource.Touch2, new Vector2(2))));
154 AddAssert("synced properly", () =>
155 !testInputManager.CurrentState.Touch.ActiveSources.HasAnyButtonPressed &&
156 testInputManager.CurrentState.Touch.TouchPositions[(int)TouchSource.Touch2] == new Vector2(2));
157 }
158
159 [Test]
160 public void TestMidiInput()
161 {
162 addTestInputManagerStep();
163
164 AddStep("press C3", () => InputManager.PressMidiKey(MidiKey.C3, 70));
165 AddAssert("synced properly", () =>
166 testInputManager.CurrentState.Midi.Keys.IsPressed(MidiKey.C3)
167 && testInputManager.CurrentState.Midi.Velocities[MidiKey.C3] == 70);
168
169 AddStep("UseParentInput = false", () => testInputManager.UseParentInput = false);
170 AddStep("release C3", () => InputManager.ReleaseMidiKey(MidiKey.C3, 40));
171 AddStep("press F#3", () => InputManager.PressMidiKey(MidiKey.FSharp3, 65));
172
173 AddStep("UseParentInput = true", () => testInputManager.UseParentInput = true);
174 AddAssert("synced properly", () =>
175 !testInputManager.CurrentState.Midi.Keys.IsPressed(MidiKey.C3) &&
176 testInputManager.CurrentState.Midi.Velocities[MidiKey.C3] == 40 &&
177 testInputManager.CurrentState.Midi.Keys.IsPressed(MidiKey.FSharp3) &&
178 testInputManager.CurrentState.Midi.Velocities[MidiKey.FSharp3] == 65);
179 }
180
181 [Test]
182 public void TestMouseTouchProductionOnPassThrough()
183 {
184 addTestInputManagerStep();
185 AddStep("setup hierarchy", () =>
186 {
187 Add(new HandlingBox
188 {
189 Alpha = 0.5f,
190 Depth = 1,
191 RelativeSizeAxes = Axes.Both,
192 OnHandle = e => e is MouseEvent,
193 });
194
195 testInputManager.Add(new HandlingBox
196 {
197 Alpha = 0.5f,
198 RelativeSizeAxes = Axes.Both,
199 OnHandle = e => e is TouchEvent,
200 });
201 });
202
203 AddStep("begin touch", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, testInputManager.ScreenSpaceDrawQuad.Centre)));
204 AddAssert("ensure parent manager produced mouse", () =>
205 InputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left &&
206 InputManager.CurrentState.Mouse.Position == testInputManager.ScreenSpaceDrawQuad.Centre);
207
208 AddAssert("pass-through did not produce mouse", () =>
209 !testInputManager.CurrentState.Mouse.Buttons.HasAnyButtonPressed &&
210 testInputManager.CurrentState.Mouse.Position != testInputManager.ScreenSpaceDrawQuad.Centre);
211
212 AddStep("end touch", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, testInputManager.ScreenSpaceDrawQuad.Centre)));
213
214 AddStep("press mouse", () => InputManager.PressButton(MouseButton.Left));
215 AddAssert("pass-through handled mouse", () => testInputManager.CurrentState.Mouse.Buttons.Single() == MouseButton.Left);
216 }
217
218 [Test]
219 public void TestTabletButtonInput()
220 {
221 addTestInputManagerStep();
222
223 AddStep("press primary pen button", () => InputManager.PressTabletPenButton(TabletPenButton.Primary));
224 AddStep("press auxiliary button 4", () => InputManager.PressTabletAuxiliaryButton(TabletAuxiliaryButton.Button4));
225
226 AddStep("UseParentInput = false", () => testInputManager.UseParentInput = false);
227
228 AddStep("release primary pen button", () => InputManager.ReleaseTabletPenButton(TabletPenButton.Primary));
229 AddStep("press tertiary pen button", () => InputManager.PressTabletPenButton(TabletPenButton.Tertiary));
230 AddStep("release auxiliary button 4", () => InputManager.ReleaseTabletAuxiliaryButton(TabletAuxiliaryButton.Button4));
231 AddStep("press auxiliary button 2", () => InputManager.PressTabletAuxiliaryButton(TabletAuxiliaryButton.Button2));
232
233 AddStep("UseParentInput = true", () => testInputManager.UseParentInput = true);
234 AddAssert("pen buttons synced properly", () =>
235 !testInputManager.CurrentState.Tablet.PenButtons.Contains(TabletPenButton.Primary)
236 && testInputManager.CurrentState.Tablet.PenButtons.Contains(TabletPenButton.Tertiary));
237 AddAssert("auxiliary buttons synced properly", () =>
238 !testInputManager.CurrentState.Tablet.AuxiliaryButtons.Contains(TabletAuxiliaryButton.Button4)
239 && testInputManager.CurrentState.Tablet.AuxiliaryButtons.Contains(TabletAuxiliaryButton.Button2));
240 }
241
242 public class TestInputManager : ManualInputManager
243 {
244 public readonly TestSceneInputManager.ContainingInputManagerStatusText Status;
245
246 public TestInputManager()
247 {
248 Size = new Vector2(0.8f);
249 Origin = Anchor.Centre;
250 Anchor = Anchor.Centre;
251 Child = Status = new TestSceneInputManager.ContainingInputManagerStatusText();
252 }
253 }
254
255 public class HandlingBox : Box
256 {
257 public Func<UIEvent, bool> OnHandle;
258
259 protected override bool Handle(UIEvent e) => OnHandle?.Invoke(e) ?? false;
260 }
261 }
262}