Browse Source

Merge pull request #9358 from AvaloniaUI/win_mouse_IntermediatePoints

Implement windows IntermediatePoints for the Mouse input
pull/9362/head
Nikita Tsukanov 3 years ago
committed by GitHub
parent
commit
56ebee2b4c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  2. 84
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  3. 2
      src/Windows/Avalonia.Win32/WindowImpl.cs

14
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1083,6 +1083,20 @@ namespace Avalonia.Win32.Interop
public const int SizeOf_BITMAPINFOHEADER = 40;
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)]
unsafe internal static extern int GetMouseMovePointsEx(
uint cbSize, MOUSEMOVEPOINT* pointsIn,
MOUSEMOVEPOINT* pointsBufferOut, int nBufPoints, uint resolution);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] // For GetMouseMovePointsEx
public struct MOUSEMOVEPOINT
{
public int x; //Specifies the x-coordinate of the mouse
public int y; //Specifies the x-coordinate of the mouse
public int time; //Specifies the time stamp of the mouse coordinate
public IntPtr dwExtraInfo; //Specifies extra information associated with this coordinate.
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool IsMouseInPointerEnabled();

84
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -272,13 +272,34 @@ namespace Avalonia.Win32
TrackMouseEvent(ref tm);
}
var point = DipFromLParam(lParam);
// Prepare points for the IntermediatePoints call.
var p = new POINT()
{
X = (int)(point.X * RenderScaling),
Y = (int)(point.Y * RenderScaling)
};
ClientToScreen(_hwnd, ref p);
var currPoint = new MOUSEMOVEPOINT()
{
x = p.X & 0xFFFF,
y = p.Y & 0xFFFF,
time = (int)timestamp
};
var prevPoint = _lastWmMousePoint;
_lastWmMousePoint = currPoint;
e = new RawPointerEventArgs(
_mouseDevice,
timestamp,
_owner,
RawPointerEventType.Move,
DipFromLParam(lParam),
GetMouseModifiers(wParam));
point,
GetMouseModifiers(wParam))
{
IntermediatePoints = new Lazy<IReadOnlyList<RawPointerPoint>>(() => CreateLazyIntermediatePoints(currPoint, prevPoint))
};
break;
}
@ -782,6 +803,65 @@ namespace Avalonia.Win32
return null;
}
private unsafe IReadOnlyList<RawPointerPoint> CreateLazyIntermediatePoints(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
fixed (MOUSEMOVEPOINT* movePoints = s_mouseHistoryInfos)
{
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,
1);
// 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<RawPointerPoint>();
}
s_intermediatePointsPooledList.Clear();
s_intermediatePointsPooledList.Capacity = pointsCount;
for (int i = pointsCount - 1; i >= 1; 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))
{
continue;
}
// Skip poins older from previous WM_MOUSEMOVE point.
if (historyInfo.time < prevMovePoint.time ||
(historyInfo.time == prevMovePoint.time &&
historyInfo.x == prevMovePoint.x &&
historyInfo.y == prevMovePoint.y))
{
continue;
}
// To support multiple screens.
if (historyInfo.x > 32767)
historyInfo.x -= 65536;
if (historyInfo.y > 32767)
historyInfo.y -= 65536;
var point = PointToClient(new PixelPoint(historyInfo.x, historyInfo.y));
s_intermediatePointsPooledList.Add(new RawPointerPoint
{
Position = point
});
}
return s_intermediatePointsPooledList;
}
}
private RawPointerEventArgs CreatePointerArgs(IInputDevice device, ulong timestamp, RawPointerEventType eventType, RawPointerPoint point, RawInputModifiers modifiers, uint rawPointerId)
{
return device is TouchDevice

2
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -65,6 +65,7 @@ namespace Avalonia.Win32
private bool _isUsingComposition;
private IBlurHost _blurHost;
private PlatformResizeReason _resizeReason;
private MOUSEMOVEPOINT _lastWmMousePoint;
#if USE_MANAGED_DRAG
private readonly ManagedWindowResizeDragHelper _managedDrag;
@ -107,6 +108,7 @@ namespace Avalonia.Win32
private static readonly POINTER_TOUCH_INFO[] s_historyTouchInfos = new POINTER_TOUCH_INFO[MaxPointerHistorySize];
private static readonly POINTER_PEN_INFO[] s_historyPenInfos = new POINTER_PEN_INFO[MaxPointerHistorySize];
private static readonly POINTER_INFO[] s_historyInfos = new POINTER_INFO[MaxPointerHistorySize];
private static readonly MOUSEMOVEPOINT[] s_mouseHistoryInfos = new MOUSEMOVEPOINT[64];
public WindowImpl()
{

Loading…
Cancel
Save