Skip to content

Commit 09a8deb

Browse files
committed
lots of stuff
revert earlier pointer event workarounds, ensure sdl events are sorted correctly each frame, remove unused code, CustomCursor equality operators for ease of use
1 parent 9abcba2 commit 09a8deb

5 files changed

Lines changed: 91 additions & 144 deletions

File tree

sources/Input/Input/CustomCursor.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace Silk.NET.Input;
33
/// <summary>
44
/// Represents a custom image for a mouse cursor.
55
/// </summary>
6-
public readonly ref struct CustomCursor
6+
public readonly ref struct CustomCursor : IEquatable<CustomCursor>
77
{
88
/// <summary>
99
/// The number of pixels in the X axis.
@@ -35,4 +35,35 @@ public readonly ref struct CustomCursor
3535
/// </summary>
3636
/// <remarks>Note that this operator does not consider reference equality</remarks>
3737
public static bool operator !=(CustomCursor left, CustomCursor right) => !(left == right);
38+
39+
/// <summary>
40+
/// Value-based equality check.
41+
/// </summary>
42+
/// <param name="other"></param>
43+
/// <returns></returns>
44+
public bool Equals(CustomCursor other) => Width == other.Width && Height == other.Height && Data.SequenceEqual(other.Data);
45+
46+
/// <summary>
47+
/// Value-based hashcode
48+
/// </summary>
49+
public override int GetHashCode()
50+
{
51+
HashCode hashCode = new();
52+
hashCode.Add(Width);
53+
hashCode.Add(Height);
54+
for (var i = 0; i < Data.Length; ++i)
55+
{
56+
hashCode.Add(Data[i]);
57+
}
58+
59+
return hashCode.ToHashCode();
60+
}
61+
62+
/// <summary>
63+
/// Reference-based equality check. Because this is a <c>ref struct</c>, this will always return <c>false</c>, as
64+
/// this cannot escape the stack, and thus cannot be boxed, and thus cannot be cast to/from <c>object</c>.
65+
/// </summary>
66+
/// <param name="o"></param>
67+
/// <returns></returns>
68+
public override bool Equals(object? o) => false;
3869
}

sources/Input/Input/Handlers/Pointers.cs

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Numerics;
44
using System.Runtime.CompilerServices;
55
using System.Runtime.InteropServices;
6+
using Silk.NET.Input.SDL3;
67

78
namespace Silk.NET.Input;
89

@@ -132,8 +133,6 @@ internal void HandleTargetChanged(PointerTargetChangedEvent @event)
132133
_clicks.RemoveAt(i--);
133134

134135
// SAFETY: We have to replace the span now as the RemoveAt could've in theory reallocated.
135-
// note from dom:
136-
// looking at RemoveAt source, i don't know if this is necessary? but maybe for earlier runtimes it is?
137136
clicks = CollectionsMarshal.AsSpan(_clicks);
138137
}
139138
}
@@ -143,7 +142,15 @@ void IPointerInputHandler.HandlePointChanged(PointChangedEvent @event) =>
143142

