Skip to content

Commit 7eaf84b

Browse files
committed
continue iterating on pointers, continue populating events as they occur
1 parent 3701591 commit 7eaf84b

File tree

10 files changed

+416
-275
lines changed

10 files changed

+416
-275
lines changed

sources/Input/Input/Implementations/EnumInfo.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ internal static class EnumInfo<T> where T : unmanaged, Enum
2626
/// </summary>
2727
public static readonly IReadOnlyList<T> UniqueValues;
2828

29-
3029
/// <summary>
3130
/// The value with the highest numerical value
3231
/// </summary>

sources/Input/Input/Implementations/SDL3/Devices/Pointers/SdlMouse.cs

Lines changed: 72 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
using System.Numerics;
55
using System.Runtime.CompilerServices;
6-
using Silk.NET.Maths;
76
using Silk.NET.SDL;
87

98
namespace Silk.NET.Input.SDL3.Devices.Pointers;
@@ -15,14 +14,17 @@ internal sealed class SdlMouse : SdlPointerDevice, IMouse, ISdlDevice<SdlMouse>
1514

1615
private readonly MouseState _state;
1716

17+
// the mouse is always considered "down" - there is no up/down state for the mouse pointer itself - only its buttons.
18+
private const bool DownState = true;
19+
1820
private SdlMouse(ulong sdlDeviceId, nint uniqueId, SdlInputBackend backend, IPointerTarget unboundedPointerTarget,
1921
ICursorConfiguration cursor)
2022
: base(backend, uniqueId, sdlDeviceId, unboundedPointerTarget)
2123
{
2224
_state = new MouseState(Buttons, Points, Vector2.Zero);
2325
Cursor = cursor;
2426
float x = 0, y = 0;
25-
var mouseInputFlags = GetButtonMaskSdl(ref x, ref y);
27+
var mouseInputFlags = GetMouseState(ref x, ref y);
2628
ApplyMouseButtonState(mouseInputFlags);
2729

2830
var window = NativeBackend.GetMouseFocus();
@@ -40,28 +42,30 @@ private SdlMouse(ulong sdlDeviceId, nint uniqueId, SdlInputBackend backend, IPoi
4042
}
4143
}
4244

43-
var pressure = _state.Buttons[PointerButton.Primary].Pressure;
44-
SetTargetPoint(windowId, new Vector3(x, y, 0), pressure);
45+
// var pressure = _state.Buttons[PointerButton.Primary].Pressure;
46+
AddOrUpdatePoint(0, windowId, new Vector3(x, y, 0), null, DownState, null, true);
4547
// var point = _unboundedPointerTarget.GetPoint(this, 0);
4648
}
4749

50+
4851
private void ApplyMouseButtonState(SdlMouseInputFlags mouseState)
4952
{
5053
foreach (var pointerButtonName in EnumInfo<PointerButton>.UniqueValues)
5154
{
52-
ref var button = ref GetButtonRef(pointerButtonName);
53-
var isDown = mouseState.Has(pointerButtonName);
54-
button = button with { IsDown = isDown, Pressure = isDown ? 1 : 0 };
55+
if (mouseState.Has(pointerButtonName))
56+
{
57+
AddButtonEvent(pointerButtonName, 0, true);
58+
}
5559
}
5660
}
5761

5862
public override void Initialize()
5963
{
60-
6164
}
6265

63-
private unsafe SdlMouseInputFlags GetButtonMaskSdl(ref float x, ref float y) =>
64-
(SdlMouseInputFlags)NativeBackend.GetMouseState((float*)Unsafe.AsPointer(ref x), (float*)Unsafe.AsPointer(ref y));
66+
private unsafe SdlMouseInputFlags GetMouseState(ref float x, ref float y) =>
67+
(SdlMouseInputFlags)NativeBackend.GetMouseState((float*)Unsafe.AsPointer(ref x),
68+
(float*)Unsafe.AsPointer(ref y));
6569

6670
public static SdlMouse CreateDevice(ulong sdlDeviceId, SdlInputBackend backend)
6771
{
@@ -113,66 +117,81 @@ private bool IsMouseRelative
113117
}
114118

