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 all 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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
static UNOApplicationDelegate *ad;
static system_theme_change_fn_ptr system_theme_change;
static id<MTLDevice> device;
static NSTimeInterval uptime = 0;

inline system_theme_change_fn_ptr uno_get_system_theme_change_callback(void)
{
Expand All @@ -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];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
12 changes: 8 additions & 4 deletions src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.XInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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),
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
Loading
Loading