Browse Source

Use the himetric location instead of the pixel location. (#16850)

* Add an overload with more precise point transformation.

* Use the himetric location instead of the pixel location.

* The pen also use the himetric location.

* Similar codes.

* Use the raw location instead of the predicted one.
See: https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_info

* Fix wrong multi-screen location

* Add lost win32 api.
fixes/tcc-333
walterlv 2 years ago
committed by GitHub
parent
commit
0bd90d802a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 19
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  2. 26
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  3. 29
      src/Windows/Avalonia.Win32/WindowImpl.cs

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

@ -112,7 +112,7 @@ namespace Avalonia.Win32.Interop
/// </summary>
Hide = 0,
/// <summary>
/// Activates and displays a window. If the window is minimized, maximized, or arranged, the system restores it to its original
/// Activates and displays a window. If the window is minimized, maximized, or arranged, the system restores it to its original
/// size and position. An application should specify this flag when displaying the window for the first time.
/// </summary>
Normal = 1,
@ -147,12 +147,12 @@ namespace Avalonia.Win32.Interop
/// </summary>
ShowNA = 8,
/// <summary>
/// Activates and displays the window. If the window is minimized, maximized, or arranged, the system restores it to its original size and position.
/// Activates and displays the window. If the window is minimized, maximized, or arranged, the system restores it to its original size and position.
/// An application should specify this flag when restoring a minimized window.
/// </summary>
Restore = 9,
/// <summary>
/// Sets the show state based on the <see cref="ShowWindowCommand"/> value specified in the STARTUPINFO structure passed to the CreateProcess function
/// Sets the show state based on the <see cref="ShowWindowCommand"/> value specified in the STARTUPINFO structure passed to the CreateProcess function
/// by the program that started the application.
/// </summary>
ShowDefault = 10,
@ -1173,6 +1173,9 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerTouchInfo(uint pointerId, out POINTER_TOUCH_INFO touchInfo);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerDeviceRects(IntPtr device, out RECT pointerDeviceRect, out RECT displayRect);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetPointerTouchInfoHistory(uint pointerId, ref int entriesCount, [MarshalAs(UnmanagedType.LPArray), In, Out] POINTER_TOUCH_INFO[] touchInfos);
@ -1221,12 +1224,12 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll", EntryPoint = "DefWindowProcW")]
public static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
public const int SC_MOUSEMOVE = 0xf012;
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "DispatchMessageW")]
public static extern IntPtr DispatchMessage(ref MSG lpmsg);
@ -1534,7 +1537,7 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll", EntryPoint = "SetCursor")]
internal static extern IntPtr SetCursor(IntPtr hCursor);
[DllImport("ole32.dll", PreserveSig = true)]
internal static extern int CoCreateInstance(in Guid clsid,
IntPtr ignore1, int ignore2, in Guid iid, [Out] out IntPtr pUnkOuter);
@ -2176,7 +2179,7 @@ namespace Avalonia.Win32.Interop
/// </summary>
CF_UNICODETEXT = 13,
/// <summary>
/// A handle to type HDROP that identifies a list of files.
/// A handle to type HDROP that identifies a list of files.
/// </summary>
CF_HDROP = 15,
}

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

