diff --git a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems
index 55205d5a899e..cb7f2da5b0e7 100644
--- a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems
+++ b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems
@@ -4266,6 +4266,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
@@ -8238,6 +8242,9 @@
PointerEventArgsTests.xaml
+
+ PointerEvent_Timestamp.xaml
+
DragCoordinates_Automated.xaml
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Basics.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Basics.xaml.cs
index 358f27386660..5b0895c7e68d 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Basics.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Basics.xaml.cs
@@ -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);
}
}
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Nested.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Nested.xaml.cs
index 3bb33644da5d..9ba6f313cf46 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Nested.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml/DragAndDrop/DragDrop_Nested.xaml.cs
@@ -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);
}
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml
new file mode 100644
index 000000000000..c74efebaf40d
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml.cs
new file mode 100644
index 000000000000..1fa9862df39c
--- /dev/null
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Input/Pointers/PointerEvent_Timestamp.xaml.cs
@@ -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 Logs { get; } = new ObservableCollection();
+
+ 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);
+ }
+ }
+}
diff --git a/src/Uno.UI.Runtime.Skia.Gtk/Input/GtkCorePointerInputSource.cs b/src/Uno.UI.Runtime.Skia.Gtk/Input/GtkCorePointerInputSource.cs
index c163cb6e5185..a3cbcb4170fd 100644
--- a/src/Uno.UI.Runtime.Skia.Gtk/Input/GtkCorePointerInputSource.cs
+++ b/src/Uno.UI.Runtime.Skia.Gtk/Input/GtkCorePointerInputSource.cs
@@ -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,
diff --git a/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Mouse.cs b/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Mouse.cs
index 84522357b42e..3d8fa5ac1359 100644
--- a/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Mouse.cs
+++ b/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Mouse.cs
@@ -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,
diff --git a/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Touch.cs b/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Touch.cs
index c261c91a1ff4..9106000a6a56 100644
--- a/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Touch.cs
+++ b/src/Uno.UI.Runtime.Skia.Linux.FrameBuffer/FrameBufferPointerInputSource.Touch.cs
@@ -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,
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.h b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.h
index 0fb4900ab266..d2f5766b7e81 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.h
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.h
@@ -12,7 +12,6 @@ typedef void (*system_theme_change_fn_ptr)(void);
system_theme_change_fn_ptr uno_get_system_theme_change_callback(void);
void uno_set_system_theme_change_callback(system_theme_change_fn_ptr p);
uint32 uno_get_system_theme(void);
-NSTimeInterval uno_get_system_uptime(void);
bool uno_app_initialize(bool *supportsMetal);
NSWindow* uno_app_get_main_window(void);
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.m b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.m
index 5eac3ebf1a47..029056058d55 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.m
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOApplication.m
@@ -7,7 +7,6 @@
static UNOApplicationDelegate *ad;
static system_theme_change_fn_ptr system_theme_change;
static id device;
-static NSTimeInterval uptime = 0;
inline system_theme_change_fn_ptr uno_get_system_theme_change_callback(void)
{
@@ -28,14 +27,6 @@ void uno_set_system_theme_change_callback(system_theme_change_fn_ptr p)
return [appearanceName isEqualToString:NSAppearanceNameAqua] ? 0 : 1;
}
-NSTimeInterval uno_get_system_uptime(void)
-{
- if (uptime == 0) {
- uptime = NSProcessInfo.processInfo.systemUptime;
- }
- return uptime;
-}
-
bool uno_app_initialize(bool *metal)
{
NSApplication *app = [NSApplication sharedApplication];
diff --git a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m
index 9d9a6ddc5142..528cd0e6d481 100644
--- a/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m
+++ b/src/Uno.UI.Runtime.Skia.MacOS/UnoNativeMac/UnoNativeMac/UNOWindow.m
@@ -912,10 +912,7 @@ - (void)sendEvent:(NSEvent *)event {
NSTimeInterval ts = event.timestamp;
data.frameId = (uint)(ts * 10.0);
-
- NSDate *now = [[NSDate alloc] init];
- NSDate *boot = [[NSDate alloc] initWithTimeInterval:uno_get_system_uptime() sinceDate:now];
- data.timestamp = (uint64)(boot.timeIntervalSinceNow * 1000000);
+ data.timestamp = (uint64)(ts * 1000000);
handled = uno_get_window_mouse_event_callback()(self, &data);
#if DEBUG_MOUSE // very noisy
diff --git a/src/Uno.UI.Runtime.Skia.Wpf/Input/WpfCorePointerInputSource.cs b/src/Uno.UI.Runtime.Skia.Wpf/Input/WpfCorePointerInputSource.cs
index 8b9524aa07c1..bf31f2fed63e 100644
--- a/src/Uno.UI.Runtime.Skia.Wpf/Input/WpfCorePointerInputSource.cs
+++ b/src/Uno.UI.Runtime.Skia.Wpf/Input/WpfCorePointerInputSource.cs
@@ -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,
@@ -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),
diff --git a/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs b/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs
index 9c241394073b..937fdcf3696a 100644
--- a/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs
+++ b/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs
@@ -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),
diff --git a/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs b/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs
index a8f20948e6cb..52bfdf30ba4b 100644
--- a/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs
+++ b/src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs
@@ -305,11 +305,14 @@ 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 deviceType = data.evtype is XiEventType.XI_TouchBegin or XiEventType.XI_TouchEnd or XiEventType.XI_TouchUpdate ? PointerDeviceType.Touch : PointerDeviceType.Mouse;
+ var pointerId = (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
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),
- (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
+ timestamp: timeInMicroseconds,
+ PointerDevice.For(deviceType),
+ pointerId,
new Point(data.event_x / scale, data.event_y / scale),
new Point(data.event_x / scale, data.event_y / scale),
properties.HasPressedButton,
@@ -345,9 +348,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),
diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBlock.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBlock.cs
index b4e54ec9d649..e9566393b64b 100644
--- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBlock.cs
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBlock.cs
@@ -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",
@@ -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();
@@ -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();
diff --git a/src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs b/src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs
index ece88216c482..c109abe69e72 100644
--- a/src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs
+++ b/src/Uno.UI.Tests/Windows_UI_Input/Given_GestureRecognizer.cs
@@ -20,6 +20,8 @@ namespace Uno.UI.Tests.Windows_UI_Input
[TestClass]
public class Given_GestureRecognizer
{
+ private const int MicrosecondsPerMillisecond = 1000;
+
private const GestureSettings ManipulationsWithoutInertia = GestureSettings.ManipulationTranslateX
| GestureSettings.ManipulationTranslateY
| GestureSettings.ManipulationTranslateRailsX
@@ -162,7 +164,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);
@@ -1002,8 +1004,8 @@ public void Manipulation_Inertia_Translate()
// flick at 2 px/ms
sut.ProcessDownEvent(10, 10, ts: 0);
- sut.ProcessMoveEvent(100, 100, ts: 100 * TimeSpan.TicksPerMillisecond);
- sut.ProcessUpEvent(102, 102, ts: 101 * TimeSpan.TicksPerMillisecond);
+ sut.ProcessMoveEvent(100, 100, ts: 100 * MicrosecondsPerMillisecond);
+ sut.ProcessUpEvent(102, 102, ts: 101 * MicrosecondsPerMillisecond);
sut.RunInertiaSync();
@@ -1049,8 +1051,8 @@ public void Manipulation_Inertia_TranslateXOnly()
// flick at 2 px/ms
sut.ProcessDownEvent(10, 10, ts: 0);
- sut.ProcessMoveEvent(100, 100, ts: 100 * TimeSpan.TicksPerMillisecond);
- sut.ProcessUpEvent(102, 102, ts: 101 * TimeSpan.TicksPerMillisecond);
+ sut.ProcessMoveEvent(100, 100, ts: 100 * MicrosecondsPerMillisecond);
+ sut.ProcessUpEvent(102, 102, ts: 101 * MicrosecondsPerMillisecond);
sut.RunInertiaSync();
@@ -1096,8 +1098,8 @@ public void Manipulation_Inertia_TranslateYOnly()
// flick at 2 px/ms
sut.ProcessDownEvent(10, 10, ts: 0);
- sut.ProcessMoveEvent(100, 100, ts: 100 * TimeSpan.TicksPerMillisecond);
- sut.ProcessUpEvent(102, 102, ts: 101 * TimeSpan.TicksPerMillisecond);
+ sut.ProcessMoveEvent(100, 100, ts: 100 * MicrosecondsPerMillisecond);
+ sut.ProcessUpEvent(102, 102, ts: 101 * MicrosecondsPerMillisecond);
sut.RunInertiaSync();
@@ -1143,8 +1145,8 @@ public void Manipulation_Inertia_Translate_Negative()
// flick at 2 px/ms
sut.ProcessDownEvent(10, 10, ts: 0);
- sut.ProcessMoveEvent(100, 100, ts: 100 * TimeSpan.TicksPerMillisecond);
- sut.ProcessUpEvent(98, 98, ts: 101 * TimeSpan.TicksPerMillisecond);
+ sut.ProcessMoveEvent(100, 100, ts: 100 * MicrosecondsPerMillisecond);
+ sut.ProcessUpEvent(98, 98, ts: 101 * MicrosecondsPerMillisecond);
sut.RunInertiaSync();
@@ -1192,8 +1194,8 @@ public void Manipulation_Inertia_RotateOnly_Trigonometric_InFirstQuadrant()
// Rotate of pi/2 in a quarter of second
sut.ProcessDownEvent(50, -25, id: 2, ts: 1); // Angle = 0
- sut.ProcessMoveEvent(50, -50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4
- sut.ProcessUpEvent(25, -50, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2
+ sut.ProcessMoveEvent(50, -50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4
+ sut.ProcessUpEvent(25, -50, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2
sut.RunInertiaSync();
@@ -1222,8 +1224,8 @@ public void Manipulation_Inertia_RotateOnly_Trigonometric_InSecondQuadrant()
// Rotate of Pi/4 in a quarter of second
sut.ProcessDownEvent(-25, -50, id: 2, ts: 1); // Angle = 0
- sut.ProcessMoveEvent(-50, -50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4
- sut.ProcessUpEvent(-50, -25, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2
+ sut.ProcessMoveEvent(-50, -50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4
+ sut.ProcessUpEvent(-50, -25, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2
sut.RunInertiaSync();
@@ -1252,8 +1254,8 @@ public void Manipulation_Inertia_RotateOnly_Trigonometric_InThirdQuadrant()
// Rotate of pi/2 in a quarter of second
sut.ProcessDownEvent(-50, 25, id: 2, ts: 1); // Angle = 0
- sut.ProcessMoveEvent(-50, 50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4
- sut.ProcessUpEvent(-25, 50, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2
+ sut.ProcessMoveEvent(-50, 50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4
+ sut.ProcessUpEvent(-25, 50, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2
sut.RunInertiaSync();
@@ -1282,8 +1284,8 @@ public void Manipulation_Inertia_RotateOnly_Trigonometric_InForthQuadrant()
// Rotate of pi/2 in a quarter of second
sut.ProcessDownEvent(25, 50, id: 2, ts: 1); // Angle = 0
- sut.ProcessMoveEvent(50, 50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4
- sut.ProcessUpEvent(50, 25, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2
+ sut.ProcessMoveEvent(50, 50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4
+ sut.ProcessUpEvent(50, 25, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2
sut.RunInertiaSync();
@@ -1312,8 +1314,8 @@ public void Manipulation_Inertia_RotateOnly_AntiTrigonometric_InFirstQuadrant()
sut.ProcessDownEvent(25, -25, id: 1, ts: 0);
sut.ProcessDownEvent(25, -50, id: 2, ts: 1); // Angle = 0
- sut.ProcessMoveEvent(50, -50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4
- sut.ProcessUpEvent(50, -25, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2
+ sut.ProcessMoveEvent(50, -50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4
+ sut.ProcessUpEvent(50, -25, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2
sut.RunInertiaSync();
@@ -1341,8 +1343,8 @@ public void Manipulation_Inertia_RotateOnly_AntiTrigonometric_InSecondQuadrant()
sut.ProcessDownEvent(-25, -25, id: 1, ts: 0);
sut.ProcessDownEvent(-50, -25, id: 2, ts: 1); // Angle = 0
- sut.ProcessMoveEvent(-50, -50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4
- sut.ProcessUpEvent(-25, -50, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2
+ sut.ProcessMoveEvent(-50, -50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4
+ sut.ProcessUpEvent(-25, -50, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2
sut.RunInertiaSync();
@@ -1371,8 +1373,8 @@ public void Manipulation_Inertia_RotateOnly_AntiTrigonometric_InThirdQuadrant()
sut.ProcessDownEvent(-25, 25, id: 1, ts: 0);
sut.ProcessDownEvent(-25, 50, id: 2, ts: 1); // Angle = 0
- sut.ProcessMoveEvent(-50, 50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4
- sut.ProcessUpEvent(-50, 25, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2
+ sut.ProcessMoveEvent(-50, 50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4
+ sut.ProcessUpEvent(-50, 25, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2
sut.RunInertiaSync();
@@ -1401,8 +1403,8 @@ public void Manipulation_Inertia_RotateOnly_AntiTrigonometric_InForthQuadrant()
sut.ProcessDownEvent(25, 25, id: 1, ts: 0);
sut.ProcessDownEvent(50, 25, id: 2, ts: 1); // Angle = 0
- sut.ProcessMoveEvent(50, 50, id: 2, ts: 100 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/4
- sut.ProcessUpEvent(25, 50, id: 2, ts: 600 * TimeSpan.TicksPerMillisecond); // Angle = -Pi/2
+ sut.ProcessMoveEvent(50, 50, id: 2, ts: 100 * MicrosecondsPerMillisecond); // Angle = -Pi/4
+ sut.ProcessUpEvent(25, 50, id: 2, ts: 600 * MicrosecondsPerMillisecond); // Angle = -Pi/2
sut.RunInertiaSync();
@@ -1449,8 +1451,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));
}
@@ -1480,8 +1482,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));
}
@@ -1511,8 +1513,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));
}
@@ -1529,8 +1531,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();
}
@@ -1545,10 +1547,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),
@@ -1559,7 +1561,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();
var holds = new List();
diff --git a/src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs b/src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs
index 51c2741190c9..f03d40afd4f5 100644
--- a/src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs
+++ b/src/Uno.UI/Runtime/BrowserPointerInputSource.wasm.cs
@@ -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;
@@ -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
diff --git a/src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs b/src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs
index 03ee67cffdce..ba0d1f858947 100644
--- a/src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs
+++ b/src/Uno.UI/UI/Input/GestureRecognizer.Gesture.cs
@@ -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();
@@ -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);
}
@@ -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
}
}
diff --git a/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.InertiaProcessor.cs b/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.InertiaProcessor.cs
index 53d8b981f8eb..94f3a4f9b7df 100644
--- a/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.InertiaProcessor.cs
+++ b/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.InertiaProcessor.cs
@@ -88,7 +88,7 @@ public InertiaProcessor(Manipulation owner, Point position, ManipulationDelta cu
/// Depending of the platform, the timestamp provided by pointer events might not be absolute,
/// so it's preferable to not compare timestamp between pointers and inertia processor.
///
- public long Elapsed => _timer.LastTickElapsed.Ticks;
+ public double Elapsed => _timer.LastTickElapsed.TotalMicroseconds;
public void Start()
{
diff --git a/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs b/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs
index 29813e861dcb..1c6153501259 100644
--- a/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs
+++ b/src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs
@@ -499,8 +499,8 @@ private ManipulationVelocities GetVelocities(ManipulationDelta delta)
{
// The _currents.Timestamp is not updated once inertia as started, we must get the elapsed duration from the inertia processor
// (and not compare it to PointerPoint.Timestamp in any way, cf. remarks on InertiaProcessor.Elapsed)
- var elapsedTicks = _inertia?.Elapsed ?? (double)_currents.Timestamp - _lastPublishedState.timestamp;
- var elapsedMs = elapsedTicks / TimeSpan.TicksPerMillisecond;
+ var elapsedMicroseconds = _inertia?.Elapsed ?? (_currents.Timestamp - _lastPublishedState.timestamp);
+ var elapsedMs = elapsedMicroseconds / 1000;
// With uno a single native event might produce multiple managed pointer events.
// In that case we would get an empty velocities ... which is often not relevant!
@@ -541,7 +541,7 @@ private void StartDragTimer()
if (_isDraggingEnable && _deviceType == PointerDeviceType.Touch)
{
_dragHoldTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
- _dragHoldTimer.Interval = new TimeSpan(DragWithTouchMinDelayTicks);
+ _dragHoldTimer.Interval = TimeSpan.FromMicroseconds(DragWithTouchMinDelayMicroseconds);
_dragHoldTimer.IsRepeating = false;
_dragHoldTimer.Tick += TouchDragMightStart;
_dragHoldTimer.Start();
@@ -565,7 +565,7 @@ private void StopDragTimer()
}
// For pen and mouse this only means down -> * moves out of tap range;
- // For touch it means down -> * moves close to origin for DragUsingFingerMinDelayTicks -> * moves far from the origin
+ // For touch it means down -> * moves close to origin for DragWithTouchMinDelayMicroseconds -> * moves far from the origin
private bool IsBeginningOfDragManipulation()
{
if (!_isDraggingEnable)
@@ -592,7 +592,7 @@ private bool IsBeginningOfDragManipulation()
// This means that this method is expected to be invoked on each move (until manipulation starts)
// in order to update the _isDraggingEnable state.
- var isInHoldPhase = current.Timestamp - down.Timestamp < DragWithTouchMinDelayTicks;
+ var isInHoldPhase = current.Timestamp - down.Timestamp < DragWithTouchMinDelayMicroseconds;
if (isInHoldPhase && isOutOfRange)
{
// The pointer moved out of range while in the hold phase, so we completely disable the drag manipulation
diff --git a/src/Uno.UI/UI/Input/GestureRecognizer.cs b/src/Uno.UI/UI/Input/GestureRecognizer.cs
index 21e15e9200f4..b714182873c2 100644
--- a/src/Uno.UI/UI/Input/GestureRecognizer.cs
+++ b/src/Uno.UI/UI/Input/GestureRecognizer.cs
@@ -26,12 +26,12 @@ public partial class GestureRecognizer
internal const int TapMaxXDelta = 10;
internal const int TapMaxYDelta = 10;
- internal const ulong MultiTapMaxDelayTicks = TimeSpan.TicksPerMillisecond * 500;
+ internal const ulong MultiTapMaxDelayMicroseconds = 500000;
- internal const long HoldMinDelayTicks = TimeSpan.TicksPerMillisecond * 800;
+ internal const long HoldMinDelayMicroseconds = 800000;
internal const float HoldMinPressure = .75f;
- internal const long DragWithTouchMinDelayTicks = TimeSpan.TicksPerMillisecond * 300; // https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#open-a-context-menu-on-an-item-you-can-drag-with-touch
+ internal const long DragWithTouchMinDelayMicroseconds = 300000; // https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#open-a-context-menu-on-an-item-you-can-drag-with-touch
private readonly Logger _log;
private IDictionary _gestures = new Dictionary(_defaultGesturesSize);
diff --git a/src/Uno.UI/UI/Input/PointerPoint.cs b/src/Uno.UI/UI/Input/PointerPoint.cs
index d70b5ed43a4e..b6e4d8014a5b 100644
--- a/src/Uno.UI/UI/Input/PointerPoint.cs
+++ b/src/Uno.UI/UI/Input/PointerPoint.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics;
using System.Text;
using System.Threading;
using Windows.Devices.Input;
diff --git a/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs b/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs
index c1197f21be79..a05407a9f6fe 100644
--- a/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs
@@ -104,7 +104,7 @@ private static bool IsMultiTapGesture((ulong id, ulong ts, Point position) previ
var currentPosition = down.Position;
return previousTap.id == currentId
- && currentTs - previousTap.ts <= GestureRecognizer.MultiTapMaxDelayTicks
+ && currentTs - previousTap.ts <= GestureRecognizer.MultiTapMaxDelayMicroseconds
&& !GestureRecognizer.IsOutOfTapRange(previousTap.position, currentPosition);
}
@@ -176,7 +176,7 @@ partial void OnPointerReleasedPartial(PointerRoutedEventArgs args)
_isPressed = false;
- if ((args.GetCurrentPoint(null).Timestamp - _lastPointerDown.point.Timestamp) >= GestureRecognizer.HoldMinDelayTicks)
+ if ((args.GetCurrentPoint(null).Timestamp - _lastPointerDown.point.Timestamp) >= GestureRecognizer.HoldMinDelayMicroseconds)
{
// Touch holding
OpenContextMenu(args.GetCurrentPoint(this).Position);
diff --git a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs
index 6044821033f0..9a45fbb35473 100644
--- a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs
+++ b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs
@@ -250,9 +250,10 @@ private PointerPointProperties GetProperties(MotionEvent nativeEvent, MotionEven
private static ulong ToTimeStamp(long uptimeMillis)
{
+ // Timestamp is in microseconds
if (FeatureConfiguration.PointerRoutedEventArgs.AllowRelativeTimeStamp)
{
- return (ulong)(TimeSpan.TicksPerMillisecond * uptimeMillis);
+ return (ulong)(uptimeMillis * 1000);
}
else
{
@@ -261,9 +262,7 @@ private static ulong ToTimeStamp(long uptimeMillis)
var sleepTime = Android.OS.SystemClock.ElapsedRealtime() - Android.OS.SystemClock.UptimeMillis();
var realUptime = (ulong)(uptimeMillis + sleepTime);
- var timestamp = TimeSpan.TicksPerMillisecond * (_unixEpochMs + realUptime);
-
- return timestamp;
+ return realUptime * 1000;
}
}
diff --git a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs
index 2d4427a8879c..80062282b032 100644
--- a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs
+++ b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs
@@ -8,8 +8,6 @@
using Uno.UI.Xaml.Core;
using Uno.UI.Xaml.Input;
-
-
#if HAS_UNO_WINUI
using Microsoft.UI.Input;
#else
@@ -77,7 +75,7 @@ internal PointerRoutedEventArgs(uint pointerId, UITouch nativeTouch, UIEvent nat
public PointerPoint GetCurrentPoint(UIElement relativeTo)
{
- var timestamp = ToTimeStamp(_nativeTouch.Timestamp);
+ var timestamp = ToTimestamp(_nativeTouch.Timestamp);
var device = global::Windows.Devices.Input.PointerDevice.For((global::Windows.Devices.Input.PointerDeviceType)Pointer.PointerDeviceType);
var rawPosition = (Point)_nativeTouch.GetPreciseLocation(null);
var position = relativeTo == null
@@ -112,13 +110,11 @@ private PointerPointProperties GetProperties()
};
#region Misc static helpers
- private static long? _bootTime;
- private static ulong ToTimeStamp(double timestamp)
+ private static ulong ToTimestamp(double nativeTimestamp)
{
- _bootTime ??= DateTime.UtcNow.Ticks - (long)(TimeSpan.TicksPerSecond * new NSProcessInfo().SystemUptime);
-
- return (ulong)_bootTime.Value + (ulong)(TimeSpan.TicksPerSecond * timestamp);
+ // iOS Timestamp is in seconds from boot time, convert to microseconds.
+ return (ulong)(nativeTimestamp * 1000 * 1000);
}
private static double? _firstTimestamp;
diff --git a/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputMouseInfo.cs b/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputMouseInfo.cs
index 91c38268a3f6..e92d773bb4df 100644
--- a/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputMouseInfo.cs
+++ b/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputMouseInfo.cs
@@ -100,9 +100,10 @@ internal PointerEventArgs ToEventArgs(InjectedInputState state, VirtualKeyModifi
properties.PointerUpdateKind = update;
+ var timestampInMicroseconds = state.Timestamp + TimeOffsetInMilliseconds * 1000;
var point = new PointerPoint(
state.FrameId + TimeOffsetInMilliseconds,
- state.Timestamp + TimeOffsetInMilliseconds,
+ timestampInMicroseconds,
PointerDevice.For(PointerDeviceType.Mouse),
uint.MaxValue - 42, // Try to avoid conflict with the real mouse pointer
position,
diff --git a/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputPointerInfo.cs b/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputPointerInfo.cs
index 7d521bfeac0f..609de983a4a5 100644
--- a/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputPointerInfo.cs
+++ b/src/Uno.UWP/UI/Input/Preview.Injection/InjectedInputPointerInfo.cs
@@ -78,10 +78,11 @@ internal PointerPoint ToPointerPoint(InjectedInputState state)
properties.IsBarrelButtonPressed = properties.IsRightButtonPressed;
}
+ var timestampInMicroseconds = state.Timestamp + TimeOffsetInMilliseconds * 1000;
var location = new Point(PixelLocation.PositionX, PixelLocation.PositionY);
var point = new PointerPoint(
state.FrameId + (uint)PerformanceCount,
- state.Timestamp + (ulong)(TimeOffsetInMilliseconds * TimeSpan.TicksPerMillisecond),
+ timestampInMicroseconds,
PointerDevice.For(state.Type),
isNew ? PointerId : state.PointerId,
location,