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

Merged
merged 19 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 6 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
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
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
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// </remarks>
public long Elapsed => _timer.LastTickElapsed.Ticks;
public double Elapsed => _timer.LastTickElapsed.TotalMicroseconds;

public void Start()
{
Expand Down
10 changes: 5 additions & 5 deletions src/Uno.UI/UI/Input/GestureRecognizer.Manipulation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down Expand Up @@ -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();
Expand All @@ -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)
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/Uno.UI/UI/Input/GestureRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint, Gesture> _gestures = new Dictionary<uint, Gesture>(_defaultGesturesSize);
Expand Down
8 changes: 8 additions & 0 deletions src/Uno.UI/UI/Input/PointerPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -37,6 +38,13 @@ internal PointerPoint(
Properties = properties;
}

/// <summary>
/// Retrieves the relative timestamp in microseconds.
/// </summary>
/// <returns>Timestamp in microseconds.</returns>
internal static ulong GetRelativeTimestamp() =>
MartinZikmund marked this conversation as resolved.
Show resolved Hide resolved
(ulong)Stopwatch.GetElapsedTime(Stopwatch.GetTimestamp()).TotalMicroseconds;

#if HAS_UNO_WINUI && IS_UNO_UI_PROJECT
public PointerPoint(global::Windows.UI.Input.PointerPoint point)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.pointers.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down
7 changes: 3 additions & 4 deletions src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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;
}
}

Expand Down
8 changes: 2 additions & 6 deletions src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
using Uno.UI.Xaml.Core;
using Uno.UI.Xaml.Input;



#if HAS_UNO_WINUI
using Microsoft.UI.Input;
#else
Expand Down Expand Up @@ -112,13 +110,11 @@ private PointerPointProperties GetProperties()
};

#region Misc static helpers
private static long? _bootTime;

private static ulong ToTimeStamp(double timestamp)
{
_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.
MartinZikmund marked this conversation as resolved.
Show resolved Hide resolved
return (ulong)(timestamp * 1000 * 1000); // Convert to microseconds
}

private static double? _firstTimestamp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ internal PointerEventArgs ToEventArgs(InjectedInputState state, VirtualKeyModifi

var point = new PointerPoint(
state.FrameId + TimeOffsetInMilliseconds,
state.Timestamp + TimeOffsetInMilliseconds,
state.Timestamp + TimeOffsetInMilliseconds * 1000,
PointerDevice.For(PointerDeviceType.Mouse),
uint.MaxValue - 42, // Try to avoid conflict with the real mouse pointer
position,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ internal PointerPoint ToPointerPoint(InjectedInputState state)
var location = new Point(PixelLocation.PositionX, PixelLocation.PositionY);
var point = new PointerPoint(
state.FrameId + (uint)PerformanceCount,
state.Timestamp + (ulong)(TimeOffsetInMilliseconds * TimeSpan.TicksPerMillisecond),
state.Timestamp + TimeOffsetInMilliseconds * 1000,
PointerDevice.For(state.Type),
isNew ? PointerId : state.PointerId,
location,
Expand Down
Loading