diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index c8247c39894fd..d2a44b87f7954 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -510,7 +510,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { @override void setup() { _addPointerEventListener('pointerdown', (html.PointerEvent event) { - final int device = event.pointerId!; + final int device = _getPointerId(event); final List pointerData = []; final _ButtonSanitizer sanitizer = _ensureSanitizer(device); final _SanitizedDetails? up = @@ -528,7 +528,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { }); _addPointerEventListener('pointermove', (html.PointerEvent event) { - final int device = event.pointerId!; + final int device = _getPointerId(event); final _ButtonSanitizer sanitizer = _ensureSanitizer(device); final List pointerData = []; final List expandedEvents = _expandEvents(event); @@ -544,7 +544,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { }, acceptOutsideGlasspane: true); _addPointerEventListener('pointerup', (html.PointerEvent event) { - final int device = event.pointerId!; + final int device = _getPointerId(event); final List pointerData = []; final _SanitizedDetails? details = _getSanitizer(device).sanitizeUpEvent(buttons: event.buttons); _removePointerIfUnhoverable(event); @@ -557,7 +557,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { // A browser fires cancel event if it concludes the pointer will no longer // be able to generate events (example: device is deactivated) _addPointerEventListener('pointercancel', (html.PointerEvent event) { - final int device = event.pointerId!; + final int device = _getPointerId(event); final List pointerData = []; final _SanitizedDetails details = _getSanitizer(device).sanitizeCancelEvent(); _removePointerIfUnhoverable(event); @@ -581,10 +581,6 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { assert(event != null); // ignore: unnecessary_null_comparison assert(details != null); // ignore: unnecessary_null_comparison final ui.PointerDeviceKind kind = _pointerTypeToDeviceKind(event.pointerType!); - // We force `device: _mouseDeviceId` on mouse pointers because Wheel events - // might come before any PointerEvents, and since wheel events don't contain - // pointerId we always assign `device: _mouseDeviceId` to them. - final int device = kind == ui.PointerDeviceKind.mouse ? _mouseDeviceId : event.pointerId!; final double tilt = _computeHighestTilt(event); final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp!); _pointerDataConverter.convert( @@ -593,7 +589,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { timeStamp: timeStamp, kind: kind, signalKind: ui.PointerSignalKind.none, - device: device, + device: _getPointerId(event), physicalX: event.client.x.toDouble() * ui.window.devicePixelRatio, physicalY: event.client.y.toDouble() * ui.window.devicePixelRatio, buttons: details.buttons, @@ -632,6 +628,14 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { } } + int _getPointerId(html.PointerEvent event) { + // We force `device: _mouseDeviceId` on mouse pointers because Wheel events + // might come before any PointerEvents, and since wheel events don't contain + // pointerId we always assign `device: _mouseDeviceId` to them. + final ui.PointerDeviceKind kind = _pointerTypeToDeviceKind(event.pointerType!); + return kind == ui.PointerDeviceKind.mouse ? _mouseDeviceId : event.pointerId!; + } + /// Tilt angle is -90 to + 90. Take maximum deflection and convert to radians. double _computeHighestTilt(html.PointerEvent e) => (e.tiltX!.abs() > e.tiltY!.abs() ? e.tiltX : e.tiltY)!.toDouble() / diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index 8aa7efd4f2edc..15a7eeee9ea7c 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart @@ -2090,6 +2090,60 @@ void testMain() { }, ); + _testEach<_PointerEventContext>( + [ + _PointerEventContext(), + ], + 'handles random pointer id on up events', + (_PointerEventContext context) { + PointerBinding.instance.debugOverrideDetector(context); + // This happens with pens that are simulated with mouse events + // (e.g. Wacom). It sends events with the pointer type "mouse", and + // assigns a random pointer ID to each event. + // + // For more info, see: https://github.com/flutter/flutter/issues/75559 + + List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + glassPane.dispatchEvent(context.mouseDown( + pointerId: 12, + button: 0, + buttons: 1, + clientX: 10.0, + clientY: 10.0, + )); + + expect(packets, hasLength(1)); + expect(packets.single.data, hasLength(2)); + + expect(packets.single.data[0].change, equals(ui.PointerChange.add)); + expect(packets.single.data[0].synthesized, equals(true)); + expect(packets.single.data[1].change, equals(ui.PointerChange.down)); + packets.clear(); + + expect( + () { + glassPane.dispatchEvent(context.mouseUp( + pointerId: 41, + button: 0, + buttons: 0, + clientX: 10.0, + clientY: 10.0, + )); + }, + returnsNormally, + ); + + expect(packets, hasLength(1)); + expect(packets.single.data, hasLength(1)); + + expect(packets.single.data[0].change, equals(ui.PointerChange.up)); + }, + ); + // TOUCH ADAPTER _testEach( @@ -2537,9 +2591,9 @@ class _PointerEventContext extends _BasicEventContext @override html.Event mouseDown( - {double clientX, double clientY, int button, int buttons}) { + {double clientX, double clientY, int button, int buttons, int pointerId = 1}) { return _downWithFullDetails( - pointer: 1, + pointer: pointerId, buttons: buttons, button: button, clientX: clientX, @@ -2581,9 +2635,9 @@ class _PointerEventContext extends _BasicEventContext @override html.Event mouseMove( - {double clientX, double clientY, int button, int buttons}) { + {double clientX, double clientY, int button, int buttons, int pointerId = 1}) { return _moveWithFullDetails( - pointer: 1, + pointer: pointerId, buttons: buttons, button: button, clientX: clientX, @@ -2623,9 +2677,9 @@ class _PointerEventContext extends _BasicEventContext } @override - html.Event mouseUp({double clientX, double clientY, int button, int buttons}) { + html.Event mouseUp({double clientX, double clientY, int button, int buttons, int pointerId = 1}) { return _upWithFullDetails( - pointer: 1, + pointer: pointerId, button: button, buttons: buttons, clientX: clientX,