144143
internal void HandlePointChanged(PointChangedEvent @event)
145144
{
146-
PointChanged?.Invoke(@event);
145+
try
146+
{
147+
PointChanged?.Invoke(@event);
148+
}
149+
catch (Exception e)
150+
{
151+
InputLog.Error($"Exception while handling point changed event: {e.Message}\n{e.StackTrace}");
152+
}
153+
147154
if (_clicks is null || @event is not { OldPoint: not null, NewPoint: { } @new })
148155
{
149156
return;
@@ -216,17 +223,15 @@ out int idx
216223

217224
_clicks.Add(
218225
new ClickData(
219-
Device: device,
220-
FirstClickButton: null,
221-
222-
// why wipe the data like this?
223-
FirstClickPosition: default(TargetPoint) with
226+
device,
227+
null,
228+
default(TargetPoint) with
224229
{
225230
Target = point.Target,
226231
Id = point.Id,
227232
},
228-
FirstClickTime: null,
229-
IsFirstClick: true
233+
null,
234+
true
230235
)
231236
);
232237
return ref CollectionsMarshal.AsSpan(_clicks)[idx];
@@ -239,41 +244,30 @@ private void HandlePointerDown(
239244
long timestamp
240245
)
241246
{
242-
if ((DoubleClick is null && Click is null) || point.Target is null)
247+
if ((_clicks is null && DoubleClick is null && Click is null) || point.Target is null)
243248
{
244249
return;
245250
}
246251

247252
ref var click = ref GetClickData(device, in point, out var idx);
248-
249-
if (click.IsFirstClick)
250-
{
251-
var time = click.FirstClickTime;
252-
click.FirstClickTime = null;
253-
}
254-
255-
// If it's the first click or the click's prior button is different, then...
256253
if (click.IsFirstClick || (click.FirstClickButton is { } firstBtn && firstBtn != button))
257254
{
258-
// ... this is the first click with the given mouse button.
255+
// This is the first click with the given mouse button.
259256
var time = click.FirstClickTime;
260257
click.FirstClickTime = null;
261258

262-
// if the click is the second click, then ...
263259
if (
264260
click is { IsFirstClick: false, FirstClickButton: { } prevBtn }
265261
&& time is { } clickTime
266262
)
267263
{
268264
// Only the mouse buttons differ so treat last click as a single click.
269-
Click?.Invoke(
270-
new PointerClickEvent(device, clickTime, click.FirstClickPosition, prevBtn)
271-
);
265+
InvokeClick(new PointerClickEvent(device, clickTime, click.FirstClickPosition, prevBtn));
272266
}
273267
}
274-
else // otherwise...
268+
else
275269
{
276-
// ... this is the second click with the same mouse button.
270+
// This is the second click with the same mouse button.
277271
if (click.FirstClickTime is { } fct && timestamp - fct <= _doubleClickTime)
278272
{
279273
// Within the maximum double click time.
@@ -290,7 +284,7 @@ long timestamp
290284

291285
// Second click was in time but outside range -> single click.
292286
// The second click is another "first click".
293-
Click?.Invoke(new PointerClickEvent(device, timestamp, point, button));
287+
InvokeClick(new PointerClickEvent(device, timestamp, point, button));
294288
}
295289
else
296290
{
@@ -325,12 +319,13 @@ long timestamp
325319

326320
private void HandleDoubleClickExceedsParameters(ref ClickData click)
327321
{
328-
click.FirstClickTime = null;
329-
click.IsFirstClick = true;
330322
if (click is { FirstClickButton: { } fcb, FirstClickTime: { } fct })
331323
{
332-
Click?.Invoke(new PointerClickEvent(click.Device, fct, click.FirstClickPosition, fcb));
324+
InvokeClick(new PointerClickEvent(click.Device, fct, click.FirstClickPosition, fcb));
333325
}
326+
327+
click.FirstClickTime = null;
328+
click.IsFirstClick = true;
334329
}
335330

336331
internal void ProcessClicks()
@@ -345,7 +340,7 @@ internal void ProcessClicks()
345340
for (var i = 0; i < clicks.Length; i++)
346341
{
347342
ref var click = ref clicks[i];
348-
if (click.FirstClickTime is not { } fct || updateTime - fct <= _doubleClickTime)
343+
if (click.FirstClickTime is not { } firstTime || updateTime - firstTime <= _doubleClickTime)
349344
{
350345
continue;
351346
}
@@ -359,6 +354,18 @@ internal void ProcessClicks()
359354
}
360355
}
361356

357+
private void InvokeClick(PointerClickEvent evt)
358+
{
359+
try
360+
{
361+
Click?.Invoke(evt);
362+
}
363+
catch (Exception e)
364+
{
365+
InputLog.Error($"Exception during click event: {e.Message}\n{e.StackTrace}");
366+
}
367+
}
368+
362369
[MethodImpl(MethodImplOptions.AggressiveInlining)]
363370
void IInputHandler<PointerTargetChangedEvent>.Handle(PointerTargetChangedEvent @event) => HandleTargetChanged(@event);
364371

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

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using System.Diagnostics.CodeAnalysis;
56
using System.Numerics;
67
using System.Runtime.CompilerServices;
@@ -38,8 +39,7 @@ protected void AddButtonEvent(PointerButton button, long timestamp, ulong sdlTim
3839
_buttons.Add(_unknownButton);
3940
}
4041

41-
var buttonSpan = CollectionsMarshal.AsSpan(_buttons);
42-
ref var myButton = ref buttonSpan[idx];
42+
ref var myButton = ref CollectionsMarshal.AsSpan(_buttons)[idx];
4343
var original = myButton;
4444
myButton = new Button<PointerButton>(button, isDown, pressure.Value);
4545

@@ -52,14 +52,6 @@ protected void AddButtonEvent(PointerButton button, long timestamp, ulong sdlTim
5252

5353
private static readonly Button<PointerButton> _unknownButton = new(PointerButton.Unknown, false, 0f);
5454

55-
private ref Button<PointerButton> GetButtonRef(PointerButton button)
56-
{
57-
var index = EnumInfo<PointerButton>.ValueIndexOfUnnamed(button);
58-
_buttons.EnsureCapacity(index + 1);
59-
var span = CollectionsMarshal.AsSpan(_buttons);
60-
return ref span[index];
61-
}
62-
6355

6456
private readonly List<Button<PointerButton>> _buttons = new(EnumInfo<PointerButton>.UniqueValues.Count);
6557
protected ButtonReadOnlyList<PointerButton> Buttons => new(_buttons);
@@ -100,7 +92,7 @@ void RepopulateActiveTargets()
10092
}
10193
}
10294

103-
private unsafe ref TargetPoint CreateOrUpdateTargetPoint(IPointerTarget? target, uint touchId,
95+
private unsafe ref TargetPoint CreateOrUpdateTargetPoint(IPointerTarget target, uint touchId,
10496
in Vector3? positionOnTarget,
10597
Ray3D<float>? ray, float? pressure, out TargetPoint? oldPoint)
10698
{
@@ -111,6 +103,8 @@ private unsafe ref TargetPoint CreateOrUpdateTargetPoint(IPointerTarget? target,
111103
"provided touchId must be 0.");
112104
}
113105

106+
Debug.Assert(target != null, "The target must be non-null for a valid point.");
107+
114108
target ??= _unboundedPointerTarget;
115109

116110
int? pointIndex = null;
@@ -241,10 +235,11 @@ protected void AddOrUpdatePoint(uint? touchId, uint? windowId, in Vector3? pos,
241235
}
242236

243237
private void GetPointIdentifiers([NotNull] ref uint? touchId, [DisallowNull] uint? windowId,
244-
out IPointerTarget? windowTarget)
238+
out IPointerTarget windowTarget)
245239
{
246240
touchId = ValidateTouchId(touchId);
247-
_ = Backend.TryGetPointerTargetForWindow(windowId.Value, out windowTarget);
241+
var success = Backend.TryGetPointerTargetForWindow(windowId.Value, out windowTarget!);
242+
Debug.Assert(success, "No pointer target found for window id {windowId} - this should never happen.");
248243

249244
return;
250245

@@ -366,8 +361,7 @@ private ref TargetPoint GetPointRef(int index)
366361
_points.Add(default);
367362
}
368363

369-
var span = CollectionsMarshal.AsSpan(_points);
370-
return ref span[index];
364+
return ref CollectionsMarshal.AsSpan(_points)[index];
371365
}
372366

373367
private uint _previousWindowId;

sources/Input/Input/Implementations/SDL3/FlagExtensions.cs

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Frozen;
5-
using System.Numerics;
6-
using System.Runtime.CompilerServices;
75

86
namespace Silk.NET.Input.SDL3;
97

@@ -12,72 +10,8 @@ internal static class FlagExtensions
1210
/// <param name="flags"></param>
1311
extension(SdlMouseInputFlags flags)
1412
{
15-
/// <summary>
16-
/// Returns the number of flags set in the given flags enum.
17-
/// </summary>
18-
/// <remarks>Works by counting the individual bits</remarks>
19-
/// <returns></returns>
20-
public int Count
21-
{
22-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23-
get => BitOperations.PopCount((uint)flags);
24-
}
25-
26-
/// <summary>
27-
/// Returns the generic <see cref="PointerButton"/>s for the sdl mouse input flags.
28-
/// </summary>
29-
/// <returns></returns>
30-
/// <exception cref="InvalidOperationException">If the input <see cref="SdlMouseInputFlags"/> has more than
31-
/// one button pressed</exception>
32-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
33-
public PointerButton ToPointerButton() => flags.Count > 0
34-
? throw new InvalidOperationException($"Too many buttons set: {BitOperations.PopCount((uint)flags)}")
35-
: _mouseButtonFlagsToPointerButton[flags];
36-
3713
public bool Has(PointerButton button) => _pointerButtonToMouseFlags.TryGetValue(button, out var converted)
3814
&& (flags & converted) == converted;
39-
40-
public void Overwrite(in Button<PointerButton>[] buttons)
41-
{
42-
for(int i = 0; i < buttons.Length; i++)
43-
{
44-
ref var button = ref buttons[i];
45-
var down = flags.Has(button.Name);
46-
button = button with { IsDown = down, Pressure = down ? 1 : 0 };
47-
}
48-
}
49-
}
50-
51-
extension(SdlPenInputFlags flags)
52-
{
53-
/// <summary>
54-
/// Returns the number of flags set in the given flags enum.
55-
/// </summary>
56-
/// <remarks>Works by counting the individual bits</remarks>
57-
/// <returns></returns>
58-
public int Count
59-
{
60-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
61-
get => BitOperations.PopCount((uint)(flags ^ SdlPenInputFlags.PenInProximity));
62-
}
63-
64-
public bool Has(PointerButton button) => _pointerButtonToPenFlags.TryGetValue(button, out var converted)
65-
&& (flags & converted) == converted;
66-
67-
/// <summary>
68-
/// Returns the generic <see cref="PointerButton"/>s for the sdl mouse input flags.
69-
/// </summary>
70-
/// <returns></returns>
71-
/// <exception cref="InvalidOperationException">If the input <see cref="SdlMouseInputFlags"/> has more than
72-
/// one button pressed</exception>
73-
public PointerButton ToPointerButton() => flags.Count > 0
74-
? throw new InvalidOperationException($"Too many buttons set: {BitOperations.PopCount((uint)flags)}")
75-
: _penButtonFlagsToPenButton[flags];
76-
}
77-
78-
extension(PointerButton button)
79-
{
80-
8115
}
8216

8317
private static readonly FrozenDictionary<SdlMouseInputFlags, PointerButton> _mouseButtonFlagsToPointerButton =
@@ -91,18 +25,4 @@ public PointerButton ToPointerButton() => flags.Count > 0
9125

9226
private static readonly FrozenDictionary<PointerButton, SdlMouseInputFlags> _pointerButtonToMouseFlags =
9327
_mouseButtonFlagsToPointerButton.ToFrozenDictionary(v => v.Value, v => v.Key);
94-
95-
private static readonly FrozenDictionary<SdlPenInputFlags, PointerButton> _penButtonFlagsToPenButton =
96-
new Dictionary<SdlPenInputFlags, PointerButton> {
97-
{ SdlPenInputFlags.Down, PointerButton.Primary },
98-
{ SdlPenInputFlags.Button1, PointerButton.Secondary },
99-
{ SdlPenInputFlags.Button2, PointerButton.MiddleButton },
100-
{ SdlPenInputFlags.Button3, PointerButton.Button4 },
101-
{ SdlPenInputFlags.Button4, PointerButton.Button5 },
102-
{ SdlPenInputFlags.EraserTip, PointerButton.EraserTip },
103-
{ SdlPenInputFlags.PenInProximity, default },
104-
}.ToFrozenDictionary();
105-
106-
private static readonly FrozenDictionary<PointerButton, SdlPenInputFlags> _pointerButtonToPenFlags =
107-
_penButtonFlagsToPenButton.ToFrozenDictionary(v => v.Value, v => v.Key);
10828
}

0 commit comments

Comments
 (0)