-
Notifications
You must be signed in to change notification settings - Fork 436
/
Copy pathUserInputManager.cs
116 lines (93 loc) · 4.56 KB
/
UserInputManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Immutable;
using System.Drawing;
using osu.Framework.Configuration;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Input.Handlers;
using osu.Framework.Input.StateChanges;
using osu.Framework.Input.StateChanges.Events;
using osu.Framework.Platform;
using osuTK;
using osuTK.Input;
using RectangleF = osu.Framework.Graphics.Primitives.RectangleF;
namespace osu.Framework.Input
{
public partial class UserInputManager : PassThroughInputManager
{
protected override ImmutableArray<InputHandler> InputHandlers => Host.AvailableInputHandlers;
public override bool HandleHoverEvents => Host.Window?.CursorInWindow.Value ?? true;
protected internal override bool ShouldBeAlive => true;
protected internal UserInputManager()
{
// UserInputManager is at the very top of the draw hierarchy, so it has no parent updating its IsAlive state
IsAlive = true;
UseParentInput = false;
}
public override void HandleInputStateChange(InputStateChangeEvent inputStateChange)
{
switch (inputStateChange)
{
case MousePositionChangeEvent mousePositionChange:
var mouse = mousePositionChange.State.Mouse;
// confine cursor
if (Host.Window != null)
{
RectangleF? cursorConfineRect = null;
var clientSize = Host.Window.ClientSize;
var windowRect = new RectangleF(0, 0, clientSize.Width, clientSize.Height);
if (Host.Window.CursorState.HasFlagFast(CursorState.Confined))
{
cursorConfineRect = Host.Window.CursorConfineRect ?? windowRect;
}
else if (mouseOutsideAllDisplays(mouse.Position))
{
// Implicitly confine the cursor to prevent a feedback loop of MouseHandler warping the cursor to an invalid position
// and the OS immediately warping it back inside a display.
// Window.CursorConfineRect is not used here as that should only be used when confining is explicitly enabled.
cursorConfineRect = windowRect;
}
if (cursorConfineRect.HasValue)
mouse.Position = Vector2.Clamp(mouse.Position, cursorConfineRect.Value.Location, cursorConfineRect.Value.Location + cursorConfineRect.Value.Size - Vector2.One);
}
break;
case ButtonStateChangeEvent<MouseButton> buttonChange:
// presses registered when the mouse pointer is outside the window are ignored.
// however, releases registered when the mouse pointer is outside the window cannot be ignored;
// handling them is essential to correctly handling mouse capture (only applicable when relative mode is disabled).
if (buttonChange.Kind == ButtonStateChangeKind.Pressed && Host.Window?.CursorInWindow.Value == false)
return;
break;
case MouseScrollChangeEvent:
if (Host.Window?.CursorInWindow.Value == false)
return;
break;
}
base.HandleInputStateChange(inputStateChange);
}
private bool mouseOutsideAllDisplays(Vector2 mousePosition)
{
Point windowLocation;
switch (Host.Window.WindowMode.Value)
{
case WindowMode.Windowed:
windowLocation = Host.Window.Position;
break;
default:
windowLocation = Host.Window.CurrentDisplayBindable.Value.Bounds.Location;
break;
}
float scale = Host.Window.Scale;
mousePosition /= scale;
int x = (int)MathF.Floor(windowLocation.X + mousePosition.X);
int y = (int)MathF.Floor(windowLocation.Y + mousePosition.Y);
foreach (var display in Host.Window.Displays)
{
if (display.Bounds.Contains(x, y))
return false;
}
return true;
}
}
}