Skip to content

Commit

Permalink
Merge pull request #12264 from unoplatform/dev/dr/captureFF
Browse files Browse the repository at this point in the history
fix(reg): Fix pointer capture broken on Firefox
  • Loading branch information
dr1rrb authored May 19, 2023
2 parents 1c08570 + 2aec00e commit 4e63502
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 90 deletions.
31 changes: 17 additions & 14 deletions src/Uno.UI/UI/Xaml/Input/Pointer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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__
Expand All @@ -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

Expand All @@ -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)
{
Expand All @@ -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;
}
}
}
}
6 changes: 3 additions & 3 deletions src/Uno.UI/UI/Xaml/Input/PointerRoutedEventArgs.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,8 +26,7 @@ partial class PointerRoutedEventArgs

internal PointerRoutedEventArgs(
double timestamp,
uint pointerId,
PointerDeviceType pointerType,
PointerIdentifier pointerUniqueId,
Point absolutePosition,
bool isInContact,
bool isInRange,
Expand All @@ -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;
}
Expand Down
24 changes: 22 additions & 2 deletions src/Uno.UI/UI/Xaml/Internal/PointerCapture.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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}");
}
}
}
7 changes: 3 additions & 4 deletions src/Uno.UI/UI/Xaml/Internal/RootVisual.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}

Expand Down
46 changes: 6 additions & 40 deletions src/Uno.UI/UI/Xaml/UIElement.Pointers.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -249,42 +252,6 @@ public static void OnNativePointerEvent()
}
}

private static readonly Dictionary<PointerIdentifier, PointerIdentifier> _nativeToManagedPointerId = new();
private static readonly Dictionary<PointerIdentifier, PointerIdentifier> _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,
Expand All @@ -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);
Expand All @@ -321,7 +288,6 @@ private static PointerRoutedEventArgs ToPointerArgs(
return new PointerRoutedEventArgs(
args.timestamp,
pointerId,
pointerType,
position,
isInContact,
isInRange,
Expand Down
57 changes: 30 additions & 27 deletions src/Uno.UWP/Devices/Input/PointerIdentifier.cs
Original file line number Diff line number Diff line change
@@ -1,43 +1,46 @@
using System;
using System.Linq;

namespace Windows.Devices.Input
namespace Windows.Devices.Input;

internal readonly struct PointerIdentifier : IEquatable<PointerIdentifier>
{
internal readonly struct PointerIdentifier : IEquatable<PointerIdentifier>
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));
/// <inheritdoc />
public override string ToString()
=> $"{Type}/{Id}";

/// <inheritdoc />
public override int GetHashCode()
=> _uid.GetHashCode();
/// <inheritdoc />
public override int GetHashCode()
=> _uid.GetHashCode();

/// <inheritdoc />
public override bool Equals(object obj)
=> obj is PointerIdentifier other && Equals(this, other);
/// <inheritdoc />
public override bool Equals(object obj)
=> obj is PointerIdentifier other && Equals(this, other);

/// <inheritdoc />
public bool Equals(PointerIdentifier other)
=> Equals(this, other);
/// <inheritdoc />
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;
}
48 changes: 48 additions & 0 deletions src/Uno.UWP/Devices/Input/PointerIdentifierPool.cs
Original file line number Diff line number Diff line change
@@ -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<PointerIdentifier, PointerIdentifier> _nativeToManagedPointerId = new();
private static readonly Dictionary<PointerIdentifier, PointerIdentifier> _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}");
}
}
}

0 comments on commit 4e63502

Please sign in to comment.