Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 47 additions & 14 deletions shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -705,29 +705,43 @@ - (BOOL)running {
return _engine != nullptr;
}

- (void)updateDisplayConfig:(NSNotification*)notification {
[self updateDisplayConfig];
}

- (void)updateDisplayConfig {
if (!_engine) {
return;
}

CVDisplayLinkRef displayLinkRef;
CGDirectDisplayID mainDisplayID = CGMainDisplayID();
CVDisplayLinkCreateWithCGDisplay(mainDisplayID, &displayLinkRef);
CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
if (!(nominal.flags & kCVTimeIsIndefinite)) {
double refreshRate = static_cast<double>(nominal.timeScale) / nominal.timeValue;
std::vector<FlutterEngineDisplay> displays;
for (NSScreen* screen : [NSScreen screens]) {
CGDirectDisplayID displayID =
static_cast<CGDirectDisplayID>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);

FlutterEngineDisplay display;
display.struct_size = sizeof(display);
display.display_id = mainDisplayID;
display.refresh_rate = round(refreshRate);
display.display_id = displayID;
display.single_display = false;
display.width = screen.frame.size.width;
display.height = screen.frame.size.height;
display.device_pixel_ratio = screen.backingScaleFactor;

CVDisplayLinkRef displayLinkRef;
CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);

if (!(nominal.flags & kCVTimeIsIndefinite)) {
double refreshRate = static_cast<double>(nominal.timeScale) / nominal.timeValue;
display.refresh_rate = round(refreshRate);
}

std::vector<FlutterEngineDisplay> displays = {display};
_embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
displays.data(), displays.size());
}
CVDisplayLinkRelease(displayLinkRef);

CVDisplayLinkRelease(displayLinkRef);
displays.push_back(std::move(display));
}
_embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
displays.data(), displays.size());
}

- (void)onSettingsChanged:(NSNotification*)notification {
Expand Down Expand Up @@ -776,14 +790,15 @@ - (void)updateWindowMetricsForViewController:(FlutterViewController*)viewControl
CGRect scaledBounds = [view convertRectToBacking:view.bounds];
CGSize scaledSize = scaledBounds.size;
double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;

auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
const FlutterWindowMetricsEvent windowMetricsEvent = {
.struct_size = sizeof(windowMetricsEvent),
.width = static_cast<size_t>(scaledSize.width),
.height = static_cast<size_t>(scaledSize.height),
.pixel_ratio = pixelRatio,
.left = static_cast<size_t>(scaledBounds.origin.x),
.top = static_cast<size_t>(scaledBounds.origin.y),
.display_id = static_cast<size_t>(displayId),
};
_embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
}
Expand Down Expand Up @@ -958,6 +973,14 @@ - (void)setUpNotificationCenterListeners {
selector:@selector(applicationWillTerminate:)
name:NSApplicationWillTerminateNotification
object:nil];
[center addObserver:self
selector:@selector(windowDidChangeScreen:)
name:NSWindowDidChangeScreenNotification
object:nil];
[center addObserver:self
selector:@selector(updateDisplayConfig:)
name:NSApplicationDidChangeScreenParametersNotification
object:nil];
}

- (void)addInternalPlugins {
Expand All @@ -981,6 +1004,16 @@ - (void)applicationWillTerminate:(NSNotification*)notification {
[self shutDownEngine];
}

- (void)windowDidChangeScreen:(NSNotification*)notification {
// Update window metric for all view controllers since the display_id has
// changed.
NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
FlutterViewController* nextViewController;
while ((nextViewController = [viewControllerEnumerator nextObject])) {
[self updateWindowMetricsForViewController:nextViewController];
}
}

- (void)onAccessibilityStatusChanged:(NSNotification*)notification {
BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
Expand Down
50 changes: 50 additions & 0 deletions shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,56 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable
EXPECT_TRUE(announced);
}

TEST_F(FlutterEngineTest, RunWithEntrypointUpdatesDisplayConfig) {
BOOL updated = FALSE;
FlutterEngine* engine = GetFlutterEngine();
auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
NotifyDisplayUpdate, ([&updated, &original_update_displays](
auto engine, auto update_type, auto* displays, auto display_count) {
updated = TRUE;
return original_update_displays(engine, update_type, displays, display_count);
}));

EXPECT_TRUE([engine runWithEntrypoint:@"main"]);
EXPECT_TRUE(updated);
}