115119
protected override bool OnePointOnly => true;
116-
120+
private bool _hintsAsEvents = false;
121+
122+
123+
/// <summary>
124+
/// <inheritdoc/>
125+
/// </summary>
126+
/// <param name="position">The window-relative position of the mouse</param>
127+
/// <returns>True if success, but see the below remarks</returns>
128+
/// <remarks>
129+
/// todo: this is the most straightforward way to do window-relative movement,
130+
/// but we don't actually get any information about whether or not
131+
/// it succeeds. The SDL documentation seems to suggest that it "just works" as opposed to their global mouse warp
132+
/// method, which provides a success result. instead of translating this "global" position to a window-specific one,
133+
/// we're gonna use SDL's implementation of window-relative movement to keep things simple.<br/><br/>
134+
/// As a result, we always return true, though the documentation suggests that it does not work specifically for
135+
/// Microsoft Remote Desktop. not sure how to detect that at the moment.<br/><br/>
136+
/// Another consequence of doing it this way is that we need to wait for SDL to push these events through the event
137+
/// queue, so while this may return 'true', this mouse object will not necessarily be in the correct position until
138+
/// the next time pumped events are processed.<br/><br/>
139+
/// We could just create a mouse motion event and push it through our internal event queues, bypassing SDL's event
140+
/// queue, but that could result in a situation where we set our internal state to represent the
141+
/// movement, even though SDL did not meaningfully succeed.<br/><br/>
142+
/// The best way around this would probably be to try global mouse warping with translated coordinates,
143+
/// and if that fails, fall back to window-relative movement.
144+
/// </remarks>
117145
public bool TrySetPosition(Vector2 position)
118146
{
119-
if (NativeBackend.WarpMouseGlobal(position.X, position.Y))
147+
// make sure we get pumped mouse events for setting the position this way
148+
if (!_hintsAsEvents)
120149
{
121-
SetTargetPoint(null, new Vector3(position.X, position.Y, 0), 0);
122-
return true;
150+
var currentHintVal = NativeBackend.GetHintBoolean(Sdl.HintMouseRelativeWarpMotion, new MaybeBool<byte>(0));
151+
if (currentHintVal == 0)
152+
{
153+
sbyte hintVal = 1;
154+
if(NativeBackend.SetHint(Sdl.HintMouseRelativeWarpMotion, new Ref<sbyte>(ref hintVal)))
155+
{
156+
_hintsAsEvents = true;
157+
}
158+
}
159+
else
160+
{
161+
_hintsAsEvents = true;
162+
}
123163
}
124164

125-
SdlLog.Error("Failed to set mouse position");
126-
return false;
165+
// providing a null window handle means that SDL will use the latest position
166+
NativeBackend.WarpMouseInWindow(default, position.X, position.Y);
167+
NeedsPump = true; // we should "pump" events immediately, but this isn't a good place to do it.
168+
return true;
127169
}
128170

129-
public void AddMotion(in MouseMotionEvent evtMotion)
130-
{
131-
var mouseWindowId = evtMotion.WindowID;
132-
var movementRelative = new Vector3(evtMotion.Xrel, evtMotion.Yrel, 0);
133-
_accumulatedMotion += movementRelative;
134-
// todo - test against evtMotion state values
135-
136-
//SetTargetPoint(mouseWindowId, _accumulatedMotion, 0, 0);
137-
SetTargetPoint(mouseWindowId, new Vector3(evtMotion.X, evtMotion.Y, 0), 0);
138-
}
171+
/// <summary>
172+
/// A user has attempted to modify the hardware mouse position - we need a pump to process these events.
173+
/// </summary>
174+
public bool NeedsPump { get; private set; }
139175

176+
public void AddMotion(in MouseMotionEvent evtMotion) =>
177+
AddOrUpdatePoint(null, evtMotion.WindowID, new Vector3(evtMotion.X, evtMotion.Y, 0), 1, null, null,
178+
evtMotion.WindowID != 0);
140179

141180

142181
public void AddButtonEvent(in MouseButtonEvent evtButton)
143182
{
144183
var button = PointerButton.Primary + evtButton.Button;
145184
const float mult = 1 / 255f;
146-
AddButtonEvent(button, evtButton.Down > 0, evtButton.Down * mult);
185+
AddButtonEvent(button, evtButton.Timestamp, evtButton.Down > 0, evtButton.Down * mult);
147186
}
148187

149188
public void AddWheelEvent(in MouseWheelEvent evtWheel)
150189
{
151-
ref var x = ref _mouseScroll.X;
152-
ref var y = ref _mouseScroll.Y;
153-
x += evtWheel.X;
154-
y += evtWheel.Y;
155-
156-
// todo - evt.Which?
157-
var hMagnitude = MathF.Abs(x);
158-
var vMagnitude = MathF.Abs(y);
159-
160-
if (hMagnitude >= 1)
161-
{
162-
// horizontal scroll "tick"
163-
_mouseScroll.X = 0;
164-
}
165-
166-
if (vMagnitude >= 1)
167-
{
168-
// vertical scroll "tick"
169-
_mouseScroll.Y = 0;
170-
}
171-
172-
_state.WheelPosition = _mouseScroll;
190+
var wheelState = _state.WheelPosition = new Vector2(evtWheel.X, evtWheel.Y);
191+
AddMouseScrollEvent(
192+
scrollWheelPosition: wheelState,
193+
windowId: evtWheel.WindowID,
194+
position: new Vector3(evtWheel.MouseX, evtWheel.MouseY, 0),
195+
isMouseRelative: evtWheel.WindowID != 0);
173196
}
174-
175-
176-
private Vector2 _mouseScroll;
177-
private Vector3 _accumulatedMotion;
178197
}

