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,