TEST_F(FlutterEngineTest, NotificationsUpdateDisplays) {
BOOL updated = FALSE;
FlutterEngine* engine = GetFlutterEngine();
auto original_set_viewport_metrics = engine.embedderAPI.SendWindowMetricsEvent;
engine.embedderAPI.SendWindowMetricsEvent = MOCK_ENGINE_PROC(
SendWindowMetricsEvent,
([&updated, &original_set_viewport_metrics](auto engine, auto* window_metrics) {
updated = TRUE;
return original_set_viewport_metrics(engine, window_metrics);
}));

EXPECT_TRUE([engine runWithEntrypoint:@"main"]);

updated = FALSE;
[[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
object:nil];
// No VC.
EXPECT_FALSE(updated);

FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine
nibName:nil
bundle:nil];
[viewController loadView];
viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);

[[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
object:nil];
EXPECT_TRUE(updated);

updated = FALSE;
[[NSNotificationCenter defaultCenter]
postNotificationName:NSApplicationDidChangeScreenParametersNotification
object:nil];
}

} // namespace flutter::testing

// NOLINTEND(clang-analyzer-core.StackAddressEscape)
11 changes: 7 additions & 4 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2062,6 +2062,7 @@ FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
SAFE_ACCESS(flutter_metrics, physical_view_inset_bottom, 0.0);
metrics.physical_view_inset_left =
SAFE_ACCESS(flutter_metrics, physical_view_inset_left, 0.0);
metrics.display_id = SAFE_ACCESS(flutter_metrics, display_id, 0);

if (metrics.device_pixel_ratio <= 0.0) {
return LOG_EMBEDDER_ERROR(
Expand Down Expand Up @@ -2998,11 +2999,13 @@ FlutterEngineResult FlutterEngineNotifyDisplayUpdate(
case kFlutterEngineDisplaysUpdateTypeStartup: {
std::vector<std::unique_ptr<flutter::Display>> displays;
for (size_t i = 0; i < display_count; i++) {
const auto* display = &embedder_displays[i];
displays.push_back(std::make_unique<flutter::Display>(
embedder_displays[i].display_id, embedder_displays[i].refresh_rate,
// TODO(dnfield): Supply real values
// https://github.com/flutter/flutter/issues/125939
-1, -1, -1));
SAFE_ACCESS(display, display_id, i), //
SAFE_ACCESS(display, refresh_rate, 0), //
SAFE_ACCESS(display, width, 0), //
SAFE_ACCESS(display, height, 0), //
SAFE_ACCESS(display, device_pixel_ratio, 1)));
}
engine->GetShell().OnDisplayUpdates(std::move(displays));
return kSuccess;
Expand Down
12 changes: 12 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,8 @@ typedef struct {
double physical_view_inset_bottom;
/// Left inset of window.
double physical_view_inset_left;
/// The identifier of the display the view is rendering on.
size_t display_id;
} FlutterWindowMetricsEvent;

/// The phase of the pointer event.
Expand Down Expand Up @@ -1673,6 +1675,16 @@ typedef struct {
/// This represents the refresh period in frames per second. This value may be
/// zero if the device is not running or unavailable or unknown.
double refresh_rate;

/// The width of the display, in physical pixels.
double width;

/// The height of the display, in physical pixels.
double height;

/// The pixel ratio of the display, which is used to convert physical pixels
/// to logical pixels.
double device_pixel_ratio;
} FlutterEngineDisplay;

/// The update type parameter that is passed to
Expand Down