forked from ppy/osu-framework
/
OpenTKRawMouseHandler.cs
120 lines (99 loc) · 4.99 KB
/
OpenTKRawMouseHandler.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
117
118
119
120
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
using System.Drawing;
using osu.Framework.Configuration;
using osu.Framework.Input;
using osu.Framework.Input.Handlers;
using osu.Framework.Platform;
using osu.Framework.Threading;
using OpenTK;
using osu.Framework.Statistics;
namespace osu.Framework.Desktop.Input.Handlers.Mouse
{
internal class OpenTKRawMouseHandler : InputHandler, IHasCursorSensitivity
{
private ScheduledDelegate scheduled;
private OpenTK.Input.MouseState? lastState;
private Vector2 currentPosition;
private bool mouseInWindow;
private readonly BindableDouble sensitivity = new BindableDouble(1) { MinValue = 0.1, MaxValue = 10 };
public BindableDouble Sensitivity => sensitivity;
public override bool Initialize(GameHost host)
{
host.Window.MouseEnter += window_MouseEnter;
host.Window.MouseLeave += window_MouseLeave;
Enabled.ValueChanged += enabled =>
{
if (enabled)
{
host.InputThread.Scheduler.Add(scheduled = new ScheduledDelegate(delegate
{
if (!host.Window.Visible)
return;
bool useRawInput = mouseInWindow && host.Window.Focused;
var state = useRawInput ? OpenTK.Input.Mouse.GetState() : OpenTK.Input.Mouse.GetCursorState();
if (state.Equals(lastState))
return;
if (useRawInput)
{
if (!lastState.HasValue)
{
// when we return from being outside of the window, we want to set the new position of our game cursor
// to where the OS cursor is, just once.
var cursorState = OpenTK.Input.Mouse.GetCursorState();
var screenPoint = host.Window.PointToClient(new Point(cursorState.X, cursorState.Y));
currentPosition = new Vector2(screenPoint.X, screenPoint.Y);
}
else
{
currentPosition += new Vector2(state.X - lastState.Value.X, state.Y - lastState.Value.Y) * (float)sensitivity.Value;
// update the windows cursor to match our raw cursor position.
// this is important when sensitivity is decreased below 1.0, where we need to ensure the cursor stays withing the window.
var screenPoint = host.Window.PointToScreen(new Point((int)currentPosition.X, (int)currentPosition.Y));
OpenTK.Input.Mouse.SetPosition(screenPoint.X, screenPoint.Y);
}
}
else
{
var screenPoint = host.Window.PointToClient(new Point(state.X, state.Y));
currentPosition = new Vector2(screenPoint.X, screenPoint.Y);
}
// While not focused, let's silently ignore everything but position.
if (host.Window.Focused)
{
lastState = state;
}
else
{
lastState = null;
state = new OpenTK.Input.MouseState();
}
PendingStates.Enqueue(new InputState { Mouse = new OpenTKPollMouseState(state, host.IsActive, currentPosition) });
FrameStatistics.Increment(StatisticsCounterType.MouseEvents);
}, 0, 0));
}
else
{
scheduled?.Cancel();
lastState = null;
}
};
Enabled.TriggerChange();
return true;
}
private void window_MouseLeave(object sender, System.EventArgs e) => mouseInWindow = false;
private void window_MouseEnter(object sender, System.EventArgs e)
{
lastState = null;
mouseInWindow = true;
}
/// <summary>
/// This input handler is always active, handling the cursor position if no other input handler does.
/// </summary>
public override bool IsActive => true;
/// <summary>
/// Lowest priority. We want the normal mouse handler to only kick in if all other handlers don't do anything.
/// </summary>
public override int Priority => 0;
}
}