diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
index d9be50a967..798affd337 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
@@ -1363,14 +1363,34 @@ namespace Avalonia.Win32
Imm32InputMethod.Current.SetLanguageAndWindow(this, Hwnd, hkl);
}
+
+ // GetPointerDeviceRects is part of the WM_POINTER API (Windows 8+) but is not implemented
+ // by Wine/Proton. Probe once and fall back to the integer pixel location when missing,
+ // otherwise the P/Invoke throws EntryPointNotFoundException for every pointer message.
+ // See https://github.com/AvaloniaUI/Avalonia/issues/21081.
+ private static readonly bool s_isGetPointerDeviceRectsAvailable = ProbeGetPointerDeviceRects();
+
+ private static bool ProbeGetPointerDeviceRects()
+ {
+ var user32 = LoadLibrary("user32.dll");
+ return user32 != IntPtr.Zero
+ && GetProcAddress(user32, nameof(GetPointerDeviceRects)) != IntPtr.Zero;
+ }
///
- /// Get the location of the pointer in himetric units.
+ /// Get the location of the pointer in screen coordinates with HIMETRIC sub-pixel precision
+ /// when supported, falling back to the integer pixel location on platforms that do not
+ /// implement GetPointerDeviceRects (e.g. Wine/Proton).
///
/// The pointer info.
- /// The location of the pointer in himetric units.
+ /// The pointer location in screen pixels.
private Point GetHimetricLocation(POINTER_INFO info)
{
+ if (!s_isGetPointerDeviceRectsAvailable)
+ {
+ return new Point(info.ptPixelLocationX, info.ptPixelLocationY);
+ }
+
GetPointerDeviceRects(info.sourceDevice, out var pointerDeviceRect, out var displayRect);
var himetricLocation = new Point(
info.ptHimetricLocationRawX * displayRect.Width / (double)pointerDeviceRect.Width + displayRect.left,