Skip to content
This repository has been archived by the owner on Feb 25, 2025. It is now read-only.

Web trackpad pan #36346

Merged
merged 13 commits into from
Dec 12, 2022
Merged

Web trackpad pan #36346

merged 13 commits into from
Dec 12, 2022

Conversation

moffatman
Copy link
Contributor

@moffatman moffatman commented Sep 22, 2022

Use a kind of heuristic to guess at whether an event is from mouse or trackpad.

Part of flutter/flutter#64210

Sequence

  1. InteractiveViewer discrete trackpad panning flutter#112171
  2. This PR

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides].
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the [CLA].
  • All existing and new tests are passing.

@flutter-dashboard flutter-dashboard bot added the platform-web Code specifically for the web engine label Sep 22, 2022
Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I did miss this PR among the other trackpad PRs!

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

CC @mdebbar in case you or anyone else more familiar with the web engine wants to take a look before this is merged.

Edit: Just noting that this PR shouldn't be merged until the framework PR is merged. I tried them out together and left a review over there with some problems I ran into.

Copy link
Contributor

@mdebbar mdebbar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR looks good to me with some minor suggestions.

@moffatman moffatman force-pushed the web_trackpad_pan branch 2 times, most recently from aa41020 to 55505af Compare November 13, 2022 00:59
@Piinks
Copy link
Contributor

Piinks commented Nov 18, 2022

I'd like to test this out on Monday, I have been working on animating discrete mouse wheel data in flutter/flutter#115314, and ran into the fact that we don't distinguish between trackpad and mouse on web.
I've been catching up on the discussion in flutter/flutter#112171 (comment) as well.
I have a bunch of different mice and trackpads I have been testing with, so I can do the same here. Thanks for contributing this! This may enable a lot of different fixes in the framework!

@skia-gold
Copy link

Gold has detected about 70 new digest(s) on patchset 9.
View them at https://flutter-engine-gold.skia.org/cl/github/36346

@skia-gold
Copy link

Gold has detected about 2 new digest(s) on patchset 9.
View them at https://flutter-engine-gold.skia.org/cl/github/36346

@moffatman
Copy link
Contributor Author

Ah I think the PR's Gold is broken now, I had the issue before, will probably have to recreate it...

@Piinks
Copy link
Contributor

Piinks commented Nov 21, 2022

Ah I think the PR's Gold is broken now, I had the issue before, will probably have to recreate it...

I think you can resolve it by creating a new commit and not force pushing. Skia Gold is easily confused by force pushes unfortunately.

@Piinks
Copy link
Contributor

Piinks commented Nov 21, 2022

Oh it looks like Gold is fine. 👍

@Piinks
Copy link
Contributor

Piinks commented Nov 21, 2022

Ok I have tested this out with 4 mice and 2 trackpads running web on Mac. 😁

IMG_0434

I ran a simple flutter app with an infinite number of scrolling items in a list, and had (in the framework) the event data output to tell me:

  1. trackpad or mouse
  2. time stamp
  3. unconverted scroll delta (before it is divided by the device pixel ratio)
  4. final scroll delta reported to the framework

For trackpads, every single one was correctly identified as a trackpad. 🎉

For the 4 mice, all of them were able to fool this into thinking it was a trackpad when I scrolled the wheel really fast. It was easier to do on some compared to others, and it would only occur once per occasion - the event before and after were correctly identified as mouse. The incoming delta was ~40 or ~320 in the instances I saw it occur.

On one of the cheap mouses I have, it was really easy to hit the 40 delta, so it was easier to reproduce. On one of the gaming mice, it was harder to trigger it, and would happen with an input of 320. I am guessing these devices all have a different level of precision.

Here is some of that output, let me know if it helps, or if I can gather more info for you. :)
This is one of the cheap mice that was really easy to trigger with a fling on the wheel.

Unconverted delta: 4.000244140625
PointerDeviceKind.mouse - 0:15:28.540299 : Offset(-0.0, 4.0)
PointerDeviceKind.mouse
Unconverted delta: 40.0189208984375
PointerDeviceKind.trackpad - 0:15:28.556200 : Offset(-0.0, 40.0)
PointerDeviceKind.trackpad
Unconverted delta: 168.1787109375
PointerDeviceKind.mouse - 0:15:28.564299 : Offset(-0.0, 168.2)
PointerDeviceKind.mouse
Unconverted delta: 500.9765625
PointerDeviceKind.mouse - 0:15:28.580200 : Offset(-0.0, 501.0)
PointerDeviceKind.mouse
Unconverted delta: 684.9090576171875
PointerDeviceKind.mouse - 0:15:28.596200 : Offset(-0.0, 684.9)
PointerDeviceKind.mouse
Unconverted delta: 365.3118896484375
PointerDeviceKind.mouse - 0:15:28.604200 : Offset(-0.0, 365.3)
PointerDeviceKind.mouse

@moffatman
Copy link
Contributor Author

I was able to reproduce it, looks like sometimes macOS scroll acceleration can by chance happen to line up with expected values for a trackpad. Should be solvable with by cross-checking with the previous event, since this shouldn't happen for more than one event in a row.

