diff --git a/src/Uno.UI/UI/Xaml/Input/Pointer.cs b/src/Uno.UI/UI/Xaml/Input/Pointer.cs index b7fc45ea6777..459065cd0fb1 100644 --- a/src/Uno.UI/UI/Xaml/Input/Pointer.cs +++ b/src/Uno.UI/UI/Xaml/Input/Pointer.cs @@ -4,7 +4,7 @@ using System.Threading; using Uno; - +using PointerIdentifier = Windows.Devices.Input.PointerIdentifier; // internal type (should be in Uno namespace) #if HAS_UNO_WINUI using Microsoft.UI.Input; #else @@ -27,7 +27,17 @@ public Pointer(uint id, PointerDeviceType type, bool isInContact, bool isInRange IsInContact = isInContact; IsInRange = isInRange; - UniqueId = new Windows.Devices.Input.PointerIdentifier((Windows.Devices.Input.PointerDeviceType)type, id); + UniqueId = new PointerIdentifier((Windows.Devices.Input.PointerDeviceType)type, id); + } + + internal Pointer(PointerIdentifier uniqueId, bool isInContact, bool isInRange) + { + PointerId = uniqueId.Id; + PointerDeviceType = (PointerDeviceType)uniqueId.Type; + IsInContact = isInContact; + IsInRange = isInRange; + + UniqueId = uniqueId; } #if __WASM__ @@ -36,7 +46,7 @@ internal Pointer(uint id, PointerDeviceType type) PointerId = id; PointerDeviceType = type; - UniqueId = new Windows.Devices.Input.PointerIdentifier((Windows.Devices.Input.PointerDeviceType)type, id); + UniqueId = new PointerIdentifier((Windows.Devices.Input.PointerDeviceType)type, id); } #endif @@ -54,9 +64,10 @@ internal Pointer(uint id, PointerDeviceType type) public bool IsInRange { get; } public override string ToString() - { - return $"{PointerDeviceType}/{PointerId}"; - } + => UniqueId.ToString(); + + public override int GetHashCode() + => UniqueId.GetHashCode(); public bool Equals(Pointer other) { @@ -74,13 +85,5 @@ public override bool Equals(object obj) return UniqueId == other.UniqueId; } - - public override int GetHashCode() - { - unchecked - { - return ((int)PointerDeviceType * 397) ^ (int)PointerId; - } - } } } diff --git a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.wasm.cs b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.wasm.cs index 934cc147e652..ad65fd38f766 100644 --- a/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.wasm.cs +++ b/src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.wasm.cs @@ -5,6 +5,7 @@ using Uno.Foundation; using Uno.UI.Xaml; +using PointerIdentifier = Windows.Devices.Input.PointerIdentifier; // internal type (should be in Uno namespace) #if HAS_UNO_WINUI using Microsoft.UI.Input; #else @@ -25,8 +26,7 @@ partial class PointerRoutedEventArgs internal PointerRoutedEventArgs( double timestamp, - uint pointerId, - PointerDeviceType pointerType, + PointerIdentifier pointerUniqueId, Point absolutePosition, bool isInContact, bool isInRange, @@ -46,7 +46,7 @@ internal PointerRoutedEventArgs( _wheel = wheel; FrameId = ToFrameId(timestamp); - Pointer = new Pointer(pointerId, pointerType, isInContact, isInRange); + Pointer = new Pointer(pointerUniqueId, isInContact, isInRange); KeyModifiers = keys; OriginalSource = source; } diff --git a/src/Uno.UI/UI/Xaml/Internal/PointerCapture.wasm.cs b/src/Uno.UI/UI/Xaml/Internal/PointerCapture.wasm.cs index df2f0ed9e561..734448a3df16 100644 --- a/src/Uno.UI/UI/Xaml/Internal/PointerCapture.wasm.cs +++ b/src/Uno.UI/UI/Xaml/Internal/PointerCapture.wasm.cs @@ -2,16 +2,36 @@ using System; using System.Linq; +using Windows.Devices.Input; using Windows.UI.Xaml; using Windows.UI.Xaml.Input; +using Uno.Foundation.Logging; namespace Uno.UI.Xaml.Core; internal partial class PointerCapture { partial void CaptureNative(UIElement target, Pointer pointer) - => WindowManagerInterop.SetPointerCapture(target.HtmlId, pointer.PointerId); + { + if (PointerIdentifierPool.TryGetNative(pointer.UniqueId, out var native)) + { + WindowManagerInterop.SetPointerCapture(target.HtmlId, native.Id); + } + else if (this.Log().IsEnabled(LogLevel.Warning)) + { + this.Log().Warn($"Cannot capture pointer, could not find native pointer id for managed pointer id {pointer.UniqueId}"); + } + } partial void ReleaseNative(UIElement target, Pointer pointer) - => WindowManagerInterop.ReleasePointerCapture(target.HtmlId, pointer.PointerId); + { + if (PointerIdentifierPool.TryGetNative(pointer.UniqueId, out var native)) + { + WindowManagerInterop.ReleasePointerCapture(target.HtmlId, native.Id); + } + else if (this.Log().IsEnabled(LogLevel.Warning)) + { + this.Log().Warn($"Cannot release pointer, could not find native pointer id for managed pointer id {pointer.UniqueId}"); + } + } } diff --git a/src/Uno.UI/UI/Xaml/Internal/RootVisual.cs b/src/Uno.UI/UI/Xaml/Internal/RootVisual.cs index 7ffc1ade6181..dd649c8b17ce 100644 --- a/src/Uno.UI/UI/Xaml/Internal/RootVisual.cs +++ b/src/Uno.UI/UI/Xaml/Internal/RootVisual.cs @@ -15,6 +15,7 @@ using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Input; +using PointerIdentifierPool = Windows.Devices.Input.PointerIdentifierPool; // internal type (should be in Uno namespace) #if HAS_UNO_WINUI using Microsoft.UI.Input; #else @@ -147,9 +148,7 @@ private static void ProcessPointerDown(PointerRoutedEventArgs args) #if __WASM__ private static void ProcessPointerCancelledWasm(PointerRoutedEventArgs args) - { - RemoveActivePointer(args.Pointer.UniqueId); - } + => PointerIdentifierPool.ReleaseManaged(args.Pointer.UniqueId); #endif internal void ProcessPointerUp(PointerRoutedEventArgs args, bool isAfterHandledUp = false) @@ -200,7 +199,7 @@ internal void ProcessPointerUp(PointerRoutedEventArgs args, bool isAfterHandledU ReleaseCaptures(args.Reset(canBubbleNatively: false)); #if __WASM__ - RemoveActivePointer(args.Pointer.UniqueId); + PointerIdentifierPool.ReleaseManaged(args.Pointer.UniqueId); #endif } diff --git a/src/Uno.UI/UI/Xaml/UIElement.Pointers.wasm.cs b/src/Uno.UI/UI/Xaml/UIElement.Pointers.wasm.cs index 29378eaa4f04..f88f602f047b 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.Pointers.wasm.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.Pointers.wasm.cs @@ -17,14 +17,17 @@ using Uno.UI.Extensions; using Uno.UI.Xaml; using Uno.UI.Xaml.Core; -using PointerIdentifier = Windows.Devices.Input.PointerIdentifier; using WinUICoreServices = Uno.UI.Xaml.Core.CoreServices; +using PointerIdentifierPool = Windows.Devices.Input.PointerIdentifierPool; // internal type (should be in Uno namespace) +using PointerIdentifier = Windows.Devices.Input.PointerIdentifier; // internal type (should be in Uno namespace) +using PointerIdentifierDeviceType = Windows.Devices.Input.PointerDeviceType; // PointerIdentifier always uses Windows.Devices as it does noe have access to Microsoft.UI.Input (Uno.UI assembly) #if HAS_UNO_WINUI using Microsoft.UI.Input; +using PointerDeviceType = Microsoft.UI.Input.PointerDeviceType; #else -using Windows.Devices.Input; using Windows.UI.Input; +using PointerDeviceType = Windows.Devices.Input.PointerDeviceType; #endif #if NET7_0_OR_GREATER @@ -249,42 +252,6 @@ public static void OnNativePointerEvent() } } - private static readonly Dictionary _nativeToManagedPointerId = new(); - private static readonly Dictionary _managedToNativePointerId = new(); - private static uint _lastUsedId; - - private static uint TransformPointerId(PointerIdentifier nativeId) - { - if (_nativeToManagedPointerId.TryGetValue(nativeId, out var managedId)) - { - return managedId.Id; - } - - managedId = new PointerIdentifier(nativeId.Type, ++_lastUsedId); - _managedToNativePointerId[managedId] = nativeId; - _nativeToManagedPointerId[nativeId] = managedId; - - return managedId.Id; - } - - internal static void RemoveActivePointer(PointerIdentifier managedId) - { - if (_managedToNativePointerId.TryGetValue(managedId, out var nativeId)) - { - _managedToNativePointerId.Remove(managedId); - _nativeToManagedPointerId.Remove(nativeId); - - if (_managedToNativePointerId.Count == 0) - { - _lastUsedId = 0; // We reset the pointer ID only when there is no active pointer. - } - } - else if (typeof(UIElement).Log().IsEnabled(LogLevel.Warning)) - { - typeof(UIElement).Log().Warn($"Received an invalid managed pointer id {managedId}"); - } - } - private static PointerRoutedEventArgs ToPointerArgs( UIElement snd, NativePointerEventArgs args, @@ -294,7 +261,7 @@ private static PointerRoutedEventArgs ToPointerArgs( const int exitOrUp = (int)(NativePointerEvent.pointerout | NativePointerEvent.pointerup); var pointerType = (PointerDeviceType)args.deviceType; - var pointerId = TransformPointerId(new PointerIdentifier((Windows.Devices.Input.PointerDeviceType)pointerType, (uint)args.pointerId)); + var pointerId = PointerIdentifierPool.RentManaged(new PointerIdentifier((PointerIdentifierDeviceType)pointerType, (uint)args.pointerId)); var src = GetElementFromHandle(args.srcHandle) ?? (UIElement)snd; var position = new Point(args.x, args.y); @@ -321,7 +288,6 @@ private static PointerRoutedEventArgs ToPointerArgs( return new PointerRoutedEventArgs( args.timestamp, pointerId, - pointerType, position, isInContact, isInRange, diff --git a/src/Uno.UWP/Devices/Input/PointerIdentifier.cs b/src/Uno.UWP/Devices/Input/PointerIdentifier.cs index f6b6d206cc91..06a6ef341446 100644 --- a/src/Uno.UWP/Devices/Input/PointerIdentifier.cs +++ b/src/Uno.UWP/Devices/Input/PointerIdentifier.cs @@ -1,43 +1,46 @@ using System; using System.Linq; -namespace Windows.Devices.Input +namespace Windows.Devices.Input; + +internal readonly struct PointerIdentifier : IEquatable { - internal readonly struct PointerIdentifier : IEquatable + private readonly long _uid; + + public PointerIdentifier(PointerDeviceType type, uint id) { - private readonly long _uid; + _uid = (long)type << 32 | id; + } - public PointerIdentifier(PointerDeviceType type, uint id) - { - _uid = (long)type << 32 | id; - } + public uint Id => unchecked((uint)(_uid & 0xFFFF_FFFF)); - public uint Id => unchecked((uint)(_uid & 0xFFFF_FFFF)); + public PointerDeviceType Type => unchecked((PointerDeviceType)(_uid >> 32)); - public PointerDeviceType Type => unchecked((PointerDeviceType)(_uid >> 32)); + /// + public override string ToString() + => $"{Type}/{Id}"; - /// - public override int GetHashCode() - => _uid.GetHashCode(); + /// + public override int GetHashCode() + => _uid.GetHashCode(); - /// - public override bool Equals(object obj) - => obj is PointerIdentifier other && Equals(this, other); + /// + public override bool Equals(object obj) + => obj is PointerIdentifier other && Equals(this, other); - /// - public bool Equals(PointerIdentifier other) - => Equals(this, other); + /// + public bool Equals(PointerIdentifier other) + => Equals(this, other); - private static bool Equals(PointerIdentifier left, PointerIdentifier right) - => left._uid == right._uid; + private static bool Equals(PointerIdentifier left, PointerIdentifier right) + => left._uid == right._uid; - public static bool operator ==(PointerIdentifier left, PointerIdentifier right) - => Equals(left, right); + public static bool operator ==(PointerIdentifier left, PointerIdentifier right) + => Equals(left, right); - public static bool operator !=(PointerIdentifier left, PointerIdentifier right) - => !Equals(left, right); + public static bool operator !=(PointerIdentifier left, PointerIdentifier right) + => !Equals(left, right); - public static implicit operator long(PointerIdentifier identifier) - => identifier._uid; - } + public static implicit operator long(PointerIdentifier identifier) + => identifier._uid; } diff --git a/src/Uno.UWP/Devices/Input/PointerIdentifierPool.cs b/src/Uno.UWP/Devices/Input/PointerIdentifierPool.cs new file mode 100644 index 000000000000..3d7af12e060d --- /dev/null +++ b/src/Uno.UWP/Devices/Input/PointerIdentifierPool.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Uno.Foundation.Logging; + +namespace Windows.Devices.Input; + +internal static class PointerIdentifierPool +{ + private static readonly Dictionary _nativeToManagedPointerId = new(); + private static readonly Dictionary _managedToNativePointerId = new(); + private static uint _lastUsedId; + + public static PointerIdentifier RentManaged(PointerIdentifier nativeId) + { + if (_nativeToManagedPointerId.TryGetValue(nativeId, out var managedId)) + { + return managedId; + } + + managedId = new PointerIdentifier(nativeId.Type, ++_lastUsedId); + _managedToNativePointerId[managedId] = nativeId; + _nativeToManagedPointerId[nativeId] = managedId; + + return managedId; + } + + public static bool TryGetNative(PointerIdentifier managedId, out PointerIdentifier nativeId) + => _managedToNativePointerId.TryGetValue(managedId, out nativeId); + + public static void ReleaseManaged(PointerIdentifier managedId) + { + if (_managedToNativePointerId.TryGetValue(managedId, out var nativeId)) + { + _managedToNativePointerId.Remove(managedId); + _nativeToManagedPointerId.Remove(nativeId); + + if (_managedToNativePointerId.Count == 0) + { + _lastUsedId = 0; // We reset the pointer ID only when there is no active pointer. + } + } + else if (typeof(PointerIdentifierPool).Log().IsEnabled(LogLevel.Warning)) + { + typeof(PointerIdentifierPool).Log().Warn($"Received an invalid managed pointer id {managedId}"); + } + } +}