Skip to content

Commit

Permalink
Use the himetric location instead of the pixel location. (#16850)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
walterlv authored Oct 14, 2024
1 parent 4e19ed8 commit 0bd90d8
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 19 deletions.
19 changes: 11 additions & 8 deletions src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public enum ShowWindowCommand
/// </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,
Expand Down Expand Up @@ -147,12 +147,12 @@ public enum ShowWindowCommand
/// </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,
Expand Down Expand Up @@ -1173,6 +1173,9 @@ public struct MOUSEMOVEPOINT
[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);

Expand Down Expand Up @@ -1221,12 +1224,12 @@ public static extern IntPtr CreateWindowEx(

[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);

Expand Down Expand Up @@ -1534,7 +1537,7 @@ public static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex)

[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);
Expand Down Expand Up @@ -2176,7 +2179,7 @@ public enum ClipboardFormat
/// </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,
}
Expand Down
26 changes: 20 additions & 6 deletions src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam,
{
requestDpi = _dpi;
}

return LoadIcon(requestIcon, requestDpi)?.Handle ?? default;

case WindowsMessage.WM_KEYDOWN:
Expand Down Expand Up @@ -778,7 +778,7 @@ protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam,
{
LostFocus?.Invoke();
}

break;

case WindowsMessage.WM_INPUTLANGCHANGE:
Expand Down Expand Up @@ -1095,8 +1095,8 @@ private RawPointerPoint CreateRawPointerPoint(POINTER_INFO pointerInfo)
}
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
{
Expand Down Expand Up @@ -1129,8 +1129,8 @@ private RawPointerPoint CreateRawPointerPoint(POINTER_TOUCH_INFO info)
}
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,
Expand Down Expand Up @@ -1198,6 +1198,20 @@ private void UpdateInputMethod(IntPtr hkl)
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)
Expand Down
29 changes: 24 additions & 5 deletions src/Windows/Avalonia.Win32/WindowImpl.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -454,7 +454,7 @@ private bool SetTransparencyMica()
{
SetUseHostBackdropBrush(false);
SetLegacyTransparency(false);

CompositionEffectsSurface!.SetBlur(_currentThemeVariant switch
{
PlatformThemeVariant.Light => BlurEffect.MicaLight,
Expand All @@ -468,7 +468,7 @@ private bool SetLegacyTransparency(bool enabled)
{
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.
Expand Down Expand Up @@ -496,7 +496,7 @@ private unsafe bool SetUseHostBackdropBrush(bool useHostBackdropBrush)

// 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;
Expand Down Expand Up @@ -657,13 +657,32 @@ public void Invalidate(Rect rect)
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 };
ScreenToClient(_hwnd, ref p);
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;
Expand Down Expand Up @@ -960,7 +979,7 @@ private void CreateWindow()

Handle = new WindowImplPlatformHandle(this);

RegisterTouchWindow(_hwnd, 0);
RegisterTouchWindow(_hwnd, 0);

if (ShCoreAvailable && Win32Platform.WindowsVersion >= PlatformConstants.Windows8_1)
{
Expand Down

0 comments on commit 0bd90d8

Please sign in to comment.