From bc407c27b59b1e40d018e5d6cddb8f37d0fdb2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krysi=C5=84ski?= Date: Mon, 24 Nov 2025 12:50:28 +0100 Subject: [PATCH] Fixed intermediate points order for windows (#20075) * Fixed points order for windows * Added static comparer * Fixed points order for windows * Added static comparer * optimization --- .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 56 ++++++++++--------- src/Windows/Avalonia.Win32/WindowImpl.cs | 7 +++ 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 4e2872b387..b5c67840d3 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.InteropServices; using Avalonia.Automation.Peers; using Avalonia.Controls; @@ -983,7 +984,8 @@ namespace Avalonia.Win32 return null; } - private unsafe IReadOnlyList CreateIntermediatePoints(MOUSEMOVEPOINT movePoint, MOUSEMOVEPOINT prevMovePoint) + private unsafe IReadOnlyList CreateIntermediatePoints(MOUSEMOVEPOINT movePoint, + MOUSEMOVEPOINT prevMovePoint) { // To understand some of this code, please check MS docs: // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmousemovepointsex#remarks @@ -994,6 +996,7 @@ namespace Avalonia.Win32 { var movePointCopy = movePoint; movePointCopy.time = 0; // empty "time" as otherwise WinAPI will always fail + int pointsCount = GetMouseMovePointsEx( (uint)(Marshal.SizeOf(movePointCopy)), &movePointCopy, movePoints, s_mouseHistoryInfos.Length, @@ -1001,47 +1004,46 @@ namespace Avalonia.Win32 // GetMouseMovePointsEx can return -1 if point wasn't found or there is so beeg delay that original points were erased from the buffer. if (pointsCount <= 1) - { return Array.Empty(); - } s_intermediatePointsPooledList.Clear(); - s_intermediatePointsPooledList.Capacity = pointsCount; - for (int i = pointsCount - 1; i >= 1; i--) + s_sortedPoints.Clear(); + + s_sortedPoints.Capacity = pointsCount; + + for (int i = 0; i < pointsCount; i++) { - var historyInfo = s_mouseHistoryInfos[i]; - // Skip points newer than current point. - if (historyInfo.time > movePoint.time || - (historyInfo.time == movePoint.time && - historyInfo.x == movePoint.x && - historyInfo.y == movePoint.y)) - { + var mp = movePoints[i]; + + var x = mp.x > 32767 ? mp.x - 65536 : mp.x; + var y = mp.y > 32767 ? mp.y - 65536 : mp.y; + + if(mp.time <= prevMovePoint.time || mp.time >= movePoint.time) continue; - } - // Skip points older from previous WM_MOUSEMOVE point. - if (historyInfo.time < prevMovePoint.time || - (historyInfo.time == prevMovePoint.time && - historyInfo.x == prevMovePoint.x && - historyInfo.y == prevMovePoint.y)) + + s_sortedPoints.Add(new InternalPoint { - continue; - } + Time = mp.time, + Pt = new PixelPoint(x, y) + }); + } - // To support multiple screens. - if (historyInfo.x > 32767) - historyInfo.x -= 65536; + // sorting is required to ensure points are in order from oldest to newest + s_sortedPoints.Sort(static (a, b) => a.Time.CompareTo(b.Time)); - if (historyInfo.y > 32767) - historyInfo.y -= 65536; + foreach (var p in s_sortedPoints) + { + var client = PointToClient(p.Pt); - var point = PointToClient(new PixelPoint(historyInfo.x, historyInfo.y)); s_intermediatePointsPooledList.Add(new RawPointerPoint { - Position = point + Position = client }); } + return s_intermediatePointsPooledList; } + } private RawPointerEventArgs CreatePointerArgs(IInputDevice device, ulong timestamp, RawPointerEventType eventType, RawPointerPoint point, RawInputModifiers modifiers, uint rawPointerId) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 769a8d6768..b1b1bb08e6 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -111,6 +111,7 @@ namespace Avalonia.Win32 private const int MaxPointerHistorySize = 512; private static readonly PooledList s_intermediatePointsPooledList = new(); + private static readonly List s_sortedPoints = new(64); private static POINTER_TOUCH_INFO[]? s_historyTouchInfos; private static POINTER_PEN_INFO[]? s_historyPenInfos; private static POINTER_INFO[]? s_historyInfos; @@ -1741,5 +1742,11 @@ namespace Avalonia.Win32 public double Scaling => _owner.RenderScaling; } + + private struct InternalPoint + { + public int Time; + public PixelPoint Pt; + } } }