Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input event Timestamp should be in microseconds #19147

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/SamplesApp/UITests.Shared/UITests.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -4266,6 +4266,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Pointers\PointerEvent_Timestamp.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\XamlUICommandTests.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -8238,6 +8242,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Pointers\PointerEventArgsTests.xaml.cs">
<DependentUpon>PointerEventArgsTests.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Pointers\PointerEvent_Timestamp.xaml.cs">
<DependentUpon>PointerEvent_Timestamp.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Input\Pointers\DragCoordinates_Automated.xaml.cs">
<DependentUpon>DragCoordinates_Automated.xaml</DependentUpon>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private static void SleepOnTouchDown(object sender, PointerRoutedEventArgs e)
{
// Ugly hack: The test engine does not allows us to perform a custom gesture (hold for 300 ms then drag)
// So we just freeze the UI thread enough to simulate the delay ...
const int holdDelay = 300 /* GestureRecognizer.DragWithTouchMinDelayTicks */ + 50 /* safety */;
const int holdDelay = 300 /* GestureRecognizer.DragWithTouchMinDelayMicroseconds */ + 50 /* safety */;
Thread.Sleep(holdDelay);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ protected override void OnPointerPressed(PointerRoutedEventArgs e)
{
// Ugly hack: The test engine does not allows us to perform a custom gesture (hold for 300 ms then drag)
// So we just freeze the UI thread enough to simulate the delay ...
const int holdDelay = 300 /* GestureRecognizer.DragWithTouchMinDelayTicks */ + 50 /* safety */;
const int holdDelay = 300 /* GestureRecognizer.DragWithTouchMinDelayMicroseconds */ + 50 /* safety */;
Thread.Sleep(holdDelay);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<UserControl
x:Class="UITests.Shared.Windows_UI_Xaml_Input.Pointers.PointerEvent_Timestamp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Sample.Shared.Tests"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">

<Grid Padding="20" RowSpacing="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
x:Name="TestBorder"
Width="100"
Height="100"
Background="Red" />

<ListView Grid.Row="1" ItemsSource="{x:Bind Logs}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Margin="8" Text="{x:Bind}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Uno.UI.Samples.Controls;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System.Collections.ObjectModel;

namespace UITests.Shared.Windows_UI_Xaml_Input.Pointers
{
[SampleControlInfo(
"Pointers",
Description =
"Click the red rectangle repeatedly. You should see tickmarks in the logs (✔️) indicating that time delta matches timestamp delta.",
IsManualTest = true)]
public sealed partial class PointerEvent_Timestamp : UserControl
{
private ulong? _lastTimestamp;
private uint? _lastFrameId;
private double? _lastElapsedTime;
private readonly Stopwatch _stopwatch = new();

public PointerEvent_Timestamp()
{
this.InitializeComponent();
TestBorder.PointerPressed += PointerEventArgsTests_PointerPressed;
_stopwatch.Start();
Unloaded += (s, e) => _stopwatch.Stop();
}

public ObservableCollection<string> Logs { get; } = new ObservableCollection<string>();

private void PointerEventArgsTests_PointerPressed(object sender, PointerRoutedEventArgs e)
{
var point = e.GetCurrentPoint(TestBorder);
var timestamp = point.Timestamp;
var frameId = point.FrameId;
var time = _stopwatch.Elapsed.TotalMicroseconds;

var log = $"Timestamp: {timestamp}, FrameId: {frameId}" + Environment.NewLine;
if (_lastTimestamp.HasValue)
{
var timeDelta = (ulong)(time - _lastElapsedTime.Value);
var timestampDelta = (timestamp - _lastTimestamp.Value);
log += $"Time Δ: {timeDelta}";

// As long as the delta differs by less than 100ms, it probably is correct.
var seemsCorrect = Math.Abs((double)timeDelta - timestampDelta) < 50_000;
log += $", Timestamp Δ: {timestampDelta} {(seemsCorrect ? "✔️" : "❌")}";

var frameIdDelta = frameId - _lastFrameId.Value;
log += $", FrameId Δ: {frameIdDelta}";
}
_lastElapsedTime = time;
_lastTimestamp = timestamp;
_lastFrameId = frameId;
Logs.Add(log);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,10 @@ private void UseDevice(PointerPoint pointer, Gdk.Device device)
}

properties.IsInRange = true;

var timeInMicroseconds = time * 1000;
var pointerPoint = new Windows.UI.Input.PointerPoint(
frameId: time,
timestamp: time * (ulong)TimeSpan.TicksPerMillisecond, // time is in ms, timestamp is in ticks
timestamp: timeInMicroseconds,
device: pointerDevice,
pointerId: pointerId,
rawPosition: rawPosition,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@ double GetAxisValue(libinput_pointer_axis axis)
properties.IsMiddleButtonPressed = _pointerPressed.Contains(libinput_event_code.BTN_MIDDLE);
properties.IsRightButtonPressed = _pointerPressed.Contains(libinput_event_code.BTN_RIGHT);

var timestampInMicroseconds = timestamp;
var pointerPoint = new Windows.UI.Input.PointerPoint(
frameId: (uint)timestamp, // UNO TODO: How should set the frame, timestamp may overflow.
timestamp: timestamp * TimeSpan.TicksPerMicrosecond,
timestamp: timestampInMicroseconds,
device: PointerDevice.For(PointerDeviceType.Mouse),
pointerId: 0,
rawPosition: _mousePosition,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,10 @@ public void ProcessTouchEvent(IntPtr rawEvent, libinput_event_type rawEventType)

properties.IsLeftButtonPressed = rawEventType != LIBINPUT_EVENT_TOUCH_UP && rawEventType != LIBINPUT_EVENT_TOUCH_CANCEL;

var timestampInMicroseconds = timestamp;
var pointerPoint = new Windows.UI.Input.PointerPoint(
frameId: (uint)timestamp, // UNO TODO: How should set the frame, timestamp may overflow.
timestamp: timestamp * TimeSpan.TicksPerMicrosecond,
timestamp: timestampInMicroseconds,
device: PointerDevice.For(PointerDeviceType.Touch),
pointerId: pointerId,
rawPosition: currentPosition,
Expand Down
1 change: 1 addition & 0 deletions src/Uno.UI.Runtime.Skia.MacOS/MacOSWindowHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ private static PointerEventArgs BuildPointerArgs(NativeMouseEventData data)
var pointerDevice = PointerDevice.For(data.PointerDeviceType);
var properties = GetPointerProperties(data).SetUpdateKindFromPrevious(_previousProperties);

// TODO: Check the macOS unit of time
MartinZikmund marked this conversation as resolved.
Show resolved Hide resolved
var point = new PointerPoint(data.FrameId, data.Timestamp, pointerDevice, data.Pid, position, position, data.InContact, properties);
var args = new PointerEventArgs(point, data.KeyModifiers);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ private IntPtr OnWmMessage(IntPtr hwnd, int msg, IntPtr wparamOriginal, IntPtr l

var point = new Windows.UI.Input.PointerPoint(
frameId: FrameIdProvider.GetNextFrameId(),
timestamp: (ulong)Environment.TickCount,
timestamp: (ulong)(Environment.TickCount64 * 1000),
device: PointerDevice.For(PointerDeviceType.Mouse),
pointerId: 1,
rawPosition: position,
Expand Down Expand Up @@ -406,11 +406,12 @@ private PointerEventArgs BuildPointerArgs(InputEventArgs args, bool? isReleaseOr
throw new ArgumentException();
}

var timestampInMicroseconds = (ulong)(args.Timestamp * 1000);
properties = properties.SetUpdateKindFromPrevious(_previous?.CurrentPoint.Properties);
var modifiers = GetKeyModifiers();
var point = new PointerPoint(
frameId: FrameIdProvider.GetNextFrameId(),
timestamp: (ulong)(args.Timestamp * TimeSpan.TicksPerMillisecond),
timestamp: timestampInMicroseconds,
device: GetPointerDevice(args),
pointerId: pointerId,
rawPosition: new Windows.Foundation.Point(position.X, position.Y),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,10 @@ private PointerPoint CreatePointFromCurrentState(IntPtr time)
? root.RasterizationScale
: 1;

var timeInMicroseconds = (ulong)(time * 1000); // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535
var point = new PointerPoint(
frameId: (uint)time, // UNO TODO: How should set the frame, timestamp may overflow.
timestamp: (uint)(time * TimeSpan.TicksPerMillisecond), // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535
timestamp: timeInMicroseconds,
PointerDevice.For(PointerDeviceType.Mouse),
0, // TODO: XInput
new Point(_mousePosition.X / scale, _mousePosition.Y / scale),
Expand Down
7 changes: 4 additions & 3 deletions src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,10 @@ public unsafe PointerEventArgs CreatePointerEventArgsFromDeviceEvent(XIDeviceEve
? XamlRoot.GetDisplayInformation(root).RawPixelsPerViewPixel
: 1;

var timeInMicroseconds = (ulong)(data.time * 1000); // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535
var point = new PointerPoint(
frameId: (uint)data.time, // UNO TODO: How should we set the frame, timestamp may overflow.
timestamp: (uint)(data.time * TimeSpan.TicksPerMillisecond), // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535
PointerDevice.For(data.evtype is XiEventType.XI_TouchBegin or XiEventType.XI_TouchEnd or XiEventType.XI_TouchUpdate ? PointerDeviceType.Touch : PointerDeviceType.Mouse),
timestamp: timeInMicroseconds, PointerDevice.For(data.evtype is XiEventType.XI_TouchBegin or XiEventType.XI_TouchEnd or XiEventType.XI_TouchUpdate ? PointerDeviceType.Touch : PointerDeviceType.Mouse),
MartinZikmund marked this conversation as resolved.
Show resolved Hide resolved
(uint)(data.evtype is XiEventType.XI_TouchBegin or XiEventType.XI_TouchEnd or XiEventType.XI_TouchUpdate ? data.detail : data.sourceid), // for touch, data.detail is the touch ID
new Point(data.event_x / scale, data.event_y / scale),
new Point(data.event_x / scale, data.event_y / scale),
Expand Down Expand Up @@ -345,9 +345,10 @@ public unsafe PointerEventArgs CreatePointerEventArgsFromEnterLeaveEvent(XIEnter
IsHorizontalMouseWheel = false,
};

var timestampInMicroseconds = (ulong)(data.time * 1000); // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535
var point = new PointerPoint(
frameId: (uint)data.time, // UNO TODO: How should we set the frame, timestamp may overflow.
timestamp: (ulong)data.time,
timestamp: timestampInMicroseconds,
PointerDevice.For(PointerDeviceType.Mouse),
(uint)data.sourceid,
new Point(data.event_x, data.event_y),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,7 @@ public async Task When_IsTextSelectionEnabled_SurrogatePair_Copy()
#endif
public async Task When_IsTextSelectionEnabled_CRLF()
{
var delayToAvoidDoubleTap = 600;
var SUT = new TextBlock
{
Text = "FirstLine\r\n Second",
Expand All @@ -1165,7 +1166,7 @@ public async Task When_IsTextSelectionEnabled_CRLF()
mouse.Release();
mouse.Press();
mouse.Release();
await WindowHelper.WaitForIdle();
await Task.Delay(delayToAvoidDoubleTap);

SUT.CopySelectionToClipboard();
await WindowHelper.WaitForIdle();
Expand All @@ -1180,7 +1181,7 @@ public async Task When_IsTextSelectionEnabled_CRLF()
mouse.Release();
mouse.Press();
mouse.Release();
await WindowHelper.WaitForIdle();
await Task.Delay(delayToAvoidDoubleTap);

SUT.CopySelectionToClipboard();
await WindowHelper.WaitForIdle();
Expand Down
28 changes: 14 additions & 14 deletions src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public void DoubleTapped_Duration()
taps.Should().BeEquivalentTo(Tap(25, 25));

// Double tapped
var tooSlow = GetPoint(25, 25, ts: 1 + GestureRecognizer.MultiTapMaxDelayTicks + 1);
var tooSlow = GetPoint(25, 25, ts: 1 + GestureRecognizer.MultiTapMaxDelayMicroseconds + 1);
sut.CanBeDoubleTap(tooSlow).Should().BeFalse();
sut.ProcessDownEvent(tooSlow);

Expand Down Expand Up @@ -1449,8 +1449,8 @@ public void Drag_Started_Mouse_Hold()

// Start mouse dragging
sut.ProcessDownEvent(25, 25, ts: 0);
sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1);
var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2);
sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1);
var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2);

drags.Should().BeEquivalentTo(Drag(start, DraggingState.Started));
}
Expand Down Expand Up @@ -1480,8 +1480,8 @@ public void Drag_Started_Pen_Hold()
using var _ = Pen();

sut.ProcessDownEvent(25, 25, ts: 0);
sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1);
var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2);
sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1);
var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2);

drags.Should().BeEquivalentTo(Drag(start, DraggingState.Started));
}
Expand Down Expand Up @@ -1511,8 +1511,8 @@ public void Drag_Started_Touch_Hold()
using var _ = Touch();

sut.ProcessDownEvent(25, 25, ts: 0);
sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1);
var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2);
sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1);
var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2);

drags.Should().BeEquivalentTo(Drag(start, DraggingState.Started));
}
Expand All @@ -1529,8 +1529,8 @@ public void Drag_Started_Touch_MovedTooFar()
sut.ProcessDownEvent(25, 25, ts: 0);
sut.ProcessMoveEvent(50, 50, ts: 1);
sut.ProcessMoveEvent(25, 25, ts: 2);
sut.ProcessMoveEvent(25, 25, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1);
sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2);
sut.ProcessMoveEvent(25, 25, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1);
sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2);