sources/Input/Input/Implementations/SDL3/Devices/Pointers/SdlPen.cs

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,6 @@ public static SdlPen CreateDevice(ulong sdlDeviceId, SdlInputBackend backend)
4848
SdlPen Create() => new(backend, uniqueId, sdlDeviceId, name.ReadToString(), backend.UnboundedPointerTarget);
4949
}
5050

51-
private void ApplyPenInputState(SdlPenInputFlags penState)
52-
{
53-
foreach (var pointerButtonName in EnumInfo<PointerButton>.UniqueValues)
54-
{
55-
ref var button = ref GetButtonRef(pointerButtonName);
56-
var isDown = penState.Has(pointerButtonName);
57-
button = button with { IsDown = isDown, Pressure = isDown ? 1 : 0 };
58-
}
59-
}
60-
61-
6251
public override void Initialize()
6352
{
6453
}
@@ -77,64 +66,83 @@ public void UpDownEvent(in PenTouchEvent evt)
7766
{
7867
MotionEvent(evt.WindowID, evt.X, evt.Y);
7968

80-
var previousPressure = GetPointPressure(0);
81-
const float divisor = 1f / 255f;
82-
var downPressure = evt.Down * divisor;
83-
if (downPressure > 0)
69+
if (evt.Down > 0)
8470
{
85-
AddButtonEvent(PointerButton.Primary, true, Math.Max(previousPressure, downPressure));
71+
const float divisor = 1f / 255f;
72+
var downPressure = evt.Down * divisor;
73+
AddButtonEvent(PointerButton.Primary, evt.Timestamp, true, downPressure);
8674
}
8775
else
8876
{
89-
AddButtonEvent(PointerButton.Primary, false, 0);
77+
AddButtonEvent(PointerButton.Primary, evt.Timestamp, false, 0);
9078
}
9179
}
9280

9381

9482
[MethodImpl(MethodImplOptions.AggressiveInlining)]
9583
public void MotionEvent(in PenMotionEvent evt) => MotionEvent(evt.WindowID, evt.X, evt.Y);
9684

97-
private void MotionEvent(in uint windowId, float x, float y)
85+
private void MotionEvent(in uint windowId, float x, float y) =>
86+
AddOrUpdatePoint(
87+
touchId: null,
88+
windowId: windowId == 0 ? null : windowId,
89+
pos: new Vector3(x, y, 0),
90+
pressure: null,
91+
isDown: null,
92+
ray: null,
93+
isPositionInWindowSpace: true);
94+
95+
public void ButtonEvent(in PenButtonEvent evt)
9896
{
99-
if (!Backend.TryGetPointerTargetForWindow(windowId, out var target))
100-
{
101-
return;
102-
}
103-
104-
SetTargetPoint(windowId, new Vector3(x, y, 0), GetPointPressure(0),0);
97+
var button = (SdlPenButton)evt.Button;
98+
var pointerButton = button switch {
99+
SdlPenButton.Button1 => PointerButton.Primary,
100+
SdlPenButton.Button2 => PointerButton.Secondary,
101+
SdlPenButton.Button3 => PointerButton.MiddleButton,
102+
SdlPenButton.Button4 => PointerButton.Button4,
103+
SdlPenButton.Button5 => PointerButton.Button5,
104+
_ => throw new ArgumentOutOfRangeException(nameof(button), button, null)
105+
};
106+
107+
AddButtonEvent(pointerButton, evt.Timestamp, evt.Down > 0, evt.Down / 255f);
105108
}
106109

107-
108-
public void ButtonEvent(in PenButtonEvent evt) => ApplyPenInputState((SdlPenInputFlags)evt.PenState);
110+
private enum SdlPenButton : byte
111+
{
112+
Button1 = 1,
113+
Button2,
114+
Button3,
115+
Button4,
116+
Button5,
117+
}
109118

110119
public void AxisEvent(in PenAxisEvent evt)
111120
{
112121
switch (evt.Axis)
113122
{
114123
case PenAxis.Pressure:
115124
{
116-
SetPointPressure(0, evt.Value);
125+
AddOrUpdatePoint(null, null, new Vector3(evt.X, evt.Y, 0), evt.Value, null, null, true);
117126
break;
118-
;
119127
}
120128
case PenAxis.Xtilt:
121129
{
122-
SetPointXTilt(0, evt.Value);
130+
UpdatePointRay(null, evt.Value, null, null, distance: null);
123131
break;
124132
}
125133
case PenAxis.Ytilt:
126134
{
127-
SetPointYTilt(0, evt.Value);
135+
UpdatePointRay(null, null, evt.Value, null, distance: null);
128136
break;
129137
}
130138
case PenAxis.Distance:
131139
{
132-
SetPointDistance(0, evt.Value);
140+
UpdatePointRay(null, null, null, null, distance: evt.Value);
133141
break;
134142
}
135143
case PenAxis.Rotation: // barrel rotation
136144
{
137-
SetPointTwist(0, evt.Value);
145+
UpdatePointRay(null, null, null, evt.Value, distance: null);
138146
break;
139147
}
140148
case PenAxis.Slider:

0 commit comments

Comments
 (0)