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

Commit 15cb5fe

Browse files
authored
[macOS] Handle interleaved movement and gesture events (#52201)
Fixes flutter/flutter#146893 The crash happens, because with magic mouse, it is quite common to receive `mouseExited:` event while the gesture event is still in progress. This would reset the gesture state while the gesture event is still active, leading to assertion when the gesture event ends. The state machine assumes that when mouse exists that also ends any gesture events. However that is not the case with magic mouse, which handles the movement and gesture events separately. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent ade5095 commit 15cb5fe

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

shell/platform/darwin/macos/framework/Source/FlutterViewController.mm

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ void Reset() {
129129
flutter_state_is_down = false;
130130
has_pending_exit = false;
131131
buttons = 0;
132-
GestureReset();
133132
}
134133
};
135134

shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ - (bool)testKeyEquivalentIsPassedToTextInputPlugin:(id)mockEngine;
101101
- (bool)testFlagsChangedEventsArePropagatedIfNotHandled:(id)mockEngine;
102102
- (bool)testKeyboardIsRestartedOnEngineRestart:(id)mockEngine;
103103
- (bool)testTrackpadGesturesAreSentToFramework:(id)mockEngine;
104+
- (bool)mouseAndGestureEventsAreHandledSeparately:(id)engineMock;
104105
- (bool)testMouseDownUpEventsSentToNextResponder:(id)mockEngine;
105106
- (bool)testModifierKeysAreSynthesizedOnMouseMove:(id)mockEngine;
106107
- (bool)testViewWillAppearCalledMultipleTimes:(id)mockEngine;
@@ -287,6 +288,12 @@ id MockGestureEvent(NSEventType type, NSEventPhase phase, double magnification,
287288
[[FlutterViewControllerTestObjC alloc] testTrackpadGesturesAreSentToFramework:mockEngine]);
288289
}
289290

291+
TEST_F(FlutterViewControllerMockEngineTest, TestmouseAndGestureEventsAreHandledSeparately) {
292+
id mockEngine = GetMockEngine();
293+
ASSERT_TRUE(
294+
[[FlutterViewControllerTestObjC alloc] mouseAndGestureEventsAreHandledSeparately:mockEngine]);
295+
}
296+
290297
TEST_F(FlutterViewControllerMockEngineTest, TestMouseDownUpEventsSentToNextResponder) {
291298
id mockEngine = GetMockEngine();
292299
ASSERT_TRUE(
@@ -1004,6 +1011,42 @@ - (bool)testTrackpadGesturesAreSentToFramework:(id)engineMock {
10041011
return true;
10051012
}
10061013

1014+
// Magic mouse can interleave mouse events with scroll events. This must not crash.
1015+
- (bool)mouseAndGestureEventsAreHandledSeparately:(id)engineMock {
1016+
FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
1017+
nibName:@""
1018+
bundle:nil];
1019+
[viewController loadView];
1020+
1021+
// Test for pan events.
1022+
// Start gesture.
1023+
CGEventRef cgEventStart = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
1024+
CGEventSetType(cgEventStart, kCGEventScrollWheel);
1025+
CGEventSetIntegerValueField(cgEventStart, kCGScrollWheelEventScrollPhase, kCGScrollPhaseBegan);
1026+
CGEventSetIntegerValueField(cgEventStart, kCGScrollWheelEventIsContinuous, 1);
1027+
[viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventStart]];
1028+
CFRelease(cgEventStart);
1029+
1030+
CGEventRef cgEventUpdate = CGEventCreateCopy(cgEventStart);
1031+
CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventScrollPhase, kCGScrollPhaseChanged);
1032+
CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventDeltaAxis2, 1); // pan_x
1033+
CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventDeltaAxis1, 2); // pan_y
1034+
[viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventUpdate]];
1035+
CFRelease(cgEventUpdate);
1036+
1037+
NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(0x00);
1038+
[viewController mouseEntered:mouseEvent];
1039+
[viewController mouseExited:mouseEvent];
1040+
1041+
// End gesture.
1042+
CGEventRef cgEventEnd = CGEventCreateCopy(cgEventStart);
1043+
CGEventSetIntegerValueField(cgEventEnd, kCGScrollWheelEventScrollPhase, kCGScrollPhaseEnded);
1044+
[viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventEnd]];
1045+
CFRelease(cgEventEnd);
1046+
1047+
return true;
1048+
}
1049+
10071050
- (bool)testViewWillAppearCalledMultipleTimes:(id)engineMock {
10081051
FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
10091052
nibName:@""

0 commit comments

Comments
 (0)