@moffatman
Copy link
Contributor Author

@Piinks See if you have the same problems with the latest change

@Piinks
Copy link
Contributor

Piinks commented Nov 22, 2022

Thanks! I will check it out today!

@Piinks
Copy link
Contributor

Piinks commented Nov 22, 2022

Still able to trip it at ~40:

Engine says: PointerDeviceKind.mouse
Unconverted delta: 4.000244140625
PointerDeviceKind.mouse - 0:00:05.808799 : Offset(-0.0, 4.0)
PointerDeviceKind.mouse
Engine says: PointerDeviceKind.trackpad
Unconverted delta: 39.991455078125
PointerDeviceKind.trackpad - 0:00:05.824900 : Offset(-0.0, 40.0)
PointerDeviceKind.trackpad
Engine says: PointerDeviceKind.mouse
Unconverted delta: 159.752197265625
PointerDeviceKind.mouse - 0:00:05.840900 : Offset(-0.0, 159.8)
PointerDeviceKind.mouse
Engine says: PointerDeviceKind.mouse
Unconverted delta: 229.3438720703125
PointerDeviceKind.mouse - 0:00:05.848900 : Offset(-0.0, 229.3)
PointerDeviceKind.mouse
Engine says: PointerDeviceKind.mouse
Unconverted delta: 1211.998291015625
PointerDeviceKind.mouse - 0:00:05.888900 : Offset(-0.0, 1212.0)
PointerDeviceKind.mouse

Let me know if there is more info I can gather.

@moffatman
Copy link
Contributor Author

Can you capture the issue again with

print('deltaX=${event.deltaX}, deltaY=${event.deltaY}, wheelDeltaX=${event.wheelDeltaX}, wheelDeltaY=${event.wheelDeltaY}, timeStamp=${event.timeStamp}');

at the beginning of _isTrackpadEvent in pointer_binding.dart? Also would be good to see at least 2 events before the incorrect one.

@Piinks
Copy link
Contributor

Piinks commented Nov 28, 2022

Can you capture the issue again with ...

Totally, whipping this up now. I was out for the holiday.

Also would be good to see at least 2 events before the incorrect one.

In the examples I shared, that is the whole event, from starting to ending the scroll on the stepped wheel. The incorrect one showed up on the second event.

@Piinks
Copy link
Contributor

Piinks commented Nov 28, 2022

Here it is with one quick twirl on the mouse wheel:

Performing hot restart...                                          153ms
Restarted application in 153ms.
deltaX=-0.0, deltaY=4.000244140625, wheelDeltaX=0, wheelDeltaY=-120, timeStamp=27866.300000190735
Mouse
deltaX=-0.0, deltaY=39.9945068359375, wheelDeltaX=0, wheelDeltaY=-120, timeStamp=27882.200000286102
Trackpad
deltaX=-0.0, deltaY=433.90625, wheelDeltaX=0, wheelDeltaY=-1200, timeStamp=27906.200000286102
Mouse
deltaX=-0.0, deltaY=654.576416015625, wheelDeltaX=0, wheelDeltaY=-1800, timeStamp=27922.300000190735
Mouse
deltaX=-0.0, deltaY=764.7760009765625, wheelDeltaX=0, wheelDeltaY=-2160, timeStamp=27938.200000286102
Mouse

@Piinks
Copy link
Contributor

Piinks commented Nov 29, 2022

Nice! I just tested it with all of the devices I have, it was correct every time. 🎉

Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yjbanov was also interested in this.. FYI

This LGTM!

@yjbanov
Copy link
Contributor

yjbanov commented Nov 29, 2022

Thanks @Piinks for jumping in and testing it thoroughly! Great to see progress made. I'm curious what failures would look and feel like if this logic fails. We can't enumerate all browser engines, and some browser engines may be customized to use different numbers. In that case, will we observe slight degradations in scrolling quality? Or could the issues be severe?

@moffatman
Copy link
Contributor Author

This doesn't affect scrolling, it's only for custom interactions such as InteractiveViewer. If there was some problem you might see strangeness there where during a pan it might zoom by accident on one event in a series. We can gate it to only certain browser / OS combinations if you want. Already Firefox is ruled out. I personally have only verified it on macOS Chrome and Safari.

@Piinks
Copy link
Contributor

Piinks commented Nov 29, 2022

Thanks for explaining @moffatman. I plan to use this in the framework to execute an animation when mouse wheel scroll input comes from a mouse, ignoring trackpad - as an additional example of how this will be used. From my understanding, this change will not necessarily change anything in the framework unless we make a change there based on the input device.

Basically, bias towards choosing mouse, but if timestamps are available,
we can check the previous event and ensure that false-mouses are avoided.
Apparently some high-precision mice use 120 instead of 240 as the
wheelDelta per tick.
@moffatman
Copy link
Contributor Author

flutter/flutter#112171 needs to be approved and merged first due to the assertion changes

Also fix setting of timeStamp in test.
@moffatman
Copy link
Contributor Author

@Piinks I updated the logic again, can you recheck with the various devices you have and see if there is a regression? Thanks 😄

@Piinks
Copy link
Contributor