drags.Should().BeEmpty();
}
Expand All @@ -1545,10 +1545,10 @@ public void Drag_CompleteGesture()
using var _ = Touch();

sut.ProcessDownEvent(25, 25, ts: 0);
sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1);
var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2);
var move = sut.ProcessMoveEvent(51, 51, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 1);
var end = sut.ProcessUpEvent(52, 52, ts: GestureRecognizer.DragWithTouchMinDelayTicks + 2);
sut.ProcessMoveEvent(26, 26, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1);
var start = sut.ProcessMoveEvent(50, 50, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2);
var move = sut.ProcessMoveEvent(51, 51, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 1);
var end = sut.ProcessUpEvent(52, 52, ts: GestureRecognizer.DragWithTouchMinDelayMicroseconds + 2);

drags.Should().BeEquivalentTo(
Drag(start, DraggingState.Started),
Expand All @@ -1559,7 +1559,7 @@ public void Drag_CompleteGesture()
[TestMethod]
public void Drag_And_Holding_Touch()
{
var delay = (ulong)Math.Max(GestureRecognizer.DragWithTouchMinDelayTicks, GestureRecognizer.HoldMinDelayTicks);
var delay = (ulong)Math.Max(GestureRecognizer.DragWithTouchMinDelayMicroseconds, GestureRecognizer.HoldMinDelayMicroseconds);
var sut = new GestureRecognizer { GestureSettings = GestureSettings.Drag | GestureSettings.Hold };
var drags = new List<DraggingEventArgs>();
var holds = new List<HoldingEventArgs>();
Expand Down
3 changes: 2 additions & 1 deletion src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal partial class BrowserPointerInputSource : IUnoCorePointerInputSource
private static readonly Logger _log = typeof(BrowserPointerInputSource).Log();
private static readonly Logger? _logTrace = _log.IsTraceEnabled(LogLevel.Trace) ? _log : null;

// TODO: Verify the boot time unit (ms or ticks)
private ulong _bootTime;
private bool _isOver;
private PointerPoint? _lastPoint;
Expand Down Expand Up @@ -321,7 +322,7 @@ internal static uint ToFrameId(double timestamp)
=> (uint)(timestamp % uint.MaxValue);

private ulong ToTimeStamp(double timestamp)
=> _bootTime + (ulong)(timestamp * TimeSpan.TicksPerMillisecond);
=> _bootTime + (ulong)(timestamp * 1000);

private static PointerUpdateKind ToUpdateKind(HtmlPointerButtonUpdate update, PointerPointProperties props)
=> update switch
Expand Down
6 changes: 3 additions & 3 deletions src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ private bool SupportsHolding()
private void StartHoldingTimer()
{
_holdingTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
_holdingTimer.Interval = TimeSpan.FromTicks(HoldMinDelayTicks);
_holdingTimer.Interval = TimeSpan.FromMicroseconds(HoldMinDelayMicroseconds);
_holdingTimer.State = this;
_holdingTimer.Tick += OnHoldingTimerTick;
_holdingTimer.Start();
Expand Down Expand Up @@ -323,7 +323,7 @@ public static bool IsMultiTapGesture((ulong id, ulong ts, Point position) previo
var currentPosition = down.Position;

return previousTap.id == currentId
&& currentTs - previousTap.ts <= MultiTapMaxDelayTicks
&& currentTs - previousTap.ts <= MultiTapMaxDelayMicroseconds
&& !IsOutOfTapRange(previousTap.position, currentPosition);
}

Expand Down Expand Up @@ -394,7 +394,7 @@ private static bool IsRightTapGesture(Gesture points, out bool isLongPress)
}

private static bool IsLongPress(PointerPoint down, PointerPoint current)
=> current.Timestamp - down.Timestamp > HoldMinDelayTicks;
=> current.Timestamp - down.Timestamp > HoldMinDelayMicroseconds;
#endregion
}
}
Expand Down
Loading
Loading