@ -176,7 +176,7 @@ namespace Avalonia.Win32
{
requestDpi = _dpi;
}
return LoadIcon(requestIcon, requestDpi)?.Handle ?? default;
case WindowsMessage.WM_KEYDOWN:
@ -778,7 +778,7 @@ namespace Avalonia.Win32
{
LostFocus?.Invoke();
}
break;
case WindowsMessage.WM_INPUTLANGCHANGE:
@ -1095,8 +1095,8 @@ namespace Avalonia.Win32
}
private RawPointerPoint CreateRawPointerPoint(POINTER_TOUCH_INFO info)
{
var pointerInfo = info.pointerInfo;
var point = PointToClient(new PixelPoint(pointerInfo.ptPixelLocationX, pointerInfo.ptPixelLocationY));
var himetricLocation = GetHimetricLocation(info.pointerInfo);
var point = PointToClient(himetricLocation);
var pointerPoint = new RawPointerPoint
{
@ -1129,8 +1129,8 @@ namespace Avalonia.Win32
}
private RawPointerPoint CreateRawPointerPoint(POINTER_PEN_INFO info)
{
var pointerInfo = info.pointerInfo;
var point = PointToClient(new PixelPoint(pointerInfo.ptPixelLocationX, pointerInfo.ptPixelLocationY));
var himetricLocation = GetHimetricLocation(info.pointerInfo);
var point = PointToClient(himetricLocation);
return new RawPointerPoint
{
Position = point,
@ -1198,6 +1198,20 @@ namespace Avalonia.Win32
Imm32InputMethod.Current.SetLanguageAndWindow(this, Hwnd, hkl);
}
/// <summary>
/// Get the location of the pointer in himetric units.
/// </summary>
/// <param name="info">The pointer info.</param>
/// <returns>The location of the pointer in himetric units.</returns>
private Point GetHimetricLocation(POINTER_INFO info)
{
GetPointerDeviceRects(info.sourceDevice, out var pointerDeviceRect, out var displayRect);
var himetricLocation = new Point(
info.ptHimetricLocationRawX * displayRect.Width / (double)pointerDeviceRect.Width + displayRect.left,
info.ptHimetricLocationRawY * displayRect.Height / (double)pointerDeviceRect.Height + displayRect.top);
return himetricLocation;
}
private static int ToInt32(IntPtr ptr)
{
if (IntPtr.Size == 4)

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

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
@ -454,7 +454,7 @@ namespace Avalonia.Win32
{
SetUseHostBackdropBrush(false);
SetLegacyTransparency(false);
CompositionEffectsSurface!.SetBlur(_currentThemeVariant switch
{
PlatformThemeVariant.Light => BlurEffect.MicaLight,
@ -468,7 +468,7 @@ namespace Avalonia.Win32
{
if (Win32Platform.WindowsVersion < PlatformConstants.Windows8 || !UseRedirectionBitmap)
return false;
// On pre-Win8 this method was blurring a window, which is a different from desired behavior.
// On win8+ we use this method as a fallback, when WinUI/DComp composition with true transparency isn't available.
// Note: there is no guarantee that this behavior won't be changed back to true blur in Win12.
@ -496,7 +496,7 @@ namespace Avalonia.Win32
// AcrylicBlur requires window to set DWMWA_USE_HOSTBACKDROPBRUSH flag on Win11+.
// It's not necessary on older versions and it's not necessary with Mica brush.
var pvUseBackdropBrush = useHostBackdropBrush ? 1 : 0;
var result = DwmSetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_USE_HOSTBACKDROPBRUSH, &pvUseBackdropBrush, sizeof(int));
return result == 0;
@ -657,6 +657,12 @@ namespace Avalonia.Win32
InvalidateRect(_hwnd, ref r, false);
}
/// <summary>
/// Transform a screen pixel point to the point in the client area.<br/>
/// To transform a point with precise value, use the <see cref="PointToClient(Point)"/> overload instead.
/// </summary>
/// <param name="point">The screen pixel point to be transformed.</param>
/// <returns>The point in the client area.</returns>
public Point PointToClient(PixelPoint point)
{
var p = new POINT { X = point.X, Y = point.Y };
@ -664,6 +670,19 @@ namespace Avalonia.Win32
return new Point(p.X, p.Y) / RenderScaling;
}
/// <summary>
/// Transform a screen point to the point in the client area.<br/>
/// Comparing to the <see cref="PixelPoint"/> overload, this method receives double values and can be more precise.
/// </summary>
/// <param name="point">The screen point to be transformed.</param>
/// <returns>The point in the client area.</returns>
public Point PointToClient(Point point)
{
var p = new POINT { X = 0, Y = 0 };
ClientToScreen(_hwnd, ref p);
return new Point(point.X - p.X, point.Y - p.Y) / RenderScaling;
}
public PixelPoint PointToScreen(Point point)
{
point *= RenderScaling;
@ -960,7 +979,7 @@ namespace Avalonia.Win32
Handle = new WindowImplPlatformHandle(this);
RegisterTouchWindow(_hwnd, 0);
RegisterTouchWindow(_hwnd, 0);
if (ShCoreAvailable && Win32Platform.WindowsVersion >= PlatformConstants.Windows8_1)
{

Loading…
Cancel
Save