Piinks commented Dec 5, 2022

Sure thing! I have even more mice now. 😳

@Piinks
Copy link
Contributor

Piinks commented Dec 5, 2022

Tested with 7 mice and 2 trackpads. I was not able to fool it. @justinmc had different luck last time with the magic mouse. On my magic mouse, I swiped and flung on it a bunch but did not see it come through as a mouse. 👍

@justinmc
Copy link
Contributor

justinmc commented Dec 5, 2022

@Piinks That's great! I'll try again with my magic mouse tonight and then plan to merge flutter/flutter#112171 if I can't reproduce that bug.

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some last minute nits.

@@ -349,7 +350,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter {
return (wheelDelta - (-3 * delta)).abs() > 1;
}

bool _isTrackpadEvent(DomWheelEvent event, DomWheelEvent? lastEvent) {
bool _isTrackpadEvent(DomWheelEvent event, DomWheelEvent? lastEvent, bool? lastEventWasTrackpad) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use _lastWheelEventWasTrackpad directly instead of passing it in?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, now that _isTrackpadEvent doesn't call itself, maybe you don't need to pass in _lastWheelEvent either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, I'll change it to just use the members directly.

@@ -264,6 +264,7 @@ abstract class _BaseAdapter {
final PointerDataConverter _pointerDataConverter;
final KeyboardConverter _keyboardConverter;
DomWheelEvent? _lastWheelEvent;
bool? _lastWheelEventWasTrackpad;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: What do you think about making this non-nullable and initializing it to false? Currently it probably represents reality more accurately as-is, because a value of null means that there was no last wheel event. However, that null state is not used anywhere.

Up to you what you think is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have it this way as in some previous iterations the null state did matter.

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

I left a review on the framework PR (flutter/flutter#112171) saying that I was unable to break this using the Magic Mouse this time. Thanks for fixing that and for cleaning up those last few comments!

Rerunning the Fuchsia test, it looked like an infrastructure flake.

@Piinks
Copy link
Contributor

Piinks commented Dec 12, 2022

The framework change landed, I am just waiting on a lean build from that in the framework and then we can land this. 🎉

@Piinks Piinks added the autosubmit Merge PR when tree becomes green via auto submit App label Dec 12, 2022
@auto-submit auto-submit bot merged commit 99f3d19 into flutter:main Dec 12, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 13, 2022
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Dec 13, 2022
…116939)

* b97d3545f [Impeller] Speculatively attempt to fix Metal PSO construction errors on host targets. (flutter/engine#38229)

* 99f3d1931 Web trackpad pan (flutter/engine#36346)

* e144f81e9 [impeller] Fix typo in compiller help (flutter/engine#38214)
loic-sharma pushed a commit to loic-sharma/flutter-engine that referenced this pull request Dec 16, 2022
* Guess at trackpad pans on web

* Add test

* Update comment

* Handle macOS accelerated scroll wheel

* Fix test after last commit

* Disable on firefox

* Pull out _isTrackpadEvent and add doc links

* Fix issue with floating point / integer conversion error.

* Workaround for magic mouse events which happen to be divisible by 120.

* Refactor to handle bad luck in accelerated mouse deltas.

Basically, bias towards choosing mouse, but if timestamps are available,
we can check the previous event and ensure that false-mouses are avoided.

* Use 120 wheelDelta to identify mouse-accelerated events instead of 240

Apparently some high-precision mice use 120 instead of 240 as the
wheelDelta per tick.

* Handle multiple bad-luck events in a row.

Also fix setting of timeStamp in test.

* Cleanup parameters
loic-sharma pushed a commit to loic-sharma/flutter-engine that referenced this pull request Jan 3, 2023
* Guess at trackpad pans on web

* Add test

* Update comment

* Handle macOS accelerated scroll wheel

* Fix test after last commit

* Disable on firefox

* Pull out _isTrackpadEvent and add doc links

* Fix issue with floating point / integer conversion error.

* Workaround for magic mouse events which happen to be divisible by 120.

* Refactor to handle bad luck in accelerated mouse deltas.

Basically, bias towards choosing mouse, but if timestamps are available,
we can check the previous event and ensure that false-mouses are avoided.

* Use 120 wheelDelta to identify mouse-accelerated events instead of 240

Apparently some high-precision mice use 120 instead of 240 as the
wheelDelta per tick.

* Handle multiple bad-luck events in a row.

Also fix setting of timeStamp in test.

* Cleanup parameters
gspencergoog pushed a commit to gspencergoog/flutter that referenced this pull request Jan 19, 2023
…lutter#116939)

* b97d3545f [Impeller] Speculatively attempt to fix Metal PSO construction errors on host targets. (flutter/engine#38229)

* 99f3d1931 Web trackpad pan (flutter/engine#36346)

* e144f81e9 [impeller] Fix typo in compiller help (flutter/engine#38214)
Comment on lines +374 to +375
// While not in any formal web standard, `blink` and `webkit` browsers use
// a delta of 120 to represent one mouse wheel turn. If both dimensions of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
autosubmit Merge PR when tree becomes green via auto submit App platform-web Code specifically for the web engine
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants